From 9d6bbf81126867891d9efca6356f0c48155b9542 Mon Sep 17 00:00:00 2001 From: zgaetano Date: Fri, 29 May 2026 21:44:52 -0400 Subject: [PATCH] fix(mam-api): /stream returns MP4 url + separate hls_url (fixes Premiere import) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HLS-VOD work made GET /assets/:id/stream return the HLS playlist URL as `url` whenever hls_s3_key was set. The Premiere plugin's "Import Proxy" downloads `url` to a file and imports it — so it was saving an .m3u8 playlist as .mp4, and Premiere rejected it ("unsupported compression type"). This hit every YouTube asset (all get HLS generated), regardless of codec. /stream now returns the directly-downloadable MP4 proxy as `url` (type mp4) and the HLS playlist as a separate `hls_url`. The web player prefers `hls_url` (so in-browser HLS playback is unchanged), while the already-installed plugin gets a real MP4 again — no plugin reinstall needed. Co-Authored-By: Claude Opus 4.8 --- services/mam-api/src/routes/assets.js | 15 ++++++++++++--- services/web-ui/public/screens-asset.jsx | 7 ++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/services/mam-api/src/routes/assets.js b/services/mam-api/src/routes/assets.js index d31387e..905ab12 100644 --- a/services/mam-api/src/routes/assets.js +++ b/services/mam-api/src/routes/assets.js @@ -595,10 +595,19 @@ router.get('/:id/stream', async (req, res, next) => { if (r.rows.length === 0) return res.status(404).json({ error: 'Asset not found' }); const a = r.rows[0]; if (a.status === 'live') return res.json({ url: `/live/${a.id}/index.m3u8`, type: 'hls', live: true }); - // Prefer the HLS rendition for recorded assets — whole-file segment GETs - // avoid the RustFS ranged-GET stitching the MP4 /video path has to do. + // `url` is the directly-downloadable MP4 proxy; `hls_url` is the HLS + // rendition for in-browser playback (whole-file segment GETs avoid the + // RustFS ranged-GET stitching the MP4 path needs). The Premiere plugin + // downloads `url` to a file and imports it, so `url` must NOT be the + // .m3u8 playlist — Premiere can't import a playlist ("unsupported + // compression type"). The web player prefers `hls_url` when present. if (a.hls_s3_key) { - return res.json({ url: `/api/v1/assets/${id}/hls/playlist.m3u8`, type: 'hls', source: 'proxy' }); + return res.json({ + url: `/api/v1/assets/${id}/video`, + type: 'mp4', + source: a.proxy_s3_key ? 'proxy' : 'original', + hls_url: `/api/v1/assets/${id}/hls/playlist.m3u8`, + }); } const VIDEO_EXTS = ['.mp4', '.mov', '.mxf', '.ts', '.m4v', '.mkv', '.avi', '.webm']; const key = a.proxy_s3_key || diff --git a/services/web-ui/public/screens-asset.jsx b/services/web-ui/public/screens-asset.jsx index d92deb7..a6beafb 100644 --- a/services/web-ui/public/screens-asset.jsx +++ b/services/web-ui/public/screens-asset.jsx @@ -65,7 +65,12 @@ function AssetDetail({ asset, onClose }) { setStreamLoading(true); window.ZAMPP_API.fetch('/assets/' + assetId + '/stream') .then(function(r) { - if (r && r.url) { + if (r && r.hls_url) { + // Prefer HLS for in-browser playback; `url` stays the MP4 proxy + // (used by the Premiere plugin importer + as a fallback). + setStreamUrl(r.hls_url); + setStreamType('hls'); + } else if (r && r.url) { setStreamUrl(r.url); setStreamType(r.type || 'mp4'); } else if (r) {