# Getting material out of video — yt-dlp + speech-to-text

> **What this chapter covers**
> The order of operations for turning a video into **something you can read** —
> and where in that order you get to **skip the download entirely**.

## First — do you actually need the video?

Same question as [the previous chapter](/guide/ht-crawlee-stealth). A video file
is **the heaviest and most fragile input in this guide**. Work down this list.

| What you need | What to fetch | Rough size |
|---|---|---|
| What was said | **Subtitles** | KB |
| Title, description, length, view count | **Metadata (JSON)** | KB |
| The content of a video with no subtitles | **Audio only** | MB |
| What was on screen (slides, a product, on-screen text) | The video | Hundreds of MB |

**Most real tasks end on the first two rows.** "What has this channel been
talking about lately", "summarise ten competitor launch videos" — none of that
needs a video file.

```mermaid
graph TD
  A["I need something out of a video"] --> B{"Are there subtitles"}
  B -->|Yes| C["Fetch subtitles only — done"]
  B -->|No| D["Fetch audio only"]
  D --> E["Speech to text"]
  C --> F["Summarise, classify, tabulate"]
  E --> F
  A --> G{"Do I need to see the screen"}
  G -->|Yes| H["Only now, the video"]
```

## Then — are you allowed to?

This comes **before** the technical part. The tool solves none of it.

- **Terms of service.** Most video platforms restrict downloading in their
  terms. What is technically possible and what is permitted are different
  questions.
- **Copyright.** A video is a copyrighted work. **Summarising a transcript for
  an internal decision** and **redistributing or re-editing the file you
  downloaded** are entirely different things. The second is out of scope here.
- **Behind a login or private.** That runs into account terms. Downloading
  **your own uploads** from your own account is not the same as getting around
  a barrier.
- **Personal data.** A public video with people in it still needs a lawful basis
  to process.
- **Load on their servers.** Don't scrape a whole channel in one burst. Space
  the requests out.

**The easiest case by far is your own channel, your own videos.** Collecting
your own subtitles or rewriting your past scripts sidesteps almost all of the
above. For anything else, staying **at the level of summarising for internal
reading** is the safe line.

## The tool — yt-dlp, and that's it

There is effectively one choice here.

| | What | State |
|---|---|---|
| **yt-dlp** | Thousands of sites, YouTube · Instagram · TikTok included | The de facto standard. Updated weekly |
| youtube-dl | What yt-dlp was forked from | Stalled. Doesn't keep up with platform changes |
| Node libraries | Mostly YouTube-only | Break every time the platform shifts |

**You need ffmpeg alongside it.** yt-dlp calls it internally to merge separately
downloaded video and audio streams, and to convert audio. Without it you get a
half-finished file and a "could not merge" message.

```bash
# macOS
brew install yt-dlp ffmpeg

# check
yt-dlp --version
```

## Four commands

### 1. Metadata only — the cheapest

```bash
yt-dlp --skip-download --dump-json "<url>" > out/meta.json
```

Title, duration, upload date, description and view count come back as one JSON
blob. Use it for **deciding what is worth watching**. For a channel listing:

```bash
yt-dlp --flat-playlist --dump-json "<channel url>" > out/list.jsonl
```

### 2. Subtitles only — usually the end of the job

```bash
yt-dlp --skip-download \
  --write-subs --write-auto-subs \
  --sub-langs "en,ko" --convert-subs srt \
  -o "out/%(id)s.%(ext)s" "<url>"
```

`--write-subs` gets **subtitles the uploader added**; `--write-auto-subs` gets
**the platform's machine-generated ones**. Turn both on and use whichever
exists.

> **Auto-generated subtitles are not a faithful transcript.** Proper nouns,
> numbers and product names are frequently wrong. Fine for a summary or for
> following the argument — **but check the source before quoting or copying a
> number.**

### 3. Audio only — when there are no subtitles

```bash
yt-dlp -f bestaudio -x --audio-format m4a \
  -o "out/%(id)s.%(ext)s" "<url>"
```

`-x` extracts audio only, which drops the size to **single-digit percentages of
the video**. The speech-to-text step that follows needs nothing more.

### 4. The video — only when you need the screen

```bash
yt-dlp -S "res:720" -o "out/%(id)s.%(ext)s" "<url>"
```

`-S "res:720"` picks **the best option that doesn't exceed 720p**. There is
rarely a reason to pull the original quality — 720p is plenty for reading
material off the screen, and the size difference is several-fold.

### Not downloading things twice

```bash
yt-dlp --download-archive out/done.txt ...
```

Records the id of everything fetched and **skips it on the next run**. Without
this, a daily pipeline re-downloads the same videos forever.

## Turning it into text

Once you have the audio, use the path from
[Working with speech and images](/guide/ht-voice-image) as-is.

```bash
curl -X POST http://localhost:27777/api/ai/v1/audio/transcriptions \
  -H "Authorization: Bearer $HT_KEY" \
  -F file=@out/abc123.m4a \
  -F model="<model name>"
```

At this point the video has become **text**, and everything after it is the same
as earlier in this guide. Create a task over [the REST API](/guide/ht-rest-api)
and hand it the summarising, classifying and tabulating.

```mermaid
graph TD
  A["1 Listing<br/>terminal"] --> B["2 Subtitles or audio<br/>terminal"]
  B --> C["3 Speech to text<br/>model API"]
  C --> D["4 Summarise<br/>task"]
  D --> E["out/summary.md"]
```

Step 3 is **skipped entirely when subtitles exist.** It is the most expensive
box in the pipeline, so simply checking for subtitles in step 2 removes most of
the cost.

## Running it in HyperTeams

Collection has no judgement in it, so it belongs to **`terminal`**, not `task` —
the same slot as the collection step in
[Case study — the content factory](/guide/ht-case-factory).

```
Daily 07:00  ·  kind: terminal
node collect-videos.mjs
```

- Keep the files inside the [working directory](/guide/ht-working-directory).
  `out/` fills with binaries, so leave it out of version control.
- **It takes a while.** Ten videos is minutes; with speech-to-text attached,
  more. That is a job for [a schedule](/guide/ht-pipelines), not for sitting in
  front of the screen.
- Leave interpretation to the next `task`. Mixing collection and judgement into
  one slot means you can't tell which half failed.

### When you just want one video, in Connect

If all you need is **a summary of a single video, right there** in a team
workspace, none of the above wiring applies. The web tool in
[tool settings](/guide/cn-tool-settings) already covers fetching YouTube
content. Come back to this chapter when you want a whole channel, every day.

## Upkeep — this one doesn't stay built either

| What | How |
|---|---|
| The platforms | Change without notice — **Instagram and TikTok far more often than YouTube** |
| yt-dlp | Follows behind and patches. A version a few days old may already fail |
| Subtitle availability | Auto-captions that existed yesterday can be gone today |

So **do not pin the version.** This is the opposite of your other dependencies.

```bash
yt-dlp -U    # the first thing to try when it breaks
```

And **don't build a pipeline that assumes success.** Quietly passing an empty
result downstream is the worst outcome — the next step will invent a plausible
summary out of nothing.

```
[Definition of done]
- If the collected count is 0, do not write the result file; report failure
- Skip items with neither subtitles nor audio, but keep them in the list
```

## Common mistakes

### Downloading the video before checking for subtitles

The most common one by far. Hundreds of megabytes and a speech-to-text run spent
on something one subtitle check would have finished. That is why the order is
**subtitles → audio → video**.

### Quoting auto-captions as if they were the transcript

Said above, worth repeating: **don't trust the numbers and proper nouns** in
machine-generated captions.

### Scraping an entire channel in one go

You get blocked, it takes forever, and most of those videos weren't needed.
**Choose from metadata first**, then fetch only what you chose.

### Re-uploading what you downloaded

This chapter is about collecting **material to read**. Redistribution and
re-editing are copyright questions, and no tool answers them for you.

---

## Check yourself

**1. You need to summarise ten videos. What do you fetch first?**

<details>
<summary>Answer</summary>

**Metadata and subtitles.** If subtitles exist you are done — no video file and
no speech-to-text step. You fetch the video only when you need to see the
screen.
</details>

**2. Why don't you pin the yt-dlp version?**

<details>
<summary>Answer</summary>

**Because the platforms keep changing and yt-dlp follows behind patching them.**
A pinned version fails silently one day. Unlike your other dependencies, `yt-dlp
-U` is the first thing to try when something stops working.
</details>

**3. What should happen when collection returns zero items?**

<details>
<summary>Answer</summary>

**Don't write the result file — report failure.** An empty result passed
downstream becomes a plausible-sounding summary built on nothing, and at an hour
when nobody is watching, that is what sticks.
</details>

---

Next, the extension that stops most Korean document automation →
[Korean official documents — hwp and hwpx](/guide/ht-korean-docs)
