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 for complete field listings.
Directory Types¶
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:
Job Status:
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¶
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¶
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<DirectoryType>(),
tags: jsonb('tags').$type<string[]>(),
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 — Complete field listings
- Migration Workflow — Drizzle Kit push
- API Media Routes — REST endpoints
- Admin Media Library — Video management UI
- Public Media Gallery — Public video gallery