style: run ruff format over the codebase

This commit is contained in:
imSp4rky
2026-07-15 14:49:27 -06:00
parent d853ef9600
commit e31e60a418
39 changed files with 492 additions and 186 deletions
+9 -2
View File
@@ -15,8 +15,15 @@ from types import SimpleNamespace
import pytest
from unshackle.core.cdm import loader as loader_mod
from unshackle.core.cdm.detect import (cdm_location, is_local_cdm, is_playready_cdm, is_remote_cdm,
is_remote_playready_cdm, is_remote_widevine_cdm, is_widevine_cdm)
from unshackle.core.cdm.detect import (
cdm_location,
is_local_cdm,
is_playready_cdm,
is_remote_cdm,
is_remote_playready_cdm,
is_remote_widevine_cdm,
is_widevine_cdm,
)
from unshackle.core.cdm.loader import _stamp_remote
pytestmark = pytest.mark.unit
+1
View File
@@ -16,6 +16,7 @@ from typing import Any, Optional
from uuid import UUID
import pytest
# lxml.etree: XML parser used to build ContentProtection fixtures for DASH.get_drm
from lxml import etree
+7 -2
View File
@@ -9,8 +9,13 @@ from pathlib import Path
import pytest
from unshackle.core.utilities import (close_debug_logger, get_debug_logger, init_debug_logger, log_event,
timed_operation)
from unshackle.core.utilities import (
close_debug_logger,
get_debug_logger,
init_debug_logger,
log_event,
timed_operation,
)
from unshackle.core.utils.redact import redact_all, redact_path, redact_text, redact_url, safe_display_url
from unshackle.core.utils.subprocess import log_tool_run
+18 -6
View File
@@ -12,12 +12,24 @@ import struct
import pytest
from unshackle.core.manifests.ism_init import (NAL_START_CODE, PIFF_SENC_UUID, box, build_avcc, build_dec3,
build_hvcc, build_init_segment, full_box,
parse_codec_private_data_colour, parse_codec_private_data_vui,
parse_hevc_sps_format, read_per_sample_iv_size, read_track_id,
remove_emulation_prevention, split_nal_units,
synthesize_aac_codec_private_data)
from unshackle.core.manifests.ism_init import (
NAL_START_CODE,
PIFF_SENC_UUID,
box,
build_avcc,
build_dec3,
build_hvcc,
build_init_segment,
full_box,
parse_codec_private_data_colour,
parse_codec_private_data_vui,
parse_hevc_sps_format,
read_per_sample_iv_size,
read_track_id,
remove_emulation_prevention,
split_nal_units,
synthesize_aac_codec_private_data,
)
# Real CodecPrivateData taken from a Smooth Streaming manifest.
VIDEO_HEVC_CPD = (
+2 -2
View File
@@ -4,11 +4,11 @@ undecidable from the manifest; the post-mux bitstream probe names it."""
from __future__ import annotations
from tests.core.test_ism_init import VIDEO_HEVC10_CPD, VIDEO_HEVC_DV_CPD, VIDEO_HEVC_PQ_CPD, VIDEO_HEVC_SDR_CPD
from unshackle.core.manifests import ISM
from unshackle.core.tracks import Video
from tests.core.test_ism_init import (VIDEO_HEVC10_CPD, VIDEO_HEVC_DV_CPD, VIDEO_HEVC_HLG_CPD, # isort: skip
VIDEO_HEVC_PQ_CPD, VIDEO_HEVC_SDR_CPD)
from tests.core.test_ism_init import VIDEO_HEVC_HLG_CPD # isort: skip
def manifest_xml(cpd: str, fourcc: str = "HVC1") -> str:
+25 -5
View File
@@ -12,8 +12,14 @@ from typing import Any, Optional
from rich.console import Console, RenderableType
from rich.panel import Panel
from unshackle.core.music.display import (MusicHeaderInfo, TrackRow, format_track_detail_quality, render_album_header,
render_artwork_preview, render_track_panel)
from unshackle.core.music.display import (
MusicHeaderInfo,
TrackRow,
format_track_detail_quality,
render_album_header,
render_artwork_preview,
render_track_panel,
)
from unshackle.core.titles.music import Song
@@ -97,8 +103,20 @@ def test_format_track_detail_quality_passthrough() -> None:
def test_render_track_panel_returns_panel() -> None:
rows = [
TrackRow(song=make_song(track=1, name="First"), quality_label="FLAC 16-bit/44.1kHz", layout="Stereo", duration_str="3:00", cd=True),
TrackRow(song=make_song(track=2, name="Second"), quality_label="OGG 320", layout="Stereo", duration_str="4:00", hires=True),
TrackRow(
song=make_song(track=1, name="First"),
quality_label="FLAC 16-bit/44.1kHz",
layout="Stereo",
duration_str="3:00",
cd=True,
),
TrackRow(
song=make_song(track=2, name="Second"),
quality_label="OGG 320",
layout="Stereo",
duration_str="4:00",
hires=True,
),
]
panel = render_track_panel(rows, total=len(rows))
assert isinstance(panel, Panel)
@@ -112,7 +130,9 @@ def test_render_track_panel_returns_panel() -> None:
def test_render_track_panel_singular_label() -> None:
rows = [TrackRow(song=make_song(name="Only"), quality_label="FLAC 16-bit/44.1kHz", layout="Stereo", duration_str="3:00")]
rows = [
TrackRow(song=make_song(name="Only"), quality_label="FLAC 16-bit/44.1kHz", layout="Stereo", duration_str="3:00")
]
text = render_to_text(render_track_panel(rows, total=1))
# The count node reads "1 Track" (singular); the panel title is always
# "Available Tracks", so we only check the count node phrasing here.
+11 -3
View File
@@ -6,9 +6,17 @@ from typing import Any, Optional
import pytest
from unshackle.core.music.extract import (build_music_from_songs, classify_release_kind, dedupe_track_options,
duration_seconds, first_number, first_text, format_duration, format_names,
year_from_value)
from unshackle.core.music.extract import (
build_music_from_songs,
classify_release_kind,
dedupe_track_options,
duration_seconds,
first_number,
first_text,
format_duration,
format_names,
year_from_value,
)
from unshackle.core.music.models import MusicTrackOption
from unshackle.core.titles.music import Music, Song
+1 -3
View File
@@ -115,9 +115,7 @@ def hand_built_ctx(body: dict[str, Any]) -> click.Context:
ctx = click.Context(parsed.command)
ctx.params = {"repack": body.get("repack", False)}
for name in ctx.params:
ctx.set_parameter_source(
name, ParameterSource.COMMANDLINE if name in body else ParameterSource.DEFAULT
)
ctx.set_parameter_source(name, ParameterSource.COMMANDLINE if name in body else ParameterSource.DEFAULT)
return ctx
+1 -3
View File
@@ -240,9 +240,7 @@ def test_refresh_reports_discarded_local_edits(monkeypatch, tmp_path):
def test_changed_services_up_to_date(monkeypatch):
monkeypatch.setattr(service_repo.binaries, "Git", "git")
# same HEAD before/after → no diff computed at all
monkeypatch.setattr(
service_repo.subprocess, "run", lambda *a, **k: pytest.fail("no git diff when HEAD unchanged")
)
monkeypatch.setattr(service_repo.subprocess, "run", lambda *a, **k: pytest.fail("no git diff when HEAD unchanged"))
assert service_repo._changed_services(Path("/x"), "same", "same") == []
@@ -24,24 +24,80 @@ from unshackle.core.utilities import is_exact_match
# Base language vs regional variant that CLDR rates at distance 0 (paradigm locales). These are
# the pairs that collapse under naive exact matching.
COLLAPSING_PAIRS = [
("ar", "ar-EG"), ("en", "en-US"), ("pt", "pt-BR"), ("fr", "fr-FR"),
("es", "es-ES"), ("de", "de-DE"), ("it", "it-IT"),
("ar", "ar-EG"),
("en", "en-US"),
("pt", "pt-BR"),
("fr", "fr-FR"),
("es", "es-ES"),
("de", "de-DE"),
("it", "it-IT"),
]
# Pairs CLDR rates at distance >0, which exact matching already separates. Never collapsed.
# Includes two-regional splits (no bare base) as services more commonly ship them.
DISTINCT_PAIRS = [
("es", "es-419"), ("fr", "fr-CA"), ("fr-FR", "fr-CA"), ("pt-PT", "pt-BR"), ("es-ES", "es-419"),
("en-US", "en-GB"), ("de-DE", "de-AT"), ("it-IT", "it-CH"), ("es-419", "es-AR"), ("no", "nn"),
("es", "es-419"),
("fr", "fr-CA"),
("fr-FR", "fr-CA"),
("pt-PT", "pt-BR"),
("es-ES", "es-419"),
("en-US", "en-GB"),
("de-DE", "de-AT"),
("it-IT", "it-CH"),
("es-419", "es-AR"),
("no", "nn"),
]
# A full real-world audio language set for one title, the kind of mix a streaming manifest ships,
# which must resolve 1:1 with no collision or loss. Tags are drawn from published streaming
# language references (Warner Bros. Discovery / Netflix) and include regional and script variants.
REAL_WORLD_AUDIO_LANGS = [
"ar", "ar-EG", "ar-SA", "bn-IN", "cmn-TW", "cs", "da", "de", "de-AT", "de-CH", "el", "en",
"en-GB", "en-AU", "es-419", "es-AR", "es-ES", "fi", "fr-CA", "fr-FR", "he", "hi", "hu", "id",
"it", "it-CH", "ja", "ko", "nl", "no", "pl", "pt-BR", "pt-PT", "ro", "ru", "sk", "sr",
"sr-Latn", "sv", "ta", "th", "tr", "uk", "vi", "yue", "zh-Hans", "zh-Hant",
"ar",
"ar-EG",
"ar-SA",
"bn-IN",
"cmn-TW",
"cs",
"da",
"de",
"de-AT",
"de-CH",
"el",
"en",
"en-GB",
"en-AU",
"es-419",
"es-AR",
"es-ES",
"fi",
"fr-CA",
"fr-FR",
"he",
"hi",
"hu",
"id",
"it",
"it-CH",
"ja",
"ko",
"nl",
"no",
"pl",
"pt-BR",
"pt-PT",
"ro",
"ru",
"sk",
"sr",
"sr-Latn",
"sv",
"ta",
"th",
"tr",
"uk",
"vi",
"yue",
"zh-Hans",
"zh-Hant",
]
@@ -9,8 +9,13 @@ import pytest
from aiohttp import web
from unshackle.core.api import handlers
from unshackle.core.api.download_manager import (DownloadJob, JobStatus, _redact_parameters, _redact_text,
_secret_values)
from unshackle.core.api.download_manager import (
DownloadJob,
JobStatus,
_redact_parameters,
_redact_text,
_secret_values,
)
from unshackle.core.api.errors import APIError, APIErrorCode
pytestmark = pytest.mark.unit
@@ -116,9 +121,7 @@ async def test_cdm_override_allowed_when_enabled(stub_handler):
async def test_cdm_override_allowlist_permits_only_named_device(stub_handler):
stub_handler.setattr(handlers.config, "serve", {"cdm_overrides": ["good"]})
assert isinstance(
await handlers.download_handler({"service": "ATV", "title_id": "t", "cdm": "good"}), web.Response
)
assert isinstance(await handlers.download_handler({"service": "ATV", "title_id": "t", "cdm": "good"}), web.Response)
with pytest.raises(APIError) as ei:
await handlers.download_handler({"service": "ATV", "title_id": "t", "cdm": "other"})
assert ei.value.error_code == APIErrorCode.FORBIDDEN
+7 -2
View File
@@ -6,8 +6,13 @@ import json
import pytest
from unshackle.core.api.errors import (APIError, APIErrorCode, build_error_response, categorize_exception,
handle_api_exception)
from unshackle.core.api.errors import (
APIError,
APIErrorCode,
build_error_response,
categorize_exception,
handle_api_exception,
)
pytestmark = pytest.mark.unit
+10 -3
View File
@@ -5,9 +5,16 @@ from __future__ import annotations
import pytest
from langcodes import Language
from unshackle.core.api.handlers import (sanitize_log, serialize_audio_track, serialize_drm, serialize_subtitle_track,
serialize_title, serialize_video_track, validate_download_parameters,
validate_service)
from unshackle.core.api.handlers import (
sanitize_log,
serialize_audio_track,
serialize_drm,
serialize_subtitle_track,
serialize_title,
serialize_video_track,
validate_download_parameters,
validate_service,
)
from unshackle.core.titles.episode import Episode
from unshackle.core.titles.movie import Movie
from unshackle.core.tracks import Audio, Subtitle, Video
+7 -5
View File
@@ -9,8 +9,12 @@ from __future__ import annotations
import pytest
from unshackle.core.api.progress import (DOWNLOAD_PROGRESS_CEILING, build_job_progress_callables,
track_progress_label, track_progress_weight)
from unshackle.core.api.progress import (
DOWNLOAD_PROGRESS_CEILING,
build_job_progress_callables,
track_progress_label,
track_progress_weight,
)
pytestmark = pytest.mark.unit
@@ -76,9 +80,7 @@ def test_weighting_makes_video_dominate_progress():
def test_active_tracks_labels_reported_and_cleared_on_done():
updates: list[dict] = []
cbs = build_job_progress_callables(
[Video(2160, "DV"), Audio("en-US", "2.0")], [_noop, _noop], updates.append
)
cbs = build_job_progress_callables([Video(2160, "DV"), Audio("en-US", "2.0")], [_noop, _noop], updates.append)
cbs[0](total=100, completed=10) # video downloading
assert updates[-1]["active_tracks"] == ["video 2160p DV"]
@@ -6,8 +6,16 @@ from enum import Enum
import pytest
from unshackle.core.remote_service import (_build_title, _build_tracks, _deserialize_audio, _deserialize_subtitle,
_deserialize_video, _enum_get, _match_track, _reconstruct_drm)
from unshackle.core.remote_service import (
_build_title,
_build_tracks,
_deserialize_audio,
_deserialize_subtitle,
_deserialize_video,
_enum_get,
_match_track,
_reconstruct_drm,
)
from unshackle.core.titles.episode import Episode
from unshackle.core.titles.movie import Movie
from unshackle.core.tracks import Audio, Subtitle, Video
+35 -14
View File
@@ -90,8 +90,11 @@ def test_failed_subtitle_does_not_truncate_video_or_audio():
h = Harness(fail_ids={"s-he"})
download_tracks_in_passes(
[video, audio, sub], 4, h.run_one,
skip_subtitle_errors=True, on_subtitle_skipped=h.on_subtitle_skipped,
[video, audio, sub],
4,
h.run_one,
skip_subtitle_errors=True,
on_subtitle_skipped=h.on_subtitle_skipped,
)
assert set(h.completed) == {"v", "a"} # both fatal tracks fully downloaded
@@ -105,8 +108,11 @@ def test_good_subtitle_kept_bad_subtitle_skipped():
h = Harness(fail_ids={"s-fr"})
download_tracks_in_passes(
[video, good, bad], 4, h.run_one,
skip_subtitle_errors=True, on_subtitle_skipped=h.on_subtitle_skipped,
[video, good, bad],
4,
h.run_one,
skip_subtitle_errors=True,
on_subtitle_skipped=h.on_subtitle_skipped,
)
assert "s-en" in h.completed # the available subtitle still downloaded
@@ -121,8 +127,11 @@ def test_subtitle_failure_stays_fatal_without_flag():
with pytest.raises(RuntimeError):
download_tracks_in_passes(
[video, sub], 4, h.run_one,
skip_subtitle_errors=False, on_subtitle_skipped=h.on_subtitle_skipped,
[video, sub],
4,
h.run_one,
skip_subtitle_errors=False,
on_subtitle_skipped=h.on_subtitle_skipped,
)
assert not DOWNLOAD_CANCELLED.is_set() # the finally clears the event even on the fatal path
@@ -135,8 +144,11 @@ def test_cancel_event_is_reset_between_titles():
h = Harness(fail_ids=set())
download_tracks_in_passes(
[video, audio], 4, h.run_one,
skip_subtitle_errors=True, on_subtitle_skipped=h.on_subtitle_skipped,
[video, audio],
4,
h.run_one,
skip_subtitle_errors=True,
on_subtitle_skipped=h.on_subtitle_skipped,
)
assert set(h.completed) == {"v", "a"}
@@ -150,8 +162,11 @@ def test_cancel_event_cleared_after_failed_final_subtitle():
h = Harness(fail_ids={"s-he"})
download_tracks_in_passes(
[video, sub], 4, h.run_one,
skip_subtitle_errors=True, on_subtitle_skipped=h.on_subtitle_skipped,
[video, sub],
4,
h.run_one,
skip_subtitle_errors=True,
on_subtitle_skipped=h.on_subtitle_skipped,
)
assert not DOWNLOAD_CANCELLED.is_set() # the helper clears it on exit for any later code
@@ -164,8 +179,11 @@ def test_all_subtitles_skipped_video_audio_kept():
h = Harness(fail_ids={"s-en", "s-he"})
download_tracks_in_passes(
[video, audio, s1, s2], 4, h.run_one,
skip_subtitle_errors=True, on_subtitle_skipped=h.on_subtitle_skipped,
[video, audio, s1, s2],
4,
h.run_one,
skip_subtitle_errors=True,
on_subtitle_skipped=h.on_subtitle_skipped,
)
assert set(h.completed) == {"v", "a"} # both fatal tracks survived
@@ -179,8 +197,11 @@ def test_duplicate_language_subtitles_distinguished_by_id():
h = Harness(fail_ids={"en-forced", "en-sdh"})
download_tracks_in_passes(
[make_video(), forced, sdh], 4, h.run_one,
skip_subtitle_errors=True, on_subtitle_skipped=h.on_subtitle_skipped,
[make_video(), forced, sdh],
4,
h.run_one,
skip_subtitle_errors=True,
on_subtitle_skipped=h.on_subtitle_skipped,
)
assert [t.id for t in h.skipped] == ["en-forced", "en-sdh"] # same language, distinct ids
+1 -5
View File
@@ -103,11 +103,7 @@ def test_merge_dedupes_repeated_style_blocks_before_cues():
def test_merge_skips_malformed_timing_line():
vtt = (
"WEBVTT\n\n"
"garbage --> not-a-time\nBroken\n\n"
"00:00:01.000 --> 00:00:02.000\nGood\n"
)
vtt = "WEBVTT\n\ngarbage --> not-a-time\nBroken\n\n00:00:01.000 --> 00:00:02.000\nGood\n"
out = merge_segmented_webvtt(vtt)
assert "Good" in out
assert "Broken" not in out