Files

168 lines
5.9 KiB
Python

from __future__ import annotations
import shutil
from pathlib import Path
import pytest
from n_m3u8dl_py import (
DownloadCancelledError,
DownloadEventKind,
DownloadRequest,
N_m3u8DL,
SelectionError,
TrackSelection,
)
def test_public_api_inspects_dash_with_stable_track_ids() -> None:
root = Path(__file__).parents[2]
manifest = root / "src/N_m3u8DL-RE.Tests/Resources/Dash/Manifest_DuplicateSegments.mpd"
info = N_m3u8DL().inspect(manifest)
assert info.protocol == "DASH"
assert info.tracks[0].track_id == "dash:0"
assert info.tracks[0].segments_count == 3
assert info.tracks[0].duration == 6
def test_api_downloads_hls_with_default_auto_selection_and_events(tmp_path: Path) -> None:
(tmp_path / "one.ts").write_bytes(b"one")
(tmp_path / "two.ts").write_bytes(b"two")
manifest = tmp_path / "movie.m3u8"
manifest.write_text("#EXTM3U\n#EXTINF:1,\none.ts\n#EXTINF:1,\ntwo.ts\n#EXT-X-ENDLIST\n")
events = []
client = N_m3u8DL()
info = client.inspect(manifest, on_event=events.append)
result = client.download(
info,
DownloadRequest(
output_dir=tmp_path / "output",
temporary_dir=tmp_path / "temp",
file_name="movie",
binary_merge=True,
),
on_event=events.append,
)
assert result.selected_track_ids == ("hls:0",)
assert result.files[0].path.read_bytes() == b"onetwo"
assert result.files[0].size == 6
assert [event.kind for event in events] == [
DownloadEventKind.INSPECT_STARTED,
DownloadEventKind.INSPECT_COMPLETED,
DownloadEventKind.DOWNLOAD_STARTED,
DownloadEventKind.PLAYLIST_LOADED,
DownloadEventKind.TRACK_STARTED,
DownloadEventKind.SEGMENT_COMPLETED,
DownloadEventKind.SEGMENT_COMPLETED,
DownloadEventKind.TRACK_COMPLETED,
DownloadEventKind.DOWNLOAD_COMPLETED,
]
segment_events = [event for event in events if event.kind is DownloadEventKind.SEGMENT_COMPLETED]
assert [(event.completed_segments, event.total_segments) for event in segment_events] == [(1, 2), (2, 2)]
def test_api_download_url_is_one_call_convenience(tmp_path: Path) -> None:
(tmp_path / "one.ts").write_bytes(b"one")
manifest = tmp_path / "movie.m3u8"
manifest.write_text("#EXTM3U\n#EXTINF:1,\none.ts\n#EXT-X-ENDLIST\n")
result = N_m3u8DL().download_url(
manifest,
DownloadRequest(output_dir=tmp_path / "output", temporary_dir=tmp_path / "temp", file_name="one-call", binary_merge=True),
)
assert result.files[0].path.read_bytes() == b"one"
def test_api_downloads_segments_without_merging_or_ffmpeg(tmp_path: Path) -> None:
(tmp_path / "one.ts").write_bytes(b"one")
(tmp_path / "two.ts").write_bytes(b"two")
manifest = tmp_path / "movie.m3u8"
manifest.write_text("#EXTM3U\n#EXTINF:1,\none.ts\n#EXTINF:1,\ntwo.ts\n#EXT-X-ENDLIST\n")
result = N_m3u8DL().download_segments_url(
manifest,
DownloadRequest(
output_dir=tmp_path / "output",
temporary_dir=tmp_path / "temporary",
file_name="movie",
ffmpeg_path="/not-used-by-download-segments",
),
)
track = result.tracks[0]
assert [path.read_bytes() for path in track.segments] == [b"one", b"two"]
assert track.directory.is_dir()
assert not list((tmp_path / "output").glob("movie.*"))
shutil.rmtree(result.temporary_dir.parent)
assert not result.temporary_dir.exists()
def test_api_downloads_only_explicit_track_id(tmp_path: Path) -> None:
(tmp_path / "video.ts").write_bytes(b"video")
(tmp_path / "audio.ts").write_bytes(b"audio")
(tmp_path / "video.m3u8").write_text("#EXTM3U\n#EXTINF:1,\nvideo.ts\n#EXT-X-ENDLIST\n")
(tmp_path / "audio.m3u8").write_text("#EXTM3U\n#EXTINF:1,\naudio.ts\n#EXT-X-ENDLIST\n")
manifest = tmp_path / "master.m3u8"
manifest.write_text(
"#EXTM3U\n"
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud",NAME="English",LANGUAGE="en",URI="audio.m3u8"\n'
'#EXT-X-STREAM-INF:BANDWIDTH=1000,RESOLUTION=640x360,AUDIO="aud"\n'
"video.m3u8\n"
)
client = N_m3u8DL()
info = client.inspect(manifest)
video = next(track for track in info.tracks if track.resolution == "640x360")
result = client.download(
info,
DownloadRequest(
output_dir=tmp_path / "output",
temporary_dir=tmp_path / "temp",
file_name="video-only",
binary_merge=True,
selection=TrackSelection(track_ids=(video.track_id,)),
),
)
assert result.selected_track_ids == (video.track_id,)
assert len(result.files) == 1
assert result.files[0].path.read_bytes() == b"video"
def test_api_rejects_unknown_track_id(tmp_path: Path) -> None:
(tmp_path / "one.ts").write_bytes(b"one")
manifest = tmp_path / "movie.m3u8"
manifest.write_text("#EXTM3U\n#EXTINF:1,\none.ts\n#EXT-X-ENDLIST\n")
client = N_m3u8DL()
info = client.inspect(manifest)
with pytest.raises(SelectionError, match="Unknown track ID"):
client.download(info, DownloadRequest(selection=TrackSelection(track_ids=("hls:404",))))
def test_api_event_callback_can_cancel_download(tmp_path: Path) -> None:
(tmp_path / "one.ts").write_bytes(b"one")
manifest = tmp_path / "movie.m3u8"
manifest.write_text("#EXTM3U\n#EXTINF:1,\none.ts\n#EXT-X-ENDLIST\n")
events = []
def on_event(event):
events.append(event)
return False if event.kind is DownloadEventKind.TRACK_STARTED else None
client = N_m3u8DL()
info = client.inspect(manifest, on_event=on_event)
with pytest.raises(DownloadCancelledError):
client.download(
info,
DownloadRequest(output_dir=tmp_path / "output", temporary_dir=tmp_path / "temp", file_name="cancelled"),
on_event=on_event,
)
assert events[-1].kind is DownloadEventKind.CANCELLED
assert not (tmp_path / "output" / "cancelled.ts").exists()