118 lines
3.3 KiB
Python
118 lines
3.3 KiB
Python
"""
|
|
Asset endpoints - media file registration and metadata.
|
|
"""
|
|
|
|
from typing import Optional
|
|
from uuid import UUID
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
|
|
from api.deps import get_asset
|
|
from api.schemas import AssetCreate, AssetResponse, AssetUpdate
|
|
from core.storage import BUCKET_IN, list_objects
|
|
|
|
router = APIRouter(prefix="/assets", tags=["assets"])
|
|
|
|
# Supported media extensions
|
|
VIDEO_EXTS = {".mp4", ".mkv", ".avi", ".mov", ".webm", ".flv", ".wmv", ".m4v"}
|
|
AUDIO_EXTS = {".mp3", ".wav", ".flac", ".aac", ".ogg", ".m4a"}
|
|
MEDIA_EXTS = VIDEO_EXTS | AUDIO_EXTS
|
|
|
|
|
|
@router.post("/", response_model=AssetResponse, status_code=201)
|
|
def create_asset(data: AssetCreate):
|
|
"""Register a media file as an asset."""
|
|
from mpr.media_assets.models import MediaAsset
|
|
|
|
asset = MediaAsset.objects.create(
|
|
filename=data.filename or data.file_path.split("/")[-1],
|
|
file_path=data.file_path,
|
|
file_size=data.file_size,
|
|
)
|
|
return asset
|
|
|
|
|
|
@router.get("/", response_model=list[AssetResponse])
|
|
def list_assets(
|
|
status: Optional[str] = Query(None, description="Filter by status"),
|
|
limit: int = Query(50, ge=1, le=100),
|
|
offset: int = Query(0, ge=0),
|
|
):
|
|
"""List assets with optional filtering."""
|
|
from mpr.media_assets.models import MediaAsset
|
|
|
|
qs = MediaAsset.objects.all()
|
|
if status:
|
|
qs = qs.filter(status=status)
|
|
return list(qs[offset : offset + limit])
|
|
|
|
|
|
@router.get("/{asset_id}", response_model=AssetResponse)
|
|
def get_asset_detail(asset_id: UUID, asset=Depends(get_asset)):
|
|
"""Get asset details."""
|
|
return asset
|
|
|
|
|
|
@router.patch("/{asset_id}", response_model=AssetResponse)
|
|
def update_asset(asset_id: UUID, data: AssetUpdate, asset=Depends(get_asset)):
|
|
"""Update asset metadata (comments, tags)."""
|
|
update_fields = []
|
|
|
|
if data.comments is not None:
|
|
asset.comments = data.comments
|
|
update_fields.append("comments")
|
|
|
|
if data.tags is not None:
|
|
asset.tags = data.tags
|
|
update_fields.append("tags")
|
|
|
|
if update_fields:
|
|
asset.save(update_fields=update_fields)
|
|
|
|
return asset
|
|
|
|
|
|
@router.delete("/{asset_id}", status_code=204)
|
|
def delete_asset(asset_id: UUID, asset=Depends(get_asset)):
|
|
"""Delete an asset."""
|
|
asset.delete()
|
|
|
|
|
|
@router.post("/scan", response_model=dict)
|
|
def scan_media_folder():
|
|
"""
|
|
Scan the S3 media-in bucket for new video/audio files and register them as assets.
|
|
"""
|
|
from mpr.media_assets.models import MediaAsset
|
|
|
|
# List objects from S3 bucket
|
|
objects = list_objects(BUCKET_IN, extensions=MEDIA_EXTS)
|
|
|
|
# Get existing filenames to avoid duplicates
|
|
existing_filenames = set(MediaAsset.objects.values_list("filename", flat=True))
|
|
|
|
registered_files = []
|
|
skipped_files = []
|
|
|
|
for obj in objects:
|
|
if obj["filename"] in existing_filenames:
|
|
skipped_files.append(obj["filename"])
|
|
continue
|
|
|
|
try:
|
|
MediaAsset.objects.create(
|
|
filename=obj["filename"],
|
|
file_path=obj["key"],
|
|
file_size=obj["size"],
|
|
)
|
|
registered_files.append(obj["filename"])
|
|
except Exception as e:
|
|
print(f"Error registering {obj['filename']}: {e}")
|
|
|
|
return {
|
|
"found": len(objects),
|
|
"registered": len(registered_files),
|
|
"skipped": len(skipped_files),
|
|
"files": registered_files,
|
|
}
|