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).
mike/download-frame-2
Mike Lang 2 years ago
parent ffae321d04
commit 8a0705c927

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

@ -125,6 +125,7 @@
</div> </div>
</div> </div>
<a href="#" id="download">Download Video</a> <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> <a href="#" id="time-converter-link">Convert Times</a>
<form id="time-converter" class="hidden"> <form id="time-converter" class="hidden">
<h2>Time Converter</h2> <h2>Time Converter</h2>

@ -439,3 +439,37 @@ function videoPlayerTimeFromVideoHumanTime(videoHumanTime) {
return hours * 3600 + minutes * 60 + seconds; 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(); updateDownloadLink();
}); });
document.getElementById("download-frame").addEventListener("click", (_event) => {
downloadFrame();
});
document.getElementById("manual-link-update").addEventListener("click", (_event) => { document.getElementById("manual-link-update").addEventListener("click", (_event) => {
const manualLinkDataContainer = document.getElementById("data-correction-manual-link"); const manualLinkDataContainer = document.getElementById("data-correction-manual-link");
manualLinkDataContainer.classList.toggle("hidden"); manualLinkDataContainer.classList.toggle("hidden");
@ -1431,26 +1435,6 @@ function videoPlayerTimeFromWubloaderTime(wubloaderTime) {
return null; 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) { function videoPlayerTimeFromDateTime(dateTime) {
const segmentList = getSegmentList(); const segmentList = getSegmentList();
for (const segment of segmentList) { for (const segment of segmentList) {
@ -1496,7 +1480,3 @@ function wubloaderTimeFromVideoHumanTime(videoHumanTime) {
} }
return wubloaderTimeFromVideoPlayerTime(videoPlayerTime); return wubloaderTimeFromVideoPlayerTime(videoPlayerTime);
} }
function getSegmentList() {
return globalPlayer.latencyController.levelDetails.fragments;
}

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

Loading…
Cancel
Save