# 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: 1. **[Video Library](video-library.md)** - Admin video management 2. **[Upload System](upload.md)** - Video upload with metadata extraction 3. **[Public Gallery](public-gallery.md)** - Public video sharing 4. **[Job Queue](jobs.md)** - 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 ```bash # 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 ```yaml 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 1. **Select Files** - Drag-and-drop or file picker - Multiple file selection - File type validation 2. **Upload to Inbox** - Stream to `/media/local/inbox` - UUID filename (prevents conflicts) - Progress tracking 3. **Extract Metadata** - FFprobe analysis (30s timeout) - Duration, dimensions, orientation - Quality calculation - Audio detection 4. **Create Database Record** - Store metadata - Set initial status - Link to user 5. **Process Video** (Future) - Generate thumbnail - Transcode formats - Move to library ### FFprobe Metadata Extraction Automatically extracts: ```typescript 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: 1. **Lock/Unlock** - Control public visibility 2. **Category Assignment** - Organize by category 3. **Public Access** - View at `/media` 4. **Reactions** - Emoji reactions from visitors ### Categories Predefined categories: - Events - Testimonials - Tutorials - Announcements - Behind the Scenes - Custom categories ## Reaction System ### Reaction Tracking ```typescript 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 ```typescript 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(), 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 ## Related Documentation - [Video Library](video-library.md) - [Upload System](upload.md) - [Public Gallery](public-gallery.md) - [Job Queue](jobs.md) - [Backend Media Module](../../backend/modules/media.md) - [Library Page](../../frontend/pages/admin/library-page.md) - [Upload Modal Component](../../frontend/components/index.md) - [FFprobe Service](../../backend/services/index.md)