feat(downloads): a clip or an edit can be a GIF, shared as a picture
A clip and an editor range can come out as a silent GIF (long side 320/480/640, 12 fps, at most 30 s, one palette per GIF); one over 30 MB is not kept. A shared picture is served inline under the preview's sandbox, and a GIF's link card is the GIF itself up to 15 MB, the still for Facebook's crawler.
This commit is contained in:
@@ -109,6 +109,12 @@ def normalize_edit_spec(spec: dict | None) -> dict:
|
||||
spec = spec or {}
|
||||
out: dict = {}
|
||||
|
||||
# A GIF output (downloads/gif) keeps its size; one it cannot name is dropped here and refused by
|
||||
# the service, which reads the raw spec for exactly that (`gif_side`).
|
||||
side = gif_side(spec)
|
||||
if side is not None:
|
||||
out["gif"] = {"side": side}
|
||||
|
||||
# Crop applies to both a single trim and a multi-segment join.
|
||||
crop = spec.get("crop") or {}
|
||||
if crop:
|
||||
@@ -197,6 +203,21 @@ def normalize_edit_spec(spec: dict | None) -> dict:
|
||||
return out
|
||||
|
||||
|
||||
def gif_side(spec: dict | None) -> int | None:
|
||||
"""The GIF size a raw edit spec asks for, or None when it asks for none — or for one that is not
|
||||
offered (the service tells the two apart by `"gif" in spec`)."""
|
||||
# Imported here: `gif` builds its command on this module's input guard.
|
||||
from app.downloads import gif as gifmod
|
||||
|
||||
raw = (spec or {}).get("gif")
|
||||
if not isinstance(raw, dict):
|
||||
return None
|
||||
try:
|
||||
return gifmod.normalize_side(raw.get("side"))
|
||||
except ValueError: # silent-ok: an unoffered size is refused by the service (`bad_gif`)
|
||||
return None
|
||||
|
||||
|
||||
def edit_sig(spec: dict) -> str:
|
||||
"""Stable 24-char signature of a (normalized) edit spec — the per-user cache identity."""
|
||||
payload = json.dumps(normalize_edit_spec(spec), sort_keys=True, separators=(",", ":"))
|
||||
@@ -208,8 +229,8 @@ def has_crop(spec: dict) -> bool:
|
||||
|
||||
|
||||
def needs_reencode(spec: dict) -> bool:
|
||||
"""A crop always re-encodes; a trim re-encodes only when the user asked for an accurate cut."""
|
||||
return bool(spec.get("crop")) or bool(spec.get("accurate"))
|
||||
"""A crop or a GIF always re-encodes; a trim only when the user asked for an accurate cut."""
|
||||
return bool(spec.get("crop")) or bool(spec.get("gif")) or bool(spec.get("accurate"))
|
||||
|
||||
|
||||
def clip_duration(spec: dict, source_duration: int | None) -> int | None:
|
||||
|
||||
Reference in New Issue
Block a user