feat(video): add video upload with remux/transcode playback

Add video as a media kind in photos: streaming upload with per-kind
limits, ffprobe metadata, poster frame reusing the image variant
pipeline, and a playback object (remux when web-compatible, else
h264/yuv420p/aac transcode). Video jobs run on a separate pool with a
long lease. Frontend gains a video viewer, grid badge/duration and
video upload.
This commit is contained in:
iu
2026-10-11 20:37:53 +03:00
parent d059d042f0
commit d38ede0201
52 changed files with 2717 additions and 82 deletions
@@ -0,0 +1,9 @@
DELETE FROM photo_objects WHERE role = 'playback';
ALTER TABLE photo_objects DROP CONSTRAINT IF EXISTS photo_objects_role_check;
ALTER TABLE photo_objects ADD CONSTRAINT photo_objects_role_check
CHECK (role IN ('original', 'thumb', 'medium', 'preview'));
ALTER TABLE variant_jobs DROP COLUMN IF EXISTS kind;
ALTER TABLE photos DROP COLUMN IF EXISTS duration_ms;
ALTER TABLE photos DROP COLUMN IF EXISTS kind;
@@ -0,0 +1,13 @@
-- Video support: media kind and duration on photos, a playback object role, and
-- a job kind distinguishing short image work from long transcoding work.
ALTER TABLE photos ADD COLUMN IF NOT EXISTS kind text NOT NULL DEFAULT 'image'
CHECK (kind IN ('image', 'video'));
ALTER TABLE photos ADD COLUMN IF NOT EXISTS duration_ms bigint;
ALTER TABLE photo_objects DROP CONSTRAINT IF EXISTS photo_objects_role_check;
ALTER TABLE photo_objects ADD CONSTRAINT photo_objects_role_check
CHECK (role IN ('original', 'thumb', 'medium', 'preview', 'playback'));
ALTER TABLE variant_jobs ADD COLUMN IF NOT EXISTS kind text NOT NULL DEFAULT 'image'
CHECK (kind IN ('image', 'video'));
@@ -0,0 +1,21 @@
DELETE FROM photo_objects WHERE role = 'playback';
CREATE TABLE photo_objects_new (
photo_id TEXT NOT NULL REFERENCES photos(id) ON DELETE CASCADE,
object_id TEXT NOT NULL REFERENCES objects(id) ON DELETE RESTRICT,
role TEXT NOT NULL CHECK (role IN ('original', 'thumb', 'medium', 'preview')),
PRIMARY KEY (photo_id, role)
);
INSERT INTO photo_objects_new (photo_id, object_id, role)
SELECT photo_id, object_id, role FROM photo_objects;
DROP TABLE photo_objects;
ALTER TABLE photo_objects_new RENAME TO photo_objects;
CREATE INDEX IF NOT EXISTS photo_objects_object_idx ON photo_objects (object_id);
ALTER TABLE variant_jobs DROP COLUMN kind;
ALTER TABLE photos DROP COLUMN duration_ms;
ALTER TABLE photos DROP COLUMN kind;
@@ -0,0 +1,24 @@
-- Video support for SQLite: media kind and duration on photos, the playback
-- object role (photo_objects is rebuilt because SQLite cannot alter a CHECK
-- constraint) and a job kind distinguishing image work from video transcoding.
ALTER TABLE photos ADD COLUMN kind TEXT NOT NULL DEFAULT 'image';
ALTER TABLE photos ADD COLUMN duration_ms INTEGER;
CREATE TABLE photo_objects_new (
photo_id TEXT NOT NULL REFERENCES photos(id) ON DELETE CASCADE,
object_id TEXT NOT NULL REFERENCES objects(id) ON DELETE RESTRICT,
role TEXT NOT NULL CHECK (role IN ('original', 'thumb', 'medium', 'preview', 'playback')),
PRIMARY KEY (photo_id, role)
);
INSERT INTO photo_objects_new (photo_id, object_id, role)
SELECT photo_id, object_id, role FROM photo_objects;
DROP TABLE photo_objects;
ALTER TABLE photo_objects_new RENAME TO photo_objects;
CREATE INDEX IF NOT EXISTS photo_objects_object_idx ON photo_objects (object_id);
ALTER TABLE variant_jobs ADD COLUMN kind TEXT NOT NULL DEFAULT 'image';