diff --git a/web/src/pages/View.tsx b/web/src/pages/View.tsx index ccb819c..f6c2924 100644 --- a/web/src/pages/View.tsx +++ b/web/src/pages/View.tsx @@ -33,6 +33,15 @@ function CloseIcon() { ); } +function formatDuration(seconds: number) { + const total = Math.floor(seconds); + const h = Math.floor(total / 3600); + const m = Math.floor((total % 3600) / 60); + const s = total % 60; + if (h > 0) return `${h}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`; + return `${m}:${s.toString().padStart(2, "0")}`; +} + function isAudioOnlyWebm(url: string): Promise { return new Promise((resolve) => { const v = document.createElement("video"); @@ -81,6 +90,7 @@ export default function View() { const [copiedItem, setCopiedItem] = createSignal(null); const [selectedIndex, setSelectedIndex] = createSignal(null); const [previewClosing, setPreviewClosing] = createSignal(false); + const [videoDurations, setVideoDurations] = createSignal>({}); const totalPlainSize = createMemo(() => items().reduce((sum, item) => sum + item.size, 0)); const selectedItem = createMemo(() => { const index = selectedIndex(); @@ -195,6 +205,7 @@ export default function View() { if (burnAfterRead()) setBurned(true); clearObjectUrls(); + setVideoDurations({}); const nextItems: ViewItem[] = []; for (const file of files) { @@ -303,6 +314,15 @@ export default function View() { } }; + const setVideoDuration = (src: string | undefined, duration: number) => { + if (!src || !Number.isFinite(duration) || duration <= 0) return; + setVideoDurations((current) => + current[src] === duration ? current : { ...current, [src]: duration }, + ); + }; + + const videoDuration = (item: ViewItem) => (item.src ? videoDurations()[item.src] : undefined); + const renderPreview = (item: ViewItem, mode: "tile" | "full" | "modal") => { if (item.contentType === "image") { return ( @@ -320,12 +340,13 @@ export default function View() { ); } if (item.contentType === "video") { - return ( + const video = (