Commit graph

650 commits

Author SHA1 Message Date
03aa7a0673 fix(video): revert S3 redirect — RustFS rejects range+Origin; proxy with cache headers
S3 at broadcastmgmt.cloud (RustFS/openresty) returns 403 on range
requests that include an Origin header on presigned URLs. The HMAC
signature only covers 'host' in X-Amz-SignedHeaders, so the browser's
cross-origin Origin header breaks signature validation.

Reverted: /stream and /video no longer redirect to signed S3 URLs.

Fixed: /video now pipes through Node with:
  Cache-Control: private, max-age=3600
  ETag and Last-Modified forwarded from S3

This means the browser caches video segments for 1h. On seek the
browser checks its cache first — only uncached byte ranges hit the
server. Combined with the 1.5Mbps proxy (was 4Mbps), seeks should
be responsive for clips under ~10 minutes.
2026-05-26 17:40:02 +00:00
37247fdfea fix(video): direct S3 signed URL for streaming + proxy bitrate 1.5Mbps
- GET /assets/:id/stream now returns a signed S3 URL directly (4h TTL)
  instead of pointing to the /video pipe endpoint. Browser streams
  directly from S3 — no Node.js bottleneck, S3 handles range requests
  natively for smooth seeking.

- GET /assets/:id/video now redirects (302) to a signed S3 URL.
  Belt-and-suspenders: any code still calling /video gets redirected.

- proxy.js: default bitrate changed from 10Mbps to 1.5Mbps, audio
  default from 192kbps to 128kbps. DB settings already updated to
  1.5Mbps. Cuts proxy file size ~6x for the same quality content.
  Existing proxies need re-generation at new bitrate.
2026-05-26 16:57:37 +00:00
a03dd36f11 fix(premiere-plugin): hide growing-count badge until count > 0
The badge initially showed '0' before any poll completed. Toggling
display via JS expects an initial display:none so the badge does not
flash in the tab nav on first connect.
2026-05-26 16:40:47 +00:00
a03c85f08a feat: server-side filmstrip worker + fix scheduler crash + fix clip freeze
Root causes found:
1. Scheduler crashing every 15s: assets table has no error_message column.
   Fix: remove error_message from UPDATE in scheduler.js (#66 regression).

2. Clip freezing: client-side filmstrip seek loop runs on main thread,
   seeks same proxy the player is streaming → both stall → freeze.
   Fix: replace browser seek loop entirely with server-side FFmpeg worker.

3. No dedicated filmstrip worker: filmstrip was never pre-built server-side.

Changes:
- services/mam-api/src/db/migrations/018-add-filmstrip-s3-key.sql
  Add filmstrip_s3_key TEXT column to assets table

- services/worker/src/workers/filmstrip.js (new)
  BullMQ worker: downloads proxy, runs FFmpeg fps filter to extract
  28 evenly-spaced JPEG frames, base64-encodes them, uploads JSON
  array to S3 at filmstrips/<assetId>.json, stores key in DB

- services/worker/src/workers/thumbnail.js
  Queue filmstrip job automatically after thumbnail completes

- services/worker/src/index.js
  Register filmstrip worker (concurrency=2), export filmstripQueue
  singleton, close it on SIGTERM

- services/mam-api/src/routes/assets.js
  - filmstripQueue added
  - POST /reprocess?type=filmstrip now supported
  - GET /:id/filmstrip returns signed S3 URL for JSON frames

- services/mam-api/src/routes/jobs.js
  filmstrip queue visible in Jobs UI

- services/web-ui/public/screens-asset.jsx
  Replace browser seek loop with fetch of /assets/:id/filmstrip
  → fetch S3 JSON → render frames. Zero browser-side video seeking.
  Right-click and Files tab re-generate via API endpoint.
2026-05-26 16:39:44 +00:00
564cf6b18f fix: thumbnail img uses signed URL from API; switch transcoding to CPU libx264
- FilesTab: fetch /assets/:id/thumbnail (returns signed S3 URL JSON),
  display the resolved URL in <img> instead of pointing directly at the
  endpoint which returns JSON not image bytes
- Transcoding: settings updated on ZAMPP1 to gpu_transcode_enabled=false,
  codec=libx264 — NVENC not available in worker container (no GPU passthrough)
  The proxy worker already has a CPU fallback but this prevents the
  unnecessary failed GPU attempt on every job
2026-05-26 16:27:27 +00:00
89645f160e fix(filmstrip): seeked event never fires at t=0; add per-frame seek timeout
Two bugs:

1. Frame 0 sets currentTime=0 but probe starts at t=0 after onloadedmetadata,
   so 'seeked' never fires (no position change). Promise hangs until the 15s
   global timeout kills the whole build. Fix: when currentTime is already at
   target (within 0.05s), call done() immediately without waiting for seeked.

2. Seeks into unbuffered regions of large MP4s can stall indefinitely.
   Fix: 3s per-frame timeout captures the current decoded frame and moves on,
   so a slow/stalled seek doesn't block the remaining 27 frames.
2026-05-26 16:21:00 +00:00
e9eeb84c5f fix(files-tab): remove inline video preview from proxy row 2026-05-26 16:10:04 +00:00
4f98f2b773 feat(asset): filmstrip right-click menu + Files tab
Filmstrip:
- Right-click on the filmstrip opens a context menu with
  'Re-generate filmstrip' and 'Re-generate proxy'
- filmstripKey state forces the build effect to re-run on demand
  without waiting for a streamUrl/totalMs change
- Context menu dismisses on click, contextmenu, and scroll

Files tab (replaces empty Versions tab):
- Proxy: status badge, S3 key path, inline video preview, re-generate button
- Hi-res master: status badge and S3 key path
- Thumbnail: status badge, S3 key path, inline thumbnail image, re-generate button
- Filmstrip: status badge, frame count, scrollable strip of first 14 frames,
  re-generate button (disabled while building)
2026-05-26 16:07:33 +00:00
b3c61134fc fix(filmstrip): remove crossOrigin=anonymous from probe video element
The /video endpoint requires session auth (requireAuth middleware).
crossOrigin='anonymous' strips cookies from the request → 401 → video
never loads → 15s timeout → filmstrip stays empty for all clips.

Same-origin video does not need crossOrigin for canvas drawImage — the
taint restriction only applies to cross-origin resources.
2026-05-26 16:03:26 +00:00
5edb4df35a fix(assets): missing closing }); on POST / route (syntax error) 2026-05-26 15:05:50 +00:00
07f8ffa6d5 feat: editor coming-soon bumper + embedded Premiere panel downloads
- Editor: overlay Coming Soon screen over NLE timeline (code preserved,
  bumper sits at z-index 100 with backdrop blur). Links to download
  ZXP and Windows installer directly from the bumper.

- Settings → Capture SDKs: new Premiere Panel section lists v1.0.0
  and v1.0.1 with ZXP + Windows Installer download buttons.
  Both releases embedded as static files in web-ui under /downloads/.

- nginx: /downloads/ location serves files as Content-Disposition
  attachment with 24h cache.

Files added:
  services/web-ui/public/downloads/dragonflight-premiere-panel-1.0.0.zxp
  services/web-ui/public/downloads/dragonflight-premiere-panel-1.0.0-windows-setup.exe
  services/web-ui/public/downloads/dragonflight-premiere-panel-1.0.1.zxp
  services/web-ui/public/downloads/dragonflight-premiere-panel-1.0.1-windows-setup.exe
2026-05-26 14:34:28 +00:00
8e0e94de3d fix: close all 24 open issues (#40–#94)
Bug fixes:
- #91: dockerApi() 10s socket timeout (Docker daemon hang)
- #77: await syncToAmpp() with .catch() — no longer fire-and-forget
- #75: migration 016 — add 'proxy','import' to job_type enum; add 'completed' to job_status
- #73: BullMQ orphan job cleanup on hard asset delete
- #70: batch-trim jobs table gets expires_at; trim-status auto-expires stale rows
- #66: scheduler tick marks stale live assets (>2h) as error
- #63: migration 017 — partial unique index prevents concurrent live asset overwrite
- #61: recorders.js uses getS3Bucket() not stale process.env.S3_BUCKET
- #60: already fixed (copy nulls proxy/thumbnail keys, requeues proxy)
- #40: already fixed (All projects clears openProject)
- #64: already fixed (sourceType/needsProxy handled)
- #90: GET /jobs now includes DB jobs table (trim jobs visible in UI)
- #74: nginx Content-Type header preserved; multer 500MB file size limit
- #68: GET /upload returns in-progress ingesting assets
- #58: /stream and /video endpoints fall back to original file for all video types
- #55: recorder poll .catch() logs auth errors cleanly; redirect stops interval
- #52: thumb-status and thumb-duration moved inside position:relative wrapper
- #50: ProjectCard gets onContextMenu handler with rename/delete menu
- #49: project context menu dismisses on contextmenu + scroll events

Features:
- #93: POST /assets/:id/reprocess?type=proxy|thumbnail — force re-queue any asset
  Asset ⋯ menu now shows 'Re-generate proxy' and 'Re-generate thumbnail' buttons

UI:
- Logo: brightness(0) invert(1) filter applied consistently in sidebar, launcher,
  and login — white logo pops on dark UI; inline style removed from login.html
2026-05-26 14:10:44 +00:00
602370be26 fix(worker): use bracket notation for @_ XML attribute property access
track?.@_currentExplodedTrackIndex is invalid JS syntax — @ is not a
valid identifier character. Replaced with track?.['@_currentExplodedTrackIndex']
so the worker process no longer crashes on startup.
2026-05-26 09:41:33 -04:00
3ebe5d6639 fix(users): invalidate sessions on password change (issue #94 bug 5) 2026-05-26 07:39:14 -04:00
6ee284e3f6 fix(auth): add brute-force rate limiting on POST /login (issue #94 bug 6) 2026-05-26 07:39:14 -04:00
bacdb9f49c fix(worker): close all Queue singletons + promotion intervals on SIGTERM (issue #94 bugs 4, 7, 10) 2026-05-26 07:38:08 -04:00
6eb98d866b fix(youtube-import): export proxyQueue singleton for clean SIGTERM shutdown (issue #94 bug 7) 2026-05-26 07:38:07 -04:00
cb0efdfdae fix(proxy): export thumbnailQueue singleton for clean SIGTERM shutdown (issue #94 bug 7) 2026-05-26 07:36:54 -04:00
a6c9529c50 fix(promotion): singleton proxyQueue; await promote(); return shutdown fn (issue #94 bugs 3, 4) 2026-05-26 07:36:08 -04:00
e289554e44 fix(trim): update jobs table status on complete/fail (issue #94 bug 2) 2026-05-26 07:35:28 -04:00
bec64e668d fix(conform): mark asset error on failure; scope asset lookup by project_id (issue #94 bugs 1, 9) 2026-05-26 07:35:13 -04:00
a0b7b42524 feat(db): add growing_retention_days setting (migration 015) 2026-05-26 07:27:23 -04:00
09e2987c14 feat(db): add growing_enabled column to recorders (migration 014) 2026-05-26 07:27:17 -04:00
ee0c2a12de Use HTML img tag for screenshot 2026-05-26 01:47:28 +00:00
782ff5b7b6 Try relative path for screenshot 2026-05-26 01:38:14 +00:00
a20b0d3fe3 Use absolute URL for screenshot in README 2026-05-26 01:36:24 +00:00
f420584e1a Move feature overview to README with screenshot 2026-05-26 01:34:18 +00:00
0d85899627 Add home dashboard screenshot 2026-05-26 01:28:44 +00:00
be9ae32a3b Add feature overview documentation 2026-05-25 21:24:48 -04:00
7fc502513e fix(#78): GET /assets — include_archived filter now independent of status filter 2026-05-25 19:29:23 -04:00
2e1ac72585 fix(#79): proxy worker respects live/ingesting status on error 2026-05-25 18:36:39 -04:00
fba671ad40 fix(#53): show error banner with retry when loadData() rejects 2026-05-25 17:42:39 -04:00
33c82cab1a fix(#38,#54): fix apiFetch Content-Type header order; fix normalizeAsset seed hash 2026-05-25 17:42:06 -04:00
75c23448b4 fix(#65): GET /schedules returns 400 for unknown status query param 2026-05-25 17:40:58 -04:00
548c2ab8a4 fix(#72,#59): remove nginx /health stub — API endpoint proxies through correctly 2026-05-25 17:38:40 -04:00
15b4d45375 fix(#48): add type:module to mam-api package.json 2026-05-25 17:37:56 -04:00
4c8c3b72bb fix filmstrip: append probe to DOM, fix race condition, add 15s timeout 2026-05-25 11:26:02 -04:00
7ea3a235da fix: filmstrip — fetch video chunk as Blob URL, append probe to DOM, add timeout 2026-05-25 11:21:38 -04:00
0481fb3ecf fix: filmstrip probe video — append to DOM, fix src/handler race, add timeout 2026-05-25 11:14:55 -04:00
37c406bf4d fix filmstrip: use hls.js for HLS stream frame capture, not only direct streams 2026-05-25 09:30:40 -04:00
b345f5f6a4 fix editor: use assetsRef to avoid stale closure in handleExternalDrop 2026-05-25 09:29:05 -04:00
OpenCode
87f14b7c71 Fix asset filmstrip and editor UX 2026-05-25 05:14:36 +00:00
c501d88c63 Auto refresh library after ingest 2026-05-25 01:13:19 -04:00
78539ec8b0 Fix editor timeline interactions 2026-05-25 01:10:45 -04:00
de895dd7f8 Fix library refresh behavior 2026-05-25 01:08:38 -04:00
3dad82d992 fix(editor): drag interactions, undo history, overflow clipping
Four critical fixes:
- Remove overflow:hidden on tlRef so Timeline.init's scroll survives re-renders
- Don't call _renderClips() inside mousedown (was destroying event target mid-drag)
- Use refs for undo history to eliminate stale closure in onClipsChanged callback
- Change .tl-clip-area overflow:hidden to overflow:visible so pointer events reach clip edges
2026-05-24 21:21:23 -04:00
4673efac6a fix(editor): setScale, hand pan, sort comparator, playhead sync, rename/delete, track selector 2026-05-24 21:03:12 -04:00
721f847b28 fix: remove openreel editor; fire df:assets-changed on upload/ingest complete 2026-05-24 20:36:04 -04:00
c36c732f47 fix: comment out editor service in docker-compose to unblock deployment 2026-05-24 18:58:22 -04:00
60e306d1db fix(hls): retry on playback failure with exponential backoff 2026-05-24 16:52:04 -04:00