mirror of https://github.com/FreeTubeApp/FreeTube
Add Live Video Support, Update Electron, Update Mini Player
This commit is contained in:
parent
e5fd7f6824
commit
896d33d434
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "FreeTube",
|
||||
"productName": "FreeTube",
|
||||
"version": "0.5.2",
|
||||
"version": "0.5.3",
|
||||
"description": "An Open Source YouTube app for privacy.",
|
||||
"main": "src/js/init.js",
|
||||
"scripts": {
|
||||
|
@ -80,26 +80,26 @@
|
|||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron-forge": "^5.2.2",
|
||||
"electron-forge-maker-appimage": "^20.28.3",
|
||||
"electron-forge": "^5.2.4",
|
||||
"electron-forge-maker-appimage": "^20.39.0",
|
||||
"electron-installer-flatpak": "^0.8.0",
|
||||
"electron-installer-snap": "^2.0.1",
|
||||
"electron-prebuilt-compile": "3.0.2",
|
||||
"electron-winstaller": "^2.7.0"
|
||||
"electron-installer-snap": "^3.1.1",
|
||||
"electron-prebuilt-compile": "4.0.0",
|
||||
"electron-winstaller": "^3.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"autolinker": "^1.6.2",
|
||||
"autolinker": "^3.0.5",
|
||||
"commonjs": "0.0.1",
|
||||
"dateformat": "^3.0.3",
|
||||
"electron-compile": "6.4.2",
|
||||
"electron-context-menu": "^0.10.0",
|
||||
"electron-compile": "6.4.4",
|
||||
"electron-context-menu": "^0.12.0",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"jquery": "^3.3.1",
|
||||
"jsdom": "^11.11.0",
|
||||
"mustache": "^2.3.0",
|
||||
"jquery": "^3.4.0",
|
||||
"jsdom": "^15.0.0",
|
||||
"mustache": "^3.0.1",
|
||||
"nedb": "^1.8.0",
|
||||
"opml-to-json": "0.0.3",
|
||||
"vue": "^2.5.17",
|
||||
"vue": "^2.6.10",
|
||||
"ytdl-core": "^0.29.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,12 +128,7 @@ let videoShortcutHandler = function (event) {
|
|||
// F Key
|
||||
event.preventDefault();
|
||||
|
||||
if (player.webkitDisplayingFullscreen) {
|
||||
player.webkitExitFullscreen
|
||||
}
|
||||
else{
|
||||
player.webkitRequestFullscreen();
|
||||
}
|
||||
$('.mejs__fullscreen-button').click();
|
||||
break;
|
||||
case 77:
|
||||
// M Key
|
||||
|
@ -230,11 +225,7 @@ let videoShortcutHandler = function (event) {
|
|||
};
|
||||
|
||||
let fullscreenVideo = function (event) {
|
||||
if (document.webkitFullscreenElement !== null) {
|
||||
document.webkitExitFullscreen();
|
||||
} else {
|
||||
$('.videoPlayer').get(0).webkitRequestFullscreen();
|
||||
}
|
||||
$('.mejs__fullscreen-button').click();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -247,7 +238,7 @@ $(document).on('click', '#showComments', showComments);
|
|||
|
||||
// $(document).on('click', '.videoPlayer', playPauseVideo);
|
||||
|
||||
// $(document).on('dblclick', '.videoPlayer', fullscreenVideo);
|
||||
$(document).on('dblclick', '.videoPlayer', fullscreenVideo);
|
||||
|
||||
$(document).on('keydown', videoShortcutHandler);
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required');
|
|||
|
||||
app.commandLine.appendSwitch('disable-web-security');
|
||||
|
||||
const isSecondInstance = app.makeSingleInstance((commandLine, workingDirectory) => {
|
||||
/*const isSecondInstance = app.makeSingleInstance((commandLine, workingDirectory) => {
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (win) {
|
||||
if (win.isMinimized()) win.restore()
|
||||
|
@ -52,9 +52,9 @@ const isSecondInstance = app.makeSingleInstance((commandLine, workingDirectory)
|
|||
|
||||
win.webContents.send('ping', commandLine)
|
||||
}
|
||||
});
|
||||
});*/
|
||||
|
||||
if (require('electron-squirrel-startup') || isSecondInstance) app.quit();
|
||||
if (require('electron-squirrel-startup')) app.quit();
|
||||
|
||||
/**
|
||||
* Initialize the Electron application
|
||||
|
|
|
@ -38,6 +38,8 @@ const shell = electron.shell; // Used to open external links into the user's nat
|
|||
const clipboard = electron.clipboard;
|
||||
const getOpml = require('opml-to-json'); // Gets the file type for imported files.
|
||||
const fs = require('fs'); // Used to read files. Specifically in the settings page.
|
||||
const url = require('url');
|
||||
const path = require('path');
|
||||
|
||||
let dialog = electron.remote.dialog; // Used for opening file browser to export / import subscriptions.
|
||||
let toastTimeout; // Timeout for toast notifications.
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FreeTube is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* A file for handling mini-player functionality
|
||||
*/
|
||||
|
||||
import Vue from '../js/vue.js';
|
||||
|
||||
const electron = require('electron');
|
||||
|
||||
let mouseTimeout; // Timeout for hiding the mouse cursor on video playback
|
||||
let checkedSettings = true;
|
||||
|
||||
let miniPlayerView = new Vue({
|
||||
el: '#player',
|
||||
data: {
|
||||
video360p: '',
|
||||
valid360p: true,
|
||||
video720p: '',
|
||||
valid720p: true,
|
||||
videoAudio: '',
|
||||
validAudio: true,
|
||||
videoDash: '',
|
||||
validDash: true,
|
||||
videoLive: '',
|
||||
validLive: false,
|
||||
subtitleHtml: '',
|
||||
videoThumbnail: '',
|
||||
defaultPlaybackRate: '',
|
||||
quality: '',
|
||||
volume: '',
|
||||
currentTime: '',
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Hide the mouse cursor after ~3 seconds. Used to hide the video when the user
|
||||
* hovers the mouse over the video player.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function hideMouseTimeout() {
|
||||
$('.videoPlayer')[0].style.cursor = 'default';
|
||||
clearTimeout(mouseTimeout);
|
||||
mouseTimeout = window.setTimeout(function () {
|
||||
$('.videoPlayer')[0].style.cursor = 'none';
|
||||
}, 2800);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the timeout for the mouse cursor as a fallback.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function removeMouseTimeout() {
|
||||
$('.videoPlayer')[0].style.cursor = 'default';
|
||||
clearTimeout(mouseTimeout);
|
||||
}
|
||||
|
||||
function checkVideoSettings() {
|
||||
if (checkedSettings === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
let player = new MediaElementPlayer('player', {
|
||||
features: ['playpause', 'current', 'loop', 'tracks', 'progress', 'duration', 'volume', 'stop', 'speed', 'quality', 'fullscreen'],
|
||||
speeds: ['2', '1.75', '1.5', '1.25', '1', '0.75', '0.5', '0.25'],
|
||||
defaultSpeed: miniPlayerView.defaultPlaybackRate,
|
||||
qualityText: 'Quality',
|
||||
defaultQuality: miniPlayerView.quality,
|
||||
stretching: 'responsive',
|
||||
startVolume: miniPlayerView.volume,
|
||||
|
||||
success: function(mediaElement, originalNode, instance) {
|
||||
console.log(mediaElement,originalNode,instance);
|
||||
|
||||
instance.currentTime = miniPlayerView.currentTime;
|
||||
instance.play();
|
||||
|
||||
checkedSettings = false;
|
||||
|
||||
/*if (autoplay) {
|
||||
instance.play();
|
||||
}
|
||||
|
||||
if (enableSubtitles) {
|
||||
instance.options.startLanguage = 'en';
|
||||
}*/
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
electron.ipcRenderer.on('ping', function(event, message) {
|
||||
console.log(message);
|
||||
|
||||
miniPlayerView.video360p = message.video360p;
|
||||
miniPlayerView.valid360p = message.valid360p;
|
||||
miniPlayerView.video720p = message.video720p;
|
||||
miniPlayerView.valid720p = message.valid720p;
|
||||
miniPlayerView.videoAudio = message.videoAudio;
|
||||
miniPlayerView.validAudio = message.validAudio;
|
||||
miniPlayerView.videoDash = message.videoDash;
|
||||
miniPlayerView.validDash = message.validDash;
|
||||
miniPlayerView.videoLive = message.videoLive;
|
||||
miniPlayerView.validLive = message.validLive;
|
||||
miniPlayerView.subtitleHtml = message.subtitleHtml;
|
||||
miniPlayerView.videoThumbnail = message.videoThumbnail;
|
||||
miniPlayerView.defaultPlaybackRate = message.defaultPlaybackRate;
|
||||
miniPlayerView.quality = message.quality;
|
||||
miniPlayerView.volume = message.volume;
|
||||
miniPlayerView.currentTime = message.currentTime;
|
||||
|
||||
window.setTimeout(checkVideoSettings, 100);
|
||||
});
|
199
src/js/player.js
199
src/js/player.js
|
@ -44,6 +44,8 @@ function playVideo(videoId, playlistId = '') {
|
|||
playerView.video720p = undefined;
|
||||
playerView.valid720p = true;
|
||||
playerView.videoUrl = '';
|
||||
playerView.videoLive = undefined;
|
||||
playerView.validLive = false;
|
||||
playerView.validDash = true;
|
||||
playerView.videoDash = invidiousInstance + '/api/manifest/dash/' + videoId + '.mpd';
|
||||
playerView.embededHtml = "<iframe width='560' height='315' src='https://www.youtube-nocookie.com/embed/" + videoId + "?rel=0' frameborder='0' allow='autoplay; encrypted-media' allowfullscreen></iframe>";
|
||||
|
@ -69,69 +71,91 @@ function playVideo(videoId, playlistId = '') {
|
|||
console.log(data);
|
||||
|
||||
let videoUrls = data.formats;
|
||||
let formatUrls = data.player_response.streamingData.adaptiveFormats;
|
||||
|
||||
// Search through the returned object to get the 360p and 720p video URLs (If available)
|
||||
Object.keys(videoUrls).forEach((key) => {
|
||||
switch (videoUrls[key]['itag']) {
|
||||
case '18':
|
||||
playerView.video360p = decodeURIComponent(videoUrls[key]['url']);
|
||||
// console.log(playerView.video360p);
|
||||
break;
|
||||
case '22':
|
||||
playerView.video720p = decodeURIComponent(videoUrls[key]['url']);
|
||||
// console.log(playerView.video720p);
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (data.player_response.videoDetails.isLiveContent !== false) {
|
||||
playerView.validDash = false;
|
||||
playerView.valid360p = false;
|
||||
playerView.valid720p = false;
|
||||
playerView.validAudio = false;
|
||||
|
||||
// Last adaptive format will be best the quality audio stream (migrate fully to adaptive formats later)
|
||||
playerView.videoAudio = decodeURIComponent(formatUrls[formatUrls.length - 1]['url']);
|
||||
playerView.validLive = true;
|
||||
|
||||
if (typeof (playerView.videoAudio) === 'undefined') {
|
||||
console.log(playerView.videoAudio);
|
||||
playerView.validAudio = false;
|
||||
youtubedlFinished = true;
|
||||
|
||||
if (youtubedlFinished && invidiousFinished) {
|
||||
loadingView.seen = false;
|
||||
|
||||
if (subscriptionView.seen === false && aboutView.seen === false && headerView.seen === false && searchView.seen === false && settingsView.seen === false && popularView.seen === false && savedView.seen === false && historyView.seen === false && channelView.seen === false && channelVideosView.seen === false) {
|
||||
playerView.seen = true;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
playerView.validLive = false;
|
||||
|
||||
let useEmbedPlayer = false;
|
||||
// Search through the returned object to get the 360p and 720p video URLs (If available)
|
||||
Object.keys(videoUrls).forEach((key) => {
|
||||
switch (videoUrls[key]['itag']) {
|
||||
case '18':
|
||||
playerView.video360p = decodeURIComponent(videoUrls[key]['url']);
|
||||
// console.log(playerView.video360p);
|
||||
break;
|
||||
case '22':
|
||||
playerView.video720p = decodeURIComponent(videoUrls[key]['url']);
|
||||
// console.log(playerView.video720p);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Default to the embeded player if the URLs cannot be found.
|
||||
if (typeof (playerView.video720p) === 'undefined' && typeof (playerView.video360p) === 'undefined') {
|
||||
//useEmbedPlayer = true;
|
||||
playerView.currentQuality = 'EMBED';
|
||||
playerView.playerSeen = false;
|
||||
playerView.valid720p = false;
|
||||
playerView.valid360p = false;
|
||||
playerView.video720p = '';
|
||||
//useEmbedPlayer = true;
|
||||
showToast('Unable to get video file. Reverting to embeded player.');
|
||||
} else if (typeof (playerView.video720p) === 'undefined' && typeof (playerView.video360p) !== 'undefined') {
|
||||
// Default to the 360p video if the 720p URL cannot be found.
|
||||
console.log('Found');
|
||||
playerView.videoUrl = playerView.video360p;
|
||||
playerView.currentQuality = '360p';
|
||||
playerView.valid720p = false;
|
||||
playerView.video720p = '';
|
||||
} else {
|
||||
// Default to the 720p video.
|
||||
playerView.videoUrl = playerView.video720p;
|
||||
playerView.currentQuality = '720p';
|
||||
//playerView.videoUrl = playerView.liveManifest;
|
||||
// Last adaptive format will be best the quality audio stream (migrate fully to adaptive formats later)
|
||||
playerView.videoAudio = decodeURIComponent(videoUrls[videoUrls.length - 1]['url']);
|
||||
|
||||
if (typeof (playerView.videoAudio) === 'undefined') {
|
||||
console.log(playerView.videoAudio);
|
||||
playerView.validAudio = false;
|
||||
}
|
||||
|
||||
let useEmbedPlayer = false;
|
||||
|
||||
// Default to the embeded player if the URLs cannot be found.
|
||||
if (typeof (playerView.video720p) === 'undefined' && typeof (playerView.video360p) === 'undefined') {
|
||||
//useEmbedPlayer = true;
|
||||
playerView.currentQuality = 'EMBED';
|
||||
playerView.playerSeen = false;
|
||||
playerView.valid720p = false;
|
||||
playerView.valid360p = false;
|
||||
playerView.video720p = '';
|
||||
//useEmbedPlayer = true;
|
||||
showToast('Unable to get video file. Reverting to embeded player.');
|
||||
} else if (typeof (playerView.video720p) === 'undefined' && typeof (playerView.video360p) !== 'undefined') {
|
||||
// Default to the 360p video if the 720p URL cannot be found.
|
||||
console.log('Found');
|
||||
playerView.videoUrl = playerView.video360p;
|
||||
playerView.currentQuality = '360p';
|
||||
playerView.valid720p = false;
|
||||
playerView.video720p = '';
|
||||
} else {
|
||||
// Default to the 720p video.
|
||||
playerView.videoUrl = playerView.video720p;
|
||||
playerView.currentQuality = '720p';
|
||||
//playerView.videoUrl = playerView.liveManifest;
|
||||
}
|
||||
|
||||
if (!useEmbedPlayer &&
|
||||
typeof (data.player_response.captions) !== 'undefined' &&
|
||||
typeof (data.player_response.captions.playerCaptionsTracklistRenderer) !== 'undefined' &&
|
||||
typeof (data.player_response.captions.playerCaptionsTracklistRenderer.captionTracks) !== 'undefined') {
|
||||
data.player_response.captions.playerCaptionsTracklistRenderer.captionTracks.forEach((caption) => {
|
||||
let subtitleUrl = invidiousInstance + '/api/v1/captions/' + videoId + '?label=' + caption.name.simpleText;
|
||||
|
||||
videoHtml = videoHtml + '<track kind="subtitles" src="' + subtitleUrl + '" srclang="' + caption.languageCode + '" label="' + caption.name.simpleText + '">';
|
||||
});
|
||||
|
||||
playerView.subtitleHtml = videoHtml;
|
||||
}
|
||||
}
|
||||
|
||||
if (!useEmbedPlayer &&
|
||||
typeof (data.player_response.captions) !== 'undefined' &&
|
||||
typeof (data.player_response.captions.playerCaptionsTracklistRenderer) !== 'undefined' &&
|
||||
typeof (data.player_response.captions.playerCaptionsTracklistRenderer.captionTracks) !== 'undefined') {
|
||||
data.player_response.captions.playerCaptionsTracklistRenderer.captionTracks.forEach((caption) => {
|
||||
let subtitleUrl = invidiousInstance + '/api/v1/captions/' + videoId + '?label=' + caption.name.simpleText;
|
||||
|
||||
videoHtml = videoHtml + '<track kind="subtitles" src="' + subtitleUrl + '" srclang="' + caption.languageCode + '" label="' + caption.name.simpleText + '">';
|
||||
});
|
||||
|
||||
playerView.subtitleHtml = videoHtml;
|
||||
}
|
||||
|
||||
youtubedlFinished = true;
|
||||
|
||||
if (youtubedlFinished && invidiousFinished) {
|
||||
|
@ -272,6 +296,11 @@ function playVideo(videoId, playlistId = '') {
|
|||
}
|
||||
}
|
||||
|
||||
if (data.liveNow !== false) {
|
||||
playerView.validLive = true;
|
||||
playerView.videoLive = data.hlsUrl;
|
||||
}
|
||||
|
||||
if (playlistId != '') {
|
||||
playerView.playlistSeen = true;
|
||||
playerView.playlistShowList = true;
|
||||
|
@ -363,12 +392,12 @@ function openMiniPlayer() {
|
|||
let videoHtml;
|
||||
// Grabs whatever the HTML is for the current video player. Done this way to grab
|
||||
// the HTML5 player (with varying qualities) as well as the YouTube embeded player.
|
||||
if ($('.videoPlayer').length > 0) {
|
||||
$('.videoPlayer').get(0).pause();
|
||||
lastTime = $('.videoPlayer').get(0).currentTime;
|
||||
videoHtml = $('.videoPlayer').get(0).outerHTML;
|
||||
if (typeof(player) !== 'undefined') {
|
||||
player.pause();
|
||||
lastTime = player.currentTime;
|
||||
//videoHtml = $('.videoPlayer').get(0).outerHTML;
|
||||
} else {
|
||||
videoHtml = $('iframe').get(0).outerHTML;
|
||||
//videoHtml = $('iframe').get(0).outerHTML;
|
||||
}
|
||||
|
||||
// Create a new browser window.
|
||||
|
@ -377,11 +406,45 @@ function openMiniPlayer() {
|
|||
let miniPlayer = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 710,
|
||||
show: false,
|
||||
title: 'FreeTube Mini-Player: ' + playerView.videoTitle,
|
||||
autoHideMenuBar: true
|
||||
});
|
||||
|
||||
miniPlayer.loadURL(url.format({
|
||||
pathname: path.join(__dirname, '/templates/miniPlayer.html'),
|
||||
protocol: 'file:',
|
||||
slashes: true,
|
||||
}));
|
||||
|
||||
miniPlayer.once('ready-to-show', () => {
|
||||
miniPlayer.show();
|
||||
|
||||
let playerData = {
|
||||
video360p: playerView.video360p,
|
||||
valid360p: playerView.valid360p,
|
||||
video720p: playerView.video720p,
|
||||
valid720p: playerView.valid720p,
|
||||
videoAudio: playerView.videoAudio,
|
||||
validAudio: playerView.validAudio,
|
||||
videoDash: playerView.videoDash,
|
||||
validDash: playerView.validDash,
|
||||
videoLive: playerView.videoLive,
|
||||
validLive: playerView.validLive,
|
||||
subtitleHtml: playerView.subtitleHtml,
|
||||
videoThumbnail: playerView.videoThumbnail,
|
||||
defaultPlaybackRate: player.options.defaultSpeed,
|
||||
quality: player.options.defaultQuality,
|
||||
volume: player.volume,
|
||||
currentTime: player.currentTime,
|
||||
};
|
||||
miniPlayer.webContents.send('ping', playerData);
|
||||
})
|
||||
|
||||
return;
|
||||
|
||||
// Use the miniPlayer.html template.
|
||||
$.get('templates/miniPlayer.html', (template) => {
|
||||
/*$.get('templates/miniPlayer.html', (template) => {
|
||||
mustache.parse(template);
|
||||
const rendered = mustache.render(template, {
|
||||
videoUrl: playerView.videoUrl,
|
||||
|
@ -390,7 +453,7 @@ function openMiniPlayer() {
|
|||
});
|
||||
// Render the template to the new browser window.
|
||||
miniPlayer.loadURL("data:text/html;charset=utf-8," + encodeURI(rendered));
|
||||
});
|
||||
});*/
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -545,13 +608,17 @@ function checkVideoSettings() {
|
|||
else {
|
||||
quality = '360p';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (playerView.validLive) {
|
||||
quality = 'Live';
|
||||
}
|
||||
|
||||
let player = new MediaElementPlayer('player', {
|
||||
features: ['playpause', 'current', 'loop', 'tracks', 'progress', 'duration', 'volume', 'stop', 'speed', 'quality', 'fullscreen'],
|
||||
|
||||
speeds: ['2', '1.75', '1.5', '1.25', '1', '0.75', '0.5', '0.25'],
|
||||
defaultSpeed: defaultPlaybackRate,
|
||||
qualityText: 'Quality',
|
||||
|
@ -582,6 +649,7 @@ function checkVideoSettings() {
|
|||
}
|
||||
else {
|
||||
checked360p = true;
|
||||
declarePlayer();
|
||||
}
|
||||
|
||||
if (playerView.valid720p !== false) {
|
||||
|
@ -593,6 +661,7 @@ function checkVideoSettings() {
|
|||
}
|
||||
else {
|
||||
checked720p = true;
|
||||
declarePlayer();
|
||||
}
|
||||
|
||||
if (playerView.validAudio !== false) {
|
||||
|
@ -604,6 +673,7 @@ function checkVideoSettings() {
|
|||
}
|
||||
else {
|
||||
checkedAudio = true;
|
||||
declarePlayer();
|
||||
}
|
||||
|
||||
if (playerView.validDash !== false) {
|
||||
|
@ -615,6 +685,7 @@ function checkVideoSettings() {
|
|||
}
|
||||
else {
|
||||
checkedDash = true;
|
||||
declarePlayer();
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
|
@ -539,7 +539,6 @@ let playerView = new Vue({
|
|||
firstLoad: true,
|
||||
publishedDate: '',
|
||||
videoUrl: '',
|
||||
videoDash: '',
|
||||
videoId: '',
|
||||
channelId: '',
|
||||
channelIcon: '',
|
||||
|
@ -557,7 +556,10 @@ let playerView = new Vue({
|
|||
valid360p: false,
|
||||
video720p: '',
|
||||
valid720p: false,
|
||||
videoDash: '',
|
||||
validDash: true,
|
||||
videoLive: '',
|
||||
validLive: false,
|
||||
embededHtml: '',
|
||||
currentSpeed: 1,
|
||||
videoTitle: '',
|
||||
|
@ -606,8 +608,8 @@ let playerView = new Vue({
|
|||
}
|
||||
},
|
||||
embededPlayer: () => {
|
||||
playerView.playerSeen = false;
|
||||
playerView.currentQuality = 'EMBED';
|
||||
playerView.playerSeen = !playerView.playerSeen;
|
||||
checkedSettings = false;
|
||||
},
|
||||
copy: (site, videoId) => {
|
||||
const url = 'https://' + site + '/watch?v=' + videoId;
|
||||
|
|
|
@ -15,279 +15,38 @@
|
|||
along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<title>Freetube Mini-Player</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #424242;
|
||||
}
|
||||
|
||||
.videoPlayer {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 54.9vw;
|
||||
#player {
|
||||
min-height: 480px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
window.$ = window.jQuery = require('jquery');
|
||||
let startTime = '{{startTime}}';
|
||||
let mouseTimeout;
|
||||
|
||||
$('.videoPlayer').ready(() => {
|
||||
$('.videoPlayer').get(0).currentTime = startTime;
|
||||
$('.videoPlayer').get(0).play();
|
||||
});
|
||||
|
||||
/**
|
||||
* Handle keyboard shortcut commands.
|
||||
*/
|
||||
let videoShortcutHandler = function (event) {
|
||||
if (event.which == 68 && event.altKey === true) {
|
||||
$('#search').focus();
|
||||
}
|
||||
|
||||
if (event.which == 82 && event.shiftKey === false && event.ctrlKey === true && !$('#jumpToInput').is(':focus') && !$('#search').is(':focus')) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
forceSubscriptions();
|
||||
}
|
||||
|
||||
let videoPlayer = $('.videoPlayer').get(0);
|
||||
if (typeof (videoPlayer) !== 'undefined' && !$('#jumpToInput').is(':focus') && !$('#search').is(':focus')) {
|
||||
switch (event.which) {
|
||||
case 32:
|
||||
// Space Bar
|
||||
event.preventDefault();
|
||||
videoPlayer.paused ? videoPlayer.play() : videoPlayer.pause();
|
||||
break;
|
||||
case 74:
|
||||
// J Key
|
||||
event.preventDefault();
|
||||
changeDurationBySeconds(-10);
|
||||
break;
|
||||
case 75:
|
||||
// K Key
|
||||
event.preventDefault();
|
||||
videoPlayer.paused ? videoPlayer.play() : videoPlayer.pause();
|
||||
break;
|
||||
case 76:
|
||||
// L Key
|
||||
event.preventDefault();
|
||||
changeDurationBySeconds(10);
|
||||
break;
|
||||
case 79:
|
||||
// O Key
|
||||
event.preventDefault();
|
||||
if (videoPlayer.playbackRate > 0.25){
|
||||
let rate = videoPlayer.playbackRate - 0.25;
|
||||
videoPlayer.playbackRate = rate;
|
||||
$('#currentSpeed').html(rate);
|
||||
}
|
||||
break;
|
||||
case 80:
|
||||
// P Key
|
||||
event.preventDefault();
|
||||
if (videoPlayer.playbackRate < 2){
|
||||
let rate = videoPlayer.playbackRate + 0.25;
|
||||
videoPlayer.playbackRate = rate;
|
||||
$('#currentSpeed').html(rate);
|
||||
}
|
||||
break;
|
||||
case 70:
|
||||
// F Key
|
||||
event.preventDefault();
|
||||
|
||||
if (videoPlayer.webkitDisplayingFullscreen) {
|
||||
videoPlayer.webkitExitFullscreen
|
||||
}
|
||||
else{
|
||||
videoPlayer.webkitRequestFullscreen();
|
||||
}
|
||||
break;
|
||||
case 77:
|
||||
// M Key
|
||||
event.preventDefault();
|
||||
let volume = videoPlayer.volume;
|
||||
|
||||
if (volume > 0) {
|
||||
changeVolume(-1);
|
||||
} else {
|
||||
changeVolume(1);
|
||||
}
|
||||
break;
|
||||
case 67:
|
||||
// C Key
|
||||
let subtitleMode = $('.videoPlayer').get(0).textTracks[0].mode;
|
||||
if (subtitleMode === 'hidden') {
|
||||
$('.videoPlayer').get(0).textTracks[0].mode = 'showing'
|
||||
} else {
|
||||
$('.videoPlayer').get(0).textTracks[0].mode = 'hidden'
|
||||
}
|
||||
break;
|
||||
case 38:
|
||||
// Up Arrow Key
|
||||
event.preventDefault();
|
||||
changeVolume(0.05);
|
||||
break;
|
||||
case 40:
|
||||
// Down Arrow Key
|
||||
event.preventDefault();
|
||||
changeVolume(-0.05);
|
||||
break;
|
||||
case 37:
|
||||
// Left Arrow Key
|
||||
event.preventDefault();
|
||||
changeDurationBySeconds(-5);
|
||||
break;
|
||||
case 39:
|
||||
// Right Arrow Key
|
||||
event.preventDefault();
|
||||
changeDurationBySeconds(5);
|
||||
break;
|
||||
case 49:
|
||||
// 1 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.1);
|
||||
break;
|
||||
case 50:
|
||||
// 2 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.2);
|
||||
break;
|
||||
case 51:
|
||||
// 3 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.3);
|
||||
break;
|
||||
case 52:
|
||||
// 4 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.4);
|
||||
break;
|
||||
case 53:
|
||||
// 5 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.5);
|
||||
break;
|
||||
case 54:
|
||||
// 6 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.6);
|
||||
break;
|
||||
case 55:
|
||||
// 7 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.7);
|
||||
break;
|
||||
case 56:
|
||||
// 8 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.8);
|
||||
break;
|
||||
case 57:
|
||||
// 9 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.9);
|
||||
break;
|
||||
case 48:
|
||||
// 0 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let fullscreenVideo = function (event) {
|
||||
if (document.webkitFullscreenElement !== null) {
|
||||
document.webkitExitFullscreen();
|
||||
} else {
|
||||
$('.videoPlayer').get(0).webkitRequestFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the volume of the video player
|
||||
*
|
||||
* @param {double} amount - The volume to increase or descrease the volume by. Will be any double between 0 and 1.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function changeVolume(amount) {
|
||||
const videoPlayer = $('.videoPlayer').get(0);
|
||||
let volume = videoPlayer.volume;
|
||||
volume = volume + amount;
|
||||
if (volume > 1) {
|
||||
videoPlayer.volume = 1;
|
||||
} else if (volume < 0) {
|
||||
videoPlayer.volume = 0;
|
||||
} else {
|
||||
videoPlayer.volume = volume;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the duration of a video by a percentage of the duration.
|
||||
*
|
||||
* @param {double} percentage - The percentage to hop to of the video. Will be any double between 0 and 1.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function changeDurationByPercentage(percentage) {
|
||||
const videoPlayer = $('.videoPlayer').get(0);
|
||||
videoPlayer.currentTime = videoPlayer.duration * percentage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the duration of the current time of a video by a few seconds.
|
||||
*
|
||||
* @param {integer} seconds - The amount of seconds to change the video by. Integer may be positive or negative.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function changeDurationBySeconds(seconds) {
|
||||
const videoPlayer = $('.videoPlayer').get(0);
|
||||
videoPlayer.currentTime = videoPlayer.currentTime + seconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the mouse cursor after ~3 seconds. Used to hide the video when the user
|
||||
* hovers the mouse over the video player.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function hideMouseTimeout() {
|
||||
$('.videoPlayer')[0].style.cursor = 'default';
|
||||
clearTimeout(mouseTimeout);
|
||||
mouseTimeout = window.setTimeout(function() {
|
||||
$('.videoPlayer')[0].style.cursor = 'none';
|
||||
}, 3150);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the timeout for the mouse cursor as a fallback.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function removeMouseTimeout() {
|
||||
$('.videoPlayer')[0].style.cursor = 'default';
|
||||
clearTimeout(mouseTimeout);
|
||||
}
|
||||
|
||||
let playPauseVideo = function(event) {
|
||||
let el = event.currentTarget;
|
||||
el.paused ? el.play() : el.pause();
|
||||
}
|
||||
|
||||
$(document).on('click', '.videoPlayer', playPauseVideo);
|
||||
$(document).on('mouseover', '.videoPlayer', hideMouseTimeout);
|
||||
$(document).on('mouseleave', '.videoPlayer', removeMouseTimeout);
|
||||
$(document).on('dblclick', '.videoPlayer', fullscreenVideo);
|
||||
$(document).on('keydown', videoShortcutHandler);
|
||||
|
||||
</script>
|
||||
<video class="videoPlayer" id='videoPlayer' type="application/x-mpegURL" object-fit='cover' onmousemove="hideMouseTimeout()" onmouseleave="removeMouseTimeout()" controls="" src='{{videoUrl}}' poster='{{videoThumbnail}}' autoplay="autoplay">
|
||||
<link rel="stylesheet" href="../style/mediaelementplayer.css" />
|
||||
<link rel="stylesheet" href="../js/plugins/quality/quality.min.css" />
|
||||
<link rel="stylesheet" href="../js/plugins/loop/loop.css" />
|
||||
<link rel="stylesheet" href="../js/plugins/speed/speed.css" />
|
||||
<link rel="stylesheet" href="../js/plugins/context-menu/context-menu.css" />
|
||||
<video id='player' class='videoPlayer' onmousemove="hideMouseTimeout()" onmouseleave="removeMouseTimeout()" :poster="videoThumbnail" onvolumechange='updateVolume()'>
|
||||
<source v-if='valid360p' data-quality='360p' type="video/mp4" :src="video360p" />
|
||||
<source v-if='valid720p' data-quality='720p' type="video/mp4" :src="video720p" />
|
||||
<source v-if='validAudio' data-quality='Audio' type="audio/mp4" :src="videoAudio" />
|
||||
<source v-if='validDash' data-quality='Auto' type="application/dash+xml" :src="videoDash" />
|
||||
<source v-if='validLive' data-quality='Live' type="application/x-mpegURL" :src="videoLive" />
|
||||
<span v-html="subtitleHtml"></span>
|
||||
</video>
|
||||
<script>window.$ = window.jQuery = require('jquery');</script>
|
||||
<script src="../js/mediaelement-and-player.min.js"></script>
|
||||
<script src="../js/plugins/quality/quality.min.js"></script>
|
||||
<script src="../js/plugins/quality/quality-i18n.js"></script>
|
||||
<script src="../js/plugins/speed/speed.js"></script>
|
||||
<script src="../js/plugins/speed/speed-i18n.js"></script>
|
||||
<script src="../js/plugins/loop/loop.js"></script>
|
||||
<script src="../js/plugins/loop/loop-i18n.js"></script>
|
||||
<script src="../js/plugins/context-menu/context-menu.js"></script>
|
||||
<script src="../js/plugins/context-menu/context-menu-i18n.js"></script>
|
||||
<script src="../js/miniPlayer.js"></script>
|
||||
<script src="../js/events.js"></script>
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<source v-if='valid360p' data-quality='360p' type="video/mp4" :src="video360p" />
|
||||
<source v-if='valid720p' data-quality='720p' type="video/mp4" :src="video720p" />
|
||||
<source v-if='validAudio' data-quality='Audio' type="audio/mp4" :src="videoAudio" />
|
||||
<source data-quality='Auto' type="application/dash+xml" :src="videoDash" />
|
||||
<source v-if='validDash' data-quality='Auto' type="application/dash+xml" :src="videoDash" />
|
||||
<source v-if='validLive' data-quality='Live' type="application/x-mpegURL" :src="videoLive" />
|
||||
<span v-html="subtitleHtml"></span>
|
||||
</video>
|
||||
<!--<video class="videoPlayer" id='videoPlayer' type="application/x-mpegURL" object-fit='cover' onmousemove="hideMouseTimeout()" onmouseleave="removeMouseTimeout()" onloadstart='checkVideoSettings()' onvolumechange='updateVolume()' controls="" onended='playNextVideo()' :src='videoUrl' :poster="videoThumbnail" v-html="subtitleHtml">
|
||||
|
@ -17,6 +18,9 @@
|
|||
<div onclick='openMiniPlayer()' class='smallButton' >
|
||||
MINI PLAYER <i class="fas fa-external-link-alt"></i>
|
||||
</div>
|
||||
<div v-on:click='embededPlayer' class='smallButton' >
|
||||
TOGGLE EMBEDDED
|
||||
</div>
|
||||
<div class='smallButton' v-on:click='save(videoId)'>
|
||||
<i id='saveIcon' :class='savedIconType' class="fa-star"></i> <span id='savedText'>{{savedText}}</span>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue