Tonne of debugging - getting ready for the production builds
This commit is contained in:
358
mkdocs/docs/v2/features/media/index.md
Normal file
358
mkdocs/docs/v2/features/media/index.md
Normal file
@@ -0,0 +1,358 @@
|
||||
# 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)
|
||||
1550
mkdocs/docs/v2/features/media/jobs.md
Normal file
1550
mkdocs/docs/v2/features/media/jobs.md
Normal file
File diff suppressed because it is too large
Load Diff
1667
mkdocs/docs/v2/features/media/public-gallery.md
Normal file
1667
mkdocs/docs/v2/features/media/public-gallery.md
Normal file
File diff suppressed because it is too large
Load Diff
1340
mkdocs/docs/v2/features/media/upload.md
Normal file
1340
mkdocs/docs/v2/features/media/upload.md
Normal file
File diff suppressed because it is too large
Load Diff
1718
mkdocs/docs/v2/features/media/video-library.md
Normal file
1718
mkdocs/docs/v2/features/media/video-library.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user