2022-10-26 02:43:58 +00:00
'use strict' ;
/* globals checkMessages */
2021-09-05 17:10:14 +00:00
function sortChange ( sortValue ) {
2022-10-26 02:43:58 +00:00
let payload = JSON . stringify ( { sort _order : sortValue } ) ;
sendPost ( payload ) ;
setTimeout ( function ( ) {
location . reload ( ) ;
} , 500 ) ;
2021-09-05 17:10:14 +00:00
}
2022-03-10 13:20:23 +00:00
// Updates video watch status when passed a video id and it's current state (ex if the video was unwatched but you want to mark it as watched you will pass "unwatched")
function updateVideoWatchStatus ( input1 , videoCurrentWatchStatus ) {
2022-10-26 02:43:58 +00:00
let videoId ;
if ( videoCurrentWatchStatus ) {
videoId = input1 ;
} else if ( input1 . getAttribute ( 'data-id' ) ) {
videoId = input1 . getAttribute ( 'data-id' ) ;
videoCurrentWatchStatus = input1 . getAttribute ( 'data-status' ) ;
}
postVideoProgress ( videoId , 0 ) ; // Reset video progress on watched/unwatched;
removeProgressBar ( videoId ) ;
let watchStatusIndicator , payload ;
if ( videoCurrentWatchStatus === 'watched' ) {
watchStatusIndicator = createWatchStatusIndicator ( videoId , 'unwatched' ) ;
payload = JSON . stringify ( { un _watched : videoId } ) ;
sendPost ( payload ) ;
} else if ( videoCurrentWatchStatus === 'unwatched' ) {
watchStatusIndicator = createWatchStatusIndicator ( videoId , 'watched' ) ;
payload = JSON . stringify ( { watched : videoId } ) ;
sendPost ( payload ) ;
}
2022-03-10 13:20:23 +00:00
2022-10-26 02:43:58 +00:00
let watchButtons = document . getElementsByClassName ( 'watch-button' ) ;
for ( let i = 0 ; i < watchButtons . length ; i ++ ) {
if ( watchButtons [ i ] . getAttribute ( 'data-id' ) === videoId ) {
watchButtons [ i ] . outerHTML = watchStatusIndicator ;
2022-03-10 13:20:23 +00:00
}
2022-10-26 02:43:58 +00:00
}
2021-09-05 17:10:14 +00:00
}
2022-03-10 13:20:23 +00:00
// Creates a watch status indicator when passed a video id and the videos watch status
function createWatchStatusIndicator ( videoId , videoWatchStatus ) {
2022-10-26 02:43:58 +00:00
let seen , title ;
if ( videoWatchStatus === 'watched' ) {
seen = 'seen' ;
title = 'Mark as unwatched' ;
} else if ( videoWatchStatus === 'unwatched' ) {
seen = 'unseen' ;
title = 'Mark as watched' ;
}
let watchStatusIndicator = ` <img src="/static/img/icon- ${ seen } .svg" alt=" ${ seen } -icon" data-id=" ${ videoId } " data-status=" ${ videoWatchStatus } " onclick="updateVideoWatchStatus(this)" class="watch-button" title=" ${ title } "> ` ;
return watchStatusIndicator ;
2022-03-10 13:20:23 +00:00
}
// function isWatched(youtube_id) {
// var payload = JSON.stringify({'watched': youtube_id});
// sendPost(payload);
// var seenIcon = document.createElement('img');
// seenIcon.setAttribute('src', "/static/img/icon-seen.svg");
// seenIcon.setAttribute('alt', 'seen-icon');
// seenIcon.setAttribute('id', youtube_id);
// seenIcon.setAttribute('title', "Mark as unwatched");
// seenIcon.setAttribute('onclick', "isUnwatched(this.id)");
// seenIcon.classList = 'seen-icon';
// document.getElementById(youtube_id).replaceWith(seenIcon);
// }
2022-02-25 03:39:33 +00:00
// Removes the progress bar when passed a video id
function removeProgressBar ( videoId ) {
2022-10-26 02:43:58 +00:00
setProgressBar ( videoId , 0 , 1 ) ;
2022-02-25 03:39:33 +00:00
}
2021-11-20 03:58:25 +00:00
function isWatchedButton ( button ) {
2022-10-26 02:43:58 +00:00
let youtube _id = button . getAttribute ( 'data-id' ) ;
let payload = JSON . stringify ( { watched : youtube _id } ) ;
button . remove ( ) ;
sendPost ( payload ) ;
setTimeout ( function ( ) {
location . reload ( ) ;
} , 1000 ) ;
2021-11-20 03:58:25 +00:00
}
2022-03-10 13:20:23 +00:00
// function isUnwatched(youtube_id) {
// postVideoProgress(youtube_id, 0); // Reset video progress on unwatched;
// var payload = JSON.stringify({'un_watched': youtube_id});
// sendPost(payload);
// var unseenIcon = document.createElement('img');
// unseenIcon.setAttribute('src', "/static/img/icon-unseen.svg");
// unseenIcon.setAttribute('alt', 'unseen-icon');
// unseenIcon.setAttribute('id', youtube_id);
// unseenIcon.setAttribute('title', "Mark as watched");
// unseenIcon.setAttribute('onclick', "isWatched(this.id)");
// unseenIcon.classList = 'unseen-icon';
// document.getElementById(youtube_id).replaceWith(unseenIcon);
// }
2021-10-07 16:38:17 +00:00
2021-11-25 04:52:14 +00:00
function unsubscribe ( id _unsub ) {
2022-10-26 02:43:58 +00:00
let payload = JSON . stringify ( { unsubscribe : id _unsub } ) ;
sendPost ( payload ) ;
let message = document . createElement ( 'span' ) ;
message . innerText = 'You are unsubscribed.' ;
document . getElementById ( id _unsub ) . replaceWith ( message ) ;
2021-11-25 09:41:58 +00:00
}
function subscribe ( id _sub ) {
2022-10-26 02:43:58 +00:00
let payload = JSON . stringify ( { subscribe : id _sub } ) ;
sendPost ( payload ) ;
let message = document . createElement ( 'span' ) ;
message . innerText = 'You are subscribed.' ;
document . getElementById ( id _sub ) . replaceWith ( message ) ;
2021-09-05 17:10:14 +00:00
}
2021-10-01 07:42:17 +00:00
function changeView ( image ) {
2022-10-26 02:43:58 +00:00
let sourcePage = image . getAttribute ( 'data-origin' ) ;
let newView = image . getAttribute ( 'data-value' ) ;
let payload = JSON . stringify ( { change _view : sourcePage + ':' + newView } ) ;
sendPost ( payload ) ;
setTimeout ( function ( ) {
location . reload ( ) ;
} , 500 ) ;
2021-10-01 07:42:17 +00:00
}
2022-05-28 06:57:29 +00:00
function changeGridItems ( image ) {
2022-10-26 02:43:58 +00:00
let operator = image . getAttribute ( 'data-value' ) ;
let payload = JSON . stringify ( { change _grid : operator } ) ;
sendPost ( payload ) ;
setTimeout ( function ( ) {
location . reload ( ) ;
} , 500 ) ;
2022-05-28 06:57:29 +00:00
}
2021-10-03 11:17:07 +00:00
function toggleCheckbox ( checkbox ) {
2022-10-26 02:43:58 +00:00
// pass checkbox id as key and checkbox.checked as value
let toggleId = checkbox . id ;
let toggleVal = checkbox . checked ;
let payloadDict = { } ;
payloadDict [ toggleId ] = toggleVal ;
let payload = JSON . stringify ( payloadDict ) ;
sendPost ( payload ) ;
setTimeout ( function ( ) {
let currPage = window . location . pathname + window . location . search ;
window . location . replace ( currPage ) ;
} , 500 ) ;
2021-10-03 11:17:07 +00:00
}
2021-09-05 17:10:14 +00:00
// download page buttons
function rescanPending ( ) {
2022-10-26 02:43:58 +00:00
let payload = JSON . stringify ( { rescan _pending : true } ) ;
animate ( 'rescan-icon' , 'rotate-img' ) ;
sendPost ( payload ) ;
setTimeout ( function ( ) {
checkMessages ( ) ;
} , 500 ) ;
2021-09-05 17:10:14 +00:00
}
function dlPending ( ) {
2022-10-26 02:43:58 +00:00
let payload = JSON . stringify ( { dl _pending : true } ) ;
animate ( 'download-icon' , 'bounce-img' ) ;
sendPost ( payload ) ;
setTimeout ( function ( ) {
checkMessages ( ) ;
} , 500 ) ;
2021-09-05 17:10:14 +00:00
}
function toIgnore ( button ) {
2022-10-26 02:43:58 +00:00
let youtube _id = button . getAttribute ( 'data-id' ) ;
let payload = JSON . stringify ( { ignore : youtube _id } ) ;
sendPost ( payload ) ;
document . getElementById ( 'dl-' + youtube _id ) . remove ( ) ;
2021-09-05 17:10:14 +00:00
}
function downloadNow ( button ) {
2022-10-26 02:43:58 +00:00
let youtube _id = button . getAttribute ( 'data-id' ) ;
let payload = JSON . stringify ( { dlnow : youtube _id } ) ;
sendPost ( payload ) ;
document . getElementById ( youtube _id ) . remove ( ) ;
setTimeout ( function ( ) {
checkMessages ( ) ;
} , 500 ) ;
2021-09-05 17:10:14 +00:00
}
2021-09-28 09:53:45 +00:00
function forgetIgnore ( button ) {
2022-10-26 02:43:58 +00:00
let youtube _id = button . getAttribute ( 'data-id' ) ;
let payload = JSON . stringify ( { forgetIgnore : youtube _id } ) ;
sendPost ( payload ) ;
document . getElementById ( 'dl-' + youtube _id ) . remove ( ) ;
2021-09-28 09:53:45 +00:00
}
function addSingle ( button ) {
2022-10-26 02:43:58 +00:00
let youtube _id = button . getAttribute ( 'data-id' ) ;
let payload = JSON . stringify ( { addSingle : youtube _id } ) ;
sendPost ( payload ) ;
document . getElementById ( 'dl-' + youtube _id ) . remove ( ) ;
setTimeout ( function ( ) {
checkMessages ( ) ;
} , 500 ) ;
2021-09-28 09:53:45 +00:00
}
2021-11-25 13:02:25 +00:00
function deleteQueue ( button ) {
2022-10-26 02:43:58 +00:00
let to _delete = button . getAttribute ( 'data-id' ) ;
let payload = JSON . stringify ( { deleteQueue : to _delete } ) ;
sendPost ( payload ) ;
// clear button
let message = document . createElement ( 'p' ) ;
message . innerText = 'deleting download queue: ' + to _delete ;
document . getElementById ( button . id ) . replaceWith ( message ) ;
2021-11-25 13:02:25 +00:00
}
2021-09-24 11:03:22 +00:00
function stopQueue ( ) {
2022-10-26 02:43:58 +00:00
let payload = JSON . stringify ( { queue : 'stop' } ) ;
sendPost ( payload ) ;
document . getElementById ( 'stop-icon' ) . remove ( ) ;
2021-09-24 11:03:22 +00:00
}
2021-09-25 08:35:36 +00:00
function killQueue ( ) {
2022-10-26 02:43:58 +00:00
let payload = JSON . stringify ( { queue : 'kill' } ) ;
sendPost ( payload ) ;
document . getElementById ( 'kill-icon' ) . remove ( ) ;
2021-09-25 08:35:36 +00:00
}
2021-09-24 11:03:22 +00:00
2021-09-14 11:24:02 +00:00
// settings page buttons
function manualImport ( ) {
2022-10-26 02:43:58 +00:00
let payload = JSON . stringify ( { 'manual-import' : true } ) ;
sendPost ( payload ) ;
// clear button
let message = document . createElement ( 'p' ) ;
message . innerText = 'processing import' ;
let toReplace = document . getElementById ( 'manual-import' ) ;
toReplace . innerHTML = '' ;
toReplace . appendChild ( message ) ;
2021-09-14 11:24:02 +00:00
}
2021-11-01 09:42:07 +00:00
function reEmbed ( ) {
2022-10-26 02:43:58 +00:00
let payload = JSON . stringify ( { 're-embed' : true } ) ;
sendPost ( payload ) ;
// clear button
let message = document . createElement ( 'p' ) ;
message . innerText = 'processing thumbnails' ;
let toReplace = document . getElementById ( 're-embed' ) ;
toReplace . innerHTML = '' ;
toReplace . appendChild ( message ) ;
2021-11-01 09:42:07 +00:00
}
2021-09-16 11:16:09 +00:00
function dbBackup ( ) {
2022-10-26 02:43:58 +00:00
let payload = JSON . stringify ( { 'db-backup' : true } ) ;
sendPost ( payload ) ;
// clear button
let message = document . createElement ( 'p' ) ;
message . innerText = 'backing up archive' ;
let toReplace = document . getElementById ( 'db-backup' ) ;
toReplace . innerHTML = '' ;
toReplace . appendChild ( message ) ;
2021-09-16 11:16:09 +00:00
}
2021-12-14 12:06:47 +00:00
function dbRestore ( button ) {
2022-10-26 02:43:58 +00:00
let fileName = button . getAttribute ( 'data-id' ) ;
let payload = JSON . stringify ( { 'db-restore' : fileName } ) ;
sendPost ( payload ) ;
// clear backup row
let message = document . createElement ( 'p' ) ;
message . innerText = 'restoring from backup' ;
let toReplace = document . getElementById ( fileName ) ;
toReplace . innerHTML = '' ;
toReplace . appendChild ( message ) ;
2021-09-20 13:26:28 +00:00
}
2021-10-08 07:56:07 +00:00
function fsRescan ( ) {
2022-10-26 02:43:58 +00:00
let payload = JSON . stringify ( { 'fs-rescan' : true } ) ;
sendPost ( payload ) ;
// clear button
let message = document . createElement ( 'p' ) ;
message . innerText = 'File system scan in progress' ;
let toReplace = document . getElementById ( 'fs-rescan' ) ;
toReplace . innerHTML = '' ;
toReplace . appendChild ( message ) ;
2021-10-08 07:56:07 +00:00
}
2022-02-11 11:19:10 +00:00
function resetToken ( ) {
2022-10-26 02:43:58 +00:00
let payload = JSON . stringify ( { 'reset-token' : true } ) ;
sendPost ( payload ) ;
let message = document . createElement ( 'p' ) ;
message . innerText = 'Token revoked' ;
document . getElementById ( 'text-reveal' ) . replaceWith ( message ) ;
2022-02-11 11:19:10 +00:00
}
2021-10-09 10:11:13 +00:00
// delete from file system
function deleteConfirm ( ) {
2022-10-26 02:43:58 +00:00
let to _show = document . getElementById ( 'delete-button' ) ;
document . getElementById ( 'delete-item' ) . style . display = 'none' ;
to _show . style . display = 'block' ;
2021-10-09 10:11:13 +00:00
}
2021-10-09 13:33:32 +00:00
function deleteVideo ( button ) {
2022-10-26 02:43:58 +00:00
let to _delete = button . getAttribute ( 'data-id' ) ;
let to _redirect = button . getAttribute ( 'data-redirect' ) ;
let payload = JSON . stringify ( { 'delete-video' : to _delete } ) ;
sendPost ( payload ) ;
setTimeout ( function ( ) {
let redirect = '/channel/' + to _redirect ;
window . location . replace ( redirect ) ;
} , 1000 ) ;
2021-10-09 10:11:13 +00:00
}
2021-10-09 13:33:32 +00:00
function deleteChannel ( button ) {
2022-10-26 02:43:58 +00:00
let to _delete = button . getAttribute ( 'data-id' ) ;
let payload = JSON . stringify ( { 'delete-channel' : to _delete } ) ;
sendPost ( payload ) ;
setTimeout ( function ( ) {
window . location . replace ( '/channel/' ) ;
} , 1000 ) ;
2021-10-09 13:33:32 +00:00
}
2021-11-20 11:27:10 +00:00
function deletePlaylist ( button ) {
2022-10-26 02:43:58 +00:00
let playlist _id = button . getAttribute ( 'data-id' ) ;
let playlist _action = button . getAttribute ( 'data-action' ) ;
let payload = JSON . stringify ( {
'delete-playlist' : {
'playlist-id' : playlist _id ,
'playlist-action' : playlist _action ,
} ,
} ) ;
sendPost ( payload ) ;
setTimeout ( function ( ) {
window . location . replace ( '/playlist/' ) ;
} , 1000 ) ;
2021-11-20 11:27:10 +00:00
}
2021-10-09 10:11:13 +00:00
function cancelDelete ( ) {
2022-10-26 02:43:58 +00:00
document . getElementById ( 'delete-button' ) . style . display = 'none' ;
document . getElementById ( 'delete-item' ) . style . display = 'block' ;
2021-10-09 10:11:13 +00:00
}
2022-07-21 10:15:36 +00:00
// get seconds from hh:mm:ss.ms timestamp
function getSeconds ( timestamp ) {
2022-10-26 02:43:58 +00:00
let elements = timestamp . split ( ':' , 3 ) ;
let secs = parseInt ( elements [ 0 ] ) * 60 * 60 + parseInt ( elements [ 1 ] ) * 60 + parseFloat ( elements [ 2 ] ) ;
return secs ;
2022-07-21 10:15:36 +00:00
}
2021-09-05 17:10:14 +00:00
// player
2022-10-26 02:43:58 +00:00
let sponsorBlock = [ ] ;
2021-09-05 17:10:14 +00:00
function createPlayer ( button ) {
2022-10-26 02:43:58 +00:00
let videoId = button . getAttribute ( 'data-id' ) ;
let videoPosition = button . getAttribute ( 'data-position' ) ;
let videoData = getVideoData ( videoId ) ;
let sponsorBlockElements = '' ;
if ( videoData . data . sponsorblock && videoData . data . sponsorblock . is _enabled ) {
sponsorBlock = videoData . data . sponsorblock ;
if ( sponsorBlock . segments . length === 0 ) {
sponsorBlockElements = `
2022-04-10 09:20:58 +00:00
< div class = "sponsorblock" id = "sponsorblock" >
< h4 > This video doesn ' t have any sponsor segments added . To add a segment go to < u > < a href = "https://www.youtube.com/watch?v=${videoId}" > this video on Youtube < /a></u > and add a segment using the < u > < a href = "https://sponsor.ajay.app/" > SponsorBlock < /a></u > extension . < / h 4 >
< / d i v >
` ;
2022-10-26 02:43:58 +00:00
} else {
if ( sponsorBlock . has _unlocked ) {
sponsorBlockElements = `
2022-04-13 03:30:32 +00:00
< div class = "sponsorblock" id = "sponsorblock" >
< h4 > This video has unlocked sponsor segments . Go to < u > < a href = "https://www.youtube.com/watch?v=${videoId}" > this video on YouTube < /a></u > and vote on the segments using the < u > < a href = "https://sponsor.ajay.app/" > SponsorBlock < /a></u > extension . < / h 4 >
< / d i v >
` ;
2022-10-26 02:43:58 +00:00
}
2022-02-05 11:26:31 +00:00
}
2022-10-26 02:43:58 +00:00
} else {
sponsorBlock = null ;
}
let videoProgress ;
if ( videoPosition ) {
videoProgress = getSeconds ( videoPosition ) ;
} else {
videoProgress = getVideoProgress ( videoId ) . position ;
}
let videoName = videoData . data . title ;
let videoTag = createVideoTag ( videoData , videoProgress ) ;
let playlist = '' ;
let videoPlaylists = videoData . data . playlist ; // Array of playlists the video is in
if ( typeof videoPlaylists !== 'undefined' ) {
let subbedPlaylists = getSubbedPlaylists ( videoPlaylists ) ; // Array of playlist the video is in that are subscribed
if ( subbedPlaylists . length !== 0 ) {
let playlistData = getPlaylistData ( subbedPlaylists [ 0 ] ) ; // Playlist data for first subscribed playlist
let playlistId = playlistData . playlist _id ;
let playlistName = playlistData . playlist _name ;
playlist = ` <h5><a href="/playlist/ ${ playlistId } /"> ${ playlistName } </a></h5> ` ;
2022-02-05 11:26:31 +00:00
}
2022-10-26 02:43:58 +00:00
}
let videoViews = formatNumbers ( videoData . data . stats . view _count ) ;
let channelId = videoData . data . channel . channel _id ;
let channelName = videoData . data . channel . channel _name ;
removePlayer ( ) ;
// If cast integration is enabled create cast button
let castButton = '' ;
if ( videoData . config . application . enable _cast ) {
castButton = ` <google-cast-launcher id="castbutton"></google-cast-launcher> ` ;
}
// Watched indicator
let watchStatusIndicator ;
if ( videoData . data . player . watched ) {
watchStatusIndicator = createWatchStatusIndicator ( videoId , 'watched' ) ;
} else {
watchStatusIndicator = createWatchStatusIndicator ( videoId , 'unwatched' ) ;
}
let playerStats = ` <div class="thumb-icon player-stats"><img src="/static/img/icon-eye.svg" alt="views icon"><span> ${ videoViews } </span> ` ;
if ( videoData . data . stats . like _count ) {
let likes = formatNumbers ( videoData . data . stats . like _count ) ;
playerStats += ` <span>|</span><img src="/static/img/icon-thumb.svg" alt="thumbs-up"><span> ${ likes } </span> ` ;
}
if ( videoData . data . stats . dislike _count && videoData . config . downloads . integrate _ryd ) {
let dislikes = formatNumbers ( videoData . data . stats . dislike _count ) ;
playerStats += ` <span>|</span><img class="dislike" src="/static/img/icon-thumb.svg" alt="thumbs-down"><span> ${ dislikes } </span> ` ;
}
playerStats += '</div>' ;
const markup = `
2022-02-05 11:26:31 +00:00
< div class = "video-player" data - id = "${videoId}" >
2022-10-24 13:11:00 +00:00
< div class = "video-modal" > < span class = "video-modal-text" > < / s p a n > < / d i v >
2022-02-24 02:36:31 +00:00
$ { videoTag }
2022-04-10 09:20:58 +00:00
< div class = "notifications" id = "notifications" > < / d i v >
$ { sponsorBlockElements }
2022-02-05 11:26:31 +00:00
< div class = "player-title boxed-content" >
< img class = "close-button" src = "/static/img/icon-close.svg" alt = "close-icon" data = "${videoId}" onclick = "removePlayer()" title = "Close player" >
2022-03-10 13:20:23 +00:00
$ { watchStatusIndicator }
2022-02-05 11:26:31 +00:00
$ { castButton }
$ { playerStats }
< div class = "player-channel-playlist" >
< h3 > < a href = "/channel/${channelId}/" > $ { channelName } < / a > < / h 3 >
$ { playlist }
< / d i v >
< a href = "/video/${videoId}/" > < h2 id = "video-title" > $ { videoName } < / h 2 > < / a >
< / d i v >
< / d i v >
` ;
2022-10-26 02:43:58 +00:00
const divPlayer = document . getElementById ( 'player' ) ;
divPlayer . innerHTML = markup ;
recordTextTrackChanges ( ) ;
2022-02-05 11:26:31 +00:00
}
2022-02-24 02:36:31 +00:00
// Add video tag to video page when passed a video id, function loaded on page load `video.html (115-117)`
2022-02-25 03:39:33 +00:00
function insertVideoTag ( videoData , videoProgress ) {
2022-10-26 02:43:58 +00:00
let videoTag = createVideoTag ( videoData , videoProgress ) ;
let videoMain = document . querySelector ( '.video-main' ) ;
videoMain . innerHTML += videoTag ;
2022-02-24 02:36:31 +00:00
}
2022-02-25 03:39:33 +00:00
// Generates a video tag with subtitles when passed videoData and videoProgress.
function createVideoTag ( videoData , videoProgress ) {
2022-10-26 02:43:58 +00:00
let videoId = videoData . data . youtube _id ;
let videoUrl = videoData . data . media _url ;
let videoThumbUrl = videoData . data . vid _thumb _url ;
let subtitles = '' ;
let videoSubtitles = videoData . data . subtitles ; // Array of subtitles
if ( typeof videoSubtitles !== 'undefined' && videoData . config . downloads . subtitle ) {
for ( let i = 0 ; i < videoSubtitles . length ; i ++ ) {
let label = videoSubtitles [ i ] . name ;
if ( videoSubtitles [ i ] . source === 'auto' ) {
label += ' - auto' ;
}
subtitles += ` <track label=" ${ label } " kind="subtitles" srclang=" ${ videoSubtitles [ i ] . lang } " src=" ${ videoSubtitles [ i ] . media _url } "> ` ;
2022-02-05 11:26:31 +00:00
}
2022-10-26 02:43:58 +00:00
}
2022-02-24 02:36:31 +00:00
2022-10-26 02:43:58 +00:00
let videoTag = `
2022-02-25 03:39:33 +00:00
< video poster = "${videoThumbUrl}" ontimeupdate = "onVideoProgress()" onpause = "onVideoPause()" onended = "onVideoEnded()" controls autoplay width = "100%" playsinline id = "video-item" >
2022-02-24 02:36:31 +00:00
< source src = "${videoUrl}#t=${videoProgress}" type = "video/mp4" id = "video-source" videoid = "${videoId}" >
$ { subtitles }
< / v i d e o >
` ;
2022-10-26 02:43:58 +00:00
return videoTag ;
2022-02-05 11:26:31 +00:00
}
2022-02-24 02:36:31 +00:00
// Gets video tag
function getVideoPlayer ( ) {
2022-10-26 02:43:58 +00:00
let videoElement = document . getElementById ( 'video-item' ) ;
return videoElement ;
2022-02-24 02:36:31 +00:00
}
// Gets the video source tag
function getVideoPlayerVideoSource ( ) {
2022-10-26 02:43:58 +00:00
let videoPlayerVideoSource = document . getElementById ( 'video-source' ) ;
return videoPlayerVideoSource ;
2022-02-24 02:36:31 +00:00
}
// Gets the current progress of the video currently in the player
function getVideoPlayerCurrentTime ( ) {
2022-10-26 02:43:58 +00:00
let videoElement = getVideoPlayer ( ) ;
if ( videoElement != null ) {
return videoElement . currentTime ;
}
2022-02-24 02:36:31 +00:00
}
// Gets the video id of the video currently in the player
function getVideoPlayerVideoId ( ) {
2022-10-26 02:43:58 +00:00
let videoPlayerVideoSource = getVideoPlayerVideoSource ( ) ;
if ( videoPlayerVideoSource != null ) {
return videoPlayerVideoSource . getAttribute ( 'videoid' ) ;
}
2022-02-24 02:36:31 +00:00
}
// Gets the duration of the video currently in the player
function getVideoPlayerDuration ( ) {
2022-10-26 02:43:58 +00:00
let videoElement = getVideoPlayer ( ) ;
if ( videoElement != null ) {
return videoElement . duration ;
}
2022-02-24 02:36:31 +00:00
}
// Gets current watch status of video based on watch button
function getVideoPlayerWatchStatus ( ) {
2022-10-26 02:43:58 +00:00
let videoId = getVideoPlayerVideoId ( ) ;
let watched = false ;
let watchButtons = document . getElementsByClassName ( 'watch-button' ) ;
for ( let i = 0 ; i < watchButtons . length ; i ++ ) {
if (
watchButtons [ i ] . getAttribute ( 'data-id' ) === videoId &&
watchButtons [ i ] . getAttribute ( 'data-status' ) === 'watched'
) {
watched = true ;
2022-02-24 02:36:31 +00:00
}
2022-10-26 02:43:58 +00:00
}
return watched ;
2022-02-24 02:36:31 +00:00
}
2022-04-16 01:53:15 +00:00
// Runs on video playback, marks video as watched if video gets to 90% or higher, sends position to api, SB skipping
2022-02-24 02:36:31 +00:00
function onVideoProgress ( ) {
2022-10-26 02:43:58 +00:00
let videoId = getVideoPlayerVideoId ( ) ;
let currentTime = getVideoPlayerCurrentTime ( ) ;
let duration = getVideoPlayerDuration ( ) ;
let videoElement = getVideoPlayer ( ) ;
let notificationsElement = document . getElementById ( 'notifications' ) ;
if ( sponsorBlock && sponsorBlock . segments ) {
for ( let i in sponsorBlock . segments ) {
if (
currentTime >= sponsorBlock . segments [ i ] . segment [ 0 ] &&
currentTime <= sponsorBlock . segments [ i ] . segment [ 0 ] + 0.3
) {
videoElement . currentTime = sponsorBlock . segments [ i ] . segment [ 1 ] ;
let notificationElement = document . getElementById (
'notification-' + sponsorBlock . segments [ i ] . UUID
) ;
if ( ! notificationElement ) {
notificationsElement . innerHTML += ` <h3 id="notification- ${
sponsorBlock . segments [ i ] . UUID
} " > Skipped sponsor segment from $ { formatTime (
sponsorBlock . segments [ i ] . segment [ 0 ]
) } to $ { formatTime ( sponsorBlock . segments [ i ] . segment [ 1 ] ) } . < / h 3 > ` ;
2022-04-10 09:20:58 +00:00
}
2022-10-26 02:43:58 +00:00
}
if ( currentTime > sponsorBlock . segments [ i ] . segment [ 1 ] + 10 ) {
let notificationsElementUUID = document . getElementById (
'notification-' + sponsorBlock . segments [ i ] . UUID
) ;
if ( notificationsElementUUID ) {
notificationsElementUUID . outerHTML = '' ;
2022-02-05 11:26:31 +00:00
}
2022-10-26 02:43:58 +00:00
}
}
}
if ( ( currentTime % 10 ) . toFixed ( 1 ) <= 0.2 ) {
// Check progress every 10 seconds or else progress is checked a few times a second
postVideoProgress ( videoId , currentTime ) ;
if ( ! getVideoPlayerWatchStatus ( ) ) {
// Check if video is already marked as watched
if ( watchedThreshold ( currentTime , duration ) ) {
updateVideoWatchStatus ( videoId , 'unwatched' ) ;
}
2022-02-05 11:26:31 +00:00
}
2022-10-26 02:43:58 +00:00
}
2022-02-05 11:26:31 +00:00
}
2022-02-25 03:39:33 +00:00
// Runs on video end, marks video as watched
function onVideoEnded ( ) {
2022-10-26 02:43:58 +00:00
let videoId = getVideoPlayerVideoId ( ) ;
if ( ! getVideoPlayerWatchStatus ( ) ) {
// Check if video is already marked as watched
updateVideoWatchStatus ( videoId , 'unwatched' ) ;
}
for ( let i in sponsorBlock . segments ) {
let notificationsElementUUID = document . getElementById (
'notification-' + sponsorBlock . segments [ i ] . UUID
) ;
if ( notificationsElementUUID ) {
notificationsElementUUID . outerHTML = '' ;
2022-04-13 03:30:32 +00:00
}
2022-10-26 02:43:58 +00:00
}
2022-02-25 03:39:33 +00:00
}
2022-02-24 02:36:31 +00:00
function watchedThreshold ( currentTime , duration ) {
2022-10-26 02:43:58 +00:00
let watched = false ;
if ( duration <= 1800 ) {
// If video is less than 30 min
if ( currentTime / duration >= 0.9 ) {
// Mark as watched at 90%
watched = true ;
2022-02-05 11:26:31 +00:00
}
2022-10-26 02:43:58 +00:00
} else {
// If video is more than 30 min
if ( currentTime >= duration - 120 ) {
// Mark as watched if there is two minutes left
watched = true ;
}
}
return watched ;
2022-02-24 02:36:31 +00:00
}
// Runs on video pause. Sends current position.
function onVideoPause ( ) {
2022-10-26 02:43:58 +00:00
let videoId = getVideoPlayerVideoId ( ) ;
let currentTime = getVideoPlayerCurrentTime ( ) ;
postVideoProgress ( videoId , currentTime ) ;
2022-02-05 11:26:31 +00:00
}
// Format numbers for frontend
function formatNumbers ( number ) {
2022-10-26 02:43:58 +00:00
let numberUnformatted = parseFloat ( number ) ;
let numberFormatted ;
if ( numberUnformatted > 999999999 ) {
numberFormatted = ( numberUnformatted / 1000000000 ) . toFixed ( 1 ) . toString ( ) + 'B' ;
} else if ( numberUnformatted > 999999 ) {
numberFormatted = ( numberUnformatted / 1000000 ) . toFixed ( 1 ) . toString ( ) + 'M' ;
} else if ( numberUnformatted > 999 ) {
numberFormatted = ( numberUnformatted / 1000 ) . toFixed ( 1 ) . toString ( ) + 'K' ;
} else {
numberFormatted = numberUnformatted ;
}
return numberFormatted ;
2022-02-05 11:26:31 +00:00
}
2022-04-10 09:20:58 +00:00
// Formats times in seconds for frontend
function formatTime ( time ) {
2022-10-26 02:43:58 +00:00
let hoursUnformatted = time / 3600 ;
let minutesUnformatted = ( time % 3600 ) / 60 ;
let secondsUnformatted = time % 60 ;
let hoursFormatted = Math . trunc ( hoursUnformatted ) ;
let minutesFormatted ;
if ( minutesUnformatted < 10 && hoursFormatted > 0 ) {
minutesFormatted = '0' + Math . trunc ( minutesUnformatted ) ;
} else {
minutesFormatted = Math . trunc ( minutesUnformatted ) ;
}
let secondsFormatted ;
if ( secondsUnformatted < 10 ) {
secondsFormatted = '0' + Math . trunc ( secondsUnformatted ) ;
} else {
secondsFormatted = Math . trunc ( secondsUnformatted ) ;
}
let timeUnformatted = '' ;
if ( hoursFormatted > 0 ) {
timeUnformatted = hoursFormatted + ':' ;
}
let timeFormatted = timeUnformatted . concat ( minutesFormatted , ':' , secondsFormatted ) ;
return timeFormatted ;
2022-04-10 09:20:58 +00:00
}
2022-02-24 02:36:31 +00:00
// Gets video data when passed video ID
2022-02-05 11:26:31 +00:00
function getVideoData ( videoId ) {
2022-10-26 02:43:58 +00:00
let apiEndpoint = '/api/video/' + videoId + '/' ;
let videoData = apiRequest ( apiEndpoint , 'GET' ) ;
return videoData ;
2022-02-05 11:26:31 +00:00
}
2022-02-24 02:36:31 +00:00
// Gets channel data when passed channel ID
2022-02-05 11:26:31 +00:00
function getChannelData ( channelId ) {
2022-10-26 02:43:58 +00:00
let apiEndpoint = '/api/channel/' + channelId + '/' ;
let channelData = apiRequest ( apiEndpoint , 'GET' ) ;
return channelData . data ;
2022-02-05 11:26:31 +00:00
}
2022-02-24 02:36:31 +00:00
// Gets playlist data when passed playlist ID
2022-02-05 11:26:31 +00:00
function getPlaylistData ( playlistId ) {
2022-10-26 02:43:58 +00:00
let apiEndpoint = '/api/playlist/' + playlistId + '/' ;
let playlistData = apiRequest ( apiEndpoint , 'GET' ) ;
return playlistData . data ;
2022-02-05 11:26:31 +00:00
}
2022-02-24 02:36:31 +00:00
// Get video progress data when passed video ID
function getVideoProgress ( videoId ) {
2022-10-26 02:43:58 +00:00
let apiEndpoint = '/api/video/' + videoId + '/progress/' ;
let videoProgress = apiRequest ( apiEndpoint , 'GET' ) ;
return videoProgress ;
2022-02-24 02:36:31 +00:00
}
2022-02-05 11:26:31 +00:00
// Given an array of playlist ids it returns an array of subbed playlist ids from that list
function getSubbedPlaylists ( videoPlaylists ) {
2022-10-26 02:43:58 +00:00
let subbedPlaylists = [ ] ;
for ( let i = 0 ; i < videoPlaylists . length ; i ++ ) {
if ( getPlaylistData ( videoPlaylists [ i ] ) . playlist _subscribed ) {
subbedPlaylists . push ( videoPlaylists [ i ] ) ;
2022-01-15 06:33:16 +00:00
}
2022-10-26 02:43:58 +00:00
}
return subbedPlaylists ;
2022-02-05 11:26:31 +00:00
}
2022-02-24 02:36:31 +00:00
// Send video position when given video id and progress in seconds
function postVideoProgress ( videoId , videoProgress ) {
2022-10-26 02:43:58 +00:00
let apiEndpoint = '/api/video/' + videoId + '/progress/' ;
let duartion = getVideoPlayerDuration ( ) ;
if ( ! isNaN ( videoProgress ) && duartion !== 'undefined' ) {
let data = {
position : videoProgress ,
} ;
if ( videoProgress === 0 ) {
apiRequest ( apiEndpoint , 'DELETE' ) ;
// console.log("Deleting Video Progress for Video ID: " + videoId + ", Progress: " + videoProgress);
} else if ( ! getVideoPlayerWatchStatus ( ) ) {
apiRequest ( apiEndpoint , 'POST' , data ) ;
// console.log("Saving Video Progress for Video ID: " + videoId + ", Progress: " + videoProgress);
2022-02-24 02:36:31 +00:00
}
2022-10-26 02:43:58 +00:00
}
2022-02-24 02:36:31 +00:00
}
2022-04-10 09:20:58 +00:00
// Send sponsor segment when given video id and and timestamps
function postSponsorSegment ( videoId , startTime , endTime ) {
2022-10-26 02:43:58 +00:00
let apiEndpoint = '/api/video/' + videoId + '/sponsor/' ;
let data = {
segment : {
startTime : startTime ,
endTime : endTime ,
} ,
} ;
apiRequest ( apiEndpoint , 'POST' , data ) ;
2022-04-10 09:20:58 +00:00
}
// Send sponsor segment when given video id and and timestamps
function postSponsorSegmentVote ( videoId , uuid , vote ) {
2022-10-26 02:43:58 +00:00
let apiEndpoint = '/api/video/' + videoId + '/sponsor/' ;
let data = {
vote : {
uuid : uuid ,
yourVote : vote ,
} ,
} ;
apiRequest ( apiEndpoint , 'POST' , data ) ;
2022-04-10 09:20:58 +00:00
}
2022-05-04 10:07:01 +00:00
function handleCookieValidate ( ) {
2022-10-26 02:43:58 +00:00
document . getElementById ( 'cookieButton' ) . remove ( ) ;
let cookieMessageElement = document . getElementById ( 'cookieMessage' ) ;
cookieMessageElement . innerHTML = ` <span>Processing.</span> ` ;
let response = postCookieValidate ( ) ;
if ( response . cookie _validated === true ) {
cookieMessageElement . innerHTML = ` <span>The cookie file is valid.</span> ` ;
} else {
cookieMessageElement . innerHTML = ` <span class="danger-zone">Warning, the cookie file is invalid.</span> ` ;
}
2022-05-04 10:07:01 +00:00
}
// Check youtube cookie settings
function postCookieValidate ( ) {
2022-10-26 02:43:58 +00:00
let apiEndpoint = '/api/cookie/' ;
return apiRequest ( apiEndpoint , 'POST' ) ;
2022-05-04 10:07:01 +00:00
}
2022-02-24 02:36:31 +00:00
// Makes api requests when passed an endpoint and method ("GET", "POST", "DELETE")
2022-02-05 11:26:31 +00:00
function apiRequest ( apiEndpoint , method , data ) {
2022-10-26 02:43:58 +00:00
const xhttp = new XMLHttpRequest ( ) ;
let sessionToken = getCookie ( 'sessionid' ) ;
xhttp . open ( method , apiEndpoint , false ) ;
xhttp . setRequestHeader ( 'X-CSRFToken' , getCookie ( 'csrftoken' ) ) ; // Used for video progress POST requests
xhttp . setRequestHeader ( 'Authorization' , 'Token ' + sessionToken ) ;
xhttp . setRequestHeader ( 'Content-Type' , 'application/json' ) ;
xhttp . send ( JSON . stringify ( data ) ) ;
return JSON . parse ( xhttp . responseText ) ;
2021-09-05 17:10:14 +00:00
}
2022-02-25 03:39:33 +00:00
// Gets origin URL
2022-02-24 02:36:31 +00:00
function getURL ( ) {
2022-10-26 02:43:58 +00:00
return window . location . origin ;
2022-02-24 02:36:31 +00:00
}
2021-09-05 17:10:14 +00:00
function removePlayer ( ) {
2022-10-26 02:43:58 +00:00
let currentTime = getVideoPlayerCurrentTime ( ) ;
let duration = getVideoPlayerDuration ( ) ;
let videoId = getVideoPlayerVideoId ( ) ;
postVideoProgress ( videoId , currentTime ) ;
setProgressBar ( videoId , currentTime , duration ) ;
let playerElement = document . getElementById ( 'player' ) ;
if ( playerElement . hasChildNodes ( ) ) {
let youtubeId = playerElement . childNodes [ 1 ] . getAttribute ( 'data-id' ) ;
let playedStatus = document . createDocumentFragment ( ) ;
let playedBox = document . getElementById ( youtubeId ) ;
if ( playedBox ) {
playedStatus . appendChild ( playedBox ) ;
2022-02-05 11:26:31 +00:00
}
2022-10-26 02:43:58 +00:00
playerElement . innerHTML = '' ;
// append played status
let videoInfo = document . getElementById ( 'video-info-' + youtubeId ) ;
if ( videoInfo ) {
videoInfo . insertBefore ( playedStatus , videoInfo . firstChild ) ;
}
}
2021-09-05 17:10:14 +00:00
}
2022-02-25 03:39:33 +00:00
// Sets the progress bar when passed a video id, video progress and video duration
function setProgressBar ( videoId , currentTime , duration ) {
2022-10-26 02:43:58 +00:00
let progressBarWidth = ( currentTime / duration ) * 100 + '%' ;
let progressBars = document . getElementsByClassName ( 'video-progress-bar' ) ;
for ( let i = 0 ; i < progressBars . length ; i ++ ) {
if ( progressBars [ i ] . id === 'progress-' + videoId ) {
if ( ! getVideoPlayerWatchStatus ( ) ) {
progressBars [ i ] . style . width = progressBarWidth ;
} else {
progressBars [ i ] . style . width = '0%' ;
}
2022-02-25 03:39:33 +00:00
}
2022-10-26 02:43:58 +00:00
}
2022-03-10 13:20:23 +00:00
2022-10-26 02:43:58 +00:00
// progressBar = document.getElementById("progress-" + videoId);
2022-02-25 03:39:33 +00:00
}
2021-12-30 15:13:47 +00:00
// multi search form
2022-05-30 08:29:52 +00:00
let searchTimeout = null ;
2021-12-30 15:13:47 +00:00
function searchMulti ( query ) {
2022-10-26 02:43:58 +00:00
clearTimeout ( searchTimeout ) ;
searchTimeout = setTimeout ( function ( ) {
if ( query . length > 1 ) {
let http = new XMLHttpRequest ( ) ;
http . onreadystatechange = function ( ) {
if ( http . readyState === 4 ) {
let response = JSON . parse ( http . response ) ;
populateMultiSearchResults ( response . results , response . queryType ) ;
2022-05-30 08:29:52 +00:00
}
2022-10-26 02:43:58 +00:00
} ;
http . open ( 'GET' , ` /api/search/?query= ${ query } ` , true ) ;
http . setRequestHeader ( 'X-CSRFToken' , getCookie ( 'csrftoken' ) ) ;
http . setRequestHeader ( 'Content-type' , 'application/json' ) ;
http . send ( ) ;
}
} , 500 ) ;
2021-12-30 15:13:47 +00:00
}
2022-01-07 11:29:25 +00:00
function getViewDefaults ( view ) {
2022-10-26 02:43:58 +00:00
let defaultView = document . getElementById ( 'id_' + view ) . value ;
return defaultView ;
2022-01-07 11:29:25 +00:00
}
2021-12-30 15:13:47 +00:00
2022-07-20 11:01:57 +00:00
function populateMultiSearchResults ( allResults , queryType ) {
2022-10-26 02:43:58 +00:00
// videos
let defaultVideo = getViewDefaults ( 'home' ) ;
let allVideos = allResults . video _results ;
let videoBox = document . getElementById ( 'video-results' ) ;
videoBox . innerHTML = '' ;
videoBox . parentElement . style . display = 'block' ;
if ( allVideos . length > 0 ) {
for ( let index = 0 ; index < allVideos . length ; index ++ ) {
const video = allVideos [ index ] . source ;
const videoDiv = createVideo ( video , defaultVideo ) ;
videoBox . appendChild ( videoDiv ) ;
}
} else {
if ( queryType === 'simple' || queryType === 'video' ) {
videoBox . innerHTML = '<p>No videos found.</p>' ;
2022-07-20 11:01:57 +00:00
} else {
2022-10-26 02:43:58 +00:00
videoBox . parentElement . style . display = 'none' ;
2022-02-05 11:26:31 +00:00
}
2022-10-26 02:43:58 +00:00
}
// channels
let defaultChannel = getViewDefaults ( 'channel' ) ;
let allChannels = allResults . channel _results ;
let channelBox = document . getElementById ( 'channel-results' ) ;
channelBox . innerHTML = '' ;
channelBox . parentElement . style . display = 'block' ;
if ( allChannels . length > 0 ) {
for ( let index = 0 ; index < allChannels . length ; index ++ ) {
const channel = allChannels [ index ] . source ;
const channelDiv = createChannel ( channel , defaultChannel ) ;
channelBox . appendChild ( channelDiv ) ;
}
} else {
if ( queryType === 'simple' || queryType === 'channel' ) {
channelBox . innerHTML = '<p>No channels found.</p>' ;
2022-07-20 11:01:57 +00:00
} else {
2022-10-26 02:43:58 +00:00
channelBox . parentElement . style . display = 'none' ;
2022-02-05 11:26:31 +00:00
}
2022-10-26 02:43:58 +00:00
}
// playlists
let defaultPlaylist = getViewDefaults ( 'playlist' ) ;
let allPlaylists = allResults . playlist _results ;
let playlistBox = document . getElementById ( 'playlist-results' ) ;
playlistBox . innerHTML = '' ;
playlistBox . parentElement . style . display = 'block' ;
if ( allPlaylists . length > 0 ) {
for ( let index = 0 ; index < allPlaylists . length ; index ++ ) {
const playlist = allPlaylists [ index ] . source ;
const playlistDiv = createPlaylist ( playlist , defaultPlaylist ) ;
playlistBox . appendChild ( playlistDiv ) ;
}
} else {
if ( queryType === 'simple' || queryType === 'playlist' ) {
playlistBox . innerHTML = '<p>No playlists found.</p>' ;
2022-07-20 11:01:57 +00:00
} else {
2022-10-26 02:43:58 +00:00
playlistBox . parentElement . style . display = 'none' ;
2022-02-05 11:26:31 +00:00
}
2022-10-26 02:43:58 +00:00
}
// fulltext
let allFullText = allResults . fulltext _results ;
let fullTextBox = document . getElementById ( 'fulltext-results' ) ;
fullTextBox . innerHTML = '' ;
fullTextBox . parentElement . style . display = 'block' ;
if ( allFullText . length > 0 ) {
for ( let i = 0 ; i < allFullText . length ; i ++ ) {
const fullText = allFullText [ i ] ;
if ( 'highlight' in fullText ) {
const fullTextDiv = createFulltext ( fullText ) ;
fullTextBox . appendChild ( fullTextDiv ) ;
}
}
} else {
if ( queryType === 'simple' || queryType === 'full' ) {
fullTextBox . innerHTML = '<p>No fulltext items found.</p>' ;
2022-07-20 14:49:32 +00:00
} else {
2022-10-26 02:43:58 +00:00
fullTextBox . parentElement . style . display = 'none' ;
2022-07-20 14:49:32 +00:00
}
2022-10-26 02:43:58 +00:00
}
2021-12-30 15:13:47 +00:00
}
function createVideo ( video , viewStyle ) {
2022-10-26 02:43:58 +00:00
// create video item div from template
const videoId = video . youtube _id ;
// const mediaUrl = video.media_url;
const thumbUrl = '/cache/' + video . vid _thumb _url ;
const videoTitle = video . title ;
const videoPublished = video . published ;
const videoDuration = video . player . duration _str ;
let watchStatusIndicator ;
if ( video . player . watched ) {
watchStatusIndicator = createWatchStatusIndicator ( videoId , 'watched' ) ;
} else {
watchStatusIndicator = createWatchStatusIndicator ( videoId , 'unwatched' ) ;
}
const channelId = video . channel . channel _id ;
const channelName = video . channel . channel _name ;
// build markup
const markup = `
2022-07-21 08:51:33 +00:00
< a href = "#player" data - id = "${videoId}" onclick = "createPlayer(this)" >
2021-12-30 15:13:47 +00:00
< div class = "video-thumb-wrap ${viewStyle}" >
< div class = "video-thumb" >
< img src = "${thumbUrl}" alt = "video-thumb" >
< / d i v >
< div class = "video-play" >
< img src = "/static/img/icon-play.svg" alt = "play-icon" >
< / d i v >
< / d i v >
< / a >
< div class = "video-desc ${viewStyle}" >
< div class = "video-desc-player" id = "video-info-${videoId}" >
2022-03-10 13:20:23 +00:00
$ { watchStatusIndicator }
2021-12-30 15:13:47 +00:00
< span > $ { videoPublished } | $ { videoDuration } < / s p a n >
< / d i v >
< div >
< a href = "/channel/${channelId}/" > < h3 > $ { channelName } < / h 3 > < / a >
< a class = "video-more" href = "/video/${videoId}/" > < h2 > $ { videoTitle } < / h 2 > < / a >
< / d i v >
< / d i v >
2022-02-05 11:26:31 +00:00
` ;
2022-10-26 02:43:58 +00:00
const videoDiv = document . createElement ( 'div' ) ;
videoDiv . setAttribute ( 'class' , 'video-item ' + viewStyle ) ;
videoDiv . innerHTML = markup ;
return videoDiv ;
2021-12-30 15:13:47 +00:00
}
function createChannel ( channel , viewStyle ) {
2022-10-26 02:43:58 +00:00
// create channel item div from template
const channelId = channel . channel _id ;
const channelName = channel . channel _name ;
const channelSubs = channel . channel _subs ;
const channelLastRefresh = channel . channel _last _refresh ;
let button ;
if ( channel . channel _subscribed ) {
button = ` <button class="unsubscribe" type="button" id=" ${ channelId } " onclick="unsubscribe(this.id)" title="Unsubscribe from ${ channelName } ">Unsubscribe</button> ` ;
} else {
button = ` <button type="button" id=" ${ channelId } " onclick="subscribe(this.id)" title="Subscribe to ${ channelName } ">Subscribe</button> ` ;
}
// build markup
const markup = `
2022-01-07 11:29:25 +00:00
< div class = "channel-banner ${viewStyle}" >
2021-12-30 15:13:47 +00:00
< a href = "/channel/${channelId}/" >
< img src = "/cache/channels/${channelId}_banner.jpg" alt = "${channelId}-banner" >
< / a >
< / d i v >
2022-01-07 11:29:25 +00:00
< div class = "info-box info-box-2 ${viewStyle}" >
2021-12-30 15:13:47 +00:00
< div class = "info-box-item" >
< div class = "round-img" >
< a href = "/channel/${channelId}/" >
< img src = "/cache/channels/${channelId}_thumb.jpg" alt = "channel-thumb" >
< / a >
< / d i v >
< div >
< h3 > < a href = "/channel/${channelId}/" > $ { channelName } < / a > < / h 3 >
< p > Subscribers : $ { channelSubs } < / p >
< / d i v >
< / d i v >
< div class = "info-box-item" >
< div >
< p > Last refreshed : $ { channelLastRefresh } < / p >
$ { button }
< / d i v >
< / d i v >
< / d i v >
2022-02-05 11:26:31 +00:00
` ;
2022-10-26 02:43:58 +00:00
const channelDiv = document . createElement ( 'div' ) ;
channelDiv . setAttribute ( 'class' , 'channel-item ' + viewStyle ) ;
channelDiv . innerHTML = markup ;
return channelDiv ;
2021-12-30 15:13:47 +00:00
}
function createPlaylist ( playlist , viewStyle ) {
2022-10-26 02:43:58 +00:00
// create playlist item div from template
const playlistId = playlist . playlist _id ;
const playlistName = playlist . playlist _name ;
const playlistChannelId = playlist . playlist _channel _id ;
const playlistChannel = playlist . playlist _channel ;
const playlistLastRefresh = playlist . playlist _last _refresh ;
let button ;
if ( playlist . playlist _subscribed ) {
button = ` <button class="unsubscribe" type="button" id=" ${ playlistId } " onclick="unsubscribe(this.id)" title="Unsubscribe from ${ playlistName } ">Unsubscribe</button> ` ;
} else {
button = ` <button type="button" id=" ${ playlistId } " onclick="subscribe(this.id)" title="Subscribe to ${ playlistName } ">Subscribe</button> ` ;
}
const markup = `
2021-12-30 15:13:47 +00:00
< div class = "playlist-thumbnail" >
< a href = "/playlist/${playlistId}/" >
< img src = "/cache/playlists/${playlistId}.jpg" alt = "${playlistId}-thumbnail" >
< / a >
< / d i v >
< div class = "playlist-desc ${viewStyle}" >
< a href = "/channel/${playlistChannelId}/" > < h3 > $ { playlistChannel } < / h 3 > < / a >
< a href = "/playlist/${playlistId}/" > < h2 > $ { playlistName } < / h 2 > < / a >
< p > Last refreshed : $ { playlistLastRefresh } < / p >
$ { button }
< / d i v >
2022-02-05 11:26:31 +00:00
` ;
2022-10-26 02:43:58 +00:00
const playlistDiv = document . createElement ( 'div' ) ;
playlistDiv . setAttribute ( 'class' , 'playlist-item ' + viewStyle ) ;
playlistDiv . innerHTML = markup ;
return playlistDiv ;
2021-12-30 15:13:47 +00:00
}
2022-07-20 14:49:32 +00:00
function createFulltext ( fullText ) {
2022-10-26 02:43:58 +00:00
const videoId = fullText . source . youtube _id ;
const videoTitle = fullText . source . title ;
const thumbUrl = fullText . source . vid _thumb _url ;
const channelId = fullText . source . subtitle _channel _id ;
const channelName = fullText . source . subtitle _channel ;
const subtitleLine = fullText . highlight . subtitle _line [ 0 ] ;
const subtitle _start = fullText . source . subtitle _start . split ( '.' ) [ 0 ] ;
const subtitle _end = fullText . source . subtitle _end . split ( '.' ) [ 0 ] ;
const markup = `
2022-07-21 10:15:36 +00:00
< a href = "#player" data - id = "${videoId}" data - position = "${subtitle_start}" onclick = "createPlayer(this)" >
2022-07-20 14:49:32 +00:00
< div class = "video-thumb-wrap list" >
< div class = "video-thumb" >
< img src = "${thumbUrl}" alt = "video-thumb" >
< / d i v >
< div class = "video-play" >
< img src = "/static/img/icon-play.svg" alt = "play-icon" >
< / d i v >
< / d i v >
< / a >
< div class = "video-desc list" >
< p > $ { subtitle _start } - $ { subtitle _end } < / p >
2022-10-24 13:11:00 +00:00
< p > $ { subtitleLine } < / p >
2022-07-20 14:49:32 +00:00
< div >
< a href = "/channel/${channelId}/" > < h3 > $ { channelName } < / h 3 > < / a >
2022-07-21 10:15:36 +00:00
< a class = "video-more" href = "/video/${videoId}/?t=${subtitle_start}" > < h2 > $ { videoTitle } < / h 2 > < / a >
2022-07-20 14:49:32 +00:00
< / d i v >
< / d i v >
2022-10-26 02:43:58 +00:00
` ;
const fullTextDiv = document . createElement ( 'div' ) ;
fullTextDiv . setAttribute ( 'class' , 'video-item list' ) ;
fullTextDiv . innerHTML = markup ;
return fullTextDiv ;
2022-07-20 14:49:32 +00:00
}
2021-12-30 15:13:47 +00:00
2021-09-05 17:10:14 +00:00
// generic
2022-07-16 15:06:26 +00:00
2021-09-05 17:10:14 +00:00
function sendPost ( payload ) {
2022-10-26 02:43:58 +00:00
let http = new XMLHttpRequest ( ) ;
http . open ( 'POST' , '/process/' , true ) ;
http . setRequestHeader ( 'X-CSRFToken' , getCookie ( 'csrftoken' ) ) ;
http . setRequestHeader ( 'Content-type' , 'application/json' ) ;
http . send ( payload ) ;
2021-09-05 17:10:14 +00:00
}
function getCookie ( c _name ) {
2022-10-26 02:43:58 +00:00
if ( document . cookie . length > 0 ) {
let c _start = document . cookie . indexOf ( c _name + '=' ) ;
if ( c _start !== - 1 ) {
c _start = c _start + c _name . length + 1 ;
let c _end = document . cookie . indexOf ( ';' , c _start ) ;
if ( c _end === - 1 ) c _end = document . cookie . length ;
return unescape ( document . cookie . substring ( c _start , c _end ) ) ;
2022-02-05 11:26:31 +00:00
}
2022-10-26 02:43:58 +00:00
}
return '' ;
2021-09-05 17:10:14 +00:00
}
// animations
2022-07-16 15:06:26 +00:00
2021-09-05 17:10:14 +00:00
function textReveal ( ) {
2022-10-26 02:43:58 +00:00
let textBox = document . getElementById ( 'text-reveal' ) ;
let button = document . getElementById ( 'text-reveal-button' ) ;
let textBoxHeight = textBox . style . height ;
if ( textBoxHeight === 'unset' ) {
textBox . style . height = '0px' ;
button . innerText = 'Show' ;
} else {
textBox . style . height = 'unset' ;
button . innerText = 'Hide' ;
}
2021-09-05 17:10:14 +00:00
}
2022-07-16 15:06:26 +00:00
function textExpand ( ) {
2022-10-26 02:43:58 +00:00
let textBox = document . getElementById ( 'text-expand' ) ;
let button = document . getElementById ( 'text-expand-button' ) ;
let style = window . getComputedStyle ( textBox ) ;
if ( style . webkitLineClamp === 'none' ) {
textBox . style [ '-webkit-line-clamp' ] = '4' ;
button . innerText = 'Show more' ;
} else {
textBox . style [ '-webkit-line-clamp' ] = 'unset' ;
button . innerText = 'Show less' ;
}
2022-07-16 15:06:26 +00:00
}
// hide "show more" button if all text is already visible
function textExpandButtonVisibilityUpdate ( ) {
2022-10-26 02:43:58 +00:00
let textBox = document . getElementById ( 'text-expand' ) ;
let button = document . getElementById ( 'text-expand-button' ) ;
if ( ! textBox || ! button ) return ;
let styles = window . getComputedStyle ( textBox ) ;
let textBoxLineClamp = styles . webkitLineClamp ;
if ( textBoxLineClamp === 'unset' ) return ; // text box is in revealed state
if ( textBox . offsetHeight < textBox . scrollHeight || textBox . offsetWidth < textBox . scrollWidth ) {
// the element has an overflow, show read more button
button . style . display = 'inline-block' ;
} else {
// the element doesn't have overflow
button . style . display = 'none' ;
}
2022-07-16 15:06:26 +00:00
}
2022-10-26 02:43:58 +00:00
document . addEventListener ( 'readystatechange' , textExpandButtonVisibilityUpdate ) ;
window . addEventListener ( 'resize' , textExpandButtonVisibilityUpdate ) ;
2022-07-16 15:06:26 +00:00
2021-09-05 17:10:14 +00:00
function showForm ( ) {
2022-10-26 02:43:58 +00:00
let formElement = document . getElementById ( 'hidden-form' ) ;
let displayStyle = formElement . style . display ;
if ( displayStyle === '' ) {
formElement . style . display = 'block' ;
} else {
formElement . style . display = '' ;
}
animate ( 'animate-icon' , 'pulse-img' ) ;
2021-09-05 17:10:14 +00:00
}
2021-09-15 13:47:26 +00:00
2022-10-22 14:23:57 +00:00
function channelFilterDownload ( value ) {
2022-10-26 02:43:58 +00:00
if ( value === 'all' ) {
window . location = '/downloads/' ;
} else {
window . location . search = '?channel=' + value ;
}
2022-10-22 14:23:57 +00:00
}
2022-03-24 08:43:15 +00:00
function showOverwrite ( ) {
2022-10-26 02:43:58 +00:00
let overwriteDiv = document . getElementById ( 'overwrite-form' ) ;
if ( overwriteDiv . classList . contains ( 'hidden-overwrite' ) ) {
overwriteDiv . classList . remove ( 'hidden-overwrite' ) ;
} else {
overwriteDiv . classList . add ( 'hidden-overwrite' ) ;
}
2022-03-24 08:43:15 +00:00
}
2021-09-15 13:47:26 +00:00
function animate ( elementId , animationClass ) {
2022-10-26 02:43:58 +00:00
let toAnimate = document . getElementById ( elementId ) ;
if ( toAnimate . className !== animationClass ) {
toAnimate . className = animationClass ;
} else {
toAnimate . classList . remove ( animationClass ) ;
}
2021-09-15 13:47:26 +00:00
}
2022-10-24 13:11:00 +00:00
// keep track of changes to the subtitles list made with the native UI
// needed so that when toggling subtitles with the shortcut we go to the last selected one, not the first one
addEventListener ( 'DOMContentLoaded' , recordTextTrackChanges ) ;
let lastSeenTextTrack = 0 ;
function recordTextTrackChanges ( ) {
2022-10-26 02:43:58 +00:00
let player = getVideoPlayer ( ) ;
if ( player == null ) {
return ;
}
player . textTracks . addEventListener ( 'change' , ( ) => {
let active = [ ... player . textTracks ] . findIndex ( x => x . mode === 'showing' ) ;
if ( active !== - 1 ) {
lastSeenTextTrack = active ;
2022-10-24 13:11:00 +00:00
}
2022-10-26 02:43:58 +00:00
} ) ;
2022-10-24 13:11:00 +00:00
}
// keyboard shortcuts for the video player
document . addEventListener ( 'keydown' , doShortcut ) ;
let modalHideTimeout = - 1 ;
function showModal ( html , duration ) {
2022-10-26 02:43:58 +00:00
let modal = document . querySelector ( '.video-modal-text' ) ;
modal . innerHTML = html ;
modal . style . display = 'initial' ;
clearTimeout ( modalHideTimeout ) ;
modalHideTimeout = setTimeout ( ( ) => {
modal . style . display = 'none' ;
} , duration ) ;
2022-10-24 13:11:00 +00:00
}
2022-10-26 02:43:58 +00:00
let videoSpeeds = [ 0.25 , 0.5 , 0.75 , 1 , 1.25 , 1.5 , 1.75 , 2 , 2.25 , 2.5 , 2.75 , 3 ] ;
2022-10-24 13:11:00 +00:00
function doShortcut ( e ) {
2022-10-26 02:43:58 +00:00
if ( ! ( e . target instanceof HTMLElement ) ) {
return ;
}
let target = e . target ;
let targetName = target . nodeName . toLowerCase ( ) ;
if (
targetName === 'textarea' ||
targetName === 'input' ||
targetName === 'select' ||
target . isContentEditable
) {
return ;
}
if ( e . altKey || e . ctrlKey || e . metaKey ) {
return ;
}
let player = getVideoPlayer ( ) ;
if ( player == null ) {
// not on the video page
return ;
}
switch ( e . key ) {
case 'c' : {
// toggle captions
let tracks = [ ... player . textTracks ] ;
if ( tracks . length === 0 ) {
break ;
}
let active = tracks . find ( x => x . mode === 'showing' ) ;
if ( active != null ) {
active . mode = 'disabled' ;
} else {
tracks [ lastSeenTextTrack ] . mode = 'showing' ;
}
break ;
2022-10-24 13:11:00 +00:00
}
2022-10-26 02:43:58 +00:00
case 'm' : {
player . muted = ! player . muted ;
break ;
2022-10-24 13:11:00 +00:00
}
2022-10-26 02:43:58 +00:00
case 'ArrowLeft' : {
if ( targetName === 'video' ) {
// hitting arrows while the video is focused will use the built-in skip
break ;
}
showModal ( '- 5 seconds' , 500 ) ;
player . currentTime -= 5 ;
break ;
2022-10-24 13:11:00 +00:00
}
2022-10-26 02:43:58 +00:00
case 'ArrowRight' : {
if ( targetName === 'video' ) {
// hitting space while the video is focused will use the built-in skip
break ;
}
showModal ( '+ 5 seconds' , 500 ) ;
player . currentTime += 5 ;
break ;
2022-10-24 13:11:00 +00:00
}
2022-10-26 02:43:58 +00:00
case '<' :
case '>' : {
// change speed
let currentSpeedIdx = videoSpeeds . findIndex ( s => s >= player . playbackRate ) ;
if ( currentSpeedIdx === - 1 ) {
// handle the case where the user manually set the speed above our max speed
currentSpeedIdx = videoSpeeds . length - 1 ;
}
let newSpeedIdx =
e . key === '<'
? Math . max ( 0 , currentSpeedIdx - 1 )
: Math . min ( videoSpeeds . length - 1 , currentSpeedIdx + 1 ) ;
let newSpeed = videoSpeeds [ newSpeedIdx ] ;
player . playbackRate = newSpeed ;
showModal ( newSpeed + 'x' , 500 ) ;
break ;
}
case ' ' : {
if ( targetName === 'video' ) {
// hitting space while the video is focused will toggle it anyway
break ;
}
e . preventDefault ( ) ;
if ( player . paused ) {
player . play ( ) ;
} else {
player . pause ( ) ;
}
break ;
}
case '?' : {
showModal (
`
2022-10-24 13:11:00 +00:00
< table style = "margin: auto; background: rgba(0,0,0,.5)" > < tbody >
< tr > < td > Show help < / t d > < t d > ? < / t d >
< tr > < td > Toggle mute < / t d > < t d > m < / t d >
< tr > < td > Toggle subtitles ( if available ) < / t d > < t d > c < / t d >
< tr > < td > Increase speed < / t d > < t d > & g t ; < / t d >
< tr > < td > Decrease speed < / t d > < t d > & l t ; < / t d >
< tr > < td > Back 5 seconds < / t d > < t d > ← < / t d >
< tr > < td > Forward 5 seconds < / t d > < t d > → < / t d >
2022-10-26 02:43:58 +00:00
` ,
3000
) ;
break ;
2022-10-24 13:11:00 +00:00
}
2022-10-26 02:43:58 +00:00
}
2022-10-24 13:11:00 +00:00
}