Core CI / core (ubuntu-latest) (push) Failing after 3m31s
Core CI / core (macos-latest) (push) Canceled after 0s
Core CI / core (windows-latest) (push) Canceled after 0s
Core CI / desktop (ubuntu-latest) (push) Successful in 5m39s
Core CI / desktop (macos-latest) (push) Canceled after 0s
Core CI / desktop (windows-latest) (push) Canceled after 0s
63 lines
3.7 KiB
Python
63 lines
3.7 KiB
Python
"""Prepare real CPU-decoded media candidates after verifying frozen labels.
|
|
|
|
No generated captions, placeholder vectors, or model calls occur in preparation.
|
|
"""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
|
|
|
|
def verify(root):
|
|
fixture_path = root / "fixture.json"
|
|
lock = json.loads((root / "labels.lock.json").read_text())
|
|
assert hashlib.sha256(fixture_path.read_bytes()).hexdigest() == lock["fixture_sha256"], "Frozen labels changed"
|
|
fixture = json.loads(fixture_path.read_text(encoding="utf-8"))
|
|
for asset in fixture["assets"]:
|
|
path = (root / asset["path"]).resolve()
|
|
assert path.is_relative_to(root.resolve()), "Asset escaped corpus root"
|
|
assert path.stat().st_size == asset["bytes"], asset["id"]
|
|
assert hashlib.sha256(path.read_bytes()).hexdigest() == asset["sha256"], asset["id"]
|
|
return fixture
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--root", type=Path, default=Path("fixtures/media"))
|
|
parser.add_argument("--ffmpeg", type=Path, required=True)
|
|
parser.add_argument("--ffprobe", type=Path, required=True)
|
|
args = parser.parse_args()
|
|
root = args.root.resolve()
|
|
fixture = verify(root)
|
|
derived = root / "derived"
|
|
derived.mkdir(exist_ok=True)
|
|
candidates = []
|
|
for asset in fixture["assets"]:
|
|
source = root / asset["path"]
|
|
if asset["kind"] == "image":
|
|
candidates.append(dict(asset=asset["id"], path=str(source), modality="image", token_budget=140))
|
|
continue
|
|
probe = json.loads(subprocess.check_output([str(args.ffprobe), "-v", "error", "-show_format", "-show_streams", "-of", "json", str(source)], text=True))
|
|
duration = float(probe["format"]["duration"])
|
|
if asset["kind"] == "video":
|
|
for stamp in range(0, int(duration) + 1, 2):
|
|
output = derived / f"{asset['id']}-frame-{stamp:04}.png"
|
|
subprocess.run([str(args.ffmpeg), "-hide_banner", "-loglevel", "error", "-hwaccel", "none", "-threads", "1", "-ss", str(stamp), "-i", str(source), "-frames:v", "1", "-vf", "scale=480:-1", "-threads", "1", "-y", str(output)], check=True)
|
|
if output.is_file():
|
|
candidates.append(dict(asset=asset["id"], path=str(output), modality="image", token_budget=70, start_s=stamp, end_s=min(stamp + 2, duration)))
|
|
if any(stream["codec_type"] == "audio" for stream in probe["streams"]):
|
|
for stamp in range(0, int(duration) + 1, 30):
|
|
output = derived / f"{asset['id']}-audio-{stamp:04}.f32"
|
|
subprocess.run([str(args.ffmpeg), "-hide_banner", "-loglevel", "error", "-hwaccel", "none", "-threads", "1", "-ss", str(stamp), "-i", str(source), "-t", "30", "-vn", "-ac", "1", "-ar", "16000", "-f", "f32le", "-threads", "1", "-y", str(output)], check=True)
|
|
if output.stat().st_size:
|
|
assert output.stat().st_size <= 480000 * 4, "Audio exceeds worker budget"
|
|
candidates.append(dict(asset=asset["id"], path=str(output), modality="audio", start_s=stamp, end_s=min(stamp + 30, duration)))
|
|
manifest = dict(fixture_sha256=hashlib.sha256((root / "fixture.json").read_bytes()).hexdigest(), ffmpeg_version=subprocess.check_output([str(args.ffmpeg), "-version"], text=True).splitlines()[0], policy="CPU-only dense reference: photos140/video70 tokens, uniform2s frames, mono16kHz30s PCM", candidates=candidates)
|
|
(derived / "candidates.json").write_text(json.dumps(manifest, indent=2), encoding="utf-8")
|
|
print(json.dumps(dict(candidates=len(candidates), images=sum(item["modality"] == "image" for item in candidates), audio=sum(item["modality"] == "audio" for item in candidates))))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|