Skip to main content

Grabinator

License: MIT Python 3.10+ Platforms

A command-line downloader for TikTok, YouTube, Dailymotion, SoundCloud, Instagram, X, and Threads — security-hardened, with automatic quality-aware deduplication and playback fixes for native macOS players. Also converts local video files to MP3 with --convert, no network access required.

Table of contents

Features

Sources TikTok, YouTube, Dailymotion, SoundCloud, Instagram, X, Threads
Naming TikTok/Instagram/X/Threads → @username_id.mp4 · YouTube/Dailymotion → title.mp4
Organization Each platform gets its own subfolder (TikTok/, YouTube/, etc.)
Audio --mp3 extracts clean, padding-free audio on any platform
Local conversion --convert PATH turns existing video files into MP3s — no downloading
Captions --captions downloads subtitles only (converted to .srt), for a video or a whole playlist
Photo posts --photos saves a TikTok/Instagram/X/Threads photo-carousel post as individual full-quality images instead of a rendered slideshow video
Channels Point a YouTube channel/handle URL at Grabinator and it downloads every upload, same as a playlist
Playlists Full YouTube/SoundCloud playlist support, with --range to grab a specific span
Quality selection Capped one tier above 1080p by default, or pick interactively with -q
Dedupe Re-downloading never creates duplicates — a lower/equal-quality repeat is skipped, a genuinely better one replaces the old file
macOS fix Auto re-encodes tracks that play in VLC but are silent/blank in QuickTime, Preview, or Photos
Proxy --proxy routes every request (including the connectivity check) through a SOCKS5/HTTP(S) proxy
Cookies --cookies-from-browser or --cookies unlock private/login-required content
Size check -c shows the estimated download size — one total for a whole playlist — before anything downloads

Supported platforms (OS)

Pure Python, using only pathlib/subprocess for filesystem and process work — it runs the same way on:

  • Linux — any distro with Python 3.10+ and ffmpeg available.
  • macOS — including the playback-compatibility fixes this tool specifically adds.
  • Windows — the script forces UTF-8 console output and enables ANSI color processing on startup, so the colored status output and emoji indicators render correctly instead of crashing or printing garbled escape codes on a legacy cmd.exe codepage. Works from PowerShell, Windows Terminal, or plain cmd.exe.

The one external dependency that varies by OS is ffmpeg itself — see Installation for the install command on each.

Installation

git clone https://github.com/eaglesquawk/grabinator.git
cd grabinator
pip install -e .

This also installs yt-dlp, tqdm, and certifi. Separately, install ffmpeg (which includes ffprobe) via your OS package manager:

brew install ffmpeg          # macOS
sudo apt install ffmpeg      # Debian/Ubuntu
winget install ffmpeg        # Windows

Usage

Single items

grabinator "https://www.tiktok.com/@user/video/123"
grabinator "https://www.youtube.com/watch?v=XXXXXXXXXXX"
grabinator "https://www.dailymotion.com/video/XXXXXXX"
grabinator "https://soundcloud.com/artist/track-name"
grabinator "https://www.instagram.com/p/XXXXXXXXXXX/"
grabinator "https://x.com/user/status/XXXXXXXXXXX"
grabinator "https://www.threads.net/@user/post/XXXXXXXXXXX"

Playlists and channels

grabinator "https://www.youtube.com/playlist?list=XXXXXXXXXXX"
grabinator "https://soundcloud.com/artist/sets/album-name"

# A YouTube channel or handle downloads every upload, the same as a playlist —
# no special flag needed, Grabinator recognizes the URL shape automatically
grabinator "https://www.youtube.com/@SomeChannel"
grabinator "https://www.youtube.com/channel/UCxxxxxxxxxxxxxxxxxxxxxx"

# Only videos 3 through 7 of a playlist or channel
grabinator "PLAYLIST_OR_CHANNEL_URL" --range 3-7

# Just the first 10
grabinator "PLAYLIST_OR_CHANNEL_URL" --range 10

Multiple URLs at once

grabinator "URL1,URL2,URL3"

Audio

# Audio only, on any platform — clean MP3, no leading silence
grabinator "URL" --mp3

The leading-silence "padding" that naive MP3 extraction leaves in place comes from source timestamps that don't start at zero — this isn't a TikTok-specific quirk, it affects YouTube (and Dailymotion, SoundCloud, the rest) just as much. --mp3 runs the exact same ffmpeg fix — zeroing negative timestamps and regenerating clean presentation timestamps before encoding — on every platform uniformly, YouTube included, so the output is padding-free no matter where it came from.

# Convert local video files to MP3 instead — no network access at all,
# originals are never touched. PATH can be a file, a folder, or a .txt
# manifest listing one path per line.
grabinator --convert /path/to/videos
grabinator --convert /path/to/video.mp4
grabinator --convert /path/to/list.txt

Captions

# Download only the subtitles for a video, saved as .srt
grabinator "URL" --captions

# Works on a whole playlist or channel too — one .srt per video
grabinator "PLAYLIST_OR_CHANNEL_URL" --captions

# A specific language (default: en)
grabinator "URL" --captions --caption-lang es

Whatever subtitle format the source actually provides gets converted to .srt, so the output is consistent regardless of platform. If a video simply has no captions available, Grabinator says so and moves on rather than failing the whole run.

Photo posts

# Save a TikTok/Instagram/X/Threads photo-carousel post as individual images,
# instead of the default behavior of rendering it into a slideshow video with audio
grabinator "URL" --photos

By default, a photo-carousel post (no native video track) is reconstructed into an MP4 slideshow set to its audio track. --photos skips that rendering step entirely and instead saves each photo exactly as downloaded — no re-encoding, so this is definitionally the best available quality — into a new folder named after the post (@username_id/), numbered in post order (1.jpg, 2.jpg, ...). Re-running against an already-downloaded post is skipped rather than re-downloading into a duplicate folder. --photos only applies to TikTok/Instagram/X/Threads; on any other platform, or if the post turns out to be a regular video rather than a photo post, Grabinator notes that and downloads normally instead. It can't be combined with --mp3, --captions, -q/--choose-quality, or -c/--check-size.

Quality selection

# Interactively pick a resolution from a numbered menu
grabinator "URL" -q

# See the estimated file size and confirm before downloading
# (one total for an entire playlist, not one prompt per video)
grabinator "URL" -c

Proxy

grabinator "URL" --proxy socks5://127.0.0.1:9050
grabinator "URL" --proxy http://user:pass@host:port

Other flags

grabinator "URL" --silent                  # suppress progress bars and status output
grabinator "URL" --output-dir /some/path   # override the default download folder
grabinator "URL" --log                     # also write this run's output to a timestamped .txt file
grabinator --version                       # print the installed version

--log writes a full copy of everything printed during that run to <output-dir>/logs/grabinator_<timestamp>.txt, one file per run, alongside — not instead of — the normal console output. Live-updating progress-bar frames aren't logged individually (that would just be noise); each bar's final result still is.

Run without installing, straight from the source file:

python src/grabinator/cli.py "URL"

Authentication (cookies)

Some content requires being logged in to view at all — a private Instagram account, an age-restricted YouTube video, some Threads posts. Grabinator doesn't handle logins itself; instead it borrows a session you already have, the same way yt-dlp does under the hood.

# Reuse cookies from a browser you're already logged into
grabinator "PRIVATE_URL" --cookies-from-browser chrome
grabinator "PRIVATE_URL" --cookies-from-browser firefox

# Or use an exported cookies.txt file instead, without touching a live browser profile
grabinator "PRIVATE_URL" --cookies /path/to/cookies.txt

The two are mutually exclusive — pick one. --cookies-from-browser reads directly from that browser's cookie storage each run; --cookies points at a Netscape-format file you've exported once (browser extensions like "Get cookies.txt" can produce this). Neither option is stored, logged, or written anywhere by Grabinator itself — they're passed straight through to yt-dlp for that run only.

Configuration

The default download folder is set at the top of src/grabinator/cli.py:

OUTPUT_DIR = Path.home() / "Downloads" / "media by Grabinator"

Edit that constant, or pass --output-dir on the command line to override it per run. Whatever you choose, each platform still gets its own subfolder underneath it.

Security measures

  • URLs are checked against an explicit host allowlist (TikTok/YouTube/Dailymotion/ SoundCloud/Instagram/X/Threads domains only) before any network request is made.
  • All subprocess calls (ffmpeg, ffprobe) use argument lists, never shell=True.
  • Filenames derived from remote titles/usernames are sanitized, and every final destination path is resolved and verified to stay inside the configured output directory before anything is written — blocking path traversal from a malicious or unexpected title.
  • Per-run limits guard against accidental bulk-scraping: at most 25 URLs in one invocation, and at most 200 videos pulled from any single playlist or channel.
  • The connectivity check only probes the exact host a given URL points to — never a fixed external address unrelated to what you asked to download.
  • --convert never opens the network at all, and never modifies or deletes the original video files it reads.
  • Cookies passed via --cookies-from-browser or --cookies are used only for that run's requests — Grabinator never writes them to disk, logs them, or includes them in the dedupe index.

Testing status

The pure logic (URL/platform detection, path safety, filename sanitization, range parsing, cookie-options handling, etc.) is covered by an automated test suite and verified on every push. A few things are logic-verified but not yet confirmed against real, live usage:

  • Windows. The UTF-8/ANSI console hardening is implemented and tested for correctness, but hasn't been run on an actual Windows machine.
  • Cookies (--cookies-from-browser, --cookies). The options are built and passed to yt-dlp correctly, but haven't been exercised against a real login-gated download.
  • Channel downloads. URL detection and normalization to a channel's "Videos" tab are tested, but a real channel hasn't been downloaded end-to-end yet.
  • Captions. The download and .srt conversion path hasn't been run against a real video with real subtitles.

If you hit something that doesn't work as documented in one of these areas, that's useful to know about.

Built on yt-dlp and ffmpeg. Use it for content you have the right to download — your own uploads, permissively licensed content, or personal archival use consistent with each platform's Terms of Service and applicable copyright law. You're responsible for how you use it.

Development

pip install -e ".[dev]"
pre-commit install
pytest

Linting/formatting runs automatically on commit via pre-commit + ruff. See CONTRIBUTING.md for how to add a new platform, what's expected of a pull request, and the release process. See CHANGELOG.md for the project's history.

Author

EagleSquwak — September 2026

License

MIT

Metadata

Release files for grabinator 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for grabinator 0.6.0
File Size Uploaded
grabinator-0.6.0.tar.gz 35.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for grabinator 0.6.0
File Interpreter ABI Platform
grabinator-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 62.2 kB

Release files / grabinator-0.6.0.tar.gz

Download URL grabinator-0.6.0.tar.gz
Size 35.2 kB
Tags Source
SHA-256 checksum
How to use checksums
312c79bdb5026d040994a8833550c37cb95dbf4d2d9b57e525459fb4132e661c
BLAKE2b-256 checksum
How to use checksums
0c201105698cd6816b06550580342e3d085cac25442e191050dfde3a950c117f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.0

Release files / grabinator-0.6.0-py3-none-any.whl

Download URL grabinator-0.6.0-py3-none-any.whl
Size 27.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f577eac746f2e11d510ffecebd6383cd96e16910cc73d2e6cac46cf5a77e6f8a
BLAKE2b-256 checksum
How to use checksums
39a0f68a7c2cc27cd6a773bb528eef5505cf6d0ae8868c0ee10158b79f860ab4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.0

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files

0.5.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page