mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-14 21:15:06 +00:00
style: clean up prose
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""Tests for SubtitleCodecChoice — notably the ``original`` keep-source sentinel that
|
||||
"""Tests for SubtitleCodecChoice, notably the ``original`` keep-source sentinel that
|
||||
services set via the ``sub_format`` override (must not be rejected as an invalid codec)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Tests for ``dl.write_export`` — the ``--export`` JSON sidecar.
|
||||
"""Tests for ``dl.write_export``, the ``--export`` JSON sidecar.
|
||||
|
||||
Regression: DRM-free tracks never pass through ``prepare_drm``, so ``write_export``
|
||||
must accept ``drm=None`` (and DRM systems without ``to_dict``/``content_keys`` such
|
||||
|
||||
@@ -61,7 +61,7 @@ VIDEO_HEVC_SDR_CPD = (
|
||||
)
|
||||
# Real Dolby Vision (dvhe, DoViProfile "stn") CodecPrivateData from a Smooth
|
||||
# manifest: NALs arrive SPS,PPS,VPS (VPS last) and the VUI colour triple is
|
||||
# Unspecified (2,2,2) — DV is signalled by FourCC only, never by CICP.
|
||||
# Unspecified (2,2,2): DV is signalled by FourCC only, never by CICP.
|
||||
VIDEO_HEVC_DV_CPD = (
|
||||
"00000001420101022000000300B00000030000030096A001E020021C4D9457B91CAF016E0404042800001F480002EE0401F4E1"
|
||||
"15EE7E0001312D00002FAF0C80000000014401C1ACBE0EC90000000140010C01FFFF022000000300B00000030000030096"
|
||||
@@ -295,7 +295,7 @@ def test_ec3_without_dolby_guid_builds_bare_entry():
|
||||
|
||||
|
||||
def test_aac_codec_private_data_synthesis_matches_real_manifest():
|
||||
# 48 kHz stereo AAC-LC must produce 0x1190 — the exact ASC real manifests carry.
|
||||
# 48 kHz stereo AAC-LC must produce 0x1190, the exact ASC real manifests carry.
|
||||
assert synthesize_aac_codec_private_data("AACL", 48000, 2).hex() == "1190"
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Tests for ``apply_service_dl_overrides`` — precedence: CLI/env > service dl > global dl > defaults."""
|
||||
"""Tests for ``apply_service_dl_overrides``. Precedence: CLI/env > service dl > global dl > defaults."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
@@ -246,7 +246,7 @@ def test_changed_services_up_to_date(monkeypatch):
|
||||
|
||||
@pytest.mark.parametrize("order", [["a", "b"], ["b", "a"]])
|
||||
def test_collision_first_source_wins(tmp_path, order):
|
||||
"""Discovery dedupes by tag honoring list order — the FIRST source to define a tag wins,
|
||||
"""Discovery dedupes by tag honoring list order: the FIRST source to define a tag wins,
|
||||
whether it's a local dir or a repo clone; later duplicates are shadowed."""
|
||||
dirs = {}
|
||||
for name in ("a", "b"):
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
Test suite for the unshackle remote-services subsystem:
|
||||
|
||||
- `unshackle/core/remote_service.py` — `RemoteClient`, `RemoteService`, helpers
|
||||
- `unshackle/core/api/` — routes, handlers, session_store, input_bridge,
|
||||
- `unshackle/core/remote_service.py`: `RemoteClient`, `RemoteService`, helpers
|
||||
- `unshackle/core/api/`: routes, handlers, session_store, input_bridge,
|
||||
download_manager, errors, compression
|
||||
- `unshackle/commands/serve.py` — CLI surface
|
||||
- `unshackle/commands/serve.py`: CLI surface
|
||||
|
||||
Two tiers:
|
||||
|
||||
@@ -38,7 +38,7 @@ The suite can either:
|
||||
`--server-url http://host:port`.
|
||||
|
||||
Spawn mode is controlled by `--spawn-serve {auto, always, never}` (default
|
||||
`auto` — spawn only when `--server-url` is empty).
|
||||
`auto` spawns only when `--server-url` is empty).
|
||||
|
||||
### Auto-spawn (recommended)
|
||||
|
||||
@@ -78,7 +78,7 @@ services you have access to. Schema (see the example file for full docs):
|
||||
services:
|
||||
EXAMPLE:
|
||||
title_url: "..." # required
|
||||
series_url: "..." # optional — overrides movie target when set
|
||||
series_url: "..." # optional, overrides movie target when set
|
||||
target_season: 1
|
||||
target_episode: 1
|
||||
search_query: "..."
|
||||
@@ -114,9 +114,9 @@ uv run pytest tests/remote -v --live
|
||||
|
||||
## Markers
|
||||
|
||||
- `unit` — fast, mocked (default)
|
||||
- `live` — needs `--live`
|
||||
- `slow` — hits real services; combine with `--live`
|
||||
- `unit`: fast, mocked (default)
|
||||
- `live`: needs `--live`
|
||||
- `slow`: hits real services; combine with `--live`
|
||||
|
||||
Run only fast unit tests:
|
||||
|
||||
@@ -127,4 +127,4 @@ uv run pytest tests/remote -m "unit and not slow"
|
||||
## Adding a new service to e2e
|
||||
|
||||
1. Add a block under `services:` in your local `fixtures.yaml`.
|
||||
2. No new Python needed — every e2e test is parametrized over the YAML.
|
||||
2. No new Python needed; every e2e test is parametrized over the YAML.
|
||||
|
||||
@@ -8,39 +8,39 @@
|
||||
# unshackle (matches the service directory name, e.g. EXAMPLE). The keys
|
||||
# below are read by the e2e tests:
|
||||
#
|
||||
# title_url (required) — movie URL/slug/ID accepted by the service
|
||||
# series_url (optional) — series URL/slug/ID; when set, the
|
||||
# title_url (required): movie URL/slug/ID accepted by the service
|
||||
# series_url (optional): series URL/slug/ID; when set, the
|
||||
# quality + license + download tests
|
||||
# target target_season/target_episode
|
||||
# instead of the movie
|
||||
# target_season (optional, default 1)
|
||||
# target_episode (optional, default 1)
|
||||
# search_query (optional) — string to send to /api/search
|
||||
# requires_auth (optional, default true) — informational
|
||||
# server_cdm (optional, default false) — informational
|
||||
# drm (optional) — informational ("widevine" / "playready")
|
||||
# search_query (optional): string to send to /api/search
|
||||
# requires_auth (optional, default true): informational
|
||||
# server_cdm (optional, default false): informational
|
||||
# drm (optional): informational ("widevine" / "playready")
|
||||
#
|
||||
# expected_quality (optional) — when present, the quality test
|
||||
# expected_quality (optional): when present, the quality test
|
||||
# asserts these limits against the
|
||||
# discovered video tracks
|
||||
# min_height (optional) — max video.height >= this
|
||||
# min_codecs (optional) — codec names that must be present
|
||||
# min_height (optional): max video.height >= this
|
||||
# min_codecs (optional): codec names that must be present
|
||||
# (AVC, HEVC, VC1, VP8, VP9, AV1)
|
||||
# min_ranges (optional) — range names that must be present
|
||||
# min_ranges (optional): range names that must be present
|
||||
# (SDR, HDR10, HDR10P, DV, HLG, HYBRID)
|
||||
# min_track_count (optional) — at least this many video tracks
|
||||
# min_track_count (optional): at least this many video tracks
|
||||
#
|
||||
# runs_download_test (optional, default false) — when true, the
|
||||
# runs_download_test (optional, default false): when true, the
|
||||
# download smoke test
|
||||
# runs for this service
|
||||
# runs_license_test (optional, default false) — when true, the
|
||||
# runs_license_test (optional, default false): when true, the
|
||||
# license test runs for
|
||||
# this service
|
||||
# license_drm (optional, default "widevine") — DRM type the
|
||||
# license_drm (optional, default "widevine"): DRM type the
|
||||
# license test
|
||||
# requests ("widevine"
|
||||
# or "playready")
|
||||
# license_quality (optional, default 1080) — pixel height of the
|
||||
# license_quality (optional, default 1080): pixel height of the
|
||||
# track the license test
|
||||
# targets
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ For any fixture service with ``runs_download_test: true``, this test:
|
||||
4. Fetches the manifest URL with the resolved headers.
|
||||
5. Asserts the body is non-empty and looks like DASH/HLS.
|
||||
|
||||
It does NOT decrypt or mux — that requires a full local CDM. It proves
|
||||
It does NOT decrypt or mux; that requires a full local CDM. It proves
|
||||
the end-to-end pipeline up to CDN reachability for the selected quality.
|
||||
"""
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ def create_session(http_session, server_url: str, service: str, title_id: str) -
|
||||
payload = {
|
||||
"service": service,
|
||||
"title_id": title_id,
|
||||
# Quality knobs — send enum names the server's session_create parser accepts.
|
||||
# Quality knobs: send enum names the server's session_create parser accepts.
|
||||
"range_": ["SDR", "HDR10", "DV"],
|
||||
"vcodec": ["AVC", "HEVC"],
|
||||
"best_available": True,
|
||||
@@ -115,13 +115,13 @@ def test_track_quality_meets_expected(http_session, server_url: str, service_cas
|
||||
tracks = r.json()
|
||||
video = tracks.get("video") or []
|
||||
summary = summarize(video)
|
||||
# Print summary even when test passes — discovery aid.
|
||||
# Print summary even when the test passes, as a discovery aid.
|
||||
with capsys.disabled():
|
||||
print(f"\n[{service}] target={kind} '{target.get('name')}' -> {summary}")
|
||||
|
||||
expected = conf.get("expected_quality") or {}
|
||||
if not expected:
|
||||
pytest.skip(f"{service}: no expected_quality in fixtures.yaml — discovered={summary}")
|
||||
pytest.skip(f"{service}: no expected_quality in fixtures.yaml; discovered={summary}")
|
||||
|
||||
if "min_height" in expected:
|
||||
assert summary["max_height"] >= expected["min_height"], (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Unit tests for DownloadJob + DownloadQueueManager state machine.
|
||||
|
||||
These tests focus on the queue manager's data layer (create/get/list/cancel/
|
||||
cleanup/serialize) — they do not exercise the actual subprocess download path.
|
||||
cleanup/serialize); they do not exercise the actual subprocess download path.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -47,7 +47,7 @@ def test_api_error_default_http_status_per_code() -> None:
|
||||
APIErrorCode.NETWORK_ERROR: 503,
|
||||
APIErrorCode.SERVICE_UNAVAILABLE: 503,
|
||||
}
|
||||
# Lock that the cases above are the *complete* live set — a new/removed code trips this.
|
||||
# Lock that the cases above are the *complete* live set; a new/removed code trips this.
|
||||
assert {c.name for c in cases} == {c.name for c in APIErrorCode}
|
||||
for code, expected in cases.items():
|
||||
assert APIError(code, "x").http_status == expected, code
|
||||
@@ -142,7 +142,7 @@ def test_handle_api_exception_categorizes_generic() -> None:
|
||||
assert body["error_code"] == "NETWORK_ERROR"
|
||||
|
||||
|
||||
# Locks the FULL observable response — (http status, error_code, retryable) — that
|
||||
# Locks the FULL observable response (http status, error_code, retryable) that
|
||||
# a client sees for a generic (non-APIError) exception flowing through the
|
||||
# handle_api_exception funnel that every route's `except Exception` path calls.
|
||||
# This is the equivalence reference: after the categorize_exception collapse,
|
||||
|
||||
@@ -190,5 +190,5 @@ async def test_auth_middleware_accepts_known_key(make_app, aiohttp_client) -> No
|
||||
app["config"]["users"]["good-key"] = {"devices": []}
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.get("/api/session/nonexistent", headers={"X-Secret-Key": "good-key"})
|
||||
# Auth passed; handler then 404s the session — anything other than 401 is fine here.
|
||||
# Auth passed; handler then 404s the session; anything other than 401 is fine here.
|
||||
assert resp.status != 401
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
Covers the selection primitives that back `-r ...,DV,HYBRID` downloads:
|
||||
|
||||
- ``Tracks.select_hybrid`` — picks the HDR base layer per resolution and the
|
||||
- ``Tracks.select_hybrid``: picks the HDR base layer per resolution and the
|
||||
single lowest DV track used as a hybrid ingredient.
|
||||
- ``Tracks.merge_video_selections`` — de-duplicates the ingredient/deliverable
|
||||
- ``Tracks.merge_video_selections``: de-duplicates the ingredient/deliverable
|
||||
overlap so a DV track that is chosen as both is not muxed/downloaded twice.
|
||||
- ``Tracks.partition_hybrid_videos`` — splits the ladder into hybrid-ingredient
|
||||
- ``Tracks.partition_hybrid_videos``: splits the ladder into hybrid-ingredient
|
||||
candidates and the standalone-deliverable pool; HDR10/HDR10+/DV only enter
|
||||
the pool when their range was explicitly requested alongside HYBRID.
|
||||
- ``Tracks.flag_hybrid_ingredients`` — marks ingredient-only tracks with
|
||||
- ``Tracks.flag_hybrid_ingredients``: marks ingredient-only tracks with
|
||||
``hybrid_base_only`` so the standalone mux loop skips them.
|
||||
|
||||
The remaining ``dl`` glue (the Cartesian deliverable product and the mux loop)
|
||||
@@ -169,7 +169,7 @@ def flagged(tracks: list[Video]) -> set[str]:
|
||||
|
||||
def test_flag_hybrid_only_flags_base_and_ingredient_dv(ladder: list[Video]) -> None:
|
||||
"""`-r HYBRID`: no deliverables, so the base and the ingredient DV are both
|
||||
skipped by the standalone mux loop — only the hybrid output remains."""
|
||||
skipped by the standalone mux loop; only the hybrid output remains."""
|
||||
hybrid_selected = list(filter(Tracks().select_hybrid(ladder, [1080]), ladder))
|
||||
Tracks.flag_hybrid_ingredients(hybrid_selected, [])
|
||||
assert flagged(ladder) == {"hdr10p-1080", "dv-360"}
|
||||
@@ -177,7 +177,7 @@ def test_flag_hybrid_only_flags_base_and_ingredient_dv(ladder: list[Video]) -> N
|
||||
|
||||
def test_flag_hybrid_plus_hdr10p_keeps_base_deliverable(ladder: list[Video]) -> None:
|
||||
"""`-r HYBRID,HDR10P`: the base is also an explicit deliverable, only the
|
||||
ingredient DV is skipped — hybrid + standalone HDR10+ are muxed."""
|
||||
ingredient DV is skipped; hybrid + standalone HDR10+ are muxed."""
|
||||
hybrid_selected = list(filter(Tracks().select_hybrid(ladder, [1080]), ladder))
|
||||
base = next(t for t in ladder if t.id == "hdr10p-1080")
|
||||
Tracks.flag_hybrid_ingredients(hybrid_selected, [base])
|
||||
@@ -186,7 +186,7 @@ def test_flag_hybrid_plus_hdr10p_keeps_base_deliverable(ladder: list[Video]) ->
|
||||
|
||||
def test_flag_hybrid_plus_hdr10p_and_dv_keeps_both_deliverables(ladder: list[Video]) -> None:
|
||||
"""`-r HYBRID,HDR10P,DV`: best DV is a deliverable, lowest DV stays
|
||||
ingredient-only — hybrid + HDR10+ + DV are muxed."""
|
||||
ingredient-only; hybrid + HDR10+ + DV are muxed."""
|
||||
hybrid_selected = list(filter(Tracks().select_hybrid(ladder, [1080]), ladder))
|
||||
base = next(t for t in ladder if t.id == "hdr10p-1080")
|
||||
best_dv = next(t for t in ladder if t.id == "dv-1080")
|
||||
|
||||
Reference in New Issue
Block a user