ffmpeg-skill 2.6.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/SKILL.md +7 -7
- package/docs/contract.md +49 -8
- package/package.json +1 -1
- package/references/gotchas.md +27 -5
- package/references/scripts.md +121 -13
- package/scripts/_common/__init__.py +6 -6
- package/scripts/_common/asr.py +747 -94
- package/scripts/_common/decision.py +13 -0
- package/scripts/_common/probe.py +92 -0
- package/scripts/_common/runner.py +35 -12
- package/scripts/_contract.py +67 -12
- package/scripts/batch.py +28 -11
- package/scripts/caption.py +22 -6
- package/scripts/cut.py +1311 -53
- package/scripts/silence.py +21 -6
package/scripts/silence.py
CHANGED
|
@@ -20,7 +20,8 @@ from typing import List, Tuple
|
|
|
20
20
|
# `detect` moved into _common/probe.py in 1.16.0 so metadata.py --auto-chapters can measure the
|
|
21
21
|
# same silences without importing this tool; the body is unchanged and the name still lives here.
|
|
22
22
|
from _common import (filler_spans, FILLER_WORDS, FILLER_AMBIGUOUS, FILLER_DISCOURSE_MARKERS,
|
|
23
|
-
FILLER_PAD,
|
|
23
|
+
FILLER_PAD, read_text_or_die)
|
|
24
|
+
from _common import asr as _asr
|
|
24
25
|
from _common import detect_silences as detect, STATE, video_args, add_common, apply_common, audio_codec_for, cfr_args, default_output, die, emit, ffmpeg_base, info, is_audio_output, print_json, probe, run, X264_PRESETS, measured_level_dbfs, fmt_secs
|
|
25
26
|
|
|
26
27
|
|
|
@@ -99,7 +100,7 @@ def resolve_filler(args, meta):
|
|
|
99
100
|
die("--filler needs word timings: pass --words transcript.json (a whisper JSON with word "
|
|
100
101
|
"timestamps) or --transcribe. There is no way to find a filler word without them -- "
|
|
101
102
|
"cutting the short quiet blips instead would remove real speech.", kind="input")
|
|
102
|
-
source, engine, raw = None, None, None
|
|
103
|
+
source, engine, raw, transcription = None, None, None, {}
|
|
103
104
|
if args.words:
|
|
104
105
|
try:
|
|
105
106
|
raw = json.loads(read_text_or_die(args.words, "--words"))
|
|
@@ -118,14 +119,20 @@ def resolve_filler(args, meta):
|
|
|
118
119
|
'{"words": [{"word": ..., "start": ..., "end": ...}]} (or the same inside '
|
|
119
120
|
'"segments").', kind="input")
|
|
120
121
|
else:
|
|
121
|
-
# No pre-check for an installed engine here:
|
|
122
|
+
# No pre-check for an installed engine here: transcribe_words_result() probes for one and
|
|
122
123
|
# raises the same die_no_engine() refusal when there is none. Two places deciding "is
|
|
123
124
|
# whisper here" is two places to disagree.
|
|
124
125
|
# The engine is driven with ITS word-timestamp option (whisper.cpp --output-json-full,
|
|
125
126
|
# faster-whisper word_timestamps=True, openai-whisper --word_timestamps True). An SRT
|
|
126
127
|
# cannot answer this question: a cue has a start and an end, a word does not.
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
# The call's own result, not module state: engine, routing and notes belong to this run.
|
|
129
|
+
asr_run = _asr.transcribe_words_result(args.input, args.filler_lang if args.filler_lang != "auto" else None,
|
|
130
|
+
engine=args.engine)
|
|
131
|
+
words, engine = asr_run.words, asr_run.engine
|
|
132
|
+
transcription = dict(asr_run.facts)
|
|
133
|
+
args._asr_notes = list(asr_run.notes)
|
|
134
|
+
family = "parakeet" if engine in _asr.PARAKEET_ENGINES else "whisper"
|
|
135
|
+
source = f"{family}:{engine}" if engine else "whisper"
|
|
129
136
|
if not words:
|
|
130
137
|
die(f"{engine or 'the local engine'} ran but produced no word-level timings, so there "
|
|
131
138
|
"is nothing for --filler to cut on. Some builds do not support word timestamps. "
|
|
@@ -168,6 +175,7 @@ def resolve_filler(args, meta):
|
|
|
168
175
|
"disfluency -- this will cut real sentences.")
|
|
169
176
|
block = {
|
|
170
177
|
"lang": lang, "source": source, "engine": engine,
|
|
178
|
+
**({"transcription": transcription} if engine and transcription else {}),
|
|
171
179
|
"words": sorted(wordlist), "removed": [dict(s) for s in spans],
|
|
172
180
|
"removed_count": len(spans),
|
|
173
181
|
"removed_seconds": round(sum(s["end"] - s["start"] for s in spans), 3),
|
|
@@ -235,8 +243,11 @@ def main() -> int:
|
|
|
235
243
|
fil.add_argument("--words", metavar="FILE",
|
|
236
244
|
help="a whisper JSON with word-level timings, for --filler")
|
|
237
245
|
fil.add_argument("--transcribe", action="store_true",
|
|
238
|
-
help="produce the word timings with a local whisper (never required; the same "
|
|
246
|
+
help="produce the word timings with a local speech engine, Parakeet or whisper (never required; the same "
|
|
239
247
|
"bridge caption.py uses)")
|
|
248
|
+
fil.add_argument("--engine", choices=_asr.ENGINE_CHOICES, default=None,
|
|
249
|
+
help="speech engine for --transcribe (default: $FFMPEG_SKILL_ASR_ENGINE, else auto = Parakeet "
|
|
250
|
+
"for English speech when installed, else whisper)")
|
|
240
251
|
fil.add_argument("--filler-list", action="store_true",
|
|
241
252
|
help="report what --filler would remove and write nothing")
|
|
242
253
|
fil.add_argument("--max-cuts", type=int, default=400,
|
|
@@ -311,6 +322,10 @@ def main() -> int:
|
|
|
311
322
|
f"({filler_info['lang']}, list {filler_info['list']})")
|
|
312
323
|
for warning in filler_info.get("warnings") or []:
|
|
313
324
|
info("warning: " + warning)
|
|
325
|
+
if getattr(args, "_asr_notes", None):
|
|
326
|
+
# an English-only transcript made on an assumption (asr.assumed_english_note): the
|
|
327
|
+
# bridge already logged the warning; the result document carries it at the top level
|
|
328
|
+
summary["notes"] = list(args._asr_notes)
|
|
314
329
|
if len(keeps) > args.max_cuts:
|
|
315
330
|
die(f"{len(keeps)} keep ranges is above --max-cuts {args.max_cuts}: the filter graph grows "
|
|
316
331
|
"with every range and a graph this size is slow and fragile. Raise --max-cuts if you "
|