I cinque video «Come si gioca» dentro il gioco: pulsante «Guarda il video» nell'aiuto di briscola, tressette, scopa, bocce e biliardo, player a tutto schermo con l'audio (il gioco e la musica si fermano, Esc torna al tavolo). video/pubblica.py li taglia dall'arrivo nel posto, li comprime per il web (~4,5 MB l'uno) e fa la copertina. Il favicon e i video copiati anche nell'immagine Docker (il Regolino online dava 404); test che ogni cartella di assets usata dal gioco sia nel Dockerfile

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1dxvtsZx8NE9fwjXUNDBr
This commit is contained in:
2026-10-26 22:37:19 +01:00
co-authored by Claude Fable 5.1
parent add5a5fe77
commit 22be1c306e
17 changed files with 82 additions and 4 deletions
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env python3
"""Porta i video «Come si gioca» nel gioco (assets/video/, story.tutorials): da quando si arriva nel posto del gioco
(la prima battuta del narratore), più piccoli e compressi per il web, con una copertina.
python3 video/pubblica.py briscola tressette scopa bocce biliardo
Prima la ripresa e il montaggio (video/SCALETTA-CARTE.md): legge video/out/tutorial-<gioco>.mp4 e il cues.json della
ripresa. Il titolo del gioco all'inizio della ripresa non serve, e col ritaglio dei tavoli verrebbe tagliato ai lati.
"""
import json
import subprocess
import sys
from pathlib import Path
VIDEO = Path(__file__).resolve().parent
GIOCO = VIDEO.parent
RIPRESE = Path.home() / "Workspaces/qemu/qemu-iso-lab/artifacts/tour/out"
def durata(f: Path) -> float:
return float(subprocess.run(["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "csv=p=0", str(f)],
capture_output=True, text=True, check=True).stdout)
for gioco in sys.argv[1:]:
ripresa = RIPRESE / f"tutorial-{gioco}"
cues = json.loads((ripresa / "cues.json").read_text())["cues"]
# Lo stesso scarto di monta.py: la ripresa parte un po' prima del copione.
scarto = max(0.0, durata(ripresa / "raw.mkv") - (cues[-1]["end"] + 1.2))
inizio = max(0.0, cues[0]["start"] + scarto - 0.4)
dentro, fuori = VIDEO / "out" / f"tutorial-{gioco}.mp4", GIOCO / "assets" / "video" / f"{gioco}.mp4"
fuori.parent.mkdir(exist_ok=True)
subprocess.run(["ffmpeg", "-loglevel", "error", "-y", "-ss", f"{inizio:.2f}", "-i", str(dentro),
"-vf", "scale=trunc(iw*0.75/2)*2:-2", "-c:v", "libx264", "-preset", "slow", "-crf", "27", "-pix_fmt", "yuv420p",
"-af", "afade=t=in:d=0.4", "-c:a", "aac", "-b:a", "96k", "-movflags", "+faststart", str(fuori)], check=True)
# La copertina: un momento al tavolo, a un terzo del video.
subprocess.run(["ffmpeg", "-loglevel", "error", "-y", "-ss", f"{durata(fuori) / 3:.1f}", "-i", str(fuori), "-frames:v", "1",
"-q:v", "4", str(fuori.with_suffix(".jpg"))], check=True)
for f in (fuori, fuori.with_suffix(".jpg")):
f.chmod(0o644)
print(f"-> {fuori} ({durata(fuori):.0f} s, {fuori.stat().st_size / 1e6:.1f} MB), da {inizio:.1f} s")