Skip to main content

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 15 Playwright sources → Cineby → legacy 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
--parallel int 3 Download N HLS fragments concurrently (0 = sequential). Speeds up larger downloads
--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

# Parallel fragment download (default: 3 concurrent)
vidsrc-dlp "Inception"                            # 3 concurrent (default)
vidsrc-dlp "Inception" --parallel 8                # 8 concurrent for faster downloads
vidsrc-dlp "Inception" --parallel 0                # sequential (one fragment at a time)

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:

  1. Vidsrc.win sources — 15 Playwright-based embed sources (videasy, vidking, mapple, vidsrcme, vidsrc.su, vidsrc.cc, etc.), up to 4K
  2. Cineby — requires Playwright, up to 4K@16Mbps
  3. vidsrc — legacy scrape, up to 800p

If Playwright is not installed, steps 1 and 2 are 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. Vidsrc.win (15 Playwright sources)│    └────────────┘
                   │  2. Cineby   (Playwright, 4K@16Mbps) │
                   │  3. vidsrc   (legacy scrape, 800p)   │
                   └──────────────────────────────────────┘

How the resolvers work

Vidsrc.win (up to 4K, requires Playwright)

vidsrc.win React SPA contains 40+ hardcoded embed sources
  → 15 most reliable extracted and ranked:
    1. 4K      (player.videasy.net)
    2. 4K2     (www.vidking.net/embed)
    3. 4KHD    (mapple.uk/watch)
    4–15. vidsrcme, vidsrc.su, vidsrc.cc, vidrush,
          vidlink, anyembed, autoembed, rivestream,
          vidify, vidzee, vidplus, …
  → each source gets a Playwright headless Chromium browser
    → anti-detection (stealth + --disable-blink-features)
    → response interception for application/vnd.apple.mpegurl
    → returns first captured m3u8 URL

Cineby (4K, requires Playwright)

cineby.at/movie/{tmdb_id}?play=true
  → Playwright headless Chromium (stealth + system Chrome fallback)
    → api.speedracelight.com/seed (Cloudflare bypassed by browser)
      → client-side JS decrypts sources
        → moon.ironwallnet.net/vd/... (4K 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vidsrc_dlp-0.14.4.tar.gz (23.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vidsrc_dlp-0.14.4-py3-none-any.whl (22.7 kB view details)

Uploaded Python 3

File details

Details for the file vidsrc_dlp-0.14.4.tar.gz.

File metadata

  • Download URL: vidsrc_dlp-0.14.4.tar.gz
  • Upload date:
  • Size: 23.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for vidsrc_dlp-0.14.4.tar.gz
Algorithm Hash digest
SHA256 5ddfeca2f8ae4874c0be5c2049106a00c1d86fd908e9b37d50f36f55ec71984b
MD5 4b4baeaa1691ca6ad3f72eb18f69e742
BLAKE2b-256 a9259e999c8736df46423f926de7c5510f72134e71a62d51963f764adb306011

See more details on using hashes here.

File details

Details for the file vidsrc_dlp-0.14.4-py3-none-any.whl.

File metadata

  • Download URL: vidsrc_dlp-0.14.4-py3-none-any.whl
  • Upload date:
  • Size: 22.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for vidsrc_dlp-0.14.4-py3-none-any.whl
Algorithm Hash digest
SHA256 88fe21f7799b589019283d499331071914725b767e42a795cf9180ead269d27e
MD5 f5a160a7f4292f4dbabcad1d96def7b6
BLAKE2b-256 c28ec3540efd7586e5a94c3b089928682d4a631509b9e323f4295ddc39cf2429

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page