From 3609684ce5d403c1cbe22a4ee08c8185bdfb1a47 Mon Sep 17 00:00:00 2001 From: PrestonN Date: Thu, 30 May 2019 15:30:02 -0400 Subject: [PATCH] Track watch progress and continue video where you left off. --- src/js/history.js | 23 ++++++ src/js/miniPlayer.js | 36 +++++++-- src/js/player.js | 122 ++++++++++++++++++++++++------- src/js/templates.js | 96 +++++++++++++++++++----- src/js/videos.js | 5 +- src/style/player.css | 10 ++- src/templates/miniPlayer.html | 2 + src/templates/settings.html | 9 ++- src/templates/videoTemplate.html | 5 +- 9 files changed, 248 insertions(+), 60 deletions(-) diff --git a/src/js/history.js b/src/js/history.js index 9ceb4c3fe..6d38550c4 100644 --- a/src/js/history.js +++ b/src/js/history.js @@ -46,6 +46,7 @@ function addToHistory(data){ videoThumbnails: data.videoThumbnails, type: 'video', timeWatched: data.timeWatched, + watchProgress: data.watchProgress, }, {}, (err, newDoc) => {}); } }); @@ -69,6 +70,27 @@ function removeFromHistory(videoId, toast = true){ }); } +/** +* Update the watch progress video from the history database file +* +* @param {string} videoId - The video ID of the video to be removed. +* +* @param {double} lengthSeconds - The amount of time the video has been watched in seconds. +* +* @return {Void} +*/ +function updateWatchProgress(videoId, lengthSeconds){ + historyDb.findOne({ videoId: videoId }, function (err, doc) { + if(doc !== null) { + historyDb.update( + { videoId: videoId }, + { + $set: { watchProgress: lengthSeconds } + }, {}, (err, newDoc) => {}); + } + }); +} + /** * Show the videos within the history database. * @@ -103,6 +125,7 @@ function showHistory(){ paid: false, type: 'video', timeWatched: video.timeWatched, + watchProgress: 0, }; addToHistory(videoData); videoData.position = index; diff --git a/src/js/miniPlayer.js b/src/js/miniPlayer.js index fd72ba07d..377566ea6 100644 --- a/src/js/miniPlayer.js +++ b/src/js/miniPlayer.js @@ -52,7 +52,21 @@ let miniPlayerView = new Vue({ autoplay: true, enableSubtitles: false, thumbnailInterval: 5, - } + }, + methods: { + legacyFormats: () => { + this.legacySeen = true; + this.playerSeen = false; + }, + dashFormats: () => { + this.legacySeen = false; + this.playerSeen = true; + }, + embedPlayer: () => { + this.legacySeen = false; + this.playerSeen = false; + } + }, }); /** @@ -170,7 +184,6 @@ function checkDashSettings() { ft.log(error); ft.log(originalNode); ft.log(instance); - showToast('There was an error with playing DASH formats. Reverting to the legacy formats.'); checkedSettings = false; miniPlayerView.currentTime = instance.currentTime; miniPlayerView.legacyFormats(); @@ -348,7 +361,7 @@ electron.ipcRenderer.on('ping', function(event, message) { }); electron.ipcRenderer.on('play360p', function(event, message) { - if (!miniPlayerView.valid360p) { + if (miniPlayerView.valid360p === false || miniPlayerView.legacySeen === false) { return; } @@ -366,7 +379,7 @@ electron.ipcRenderer.on('play360p', function(event, message) { }); electron.ipcRenderer.on('play720p', function(event, message) { - if (!miniPlayerView.valid720p) { + if (miniPlayerView.valid720p === false || miniPlayerView.legacySeen === false) { return; } @@ -384,7 +397,7 @@ electron.ipcRenderer.on('play720p', function(event, message) { }); electron.ipcRenderer.on('playAudio', function(event, message) { - if (!miniPlayerView.validAudio) { + if (miniPlayerView.validAudio === false || miniPlayerView.legacySeen === false) { return; } @@ -410,3 +423,16 @@ electron.ipcRenderer.on('videoLoop', function(event, message) { videoPlayer.loop = !videoPlayer.loop; }); + +window.onbeforeunload = (e) => { + let lengthSeconds = 0; + + if (miniPlayerView.legacySeen === false) { + lengthSeconds = player.currentTime; + } + else { + lengthSeconds = $('.videoPlayer').get(0).currentTime; + } + + updateWatchProgress(miniPlayerView.videoId, lengthSeconds); +}; diff --git a/src/js/player.js b/src/js/player.js index d302d1f14..c2169c4e4 100644 --- a/src/js/player.js +++ b/src/js/player.js @@ -401,25 +401,34 @@ function playVideo(videoId, playlistId = '') { } if (rememberHistory === true) { - let historyData = { - videoId: videoId, - published: data.published, - publishedText: playerView.publishedDate, - description: data.description, - viewCount: data.viewCount, - title: playerView.videoTitle, - lengthSeconds: data.lengthSeconds, - videoThumbnails: playerView.videoThumbnail, - author: playerView.channelName, - authorId: playerView.channelId, - liveNow: false, - paid: false, - type: 'video', - timeWatched: new Date().getTime(), - }; + historyDb.findOne({ videoId: playerView.videoId }, function (err, doc) { + let watchProgress = 0; - ft.log(historyData); - addToHistory(historyData); + if(doc !== null) { + watchProgress = doc.watchProgress; + } + + let historyData = { + videoId: videoId, + published: data.published, + publishedText: playerView.publishedDate, + description: data.description, + viewCount: data.viewCount, + title: playerView.videoTitle, + lengthSeconds: data.lengthSeconds, + videoThumbnails: playerView.videoThumbnail, + author: playerView.channelName, + authorId: playerView.channelId, + liveNow: false, + paid: false, + type: 'video', + timeWatched: new Date().getTime(), + watchProgress: watchProgress, + }; + + ft.log(historyData); + addToHistory(historyData); + }); } }); } @@ -457,7 +466,31 @@ function openMiniPlayer() { autoHideMenuBar: true }); - const template = [{ + const template = [ + { + label: 'Player', + submenu: [ + { + label: 'Dash Player', + click() { + miniPlayer.webContents.send('dashPlayer', ''); + }, + }, + { + label: 'Legacy Player', + click() { + miniPlayer.webContents.send('legacyPlayer', ''); + }, + }, + { + label: 'YouTube Player', + click() { + miniPlayer.webContents.send('youtubePlayer', ''); + }, + }, + ], + }, + { label: 'Quality', submenu: [{ label: '360p', @@ -579,9 +612,7 @@ function openMiniPlayer() { const menu = electron.remote.Menu.buildFromTemplate(template); - if(playerView.legacySeen) { - miniPlayer.setMenu(menu); - } + miniPlayer.setMenu(menu); miniPlayer.loadURL(url.format({ pathname: path.join(__dirname, '/templates/miniPlayer.html'), @@ -1051,10 +1082,17 @@ function checkDashSettings() { return; } - if (typeof (playerView.currentTime) !== 'undefined') { - instance.currentTime = playerView.currentTime; - playerView.currentTime = undefined; - } + historyDb.findOne({ videoId: playerView.videoId }, function (err, doc) { + if(doc !== null) { + if (typeof (playerView.currentTime) !== 'undefined') { + instance.currentTime = playerView.currentTime; + playerView.currentTime = undefined; + } + else if (doc.watchProgress < instance.duration - 5) { + instance.currentTime = doc.watchProgress; + } + } + }); let selectedOption = false; qualityOptions.reverse().forEach((option, index) => { @@ -1105,7 +1143,7 @@ function checkDashSettings() { } function checkLegacySettings() { - let player = document.getElementById('legacyPlayer'); + let player = $('.videoPlayer').get(0); let checked720p = false; let checked360p = false; @@ -1118,13 +1156,24 @@ function checkLegacySettings() { if (typeof (playerView.currentTime) !== 'undefined') { player.currentTime = playerView.currentTime; - playerView.currentTime = undefined; } if (autoplay) { player.play(); } + window.setTimeout(() => { + historyDb.findOne({ videoId: playerView.videoId }, function (err, doc) { + if(doc !== null) { + if (doc.watchProgress < player.duration - 5 && typeof (playerView.currentTime) === 'undefined') { + player.currentTime = doc.watchProgress; + } + + playerView.currentTime = undefined; + } + }); + }, 400); + if (enableSubtitles) { player.textTracks[0].mode = 'showing' } @@ -1320,3 +1369,20 @@ function parseDescription(descriptionText) { return descriptionText; } + +window.onbeforeunload = (e) => { + if (playerView.seen === false) { + return; + } + + let lengthSeconds = 0; + + if (playerView.legacySeen === false) { + lengthSeconds = player.currentTime; + } + else { + lengthSeconds = $('.videoPlayer').get(0).currentTime; + } + + updateWatchProgress(playerView.videoId, lengthSeconds); +}; diff --git a/src/js/templates.js b/src/js/templates.js index f9aebc773..54eceb471 100644 --- a/src/js/templates.js +++ b/src/js/templates.js @@ -174,8 +174,13 @@ let subscriptionView = new Vue({ toggleSave: (videoId) => { addSavedVideo(videoId); }, - copy: (site, videoId) => { - const url = 'https://' + site + '/watch?v=' + videoId; + copyYouTube: (videoId) => { + const url = 'https://youtube.com/watch?v=' + videoId; + clipboard.writeText(url); + showToast('URL has been copied to the clipboard'); + }, + copyInvidious: (videoId) => { + const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, @@ -208,8 +213,13 @@ let popularView = new Vue({ toggleSave: (videoId) => { addSavedVideo(videoId); }, - copy: (site, videoId) => { - const url = 'https://' + site + '/watch?v=' + videoId; + copyYouTube: (videoId) => { + const url = 'https://youtube.com/watch?v=' + videoId; + clipboard.writeText(url); + showToast('URL has been copied to the clipboard'); + }, + copyInvidious: (videoId) => { + const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, @@ -241,8 +251,13 @@ let trendingView = new Vue({ toggleSave: (videoId) => { addSavedVideo(videoId); }, - copy: (site, videoId) => { - const url = 'https://' + site + '/watch?v=' + videoId; + copyYouTube: (videoId) => { + const url = 'https://youtube.com/watch?v=' + videoId; + clipboard.writeText(url); + showToast('URL has been copied to the clipboard'); + }, + copyInvidious: (videoId) => { + const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, @@ -274,8 +289,13 @@ let savedView = new Vue({ toggleSave: (videoId) => { toggleSavedVideo(videoId); }, - copy: (site, videoId) => { - const url = 'https://' + site + '/watch?v=' + videoId; + copyYouTube: (videoId) => { + const url = 'https://youtube.com/watch?v=' + videoId; + clipboard.writeText(url); + showToast('URL has been copied to the clipboard'); + }, + copyInvidious: (videoId) => { + const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, @@ -307,8 +327,13 @@ let historyView = new Vue({ toggleSave: (videoId) => { addSavedVideo(videoId); }, - copy: (site, videoId) => { - const url = 'https://' + site + '/watch?v=' + videoId; + copyYouTube: (videoId) => { + const url = 'https://youtube.com/watch?v=' + videoId; + clipboard.writeText(url); + showToast('URL has been copied to the clipboard'); + }, + copyInvidious: (videoId) => { + const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, @@ -352,8 +377,13 @@ let playlistView = new Vue({ toggleSave: (videoId) => { addSavedVideo(videoId); }, - copy: (site, videoId) => { - const url = 'https://' + site + '/watch?v=' + videoId; + copyYouTube: (videoId) => { + const url = 'https://youtube.com/watch?v=' + videoId; + clipboard.writeText(url); + showToast('URL has been copied to the clipboard'); + }, + copyInvidious: (videoId) => { + const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, @@ -450,8 +480,13 @@ let searchView = new Vue({ toggleSave: (videoId) => { addSavedVideo(videoId); }, - copy: (site, videoId) => { - const url = 'https://' + site + '/watch?v=' + videoId; + copyYouTube: (videoId) => { + const url = 'https://youtube.com/watch?v=' + videoId; + clipboard.writeText(url); + showToast('URL has been copied to the clipboard'); + }, + copyInvidious: (videoId) => { + const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, @@ -519,8 +554,13 @@ let channelVideosView = new Vue({ nextPage: () => { channelNextPage(); }, - copy: (site, videoId) => { - const url = 'https://' + site + '/watch?v=' + videoId; + copyYouTube: (videoId) => { + const url = 'https://youtube.com/watch?v=' + videoId; + clipboard.writeText(url); + showToast('URL has been copied to the clipboard'); + }, + copyInvidious: (videoId) => { + const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, @@ -752,7 +792,28 @@ let backButtonView = new Vue({ }, }); -function hideViews(){ +function hideViews() { + if (playerView.seen !== false) { + let lengthSeconds = 0; + let duration = 0; + + if (playerView.legacySeen === false) { + lengthSeconds = player.currentTime; + duration = player.duration; + } + else { + lengthSeconds = $('.videoPlayer').get(0).currentTime; + duration = $('.videoPlayer').get(0).duration; + } + + updateWatchProgress(playerView.videoId, lengthSeconds); + + let videoIndex = subscriptionView.videoList.findIndex(x => x.id === playerView.videoId); + + subscriptionView.videoList[videoIndex].watched = true; + subscriptionView.videoList[videoIndex].progressPercentage = (lengthSeconds / duration) * 100; + } + subscriptionView.seen = false; noSubscriptions.seen = false; aboutView.seen = false; @@ -767,6 +828,5 @@ function hideViews(){ playerView.seen = false; channelView.seen = false; channelVideosView.seen = false; - backButtonView.lastView = false; } diff --git a/src/js/videos.js b/src/js/videos.js index 148cd849e..8459b68f5 100644 --- a/src/js/videos.js +++ b/src/js/videos.js @@ -108,13 +108,14 @@ function displayVideo(videoData, listType = '') { video.isPlaylist = true; } - historyDb.find({ + historyDb.findOne({ videoId: video.id }, (err, docs) => { if (jQuery.isEmptyObject(docs)) { - // Do nothing + video.progressPercentage = 0; } else { video.watched = true; + video.progressPercentage = (docs.watchProgress / videoData.lengthSeconds) * 100; } video.views = videoData.viewCount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); diff --git a/src/style/player.css b/src/style/player.css index ac30bf49b..58dcc5188 100644 --- a/src/style/player.css +++ b/src/style/player.css @@ -148,7 +148,7 @@ iframe { .videoDuration { float: right; position: relative; - bottom: 36px; + bottom: 40px; color: white; background-color: black; opacity: 0.7; @@ -168,6 +168,14 @@ iframe { pointer-events: none; } +.watchedProgressBar { + background-color: #f44336; + height: 3px; + position: relative; + bottom: 190px; + opacity: 0.8; +} + .videoPlaylist { float: right; width: 45%; diff --git a/src/templates/miniPlayer.html b/src/templates/miniPlayer.html index 69ab90cfc..2676ff57d 100644 --- a/src/templates/miniPlayer.html +++ b/src/templates/miniPlayer.html @@ -63,3 +63,5 @@ body { + + diff --git a/src/templates/settings.html b/src/templates/settings.html index f3c03d1bb..e5ac98cfd 100644 --- a/src/templates/settings.html +++ b/src/templates/settings.html @@ -324,10 +324,11 @@

Your Info:

-

ip: {{checkProxyResult.ip}}

-

country: {{checkProxyResult.country}}

-

city: {{checkProxyResult.city || "N/A"}}

-

hostname: {{checkProxyResult.hostname || "N/A"}}

+

Ip: {{checkProxyResult.ip}}

+

Country: {{checkProxyResult.country}}

+

Region: {{checkProxyResult.region}}

+

City: {{checkProxyResult.city || "N/A"}}

+

Hostname: {{checkProxyResult.hostname || "N/A"}}

Clicking "TEST PROXY" button will send a request to https://ipinfo.io/json

diff --git a/src/templates/videoTemplate.html b/src/templates/videoTemplate.html index db7565752..aa72f8016 100644 --- a/src/templates/videoTemplate.html +++ b/src/templates/videoTemplate.html @@ -12,11 +12,11 @@
  • Open in YouTube
  • -
  • Copy YouTube Link
  • +
  • Copy YouTube Link
  • Open in Invidious
  • -
  • Copy Invidious Link
  • +
  • Copy Invidious Link
  • Open in Mini Player
  • @@ -27,6 +27,7 @@
    WATCHED
    +

    {{video.title}}

    {{video.channelName}}