Finished with Phase 2.6
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
"""ffmpeg thumbnail generation for gallery media (videos + gifs).
|
||||
|
||||
Grid previews use a small cached JPEG extracted by ffmpeg; the browser only
|
||||
receives the real file when the full-screen modal opens.
|
||||
"""
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
FFMPEG = shutil.which('ffmpeg')
|
||||
|
||||
MAX_WIDTH = 480
|
||||
|
||||
|
||||
def thumbnail_stored_path(file_index):
|
||||
"""Relative stored path of the cached thumbnail for an asset file."""
|
||||
return f'thumbnails/{file_index.uuid}.jpg'
|
||||
|
||||
|
||||
def thumbnail_ready(file_index):
|
||||
return (Path(settings.MEDIA_ROOT) / thumbnail_stored_path(file_index)).is_file()
|
||||
|
||||
|
||||
def generate_thumbnail(file_index):
|
||||
"""Generate (and cache) a JPEG thumbnail for a video/gif FileIndex.
|
||||
|
||||
Returns the relative stored path, or None when ffmpeg is unavailable or
|
||||
extraction fails. Idempotent — a cached thumbnail is reused.
|
||||
"""
|
||||
if not FFMPEG or not file_index.stored_path:
|
||||
return None
|
||||
src = (Path(settings.MEDIA_ROOT) / file_index.stored_path).resolve()
|
||||
if not src.is_file():
|
||||
return None
|
||||
|
||||
out_rel = thumbnail_stored_path(file_index)
|
||||
out = (Path(settings.MEDIA_ROOT) / out_rel).resolve()
|
||||
if out.is_file():
|
||||
return out_rel
|
||||
|
||||
out.parent.mkdir(parents=True, exist_ok=True)
|
||||
tmp = out.with_suffix('.tmp.jpg')
|
||||
cmd = [
|
||||
FFMPEG, '-y', '-i', str(src),
|
||||
'-frames:v', '1',
|
||||
'-vf', f"scale='min({MAX_WIDTH},iw)':-2",
|
||||
'-q:v', '4',
|
||||
str(tmp),
|
||||
]
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, timeout=30)
|
||||
except (subprocess.TimeoutExpired, OSError):
|
||||
return None
|
||||
if result.returncode != 0 or not tmp.is_file():
|
||||
return None
|
||||
tmp.replace(out)
|
||||
return out_rel
|
||||
Reference in New Issue
Block a user