4.2 KiB
4.2 KiB
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 secondswidth/height— Video dimensions (pixels)orientation— portrait, landscape, squarequality— 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:
export type ResourceCategory = 'gpu_ai' | 'gpu_encode' | 'cpu';
Job Status:
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
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