# Media Models (Drizzle ORM) ## Overview The Media module uses **Drizzle ORM** (separate from Prisma) to manage video library, compilations, and job queue. **Models (3):** - videos — Video library with metadata - compilations — Video compilation tracking - jobs — Job queue with resource management **ORM:** Drizzle (not Prisma) **API:** Fastify (port 4100, separate from Express main API) **Migration:** `npx drizzle-kit push` (no migration files) **Key Features:** - FFprobe metadata extraction (duration, dimensions, orientation, quality, audio) - Directory type enum (9 types: studios, gifs, private, inbox, curated, playback, compilations, videos, highlights) - Public media engagement stats (historical) - Job queue with GPU/CPU resource categories - Video upload with automatic metadata extraction See [Schema Reference](../schema.md#media-drizzle-orm) for complete field listings. --- ## Directory Types ```typescript export const DIRECTORY_TYPES = [ 'studios', 'gifs', 'private', 'inbox', 'curated', 'playback', 'compilations', 'videos', 'highlights' ] as const; ``` **Usage:** - Efficient filtering (indexed) - Replaces LIKE patterns (e.g., `path LIKE '%/studios/%'`) --- ## Video Metadata (FFprobe) **Extracted Fields:** - `durationSeconds` — Video duration in seconds - `width` / `height` — Video dimensions (pixels) - `orientation` — portrait, landscape, square - `quality` — 1080p, 720p, 480p, etc. - `hasAudio` — Audio track present flag **Extraction Service:** `api/src/modules/media/services/ffprobe.service.ts` **Timeout:** 30 seconds for metadata extraction **Validation:** Decodes 5 frames with 60s timeout --- ## Job Queue **Resource Categories:** ```typescript export type ResourceCategory = 'gpu_ai' | 'gpu_encode' | 'cpu'; ``` **Job Status:** ```typescript export type JobStatus = 'pending' | 'queued' | 'running' | 'completed' | 'failed' | 'cancelled'; ``` **Job Types (21 total):** - compilation, scan, public_scan, organize, organize_studio - reencode_streaming, compile_random, compile_quad, compile_quad_horizontal - compile_triple_vertical, compile_mega, compile_gif, generate_gif - fetch, digest, digest_generate, clip_generate, highlight_generate - tag_generation, scene_extract, clip_extract_only, auto_organize_publish **Queue Processing:** - Ordered by: status (pending first), priority (lower = higher), createdAt (FIFO) - Uses composite index: `[status, priority, createdAt]` --- ## Video Upload Flow ```mermaid sequenceDiagram participant Client participant API participant FFprobe participant DB Client->>API: POST /api/media/upload (multipart/form-data) API->>API: Stream file to /media/local/inbox API->>FFprobe: Extract metadata (duration, width, height, etc.) FFprobe-->>API: metadata API->>DB: INSERT INTO videos (path, filename, durationSeconds, ...) DB-->>API: video record API-->>Client: { id, path, metadata } ``` **Volume Mount:** `/media/local/inbox:rw` (read-write), library remains `:ro` --- ## Drizzle Schema Example ```typescript export const videos = pgTable('videos', { id: serial('id').primaryKey(), path: text('path').notNull().unique(), filename: text('filename').notNull(), durationSeconds: integer('duration_seconds'), quality: text('quality'), orientation: text('orientation'), hasAudio: boolean('has_audio').default(true), width: integer('width'), height: integer('height'), directoryType: text('directory_type').$type(), tags: jsonb('tags').$type(), createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), }, (table) => ({ directoryTypeIdx: index('idx_directory_type').on(table.directoryType), fingerprintIdx: index('idx_videos_fingerprint').on( table.durationSeconds, table.fileSize, table.width, table.height ), })); ``` --- ## Related Documentation - [Schema Reference](../schema.md#media-drizzle-orm) — Complete field listings - [Migration Workflow](../migrations.md) — Drizzle Kit push - [API Media Routes](../../api/media.md) — REST endpoints - [Admin Media Library](../../admin/media-library.md) — Video management UI - [Public Media Gallery](../../admin/public-media.md) — Public video gallery