feat: media processing pipeline, spatial indexing, canvas-based video rendering
- Background media worker: polls media_jobs table, runs ffprobe+ffmpeg with concurrency limit, generates video posters, emits socket events - Non-blocking video upload: stores file + enqueues job, returns immediately - Poster hydration on board load: GET /boards/:id injects poster/dimensions from DB into canvas_state video objects - SpatialGrid: fixed-cell (512px) spatial hash for O(nearby) culling instead of O(all) item scanning, eliminates setTimeout violations - Canvas-based video rendering: draws video frames to offscreen canvas then uploads to GPU, completely eliminates GL_INVALID_OPERATION errors from PixiJS VideoSource auto-update mechanism - Server poster upgrade path: culling ticker and applyProcessedMedia() both upgrade client-captured posters to server posters when available - Pause restores server poster (paused video behaves like an image) - Selection drag-end persistence: onObjectDragEnd broadcasts + saves + undo - Live media:job:update socket handler patches scene data + VideoSprite dimensions without broadcast fanout
This commit is contained in:
@@ -111,6 +111,14 @@ export default function Editor({ isPublicView }: EditorProps) {
|
||||
if (changedIds && changedIds.length > 0) {
|
||||
// Incremental: broadcast only changed elements
|
||||
syncRef.current?.broadcastElements(changedIds);
|
||||
// Keep spatial index in sync for all local mutations (align/flip/arrange/etc.)
|
||||
const scene = canvasRef.current?.getScene();
|
||||
if (scene) {
|
||||
for (const id of changedIds) {
|
||||
const item = scene.items.get(id);
|
||||
if (item) scene.updateSpatialEntry(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Full scene sync happens via debounced SceneManager.onChange chain in sync.ts
|
||||
if (undoRef.current && !undoRef.current.isLocked()) {
|
||||
|
||||
Reference in New Issue
Block a user