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 via VidSrc's 4-hop chain, and download via yt-dlp.
Install
pip install vidsrc-dlp
cp .env.example .env # add your TMDB_API_KEY
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 |
best |
Target resolution. best = highest available. E.g. 1080p, 720p |
--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
vidsrc-dlp "Inception"
vidsrc-dlp "The Matrix" --year 1999
# Custom quality + skip prompt
vidsrc-dlp "Interstellar" --quality 1080p --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
When --quality best (the default), the stream is inspected with yt-dlp before downloading and the highest resolution variant is auto-selected. The detected resolutions are logged so you can see what's available:
[INFO] Available qualities: 1080p, 720p, 480p, 360p
[INFO] Format: bestvideo+bestaudio/best
Pass a specific height via --quality 1080p or --quality 720p to override and constrain the selection.
.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="best", 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 |
best |
Target resolution. best = highest available. 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" |
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 — search API for movie/TV metadata
- VidSrc Resolver —
requests-only 4-hop chain to extract an HLS URL - Downloader —
yt-dlp+ ffmpeg to download and transcode to MP4
How the resolver works
vidsrc.to/embed/{movie|tv}/{tmdb_id}[/{season}/{episode}]
→ vsembed.ru iframe (parse src attr)
→ cloudorchestranova.com/rcp/{hash} (parse prorcp hash)
→ cloudorchestranova.com/prorcp/{hash} (m3u8 URLs with __TOKEN__ placeholders)
→ generate.php endpoints (JWT tokens)
→ final m3u8 URL
Adding a new stream provider
Implement StreamProvider from vidsrc_dlp.utils.
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.0.tar.gz.
File metadata
- Download URL: vidsrc_dlp-0.7.0.tar.gz
- Upload date:
- Size: 17.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9fa8fea9a628b7216e0f0e8bdd411612502f03d8a89a01d4388d8a9c0cd55104
|
|
| MD5 |
0e34fe3858e756e1da9045f7f49531dd
|
|
| BLAKE2b-256 |
c0723b4269cf04177d0472f14b939cdca78b86aee66bef402288421fb6c1192a
|
File details
Details for the file vidsrc_dlp-0.7.0-py3-none-any.whl.
File metadata
- Download URL: vidsrc_dlp-0.7.0-py3-none-any.whl
- Upload date:
- Size: 17.1 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 |
f7c0add0b449af12bb86fecca49cfd1f37babb2b69a051f95e8168853293e0f8
|
|
| MD5 |
36ecdfaae23aaf6d90ad9b316f1c9717
|
|
| BLAKE2b-256 |
390d6efeff4c33898f71a2ed0d5c35b5ce14a8b197981d73500304a84af61b86
|