[WIP] Early Progress on Playlist support

This commit is contained in:
Preston 2018-04-08 21:04:41 -04:00
parent a64ba1e2f0
commit 195eb20b03
10 changed files with 344 additions and 193 deletions

View File

@ -59,7 +59,6 @@
},
"dependencies": {
"autolinker": "^1.6.2",
"dashjs": "^2.6.7",
"dateformat": "^3.0.3",
"electron-compile": "^6.4.2",
"electron-squirrel-startup": "^1.0.0",

View File

@ -52,14 +52,15 @@
<div id="sideNav">
<div class="sideNavContainer">
<ul>
<li onclick='loadSubscriptions()'><i class="fas fa-users"></i>&nbsp;&nbsp;Subscriptions</li>
<li onclick='showMostPopular()'><i class="fas fa-fire"></i>&nbsp;&nbsp;Most Popular</li>
<li onclick='loadSubscriptions()'><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='showSavedVideos()'><i class="fas fa-list-ul"></i>&nbsp;&nbsp;Playlists</li>
<li onclick='showHistory()'><i class="fas fa-history"></i>&nbsp;&nbsp;History</li>
</ul>
<hr />
<ul>
<li onclick='showSettings()'><i class="fas fa-cog"></i>&nbsp;&nbsp;Settings</li>
<li onclick='showSettings()'><i class="fas fa-sliders-h"></i>&nbsp;&nbsp;Settings</li>
<li onclick='showAbout()'><i class="far fa-question-circle"></i>&nbsp;&nbsp;About</li>
</ul>
<hr />

View File

@ -1,4 +1,4 @@
/*
/*
This file is part of FreeTube.
FreeTube is free software: you can redistribute it and/or modify
@ -200,7 +200,12 @@ let videoShortcutHandler = function(event) {
};
let fullscreenVideo = function(event){
$('.videoPlayer').get(0).webkitRequestFullscreen();
if (document.webkitFullscreenElement !== null){
document.webkitExitFullscreen();
}
else{
$('.videoPlayer').get(0).webkitRequestFullscreen();
}
}
/**

View File

@ -18,9 +18,9 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
/*
* File for main layout manipulation and general variable configuration.
* There are some functions from other files that will probably need to be moved to here.
*/
* File for main layout manipulation and general variable configuration.
* There are some functions from other files that will probably need to be moved to here.
*/
// Add general variables. Please put all require statements here.
const Datastore = require('nedb'); // database logic
@ -28,6 +28,8 @@ window.$ = window.jQuery = require('jquery');
const mustache = require('mustache'); // templating
const dateFormat = require('dateformat'); // formatting dates
const RxPlayer = require('rx-player'); // formatting dates
// Used for finding links within text and making them clickable. Used mostly for video descriptions.
const autolinker = require('autolinker');
const electron = require('electron');
@ -49,8 +51,8 @@ let dialog = require('electron').remote.dialog; // Used for opening file browser
let toastTimeout; // Timeout for toast notifications.
let mouseTimeout; // Timeout for hiding the mouse cursor on video playback
require.extensions['.html'] = function (module, filename) {
module.exports = fs.readFileSync(filename, 'utf8');
require.extensions['.html'] = function(module, filename) {
module.exports = fs.readFileSync(filename, 'utf8');
};
const subDb = new Datastore({
@ -78,14 +80,14 @@ const settingsDb = new Datastore({
checkDefaultSettings();
// Ppen links externally by default
$(document).on('click', 'a[href^="http"]', (event) =>{
$(document).on('click', 'a[href^="http"]', (event) => {
let el = event.currentTarget;
event.preventDefault();
shell.openExternal(el.href);
});
// Open links externally on middle click.
$(document).on('auxclick', 'a[href^="http"]', (event) =>{
$(document).on('auxclick', 'a[href^="http"]', (event) => {
let el = event.currentTarget;
event.preventDefault();
shell.openExternal(el.href);
@ -119,10 +121,10 @@ $(document).ready(() => {
});
/**
* Toggle the ability to view the side navigation bar.
*
* @return {Void}
*/
* Toggle the ability to view the side navigation bar.
*
* @return {Void}
*/
function toggleSideNavigation() {
const sideNav = document.getElementById('sideNav');
const mainContainer = document.getElementById('main');
@ -137,10 +139,10 @@ function toggleSideNavigation() {
}
/**
* Clears out the #main container to allow other information to be shown.
*
* @return {Void}
*/
* Clears out the #main container to allow other information to be shown.
*
* @return {Void}
*/
function clearMainContainer() {
const container = document.getElementById('main');
container.innerHTML = '';
@ -158,6 +160,7 @@ function startLoadingAnimation() {
searchBar.disabled = true;
goToVideoInput.disabled = true;
}
function stopLoadingAnimation() {
const loading = document.getElementById('loading');
const sideNavDisabled = document.getElementById('sideNavDisabled');
@ -171,12 +174,12 @@ function stopLoadingAnimation() {
}
/**
* Creates a div container in #main meant to be a container for video lists.
*
* @param {string} headerLabel - The header of the container. Not used for showing video recommendations.
*
* @return {Void}
*/
* Creates a div container in #main meant to be a container for video lists.
*
* @param {string} headerLabel - The header of the container. Not used for showing video recommendations.
*
* @return {Void}
*/
function createVideoListContainer(headerLabel = '') {
const videoListContainer = document.createElement("div");
videoListContainer.id = 'videoListContainer';
@ -192,11 +195,11 @@ function createVideoListContainer(headerLabel = '') {
}
/**
* Displays the about page to #main
*
* @return {Void}
*/
function showAbout(){
* Displays the about page to #main
*
* @return {Void}
*/
function showAbout() {
// Remove current information and display loading animation
clearMainContainer();
startLoadingAnimation();
@ -212,14 +215,14 @@ function showAbout(){
}
/**
* Display a toast message in the bottom right corner of the page.
* The toast automatically disappears after a timeout.
*
* @param {string} message - The toast message.
*
* @return {Void}
*/
function showToast(message){
* Display a toast message in the bottom right corner of the page.
* The toast automatically disappears after a timeout.
*
* @param {string} message - The toast message.
*
* @return {Void}
*/
function showToast(message) {
let toast = document.getElementById('toast');
let toastMessage = document.getElementById('toastMessage');
@ -235,27 +238,27 @@ function showToast(message){
}
/**
* Hide the toast notification from the page.
*
* @return {Void}
*/
function hideToast(){
* Hide the toast notification from the page.
*
* @return {Void}
*/
function hideToast() {
let toast = document.getElementById('toast');
toast.style.opacity = 0;
toast.style.visibility = 'hidden';
}
/**
* Displays a confirmation box before performing an action. The action will be performed
* if the user clicks 'yes'.
*
* @param {string} message - The message to be displayed in the confirmation box
* @param {function} performFunction - The function to be performed upon confirmation
* @param {*} parameters - The parameters that will be sent to performFunction
*
* @return {Void}
*/
function confirmFunction(message, performFunction, parameters){
* Displays a confirmation box before performing an action. The action will be performed
* if the user clicks 'yes'.
*
* @param {string} message - The message to be displayed in the confirmation box
* @param {function} performFunction - The function to be performed upon confirmation
* @param {*} parameters - The parameters that will be sent to performFunction
*
* @return {Void}
*/
function confirmFunction(message, performFunction, parameters) {
let confirmContainer = document.getElementById('confirmFunction');
let confirmMessage = document.getElementById('confirmMessage');
@ -269,44 +272,43 @@ function confirmFunction(message, performFunction, parameters){
}
/**
* Hides the confirmation box. Happens when the user clicks on 'no'.
*
* @return {Void}
*/
function hideConfirmFunction(){
* Hides the confirmation box. Happens when the user clicks on 'no'.
*
* @return {Void}
*/
function hideConfirmFunction() {
let confirmContainer = document.getElementById('confirmFunction');
confirmContainer.style.visibility = 'hidden';
}
/**
* Hide the mouse cursor after ~3 seconds. Used to hide the video when the user
* hovers the mouse over the video player.
*
* @return {Void}
*/
function hideMouseTimeout(){
* Hide the mouse cursor after ~3 seconds. Used to hide the video when the user
* hovers the mouse over the video player.
*
* @return {Void}
*/
function hideMouseTimeout() {
$('.videoPlayer')[0].style.cursor = 'default';
clearTimeout(mouseTimeout);
mouseTimeout = window.setTimeout(function(){
mouseTimeout = window.setTimeout(function() {
$('.videoPlayer')[0].style.cursor = 'none';
}, 3150);
}
/**
* Remove the timeout for the mouse cursor as a fallback.
*
* @return {Void}
*/
function removeMouseTimeout(){
* Remove the timeout for the mouse cursor as a fallback.
*
* @return {Void}
*/
function removeMouseTimeout() {
$('.videoPlayer')[0].style.cursor = 'default';
clearTimeout(mouseTimeout);
}
function showVideoOptions(element){
if (element.nextElementSibling.style.display == 'none' || element.nextElementSibling.style.display == ''){
element.nextElementSibling.style.display = 'inline-block'
}
else{
element.nextElementSibling.style.display = 'none'
}
function showVideoOptions(element) {
if (element.nextElementSibling.style.display == 'none' || element.nextElementSibling.style.display == '') {
element.nextElementSibling.style.display = 'inline-block'
} else {
element.nextElementSibling.style.display = 'none'
}
}

View File

@ -143,7 +143,7 @@ function playVideo(videoId, videoThumbnail = '') {
}
if (!useEmbedPlayer) {
videoHtml = '<video data-dashjs-player class="videoPlayer" type="application/x-mpegURL" onmousemove="hideMouseTimeout()" onmouseleave="removeMouseTimeout()" controls="" src="' + defaultUrl + '" poster="' + videoThumbnail + '" autoplay>';
videoHtml = '<video class="videoPlayer" type="application/x-mpegURL" onmousemove="hideMouseTimeout()" onmouseleave="removeMouseTimeout()" controls="" src="' + defaultUrl + '" poster="' + videoThumbnail + '" autoplay>';
if(typeof(info.player_response.captions) === 'object'){

View File

@ -18,12 +18,12 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
/**
* Perform a search using the YouTube API. The search query is grabbed from the #search element.
*
* @param {string} nextPageToken - Optional: The page token to be inlcuded in the search.
*
* @return {Void}
*/
* Perform a search using the YouTube API. The search query is grabbed from the #search element.
*
* @param {string} nextPageToken - Optional: The page token to be inlcuded in the search.
*
* @return {Void}
*/
function search(nextPageToken = '') {
const query = document.getElementById('search').value;
@ -42,13 +42,45 @@ function search(nextPageToken = '') {
youtubeAPI('search', {
q: query,
part: 'id',
type: 'video',
pageToken: nextPageToken,
maxResults: 25,
}, function (data){
let grabDuration = getDuration(data.items);
}, function(data) {
console.log(data);
let channels = data.items.filter((item) => {
if (item.id.kind === 'youtube#channel') {
return true;
}
});
let playlists = data.items.filter((item) => {
if (item.id.kind === 'youtube#playlist') {
return true;
}
});
let videos = data.items.filter((item) => {
if (item.id.kind === 'youtube#video') {
return true;
}
});
console.log(channels);
console.log(typeof(channels));
console.log(playlists);
if(playlists.length > 0){
displayPlaylists(playlists);
}
if(channels.length > 0){
displayChannels(channels);
}
let grabDuration = getDuration(videos);
grabDuration.then((videoList) => {
console.log(videoList);
videoList.items.forEach(displayVideo);
});
@ -61,30 +93,27 @@ function search(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){
* 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'){
for (let i = 0; i < data.length; i++) {
if (videoIdList === '') {
if (typeof(data[i]['id']) === 'string') {
videoIdList = data[i]['id'];
}
else{
} else {
videoIdList = data[i]['id']['videoId'];
}
}
else{
if(typeof(data[i]['id']) === 'string'){
} else {
if (typeof(data[i]['id']) === 'string') {
videoIdList = videoIdList + ', ' + data[i]['id'];
}
else{
} else {
videoIdList = videoIdList + ', ' + data[i]['id']['videoId'];
}
}
@ -100,14 +129,14 @@ function getDuration(data){
}
/**
* Display a video on the page. Function is typically contained in a loop.
*
* @param {video} video - The video ID of the video to be displayed.
* @param {string} listType - Optional: Specifies the list type of the video
* Used for displaying the remove icon for history and saved videos.
*
* @return {Void}
*/
* Display a video on the page. Function is typically contained in a loop.
*
* @param {video} video - The video ID of the video to be displayed.
* @param {string} listType - Optional: Specifies the list type of the video
* Used for displaying the remove icon for history and saved videos.
*
* @return {Void}
*/
function displayVideo(video, listType = '') {
const videoSnippet = video.snippet;
@ -132,18 +161,18 @@ function displayVideo(video, listType = '') {
};
// Includes text if the video is live.
const liveText = (videoSnippet.liveBroadcastContent === 'live')? 'LIVE NOW' : '';
const liveText = (videoSnippet.liveBroadcastContent === 'live') ? 'LIVE NOW' : '';
const videoListTemplate = require('./templates/videoList.html');
mustache.parse(videoListTemplate);
const rendered = mustache.render(videoListTemplate, {
videoId: videoId,
videoThumbnail: videoSnippet.thumbnails.medium.url,
videoTitle: videoSnippet.title,
channelName: videoSnippet.channelTitle,
videoThumbnail: videoSnippet.thumbnails.medium.url,
videoTitle: videoSnippet.title,
channelName: videoSnippet.channelTitle,
videoDescription: videoSnippet.description,
channelId: videoSnippet.channelId,
videoDuration: videoDuration,
channelId: videoSnippet.channelId,
videoDuration: videoDuration,
publishedDate: publishedDate,
liveText: liveText,
deleteHtml: deleteHtml,
@ -158,13 +187,95 @@ function displayVideo(video, listType = '') {
}
}
function displayChannels(channels) {
let channelIds;
channels.forEach((channel) => {
if (typeof(channelIds) === 'undefined') {
channelIds = channel.id.channelId;
} else {
channelIds = channelIds + ',' + channel.id.channelId;
}
});
console.log(channelIds);
youtubeAPI('channels', {
part: 'snippet,statistics',
id: channelIds,
}, function(data) {
console.log(data);
let items = data['items'].reverse();
const videoListTemplate = require('./templates/channelList.html');
console.log(items);
items.forEach((item) => {
mustache.parse(videoListTemplate);
let rendered = mustache.render(videoListTemplate, {
channelId: item.id,
channelThumbnail: item.snippet.thumbnails.medium.url,
channelName: item.snippet.title,
channelDescription: item.snippet.description,
subscriberCount: item.statistics.subscriberCount,
videoCount: item.statistics.videoCount,
});
$(rendered).insertBefore('#getNextPage');
});
});
}
function displayPlaylists(playlists) {
let playlistIds;
playlists.forEach((playlist) => {
if (typeof(playlistIds) === 'undefined') {
playlistIds = playlist.id.playlistId;
} else {
playlistIds = playlistIds + ',' + playlist.id.playlistId;
}
});
console.log(playlistIds);
youtubeAPI('playlists', {
part: 'snippet,contentDetails',
id: playlistIds,
}, function(data) {
console.log(data);
let items = data['items'].reverse();
const playlistListTemplate = require('./templates/playlistList.html');
console.log(items);
items.forEach((item) => {
let dateString = new Date(item.snippet.publishedAt);
let publishedDate = dateFormat(dateString, "mmm dS, yyyy");
mustache.parse(playlistListTemplate);
let rendered = mustache.render(playlistListTemplate, {
channelId: item.snippet.channelId,
channelName: item.snippet.channelTitle,
playlistThumbnail: item.snippet.thumbnails.medium.url,
playlistTitle: item.snippet.title,
playlistDescription: item.snippet.description,
videoCount: item.contentDetails.itemCount,
publishedDate: publishedDate,
});
$(rendered).insertBefore('#getNextPage');
});
});
}
/**
* Changes the page token to the next page button during a video search.
*
* @param {string} nextPageToken - The page token to replace the button function.
*
* @return {Void}
*/
* Changes the page token to the next page button during a video search.
*
* @param {string} nextPageToken - The page token to replace the button function.
*
* @return {Void}
*/
function addNextPage(nextPageToken) {
let oldFetchButton = document.getElementById('getNextPage');
@ -185,19 +296,19 @@ function addNextPage(nextPageToken) {
}
/**
* Grab the video recommendations for a video. This does not get recommendations based on what you watch,
* as that would defeat the main purpose of using FreeTube. At any time you can check the video on HookTube
* and compare the recommendations there. They should be nearly identical.
*
* @param {string} videoId - The video ID of the video to get recommendations from.
*/
* Grab the video recommendations for a video. This does not get recommendations based on what you watch,
* as that would defeat the main purpose of using FreeTube. At any time you can check the video on HookTube
* and compare the recommendations there. They should be nearly identical.
*
* @param {string} videoId - The video ID of the video to get recommendations from.
*/
function showVideoRecommendations(videoId) {
youtubeAPI('search', {
part: 'id',
type: 'video',
relatedToVideoId: videoId,
maxResults: 15,
}, function (data){
}, function(data) {
let grabDuration = getDuration(data.items);
grabDuration.then((videoList) => {
videoList.items.forEach((video) => {
@ -208,10 +319,10 @@ function showVideoRecommendations(videoId) {
mustache.parse(recommTemplate);
const rendered = mustache.render(recommTemplate, {
videoId: video.id,
videoTitle: snippet.title,
channelName: snippet.channelTitle,
videoTitle: snippet.title,
channelName: snippet.channelTitle,
videoThumbnail: snippet.thumbnails.medium.url,
videoDuration: videoDuration,
videoDuration: videoDuration,
publishedDate: dateFormat(snippet.publishedAt, "mmm dS, yyyy")
});
const recommendationHtml = $('#recommendations').html();
@ -222,11 +333,11 @@ function showVideoRecommendations(videoId) {
}
/**
* Check if a link is a valid YouTube/HookTube link and play that video. Gets input
* from the #jumpToInput element.
*
* @return {Void}
*/
* Check if a link is a valid YouTube/HookTube link and play that video. Gets input
* from the #jumpToInput element.
*
* @return {Void}
*/
function parseVideoLink() {
let input = document.getElementById('jumpToInput').value;
@ -250,19 +361,19 @@ 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){
* 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/, '');
return x.replace(/\D/, '');
}
});
@ -270,30 +381,25 @@ function parseVideoDuration(durationString){
let minutes = (parseInt(match[1]) || 0);
let seconds = (parseInt(match[2]) || 0);
if (hours != 0){
if (hours != 0) {
duration = hours + ':';
}
else{
} else {
duration = minutes + ':';
}
if (hours != 0 && minutes < 10){
if (hours != 0 && minutes < 10) {
duration = duration + '0' + minutes + ':';
}
else if (hours != 0 && minutes > 10){
} else if (hours != 0 && minutes > 10) {
duration = duration + minutes + ':';
}
else if (hours != 0 && minutes == 0){
} else if (hours != 0 && minutes == 0) {
duration = duration + '00:';
}
if (seconds == 0){
if (seconds == 0) {
duration = duration + '00';
}
else if (seconds < 10){
} else if (seconds < 10) {
duration = duration + '0' + seconds;
}
else{
} else {
duration = duration + seconds;
}
@ -301,10 +407,10 @@ function parseVideoDuration(durationString){
}
/**
* Grab the most popular videos over the last couple of days and display them.
*
* @return {Void}
*/
* Grab the most popular videos over the last couple of days and display them.
*
* @return {Void}
*/
function showMostPopular() {
clearMainContainer();
startLoadingAnimation();
@ -323,7 +429,7 @@ function showMostPopular() {
type: 'video',
publishedAfter: d.toISOString(),
maxResults: 50,
}, function (data){
}, function(data) {
createVideoListContainer('Most Popular:');
console.log(data);
let grabDuration = getDuration(data.items);
@ -337,13 +443,13 @@ function showMostPopular() {
}
/**
* Create a link of the video to HookTube or YouTube and copy it to the user's clipboard.
*
* @param {string} website - The website to watch the video on.
* @param {string} videoId - The video ID of the video to add to the URL
*
* @return {Void}
*/
* Create a link of the video to HookTube or YouTube and copy it to the user's clipboard.
*
* @param {string} website - The website to watch the video on.
* @param {string} videoId - The video ID of the video to add to the URL
*
* @return {Void}
*/
function copyLink(website, videoId) {
// Create the URL and copy to the clipboard.
const url = 'https://' + website + '.com/watch?v=' + videoId;
@ -352,19 +458,19 @@ function copyLink(website, videoId) {
}
/**
* Get the YouTube embeded player of a video as well as channel information..
*
* @param {string} videoId - The video ID of the video to get.
*
* @return {promise} - The HTML of the embeded player
*/
* Get the YouTube embeded player of a video as well as channel information..
*
* @param {string} videoId - The video ID of the video to get.
*
* @return {promise} - The HTML of the embeded player
*/
function getChannelAndPlayer(videoId) {
console.log(videoId);
return new Promise((resolve, reject) => {
youtubeAPI('videos', {
part: 'snippet,player',
id: videoId,
}, function (data){
}, function(data) {
let embedHtml = data.items[0].player.embedHtml;
embedHtml = embedHtml.replace('src="', 'src="https:');
embedHtml = embedHtml.replace('width="480px"', '');
@ -376,20 +482,20 @@ function getChannelAndPlayer(videoId) {
}
/**
* Check to see if the video URLs are valid. Change the video quality if one is not.
* The API will grab video URLs, but they will sometimes return a 404. This
* is why this check is needed. The video URL will typically be resolved over time.
*
* @param {string} video480p - The URL to the 480p video.
* @param {string} video720p - The URL to the 720p video.
*/
* Check to see if the video URLs are valid. Change the video quality if one is not.
* The API will grab video URLs, but they will sometimes return a 404. This
* is why this check is needed. The video URL will typically be resolved over time.
*
* @param {string} video480p - The URL to the 480p video.
* @param {string} video720p - The URL to the 720p video.
*/
function checkVideoUrls(video480p, video720p) {
const currentQuality = $('#currentQuality').html();
let buttonEmbed = document.getElementById('qualityEmbed');
let valid480 = false;
if (typeof(video480p) !== 'undefined'){
if (typeof(video480p) !== 'undefined') {
let get480pUrl = fetch(video480p);
get480pUrl.then((status) => {
switch (status.status) {
@ -412,7 +518,7 @@ function checkVideoUrls(video480p, video720p) {
break;
default:
console.log('480p is valid');
if (currentQuality === '720p' && typeof(video720p) === 'undefined'){
if (currentQuality === '720p' && typeof(video720p) === 'undefined') {
changeQuality(video480p);
}
break;
@ -420,7 +526,7 @@ function checkVideoUrls(video480p, video720p) {
});
}
if (typeof(video720p) !== 'undefined'){
if (typeof(video720p) !== 'undefined') {
let get720pUrl = fetch(video720p);
get720pUrl.then((status) => {
switch (status.status) {
@ -430,7 +536,7 @@ function checkVideoUrls(video480p, video720p) {
$(document).on('click', '#quality720p', (event) => {
changeQuality('');
});
if (typeof(valid480) !== 'undefined'){
if (typeof(valid480) !== 'undefined') {
changeQuality(video480p, '480p');
}
break;
@ -444,7 +550,7 @@ function checkVideoUrls(video480p, video720p) {
break;
default:
console.log('720p is valid');
if (currentQuality === '720p'){
if (currentQuality === '720p') {
return;
}
break;

View File

@ -27,6 +27,21 @@ iframe {
width: 100%;
}
.channelThumbnail {
width: 140px;
height: 140px;
float: left;
cursor: pointer;
position: relative;
left: 60px;
}
.channelThumbnail img{
width: 100%;
border-radius: 200px 200px 200px 200px;
-webkit-border-radius: 200px 200px 200px 200px;
}
.videoOptions {
float: right;
width: 50px;

View File

@ -0,0 +1,9 @@
<div class='video'>
<div class='channelThumbnail'>
<img onclick='goToChannel("{{channelId}}")' src={{channelThumbnail}} />
</div>
<p onclick='goToChannel("{{channelId}}")' class='videoTitle'>{{channelName}}</p>
<p onclick='goToChannel("{{channelId}}")' class='channelName'>{{subscriberCount}} subscribers - {{videoCount}} videos</p>
<p onclick='goToChannel("{{channelId}}")' class='videoDescription'>{{channelDescription}}</p>
</div>
<hr />

View File

@ -0,0 +1,10 @@
<div class='video'>
<div class='videoThumbnail'>
<img onclick='console.log("Not Implemented")' src={{playlistThumbnail}} />
</div>
<p onclick='goToChannel("{{channelId}}")' class='videoTitle'>{{playlistTitle}}</p>
<p onclick='goToChannel("{{channelId}}")' class='channelName'>{{channelName}} - {{publishedDate}}</p>
<p onclick='goToChannel("{{channelId}}")' class='videoDescription'>{{playlistDescription}}</p>
<p>VIEW FULL PLAYLIST ({{videoCount}} videos)</p>
</div>
<hr />

4
src/templates/video.html Normal file
View File

@ -0,0 +1,4 @@
<video class="videoPlayer" onmousemove="hideMouseTimeout()" onmouseleave="removeMouseTimeout()" controls="" poster="{{videoThumbnail}}" autoplay>
<source src="{{videoUrl}}" type="video/mp4">
{{subtitleElements}}
</video>