Committing second batch of Vue.js implementation

This commit is contained in:
PrestonN 2018-06-25 16:04:11 -04:00
parent daeb89d74d
commit 1f79785cb4
16 changed files with 595 additions and 255 deletions

View File

@ -15,7 +15,7 @@
</head>
<body>
<div id='loading'>
<div v-if='seen' id='loading'>
<div class="spinner">
<div class="double-bounce1"></div>
<div class="double-bounce2"></div>
@ -44,14 +44,14 @@
<div class="sideNavContainer">
<ul>
<li v-on:click='subscriptions'><i class="fas fa-rss"></i>&nbsp;&nbsp;Subscriptions</li>
<li onclick='showMostPopular()'><i class="fas fa-users"></i>&nbsp;&nbsp;Most Popular</li>
<li onclick='showSavedVideos()'><i class="fas fa-star"></i>&nbsp;&nbsp;Saved</li>
<li onclick='showHistory()'><i class="fas fa-history"></i>&nbsp;&nbsp;History</li>
<li 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;Saved</li>
<li v-on:click='history'><i class="fas fa-history"></i>&nbsp;&nbsp;History</li>
</ul>
<hr />
<ul>
<li onclick='showSettings()'><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>
<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'>
@ -59,18 +59,26 @@
</div>
</div>
<div id="main">
<div id='channelView'></div>
<div id='mainHeaderView'></div>
<div id='aboutView'></div>
<div id='subscriptionView'></div>
<div id='channelVideosView'></div>
<div id='searchView'></div>
<div id='subscriptionView'></div>
<div id='popularView'></div>
<div id='savedView'></div>
<div id='historyView'></div>
<div id='aboutView'></div>
<div id='settingsView'></div>
<div id='playerView'></div>
</div>
<div id='progressView'></div>
</body>
<script src="js/youtubeApi.js"></script>
<script src="js/settings.js"></script>
<script src="js/updates.js"></script>
<script src="js/db.js"></script>
<script src="js/layout.js"></script>
<script src="js/templates.js"></script>
<script src="js/settings.js"></script>
<script src="js/videos.js"></script>
<script src="js/player.js"></script>
<script src="js/subscriptions.js"></script>

View File

@ -40,14 +40,18 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
* @return {Void}
*/
function goToChannel(channelId) {
event.stopPropagation();
clearMainContainer();
startLoadingAnimation();
//event.stopPropagation();
//clearMainContainer();
//startLoadingAnimation();
let subButtonText;
headerView.title = 'Latest Uploads';
hideViews();
loadingView.seen = true;
//let subButtonText;
// Setting subButtonText here as Mustache templates are logic-less.
isSubscribed(channelId).then((subscribed) => {
subButtonText = (subscribed ? "UNSUBSCRIBE" : "SUBSCRIBE");
channelView.subButtonText = (subscribed ? "UNSUBSCRIBE" : "SUBSCRIBE");
});
// Grab general channel information
@ -57,7 +61,14 @@ function goToChannel(channelId) {
}, (data) => {
const channelData = data.items[0];
const channelViewTemplate = require('./templates/channelView.html');
channelView.id = channelId;
channelView.name = channelData.brandingSettings.channel.title;
channelView.banner = channelData.brandingSettings.image.bannerImageUrl;
channelView.icon = channelData.snippet.thumbnails.high.url;
channelView.subCount = channelData.statistics.subscriberCount.toLocaleString(); //toLocaleString adds commas as thousands separators
channelView.description = autolinker.link(channelData.brandingSettings.channel.description); //autolinker makes URLs clickable
/*const channelViewTemplate = require('./templates/channelView.html');
mustache.parse(channelViewTemplate);
const rendered = mustache.render(channelViewTemplate, {
channelId: channelId,
@ -69,7 +80,7 @@ function goToChannel(channelId) {
subButtonText: subButtonText,
});
$('#main').html(rendered);
stopLoadingAnimation();
stopLoadingAnimation();*/
// Grab the channel's latest uploads. API forces a max of 50.
youtubeAPI('search', {
@ -83,8 +94,12 @@ function goToChannel(channelId) {
let grabDuration = getDuration(data.items);
grabDuration.then((videoList) => {
channelView.seen = true;
channelVideosView.videoList = [];
channelVideosView.seen = true;
loadingView.seen = false;
videoList.items.forEach((video) => {
displayVideo(video);
displayVideo(video, 'channel');
});
});
});

View File

@ -54,8 +54,8 @@ function removeFromHistory(videoId){
* @return {Void}
*/
function showHistory(){
clearMainContainer();
startLoadingAnimation();
//clearMainContainer();
//startLoadingAnimation();
console.log('checking history');
let videoList = '';
@ -80,15 +80,17 @@ function showHistory(){
id: videoList,
maxResults: 50,
}, function (data) {
createVideoListContainer('Watch History:');
//createVideoListContainer('Watch History:');
let grabDuration = getDuration(data.items);
grabDuration.then((videoList) => {
historyView.videoList = [];
loadingView.seen = false;
videoList.items.forEach((video) => {
displayVideo(video, 'history');
});
});
stopLoadingAnimation()
//stopLoadingAnimation()
});
});
}

View File

@ -45,7 +45,6 @@ const fs = require('fs'); // Used to read files. Specifically in the settings pa
const tor = require('tor-request');
let currentTheme = '';
let apiKey;
let useTor = false;
let dialog = electron.remote.dialog; // Used for opening file browser to export / import subscriptions.
let toastTimeout; // Timeout for toast notifications.
@ -57,7 +56,7 @@ require.extensions['.html'] = function(module, filename) {
// Grabs the default settings from the settings database file. Makes defaults if
// none are found.
checkDefaultSettings();
//checkDefaultSettings();
electron.ipcRenderer.on('ping', function(event, message) {
console.log(message);

View File

@ -30,14 +30,18 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
*/
function playVideo(videoId, videoThumbnail = '', useWindowPlayer = false) {
if (useWindowPlayer === false){
clearMainContainer();
startLoadingAnimation();
//clearMainContainer();
//startLoadingAnimation();
}
else{
showToast('Getting video information. Please wait...')
}
let subscribeText = '';
hideViews();
playerView.videoId = videoId;
playerView.embededHtml = "<iframe width='560' height='315' src='https://www.youtube-nocookie.com/embed/" + videoId + "?rel=0' frameborder='0' allow='autoplay; encrypted-media' allowfullscreen></iframe>";
/*let subscribeText = '';
let savedText = '';
let savedIconClass = '';
let savedIconColor = '';
@ -60,18 +64,18 @@ function playVideo(videoId, videoThumbnail = '', useWindowPlayer = false) {
let videoLikes;
let videoDislikes;
let totalLikes;
let likePercentage;
let likePercentage;*/
const checkSavedVideo = videoIsSaved(videoId);
// Change the save button icon and text depending on if the user has saved the video or not.
checkSavedVideo.then((results) => {
if (results === false) {
savedText = 'SAVE';
savedIconClass = 'far unsaved';
playerView.savedText = 'SAVE';
playerView.savedIconType = 'far unsaved';
} else {
savedText = 'SAVED';
savedIconClass = 'fas saved';
playerView.savedText = 'SAVED';
playerView.savedIconType = 'fas saved';
}
});
@ -82,10 +86,10 @@ function playVideo(videoId, videoThumbnail = '', useWindowPlayer = false) {
console.log(data);
// Figure out the width for the like/dislike bar.
videoLikes = data['items'][0]['statistics']['likeCount'];
videoDislikes = data['items'][0]['statistics']['dislikeCount'];
totalLikes = parseInt(videoLikes) + parseInt(videoDislikes);
likePercentage = parseInt((videoLikes / totalLikes) * 100);
playerView.videoLikes = data['items'][0]['statistics']['likeCount'];
playerView.videoDislikes = data['items'][0]['statistics']['dislikeCount'];
let totalLikes = parseInt(playerView.videoLikes) + parseInt(playerView.videoDislikes);
playerView.likePercentage = parseInt((playerView.videoLikes / totalLikes) * 100);
});
/*
@ -94,62 +98,66 @@ function playVideo(videoId, videoThumbnail = '', useWindowPlayer = false) {
youtubedlGetInfo(videoId, (info) => {
console.log(info);
console.log(videoLikes);
//console.log(videoLikes);
channelId = info['author']['id'];
let channelThumbnail = info['author']['avatar'];
playerView.videoTitle = info['title'];
playerView.channelName = info['author']['name'];
playerView.channelId = info['author']['id'];
playerView.channelIcon = info['author']['avatar'];
let videoUrls = info['formats'];
// Add commas to the video view count.
const videoViews = info['view_count'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
playerView.videoViews = info['view_count'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
videoThumbnail = info['player_response']['videoDetails']['thumbnail']['thumbnails'][3]['url'];
playerView.videoThumbnail = info['player_response']['videoDetails']['thumbnail']['thumbnails'][3]['url'];
// Format the date to a more readable format.
let dateString = new Date(info['published']);
dateString.setDate(dateString.getDate() + 1);
const publishedDate = dateFormat(dateString, "mmm dS, yyyy");
playerView.publishedDate = dateFormat(dateString, "mmm dS, yyyy");
let description = info['description'];
// Adds clickable links to the description.
description = autolinker.link(description);
playerView.description = autolinker.link(description);
// Search through the returned object to get the 480p and 720p video URLs (If available)
Object.keys(videoUrls).forEach((key) => {
switch (videoUrls[key]['itag']) {
case '18':
video480p = decodeURIComponent(videoUrls[key]['url']);
console.log(video480p);
playerView.video480p = decodeURIComponent(videoUrls[key]['url']);
//console.log(video480p);
break;
case '22':
video720p = decodeURIComponent(videoUrls[key]['url']);
console.log(video720p);
playerView.video720p = decodeURIComponent(videoUrls[key]['url']);
//console.log(video720p);
break;
}
});
// Default to the embeded player if the URLs cannot be found.
if (typeof(video720p) === 'undefined' && typeof(video480p) === 'undefined') {
useEmbedPlayer = true;
defaultQuality = 'EMBED';
videoHtml = embedPlayer.replace(/\&quot\;/g, '"');
if (typeof(playerView.video720p) === 'undefined' && typeof(playerView.video480p) === 'undefined') {
//useEmbedPlayer = true;
playerView.currentQuality = 'EMBED';
playerView.videoUrl = embedPlayer.replace(/\&quot\;/g, '"');
showToast('Unable to get video file. Reverting to embeded player.');
} else if (typeof(video720p) === 'undefined' && typeof(video480p) !== 'undefined') {
// Default to the 480p video if the 720p URL cannot be found.
defaultUrl = video480p;
defaultQuality = '480p';
playerView.videoUrl = playerView.video480p;
playerView.currentQuality = '480p';
} else {
// Default to the 720p video.
defaultUrl = video720p;
defaultQuality = '720p';
playerView.videoUrl = playerView.video720p;
playerView.currentQuality = '720p';
// Force the embeded player if needed.
//videoHtml = embedPlayer;
}
let useEmbedPlayer = false;
if (!useEmbedPlayer) {
//videoHtml = '<video class="videoPlayer" type="application/x-mpegURL" onmousemove="hideMouseTimeout()" onmouseleave="removeMouseTimeout()" controls="" src="' + defaultUrl + '" poster="' + videoThumbnail + '" autoplay>';
let videoHtml = '';
if (typeof(info.player_response.captions) === 'object') {
if (typeof(info.player_response.captions.playerCaptionsTracklistRenderer.captionTracks) === 'object') {
@ -168,28 +176,33 @@ function playVideo(videoId, videoThumbnail = '', useWindowPlayer = false) {
}
}
//videoHtml = videoHtml + '</video>';
playerView.subtitleHtml = videoHtml;
}
const checkSubscription = isSubscribed(channelId);
const checkSubscription = isSubscribed(playerView.channelId);
// Change the subscribe button text depending on if the user has subscribed to the channel or not.
checkSubscription.then((results) => {
const subscribeButton = document.getElementById('subscribeButton');
if (results === false) {
if (subscribeButton != null) {
subscribeButton.innerHTML = 'SUBSCRIBE';
playerView.subscribedText = 'SUBSCRIBE';
}
} else {
if (subscribeButton != null) {
subscribeButton.innerHTML = 'UNSUBSCRIBE';
playerView.subscribedText = 'UNSUBSCRIBE';
}
}
});
const playerTemplate = require('./templates/player.html')
showVideoRecommendations(videoId);
loadingView.seen = false;
playerView.seen = true;
addToHistory(videoId);
/*const playerTemplate = require('./templates/player.html')
mustache.parse(playerTemplate);
const rendered = mustache.render(playerTemplate, {
videoQuality: defaultQuality,
@ -253,14 +266,14 @@ function playVideo(videoId, videoThumbnail = '', useWindowPlayer = false) {
textTracks[track].mode = 'hidden';
});
}
}
}*/
// Sometimes a video URL is found, but the video will not play. I believe the issue is
// that the video has yet to render for that quality, as the video will be available at a later time.
// This will check the URLs and switch video sources if there is an error.
//checkVideoUrls(video480p, video720p);
window.setTimeout(checkVideoUrls, 5000, video480p, video720p);
//window.setTimeout(checkVideoUrls, 5000, video480p, video720p);
});
}
@ -272,7 +285,7 @@ function playVideo(videoId, videoThumbnail = '', useWindowPlayer = false) {
*
* @return {Void}
*/
function openMiniPlayer(videoThumbnail) {
function openMiniPlayer() {
let lastTime;
let videoHtml;
@ -299,7 +312,7 @@ function openMiniPlayer(videoThumbnail) {
mustache.parse(template);
const rendered = mustache.render(template, {
videoHtml: videoHtml,
videoThumbnail: videoThumbnail,
videoThumbnail: playerView.thumbnail,
startTime: lastTime,
});
// Render the template to the new browser window.

View File

@ -74,23 +74,15 @@ function toggleSavedVideo(videoId) {
event.stopPropagation();
const checkIfSaved = videoIsSaved(videoId);
const saveIcon = document.getElementById('saveIcon');
const savedText = document.getElementById('savedText');
checkIfSaved.then((results) => {
if (results === false) {
savedText.innerHTML = 'SAVED';
saveIcon.classList.remove('far');
saveIcon.classList.remove('unsaved');
saveIcon.classList.add('fas');
saveIcon.classList.add('saved');
playerView.savedText = 'SAVED';
playerView.savedIconType = 'fas saved';
addSavedVideo(videoId);
} else {
savedText.innerHTML = 'SAVE';
saveIcon.classList.remove('fas');
saveIcon.classList.remove('saved');
saveIcon.classList.add('far');
saveIcon.classList.add('unsaved');
playerView.savedText = 'SAVE';
playerView.savedIconType = 'far unsaved';
removeSavedVideo(videoId);
}
});
@ -121,8 +113,8 @@ function videoIsSaved(videoId) {
* @return {Void}
*/
function showSavedVideos(){
clearMainContainer();
startLoadingAnimation();
//clearMainContainer();
//startLoadingAnimation();
console.log('checking saved videos');
let videoList = '';
@ -151,14 +143,16 @@ function showSavedVideos(){
maxResults: 50,
}, (data) => {
// Render the videos to the screen
createVideoListContainer('Saved Videos:');
//createVideoListContainer('Saved Videos:');
let grabDuration = getDuration(data.items);
grabDuration.then((videoList) => {
savedView.videoList = [];
loadingView.seen = false;
videoList.items.forEach((video) => {
displayVideo(video, 'saved');
});
});
stopLoadingAnimation();
//stopLoadingAnimation();
});
});
}

View File

@ -29,12 +29,12 @@ const apiKeyBank = ['AIzaSyC9E579nh_qqxg6BH4xIce3k_7a9mT4uQc', 'AIzaSyCKplYT6hZI
*
* @return {Void}
*/
function showSettings() {
clearMainContainer();
startLoadingAnimation();
function updateSettingsView() {
//clearMainContainer();
//startLoadingAnimation();
let isChecked = '';
let key = '';
//let isChecked = '';
//let key = '';
/*
* Check the settings database for the user's current settings. This is so the
@ -45,7 +45,7 @@ function showSettings() {
switch (setting['_id']) {
case 'apiKey':
if (apiKeyBank.indexOf(setting['value']) == -1) {
key = setting['value'];
settingsView.apiKey = setting['value'];
}
break;
case 'theme':
@ -56,7 +56,7 @@ function showSettings() {
});
// Grab the settings.html template to prepare for rendering
const settingsTemplate = require('./templates/settings.html')
/*const settingsTemplate = require('./templates/settings.html')
mustache.parse(settingsTemplate);
const rendered = mustache.render(settingsTemplate, {
isChecked: isChecked,
@ -64,19 +64,19 @@ function showSettings() {
});
// Render template to application
$('#main').html(rendered);
stopLoadingAnimation();
stopLoadingAnimation();*/
// Check / uncheck the switch depending on the user's settings.
if (currentTheme === 'light') {
document.getElementById('themeSwitch').checked = false;
settingsView.useTheme = false;
} else {
document.getElementById('themeSwitch').checked = true;
settingsView.useTheme = true;
}
if (useTor) {
document.getElementById('torSwitch').checked = true;
settingsView.useTor = true;
} else {
document.getElementById('torSwitch').checked = false;
settingsView.useTor = false;
}
});
}
@ -89,12 +89,12 @@ function showSettings() {
function checkDefaultSettings() {
// Grab a random API Key.
apiKey = apiKeyBank[Math.floor(Math.random() * apiKeyBank.length)];
settingsView.apiKey = apiKeyBank[Math.floor(Math.random() * apiKeyBank.length)];
let newSetting;
let settingDefaults = {
'theme': 'light',
'apiKey': apiKey,
'apiKey': settingsView.apiKey,
'useTor': false
};
@ -121,7 +121,7 @@ function checkDefaultSettings() {
break;
case 'apiKey':
if (apiKeyBank.indexOf(docs[0]['value']) == -1) {
apiKey = docs[0]['value'];
settingsView.apiKey = docs[0]['value'];
}
break;
case 'useTor':
@ -146,7 +146,7 @@ function updateSettings() {
let key = document.getElementById('api-key').value;
let theme = 'light';
apiKey = apiKeyBank[Math.floor(Math.random() * apiKeyBank.length)];
settingsView.apiKey = apiKeyBank[Math.floor(Math.random() * apiKeyBank.length)];
console.log(themeSwitch);
@ -188,7 +188,7 @@ function updateSettings() {
settingsDb.update({
_id: 'apiKey'
}, {
value: apiKey
value: settingsView.apiKey
}, {});
}
@ -424,3 +424,5 @@ function clearFile(type, showMessage = true){
}
})
}
checkDefaultSettings();

View File

@ -21,6 +21,9 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
* File for all functions related to subscriptions.
*/
let subscriptionTimer;
let checkSubscriptions = true;
/**
* Add a channel to the user's subscription database.
*
@ -78,8 +81,16 @@ function removeSubscription(channelId) {
* @return {Void}
*/
function loadSubscriptions() {
showToast('Getting Subscriptions. Please wait...');
const loading = document.getElementById('loading');
if (checkSubscriptions === false){
console.log('Will not load subscriptions. Timer still on.');
return;
}
else{
showToast('Refreshing Subscription List. Please wait...');
checkSubscriptions = false;
}
//const loading = document.getElementById('loading');
//startLoadingAnimation()
@ -87,7 +98,6 @@ function loadSubscriptions() {
const subscriptions = returnSubscriptions();
// Welcome to callback hell, we hope you enjoy your stay.
subscriptions.then((results) => {
let channelId = '';
let videoList = [];
@ -108,6 +118,7 @@ function loadSubscriptions() {
console.log(data);
videoList = videoList.concat(data.items);
counter++;
progressView.progressWidth = (counter / results.length) * 100;
if (counter === results.length) {
videoList.sort((a, b) => {
const date1 = Date.parse(a.snippet.publishedAt);
@ -129,7 +140,9 @@ function loadSubscriptions() {
list.items.forEach((video) => {
displayVideo(video, 'subscriptions');
});
stopLoadingAnimation();
loadingView.seen = false;
progressView.seen = false;
progressView.progressWidth = 0;
});
} else {
console.log(videoList);
@ -147,7 +160,12 @@ function loadSubscriptions() {
finishedList.forEach((video) => {
displayVideo(video, 'subscriptions');
});
stopLoadingAnimation();
loadingView.seen = false;
progressView.seen = false;
progressView.progressWidth = 0;
subscriptionTimer = window.setTimeout(() => {
checkSubscriptions = true;
}, 60000);
});
});
}
@ -160,7 +178,7 @@ function loadSubscriptions() {
} else {
// User has no subscriptions. Display message.
const container = document.getElementById('main');
stopLoadingAnimation();
//stopLoadingAnimation();
container.innerHTML = `<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>`;

View File

@ -19,22 +19,88 @@ import Vue from './js/vue.js';
const mainHeaderTemplate = require('./templates/mainHeader.html');
const aboutTemplate = require('./templates/about.html');
const settingsTemplate = require('./templates/settings.html');
const videoListTemplate = require('./templates/videoTemplate.html');
const nextPageTemplate = require('./templates/searchNextPage.html');
const playerTemplate = require('./templates/player.html');
const channelTemplate = require('./templates/channelView.html');
const progressViewTemplate = require('./templates/progressView.html');
/*
* Progress view
*
* Shows progress bar on bottom of application.
*
* seen: Toggles visibility of view
* progressWidth: sets width of the progress bar
*/
let progressView = new Vue({
el: '#progressView',
data: {
seen: true,
progressWidth: 0
},
template: progressViewTemplate
});
let loadingView = new Vue({
el: '#loading',
data: {
seen: false
}
});
let sideNavBar = new Vue({
el: '#sideNav',
methods: {
about: (event) => {
hideViews();
aboutView.seen = true;
},
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(popularView.videoList.length === 0){
loadingView.seen = true;
}
headerView.seen = true;
headerView.title = 'Most Popular';
popularView.seen = true;
showMostPopular();
},
saved: (event) => {
hideViews();
if(savedView.videoList.length === 0){
loadingView.seen = true;
}
headerView.seen = true;
headerView.title = 'Saved Videos';
savedView.seen = true;
showSavedVideos();
},
history: (event) => {
hideViews();
if(historyView.videoList.length === 0){
loadingView.seen = true;
}
headerView.seen = true;
headerView.title = 'Video History';
historyView.seen = true;
showHistory();
},
settings: (event) => {
hideViews();
settingsView.seen = true;
updateSettingsView();
},
about: (event) => {
hideViews();
aboutView.seen = true;
}
}
});
@ -57,33 +123,68 @@ let subscriptionView = new Vue({
},
methods: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
},
channel: (channelId) => {
goToChannel(channelId)
goToChannel(channelId);
}
},
template: videoListTemplate
});
let searchView = new Vue({
el: '#searchView',
let popularView = new Vue({
el: '#popularView',
data: {
seen: false,
isSearch: true,
nextPageToken: '',
isSearch: false,
videoList: []
},
methods: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
},
channel: (channelId) => {
goToChannel(channelId)
goToChannel(channelId);
}
},
template: videoListTemplate
});
let savedView = new Vue({
el: '#savedView',
data: {
seen: false,
isSearch: false,
videoList: []
},
methods: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
},
nextPage: (nextPageToken) => {
console.log(searchView.nextPageToken);
search(searchView.nextPageToken);
channel: (channelId) => {
goToChannel(channelId);
}
},
template: videoListTemplate
});
let historyView = new Vue({
el: '#historyView',
data: {
seen: false,
isSearch: false,
videoList: []
},
methods: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
},
channel: (channelId) => {
goToChannel(channelId);
}
},
template: videoListTemplate
@ -98,9 +199,145 @@ let aboutView = new Vue({
template: aboutTemplate
});
let settingsView = new Vue({
el: '#settingsView',
data: {
seen: false,
useTheme: false,
useTor: false,
apiKey: ''
},
template: settingsTemplate
});
let searchView = new Vue({
el: '#searchView',
data: {
seen: false,
isSearch: true,
nextPageToken: '',
videoList: []
},
methods: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
},
channel: (channelId) => {
goToChannel(channelId);
},
nextPage: (nextPageToken) => {
console.log(searchView.nextPageToken);
search(searchView.nextPageToken);
}
},
template: videoListTemplate
});
let channelView = new Vue({
el: '#channelView',
data: {
seen: false,
id: '',
name: '',
icon: '',
baner: '',
subCount: '',
subButtonText: '',
description: ''
},
methods: {
subscription: (channelId) => {
toggleSubscription(channelId);
}
},
template: channelTemplate
});
let channelVideosView = new Vue({
el: '#channelVideosView',
data: {
seen: false,
isSearch: false,
videoList: []
},
method: {
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
},
channel: (channelId) => {
goToChannel(channelId);
}
},
template: videoListTemplate
});
let playerView = new Vue({
el: '#playerView',
data: {
seen: false,
publishedDate: '',
videoUrl: '',
videoId: '',
channelId: '',
channelIcon: '',
channelName: '',
subscribedText: '',
savedText: '',
savedIconType: 'far',
description: '',
videoThumbnail: '',
subtitleHtml: '',
currentQuality: '',
video480p: '',
video720p: '',
embededHtml: '',
currentSpeed: 1,
videoTitle: '',
videoViews: '',
likePercentage: 0,
videoLikes: 0,
videoDislikes: 0,
recommendedVideoList: []
},
methods: {
channel: (channelId) => {
goToChannel(channelId);
},
subscription: (videoId) => {
toggleSubscription(videoId);
},
quality: (url, qualityText) => {
console.log(url);
console.log(qualityText);
},
copy: (site, videoId) => {
const url = 'https://' + site + '.com/watch?v=' + videoId;
clipboard.writeText(url);
showToast('URL has been copied to the clipboard');
},
save: (videoId) => {
toggleSavedVideo(videoId);
},
play: (videoId) => {
loadingView.seen = true;
playVideo(videoId);
}
},
template: playerTemplate
});
function hideViews(){
subscriptionView.seen = false;
aboutView.seen = false;
headerView.seen = false;
searchView.seen = false;
settingsView.seen = false;
popularView.seen = false;
savedView.seen = false;
historyView.seen = false;
playerView.seen = false;
channelView.seen = false;
channelVideosView.seen = false;
}

View File

@ -188,6 +188,18 @@ function displayVideo(videoData, listType = '') {
case 'search':
searchView.videoList = searchView.videoList.concat(video);
break;
case 'popular':
popularView.videoList = popularView.videoList.concat(video);
break;
case 'saved':
savedView.videoList = savedView.videoList.concat(video);
break;
case 'history':
historyView.videoList = historyView.videoList.concat(video);
break;
case 'channel':
channelVideosView.videoList = channelVideosView.videoList.concat(video);
break;
}
@ -347,6 +359,8 @@ function addNextPage(nextPageToken) {
* @param {string} videoId - The video ID of the video to get recommendations from.
*/
function showVideoRecommendations(videoId) {
playerView.recommendedVideoList = [];
youtubeAPI('search', {
part: 'id',
type: 'video',
@ -356,10 +370,18 @@ function showVideoRecommendations(videoId) {
let grabDuration = getDuration(data.items);
grabDuration.then((videoList) => {
videoList.items.forEach((video) => {
let data = {}
const snippet = video.snippet;
const videoDuration = parseVideoDuration(video.contentDetails.duration);
const recommTemplate = require('./templates/recommendations.html')
data.duration = parseVideoDuration(video.contentDetails.duration);
data.id = video.id;
data.title = snippet.title;
data.channelName = snippet.channelTitle;
data.thumbnail = snippet.thumbnails.medium.url;
data.publishedDate = dateFormat(snippet.publishedAt, "mmm dS, yyyy");
playerView.recommendedVideoList = playerView.recommendedVideoList.concat(data);
/*const recommTemplate = require('./templates/recommendations.html')
mustache.parse(recommTemplate);
const rendered = mustache.render(recommTemplate, {
videoId: video.id,
@ -370,7 +392,7 @@ function showVideoRecommendations(videoId) {
publishedDate: dateFormat(snippet.publishedAt, "mmm dS, yyyy")
});
const recommendationHtml = $('#recommendations').html();
$('#recommendations').html(recommendationHtml + rendered);
$('#recommendations').html(recommendationHtml + rendered);*/
});
});
});
@ -465,8 +487,8 @@ function parseVideoDuration(durationString) {
* @return {Void}
*/
function showMostPopular() {
clearMainContainer();
startLoadingAnimation();
//clearMainContainer();
//startLoadingAnimation();
// Get the date of 2 days ago.
var d = new Date();
@ -483,15 +505,19 @@ function showMostPopular() {
publishedAfter: d.toISOString(),
maxResults: 50,
}, function(data) {
createVideoListContainer('Most Popular:');
//createVideoListContainer('Most Popular:');
console.log(data);
let grabDuration = getDuration(data.items);
grabDuration.then((videoList) => {
console.log(videoList);
videoList.items.forEach(displayVideo);
popularView.videoList = [];
loadingView.seen = false;
videoList.items.forEach((video) => {
displayVideo(video, 'popular');
});
});
stopLoadingAnimation();
//stopLoadingAnimation();
});
}

View File

@ -9,7 +9,7 @@
*/
function youtubeAPI(resource, params, success) {
params.key = apiKey;
params.key = settingsView.apiKey;
if (useTor) {
tor.request('https://www.googleapis.com/youtube/v3/' + resource + '?' + $.param(params), function(err, res, body) {

View File

@ -25,6 +25,16 @@ a {
color: #2196F3;
}
#progressBar{
height: 3px;
background-color: #f44336;
display: block;
position: absolute;
bottom: 0;
left: 0;
z-index: 1;
}
.center {
text-align: center;
margin: 0 auto;
@ -176,7 +186,6 @@ a {
left: 5;
bottom: 0;
right: 0;
display: none;
}
.settingsInput {

View File

@ -1,21 +1,20 @@
<img class='channelViewBanner' src='{{channelBanner}}' />
<br />
<div class='channelViewTitle'>
<img class='channelViewImage' src='{{channelImage}}' />
<span class='channelViewName'>{{channelName}}</span>
<div v-if='seen'>
<img class='channelViewBanner' :src='banner' />
<br />
<span class='channelViewSubs'>{{subCount}} Subscribers</span>
<div id='subscribeButton' class='channelSubButton' onclick='toggleSubscription("{{channelId}}");'>
{{subButtonText}}
<div class='channelViewTitle'>
<img class='channelViewImage' :src='icon' />
<span class='channelViewName'>{{name}}</span>
<br />
<span class='channelViewSubs'>{{subCount}} Subscribers</span>
<div id='subscribeButton' class='channelSubButton' v-on:click='subscription(channelId);'>
{{subButtonText}}
</div>
</div>
</div>
<br />
<hr />
<div class='channelViewDescription'>
{{{channelDescription}}}
</div>
<br />
<hr />
<div id='videoListContainer'>
<h2>Latest Uploads</h2>
<br />
<hr />
<div class='channelViewDescription'>
<span v-html='description'></span>
</div>
<br />
<hr />
</div>

View File

@ -1,82 +1,95 @@
<video class="videoPlayer" type="application/x-mpegURL" onmousemove="hideMouseTimeout()" onmouseleave="removeMouseTimeout()" controls="" src="{{defaultUrl}}" poster="{{videoThumbnail}}" autoplay>
{{{subtitleHtml}}}
</video>
<div class='statistics'>
<div class='smallButton' onclick="openMiniPlayer('{{videoThumbnail}}')">
MINI PLAYER <i class="fas fa-external-link-alt"></i>
</div>
<div class='smallButton videoQuality'>
<span id='currentQuality'>{{videoQuality}}</span> <i class="fas fa-angle-down"></i>
<div class='qualityTypes'>
<ul>
<li id='quality480p' onclick='changeQuality("{{video480p}}", "480p")'>480p</li>
<li id='quality720p' onclick='changeQuality("{{video720p}}", "720p")'>720p</li>
<li id='qualityEmbed' onclick='changeQuality("{{embedPlayer}}", "EMBED", true)'>EMBED</li>
</ul>
<div v-if='seen'>
<video class="videoPlayer" type="application/x-mpegURL" onmousemove="hideMouseTimeout()" onmouseleave="removeMouseTimeout()" controls="" :src='videoUrl' :poster="videoThumbnail" v-html="subtitleHtml" autoplay>
</video>
<div class='statistics'>
<div onclick='openMiniPlayer()' class='smallButton' >
MINI PLAYER <i class="fas fa-external-link-alt"></i>
</div>
</div>
<div class='smallButton videoSpeed'>
<span id='currentSpeed'>1</span>x <i class="fas fa-angle-down"></i>
<div class='speedTypes'>
<ul>
<li onclick='changeVideoSpeed(0.25)'>0.25x</li>
<li onclick='changeVideoSpeed(0.5)'>0.5x</li>
<li onclick='changeVideoSpeed(0.75)'>0.75x</li>
<li onclick='changeVideoSpeed(1)'>1x</li>
<li onclick='changeVideoSpeed(1.25)'>1.25x</li>
<li onclick='changeVideoSpeed(1.5)'>1.5x</li>
<li onclick='changeVideoSpeed(1.75)'>1.75x</li>
<li onclick='changeVideoSpeed(2)'>2x</li>
</ul>
</div>
</div>
<div onclick='toggleSavedVideo("{{videoId}}")' class='smallButton'>
<i id='saveIcon' style='color: {{savedIconColor}};' class="{{savedIconClass}} fa-star"></i> <span id='savedText'>{{savedText}}</span>
</div>
<div class='smallButton' onclick='copyLink("youtube", "{{videoId}}")'>
COPY YOUTUBE LINK
</div>
<a href='https://youtube.com/watch?v={{videoId}}'>
<div class='smallButton'>
OPEN IN YOUTUBE
</div>
</a>
<div class='smallButton' onclick='copyLink("hooktube", "{{videoId}}")'>
COPY HOOKTUBE LINK
</div>
<a href='https://hooktube.com/watch?v={{videoId}}'>
<div class='smallButton'>
OPEN IN HOOKTUBE
</div>
</a>
<br />
<p class='title'>{{videoTitle}}</p>
<p class='views'>{{videoViews}} views</p>
<div class='likeContainer'>
<div class='dislikeBar'>
<div class='likeBar' style='width: {{likePercentage}}%;'>
<div class='smallButton videoQuality'>
<span id='currentQuality'>{{currentQuality}}</span> <i class="fas fa-angle-down"></i>
<div class='qualityTypes'>
<ul>
<li id='quality480p' v-on:click='quality(video480p, "480p")'>480p</li>
<li id='quality720p' v-on:click='quality(video720p, "720p")'>720p</li>
<li id='qualityEmbed' v-on:click='quality(embededPlayer, "EMBED")'>EMBED</li>
</ul>
</div>
</div>
<span class='likes'><i class="fas fa-thumbs-up"></i> {{videoLikes}}</span><span class='dislikes'><i class="fas fa-thumbs-down"></i> {{videoDislikes}}</span>
<div class='smallButton videoSpeed'>
<span id='currentSpeed'>{{currentSpeed}}</span>x <i class="fas fa-angle-down"></i>
<div class='speedTypes'>
<ul>
<li onclick='changeVideoSpeed(0.25)'>0.25x</li>
<li onclick='changeVideoSpeed(0.5)'>0.5x</li>
<li onclick='changeVideoSpeed(0.75)'>0.75x</li>
<li onclick='changeVideoSpeed(1)'>1x</li>
<li onclick='changeVideoSpeed(1.25)'>1.25x</li>
<li onclick='changeVideoSpeed(1.5)'>1.5x</li>
<li onclick='changeVideoSpeed(1.75)'>1.75x</li>
<li onclick='changeVideoSpeed(2)'>2x</li>
</ul>
</div>
</div>
<div class='smallButton' v-on:click='save(videoId)'>
<i id='saveIcon' :class='savedIconType' class="fa-star"></i> <span id='savedText'>{{savedText}}</span>
</div>
<div class='smallButton' v-on:click='copy("youtube", videoId)'>
COPY YOUTUBE LINK
</div>
<a :href='"https://youtube.com/watch?v=" + videoId'>
<div class='smallButton'>
OPEN IN YOUTUBE
</div>
</a>
<div class='smallButton' v-on:click='copy("hooktube", videoId)'>
COPY HOOKTUBE LINK
</div>
<a :href='"https://hooktube.com/watch?v=" + videoId'>
<div class='smallButton'>
OPEN IN HOOKTUBE
</div>
</a>
<br />
<p class='title'>{{videoTitle}}</p>
<p class='views'>{{videoViews}} views</p>
<div class='likeContainer'>
<div class='dislikeBar'>
<div class='likeBar' :style='{width: likePercentage + "%"}'>
</div>
</div>
<span class='likes'><i class="fas fa-thumbs-up"></i> {{videoLikes}}</span><span class='dislikes'><i class="fas fa-thumbs-down"></i> {{videoDislikes}}</span>
</div>
</div>
<div class="details">
<img id='channelIcon' v-on:click='channel(channelId)' :src="channelIcon" />
<p id='channelName' v-on:click='channel(channelId)'>{{channelName}}</p>
<p id='publishDate'>Published on {{publishedDate}}</p>
<div id='subscribeButton' class='playerSubButton' v-on:click='subscription(channelId)'>{{subscribedText}}</div>
<br /><br />
<div id='description'>
<span v-html="description"></span>
</div>
</div>
<div id='showComments'>
Show Comments <i class="far fa-comments"></i> (Max of 100)
</div>
<div id='comments' :data-video-id="videoId">
</div>
<div id='recommendations'>
<strong>Recommendations</strong>
<div v-for='video in recommendedVideoList'>
<div class='recommendVideo' v-on:click='play(video.id)'>
<div class='recommendThumbnail'>
<img :src='video.thumbnail'></img>
<p v-on:click='play(video.id)' class='videoDuration'>{{video.duration}}</p>
</div>
<p class='recommendTitle'>{{video.title}}</p>
<p class='recommendChannel'>{{video.channelName}}</p>
<p class='recommendDate'>{{video.publishedDate}}</p>
</div>
<hr />
</div>
</div>
</div>
<div class="details">
<img id='channelIcon' onclick='goToChannel("{{channelId}}")' src="{{channelIcon}}" />
<p id='channelName' onclick='goToChannel("{{channelId}}")'>{{channelName}}</p>
<p id='publishDate'>Published on {{publishedDate}}</p>
<div id='subscribeButton' class='playerSubButton' onclick='toggleSubscription("{{channelId}}")'>{{isSubscribed}}</div>
<br /><br />
<div id='description'>
{{{description}}}
</div>
</div>
<div id='showComments'>
Show Comments <i class="far fa-comments"></i> (Max of 100)
</div>
<div id='comments' data-video-id="{{videoId}}">
</div>
<div id='recommendations'>
<strong>Recommendations</strong>
</div>

View File

@ -0,0 +1,3 @@
<div v-if='seen'>
<span id='progressBar' :style='{ width: progressWidth + "%" }'></span>
</div>

View File

@ -1,35 +1,37 @@
<h1 class="center">Settings</h1>
<div class='center'>
<input type='text' name='api-key' id='api-key' class='settingsInput' value='{{key}}' placeholder='API Key' />
<br />
<label for='api-key'>Set API Key: Leave blank to use default</label>
<br />
<input type="checkbox" id="themeSwitch" name="set-name" class="switch-input" onchange='toggleTheme(this)' {{isChecked}}>
<label for="themeSwitch" class="switch-label">Use Dark Theme</label>
<input type="checkbox" id="torSwitch" name="set-name" class="switch-input" {{isChecked}}>
<label for="torSwitch" class="switch-label">Use Tor for API calls</label>
</div>
<div class='center'>
<div onclick='importSubscriptions()' class='settingsButton'>
IMPORT SUBSCRIPTIONS
<div v-if="seen">
<h1 class="center">Settings</h1>
<div class='center'>
<input type='text' name='api-key' id='api-key' class='settingsInput' :value='apiKey' placeholder='API Key' />
<br />
<label for='api-key'>Set API Key: Leave blank to use default</label>
<br />
<input type="checkbox" id="themeSwitch" name="set-name" class="switch-input" onchange='toggleTheme(this)' :checked='useTheme'>
<label for="themeSwitch" class="switch-label">Use Dark Theme</label>
<input type="checkbox" id="torSwitch" name="set-name" class="switch-input" :checked='useTor'>
<label for="torSwitch" class="switch-label">Use Tor for API calls</label>
</div>
<div onclick='exportSubscriptions();' class='settingsButton'>
EXPORT SUBSCRIPTIONS
<div class='center'>
<div onclick='importSubscriptions()' class='settingsButton'>
IMPORT SUBSCRIPTIONS
</div>
<div onclick='exportSubscriptions();' class='settingsButton'>
EXPORT SUBSCRIPTIONS
</div>
</div>
<br /><br />
<div class='center'>
<div onclick='confirmFunction("Are you sure you want to delete your history?", clearFile, "history")' class='settingsButton'>
CLEAR HISTORY
</div>
<div onclick='confirmFunction("Are you sure you want to remove all saved videos?", clearFile, "saved")' class='settingsButton'>
CLEAR SAVED VIDEOS
</div>
<div onclick='confirmFunction("Are you sure you want to remove all subscriptions?", clearFile, "subscriptions")' class='settingsButton'>
CLEAR SUBSCRIPTIONS
</div>
</div>
<br /><br />
<div onclick='updateSettings()' class='center settingsSubmit'>
SAVE SETTINGS
</div>
</div>
<br /><br />
<div class='center'>
<div onclick='confirmFunction("Are you sure you want to delete your history?", clearFile, "history")' class='settingsButton'>
CLEAR HISTORY
</div>
<div onclick='confirmFunction("Are you sure you want to remove all saved videos?", clearFile, "saved")' class='settingsButton'>
CLEAR SAVED VIDEOS
</div>
<div onclick='confirmFunction("Are you sure you want to remove all subscriptions?", clearFile, "subscriptions")' class='settingsButton'>
CLEAR SUBSCRIPTIONS
</div>
</div>
<br /><br />
<div onclick='updateSettings()' class='center settingsSubmit'>
SAVE SETTINGS
</div>