Files
ECC/skills/taste-application/scripts/tasteforge/media/common.py
T
928c1dea72 feat(tasteforge): package reusable workflows and preserve native edits (#3033)
* feat: bundle standalone taste distillation and application workflows

* docs: fix imported taste skill markdown lint

* docs: align Turkish agent catalog with taste skills

* refactor: make ECC the canonical reusable video engine

* fix: preserve video duration when applying image overlays

* fix: preserve background colors in image compositing

* fix: report best-effort duration targets and shortfalls

* feat: ship verified Fusion presets with compatibility provenance

* feat(tasteforge): preserve native edits in application bundles

* feat(tasteforge): compile local preservation without hosted input

* fix: update js-yaml to patched 4.3.2

* test: report bounded Stop wrapper failure diagnostics

* fix(tasteforge): fail closed on unsafe output names, missing overlays and cadence

- cli: default report and spec paths are derived from pack name and profile
  genre; require the manifest's name pattern before using either as a
  filename part so a traversal string cannot write outside cwd/out.
- apply_local: a pack without cadence.json, or with no measured shots and
  no explicit mean_shot, raises instead of silently planning 1.0s shots and
  reporting a measured cadence.
- legacy apply: a missing overlay aborts before any paid upload; forge()
  would have rejected it after every take was generated.
- requirements-live: pin fal-client>=0.13.0, the first release whose
  subscribe() accepts client_timeout.

Addresses the five P1 findings from the independent review of #3033.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015fxHRsydPqEcYngGbqkgt1

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 15:31:36 +01:00

39 lines
1.3 KiB
Python

"""Validation and transactional output for local media tools."""
import math
import os
import tempfile
from contextlib import contextmanager
from pathlib import Path
def geometry(width, height, fps, duration=1):
for value in (width, height, fps, duration):
if isinstance(value, bool) or not math.isfinite(value) or value <= 0:
raise ValueError("Geometry, fps and duration must be finite and positive")
if width != int(width) or height != int(height) or width % 2 or height % 2:
raise ValueError("Width and height must be even integers")
@contextmanager
def output_file(destination, overwrite=False):
destination = Path(destination)
if os.path.lexists(destination) and not overwrite:
raise FileExistsError(destination)
destination.parent.mkdir(parents=True, exist_ok=True)
fd, filename = tempfile.mkstemp(
prefix=".media-", suffix=destination.suffix, dir=destination.parent
)
os.close(fd)
temporary = Path(filename)
try:
yield temporary
if temporary.stat().st_size == 0:
raise RuntimeError("Media command produced an empty output")
if overwrite:
os.replace(temporary, destination)
else:
os.link(temporary, destination)
finally:
temporary.unlink(missing_ok=True)