mirror of https://github.com/FreeTubeApp/FreeTube
Track watch progress and continue video where you left off.
This commit is contained in:
parent
a89a651cd6
commit
3609684ce5
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
122
src/js/player.js
122
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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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, ",");
|
||||
|
|
|
@ -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%;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue