4.6 KiB
4.6 KiB
Media Storage Architecture
Overview
MPR stores media file paths relative to the media root to ensure portability between local development and cloud deployments (AWS S3, etc.).
Storage Strategy
File Path Storage
- Database: Stores only the relative path (e.g.,
videos/sample.mp4) - Media Root: Configurable base directory via
MEDIA_ROOTenv var - Serving: Base URL configurable via
MEDIA_BASE_URLenv var
Why Relative Paths?
- Portability: Same database works locally and in cloud
- Flexibility: Easy to switch between storage backends
- Simplicity: No need to update paths when migrating
Local Development
Configuration
MEDIA_ROOT=/app/media
File Structure
/app/media/
├── video1.mp4
├── video2.mp4
└── subfolder/
└── video3.mp4
Database Storage
filename: video1.mp4
file_path: video1.mp4
filename: video3.mp4
file_path: subfolder/video3.mp4
URL Serving
- Nginx serves via
location /media { alias /app/media; } - Frontend accesses:
http://mpr.local.ar/media/video1.mp4 - Video player:
<video src="/media/video1.mp4" />
AWS/Cloud Deployment
S3 Configuration
MEDIA_ROOT=s3://my-bucket/media/
MEDIA_BASE_URL=https://my-bucket.s3.amazonaws.com/media/
S3 Structure
s3://my-bucket/media/
├── video1.mp4
├── video2.mp4
└── subfolder/
└── video3.mp4
Database Storage (Same!)
filename: video1.mp4
file_path: video1.mp4
filename: video3.mp4
file_path: subfolder/video3.mp4
URL Serving
- Frontend prepends
MEDIA_BASE_URL:https://my-bucket.s3.amazonaws.com/media/video1.mp4 - Video player:
<video src="https://my-bucket.s3.amazonaws.com/media/video1.mp4" />
API Endpoints
Scan Media Folder
POST /api/assets/scan
Behavior:
- Recursively scans
MEDIA_ROOTdirectory - Finds all video/audio files (mp4, mkv, avi, mov, mp3, wav, etc.)
- Stores paths relative to MEDIA_ROOT
- Skips already-registered files (by filename)
- Returns summary:
{ found, registered, skipped, files }
Example:
curl -X POST http://mpr.local.ar/api/assets/scan
{
"found": 15,
"registered": 12,
"skipped": 3,
"files": ["video1.mp4", "video2.mp4", ...]
}
Create Asset
POST /api/assets/
Content-Type: application/json
{
"file_path": "/app/media/video.mp4",
"filename": "video.mp4"
}
Behavior:
- Validates file exists
- Converts absolute path to relative (relative to
MEDIA_ROOT) - Stores relative path in database
Migration Guide
Moving from Local to S3
-
Upload files to S3:
aws s3 sync /app/media/ s3://my-bucket/media/ -
Update environment variables:
MEDIA_ROOT=s3://my-bucket/media/ MEDIA_BASE_URL=https://my-bucket.s3.amazonaws.com/media/ -
Database paths remain unchanged (already relative)
-
Update frontend to use
MEDIA_BASE_URLfrom config
Moving from S3 to Local
-
Download files from S3:
aws s3 sync s3://my-bucket/media/ /app/media/ -
Update environment variables:
MEDIA_ROOT=/app/media # Remove MEDIA_BASE_URL or set to /media/ -
Database paths remain unchanged (already relative)
Implementation Details
Backend (FastAPI)
File path normalization (api/routes/assets.py):
import os
from pathlib import Path
media_root = Path(os.environ.get("MEDIA_ROOT", "/app/media"))
# Convert absolute to relative
file_path = Path("/app/media/subfolder/video.mp4")
rel_path = str(file_path.relative_to(media_root))
# Result: "subfolder/video.mp4"
Frontend (React)
Current implementation:
<video src={`/media/${asset.file_path}`} />
Future cloud implementation:
const MEDIA_BASE_URL = import.meta.env.VITE_MEDIA_BASE_URL || '/media/';
<video src={`${MEDIA_BASE_URL}${asset.file_path}`} />
Supported File Types
Video formats:
.mp4,.mkv,.avi,.mov,.webm,.flv,.wmv,.m4v
Audio formats:
.mp3,.wav,.flac,.aac,.ogg,.m4a
Best Practices
- Always use relative paths when storing file references
- Use environment variables for media root and base URL
- Validate file existence before creating assets
- Scan periodically to discover new files
- Use presigned URLs for S3 private buckets (TODO: implement)
Future Enhancements
- S3 presigned URL generation for private buckets
- CloudFront CDN integration
- Multi-region S3 replication
- Automatic metadata extraction on upload
- Thumbnail generation and storage