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> <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">

@ -126,6 +126,7 @@
</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>

@ -419,3 +419,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