Add web upload: POST /api/upload stores MP4s into dated archive, plus Upload UI panel
This commit is contained in:
+48
@@ -169,6 +169,16 @@
|
||||
<div class="highlight-list" id="highlight-list"><span style="color:var(--dim);font-size:12px">No highlights yet.</span></div>
|
||||
</section>
|
||||
|
||||
<div class="encode-panel">
|
||||
<button class="encode-toggle" id="upload-toggle"><svg viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>Upload</button>
|
||||
<div class="encode-body" id="upload-body" style="flex-wrap:wrap">
|
||||
<input type="file" id="upload-file" accept="video/mp4" style="font-size:12px;color:var(--dim)">
|
||||
<span class="enc-status" style="min-width:120px">Date: <b id="upload-date">-</b></span>
|
||||
<button id="upload-btn" disabled>Upload</button>
|
||||
<span class="enc-status" id="upload-status" style="min-width:200px">Pick an MP4 named HH.MM.SS.mp4</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="encode-panel">
|
||||
<button class="encode-toggle" id="encode-toggle"><svg viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>Encoding</button>
|
||||
<div class="encode-body" id="encode-body">
|
||||
@@ -512,6 +522,44 @@
|
||||
else if (e.code === "KeyM") { video.muted = !video.muted; updateVolIcon(); }
|
||||
});
|
||||
|
||||
// upload
|
||||
const uploadToggle = document.querySelector("#upload-toggle");
|
||||
uploadToggle.addEventListener("click", () => {
|
||||
uploadToggle.classList.toggle("open");
|
||||
document.querySelector("#upload-body").classList.toggle("show");
|
||||
document.querySelector("#upload-date").textContent = dateSelect.value || "-";
|
||||
});
|
||||
const uploadFile = document.querySelector("#upload-file");
|
||||
const uploadBtn = document.querySelector("#upload-btn");
|
||||
uploadFile.addEventListener("change", () => {
|
||||
uploadBtn.disabled = !uploadFile.files.length;
|
||||
document.querySelector("#upload-status").textContent = uploadFile.files.length
|
||||
? `${uploadFile.files[0].name} (${(uploadFile.files[0].size / 1048576).toFixed(1)} MB)` : "Pick an MP4 named HH.MM.SS.mp4";
|
||||
});
|
||||
uploadBtn.addEventListener("click", async () => {
|
||||
const file = uploadFile.files[0];
|
||||
if (!file) return;
|
||||
const name = file.name;
|
||||
const dot = name.lastIndexOf(".");
|
||||
const filename = dot > 0 ? name.slice(0, dot).padStart(8, "0").slice(-8) + name.slice(dot).toLowerCase() : name;
|
||||
if (!/^\d{2}\.\d{2}\.\d{2}\.mp4$/i.test(filename)) {
|
||||
document.querySelector("#upload-status").textContent = "Bad name: use HH.MM.SS.mp4 (e.g. 09.28.36.mp4)";
|
||||
return;
|
||||
}
|
||||
uploadBtn.disabled = true;
|
||||
document.querySelector("#upload-status").textContent = "Uploading...";
|
||||
try {
|
||||
const r = await fetch(`/api/upload?date=${dateSelect.value}&filename=${encodeURIComponent(filename)}`, {method: "POST", body: file});
|
||||
const d = await r.json().catch(() => ({}));
|
||||
document.querySelector("#upload-status").textContent = r.ok
|
||||
? `Stored: ${d.file}` : `Failed: ${d.message || r.status}`;
|
||||
if (r.ok) { uploadFile.value = ""; loadDate(dateSelect.value); }
|
||||
} catch (err) {
|
||||
document.querySelector("#upload-status").textContent = "Failed: network error";
|
||||
}
|
||||
uploadBtn.disabled = !uploadFile.files.length;
|
||||
});
|
||||
|
||||
dateSelect.addEventListener("change", () => loadDate(dateSelect.value));
|
||||
addEventListener("resize", drawTimeline);
|
||||
loadDate("").catch(() => {});
|
||||
|
||||
Reference in New Issue
Block a user