Refactor code and improve comments (#40)

* Refactor channels.js

* Refactor templates/about.html

* Refactor events.js, fix template bug

* Improve comments in youtubeApi.js

* Remove duplicated apiKeyBank in settings.js

* Capitalize comments in init.js

* Minor style changes in savedVideos.js

* Refactor videos.js, rename displayVideos() to displayVideo()

* Minor changes in layout.js and videos.js
This commit is contained in:
Larivact 2018-03-13 00:37:30 +01:00 committed by Preston
parent d09138c1a6
commit 845aca3fc9
12 changed files with 90 additions and 143 deletions

View File

@ -33,9 +33,9 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
}*/
/**
* View a channel page, displaying recent uplaods.
* Display a channel page, showing latest uploads.
*
* @param {string} channelId - The channel ID to go to.
* @param {string} channelId - The channel ID to display.
*
* @return {Void}
*/
@ -44,56 +44,34 @@ function goToChannel(channelId) {
clearMainContainer();
startLoadingAnimation();
// Check if the user is subscribed to the channel. Display different text based on the information
let subscribeText = '';
const checkSubscription = isSubscribed(channelId);
checkSubscription.then((results) => {
if(results === false){
subscribeText = 'SUBSCRIBE';
}
else{
subscribeText = 'UNSUBSCRIBE';
}
let subButtonText;
// Setting subButtonText here as Mustache templates are logic-less.
isSubscribed(channelId).then((subscribed) => {
subButtonText = (subscribed ? "UNSUBSCRIBE" : "SUBSCRIBE");
});
// Call YouTube API to grab channel information
// Grab general channel information
youtubeAPI('channels', {
part: 'snippet, brandingSettings, statistics',
part: 'snippet,brandingSettings,statistics',
id: channelId,
}, function (data){
// Set variables of extracted information
const brandingSettings = data['items'][0]['brandingSettings'];
const statistics = data['items'][0]['statistics'];
const snippet = data['items'][0]['snippet'];
const channelName = brandingSettings['channel']['title'];
const channelBanner = brandingSettings['image']['bannerImageUrl'];
const channelImage = snippet['thumbnails']['high']['url'];
const channelData = data.items[0];
// Channels normally have links in their channel description. This makes them clickable.
const channelDescription = autolinker.link(brandingSettings['channel']['description']);
// Add commas to sub count to make them more readable.
let subCount = statistics['subscriberCount'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
// Grab the channelView.html template and fill it in with the above variables.
const channelViewTemplate = require('./templates/channelView.html');
mustache.parse(channelViewTemplate);
const rendered = mustache.render(channelViewTemplate, {
channelName: channelName,
channelImage: channelImage,
channelBanner: channelBanner,
channelId: channelId,
subCount: subCount,
channelDescription: channelDescription,
isSubscribed: subscribeText,
channelName: channelData.brandingSettings.channel.title,
channelBanner: channelData.brandingSettings.image.bannerImageUrl,
channelImage: channelData.snippet.thumbnails.high.url,
subCount: channelData.statistics.subscriberCount.toLocaleString(), //toLocaleString adds commas as thousands separators
channelDescription: autolinker.link(channelData.brandingSettings.channel.description), //autolinker makes URLs clickable
subButtonText: subButtonText,
});
// Render the template on to #main
$('#main').html(rendered);
stopLoadingAnimation();
// Grab the channel's latest upload. API forces a max of 50.
// Grab the channel's latest uploads. API forces a max of 50.
youtubeAPI('search', {
part: 'snippet',
channelId: channelId,
@ -103,7 +81,7 @@ function goToChannel(channelId) {
}, function (data) {
// Display recent uploads to #main
data['items'].forEach((video) => {
displayVideos(video);
displayVideo(video);
});
});
});

View File

@ -32,31 +32,26 @@ let showComments = function(event) {
if (comments.css('display') === 'none') {
comments.attr('loaded', 'true');
const commentsTemplate = require('./templates/comments.html');
youtubeAPI('commentThreads', {
'videoId': $('#comments').attr('data-video-id'),
'part': 'snippet,replies',
'maxResults': 100,
}, function (data){
let comments = [];
let items = data.items;
commentsTemplate.done((template) => {
youtubeAPI('commentThreads', {
'videoId': $('#comments').attr('data-video-id'),
'part': 'snippet,replies',
'maxResults': 100,
}, function (data){
let comments = [];
let items = data.items;
items.forEach((object) => {
let snippet = object.snippet.topLevelComment.snippet;
items.forEach((object) => {
let snippet = object['snippet']['topLevelComment']['snippet'];
let dateString = new Date(snippet.publishedAt);
let publishedDate = dateFormat(dateString, "mmm dS, yyyy");
snippet.publishedAt = dateFormat(new Date(snippet.publishedAt), "mmm dS, yyyy");
snippet.publishedAt = publishedDate;
comments.push(snippet);
})
const html = mustache.render(template, {
comments: comments,
});
$('#comments').html(html);
comments.push(snippet);
})
const commentsTemplate = require('./templates/comments.html');
const html = mustache.render(commentsTemplate, {
comments: comments,
});
$('#comments').html(html);
});
comments.show();

View File

@ -82,7 +82,7 @@ function showHistory(){
}, function (data) {
createVideoListContainer('Watch History:');
data['items'].forEach((video) => {
displayVideos(video, 'history');
displayVideo(video, 'history');
});
stopLoadingAnimation()
});

View File

@ -28,7 +28,7 @@ let win;
if(require('electron-squirrel-startup')) app.quit();
/**
* initialize the electron application
* Initialize the Electron application
* 1. create the browser window
* 2. load the index.html
*/
@ -93,14 +93,14 @@ let init = function() {
};
/**
* quit the application
* Quit the application
*/
let allWindowsClosed = function() {
app.quit();
};
/**
* on mac, when dock icon is clicked,
* On Mac, when dock icon is clicked,
* create a new window and launch the editor
*/
let active = function() {

View File

@ -23,10 +23,10 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
*/
// Add general variables. Please put all require statements here.
const Datastore = require('nedb'); // Database logic
const Datastore = require('nedb'); // database logic
window.$ = window.jQuery = require('jquery');
const mustache = require('mustache'); // Templating
const dateFormat = require('dateformat'); // Formating Dates
const mustache = require('mustache'); // templating
const dateFormat = require('dateformat'); // formatting dates
// Used for finding links within text and making them clickable. Used mostly for video descriptions.
const autolinker = require('autolinker');
@ -41,6 +41,7 @@ const localDataStorage = electron.remote.app.getPath('userData'); // Grabs the u
const clipboard = electron.clipboard;
const getOpml = require('opml-to-json'); // Gets the file type for imported files.
const fs = require('fs'); // Used to read files. Specifically in the settings page.
let currentTheme = '';
let apiKey;
let dialog = require('electron').remote.dialog; // Used for opening file browser to export / import subscriptions.
@ -51,25 +52,21 @@ require.extensions['.html'] = function (module, filename) {
module.exports = fs.readFileSync(filename, 'utf8');
};
// Subscriptions database file
const subDb = new Datastore({
filename: localDataStorage + '/subscriptions.db',
autoload: true
});
// History database file
const historyDb = new Datastore({
filename: localDataStorage + '/videohistory.db',
autoload: true
});
// Saved videos database file
const savedVidsDb = new Datastore({
filename: localDataStorage + '/savedvideos.db',
autoload: true
});
// Settings database file.
const settingsDb = new Datastore({
filename: localDataStorage + 'settings.db',
autoload: true
@ -155,10 +152,10 @@ function startLoadingAnimation() {
const searchBar = document.getElementById('search');
const goToVideoInput = document.getElementById('jumpToInput');
loading.style.display = 'inherit';
sideNavDisabled.style.display = 'inherit';
searchBar.disabled = true;
goToVideoInput.disabled = true;
loading.style.display = 'inherit';
sideNavDisabled.style.display = 'inherit';
searchBar.disabled = true;
goToVideoInput.disabled = true;
}
function stopLoadingAnimation() {
const loading = document.getElementById('loading');
@ -166,10 +163,10 @@ function stopLoadingAnimation() {
const searchBar = document.getElementById('search');
const goToVideoInput = document.getElementById('jumpToInput');
loading.style.display = 'none';
sideNavDisabled.style.display = 'none';
searchBar.disabled = false;
goToVideoInput.disabled = false;
loading.style.display = 'none';
sideNavDisabled.style.display = 'none';
searchBar.disabled = false;
goToVideoInput.disabled = false;
}
/**
@ -203,7 +200,6 @@ function showAbout(){
clearMainContainer();
startLoadingAnimation();
// Grab about.html to be used as a template
const aboutTemplate = require('./templates/about.html')
mustache.parse(aboutTemplate);
$('#main').html(
@ -215,10 +211,10 @@ function showAbout(){
}
/**
* Display a toast message in the bottom right corner of the page. Toast will
* automatically disappear after 5 seconds.
* Display a toast message in the bottom right corner of the page.
* The toast automatically disappears after a timeout.
*
* @param {string} message - The message to be displayed in the toast.
* @param {string} message - The toast message.
*
* @return {Void}
*/
@ -234,7 +230,7 @@ function showToast(message){
toast.style.opacity = 0.9;
// Set the timer for the toast to be removed.
toastTimeout = window.setTimeout(hideToast, 5000); // 5 seconds
toastTimeout = window.setTimeout(hideToast, 5000);
}
/**

View File

@ -126,12 +126,12 @@ function showSavedVideos(){
// TODO: Allow the app to show more than 50 saved videos.
if(docs.length > 49){
for (let i = 0; i < 49; i++) {
videoList = videoList + ',' + docs[i]['videoId'];
videoList = videoList + ',' + docs[i].videoId;
}
}
else{
docs.forEach((video) => {
videoList = videoList + ',' + video['videoId'];
videoList = videoList + ',' + video.videoId;
});
}
@ -143,8 +143,8 @@ function showSavedVideos(){
}, function (data) {
// Render the videos to the screen
createVideoListContainer('Saved Videos:');
data['items'].forEach((video) => {
displayVideos(video, 'history');
data.items.forEach((video) => {
displayVideo(video, 'history');
});
stopLoadingAnimation();
});

View File

@ -135,9 +135,6 @@ function updateSettings() {
var themeSwitch = document.getElementById('themeSwitch').checked;
var key = document.getElementById('api-key').value;
// To any third party devs that fork the project, please be ethical and change the API keys.
const apiKeyBank = ['AIzaSyC9E579nh_qqxg6BH4xIce3k_7a9mT4uQc', 'AIzaSyCKplYT6hZIlm2O9FbWTi1G7rkpsLNTq78', 'AIzaSyAE5xzh5GcA_tEDhXmMFd1pEzrL-W7z51E', 'AIzaSyDoFzqwuO9l386eF6BmNkVapjiTJ93CBy4', 'AIzaSyBljfZFPioB0TRJAj-0LS4tlIKl2iucyY4'];
apiKey = apiKeyBank[Math.floor(Math.random() * apiKeyBank.length)];
if (themeSwitch == true) {

View File

@ -136,13 +136,13 @@ function loadSubscriptions() {
if(videoList.length < 100){
videoList.forEach((video) => {
console.log('Getting all videos');
displayVideos(video);
displayVideo(video);
});
}
else{
console.log('Getting top 100 videos');
for(let i = 0; i < 100; i++){
displayVideos(videoList[i]);
displayVideo(videoList[i]);
}
}
stopLoadingAnimation()

View File

@ -17,10 +17,6 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
/*
* File for functions related to videos.
*/
/**
* Perform a search using the YouTube API. The search query is grabbed from the #search element.
*
@ -32,7 +28,6 @@ function search(nextPageToken = '') {
const query = document.getElementById('search').value;
if (query === '') {
showToast('Search Field empty. Please input a search term.');
return;
}
@ -44,7 +39,6 @@ function search(nextPageToken = '') {
showToast('Fetching results. Please wait...');
}
// Start API request
youtubeAPI('search', {
q: query,
part: 'id, snippet',
@ -52,12 +46,11 @@ function search(nextPageToken = '') {
pageToken: nextPageToken,
maxResults: 25,
}, function (data){
console.log(data);
if (nextPageToken === '') {
createVideoListContainer('Search Results:');
createVideoListContainer('Search results:');
stopLoadingAnimation();
}
data.items.forEach(displayVideos);
data.items.forEach(displayVideo);
addNextPage(data.nextPageToken);
})
}
@ -71,47 +64,45 @@ function search(nextPageToken = '') {
*
* @return {Void}
*/
function displayVideos(video, listType = null) {
function displayVideo(video, listType = null) {
const videoSnippet = video['snippet'];
const videoSnippet = video.snippet;
// Grab the published date for the video and convert to a user readable state.
const dateString = new Date(videoSnippet['publishedAt']);
const dateString = new Date(videoSnippet.publishedAt);
const publishedDate = dateFormat(dateString, "mmm dS, yyyy");
let deleteHtml = '';
let liveText = '';
let videoId = video['id']['videoId'];
let videoId = video.id.videoId;
const searchMenu = $('#videoListContainer').html();
// Include a remove icon in the list if the application is displaying the history list or saved videos.
if (listType === 'saved') {
videoId = video['id'];
videoId = video.id;
deleteHtml = '<i onclick="removeSavedVideo(\'' + videoId + '\'); showSavedVideos();" class="videoDelete fas fa-times"></i>';
} else if (listType === 'history') {
videoId = video['id'];
videoId = video.id;
deleteHtml = '<i onclick="removeFromHistory(\'' + videoId + '\'); showHistory()" class="videoDelete fas fa-times"></i>';
}
let liveText = '';
// Includes text if the video is live.
if (videoSnippet['liveBroadcastContent'] === 'live') {
if (videoSnippet.liveBroadcastContent === 'live') {
liveText = 'LIVE NOW';
}
// Grab the search template for the video.
const videoListTemplate = require('./templates/videoList.html');
// Render / Manipulate the template. Replace variables with data from the video.
mustache.parse(videoListTemplate);
const rendered = mustache.render(videoListTemplate, {
videoThumbnail: videoSnippet['thumbnails']['medium']['url'],
videoTitle: videoSnippet['title'],
channelName: videoSnippet['channelTitle'],
videoDescription: videoSnippet['description'],
videoId: videoId,
videoThumbnail: videoSnippet.thumbnails.medium.url,
videoTitle: videoSnippet.title,
channelName: videoSnippet.channelTitle,
videoDescription: videoSnippet.description,
channelId: videoSnippet.channelId,
publishedDate: publishedDate,
liveText: liveText,
videoId: videoId,
channelId: videoSnippet['channelId'],
deleteHtml: deleteHtml,
});
// Apply the render to the page
@ -165,22 +156,16 @@ function showVideoRecommendations(videoId) {
}, function (data){
const recommendations = data.items;
recommendations.forEach((data) => {
const snippet = data['snippet'];
const videoId = data['id']['videoId'];
const videoTitle = snippet['title'];
const channelName = snippet['channelTitle'];
const videoThumbnail = snippet['thumbnails']['medium']['url'];
const dateString = snippet['publishedAt'];
const publishedDate = dateFormat(dateString, "mmm dS, yyyy");
const snippet = data.snippet;
const recommTemplate = require('./templates/recommendations.html')
mustache.parse(recommTemplate);
const rendered = mustache.render(recommTemplate, {
videoId: videoId,
videoTitle: videoTitle,
channelName: channelName,
videoThumbnail: videoThumbnail,
publishedDate: publishedDate,
videoId: data.id.videoId,
videoTitle: snippet.title,
channelName: snippet.channelTitle,
videoThumbnail: snippet.thumbnails.medium.url,
publishedDate: dateFormat(snippet.publishedAt, "mmm dS, yyyy")
});
const recommendationHtml = $('#recommendations').html();
$('#recommendations').html(recommendationHtml + rendered);
@ -228,7 +213,6 @@ function showMostPopular() {
// Get the date of 2 days ago.
var d = new Date();
d.setDate(d.getDate() - 2);
console.log(d.toString());
// Grab all videos published 2 days ago and after and order them by view count.
// These are the videos that are considered as 'most popular' and is how similar
@ -243,7 +227,7 @@ function showMostPopular() {
}, function (data){
createVideoListContainer('Most Popular:');
stopLoadingAnimation();
data['items'].forEach(displayVideos);
data.items.forEach(displayVideo);
});
}
@ -284,7 +268,6 @@ function getChannelAndPlayer(videoId) {
resolve([embedHtml, data.items[0].snippet.channelId]);
});
});
}
/**

View File

@ -22,10 +22,10 @@ function youtubeAPI(resource, params, success) {
}
/**
* Use youtube-dl to get the info for a video.
* Use youtube-dl to resolve a video.
*
* @param {string} videoId - The video Id to get info from.
* @param {function} callback - The callback function when the call is finished.
* @param {function} callback - The function called on success with the info.
*
* @return {Void}
*/

View File

@ -1,7 +1,5 @@
<h1 class='center'>FreeTube {{versionNumber}} Beta</h1>
<br />
<h2 class='center'>This software is FOSS and released under the <a href='https://www.gnu.org/licenses/quick-guide-gplv3.html'>GNU Public License v3</a></h2>
<br />
<p class='center'>
Found a bug? Want to suggest a feature? Want to help out? Check out our <a href='https://github.com/FreeTubeApp/FreeTube'>GitHub</a> page. Pull requests are welcome.
</p>
<div class='center'>
<h1>FreeTube {{versionNumber}} Beta</h1>
<h2>This software is FOSS and released under the <a href='https://www.gnu.org/licenses/quick-guide-gplv3.html'>GNU Public License v3+</a>.</h2>
Found a bug? Want to suggest a feature? Want to help out? Check out our <a href='https://github.com/FreeTubeApp/FreeTube'>GitHub</a> page. Pull requests are welcome.
</div>

View File

@ -6,7 +6,7 @@
<br />
<span class='channelViewSubs'>{{subCount}} Subscribers</span>
<div id='subscribeButton' class='channelSubButton' onclick='toggleSubscription("{{channelId}}");'>
{{isSubscribed}}
{{subButtonText}}
</div>
</div>
<br />