Model or dataset
JimmySadek/youtube-fetcher-to-markdown avatar
JimmySadek/youtube-fetcher-to-markdown

youtube-fetcher-to-markdown: transcripts as archival notes, not raw caption dumps

Portable AI-agent skill: capture YouTube transcripts as Obsidian-ready Markdown with metadata, linked timestamps, language selection, and subtitle exports

476 stars36 forksPythonMIT

At a glance

What is it?
A Python skill that turns a YouTube URL into an Obsidian-ready Markdown file with frontmatter, chapters and provenance. It is a narrow tool with a clear boundary: it archives captions, it does not transcribe audio.
Who is it for?
Adopt it if you keep a local Markdown knowledge base and want each video archived as one file with a stable filename, frontmatter and linked chapters. Skip it if your sources need speech recognition, since the README describes caption capture only, or if you want a hosted service with a search index.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 10 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap between a caption dump and a note you can query later

Pasting a YouTube link into a transcript extractor usually returns a wall of text. Six months later that text has no channel name attached, no capture date, no video ID, and no way to tell whether the captions were human-written or machine-generated. The README frames the project against exactly that failure: most extractors stop at raw caption text, while an archival note also needs the source URL, creator, capture date, actual language, description, chapters and a predictable filename. The target user is someone running a local knowledge base, Obsidian in the examples, where notes are queried rather than read one at a time. The YAML frontmatter is what makes that possible, and the README points at Dataview as one way to query it while keeping the Markdown portable to Logseq and plain text workflows. The scope is deliberately small. This is a capture tool, not a research assistant, and it makes no attempt to summarise or index what it writes.

What lands on disk, and why the filename matters

The output is one Markdown file per video. The README gives this example path: ~/yt_transcripts/2026-03-04_obsidian-the-king-of-learning-tools_[hSTy_BInQs8].md. The pattern is capture date, slugified title, then the video ID in brackets, which means two videos with identical titles do not collide and a renamed video does not orphan the note. Inside, the file opens with frontmatter fields including title, channel, url, video_id, fetched, source_project, language, caption_type, duration, upload_date and a tags list. A Video Details table repeats several of those fields in human-readable form, followed by the description and the transcript body. The caption_type field carries a distinction that matters for anyone citing a video: the example shows manual, and the feature list separates manual from auto-generated captions. The language field records what was actually captured rather than what was requested, which is the difference between a note you can trust and one you have to re-check.

Installation, and the interpreter question agents get wrong

The skill installs through the skills CLI: npx skills add JimmySadek/youtube-fetcher-to-markdown. Cloning the repository works too, and from the skill directory you create an isolated environment and install requirements, then verify with scripts/fetch_transcript.py --check-deps. Python 3.8 through 3.14 is listed as supported for captions, with 3.10 or newer recommended for current optional yt-dlp releases. That word optional carries weight. yt-dlp supplies descriptions, chapters, duration and upload dates; without it the script falls back to oEmbed for title and channel when accessible, and --no-metadata skips both providers. The script never installs packages on its own. The README also flags a trap specific to agent use: an agent should use the same interpreter as the environment, and if the skill installation is read-only you create the environment elsewhere and pass the full path to requirements.txt. Read-only skill directories are common in managed agent setups, so this is not a hypothetical.

Output paths, language selection and the overwrite contract

Output location resolves in a fixed order: --output for one exact file, then --output-dir for the run, then the YOUTUBE_FETCHER_DIR environment variable, then ~/yt_transcripts/ by default. Language handling is where the flag design earns its keep. The README shows --lang fr,de for an ordered preference list with English as final fallback, --lang ja --strict-lang to fail clearly when Japanese captions are unavailable, and --lang auto to capture whatever exists. Explicit YouTube translation is supported and labeled in the output with both the source language and machine-translation provenance, which is the honest way to handle it. The overwrite rule is the part most likely to surprise people. Every format preserves an existing destination and exits with code 3, and the README states this happens before any network request when the destination is already known. There is no hidden interactive prompt, in a terminal or an agent session. --force replaces the file completely, including annotations you may have added by hand. A default Markdown refresh reuses the existing note's path even if the title or capture date changed, while an explicit --output is honored independently, so two files can hold different languages of the same video. Writes go through a temporary file beside the destination. On filesystems with hard links the new file appears only once its UTF-8 content is complete; elsewhere exclusive creation still refuses to open an existing file for writing, but a new file can be visible mid-write. Handled failures remove the partial file, though the README concedes that abrupt termination or disk failure can leave one behind. Forced refreshes preserve existing POSIX permission modes.

The hard limit: captions only, never audio

Nothing in the material describes speech recognition. The project captures captions that YouTube already holds, manual or auto-generated, and the language flags select among them. A video with captions disabled, a private recording, a podcast hosted outside YouTube, or a lecture uploaded without any subtitle track is outside what this tool can produce, and --strict-lang turning that into a clear failure is the correct behaviour rather than a defect. The same boundary applies to the metadata layer. Drop yt-dlp and you lose chapters, duration, upload date and the description; the fallback is oEmbed for title and channel only. Anyone expecting a full archival record from a bare Python environment will get a thinner file than the README's example suggests. There is also a dependency risk the README acknowledges indirectly by pinning a Python range and recommending 3.10 or newer for yt-dlp: caption extraction depends on an external service's behaviour, and a change on that side is not something this repository controls.

How it differs from yt-dlp used directly

The obvious alternative is calling yt-dlp yourself with --write-auto-subs and --convert-subs, which is what sits underneath much of this space. The difference is not extraction capability, it is what gets assembled around the extraction. yt-dlp writes subtitle files and, with --write-info-json, a separate metadata document; you then write the glue that merges them into one note, names it consistently, and decides what happens when you fetch the same video twice. This project ships that glue: a single Markdown file with frontmatter, a chapter list, translation provenance, and an overwrite policy expressed as exit code 3 before any network call. The trade-off runs the other way too. yt-dlp handles downloads, playlists, format selection and sites far beyond YouTube, and it is a general tool you can script against anything. If you need the video file, or you are pulling from Vimeo, this skill is the wrong layer. It is also not a competitor to hosted transcription APIs, which solve a different problem by generating text from audio where no captions exist.

Maintenance cost and what the MIT licence leaves you

The repository shows two releases, v1.2.0 in September 2026 and v1.1.0 the month before, with the newer notes covering language options, exports and safer overwrite behaviour. That is a short history, and the README does not describe a deprecation policy or a compatibility guarantee across versions. Your upgrade surface is small: requirements.txt, the optional yt-dlp install, and the Python range. Because the script never installs packages itself, upgrading means re-running pip in your environment and re-checking with --check-deps. The MIT licence permits commercial use, modification and redistribution, and it comes with no warranty, which is the standard arrangement and not legal advice. The practical consequence for a knowledge base is that your notes are plain Markdown with YAML frontmatter, so they outlive the tool. If the project stops being maintained, the files remain readable and queryable; only new captures stop.

Editorial conclusion

Adopt it if you keep a local Markdown knowledge base and want each video archived as one file with a stable filename, frontmatter and linked chapters. Skip it if your sources need speech recognition, since the README describes caption capture only, or if you want a hosted service with a search index. Before rolling it out, run --check-deps against the interpreter your agent will actually use, and confirm whether yt-dlp is installed, because without it descriptions, chapters, duration and upload dates are absent and only oEmbed title and channel remain.

Official sources

  1. Issues
  2. JimmySadek/youtube-fetcher-to-markdown on GitHub
  3. License: MIT
  4. README
  5. Releases
Community notes

Community notes