CLI to search TMDB, resolve VidSrc streams, and download via yt-dlp
Project description
vidsrc-dlp
DISCLAIMER:
This project was vibecoded (using Opencode), to a fit a niche that I had in my personal Jellyfin setup. The code here is as is, and by using it you accept the risks of running unverified, AI generated code. Maybe I'll rewrite it myself if I get time.
Search TMDB, resolve a playable HLS stream, and download via yt-dlp. Automatically tries multiple providers for the best quality available.
Install
pip install vidsrc-dlp
cp .env.example .env # add your TMDB_API_KEY
For 4K quality (requires Playwright):
pip install vidsrc-dlp[playwright]
playwright install chromium
Or for local development:
git clone https://github.com/tecknet-gg/vidsrc-dlp
cd vidsrc-dlp
python -m venv .venv && source .venv/bin/activate
pip install -e .
cp .env.example .env # add your TMDB_API_KEY
CLI Reference
General
| Flag | Type | Default | Description |
|---|---|---|---|
query |
str |
required | Movie or TV show title |
--type |
movie / tv |
movie |
Media type |
--year |
int |
— | Filter by release / air year |
--quality |
str |
1080p |
Target resolution. best = highest available (may be very large). E.g. 1080p, 720p |
--provider |
auto / vidsrc |
auto |
auto tries Cineby → Mapple → vidsrc. vidsrc uses legacy single-domain |
--season |
int |
— | Season number (required for TV) |
--episode |
int |
— | Episode number (required for TV) |
--movies-dir |
str |
from .env |
Override movie output directory |
--tv-dir |
str |
from .env |
Override TV output directory |
--no-confirm |
flag | — | Skip download confirmation prompt |
--verbose |
flag | — | Enable debug logging |
--verbose-ytdlp |
flag | — | Show yt-dlp output |
Examples
# Simple movie download (1080p default, ~6 GiB for 2.5hr)
vidsrc-dlp "Spider-Man: No Way Home"
# 4K quality (requires Playwright, ~18 GiB for 2.5hr)
vidsrc-dlp "Interstellar" --quality best
# Custom quality + skip prompt
vidsrc-dlp "Inception" --quality 720p --no-confirm
# TV episode
vidsrc-dlp "Breaking Bad" --type tv --season 1 --episode 1
# Custom output directories
vidsrc-dlp "Inception" --movies-dir "/Volumes/Media/Movies"
vidsrc-dlp "Breaking Bad" --type tv --season 1 --episode 1 --tv-dir "/Volumes/Media/TV"
# Debug mode
vidsrc-dlp "Inception" --verbose --no-confirm
Quality selection
Default quality is 1080p, capping downloads at ~6 GiB for a 2.5hr film. Use --quality best to select the highest available variant (may be 4K/2160p, ~18 GiB). The detected resolutions are logged:
[INFO] Available qualities: 480p, 720p, 1080p, 2160p
[INFO] Format: bestvideo[height<=1080]+bestaudio/best[height<=1080]/best
Pass --quality 720p or --quality 480p to constrain further.
Provider selection
--provider auto (default) tries in order:
- Cineby — requires Playwright, up to 4K@16Mbps
- Mapple — no browser needed, up to 1080p@8Mbps
- vidsrc — legacy scrape, up to 800p
If Playwright is not installed, Cineby is skipped automatically.
.env configuration
TMDB_API_KEY=your_api_key_here
MOVIES_DIR=./downloads/movies
TV_DIR=./downloads/tv
CLI flags --movies-dir and --tv-dir override the .env values when provided.
Inline API (Python)
Public functions
search_movie(query, year=None, api_key=None) → list[Media]
| Param | Type | Default | Description |
|---|---|---|---|
query |
str |
— | Movie title to search for |
year |
int |
None |
Filter by release year |
api_key |
str |
None |
TMDB key (falls back to .env) |
>>> from vidsrc_dlp import search_movie
>>> movies = search_movie("Inception", year=2010)
>>> movies[0].title
'Inception'
search_tv(query, year=None, api_key=None) → list[Media]
| Param | Type | Default | Description |
|---|---|---|---|
query |
str |
— | TV show title to search for |
year |
int |
None |
Filter by first air year |
api_key |
str |
None |
TMDB key (falls back to .env) |
>>> from vidsrc_dlp import search_tv
>>> shows = search_tv("Breaking Bad")
>>> shows[0].title
'Breaking Bad'
get_movie_details(tmdb_id, api_key=None) → Media | None
| Param | Type | Default | Description |
|---|---|---|---|
tmdb_id |
int |
— | TMDB movie ID |
api_key |
str |
None |
TMDB key (falls back to .env) |
Returns enriched metadata (IMDb ID, genres, rating, poster path).
>>> from vidsrc_dlp import get_movie_details
>>> m = get_movie_details(27205)
>>> m.imdb_id
'tt1375666'
>>> m.genres
['Action', 'Science Fiction', 'Adventure']
>>> m.vote_average
8.4
get_tv_details(tmdb_id, api_key=None) → Media | None
| Param | Type | Default | Description |
|---|---|---|---|
tmdb_id |
int |
— | TMDB TV show ID |
api_key |
str |
None |
TMDB key (falls back to .env) |
>>> from vidsrc_dlp import get_tv_details
>>> show = get_tv_details(1396)
>>> show.imdb_id
'tt0903747'
resolve(media, season=None, episode=None) → StreamInfo | None
| Param | Type | Default | Description |
|---|---|---|---|
media |
Media |
— | Movie or TV show to resolve |
season |
int |
None |
Season (reads from media.season) |
episode |
int |
None |
Episode (reads from media.episode) |
>>> from vidsrc_dlp import search_movie, resolve
>>> movie = search_movie("Inception")[0]
>>> stream = resolve(movie)
>>> stream.url.startswith("http")
True
download(stream, media, quality="1080p", movies_dir=None, tv_dir=None) → bool
| Param | Type | Default | Description |
|---|---|---|---|
stream |
StreamInfo |
— | Stream from resolve() |
media |
Media |
— | Movie or TV show metadata |
quality |
str |
1080p |
Target resolution. best = highest. E.g. 1080p, 720p |
movies_dir |
str |
from .env |
Override movie output path |
tv_dir |
str |
from .env |
Override TV output path |
>>> from vidsrc_dlp import search_movie, resolve, download
>>> movie = search_movie("Inception")[0]
>>> stream = resolve(movie)
>>> download(stream, movie)
True
Data types
Media
| Field | Type | Description |
|---|---|---|
id |
int |
TMDB ID |
title |
str |
Movie or show name |
media_type |
MediaType |
MOVIE or TV |
year |
int | None |
Release / air year |
release_date |
str |
Full date string |
overview |
str |
Plot synopsis |
season |
int | None |
Season number (for TV) |
episode |
int | None |
Episode number (for TV) |
episode_title |
str | None |
Episode name |
imdb_id |
str | None |
IMDb ID (after get_*_details()) |
genres |
list[str] |
Genre names |
vote_average |
float | None |
TMDB rating |
poster_path |
str | None |
TMDB poster path |
StreamInfo
| Field | Type | Description |
|---|---|---|
url |
str |
Resolved HLS m3u8 URL |
headers |
dict[str, str] |
HTTP headers for download (User-Agent) |
referer |
str |
Referer URL required by CDN |
stream_type |
str |
Always "hls" |
urls |
list[str] |
All resolved variant URLs (for quality selection) |
MediaType (enum)
MediaType.MOVIE
MediaType.TV
Full workflow example
from vidsrc_dlp import (
search_movie, get_movie_details, resolve, download,
search_tv, get_tv_details, MediaType, Media,
)
# === Movie ===
movies = search_movie("Inception", year=2010) # → list[Media]
movie = get_movie_details(movies[0].id) # → Media (enriched)
print(movie.imdb_id, movie.genres, movie.year) # tt1375666, [...]
stream = resolve(movie) # → StreamInfo
download(stream, movie, quality="1080p") # → bool
# === TV ===
shows = search_tv("Breaking Bad")
show = get_tv_details(shows[0].id)
show.season = 1 # set before resolve()
show.episode = 1
show.episode_title = "Pilot"
stream = resolve(show)
download(stream, show)
# === Pass api_key inline (skip .env) ===
movies = search_movie("Inception", api_key="your_key")
stream = resolve(movies[0])
download(stream, movies[0], movies_dir="/custom/path")
Architecture
┌─────────────┐ ┌──────────────────────────────────────┐ ┌────────────┐
│ TMDB API │───▶│ Multi-Provider Stream Resolver │───▶│ yt-dlp │
└─────────────┘ │ │ │ + ffmpeg │
│ 1. Cineby (Playwright, 4K@16Mbps) │ └────────────┘
│ 2. Mapple (direct API, 1080p@8Mbps)│
│ 3. vidsrc (legacy scrape, 800p) │
└──────────────────────────────────────┘
How the resolvers work
Cineby (4K, requires Playwright)
cineby.at/movie/{tmdb_id}?play=true
→ Playwright headless Chromium
→ api.speedracelight.com/seed (Cloudflare bypassed by browser)
→ client-side JS decrypts sources
→ moon.ironwallnet.net/vd/... (4K HLS master playlist)
Mapple (1080p, no browser)
mapple.rip/api/stream?mediaId={tmdb_id}&mediaType=movie&source=mapple&apikey=...
→ source.heistotron.uk/p/... (1080p HLS master playlist)
VidSrc (800p, legacy fallback)
vidsrc.to/embed/{movie|tv}/{tmdb_id}[/{season}/{episode}]
→ vsembed.ru iframe
→ cloudorchestranova.com/rcp/{hash}
→ cloudorchestranova.com/prorcp/{hash}
→ generate.php endpoints (JWT tokens)
→ final m3u8 URL
Adding a new stream provider
Implement StreamProvider from vidsrc_dlp.utils and register it in MultiDomainResolver.resolve().
Acknowledgements
- MaheshSharan/vidsrc — The request-based 4-hop resolution chain was reverse-engineered from this open-source PHP scraper.
- TMDB — Movie and TV metadata API.
- yt-dlp — Download engine for HLS fetching, decryption, and ffmpeg transcoding.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vidsrc_dlp-0.7.2.tar.gz.
File metadata
- Download URL: vidsrc_dlp-0.7.2.tar.gz
- Upload date:
- Size: 18.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aa7c9b2b4f5e5aebd81e8f2e33514dab1ef522414de5e488495c8cf51d488bc9
|
|
| MD5 |
3a07009e13743bc7d6aac3d530da6fe8
|
|
| BLAKE2b-256 |
834f4e9444d24b22df39bb0ea0bcd3f82b8c104d88e9ea3b0ea660bc3b943c81
|
File details
Details for the file vidsrc_dlp-0.7.2-py3-none-any.whl.
File metadata
- Download URL: vidsrc_dlp-0.7.2-py3-none-any.whl
- Upload date:
- Size: 17.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8328ba76711294a466e7f2908c912bc2e5f01d0f039641657bf393e8b80964b9
|
|
| MD5 |
8723f7f563fb6052ea62ac08df75a85a
|
|
| BLAKE2b-256 |
d3b1f330f08e28032f0ad1f58f4f0c4734fc1f71b7bf1d6adb74ff1040470941
|