359 lines
8.7 KiB
Markdown

# 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<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
## 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)