|
|
@ -1,16 +1,25 @@
|
|
|
|
var googleUser = null;
|
|
|
|
var googleUser = null;
|
|
|
|
var videoInfo;
|
|
|
|
var videoInfo;
|
|
|
|
var currentRange = 1;
|
|
|
|
var currentRange = 1;
|
|
|
|
|
|
|
|
let globalPageState = 0;
|
|
|
|
|
|
|
|
|
|
|
|
const CHAPTER_MARKER_DELIMITER = "\n==========\n";
|
|
|
|
const CHAPTER_MARKER_DELIMITER = "\n==========\n";
|
|
|
|
const CHAPTER_MARKER_DELIMITER_PARTIAL = "==========";
|
|
|
|
const CHAPTER_MARKER_DELIMITER_PARTIAL = "==========";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const PAGE_STATE = {
|
|
|
|
|
|
|
|
CLEAN: 0,
|
|
|
|
|
|
|
|
DIRTY: 1,
|
|
|
|
|
|
|
|
SUBMITTING: 2,
|
|
|
|
|
|
|
|
CONFIRMING: 3,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener("DOMContentLoaded", async (event) => {
|
|
|
|
window.addEventListener("DOMContentLoaded", async (event) => {
|
|
|
|
commonPageSetup();
|
|
|
|
commonPageSetup();
|
|
|
|
globalLoadChatWorker.onmessage = (event) => {
|
|
|
|
globalLoadChatWorker.onmessage = (event) => {
|
|
|
|
updateChatDataFromWorkerResponse(event.data);
|
|
|
|
updateChatDataFromWorkerResponse(event.data);
|
|
|
|
renderChatLog();
|
|
|
|
renderChatLog();
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
window.addEventListener("beforeunload", handleLeavePage);
|
|
|
|
|
|
|
|
|
|
|
|
const timeUpdateForm = document.getElementById("stream-time-settings");
|
|
|
|
const timeUpdateForm = document.getElementById("stream-time-settings");
|
|
|
|
timeUpdateForm.addEventListener("submit", async (event) => {
|
|
|
|
timeUpdateForm.addEventListener("submit", async (event) => {
|
|
|
@ -51,7 +60,8 @@ window.addEventListener("DOMContentLoaded", async (event) => {
|
|
|
|
newDuration += segmentList[segmentList.length - 1].duration;
|
|
|
|
newDuration += segmentList[segmentList.length - 1].duration;
|
|
|
|
|
|
|
|
|
|
|
|
// Abort for ranges that exceed new times
|
|
|
|
// Abort for ranges that exceed new times
|
|
|
|
for (const rangeContainer of document.getElementById("range-definitions").children) {
|
|
|
|
const rangeDefinitionsElements = document.getElementById("range-definitions").children;
|
|
|
|
|
|
|
|
for (const rangeContainer of rangeDefinitionsElements) {
|
|
|
|
const rangeStartField = rangeContainer.getElementsByClassName("range-definition-start")[0];
|
|
|
|
const rangeStartField = rangeContainer.getElementsByClassName("range-definition-start")[0];
|
|
|
|
const rangeEndField = rangeContainer.getElementsByClassName("range-definition-end")[0];
|
|
|
|
const rangeEndField = rangeContainer.getElementsByClassName("range-definition-end")[0];
|
|
|
|
const rangeStart = videoPlayerTimeFromVideoHumanTime(rangeStartField.value);
|
|
|
|
const rangeStart = videoPlayerTimeFromVideoHumanTime(rangeStartField.value);
|
|
|
@ -68,7 +78,6 @@ window.addEventListener("DOMContentLoaded", async (event) => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const rangesData = [];
|
|
|
|
const rangesData = [];
|
|
|
|
const rangeDefinitionsElements = document.getElementById("range-definitions").children;
|
|
|
|
|
|
|
|
for (const rangeContainer of rangeDefinitionsElements) {
|
|
|
|
for (const rangeContainer of rangeDefinitionsElements) {
|
|
|
|
const rangeStartField = rangeContainer.getElementsByClassName("range-definition-start")[0];
|
|
|
|
const rangeStartField = rangeContainer.getElementsByClassName("range-definition-start")[0];
|
|
|
|
const rangeEndField = rangeContainer.getElementsByClassName("range-definition-end")[0];
|
|
|
|
const rangeEndField = rangeContainer.getElementsByClassName("range-definition-end")[0];
|
|
|
@ -152,19 +161,22 @@ window.addEventListener("DOMContentLoaded", async (event) => {
|
|
|
|
|
|
|
|
|
|
|
|
const addRangeIcon = document.getElementById("add-range-definition");
|
|
|
|
const addRangeIcon = document.getElementById("add-range-definition");
|
|
|
|
if (videoInfo.state !== "DONE") {
|
|
|
|
if (videoInfo.state !== "DONE") {
|
|
|
|
addRangeIcon.addEventListener("click", (_event) => {
|
|
|
|
addRangeIcon.addEventListener("click", (event) => {
|
|
|
|
addRangeDefinition();
|
|
|
|
addRangeDefinition();
|
|
|
|
|
|
|
|
handleFieldChange(event);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
addRangeIcon.addEventListener("keypress", (event) => {
|
|
|
|
addRangeIcon.addEventListener("keypress", (event) => {
|
|
|
|
if (event.key === "Enter") {
|
|
|
|
if (event.key === "Enter") {
|
|
|
|
addRangeDefinition();
|
|
|
|
addRangeDefinition();
|
|
|
|
|
|
|
|
handleFieldChange(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const enableChaptersElem = document.getElementById("enable-chapter-markers");
|
|
|
|
const enableChaptersElem = document.getElementById("enable-chapter-markers");
|
|
|
|
enableChaptersElem.addEventListener("change", (_event) => {
|
|
|
|
enableChaptersElem.addEventListener("change", (event) => {
|
|
|
|
changeEnableChaptersHandler();
|
|
|
|
changeEnableChaptersHandler();
|
|
|
|
|
|
|
|
handleFieldChange(event);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
for (const rangeStartSet of document.getElementsByClassName("range-definition-set-start")) {
|
|
|
|
for (const rangeStartSet of document.getElementsByClassName("range-definition-set-start")) {
|
|
|
@ -180,13 +192,15 @@ window.addEventListener("DOMContentLoaded", async (event) => {
|
|
|
|
rangeEndPlay.addEventListener("click", rangePlayFromEndHandler);
|
|
|
|
rangeEndPlay.addEventListener("click", rangePlayFromEndHandler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const rangeStart of document.getElementsByClassName("range-definition-start")) {
|
|
|
|
for (const rangeStart of document.getElementsByClassName("range-definition-start")) {
|
|
|
|
rangeStart.addEventListener("change", (_event) => {
|
|
|
|
rangeStart.addEventListener("change", (event) => {
|
|
|
|
rangeDataUpdated();
|
|
|
|
rangeDataUpdated();
|
|
|
|
|
|
|
|
handleFieldChange(event);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const rangeEnd of document.getElementsByClassName("range-definition-end")) {
|
|
|
|
for (const rangeEnd of document.getElementsByClassName("range-definition-end")) {
|
|
|
|
rangeEnd.addEventListener("change", (_event) => {
|
|
|
|
rangeEnd.addEventListener("change", (event) => {
|
|
|
|
rangeDataUpdated();
|
|
|
|
rangeDataUpdated();
|
|
|
|
|
|
|
|
handleFieldChange(event);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const addChapterMarker of document.getElementsByClassName(
|
|
|
|
for (const addChapterMarker of document.getElementsByClassName(
|
|
|
@ -195,17 +209,22 @@ window.addEventListener("DOMContentLoaded", async (event) => {
|
|
|
|
addChapterMarker.addEventListener("click", addChapterMarkerHandler);
|
|
|
|
addChapterMarker.addEventListener("click", addChapterMarkerHandler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById("video-info-title").addEventListener("input", (_event) => {
|
|
|
|
document.getElementById("video-info-title").addEventListener("input", (event) => {
|
|
|
|
validateVideoTitle();
|
|
|
|
validateVideoTitle();
|
|
|
|
document.getElementById("video-info-title-abbreviated").innerText =
|
|
|
|
document.getElementById("video-info-title-abbreviated").innerText =
|
|
|
|
videoInfo.title_prefix + document.getElementById("video-info-title").value;
|
|
|
|
videoInfo.title_prefix + document.getElementById("video-info-title").value;
|
|
|
|
|
|
|
|
handleFieldChange(event);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
document.getElementById("video-info-description").addEventListener("input", (_event) => {
|
|
|
|
document.getElementById("video-info-description").addEventListener("input", (event) => {
|
|
|
|
validateVideoDescription();
|
|
|
|
validateVideoDescription();
|
|
|
|
|
|
|
|
handleFieldChange(event);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
document
|
|
|
|
document
|
|
|
|
.getElementById("video-info-thumbnail-mode")
|
|
|
|
.getElementById("video-info-thumbnail-mode")
|
|
|
|
.addEventListener("change", updateThumbnailInputState);
|
|
|
|
.addEventListener("change", updateThumbnailInputState);
|
|
|
|
|
|
|
|
document
|
|
|
|
|
|
|
|
.getElementById("video-info-thumbnail-time")
|
|
|
|
|
|
|
|
.addEventListener("change", handleFieldChange);
|
|
|
|
document.getElementById("video-info-thumbnail-time-set").addEventListener("click", (_event) => {
|
|
|
|
document.getElementById("video-info-thumbnail-time-set").addEventListener("click", (_event) => {
|
|
|
|
const field = document.getElementById("video-info-thumbnail-time");
|
|
|
|
const field = document.getElementById("video-info-thumbnail-time");
|
|
|
|
const videoPlayer = document.getElementById("video");
|
|
|
|
const videoPlayer = document.getElementById("video");
|
|
|
@ -264,6 +283,8 @@ window.addEventListener("DOMContentLoaded", async (event) => {
|
|
|
|
);
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that changing values on load doesn't set keep the page dirty.
|
|
|
|
|
|
|
|
globalPageState = PAGE_STATE.CLEAN;
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById("submit-button").addEventListener("click", (_event) => {
|
|
|
|
document.getElementById("submit-button").addEventListener("click", (_event) => {
|
|
|
|
submitVideo();
|
|
|
|
submitVideo();
|
|
|
@ -619,6 +640,9 @@ async function initializeVideoInfo() {
|
|
|
|
const timePercent = (videoElement.currentTime / videoElement.duration) * 100;
|
|
|
|
const timePercent = (videoElement.currentTime / videoElement.duration) * 100;
|
|
|
|
document.getElementById("waveform-marker").style.left = `${timePercent}%`;
|
|
|
|
document.getElementById("waveform-marker").style.left = `${timePercent}%`;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Ensure that changes made to fields during initial load don't affect the state
|
|
|
|
|
|
|
|
globalPageState = PAGE_STATE.CLEAN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function updateWaveform() {
|
|
|
|
function updateWaveform() {
|
|
|
@ -651,7 +675,9 @@ async function googleSignOut() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function updateThumbnailInputState() {
|
|
|
|
function updateThumbnailInputState(event) {
|
|
|
|
|
|
|
|
handleFieldChange(event);
|
|
|
|
|
|
|
|
|
|
|
|
const newValue = document.getElementById("video-info-thumbnail-mode").value;
|
|
|
|
const newValue = document.getElementById("video-info-thumbnail-mode").value;
|
|
|
|
const unhideIDs = [];
|
|
|
|
const unhideIDs = [];
|
|
|
|
|
|
|
|
|
|
|
@ -723,21 +749,6 @@ function validateVideoDescription() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function submitVideo() {
|
|
|
|
async function submitVideo() {
|
|
|
|
const enableChaptersElem = document.getElementById("enable-chapter-markers");
|
|
|
|
|
|
|
|
const chapterStartFieldList = document.getElementsByClassName("range-definition-chapter-time");
|
|
|
|
|
|
|
|
if (enableChaptersElem.checked && chapterStartFieldList.length > 0) {
|
|
|
|
|
|
|
|
const firstRangeStartElem = document.getElementsByClassName("range-definition-start")[0];
|
|
|
|
|
|
|
|
const firstRangeStart = videoPlayerTimeFromMVideoHumanTime(firstRangeStartElem.value);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const firstChapterStartField = chapterStartFieldList[0];
|
|
|
|
|
|
|
|
const firstChapterStart = videoPlayerTimeFromVideoHumanTime(firstChapterStartField.value);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (firstRangeStart !== firstChapterStart) {
|
|
|
|
|
|
|
|
addError("The first chapter marker must be at the beginning of the video");
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return sendVideoData("EDITED", false);
|
|
|
|
return sendVideoData("EDITED", false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -763,7 +774,6 @@ async function sendVideoData(newState, overrideChanges) {
|
|
|
|
const submissionResponseElem = document.getElementById("submission-response");
|
|
|
|
const submissionResponseElem = document.getElementById("submission-response");
|
|
|
|
submissionResponseElem.classList.value = ["submission-response-pending"];
|
|
|
|
submissionResponseElem.classList.value = ["submission-response-pending"];
|
|
|
|
submissionResponseElem.innerText = "Submitting video...";
|
|
|
|
submissionResponseElem.innerText = "Submitting video...";
|
|
|
|
window.addEventListener("beforeunload", handleLeavePageWhilePending);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const rangesData = [];
|
|
|
|
const rangesData = [];
|
|
|
|
let chaptersData = [];
|
|
|
|
let chaptersData = [];
|
|
|
@ -994,6 +1004,8 @@ async function sendVideoData(newState, overrideChanges) {
|
|
|
|
editData.override_changes = true;
|
|
|
|
editData.override_changes = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
globalPageState = PAGE_STATE.SUBMITTING;
|
|
|
|
|
|
|
|
|
|
|
|
const submitResponse = await fetch(`/thrimshim/${videoInfo.id}`, {
|
|
|
|
const submitResponse = await fetch(`/thrimshim/${videoInfo.id}`, {
|
|
|
|
method: "POST",
|
|
|
|
method: "POST",
|
|
|
|
headers: {
|
|
|
|
headers: {
|
|
|
@ -1003,9 +1015,8 @@ async function sendVideoData(newState, overrideChanges) {
|
|
|
|
body: JSON.stringify(editData),
|
|
|
|
body: JSON.stringify(editData),
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
window.removeEventListener("beforeunload", handleLeavePageWhilePending);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (submitResponse.ok) {
|
|
|
|
if (submitResponse.ok) {
|
|
|
|
|
|
|
|
globalPageState = PAGE_STATE.CLEAN;
|
|
|
|
submissionResponseElem.classList.value = ["submission-response-success"];
|
|
|
|
submissionResponseElem.classList.value = ["submission-response-success"];
|
|
|
|
if (newState === "EDITED") {
|
|
|
|
if (newState === "EDITED") {
|
|
|
|
submissionResponseElem.innerText = "Submitted edit";
|
|
|
|
submissionResponseElem.innerText = "Submitted edit";
|
|
|
@ -1029,8 +1040,10 @@ async function sendVideoData(newState, overrideChanges) {
|
|
|
|
submissionResponseElem.innerText = `Submitted state ${newState}`;
|
|
|
|
submissionResponseElem.innerText = `Submitted state ${newState}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
|
|
|
|
globalPageState = PAGE_STATE.DIRTY;
|
|
|
|
submissionResponseElem.classList.value = ["submission-response-error"];
|
|
|
|
submissionResponseElem.classList.value = ["submission-response-error"];
|
|
|
|
if (submitResponse.status === 409) {
|
|
|
|
if (submitResponse.status === 409) {
|
|
|
|
|
|
|
|
globalPageState = PAGE_STATE.CONFIRMING;
|
|
|
|
const serverErrorNode = document.createTextNode(await submitResponse.text());
|
|
|
|
const serverErrorNode = document.createTextNode(await submitResponse.text());
|
|
|
|
const submitButton = document.createElement("button");
|
|
|
|
const submitButton = document.createElement("button");
|
|
|
|
submitButton.innerText = "Submit Anyway";
|
|
|
|
submitButton.innerText = "Submit Anyway";
|
|
|
@ -1080,10 +1093,29 @@ function unformatChapterTime(chapterTime) {
|
|
|
|
return hours * 3600 + minutes * 60 + seconds;
|
|
|
|
return hours * 3600 + minutes * 60 + seconds;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleLeavePageWhilePending(event) {
|
|
|
|
function handleFieldChange(_event) {
|
|
|
|
|
|
|
|
globalPageState = PAGE_STATE.DIRTY;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function handleLeavePage(event) {
|
|
|
|
|
|
|
|
if (globalPageState === PAGE_STATE.CLEAN) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
event.preventDefault();
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
|
|
switch (globalPageState) {
|
|
|
|
|
|
|
|
case PAGE_STATE.DIRTY:
|
|
|
|
event.returnValue =
|
|
|
|
event.returnValue =
|
|
|
|
"The video submission is still pending. Are you sure you want to exit? You may lose your edits.";
|
|
|
|
"There are unsaved edits. Are you sure you want to exit? You will lose your edits.";
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PAGE_STATE.SUBMITTING:
|
|
|
|
|
|
|
|
event.returnValue =
|
|
|
|
|
|
|
|
"The video is stsill being submitted. Are you sure you want to exit? You may lose your edits.";
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PAGE_STATE.CONFIRMING:
|
|
|
|
|
|
|
|
event.returnValue =
|
|
|
|
|
|
|
|
"There's a confirmation for video submission. Are you sure you want to exit? You will lose your edits.";
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
return event.returnValue;
|
|
|
|
return event.returnValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -1288,6 +1320,8 @@ function rangeDefinitionDOM() {
|
|
|
|
rangeEndPlay.addEventListener("click", rangePlayFromEndHandler);
|
|
|
|
rangeEndPlay.addEventListener("click", rangePlayFromEndHandler);
|
|
|
|
|
|
|
|
|
|
|
|
removeRange.addEventListener("click", (event) => {
|
|
|
|
removeRange.addEventListener("click", (event) => {
|
|
|
|
|
|
|
|
handleFieldChange(event);
|
|
|
|
|
|
|
|
|
|
|
|
let rangeContainer = event.currentTarget;
|
|
|
|
let rangeContainer = event.currentTarget;
|
|
|
|
while (rangeContainer && !rangeContainer.classList.contains("range-definition-removable")) {
|
|
|
|
while (rangeContainer && !rangeContainer.classList.contains("range-definition-removable")) {
|
|
|
|
rangeContainer = rangeContainer.parentElement;
|
|
|
|
rangeContainer = rangeContainer.parentElement;
|
|
|
@ -1495,6 +1529,7 @@ function chapterMarkerDefinitionDOM() {
|
|
|
|
function addChapterMarkerHandler(event) {
|
|
|
|
function addChapterMarkerHandler(event) {
|
|
|
|
const newChapterMarker = chapterMarkerDefinitionDOM();
|
|
|
|
const newChapterMarker = chapterMarkerDefinitionDOM();
|
|
|
|
event.currentTarget.previousElementSibling.appendChild(newChapterMarker);
|
|
|
|
event.currentTarget.previousElementSibling.appendChild(newChapterMarker);
|
|
|
|
|
|
|
|
handleFieldChange(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function rangeDataUpdated() {
|
|
|
|
async function rangeDataUpdated() {
|
|
|
|