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.
Same question as the previous chapter. 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.
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"]This comes before the technical part. The tool solves none of it.
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.
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.
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:
--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.
-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.
-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.
Records the id of everything fetched and skips it on the next run. Without this, a daily pipeline re-downloads the same videos forever.
Once you have the audio, use the path from Working with speech and images as-is.
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 and hand it the summarising, classifying and tabulating.
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.
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.
out/ fills with binaries, so leave it out of version control.task. Mixing collection and judgement into
one slot means you can't tell which half failed.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 already covers fetching YouTube content. Come back to this chapter when you want a whole channel, every day.
| 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.
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.
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.
Said above, worth repeating: don't trust the numbers and proper nouns in machine-generated captions.
You get blocked, it takes forever, and most of those videos weren't needed. Choose from metadata first, then fetch only what you chose.
This chapter is about collecting material to read. Redistribution and re-editing are copyright questions, and no tool answers them for you.
1. You need to summarise ten videos. What do you fetch first?
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.
2. Why don't you pin the yt-dlp version?
Because the platforms keep changing and yt-dlp follows behind patching them. A pinned version fails silently one day. Unlike your other dependencies,
3. What should happen when collection returns zero items?
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.
Next, the extension that stops most Korean document automation → Korean official documents — hwp and hwpx
# macOSbrew install yt-dlp ffmpeg# checkyt-dlp --versionyt-dlp --skip-download --dump-json "<url>" > out/meta.jsonyt-dlp --flat-playlist --dump-json "<channel url>" > out/list.jsonlyt-dlp --skip-download \ --write-subs --write-auto-subs \ --sub-langs "en,ko" --convert-subs srt \ -o "out/%(id)s.%(ext)s" "<url>"yt-dlp -f bestaudio -x --audio-format m4a \ -o "out/%(id)s.%(ext)s" "<url>"yt-dlp -S "res:720" -o "out/%(id)s.%(ext)s" "<url>"yt-dlp --download-archive out/done.txt ...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>"Daily 07:00 · kind: terminalnode collect-videos.mjsyt-dlp -U # the first thing to try when it breaks[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 listyt-dlp -U