diff --git a/backend/app/routers/library.py b/backend/app/routers/library.py index baf9ed6..6cf4470 100644 --- a/backend/app/routers/library.py +++ b/backend/app/routers/library.py @@ -842,6 +842,34 @@ async def trigger_backfill_phashes(current_user: User = Depends(get_current_user return {"status": "error", "message": str(e)} +@router.post("/maintenance/backfill-video-cache") +async def trigger_backfill_video_cache( + db: AsyncSession = Depends(get_db), + current_user: User = Depends(get_current_user), +): + """Pre-transcode every active video in the library so /playback hits + the cache on first user click instead of paying the encode cost + inline. Idempotent — pretranscode_video skips photos whose cache is + already populated and current. CPU-bound; runs on the low-priority + queue so it doesn't fight thumbnails or other user-facing tasks.""" + from app.tasks.video import pretranscode_video + result = await db.execute( + select(Photo.id, Photo.filepath).where( + Photo.media_type == 'video', + Photo.is_discarded.is_(False), + ) + ) + rows = result.all() + queued = 0 + for photo_id, filepath in rows: + try: + pretranscode_video.delay(photo_id, filepath) + queued += 1 + except Exception as e: + logger.warning(f"failed to queue pretranscode for {photo_id}: {e}") + return {"status": "queued", "count": queued} + + @router.post("/maintenance/start-watcher") async def start_file_watcher(current_user: User = Depends(get_current_user)): """Start the filesystem watcher. Uses a Redis lock so only one diff --git a/backend/app/routers/photos.py b/backend/app/routers/photos.py index 6392693..d161374 100644 --- a/backend/app/routers/photos.py +++ b/backend/app/routers/photos.py @@ -14,7 +14,6 @@ import asyncio import base64 import json import os -import subprocess import logging logger = logging.getLogger(__name__) @@ -28,6 +27,7 @@ from app.models.tags import photo_tags from app.schemas.photos import PhotoResponse, PhotoUpdate, PhotoListResponse, BulkAction from app.services.exif_writer import ExifWriteError, write_taken_at from app.services.date_guess import has_date_warning as compute_date_warning +from app.services import video as video_service from app.dependencies import ( get_current_user, get_current_user_media, get_user_photo, get_user_or_shared_heap, get_user_or_shared_folder, @@ -854,23 +854,6 @@ async def get_original( ) -# Cached H.264/MP4 transcodes for /playback. iPhone shoots HEVC Main 10 by -# default and Chrome/Firefox can't decode 10-bit HEVC reliably, so anything -# that isn't already h264 in a web-safe container gets transcoded once and -# served from this directory thereafter. -_VIDEO_CACHE_DIR = Path('/data/video-cache') -_VIDEO_CACHE_DIR.mkdir(parents=True, exist_ok=True) - -# Video codecs that play in