DEV Community

Nidhi
Nidhi

Posted on

How to get around NotebookLM's 50-source limit with YouTube playlists

NotebookLM (Google renamed it Gemini Notebook in July 2026, same product) is one of the best free ways to study from long video courses. You add a YouTube link, it reads the video's captions, and you can ask questions, make flashcards or generate an audio overview.

The problem shows up with real courses. Google's help pages list these limits for the free plan:

  • 50 sources per notebook
  • 500,000 words per source (or 200 MB per uploaded file)

Each YouTube link is one source. A semester course with 120 lectures needs three notebooks, and then you can't ask one question across the whole course.

The fix is in the second number. 50 sources is small, but 500,000 words per source is big. One hour of lecture speech is roughly 8,000–10,000 words of captions, so one source can hold around 45–55 hours of transcript. Turn the playlist into one text file and the course becomes one source, leaving 49 free slots for slides, papers and notes.

Below is the free, do-it-yourself way, then a shortcut if you don't want to touch a terminal.

What you need

  • Python 3
  • yt-dlp (pip install yt-dlp)
  • A playlist whose videos have captions (manual or auto-generated). This method reads captions; it does not transcribe audio, so a video without captions gives you nothing.

Step 1: Download only the captions for the whole playlist

mkdir subs && cd subs
yt-dlp --skip-download --write-subs --write-auto-subs \
  --sub-langs "en.*" --sub-format vtt \
  -o "%(playlist_index)03d - %(title)s.%(ext)s" \
  "https://www.youtube.com/playlist?list=YOUR_PLAYLIST_ID"
cd ..
Enter fullscreen mode Exit fullscreen mode

What the flags do:

  • --skip-download fetches no video or audio, only subtitle files.
  • --write-subs --write-auto-subs takes the creator's captions if they exist, otherwise YouTube's auto-captions.
  • --sub-langs "en.*" picks English tracks. Change en to your language code. If you get the wrong language, run yt-dlp --list-subs URL on one video to see the exact track names.
  • %(playlist_index)03d puts a 001, 002, ... prefix on each file so the lectures stay in order.

If you get "Sign in to confirm you're not a bot" errors, you're probably on a cloud server or VPN. YouTube blocks many datacenter IPs. Run it from your normal home connection, and add --sleep-requests 1 for big playlists so you don't hammer YouTube.

Step 2: Combine the subtitle files into one clean Markdown file

Raw .vtt files are full of timing lines, formatting tags and, for auto-captions, every line repeated two or three times as the captions "roll". This script strips all of that, keeps the lectures in order under a ## heading each, and starts a new part file only if you go past 450,000 words (a safety margin under the 500k cap). It never cuts a lecture in half.

Save it as combine_subs.py:

#!/usr/bin/env python3
"""Combine every .vtt subtitle file in a folder into one Markdown file per
~450,000 words (under Gemini Notebook / NotebookLM's 500,000-words-per-source cap).
Usage: python3 combine_subs.py ./subs "My Course"
"""
import re, sys
from pathlib import Path

WORD_CAP = 450_000
TAG = re.compile(r"<[^>]+>")            # <00:00:01.234><c> word timing tags
CUE_TIME = re.compile(r"-->")           # 00:00:01.000 --> 00:00:03.000 lines

def vtt_to_text(path: Path) -> str:
    lines, last = [], None
    for raw in path.read_text(encoding="utf-8", errors="ignore").splitlines():
        line = TAG.sub("", raw).strip()
        if (not line or line == "WEBVTT" or CUE_TIME.search(line)
                or line.startswith(("Kind:", "Language:", "NOTE")) or line.isdigit()):
            continue
        if line == last:                # auto-captions repeat each line as they roll
            continue
        lines.append(line)
        last = line
    return " ".join(lines)

def main(folder: str, title: str) -> None:
    files = sorted(Path(folder).glob("*.vtt"))
    if not files:
        sys.exit(f"No .vtt files in {folder}")
    parts, cur, words = [], [f"# {title}\n"], 0
    for f in files:
        name = re.sub(r"\.[\w-]+\.vtt$", "", f.name)   # drop ".en.vtt"
        body = vtt_to_text(f)
        n = len(body.split())
        if words and words + n > WORD_CAP:              # start a new part between videos
            parts.append(cur)
            cur, words = [f"# {title} (continued)\n"], 0
        cur.append(f"\n## {name}\n\n{body}\n")
        words += n
    parts.append(cur)
    for i, p in enumerate(parts, 1):
        out = Path(f"{title} - part {i}.md" if len(parts) > 1 else f"{title}.md")
        out.write_text("".join(p), encoding="utf-8")
        print(f"wrote {out} ({sum(len(x.split()) for x in p):,} words)")

if __name__ == "__main__":
    main(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else "Playlist")
Enter fullscreen mode Exit fullscreen mode

Run it:

python3 combine_subs.py ./subs "Intro to Algorithms"
# example output: wrote Intro to Algorithms.md (182,000 words)
Enter fullscreen mode Exit fullscreen mode

Big courses come out as ... - part 1.md, ... - part 2.md, each under the cap.

Step 3: Upload it to NotebookLM as one source

  1. Open your notebook, click Add sources, and upload the .md file. If the upload dialog won't take .md on your account, rename it to .txt; it's plain text either way.
  2. Because every lecture sits under its own ## 001 - Title heading, citations point you to the right lecture.
  3. Use the 49 free slots for the course slides, papers and your own notes.

Prompts that work well on a whole-course source

  • "Make a one-page outline of the course, one line per lecture, using the lecture headings."
  • "Which lectures explain dynamic programming? Quote the key sentence from each."
  • "Write 20 exam-style questions that need ideas from at least two different lectures."

Limits to know (so you're not surprised)

  • Captions only. If a lecturer writes a formula on the board without saying it, it isn't in the transcript. Add the slides as a separate source.
  • Auto-captions make mistakes with names and technical terms. Spot-check anything you'll quote.
  • Timestamps are dropped by this script to save words. If you need to jump back to the exact moment, keep the original .vtt files, or export SRT instead.
  • Private, members-only and caption-less videos won't work with any caption-based method.

The shortcut (if you'd rather not run scripts)

I built a web tool, YT Transcriber, that does Steps 1 and 2 in the browser: paste a playlist or channel URL and download it as one file for NotebookLM, ChatGPT or Claude, auto-split under 450,000 words between videos. You can try one video on the homepage with no sign-in. With a Google sign-in it's 3 videos a day free, and $9/month for whole playlists. Disclosure: I'm the maker, a student. It reads captions like the script above, so the same limits apply.

The free script works fine too. The point of this post is the trick: one big file beats 50 small links.

Top comments (1)

Collapse
 
suppdevbot profile image
DEV SUPPORTS •
You need to verify your account.
Enter fullscreen mode Exit fullscreen mode

tr.ee/dev-to