thrimbletrimmer: Add button to download current frame

It always uses source quality and downloads as a PNG.

We use the browser.downloads api to construct the URL on demand.

Note we might not always get the exact right frame in 60fps streams,
we might get one before or after (and "frame seek" in the editor skips 2 frames in this case).
pull/297/head
Mike Lang 2 years ago committed by Mike Lang
parent 1cb819a4c5
commit adb6e2ae10

@ -235,6 +235,7 @@
<option value="mpegts">MPEG-TS (slow, consumes server resources)</option>
</select>
<a id="download-link">Download Video</a>
<a href="#" id="download-frame">Download Current Frame as Image</a>
</div>
<div id="data-correction">

@ -126,6 +126,7 @@
</div>
<a href="#" id="download">Download Video</a>
<a href="#" id="download-frame">Download Current Frame as Image</a>
<a href="#" id="time-converter-link">Convert Times</a>
<form id="time-converter" class="hidden">
<h2>Time Converter</h2>

@ -419,3 +419,37 @@ function videoPlayerTimeFromVideoHumanTime(videoHumanTime) {
return hours * 3600 + minutes * 60 + seconds;
}
function getSegmentList() {
return globalPlayer.latencyController.levelDetails.fragments;
}
function dateTimeFromVideoPlayerTime(videoPlayerTime) {
const segmentList = getSegmentList();
let segmentStartTime;
let segmentStartISOTime;
for (const segment of segmentList) {
const segmentEndTime = segment.start + segment.duration;
if (videoPlayerTime >= segment.start && videoPlayerTime < segmentEndTime) {
segmentStartTime = segment.start;
segmentStartISOTime = segment.rawProgramDateTime;
break;
}
}
if (segmentStartISOTime === undefined) {
return null;
}
const wubloaderDateTime = DateTime.fromISO(segmentStartISOTime);
const offset = videoPlayerTime - segmentStartTime;
return wubloaderDateTime.plus({ seconds: offset });
}
function downloadFrame() {
const videoElement = document.getElementById("video");
const dateTime = dateTimeFromVideoPlayerTime(videoElement.currentTime);
const url = `/frame/${globalStreamName}/source.png?timestamp=${wubloaderTimeFromDateTime(dateTime)}`;
// Avoid : as it causes problems on Windows
const filename = `${dateTime.toFormat("yyyy-LL-dd'T'HH-mm-ss.SSS")}.png`;
// TODO REPLACE WITH CORRECT CALL
console.log(`Would download ${url} as ${filename}`);
}

@ -220,6 +220,10 @@ window.addEventListener("DOMContentLoaded", async (event) => {
updateDownloadLink();
});
document.getElementById("download-frame").addEventListener("click", (_event) => {
downloadFrame();
});
document.getElementById("manual-link-update").addEventListener("click", (_event) => {
const manualLinkDataContainer = document.getElementById("data-correction-manual-link");
manualLinkDataContainer.classList.toggle("hidden");
@ -1431,26 +1435,6 @@ function videoPlayerTimeFromWubloaderTime(wubloaderTime) {
return null;
}
function dateTimeFromVideoPlayerTime(videoPlayerTime) {
const segmentList = getSegmentList();
let segmentStartTime;
let segmentStartISOTime;
for (const segment of segmentList) {
const segmentEndTime = segment.start + segment.duration;
if (videoPlayerTime >= segment.start && videoPlayerTime < segmentEndTime) {
segmentStartTime = segment.start;
segmentStartISOTime = segment.rawProgramDateTime;
break;
}
}
if (segmentStartISOTime === undefined) {
return null;
}
const wubloaderDateTime = DateTime.fromISO(segmentStartISOTime);
const offset = videoPlayerTime - segmentStartTime;
return wubloaderDateTime.plus({ seconds: offset });
}
function videoPlayerTimeFromDateTime(dateTime) {
const segmentList = getSegmentList();
for (const segment of segmentList) {
@ -1496,7 +1480,3 @@ function wubloaderTimeFromVideoHumanTime(videoHumanTime) {
}
return wubloaderTimeFromVideoPlayerTime(videoPlayerTime);
}
function getSegmentList() {
return globalPlayer.latencyController.levelDetails.fragments;
}

@ -28,6 +28,10 @@ window.addEventListener("DOMContentLoaded", async (event) => {
updateTimeSettings();
});
document.getElementById("download-frame").addEventListener("click", (_event) => {
downloadFrame();
});
const timeConversionForm = document.getElementById("time-converter");
timeConversionForm.addEventListener("submit", (event) => {
event.preventDefault();

Loading…
Cancel
Save