@ -2,6 +2,7 @@ var googleUser = null;
var videoInfo ;
var currentRange = 1 ;
let knownTransitions = [ ] ;
let thumbnailTemplates = { } ;
let globalPageState = 0 ;
const CHAPTER _MARKER _DELIMITER = "\n==========\n" ;
@ -227,6 +228,9 @@ window.addEventListener("DOMContentLoaded", async (event) => {
validateVideoDescription ( ) ;
handleFieldChange ( event ) ;
} ) ;
document
. getElementById ( "video-info-thumbnail-template" )
. addEventListener ( "change" , thumbnailTemplateChanged ) ;
document
. getElementById ( "video-info-thumbnail-mode" )
. addEventListener ( "change" , updateThumbnailInputState ) ;
@ -267,27 +271,50 @@ window.addEventListener("DOMContentLoaded", async (event) => {
return ;
}
const imageTemplate = document . getElementById ( "video-info-thumbnail-template" ) . value ;
imageElement . src = ` /thumbnail/ ${ globalStreamName } /source.png?timestamp= ${ imageTime } &template= ${ imageTemplate } ` ;
const [ crop , loc ] = getTemplatePosition ( ) ;
const queryParts = [
` timestamp= ${ imageTime } ` ,
` template= ${ imageTemplate } ` ,
` crop= ${ crop . join ( "," ) } ` ,
` location= ${ loc . join ( "," ) } ` ,
] ;
imageElement . src = ` /thumbnail/ ${ globalStreamName } /source.png? ${ queryParts . join ( "&" ) } ` ;
imageElement . classList . remove ( "hidden" ) ;
} ) ;
const thumbnailTemplateSelection = document . getElementById ( "video-info-thumbnail-template" ) ;
const thumbnailTemplatesListResponse = await fetch ( "/thrimshim/templates" ) ;
if ( thumbnailTemplatesListResponse . ok ) {
const thumbnailTemplatesList = ( await thumbnailTemplatesListResponse . json ( ) ) . map ( t => t . name ) ;
thumbnailTemplatesList . sort ( ) ;
for ( const templateName of thumbnailTemplatesList ) {
const thumbnailTemplatesList = await thumbnailTemplatesListResponse . json ( ) ;
const templateNames = thumbnailTemplatesList . map ( t => t . name ) ;
templateNames . sort ( ) ;
for ( const template of thumbnailTemplatesList ) {
thumbnailTemplates [ template . name ] = template ;
}
for ( const templateName of templateNames ) {
const templateOption = document . createElement ( "option" ) ;
templateOption . innerText = templateName ;
templateOption . value = templateName ;
templateOption . title = thumbnailTemplates [ templateName ] . description ;
if ( templateName === videoInfo . thumbnail _template ) {
templateOption . selected = true ;
}
thumbnailTemplateSelection . appendChild ( templateOption ) ;
}
thumbnailTemplateChanged ( ) ;
} else {
addError ( "Failed to load thumbnail templates list" ) ;
}
if ( videoInfo . thumbnail _crop !== null ) {
for ( let i = 0 ; i < 4 ; i ++ ) {
document . getElementById ( ` video-info-thumbnail-crop- ${ i } ` ) . value = videoInfo . thumbnail _crop [ i ] ;
}
}
if ( videoInfo . thumbnail _location !== null ) {
for ( let i = 0 ; i < 4 ; i ++ ) {
document . getElementById ( ` video-info-thumbnail-location- ${ i } ` ) . value = videoInfo . thumbnail _location [ i ] ;
}
}
document . getElementById ( "video-info-thumbnail-mode" ) . value = videoInfo . thumbnail _mode ;
updateThumbnailInputState ( ) ;
if ( videoInfo . thumbnail _time ) {
@ -841,6 +868,7 @@ function updateThumbnailInputState(event) {
} else if ( newValue === "TEMPLATE" ) {
unhideIDs . push ( "video-info-thumbnail-template-options" ) ;
unhideIDs . push ( "video-info-thumbnail-time-options" ) ;
unhideIDs . push ( "video-info-thumbnail-position-options" ) ;
unhideIDs . push ( "video-info-thumbnail-template-preview" ) ;
} else if ( newValue === "CUSTOM" ) {
unhideIDs . push ( "video-info-thumbnail-custom-options" ) ;
@ -856,6 +884,38 @@ function updateThumbnailInputState(event) {
}
}
function thumbnailTemplateChanged ( event ) {
handleFieldChange ( event ) ;
const newTemplate = document . getElementById ( "video-info-thumbnail-template" ) . value ;
for ( const field of [ "crop" , "location" ] ) {
const newValue = thumbnailTemplates [ newTemplate ] [ field ] ;
for ( let i = 0 ; i < 4 ; i ++ ) {
document . getElementById ( ` video-info-thumbnail- ${ field } - ${ i } ` ) . value = newValue [ i ] ;
}
}
}
// Returns [crop, location], with either being null on error.
function getTemplatePosition ( ) {
const ret = [ ] ;
for ( const field of [ "crop" , "location" ] ) {
let values = [ null , null , null , null ] ;
for ( let i = 0 ; i < 4 ; i ++ ) {
const value = parseInt ( document . getElementById ( ` video-info-thumbnail- ${ field } - ${ i } ` ) . value ) ;
if ( isNaN ( value ) ) {
values = null ;
break ;
}
values [ i ] = value ;
}
ret . push ( values ) ;
}
return ret ;
}
function getStartTime ( ) {
if ( ! globalStartTimeString ) {
return null ;
@ -1092,6 +1152,8 @@ async function sendVideoData(newState, overrideChanges) {
let thumbnailTemplate = null ;
let thumbnailTime = null ;
let thumbnailImage = null ;
let thumbnailCrop = null ;
let thumbnailLocation = null ;
if ( thumbnailMode === "BARE" || thumbnailMode === "TEMPLATE" ) {
thumbnailTime = wubloaderTimeFromVideoHumanTime (
document . getElementById ( "video-info-thumbnail-time" ) . value ,
@ -1103,6 +1165,11 @@ async function sendVideoData(newState, overrideChanges) {
}
if ( thumbnailMode === "TEMPLATE" ) {
thumbnailTemplate = document . getElementById ( "video-info-thumbnail-template" ) . value ;
[ thumbnailCrop , thumbnailLocation ] = getTemplatePosition ( ) ;
if ( thumbnailCrop === null || thumbnailLocation === null ) {
submissionError ( "The thumbnail crop/location options are invalid" ) ;
return ;
}
}
if ( thumbnailMode === "CUSTOM" ) {
const fileInput = document . getElementById ( "video-info-thumbnail-custom" ) ;
@ -1164,6 +1231,8 @@ async function sendVideoData(newState, overrideChanges) {
state : newState ,
thumbnail _mode : thumbnailMode ,
thumbnail _template : thumbnailTemplate ,
thumbnail _crop : thumbnailCrop ,
thumbnail _location : thumbnailLocation ,
thumbnail _time : thumbnailTime ,
thumbnail _image : thumbnailImage ,