Alle 44 Tabs des Plan-Dokuments (Drehbücher, Sprechtexte DE/ES/FR, Clip-Prompts, Adventskalender, Kanaltexte, Produktionsliste) als Markdown unter drehbuecher/, konzept/, ideen/ und veroeffentlichung/. Dazu Projektstand und Entscheidungen (wissen/), Schnitt-Pipeline, Shorts-Katalog, Figuren-Referenzbilder und das Kinderkanal-Team als Subagenten (.claude/agents/). Videos, Startbilder und Ton bleiben außerhalb des Git. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
72 lines
2.9 KiB
Python
72 lines
2.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Speichert das letzte Bild eines Clips als PNG (fuer Clip-Ketten: 'Ende N' = Startbild von Clip N+1).
|
|
|
|
python -I letztes_bild.py --clip f01_c08
|
|
python -I letztes_bild.py --clip f01_c08 f01_c09 t09_c03
|
|
python -I letztes_bild.py --datei D:\\irgendwo\\clip.mp4
|
|
python -I letztes_bild.py --ordner folge01 (alle Clips dieses Unterordners von clips\\)
|
|
|
|
Ausgabe: <ausgabe>\\startbilder\\<clipname>_ende.png (Standard: schnitt\\ausgabe\\startbilder)
|
|
Fuer den Projektordner startbilder\\ : --ausgabe <Pino-Kanal> (dann liegt es in <Pino-Kanal>\\startbilder)
|
|
und --ausserhalb. Ohne diese Option wird nur unter schnitt\\ geschrieben.
|
|
"""
|
|
import argparse
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) # noetig bei python -I
|
|
import pinolib as P # noqa: E402
|
|
|
|
|
|
def letztes_bild(quelle, ziel, trocken=False):
|
|
# -sseof: 1 s vor Ende lesen, -update 1: Datei bei jedem Bild ueberschreiben -> bleibt das letzte
|
|
P.ffmpeg_lauf(["-sseof", "-1", "-i", str(quelle), "-an", "-update", "1",
|
|
"-frames:v", "100000", "-pix_fmt", "rgb24", str(ziel)],
|
|
dry=trocken, was="Letztes Bild")
|
|
|
|
|
|
def main():
|
|
P.init_konsole()
|
|
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
ap.add_argument("--clip", nargs="*", default=[])
|
|
ap.add_argument("--datei", nargs="*", default=[])
|
|
ap.add_argument("--ordner", help="Unterordner von clips\\ (z. B. folge01)")
|
|
ap.add_argument("--clips-ordner", default=str(P.PROJEKT / "clips"), dest="clips_ordner")
|
|
ap.add_argument("--ausgabe", default=str(P.AUSGABE))
|
|
ap.add_argument("--ueberschreiben", action="store_true")
|
|
ap.add_argument("--ausserhalb", action="store_true")
|
|
ap.add_argument("--trocken", action="store_true")
|
|
args = ap.parse_args()
|
|
|
|
quellen = []
|
|
for t in args.clip:
|
|
for tok in P.expandiere(t):
|
|
p = P.clip_pfad(tok, args.clips_ordner)
|
|
if p is None:
|
|
raise P.Fehler(f"Clip nicht gefunden: {tok}")
|
|
quellen.append(p)
|
|
quellen += [Path(d) for d in args.datei]
|
|
if args.ordner:
|
|
quellen += sorted((Path(args.clips_ordner) / args.ordner).glob("*.mp4"))
|
|
if not quellen:
|
|
raise P.Fehler("Nichts angegeben (--clip, --datei oder --ordner).")
|
|
|
|
ziel_dir = P.pruefe_ziel(Path(args.ausgabe) / "startbilder", args.ausserhalb)
|
|
if not args.trocken:
|
|
ziel_dir.mkdir(parents=True, exist_ok=True)
|
|
for q in quellen:
|
|
z = ziel_dir / (Path(q).stem + "_ende.png")
|
|
P.schreibschutz(z, args.ueberschreiben)
|
|
letztes_bild(q, z, args.trocken)
|
|
if not args.trocken:
|
|
v = P.probe(q)["video"]
|
|
print(f"{z} ({v['width']}x{v['height']})")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except P.Fehler as e:
|
|
print(f"FEHLER: {e}")
|
|
sys.exit(1)
|