# Video Use Best Practices

> Best practices for using browser-use/video-use to edit Hebrew videos end-to-end with Claude Code. Covers the Hebrew-specific deltas to video-use's 12 Hard Rules: SUB_FORCE_STYLE override (Helvetica has no Hebrew glyphs), the python-bidi pre-shape recipe for libass+SRT BiDi failures on macOS, Hebrew filler-word post-pass on Scribe word timestamps, fontsdir= parameter for reliable font discovery, takes_packed.md handling for Hebrew with sofit/nikud/code-switching, and animation slot guidance that defers to hyperframes-best-practices and remotion-best-practices. Use when editing Hebrew talking-head video, podcast clips, tutorials, or marketing video with video-use. Do NOT use for non-Hebrew video-use sessions (read upstream SKILL.md directly), Hebrew podcast audio-only post-production (use hebrew-podcast-postproduction), or generic FFmpeg work without video-use orchestration.

- Skill: `skills-il/video-use-best-practices` (Agent Skill, multi-file: 16 files)
- Install (CLI): `npx skillmds@latest add skills-il/video-use-best-practices`
- Raw SKILL.md: https://api.skillmd.com/api/skills/skills-il/video-use-best-practices/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- License: MIT
- Author: skills-il (https://skillmd.com/u/skills-il)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/skills-il/video-use-best-practices

---


# video-use Best Practices (Hebrew)

## Problem

video-use ships with a strong English-first default: the bundled `SUB_FORCE_STYLE` uses Helvetica (no Hebrew glyphs, renders as boxes), 2-word UPPERCASE chunks (Hebrew has no uppercase), and the filler-removal step assumes an English filler lexicon while Scribe itself does not tag fillers per-word in any language. Hebrew creators using video-use hit the same three walls every time: missing-glyph boxes burned into the final video, captions that look wrong because UPPERCASE doesn't exist in Hebrew, and a "filler removal" step that leaves "אֶה", "כאילו", "יעני", and friends in. On macOS there's also a fourth wall: libass + SRT BiDi reordering is unreliable even with a fully-built libass, so Hebrew sentences render as characters drawn left-to-right in source byte order rather than in proper RTL visual order. This skill is the Hebrew-specific override layer on top of video-use's 12 Hard Rules. None of the upstream rules change, only the style values, font fallback chain, filler lexicon, caption-burn recipe, and self-eval frame checks.

## Pricing you should understand before you start

video-use is free; underlying services are paid. Mode determines cost.

| Mode | What | Best for | Cost / 1hr video |
|------|------|----------|------------------|
| **A. Captions-only** (`scripts/captions-only.sh`) | Transcribe + burn Hebrew captions on original. No cuts. | Lectures, webinars, podcast videos. | **~$1-3 total** |
| **B. Full cut** (default video-use flow) | Inventory, strategy, cut, render, self-eval. | Teaser from raw footage; multi-take selection. | **~$25-60** (1hr); **$120-300** (3hr). Scales super-linearly. |

Scribe v2 is **$0.22 per hour** of audio on the API rate card. `scribe_v1` was removed on 9 July 2026, so `scribe_v2` is the only model the scripts may send. Claude tokens dominate Mode A's cost; local FFmpeg rendering is $0.

**Two rate cards, do not mix them.** $0.22/hr is pay-as-you-go **API**. On a subscription you spend **credits**: STT bills ~330 credits/minute, so an hour is 19,800 credits, two thirds of Starter's 30,000 ($6/mo). The free plan's 10,000/month covers ~30 minutes. Warn non-technical users upfront.

**Pick the cheap one if unsure.** Captions-only is 20-100x cheaper on long videos, produces the same caption quality, and caches the transcript, so cutting later costs little extra.

**Pricing trap:** `no_verbatim=true` sounds like a saving but is destructive (the agent loses per-instance keep/cut decisions, usually forcing a re-transcription). Keep `no_verbatim=false` and run the Hebrew lexicon post-pass instead.

Rate-card breakdown, free-tier arithmetic, validated runs and the offline Whisper fallback: [references/pricing.md](references/pricing.md).

## Instructions

This skill is an **overlay** on top of video-use's upstream SKILL.md. Read the upstream SKILL.md first for the 12 Hard Rules, the EDL JSON schema, `takes_packed.md`, `render.py`, and the parallel-animation pattern. Then apply the Hebrew deltas below.

### Step 0 (FIRST TURN ONLY): Price disclaimer + mode selection

On the **first turn of a new session** (not on follow-ups within the same session), state the price disclaimer AND ask which mode. Both in one message. Do NOT re-show this on subsequent turns once the user has picked.

> *"לפני שמתחילים, חשוב שתדע: השימוש בסקיל הזה צורך מ-ElevenLabs (כ-$0.22 לשעת אודיו) ומ-Anthropic Claude (תלוי במסלול). העלות מצטברת אצלך, לא אצלי. שני מסלולים:*
>
> *(א) כתוביות בלבד על כל הסרטון, בלי חיתוך. כ-$1 עד $3 לכל סרטון. מתאים להרצאה, וובינר, פודקאסט.*
>
> *(ב) חיתוך לטיזר/קליפ קצר עם כתוביות. כ-$10 עד $300 לפי אורך המקור.*
>
> *מה אתה מעדיף?"*

English: *"Before we start, costs: ElevenLabs (~$0.22/hr) + Claude (depends on mode). Two paths: (A) captions-only ~$1-3, or (B) full cut ~$10-300. Which one?"*

Routing: **A → Step 8** (skip 1-7). **B → Step 1**. **Unsure → recommend A** (Scribe transcript caches, so adding B later only adds ~$0.08).

### Step 1: Verify the environment before the first render

Hard Rule 1 (subtitles applied LAST in filter chain) plus libass font resolution means a missing Hebrew font, a missing libass build, or a missing python-bidi install all produce silent failures: the SRT renders as `□□□` boxes burned into the final video, or as Hebrew with characters in the wrong positions. Verify all three before you cut.

```bash
# 1. ffmpeg has libass + fontconfig + libharfbuzz
ffmpeg -version 2>&1 | grep -oE 'enable-(libass|fontconfig|libharfbuzz|libfreetype)' | sort -u
# Expect: enable-fontconfig, enable-libass, enable-libfreetype, enable-libharfbuzz

# 2. Hebrew fonts are installed (Heebo, Rubik, Assistant, Noto Sans Hebrew)
fc-list :lang=he | head -5

# 3. python-bidi is installed (needed for the macOS BiDi workaround in Step 7)
python3 -c 'import bidi; print(bidi.__version__)'
```

If any check fails:
- Missing libass in ffmpeg → read `references/macos-ffmpeg-setup.md`, install the static evermeet build or use the homebrew-ffmpeg tap.
- Missing Hebrew fonts → `bash scripts/install-hebrew-fonts.sh` (idempotent installer for the canonical 4 fonts).
- Missing python-bidi → `pip3 install python-bidi`.

### Step 2: Override `SUB_FORCE_STYLE` for Hebrew

The bundled `bold-overlay` style in `render.py` is `FontName=Helvetica,FontSize=18,Bold=1,...,Alignment=2,MarginV=90`, against libass's default `PlayResY: 288`, i.e. 31.25% of frame height. (Upstream's own SKILL.md prose says `MarginV=35`; its shipped code says 90. The code is ground truth.) For Hebrew, override before invoking `render.py --build-subtitles`:

```python
# Hebrew override for video-use SUB_FORCE_STYLE.
# Applied via render.py monkeypatch OR by setting the env var the helper reads.
SUB_FORCE_STYLE_HE = (
    "FontName=Heebo,"
    "FontSize=22,"           # Hebrew x-height runs taller than Helvetica at the same point size
    "Bold=1,"
    "PrimaryColour=&H00FFFFFF,"
    "OutlineColour=&H00000000,"
    "BackColour=&H00000000,"
    "BorderStyle=1,Outline=2,Shadow=0,"
    "Alignment=2,"
    "Spacing=2,"             # default Hebrew tracking is cramped in libass
    "MarginV=50,"            # Hebrew descenders + diacritics need more bottom clearance than Latin
    "Encoding=1"             # libass rule: MUST always be 1
)
```

**Important:** overriding `SUB_FORCE_STYLE` is necessary but NOT sufficient on macOS. video-use's render.py path produces SRT files, and SRT + libass + BiDi is unreliable on macOS regardless of style settings. You must ALSO follow Step 7 (caption burn-in recipe) to get correct visual output. The override in this step ensures the right font and spacing for when libass eventually gets there.

The full ready-to-use override file is at `references/sub-force-style-hebrew.md` with three variants: `bold-overlay-he` (kinetic typography, 4-6 word chunks since UPPERCASE does not exist in Hebrew), `natural-sentence-he` (documentary/tutorial), and `vertical-social-he` (1080x1920 with `MarginV=120` to clear platform UI).

### Step 3: Add a Hebrew filler-word post-pass

**Important correction to a common assumption:** ElevenLabs Scribe does NOT tag fillers per word in ANY language. The Scribe word object exposes `type` values of `'word'`, `'spacing'`, or `'audio_event'` only, there is no `'filler'` or `'is_filler'` field. The only filler-related control is the request-level boolean `no_verbatim` (scribe_v2 only), which is **destructive**: it removes filler words, false starts, and disfluencies from the output entirely instead of marking them for review. `tag_audio_events` tags non-speech audio like `(laughter)` and `(applause)`, not verbal hesitations.

What that means in practice: call Scribe with `no_verbatim=false` (the default, keeps fillers in the word stream verbatim), then run your own lexicon match over `words[].text` to mark filler candidates. Cutting based on a lexicon you control beats `no_verbatim=true` because it preserves the editor sub-agent's ability to keep meaning-bearing instances.

Apply the Hebrew filler lexicon at `references/hebrew-filler-words.md`. The full list there has ~35 entries split into ALWAYS-FILLER (safe to auto-cut) and CONTEXT-DEPENDENT (flag for the editor sub-agent, these words have real meaning in some contexts). The core entries:

```
ALWAYS-FILLER:
אֶה, אה, אֶמ, אממ, אמממ, אהמ, המ, ממ

CONTEXT-DEPENDENT (flag, don't auto-cut):
כאילו, יעני, אז, אז ככה, בעצם, טוב, טוב נו, אוקיי, סבבה,
נו, האמת, בסדר, וואלה, כזה, ככה, נכון, פשוט, ממש,
סוג של, בקיצור, כנראה, לדעתי,
את יודע, את יודעת, אתה יודע, אתה מבין, את מבינה, הבנת,
תראה, תראי, שמע, שמעי, בוא, בואי
```

Apply the same rule as upstream: do not strip mid-phrase. Treat detected ALWAYS-FILLER tokens as silence-equivalent cut candidates with the same 30-200ms padding window (Hard Rule 7) and the same word-boundary snap (Hard Rule 6). For CONTEXT-DEPENDENT tokens, surface them to the editor sub-agent with a flag rather than auto-cutting.

**Editorial nuance:** the CONTEXT-DEPENDENT entries each have a literal sense and a filler sense. "כאילו" is filler in "זה כאילו לא עבד" but a literal "as if" in "התנהגה כאילו לא קרה כלום". "תראה / שמע / בוא" function as turn-starters far more often than as the literal verb in spoken Hebrew, but they ARE sometimes literal. Per upstream's "Unavoidable slips are kept if no better take exists" rule, prefer leaving them in over multiple cuts in tight succession. A small number per minute tends to read as natural Israeli speech.

### Step 4: `takes_packed.md` with Hebrew transcripts

`pack_transcripts.py` works unchanged on Hebrew Scribe JSON, it is locale-blind, breaks on silence >=0.5s, and produces phrase-level lines. Three Hebrew-specific things to watch:

1. **Code-switching is common.** Tech tutorials say "התקנתי React" (mid-sentence English). The phrase boundary stays on silence, not on script change. Do not try to "fix" this in the packed transcript, let the editor sub-agent see the mixed line as a single phrase. Hard Rule 6 (snap cuts to word boundaries) still applies; the boundary is in the Scribe JSON regardless of script.
2. **Nikud (vowel marks) is usually absent in Scribe output.** Do not add it. The Scribe transcript is for cut decisions, not for end-user reading. Burn-in subtitles use the same un-nikud-ed text.
3. **Sofit forms (ם ן ץ ף ך) appear correctly in Scribe output** when used at end-of-word. If you see middle-of-word sofit forms, that is a Scribe error, flag in pre-scan (upstream step 2) but do not silently rewrite.

### Step 5: Animation slots, defer to existing Hebrew skills

video-use's upstream SKILL.md says: *"Do not default to Remotion just because the animation is web-adjacent."* For Hebrew animation slots, the priority is the same, and we have a dedicated skill per engine:

| Slot type | Engine | Skill to load into the sub-agent prompt |
|-----------|--------|------------------------------------------|
| Kinetic typography, callout cards, product UI motion | HyperFrames | `hyperframes-best-practices` (covers Heebo via Google Fonts auto-fetch, `dir="rtl"`, `<bdi>` for mixed scripts) |
| Component-state compositions, existing Remotion brand system | Remotion | `remotion-best-practices` (covers Hebrew Google Fonts, bidi text animations) |
| Diagrams, equation derivations | Manim | (no Hebrew skill yet, Manim's Hebrew support is limited; pre-render Hebrew labels as PNG and import as `ImageMobject`) |
| Simple counters, typewriter, single bar reveals | PIL | Use `Pillow.ImageFont.truetype("Heebo-Bold.ttf", size, layout_engine=ImageFont.Layout.RAQM)` to enable HarfBuzz-based BiDi. Without `RAQM`, Pillow draws glyphs in input order and Hebrew comes out backwards |

When spawning a parallel sub-agent for a Hebrew animation slot, include in the prompt: *"This is a Hebrew animation. Load `hyperframes-best-practices` (or `remotion-best-practices`) before writing any composition code. Use Heebo as the default font."* The 10-point sub-agent brief from upstream SKILL.md applies unchanged.

### Step 6: Frame-sampling self-eval (you must look at actual pixels)

Upstream's self-eval runs `timeline_view` at every cut boundary and checks four things. For Hebrew you must **actually open and look at sampled frames** for two more. Do not trust that libass succeeded; the failure modes are silent.

The mandatory protocol:

**Do not hand-roll the crop.** `burn-hebrew-captions.sh` already writes verification frames into a `verify_*/` directory, cropped to where it actually placed the captions. Open those. A fixed crop like `crop=iw:200:0:ih-220` was correct only while the margin was a fixed 80px; now that geometry is a ratio of frame height, that strip holds no captions at all on a 9:16 render, so a self-eval against it certifies an empty band.

To sample a boundary the script missed, derive the window from the values it logged (`Font Npx, bottom margin Mpx`):

```bash
# Read FONTSIZE and MARGINV from the burn script's own log line for this render.
CROP_H=$(( FONTSIZE * 7 ))                  # anchored on the margin, opening upward
CROP_Y=$(( BASE_H - MARGINV - FONTSIZE * 6 ))
ffmpeg -y -ss <time> -i final.mp4 -frames:v 1 \
  -vf "crop=iw:${CROP_H}:0:${CROP_Y}" /tmp/verify/t<time>s.png
```

Then **open each PNG and verify with your own eyes**:

5. **Glyph fallback boxes.** Scan the subtitle row. If you see `□`, `?`, or visibly different fonts mid-line, libass picked a fallback font. The most common cause: `fontsdir=` parameter was not passed to the `subtitles` filter (Step 7 handles this). Re-render with `fontsdir=$HOME/Library/Fonts` (macOS) or wherever `fc-list :family=Heebo file` reports.

6. **RTL visual order (the most-missed check).** For a known caption line, verify the pixel left-to-right order is the REVERSE of source byte order. Example: source SRT line `ספריית הסקילז AI שבניתי.` should render with pixel LTR order:
   ```
   ספריית [right]   הסקילז   AI   שבניתי   . [left]
   ```
   The first source word (`ספריית`) ends up on the visual RIGHT. The period (last source byte) ends up on the visual LEFT. `AI` is embedded LTR within the RTL flow.

   **If the pixel LTR order matches the source byte order instead of being reversed, BiDi was not applied.** Symptoms: words look correctly shaped but appear in "English-style" left-to-right positions, with period on the right. This is the most common Hebrew failure on macOS and the recipe in Step 7 fixes it.

Keep the 3-pass cap from upstream. After 3 failed renders, stop iterating and flag to the user, the problem is environmental (font install, ffmpeg build, missing python-bidi), not editorial.

**Honest example:** the first validated run (May 2026) passed self-eval because the agent only checked for "no boxes", not pixel-vs-source order. Output had perfect Heebo glyphs but every Hebrew line was reversed (libass skipped BiDi). Step 7 fixes it.

### Step 7: Caption burn-in recipe (the one that actually works on macOS)

The bundled `scripts/burn-hebrew-captions.sh` does this in one command. The recipe is short:

1. **Sanitize the SRT** for Scribe garbage characters (Devanagari `्स` etc. that Scribe occasionally drops mid-Hebrew). Auto-fixes known patterns, warns on unknown ones.
2. **Pre-shape the Hebrew with python-bidi** (logical to display order), convert to ASS, patch the ASS `Style: Default` line (FontName=Heebo, size, Bold, Spacing, Encoding=1), and burn via the FFmpeg `subtitles=` filter with explicit `fontsdir=` (the style lives in the patched ASS, so no `force_style` is needed). On macOS, libass does NOT reorder SRT BiDi (Hebrew comes out left-to-right in source byte order); pre-shaping with `python-bidi` `get_display` makes libass draw what it sees. Do NOT remove the pre-shape step. **The `subtitles` filter's documented `shaping=complex` option does not replace it**: FFmpeg calls complex shaping "required for correct rendering of complex scripts such as Arabic, Hebrew", but shaping is glyph substitution and positioning, not bidirectional reordering. Tested 2026-08-26 on a HarfBuzz-enabled build: `shaping=auto` and `shaping=complex` produce byte-identical output, and both still render an un-pre-shaped Hebrew line in source byte order.
3. **Sample verification frames** (1 per minute, capped at 30).

One-line invocation:

```bash
bash scripts/burn-hebrew-captions.sh \
  --base   edit/base.mp4 \
  --srt    edit/master.srt \
  --out    edit/final.mp4 \
  --ffmpeg /tmp/ffmpeg
```

**Why (macOS):** `ffmpeg -i master.srt master.ass` alone does not fix BiDi; the python-bidi pre-shape converts logical to display order before libass sees it. Always verify rendered frames by comparing pixel order to source byte order, not just "no boxes" (a line can render with perfect glyphs yet be fully reversed).

**Caption geometry auto-scales off the probed frame height.** The script rewrites `PlayResY: 288` to the real height, which changes the unit `MarginV`, `FontSize`, `Outline` and the side margins are expressed in, so the old fixed values meant something different at every resolution. Measured on 1080x1920, they put the baseline 4.6% from the bottom, inside the UI band upstream's `MarginV=90` (31.25%) exists to clear. A 9:16 source now uses upstream's ratios; landscape, 4:5 and square keep the previously shipping ones, so the lecture case is unchanged. `--font-size` and `--margin-v` still win. See `references/sub-force-style-hebrew.md`.

**Validated on macOS only.** On a libass build that DOES reorder, pre-shaped text is reversed a second time and every caption comes out backwards with no error. The script warns when `uname -s` is not `Darwin`; elsewhere, check the verification frames first and confirm the FIRST source word lands at the visual RIGHT.

**The pre-shape must force RTL base direction.** `get_display(line)` alone infers paragraph direction from the first strong character, so a caption starting with a Latin token ("React הוא מעולה") is treated as an LTR paragraph and renders in reversed word order. The script passes `base_dir='R'`. Do not remove it, and add it to any pre-shape you write yourself.

Note: since v1.3.0 the script sanitizes into a working copy and **no longer rewrites the SRT you pass to `--srt`** (earlier versions overwrote video-use's own `master.srt`). `captions-only.sh` copies the sidecar `.he.srt` before the burn, so it keeps punctuation the burned-in captions do not.

### Step 8: Long video, captions-only mode (cheap path for non-editors)

**Use this when:** the user has a full lecture, webinar, podcast video or talking-head recording and just wants Hebrew captions burned in on the whole thing. No cuts, no take selection. This is the most common non-technical request, and Steps 1-7 are overkill and 20-100x more expensive for it.

The bundled `scripts/captions-only.sh` collapses the full workflow into one command:

```bash
bash scripts/captions-only.sh ~/Movies/my-lecture.mp4 \
  [--strip-fillers]   # drop אה/אממ from the on-screen text; audio untouched
  [--yes]             # skip the cost-confirmation prompt (CI / batch)
  [--output PATH] [--ffmpeg /tmp/ffmpeg]
```

What it does, end-to-end:
1. Auto-detects `ELEVENLABS_API_KEY` from env or `~/Developer/video-use/.env`
2. Probes the video duration and prints the estimated Scribe cost (unless `--yes` is passed)
3. Transcribes the full video via **Scribe v2** with `language_code=heb` and `timestamps_granularity=word` (`scribe_v1` was removed on 9 July 2026 and now returns an error)
4. **Auto-recovers Scribe gaps** (v1.2.7+): re-transcribes any 30s+ mid-file silence, or a tail ending >10s early, and merges the words back in. Scribe silently drops chunks on long Hebrew files. Each recovery is an extra billed call beyond the quoted estimate.
5. Builds an SRT chunking 5-7 words per caption, breaking on silence ≥250ms or sentence-end punctuation
6. (Optional) strips ALWAYS-FILLER tokens from the SRT if `--strip-fillers` is passed
7. Invokes `burn-hebrew-captions.sh` which:
   - **Strips sentence-end `.`/`?`/`!` from Hebrew lines by default** (BBC/Netflix caption style), because punctuation position under the pre-shape recipe is unpredictable across script mixes. To keep it, comment out the `SENTENCE_END` block in Step 0 of `burn-hebrew-captions.sh`.
   - Does the python-bidi pre-shape + libass burn with Heebo + verify frames

Output lands at `<input>.captioned.<ext>` (or `--output`). The merged SRT is written alongside as `<output>.he.srt` for sidecar upload to YouTube/Vimeo. Verify frames land in a `verify_*/` directory.

**Tuning:** thresholds and punctuation-stripping are documented in `references/captions-only-tuning.md`. Both the main pass and gap recovery use `scribe_v2`; there is no v1/v2 split to tune any more.

**Cost (API rate):** a 1-hour Hebrew lecture is ~$0.22 Scribe + ~$1 Claude = **~$1.20**. A 3-hour webinar: ~$0.66 + ~$2 = **~$2.70**. The $1-3 band holds because Claude tokens dominate. Full cut on the same 3-hour source: **$120-300**.

**When NOT to use this:** cutting to a teaser, picking the best of several takes, or rearranging beats all need the full Steps 1-7 workflow. Captions-only just captions the original.

### Step 9: Sample Hebrew prompts to drive the conversation phase

video-use's "Converse" step (step 3 of "The process") asks questions shaped by the material. Sample Hebrew prompts users send:

| Hebrew user message | What it maps to in the upstream workflow |
|---------------------|------------------------------------------|
| "ערוך את הקבצים האלה לסרטון השקה" | "edit these into a launch video", full inventory then strategy then execute |
| "תנקה את השתיקות ואת המילים המיותרות" | Filler removal + dead-space cuts only |
| "תוסיף כתוביות בעברית במרכז התחתון" | Subtitle generation with Hebrew SUB_FORCE_STYLE override AND `burn-hebrew-captions.sh` |
| "תוסיף גרידינג חם" | `grade.py --filter` with `warm_cinematic` preset (works language-agnostic) |
| "תפצל למקטעים של 30 שניות לאינסטה" | Vertical 1080x1920 reformat with `vertical-social-he` style |
| "תבדוק שאין שגיאות בכתוביות בעברית" | Trigger Step 6 frame-sampling self-eval (glyph fallback + RTL pixel order) |

## Examples

### Example 1: Long lecture, captions-only (most common ask)

User says: *"תוסיף כתוביות לסרטון הזה."* (no editing requested)

Step 0 mode pick: A. Then: `bash scripts/captions-only.sh ~/Movies/lecture.mp4`. Done. Output `lecture.captioned.mp4` + side `lecture.he.srt` for YouTube. ~$1-3 total.

### Example 2: Raw footage to edited cut with captions

User says: *"ערוך את הקבצים האלה לסרטון השקה. תוסיף כתוביות בעברית."*

Step 0 mode pick: B. Then: env check (Step 1), inventory + transcribe + filler post-pass (Steps 2-3), converse + EDL (Steps 4-5), `render.py` produces `base.mp4`, `burn-hebrew-captions.sh` produces `final.mp4`, self-eval per Step 6.

### Example 3: Mixed Hebrew/English tech tutorial

Source phrases like `התקנתי React אבל הוא לא טוען`. Both modes work: `python-bidi.get_display()` preserves "React" as `React` (not `tcaeR`) while reversing the surrounding Hebrew. Verify in Step 6 that mixed-script lines render with English LTR inside RTL flow.

## Recommended MCP Servers

video-use is a standalone Claude Code skill and does not require any MCP server. If your Hebrew editing workflow needs live data (for example, fetching trending Hebrew tweets to caption over, or pulling Bituach Leumi PSA copy), check the skills-il MCP directory at https://agentskills.co.il/mcp.

## Reference Links

| Source | URL | What to Check |
|--------|-----|---------------|
| video-use upstream | https://github.com/browser-use/video-use | SKILL.md (12 Hard Rules), install.md, helpers/ |
| libass ASS format guide | https://github.com/libass/libass/wiki/ASS-File-Format-Guide | UTF-8 BOM requirement, Encoding field rules |
| FFmpeg subtitles filter | https://ffmpeg.org/ffmpeg-filters.html | `subtitles=` filter syntax, libass dependency |
| Heebo on Google Fonts | https://fonts.google.com/specimen/Heebo | License, weights, character coverage |
| Noto Sans Hebrew | https://fonts.google.com/noto/specimen/Noto+Sans+Hebrew | Hebrew script coverage |
| Unicode BiDi algorithm | https://www.unicode.org/reports/tr9/ | UAX #9, directional isolation rules for mixed Hebrew+Latin |
| python-bidi | https://github.com/MeirKriheli/python-bidi | `get_display()` for the macOS BiDi workaround |
| ElevenLabs Scribe v2 | https://elevenlabs.io/blog/introducing-scribe-v2 | Current Scribe model (v2, January 2026). Multi-language transcription, Hebrew supported. (meet-scribe was the v1 announcement.) |
| ElevenLabs pricing | https://elevenlabs.io/pricing | Scribe per-hour cost and free tier limits |

## Bundled Resources

### Scripts

- `scripts/install-hebrew-fonts.sh`: Idempotent installer for Heebo, Rubik, Assistant, Noto Sans Hebrew. macOS (Homebrew cask) and Debian/Ubuntu (apt + manual fallback) paths. Re-runs `fc-cache` and verifies via `fc-list :lang=he`.
- `scripts/burn-hebrew-captions.sh`: The Step 7 caption burn-in recipe in one command. Pre-shapes SRT with python-bidi, converts to ASS, patches PlayRes/Style/Spacing, burns with explicit fontsdir, samples verification frames. Pre-flight checks that ffmpeg has libass and that Hebrew fonts exist before running. Step 0 of the script also sanitizes Scribe garbage characters (Devanagari etc.) with auto-fixes.
- `scripts/captions-only.sh`: The Step 8 cheap path for non-technical users. One command, full video in, captioned full video out. Transcribes via Scribe, builds SRT, optionally strips ALWAYS-FILLER tokens (`--strip-fillers`), then runs burn-hebrew-captions.sh. ~$1-3 total regardless of video length, vs. ~$25-300 for the full cut workflow.

### References

- `references/sub-force-style-hebrew.md`: Three ready-to-use `SUB_FORCE_STYLE` overrides for Hebrew (`bold-overlay-he`, `natural-sentence-he`, `vertical-social-he`). Documents why each value differs from the upstream Latin defaults, including PlayResX/Y and Spacing notes.
- `references/hebrew-filler-words.md`: Annotated Hebrew filler list with editorial guidance (which are always-fillers vs. sometimes-load-bearing). Drop-in for the Step 3 post-pass.
- `references/macos-ffmpeg-setup.md`: Fixes for the common Homebrew ffmpeg-without-libass trap and other macOS-specific gotchas (loudnorm on freeze frames, drawtext fallback to PIL, libass+SRT BiDi failure mode).
- `references/known-limitations.md`: Validated-vs-assumed boundaries, unvalidated style variants, and behaviours this skill does not cover.
- `references/captions-only-tuning.md`: All `captions-only.sh` tunables in one place , thresholds (`MAX_WORD_DUR`, `GAP_THRESHOLD`, `TAIL_THRESHOLD`, `MAX_DISPLAY_SEC`), the punctuation-stripping default + how to disable, side-output paths, and the full flags reference. Read this before changing any caption behavior.
- `references/quick-test.md`: 10-second synthetic Hebrew test video recipe (uses macOS `say -v Carmit` + ffmpeg). Costs ~$0.001 in Scribe and lets you validate the full pipeline end-to-end without burning your free-tier quota on real footage.

## Gotchas

- **Scribe occasionally drops non-Hebrew Unicode characters into Hebrew transcripts.** Most commonly Devanagari (`्` `स`) at the end of words where the speaker's soft `-s` or `-m` ending sounded ambiguous. Classic failure: "סקילים" transcribed as "סקיל्स". These chars render as boxes in the burn. `burn-hebrew-captions.sh` Step 0 auto-fixes the known patterns and warns on unknown ones; extend `auto_fixes` when you find more.
- **Helvetica is the most common mistake.** The bundled `SUB_FORCE_STYLE` in `render.py` uses `FontName=Helvetica`. Helvetica's Hebrew glyphs do NOT exist in the macOS or Linux Helvetica builds (Apple's "Helvetica" font is Latin-only; Linux usually maps it to a metric-equivalent). libass silently falls back to a tofu box. Always override `FontName` to a known Hebrew font before invoking the caption-burn step.
- **The python-bidi pre-shape is required on macOS, do NOT skip it or feed a raw SRT.** macOS libass does not reliably apply BiDi reordering to a raw SRT even when libfribidi is linked, so feeding the source SRT directly to `subtitles=` renders Hebrew reversed (characters drawn left-to-right in source byte order, period on the right). Pre-shaping with python-bidi (`get_display`) before the SRT->ASS burn is what makes the on-screen result RTL-correct. Do NOT remove the python-bidi step or feed a raw SRT, either one silently flips the layout back to reversed/broken. Any older note claiming libass handles SRT BiDi without the pre-shape is wrong for macOS.
- **Homebrew's default ffmpeg lacks libass and fontconfig.** As of 2026-05, a fresh `brew install ffmpeg` produces a binary that cannot burn captions at all. See `references/macos-ffmpeg-setup.md` for the static-build or homebrew-tap fixes.
- **2-word UPPERCASE chunks do not translate to Hebrew.** Hebrew has no uppercase. Do not try to fake it with `\fnHeebo Bold`, the result looks the same as regular Heebo. The Hebrew kinetic-typography equivalent is bold weight + larger size + tighter line breaks (4-6 Hebrew words per chunk, since Hebrew words are shorter than English on average).
- **`fontsdir=` is more reliable than fontconfig.** Even when `fc-match Heebo` resolves correctly, the `subtitles` filter sometimes ignores fontconfig and falls back to libass's built-in font, producing the wrong typeface in the burned-in output. Always pass `subtitles=foo.ass:fontsdir=$HOME/Library/Fonts` (or wherever Heebo lives per `fc-list :family=Heebo file`).
- **`loudnorm` fails on freeze frames with silent audio.** If your edit ends with a freeze frame that has no audio (or has dead air longer than ~1.5s), `loudnorm` returns `I=-inf` and aborts. Workarounds: layer 2 seconds of room tone over the freeze before rendering, or skip loudnorm entirely with `--no-loudnorm` (audio stays at original levels, no normalization applied).
- **`drawtext` may be missing in static ffmpeg builds** even when other filters work. If you need text overlays outside of subtitles (e.g., a CTA freeze frame caption), test with `ffmpeg -h filter=drawtext` first. If unavailable, generate the overlay via PIL (with `layout_engine=ImageFont.Layout.RAQM` for Hebrew) and composite with ffmpeg's `overlay` filter.
- **Do not pre-translate code-switched English brands.** Users typing "תוסיף ראקט" (transliteration of "React") instead of "React" causes Scribe to transcribe phonetically wrong. Tell the user during the conversation phase: code-switched English stays English in the transcript and in the burned-in caption, Hebrew speakers expect this.
- **`yt-dlp` Hebrew filenames.** When pulling Hebrew sources from URLs, `yt-dlp` writes filenames with Hebrew chars. Subsequent FFmpeg + libass passes work fine on macOS but can break on Linux filesystems with non-UTF-8 locales (`LC_ALL=C` is the common culprit). Set `LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8` in the install.md `.env` if you see "No such file" errors on visibly-present files.

## Known Limitations

Validated-vs-assumed boundaries, unvalidated variants, and behaviours this skill does not cover: [references/known-limitations.md](references/known-limitations.md). Read it before promising a result the skill has not measured.

## Troubleshooting

### One word in the caption renders as a weird non-Hebrew character (Greek/Devanagari/Tamil look)

Cause: Scribe transcribed a Hebrew word with a non-Hebrew Unicode character mixed in. Most common: Devanagari `्स` instead of `ים` at the end of a word with a soft `-s` sound (e.g., the colloquial pronunciation of "סקילים").

Solution: scan the SRT for non-Hebrew/Latin characters and fix them. `burn-hebrew-captions.sh` does this in its Step 0 sanitization pass; if you find a new pattern that auto-fix misses, add it to the `auto_fixes` dict in the script and submit a PR. For ad-hoc one-offs: `sed -i '' 's/סקיל्स/סקילים/g' master.srt` then re-run the burn script.

### Captions render as `□□□` boxes
Cause: libass cannot find a Hebrew font, or ffmpeg lacks libass. Fix: `ffmpeg -version | grep enable-libass` (if empty, see `references/macos-ffmpeg-setup.md`); `fc-list :lang=he` (if empty, run `install-hebrew-fonts.sh`); always pass `fontsdir=$HOME/Library/Fonts` explicitly.

### Captions render reversed (period on the right, words/chars in source byte order)
This means the python-bidi pre-shape did not run. macOS libass does not reorder a raw SRT (see the Gotchas note), so the pre-shape is mandatory, do NOT feed a raw SRT to `subtitles=`. Re-run `burn-hebrew-captions.sh` (which performs the pre-shape in Step 1) and confirm python-bidi is installed. Verify by comparing rendered pixel order to source byte order, not just "no boxes".

### Captions have no periods or question marks
Default behavior since v1.2.6: `burn-hebrew-captions.sh` Step 0 strips trailing `.`/`?`/`!` from any caption line containing Hebrew. BBC/Netflix caption style guides recommend this because line breaks and timing already signal end-of-thought. To restore punctuation, comment out the `SENTENCE_END` block in the Step 0 Python heredoc of `burn-hebrew-captions.sh`. Full rationale + alternatives in `references/captions-only-tuning.md`.

### Scribe gap-recovery itself returned an HTTP error
Symptom: `captions-only.sh` logs `WARN: gap re-transcribe returned HTTP <code>, skipping` and that window stays uncaptioned. Cause: Scribe v2 occasionally rate-limits, times out, or rejects very short or mid-word windows. Fix: (1) re-run the script , cached bulk transcript means only the failed gap retries, no extra base cost; (2) if it fails twice, extract a slightly wider window manually (`ffmpeg -ss <start-1> -t <dur+2>`), curl Scribe yourself, drop words into the cached `scribe.json`; (3) lower `GAP_THRESHOLD` to 20s so recovery splits long failing windows.

### Wrong font (not Heebo) despite Heebo installed
Cause: libass fell back to its built-in font because fontconfig integration is unreliable. Fix: pass `fontsdir=$HOME/Library/Fonts` explicitly to `subtitles=`.

### Captions render at tiny size on HD output
Cause: ffmpeg's SRT->ASS default is `PlayResX: 384, PlayResY: 288` and libass scales font size relative to PlayResY. Fix: set PlayResX/Y to match output. `burn-hebrew-captions.sh` does this automatically.

### Filler removal cut English but left Hebrew
Cause: Scribe doesn't tag fillers in any language; you skipped the Hebrew lexicon post-pass. Fix: apply `references/hebrew-filler-words.md` to Scribe word timestamps before computing cut candidates.

### Local renders fine but cloud render shows boxes
Cause: cloud container has no Hebrew fonts. Fix: add `install-hebrew-fonts.sh` to the container build step, or bundle Heebo into `<edit>/fonts/` and pass `fontsdir=<edit>/fonts`.

### `loudnorm` errors with `I=-inf`
Cause: video ends with silence (freeze frame with no audio). Fix: layer 2s of room tone over the silent portion, OR pass `--no-loudnorm` to `render.py`.

