From 9c3c328a3a6664f8145e13c13419d396a1d68fa6 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Tue, 23 Jun 2026 09:23:31 -0700 Subject: [PATCH] tapes: make serving kind-aware (video vs frame backing store) get-tape now returns kind ('zip'|'mp4', default 'zip') + mp4Status. The /media/tapes/ redirect picks .mp4 for video-backed tapes and .zip for frame-based ones (explicit .mp4/.zip on the request still wins). Missing kind defaults to zip, so all existing frame tapes resolve exactly as before. Step A of camera-roll video -> !tape (video-backed tapes). --- lith/server.mjs | 11 ++++++++--- system/netlify/functions/get-tape.mjs | 6 +++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lith/server.mjs b/lith/server.mjs index 9fdfa73679..fa7728e9e4 100644 --- a/lith/server.mjs +++ b/lith/server.mjs @@ -911,15 +911,20 @@ app.all("/media/*rest", async (req, res) => { }; } - // /media/tapes/CODE → get-tape function → redirect to DO Spaces + // /media/tapes/CODE → get-tape function → redirect to DO Spaces. + // The backing store is .mp4 for video-based tapes (kind "mp4") and .zip for + // frame-based recordings (kind "zip", the default for legacy tapes). An + // explicit extension on the request (CODE.mp4 / CODE.zip) wins over kind. if (parts[0] === "tapes" && parts[1]) { - const code = parts[1].replace(/\.zip$/, ""); + const reqExt = /\.(mp4|zip)$/.exec(parts[1])?.[1]; + const code = parts[1].replace(/\.(mp4|zip)$/, ""); try { const result = await functions["get-tape"](mediaEvent("/api/get-tape", { code }), {}); if (result.statusCode === 200) { const tape = JSON.parse(result.body); const bucket = tape.bucket || "art-aesthetic-computer"; - const key = tape.user ? `${tape.user}/${tape.slug}.zip` : `${tape.slug}.zip`; + const ext = reqExt || (tape.kind === "mp4" ? "mp4" : "zip"); + const key = tape.user ? `${tape.user}/${tape.slug}.${ext}` : `${tape.slug}.${ext}`; return res.redirect(302, `https://${bucket}.sfo3.digitaloceanspaces.com/${key}`); } } catch {} diff --git a/system/netlify/functions/get-tape.mjs b/system/netlify/functions/get-tape.mjs index 456ebca5a4..68bf073d8b 100644 --- a/system/netlify/functions/get-tape.mjs +++ b/system/netlify/functions/get-tape.mjs @@ -31,7 +31,9 @@ export async function handler(event, context) { return respond(404, { message: "Tape not found" }); } - // Return tape info (excluding mp4 fields - not needed for playback) + // Return tape info. `kind` distinguishes the backing store: + // "zip" = frame-based recording (default for legacy tapes), + // "mp4" = video-based tape (e.g. a posted camera-roll clip). return respond(200, { slug: tape.slug, code: tape.code, @@ -39,6 +41,8 @@ export async function handler(event, context) { bucket: tape.bucket, user: tape.user, nuked: tape.nuked || false, + kind: tape.kind || "zip", + mp4Status: tape.mp4Status, }); } catch (error) { -- 2.51.2