This repository has been archived on 2026-05-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
imagerepo/app/tasks/thumbnail.py
T

154 lines
4.5 KiB
Python

# app/tasks/thumbnail.py
"""
Thumbnail generation tasks.
Handles generating thumbnails for images and videos,
both individually and in bulk.
"""
import logging
from datetime import datetime
from app.celery_app import celery
from app import db
from app.models import ImageRecord, ImportTask
log = logging.getLogger('celery.tasks.thumbnail')
@celery.task(bind=True, name='app.tasks.thumbnail.generate_thumbnail_task',
max_retries=2, default_retry_delay=30)
def generate_thumbnail_task(self, image_id: int, overwrite: bool = False):
"""
Generate thumbnail for a single image/video.
Args:
image_id: ID of the ImageRecord
overwrite: Whether to overwrite existing thumbnail
Returns:
dict with status and thumbnail path
"""
from app.utils.image_importer import generate_thumbnail, generate_video_thumbnail_mirrored
record = ImageRecord.query.get(image_id)
if not record:
log.warning(f"Image {image_id} not found")
return {'error': 'Image not found'}
try:
filepath = record.filepath
if not filepath:
return {'error': 'No filepath'}
# Check if thumbnail already exists and we're not overwriting
if record.thumb_path and not overwrite:
import os
if os.path.exists(record.thumb_path):
return {'status': 'exists', 'thumb_path': record.thumb_path}
log.debug(f"Generating thumbnail for: {filepath}")
is_video = filepath.lower().endswith(('.mp4', '.mov', '.webm', '.avi', '.mkv'))
if is_video:
thumb_path = generate_video_thumbnail_mirrored(filepath)
else:
thumb_path = generate_thumbnail(filepath, overwrite=overwrite)
if thumb_path:
record.thumb_path = thumb_path
db.session.commit()
log.info(f"Generated thumbnail: {thumb_path}")
return {'status': 'success', 'thumb_path': thumb_path}
else:
return {'status': 'failed', 'error': 'Thumbnail generation returned None'}
except Exception as e:
db.session.rollback()
log.error(f"Thumbnail generation failed for {image_id}: {e}", exc_info=True)
raise self.retry(exc=e)
@celery.task(name='app.tasks.thumbnail.regenerate_all_thumbnails')
def regenerate_all_thumbnails(batch_size: int = 100, overwrite: bool = True):
"""
Queue thumbnail regeneration for all images.
Processes images in batches to avoid memory issues.
Args:
batch_size: Number of images to process per batch
overwrite: Whether to overwrite existing thumbnails
Returns:
dict with count of queued tasks
"""
log.info("Starting bulk thumbnail regeneration")
count = 0
last_id = 0
while True:
records = ImageRecord.query.filter(
ImageRecord.id > last_id,
ImageRecord.filepath.isnot(None)
).order_by(ImageRecord.id.asc()).limit(batch_size).all()
if not records:
break
for record in records:
generate_thumbnail_task.delay(record.id, overwrite=overwrite)
count += 1
last_id = records[-1].id
log.info(f"Queued {count} thumbnail tasks so far...")
log.info(f"Thumbnail regeneration complete: queued {count} tasks")
return {'queued': count}
@celery.task(name='app.tasks.thumbnail.regenerate_missing_thumbnails')
def regenerate_missing_thumbnails(batch_size: int = 100):
"""
Generate thumbnails only for images that don't have them.
Args:
batch_size: Number of images to process per batch
Returns:
dict with count of queued tasks
"""
import os
log.info("Checking for missing thumbnails")
count = 0
last_id = 0
while True:
records = ImageRecord.query.filter(
ImageRecord.id > last_id,
ImageRecord.filepath.isnot(None)
).order_by(ImageRecord.id.asc()).limit(batch_size).all()
if not records:
break
for record in records:
# Check if thumbnail is missing
needs_thumb = False
if not record.thumb_path:
needs_thumb = True
elif not os.path.exists(record.thumb_path):
needs_thumb = True
if needs_thumb:
generate_thumbnail_task.delay(record.id, overwrite=False)
count += 1
last_id = records[-1].id
log.info(f"Missing thumbnail check complete: queued {count} tasks")
return {'queued': count}