feat(thumbs): proxy Nextcloud previews instead of duplicating the cache
mule-image was generating and storing three WebP sizes per photo in /data/thumbs while Nextcloud already keeps its own previews for the same source files. Frontend thumbnail requests now proxy NC's /index.php/core/preview keyed by the photo's Nextcloud fileid, authenticated with the owner's encrypted app password. - new column photos.nextcloud_fileid (alembic 0018) plus an index - get_preview_async + fetch_fileid helpers in nextcloud_dav.py - thumb route proxies NC primary, falls back to /data/thumbs (legacy rows / NC unreachable) so a single-file revert restores the old path - extract_metadata caches the fileid on first run for new photos - generate_thumbnails now writes only medium since the vision worker still loads it from disk; small + large drop out of the worker path - backend/scripts/backfill_nextcloud_fileid.py for one-shot population of existing rows: docker exec mulita-backend python -m scripts.backfill_nextcloud_fileid X-Mule-Thumb-Source response header marks each request 'nextcloud' or 'disk' for observability while the rollout settles.
This commit is contained in:
@@ -47,6 +47,14 @@ THUMB_SIZES = {
|
||||
'large': settings.thumbnails.large
|
||||
}
|
||||
|
||||
# Sizes the worker actually writes to /data/thumbs. We used to write all
|
||||
# three, but the API now proxies Nextcloud's /core/preview for `small`
|
||||
# and `large` — only `medium` survives on disk because the vision
|
||||
# pipeline (app.tasks.vision) still loads it from there. When vision
|
||||
# moves to NC previews too, this set drops to empty and the file
|
||||
# pipeline can be deleted entirely.
|
||||
WORKER_THUMB_SIZES = {'medium'}
|
||||
|
||||
def get_thumb_path(photo_id: str, size: str, user_id: str = None) -> str:
|
||||
"""Get the path for a thumbnail file.
|
||||
|
||||
@@ -363,14 +371,18 @@ async def _generate_thumbnails_async(photo_id: str, task):
|
||||
logger.warning(f"phash failed for {photo_id}: {e}")
|
||||
photo.phash = None
|
||||
|
||||
# Generate thumbnails for each size
|
||||
# Generate only the sizes the worker still owns on disk
|
||||
# (see WORKER_THUMB_SIZES above). The API serves the rest
|
||||
# via Nextcloud's preview endpoint.
|
||||
for size_name, size_value in THUMB_SIZES.items():
|
||||
if size_name not in WORKER_THUMB_SIZES:
|
||||
continue
|
||||
thumb_path = get_thumb_path(photo_id, size_name, photo.user_id)
|
||||
generate_thumbnail(image, size_value, thumb_path)
|
||||
|
||||
|
||||
# Update database with thumbnail path
|
||||
setattr(photo, f'thumb_{size_name}', thumb_path)
|
||||
|
||||
|
||||
# Update progress
|
||||
task.update_state(
|
||||
state='PROGRESS',
|
||||
|
||||
Reference in New Issue
Block a user