diff --git a/thrimbletrimmer/edit.html b/thrimbletrimmer/edit.html
index 4166b18..1245d6b 100644
--- a/thrimbletrimmer/edit.html
+++ b/thrimbletrimmer/edit.html
@@ -18,6 +18,8 @@
content="345276493482-r84m2giavk10glnmqna0lbq8e1hdaus0.apps.googleusercontent.com"
/>
+
+
@@ -261,21 +263,46 @@
Regions are given as pixel coordinates of the top-left and bottom-right corners.
Note that if the regions are different sizes, the image will be stretched.
- Crop:
-
-
- to
-
-
-
-
- Location:
-
-
- to
-
-
+
+ Update Source Images
+
+
+
{
commonPageSetup();
globalLoadChatWorker.onmessage = (event) => {
@@ -259,6 +263,42 @@ window.addEventListener("DOMContentLoaded", async (event) => {
});
}
+ document
+ .getElementById("video-info-thumbnail-template-source-image-update")
+ .addEventListener("click", async (_event) => {
+ const videoFrameImageElement = document.getElementById("video-info-thumbnail-template-video-source-image");
+
+ const timeEntryElement = document.getElementById("video-info-thumbnail-time");
+ const imageTime = wubloaderTimeFromVideoHumanTime(timeEntryElement.value);
+ if (imageTime === null) {
+ videoFrameImageElement.classList.add("hidden");
+ addError("Couldn't preview thumbnail; couldn't parse thumbnail frame timestamp");
+ return;
+ }
+ const imageTemplate = document.getElementById("video-info-thumbnail-template").value;
+ const videoFrameQuery = new URLSearchParams({
+ timestamp: imageTime,
+ });
+ videoFrameImageElement.src = `/frame/${globalStreamName}/source.png?${videoFrameQuery}`;
+ videoFrameImageElement.classList.remove("hidden");
+
+ const templateImageElement = document.getElementById("video-info-thumbnail-template-overlay-image");
+
+ templateImageElement.src = `/thrimshim/template/${imageTemplate}.png`;
+ templateImageElement.classList.remove("hidden");
+
+ createTemplateCropWidgets();
+ });
+
+ document.getElementById("video-info-thumbnail-crop-0").addEventListener("input", updateTemplateCropWidgets);
+ document.getElementById("video-info-thumbnail-crop-1").addEventListener("input", updateTemplateCropWidgets);
+ document.getElementById("video-info-thumbnail-crop-2").addEventListener("input", updateTemplateCropWidgets);
+ document.getElementById("video-info-thumbnail-crop-3").addEventListener("input", updateTemplateCropWidgets);
+ document.getElementById("video-info-thumbnail-location-0").addEventListener("input", updateTemplateCropWidgets);
+ document.getElementById("video-info-thumbnail-location-1").addEventListener("input", updateTemplateCropWidgets);
+ document.getElementById("video-info-thumbnail-location-2").addEventListener("input", updateTemplateCropWidgets);
+ document.getElementById("video-info-thumbnail-location-3").addEventListener("input", updateTemplateCropWidgets);
+
document
.getElementById("video-info-thumbnail-template-preview-generate")
.addEventListener("click", async (_event) => {
@@ -2156,3 +2196,70 @@ function canEditMetadata() {
function isNonVideoInput(element) {
return element.id.startsWith("data-correction-force-reset");
}
+
+/**
+ * Helper function to create the Jcrop widgets the first time the user chooses
+ * to load the advanced template cropping tool images in a given session.
+ */
+function createTemplateCropWidgets() {
+ if (videoFrameStage == null) {
+ videoFrameStage = Jcrop.attach('video-info-thumbnail-template-video-source-image');
+ videoFrameStage.listen('crop.update',function(widget,e){
+ const pos = widget.pos;
+ const fieldX1 = document.getElementById("video-info-thumbnail-crop-0");
+ const fieldY1 = document.getElementById("video-info-thumbnail-crop-1");
+ const fieldX2 = document.getElementById("video-info-thumbnail-crop-2");
+ const fieldY2 = document.getElementById("video-info-thumbnail-crop-3");
+ fieldX1.value = pos.x*2;
+ fieldY1.value = pos.y*2;
+ fieldX2.value = (pos.x+pos.w)*2;
+ fieldY2.value = (pos.y+pos.h)*2;
+ });
+ }
+ if (templateStage == null) {
+ templateStage = Jcrop.attach('video-info-thumbnail-template-overlay-image');
+ templateStage.listen('crop.change',function(widget,e){
+ const pos = widget.pos;
+ const fieldX1 = document.getElementById("video-info-thumbnail-location-0");
+ const fieldY1 = document.getElementById("video-info-thumbnail-location-1");
+ const fieldX2 = document.getElementById("video-info-thumbnail-location-2");
+ const fieldY2 = document.getElementById("video-info-thumbnail-location-3");
+ fieldX1.value = pos.x*2;
+ fieldY1.value = pos.y*2;
+ fieldX2.value = (pos.x+pos.w)*2;
+ fieldY2.value = (pos.y+pos.h)*2;
+ });
+ }
+
+ updateTemplateCropWidgets();
+}
+
+/**
+ * Helper function to update the Jcrop widgets (creating them if needed) based
+ * on the current values in the advanced template cropping text input fields.
+ */
+function updateTemplateCropWidgets() {
+ const videoFieldX1 = document.getElementById("video-info-thumbnail-crop-0");
+ const videoFieldY1 = document.getElementById("video-info-thumbnail-crop-1");
+ const videoFieldX2 = document.getElementById("video-info-thumbnail-crop-2");
+ const videoFieldY2 = document.getElementById("video-info-thumbnail-crop-3");
+ const videoFrameRect = Jcrop.Rect.create(videoFieldX1.value/2, videoFieldY1.value/2, (videoFieldX2.value-videoFieldX1.value)/2, (videoFieldY2.value-videoFieldY1.value)/2);
+ if (videoFrameStage.active == null) {
+ videoFrameStage.newWidget(videoFrameRect);
+ } else {
+ videoFrameStage.active.pos = videoFrameRect;
+ videoFrameStage.active.render();
+ }
+
+ const templateFieldX1 = document.getElementById("video-info-thumbnail-location-0");
+ const templateFieldY1 = document.getElementById("video-info-thumbnail-location-1");
+ const templateFieldX2 = document.getElementById("video-info-thumbnail-location-2");
+ const templateFieldY2 = document.getElementById("video-info-thumbnail-location-3");
+ const templateRect = Jcrop.Rect.create(templateFieldX1.value/2, templateFieldY1.value/2, (templateFieldX2.value-templateFieldX1.value)/2, (templateFieldY2.value-templateFieldY1.value)/2);
+ if (templateStage.active == null) {
+ templateStage.newWidget(templateRect);
+ } else {
+ templateStage.active.pos = templateRect;
+ templateStage.active.render();
+ }
+}
\ No newline at end of file
diff --git a/thrimbletrimmer/styles/thrimbletrimmer.css b/thrimbletrimmer/styles/thrimbletrimmer.css
index 4452e50..e170b97 100644
--- a/thrimbletrimmer/styles/thrimbletrimmer.css
+++ b/thrimbletrimmer/styles/thrimbletrimmer.css
@@ -371,6 +371,18 @@ input.range-definition-chapter-marker-description {
max-width: 320px;
}
+#video-info-thumbnail-template-video-source-image {
+ max-width: 640px;
+}
+
+#video-info-thumbnail-template-overlay-image {
+ max-width: 640px;
+}
+
+.video-info-thumbnail-advanced-crop-flex-wrapper {
+ display: flex;
+}
+
.submission-response-error {
white-space: pre-wrap;
}