359 lines
8.7 KiB
Markdown
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)
|