Implement Local Profiles

This commit is contained in:
PrestonN 2019-07-26 15:07:33 -04:00
parent da2a4c9076
commit 25f9ec57d1
16 changed files with 2563 additions and 1376 deletions

View File

@ -2,165 +2,168 @@
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style/main.css">
<link rel="stylesheet" href="style/lightTheme.css">
<link rel="stylesheet" href="style/videoGrid.css">
<link rel="stylesheet" href="style/player.css">
<link rel="stylesheet" href="style/playerFix.css">
<link rel="stylesheet" href="style/channel.css">
<link rel="stylesheet" href="style/playlist.css">
<link rel="stylesheet" href="style/loading.css">
<link rel="stylesheet" href="style/select.css">
<link rel="stylesheet" href="style/fa-solid.min.css">
<link rel="stylesheet" href="style/fontawesome-all.min.css">
<link rel="stylesheet" href="style/mediaelementplayer.css" />
<link rel="stylesheet" href="style/radioButton.css" />
<link rel="stylesheet" href="style/mdSlider.css" />
<link rel="stylesheet" href="js/plugins/title/title.css" />
<link rel="stylesheet" href="js/plugins/quality/quality.min.css" />
<link rel="stylesheet" href="js/plugins/loop/loop.css" />
<link rel="stylesheet" href="js/plugins/speed/speed.css" />
<link rel="stylesheet" href="js/plugins/context-menu/context-menu.css" />
<link rel="shortcut icon" href="icons/iconColor.ico" type="image/x-icon" />
<title>FreeTube Player</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="style/main.css">
<link rel="stylesheet" href="style/lightTheme.css">
<link rel="stylesheet" href="style/videoGrid.css">
<link rel="stylesheet" href="style/player.css">
<link rel="stylesheet" href="style/playerFix.css">
<link rel="stylesheet" href="style/channel.css">
<link rel="stylesheet" href="style/playlist.css">
<link rel="stylesheet" href="style/loading.css">
<link rel="stylesheet" href="style/select.css">
<link rel="stylesheet" href="style/fa-solid.min.css">
<link rel="stylesheet" href="style/fontawesome-all.min.css">
<link rel="stylesheet" href="style/mediaelementplayer.css" />
<link rel="stylesheet" href="style/radioButton.css" />
<link rel="stylesheet" href="style/mdSlider.css" />
<link rel="stylesheet" href="js/plugins/title/title.css" />
<link rel="stylesheet" href="js/plugins/quality/quality.min.css" />
<link rel="stylesheet" href="js/plugins/loop/loop.css" />
<link rel="stylesheet" href="js/plugins/speed/speed.css" />
<link rel="stylesheet" href="js/plugins/context-menu/context-menu.css" />
<link rel="shortcut icon" href="icons/iconColor.ico" type="image/x-icon" />
<title>FreeTube Player</title>
</head>
<body>
<div v-if='seen' id='loading'>
<div class="spinner">
<div class="double-bounce1"></div>
<div class="double-bounce2"></div>
<div v-if='seen' id='loading'>
<div class="spinner">
<div class="double-bounce1"></div>
<div class="double-bounce2"></div>
</div>
</div>
</div>
<div id='confirmFunction'>
<span id='confirmMessage'>Would you like to perform the function?</span>
<div class='confirmButton' id='confirmYes'>Yes</div>
<div class='confirmButton' id='confirmNo'>No</div>
</div>
<div id='toast'>
<span id='toastMessage'></span>
<i onclick='hideToast()' class="closeToast fas fa-times"></i>
</div>
<div id="topNav" class="topNav">
<i onclick='toggleSideNavigation()' class="fas fa-bars" id='menuButton'></i>
<!--<i onclick='forceSubscriptions()' class="fas fa-sync" id='reloadButton' title='Force Subscription Reload'></i>-->
<i v-on:click='back' v-if="canShowBackButton" class="fas fa-arrow-left" id="backButton"></i>
<div class="searchBar">
<input id='search' class="search" type="text" placeholder="Search / Go to URL">
<span class='searchButton' onclick='parseSearchText()'><i class="fas fa-arrow-right" style='position: relative; right: 30px; cursor: pointer' title='Search'></i></span>
<span class='filterButton' onclick='searchFilter.seen = !searchFilter.seen'><i class="fas fa-filter" style='margin-right: -10px; cursor: pointer' title='Filter'></i></span>
<div id='confirmFunction'>
<span id='confirmMessage'>Would you like to perform the function?</span>
<div class='confirmButton' id='confirmYes'>Yes</div>
<div class='confirmButton' id='confirmNo'>No</div>
</div>
<img src='icons/iconBlackSmall.png' id='menuIcon' /> &nbsp;
<img src='icons/textBlackSmall.png' id='menuText' />
</div>
<div id='sideNavDisabled'></div>
<div id="sideNav">
<div class="sideNavContainer">
<ul>
<li v-on:click='subscriptions'><i class="fas fa-rss"></i>&nbsp;&nbsp;Subscriptions <i onclick='event.stopPropagation(); forceSubscriptions()' class="fas fa-sync" id='reloadButton' title='Force Subscription Reload'></i></li>
<li v-if='!distractionFreeMode' v-on:click='trending'><i class="fas fa-fire"></i>&nbsp;&nbsp;Trending</li>
<li v-if='!distractionFreeMode' v-on:click='popular'><i class="fas fa-users"></i>&nbsp;&nbsp;Most Popular</li>
<li v-on:click='saved'><i class="fas fa-star"></i>&nbsp;&nbsp;Favorites</li>
<li v-on:click='history'><i class="fas fa-history"></i>&nbsp;&nbsp;History</li>
</ul>
<hr />
<ul>
<li v-on:click='settings'><i class="fas fa-sliders-h"></i>&nbsp;&nbsp;Settings</li>
<li v-on:click='about'><i class="fas fa-info-circle"></i>&nbsp;&nbsp;About</li>
</ul>
<hr />
<ul id='subscriptions'>
</ul>
<div id='toast'>
<span id='toastMessage'></span>
<i onclick='hideToast()' class="closeToast fas fa-times"></i>
</div>
</div>
<div id="main">
<div id='searchFilter' class='center' v-show='seen'>
<h2>Search Filters</h2>
<br />
<div style='position: relative; bottom: 80px;' class='pure-radiobutton filter'>
<h3 class='filterTitle'>Sort By</h3>
<input class='radio' id='searchSortBy1' type="radio" name="searchSortBy" value="relevance" checked='checked' />
<label for='searchSortBy1'>Most Relevant</label>
<br /><br />
<input class='radio' id='searchSortBy2' type="radio" name="searchSortBy" value="rating" />
<label for='searchSortBy2'>Rating</label>
<br /><br />
<input class='radio' id='searchSortBy3' type="radio" name="searchSortBy" value="upload_date" />
<label for='searchSortBy3'>Upload Date</label>
<br /><br />
<input class='radio' id='searchSortBy4' type="radio" name="searchSortBy" value="view_count" />
<label for='searchSortBy4'>View Count</label>
</div>
<div class='pure-radiobutton filter'>
<h3>Time</h3>
<input class='radio' id='searchDate1' type="radio" name="searchDate" value="" checked='checked' />
<label for='searchDate1'>Any Time</label>
<br /><br />
<input class='radio' id='searchDate2' type="radio" name="searchDate" value="hour" />
<label for='searchDate2'>Last Hour</label>
<br /><br />
<input class='radio' id='searchDate3' type="radio" name="searchDate" value="today" />
<label for='searchDate3'>Today</label>
<br /><br />
<input class='radio' id='searchDate4' type="radio" name="searchDate" value="week" />
<label for='searchDate4'>This Week</label>
<br /><br />
<input class='radio' id='searchDate5' type="radio" name="searchDate" value="month" />
<label for='searchDate5'>This Month</label>
<br /><br />
<input class='radio' id='searchDate6' type="radio" name="searchDate" value="year" />
<label for='searchDate6'>This Year</label>
</div>
<div style='position: relative; bottom: 80px;' class='pure-radiobutton filter'>
<h3 class='filterTitle'>Type</h3>
<input class='radio' id='searchType1' type="radio" name="searchType" value="all" checked='checked' />
<label for='searchType1'>All Types</label>
<br /><br />
<input class='radio' id='searchType2' type="radio" name="searchType" value="video" />
<label for='searchType2'>Videos</label>
<br /><br />
<input class='radio' id='searchType3' type="radio" name="searchType" value="channel" />
<label for='searchType3'>Channels</label>
<br /><br />
<input class='radio' id='searchType4' type="radio" name="searchType" value="playlist" />
<label for='searchType4'>Playlists</label>
</div>
<div style='position: relative; bottom: 120px;' class='pure-radiobutton filter'>
<h3 class='filterTitle'>Duration</h3>
<input class='radio' id='searchDuration1' type="radio" name="searchDuration" value="" checked='checked' />
<label for='searchDuration1'>All Durations</label>
<br /><br />
<input class='radio' id='searchDuration2' type="radio" name="searchDuration" value="short" />
<label for='searchDuration2'>Short (< 4 minutes)</label>
<br /><br />
<input class='radio' id='searchDuration3' type="radio" name="searchDuration" value="long" />
<label for='searchDuration3'>Long (> 20 minutes)</label>
</div>
<div id="topNav" class="topNav">
<i onclick='toggleSideNavigation()' class="fas fa-bars" id='menuButton'></i>
<i v-on:click='back' v-if="canShowBackButton" class="fas fa-arrow-left" id="backButton"></i>
<img src='icons/iconBlackSmall.png' id='menuIcon' /> &nbsp;
<img src='icons/textBlackSmall.png' id='menuText' />
<div class="searchBar">
<input id='search' class="search" type="text" placeholder="Search / Go to URL">
<span class='searchButton' onclick='parseSearchText()'><i class="fas fa-arrow-right" style='position: relative; right: 30px; cursor: pointer' title='Search'></i></span>
<span class='filterButton' onclick='searchFilter.seen = !searchFilter.seen'><i class="fas fa-filter" style='margin-right: -10px; cursor: pointer' title='Filter'></i></span>
</div>
<div id='currentProfileView'></div>
<div id='profileSelectView'></div>
</div>
<div id='noSubscriptions' v-if='seen'>
<h2 class="message">
<div id='sideNavDisabled'></div>
<div id="sideNav">
<div class="sideNavContainer">
<ul>
<li v-on:click='subscriptions'><i class="fas fa-rss"></i>&nbsp;&nbsp;Subscriptions <i onclick='event.stopPropagation(); forceSubscriptions()' class="fas fa-sync" id='reloadButton' title='Force Subscription Reload'></i></li>
<li v-if='!distractionFreeMode' v-on:click='trending'><i class="fas fa-fire"></i>&nbsp;&nbsp;Trending</li>
<li v-if='!distractionFreeMode' v-on:click='popular'><i class="fas fa-users"></i>&nbsp;&nbsp;Most Popular</li>
<li v-on:click='saved'><i class="fas fa-star"></i>&nbsp;&nbsp;Favorites</li>
<li v-on:click='history'><i class="fas fa-history"></i>&nbsp;&nbsp;History</li>
</ul>
<hr />
<ul>
<li v-on:click='settings'><i class="fas fa-sliders-h"></i>&nbsp;&nbsp;Settings</li>
<li v-on:click='about'><i class="fas fa-info-circle"></i>&nbsp;&nbsp;About</li>
</ul>
<hr />
<ul id='subscriptions'>
</ul>
</div>
</div>
<div id="main">
<div id='searchFilter' class='center' v-show='seen'>
<h2>Search Filters</h2>
<br />
<div style='position: relative; bottom: 80px;' class='pure-radiobutton filter'>
<h3 class='filterTitle'>Sort By</h3>
<input class='radio' id='searchSortBy1' type="radio" name="searchSortBy" value="relevance" checked='checked' />
<label for='searchSortBy1'>Most Relevant</label>
<br /><br />
<input class='radio' id='searchSortBy2' type="radio" name="searchSortBy" value="rating" />
<label for='searchSortBy2'>Rating</label>
<br /><br />
<input class='radio' id='searchSortBy3' type="radio" name="searchSortBy" value="upload_date" />
<label for='searchSortBy3'>Upload Date</label>
<br /><br />
<input class='radio' id='searchSortBy4' type="radio" name="searchSortBy" value="view_count" />
<label for='searchSortBy4'>View Count</label>
</div>
<div class='pure-radiobutton filter'>
<h3>Time</h3>
<input class='radio' id='searchDate1' type="radio" name="searchDate" value="" checked='checked' />
<label for='searchDate1'>Any Time</label>
<br /><br />
<input class='radio' id='searchDate2' type="radio" name="searchDate" value="hour" />
<label for='searchDate2'>Last Hour</label>
<br /><br />
<input class='radio' id='searchDate3' type="radio" name="searchDate" value="today" />
<label for='searchDate3'>Today</label>
<br /><br />
<input class='radio' id='searchDate4' type="radio" name="searchDate" value="week" />
<label for='searchDate4'>This Week</label>
<br /><br />
<input class='radio' id='searchDate5' type="radio" name="searchDate" value="month" />
<label for='searchDate5'>This Month</label>
<br /><br />
<input class='radio' id='searchDate6' type="radio" name="searchDate" value="year" />
<label for='searchDate6'>This Year</label>
</div>
<div style='position: relative; bottom: 80px;' class='pure-radiobutton filter'>
<h3 class='filterTitle'>Type</h3>
<input class='radio' id='searchType1' type="radio" name="searchType" value="all" checked='checked' />
<label for='searchType1'>All Types</label>
<br /><br />
<input class='radio' id='searchType2' type="radio" name="searchType" value="video" />
<label for='searchType2'>Videos</label>
<br /><br />
<input class='radio' id='searchType3' type="radio" name="searchType" value="channel" />
<label for='searchType3'>Channels</label>
<br /><br />
<input class='radio' id='searchType4' type="radio" name="searchType" value="playlist" />
<label for='searchType4'>Playlists</label>
</div>
<div style='position: relative; bottom: 120px;' class='pure-radiobutton filter'>
<h3 class='filterTitle'>Duration</h3>
<input class='radio' id='searchDuration1' type="radio" name="searchDuration" value="" checked='checked' />
<label for='searchDuration1'>All Durations</label>
<br /><br />
<input class='radio' id='searchDuration2' type="radio" name="searchDuration" value="short" />
<label for='searchDuration2'>Short (
< 4 minutes)</label> <br /><br />
<input class='radio' id='searchDuration3' type="radio" name="searchDuration" value="long" />
<label for='searchDuration3'>Long (> 20 minutes)</label>
</div>
</div>
<div id='noSubscriptions' v-if='seen'>
<h2 class="message">
Your Subscription list is currently empty. Start adding subscriptions
to see them here.
<br /><br />
<i class="far fa-frown" style="font-size: 200px"></i>
</h2>
</div>
<div id='channelView'></div>
<div id='mainHeaderView'></div>
<div id='channelVideosView'></div>
<div id='searchView'></div>
<div id='subscriptionView'></div>
<div id='popularView'></div>
<div id='trendingView'></div>
<div id='savedView'></div>
<div id='historyView'></div>
<div id='playlistView'></div>
<div id='aboutView'></div>
<div id='settingsView'></div>
<div id='playerView'></div>
<div id='subscriptionManagerView'></div>
<div id='editProfileView'></div>
</div>
<div id='channelView'></div>
<div id='mainHeaderView'></div>
<div id='channelVideosView'></div>
<div id='searchView'></div>
<div id='subscriptionView'></div>
<div id='popularView'></div>
<div id='trendingView'></div>
<div id='savedView'></div>
<div id='historyView'></div>
<div id='playlistView'></div>
<div id='aboutView'></div>
<div id='settingsView'></div>
<div id='playerView'></div>
</div>
<div id='progressView'></div>
<div id='progressView'></div>
</body>
<script src="js/vue.js"></script>
<script src="js/general.js"></script>
@ -192,4 +195,5 @@
<script src="js/plugins/context-menu/context-menu-i18n.js"></script>
<script src="js/plugins/timerailthumbnails/vtt.min.js"></script>
<script src="js/plugins/timerailthumbnails/mep-feature-time-rail-thumbnails.js"></script>
</html>

View File

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

View File

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

View File

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

View File

@ -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("<opml")) {
getOpml(data, function(error, json) {
getOpml(data, function (error, json) {
if (!error) {
clearFile('subscriptions', false);
importOpmlSubs(json['children'][0]['children']);
@ -757,15 +817,64 @@ function importSubscriptions() {
return;
}
clearFile('subscriptions', false);
subDb.remove({}, {
multi: true
}, function (err, numRemoved) {});
let textDecode = new TextDecoder("utf-8").decode(data);
textDecode = textDecode.split("\n");
textDecode.pop();
console.log(textDecode);
fs.writeFile(appDatabaseFile, data, function(writeErr) {
if (writeErr) {
showToast('Unable to create file. Please check your permissions and try again.');
throw writeErr;
textDecode.forEach((data) => {
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();

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -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 */

View File

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

View File

@ -0,0 +1,5 @@
<div>
<div id='currentProfile' :style='{ backgroundColor: activeProfile.color }' onclick='profileSelectView.seen = !profileSelectView.seen'>
<span id='currentInitial' :style='{ color: activeProfileInitialColor }'>{{activeProfileInitial}}</span>
</div>
</div>

View File

@ -0,0 +1,75 @@
<div v-if='seen'>
<div v-if='!isNewProfile' class='card'>
<h2>Subscription List for {{profileName}} Profile</h2>
<h3>{{amountSelected}} Selected</h3>
<br />
<div class='profileEditPadding' v-on:click='channel.checked = !channel.checked' v-for='channel in subscriptionList'>
<div class='profileEdit' :style="{ backgroundImage: 'url(' + channel.channelThumbnail + ')' }" style='cursor: pointer; background-size: contain;'>
</div>
<div v-if='channel.checked' class='profileEditSelected'><i class='fas fa-check'></i></div>
<div class='profileEditName'><span>{{channel.channelName}}</span></div>
</div>
<br /><br /><br />
<div class="select center">
<select id="profileListSelect" class="select-text" v-model='selectedProfile' required>
<option v-for='profile in profileList' :value='profile.name'>{{profile.name}}</option>
</select>
<span class="select-highlight"></span>
<span class="select-bar"></span>
<label class="select-label">Profile Select</label>
</div>
<div class='center'>
<div v-on:click='selectAll' class='settingsButton'>
SELECT ALL
</div>
<div v-on:click='selectNone' class='settingsButton'>
SELECT NONE
</div>
<div v-on:click='move' class='settingsButton'>
MOVE SELECTED
</div>
<div v-on:click='copy' class='settingsButton'>
COPY SELECTED
</div>
<div v-on:click='deleteChannel' style='color: #f44336' class='settingsButton'>
DELETE SELECTED
</div>
</div>
</div>
<div class='card'>
<h2 v-if='isNewProfile'>Add New Profile</h2>
<h2 v-if='!isNewProfile'>Edit Profile Attributes</h2>
<div class="input-text-settings">
<input type="text" placeholder='Profile Name' name="set-name" v-model='newProfileName' />
</div>
<h3>Color Picker</h3>
<div class='colorPickerPadding' v-for='(value, key) in colorPalette'>
<div class='colorPicker' v-on:click='changeProfileColor(value)' :style='{ backgroundColor: value }' style='cursor: pointer;'></div>
</div>
<br />
<div class="input-text-settings">
<input type="text" placeholder='HEX Color' name="set-name" v-model='newProfileColor' />
</div>
<br />
<h3>New Profile Color</h3>
<div class='colorPickerPadding'>
<div class='colorPicker' :style='{ backgroundColor: newProfileColor }'></div>
</div>
<div class='center'>
<div v-if='isNewProfile' v-on:click='updateProfile' class='settingsButton'>
ADD PROFILE
</div>
<div v-else>
<div v-on:click='updateProfile' class='settingsButton'>
UPDATE PROFILE
</div>
<div v-on:click='defaultProfile' class='settingsButton'>
MAKE DEFAULT PROFILE
</div>
<div v-on:click='deleteProfile' style='color: #f44336' class='settingsButton'>
DELETE PROFILE
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,11 @@
<div v-if='seen' id='profileSelect'>
<h3 id='profileSelectHeader'>Profile Select<span v-on:click='showSubscriptionManager'><i class="fas fa-sliders-h"></i></span></h3>
<div id='profileSelectList'>
<div class='profile' v-on:click='setActiveProfile(index)' v-for='(profile, index) in profileList'>
<div class='profileBackground' :style='{ backgroundColor: profile.color }'>
<span class='profileInitial' :style='{ color: profileTextColor[index] }'>{{profileInitials[index]}}</span>
</div>
<p class='profileName'>{{profile.name}}</p>
</div>
</div>
</div>

View File

@ -349,6 +349,9 @@
<label class="select-label">Subscriptions Export Format</label>
</div>
<div class='center'>
<div onclick='hideViews(); subscriptionManagerView.seen = true;' class='settingsButton'>
MANAGE SUBSCRIPTIONS
</div>
<div onclick='importSubscriptions()' class='settingsButton'>
IMPORT SUBSCRIPTIONS
</div>
@ -397,13 +400,13 @@
<br />
<br />
<div class='center'>
<div onclick='confirmFunction("Are you sure you want to delete your history?", clearFile, "history")' class='settingsButton'>
<div onclick='confirmFunction("Are you sure you want to delete your history?", clearFile, "history")' style='color: #f44336' class='settingsButton'>
CLEAR HISTORY
</div>
<div onclick='confirmFunction("Are you sure you want to remove all saved videos?", clearFile, "saved")' class='settingsButton'>
<div onclick='confirmFunction("Are you sure you want to remove all saved videos?", clearFile, "saved")' style='color: #f44336' class='settingsButton'>
CLEAR FAVORITED VIDEOS
</div>
<div onclick='confirmFunction("Are you sure you want to remove all subscriptions?", clearFile, "subscriptions")' class='settingsButton'>
<div onclick='confirmFunction("Are you sure you want to remove all subscriptions?", clearFile, "subscriptions")' style='color: #f44336' class='settingsButton'>
CLEAR SUBSCRIPTIONS
</div>
</div>

View File

@ -0,0 +1,18 @@
<div v-if='seen'>
<div class='card'>
<h2>Subscription / Profile Manager</h2>
<br />
<div class='profileEditPadding' v-for='(profile, index) in profileList' v-on:click='editProfile(false, index)'>
<div class='profileEdit' :style='{ backgroundColor: profile.color }' style='cursor: pointer;'>
<span class='profileEditInitial' :style='{ color: profileTextColor[index] }'>{{profileInitials[index]}}</span>
</div>
<div class='profileEditName'><span>{{profile.name}}</span></div>
</div>
<div class='profileEditPadding' v-on:click='editProfile(true, -1)'>
<div class='profileEdit' style='background-color: #9E9E9E; cursor: pointer;'>
<i class='profileEditInitial fas fa-plus' style='color: #000000; top: 14px;'></i>
</div>
<div class='profileEditName'><span>Add New Profile</span></div>
</div>
</div>
</div>