mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-12 12:05:07 +00:00
Supplied IDs resolve directly via a shared resolve_by_ids and never fall back to title search; metadata_providers gains per-kind ordering and a disable_metadata switch stops all automatic lookups while explicit IDs keep working. BREAKING: --tmdb/--imdb/--tvdb are now mutually exclusive and require a usable provider; --enrich also overwrites original language; the imdbapi provider and imdb_api_enabled are replaced by a keyless imdb provider.
132 lines
5.2 KiB
Python
132 lines
5.2 KiB
Python
"""Precedence of user-supplied external IDs in providers.resolve_by_ids."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Optional, Union
|
|
|
|
import pytest
|
|
|
|
from unshackle.core import providers
|
|
from unshackle.core.config import config
|
|
from unshackle.core.providers._base import ExternalIds, MetadataProvider, MetadataResult
|
|
|
|
|
|
class FakeProvider(MetadataProvider):
|
|
"""Stands in for a real provider; records the ID it was asked for."""
|
|
|
|
def __init__(self, name: str, result: Optional[MetadataResult] = None, raises: bool = False) -> None:
|
|
self.NAME = name
|
|
self._result = result
|
|
self._raises = raises
|
|
self.asked: list[Union[int, str]] = []
|
|
super().__init__()
|
|
|
|
def is_available(self) -> bool:
|
|
return True
|
|
|
|
def search(self, title: str, year: Optional[int], kind: str) -> Optional[MetadataResult]:
|
|
raise AssertionError("resolve_by_ids must not search when an ID was supplied")
|
|
|
|
def get_by_id(self, provider_id: Union[int, str], kind: str) -> Optional[MetadataResult]:
|
|
self.asked.append(provider_id)
|
|
if self._raises:
|
|
raise ValueError("provider is down")
|
|
return self._result
|
|
|
|
def get_external_ids(self, provider_id: Union[int, str], kind: str) -> ExternalIds:
|
|
return ExternalIds()
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _isolate(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""Keep every test off the network and on the default provider order."""
|
|
monkeypatch.setattr(config, "metadata_providers", [])
|
|
monkeypatch.setattr(providers, "fetch_external_ids", lambda *a, **kw: ExternalIds())
|
|
monkeypatch.setattr(providers, "enrich_ids", lambda result: None)
|
|
monkeypatch.setattr(
|
|
providers,
|
|
"search_metadata",
|
|
lambda *a, **kw: pytest.fail("search_metadata must not run when an ID was supplied"),
|
|
)
|
|
|
|
|
|
def _patch_providers(monkeypatch: pytest.MonkeyPatch, available: dict[str, FakeProvider]) -> None:
|
|
monkeypatch.setattr(providers, "get_provider", lambda name: available.get(name))
|
|
|
|
|
|
def test_supplied_ids_survive_a_providers_own_answer(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
# tmdb answers with a different imdb id; the one the user gave must win
|
|
tmdb = FakeProvider(
|
|
"tmdb",
|
|
MetadataResult(title="Inception", year=2010, kind="movie", external_ids=ExternalIds(imdb_id="tt9999999")),
|
|
)
|
|
_patch_providers(monkeypatch, {"tmdb": tmdb})
|
|
|
|
result = providers.resolve_by_ids(27205, "tt1375666", kind="movie")
|
|
|
|
assert result is not None
|
|
assert result.external_ids.tmdb_id == 27205
|
|
assert result.external_ids.tmdb_kind == "movie"
|
|
assert result.external_ids.imdb_id == "tt1375666"
|
|
|
|
|
|
def test_imdb_falls_through_to_omdb(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
imdb = FakeProvider("imdb", raises=True)
|
|
omdb = FakeProvider("omdb", MetadataResult(title="Inception", year=2010, kind="movie"))
|
|
_patch_providers(monkeypatch, {"imdb": imdb, "omdb": omdb})
|
|
|
|
result = providers.resolve_by_ids(imdb_id="tt1375666", kind="movie")
|
|
|
|
assert result is not None
|
|
assert result.title == "Inception"
|
|
assert imdb.asked == ["tt1375666"]
|
|
assert omdb.asked == ["tt1375666"]
|
|
|
|
|
|
def test_all_providers_down_synthesizes_a_result(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
_patch_providers(monkeypatch, {})
|
|
|
|
result = providers.resolve_by_ids(tvdb_id=73871, title="Futurama", year=1999, kind="tv")
|
|
|
|
assert result is not None
|
|
assert result.title == "Futurama"
|
|
assert result.year == 1999
|
|
assert result.external_ids.tvdb_id == 73871
|
|
|
|
|
|
def test_no_ids_searches(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
calls: list[tuple] = []
|
|
monkeypatch.setattr(providers, "search_metadata", lambda *a, **kw: calls.append(a) or None)
|
|
_patch_providers(monkeypatch, {})
|
|
|
|
providers.resolve_by_ids(title="Futurama", year=1999, kind="tv")
|
|
|
|
assert calls and calls[0][:3] == ("Futurama", 1999, "tv")
|
|
|
|
|
|
def test_no_ids_and_no_title_returns_none(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
_patch_providers(monkeypatch, {})
|
|
assert providers.resolve_by_ids(kind="tv") is None
|
|
|
|
|
|
def test_per_kind_order_puts_tvdb_first_for_tv(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(config, "metadata_providers", {"tv": ["tvdb", "tmdb", "imdb", "omdb"]})
|
|
tvdb = FakeProvider("tvdb", MetadataResult(title="Futurama", year=1999, kind="tv"))
|
|
tmdb = FakeProvider("tmdb", MetadataResult(title="Wrong Show", year=2020, kind="tv"))
|
|
_patch_providers(monkeypatch, {"tvdb": tvdb, "tmdb": tmdb})
|
|
|
|
result = providers.resolve_by_ids(615, tvdb_id=73871, kind="tv")
|
|
|
|
assert result is not None
|
|
assert result.title == "Futurama"
|
|
assert tmdb.asked == []
|
|
# movies keep the default order, so tmdb is consulted before tvdb there
|
|
assert [cls.NAME for cls in providers.provider_order("movie")] == list(providers.DEFAULT_ORDER)
|
|
|
|
|
|
def test_flat_list_config_applies_to_both_kinds(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(config, "metadata_providers", ["tvdb", "tmdb"])
|
|
assert [cls.NAME for cls in providers.provider_order("tv")] == ["tvdb", "tmdb"]
|
|
assert [cls.NAME for cls in providers.provider_order("movie")] == ["tvdb", "tmdb"]
|
|
assert [cls.NAME for cls in providers.provider_order()] == ["tvdb", "tmdb"]
|