ArtificialGuyBR

Home / Blog / Training LoRAs

YT-Frame-Extractor: frames from YouTube videos, without the dark ones

A small Python script that downloads a video from a list of YouTube links and pulls evenly spread random frames, skipping dark ones and the first and last te…

2 sources cited Training LoRAs

YT-Frame-Extractor: frames from YouTube videos, without the dark ones

TL;DR

  • It is one Python file, extractor.py, that reads a list of YouTube links from videos.txt, downloads each video, and pulls a set number of frames as PNGs into per-video folders, then zips the result.
  • Two choices make the output usable for a dataset: frames are drawn across the whole video (the first and last tenth are skipped) and a frame is rejected when its mean luminance is below 20% of 255, so black frames do not end up in the set.
  • Defaults are hardcoded in main(): 200 frames per video, frames as the output root, and videos.txt in the working directory.
  • The repository's README is a single title line — everything below comes from reading extractor.py, so treat this as a description of the code, not of documentation.

What it is and who it is for

YT-Frame-Extractor is the kind of script people write once and then keep: a way to build an image dataset from video without sitting through the video with a screenshot key. It depends on pytube, OpenCV and NumPy, and it is small enough to read in one sitting.

It is for the person who has a folder of YouTube links and wants frames for a LoRA or a checkpoint, and for the one who already tried a frame extractor and got three hundred near-identical frames of a title card.

What the code does

  • Reads URLs from videos.txt, one per line.
  • Downloads each video with pytube, keeping the progressive MP4 at the highest available resolution, with five attempts and a five-second wait between them.
  • Divides the video into equal segments and draws one random frame per segment, so coverage is spread across the whole runtime instead of clustered at the start.
  • Skips the first and last 10% of frames, which is what keeps an intro sequence, a logo and end credits out of the dataset.
  • Checks each candidate's mean luminance in grayscale and retries the draw when the frame is darker than 20% of 255 — the concrete answer to "half my dataset is black frames".
  • Writes frame_{i}.png with PNG compression level 0, one thread per frame, then zips the whole frames directory.

How to use it

git clone https://github.com/artificialguybr/YT-Frame-Extractor.git
cd YT-Frame-Extractor
pip install pytube opencv-python numpy

Then create videos.txt with one URL per line and run the script:

python extractor.py

The output lands in frames/<video-name>/frame_0.png and onward, and a ZIP of the whole frames directory is created at the end. num_frames and output_root are set inside main() — 200 and frames — so changing the count means editing the file rather than passing an argument.

What to expect, honestly

There is no command-line interface, no configuration file and no progress reporting beyond a print per video. The number of attempts and the timeout constant live in the code, and a video that fails all five downloads is skipped with a message rather than stopping the run.

Because the frame order follows the segment index, the numbering is a spread over the video rather than a timeline — frame_0.png is not the first frame of the video. That is fine for a training set and confusing for anything else.

The luminance filter is a single mean value, so a frame that is dark but interesting ("a night scene") is rejected while a bright unusable frame can pass. It reduces the worst of the problem, and it does not solve it.

When to use it, when not to

Use it when you need hundreds of frames from a long video for a LoRA dataset, when the video has credits and intros you do not want, or when you want a script small enough to modify for your own filtering rule.

Do not use it for a video you need to sample precisely — the frames are random within segments, so a specific second in the video is not recoverable by index. Do not use it when pytube has broken against a YouTube change either: that dependency is outside the script, and the 2024 code reflects the API of its time.

Sources

  1. YT-Frame-Extractor — repository — the repository has a README with a title only, so this article reads extractor.py directly for behavior, defaults and dependencies.
  2. extractor.py — download retries, segment sampling, luminance threshold, PNG output, zip step.

Project page: YT-Frame-Extractor

For what to do with the frames afterwards, see automated dataset creation for LoRA training and building a training dataset.

FAQ

Does it need a YouTube API key? No. It uses pytube to download the video, with five attempts and a five-second wait between them.

Why are some frames skipped? The script redraws a frame when its mean grayscale luminance is below 20% of 255, which keeps black and near-black frames out of the dataset.

Which part of the video is used? Segments cover the whole runtime, but the first and last 10% are excluded so intros and credits stay out.

Can I choose how many frames? Only in code: num_frames defaults to 200 and is set inside main(), since the script has no command-line options.