diff --git a/src/index.html b/src/index.html index 39983ce4e..e54f86922 100644 --- a/src/index.html +++ b/src/index.html @@ -2,165 +2,168 @@ - - - - - - - - - - - - - - - - - - - - - - FreeTube Player + + + + + + + + + + + + + + + + + + + + + + FreeTube Player -
-
-
-
+
+
+
+
+
-
-
- Would you like to perform the function? -
Yes
-
No
-
-
- - -
-
- - - - -
-
-
-
    -
  •   Subscriptions
  • -
  •   Trending
  • -
  •   Most Popular
  • -
  •   Favorites
  • -
  •   History
  • -
-
-
    -
  •   Settings
  • -
  •   About
  • -
-
-
    -
+
+ +
-
-
-
-

Search Filters

-
-
-

Sort By

- - -

- - -

- - -

- - -
-
-

Time

- - -

- - -

- - -

- - -

- - -

- - -
-
-

Type

- - -

- - -

- - -

- - -
-
-

Duration

- - -

- - -

- - -
+
+ + +   + + +
+
-
-

+
+
+
+
    +
  •   Subscriptions
  • +
  •   Trending
  • +
  •   Most Popular
  • +
  •   Favorites
  • +
  •   History
  • +
+
+
    +
  •   Settings
  • +
  •   About
  • +
+
+
    +
+
+
+
+
+

Search Filters

+
+
+

Sort By

+ + +

+ + +

+ + +

+ + +
+
+

Time

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +
+
+

Type

+ + +

+ + +

+ + +

+ + +
+
+

Duration

+ + +

+ +

+ + +
+
+
+

Your Subscription list is currently empty. Start adding subscriptions to see them here.

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
+
@@ -192,4 +195,5 @@ + diff --git a/src/js/events.js b/src/js/events.js index 3da07e5df..008ab69c6 100644 --- a/src/js/events.js +++ b/src/js/events.js @@ -31,28 +31,28 @@ let showComments = function (event, continuation = '') { comments.attr('loaded', 'true'); invidiousAPI('comments', $('#comments').attr('data-video-id'), {}, (data) => { - ft.log(data); + ft.log(data); - let comments = []; + let comments = []; - data.comments.forEach((object) => { + data.comments.forEach((object) => { - let snippet = { - author: object.author, - authorId: object.authorId, - authorThumbnail: object.authorThumbnails[0].url, - published: object.publishedText, - authorComment: object.content, - } + let snippet = { + author: object.author, + authorId: object.authorId, + authorThumbnail: object.authorThumbnails[0].url, + published: object.publishedText, + authorComment: object.content, + } - comments.push(snippet); - }) - const commentsTemplate = require('./templates/comments.html'); - const html = mustache.render(commentsTemplate, { - comments: comments, - }); + comments.push(snippet); + }) + const commentsTemplate = require('./templates/comments.html'); + const html = mustache.render(commentsTemplate, { + comments: comments, + }); - $('#comments').html(html); + $('#comments').html(html); }); comments.show(); @@ -67,49 +67,47 @@ let showComments = function (event, continuation = '') { let playPauseVideo = function (event) { let el = event.currentTarget; if (el.paused && $('.videoPlayer').is(':hover')) { - $('.videoPlayer')[0].style.cursor = 'none'; + $('.videoPlayer')[0].style.cursor = 'none'; } }; /** -* Handle keyboard shortcut commands. -*/ + * Handle keyboard shortcut commands. + */ let videoShortcutHandler = function (event) { let videoPlayer; if (event.which == 68 && event.altKey === true) { - $('#search').focus(); + $('#search').focus(); } if (event.which == 82 && event.shiftKey === false && event.ctrlKey === true && !$('#jumpToInput').is(':focus') && !$('#search').is(':focus')) { - event.stopPropagation(); - event.preventDefault(); - forceSubscriptions(); + event.stopPropagation(); + event.preventDefault(); + forceSubscriptions(); } - if ((typeof(playerView) !== 'undefined' && playerView.legacySeen) || (typeof(miniPlayerView) !== 'undefined' && miniPlayerView.legacySeen)) { - videoPlayer = $('.videoPlayer').get(0); - } - else { - videoPlayer = $('#player').get(0); + if ((typeof (playerView) !== 'undefined' && playerView.legacySeen) || (typeof (miniPlayerView) !== 'undefined' && miniPlayerView.legacySeen)) { + videoPlayer = $('.videoPlayer').get(0); + } else { + videoPlayer = $('#player').get(0); } - if (typeof(videoPlayer) !== 'undefined' && !$('#jumpToInput').is(':focus') && !$('#search').is(':focus')) { + if (typeof (videoPlayer) !== 'undefined' && !$('#jumpToInput').is(':focus') && !$('#search').is(':focus')) { switch (event.which) { case 32: // Space Bar event.preventDefault(); if ($('.videoPlayer').is(':focus')) { - return; + return; } if (videoPlayer.paused) { - videoPlayer.play(); - if($('.videoPlayer').is(':hover')) { - $('.videoPlayer')[0].style.cursor = 'none'; - } - } - else { - videoPlayer.pause(); + videoPlayer.play(); + if ($('.videoPlayer').is(':hover')) { + $('.videoPlayer')[0].style.cursor = 'none'; + } + } else { + videoPlayer.pause(); } break; case 74: @@ -121,13 +119,12 @@ let videoShortcutHandler = function (event) { // K Key event.preventDefault(); if (videoPlayer.paused) { - videoPlayer.play(); - if($('.videoPlayer').is(':hover')) { - $('.videoPlayer')[0].style.cursor = 'none'; - } - } - else { - videoPlayer.pause(); + videoPlayer.play(); + if ($('.videoPlayer').is(':hover')) { + $('.videoPlayer')[0].style.cursor = 'none'; + } + } else { + videoPlayer.pause(); } break; case 76: @@ -138,19 +135,19 @@ let videoShortcutHandler = function (event) { case 79: // O Key event.preventDefault(); - if (videoPlayer.playbackRate > 0.25){ - let rate = videoPlayer.playbackRate - 0.25; - videoPlayer.playbackRate = rate; - changeVideoSpeed(rate); + if (videoPlayer.playbackRate > 0.25) { + let rate = videoPlayer.playbackRate - 0.25; + videoPlayer.playbackRate = rate; + changeVideoSpeed(rate); } break; case 80: // P Key event.preventDefault(); - if (videoPlayer.playbackRate < 2){ - let rate = videoPlayer.playbackRate + 0.25; - videoPlayer.playbackRate = rate; - changeVideoSpeed(rate); + if (videoPlayer.playbackRate < 2) { + let rate = videoPlayer.playbackRate + 0.25; + videoPlayer.playbackRate = rate; + changeVideoSpeed(rate); } break; case 70: @@ -172,22 +169,20 @@ let videoShortcutHandler = function (event) { case 67: // C Key if (playerView.legacySeen) { - let subtitleMode = videoPlayer.textTracks[0].mode; - if (subtitleMode === 'hidden') { - videoPlayer.textTracks[0].mode = 'showing' - } else { - videoPlayer.textTracks[0].mode = 'hidden' - } - } - else { - let captionOptions = $('.mejs__captions-selector-input').get(); + let subtitleMode = videoPlayer.textTracks[0].mode; + if (subtitleMode === 'hidden') { + videoPlayer.textTracks[0].mode = 'showing' + } else { + videoPlayer.textTracks[0].mode = 'hidden' + } + } else { + let captionOptions = $('.mejs__captions-selector-input').get(); - if (!captionOptions[0].labels[0].className.includes('mejs__captions-selected')) { - captionOptions[0].click(); - } - else { - captionOptions[1].click(); - } + if (!captionOptions[0].labels[0].className.includes('mejs__captions-selected')) { + captionOptions[0].click(); + } else { + captionOptions[1].click(); + } } break; case 38: @@ -265,31 +260,30 @@ let videoShortcutHandler = function (event) { }; let fullscreenVideo = function (event) { - if (playerView.legacySeen) { - if (document.webkitFullscreenElement !== null) { - document.webkitExitFullscreen(); + if (playerView.legacySeen) { + if (document.webkitFullscreenElement !== null) { + document.webkitExitFullscreen(); + } else { + $('.videoPlayer').get(0).webkitRequestFullscreen(); + } } else { - $('.videoPlayer').get(0).webkitRequestFullscreen(); - } - } - else { - $('.mejs__fullscreen-button').click(); + $('.mejs__fullscreen-button').click(); - if (document.webkitFullscreenElement !== null) { - console.log('changing screen size...'); - const currentWindow = electron.remote.getCurrentWindow(); - let bounds = currentWindow.getBounds(); - let newBounds = { - height: bounds.height, - width: bounds.width + 1, - x: bounds.x, - y: bounds.y, - } + if (document.webkitFullscreenElement !== null) { + console.log('changing screen size...'); + const currentWindow = electron.remote.getCurrentWindow(); + let bounds = currentWindow.getBounds(); + let newBounds = { + height: bounds.height, + width: bounds.width + 1, + x: bounds.x, + y: bounds.y, + } - currentWindow.setBounds(newBounds); - currentWindow.setBounds(bounds); + currentWindow.setBounds(newBounds); + currentWindow.setBounds(bounds); + } } - } } /** @@ -310,24 +304,38 @@ $(document).on('click', '#confirmNo', hideConfirmFunction); // Open links externally by default $(document).on('click', 'a[href^="http"]', (event) => { - let el = event.currentTarget; - if (!el.href.includes('freetube://')) { - event.preventDefault(); - shell.openExternal(el.href); - } - else{ - search(el.href); - } + let el = event.currentTarget; + if (!el.href.includes('freetube://')) { + event.preventDefault(); + shell.openExternal(el.href); + } else { + search(el.href); + } }); // Open links externally on middle click. $(document).on('auxclick', 'a[href^="http"]', (event) => { - let el = event.currentTarget; - if (!el.href.includes('freetube://')) { - event.preventDefault(); - shell.openExternal(el.href); - } - else{ - search(el.href); - } + let el = event.currentTarget; + if (!el.href.includes('freetube://')) { + event.preventDefault(); + shell.openExternal(el.href); + } else { + search(el.href); + } +}); + +window.addEventListener('mousewheel', function (event) { + if (playerView.seen !== false && $('.mejs__volume-slider').is(':hover')) { + event.stopPropagation(); + event.preventDefault(); + if (event.deltaY <= 0) { + // Scroll up + changeVolume(0.05); + } else { + // Scroll down + changeVolume(-0.05); + } + } +}, { + passive: false }); diff --git a/src/js/init.js b/src/js/init.js index 86c0c8f00..b6f1aa279 100644 --- a/src/js/init.js +++ b/src/js/init.js @@ -72,7 +72,7 @@ let init = function () { height: 800, autoHideMenuBar: true, webPreferences: { - nodeIntegration: true, + nodeIntegration: true, }, icon: path.join(__dirname, '..', 'icons', 'iconColor.png') }); @@ -85,11 +85,11 @@ let init = function () { let monitorList = screen.getAllDisplays(); let width = 0; for (let i = 0; i < monitorList.length - 1; i++) { - width = width + monitorList[i].size.width; + width = width + monitorList[i].size.width; } if (width <= doc.value.x) { - win.setBounds(doc.value); + win.setBounds(doc.value); } } } @@ -103,22 +103,21 @@ let init = function () { _id: 'proxy' }, (err, doc) => { if (doc !== null) { - win.webContents.session.setProxy({ - proxyRules: doc.value - }, function () { - win.loadURL(url.format({ - pathname: path.join(__dirname, '../index.html'), - protocol: 'file:', - slashes: true, - })); - }); - } - else { - win.loadURL(url.format({ - pathname: path.join(__dirname, '../index.html'), - protocol: 'file:', - slashes: true, - })); + win.webContents.session.setProxy({ + proxyRules: doc.value + }, function () { + win.loadURL(url.format({ + pathname: path.join(__dirname, '../index.html'), + protocol: 'file:', + slashes: true, + })); + }); + } else { + win.loadURL(url.format({ + pathname: path.join(__dirname, '../index.html'), + protocol: 'file:', + slashes: true, + })); } }); } else { @@ -267,8 +266,8 @@ let init = function () { */ let allWindowsClosed = function () { if (win !== null) { - win.webContents.session.clearStorageData([], (data) => {}); - win.webContents.session.clearCache((data) => {}); + win.webContents.session.clearStorageData([], (data) => {}); + win.webContents.session.clearCache((data) => {}); } app.quit(); }; @@ -284,20 +283,20 @@ let active = function () { }; if (!gotTheLock) { - app.quit() + app.quit() } else { - app.on('second-instance', (event, commandLine, workingDirectory) => { - // Someone tried to run a second instance, we should focus our window. - if (win) { - if (win.isMinimized()) win.restore() - win.focus() + app.on('second-instance', (event, commandLine, workingDirectory) => { + // Someone tried to run a second instance, we should focus our window. + if (win) { + if (win.isMinimized()) win.restore() + win.focus() - win.webContents.send('ping', commandLine) - } - }) + win.webContents.send('ping', commandLine) + } + }) - // Create myWindow, load the rest of the app, etc... - app.on('ready', init); + // Create myWindow, load the rest of the app, etc... + app.on('ready', init); } /** diff --git a/src/js/layout.js b/src/js/layout.js index a816fed29..9b2f8033b 100644 --- a/src/js/layout.js +++ b/src/js/layout.js @@ -45,14 +45,14 @@ let dialog = electron.remote.dialog; // Used for opening file browser to export let toastTimeout; // Timeout for toast notifications. let mouseTimeout; // Timeout for hiding the mouse cursor on video playback -require.extensions['.html'] = function(module, filename) { +require.extensions['.html'] = function (module, filename) { module.exports = fs.readFileSync(filename, 'utf8'); }; // Grabs the default settings from the settings database file. Makes defaults if // none are found. -electron.ipcRenderer.on('ping', function(event, message) { +electron.ipcRenderer.on('ping', function (event, message) { ft.log(message); let url = message[message.length - 1]; if (url) { @@ -63,7 +63,7 @@ electron.ipcRenderer.on('ping', function(event, message) { }); // Listens for proxy to be set in main process -electron.ipcRenderer.on('proxyAvailable', function(event, message) { +electron.ipcRenderer.on('proxyAvailable', function (event, message) { proxyAvailable = true; }); @@ -88,39 +88,38 @@ $(document).ready(() => { _id: 'invidious' }] }, (err, docs) => { - if (typeof(docs[0]) !== 'undefined') { - invidiousInstance = docs[0].value; + if (typeof (docs[0]) !== 'undefined') { + invidiousInstance = docs[0].value; } loadingView.seen = true; - if (typeof(docs[1]) !== 'undefined') { + if (typeof (docs[1]) !== 'undefined') { switch (docs[1].value) { - case 'subscriptions': - sideNavBar.subscriptions(); - break; - case 'trending': - sideNavBar.trending(); - break; - case 'popular': - sideNavBar.popular(); - break; - case 'favorites': - sideNavBar.saved(); - break; - case 'history': - sideNavBar.history(); - break; + case 'subscriptions': + sideNavBar.subscriptions(); + break; + case 'trending': + sideNavBar.trending(); + break; + case 'popular': + sideNavBar.popular(); + break; + case 'favorites': + sideNavBar.saved(); + break; + case 'history': + sideNavBar.history(); + break; } - } - else { - sideNavBar.subscriptions(); + } else { + sideNavBar.subscriptions(); } }); - $.get('https://write.as/freetube/feed/', function(data) { + $.get('https://write.as/freetube/feed/', function (data) { aboutView.rssFeed = []; - $(data).find("item").each(function() { + $(data).find("item").each(function () { let el = $(this); let rssData = { title: el.find("title").text(), @@ -147,31 +146,31 @@ function toggleSideNavigation() { sideNav.style.display = 'inline'; mainContainer.style.marginLeft = '250px'; if (confirmSettings !== null) { - confirmSettings.style.marginLeft = '250px'; + confirmSettings.style.marginLeft = '250px'; } } else { sideNav.style.display = 'none'; mainContainer.style.marginLeft = '0px'; if (confirmSettings !== null) { - confirmSettings.style.marginLeft = '0px'; + confirmSettings.style.marginLeft = '0px'; } } if (playerView.playerSeen) { - // This is a really dumb way to fix the issue of the video player - // not resizing properly when the side bar is toggled. + // This is a really dumb way to fix the issue of the video player + // not resizing properly when the side bar is toggled. - const currentWindow = electron.remote.getCurrentWindow(); - let bounds = currentWindow.getBounds(); - let newBounds = { - height: bounds.height, - width: bounds.width + 1, - x: bounds.x, - y: bounds.y, - } + const currentWindow = electron.remote.getCurrentWindow(); + let bounds = currentWindow.getBounds(); + let newBounds = { + height: bounds.height, + width: bounds.width + 1, + x: bounds.x, + y: bounds.y, + } - currentWindow.setBounds(newBounds); - currentWindow.setBounds(bounds); + currentWindow.setBounds(newBounds); + currentWindow.setBounds(bounds); } } @@ -220,20 +219,25 @@ function hideToast() { * @return {Void} */ function confirmFunction(message, performFunction, parameters = '') { + let confirmYes = document.getElementById('confirmYes'); let confirmContainer = document.getElementById('confirmFunction'); let confirmMessage = document.getElementById('confirmMessage'); + confirmYes.removeAttribute("onclick"); + confirmMessage.innerHTML = message; confirmContainer.style.visibility = 'visible'; - $(document).on('click', '#confirmYes', (event) => { + confirmYes.onclick = function (event) { + event.preventDefault(); + event.stopPropagation(); if (parameters != '') { performFunction(parameters); } else { performFunction(); } hideConfirmFunction(); - }); + }; } /** @@ -255,7 +259,7 @@ function hideConfirmFunction() { function hideMouseTimeout() { $('.videoPlayer')[0].style.cursor = 'default'; clearTimeout(mouseTimeout); - mouseTimeout = window.setTimeout(function() { + mouseTimeout = window.setTimeout(function () { $('.videoPlayer')[0].style.cursor = 'none'; }, 2650); } @@ -287,7 +291,7 @@ function proxyRequest(callback) { let counter = 0; // Wait for proxy to become available - proxyCheckingInterval = setInterval(function() { + proxyCheckingInterval = setInterval(function () { if (proxyAvailable) { clearInterval(proxyCheckingInterval) diff --git a/src/js/settings.js b/src/js/settings.js index d47cbb523..228549bab 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -41,6 +41,24 @@ let invidiousInstance = 'https://invidio.us'; let checkedSettings = false; // Used to prevent data leak when using self-hosted Invidious Instance let debugMode = false; let defaultPage = 'subscriptions'; +const 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', +}; /** * Display the settings screen to the user. @@ -56,10 +74,10 @@ function updateSettingsView() { settingsDb.find({}, (err, docs) => { docs.forEach((setting) => { switch (setting['_id']) { - case 'theme': - if (currentTheme == '') { - currentTheme = setting['value']; - } + case 'theme': + if (currentTheme == '') { + currentTheme = setting['value']; + } } }); @@ -137,6 +155,9 @@ function checkDefaultSettings() { let newSetting; + let colorPaletteKeys = Object.keys(colorPalette); + let randomColor = colorPalette[colorPaletteKeys[colorPaletteKeys.length * Math.random() << 0]]; + let settingDefaults = { 'theme': 'light', 'useTor': false, @@ -159,6 +180,11 @@ function checkDefaultSettings() { 'distractionFreeMode': false, 'hideWatchedSubs': false, 'videoView': 'grid', + 'profileList': [{ + name: 'Default', + color: randomColor + }, ], + 'defaultProfile': 'Default', }; ft.log(settingDefaults); @@ -180,95 +206,130 @@ function checkDefaultSettings() { } if (key == 'videoView') { - enableGridView(); + enableGridView(); + } + + if (key == 'profileList') { + profileSelectView.profileList = settingDefaults.profileList; + profileSelectView.setActiveProfile(0); + + subDb.find({}, (err, docs) => { + if (!jQuery.isEmptyObject(docs)) { + docs.forEach((doc) => { + subDb.update({ + channelId: doc.channelId + }, { + $set: { + profile: [{ + value: 'Default' + }] + } + }, {}, (err, newDoc) => { + profileSelectView.setActiveProfile(0); + }); + }); + } + }); } } else { switch (docs[0]['_id']) { - case 'theme': - setTheme(docs[0]['value']); - break; - case 'useTor': - useTor = docs[0]['value']; - break; - case 'history': - rememberHistory = docs[0]['value']; - break; - case 'autoplay': - autoplay = docs[0]['value']; - break; - case 'autoplayPlaylists': - settingsView.autoplayPlaylists = docs[0]['value']; - break; - case 'playNextVideo': - settingsView.playNextVideo = docs[0]['value']; - break; - case 'subtitles': - enableSubtitles = docs[0]['value']; - break; - case 'updates': - checkForUpdates = docs[0]['value']; + case 'theme': + setTheme(docs[0]['value']); + break; + case 'useTor': + useTor = docs[0]['value']; + break; + case 'history': + rememberHistory = docs[0]['value']; + break; + case 'autoplay': + autoplay = docs[0]['value']; + break; + case 'autoplayPlaylists': + settingsView.autoplayPlaylists = docs[0]['value']; + break; + case 'playNextVideo': + settingsView.playNextVideo = docs[0]['value']; + break; + case 'subtitles': + enableSubtitles = docs[0]['value']; + break; + case 'updates': + checkForUpdates = docs[0]['value']; - if (checkForUpdates) { - checkForNewUpdate(); - } - break; - case 'player': - defaultPlayer = docs[0]['value']; - break; - case 'quality': - defaultQuality = docs[0]['value']; - break; - case 'volume': - defaultVolume = docs[0]['value']; - currentVolume = docs[0]['value']; - break; - case 'rate': - defaultPlaybackRate = docs[0]['value']; - break; - case 'proxy': - defaultProxy = docs[0]['value']; + if (checkForUpdates) { + checkForNewUpdate(); + } + break; + case 'player': + defaultPlayer = docs[0]['value']; + break; + case 'quality': + defaultQuality = docs[0]['value']; + break; + case 'volume': + defaultVolume = docs[0]['value']; + currentVolume = docs[0]['value']; + break; + case 'rate': + defaultPlaybackRate = docs[0]['value']; + break; + case 'proxy': + defaultProxy = docs[0]['value']; - if (useTor && defaultProxy) { - electron.ipcRenderer.send("setProxy", defaultProxy); - } - break; - case 'invidious': - invidiousInstance = docs[0]['value'].replace(/\/$/, ''); - settingsView.invidiousInstance = invidiousInstance; - break; - case 'region': - defaultRegion = docs[0]['value']; - settingsView.region = docs[0]['value']; - break; - case 'localScrape': - getVideosLocally = docs[0]['value']; - settingsView.localScrape = docs[0]['value']; - break; - case 'debugMode': - debugMode = docs[0]['value']; - settingsView.debugMode = docs[0]['value']; - break; - case 'startScreen': - defaultPage = docs[0]['value']; - break; - case 'distractionFreeMode': - settingsView.setDistractionFreeMode(docs[0]['value']); - break; - case 'hideWatchedSubs': - hideWatchedSubs = docs[0]['value']; - settingsView.hideWatchedSubs = docs[0]['value']; - break; - case 'videoView': - settingsView.videoView = docs[0]['value']; - if (settingsView.videoView == 'grid') { - enableGridView(); - } - else { - enableListView(); - } - break; - default: - break; + if (useTor && defaultProxy) { + electron.ipcRenderer.send("setProxy", defaultProxy); + } + break; + case 'invidious': + invidiousInstance = docs[0]['value'].replace(/\/$/, ''); + settingsView.invidiousInstance = invidiousInstance; + break; + case 'region': + defaultRegion = docs[0]['value']; + settingsView.region = docs[0]['value']; + break; + case 'localScrape': + getVideosLocally = docs[0]['value']; + settingsView.localScrape = docs[0]['value']; + break; + case 'debugMode': + debugMode = docs[0]['value']; + settingsView.debugMode = docs[0]['value']; + break; + case 'startScreen': + defaultPage = docs[0]['value']; + break; + case 'distractionFreeMode': + settingsView.setDistractionFreeMode(docs[0]['value']); + break; + case 'hideWatchedSubs': + hideWatchedSubs = docs[0]['value']; + settingsView.hideWatchedSubs = docs[0]['value']; + break; + case 'videoView': + settingsView.videoView = docs[0]['value']; + if (settingsView.videoView == 'grid') { + enableGridView(); + } else { + enableListView(); + } + break; + case 'profileList': + profileSelectView.profileList = docs[0]['value']; + break; + case 'defaultProfile': + let profileIndex = profileSelectView.profileList.findIndex(x => x.name === docs[0]['value']); + settingsView.defaultProfile = docs[0]['value']; + + if (profileIndex === -1) { + profileSelectView.setActiveProfile(0); + } else { + profileSelectView.setActiveProfile(profileIndex); + } + break; + default: + break; } } }); @@ -334,7 +395,7 @@ function updateSettings() { _id: 'theme' }, { value: theme - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); }); @@ -344,7 +405,7 @@ function updateSettings() { _id: 'useTor' }, { value: torSwitch - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); useTor = torSwitch; @@ -355,7 +416,7 @@ function updateSettings() { _id: 'proxy' }, { value: proxyAddress - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); defaultProxy = proxyAddress; @@ -366,7 +427,7 @@ function updateSettings() { _id: 'invidious' }, { value: invidious - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); settingsView.invidiousInstance = invidious; @@ -378,7 +439,7 @@ function updateSettings() { _id: 'history' }, { value: historySwitch - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); rememberHistory = historySwitch; @@ -389,7 +450,7 @@ function updateSettings() { _id: 'autoplay' }, { value: autoplaySwitch - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); autoplay = autoplaySwitch; @@ -400,7 +461,7 @@ function updateSettings() { _id: 'autoplayPlaylists' }, { value: autoplayPlaylistsSwitch - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); settingsView.autoplayPlaylists = autoplayPlaylistsSwitch; @@ -411,7 +472,7 @@ function updateSettings() { _id: 'playNextVideo' }, { value: playNextVideoSwitch - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); settingsView.playNextVideo = playNextVideoSwitch; @@ -422,7 +483,7 @@ function updateSettings() { _id: 'localScrape' }, { value: localSwitch - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); getVideosLocally = localSwitch; @@ -433,7 +494,7 @@ function updateSettings() { _id: 'subtitles' }, { value: subtitlesSwitch - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); enableSubtitles = subtitlesSwitch; @@ -444,7 +505,7 @@ function updateSettings() { _id: 'updates' }, { value: updatesSwitch - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); checkForUpdates = updatesSwitch; @@ -455,7 +516,7 @@ function updateSettings() { _id: 'player' }, { value: playerSelect - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); defaultPlayer = playerSelect; @@ -466,7 +527,7 @@ function updateSettings() { _id: 'quality' }, { value: qualitySelect - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); defaultQuality = qualitySelect; @@ -477,7 +538,7 @@ function updateSettings() { _id: 'volume' }, { value: volumeSelect - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); defaultVolume = volumeSelect; @@ -489,7 +550,7 @@ function updateSettings() { _id: 'rate' }, { value: rateSelect - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); defaultPlaybackRate = rateSelect; @@ -500,7 +561,7 @@ function updateSettings() { _id: 'region' }, { value: regionSelect - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); settingsView.region = regionSelect; @@ -511,7 +572,7 @@ function updateSettings() { _id: 'debugMode' }, { value: debugMode - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); settingsView.debugMode = debugMode; @@ -522,7 +583,7 @@ function updateSettings() { _id: 'startScreen' }, { value: pageSelect - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); }); @@ -532,7 +593,7 @@ function updateSettings() { _id: 'distractionFreeMode' }, { value: distractionFreeMode - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); }); @@ -542,7 +603,7 @@ function updateSettings() { _id: 'hideWatchedSubs' }, { value: hideSubs - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); hideWatchedSubs = hideSubs; @@ -554,14 +615,13 @@ function updateSettings() { _id: 'videoView' }, { value: videoViewType - }, {}, function(err, numReplaced) { + }, {}, function (err, numReplaced) { ft.log(err); ft.log(numReplaced); if (settingsView.videoView == 'grid') { - enableGridView(); - } - else { - enableListView(); + enableGridView(); + } else { + enableListView(); } }); @@ -614,24 +674,24 @@ function setTheme(option) { // Grab the css file to be used. switch (option) { - case 'light': - cssFile = './style/lightTheme.css'; - document.getElementById('menuText').src = 'icons/textBlackSmall.png'; - document.getElementById('menuIcon').src = 'icons/iconBlackSmall.png'; - document.getElementById('menuButton').style.color = 'black'; - document.getElementById('reloadButton').style.color = 'black'; - break; - case 'dark': - cssFile = './style/darkTheme.css'; - document.getElementById('menuText').src = 'icons/textColorSmall.png'; - document.getElementById('menuIcon').src = 'icons/iconColorSmall.png'; - document.getElementById('menuButton').style.color = 'white'; - document.getElementById('reloadButton').style.color = 'white'; - break; - default: - // Default to the light theme - cssFile = './style/lightTheme.css'; - break; + case 'light': + cssFile = './style/lightTheme.css'; + document.getElementById('menuText').src = 'icons/textBlackSmall.png'; + document.getElementById('menuIcon').src = 'icons/iconBlackSmall.png'; + document.getElementById('menuButton').style.color = 'black'; + document.getElementById('reloadButton').style.color = 'black'; + break; + case 'dark': + cssFile = './style/darkTheme.css'; + document.getElementById('menuText').src = 'icons/textColorSmall.png'; + document.getElementById('menuIcon').src = 'icons/iconColorSmall.png'; + document.getElementById('menuButton').style.color = 'white'; + document.getElementById('reloadButton').style.color = 'white'; + break; + default: + // Default to the light theme + cssFile = './style/lightTheme.css'; + break; } newTheme.setAttribute("href", cssFile); @@ -640,31 +700,31 @@ function setTheme(option) { } function enableGridView() { - let cssFile; - const currentView = document.getElementsByTagName("link").item(2); + let cssFile; + const currentView = document.getElementsByTagName("link").item(2); - // Create a link element - const newView = document.createElement("link"); - newView.setAttribute("rel", "stylesheet"); - newView.setAttribute("type", "text/css"); - newView.setAttribute("href", './style/videoGrid.css'); + // Create a link element + const newView = document.createElement("link"); + newView.setAttribute("rel", "stylesheet"); + newView.setAttribute("type", "text/css"); + newView.setAttribute("href", './style/videoGrid.css'); - // Replace the current theme with the new theme - document.getElementsByTagName("head").item(0).replaceChild(newView, currentView); + // Replace the current theme with the new theme + document.getElementsByTagName("head").item(0).replaceChild(newView, currentView); } function enableListView() { - let cssFile; - const currentView = document.getElementsByTagName("link").item(2); + let cssFile; + const currentView = document.getElementsByTagName("link").item(2); - // Create a link element - const newView = document.createElement("link"); - newView.setAttribute("rel", "stylesheet"); - newView.setAttribute("type", "text/css"); - newView.setAttribute("href", './style/videoList.css'); + // Create a link element + const newView = document.createElement("link"); + newView.setAttribute("rel", "stylesheet"); + newView.setAttribute("type", "text/css"); + newView.setAttribute("href", './style/videoList.css'); - // Replace the current theme with the new theme - document.getElementsByTagName("head").item(0).replaceChild(newView, currentView); + // Replace the current theme with the new theme + document.getElementsByTagName("head").item(0).replaceChild(newView, currentView); } /** @@ -725,8 +785,8 @@ function importSubscriptions() { name: 'Database File', extensions: ['*'] }, ] - }, function(fileLocation) { - if (typeof(fileLocation) === 'undefined') { + }, function (fileLocation) { + if (typeof (fileLocation) === 'undefined') { ft.log('Import Aborted'); return; } @@ -735,14 +795,14 @@ function importSubscriptions() { let fileType = (i < 0) ? '' : fileLocation[0].substr(i); ft.log(fileType); - fs.readFile(fileLocation[0], function(readErr, data) { + fs.readFile(fileLocation[0], function (readErr, data) { if (readErr) { showToast('Unable to read file. File may be corrupt or have invalid permissions.'); throw readErr; } if (data.includes(" { + let parsedData = JSON.parse(data); + + let newSubscription = { + channelId: parsedData.channelId, + channelName: parsedData.channelName, + channelThumbnail: parsedData.channelThumbnail, + } + + if (typeof (parsedData.profile) !== 'undefined') { + let profileList = []; + parsedData.profile.forEach((profile) => { + if (profileList.indexOf(profile.value) !== -1) { + console.log('found duplicate'); + return; + } + + profileList.push(profile.value); + + // Sometimes adding the same channel to the database too fast + // will duplicate the channel in the wrong profile. The wait + // time to add to the database is randomized to prevent this. + let randomNumber = Math.floor((Math.random() * 10000) + 1); + window.setTimeout(() => { + let existingProfileIndex = profileSelectView.profileList.findIndex(x => x.name === profile); + if (existingProfileIndex === -1) { + // User doesn't have this profile, let's create it. + + let colorPaletteKeys = Object.keys(colorPalette); + let randomColor = colorPalette[colorPaletteKeys[colorPaletteKeys.length * Math.random() << 0]]; + editProfileView.isNewProfile = true; + editProfileView.newProfileColor = randomColor; + editProfileView.newProfileName = profile.value; + editProfileView.updateProfile(false); + } + addSubscription(newSubscription, true, profile.value); + displaySubs(); + }, randomNumber); + }); + } else { + let randomNumber = Math.floor((Math.random() * 1000) + 1); + window.setTimeout(() => { + addSubscription(newSubscription) + }, randomNumber); } - showToast('Susbcriptions have been successfully imported. Please restart FreeTube for the changes to take effect.'); }); + + window.setTimeout(() => { + displaySubs() + }, 8000); }) }); } @@ -829,42 +938,42 @@ function exportSubscriptions() { } const dateYear = date.getFullYear(); - const dateString = 'freetube-subscriptions-' + dateYear + '-' + dateMonth + '-' + dateDay; + const dateString = 'freetube-subscriptions-' + dateYear + '-' + dateMonth + '-' + dateDay + '.db'; switch (document.querySelector('#exportSelect').value) { - case "NewPipe": - exportNewpipeSubscriptions(dateYear, dateMonth, dateDay); - break; - case "OPML": - exportOpmlSubscriptions(dateYear, dateMonth, dateDay); - break; - default: - // Open user file browser. User gives location of file to be created. - dialog.showSaveDialog({ - defaultPath: dateString, - filters: [{ - name: 'Database File', - extensions: ['db'] - }, ] - }, function(fileLocation) { - ft.log(fileLocation); - if (typeof(fileLocation) === 'undefined') { - ft.log('Export Aborted'); - return; + case "NewPipe": + exportNewpipeSubscriptions(dateYear, dateMonth, dateDay); + break; + case "OPML": + exportOpmlSubscriptions(dateYear, dateMonth, dateDay); + break; + default: + // Open user file browser. User gives location of file to be created. + dialog.showSaveDialog({ + defaultPath: dateString, + filters: [{ + name: 'Database File', + extensions: ['db'] + }, ] + }, function (fileLocation) { + ft.log(fileLocation); + if (typeof (fileLocation) === 'undefined') { + ft.log('Export Aborted'); + return; + } + fs.readFile(appDatabaseFile, function (readErr, data) { + if (readErr) { + throw readErr; } - fs.readFile(appDatabaseFile, function(readErr, data) { - if (readErr) { - throw readErr; + fs.writeFile(fileLocation, data, function (writeErr) { + if (writeErr) { + throw writeErr; } - fs.writeFile(fileLocation, data, function(writeErr) { - if (writeErr) { - throw writeErr; - } - showToast('Susbcriptions have been successfully exported'); - }); - }) - }); + showToast('Susbcriptions have been successfully exported'); + }); + }) + }); } } /** @@ -882,9 +991,9 @@ function exportNewpipeSubscriptions(dateYear, dateMonth, dateDay) { name: 'JSON', extensions: ['json'] }, ] - }, function(fileLocation) { + }, function (fileLocation) { ft.log(fileLocation); - if (typeof(fileLocation) === 'undefined') { + if (typeof (fileLocation) === 'undefined') { ft.log('Export Aborted'); return; } @@ -905,7 +1014,7 @@ function exportNewpipeSubscriptions(dateYear, dateMonth, dateDay) { newpipe.subscriptions.push(subs); } - fs.writeFile(fileLocation, JSON.stringify(newpipe), function(writeErr) { + fs.writeFile(fileLocation, JSON.stringify(newpipe), function (writeErr) { if (writeErr) { throw writeErr; } else { @@ -931,9 +1040,9 @@ function exportOpmlSubscriptions(dateYear, dateMonth, dateDay) { name: 'OPML', extensions: ['opml'] }, ] - }, function(fileLocation) { + }, function (fileLocation) { ft.log(fileLocation); - if (typeof(fileLocation) === 'undefined') { + if (typeof (fileLocation) === 'undefined') { ft.log('Export Aborted'); return; } @@ -952,7 +1061,7 @@ function exportOpmlSubscriptions(dateYear, dateMonth, dateDay) { opml += subs; } - fs.writeFile(fileLocation, opml, function(writeErr) { + fs.writeFile(fileLocation, opml, function (writeErr) { if (writeErr) { throw writeErr; } @@ -974,22 +1083,22 @@ function clearFile(type, showMessage = true) { let dataBaseFile; switch (type) { - case 'subscriptions': - dataBaseFile = localDataStorage + '/subscriptions.db'; - break; - case 'history': - dataBaseFile = localDataStorage + '/videohistory.db'; - break; - case 'saved': - dataBaseFile = localDataStorage + '/savedvideos.db'; - break; - default: - showToast('Unknown file: ' + type) - return + case 'subscriptions': + dataBaseFile = localDataStorage + '/subscriptions.db'; + break; + case 'history': + dataBaseFile = localDataStorage + '/videohistory.db'; + break; + case 'saved': + dataBaseFile = localDataStorage + '/savedvideos.db'; + break; + default: + showToast('Unknown file: ' + type) + return } // Replace data with an empty string. - fs.writeFile(dataBaseFile, '', function(err) { + fs.writeFile(dataBaseFile, '', function (err) { if (err) { throw err; } @@ -1001,11 +1110,11 @@ function clearFile(type, showMessage = true) { } function showSettingsConfirm() { - $('#confirmSettings').get(0).style.opacity = 0.9; + $('#confirmSettings').get(0).style.opacity = 0.9; } function hideSettingsConfirm() { - $('#confirmSettings').get(0).style.opacity = 0; + $('#confirmSettings').get(0).style.opacity = 0; } checkDefaultSettings(); diff --git a/src/js/subscriptions.js b/src/js/subscriptions.js index fa2d0b9c1..a91acb0dd 100644 --- a/src/js/subscriptions.js +++ b/src/js/subscriptions.js @@ -25,6 +25,7 @@ let subscriptionTimer; let forceTimer; let checkSubscriptions = true; let forceSubs = true; +let displaySubsLock = false; /** * Add a channel to the user's subscription database. @@ -33,15 +34,46 @@ let forceSubs = true; * * @return {Void} */ -function addSubscription(data, useToast = true) { +function addSubscription(data, useToast = true, profile = '') { ft.log('Channel Data: ', data); - // Refresh the list of subscriptions on the side navigation bar. - subDb.insert(data, (err, newDoc) => { - if (useToast) { - showToast('Added ' + data.channelName + ' to subscriptions.'); + let newSubscription = data; + newSubscription.profile = [{ + value: profile + }]; + + if (profile === '') { + newSubscription.profile = [{ + value: profileSelectView.activeProfile.name + }]; + } + + subDb.find({ + channelId: data.channelId + }, (err, docs) => { + if (jQuery.isEmptyObject(docs)) { + subDb.insert(newSubscription, (err, newDoc) => { + if (useToast) { + showToast('Added ' + newSubscription.channelName + ' to subscriptions.'); + } + // Refresh the list of subscriptions on the side navigation bar. + displaySubs(); + }); + } else { + subDb.update({ + channelId: data.channelId, + }, { + $push: { + profile: { + value: newSubscription.profile[0].value + } + } + }, (err, numAdded) => { + // Refresh the list of subscriptions on the side navigation bar. + displaySubs(); + showToast('Added ' + data.channelName + ' to subscriptions.'); + }); } - displaySubs(); }); } @@ -53,12 +85,32 @@ function addSubscription(data, useToast = true) { * @return {Void} */ function removeSubscription(channelId) { - subDb.remove({ + subDb.find({ channelId: channelId - }, {}, (err, numRemoved) => { - // Refresh the list of subscriptions on the side navigation bar. - displaySubs(); - showToast('Removed channel from subscriptions.'); + }, (err, docs) => { + if (docs[0].profile.length > 1) { + subDb.update({ + channelId: channelId, + }, { + $pull: { + profile: { + value: profileSelectView.activeProfile.name + } + } + }, (err, numAdded) => { + // Refresh the list of subscriptions on the side navigation bar. + displaySubs(); + showToast('Removed channel from subscriptions.'); + }); + } else { + subDb.remove({ + channelId: channelId + }, {}, (err, numRemoved) => { + // Refresh the list of subscriptions on the side navigation bar. + displaySubs(); + showToast('Removed channel from subscriptions.'); + }); + } }); } @@ -95,15 +147,17 @@ function loadSubscriptions() { invidiousAPI('channels/latest', channelId, {}, (data) => { data.forEach((video, index) => { - data[index].author = results[i]['channelName']; - historyDb.findOne({ videoId: video.videoId }, function (err, doc) { - if(doc === null) { - data[index].watched = false - } - else { - data[index].watched = true; - } - }); + data[index].author = results[i]['channelName']; + data[index].profile = results[i]['profile']; + historyDb.findOne({ + videoId: video.videoId + }, function (err, doc) { + if (doc === null) { + data[index].watched = false + } else { + data[index].watched = true; + } + }); }); videoList = videoList.concat(data); counter = counter + 1; @@ -139,11 +193,15 @@ function addSubsToView(videoList) { }); if (hideWatchedSubs) { - videoList = videoList.filter(a => { - return !a.watched; - }); + videoList = videoList.filter(a => { + return !a.watched; + }); } + videoList = videoList.filter(a => { + return a.profile.map(x => x.value).indexOf(profileSelectView.activeProfile.name) !== -1 + }); + videoList.sort((a, b) => { return b.published - a.published; }); @@ -209,12 +267,24 @@ function returnSubscriptions() { * @return {Void} */ function displaySubs() { + if (displaySubsLock) { + return; + } + + displaySubsLock = true; + const subList = document.getElementById('subscriptions'); subList.innerHTML = ''; // Sort alphabetically - subDb.find({}).sort({ + subDb.find({ + profile: { + $elemMatch: { + value: profileSelectView.activeProfile.name + } + } + }).sort({ channelName: 1 }).exec((err, subs) => { subs.forEach((channel) => { @@ -230,6 +300,8 @@ function displaySubs() { const subscriptionsHtml = $('#subscriptions').html(); $('#subscriptions').html(subscriptionsHtml + rendered); }); + + displaySubsLock = false; }); // Add onclick function @@ -268,10 +340,20 @@ function toggleSubscription(data) { * * @return {promise} - A boolean value if the channel is currently subscribed or not. */ -function isSubscribed(channelId) { +function isSubscribed(channelId, profile = profileSelectView.activeProfile.name) { return new Promise((resolve, reject) => { subDb.find({ - channelId: channelId + $and: [{ + channelId: channelId + }, + { + profile: { + $elemMatch: { + value: profile + } + } + }, + ] }, (err, docs) => { if (jQuery.isEmptyObject(docs)) { resolve(false); diff --git a/src/js/templates.js b/src/js/templates.js index 08039753d..5c25ff29c 100644 --- a/src/js/templates.js +++ b/src/js/templates.js @@ -23,837 +23,1456 @@ 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'); /* -* Progress view -* -* Shows progress bar on bottom of application. -* -* seen: Toggles visibility of view -* progressWidth: sets width of the progress bar -*/ + * 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 + el: '#progressView', + data: { + seen: true, + progressWidth: 0 + }, + template: progressViewTemplate }); let loadingView = new Vue({ - el: '#loading', - data: { - seen: false - } + el: '#loading', + data: { + seen: false + } }); let searchFilter = new Vue({ - el: '#searchFilter', - data: { - seen: false - } + el: '#searchFilter', + data: { + seen: false + } }); let noSubscriptions = new Vue({ - el: '#noSubscriptions', - data: { - seen: false - } + 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(); + el: '#sideNav', + data: { + distractionFreeMode: false }, - 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; + 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 + el: '#mainHeaderView', + data: { + seen: true, + title: 'Latest Subscriptions' + }, + template: mainHeaderTemplate }); let subscriptionView = new Vue({ - el: '#subscriptionView', - data: { - seen: true, - isSearch: false, - videoList: [], - fullVideoList: [], - }, - methods: { - play: (videoId) => { - loadingView.seen = true; - playVideo(videoId); + el: '#subscriptionView', + data: { + seen: true, + isSearch: false, + videoList: [], + fullVideoList: [], }, - channel: (channelId) => { - goToChannel(channelId); + 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'); + }, + 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); + } }, - 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'); - }, - 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 + template: videoListTemplate }); let popularView = new Vue({ - el: '#popularView', - data: { - seen: false, - isSearch: false, - videoList: [] - }, - methods: { - play: (videoId) => { - loadingView.seen = true; - playVideo(videoId); + el: '#popularView', + data: { + seen: false, + isSearch: false, + videoList: [] }, - channel: (channelId) => { - goToChannel(channelId); + 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'); + }, + 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); + } }, - 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'); - }, - 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 + template: videoListTemplate }); let trendingView = new Vue({ - el: '#trendingView', - data: { - seen: false, - isSearch: false, - videoList: [] - }, - methods: { - play: (videoId) => { - loadingView.seen = true; - playVideo(videoId); + el: '#trendingView', + data: { + seen: false, + isSearch: false, + videoList: [] }, - channel: (channelId) => { - goToChannel(channelId); + 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'); + }, + 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); + } }, - 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'); - }, - 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 + template: videoListTemplate }); let savedView = new Vue({ - el: '#savedView', - data: { - seen: false, - isSearch: false, - videoList: [] - }, - methods: { - play: (videoId) => { - loadingView.seen = true; - playVideo(videoId); + el: '#savedView', + data: { + seen: false, + isSearch: false, + videoList: [] }, - channel: (channelId) => { - goToChannel(channelId); + 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'); + }, + 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); + } }, - 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'); - }, - 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 + template: videoListTemplate }); let historyView = new Vue({ - el: '#historyView', - data: { - seen: false, - isSearch: false, - videoList: [] - }, - methods: { - play: (videoId) => { - loadingView.seen = true; - playVideo(videoId); + el: '#historyView', + data: { + seen: false, + isSearch: false, + videoList: [] }, - channel: (channelId) => { - goToChannel(channelId); + 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'); + }, + 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); + } }, - 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'); - }, - 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 + 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); + 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 + }, + channel: (channelId) => { + goToChannel(channelId); - backButtonView.lastView = playlistView + backButtonView.lastView = playlistView + }, + 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'); + }, + copyInvidious: (videoId) => { + const url = invidiousInstance + '/watch?v=' + videoId; + clipboard.writeText(url); + showToast('URL has been copied to the clipboard'); + }, + history: (videoId) => { + removeFromHistory(videoId); + } }, - 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'); - }, - copyInvidious: (videoId) => { - const url = invidiousInstance + '/watch?v=' + videoId; - clipboard.writeText(url); - showToast('URL has been copied to the clipboard'); - }, - history: (videoId) => { - removeFromHistory(videoId); - } - }, - template: playlistViewTemplate + template: playlistViewTemplate }); let aboutView = new Vue({ - el: '#aboutView', - data: { - seen: false, - rssFeed: [], - versionNumber: electron.remote.app.getVersion() - }, - template: aboutTemplate + 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, - debugMode: false, - distractionFreeMode: false, - defaultVolume: 1, - defaultVideoSpeed: 1, - subWatched: false, - videoView: 'grid', - }, - methods: { - checkProxy() { - this.checkProxyResult = false; - this.proxyTestLoading = true; - let data = { - proxyAddress: this.proxyAddress, - }; - electron.ipcRenderer.send("setProxy", data); + 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', + }, + 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 => { - console.log(response); - this.checkProxyResult = response; - }) - .fail((xhr, textStatus, error) => { - console.log(xhr); - console.log(textStatus); - showToast('Proxy test failed'); - }).always(() =>{ - this.proxyTestLoading = false; - if (!useTor) { - electron.ipcRenderer.send("setProxy", {}); - } - }); - }) + proxyRequest(() => { + $.ajax({ + url: "https://ipinfo.io/json", + dataType: 'json', + }).done(response => { + console.log(response); + this.checkProxyResult = response; + }) + .fail((xhr, textStatus, error) => { + console.log(xhr); + console.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; + }, }, - 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); + } }, - }, - computed: { - proxyTestButtonText() { - return this.proxyTestLoading ? "LOADING..." : "TEST PROXY" - }, - volumeHtml() { - return Math.round(this.defaultVolume * 100); - } - }, - template: settingsTemplate + template: settingsTemplate }); let searchView = new Vue({ - el: '#searchView', - data: { - seen: false, - isSearch: true, - page: 1, - videoList: [] - }, - methods: { - play: (videoId) => { - loadingView.seen = true; - playVideo(videoId); + 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 + }, + 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'); - }, - 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 + }, + 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'); + }, + 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 + backButtonView.lastView = searchView + }, + miniPlayer: (videoId) => { + clickMiniPlayer(videoId); + } }, - miniPlayer: (videoId) => { - clickMiniPlayer(videoId); - } - }, - template: videoListTemplate + template: videoListTemplate }); let channelView = new Vue({ - el: '#channelView', - data: { - seen: false, - id: '', - name: '', - icon: '', - baner: '', - subCount: '', - subButtonText: '', - description: '', - distractionFreeMode: false - }, - methods: { - subscription: (channelId) => { - let channelData = { - channelId: channelView.id, - channelName: channelView.name, - channelThumbnail: channelView.icon - }; - toggleSubscription(channelData); + el: '#channelView', + data: { + seen: false, + id: '', + name: '', + icon: '', + baner: '', + subCount: '', + subButtonText: '', + description: '', + distractionFreeMode: false }, - }, - template: channelTemplate + methods: { + subscription: (channelId) => { + let channelData = { + channelId: channelView.id, + channelName: channelView.name, + channelThumbnail: channelView.icon + }; + toggleSubscription(channelData); + }, + }, + template: channelTemplate }); let channelVideosView = new Vue({ - el: '#channelVideosView', - data: { - seen: false, - channelId: '', - isSearch: true, - page: 2, - videoList: [] - }, - methods: { - play: (videoId) => { - loadingView.seen = true; - playVideo(videoId); + el: '#channelVideosView', + data: { + seen: false, + channelId: '', + isSearch: true, + page: 2, + videoList: [] }, - channel: (channelId) => { - goToChannel(channelId); + 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'); + }, + 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); + } }, - 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'); - }, - 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 + 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 - }, - methods: { - channel: (channelId) => { - goToChannel(channelId); + 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 }, - 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; - } + 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; + checkedVideoSettings = false; - playerView.playerSeen = false; - playerView.legacySeen = true; - }, - dashFormats: () => { - if (typeof($('#legacyPlayer').get(0)) !== 'undefined') { - playerView.currentTime = $('#legacyPlayer').get(0).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; - }, - copyYouTube: (videoId) => { - const url = 'https://youtube.com/watch?v=' + videoId; - clipboard.writeText(url); - showToast('URL has been copied to the clipboard'); - }, - openYouTube: (videoId) => { - shell.openExternal('https://youtube.com/watch?v=' + videoId); - }, - copyInvidious: (videoId) => { - const url = invidiousInstance + '/watch?v=' + videoId; - clipboard.writeText(url); - showToast('URL has been copied to the clipboard'); - }, - openInvidious: (videoId) => { - shell.openExternal(invidiousInstance + '/watch?v=' + videoId); - }, - save: (videoId) => { - toggleSavedVideo(videoId); - }, - play: (videoId, playlistId = '') => { - loadingView.seen = true; - playVideo(videoId, playlistId); - }, - loop: () => { - let legacyPlayer = $('.videoPlayer').get(0); + playerView.legacySeen = false; + playerView.playerSeen = true; + }, + copyYouTube: (videoId) => { + const url = 'https://youtube.com/watch?v=' + videoId; + clipboard.writeText(url); + showToast('URL has been copied to the clipboard'); + }, + openYouTube: (videoId) => { + shell.openExternal('https://youtube.com/watch?v=' + videoId); + }, + copyInvidious: (videoId) => { + const url = invidiousInstance + '/watch?v=' + videoId; + clipboard.writeText(url); + showToast('URL has been copied to the clipboard'); + }, + openInvidious: (videoId) => { + shell.openExternal(invidiousInstance + '/watch?v=' + videoId); + }, + 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.') - } + 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; + } + }, }, - playlist: (playlistId) => { - showPlaylist(playlistId); + 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'; + } }, - 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 + 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"; + el: '#backButton', + data: { + lastView: false + }, + methods: { + back: function () { + // variable here because this.lastView gets reset in hideViews() + const isSearch = this.lastView.$options.el === "#searchView"; - hideViews(); - loadingView.seen = false; + 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; + // 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 not search then this.lastView has to be playlistView + // reset this.lastView + this.lastView = false; + } else { + // if not search then this.lastView has to be playlistView - // Change back to playlistView - playlistView.seen = true; + // 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 or playlistView - return !!this.lastView && !this.lastView.seen && this.lastView.videoList.length > 0; - } - }, + // 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 or playlistView + return !!this.lastView && !this.lastView.seen && this.lastView.videoList.length > 0; + } + }, +}); + +let profileSelectView = new Vue({ + el: '#profileSelectView', + data: { + seen: false, + activeProfile: [], + activeProfileInitial: '', + activeProfileInitialColor: '#000000', + profileList: [], + }, + methods: { + showSubscriptionManager: function () { + hideViews(); + subscriptionManagerView.seen = true; + }, + setActiveProfile: function (index) { + 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 = ''; + editProfileView.newProfileColor = ''; + editProfileView.subscriptionList = []; + } else { + editProfileView.profileName = this.profileList[index].name; + editProfileView.profileColor = this.profileList[index].color; + editProfileView.newProfileName = this.profileList[index].name; + editProfileView.newProfileColor = this.profileList[index].color; + // 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; + //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: '', + newProfileColor: '', + 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.newProfileColor = value; + }, + selectAll: function () { + this.subscriptionList.forEach(channel => { + channel.checked = true; + }); + }, + selectNone: function () { + this.subscriptionList.forEach((channel) => { + channel.checked = false; + }); + }, + defaultProfile: function () { + 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) { + console.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) { + 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) => { + console.log(numRemoved); + }); + } + + subDb.update({ + profile: { + $elemMatch: { + value: editProfileView.profileName + } + }, + }, { + $push: { + profile: { + value: editProfileView.newProfileName + } + } + }, { + multi: true + }, (err, numRemoved) => { + if (err) { + console.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) { + console.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; + } + let confirmString = 'Would you like to move the selected channels 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) { + console.log(err); + } + }); + + channel.profile = this.selectedProfile; + amountRemoved++; + + subDb.update({ + channelId: channel.channelId, + }, { + $pull: { + profile: { + value: this.profileName + } + } + }, (err, numRemoved) => { + if (err) { + console.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; + } + let confirmString = 'Would you like to copy the selected channels 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) { + console.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 channels from this profile?'; + let amountDeleted = 0; + + confirmFunction(confirmString, () => { + this.subscriptionList.forEach((channel, index) => { + if (channel.checked) { + subDb.update({ + channelId: channel.channelId, + }, { + $pull: { + profile: { + value: editProfileView.profileName + } + } + }, { + multi: true + }, (err, numRemoved) => { + if (err) { + console.log(err); + } + + let subMap = subscriptionView.fullVideoList.map(x => x.author === channel.channelName); + + 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); + } + } + + editProfileView.subscriptionList.splice(index, 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.'); + }, 500); + }); + }, + }, + computed: { + 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.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; + 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; + } } - 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; - settingsView.seen = false; - popularView.seen = false; - trendingView.seen = false; - savedView.seen = false; - historyView.seen = false; - playlistView.seen = false; - playerView.seen = false; - channelView.seen = false; - channelVideosView.seen = false; - backButtonView.lastView = false; + subscriptionView.seen = false; + noSubscriptions.seen = false; + aboutView.seen = false; + headerView.seen = false; + searchView.seen = false; + settingsView.seen = false; + popularView.seen = false; + trendingView.seen = false; + savedView.seen = false; + historyView.seen = false; + playlistView.seen = false; + playerView.seen = false; + channelView.seen = false; + channelVideosView.seen = false; + profileSelectView.seen = false; + subscriptionManagerView.seen = false; + editProfileView.seen = false; + backButtonView.lastView = false; } diff --git a/src/style/darkTheme.css b/src/style/darkTheme.css index 29af5a291..8b64c21a5 100644 --- a/src/style/darkTheme.css +++ b/src/style/darkTheme.css @@ -211,6 +211,22 @@ input[type=text] { color: white; } +.profile:hover { + background-color: #616161; +} + +.profileEditSelected { + background-color: #424242; +} + +.profileSelectSettings { + background-color: #9E9E9E; +} + +.profileEditPadding:hover { + background-color: #616161; +} + #main { color: #EEEEEE; } @@ -291,3 +307,12 @@ input[type=text] { #miniPL { background-color: #424242; } + +#profileSelect { + background-color: #424242; + color: white; +} + +#profileSelectHeader span:hover { + background-color: #616161; +} diff --git a/src/style/lightTheme.css b/src/style/lightTheme.css index c5d764f08..dbaf9a711 100644 --- a/src/style/lightTheme.css +++ b/src/style/lightTheme.css @@ -180,6 +180,22 @@ body { color: black; } +.profile:hover { + background-color: #e0e0e0; +} + +.profileEditSelected { + background-color: #424242; +} + +.profileSelectSettings { + background-color: #9E9E9E; +} + +.profileEditPadding:hover { + background-color: #e0e0e0; +} + #subscriptions img { border: 0px solid #000000; } @@ -229,3 +245,11 @@ body { #miniPL { background-color: white; } + +#profileSelect { + background-color: white; +} + +#profileSelectHeader span:hover { + background-color: #e0e0e0; +} diff --git a/src/style/main.css b/src/style/main.css index 89b6a5f51..aeaaf2a37 100644 --- a/src/style/main.css +++ b/src/style/main.css @@ -43,20 +43,20 @@ a { } .link { - color: #2196F3; - cursor: pointer; - font-weight: bold; - font-decoration-line: 'underline'; + color: #2196F3; + cursor: pointer; + font-weight: bold; + font-decoration-line: 'underline'; } -#progressBar{ - height: 3px; - background-color: #f44336; - display: block; - position: fixed; - bottom: 0; - left: 0; - z-index: 1; +#progressBar { + height: 3px; + background-color: #f44336; + display: block; + position: fixed; + bottom: 0; + left: 0; + z-index: 1; } .center { @@ -86,7 +86,7 @@ a { font-size: 20px; margin-left: 20px; position: relative; - top: 3px; + bottom: 10px; } #reloadButton { @@ -101,30 +101,230 @@ a { font-size: 20px; margin-left: 20px; position: relative; - top: 3px; + bottom: 10px; } #menuIcon { width: 25px; position: relative; - top: 15px; - right: 120px; - float: right; + left: 20px; + bottom: 5px; } #menuText { width: 100px; position: relative; - top: 5px; - left: 10px; + top: 7px; + left: 15px; +} + +#currentProfile { + width: 40px; + height: 40px; + position: absolute; + right: 10px; + top: 10px; + cursor: pointer; + border-radius: 200px 200px 200px 200px; + -webkit-border-radius: 200px 200px 200px 200px; +} + +#currentInitial { + font-size: 22px; + text-align: center; + position: relative; + left: 13px; + bottom: 10px; +} + +#profileSelect { + width: 275px; + position: absolute; + right: 30px; + top: 50px; + -webkit-box-shadow: -15px 25px 50px -30px rgba(0, 0, 0, 0.75); + -moz-box-shadow: -15px 25px 50px -30px rgba(0, 0, 0, 0.75); + box-shadow: -15px 25px 50px -30px rgba(0, 0, 0, 0.75); +} + +#profileSelectHeader { + height: 55px; + margin-top: 0px; + margin-bottom: 0px; + line-height: 55px; + padding-left: 10px; +} + +#profileSelectHeader span { float: right; + width: 40px; + height: 40px; + margin-right: 10px; + margin-top: 5px; + cursor: pointer; + border-radius: 200px 200px 200px 200px; + -webkit-border-radius: 200px 200px 200px 200px; + -webkit-transition: background 0.2s ease-out; + -moz-transition: background 0.2s ease-out; + -o-transition: background 0.2s ease-out; + transition: background 0.2s ease-out; +} + +#profileSelectHeader span:hover { + -moz-transition: background 0.2s ease-in; + -o-transition: background 0.2s ease-in; + transition: background 0.2s ease-in; +} + +#profileSelectHeader i { + font-size: 15px; + position: relative; + left: 11px; + top: 8px; +} + +#profileSelectList { + overflow-y: auto; + overflow-x: hidden; + max-height: 400px; +} + +.profile { + width: 265px; + height: 30px; + padding-top: 10px; + padding-left: 10px; + padding-bottom: 10px; + cursor: pointer; + -webkit-transition: background 0.2s ease-out; + -moz-transition: background 0.2s ease-out; + -o-transition: background 0.2s ease-out; + transition: background 0.2s ease-out; +} + +.profile:hover { + -moz-transition: background 0.2s ease-in; + -o-transition: background 0.2s ease-in; + transition: background 0.2s ease-in; +} + +.profileBackground { + display: inline-block; + width: 30px; + height: 30px; + float: left; + border-radius: 200px 200px 200px 200px; + -webkit-border-radius: 200px 200px 200px 200px; +} + +.profileEditSelected { + display: inline-block; + width: 50px; + height: 50px; + float: left; + position: absolute; + margin-left: -50px; + opacity: 0.8; + border-radius: 200px 200px 200px 200px; + -webkit-border-radius: 200px 200px 200px 200px; +} + +.profileEditSelected i { + font-size: 22px; + position: relative; + top: 16px; +} + +.profileInitial { + font-size: 15px; + display: inline-block; + position: relative; + left: 10px; + bottom: 16px; +} + +.profileName { + display: inline-block; + position: relative; + bottom: 30px; + left: 10px; +} + +.profileSelectSettings { + margin: 0 auto; + line-height: 55px; +} + +.profileSelectSettings i { + position: relative; + left: 15px; + bottom: 2px; +} + +.colorPicker { + display: inline-block; + width: 80px; + height: 80px; + border-radius: 200px 200px 200px 200px; + -webkit-border-radius: 200px 200px 200px 200px; +} + +.colorPicker span { + position: relative; + top: 15px; +} + +.colorPickerPadding { + display: inline-block; + width: 80px; + height: 80px; + padding: 10px; +} + +.profileEdit { + display: inline-block; + width: 50px; + height: 50px; + margin-bottom: 5px; + border-radius: 200px 200px 200px 200px; + -webkit-border-radius: 200px 200px 200px 200px; +} + +.profileEditInitial { + font-size: 25px; + position: relative; + top: 11px; +} + +.profileEditName { + font-size: 13px; + height: 60px; + overflow: hidden; +} + +.profileEditPadding { + display: inline-block; + width: 100px; + height: 115px; + padding: 10px; + cursor: pointer; + -webkit-transition: background 0.2s ease-out; + -moz-transition: background 0.2s ease-out; + -o-transition: background 0.2s ease-out; + transition: background 0.2s ease-out; +} + +.profileEditPadding:hover { + -moz-transition: background 0.2s ease-in; + -o-transition: background 0.2s ease-in; + transition: background 0.2s ease-in; } .searchBar { position: absolute; - right: 30px; + right: -75px; top: 0; - width: 800px; + width: 750px; } .searchBar input { @@ -133,10 +333,10 @@ a { } #searchFilter { - width: 90%; - padding: 10px; - margin-bottom: 20px; - -webkit-box-shadow: 4px -2px 51px -6px rgba(0, 0, 0, 0.75); + width: 90%; + padding: 10px; + margin-bottom: 20px; + -webkit-box-shadow: 4px -2px 51px -6px rgba(0, 0, 0, 0.75); } .searchButton { @@ -145,18 +345,18 @@ a { } .filterButton { - text-decoration: none; - margin-right: 10px; - cursor: pointer; + text-decoration: none; + margin-right: 10px; + cursor: pointer; } .filter { - width: 180px; - height: 295px; - padding: 10px; - border-right: 1px solid #f44336; - display: inline-block; - text-align: left; + width: 180px; + height: 295px; + padding: 10px; + border-right: 1px solid #f44336; + display: inline-block; + text-align: left; } #sideNav { @@ -241,14 +441,14 @@ a { } #loading { - width: 75%; - height: 20%; - margin: auto; - position: absolute; - top: 0; - left: 5; - bottom: 0; - right: 0; + width: 75%; + height: 20%; + margin: auto; + position: absolute; + top: 0; + left: 5; + bottom: 0; + right: 0; } .settingsInput { @@ -257,27 +457,27 @@ a { } .settingsSlider { - padding: 30px; + padding: 30px; } #confirmSettings { - position: fixed; - margin-left: 250px; - margin-right: auto; - left: 4%; - right: 4%; - bottom: 20px; - height: 50px; - background-color: #212121; - color: #f5f5f5; - padding: 10px; - opacity: 0; - z-index: 1; - -webkit-box-shadow: 4px -2px 51px -6px rgba(0, 0, 0, 0.75); - -webkit-transition: opacity 0.4s ease-in-out; - -moz-transition: opacity 0.4s ease-in-out; - -ms-transition: opacity 0.4s ease-in-out; - -o-transition: opacity 0.4s ease-in-out; + position: fixed; + margin-left: 250px; + margin-right: auto; + left: 4%; + right: 4%; + bottom: 20px; + height: 50px; + background-color: #212121; + color: #f5f5f5; + padding: 10px; + opacity: 0; + z-index: 1; + -webkit-box-shadow: 4px -2px 51px -6px rgba(0, 0, 0, 0.75); + -webkit-transition: opacity 0.4s ease-in-out; + -moz-transition: opacity 0.4s ease-in-out; + -ms-transition: opacity 0.4s ease-in-out; + -o-transition: opacity 0.4s ease-in-out; } .input-text-settings input { @@ -315,14 +515,14 @@ a { } .card { - margin: 0 auto; - width: 90%; - text-align: center; - padding: 10px; - margin-bottom: 30px; - -webkit-box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75); - -moz-box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75); - box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75); + margin: 0 auto; + width: 90%; + text-align: center; + padding: 10px; + margin-bottom: 30px; + -webkit-box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75); + -moz-box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75); + box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75); } .help { @@ -413,15 +613,15 @@ a { } .blogFeed a { - font-size: 20px; - font-weight: bold; + font-size: 20px; + font-weight: bold; } @media only screen and (max-width: 900px) { - .searchBar { - width: 500px; - right: 80px; - } + .searchBar { + width: 500px; + right: -15px; + } } /* Thanks to Guus Lieben for the Material Design Switch */ diff --git a/src/style/player.css b/src/style/player.css index a695c0307..211054c4d 100644 --- a/src/style/player.css +++ b/src/style/player.css @@ -28,14 +28,15 @@ iframe { .videoPlayer { width: 100%; height: 100%; + touch-action: none; } #player { - min-width: 100%; + min-width: 100%; } #legacyPlayer { - max-height: 80vh; + max-height: 80vh; } .statistics { diff --git a/src/templates/currentProfileView.html b/src/templates/currentProfileView.html new file mode 100644 index 000000000..6846de9bb --- /dev/null +++ b/src/templates/currentProfileView.html @@ -0,0 +1,5 @@ +
+
+ {{activeProfileInitial}} +
+
diff --git a/src/templates/editProfileView.html b/src/templates/editProfileView.html new file mode 100644 index 000000000..758773e50 --- /dev/null +++ b/src/templates/editProfileView.html @@ -0,0 +1,75 @@ +
+
+

Subscription List for {{profileName}} Profile

+

{{amountSelected}} Selected

+
+
+
+
+
+
{{channel.channelName}}
+
+


+
+ + + + +
+
+
+ SELECT ALL +
+
+ SELECT NONE +
+
+ MOVE SELECTED +
+
+ COPY SELECTED +
+
+ DELETE SELECTED +
+
+
+
+

Add New Profile

+

Edit Profile Attributes

+
+ +
+

Color Picker

+
+
+
+
+
+ +
+
+

New Profile Color

+
+
+
+
+
+ ADD PROFILE +
+
+
+ UPDATE PROFILE +
+
+ MAKE DEFAULT PROFILE +
+
+ DELETE PROFILE +
+
+
+
+
diff --git a/src/templates/profileSelectView.html b/src/templates/profileSelectView.html new file mode 100644 index 000000000..ae6262df1 --- /dev/null +++ b/src/templates/profileSelectView.html @@ -0,0 +1,11 @@ +
+

Profile Select

+
+
+
+ {{profileInitials[index]}} +
+

{{profile.name}}

+
+
+
diff --git a/src/templates/settings.html b/src/templates/settings.html index 4cf5b8763..eb793cb8d 100644 --- a/src/templates/settings.html +++ b/src/templates/settings.html @@ -349,6 +349,9 @@
+
+ MANAGE SUBSCRIPTIONS +
IMPORT SUBSCRIPTIONS
@@ -397,13 +400,13 @@

-
+
CLEAR HISTORY
-
+
CLEAR FAVORITED VIDEOS
-
+
CLEAR SUBSCRIPTIONS
diff --git a/src/templates/subscriptionManagerView.html b/src/templates/subscriptionManagerView.html new file mode 100644 index 000000000..e95c40b07 --- /dev/null +++ b/src/templates/subscriptionManagerView.html @@ -0,0 +1,18 @@ +
+
+

Subscription / Profile Manager

+
+
+
+ {{profileInitials[index]}} +
+
{{profile.name}}
+
+
+
+ +
+
Add New Profile
+
+
+