Initial commit of video_extractor toolkit
Widevine/DRM research and video download scripts (Apple, CDA, Sky, TVP), CDM tooling, and helper utilities. Excludes downloads/. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WFUhtZEpkGZJjNCGFS9r4Z
This commit is contained in:
Executable
+121
@@ -0,0 +1,121 @@
|
||||
TVP VOD Downloader
|
||||
==================
|
||||
|
||||
Downloads Widevine L3 DRM-protected videos from vod.tvp.pl as decrypted MP4 files.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
Python packages:
|
||||
pip install pywidevine requests
|
||||
|
||||
System tools (already present in ./tools/):
|
||||
tools/N_m3u8DL-RE — DASH segment downloader
|
||||
tools/mp4decrypt — CENC decryption (Bento4)
|
||||
ffmpeg — must be in PATH (used for muxing)
|
||||
|
||||
Widevine device file (already present in ./L3/):
|
||||
L3/google_sdk_gphone64_x86_64_3198748f_22596_l3.wvd
|
||||
|
||||
|
||||
USAGE
|
||||
-----
|
||||
|
||||
1. Download a single episode by product ID:
|
||||
|
||||
python3 tvp_download.py 2993670
|
||||
|
||||
2. Download multiple episodes by product ID:
|
||||
|
||||
python3 tvp_download.py 2726892 2785411 2871666
|
||||
|
||||
3. Download all episodes of a series by series ID:
|
||||
|
||||
python3 tvp_download.py --series 2726890
|
||||
|
||||
Output files are saved to ./downloads/ as:
|
||||
sXXeXX_title.mp4 (e.g. s01e13_zaslubiny.mp4)
|
||||
|
||||
|
||||
FINDING IDs
|
||||
-----------
|
||||
|
||||
Product ID and series ID come from the vod.tvp.pl URL:
|
||||
|
||||
https://vod.tvp.pl/seriale,18/nazwa-serialu-odcinki,{SERIES_ID}/odcinek-N,S01E01,{PRODUCT_ID}
|
||||
|
||||
Example — "Wojna zastępcza":
|
||||
Series ID: 2726890 (from the URL segment "...-odcinki,2726890/...")
|
||||
Episode 13: 2993670 (from the URL segment "...,S01E13,2993670")
|
||||
|
||||
You can also pass the URL directly to the browser and copy the product ID from the address bar
|
||||
when playing an episode.
|
||||
|
||||
|
||||
HOW IT WORKS
|
||||
------------
|
||||
|
||||
For each episode:
|
||||
1. Fetches the DASH playlist:
|
||||
GET /api/products/{id}/videos/playlist?platform=BROWSER&videoType=MOVIE
|
||||
2. Extracts the Widevine PSSH from the MPD manifest.
|
||||
3. Sends a license challenge to TVP's Widevine license server using the
|
||||
L3 device certificate (.wvd file) and pywidevine.
|
||||
4. Gets the AES-128 content key from the license response.
|
||||
5. Downloads all encrypted video and audio segments via N_m3u8DL-RE.
|
||||
6. Decrypts segments with mp4decrypt using the content key.
|
||||
7. Muxes video + audio into a single MP4 with ffmpeg.
|
||||
|
||||
|
||||
OUTPUT
|
||||
------
|
||||
|
||||
Each episode is saved as a single MP4:
|
||||
./downloads/{product_id}_{title}.mp4
|
||||
|
||||
Quality: best available (typically 1920x1080 H.264, AAC audio, Polish).
|
||||
Size: roughly 1.2-1.5 GB per 45-minute episode at full quality.
|
||||
|
||||
|
||||
EXAMPLE SESSION
|
||||
---------------
|
||||
|
||||
# List all episodes of "Wojna zastępcza" and download them all:
|
||||
python3 tvp_download.py --series 2726890
|
||||
|
||||
# Output:
|
||||
# Found 13 episodes:
|
||||
# E01 | 2726892 | odc. 1 – Werbunek
|
||||
# E02 | 2785411 | odc. 2 – Legnica
|
||||
# ...
|
||||
# ============================================================
|
||||
# Product 2726892: 'odc. 1 – Werbunek' [EPISODE]
|
||||
# Key: d431b94c...:c3e2e41c...
|
||||
# Running: N_m3u8DL-RE 2726892_odc__1___Werbunek
|
||||
# [+] Saved: s01e01_werbunek.mp4 (1248 MB)
|
||||
# ...
|
||||
|
||||
|
||||
TROUBLESHOOTING
|
||||
---------------
|
||||
|
||||
"No .wvd file found":
|
||||
The Widevine device file is missing from ./L3/. The file
|
||||
google_sdk_gphone64_x86_64_3198748f_22596_l3.wvd must be present.
|
||||
|
||||
"Playlist fetch failed" / 404:
|
||||
The product ID may be wrong, or the content may have been removed from TVP VOD.
|
||||
Verify the URL on vod.tvp.pl.
|
||||
|
||||
"License server 406":
|
||||
Do not add Accept: application/json to the license POST request.
|
||||
The fetch_content_key() function uses only Content-Type: application/octet-stream.
|
||||
|
||||
"No CONTENT keys returned":
|
||||
The WVD file may have system_id=0 (broken). The script auto-selects the WVD
|
||||
with a non-zero system_id. If all WVDs have system_id=0, the device file needs
|
||||
to be regenerated.
|
||||
|
||||
Download hangs at 0%:
|
||||
Avoid piping N_m3u8DL-RE output through head or other tools — the broken pipe
|
||||
stalls the process. Always let it write to stdout directly or redirect to a file.
|
||||
Reference in New Issue
Block a user