1. Server posters no longer auto-load in VideoSprite constructor.
loadServerPoster() is now public and called by the culling system
only when within MAX_POSTER_VIDEOS budget. Videos outside the
budget stay as placeholders regardless of server poster availability.
2. video-utils cleanup() now uses statSync to distinguish files from
directories, unlinks files first, then rmdirs. Previously tmpDir
was passed through path.dirname() which resolved to /tmp instead
of the created temp directory.
Upload pipeline now extracts poster frame (JPEG) and metadata
(width, height, duration) from videos at upload time using ffmpeg.
Server:
- Add ffmpeg to Docker image (Alpine)
- video-utils.js: probeVideo() and extractPoster() using ffprobe/ffmpeg
- Upload response includes poster_asset_key and duration
Frontend:
- VideoObject gains poster and duration fields in scene format
- VideoSprite accepts posterAssetKey + TextureManager, loads poster
as a regular image texture on construction (no <video> needed)
- Server poster loaded/released via TextureManager ref counting
- addVideoFromUpload passes poster and duration through to scene data
- image-drop.ts forwards poster_asset_key from upload response
Result: videos with server posters render as images by default.
Zero <video> elements needed for thumbnails. Only explicit play
creates a media element.