8.7 KiB

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

    • 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:

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