Media Manager¶
The Media Manager provides a complete video library system with upload, metadata extraction, public gallery, reaction system, and job queue monitoring. Built as a separate Fastify microservice with Drizzle ORM.
Overview¶
The Media Manager consists of four integrated components:
- Video Library - Admin video management
- Upload System - Video upload with metadata extraction
- Public Gallery - Public video sharing
- Job Queue - Background job monitoring
Features¶
Video Library¶
- Video CRUD operations
- Metadata editing (title, description, tags)
- Lock/unlock videos
- Bulk operations (delete, lock, share)
- Search and filtering
- Thumbnail generation (future)
Upload System¶
- Drag-and-drop upload
- Single and batch upload
- Progress tracking
- Automatic metadata extraction (FFprobe)
- Duration
- Dimensions (width x height)
- Orientation (landscape/portrait/square)
- Quality (resolution-based: 4K, 1080p, 720p, etc.)
- Audio detection
- File validation (type, size)
- Supported formats: MP4, MOV, AVI, MKV, WebM, M4V, FLV
- Max file size: 10GB
Public Gallery¶
- Shared videos display
- Category filtering
- Reaction system (6 emojis)
- Video cards with thumbnails
- Lock/unlock control
- Visitor reactions
Reaction System¶
Six emoji reactions:
- Like (👍)
- Love (❤️)
- Laugh (😂)
- Wow (😮)
- Sad (😢)
- Angry (😡)
Architecture¶
Dual API Design¶
Express API (Port 4000) - Main V2 features - Prisma ORM - PostgreSQL
Fastify Media API (Port 4100) - Media-specific operations - Drizzle ORM - Same PostgreSQL database - Optimized for file uploads
Backend Components¶
Media API:
- api/src/media-server.ts - Fastify entry point
- api/src/modules/media/routes/ - Video, upload, shared, reactions, jobs
- api/src/modules/media/services/ - FFprobe, video service
- api/src/modules/media/db/schema.ts - Drizzle schema
Database Tables:
- videos - Video metadata (Drizzle)
- shared_media - Public gallery (Drizzle)
- media_reactions - Reaction tracking (Drizzle)
- media_jobs - Job queue (Drizzle)
Frontend Components¶
Admin Pages:
- admin/src/pages/media/LibraryPage.tsx - Video library management
- admin/src/pages/media/SharedMediaPage.tsx - Public gallery admin
- admin/src/pages/media/MediaJobsPage.tsx - Job queue monitoring
Public Pages:
- admin/src/pages/public/MediaGalleryPage.tsx - Public video gallery
- admin/src/pages/public/MediaViewerPage.tsx - Video detail page
Components:
- admin/src/components/media/VideoCard.tsx - Video display card
- admin/src/components/media/BulkActions.tsx - Batch operations
- admin/src/components/media/UploadVideoModal.tsx - Upload interface
API Clients:
- admin/src/lib/media-api.ts - Authenticated media API client
- admin/src/lib/media-public-api.ts - Public media API client
Configuration¶
Environment Variables¶
# Enable media features
ENABLE_MEDIA_FEATURES=true
# Media API port
MEDIA_API_PORT=4100
# Media storage
MEDIA_LIBRARY_PATH=/media/local/library # Read-only library
MEDIA_INBOX_PATH=/media/local/inbox # Read-write inbox
Docker Volumes¶
volumes:
# Library (read-only)
- /path/to/library:/media/local/library:ro
# Inbox (read-write for uploads)
- /path/to/inbox:/media/local/inbox:rw
Upload System¶
Upload Flow¶
- Select Files
- Drag-and-drop or file picker
- Multiple file selection
-
File type validation
-
Upload to Inbox
- Stream to
/media/local/inbox - UUID filename (prevents conflicts)
-
Progress tracking
-
Extract Metadata
- FFprobe analysis (30s timeout)
- Duration, dimensions, orientation
- Quality calculation
-
Audio detection
-
Create Database Record
- Store metadata
- Set initial status
-
Link to user
-
Process Video (Future)
- Generate thumbnail
- Transcode formats
- Move to library
FFprobe Metadata Extraction¶
Automatically extracts:
interface VideoMetadata {
duration: number; // Seconds (e.g., 125.5)
width: number; // Pixels (e.g., 1920)
height: number; // Pixels (e.g., 1080)
orientation: string; // 'landscape' | 'portrait' | 'square'
quality: string; // '4K' | '1080p' | '720p' | 'SD'
hasAudio: boolean; // Audio stream detected
}
Quality Calculation: - 4K: ≥2160p (3840x2160) - 1080p: ≥1080p (1920x1080) - 720p: ≥720p (1280x720) - SD: <720p
Orientation: - Landscape: width > height - Portrait: height > width - Square: width ≈ height (within 10%)
Supported Formats¶
- MP4 - H.264/H.265 video
- MOV - QuickTime
- AVI - Audio Video Interleave
- MKV - Matroska
- WebM - Web-optimized
- M4V - iTunes video
- FLV - Flash video
Public Gallery¶
Sharing System¶
Videos can be shared publicly:
- Lock/Unlock - Control public visibility
- Category Assignment - Organize by category
- Public Access - View at
/media - Reactions - Emoji reactions from visitors
Categories¶
Predefined categories:
- Events
- Testimonials
- Tutorials
- Announcements
- Behind the Scenes
- Custom categories
Reaction System¶
Reaction Tracking¶
interface MediaReaction {
id: number;
videoId: number;
reactionType: string; // 'like' | 'love' | 'laugh' | 'wow' | 'sad' | 'angry'
sessionId: string; // Unique session identifier
createdAt: Date;
}
Session-Based Reactions¶
- One reaction per video per session
- Session ID in localStorage
- Update reaction (change type)
- Remove reaction (click again)
Job Queue¶
Background jobs for:
- Video processing
- Thumbnail generation
- Format transcoding
- Metadata extraction
- File cleanup
Job Monitoring¶
Admin can:
- View job status
- Monitor progress
- Retry failed jobs
- View error logs
Database Schema (Drizzle)¶
Videos Table¶
export const videos = pgTable('videos', {
id: serial('id').primaryKey(),
title: varchar('title', { length: 255 }).notNull(),
description: text('description'),
filename: varchar('filename', { length: 255 }).notNull(),
filepath: varchar('filepath', { length: 500 }).notNull(),
duration: integer('duration'), // Seconds
width: integer('width'), // Pixels
height: integer('height'), // Pixels
orientation: varchar('orientation', { length: 20 }), // 'landscape' | 'portrait' | 'square'
quality: varchar('quality', { length: 20 }), // '4K' | '1080p' | '720p' | 'SD'
hasAudio: boolean('has_audio'),
tags: json('tags').$type<string[]>(),
locked: boolean('locked').default(false),
uploadedBy: integer('uploaded_by'),
createdAt: timestamp('created_at').defaultNow(),
updatedAt: timestamp('updated_at').defaultNow(),
});
API Endpoints¶
Admin Endpoints (Media API - Port 4100)¶
GET /media-api/videos # List videos
POST /media-api/videos # Create video (manual)
GET /media-api/videos/:id # Get video
PATCH /media-api/videos/:id # Update video
DELETE /media-api/videos/:id # Delete video
POST /media-api/upload # Upload single video
POST /media-api/upload/batch # Upload multiple videos
GET /media-api/shared # List shared videos
POST /media-api/shared # Share video
DELETE /media-api/shared/:id # Unshare video
GET /media-api/jobs # List jobs
Public Endpoints¶
GET /media-api/public/videos # List public videos
GET /media-api/public/videos/:id # Get public video
POST /media-api/reactions # Add/update reaction
DELETE /media-api/reactions/:id # Remove reaction
Security¶
File Validation¶
- MIME type checking
- File extension validation
- File size limits (10GB max)
- Path traversal prevention
- Null byte detection
Access Control¶
- Admin-only uploads
- Lock/unlock controls
- Public visibility flags
- Session-based reactions
Performance¶
Upload Optimization¶
- Streaming uploads (no memory buffering)
- UUID filenames (no collisions)
- Async metadata extraction
- Progress tracking
Gallery Optimization¶
- Lazy loading
- Thumbnail caching (future)
- Paginated results
- Infinite scroll