Add video load time modifications, and account for run-to-live

pull/237/head
ElementalAlchemist 3 years ago committed by Mike Lang
parent 344ca041a3
commit 3fe2848e60

@ -20,20 +20,25 @@
</head> </head>
<body> <body>
<div id="errors"></div> <div id="errors"></div>
<div id="stream-time-settings"> <form id="stream-time-settings">
<div> <div>
<span class="field-label">Stream</span> <span class="field-label">Stream</span>
<span id="stream-time-setting-stream"></span> <span id="stream-time-setting-stream"></span>
</div> </div>
<div> <div>
<span class="field-label">Start Time</span> <label for="stream-time-setting-start" class="field-label">Start Time</label>
<span id="stream-time-setting-start"></span> <input type="text" id="stream-time-setting-start" />
<button id="stream-time-setting-start-pad" type="button">Pad 1 minute</button>
</div> </div>
<div> <div>
<span class="field-label">End Time</span> <label for="stream-time-setting-end" class="field-label">End Time</label>
<span id="stream-time-setting-end"></span> <input type="text" id="stream-time-setting-end" />
<button id="stream-time-setting-end-pad" type="button">Pad 1 minute</button>
</div> </div>
</div> <div>
<button type="submit" id="stream-time-settings-submit">Update Time Range</button>
</div>
</form>
<video id="video" class="video-js" controls preload="auto"></video> <video id="video" class="video-js" controls preload="auto"></video>
<div id="clip-bar"></div> <div id="clip-bar"></div>

@ -76,6 +76,11 @@ async function loadVideoPlayerFromDefaultPlaylist() {
await loadVideoPlayer(playlistURL); await loadVideoPlayer(playlistURL);
} }
function updateSegmentPlaylist() {
const playlistURL = `/playlist/${globalStreamName}.m3u8`;
updateVideoPlayer(playlistURL);
}
function updateVideoPlayer(newPlaylistURL) { function updateVideoPlayer(newPlaylistURL) {
let rangedPlaylistURL = assembleVideoPlaylistURL(newPlaylistURL); let rangedPlaylistURL = assembleVideoPlaylistURL(newPlaylistURL);
const player = getVideoJS(); const player = getVideoJS();
@ -95,6 +100,10 @@ function parseInputTimeAsNumberOfSeconds(inputTime) {
return (parseInt(parts[0]) + (parts[1] || 0) / 60 + (parts[2] || 0) / 3600) * 60 * 60 * direction; return (parseInt(parts[0]) + (parts[1] || 0) / 60 + (parts[2] || 0) / 3600) * 60 * 60 * direction;
} }
function dateObjFromWubloaderTime(wubloaderTime) {
return new Date(`${wubloaderTime}Z`);
}
function getWubloaderTimeFromDate(date) { function getWubloaderTimeFromDate(date) {
if (!date) { if (!date) {
return null; return null;

@ -4,10 +4,119 @@ var currentRange = 1;
window.addEventListener("DOMContentLoaded", async (event) => { window.addEventListener("DOMContentLoaded", async (event) => {
commonPageSetup(); commonPageSetup();
const timeUpdateForm = document.getElementById("stream-time-settings");
timeUpdateForm.addEventListener("submit", (event) => {
event.preventDefault();
if (!videoInfo) {
addError(
"Time updates are ignored before the video metadata has been retrieved from Wubloader."
);
return;
}
const newStartField = document.getElementById("stream-time-setting-start");
const newStart = dateObjFromInputTime(newStartField.value);
if (!newStart) {
addError("Failed to parse start time");
return;
}
const newEndField = document.getElementById("stream-time-setting-end");
let newEnd = null;
if (newEndField.value !== "") {
newEnd = dateObjFromInputTime(newEndField.value);
if (!newEnd) {
addError("Failed to parse end time");
return;
}
}
const oldStart = getStartTime();
const startAdjustment = newStart - oldStart;
const newDuration = newEnd === null ? Infinity : newEnd - newStart;
// Abort for ranges that exceed new times
for (const rangeContainer of document.getElementById("range-definitions").children) {
const rangeStartField = rangeContainer.getElementsByClassName("range-definition-start")[0];
const rangeEndField = rangeContainer.getElementsByClassName("range-definition-end")[0];
const rangeStart = videoPlayerTimeFromVideoHumanTime(rangeStartField.value);
const rangeEnd = videoPlayerTimeFromVideoHumanTime(rangeEndField.value);
if (rangeStart !== null && rangeStart < startAdjustment) {
addError("The specified video load time excludes part of an edited clip range.");
return;
}
if (rangeEnd !== null && rangeEnd + startAdjustment > newDuration) {
addError("The specified video load time excludes part of an edited clip range.");
return;
}
}
globalStartTimeString = getWubloaderTimeFromDateWithMilliseconds(newStart);
globalEndTimeString = getWubloaderTimeFromDateWithMilliseconds(newEnd);
updateSegmentPlaylist();
for (const rangeContainer of document.getElementById("range-definitions").children) {
const rangeStartField = rangeContainer.getElementsByClassName("range-definition-start")[0];
const rangeEndField = rangeContainer.getElementsByClassName("range-definition-end")[0];
const rangeStart = videoPlayerTimeFromVideoHumanTime(rangeStartField.value);
if (rangeStart !== null) {
rangeStartField.value = videoHumanTimeFromVideoPlayerTime(startAdjustment + rangeStart);
}
const rangeEnd = videoPlayerTimeFromVideoHumanTime(rangeEndField.value);
if (rangeEnd !== null) {
rangeEndField.value = videoHumanTimeFromVideoPlayerTime(startAdjustment + rangeEnd);
}
}
const waveformImage = document.getElementById("waveform");
if (newEnd === null) {
waveformImage.classList.add("hidden");
} else {
updateWaveform();
waveformImage.classList.remove("hidden");
}
});
await loadVideoInfo(); await loadVideoInfo();
updateDownloadLink(); updateDownloadLink();
document.getElementById("stream-time-setting-start-pad").addEventListener("click", (_event) => {
const startTimeField = document.getElementById("stream-time-setting-start");
let startTime = startTimeField.value;
startTime = parseInputTimeAsNumberOfSeconds(startTime);
if (isNaN(startTime)) {
addError("Couldn't parse entered start time for padding");
return;
}
startTime -= 60;
const startTimeDate = new Date(globalBusStartTime);
startTimeDate.setSeconds(startTimeDate.getSeconds() + startTime);
startTime = getBusTimeFromDateObj(startTimeDate);
startTimeField.value = startTime;
});
document.getElementById("stream-time-setting-end-pad").addEventListener("click", (_event) => {
const endTimeField = document.getElementById("stream-time-setting-end");
let endTime = endTimeField.value;
endTime = parseInputTimeAsNumberOfSeconds(endTime);
if (isNaN(endTime)) {
addError("Couldn't parse entered end time for padding");
return;
}
endTime += 60;
const endTimeDate = new Date(globalBusStartTime);
endTimeDate.setSeconds(endTimeDate.getSeconds() + endTime);
endTime = getBusTimeFromDateObj(endTimeDate);
endTimeField.value = endTime;
});
const addRangeIcon = document.getElementById("add-range-definition"); const addRangeIcon = document.getElementById("add-range-definition");
addRangeIcon.addEventListener("click", (_event) => { addRangeIcon.addEventListener("click", (_event) => {
addRangeDefinition(); addRangeDefinition();
@ -113,30 +222,41 @@ async function initializeVideoInfo() {
globalStreamName = videoInfo.video_channel; globalStreamName = videoInfo.video_channel;
globalBusStartTime = new Date(videoInfo.bustime_start); globalBusStartTime = new Date(videoInfo.bustime_start);
globalStartTimeString = videoInfo.event_start; const eventStartTime = dateObjFromWubloaderTime(videoInfo.event_start);
globalEndTimeString = videoInfo.event_end; const eventEndTime = dateObjFromWubloaderTime(videoInfo.event_end);
// To account for various things (stream delay, just slightly off logging, etc.), we pad the start time by one minute
eventStartTime.setMinutes(eventStartTime.getMinutes() - 1);
// To account for various things (stream delay, just slightly off logging, etc.), we pad the end time by one minute.
// To account for the fact that we don't record seconds, but the event could've ended any time in the recorded minute, we pad by an additional minute.
eventEndTime.setMinutes(eventEndTime.getMinutes() + 2);
globalStartTimeString = getWubloaderTimeFromDateWithMilliseconds(eventStartTime);
globalEndTimeString = getWubloaderTimeFromDateWithMilliseconds(eventEndTime);
// If a video was previously edited to points outside the video range, we should expand the loaded video to include the edited range // If a video was previously edited to points outside the video range, we should expand the loaded video to include the edited range
if (videoInfo.video_start) { if (videoInfo.video_start) {
const eventStartTime = dateObjFromWubloaderTime(videoInfo.event_start);
const videoStartTime = dateObjFromWubloaderTime(videoInfo.video_start); const videoStartTime = dateObjFromWubloaderTime(videoInfo.video_start);
if (videoStartTime < eventStartTime) { if (videoStartTime < eventStartTime) {
globalStartTimeString = videoInfo.video_start; videoStartTime.setMinutes(videoStartTime.getMinutes() - 1);
globalStartTimeString = getWubloaderTimeFromDateWithMilliseconds(videoStartTime);
} }
} }
if (videoInfo.video_end) { if (videoInfo.video_end) {
const eventEndTime = dateObjFromWubloaderTime(videoInfo.event_end);
const videoEndTime = dateObjFromWubloaderTime(videoInfo.video_end); const videoEndTime = dateObjFromWubloaderTime(videoInfo.video_end);
if (videoEndTime > eventEndTime) { if (videoEndTime > eventEndTime) {
globalEndTimeString = videoInfo.video_end; // If we're getting the time from a previous draft edit, we don't need to pad as hard on the end
videoEndTime.setMinutes(videoEndTime.getMinutes() + 1);
globalEndTimeString = getWubloaderTimeFromDateWithMilliseconds(videoEndTime);
} }
} }
document.getElementById("stream-time-setting-stream").innerText = globalStreamName; document.getElementById("stream-time-setting-stream").innerText = globalStreamName;
document.getElementById("stream-time-setting-start").innerText = document.getElementById("stream-time-setting-start").value =
getBusTimeFromTimeString(globalStartTimeString); getBusTimeFromTimeString(globalStartTimeString);
document.getElementById("stream-time-setting-end").innerText = document.getElementById("stream-time-setting-end").value =
getBusTimeFromTimeString(globalEndTimeString); getBusTimeFromTimeString(globalEndTimeString);
updateWaveform(); updateWaveform();
@ -276,27 +396,22 @@ function getStartTime() {
if (!globalStartTimeString) { if (!globalStartTimeString) {
return null; return null;
} }
return dateObjFromWubloaderTime(globalStartTimeString);
const date = dateObjFromWubloaderTime(globalStartTimeString);
// To account for various things (stream delay, just slightly off logging, etc.), we pad the start time by one minute
date.setMinutes(date.getMinutes() - 1);
return date;
} }
function getEndTime() { function getEndTime() {
if (!globalEndTimeString) { if (!globalEndTimeString) {
return null; return null;
} }
return dateObjFromWubloaderTime(globalEndTimeString);
const date = dateObjFromWubloaderTime(globalEndTimeString);
// To account for various things (stream delay, just slightly off logging, etc.), we pad the end time by one minute.
// To account for the fact that we don't record seconds, but the event could've ended any time in the recorded minute, we pad by an additional minute.
date.setMinutes(date.getMinutes() + 2);
return date;
} }
function getBusTimeFromTimeString(timeString) { function getBusTimeFromTimeString(timeString) {
const time = dateObjFromWubloaderTime(timeString); const time = dateObjFromWubloaderTime(timeString);
return getBusTimeFromDateObj(time);
}
function getBusTimeFromDateObj(time) {
const busTimeMilliseconds = time - globalBusStartTime; const busTimeMilliseconds = time - globalBusStartTime;
let remainingBusTimeSeconds = busTimeMilliseconds / 1000; let remainingBusTimeSeconds = busTimeMilliseconds / 1000;
@ -304,7 +419,7 @@ function getBusTimeFromTimeString(timeString) {
remainingBusTimeSeconds %= 3600; remainingBusTimeSeconds %= 3600;
let minutes = Math.floor(remainingBusTimeSeconds / 60); let minutes = Math.floor(remainingBusTimeSeconds / 60);
let seconds = remainingBusTimeSeconds % 60; let seconds = remainingBusTimeSeconds % 60;
let milliseconds = Math.round(seconds % 1 * 1000); let milliseconds = Math.round((seconds % 1) * 1000);
seconds = Math.trunc(seconds); seconds = Math.trunc(seconds);
while (minutes.toString().length < 2) { while (minutes.toString().length < 2) {
@ -929,8 +1044,12 @@ function wubloaderTimeFromVideoHumanTime(videoHumanTime) {
return wubloaderTimeFromVideoPlayerTime(videoPlayerTime); return wubloaderTimeFromVideoPlayerTime(videoPlayerTime);
} }
function dateObjFromWubloaderTime(wubloaderTime) { function dateObjFromInputTime(inputTime) {
return new Date(`${wubloaderTime}Z`); const inputSeconds = parseInputTimeAsNumberOfSeconds(inputTime);
if (isNaN(inputSeconds)) {
return null;
}
return new Date(globalBusStartTime.getTime() + 1000 * inputSeconds);
} }
function getPlaylistData() { function getPlaylistData() {

@ -36,7 +36,7 @@ async function loadDefaults() {
function getStartTime() { function getStartTime() {
switch (globalVideoTimeReference) { switch (globalVideoTimeReference) {
case 1: case 1:
return new Date(globalStartTimeString + "Z"); return dateObjFromWubloaderTime(globalStartTimeString);
case 2: case 2:
return new Date( return new Date(
globalBusStartTime.getTime() + 1000 * parseInputTimeAsNumberOfSeconds(globalStartTimeString) globalBusStartTime.getTime() + 1000 * parseInputTimeAsNumberOfSeconds(globalStartTimeString)
@ -55,7 +55,7 @@ function getEndTime() {
} }
switch (globalVideoTimeReference) { switch (globalVideoTimeReference) {
case 1: case 1:
return new Date(globalEndTimeString + "Z"); return dateObjFromWubloaderTime(globalEndTimeString);
case 2: case 2:
return new Date( return new Date(
globalBusStartTime.getTime() + 1000 * parseInputTimeAsNumberOfSeconds(globalEndTimeString) globalBusStartTime.getTime() + 1000 * parseInputTimeAsNumberOfSeconds(globalEndTimeString)
@ -87,11 +87,6 @@ function updateTimeSettings() {
} }
} }
function updateSegmentPlaylist() {
const playlistURL = `/playlist/${globalStreamName}.m3u8`;
updateVideoPlayer(playlistURL);
}
function updateDownloadLink() { function updateDownloadLink() {
const downloadLink = document.getElementById("download"); const downloadLink = document.getElementById("download");
const downloadURL = generateDownloadURL(getStartTime(), getEndTime(), "rough", true, "source"); const downloadURL = generateDownloadURL(getStartTime(), getEndTime(), "rough", true, "source");

Loading…
Cancel
Save