Files
Owlbot/owlbot/builtin_modules/clips/static/editor.js
T
LogalDeveloper 84b057a400
Dependency Audit / Dependency Audit (push) Successful in 18s
CI / Formatting (push) Successful in 14s
CI / Linting (push) Successful in 15s
CI / Tests (Python 3.12) (push) Successful in 29s
CI / Tests (Python 3.13) (push) Successful in 28s
CI / Tests (Python 3.14) (push) Successful in 27s
CI / Type Checking (push) Successful in 25s
CI / Spelling (push) Successful in 15s
Added clips module.
2026-03-03 19:29:23 -05:00

218 lines
7.5 KiB
JavaScript

(function() {
// Configuration
const cfg = window.EDITOR_CONFIG;
const DURATION = cfg.duration;
const MIN_LENGTH = cfg.minLength;
const MAX_LENGTH = cfg.maxLength;
// DOM References
const video = document.getElementById("video");
const slider = document.getElementById("slider");
const track = document.getElementById("track");
const handleStart = document.getElementById("handle-start");
const handleEnd = document.getElementById("handle-end");
const startTimeEl = document.getElementById("start-time");
const endTimeEl = document.getElementById("end-time");
const clipLengthEl = document.getElementById("clip-length");
const playhead = document.getElementById("playhead");
const btnPreview = document.getElementById("btn-preview");
const pctEl = document.getElementById("loading-pct");
const loadingEl = document.getElementById("loading");
// Utilities
function formatTime(s) {
s = Math.round(s);
const m = Math.floor(s / 60);
const sec = s % 60;
return m + ":" + (sec < 10 ? "0" : "") + sec;
}
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
function pctToTime(pct) {
return Math.round(clamp(pct, 0, 1) * DURATION);
}
function getSliderPct(e) {
const rect = slider.getBoundingClientRect();
return (e.clientX - rect.left) / rect.width;
}
// Video Loading
function showLoadError(message) {
const alert = document.createElement("div");
alert.className = "alert alert-danger";
alert.setAttribute("role", "alert");
alert.textContent = message;
const link = document.createElement("a");
link.href = cfg.listUrl;
link.textContent = "Return to clips list";
loadingEl.textContent = "";
loadingEl.appendChild(alert);
loadingEl.appendChild(link);
}
const xhr = new XMLHttpRequest();
xhr.open("GET", cfg.previewUrl);
xhr.responseType = "blob";
xhr.onprogress = function(e) {
if (e.lengthComputable) {
pctEl.textContent = Math.round((e.loaded / e.total) * 100) + "%";
}
};
xhr.onerror = function() {
showLoadError("Failed to load the video preview. Please check your connection and try again.");
};
xhr.onload = function() {
if (xhr.status !== 200) {
showLoadError("This editor session has expired or is no longer available.");
return;
}
video.src = URL.createObjectURL(xhr.response);
video.addEventListener("loadedmetadata", function() {
video.currentTime = startTime;
}, { once: true });
loadingEl.classList.add("d-none");
document.getElementById("editor-controls").classList.remove("d-none");
};
xhr.send();
// Slider State & UI
const clipLength = Math.min(MAX_LENGTH, DURATION);
let startTime = DURATION - clipLength;
let endTime = DURATION;
function updateUI() {
const startPct = (startTime / DURATION) * 100;
const endPct = (endTime / DURATION) * 100;
handleStart.style.left = startPct + "%";
handleEnd.style.left = endPct + "%";
track.style.left = startPct + "%";
track.style.width = (endPct - startPct) + "%";
startTimeEl.textContent = formatTime(startTime);
endTimeEl.textContent = formatTime(endTime);
clipLengthEl.textContent = formatTime(endTime - startTime);
}
function updatePlayhead() {
const pct = (video.currentTime / DURATION) * 100;
playhead.style.left = pct + "%";
}
// Slider Interaction
let dragging = null;
let dragOffset = 0;
function onPointerDown(e) {
const target = e.target;
if (target === handleStart) {
dragging = "start";
} else if (target === handleEnd) {
dragging = "end";
} else if (target === track) {
dragging = "track";
dragOffset = pctToTime(getSliderPct(e)) - startTime;
}
if (dragging) {
e.preventDefault();
if (previewActive) {
video.pause();
setPreviewActive(false);
}
document.addEventListener("pointermove", onPointerMove);
document.addEventListener("pointerup", onPointerUp);
}
}
function onPointerMove(e) {
const t = pctToTime(getSliderPct(e));
const length = endTime - startTime;
if (dragging === "start") {
startTime = clamp(t, 0, DURATION - MIN_LENGTH);
if (endTime - startTime < MIN_LENGTH) endTime = startTime + MIN_LENGTH;
if (endTime - startTime > MAX_LENGTH) endTime = startTime + MAX_LENGTH;
endTime = Math.min(endTime, DURATION);
} else if (dragging === "end") {
endTime = clamp(t, MIN_LENGTH, DURATION);
if (endTime - startTime < MIN_LENGTH) startTime = endTime - MIN_LENGTH;
if (endTime - startTime > MAX_LENGTH) startTime = endTime - MAX_LENGTH;
startTime = Math.max(startTime, 0);
} else if (dragging === "track") {
let newStart = t - dragOffset;
newStart = clamp(newStart, 0, DURATION - length);
startTime = Math.round(newStart);
endTime = startTime + length;
}
video.currentTime = dragging === "end" ? endTime : startTime;
updateUI();
}
function onPointerUp() {
dragging = null;
document.removeEventListener("pointermove", onPointerMove);
document.removeEventListener("pointerup", onPointerUp);
}
// Preview Playback
let previewActive = false;
function setPreviewActive(active) {
previewActive = active;
if (active) {
btnPreview.textContent = "Stop Preview";
btnPreview.classList.remove("btn-primary");
btnPreview.classList.add("btn-danger");
} else {
btnPreview.textContent = "Preview Clip";
btnPreview.classList.remove("btn-danger");
btnPreview.classList.add("btn-primary");
}
}
// Form Submission
function onCreateClick() {
document.getElementById("editor-controls").classList.add("d-none");
document.getElementById("processing").classList.remove("d-none");
document.title = "Creating Clip...";
document.querySelector(".breadcrumb-item.active").textContent = "Creating Clip";
document.querySelector("h1").textContent = "Creating Clip";
document.getElementById("form-start").value = startTime;
document.getElementById("form-end").value = endTime;
document.getElementById("form-title").value = document.getElementById("title").value;
document.getElementById("submit-form").submit();
}
// Event Bindings
slider.addEventListener("pointerdown", onPointerDown);
btnPreview.addEventListener("click", function() {
if (previewActive) {
video.pause();
setPreviewActive(false);
} else {
video.currentTime = startTime;
video.play();
setPreviewActive(true);
}
});
video.addEventListener("timeupdate", function() {
updatePlayhead();
if (previewActive && video.currentTime >= endTime) {
video.pause();
setPreviewActive(false);
}
});
document.getElementById("btn-create").addEventListener("click", onCreateClick);
// Initialization
updateUI();
})();