ffmpeg-skill 3.1.0 → 3.1.2
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/SKILL.md +163 -170
- package/docs/contract.md +10 -4
- package/package.json +6 -1
- package/references/flags.md +16 -0
- package/references/gotchas.md +29 -2
- package/references/report.md +43 -0
- package/references/requests.md +95 -0
- package/references/timestamps.md +3 -0
- package/references/workflow.md +53 -0
- package/scripts/_common/__init__.py +2 -2
- package/scripts/_common/asr.py +10 -4
- package/scripts/_common/emoji.py +49 -3
- package/scripts/_common/runner.py +37 -2
- package/scripts/_contract.py +5 -1
- package/scripts/caption.py +44 -6
- package/scripts/cut.py +13 -2
- package/scripts/graphics.py +9 -3
- package/scripts/look.py +10 -0
- package/scripts/silence.py +5 -1
package/scripts/graphics.py
CHANGED
|
@@ -32,7 +32,7 @@ import sys
|
|
|
32
32
|
from typing import List, Optional, Tuple
|
|
33
33
|
|
|
34
34
|
from _platforms import PLATFORMS, PLATFORM_CHOICES, safe_margins_px, resolve as resolve_platform
|
|
35
|
-
from _common import (aac_args, add_common, brand_caption_style, script_font_for_text, apply_common, cfr_args,
|
|
35
|
+
from _common import (require_filter, aac_args, add_common, brand_caption_style, script_font_for_text, apply_common, cfr_args,
|
|
36
36
|
color_hex, default_output, die, draws_chroma, drawn_chain, emit, escape_drawtext, escape_filter_path,
|
|
37
37
|
ffmpeg_base, info, label_chain, load_brand, parse_time, probe, rgb_chain_to_bt709, run, run_keeping_subtitles, video_args,
|
|
38
38
|
drawtext_boxborderw, drawtext_center_align, X264_PRESETS, time_arg, fmt_secs, STATE, drawtext_text_opts,
|
|
@@ -237,13 +237,18 @@ def main(force_route: Optional[str] = None) -> int:
|
|
|
237
237
|
emoji_mode = None
|
|
238
238
|
emoji_overlays: List[dict] = []
|
|
239
239
|
if has_emoji(all_text):
|
|
240
|
-
|
|
240
|
+
_ff = args.font_file or brand.get("font_file")
|
|
241
|
+
support = emoji_support(emoji_assets, probe=True, fonts_dir=os.path.dirname(os.path.abspath(_ff)) if _ff else None)
|
|
241
242
|
emoji_mode = support["mode"] if args.emoji == "auto" else args.emoji
|
|
242
243
|
if args.emoji == "color" and not support["libass_color"]:
|
|
243
244
|
die("--emoji color: this ffmpeg renders emoji monochrome through libass "
|
|
244
245
|
f"({support['detail']}) -- pass --emoji-assets DIR for colour, or --emoji mono", kind="input")
|
|
245
246
|
if args.emoji == "png" and not emoji_assets:
|
|
246
247
|
die("--emoji png: no emoji assets directory resolved -- " + EMOJI_ASSET_HINT, kind="input")
|
|
248
|
+
if args.emoji == "mono" and support.get("libass_glyph") is False:
|
|
249
|
+
die("--emoji mono: this ffmpeg draws no emoji glyph through libass "
|
|
250
|
+
f"({support['detail']}), so they would come out as empty boxes -- pass --emoji-assets DIR "
|
|
251
|
+
"for colour, or --emoji none", kind="input")
|
|
247
252
|
if args.text_render == "drawtext" and shaping:
|
|
248
253
|
die(f"{LANGUAGE_NAMES.get(_script, _script)} text cannot be shaped by drawtext on any ffmpeg "
|
|
249
254
|
"build (the marks are reordered by harfbuzz, which drawtext does not use): drop "
|
|
@@ -642,6 +647,7 @@ def main(force_route: Optional[str] = None) -> int:
|
|
|
642
647
|
"assets": emoji_assets, "missing": missing,
|
|
643
648
|
"overlays": len(emoji_overlays)}
|
|
644
649
|
ass_path = args.write_ass or os.path.splitext(output)[0] + "_gfx.ass"
|
|
650
|
+
require_filter("ass", "drawing this text through libass") # before the sidecar is written
|
|
645
651
|
if STATE.dry_run:
|
|
646
652
|
info(f"[dry-run] would write {ass_path} ({len(elements)} text elements)")
|
|
647
653
|
else:
|
|
@@ -682,7 +688,7 @@ def main(force_route: Optional[str] = None) -> int:
|
|
|
682
688
|
chains = list(fc) if fc else [f"[0:v]{text or 'null'}[vout]"]
|
|
683
689
|
last = "vout"
|
|
684
690
|
if route == "ass":
|
|
685
|
-
vf = f"ass={escape_filter_path(ass_path)}"
|
|
691
|
+
vf = f"ass=filename={escape_filter_path(ass_path)}"
|
|
686
692
|
fdir = ass_fonts_dir()
|
|
687
693
|
if fdir:
|
|
688
694
|
vf += f":fontsdir={escape_filter_path(fdir)}"
|
package/scripts/look.py
CHANGED
|
@@ -19,6 +19,8 @@ from typing import List, Optional
|
|
|
19
19
|
from _platforms import PLATFORMS, PLATFORM_CHOICES, resolve as resolve_platform
|
|
20
20
|
from _common import STATE, require_tool, add_common, apply_common, default_font_file, die, emit, escape_drawtext, ffmpeg_base, info, parse_time, probe, run, run_analysis, time_arg
|
|
21
21
|
from _common import drawtext_fontfile # noqa: E402
|
|
22
|
+
from _common import ffmpeg_filters, filter_missing_hint # noqa: E402
|
|
23
|
+
from _common.emit import note, record_fallback # noqa: E402
|
|
22
24
|
|
|
23
25
|
FONT = "fontcolor=white:fontsize=h/18:box=1:boxcolor=black@0.55:boxborderw=6:x=8:y=8"
|
|
24
26
|
|
|
@@ -162,6 +164,14 @@ def main() -> int:
|
|
|
162
164
|
# a resolvable font file, given as fontfile=, is the only form confirmed not to crash drawtext's
|
|
163
165
|
# own fontconfig resolution on some real Windows ffmpeg builds (#100); font= is the fallback when
|
|
164
166
|
# nothing can be resolved, unchanged from before this existed.
|
|
167
|
+
if not args.no_timecode and ffmpeg_filters() and "drawtext" not in ffmpeg_filters():
|
|
168
|
+
# a build without drawtext (Homebrew's plain ffmpeg) cannot stamp the timecodes, but the
|
|
169
|
+
# sheet itself only needs tile: draw it without them and say so, rather than failing the
|
|
170
|
+
# step every picture change ends with (#394)
|
|
171
|
+
args.no_timecode = True
|
|
172
|
+
record_fallback("this ffmpeg build has no drawtext filter, so the sheet has no timecode stamps "
|
|
173
|
+
"(the same as --no-timecode)", "look_no_drawtext")
|
|
174
|
+
note("no timecode stamps: this ffmpeg build has no drawtext filter (" + filter_missing_hint("drawtext") + ")")
|
|
165
175
|
if args.no_timecode:
|
|
166
176
|
font_prefix, tc = "", "" # nothing is drawn, so no font is resolved or copied
|
|
167
177
|
else:
|
package/scripts/silence.py
CHANGED
|
@@ -246,8 +246,12 @@ def resolve_filler(args, meta):
|
|
|
246
246
|
# faster-whisper word_timestamps=True, openai-whisper --word_timestamps True). An SRT
|
|
247
247
|
# cannot answer this question: a cue has a start and an end, a word does not.
|
|
248
248
|
# The call's own result, not module state: engine, routing and notes belong to this run.
|
|
249
|
+
# Whisper leaves "uh"/"um" out of its text unless the context shows them: prime it with the
|
|
250
|
+
# filler list of the language asked for (English under auto, where most filler cuts are)
|
|
251
|
+
primer = FILLER_WORDS.get(args.filler_lang if args.filler_lang != "auto" else "en") or []
|
|
252
|
+
prompt = (", ".join(sorted(primer)[:8]).capitalize() + ". So, I mean...") if primer else None
|
|
249
253
|
asr_run = _asr.transcribe_words_result(args.input, args.filler_lang if args.filler_lang != "auto" else None,
|
|
250
|
-
model=args.model, engine=args.engine)
|
|
254
|
+
model=args.model, engine=args.engine, prompt=prompt)
|
|
251
255
|
words, engine = asr_run.words, asr_run.engine
|
|
252
256
|
transcription = dict(asr_run.facts)
|
|
253
257
|
args._asr_notes = list(asr_run.notes)
|