Track watch progress and continue video where you left off.

This commit is contained in:
PrestonN 2019-05-30 15:30:02 -04:00
parent a89a651cd6
commit 3609684ce5
9 changed files with 248 additions and 60 deletions

View File

@ -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;

View File

@ -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);
};

View File

@ -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);
};

View File

@ -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;
}

View File

@ -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, ",");

View File

@ -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%;

View File

@ -63,3 +63,5 @@ body {
<script src="../js/general.js"></script>
<script src="../js/miniPlayer.js"></script>
<script src="../js/events.js"></script>
<script src="../js/db.js"></script>
<script src="../js/history.js"></script>

View File

@ -324,10 +324,11 @@
<div id="ipInfoSettings" v-if="checkProxyResult">
<h4>Your Info:</h4>
<p>ip: {{checkProxyResult.ip}}</p>
<p>country: {{checkProxyResult.country}}</p>
<p>city: {{checkProxyResult.city || "N/A"}}</p>
<p>hostname: {{checkProxyResult.hostname || "N/A"}}</p>
<p>Ip: {{checkProxyResult.ip}}</p>
<p>Country: {{checkProxyResult.country}}</p>
<p>Region: {{checkProxyResult.region}}</p>
<p>City: {{checkProxyResult.city || "N/A"}}</p>
<p>Hostname: {{checkProxyResult.hostname || "N/A"}}</p>
</div>
<p>Clicking "TEST PROXY" button will send a request to https://ipinfo.io/json</p>

View File

@ -12,11 +12,11 @@
<a :href='video.youtubeUrl' onclick='showVideoOptions(this.parentNode.previousSibling);'>
<li>Open in YouTube</li>
</a>
<li v-on:click='copy("youtube.com", video.id)' onclick='showVideoOptions(this.parentNode.previousSibling);'>Copy YouTube Link</li>
<li v-on:click='copyYouTube(video.id)' onclick='showVideoOptions(this.parentNode.previousSibling);'>Copy YouTube Link</li>
<a :href='video.invidiousUrl' onclick='showVideoOptions(this.parentNode.previousSibling);'>
<li>Open in Invidious</li>
</a>
<li v-on:click='copy("invidio.us", video.id)' onclick='showVideoOptions(this.parentNode.previousSibling);'>Copy Invidious Link</li>
<li v-on:click='copyInvidious(video.id)' onclick='showVideoOptions(this.parentNode.previousSibling);'>Copy Invidious Link</li>
<li v-on:click='miniPlayer(video.id);' onclick='showVideoOptions(this.parentNode.previousSibling);'>Open in Mini Player</li>
</ul>
</div>
@ -27,6 +27,7 @@
<div v-if='video.watched' class='videoWatched'>
WATCHED
</div>
<div v-if='video.watched' class='watchedProgressBar' :style='{width: video.progressPercentage + "%"}'></div>
</div>
<p v-on:click='play(video.id)' class='videoTitle'>{{video.title}}</p>
<p v-on:click='channel(video.channelId)' class='channelName'>{{video.channelName}}</p>