100 lines
3.4 KiB
Markdown
100 lines
3.4 KiB
Markdown
# N_m3u8DL-PY
|
|
|
|
`N_m3u8DL-PY` is a Python reimplementation of the downloader architecture in
|
|
the parent `N_m3u8DL-RE` project. It accepts HLS, DASH and Smooth Streaming
|
|
manifests from HTTP URLs or local files and downloads selected media tracks.
|
|
|
|
```bash
|
|
python -m n_m3u8dl_py <input> --auto-select
|
|
```
|
|
|
|
The command line intentionally follows the original project where practical.
|
|
Implemented workflows include:
|
|
|
|
- HLS master/media playlists, `EXT-X-MAP`, byte ranges, discontinuities and
|
|
AES-128/AES-128-ECB decryption through the system `openssl` command.
|
|
- DASH `SegmentTemplate`, `SegmentTimeline`, `SegmentList`, `SegmentBase`,
|
|
BaseURL inheritance and common CENC metadata detection.
|
|
- Smooth Streaming manifest parsing for supported video/audio/text tracks.
|
|
- Parallel segment download, retries, HTTP headers/proxy support, range and
|
|
ad filtering, binary concat, ffmpeg merge and post-download muxing.
|
|
|
|
DRM/CENC media cannot be decrypted in Python. The CLI passes supplied keys to
|
|
the configured external decryption tool (`mp4decrypt`, `shaka-packager`, or
|
|
`ffmpeg`) after download where possible.
|
|
|
|
## Python API
|
|
|
|
The package exports a stable synchronous API for integration in another Python
|
|
application. It never opens an interactive track picker.
|
|
|
|
```python
|
|
from pathlib import Path
|
|
|
|
from n_m3u8dl_py import DownloadRequest, N_m3u8DL, TrackSelection
|
|
|
|
client = N_m3u8DL()
|
|
media = client.inspect("https://example.com/master.m3u8")
|
|
|
|
# The default request chooses the best video, the best audio per language,
|
|
# and all subtitle tracks. Track IDs can be selected explicitly when needed.
|
|
result = client.download(
|
|
media,
|
|
DownloadRequest(output_dir=Path("downloads"), file_name="episode"),
|
|
)
|
|
|
|
# Or use the one-call convenience method when track inspection is unnecessary.
|
|
result = client.download_url(
|
|
"https://example.com/master.m3u8",
|
|
DownloadRequest(output_dir=Path("downloads")),
|
|
)
|
|
|
|
# Download ordered segment files without merging tracks. The caller owns and
|
|
# removes result.temporary_dir after its own post-processing is complete.
|
|
segments = client.download_segments(
|
|
media,
|
|
DownloadRequest(output_dir=Path("downloads"), temporary_dir=Path("work")),
|
|
)
|
|
for track in segments.tracks:
|
|
print(track.track_id, track.initialization, track.segments)
|
|
|
|
video = next(track for track in media.tracks if track.resolution)
|
|
video_only = client.download(
|
|
media,
|
|
DownloadRequest(
|
|
output_dir=Path("downloads"),
|
|
selection=TrackSelection(track_ids=(video.track_id,)),
|
|
),
|
|
)
|
|
```
|
|
|
|
Pass `on_event` to `inspect()` or `download()` for structured progress. A
|
|
callback may return `False` to cancel the current task; cancellation raises
|
|
`DownloadCancelledError`. Successful calls return `MediaInfo` and
|
|
`DownloadResult`; manifest, selection, and download failures raise the
|
|
corresponding public exception classes.
|
|
|
|
```python
|
|
from n_m3u8dl_py import DownloadEventKind
|
|
|
|
def show_progress(event):
|
|
if event.kind is DownloadEventKind.SEGMENT_COMPLETED:
|
|
print(event.track_id, event.completed_segments, event.total_segments)
|
|
|
|
media = client.inspect("playlist.m3u8", on_event=show_progress)
|
|
result = client.download(media, on_event=show_progress)
|
|
```
|
|
|
|
Create an editable local installation:
|
|
|
|
```bash
|
|
python -m venv --system-site-packages .venv
|
|
.venv/bin/python -m pip install --no-build-isolation -e .
|
|
```
|
|
|
|
Run the test suite when `pytest` is available:
|
|
|
|
```bash
|
|
.venv/bin/python -m pytest tests
|
|
```
|