"""Media conversion into web-friendly formats.""" import shutil import subprocess import uuid from pathlib import Path from PIL import Image, ImageSequence IMAGE_EXTS = {".png", ".jpg", ".jpeg", ".webp", ".bmp", ".gif", ".tga", ".avif"} AUDIO_EXTS = {".ogg", ".mp3", ".wav", ".opus", ".flac", ".m4a", ".oga"} VIDEO_EXTS = {".webm", ".ogv", ".mp4", ".mkv", ".avi", ".mpg", ".mpeg", ".m4v"} FONT_EXTS = {".ttf", ".otf", ".woff", ".woff2", ".ttc"} WEBP_MAX = 16383 def kind_of(path: Path) -> str | None: ext = path.suffix.lower() if ext in IMAGE_EXTS: return "image" if ext in AUDIO_EXTS: return "audio" if ext in VIDEO_EXTS: return "video" if ext in FONT_EXTS: return "font" return None def target_ext(path: Path) -> str: kind = kind_of(path) return {"image": ".webp", "audio": ".ogg", "video": ".webm"}.get(kind, path.suffix.lower()) def convert(src: Path, out_dir: Path) -> tuple[Path, str]: """Convert one file into out_dir. Returns (output file, extension used for the blob).""" kind = kind_of(src) tmp = str(out_dir / uuid.uuid4().hex) if kind == "image": return _image(src, Path(tmp)) if kind == "audio": return _ffmpeg(src, Path(tmp + ".ogg"), ["-vn", "-c:a", "libopus", "-b:a", "112k", "-vbr", "on"]), ".ogg" if kind == "video": if src.suffix.lower() == ".webm": out = Path(tmp + ".webm") shutil.copyfile(src, out) return out, ".webm" return _ffmpeg(src, Path(tmp + ".webm"), ["-c:v", "libvpx-vp9", "-crf", "34", "-b:v", "0", "-deadline", "realtime", "-cpu-used", "8", "-row-mt", "1", "-c:a", "libopus", "-b:a", "112k"] ), ".webm" out = Path(tmp + src.suffix.lower()) shutil.copyfile(src, out) return out, src.suffix.lower() def _image(src: Path, tmp: Path) -> tuple[Path, str]: with Image.open(src) as img: animated = getattr(img, "is_animated", False) if max(img.size) > WEBP_MAX: out = tmp.with_suffix(".png") img.save(out, "PNG", optimize=False) return out, ".png" out = tmp.with_suffix(".webp") if animated: frames = [f.convert("RGBA") for f in ImageSequence.Iterator(img)] frames[0].save(out, "WEBP", save_all=True, append_images=frames[1:], quality=85, duration=img.info.get("duration", 100), loop=0) return out, ".webp" has_alpha = img.mode in ("RGBA", "LA", "PA") or (img.mode == "P" and "transparency" in img.info) img = img.convert("RGBA" if has_alpha else "RGB") img.save(out, "WEBP", quality=88, method=4, alpha_quality=95) return out, ".webp" def _ffmpeg(src: Path, out: Path, args: list[str]) -> Path: proc = subprocess.run( ["ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error", "-y", "-i", str(src), *args, str(out)], capture_output=True, text=True, timeout=3600, ) if proc.returncode != 0: raise RuntimeError(f"ffmpeg: {proc.stderr.strip()[-300:]}") return out