mirror of https://github.com/FreeTubeApp/FreeTube
Features: Video Duration, Double click to full screen and more.
Loading Subscriptions is MUCH faster.
This commit is contained in:
parent
5729aa2c8b
commit
1ac6ce7dce
|
@ -65,7 +65,6 @@
|
|||
"jquery": "^3.3.1",
|
||||
"mustache": "^2.3.0",
|
||||
"nedb": "^1.8.0",
|
||||
"node-async-loop": "^1.2.2",
|
||||
"opml-to-json": "0.0.3",
|
||||
"youtube-dl": "^1.12.2"
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 361 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
@ -80,8 +80,12 @@ function goToChannel(channelId) {
|
|||
order: 'date',
|
||||
}, function (data) {
|
||||
// Display recent uploads to #main
|
||||
data['items'].forEach((video) => {
|
||||
displayVideo(video);
|
||||
let grabDuration = getDuration(data.items);
|
||||
|
||||
grabDuration.then((videoList) => {
|
||||
videoList.items.forEach((video) => {
|
||||
displayVideo(video, 'history');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -199,6 +199,10 @@ let videoShortcutHandler = function(event) {
|
|||
}
|
||||
};
|
||||
|
||||
let fullscreenVideo = function(event){
|
||||
$('.videoPlayer').get(0).webkitRequestFullscreen()
|
||||
}
|
||||
|
||||
/**
|
||||
* ---------------------------
|
||||
* Bind click events
|
||||
|
@ -208,6 +212,8 @@ $(document).on('click', '#showComments', showComments);
|
|||
|
||||
$(document).on('click', '.videoPlayer', playPauseVideo);
|
||||
|
||||
$(document).on('dblclick', '.videoPlayer', fullscreenVideo);
|
||||
|
||||
$(document).on('keydown', videoShortcutHandler);
|
||||
|
||||
$(document).on('click', '#confirmNo', hideConfirmFunction);
|
||||
|
|
|
@ -81,8 +81,12 @@ function showHistory(){
|
|||
maxResults: 50,
|
||||
}, function (data) {
|
||||
createVideoListContainer('Watch History:');
|
||||
data['items'].forEach((video) => {
|
||||
displayVideo(video, 'history');
|
||||
let grabDuration = getDuration(data.items);
|
||||
|
||||
grabDuration.then((videoList) => {
|
||||
videoList.items.forEach((video) => {
|
||||
displayVideo(video, 'history');
|
||||
});
|
||||
});
|
||||
stopLoadingAnimation()
|
||||
});
|
||||
|
|
|
@ -69,17 +69,6 @@ function playVideo(videoId) {
|
|||
return;
|
||||
}
|
||||
|
||||
const checkSubscription = isSubscribed(channelId);
|
||||
|
||||
// Change the subscribe button text depending on if the user has subscribed to the channel or not.
|
||||
checkSubscription.then((results) => {
|
||||
if (results === false) {
|
||||
subscribeText = 'SUBSCRIBE';
|
||||
} else {
|
||||
subscribeText = 'UNSUBSCRIBE';
|
||||
}
|
||||
});
|
||||
|
||||
const checkSavedVideo = videoIsSaved(videoId);
|
||||
|
||||
// Change the save button icon and text depending on if the user has saved the video or not.
|
||||
|
@ -94,7 +83,7 @@ function playVideo(videoId) {
|
|||
});
|
||||
|
||||
/*
|
||||
* FreeTube calls youtube-dl server to grab the direct video URL.
|
||||
* FreeTube calls youtube-dl to grab the direct video URL.
|
||||
*/
|
||||
youtubedlGetInfo(videoId, (info) => {
|
||||
console.log(info);
|
||||
|
@ -109,7 +98,6 @@ function playVideo(videoId) {
|
|||
let dateString = info['upload_date'];
|
||||
dateString = [dateString.slice(0, 4), '-', dateString.slice(4)].join('');
|
||||
dateString = [dateString.slice(0, 7), '-', dateString.slice(7)].join('');
|
||||
console.log(dateString);
|
||||
const publishedDate = dateFormat(dateString, "mmm dS, yyyy");
|
||||
|
||||
// Figure out the width for the like/dislike bar.
|
||||
|
@ -127,7 +115,6 @@ function playVideo(videoId) {
|
|||
|
||||
// Grab all subtitles
|
||||
Object.keys(videoSubtitles).forEach((subtitle) => {
|
||||
console.log(subtitle);
|
||||
|
||||
subtitleLabel = subtitle.toUpperCase();
|
||||
subtitleUrl = videoSubtitles[subtitle]['url'];
|
||||
|
@ -142,7 +129,6 @@ function playVideo(videoId) {
|
|||
|
||||
// Search through the returned object to get the 480p and 720p video URLs (If available)
|
||||
Object.keys(videoUrls).forEach((key) => {
|
||||
console.log(key);
|
||||
switch (videoUrls[key]['format_id']) {
|
||||
case '18':
|
||||
video480p = videoUrls[key]['url'];
|
||||
|
@ -173,11 +159,18 @@ function playVideo(videoId) {
|
|||
|
||||
if (!useEmbedPlayer) {
|
||||
videoHtml = '<video class="videoPlayer" onmousemove="hideMouseTimeout()" onmouseleave="removeMouseTimeout()" controls="" src="' + defaultUrl + '" poster="' + videoThumbnail + '" autoplay>' + subtitleHtml + '</video>';
|
||||
|
||||
console.log(videoHtml);
|
||||
}
|
||||
|
||||
console.log(defaultUrl);
|
||||
const checkSubscription = isSubscribed(channelId);
|
||||
|
||||
// Change the subscribe button text depending on if the user has subscribed to the channel or not.
|
||||
checkSubscription.then((results) => {
|
||||
if (results === false) {
|
||||
subscribeText = 'SUBSCRIBE';
|
||||
} else {
|
||||
subscribeText = 'UNSUBSCRIBE';
|
||||
}
|
||||
});
|
||||
|
||||
// API Request
|
||||
youtubeAPI('channels', {
|
||||
|
|
|
@ -143,8 +143,11 @@ function showSavedVideos(){
|
|||
}, (data) => {
|
||||
// Render the videos to the screen
|
||||
createVideoListContainer('Saved Videos:');
|
||||
data.items.forEach((video) => {
|
||||
displayVideo(video, 'saved');
|
||||
let grabDuration = getDuration(data.items);
|
||||
grabDuration.then((videoList) => {
|
||||
videoList.items.forEach((video) => {
|
||||
displayVideo(video, 'saved');
|
||||
});
|
||||
});
|
||||
stopLoadingAnimation();
|
||||
});
|
||||
|
|
|
@ -18,23 +18,23 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
|
||||
/*
|
||||
* File for all functions related to subscriptions.
|
||||
*/
|
||||
* File for all functions related to subscriptions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a channel to the user's subscription database.
|
||||
*
|
||||
* @param {string} channelId - The channel ID to add to the subscriptions database.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
* Add a channel to the user's subscription database.
|
||||
*
|
||||
* @param {string} channelId - The channel ID to add to the subscriptions database.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function addSubscription(channelId, useToast = true) {
|
||||
console.log(channelId);
|
||||
// Request YouTube API
|
||||
youtubeAPI('channels', {
|
||||
part: 'snippet',
|
||||
id: channelId,
|
||||
}, function (data){
|
||||
}, function(data) {
|
||||
const channelInfo = data['items'][0]['snippet'];
|
||||
const channelName = channelInfo['title'];
|
||||
const thumbnail = channelInfo['thumbnails']['high']['url'];
|
||||
|
@ -47,7 +47,7 @@ function addSubscription(channelId, useToast = true) {
|
|||
|
||||
// Refresh the list of subscriptions on the side navigation bar.
|
||||
subDb.insert(channel, (err, newDoc) => {
|
||||
if (useToast){
|
||||
if (useToast) {
|
||||
showToast('Added ' + channelName + ' to subscriptions.');
|
||||
displaySubs();
|
||||
}
|
||||
|
@ -56,12 +56,12 @@ function addSubscription(channelId, useToast = true) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Remove a channel from the subscriptions database.
|
||||
*
|
||||
* @param {string} channelId - The channel ID to be removed.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
* Remove a channel from the subscriptions database.
|
||||
*
|
||||
* @param {string} channelId - The channel ID to be removed.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function removeSubscription(channelId) {
|
||||
subDb.remove({
|
||||
channelId: channelId
|
||||
|
@ -73,10 +73,10 @@ function removeSubscription(channelId) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Load the recent uploads of the user's subscriptions.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
* Load the recent uploads of the user's subscriptions.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function loadSubscriptions() {
|
||||
clearMainContainer();
|
||||
const loading = document.getElementById('loading');
|
||||
|
@ -89,8 +89,82 @@ function loadSubscriptions() {
|
|||
|
||||
// Welcome to callback hell, we hope you enjoy your stay.
|
||||
subscriptions.then((results) => {
|
||||
let channelId = '';
|
||||
let videoList = [];
|
||||
|
||||
if (results.length > 0) {
|
||||
let counter = 0;
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
channelId = results[i]['channelId'];
|
||||
|
||||
youtubeAPI('search', {
|
||||
part: 'snippet',
|
||||
channelId: channelId,
|
||||
type: 'video',
|
||||
maxResults: 15,
|
||||
order: 'date',
|
||||
}, (data) => {
|
||||
console.log(data);
|
||||
videoList = videoList.concat(data.items);
|
||||
counter++;
|
||||
if (counter === results.length) {
|
||||
videoList.sort((a, b) => {
|
||||
const date1 = Date.parse(a.snippet.publishedAt);
|
||||
const date2 = Date.parse(b.snippet.publishedAt);
|
||||
|
||||
return date2.valueOf() - date1.valueOf();
|
||||
});
|
||||
|
||||
// Render the videos to the application.
|
||||
createVideoListContainer('Latest Subscriptions:');
|
||||
|
||||
// The YouTube website limits the subscriptions to 100 before grabbing more so we only show 100
|
||||
// to keep the app running at a good speed.
|
||||
if (videoList.length < 50) {
|
||||
let grabDuration = getDuration(videoList.slice(0, 49));
|
||||
|
||||
grabDuration.then((list) => {
|
||||
list.items.forEach((video) => {
|
||||
displayVideo(video);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.log(videoList);
|
||||
let finishedList = [];
|
||||
let firstBatchDuration = getDuration(videoList.slice(0, 49));
|
||||
|
||||
firstBatchDuration.then((list1) => {
|
||||
finishedList = finishedList.concat(list1.items);
|
||||
let secondBatchDuration = getDuration(videoList.slice(50, 99));
|
||||
|
||||
secondBatchDuration.then((list2) => {
|
||||
finishedList = finishedList.concat(list2.items);
|
||||
finishedList.forEach((video) => {
|
||||
displayVideo(video);
|
||||
});
|
||||
stopLoadingAnimation();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
// User has no subscriptions. Display message.
|
||||
const container = document.getElementById('main');
|
||||
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>`;
|
||||
}
|
||||
|
||||
|
||||
// Yes, This function is the thing that needs to most improvment
|
||||
/*
|
||||
if (results.length > 0) {
|
||||
showToast('Getting Subscriptions. This may take a while...');
|
||||
|
||||
|
@ -101,68 +175,84 @@ function loadSubscriptions() {
|
|||
* while then sorting them afterwards, this was my best solution at the time. I'm sure someone more
|
||||
* experienced in Node can help out with this.
|
||||
*/
|
||||
asyncLoop(results, (sub, next) => {
|
||||
const channelId = sub['channelId'];
|
||||
/*
|
||||
asyncLoop(results, (sub, next) => {
|
||||
const channelId = sub['channelId'];
|
||||
|
||||
/*
|
||||
* Grab the channels 15 most recent uploads. Typically this should be enough.
|
||||
* This number can be changed if we feel necessary.
|
||||
*/
|
||||
youtubeAPI('search', {
|
||||
part: 'snippet', // Try getting content details for video duration in the near future.
|
||||
channelId: channelId,
|
||||
type: 'video',
|
||||
maxResults: 15,
|
||||
order: 'date',
|
||||
}, function (data){
|
||||
videoList = videoList.concat(data['items']);
|
||||
// Iterate through the next object in the loop.
|
||||
next();
|
||||
});
|
||||
}, (err) => {
|
||||
// Sort the videos by date
|
||||
videoList.sort((a, b) => {
|
||||
const date1 = Date.parse(a.snippet.publishedAt);
|
||||
const date2 = Date.parse(b.snippet.publishedAt);
|
||||
/*
|
||||
* Grab the channels 15 most recent uploads. Typically this should be enough.
|
||||
* This number can be changed if we feel necessary.
|
||||
*/
|
||||
/*
|
||||
youtubeAPI('search', {
|
||||
part: 'snippet',
|
||||
channelId: channelId,
|
||||
type: 'video',
|
||||
maxResults: 15,
|
||||
order: 'date',
|
||||
}, (data) => {
|
||||
videoList = videoList.concat(data.items);
|
||||
|
||||
return date2.valueOf() - date1.valueOf();
|
||||
});
|
||||
next();
|
||||
});
|
||||
}, (err) => {
|
||||
// Sort the videos by date
|
||||
videoList.sort((a, b) => {
|
||||
const date1 = Date.parse(a.snippet.publishedAt);
|
||||
const date2 = Date.parse(b.snippet.publishedAt);
|
||||
|
||||
// Render the videos to the application.
|
||||
createVideoListContainer('Latest Subscriptions:');
|
||||
return date2.valueOf() - date1.valueOf();
|
||||
});
|
||||
|
||||
// The YouTube website limits the subscriptions to 100 before grabbing more so we only show 100
|
||||
// to keep the app running at a good speed.
|
||||
if(videoList.length < 100){
|
||||
videoList.forEach((video) => {
|
||||
console.log('Getting all videos');
|
||||
displayVideo(video);
|
||||
// Render the videos to the application.
|
||||
createVideoListContainer('Latest Subscriptions:');
|
||||
|
||||
// The YouTube website limits the subscriptions to 100 before grabbing more so we only show 100
|
||||
// to keep the app running at a good speed.
|
||||
if(videoList.length < 50){
|
||||
let grabDuration = getDuration(videoList.slice(0,49));
|
||||
|
||||
grabDuration.then((list) => {
|
||||
list.items.forEach((video) => {
|
||||
displayVideo(video);
|
||||
});
|
||||
});
|
||||
}
|
||||
else{
|
||||
let finishedList = []
|
||||
let firstBatchDuration = getDuration(videoList.slice(0, 49));
|
||||
|
||||
firstBatchDuration.then((list1) => {
|
||||
finishedList = finishedList.concat(list1.items);
|
||||
let secondBatchDuration = getDuration(videoList.slice(50, 99));
|
||||
|
||||
secondBatchDuration.then((list2) => {
|
||||
finishedList = finishedList.concat(list2.items);
|
||||
finishedList.forEach((video) => {
|
||||
displayVideo(video);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
stopLoadingAnimation()
|
||||
});
|
||||
}
|
||||
else{
|
||||
console.log('Getting top 100 videos');
|
||||
for(let i = 0; i < 100; i++){
|
||||
displayVideo(videoList[i]);
|
||||
}
|
||||
}
|
||||
stopLoadingAnimation()
|
||||
});
|
||||
} else {
|
||||
// User has no subscriptions. Display message.
|
||||
const container = document.getElementById('main');
|
||||
stopLoadingAnimation()
|
||||
} else {
|
||||
// User has no subscriptions. Display message.
|
||||
const container = document.getElementById('main');
|
||||
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>`;
|
||||
}
|
||||
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>`;
|
||||
}
|
||||
});*/
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of subscriptions from the user's subscription database.
|
||||
*
|
||||
* @return {promise} The list of subscriptions.
|
||||
*/
|
||||
* Get the list of subscriptions from the user's subscription database.
|
||||
*
|
||||
* @return {promise} The list of subscriptions.
|
||||
*/
|
||||
function returnSubscriptions() {
|
||||
return new Promise((resolve, reject) => {
|
||||
subDb.find({}, (err, subs) => {
|
||||
|
@ -172,10 +262,10 @@ function returnSubscriptions() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Display the list of subscriptions on the side navigation bar.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
* Display the list of subscriptions on the side navigation bar.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function displaySubs() {
|
||||
const subList = document.getElementById('subscriptions');
|
||||
|
||||
|
@ -205,11 +295,11 @@ function displaySubs() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds / Removes a subscription based on if the channel is in the database or not.
|
||||
* @param {string} channelId - The channel ID to check
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
* Adds / Removes a subscription based on if the channel is in the database or not.
|
||||
* @param {string} channelId - The channel ID to check
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function toggleSubscription(channelId) {
|
||||
event.stopPropagation();
|
||||
|
||||
|
@ -219,12 +309,12 @@ function toggleSubscription(channelId) {
|
|||
checkIfSubscribed.then((results) => {
|
||||
|
||||
if (results === false) {
|
||||
if(subscribeButton != null){
|
||||
if (subscribeButton != null) {
|
||||
subscribeButton.innerHTML = 'UNSUBSCRIBE';
|
||||
}
|
||||
addSubscription(channelId);
|
||||
} else {
|
||||
if(subscribeButton != null){
|
||||
if (subscribeButton != null) {
|
||||
subscribeButton.innerHTML = 'SUBSCRIBE';
|
||||
}
|
||||
removeSubscription(channelId);
|
||||
|
@ -233,15 +323,17 @@ function toggleSubscription(channelId) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if the user is subscribed to a channel or not.
|
||||
*
|
||||
* @param {string} channelId - The channel ID to check
|
||||
*
|
||||
* @return {promise} - A boolean value if the channel is currently subscribed or not.
|
||||
*/
|
||||
* Check if the user is subscribed to a channel or not.
|
||||
*
|
||||
* @param {string} channelId - The channel ID to check
|
||||
*
|
||||
* @return {promise} - A boolean value if the channel is currently subscribed or not.
|
||||
*/
|
||||
function isSubscribed(channelId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
subDb.find({channelId: channelId}, (err, docs) => {
|
||||
subDb.find({
|
||||
channelId: channelId
|
||||
}, (err, docs) => {
|
||||
if (jQuery.isEmptyObject(docs)) {
|
||||
resolve(false);
|
||||
} else {
|
||||
|
|
147
src/js/videos.js
147
src/js/videos.js
|
@ -41,20 +41,64 @@ function search(nextPageToken = '') {
|
|||
|
||||
youtubeAPI('search', {
|
||||
q: query,
|
||||
part: 'id, snippet',
|
||||
part: 'id',
|
||||
type: 'video',
|
||||
pageToken: nextPageToken,
|
||||
maxResults: 25,
|
||||
}, function (data){
|
||||
let grabDuration = getDuration(data.items);
|
||||
|
||||
grabDuration.then((videoList) => {
|
||||
videoList.items.forEach(displayVideo);
|
||||
});
|
||||
|
||||
if (nextPageToken === '') {
|
||||
createVideoListContainer('Search results:');
|
||||
stopLoadingAnimation();
|
||||
}
|
||||
data.items.forEach(displayVideo);
|
||||
addNextPage(data.nextPageToken);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab the duration of the videos
|
||||
*
|
||||
* @param {array} data - An array of videos to get the duration from
|
||||
*
|
||||
* @return {promise} - The list of videos with the duration included.
|
||||
*/
|
||||
function getDuration(data){
|
||||
return new Promise((resolve, reject) => {
|
||||
let videoIdList = '';
|
||||
|
||||
for(let i = 0; i < data.length; i++){
|
||||
if (videoIdList === ''){
|
||||
if(typeof(data[i]['id']) === 'string'){
|
||||
videoIdList = data[i]['id'];
|
||||
}
|
||||
else{
|
||||
videoIdList = data[i]['id']['videoId'];
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(typeof(data[i]['id']) === 'string'){
|
||||
videoIdList = videoIdList + ', ' + data[i]['id'];
|
||||
}
|
||||
else{
|
||||
videoIdList = videoIdList + ', ' + data[i]['id']['videoId'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
youtubeAPI('videos', {
|
||||
part: 'snippet, contentDetails',
|
||||
id: videoIdList
|
||||
}, (data) => {
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a video on the page. Function is typically contained in a loop.
|
||||
*
|
||||
|
@ -66,9 +110,14 @@ function search(nextPageToken = '') {
|
|||
*/
|
||||
function displayVideo(video, listType = '') {
|
||||
const videoSnippet = video.snippet;
|
||||
|
||||
const videoDuration = parseVideoDuration(video.contentDetails.duration);
|
||||
//const videoDuration = '00:00';
|
||||
|
||||
// Grab the published date for the video and convert to a user readable state.
|
||||
const dateString = new Date(videoSnippet.publishedAt);
|
||||
const publishedDate = dateFormat(dateString, "mmm dS, yyyy");
|
||||
|
||||
const searchMenu = $('#videoListContainer').html();
|
||||
const videoId = video.id;
|
||||
|
||||
|
@ -94,6 +143,7 @@ function displayVideo(video, listType = '') {
|
|||
channelName: videoSnippet.channelTitle,
|
||||
videoDescription: videoSnippet.description,
|
||||
channelId: videoSnippet.channelId,
|
||||
videoDuration: videoDuration,
|
||||
publishedDate: publishedDate,
|
||||
liveText: liveText,
|
||||
deleteHtml: deleteHtml,
|
||||
|
@ -143,26 +193,30 @@ function addNextPage(nextPageToken) {
|
|||
*/
|
||||
function showVideoRecommendations(videoId) {
|
||||
youtubeAPI('search', {
|
||||
part: 'snippet',
|
||||
part: 'id',
|
||||
type: 'video',
|
||||
relatedToVideoId: videoId,
|
||||
maxResults: 15,
|
||||
}, function (data){
|
||||
const recommendations = data.items;
|
||||
recommendations.forEach((data) => {
|
||||
const snippet = data.snippet;
|
||||
let grabDuration = getDuration(data.items);
|
||||
grabDuration.then((videoList) => {
|
||||
videoList.items.forEach((video) => {
|
||||
const snippet = video.snippet;
|
||||
const videoDuration = parseVideoDuration(video.contentDetails.duration);
|
||||
|
||||
const recommTemplate = require('./templates/recommendations.html')
|
||||
mustache.parse(recommTemplate);
|
||||
const rendered = mustache.render(recommTemplate, {
|
||||
videoId: data.id.videoId,
|
||||
videoTitle: snippet.title,
|
||||
channelName: snippet.channelTitle,
|
||||
videoThumbnail: snippet.thumbnails.medium.url,
|
||||
publishedDate: dateFormat(snippet.publishedAt, "mmm dS, yyyy")
|
||||
const recommTemplate = require('./templates/recommendations.html')
|
||||
mustache.parse(recommTemplate);
|
||||
const rendered = mustache.render(recommTemplate, {
|
||||
videoId: video.id,
|
||||
videoTitle: snippet.title,
|
||||
channelName: snippet.channelTitle,
|
||||
videoThumbnail: snippet.thumbnails.medium.url,
|
||||
videoDuration: videoDuration,
|
||||
publishedDate: dateFormat(snippet.publishedAt, "mmm dS, yyyy")
|
||||
});
|
||||
const recommendationHtml = $('#recommendations').html();
|
||||
$('#recommendations').html(recommendationHtml + rendered);
|
||||
});
|
||||
const recommendationHtml = $('#recommendations').html();
|
||||
$('#recommendations').html(recommendationHtml + rendered);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -195,6 +249,57 @@ function parseVideoLink() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert duration into a more readable format
|
||||
*
|
||||
* @param {string} durationString - The string containing the video duration. Formated as 'PT12H34M56S'
|
||||
*
|
||||
* @return {string} - The formated string. Ex: 12:34:56
|
||||
*/
|
||||
function parseVideoDuration(durationString){
|
||||
let match = durationString.match(/PT(\d+H)?(\d+M)?(\d+S)?/);
|
||||
let duration = '';
|
||||
|
||||
match = match.slice(1).map(function(x) {
|
||||
if (x != null) {
|
||||
return x.replace(/\D/, '');
|
||||
}
|
||||
});
|
||||
|
||||
let hours = (parseInt(match[0]) || 0);
|
||||
let minutes = (parseInt(match[1]) || 0);
|
||||
let seconds = (parseInt(match[2]) || 0);
|
||||
|
||||
if (hours != 0){
|
||||
duration = hours + ':';
|
||||
}
|
||||
else{
|
||||
duration = minutes + ':';
|
||||
}
|
||||
|
||||
if (hours != 0 && minutes < 10){
|
||||
duration = duration + '0' + minutes + ':';
|
||||
}
|
||||
else if (hours != 0 && minutes > 10){
|
||||
duration = duration + minutes + ':';
|
||||
}
|
||||
else if (hours != 0 && minutes == 0){
|
||||
duration = duration + '00:';
|
||||
}
|
||||
|
||||
if (seconds == 0){
|
||||
duration = duration + '00';
|
||||
}
|
||||
else if (seconds < 10){
|
||||
duration = duration + '0' + seconds;
|
||||
}
|
||||
else{
|
||||
duration = duration + seconds;
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab the most popular videos over the last couple of days and display them.
|
||||
*
|
||||
|
@ -213,15 +318,21 @@ function showMostPopular() {
|
|||
// Applications grab these. Videos in the 'Trending' tab on YouTube will be different.
|
||||
// And there is no way to grab those videos.
|
||||
youtubeAPI('search', {
|
||||
part: 'snippet',
|
||||
part: 'id',
|
||||
order: 'viewCount',
|
||||
type: 'video',
|
||||
publishedAfter: d.toISOString(),
|
||||
maxResults: 50,
|
||||
}, function (data){
|
||||
createVideoListContainer('Most Popular:');
|
||||
console.log(data);
|
||||
let grabDuration = getDuration(data.items);
|
||||
|
||||
grabDuration.then((videoList) => {
|
||||
console.log(videoList);
|
||||
videoList.items.forEach(displayVideo);
|
||||
});
|
||||
stopLoadingAnimation();
|
||||
data.items.forEach(displayVideo);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
iframe{
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 41.25vw;
|
||||
}
|
||||
|
||||
#main{
|
||||
margin-top: 80px;
|
||||
margin-left: 250px;
|
||||
#main {
|
||||
margin-top: 80px;
|
||||
margin-left: 250px;
|
||||
}
|
||||
|
||||
.video{
|
||||
.video {
|
||||
width: 95%;
|
||||
max-width: 1000px;
|
||||
height: 145px;
|
||||
|
@ -16,68 +16,90 @@ iframe{
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.videoThumbnail{
|
||||
.videoThumbnail {
|
||||
width: 275px;
|
||||
height: 160px;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.videoTitle{
|
||||
.videoThumbnail img{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.videoTitle {
|
||||
font-weight: bold;
|
||||
margin-left: 285px;
|
||||
margin-top: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.channelName{
|
||||
.channelName {
|
||||
margin-left: 285px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.videoDescription{
|
||||
.videoDescription {
|
||||
margin-left: 285px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
height: 45px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.videoPlayer{width: 100%;}
|
||||
|
||||
.statistics{
|
||||
padding: 20px;
|
||||
padding-bottom: 45px;
|
||||
.videoDuration {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
position: relative;
|
||||
bottom: 36px;
|
||||
color: white;
|
||||
background-color: black;
|
||||
opacity: 0.7;
|
||||
padding: 2px;
|
||||
font-size: 13px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.title{
|
||||
.videoPlayer {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.statistics {
|
||||
padding: 20px;
|
||||
padding-bottom: 45px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.views{
|
||||
.views {
|
||||
margin-top: -10px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.details{
|
||||
.details {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.likeContainer{
|
||||
.likeContainer {
|
||||
width: 300px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.likes{
|
||||
.likes {
|
||||
float: left;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dislikes{
|
||||
.dislikes {
|
||||
float: right;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dislikeBar{
|
||||
.dislikeBar {
|
||||
background-color: #9E9E9E;
|
||||
width: 300px;
|
||||
height: 5px;
|
||||
|
@ -86,14 +108,14 @@ iframe{
|
|||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
}
|
||||
|
||||
.likeBar{
|
||||
.likeBar {
|
||||
height: 5px;
|
||||
background-color: #2196F3;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
}
|
||||
|
||||
#channelIcon{
|
||||
#channelIcon {
|
||||
float: left;
|
||||
width: 80px;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
|
@ -115,26 +137,26 @@ iframe{
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
#channelName{
|
||||
#channelName {
|
||||
font-weight: bold;
|
||||
margin-left: 95px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#publishDate{
|
||||
#publishDate {
|
||||
margin-left: 95px;
|
||||
font-size: 13px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
#description{
|
||||
#description {
|
||||
white-space: pre-line;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.playerSubButton{
|
||||
.playerSubButton {
|
||||
float: right;
|
||||
width: 125px;
|
||||
height: 50px;
|
||||
|
@ -144,7 +166,7 @@ iframe{
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.smallButton{
|
||||
.smallButton {
|
||||
float: right;
|
||||
height: 30px;
|
||||
font-size: 10px;
|
||||
|
@ -156,15 +178,15 @@ iframe{
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.videoQuality{
|
||||
.videoQuality {
|
||||
width: 42px;
|
||||
}
|
||||
|
||||
.videoQuality:hover .qualityTypes{
|
||||
.videoQuality:hover .qualityTypes {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.qualityTypes{
|
||||
.qualityTypes {
|
||||
visibility: hidden;
|
||||
width: 72px;
|
||||
position: relative;
|
||||
|
@ -172,27 +194,27 @@ iframe{
|
|||
right: 15px;
|
||||
}
|
||||
|
||||
.qualityTypes ul{
|
||||
.qualityTypes ul {
|
||||
list-style-type: none;
|
||||
position: relative;
|
||||
right: 24px;
|
||||
}
|
||||
|
||||
.qualityTypes ul li{
|
||||
.qualityTypes ul li {
|
||||
width: 72px;
|
||||
position: relative;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
.videoSpeed{
|
||||
.videoSpeed {
|
||||
width: 42px;
|
||||
}
|
||||
|
||||
.videoSpeed:hover .speedTypes{
|
||||
.videoSpeed:hover .speedTypes {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.speedTypes{
|
||||
.speedTypes {
|
||||
visibility: hidden;
|
||||
width: 72px;
|
||||
position: relative;
|
||||
|
@ -200,19 +222,19 @@ iframe{
|
|||
right: 15px;
|
||||
}
|
||||
|
||||
.speedTypes ul{
|
||||
.speedTypes ul {
|
||||
list-style-type: none;
|
||||
position: relative;
|
||||
right: 24px;
|
||||
}
|
||||
|
||||
.speedTypes ul li{
|
||||
.speedTypes ul li {
|
||||
width: 72px;
|
||||
position: relative;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
#showComments{
|
||||
#showComments {
|
||||
text-align: center;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
|
@ -220,33 +242,38 @@ iframe{
|
|||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#recommendations{
|
||||
#recommendations {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.recommendVideo{
|
||||
.recommendVideo {
|
||||
cursor: pointer;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.recommendThumbnail{
|
||||
.recommendThumbnail {
|
||||
width: 250px;
|
||||
height: 145px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.recommendTitle{
|
||||
.recommendThumbnail img{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.recommendTitle {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-left: 260px;
|
||||
}
|
||||
|
||||
.recommendChannel{
|
||||
.recommendChannel {
|
||||
margin-left: 260px;
|
||||
font-size: 14px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.recommendDate{
|
||||
.recommendDate {
|
||||
margin-left: 260px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<div class='recommendVideo' onclick='playVideo("{{videoId}}")'>
|
||||
<image class='recommendThumbnail' src='{{videoThumbnail}}'></image>
|
||||
<div class='recommendThumbnail'>
|
||||
<img src='{{videoThumbnail}}'></img>
|
||||
<p onclick='playVideo("{{videoId}}")' class='videoDuration'>{{videoDuration}}</p>
|
||||
</div>
|
||||
<p class='recommendTitle'>{{videoTitle}}</p>
|
||||
<p class='recommendChannel'>{{channelName}}</p>
|
||||
<p class='recommendDate'>{{publishedDate}}</p>
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<div class='video'>
|
||||
{{{deleteHtml}}}
|
||||
<img onclick='playVideo("{{videoId}}")' src={{videoThumbnail}} class='videoThumbnail' />
|
||||
<div class='videoThumbnail'>
|
||||
<img onclick='playVideo("{{videoId}}")' src={{videoThumbnail}} />
|
||||
<p onclick='playVideo("{{videoId}}")' class='videoDuration'>{{videoDuration}}</p>
|
||||
</div>
|
||||
<p onclick='playVideo("{{videoId}}")' class='videoTitle'>{{videoTitle}}</p>
|
||||
<p onclick='goToChannel("{{channelId}}")' class='channelName'>{{channelName}} - {{publishedDate}}</p>
|
||||
<p onclick='playVideo("{{videoId}}")' class='videoDescription'>{{videoDescription}}</p>
|
||||
|
|
Loading…
Reference in New Issue