FreeTube/src/js/templates.js

1801 lines
63 KiB
JavaScript

/*
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/>.
*/
const mainHeaderTemplate = require('./templates/mainHeader.html');
const aboutTemplate = require('./templates/about.html');
const settingsTemplate = require('./templates/settings.html');
const videoListTemplate = require('./templates/videoTemplate.html');
const playerTemplate = require('./templates/player.html');
const channelTemplate = require('./templates/channelView.html');
const progressViewTemplate = require('./templates/progressView.html');
const playlistViewTemplate = require('./templates/playlistView.html');
const currentProfileViewTemplate = require('./templates/currentProfileView.html');
const profileSelectViewTemplate = require('./templates/profileSelectView.html');
const subscriptionManagerViewTemplate = require('./templates/subscriptionManagerView.html');
const editProfileViewTemplate = require('./templates/editProfileView.html');
const searchSuggestionsViewTemplate = require('./templates/searchSuggestionsView.html');
/*
* Progress view
*
* Shows progress bar on bottom of application.
*
* seen: Toggles visibility of view
* progressWidth: sets width of the progress bar
*/
let progressView = new Vue({
el: '#progressView',
data: {
seen: true,
progressWidth: 0
},
template: progressViewTemplate
});
let loadingView = new Vue({
el: '#loading',
data: {
seen: false
}
});
let searchFilter = new Vue({
el: '#searchFilter',
data: {
seen: false
}
});
let noSubscriptions = new Vue({
el: '#noSubscriptions',
data: {
seen: false
}
});
let sideNavBar = new Vue({
el: '#sideNav',
data: {
distractionFreeMode: false
},
methods: {
subscriptions: (event) => {
hideViews();
if (subscriptionView.videoList.length === 0) {
loadingView.seen = true;
}
headerView.seen = true;
headerView.title = 'Latest Subscriptions';
subscriptionView.seen = true;
loadSubscriptions();
},
popular: (event) => {
hideViews();
if (loadingView.seen !== false) {
loadingView.seen = false;
}
if (popularView.videoList.length === 0) {
loadingView.seen = true;
}
headerView.seen = true;
headerView.title = 'Most Popular';
popularView.seen = true;
showMostPopular();
},
trending: (event) => {
hideViews();
if (loadingView.seen !== false) {
loadingView.seen = false;
}
if (trendingView.videoList.length === 0) {
loadingView.seen = true;
}
headerView.seen = true;
headerView.title = 'Trending';
trendingView.seen = true;
showTrending();
},
saved: (event) => {
hideViews();
if (loadingView.seen !== false) {
loadingView.seen = false;
} else {
loadingView.seen = true;
}
headerView.seen = true;
headerView.title = 'Favorited Videos';
savedView.seen = true;
showSavedVideos();
},
history: (event) => {
hideViews();
if (loadingView.seen !== false) {
loadingView.seen = false;
} else {
loadingView.seen = true;
}
headerView.seen = true;
headerView.title = 'Video History';
historyView.seen = true;
showHistory();
},
settings: (event) => {
hideViews();
if (loadingView.seen !== false) {
loadingView.seen = false;
}
settingsView.seen = true;
updateSettingsView();
},
about: (event) => {
hideViews();
if (loadingView.seen !== false) {
loadingView.seen = false;
}
aboutView.seen = true;
}
}
});
let headerView = new Vue({
el: '#mainHeaderView',
data: {
seen: true,
title: 'Latest Subscriptions'
},
template: mainHeaderTemplate
});
let searchSuggestionsView = new Vue({
el: '#searchSuggestionsView',
data: {
seen: false,
suggestionList: [],
},
methods: {
newSearchTerm: (text) => {
loadingView.seen = true;
document.getElementById('search').value = text;
getSearchSuggestion();
search();
searchSuggestionsView.seen = false;
},
},
template: searchSuggestionsViewTemplate
});
let subscriptionView = new Vue({
el: '#subscriptionView',
data: {
seen: true,
isSearch: false,
videoList: [],
fullVideoList: [],
},
methods: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
},
channel: (channelId) => {
goToChannel(channelId);
},
toggleSave: (videoId) => {
addSavedVideo(videoId);
},
copyYouTube: (videoId) => {
const url = 'https://youtube.com/watch?v=' + videoId;
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
copyYouTubeNoCookie: (videoId) => {
const url = 'https://www.youtube-nocookie.com/embed/' + 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');
},
history: (videoId) => {
removeFromHistory(videoId);
},
miniPlayer: (videoId) => {
ft.log(videoId);
clickMiniPlayer(videoId);
}
},
template: videoListTemplate
});
let popularView = new Vue({
el: '#popularView',
data: {
seen: false,
isSearch: false,
videoList: []
},
methods: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
},
channel: (channelId) => {
goToChannel(channelId);
},
toggleSave: (videoId) => {
addSavedVideo(videoId);
},
copyYouTube: (videoId) => {
const url = 'https://youtube.com/watch?v=' + videoId;
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
copyYouTubeNoCookie: (videoId) => {
const url = 'https://www.youtube-nocookie.com/embed/' + 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');
},
history: (videoId) => {
removeFromHistory(videoId);
},
miniPlayer: (videoId) => {
clickMiniPlayer(videoId);
}
},
template: videoListTemplate
});
let trendingView = new Vue({
el: '#trendingView',
data: {
seen: false,
isSearch: false,
videoList: []
},
methods: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
},
channel: (channelId) => {
goToChannel(channelId);
},
toggleSave: (videoId) => {
addSavedVideo(videoId);
},
copyYouTube: (videoId) => {
const url = 'https://youtube.com/watch?v=' + videoId;
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
copyYouTubeNoCookie: (videoId) => {
const url = 'https://www.youtube-nocookie.com/embed/' + 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');
},
history: (videoId) => {
removeFromHistory(videoId);
},
miniPlayer: (videoId) => {
clickMiniPlayer(videoId);
}
},
template: videoListTemplate
});
let savedView = new Vue({
el: '#savedView',
data: {
seen: false,
isSearch: false,
videoList: []
},
methods: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
},
channel: (channelId) => {
goToChannel(channelId);
},
toggleSave: (videoId) => {
toggleSavedVideo(videoId);
},
copyYouTube: (videoId) => {
const url = 'https://youtube.com/watch?v=' + videoId;
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
copyYouTubeNoCookie: (videoId) => {
const url = 'https://www.youtube-nocookie.com/embed/' + 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');
},
history: (videoId) => {
removeFromHistory(videoId);
},
miniPlayer: (videoId) => {
clickMiniPlayer(videoId);
}
},
template: videoListTemplate
});
let historyView = new Vue({
el: '#historyView',
data: {
seen: false,
isSearch: false,
videoList: []
},
methods: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
},
channel: (channelId) => {
goToChannel(channelId);
},
toggleSave: (videoId) => {
addSavedVideo(videoId);
},
copyYouTube: (videoId) => {
const url = 'https://youtube.com/watch?v=' + videoId;
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
copyYouTubeNoCookie: (videoId) => {
const url = 'https://www.youtube-nocookie.com/embed/' + 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');
},
history: (videoId) => {
removeFromHistory(videoId);
},
miniPlayer: (videoId) => {
clickMiniPlayer(videoId);
}
},
template: videoListTemplate
});
let playlistView = new Vue({
el: '#playlistView',
data: {
seen: false,
playlistId: '',
channelName: '',
channelId: '',
thumbnail: '',
title: '',
videoCount: '',
viewCount: '',
description: '',
lastUpdated: '',
videoList: []
},
methods: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId, playlistView.playlistId);
backButtonView.lastView = playlistView
},
channel: (channelId) => {
goToChannel(channelId);
backButtonView.lastView = playlistView
},
toggleSave: (videoId) => {
addSavedVideo(videoId);
},
openYouTube: (videoId) => {
const url = 'https://youtube.com/watch?v=' + videoId;
shell.openExternal(url);
},
copyYouTube: (videoId) => {
const url = 'https://youtube.com/watch?v=' + videoId;
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
openYouTubeNoCookie: (videoId) => {
const url = 'https://www.youtube-nocookie.com/embed/' + videoId;
shell.openExternal(url);
},
copyYouTubeNoCookie: (videoId) => {
const url = 'https://www.youtube-nocookie.com/embed/' + videoId;
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
openInvidious: (videoId) => {
const url = invidiousInstance + '/watch?v=' + videoId;
shell.openExternal(url);
},
copyInvidious: (videoId) => {
const url = invidiousInstance + '/watch?v=' + videoId;
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
copyYouTubePlaylist: (playlistId) => {
const url = `https://www.youtube.com/playlist?list=${playlistId}`
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
copyInvidiousPlaylist: (playlistId) => {
const url = `${invidiousInstance}/playlist?list=${playlistId}`
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
openYouTubePlaylist: (playlistId) => {
const url = `https://www.youtube.com/playlist?list=${playlistId}`
shell.openExternal(url);
},
openInvidiousPlaylist: (playlistId) => {
const url = `${invidiousInstance}/playlist?list=${playlistId}`
shell.openExternal(url);
},
history: (videoId) => {
removeFromHistory(videoId);
}
},
template: playlistViewTemplate
});
let aboutView = new Vue({
el: '#aboutView',
data: {
seen: false,
rssFeed: [],
versionNumber: electron.remote.app.getVersion()
},
template: aboutTemplate
});
let settingsView = new Vue({
el: '#settingsView',
data: {
seen: false,
useTheme: false,
useTor: false,
apiKey: '',
history: true,
autoplay: true,
autoplayPlaylists: true,
playNextVideo: false,
subtitles: false,
updates: true,
localScrape: true,
region: 'US',
proxyAddress: false,
invidiousInstance: 'https://invidio.us',
checkProxyResult: false,
proxyTestLoading: false,
hideWatchedSubs: false,
debugMode: false,
distractionFreeMode: false,
defaultVolume: 1,
defaultVideoSpeed: 1,
subWatched: false,
videoView: 'grid',
defaultProfile: 'Default',
proxyVideos: false,
},
methods: {
checkProxy() {
this.checkProxyResult = false;
this.proxyTestLoading = true;
electron.ipcRenderer.send("setProxy", this.proxyAddress);
proxyRequest(() => {
$.ajax({
url: "https://ipinfo.io/json",
dataType: 'json',
}).done(response => {
ft.log(response);
this.checkProxyResult = response;
})
.fail((xhr, textStatus, error) => {
ft.log(xhr);
ft.log(textStatus);
showToast('Proxy test failed');
}).always(() => {
this.proxyTestLoading = false;
if (!useTor) {
electron.ipcRenderer.send("setProxy", {});
}
});
})
},
setDistractionFreeMode(setting) {
settingsView.distractionFreeMode = setting;
sideNavBar.distractionFreeMode = setting;
channelView.distractionFreeMode = setting;
playerView.distractionFreeMode = setting;
},
},
computed: {
proxyTestButtonText() {
return this.proxyTestLoading ? "LOADING..." : "TEST PROXY"
},
volumeHtml() {
return Math.round(this.defaultVolume * 100);
}
},
template: settingsTemplate
});
let searchView = new Vue({
el: '#searchView',
data: {
seen: false,
isSearch: true,
page: 1,
videoList: []
},
methods: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
backButtonView.lastView = searchView
},
channel: (channelId) => {
goToChannel(channelId);
backButtonView.lastView = searchView
},
toggleSave: (videoId) => {
addSavedVideo(videoId);
},
copyYouTube: (videoId) => {
const url = 'https://youtube.com/watch?v=' + videoId;
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
copyYouTubeNoCookie: (videoId) => {
const url = 'https://www.youtube-nocookie.com/embed/' + 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');
},
nextPage: () => {
ft.log(searchView.page);
search(searchView.page);
},
playlist: (playlistId) => {
showPlaylist(playlistId);
backButtonView.lastView = searchView
},
miniPlayer: (videoId) => {
clickMiniPlayer(videoId);
}
},
template: videoListTemplate
});
let channelView = new Vue({
el: '#channelView',
data: {
seen: false,
aboutTabSeen: false,
id: '',
name: '',
icon: '',
banner: '',
subCount: '',
subButtonText: '',
description: '',
channelSearchValue: '',
distractionFreeMode: false,
featuredChannels: [],
},
methods: {
videoTab: () => {
channelVideosView.seen = true;
channelView.aboutTabSeen = false;
channelPlaylistsView.seen = false;
channelSearchView.seen = false;
},
playlistTab: () => {
channelPlaylistsView.seen = true;
channelVideosView.seen = false;
channelView.aboutTabSeen = false;
channelSearchView.seen = false;
},
aboutTab: () => {
channelView.aboutTabSeen = true;
channelVideosView.seen = false;
channelPlaylistsView.seen = false;
channelSearchView.seen = false;
},
subscription: (channelId) => {
let channelData = {
channelId: channelView.id,
channelName: channelView.name,
channelThumbnail: channelView.icon
};
toggleSubscription(channelData);
},
sort: () => {
if (channelVideosView.seen) {
channelVideosView.page = 1;
channelVideosView.videoList = [];
channelNextPage();
} else {
// Playlist View is active
channelPlaylistsView.continuationString = '';
channelPlaylistsView.videoList = [];
channelPlaylistNextPage();
}
},
search: () => {
channelSearchView.page = 1;
channelSearchView.videoList = [];
channelView.aboutTabSeen = false;
channelVideosView.seen = false;
channelPlaylistsView.seen = false;
channelSearchView.seen = true;
searchChannel();
},
goToChannel: (channelId) => {
goToChannel(channelId);
},
},
computed: {
sortOptions: () => {
if (channelVideosView.seen) {
return [{
value: 'newest',
label: 'Newest'
},
{
value: 'oldest',
label: 'Oldest'
},
{
value: 'popular',
label: 'Most Popular'
},
];
} else {
return [{
value: 'newest',
label: 'Newest'
},
{
value: 'oldest',
label: 'Oldest'
},
{
value: 'last',
label: 'Last Video Added'
},
];
}
},
},
template: channelTemplate
});
let channelVideosView = new Vue({
el: '#channelVideosView',
data: {
seen: false,
isSearch: true,
page: 2,
videoList: []
},
methods: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
},
channel: (channelId) => {
goToChannel(channelId);
},
toggleSave: (videoId) => {
addSavedVideo(videoId);
},
nextPage: () => {
channelNextPage();
},
copyYouTube: (videoId) => {
const url = 'https://youtube.com/watch?v=' + videoId;
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
copyYouTubeNoCookie: (videoId) => {
const url = 'https://www.youtube-nocookie.com/embed/' + 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');
},
history: (videoId) => {
removeFromHistory(videoId);
},
miniPlayer: (videoId) => {
clickMiniPlayer(videoId);
}
},
template: videoListTemplate
});
let channelPlaylistsView = new Vue({
el: '#channelPlaylistsView',
data: {
seen: false,
isSearch: true,
page: 2,
continuationString: '',
videoList: []
},
methods: {
playlist: (playlistId) => {
showPlaylist(playlistId);
},
channel: (channelId) => {
goToChannel(channelId);
},
toggleSave: (videoId) => {
addSavedVideo(videoId);
},
nextPage: () => {
channelPlaylistNextPage();
},
},
template: videoListTemplate
});
let channelSearchView = new Vue({
el: '#channelSearchView',
data: {
seen: false,
channelId: '',
isSearch: true,
page: 2,
videoList: []
},
methods: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
},
channel: (channelId) => {
goToChannel(channelId);
},
playlist: (playlistId) => {
showPlaylist(playlistId);
},
toggleSave: (videoId) => {
addSavedVideo(videoId);
},
nextPage: () => {
showToast('Fetching results. Please wait…');
searchChannel();
},
copyYouTube: (videoId) => {
const url = 'https://youtube.com/watch?v=' + videoId;
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
copyYouTubeNoCookie: (videoId) => {
const url = 'https://www.youtube-nocookie.com/embed/' + 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');
},
history: (videoId) => {
removeFromHistory(videoId);
},
miniPlayer: (videoId) => {
clickMiniPlayer(videoId);
}
},
template: videoListTemplate
});
let playerView = new Vue({
el: '#playerView',
data: {
seen: false,
playlistSeen: false,
legacySeen: false,
firstLoad: true,
currentTime: undefined,
publishedDate: '',
videoUrl: '',
videoId: '',
channelId: '',
channelIcon: '',
channelName: '',
subscribedText: '',
subscriptionCount: '',
savedText: '',
savedIconType: 'far',
description: '',
videoThumbnail: '',
subtitleHtml: '',
currentQuality: '',
videoAudio: '',
validAudio: false,
video360p: '',
valid360p: false,
video720p: '',
valid720p: false,
videoDash: '',
validDash: true,
videoLive: '',
validLive: false,
embededHtml: '',
currentSpeed: 1,
lengthSeconds: 0,
videoTitle: '',
videoViews: '',
likePercentage: 0,
videoLikes: 0,
videoDislikes: 0,
playerSeen: true,
playlistTitle: '',
playlistChannelName: '',
playlistIndex: 1,
playlistTotal: 1,
playlistLoop: false,
playlistShuffle: false,
playlistShowList: true,
recommendedVideoList: [],
playlistVideoList: [],
distractionFreeMode: false,
includeCurrentTime: false
},
methods: {
channel: (channelId) => {
goToChannel(channelId);
},
subscription: () => {
let channelData = {
channelId: playerView.channelId,
channelName: playerView.channelName,
channelThumbnail: playerView.channelIcon
};
toggleSubscription(channelData);
},
quality: (url, qualityText) => {
ft.log(url);
ft.log(qualityText);
if (playerView.legacySeen === true) {
// Update time to new url
const currentPlayBackTime = $('.videoPlayer').get(0).currentTime;
ft.log(currentPlayBackTime);
playerView.videoUrl = url;
playerView.currentQuality = qualityText;
setTimeout(() => {
$('.videoPlayer').get(0).currentTime = currentPlayBackTime;
$('.videoPlayer').get(0).play();
}, 100);
}
},
embededPlayer: () => {
playerView.playerSeen = false;
playerView.legacySeen = false;
playerView.currentTime = undefined;
checkedVideoSettings = false;
},
legacyFormats: () => {
if (typeof (player) !== 'undefined') {
playerView.currentTime = player.currentTime;
}
checkedVideoSettings = false;
playerView.playerSeen = false;
playerView.legacySeen = true;
},
dashFormats: () => {
if (typeof ($('#legacyPlayer').get(0)) !== 'undefined') {
playerView.currentTime = $('#legacyPlayer').get(0).currentTime;
}
checkedVideoSettings = false;
playerView.legacySeen = false;
playerView.playerSeen = true;
},
toggleSave: (videoId) => {
addSavedVideo(videoId);
},
copyYouTube: (videoId) => {
const url = 'https://youtube.com/watch?v=' + videoId + currentTimeParameter();
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
openYouTube: (videoId) => {
shell.openExternal('https://youtube.com/watch?v=' + videoId + currentTimeParameter());
},
openYouTubeNoCookie: (videoId) => {
const url = 'https://www.youtube-nocookie.com/embed/' + videoId;
shell.openExternal(url);
},
copyYouTubeNoCookie: (videoId) => {
const url = 'https://www.youtube-nocookie.com/embed/' + videoId;
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
copyInvidious: (videoId) => {
const url = invidiousInstance + '/watch?v=' + videoId + currentTimeParameter();
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
openInvidious: (videoId) => {
shell.openExternal(invidiousInstance + '/watch?v=' + videoId + currentTimeParameter());
},
save: (videoId) => {
toggleSavedVideo(videoId);
},
play: (videoId, playlistId = '') => {
loadingView.seen = true;
playVideo(videoId, playlistId);
},
loop: () => {
let legacyPlayer = $('.videoPlayer').get(0);
if (legacyPlayer.loop === false) {
legacyPlayer.loop = true;
showToast('Video loop has been turned on.');
} else {
legacyPlayer.loop = false;
showToast('Video loop has been turned off.')
}
},
playlist: (playlistId) => {
showPlaylist(playlistId);
},
playlistLoopToggle: () => {
if (playerView.playlistLoop !== false) {
showToast('Playlist will no longer loop');
playerView.playlistLoop = false;
} else {
showToast('Playlist will now loop');
playerView.playlistLoop = true;
}
},
playlistShuffleToggle: () => {
if (playerView.playlistShuffle !== false) {
showToast('Playlist will no longer shuffle');
playerView.playlistShuffle = false;
} else {
showToast('Playlist will now shuffle');
playerView.playlistShuffle = true;
}
},
},
computed: {
thumbnailInterval: function () {
if (this.lengthSeconds < 120) {
return 1;
} else if (this.lengthSeconds < 300) {
return 2;
} else if (this.lengthSeconds < 900) {
return 5;
} else {
return 10;
}
},
storyBoardUrl: function () {
return invidiousInstance + '/api/v1/storyboards/' + this.videoId + '?height=90';
}
},
template: playerTemplate
});
let backButtonView = new Vue({
el: '#backButton',
data: {
lastView: false
},
methods: {
back: function () {
// variable here because this.lastView gets reset in hideViews()
const isSearch = this.lastView.$options.el === "#searchView";
const isSubManager = this.lastView.$options.el === "#subscriptionManagerView";
hideViews();
loadingView.seen = false;
// Check if lastView was search
if (isSearch) {
// Change back to searchView
headerView.seen = true;
headerView.title = 'Search Results';
searchView.seen = true;
// reset this.lastView
this.lastView = false;
} else if (isSubManager) {
subscriptionManagerView.seen = true;
this.lastView = false;
} else {
// if not search then this.lastView has to be playlistView
// Change back to playlistView
playlistView.seen = true;
// Check if searchView has videos if it does set this.lastView as searchView
this.lastView = searchView.videoList.length > 0 ? searchView : false;
}
}
},
computed: {
canShowBackButton: function () {
// this.lastView can be either searchView, subscriptionManagerView, or playlistView
return !!this.lastView && !this.lastView.seen;
}
},
});
let profileSelectView = new Vue({
el: '#profileSelectView',
data: {
seen: false,
activeProfile: [],
activeProfileInitial: '',
activeProfileInitialColor: '#000000',
profileList: [],
profileLock: false,
},
methods: {
showSubscriptionManager: function () {
hideViews();
subscriptionManagerView.seen = true;
},
setActiveProfile: function (index) {
if (profileSelectView.profileLock !== false) {
window.setTimeout(() => {
profileSelectView.setActiveProfile(index);
}, 1000);
return;
}
profileSelectView.profileLock = true;
this.activeProfile = this.profileList[index];
this.activeProfileInitial = this.profileInitials[index];
this.activeProfileInitialColor = this.profileTextColor[index];
this.seen = false;
displaySubs();
addSubsToView(subscriptionView.fullVideoList);
if (playerView.seen !== false || channelView.seen !== false) {
let checkSubscription;
if (playerView.seen !== false) {
checkSubscription = isSubscribed(playerView.channelId);
} else {
checkSubscription = isSubscribed(channelView.channelId);
}
checkSubscription.then((results) => {
if (results === false) {
channelView.subButtonText = 'SUBSCRIBE';
playerView.subscribedText = 'SUBSCRIBE';
} else {
channelView.subButtonText = 'UNSUBSCRIBE';
playerView.subscribedText = 'UNSUBSCRIBE';
}
});
}
}
},
computed: {
profileInitials: function () {
let initials = [];
if (this.profileList.length > 0) {
this.profileList.forEach((profile) => {
initials.push(profile.name.charAt(0));
});
}
return initials;
},
profileTextColor: function () {
let colors = [];
if (this.profileList.length > 0) {
this.profileList.forEach((profile) => {
let cutHex = (profile.color.charAt(0) == "#") ? profile.color.substring(1, 7) : h;
let colorValueR = parseInt(cutHex.substring(0, 2), 16);
let colorValueG = parseInt(cutHex.substring(2, 4), 16);
let colorValueB = parseInt(cutHex.substring(4, 6), 16);
let luminance = (0.299 * colorValueR + 0.587 * colorValueG + 0.114 * colorValueB) / 255;
if (luminance > 0.5) {
colors.push('#000000');
} else {
colors.push('#FFFFFF');
}
});
}
return colors;
}
},
template: profileSelectViewTemplate
});
let currentProfileView = new Vue({
el: '#currentProfileView',
data: {},
computed: {
activeProfile: function () {
return profileSelectView.activeProfile;
},
activeProfileInitial: function () {
return profileSelectView.activeProfileInitial;
},
activeProfileInitialColor: function () {
return profileSelectView.activeProfileInitialColor;
}
},
template: currentProfileViewTemplate
});
let subscriptionManagerView = new Vue({
el: '#subscriptionManagerView',
data: {
seen: false,
},
methods: {
editProfile: function (isNewProfile, index) {
hideViews();
editProfileView.isNewProfile = isNewProfile;
if (isNewProfile) {
editProfileView.profileName = '';
editProfileView.profileColor = '';
editProfileView.newProfileName = 'Profile ' + (this.profileList.length + 1);
let colorPaletteKeys = Object.keys(editProfileView.colorPalette);
let randomColor = colorPalette[colorPaletteKeys[colorPaletteKeys.length * Math.random() << 0]];
editProfileView.newProfileColorText = randomColor;
editProfileView.subscriptionList = [];
} else {
editProfileView.profileName = this.profileList[index].name;
editProfileView.profileColor = this.profileList[index].color;
editProfileView.newProfileName = this.profileList[index].name;
editProfileView.newProfileColorText = this.profileList[index].color;
if (this.profileList[index].name === 'All Channels') {
// Sort alphabetically
subDb.find({}).sort({
channelName: 1
}).exec((err, subs) => {
let list = [];
subs.forEach((sub) => {
sub.checked = false;
list.push(sub);
});
editProfileView.subscriptionList = list;
});
} else {
// Sort alphabetically
subDb.find({
profile: {
$elemMatch: {
value: this.profileList[index].name
}
}
}).sort({
channelName: 1
}).exec((err, subs) => {
let list = [];
subs.forEach((sub) => {
sub.checked = false;
list.push(sub);
});
editProfileView.subscriptionList = list;
});
}
}
editProfileView.seen = true;
loadingView.seen = false;
backButtonView.lastView = subscriptionManagerView;
}
},
computed: {
profileList: function () {
return profileSelectView.profileList;
},
profileInitials: function () {
return profileSelectView.profileInitials;
},
profileTextColor: function () {
return profileSelectView.profileTextColor;
}
},
template: subscriptionManagerViewTemplate
});
let editProfileView = new Vue({
el: '#editProfileView',
data: {
seen: false,
isNewProfile: false,
subscriptionList: [],
profileName: '',
profileColor: '',
newProfileName: '',
newProfileColorText: '',
selectedProfile: '',
colorPalette: {
red: '#d50000',
pink: '#C51162',
purple: '#AA00FF',
deepPurple: '#6200EA',
indigo: '#304FFE',
blue: '#2962FF',
lightBlue: '#0091EA',
cyan: '#00B8D4',
teal: '#00BFA5',
green: '#00C853',
lightGreen: '#64DD17',
lime: '#AEEA00',
yellow: '#FFD600',
amber: '#FFAB00',
orange: '#FF6D00',
deepOrange: '#DD2C00',
},
},
methods: {
changeProfileColor: function (value) {
this.newProfileColorText = value;
},
selectAll: function () {
this.subscriptionList.forEach(channel => {
channel.checked = true;
});
},
selectNone: function () {
this.subscriptionList.forEach((channel) => {
channel.checked = false;
});
},
defaultProfile: function () {
if (editProfileView.profileName === settingsView.defaultProfile) {
showToast('This profile is already set as your default.');
return;
}
settingsDb.update({
_id: 'defaultProfile'
}, {
value: editProfileView.profileName
}, {}, function (err, numUpdated) {
showToast(editProfileView.profileName + ' is now your default profile.');
settingsView.defaultProfile = editProfileView.profileName;
});
},
deleteProfile: function () {
if (this.profileName === settingsView.defaultProfile) {
showToast('You cannot delete your default profile.');
return;
}
let confirmString = 'Are you sure you want to delete this profile? Any subscriptions will also be deleted.';
confirmFunction(confirmString, () => {
settingsDb.find({
_id: 'profileList'
}, (err, docs) => {
let profiles = docs[0].value;
let nameIndex = profiles.findIndex(x => x.name === this.profileName);
profiles.splice(nameIndex, 1);
settingsDb.update({
_id: 'profileList'
}, {
value: profiles
}, {}, function (err, numUpdated) {});
subDb.update({
profile: {
$elemMatch: {
value: editProfileView.profileName
}
},
}, {
$pull: {
profile: {
value: editProfileView.profileName
}
}
}, {
multi: true
}, (err, numRemoved) => {
if (err) {
ft.log(err);
}
let subMap = subscriptionView.fullVideoList.map(x => x.profile.map(x => x.value).indexOf(editProfileView.profileName) !== -1);
for (let i = 0; i < subMap.length; i++) {
if (subMap[i]) {
let subProfileIndex = subscriptionView.fullVideoList[i].profile.findIndex(x => x.value === editProfileView.profileName);
subscriptionView.fullVideoList[i].profile.splice(subProfileIndex, 1);
}
}
let profileIndex = profileSelectView.profileList.findIndex(x => x.name === editProfileView.profileName);
profileSelectView.profileList.splice(profileIndex, 1);
if (profileSelectView.activeProfile.name === this.profileName) {
profileSelectView.setActiveProfile(0);
}
hideViews();
subscriptionManagerView.seen = true;
showToast('Profile has been successfully deleted');
});
});
});
},
updateProfile: function (updateView = true) {
if (this.newProfileName === '') {
showToast('Profile name cannot be blank.');
return;
}
let patt = new RegExp("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$");
if (patt.test(this.newProfileColor)) {
if (this.isNewProfile) {
let newNameIndex = this.profileList.findIndex(x => x.name === this.newProfileName);
if (newNameIndex !== -1 && this.profileName !== this.newProfileName) {
showToast('Profile name already exists. Please choose a different profile name.');
return;
}
// Create new profile
settingsDb.find({
_id: 'profileList'
}, (err, docs) => {
let profiles = docs[0].value;
let newNameIndex = profiles.findIndex(x => x.name === this.newProfileName);
if (newNameIndex !== -1 && this.profileName !== this.newProfileName) {
showToast('Profile name already exists. Please choose a different profile name.');
return;
} else {
let newProfile = {
name: this.newProfileName,
color: this.newProfileColor
};
profiles.push(newProfile);
settingsDb.update({
_id: 'profileList'
}, {
value: profiles
}, {}, function (err, numUpdated) {
profileSelectView.profileList.push(newProfile);
if (updateView) {
hideViews();
subscriptionManagerView.seen = true;
showToast('The ' + newProfile.name + ' profile has been added!');
}
});
}
});
} else {
// Update existing profile
settingsDb.find({
_id: 'profileList'
}, (err, docs) => {
let profiles = docs[0].value;
let newNameIndex = profiles.findIndex(x => x.name === this.newProfileName);
if (newNameIndex !== -1 && this.profileName !== this.newProfileName) {
showToast('Profile name already exists. Please choose a different profile name.');
return;
} else {
let oldNameIndex = profiles.findIndex(x => x.name === this.profileName);
profiles[oldNameIndex].name = this.newProfileName;
profiles[oldNameIndex].color = this.newProfileColor;
settingsDb.update({
_id: 'profileList'
}, {
value: profiles
}, {}, function (err, numUpdated) {
if (editProfileView.profileName === settingsView.defaultProfile) {
settingsDb.update({
_id: 'defaultProfile',
}, {
$set: {
value: editProfileView.newProfileName
}
}, (err, numRemoved) => {
ft.log(numRemoved);
});
}
subDb.update({
profile: {
$elemMatch: {
value: editProfileView.profileName
}
},
}, {
$push: {
profile: {
value: editProfileView.newProfileName
}
}
}, {
multi: true
}, (err, numRemoved) => {
if (err) {
ft.log(err);
}
let profileIndex = profileSelectView.profileList.findIndex(x => x.name === editProfileView.profileName);
profileSelectView.profileList[profileIndex].name = editProfileView.newProfileName;
profileSelectView.profileList[profileIndex].color = editProfileView.newProfileColor;
profileSelectView.setActiveProfile(profileIndex);
let subMap = subscriptionView.fullVideoList.map(x => x.profile.map(x => x.value).indexOf(editProfileView.profileName) !== -1);
for (let i = 0; i < subMap.length; i++) {
if (subMap[i]) {
let subProfileIndex = subscriptionView.fullVideoList[i].profile.findIndex(x => x.value === editProfileView.profileName);
subscriptionView.fullVideoList[i].profile.splice(subProfileIndex, 1);
subscriptionView.fullVideoList[i].profile.push(editProfileView.newProfileName);
}
}
if (editProfileView.profileName !== editProfileView.newProfileName) {
subDb.update({
profile: {
$elemMatch: {
value: editProfileView.profileName
}
},
}, {
$pull: {
profile: {
value: editProfileView.profileName
}
}
}, {
multi: true
}, (err, numRemoved) => {
if (err) {
ft.log(err);
}
});
}
editProfileView.profileName = editProfileView.newProfileName;
editProfileView.profileColor = editProfileView.newProfileColor;
window.setTimeout(() => {
// Refresh the list of subscriptions on the side navigation bar and subscriptions view.
displaySubs();
addSubsToView(subscriptionView.fullVideoList);
showToast('Profile has been successfully updated!');
}, 100);
});
});
}
});
}
} else {
showToast('The current HEX value is not valid, please fix and try again.');
}
},
move: function () {
if (this.amountSelected === 0) {
showToast('A channel must be selected before it can be moved.');
return;
}
if (this.selectedProfile === '') {
showToast('A profile must be selected first before channels can be moved.');
return;
}
if (this.selectedProfile === this.profileName) {
showToast('Select a profile other than the one being edited.');
return;
}
if (this.selectedProfile === 'All Channels') {
showToast('There is no need to move channels to "All Channels".');
return;
}
let confirmString = 'Would you like to move the selected channel(s) to the ' + this.selectedProfile + ' profile?';
confirmFunction(confirmString, () => {
let amountRemoved = 0;
this.subscriptionList.forEach(channel => {
if (channel.checked) {
subDb.update({
channelId: channel.channelId,
}, {
$push: {
profile: {
value: this.selectedProfile
}
}
}, (err, numRemoved) => {
if (err) {
ft.log(err);
}
});
channel.profile = this.selectedProfile;
amountRemoved++;
subDb.update({
channelId: channel.channelId,
}, {
$pull: {
profile: {
value: this.profileName
}
}
}, (err, numRemoved) => {
if (err) {
ft.log(err);
}
let subMap = subscriptionView.fullVideoList.map(x => x.author === channel.channelName && x.profile.map(x => x.value).indexOf(this.profileName) !== -1);
for (let i = 0; i < subMap.length; i++) {
if (subMap[i] !== false) {
subscriptionView.fullVideoList[i].profile.push({
value: this.selectedProfile
});
let profileIndex = subscriptionView.fullVideoList[i].profile.findIndex(x => x.value === this.profileName);
subscriptionView.fullVideoList[i].profile.splice(profileIndex, 1);
}
}
let index = this.subscriptionList.findIndex(x => x.channelName === channel.channelName);
this.subscriptionList.splice(index, 1);
});
}
});
window.setTimeout(() => {
// Refresh the list of subscriptions on the side navigation bar and subscriptions view.
displaySubs();
addSubsToView(subscriptionView.fullVideoList);
showToast('Moved ' + amountRemoved + ' channel(s) to the ' + this.selectedProfile + ' profile.');
}, 500);
});
},
copy: function () {
if (this.amountSelected === 0) {
showToast('A channel must be selected before it can be moved.');
return;
}
if (this.selectedProfile === '') {
showToast('A profile must be selected first before channels can be moved.');
return;
}
if (this.selectedProfile === this.profileName) {
showToast('Select a profile other than the one being edited.');
return;
}
if (this.selectedProfile === 'All Channels') {
showToast('There is no need to copy channels to "All Channels".');
return;
}
let confirmString = 'Would you like to copy the selected channel(s) to the ' + this.selectedProfile + ' profile?';
confirmFunction(confirmString, () => {
let amountCopied = 0;
this.subscriptionList.forEach(channel => {
if (channel.checked) {
subDb.find({
channelId: channel.channelId,
profile: {
$elemMatch: {
value: this.selectedProfile
}
}
}, {}, (err, subs) => {
if (subs.length === 0) {
channel.profile.push(this.selectedProfile);
subDb.update({
channelId: channel.channelId,
}, {
$push: {
profile: {
value: this.selectedProfile
}
}
}, (err, numAdded) => {
if (err) {
ft.log(err);
}
let subMap = subscriptionView.fullVideoList.map(x => x.author === channel.channelName);
for (let i = 0; i < subMap.length; i++) {
if (subMap[i] !== false) {
subscriptionView.fullVideoList[i].profile.push({
value: this.selectedProfile
});
}
}
amountCopied++;
});
}
});
}
});
window.setTimeout(() => {
// Refresh the list of subscriptions on the side navigation bar and subscriptions view.
displaySubs();
addSubsToView(subscriptionView.fullVideoList);
showToast('Copied ' + amountCopied + ' channel(s) to the ' + this.selectedProfile + ' profile.');
}, 500);
});
},
deleteChannel: function () {
let confirmString = 'Are you sure you want to delete the selected channel(s) from this profile?';
let amountDeleted = 0;
if (this.amountSelected === 0) {
showToast('A channel must be selected before it can be deleted.');
return;
}
confirmFunction(confirmString, () => {
this.subscriptionList.forEach((channel, index) => {
ft.log(channel);
if (channel.checked) {
removeSubscription(channel.channelId, editProfileView.profileName, false);
let subViewListMap = subscriptionView.fullVideoList.map(x => x.author === channel.channelName);
for (let i = 0; i < subViewListMap.length; i++) {
if (subViewListMap[i]) {
let subProfileIndex = subscriptionView.fullVideoList[i].profile.findIndex(x => x.value === editProfileView.profileName);
subscriptionView.fullVideoList[i].profile.splice(subProfileIndex, 1);
}
}
amountDeleted++;
}
});
window.setTimeout(() => {
// Refresh the list of subscriptions on the side navigation bar and subscriptions view.
displaySubs();
addSubsToView(subscriptionView.fullVideoList);
showToast(amountDeleted + ' channel(s) have been deleted from this profile.');
this.subscriptionList = this.subscriptionList.filter(a => {
return !a.checked;
});
}, 500);
});
},
},
computed: {
newProfileColor: function () {
if (this.newProfileColorText[0] === '#') {
return this.newProfileColorText;
} else {
return '#' + this.newProfileColorText;
}
},
isDefaultProfile: function () {
return settingsView.defaultProfile === this.profileName;
},
profileList: function () {
return profileSelectView.profileList;
},
amountSelected: function () {
let amount = 0;
this.subscriptionList.forEach((item) => {
if (item.checked) {
amount++;
}
});
return amount;
}
},
template: editProfileViewTemplate
});
function hideViews() {
if (playerView.seen !== false && (playerView.playerSeen || playerView.legacySeen)) {
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);
if (videoIndex !== -1) {
subscriptionView.videoList[videoIndex].watched = true;
subscriptionView.videoList[videoIndex].progressPercentage = (lengthSeconds / duration) * 100;
}
}
subscriptionView.seen = false;
noSubscriptions.seen = false;
aboutView.seen = false;
headerView.seen = false;
searchView.seen = false;
searchSuggestionsView.seen = false;
settingsView.seen = false;
popularView.seen = false;
trendingView.seen = false;
savedView.seen = false;
historyView.seen = false;
playlistView.seen = false;
playerView.seen = false;
playerView.playerSeen = false;
playerView.legacySeen = false;
channelView.seen = false;
channelVideosView.seen = false;
channelPlaylistsView.seen = false;
channelSearchView.seen = false;
profileSelectView.seen = false;
subscriptionManagerView.seen = false;
editProfileView.seen = false;
backButtonView.lastView = false;
}
function currentTimeParameter(){
if (!playerView.includeCurrentTime) return "";
if (typeof (playerView.currentTime) !== 'undefined') return "&t=" + Math.floor(playerView.currentTime);
if (typeof (player) !== 'undefined') return "&t=" + Math.floor(player.currentTime);
const legacyPlayer = $('#legacyPlayer').get(0);
if (typeof (legacyPlayer) !== 'undefined') return "&t=" + Math.floor(legacyPlayer.currentTime);
return "";
}