diff --git a/.prettierrc.yaml b/.prettierrc.yaml
new file mode 100644
index 0000000..2c4b8e9
--- /dev/null
+++ b/.prettierrc.yaml
@@ -0,0 +1,11 @@
+# Config for auto-formatting of our JS files using prettier
+# https://prettier.io/docs/en/configuration.html
+
+# This is NOT a length limit but a hint to the auto-formatter for how long lines should try to be.
+printWidth: 100
+# Use tabs for indentation, spaces for alignment
+useTabs: true
+# do {foo: bar} not { foo: bar }. personal preference.
+bracketSpacing: false
+# prefer "arg => body" over "(arg) => body" when possible
+arrowParens: avoid
diff --git a/thrimbletrimmer/scripts/IO.js b/thrimbletrimmer/scripts/IO.js
index 3ce0eaa..7b08474 100644
--- a/thrimbletrimmer/scripts/IO.js
+++ b/thrimbletrimmer/scripts/IO.js
@@ -1,423 +1,515 @@
var desertBusStart = new Date("1970-01-01T00:00:00Z");
-var timeFormat = 'AGO';
-
-pageSetup = function(isEditor) {
-
- //Get values from ThrimShim
- if(isEditor && /id=/.test(document.location.search)) {
- var rowId = /id=(.*)(?:&|$)/.exec(document.location.search)[1];
- fetch("/thrimshim/"+rowId).then(data => data.json()).then(function (data) {
- if (!data) {
- alert("No video available for stream.");
- return;
- }
- document.data = data
- desertBusStart = new Date(data.bustime_start);
- document.getElementById("VideoTitlePrefix").value = data.title_prefix;
- document.getElementById("VideoTitle").setAttribute("maxlength", data.title_max_length);
-
- document.getElementById("StreamName").value = data.video_channel;
- document.getElementById("hiddenSubmissionID").value = data.id;
- // for editor, switch to bustime since that's the default
- timeFormat = 'BUSTIME';
- // Apply padding - start 1min early, finish 2min late because these times are generally
- // rounded down to the minute, so if something ends at "00:10" it might actually end
- // at 00:10:59 so we should pad to 00:12:00.
- var start = (data.event_start) ? new Date(fromTimestamp(data.event_start).getTime() - 60*1000) : null;
- var end = (data.event_end) ? new Date(fromTimestamp(data.event_end).getTime() + 2*60*1000) : null;
- setTimeRange(start, end);
- // title and description both default to row description
- document.getElementById("VideoTitle").value = data.video_title ? data.video_title : data.description;
- document.getElementById("VideoDescription").value = data.video_description ? data.video_description : data.description;
- // tags default to tags from sheet
- document.getElementById("VideoTags").value = tags_list_to_string(data.video_tags ? data.video_tags : data.tags);
-
- // If any edit notes, show them
- if (data.notes.length > 0) {
- document.getElementById("EditNotes").value = data.notes;
- document.getElementById("EditNotesPane").style.display = "block";
- }
-
- // Restore advanced options. If any of these are non-default, automatically expand the advanced options pane.
- setOptions('uploadLocation', data.upload_locations, data.upload_location);
- document.getElementById("AllowHoles").checked = data.allow_holes;
- document.getElementById("uploaderWhitelist").value = (!!data.uploader_whitelist) ? data.uploader_whitelist.join(",") : "";
- if (
- (
- data.upload_locations.length > 0
- && data.upload_location != null
- && data.upload_location != data.upload_locations[0]
- )
- || data.allow_holes
- || !!data.uploader_whitelist
- ) {
- document.getElementById('wubloaderAdvancedInputTable').style.display = "block";
- }
-
- loadPlaylist(isEditor, data.video_start, data.video_end, data.video_quality);
- });
- }
- else {
- if (isEditor) { document.getElementById('SubmitButton').disabled = true; }
-
- fetch("/thrimshim/defaults").then(data => data.json()).then(function (data) {
- if (!data) {
- alert("Editor results call failed, is thrimshim running?");
- return;
- }
- desertBusStart = new Date(data.bustime_start);
- document.getElementById("StreamName").value = data.video_channel;
- if (isEditor) {
- document.getElementById("VideoTitlePrefix").value = data.title_prefix;
- document.getElementById("VideoTitle").setAttribute("maxlength", data.title_max_length);
- setOptions('uploadLocation', data.upload_locations);
- }
-
- // Default time format changes depending on mode.
- // But in both cases the default input value is 10min ago / "",
- // it's just for editor we convert it before the user sees.
- if (isEditor) {
- toggleTimeInput('BUSTIME');
- }
-
- loadPlaylist(isEditor);
- });
-
- }
+var timeFormat = "AGO";
+
+pageSetup = function (isEditor) {
+ //Get values from ThrimShim
+ if (isEditor && /id=/.test(document.location.search)) {
+ var rowId = /id=(.*)(?:&|$)/.exec(document.location.search)[1];
+ fetch("/thrimshim/" + rowId)
+ .then(data => data.json())
+ .then(function (data) {
+ if (!data) {
+ alert("No video available for stream.");
+ return;
+ }
+ document.data = data;
+ desertBusStart = new Date(data.bustime_start);
+ document.getElementById("VideoTitlePrefix").value = data.title_prefix;
+ document.getElementById("VideoTitle").setAttribute("maxlength", data.title_max_length);
+
+ document.getElementById("StreamName").value = data.video_channel;
+ document.getElementById("hiddenSubmissionID").value = data.id;
+ // for editor, switch to bustime since that's the default
+ timeFormat = "BUSTIME";
+ // Apply padding - start 1min early, finish 2min late because these times are generally
+ // rounded down to the minute, so if something ends at "00:10" it might actually end
+ // at 00:10:59 so we should pad to 00:12:00.
+ var start = data.event_start
+ ? new Date(fromTimestamp(data.event_start).getTime() - 60 * 1000)
+ : null;
+ var end = data.event_end
+ ? new Date(fromTimestamp(data.event_end).getTime() + 2 * 60 * 1000)
+ : null;
+ setTimeRange(start, end);
+ // title and description both default to row description
+ document.getElementById("VideoTitle").value = data.video_title
+ ? data.video_title
+ : data.description;
+ document.getElementById("VideoDescription").value = data.video_description
+ ? data.video_description
+ : data.description;
+ // tags default to tags from sheet
+ document.getElementById("VideoTags").value = tags_list_to_string(
+ data.video_tags ? data.video_tags : data.tags
+ );
+
+ // If any edit notes, show them
+ if (data.notes.length > 0) {
+ document.getElementById("EditNotes").value = data.notes;
+ document.getElementById("EditNotesPane").style.display = "block";
+ }
+
+ // Restore advanced options. If any of these are non-default, automatically expand the advanced options pane.
+ setOptions("uploadLocation", data.upload_locations, data.upload_location);
+ document.getElementById("AllowHoles").checked = data.allow_holes;
+ document.getElementById("uploaderWhitelist").value = !!data.uploader_whitelist
+ ? data.uploader_whitelist.join(",")
+ : "";
+ if (
+ (data.upload_locations.length > 0 &&
+ data.upload_location != null &&
+ data.upload_location != data.upload_locations[0]) ||
+ data.allow_holes ||
+ !!data.uploader_whitelist
+ ) {
+ document.getElementById("wubloaderAdvancedInputTable").style.display = "block";
+ }
+
+ loadPlaylist(isEditor, data.video_start, data.video_end, data.video_quality);
+ });
+ } else {
+ if (isEditor) {
+ document.getElementById("SubmitButton").disabled = true;
+ }
+
+ fetch("/thrimshim/defaults")
+ .then(data => data.json())
+ .then(function (data) {
+ if (!data) {
+ alert("Editor results call failed, is thrimshim running?");
+ return;
+ }
+ desertBusStart = new Date(data.bustime_start);
+ document.getElementById("StreamName").value = data.video_channel;
+ if (isEditor) {
+ document.getElementById("VideoTitlePrefix").value = data.title_prefix;
+ document.getElementById("VideoTitle").setAttribute("maxlength", data.title_max_length);
+ setOptions("uploadLocation", data.upload_locations);
+ }
+
+ // Default time format changes depending on mode.
+ // But in both cases the default input value is 10min ago / "",
+ // it's just for editor we convert it before the user sees.
+ if (isEditor) {
+ toggleTimeInput("BUSTIME");
+ }
+
+ loadPlaylist(isEditor);
+ });
+ }
};
// Time-formatting functions
-parseDuration = function(duration) {
- var direction = 1;
- if(duration.startsWith("-")) {
- duration = duration.slice(1);
- direction = -1;
- }
- var parts = duration.split(':');
- return (parseInt(parts[0]) + (parts[1] || "0")/60 + (parts[2] || "0")/3600) * 60 * 60 * direction;
-}
-
-toBustime = function(date) {
- return (date < desertBusStart ? "-":"") + videojs.formatTime(Math.abs((date - desertBusStart)/1000), 600.01).padStart(7, "0:");
+parseDuration = function (duration) {
+ var direction = 1;
+ if (duration.startsWith("-")) {
+ duration = duration.slice(1);
+ direction = -1;
+ }
+ var parts = duration.split(":");
+ return (
+ (parseInt(parts[0]) + (parts[1] || "0") / 60 + (parts[2] || "0") / 3600) * 60 * 60 * direction
+ );
+};
+
+toBustime = function (date) {
+ return (
+ (date < desertBusStart ? "-" : "") +
+ videojs.formatTime(Math.abs((date - desertBusStart) / 1000), 600.01).padStart(7, "0:")
+ );
};
-fromBustime = function(bustime) {
- return new Date(desertBusStart.getTime() + 1000 * parseDuration(bustime));
+fromBustime = function (bustime) {
+ return new Date(desertBusStart.getTime() + 1000 * parseDuration(bustime));
};
-toTimestamp = function(date) {
- return date.toISOString().substring(0, 19);
-}
+toTimestamp = function (date) {
+ return date.toISOString().substring(0, 19);
+};
-fromTimestamp = function(ts) {
- return new Date(ts + "Z");
-}
+fromTimestamp = function (ts) {
+ return new Date(ts + "Z");
+};
-toAgo = function(date) {
- now = new Date()
- return (date < now ? "":"-") + videojs.formatTime(Math.abs((date - now)/1000), 600.01).padStart(7, "0:");
-}
+toAgo = function (date) {
+ now = new Date();
+ return (
+ (date < now ? "" : "-") +
+ videojs.formatTime(Math.abs((date - now) / 1000), 600.01).padStart(7, "0:")
+ );
+};
-fromAgo = function(ago) {
- return new Date(new Date().getTime() - 1000 * parseDuration(ago));
-}
+fromAgo = function (ago) {
+ return new Date(new Date().getTime() - 1000 * parseDuration(ago));
+};
// Set the stream start/end range from a pair of Dates using the current format
// If given null, sets to blank.
-setTimeRange = function(start, end) {
- var toFunc = {
- UTC: toTimestamp,
- BUSTIME: toBustime,
- AGO: toAgo,
- }[timeFormat];
- document.getElementById("StreamStart").value = (start) ? toFunc(start) : "";
- document.getElementById("StreamEnd").value = (end) ? toFunc(end) : "";
-}
+setTimeRange = function (start, end) {
+ var toFunc = {
+ UTC: toTimestamp,
+ BUSTIME: toBustime,
+ AGO: toAgo,
+ }[timeFormat];
+ document.getElementById("StreamStart").value = start ? toFunc(start) : "";
+ document.getElementById("StreamEnd").value = end ? toFunc(end) : "";
+};
// Get the current start/end range as Dates using the current format
// Returns an object containing 'start' and 'end' fields.
// If either is empty / invalid, returns null.
-getTimeRange = function() {
- var fromFunc = {
- UTC: fromTimestamp,
- BUSTIME: fromBustime,
- AGO: fromAgo,
- }[timeFormat];
- var convert = function(value) {
- if (!value) { return null; }
- var date = fromFunc(value);
- return (isNaN(date)) ? null : date;
- };
- return {
- start: convert(document.getElementById("StreamStart").value),
- end: convert(document.getElementById("StreamEnd").value),
- };
-}
-
-getTimeRangeAsTimestamp = function() {
- var range = getTimeRange();
- return {
- // if not null, format as timestamp
- start: range.start && toTimestamp(range.start),
- end: range.end && toTimestamp(range.end),
- };
-}
-
-toggleHiddenPane = function(paneID) {
- var pane = document.getElementById(paneID);
- pane.style.display = (pane.style.display === "none") ? "block":"none";
-}
-
-toggleUltrawide = function() {
- var body = document.getElementsByTagName("Body")[0];
- body.classList.contains("ultrawide") ? body.classList.remove("ultrawide"):body.classList.add("ultrawide");
-}
-
-toggleTimeInput = function(toggleInput) {
- // Get times using current format, then change format, then write them back
- var range = getTimeRange();
- timeFormat = toggleInput;
- setTimeRange(range.start, range.end);
-}
+getTimeRange = function () {
+ var fromFunc = {
+ UTC: fromTimestamp,
+ BUSTIME: fromBustime,
+ AGO: fromAgo,
+ }[timeFormat];
+ var convert = function (value) {
+ if (!value) {
+ return null;
+ }
+ var date = fromFunc(value);
+ return isNaN(date) ? null : date;
+ };
+ return {
+ start: convert(document.getElementById("StreamStart").value),
+ end: convert(document.getElementById("StreamEnd").value),
+ };
+};
+
+getTimeRangeAsTimestamp = function () {
+ var range = getTimeRange();
+ return {
+ // if not null, format as timestamp
+ start: range.start && toTimestamp(range.start),
+ end: range.end && toTimestamp(range.end),
+ };
+};
+
+toggleHiddenPane = function (paneID) {
+ var pane = document.getElementById(paneID);
+ pane.style.display = pane.style.display === "none" ? "block" : "none";
+};
+
+toggleUltrawide = function () {
+ var body = document.getElementsByTagName("Body")[0];
+ body.classList.contains("ultrawide")
+ ? body.classList.remove("ultrawide")
+ : body.classList.add("ultrawide");
+};
+
+toggleTimeInput = function (toggleInput) {
+ // Get times using current format, then change format, then write them back
+ var range = getTimeRange();
+ timeFormat = toggleInput;
+ setTimeRange(range.start, range.end);
+};
// For a given select input element id, add the given list of options.
// If selected is given, it should be the name of an option to select.
// Otherwise the first one is used.
-setOptions = function(element, options, selected) {
- if (!selected && options.length > 0) {
- selected = options[0]
- }
- options.forEach(function(option) {
- document.getElementById(element).innerHTML += '';
- });
-}
-
-buildQuery = function(params) {
- return Object.keys(params).filter(key => params[key] !== null).map(key =>
- encodeURIComponent(key) + '=' + encodeURIComponent(params[key])
- ).join('&');
-}
-
-loadPlaylist = function(isEditor, startTrim, endTrim, defaultQuality) {
- var playlist = "/playlist/" + document.getElementById("StreamName").value + ".m3u8";
-
- var range = getTimeRangeAsTimestamp();
- var queryString = buildQuery(range);
+setOptions = function (element, options, selected) {
+ if (!selected && options.length > 0) {
+ selected = options[0];
+ }
+ options.forEach(function (option) {
+ document.getElementById(element).innerHTML +=
+ '";
+ });
+};
+
+buildQuery = function (params) {
+ return Object.keys(params)
+ .filter(key => params[key] !== null)
+ .map(key => encodeURIComponent(key) + "=" + encodeURIComponent(params[key]))
+ .join("&");
+};
+
+loadPlaylist = function (isEditor, startTrim, endTrim, defaultQuality) {
+ var playlist = "/playlist/" + document.getElementById("StreamName").value + ".m3u8";
+
+ var range = getTimeRangeAsTimestamp();
+ var queryString = buildQuery(range);
// Preserve existing edit times
if (player && player.trimmingControls && player.vhs.playlists.master) {
var discontinuities = mapDiscontinuities();
if (!startTrim) {
- startTrim = getRealTimeForPlayerTime(discontinuities, player.trimmingControls().options.startTrim);
- if (startTrim) {startTrim = startTrim.replace('Z','');}
+ startTrim = getRealTimeForPlayerTime(
+ discontinuities,
+ player.trimmingControls().options.startTrim
+ );
+ if (startTrim) {
+ startTrim = startTrim.replace("Z", "");
+ }
}
if (!endTrim) {
- endTrim = getRealTimeForPlayerTime(discontinuities, player.trimmingControls().options.endTrim);
- if (endTrim) {endTrim = endTrim.replace('Z','');}
+ endTrim = getRealTimeForPlayerTime(
+ discontinuities,
+ player.trimmingControls().options.endTrim
+ );
+ if (endTrim) {
+ endTrim = endTrim.replace("Z", "");
+ }
}
}
- setupPlayer(isEditor, playlist + '?' + queryString, startTrim, endTrim);
-
- //Get quality levels for advanced properties / download
- document.getElementById('qualityLevel').innerHTML = "";
- fetch('/files/' + document.getElementById('StreamName').value).then(data => data.json()).then(function (data) {
- if (!data.length) {
- console.log("Could not retrieve quality levels");
- return;
- }
- var qualityLevels = data.sort().reverse();
- setOptions('qualityLevel', qualityLevels, defaultQuality);
- if (!!defaultQuality && qualityLevels.length > 0 && defaultQuality != qualityLevels[0]) {
- document.getElementById('wubloaderAdvancedInputTable').style.display = "block";
- }
- });
+ setupPlayer(isEditor, playlist + "?" + queryString, startTrim, endTrim);
+
+ //Get quality levels for advanced properties / download
+ document.getElementById("qualityLevel").innerHTML = "";
+ fetch("/files/" + document.getElementById("StreamName").value)
+ .then(data => data.json())
+ .then(function (data) {
+ if (!data.length) {
+ console.log("Could not retrieve quality levels");
+ return;
+ }
+ var qualityLevels = data.sort().reverse();
+ setOptions("qualityLevel", qualityLevels, defaultQuality);
+ if (!!defaultQuality && qualityLevels.length > 0 && defaultQuality != qualityLevels[0]) {
+ document.getElementById("wubloaderAdvancedInputTable").style.display = "block";
+ }
+ });
};
-thrimbletrimmerSubmit = function(state, override_changes=false) {
- document.getElementById('SubmitButton').disabled = true;
- var discontinuities = mapDiscontinuities();
+thrimbletrimmerSubmit = function (state, override_changes = false) {
+ document.getElementById("SubmitButton").disabled = true;
+ var discontinuities = mapDiscontinuities();
- var start = getRealTimeForPlayerTime(discontinuities, player.trimmingControls().options.startTrim);
- if (start) {start = start.replace('Z','');}
+ var start = getRealTimeForPlayerTime(
+ discontinuities,
+ player.trimmingControls().options.startTrim
+ );
+ if (start) {
+ start = start.replace("Z", "");
+ }
var end = getRealTimeForPlayerTime(discontinuities, player.trimmingControls().options.endTrim);
- if (end) {end = end.replace('Z','');}
-
- var wubData = {
- video_start:start,
- video_end:end,
- video_title:document.getElementById("VideoTitle").value,
- video_description:document.getElementById("VideoDescription").value,
- video_tags:tags_string_to_list(document.getElementById("VideoTags").value),
- allow_holes:document.getElementById('AllowHoles').checked,
- upload_location:document.getElementById('uploadLocation').value,
- video_channel:document.getElementById("StreamName").value,
- video_quality:document.getElementById('qualityLevel').options[document.getElementById('qualityLevel').options.selectedIndex].value,
- uploader_whitelist:(document.getElementById('uploaderWhitelist').value ? document.getElementById('uploaderWhitelist').value.split(','):null),
- state:state,
+ if (end) {
+ end = end.replace("Z", "");
+ }
+
+ var wubData = {
+ video_start: start,
+ video_end: end,
+ video_title: document.getElementById("VideoTitle").value,
+ video_description: document.getElementById("VideoDescription").value,
+ video_tags: tags_string_to_list(document.getElementById("VideoTags").value),
+ allow_holes: document.getElementById("AllowHoles").checked,
+ upload_location: document.getElementById("uploadLocation").value,
+ video_channel: document.getElementById("StreamName").value,
+ video_quality:
+ document.getElementById("qualityLevel").options[
+ document.getElementById("qualityLevel").options.selectedIndex
+ ].value,
+ uploader_whitelist: document.getElementById("uploaderWhitelist").value
+ ? document.getElementById("uploaderWhitelist").value.split(",")
+ : null,
+ state: state,
//pass back the sheet columns to check if any have changed
- sheet_name:document.data.sheet_name,
- event_start:document.data.event_start,
- event_end:document.data.event_end,
- category:document.data.category,
- description:document.data.description,
- notes:document.data.notes,
- tags:document.data.tags,
- };
- if (!!user) {
- wubData.token = user.getAuthResponse().id_token
- }
+ sheet_name: document.data.sheet_name,
+ event_start: document.data.event_start,
+ event_end: document.data.event_end,
+ category: document.data.category,
+ description: document.data.description,
+ notes: document.data.notes,
+ tags: document.data.tags,
+ };
+ if (!!user) {
+ wubData.token = user.getAuthResponse().id_token;
+ }
if (override_changes) {
wubData["override_changes"] = true;
}
- console.log(wubData);
- console.log(JSON.stringify(wubData));
+ console.log(wubData);
+ console.log(JSON.stringify(wubData));
- if (!wubData.video_start) {alert("No start time set"); return;}
- if (!wubData.video_end) {alert("No end time set"); return;}
+ if (!wubData.video_start) {
+ alert("No start time set");
+ return;
+ }
+ if (!wubData.video_end) {
+ alert("No end time set");
+ return;
+ }
- //Submit to thrimshim
+ //Submit to thrimshim
var rowId = /id=(.*)(?:&|$)/.exec(document.location.search)[1];
- fetch("/thrimshim/" + rowId, {
- method: 'POST',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(wubData)
- })
- .then(response => response.text().then(text => {
- if (!response.ok) {
- var error = response.statusText + ": " + text;
- if (response.status == 409) {
- dialogue = text + "\nClick Ok to submit anyway; Click Cancel to return to editing"
- if (confirm(dialogue)) {
- thrimbletrimmerSubmit(state, true);
- }
- } else {
- alert(error);
- }
- } else if (state == 'EDITED') {
- alert(`Edit submitted for video from ${start} to ${end}`)
- } else {
- alert("Draft saved");
- }
- document.getElementById('SubmitButton').disabled = false;
- }));
+ fetch("/thrimshim/" + rowId, {
+ method: "POST",
+ headers: {
+ Accept: "application/json",
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(wubData),
+ }).then(response =>
+ response.text().then(text => {
+ if (!response.ok) {
+ var error = response.statusText + ": " + text;
+ if (response.status == 409) {
+ dialogue = text + "\nClick Ok to submit anyway; Click Cancel to return to editing";
+ if (confirm(dialogue)) {
+ thrimbletrimmerSubmit(state, true);
+ }
+ } else {
+ alert(error);
+ }
+ } else if (state == "EDITED") {
+ alert(`Edit submitted for video from ${start} to ${end}`);
+ } else {
+ alert("Draft saved");
+ }
+ document.getElementById("SubmitButton").disabled = false;
+ })
+ );
};
-thrimbletrimmerDownload = function(isEditor) {
- var range = getTimeRangeAsTimestamp();
- if (isEditor) {
- if(player.trimmingControls().options.startTrim >= player.trimmingControls().options.endTrim) {
- alert("End Time must be greater than Start Time");
- return;
- }
- var discontinuities = mapDiscontinuities();
- range.start = getRealTimeForPlayerTime(discontinuities, player.trimmingControls().options.startTrim);
- range.end = getRealTimeForPlayerTime(discontinuities, player.trimmingControls().options.endTrim);
- }
-
- var targetURL = "/cut/" + document.getElementById("StreamName").value +
- "/"+document.getElementById('qualityLevel').options[document.getElementById('qualityLevel').options.selectedIndex].value+".ts" +
- "?" + buildQuery({
- start: range.start,
- end: range.end,
+thrimbletrimmerDownload = function (isEditor) {
+ var range = getTimeRangeAsTimestamp();
+ if (isEditor) {
+ if (player.trimmingControls().options.startTrim >= player.trimmingControls().options.endTrim) {
+ alert("End Time must be greater than Start Time");
+ return;
+ }
+ var discontinuities = mapDiscontinuities();
+ range.start = getRealTimeForPlayerTime(
+ discontinuities,
+ player.trimmingControls().options.startTrim
+ );
+ range.end = getRealTimeForPlayerTime(
+ discontinuities,
+ player.trimmingControls().options.endTrim
+ );
+ }
+
+ var targetURL =
+ "/cut/" +
+ document.getElementById("StreamName").value +
+ "/" +
+ document.getElementById("qualityLevel").options[
+ document.getElementById("qualityLevel").options.selectedIndex
+ ].value +
+ ".ts" +
+ "?" +
+ buildQuery({
+ start: range.start,
+ end: range.end,
// In non-editor, always use rough cut. They don't have the edit controls to do
// fine time selection anyway.
- type: (isEditor) ? (
- document.getElementById('DownloadType').options[document.getElementById('DownloadType').options.selectedIndex].value
- ) : "rough",
- // Always allow holes in non-editor, accidentially including holes isn't important
- allow_holes: (isEditor) ? String(document.getElementById('AllowHoles').checked) : "true",
- });
- console.log(targetURL);
- document.getElementById('DownloadLink').href = targetURL;
- document.getElementById('DownloadLink').style.display = "";
+ type: isEditor
+ ? document.getElementById("DownloadType").options[
+ document.getElementById("DownloadType").options.selectedIndex
+ ].value
+ : "rough",
+ // Always allow holes in non-editor, accidentially including holes isn't important
+ allow_holes: isEditor ? String(document.getElementById("AllowHoles").checked) : "true",
+ });
+ console.log(targetURL);
+ document.getElementById("DownloadLink").href = targetURL;
+ document.getElementById("DownloadLink").style.display = "";
};
-thrimbletrimmerManualLink = function() {
- document.getElementById("ManualButton").disabled = true;
- var rowId = /id=(.*)(?:&|$)/.exec(document.location.search)[1];
- var upload_location = (document.getElementById("ManualYoutube").checked) ? "youtube-manual" : "manual";
- var body = {link: document.getElementById("ManualLink").value, upload_location: upload_location};
- if (!!user) {
- body.token = user.getAuthResponse().id_token;
- }
- fetch("/thrimshim/manual-link/"+rowId, {
- method: 'POST',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(body)
- })
- .then(response => response.text().then(text => {
- if (!response.ok) {
- var error = response.statusText + ": " + text;
- console.log(error);
- alert(error);
- document.getElementById("ManualButton").disabled = false;
- } else {
- alert("Manual link set to " + body.link);
- setTimeout(() => { window.location.href = '/thrimbletrimmer/dashboard.html'; }, 500);
- }
- }));
+thrimbletrimmerManualLink = function () {
+ document.getElementById("ManualButton").disabled = true;
+ var rowId = /id=(.*)(?:&|$)/.exec(document.location.search)[1];
+ var upload_location = document.getElementById("ManualYoutube").checked
+ ? "youtube-manual"
+ : "manual";
+ var body = {
+ link: document.getElementById("ManualLink").value,
+ upload_location: upload_location,
+ };
+ if (!!user) {
+ body.token = user.getAuthResponse().id_token;
+ }
+ fetch("/thrimshim/manual-link/" + rowId, {
+ method: "POST",
+ headers: {
+ Accept: "application/json",
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(body),
+ }).then(response =>
+ response.text().then(text => {
+ if (!response.ok) {
+ var error = response.statusText + ": " + text;
+ console.log(error);
+ alert(error);
+ document.getElementById("ManualButton").disabled = false;
+ } else {
+ alert("Manual link set to " + body.link);
+ setTimeout(() => {
+ window.location.href = "/thrimbletrimmer/dashboard.html";
+ }, 500);
+ }
+ })
+ );
};
-thrimbletrimmerResetLink = function(force) {
- var rowId = /id=(.*)(?:&|$)/.exec(document.location.search)[1];
- if(force && !confirm(
- 'Are you sure you want to reset this event? ' +
- 'This will set the row back to UNEDITED and forget about any video that already may exist. ' +
- 'It is intended as a last-ditch command to clear a malfunctioning cutter, ' +
- 'or if a video needs to be re-edited and replaced. ' +
- 'IT IS YOUR RESPONSIBILITY TO DEAL WITH ANY VIDEO THAT MAY HAVE ALREADY BEEN UPLOADED. '
- )) {
- return;
- }
- document.getElementById("ResetButton").disabled = true;
- document.getElementById("CancelButton").disabled = true;
- var body = {}
- if (!!user) {
- body.token = user.getAuthResponse().id_token;
- }
- fetch("/thrimshim/reset/"+rowId + "?force=" + force, {
- method: 'POST',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(body)
- })
- .then(response => response.text().then(text => {
- if (!response.ok) {
- var error = response.statusText + ": " + text;
- console.log(error);
- alert(error);
- document.getElementById("ResetButton").disabled = false;
- document.getElementById("CancelButton").disabled = true;
- } else {
- alert("Row has been " + ((force) ? "reset" : "cancelled") +". Reloading...");
- setTimeout(() => { window.location.reload(); }, 500);
- }
- }));
+thrimbletrimmerResetLink = function (force) {
+ var rowId = /id=(.*)(?:&|$)/.exec(document.location.search)[1];
+ if (
+ force &&
+ !confirm(
+ "Are you sure you want to reset this event? " +
+ "This will set the row back to UNEDITED and forget about any video that already may exist. " +
+ "It is intended as a last-ditch command to clear a malfunctioning cutter, " +
+ "or if a video needs to be re-edited and replaced. " +
+ "IT IS YOUR RESPONSIBILITY TO DEAL WITH ANY VIDEO THAT MAY HAVE ALREADY BEEN UPLOADED. "
+ )
+ ) {
+ return;
+ }
+ document.getElementById("ResetButton").disabled = true;
+ document.getElementById("CancelButton").disabled = true;
+ var body = {};
+ if (!!user) {
+ body.token = user.getAuthResponse().id_token;
+ }
+ fetch("/thrimshim/reset/" + rowId + "?force=" + force, {
+ method: "POST",
+ headers: {
+ Accept: "application/json",
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(body),
+ }).then(response =>
+ response.text().then(text => {
+ if (!response.ok) {
+ var error = response.statusText + ": " + text;
+ console.log(error);
+ alert(error);
+ document.getElementById("ResetButton").disabled = false;
+ document.getElementById("CancelButton").disabled = true;
+ } else {
+ alert("Row has been " + (force ? "reset" : "cancelled") + ". Reloading...");
+ setTimeout(() => {
+ window.location.reload();
+ }, 500);
+ }
+ })
+ );
};
-tags_list_to_string = function(tag_list) {
+tags_list_to_string = function (tag_list) {
return tag_list.join(", ");
-}
+};
-tags_string_to_list = function(tag_string) {
- return tag_string.split(",").map(tag => tag.trim()).filter(tag => tag.length > 0);
-}
+tags_string_to_list = function (tag_string) {
+ return tag_string
+ .split(",")
+ .map(tag => tag.trim())
+ .filter(tag => tag.length > 0);
+};
-round_trip_tag_string = function() {
+round_trip_tag_string = function () {
var element = document.getElementById("VideoTags");
- element.value = tags_list_to_string(
- tags_string_to_list(
- element.value
- )
- );
-}
+ element.value = tags_list_to_string(tags_string_to_list(element.value));
+};
diff --git a/thrimbletrimmer/scripts/keyboardShortcuts.js b/thrimbletrimmer/scripts/keyboardShortcuts.js
index 71191e8..c5fec9a 100644
--- a/thrimbletrimmer/scripts/keyboardShortcuts.js
+++ b/thrimbletrimmer/scripts/keyboardShortcuts.js
@@ -1,99 +1,102 @@
-function changeSpeed(direction) {
- var speeds = [0.5, 1, 1.25, 1.5, 2];
- var currentIndex = speeds.indexOf(player.playbackRate());
- if (currentIndex < 0) {
- // not present
- return;
- }
- var newIndex = currentIndex + direction;
- if (newIndex < 0 || newIndex >= speeds.length) {
- // out of range
- return
- }
- player.playbackRate(speeds[newIndex]);
-}
-
-document.addEventListener('keypress', (event) => {
- //if(event.target.nodeName == "BODY") {
- if(event.target.nodeName !== "INPUT" && event.target.nodeName !== "TEXTAREA") {
- switch(event.key) {
- case "j":
- player.currentTime(player.currentTime()-10);
- break;
- case "k":
- case " ": // also pause on space
- player.paused() ? player.play():player.pause();
- break;
- case "l":
- player.currentTime(player.currentTime()+10);
- break;
- case ",":
- player.currentTime(player.currentTime()-0.1);
- break;
- case ".":
- player.currentTime(player.currentTime()+0.1);
- break;
- case "i":
- player.trimmingControls().updateTrimTimes(player.currentTime(), player.trimmingControls().options.endTrim);
- break;
- case "o":
- player.trimmingControls().updateTrimTimes(player.trimmingControls().options.startTrim, player.currentTime());
- break;
- case "=":
- changeSpeed(1);
- break
- case "-":
- changeSpeed(-1);
- break
- case "0":
- player.currentTime(0);
- break;
- case "1":
- player.currentTime(player.duration()*0.1);
- break;
- case "2":
- player.currentTime(player.duration()*0.2);
- break;
- case "3":
- player.currentTime(player.duration()*0.3);
- break;
- case "4":
- player.currentTime(player.duration()*0.4);
- break;
- case "5":
- player.currentTime(player.duration()*0.5);
- break;
- case "6":
- player.currentTime(player.duration()*0.6);
- break;
- case "7":
- player.currentTime(player.duration()*0.7);
- break;
- case "8":
- player.currentTime(player.duration()*0.8);
- break;
- case "9":
- player.currentTime(player.duration()*0.9);
- break;
- }
- }
-
- // const keyName = event.key;
- // console.log('keypress event\n\n' + 'key: ' + keyName);
- // console.log(event.target.nodeName);
-});
-
-//Arrow keys only detected on keydown, keypress only works in "some" browsers
-document.addEventListener('keydown', (event) => {
- if(event.target.nodeName !== "INPUT" && event.target.nodeName !== "TEXTAREA") {
- switch(event.keyCode) {
- case 37:
- player.currentTime(player.currentTime()-5);
- break;
- case 39:
- player.currentTime(player.currentTime()+5);
- break;
-
- }
- }
-});
+function changeSpeed(direction) {
+ var speeds = [0.5, 1, 1.25, 1.5, 2];
+ var currentIndex = speeds.indexOf(player.playbackRate());
+ if (currentIndex < 0) {
+ // not present
+ return;
+ }
+ var newIndex = currentIndex + direction;
+ if (newIndex < 0 || newIndex >= speeds.length) {
+ // out of range
+ return;
+ }
+ player.playbackRate(speeds[newIndex]);
+}
+
+document.addEventListener("keypress", event => {
+ //if(event.target.nodeName == "BODY") {
+ if (event.target.nodeName !== "INPUT" && event.target.nodeName !== "TEXTAREA") {
+ switch (event.key) {
+ case "j":
+ player.currentTime(player.currentTime() - 10);
+ break;
+ case "k":
+ case " ": // also pause on space
+ player.paused() ? player.play() : player.pause();
+ break;
+ case "l":
+ player.currentTime(player.currentTime() + 10);
+ break;
+ case ",":
+ player.currentTime(player.currentTime() - 0.1);
+ break;
+ case ".":
+ player.currentTime(player.currentTime() + 0.1);
+ break;
+ case "i":
+ player
+ .trimmingControls()
+ .updateTrimTimes(player.currentTime(), player.trimmingControls().options.endTrim);
+ break;
+ case "o":
+ player
+ .trimmingControls()
+ .updateTrimTimes(player.trimmingControls().options.startTrim, player.currentTime());
+ break;
+ case "=":
+ changeSpeed(1);
+ break;
+ case "-":
+ changeSpeed(-1);
+ break;
+ case "0":
+ player.currentTime(0);
+ break;
+ case "1":
+ player.currentTime(player.duration() * 0.1);
+ break;
+ case "2":
+ player.currentTime(player.duration() * 0.2);
+ break;
+ case "3":
+ player.currentTime(player.duration() * 0.3);
+ break;
+ case "4":
+ player.currentTime(player.duration() * 0.4);
+ break;
+ case "5":
+ player.currentTime(player.duration() * 0.5);
+ break;
+ case "6":
+ player.currentTime(player.duration() * 0.6);
+ break;
+ case "7":
+ player.currentTime(player.duration() * 0.7);
+ break;
+ case "8":
+ player.currentTime(player.duration() * 0.8);
+ break;
+ case "9":
+ player.currentTime(player.duration() * 0.9);
+ break;
+ }
+ }
+
+ // const keyName = event.key;
+ // console.log('keypress event\n\n' + 'key: ' + keyName);
+ // console.log(event.target.nodeName);
+});
+
+//Arrow keys only detected on keydown, keypress only works in "some" browsers
+document.addEventListener("keydown", event => {
+ if (event.target.nodeName !== "INPUT" && event.target.nodeName !== "TEXTAREA") {
+ switch (event.keyCode) {
+ case 37:
+ player.currentTime(player.currentTime() - 5);
+ break;
+ case 39:
+ player.currentTime(player.currentTime() + 5);
+ break;
+ }
+ }
+});
diff --git a/thrimbletrimmer/scripts/playerSetup.js b/thrimbletrimmer/scripts/playerSetup.js
index 71b1f3c..77a1342 100644
--- a/thrimbletrimmer/scripts/playerSetup.js
+++ b/thrimbletrimmer/scripts/playerSetup.js
@@ -1,98 +1,115 @@
-var player = null;
-
-function setupPlayer(isEditor, source, startTrim, endTrim) {
- document.getElementById("my-player").style.display = "";
- //Make poster of DB logo in correct aspect ratio, to control initial size of fluid container.
- var options = {
- sources: [{ src: source }],
- liveui: true,
- //fluid:true,
- controls:true,
- autoplay:false,
- width:1280,
- height:420,
- playbackRates: [0.5, 1, 1.25, 1.5, 2],
- inactivityTimeout: 0,
- controlBar: {
- fullscreenToggle: true,
- volumePanel: {
- inline: false
- }
- }
- };
- if(player) { //Destroy and recreate the player if it already exists.
- player.dispose();
- document.getElementById("EditorContainer").innerHTML = `
-
- `;
- }
- player = videojs('my-player', options, function onPlayerReady() {
- videojs.log('Your player is ready!');
-
- // Set player volume to 50% by default
- var defaultVolume = 0.5;
- this.volume(defaultVolume);
-
- // In this context, `this` is the player that was created by Video.js.
- this.on('ready', function() {
- //this.play();
- });
-
- this.vhs.playlists.on('loadedmetadata', function() {
- // setTimeout(function() { player.play(); }, 1000);
- player.hasStarted(true); //So it displays all the controls.
- if (isEditor) {
- var stream_start = player.vhs.playlists.master.playlists.filter(playlist => typeof playlist.discontinuityStarts !== "undefined")[0].dateTimeObject;
- startTrim = startTrim ? (new Date(startTrim+"Z")-stream_start)/1000:0;
- endTrim = endTrim ? (new Date(endTrim+"Z")-stream_start)/1000:player.duration();
- var trimmingControls = player.trimmingControls({ startTrim:startTrim, endTrim:endTrim });
- }
- });
-
- // How about an event listener?
- this.on('ended', function() {
- videojs.log('Awww...over so soon?!');
- });
-
- this.on('error', function() {
- videojs.log("Could not load video stream");
- alert("No video available for stream.");
- })
- });
- var hlsQS = player.hlsQualitySelector();
-}
-
-mapDiscontinuities = function() {
- var playlist = player.vhs.playlists.master.playlists.filter(playlist => typeof playlist.discontinuityStarts !== "undefined")[0]; //Only one of the playlists will have the discontinuity or stream start objects, and it's not necessarily the first one or the source one.
- var discontinuities = playlist.discontinuityStarts.map(segmentIndex => { return {segmentIndex:segmentIndex, segmentTimestamp:playlist.segments[segmentIndex].dateTimeObject, playbackIndex:null}; });
- //var lastDiscontinuity = Math.max(...playlist.discontinuityStarts);
- var lastDiscontinuity = playlist.discontinuityStarts.slice(-1).pop(); //Assumes discontinuities are sorted in ascending order.
-
- var durationMarker = 0;
- for (var index = 0; index <= lastDiscontinuity; index++) {
- let segment = playlist.segments[index];
- if(segment.discontinuity) {
- discontinuities.find(discontinuity => discontinuity.segmentIndex == index).playbackIndex = durationMarker;
- }
- durationMarker += segment.duration;
- }
-
- return discontinuities;
-};
-
-getRealTimeForPlayerTime = function(discontinuities, playbackIndex) {
- var streamStart = player.vhs.playlists.master.playlists.filter(playlist => typeof playlist.dateTimeObject !== "undefined")[0].dateTimeObject; //Only one of the playlists will have the discontinuity or stream start objects, and it's not necessarily the first one or the source one.
-
- //Find last discontinuity before playbackIndex
- var lastDiscontinuity = discontinuities.filter(discontinuity => discontinuity.playbackIndex < playbackIndex).slice(-1).pop();
- if(lastDiscontinuity) {
- streamStart = lastDiscontinuity.segmentTimestamp;
- playbackIndex -= lastDiscontinuity.playbackIndex;
- }
-
- var realTime = streamStart.getTime()+playbackIndex*1000;
-
- return (isFinite(realTime)) ? new Date(realTime).toISOString() : null;
-};
+var player = null;
+
+function setupPlayer(isEditor, source, startTrim, endTrim) {
+ document.getElementById("my-player").style.display = "";
+ //Make poster of DB logo in correct aspect ratio, to control initial size of fluid container.
+ var options = {
+ sources: [{src: source}],
+ liveui: true,
+ //fluid:true,
+ controls: true,
+ autoplay: false,
+ width: 1280,
+ height: 420,
+ playbackRates: [0.5, 1, 1.25, 1.5, 2],
+ inactivityTimeout: 0,
+ controlBar: {
+ fullscreenToggle: true,
+ volumePanel: {
+ inline: false,
+ },
+ },
+ };
+ if (player) {
+ //Destroy and recreate the player if it already exists.
+ player.dispose();
+ document.getElementById("EditorContainer").innerHTML = `
+
+ `;
+ }
+ player = videojs("my-player", options, function onPlayerReady() {
+ videojs.log("Your player is ready!");
+
+ // Set player volume to 50% by default
+ var defaultVolume = 0.5;
+ this.volume(defaultVolume);
+
+ // In this context, `this` is the player that was created by Video.js.
+ this.on("ready", function () {
+ //this.play();
+ });
+
+ this.vhs.playlists.on("loadedmetadata", function () {
+ // setTimeout(function() { player.play(); }, 1000);
+ player.hasStarted(true); //So it displays all the controls.
+ if (isEditor) {
+ var stream_start = player.vhs.playlists.master.playlists.filter(
+ playlist => typeof playlist.discontinuityStarts !== "undefined"
+ )[0].dateTimeObject;
+ startTrim = startTrim ? (new Date(startTrim + "Z") - stream_start) / 1000 : 0;
+ endTrim = endTrim ? (new Date(endTrim + "Z") - stream_start) / 1000 : player.duration();
+ var trimmingControls = player.trimmingControls({startTrim: startTrim, endTrim: endTrim});
+ }
+ });
+
+ // How about an event listener?
+ this.on("ended", function () {
+ videojs.log("Awww...over so soon?!");
+ });
+
+ this.on("error", function () {
+ videojs.log("Could not load video stream");
+ alert("No video available for stream.");
+ });
+ });
+ var hlsQS = player.hlsQualitySelector();
+}
+
+mapDiscontinuities = function () {
+ var playlist = player.vhs.playlists.master.playlists.filter(
+ playlist => typeof playlist.discontinuityStarts !== "undefined"
+ )[0]; //Only one of the playlists will have the discontinuity or stream start objects, and it's not necessarily the first one or the source one.
+ var discontinuities = playlist.discontinuityStarts.map(segmentIndex => {
+ return {
+ segmentIndex: segmentIndex,
+ segmentTimestamp: playlist.segments[segmentIndex].dateTimeObject,
+ playbackIndex: null,
+ };
+ });
+ //var lastDiscontinuity = Math.max(...playlist.discontinuityStarts);
+ var lastDiscontinuity = playlist.discontinuityStarts.slice(-1).pop(); //Assumes discontinuities are sorted in ascending order.
+
+ var durationMarker = 0;
+ for (var index = 0; index <= lastDiscontinuity; index++) {
+ let segment = playlist.segments[index];
+ if (segment.discontinuity) {
+ discontinuities.find(discontinuity => discontinuity.segmentIndex == index).playbackIndex =
+ durationMarker;
+ }
+ durationMarker += segment.duration;
+ }
+
+ return discontinuities;
+};
+
+getRealTimeForPlayerTime = function (discontinuities, playbackIndex) {
+ var streamStart = player.vhs.playlists.master.playlists.filter(
+ playlist => typeof playlist.dateTimeObject !== "undefined"
+ )[0].dateTimeObject; //Only one of the playlists will have the discontinuity or stream start objects, and it's not necessarily the first one or the source one.
+
+ //Find last discontinuity before playbackIndex
+ var lastDiscontinuity = discontinuities
+ .filter(discontinuity => discontinuity.playbackIndex < playbackIndex)
+ .slice(-1)
+ .pop();
+ if (lastDiscontinuity) {
+ streamStart = lastDiscontinuity.segmentTimestamp;
+ playbackIndex -= lastDiscontinuity.playbackIndex;
+ }
+
+ var realTime = streamStart.getTime() + playbackIndex * 1000;
+
+ return isFinite(realTime) ? new Date(realTime).toISOString() : null;
+};