2022-10-26 02:43:58 +00:00
'use strict' ;
/* globals checkMessages */
2023-11-01 15:49:33 +00:00
function sortChange ( button ) {
let apiEndpoint = '/api/config/user/' ;
let data = { } ;
data [ button . name ] = button . value ;
apiRequest ( apiEndpoint , 'POST' , data ) ;
2022-10-26 02:43:58 +00:00
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 ) ;
2022-12-23 13:43:36 +00:00
let watchStatusIndicator ;
let apiEndpoint = '/api/watched/' ;
2022-10-26 02:43:58 +00:00
if ( videoCurrentWatchStatus === 'watched' ) {
watchStatusIndicator = createWatchStatusIndicator ( videoId , 'unwatched' ) ;
2022-12-23 13:43:36 +00:00
apiRequest ( apiEndpoint , 'POST' , { id : videoId , is _watched : false } ) ;
2022-10-26 02:43:58 +00:00
} else if ( videoCurrentWatchStatus === 'unwatched' ) {
watchStatusIndicator = createWatchStatusIndicator ( videoId , 'watched' ) ;
2022-12-23 13:43:36 +00:00
apiRequest ( apiEndpoint , 'POST' , { id : videoId , is _watched : true } ) ;
2022-10-26 02:43:58 +00:00
}
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
}
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' ) ;
2022-12-23 13:39:14 +00:00
let apiEndpoint = '/api/watched/' ;
2022-12-23 13:43:36 +00:00
let data = { id : youtube _id , is _watched : true } ;
2023-09-21 15:40:42 +00:00
apiRequest ( apiEndpoint , 'POST' , data ) ;
setTimeout ( function ( ) {
location . reload ( ) ;
} , 1000 ) ;
}
function isUnwatchedButton ( button ) {
let youtube _id = button . getAttribute ( 'data-id' ) ;
let apiEndpoint = '/api/watched/' ;
let data = { id : youtube _id , is _watched : false } ;
2022-12-23 13:39:14 +00:00
apiRequest ( apiEndpoint , 'POST' , data ) ;
2022-10-26 02:43:58 +00:00
setTimeout ( function ( ) {
location . reload ( ) ;
} , 1000 ) ;
2021-11-20 03:58:25 +00:00
}
2023-08-23 16:28:09 +00:00
function subscribeStatus ( subscribeButton ) {
let id = subscribeButton . getAttribute ( 'data-id' ) ;
let type = subscribeButton . getAttribute ( 'data-type' ) ;
let subscribe = Boolean ( subscribeButton . getAttribute ( 'data-subscribe' ) ) ;
let apiEndpoint ;
let data ;
if ( type === 'channel' ) {
apiEndpoint = '/api/channel/' ;
data = { data : [ { channel _id : id , channel _subscribed : subscribe } ] } ;
} else if ( type === 'playlist' ) {
apiEndpoint = '/api/playlist/' ;
data = { data : [ { playlist _id : id , playlist _subscribed : subscribe } ] } ;
}
apiRequest ( apiEndpoint , 'POST' , data ) ;
2022-10-26 02:43:58 +00:00
let message = document . createElement ( 'span' ) ;
2023-08-23 16:28:09 +00:00
if ( subscribe ) {
message . innerText = 'You are subscribed.' ;
} else {
message . innerText = 'You are unsubscribed.' ;
}
subscribeButton . 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' ) ;
2023-11-01 15:49:33 +00:00
let apiEndpoint = '/api/config/user/' ;
let data = { } ;
data [ ` view_style_ ${ sourcePage } ` ] = newView ;
console . log ( data ) ;
apiRequest ( apiEndpoint , 'POST' , data ) ;
2022-10-26 02:43:58 +00:00
setTimeout ( function ( ) {
location . reload ( ) ;
} , 500 ) ;
2021-10-01 07:42:17 +00:00
}
2022-05-28 06:57:29 +00:00
function changeGridItems ( image ) {
2023-11-01 15:49:33 +00:00
let newGridItems = Number ( image . getAttribute ( 'data-value' ) ) ;
let apiEndpoint = '/api/config/user/' ;
let data = { grid _items : newGridItems } ;
apiRequest ( apiEndpoint , 'POST' , data ) ;
2022-10-26 02:43:58 +00:00
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
2023-11-01 15:49:33 +00:00
let apiEndpoint = '/api/config/user/' ;
let data = { } ;
data [ checkbox . id ] = checkbox . checked ;
apiRequest ( apiEndpoint , 'POST' , data ) ;
2022-10-26 02:43:58 +00:00
setTimeout ( function ( ) {
2023-02-17 07:04:52 +00:00
let currPage = window . location . pathname ;
2022-10-26 02:43:58 +00:00
window . location . replace ( currPage ) ;
} , 500 ) ;
2021-10-03 11:17:07 +00:00
}
2022-12-19 06:04:53 +00:00
// start reindex task
function reindex ( button ) {
let apiEndpoint = '/api/refresh/' ;
if ( button . getAttribute ( 'data-extract-videos' ) ) {
apiEndpoint += '?extract_videos=true' ;
}
let type = button . getAttribute ( 'data-type' ) ;
let id = button . getAttribute ( 'data-id' ) ;
let data = { } ;
data [ type ] = [ id ] ;
apiRequest ( apiEndpoint , 'POST' , data ) ;
let message = document . createElement ( 'p' ) ;
message . innerText = 'Reindex scheduled' ;
document . getElementById ( 'reindex-button' ) . replaceWith ( message ) ;
2023-04-08 10:18:42 +00:00
setTimeout ( function ( ) {
checkMessages ( ) ;
} , 500 ) ;
2022-12-19 06:04:53 +00:00
}
2021-09-05 17:10:14 +00:00
// download page buttons
function rescanPending ( ) {
2023-03-23 04:28:42 +00:00
let apiEndpoint = '/api/task-name/update_subscribed/' ;
apiRequest ( apiEndpoint , 'POST' ) ;
2022-10-26 02:43:58 +00:00
animate ( 'rescan-icon' , 'rotate-img' ) ;
setTimeout ( function ( ) {
checkMessages ( ) ;
} , 500 ) ;
2021-09-05 17:10:14 +00:00
}
function dlPending ( ) {
2023-03-23 04:28:42 +00:00
let apiEndpoint = '/api/task-name/download_pending/' ;
apiRequest ( apiEndpoint , 'POST' ) ;
2022-10-26 02:43:58 +00:00
animate ( 'download-icon' , 'bounce-img' ) ;
setTimeout ( function ( ) {
checkMessages ( ) ;
} , 500 ) ;
2021-09-05 17:10:14 +00:00
}
2023-07-24 17:05:43 +00:00
function addToQueue ( autostart = false ) {
2023-04-22 04:57:09 +00:00
let textArea = document . getElementById ( 'id_vid_url' ) ;
if ( textArea . value === '' ) {
2023-07-24 17:05:43 +00:00
return ;
2023-04-22 04:57:09 +00:00
}
2023-07-24 17:05:43 +00:00
let toPost = { data : [ { youtube _id : textArea . value , status : 'pending' } ] } ;
2023-04-22 04:57:09 +00:00
let apiEndpoint = '/api/download/' ;
2023-04-29 07:17:36 +00:00
if ( autostart ) {
apiEndpoint = ` ${ apiEndpoint } ?autostart=true ` ;
}
2023-04-22 04:57:09 +00:00
apiRequest ( apiEndpoint , 'POST' , toPost ) ;
2023-04-29 07:17:36 +00:00
textArea . value = '' ;
2023-04-22 04:57:09 +00:00
setTimeout ( function ( ) {
checkMessages ( ) ;
} , 500 ) ;
2023-04-29 07:17:36 +00:00
showForm ( ) ;
2023-04-22 04:57:09 +00:00
}
2024-03-10 15:57:59 +00:00
//shows the video sub menu popup
function showAddToPlaylistMenu ( input1 ) {
let dataId , playlists , form _code , buttonId ;
dataId = input1 . getAttribute ( 'data-id' ) ;
buttonId = input1 . getAttribute ( 'id' ) ;
playlists = getCustomPlaylists ( ) ;
//hide the invoking button
input1 . style . visibility = "hidden" ;
//show the form
form _code = '<div class="video-popup-menu"><img src="/static/img/icon-close.svg" class="video-popup-menu-close-button" title="Close menu" onclick="removeDotMenu(this, \'' + buttonId + '\')"/><h3>Add video to...</h3>' ;
for ( let i = 0 ; i < playlists . length ; i ++ ) {
let obj = playlists [ i ] ;
form _code += '<p onclick="addToCustomPlaylist(this, \'' + dataId + '\',\'' + obj . playlist _id + '\')"><img class="p-button" src="/static/img/icon-unseen.svg"/>' + obj . playlist _name + '</p>' ;
}
form _code += '<p><a href="/playlist">Create playlist</a></p></div>' ;
input1 . parentNode . parentNode . innerHTML += form _code ;
}
//handles user action of adding a video to a custom playlist
function addToCustomPlaylist ( input , video _id , playlist _id ) {
let apiEndpoint = '/api/playlist/' + playlist _id + '/' ;
let data = { "action" : "create" , "video_id" : video _id } ;
apiRequest ( apiEndpoint , 'POST' , data ) ;
//mark the item added in the ui
input . firstChild . src = '/static/img/icon-seen.svg' ;
}
function removeDotMenu ( input1 , button _id ) {
//show the menu button
document . getElementById ( button _id ) . style . visibility = "visible" ;
//remove the form
input1 . parentNode . remove ( ) ;
}
//shows the video sub menu popup on custom playlist page
function showCustomPlaylistMenu ( input1 , playlist _id , current _page , last _page ) {
let dataId , form _code , buttonId ;
dataId = input1 . getAttribute ( 'data-id' ) ;
buttonId = input1 . getAttribute ( 'id' ) ;
//hide the invoking button
input1 . style . visibility = "hidden" ;
//show the form
form _code = '<div class="video-popup-menu"><img src="/static/img/icon-close.svg" class="video-popup-menu-close-button" title="Close menu" onclick="removeDotMenu(this, \'' + buttonId + '\')"/><h3>Move Video</h3>' ;
form _code += '<img class="move-video-button" data-id="' + dataId + '" data-context="top" onclick="moveCustomPlaylistVideo(this,\'' + playlist _id + '\',' + current _page + ',' + last _page + ')" src="/static/img/icon-arrow-top.svg" title="Move to top"/>' ;
form _code += '<img class="move-video-button" data-id="' + dataId + '" data-context="up" onclick="moveCustomPlaylistVideo(this,\'' + playlist _id + '\',' + current _page + ',' + last _page + ')" src="/static/img/icon-arrow-up.svg" title="Move up"/>' ;
form _code += '<img class="move-video-button" data-id="' + dataId + '" data-context="down" onclick="moveCustomPlaylistVideo(this,\'' + playlist _id + '\',' + current _page + ',' + last _page + ')" src="/static/img/icon-arrow-down.svg" title="Move down"/>' ;
form _code += '<img class="move-video-button" data-id="' + dataId + '" data-context="bottom" onclick="moveCustomPlaylistVideo(this,\'' + playlist _id + '\',' + current _page + ',' + last _page + ')" src="/static/img/icon-arrow-bottom.svg" title="Move to bottom"/>' ;
form _code += '<img class="move-video-button" data-id="' + dataId + '" data-context="remove" onclick="moveCustomPlaylistVideo(this,\'' + playlist _id + '\',' + current _page + ',' + last _page + ')" src="/static/img/icon-remove.svg" title="Remove from playlist"/>' ;
form _code += '</div>' ;
input1 . parentNode . parentNode . innerHTML += form _code ;
}
//process custom playlist form actions
function moveCustomPlaylistVideo ( input1 , playlist _id , current _page , last _page ) {
let dataId , dataContext ;
dataId = input1 . getAttribute ( 'data-id' ) ;
dataContext = input1 . getAttribute ( 'data-context' ) ;
let apiEndpoint = '/api/playlist/' + playlist _id + '/' ;
let data = { "action" : dataContext , "video_id" : dataId } ;
apiRequest ( apiEndpoint , 'POST' , data ) ;
let itemDom = input1 . parentElement . parentElement . parentElement ;
let listDom = itemDom . parentElement ;
if ( dataContext === "up" )
{
let sibling = itemDom . previousElementSibling ;
if ( sibling !== null )
{
sibling . before ( itemDom ) ;
}
else if ( current _page > 1 )
{
itemDom . remove ( ) ;
}
}
else if ( dataContext === "down" )
{
let sibling = itemDom . nextElementSibling ;
if ( sibling !== null )
{
sibling . after ( itemDom ) ;
}
else if ( current _page !== last _page )
{
itemDom . remove ( ) ;
}
}
else if ( dataContext === "top" )
{
let sibling = listDom . firstElementChild ;
if ( sibling !== null )
{
sibling . before ( itemDom ) ;
}
if ( current _page > 1 )
{
itemDom . remove ( ) ;
}
}
else if ( dataContext === "bottom" )
{
let sibling = listDom . lastElementChild ;
if ( sibling !== null )
{
sibling . after ( itemDom ) ;
}
if ( current _page !== last _page )
{
itemDom . remove ( ) ;
}
}
else if ( dataContext === "remove" )
{
itemDom . remove ( ) ;
}
}
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' ) ;
2022-12-22 12:42:59 +00:00
let apiEndpoint = '/api/download/' + youtube _id + '/' ;
2022-12-23 13:43:36 +00:00
apiRequest ( apiEndpoint , 'POST' , { status : 'ignore' } ) ;
2022-10-26 02:43:58 +00:00
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' ) ;
2023-01-02 09:50:53 +00:00
let apiEndpoint = '/api/download/' + youtube _id + '/' ;
apiRequest ( apiEndpoint , 'POST' , { status : 'priority' } ) ;
2022-10-26 02:43:58 +00:00
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' ) ;
2022-12-22 12:12:09 +00:00
let apiEndpoint = '/api/download/' + youtube _id + '/' ;
2022-12-22 12:57:19 +00:00
apiRequest ( apiEndpoint , 'DELETE' ) ;
2022-10-26 02:43:58 +00:00
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' ) ;
2022-12-22 12:48:24 +00:00
let apiEndpoint = '/api/download/' + youtube _id + '/' ;
2022-12-23 13:43:36 +00:00
apiRequest ( apiEndpoint , 'POST' , { status : 'pending' } ) ;
2022-10-26 02:43:58 +00:00
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' ) ;
2022-12-22 12:57:19 +00:00
let apiEndpoint = '/api/download/?filter=' + to _delete ;
apiRequest ( apiEndpoint , 'DELETE' ) ;
2022-10-26 02:43:58 +00:00
// 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
}
2023-03-22 10:00:55 +00:00
function stopTask ( icon ) {
let taskId = icon . getAttribute ( 'data' ) ;
let apiEndpoint = ` /api/task-id/ ${ taskId } / ` ;
2023-03-22 10:05:05 +00:00
apiRequest ( apiEndpoint , 'POST' , { command : 'stop' } ) ;
2023-03-24 08:18:26 +00:00
icon . remove ( ) ;
2021-09-24 11:03:22 +00:00
}
2023-03-22 10:00:55 +00:00
function killTask ( icon ) {
let taskId = icon . getAttribute ( 'data' ) ;
let apiEndpoint = ` /api/task-id/ ${ taskId } / ` ;
2023-03-22 10:05:05 +00:00
apiRequest ( apiEndpoint , 'POST' , { command : 'kill' } ) ;
2023-03-24 08:18:26 +00:00
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 ( ) {
2023-03-23 04:28:42 +00:00
let apiEndpoint = '/api/task-name/manual_import/' ;
apiRequest ( apiEndpoint , 'POST' ) ;
2022-10-26 02:43:58 +00:00
// clear button
let message = document . createElement ( 'p' ) ;
message . innerText = 'processing import' ;
let toReplace = document . getElementById ( 'manual-import' ) ;
toReplace . innerHTML = '' ;
toReplace . appendChild ( message ) ;
2023-03-19 10:26:09 +00:00
setTimeout ( function ( ) {
location . replace ( '#notifications' ) ;
checkMessages ( ) ;
} , 200 ) ;
2021-09-14 11:24:02 +00:00
}
2021-11-01 09:42:07 +00:00
function reEmbed ( ) {
2023-03-23 04:28:42 +00:00
let apiEndpoint = '/api/task-name/resync_thumbs/' ;
apiRequest ( apiEndpoint , 'POST' ) ;
2022-10-26 02:43:58 +00:00
// clear button
let message = document . createElement ( 'p' ) ;
message . innerText = 'processing thumbnails' ;
let toReplace = document . getElementById ( 're-embed' ) ;
toReplace . innerHTML = '' ;
toReplace . appendChild ( message ) ;
2023-03-19 10:26:09 +00:00
setTimeout ( function ( ) {
location . replace ( '#notifications' ) ;
checkMessages ( ) ;
} , 200 ) ;
2021-11-01 09:42:07 +00:00
}
2021-09-16 11:16:09 +00:00
function dbBackup ( ) {
2023-11-01 07:05:11 +00:00
let apiEndpoint = '/api/backup/' ;
2023-03-23 04:28:42 +00:00
apiRequest ( apiEndpoint , 'POST' ) ;
2022-10-26 02:43:58 +00:00
// clear button
let message = document . createElement ( 'p' ) ;
message . innerText = 'backing up archive' ;
let toReplace = document . getElementById ( 'db-backup' ) ;
toReplace . innerHTML = '' ;
toReplace . appendChild ( message ) ;
2023-03-19 10:26:09 +00:00
setTimeout ( function ( ) {
location . replace ( '#notifications' ) ;
checkMessages ( ) ;
} , 200 ) ;
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' ) ;
2023-11-01 07:05:11 +00:00
let apiEndpoint = ` /api/backup/ ${ fileName } / ` ;
apiRequest ( apiEndpoint , 'POST' ) ;
2022-10-26 02:43:58 +00:00
// clear backup row
let message = document . createElement ( 'p' ) ;
message . innerText = 'restoring from backup' ;
let toReplace = document . getElementById ( fileName ) ;
toReplace . innerHTML = '' ;
toReplace . appendChild ( message ) ;
2023-03-19 10:26:09 +00:00
setTimeout ( function ( ) {
location . replace ( '#notifications' ) ;
checkMessages ( ) ;
} , 200 ) ;
2021-09-20 13:26:28 +00:00
}
2021-10-08 07:56:07 +00:00
function fsRescan ( ) {
2023-03-23 04:28:42 +00:00
let apiEndpoint = '/api/task-name/rescan_filesystem/' ;
apiRequest ( apiEndpoint , 'POST' ) ;
2022-10-26 02:43:58 +00:00
// 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 ) ;
2023-03-19 10:26:09 +00:00
setTimeout ( function ( ) {
location . replace ( '#notifications' ) ;
checkMessages ( ) ;
} , 200 ) ;
2021-10-08 07:56:07 +00:00
}
2022-02-11 11:19:10 +00:00
function resetToken ( ) {
2023-03-13 03:07:06 +00:00
let apiEndpoint = '/api/token/' ;
let result = apiRequest ( apiEndpoint , 'DELETE' ) ;
if ( result && result . success ) {
let message = document . createElement ( 'p' ) ;
message . innerText = 'Token revoked' ;
document . getElementById ( 'text-reveal' ) . replaceWith ( message ) ;
} else {
console . error ( 'unable to revoke token' ) ;
}
2022-02-11 11:19:10 +00:00
}
2022-11-14 09:31:49 +00:00
// restore from snapshot
2022-10-29 13:12:54 +00:00
function restoreSnapshot ( snapshotId ) {
console . log ( 'restore ' + snapshotId ) ;
let apiEndpoint = '/api/snapshot/' + snapshotId + '/' ;
apiRequest ( apiEndpoint , 'POST' ) ;
let message = document . createElement ( 'p' ) ;
message . innerText = 'Snapshot restore started' ;
document . getElementById ( snapshotId ) . parentElement . replaceWith ( message ) ;
}
2022-10-31 14:21:28 +00:00
function createSnapshot ( ) {
console . log ( 'create snapshot now' ) ;
let apiEndpoint = '/api/snapshot/' ;
apiRequest ( apiEndpoint , 'POST' ) ;
let message = document . createElement ( 'span' ) ;
message . innerText = 'Snapshot in progress' ;
document . getElementById ( 'createButton' ) . replaceWith ( message ) ;
}
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' ) ;
2022-12-22 12:01:13 +00:00
let apiEndpoint = '/api/video/' + to _delete + '/' ;
apiRequest ( apiEndpoint , 'DELETE' ) ;
2022-10-26 02:43:58 +00:00
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' ) ;
2022-12-22 12:24:48 +00:00
let apiEndpoint = '/api/channel/' + to _delete + '/' ;
apiRequest ( apiEndpoint , 'DELETE' ) ;
2022-10-26 02:43:58 +00:00
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' ) ;
2023-08-23 16:54:36 +00:00
let apiEndpoint = ` /api/playlist/ ${ playlist _id } / ` ;
if ( playlist _action === 'delete-videos' ) {
apiEndpoint += '?delete-videos=true' ;
}
apiRequest ( apiEndpoint , 'DELETE' ) ;
2022-10-26 02:43:58 +00:00
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 ;
2024-02-05 20:55:05 +00:00
let videoTag = createVideoTag ( videoData , videoProgress , true ) ;
2022-10-26 02:43:58 +00:00
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 = '' ;
2023-10-28 03:27:03 +00:00
if ( videoData . config . enable _cast ) {
2022-10-26 02:43:58 +00:00
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.
2024-02-05 20:55:05 +00:00
function createVideoTag ( videoData , videoProgress , autoplay = false ) {
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 = `
2024-02-05 20:55:05 +00:00
< video poster = "${videoThumbUrl}" onvolumechange = "onVolumeChange(this)" onloadstart = "this.volume=getPlayerVolume()" ontimeupdate = "onVideoProgress()" onpause = "onVideoPause()" onended = "onVideoEnded()" $ {
autoplay ? 'autoplay' : ''
} controls 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
}
2023-11-09 02:31:19 +00:00
function onVolumeChange ( videoTag ) {
localStorage . setItem ( 'playerVolume' , videoTag . volume ) ;
}
function getPlayerVolume ( ) {
return localStorage . getItem ( 'playerVolume' ) ? ? 1 ;
}
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
}
}
}
2024-02-05 20:55:05 +00:00
if ( currentTime < 10 ) return ;
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 ( ) ;
2024-02-05 20:55:05 +00:00
if ( currentTime < 10 ) return ;
2022-10-26 02:43:58 +00:00
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
}
2024-03-10 15:57:59 +00:00
// Gets custom playlists
function getCustomPlaylists ( ) {
let apiEndpoint = '/api/playlist/?playlist_type=custom' ;
let playlistData = apiRequest ( apiEndpoint , 'GET' ) ;
return playlistData . data ;
}
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 ) ) ;
2022-11-21 08:13:37 +00:00
if ( xhttp . status === 404 ) {
return false ;
} else {
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 ;
2023-03-04 03:51:38 +00:00
let searchHttpRequest = 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 ( ) {
2023-03-04 03:51:38 +00:00
if ( query . length > 0 ) {
if ( searchHttpRequest ) {
searchHttpRequest . abort ( ) ;
}
searchHttpRequest = new XMLHttpRequest ( ) ;
searchHttpRequest . onreadystatechange = function ( ) {
if ( searchHttpRequest . readyState === 4 ) {
const response = JSON . parse ( searchHttpRequest . response ) ;
2022-10-26 02:43:58 +00:00
populateMultiSearchResults ( response . results , response . queryType ) ;
2022-05-30 08:29:52 +00:00
}
2022-10-26 02:43:58 +00:00
} ;
2023-03-04 03:51:38 +00:00
searchHttpRequest . open ( 'GET' , ` /api/search/?query= ${ query } ` , true ) ;
searchHttpRequest . setRequestHeader ( 'X-CSRFToken' , getCookie ( 'csrftoken' ) ) ;
searchHttpRequest . setRequestHeader ( 'Content-type' , 'application/json' ) ;
searchHttpRequest . send ( ) ;
} else {
if ( searchHttpRequest ) {
searchHttpRequest . abort ( ) ;
searchHttpRequest = null ;
}
// show the placeholder container and hide the results container
document . getElementById ( 'multi-search-results' ) . style . display = 'none' ;
document . getElementById ( 'multi-search-results-placeholder' ) . style . display = 'block' ;
2022-10-26 02:43:58 +00:00
}
} , 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 ) {
2023-03-04 03:51:38 +00:00
// show the results container and hide the placeholder container
document . getElementById ( 'multi-search-results' ) . style . display = 'block' ;
document . getElementById ( 'multi-search-results-placeholder' ) . style . display = 'none' ;
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 ++ ) {
2023-09-04 13:22:00 +00:00
const video = allVideos [ index ] ;
2022-10-26 02:43:58 +00:00
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 ++ ) {
2023-09-04 13:22:00 +00:00
const channel = allChannels [ index ] ;
2022-10-26 02:43:58 +00:00
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 ++ ) {
2023-09-04 13:22:00 +00:00
const playlist = allPlaylists [ index ] ;
2022-10-26 02:43:58 +00:00
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 ] ;
2023-09-04 13:22:00 +00:00
if ( 'subtitle_line' in fullText ) {
2022-10-26 02:43:58 +00:00
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;
2022-11-21 08:13:37 +00:00
// const thumbUrl = '/cache/' + video.vid_thumb_url;
2022-10-26 02:43:58 +00:00
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" >
2022-11-21 08:13:37 +00:00
< img src = "${video.vid_thumb_url}" alt = "video-thumb" >
2021-12-30 15:13:47 +00:00
< / 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 ) {
2023-08-23 16:28:09 +00:00
button = ` <button class="unsubscribe" type="button" data-id=" ${ channelId } " data-subscribe="" data-type="channel" onclick="subscribeStatus(this)" title="Unsubscribe from ${ channelName } ">Unsubscribe</button> ` ;
2022-10-26 02:43:58 +00:00
} else {
2023-08-23 16:28:09 +00:00
button = ` <button type="button" data-id=" ${ channelId } " data-subscribe="true" data-type="channel" onclick="subscribeStatus(this)" title="Subscribe to ${ channelName } ">Subscribe</button> ` ;
2022-10-26 02:43:58 +00:00
}
// 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 ) {
2023-08-23 16:28:09 +00:00
button = ` <button class="unsubscribe" type="button" data-id=" ${ playlistId } " data-subscribe="" data-type="playlist" onclick="subscribeStatus(this)" title="Unsubscribe from ${ playlistName } ">Unsubscribe</button> ` ;
2022-10-26 02:43:58 +00:00
} else {
2023-08-23 16:28:09 +00:00
button = ` <button type="button" data-id=" ${ playlistId } " data-subscribe="true" data-type="playlist" onclick="subscribeStatus(this)" title="Subscribe to ${ playlistName } ">Subscribe</button> ` ;
2022-10-26 02:43:58 +00:00
}
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 ) {
2023-09-04 13:22:00 +00:00
const videoId = fullText . youtube _id ;
const subtitle _start = fullText . subtitle _start . split ( '.' ) [ 0 ] ;
const subtitle _end = fullText . subtitle _end . split ( '.' ) [ 0 ] ;
2022-10-26 02:43:58 +00:00
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" >
2023-09-04 13:22:00 +00:00
< img src = "${fullText.vid_thumb_url}" alt = "video-thumb" >
2022-07-20 14:49:32 +00:00
< / 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" >
< div >
2023-09-04 13:44:31 +00:00
< a href = "/channel/${fullText.subtitle_channel_id}/" > < h3 > $ { fullText . subtitle _channel } < / h 3 > < / a >
< a class = "video-more" href = "/video/${videoId}/?t=${subtitle_start}" > < h2 > $ { fullText . title } < / h 2 > < / a >
2022-07-20 14:49:32 +00:00
< / d i v >
2023-09-04 13:44:31 +00:00
< p > $ { subtitle _start } - $ { subtitle _end } < / p >
< p > $ { fullText . subtitle _line } < / p >
< span class = "settings-current" > Score : $ { fullText . _score } < / s p a n >
2022-07-20 14:49:32 +00:00
< / 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
2022-11-14 09:31:49 +00:00
function getComments ( videoId ) {
let apiEndpoint = '/api/video/' + videoId + '/comment/' ;
let response = apiRequest ( apiEndpoint , 'GET' ) ;
let allComments = response . data ;
writeComments ( allComments ) ;
}
function writeComments ( allComments ) {
let commentsListBox = document . getElementById ( 'comments-list' ) ;
for ( let i = 0 ; i < allComments . length ; i ++ ) {
const rootComment = allComments [ i ] ;
let commentBox = createCommentBox ( rootComment , true ) ;
// add replies to commentBox
if ( rootComment . comment _replies ) {
let commentReplyBox = document . createElement ( 'div' ) ;
commentReplyBox . setAttribute ( 'class' , 'comments-replies' ) ;
2022-11-21 02:55:33 +00:00
commentReplyBox . setAttribute ( 'id' , rootComment . comment _id + '-replies' ) ;
let totalReplies = rootComment . comment _replies . length ;
if ( totalReplies > 0 ) {
let replyButton = createReplyButton ( rootComment . comment _id + '-replies' , totalReplies ) ;
commentBox . appendChild ( replyButton ) ;
}
for ( let j = 0 ; j < totalReplies ; j ++ ) {
2022-11-14 09:31:49 +00:00
const commentReply = rootComment . comment _replies [ j ] ;
let commentReplyDiv = createCommentBox ( commentReply , false ) ;
commentReplyBox . appendChild ( commentReplyDiv ) ;
}
2022-11-21 02:55:33 +00:00
if ( totalReplies > 0 ) {
2022-11-14 09:31:49 +00:00
commentBox . appendChild ( commentReplyBox ) ;
}
}
commentsListBox . appendChild ( commentBox ) ;
}
}
2022-11-21 02:55:33 +00:00
function createReplyButton ( replyId , totalReplies ) {
let replyButton = document . createElement ( 'button' ) ;
2023-07-31 16:57:03 +00:00
replyButton . innerHTML = ` <span id="toggle-icon">▼</span> ${ totalReplies } replies ` ;
2022-11-21 02:55:33 +00:00
replyButton . setAttribute ( 'data-id' , replyId ) ;
replyButton . setAttribute ( 'onclick' , 'toggleCommentReplies(this)' ) ;
2022-11-21 03:05:28 +00:00
return replyButton ;
2022-11-21 02:55:33 +00:00
}
function toggleCommentReplies ( button ) {
2022-11-21 03:05:28 +00:00
let commentReplyId = button . getAttribute ( 'data-id' ) ;
2022-11-21 02:55:33 +00:00
let state = document . getElementById ( commentReplyId ) . style . display ;
2022-11-21 08:13:37 +00:00
if ( state === 'none' || state === '' ) {
2022-11-21 03:05:28 +00:00
document . getElementById ( commentReplyId ) . style . display = 'block' ;
2023-07-31 16:48:46 +00:00
button . querySelector ( '#toggle-icon' ) . innerHTML = '▲' ;
2022-11-21 02:55:33 +00:00
} else {
2022-11-21 03:05:28 +00:00
document . getElementById ( commentReplyId ) . style . display = 'none' ;
2023-07-31 16:48:46 +00:00
button . querySelector ( '#toggle-icon' ) . innerHTML = '▼' ;
2022-11-21 02:55:33 +00:00
}
}
2022-11-14 09:31:49 +00:00
function createCommentBox ( comment , isRoot ) {
let commentBox = document . createElement ( 'div' ) ;
commentBox . setAttribute ( 'class' , 'comment-box' ) ;
let commentClass ;
if ( isRoot ) {
commentClass = 'root-comment' ;
} else {
commentClass = 'reply-comment' ;
}
commentBox . classList . add = commentClass ;
let commentAuthor = document . createElement ( 'h3' ) ;
commentAuthor . innerText = comment . comment _author ;
if ( comment . comment _author _is _uploader ) {
commentAuthor . setAttribute ( 'class' , 'comment-highlight' ) ;
}
commentBox . appendChild ( commentAuthor ) ;
let commentText = document . createElement ( 'p' ) ;
commentText . innerText = comment . comment _text ;
commentBox . appendChild ( commentText ) ;
const spacer = '<span class="space-carrot">|</span>' ;
let commentMeta = document . createElement ( 'div' ) ;
commentMeta . setAttribute ( 'class' , 'comment-meta' ) ;
commentMeta . innerHTML = ` <span> ${ comment . comment _time _text } </span> ` ;
if ( comment . comment _likecount > 0 ) {
2022-11-21 03:05:28 +00:00
let numberFormatted = formatNumbers ( comment . comment _likecount ) ;
2022-11-18 02:51:18 +00:00
commentMeta . innerHTML += ` ${ spacer } <span class="thumb-icon"><img src="/static/img/icon-thumb.svg"> ${ numberFormatted } </span> ` ;
2022-11-14 09:31:49 +00:00
}
if ( comment . comment _is _favorited ) {
commentMeta . innerHTML += ` ${ spacer } <span class="comment-like"><img src="/static/img/icon-heart.svg"></span> ` ;
}
commentBox . appendChild ( commentMeta ) ;
return commentBox ;
}
2022-11-21 08:13:37 +00:00
function getSimilarVideos ( videoId ) {
let apiEndpoint = '/api/video/' + videoId + '/similar/' ;
let response = apiRequest ( apiEndpoint , 'GET' ) ;
if ( ! response ) {
populateEmpty ( ) ;
return ;
}
let allSimilar = response . data ;
if ( allSimilar . length > 0 ) {
populateSimilar ( allSimilar ) ;
}
}
function populateSimilar ( allSimilar ) {
let similarBox = document . getElementById ( 'similar-videos' ) ;
for ( let i = 0 ; i < allSimilar . length ; i ++ ) {
const similarRaw = allSimilar [ i ] ;
let similarDiv = createVideo ( similarRaw , 'grid' ) ;
similarBox . appendChild ( similarDiv ) ;
}
}
function populateEmpty ( ) {
let similarBox = document . getElementById ( 'similar-videos' ) ;
let emptyMessage = document . createElement ( 'p' ) ;
emptyMessage . innerText = 'No similar videos found.' ;
similarBox . appendChild ( emptyMessage ) ;
}
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 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
2023-07-31 17:30:11 +00:00
function textReveal ( button ) {
let revealBox = button . parentElement . parentElement ;
let textBox = revealBox . querySelector ( '#text-reveal' ) ;
2022-10-26 02:43:58 +00:00
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
2024-03-10 15:57:59 +00:00
function showForm ( id ) {
let id2 = id === undefined ? 'hidden-form' : id ;
let formElement = document . getElementById ( id2 ) ;
2022-10-26 02:43:58 +00:00
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
2023-03-31 15:28:46 +00:00
// need useCapture so we can prevent events from reaching the player
document . addEventListener ( 'keydown' , doShortcut , true ) ;
2022-10-24 13:11:00 +00:00
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
}
2023-08-26 14:15:36 +00:00
case 'f' : {
e . preventDefault ( ) ;
if ( document . fullscreenElement === null ) {
player . requestFullscreen ( ) . catch ( e => {
console . error ( e ) ;
showModal ( 'Unable to enter fullscreen' , 3000 ) ;
} ) ;
} else {
document . exitFullscreen ( ) . catch ( e => {
console . error ( e ) ;
showModal ( 'Unable to exit fullscreen' , 3000 ) ;
} ) ;
}
break ;
}
2022-10-26 02:43:58 +00:00
case 'ArrowLeft' : {
2023-03-31 15:28:46 +00:00
e . preventDefault ( ) ;
2022-10-26 02:43:58 +00:00
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' : {
2023-03-31 15:28:46 +00:00
e . preventDefault ( ) ;
2022-10-26 02:43:58 +00:00
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 ' ' : {
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 >
2023-08-26 14:15:36 +00:00
< tr > < td > Toggle fullscreen < / t d > < t d > f < / t d >
2022-10-24 13:11:00 +00:00
< 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
}