Skip to content

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 - Admin video management
  2. Upload System - Video upload with metadata extraction
  3. Public Gallery - Public video sharing
  4. 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
  • 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

  1. Select Files
  2. Drag-and-drop or file picker
  3. Multiple file selection
  4. File type validation

  5. Upload to Inbox

  6. Stream to /media/local/inbox
  7. UUID filename (prevents conflicts)
  8. Progress tracking

  9. Extract Metadata

  10. FFprobe analysis (30s timeout)
  11. Duration, dimensions, orientation
  12. Quality calculation
  13. Audio detection

  14. Create Database Record

  15. Store metadata
  16. Set initial status
  17. Link to user

  18. Process Video (Future)

  19. Generate thumbnail
  20. Transcode formats
  21. 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

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

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
  • Lazy loading
  • Thumbnail caching (future)
  • Paginated results
  • Infinite scroll