// Per-project video asset library from media/videos/videos.json. // Includes both local clips and references to shared assets (is_shared=true). // minio_object_key is null today; populated when assets migrate to MinIO. exports.up = (pgm) => { pgm.createTable('video_assets', { id: 'id', project_id: { type: 'integer', notNull: true, references: 'videos', onDelete: 'CASCADE' }, asset_key: { type: 'varchar(200)', notNull: true }, // key in videos.json description: { type: 'text' }, source_file: { type: 'varchar(500)' }, // original local path output_file: { type: 'varchar(500)' }, // rendered output path minio_object_key: { type: 'varchar(500)' }, // future MinIO key is_shared: { type: 'boolean', notNull: true, default: false }, cutout_name: { type: 'varchar(50)' }, // talkinghead | square | fullscreen duration_seconds: { type: 'numeric' }, has_audio: { type: 'boolean', notNull: true, default: false }, volume: { type: 'numeric', default: 1.0 }, always_visible: { type: 'boolean', notNull: true, default: false }, pause_narration_seconds: { type: 'numeric' }, skip_seconds: { type: 'numeric' }, take_seconds: { type: 'numeric' }, filters: { type: 'jsonb' }, // per-asset filter overrides raw_json: { type: 'jsonb' }, // full original entry created_at: { type: 'timestamptz', notNull: true, default: pgm.func('NOW()') }, updated_at: { type: 'timestamptz', notNull: true, default: pgm.func('NOW()') }, }); pgm.addConstraint('video_assets', 'video_assets_project_key_unique', 'UNIQUE (project_id, asset_key)'); pgm.createIndex('video_assets', 'project_id', { name: 'idx_video_assets_project_id' }); pgm.createIndex('video_assets', 'is_shared', { name: 'idx_video_assets_is_shared' }); }; exports.down = (pgm) => { pgm.dropTable('video_assets'); };