/*
This file is part of FreeTube.
FreeTube is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FreeTube is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FreeTube. If not, see .
*/
/*
* File for functions related to videos.
*/
let checkedVideoSettings = false;
/**
* Display the video player and play a video
*
* @param {string} videoId - The video ID of the video to be played.
*
* @return {Void}
*/
function playVideo(videoId, playlistId = '') {
hideViews();
let youtubedlFinished = false;
let invidiousFinished = false;
checkedVideoSettings = false;
playerView.firstLoad = true;
playerView.videoId = videoId;
playerView.videoAudio = undefined;
playerView.validAudio = true;
playerView.video360p = undefined;
playerView.valid360p = true;
playerView.video720p = undefined;
playerView.valid720p = true;
playerView.videoUrl = '';
playerView.currentTime = undefined;
playerView.subtitleHtml = '';
playerView.videoLive = undefined;
playerView.validLive = false;
playerView.validDash = true;
playerView.videoDash = invidiousInstance + '/api/manifest/dash/' + videoId + '.mpd?unique_res=1';
playerView.embededHtml = "";
let videoHtml = '';
let player;
switch (defaultPlayer) {
case 'dash':
playerView.playerSeen = true;
playerView.legacySeen = false;
break;
case 'legacy':
playerView.playerSeen = false;
playerView.legacySeen = true;
break;
case 'embed':
playerView.playerSeen = false;
playerView.legacySeen = false;
break;
default:
}
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) {
playerView.savedText = 'FAVORITE';
playerView.savedIconType = 'far unsaved';
} else {
playerView.savedText = 'FAVORITED';
playerView.savedIconType = 'fas saved';
}
});
if (getVideosLocally) {
youtubedlGetInfo(videoId, (data) => {
ft.log(data);
let videoUrls = data.formats;
if (data.player_response.videoDetails.isLiveContent !== false) {
playerView.validDash = false;
playerView.valid360p = false;
playerView.valid720p = false;
playerView.validAudio = false;
playerView.playerSeen = true;
playerView.legacySeen = false;
playerView.validLive = true;
youtubedlFinished = true;
if (youtubedlFinished && invidiousFinished) {
loadingView.seen = false;
if (subscriptionView.seen === false && aboutView.seen === false && headerView.seen === false && searchView.seen === false && settingsView.seen === false && popularView.seen === false && savedView.seen === false && historyView.seen === false && channelView.seen === false && channelVideosView.seen === false) {
playerView.seen = true;
} else {
return;
}
}
} else {
playerView.validLive = false;
// Search through the returned object to get the 360p and 720p video URLs (If available)
Object.keys(videoUrls).forEach((key) => {
switch (videoUrls[key]['itag']) {
case '18':
playerView.video360p = decodeURIComponent(videoUrls[key]['url']);
// ft.log(playerView.video360p);
break;
case '22':
playerView.video720p = decodeURIComponent(videoUrls[key]['url']);
// ft.log(playerView.video720p);
break;
}
});
if (videoUrls.length > 3) {
// Last adaptive format will be the best quality audio stream
playerView.videoAudio = decodeURIComponent(videoUrls[videoUrls.length - 1]['url']);
} else {
playerView.playerSeen = false;
playerView.legacySeen = true;
}
if (typeof(playerView.videoAudio) === 'undefined') {
ft.log(playerView.videoAudio);
playerView.validAudio = false;
}
let useEmbedPlayer = false;
// Default to the embeded player if the URLs cannot be found.
if (typeof(playerView.video720p) === 'undefined' && typeof(playerView.video360p) === 'undefined') {
//useEmbedPlayer = true;
playerView.currentQuality = 'EMBED';
playerView.playerSeen = false;
playerView.valid720p = false;
playerView.valid360p = false;
playerView.video720p = '';
//useEmbedPlayer = true;
showToast('Unable to get video file. Reverting to embeded player.');
} else if (typeof(playerView.video720p) === 'undefined' && typeof(playerView.video360p) !== 'undefined') {
// Default to the 360p video if the 720p URL cannot be found.
ft.log('Found');
playerView.videoUrl = playerView.video360p;
playerView.currentQuality = '360p';
playerView.valid720p = false;
playerView.video720p = '';
} else {
// Default to the 720p video.
playerView.videoUrl = playerView.video720p;
playerView.currentQuality = '720p';
//playerView.videoUrl = playerView.liveManifest;
}
}
youtubedlFinished = true;
if (youtubedlFinished && invidiousFinished) {
loadingView.seen = false;
if (subscriptionView.seen === false && aboutView.seen === false && headerView.seen === false && searchView.seen === false && settingsView.seen === false && popularView.seen === false && savedView.seen === false && historyView.seen === false && channelView.seen === false && channelVideosView.seen === false) {
playerView.seen = true;
} else {
return;
}
}
});
} else {
youtubedlFinished = true;
ft.log("Grabbing videos through Invidious");
}
invidiousAPI('videos', videoId, {}, (data) => {
ft.log(data);
// Figure out the width for the like/dislike bar.
playerView.videoLikes = data.likeCount;
playerView.videoDislikes = data.dislikeCount;
let totalLikes = parseInt(playerView.videoLikes) + parseInt(playerView.videoDislikes);
playerView.likePercentage = parseInt((playerView.videoLikes / totalLikes) * 100);
playerView.videoTitle = data.title;
playerView.channelName = data.author;
playerView.channelId = data.authorId;
playerView.channelIcon = data.authorThumbnails[2].url;
playerView.lengthSeconds = data.lengthSeconds;
if (playerView.channelIcon.includes('https:') === false) {
playerView.channelIcon = 'https:' + playerView.channelIcon;
}
// Add commas to the video view count.
playerView.videoViews = data.viewCount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
playerView.videoThumbnail = data.videoThumbnails[0].url;
// Format the date to a more readable format.
let dateString = new Date(data.published * 1000);
dateString.setDate(dateString.getDate() + 1);
playerView.publishedDate = dateFormat(dateString, "mmm dS, yyyy");
playerView.description = parseDescription(data.descriptionHtml);
const checkSubscription = isSubscribed(playerView.channelId);
checkSubscription.then((results) => {
if (results === false) {
playerView.subscribedText = 'SUBSCRIBE';
} else {
playerView.subscribedText = 'UNSUBSCRIBE';
}
});
playerView.recommendedVideoList = [];
data.recommendedVideos.forEach((video) => {
let data = {};
let time = video.lengthSeconds;
let hours = 0;
if (time >= 3600) {
hours = Math.floor(time / 3600);
time = time - hours * 3600;
}
let minutes = Math.floor(time / 60);
let seconds = time - minutes * 60;
if (seconds < 10) {
seconds = '0' + seconds;
}
if (hours > 0) {
data.duration = hours + ":" + minutes + ":" + seconds;
} else {
data.duration = minutes + ":" + seconds;
}
data.id = video.videoId;
data.title = video.title;
data.channelName = video.author;
data.thumbnail = video.videoThumbnails[4].url;
data.viewCount = video.viewCountText;
playerView.recommendedVideoList = playerView.recommendedVideoList.concat(data);
});
if (typeof(data.captions) !== 'undefined') {
data.captions.forEach((caption) => {
let subtitleUrl = invidiousInstance + caption.url;
videoHtml = videoHtml + '