refactor(application): inject kb/prober into resolve_destination use cases
Remove the module-level _KB / _PROBER singletons from
alfred/application/filesystem/resolve_destination.py. The four
resolve_{season,episode,movie,series}_destination use cases now take
kb: ReleaseKnowledge and prober: MediaProber as required arguments,
matching the shape of inspect_release.
The singletons now live at the agent-tools frontier
(alfred/agent/tools/filesystem.py), where the LLM-facing wrappers
instantiate YamlReleaseKnowledge / FfprobeMediaProber once and thread
them through. The wrappers' Python signatures are unchanged — the
inspect-based JSON-schema generator in agent/registry.py still sees the
same LLM-passable params.
analyze_release drops the dirty 'from ... import _KB' indirection.
Tests inject their own stubs by keyword (prober=_StubProber(...)) via
thin convenience wrappers, replacing the prior
monkeypatch.setattr(rd, '_PROBER', ...) pattern.
testing/debug_release.py: instantiate YamlReleaseKnowledge() /
FfprobeMediaProber() inline at the two call sites.
Suite: 1077 passed.
This commit is contained in:
@@ -57,6 +57,21 @@ callers).
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- **`resolve_destination` use cases take `kb` / `prober` as required
|
||||||
|
params; module-level singletons gone.** The four
|
||||||
|
`resolve_{season,episode,movie,series}_destination` use cases now
|
||||||
|
accept `kb: ReleaseKnowledge` and `prober: MediaProber` as required
|
||||||
|
arguments, matching the shape of `inspect_release`. The module-level
|
||||||
|
`_KB = YamlReleaseKnowledge()` and `_PROBER = FfprobeMediaProber()`
|
||||||
|
singletons that previously lived in
|
||||||
|
`alfred/application/filesystem/resolve_destination.py` are removed —
|
||||||
|
the application layer no longer reaches into infrastructure. The
|
||||||
|
singletons now live at the agent-tools frontier
|
||||||
|
(`alfred/agent/tools/filesystem.py`), where the LLM-facing wrappers
|
||||||
|
instantiate them once and thread them through. `analyze_release` no
|
||||||
|
longer needs the dirty `from ... import _KB` indirection. Tests
|
||||||
|
inject their own stubs by keyword (`prober=_StubProber(...)`) instead
|
||||||
|
of monkeypatching a module attribute.
|
||||||
- **`ParsePath` enum renamed to `TokenizationRoute`.** The old name
|
- **`ParsePath` enum renamed to `TokenizationRoute`.** The old name
|
||||||
collided with `pathlib.Path` in code-reading mental models, and was
|
collided with `pathlib.Path` in code-reading mental models, and was
|
||||||
one letter from `parse_path` (the field that holds the value) — making
|
one letter from `parse_path` (the field that holds the value) — making
|
||||||
|
|||||||
@@ -26,10 +26,15 @@ from alfred.application.filesystem.resolve_destination import (
|
|||||||
resolve_series_destination as _resolve_series_destination,
|
resolve_series_destination as _resolve_series_destination,
|
||||||
)
|
)
|
||||||
from alfred.infrastructure.filesystem import FileManager, create_folder, move
|
from alfred.infrastructure.filesystem import FileManager, create_folder, move
|
||||||
|
from alfred.infrastructure.knowledge.release_kb import YamlReleaseKnowledge
|
||||||
from alfred.infrastructure.metadata import MetadataStore
|
from alfred.infrastructure.metadata import MetadataStore
|
||||||
from alfred.infrastructure.persistence import get_memory
|
from alfred.infrastructure.persistence import get_memory
|
||||||
from alfred.infrastructure.probe import FfprobeMediaProber
|
from alfred.infrastructure.probe import FfprobeMediaProber
|
||||||
|
|
||||||
|
# Agent-tools frontier: this is the legitimate home for the singletons that
|
||||||
|
# back every LLM-exposed wrapper. The use cases below take ``kb`` / ``prober``
|
||||||
|
# as required params; tests inject their own stubs.
|
||||||
|
_KB = YamlReleaseKnowledge()
|
||||||
_PROBER = FfprobeMediaProber()
|
_PROBER = FfprobeMediaProber()
|
||||||
|
|
||||||
_LEARNED_ROOT = Path(_alfred_pkg.__file__).parent.parent / "data" / "knowledge"
|
_LEARNED_ROOT = Path(_alfred_pkg.__file__).parent.parent / "data" / "knowledge"
|
||||||
@@ -60,7 +65,13 @@ def resolve_season_destination(
|
|||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Thin tool wrapper — semantics live in alfred/agent/tools/specs/resolve_season_destination.yaml."""
|
"""Thin tool wrapper — semantics live in alfred/agent/tools/specs/resolve_season_destination.yaml."""
|
||||||
return _resolve_season_destination(
|
return _resolve_season_destination(
|
||||||
release_name, tmdb_title, tmdb_year, confirmed_folder, source_path
|
release_name,
|
||||||
|
tmdb_title,
|
||||||
|
tmdb_year,
|
||||||
|
_KB,
|
||||||
|
_PROBER,
|
||||||
|
confirmed_folder,
|
||||||
|
source_path,
|
||||||
).to_dict()
|
).to_dict()
|
||||||
|
|
||||||
|
|
||||||
@@ -78,6 +89,8 @@ def resolve_episode_destination(
|
|||||||
source_file,
|
source_file,
|
||||||
tmdb_title,
|
tmdb_title,
|
||||||
tmdb_year,
|
tmdb_year,
|
||||||
|
_KB,
|
||||||
|
_PROBER,
|
||||||
tmdb_episode_title,
|
tmdb_episode_title,
|
||||||
confirmed_folder,
|
confirmed_folder,
|
||||||
).to_dict()
|
).to_dict()
|
||||||
@@ -91,7 +104,7 @@ def resolve_movie_destination(
|
|||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Thin tool wrapper — semantics live in alfred/agent/tools/specs/resolve_movie_destination.yaml."""
|
"""Thin tool wrapper — semantics live in alfred/agent/tools/specs/resolve_movie_destination.yaml."""
|
||||||
return _resolve_movie_destination(
|
return _resolve_movie_destination(
|
||||||
release_name, source_file, tmdb_title, tmdb_year
|
release_name, source_file, tmdb_title, tmdb_year, _KB, _PROBER
|
||||||
).to_dict()
|
).to_dict()
|
||||||
|
|
||||||
|
|
||||||
@@ -104,7 +117,13 @@ def resolve_series_destination(
|
|||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Thin tool wrapper — semantics live in alfred/agent/tools/specs/resolve_series_destination.yaml."""
|
"""Thin tool wrapper — semantics live in alfred/agent/tools/specs/resolve_series_destination.yaml."""
|
||||||
return _resolve_series_destination(
|
return _resolve_series_destination(
|
||||||
release_name, tmdb_title, tmdb_year, confirmed_folder, source_path
|
release_name,
|
||||||
|
tmdb_title,
|
||||||
|
tmdb_year,
|
||||||
|
_KB,
|
||||||
|
_PROBER,
|
||||||
|
confirmed_folder,
|
||||||
|
source_path,
|
||||||
).to_dict()
|
).to_dict()
|
||||||
|
|
||||||
|
|
||||||
@@ -191,7 +210,6 @@ def set_path_for_folder(folder_name: str, path_value: str) -> dict[str, Any]:
|
|||||||
|
|
||||||
def analyze_release(release_name: str, source_path: str) -> dict[str, Any]:
|
def analyze_release(release_name: str, source_path: str) -> dict[str, Any]:
|
||||||
"""Thin tool wrapper — semantics live in alfred/agent/tools/specs/analyze_release.yaml."""
|
"""Thin tool wrapper — semantics live in alfred/agent/tools/specs/analyze_release.yaml."""
|
||||||
from alfred.application.filesystem.resolve_destination import _KB # noqa: PLC0415
|
|
||||||
from alfred.application.release import inspect_release # noqa: PLC0415
|
from alfred.application.release import inspect_release # noqa: PLC0415
|
||||||
|
|
||||||
result = inspect_release(release_name, Path(source_path), _KB, _PROBER)
|
result = inspect_release(release_name, Path(source_path), _KB, _PROBER)
|
||||||
|
|||||||
@@ -26,34 +26,30 @@ from alfred.application.release import inspect_release
|
|||||||
from alfred.domain.release import parse_release
|
from alfred.domain.release import parse_release
|
||||||
from alfred.domain.release.ports import ReleaseKnowledge
|
from alfred.domain.release.ports import ReleaseKnowledge
|
||||||
from alfred.domain.release.value_objects import ParsedRelease
|
from alfred.domain.release.value_objects import ParsedRelease
|
||||||
from alfred.infrastructure.knowledge.release_kb import YamlReleaseKnowledge
|
from alfred.domain.shared.ports import MediaProber
|
||||||
from alfred.infrastructure.persistence import get_memory
|
from alfred.infrastructure.persistence import get_memory
|
||||||
from alfred.infrastructure.probe import FfprobeMediaProber
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Single module-level knowledge instance. YAML is loaded once at first import.
|
|
||||||
# Tests that need a custom KB can monkeypatch this attribute.
|
|
||||||
_KB: ReleaseKnowledge = YamlReleaseKnowledge()
|
|
||||||
|
|
||||||
# Module-level prober — same singleton style as _KB. Tests that need a custom
|
def _resolve_parsed(
|
||||||
# adapter can monkeypatch this attribute.
|
release_name: str,
|
||||||
_PROBER = FfprobeMediaProber()
|
source_path: str | None,
|
||||||
|
kb: ReleaseKnowledge,
|
||||||
|
prober: MediaProber,
|
||||||
def _resolve_parsed(release_name: str, source_path: str | None) -> ParsedRelease:
|
) -> ParsedRelease:
|
||||||
"""Pick the right entry point depending on whether we have a path.
|
"""Pick the right entry point depending on whether we have a path.
|
||||||
|
|
||||||
When ``source_path`` is provided and points to something that exists,
|
When ``source_path`` is provided and points to something that exists,
|
||||||
we run the full inspection pipeline so probe data can refresh
|
we run the full inspection pipeline so probe data can refresh tech
|
||||||
``tech_string`` (which feeds every filename builder). Otherwise we
|
fields (which feed every filename builder). Otherwise we fall back
|
||||||
fall back to a parse-only path — same behavior as before.
|
to a parse-only path — same behavior as before.
|
||||||
"""
|
"""
|
||||||
if source_path:
|
if source_path:
|
||||||
path = Path(source_path)
|
path = Path(source_path)
|
||||||
if path.exists():
|
if path.exists():
|
||||||
return inspect_release(release_name, path, _KB, _PROBER).parsed
|
return inspect_release(release_name, path, kb, prober).parsed
|
||||||
parsed, _ = parse_release(release_name, _KB)
|
parsed, _ = parse_release(release_name, kb)
|
||||||
return parsed
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
@@ -259,6 +255,8 @@ def resolve_season_destination(
|
|||||||
release_name: str,
|
release_name: str,
|
||||||
tmdb_title: str,
|
tmdb_title: str,
|
||||||
tmdb_year: int,
|
tmdb_year: int,
|
||||||
|
kb: ReleaseKnowledge,
|
||||||
|
prober: MediaProber,
|
||||||
confirmed_folder: str | None = None,
|
confirmed_folder: str | None = None,
|
||||||
source_path: str | None = None,
|
source_path: str | None = None,
|
||||||
) -> ResolvedSeasonDestination:
|
) -> ResolvedSeasonDestination:
|
||||||
@@ -280,8 +278,8 @@ def resolve_season_destination(
|
|||||||
message="TV show library path is not configured.",
|
message="TV show library path is not configured.",
|
||||||
)
|
)
|
||||||
|
|
||||||
parsed = _resolve_parsed(release_name, source_path)
|
parsed = _resolve_parsed(release_name, source_path, kb, prober)
|
||||||
tmdb_title_safe = _KB.sanitize_for_fs(tmdb_title)
|
tmdb_title_safe = kb.sanitize_for_fs(tmdb_title)
|
||||||
computed_name = parsed.show_folder_name(tmdb_title_safe, tmdb_year)
|
computed_name = parsed.show_folder_name(tmdb_title_safe, tmdb_year)
|
||||||
|
|
||||||
resolved = _resolve_series_folder(
|
resolved = _resolve_series_folder(
|
||||||
@@ -314,6 +312,8 @@ def resolve_episode_destination(
|
|||||||
source_file: str,
|
source_file: str,
|
||||||
tmdb_title: str,
|
tmdb_title: str,
|
||||||
tmdb_year: int,
|
tmdb_year: int,
|
||||||
|
kb: ReleaseKnowledge,
|
||||||
|
prober: MediaProber,
|
||||||
tmdb_episode_title: str | None = None,
|
tmdb_episode_title: str | None = None,
|
||||||
confirmed_folder: str | None = None,
|
confirmed_folder: str | None = None,
|
||||||
) -> ResolvedEpisodeDestination:
|
) -> ResolvedEpisodeDestination:
|
||||||
@@ -332,11 +332,11 @@ def resolve_episode_destination(
|
|||||||
message="TV show library path is not configured.",
|
message="TV show library path is not configured.",
|
||||||
)
|
)
|
||||||
|
|
||||||
parsed = _resolve_parsed(release_name, source_file)
|
parsed = _resolve_parsed(release_name, source_file, kb, prober)
|
||||||
ext = Path(source_file).suffix
|
ext = Path(source_file).suffix
|
||||||
tmdb_title_safe = _KB.sanitize_for_fs(tmdb_title)
|
tmdb_title_safe = kb.sanitize_for_fs(tmdb_title)
|
||||||
tmdb_episode_title_safe = (
|
tmdb_episode_title_safe = (
|
||||||
_KB.sanitize_for_fs(tmdb_episode_title) if tmdb_episode_title else None
|
kb.sanitize_for_fs(tmdb_episode_title) if tmdb_episode_title else None
|
||||||
)
|
)
|
||||||
computed_name = parsed.show_folder_name(tmdb_title_safe, tmdb_year)
|
computed_name = parsed.show_folder_name(tmdb_title_safe, tmdb_year)
|
||||||
|
|
||||||
@@ -375,6 +375,8 @@ def resolve_movie_destination(
|
|||||||
source_file: str,
|
source_file: str,
|
||||||
tmdb_title: str,
|
tmdb_title: str,
|
||||||
tmdb_year: int,
|
tmdb_year: int,
|
||||||
|
kb: ReleaseKnowledge,
|
||||||
|
prober: MediaProber,
|
||||||
) -> ResolvedMovieDestination:
|
) -> ResolvedMovieDestination:
|
||||||
"""
|
"""
|
||||||
Compute destination paths for a movie file.
|
Compute destination paths for a movie file.
|
||||||
@@ -392,9 +394,9 @@ def resolve_movie_destination(
|
|||||||
message="Movie library path is not configured.",
|
message="Movie library path is not configured.",
|
||||||
)
|
)
|
||||||
|
|
||||||
parsed = _resolve_parsed(release_name, source_file)
|
parsed = _resolve_parsed(release_name, source_file, kb, prober)
|
||||||
ext = Path(source_file).suffix
|
ext = Path(source_file).suffix
|
||||||
tmdb_title_safe = _KB.sanitize_for_fs(tmdb_title)
|
tmdb_title_safe = kb.sanitize_for_fs(tmdb_title)
|
||||||
|
|
||||||
folder_name = parsed.movie_folder_name(tmdb_title_safe, tmdb_year)
|
folder_name = parsed.movie_folder_name(tmdb_title_safe, tmdb_year)
|
||||||
filename = parsed.movie_filename(tmdb_title_safe, tmdb_year, ext)
|
filename = parsed.movie_filename(tmdb_title_safe, tmdb_year, ext)
|
||||||
@@ -416,6 +418,8 @@ def resolve_series_destination(
|
|||||||
release_name: str,
|
release_name: str,
|
||||||
tmdb_title: str,
|
tmdb_title: str,
|
||||||
tmdb_year: int,
|
tmdb_year: int,
|
||||||
|
kb: ReleaseKnowledge,
|
||||||
|
prober: MediaProber,
|
||||||
confirmed_folder: str | None = None,
|
confirmed_folder: str | None = None,
|
||||||
source_path: str | None = None,
|
source_path: str | None = None,
|
||||||
) -> ResolvedSeriesDestination:
|
) -> ResolvedSeriesDestination:
|
||||||
@@ -435,8 +439,8 @@ def resolve_series_destination(
|
|||||||
message="TV show library path is not configured.",
|
message="TV show library path is not configured.",
|
||||||
)
|
)
|
||||||
|
|
||||||
parsed = _resolve_parsed(release_name, source_path)
|
parsed = _resolve_parsed(release_name, source_path, kb, prober)
|
||||||
tmdb_title_safe = _KB.sanitize_for_fs(tmdb_title)
|
tmdb_title_safe = kb.sanitize_for_fs(tmdb_title)
|
||||||
computed_name = parsed.show_folder_name(tmdb_title_safe, tmdb_year)
|
computed_name = parsed.show_folder_name(tmdb_title_safe, tmdb_year)
|
||||||
|
|
||||||
resolved = _resolve_series_folder(
|
resolved = _resolve_series_folder(
|
||||||
|
|||||||
@@ -124,8 +124,16 @@ def dry_run(release_name: str) -> None:
|
|||||||
from alfred.application.filesystem.resolve_destination import (
|
from alfred.application.filesystem.resolve_destination import (
|
||||||
resolve_season_destination,
|
resolve_season_destination,
|
||||||
)
|
)
|
||||||
|
from alfred.infrastructure.knowledge.release_kb import YamlReleaseKnowledge
|
||||||
|
from alfred.infrastructure.probe import FfprobeMediaProber
|
||||||
|
|
||||||
result = resolve_season_destination(release_name, tmdb_title, tmdb_year)
|
result = resolve_season_destination(
|
||||||
|
release_name,
|
||||||
|
tmdb_title,
|
||||||
|
tmdb_year,
|
||||||
|
YamlReleaseKnowledge(),
|
||||||
|
FfprobeMediaProber(),
|
||||||
|
)
|
||||||
d = result.to_dict()
|
d = result.to_dict()
|
||||||
print()
|
print()
|
||||||
print(json.dumps(d, indent=2, ensure_ascii=False))
|
print(json.dumps(d, indent=2, ensure_ascii=False))
|
||||||
@@ -203,8 +211,16 @@ def do_move(release_name: str, source_folder: str | None = None) -> None:
|
|||||||
from alfred.application.filesystem.resolve_destination import (
|
from alfred.application.filesystem.resolve_destination import (
|
||||||
resolve_season_destination,
|
resolve_season_destination,
|
||||||
)
|
)
|
||||||
|
from alfred.infrastructure.knowledge.release_kb import YamlReleaseKnowledge
|
||||||
|
from alfred.infrastructure.probe import FfprobeMediaProber
|
||||||
|
|
||||||
result = resolve_season_destination(release_name, tmdb_title, tmdb_year)
|
result = resolve_season_destination(
|
||||||
|
release_name,
|
||||||
|
tmdb_title,
|
||||||
|
tmdb_year,
|
||||||
|
YamlReleaseKnowledge(),
|
||||||
|
FfprobeMediaProber(),
|
||||||
|
)
|
||||||
d = result.to_dict()
|
d = result.to_dict()
|
||||||
|
|
||||||
if d["status"] == "needs_clarification":
|
if d["status"] == "needs_clarification":
|
||||||
|
|||||||
@@ -31,13 +31,53 @@ from alfred.application.filesystem.resolve_destination import (
|
|||||||
_Clarification,
|
_Clarification,
|
||||||
_find_existing_tvshow_folders,
|
_find_existing_tvshow_folders,
|
||||||
_resolve_series_folder,
|
_resolve_series_folder,
|
||||||
resolve_episode_destination,
|
resolve_episode_destination as _resolve_episode_destination,
|
||||||
resolve_movie_destination,
|
resolve_movie_destination as _resolve_movie_destination,
|
||||||
resolve_season_destination,
|
resolve_season_destination as _resolve_season_destination,
|
||||||
resolve_series_destination,
|
resolve_series_destination as _resolve_series_destination,
|
||||||
)
|
)
|
||||||
|
from alfred.infrastructure.knowledge.release_kb import YamlReleaseKnowledge
|
||||||
from alfred.infrastructure.persistence import Memory, set_memory
|
from alfred.infrastructure.persistence import Memory, set_memory
|
||||||
|
|
||||||
|
_KB = YamlReleaseKnowledge()
|
||||||
|
|
||||||
|
|
||||||
|
class _NullProber:
|
||||||
|
"""Default prober stub — never returns probe data."""
|
||||||
|
|
||||||
|
def list_subtitle_streams(self, video): # pragma: no cover
|
||||||
|
return []
|
||||||
|
|
||||||
|
def probe(self, video):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
_DEFAULT_PROBER = _NullProber()
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_season_destination(*args, prober=None, **kwargs):
|
||||||
|
return _resolve_season_destination(
|
||||||
|
*args, kb=_KB, prober=prober or _DEFAULT_PROBER, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_episode_destination(*args, prober=None, **kwargs):
|
||||||
|
return _resolve_episode_destination(
|
||||||
|
*args, kb=_KB, prober=prober or _DEFAULT_PROBER, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_movie_destination(*args, prober=None, **kwargs):
|
||||||
|
return _resolve_movie_destination(
|
||||||
|
*args, kb=_KB, prober=prober or _DEFAULT_PROBER, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_series_destination(*args, prober=None, **kwargs):
|
||||||
|
return _resolve_series_destination(
|
||||||
|
*args, kb=_KB, prober=prober or _DEFAULT_PROBER, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
REL_EPISODE = "Oz.S01E01.1080p.WEBRip.x265-KONTRAST"
|
REL_EPISODE = "Oz.S01E01.1080p.WEBRip.x265-KONTRAST"
|
||||||
REL_SEASON = "Oz.S03.1080p.WEBRip.x265-KONTRAST"
|
REL_SEASON = "Oz.S03.1080p.WEBRip.x265-KONTRAST"
|
||||||
REL_MOVIE = "Inception.2010.1080p.BluRay.x265-GROUP"
|
REL_MOVIE = "Inception.2010.1080p.BluRay.x265-GROUP"
|
||||||
@@ -365,46 +405,40 @@ class TestProbeEnrichmentWiring:
|
|||||||
should pick up ffprobe data via inspect_release and let the enriched
|
should pick up ffprobe data via inspect_release and let the enriched
|
||||||
tech_string land in the destination name."""
|
tech_string land in the destination name."""
|
||||||
|
|
||||||
def test_movie_picks_up_probe_quality(
|
def test_movie_picks_up_probe_quality(self, cfg_memory, tmp_path):
|
||||||
self, cfg_memory, tmp_path, monkeypatch
|
|
||||||
):
|
|
||||||
from alfred.application.filesystem import resolve_destination as rd
|
|
||||||
|
|
||||||
monkeypatch.setattr(rd, "_PROBER", _StubProber(_stereo_movie_info()))
|
|
||||||
# Release name parses to "movie" but is missing the quality token;
|
# Release name parses to "movie" but is missing the quality token;
|
||||||
# probe must supply 1080p and refresh tech_string.
|
# probe must supply 1080p and refresh tech_string.
|
||||||
bare_name = "Inception.2010.BluRay.x264-GROUP"
|
bare_name = "Inception.2010.BluRay.x264-GROUP"
|
||||||
video = tmp_path / "movie.mkv"
|
video = tmp_path / "movie.mkv"
|
||||||
video.write_bytes(b"")
|
video.write_bytes(b"")
|
||||||
|
|
||||||
out = resolve_movie_destination(bare_name, str(video), "Inception", 2010)
|
out = resolve_movie_destination(
|
||||||
|
bare_name,
|
||||||
|
str(video),
|
||||||
|
"Inception",
|
||||||
|
2010,
|
||||||
|
prober=_StubProber(_stereo_movie_info()),
|
||||||
|
)
|
||||||
|
|
||||||
assert out.status == "ok"
|
assert out.status == "ok"
|
||||||
# tech_string -> "1080p.BluRay.x264" -> "1080p" shows up in names.
|
# tech_string -> "1080p.BluRay.x264" -> "1080p" shows up in names.
|
||||||
assert "1080p" in out.movie_folder_name
|
assert "1080p" in out.movie_folder_name
|
||||||
assert "1080p" in out.filename
|
assert "1080p" in out.filename
|
||||||
|
|
||||||
def test_movie_skips_probe_when_path_missing(self, cfg_memory, monkeypatch):
|
def test_movie_skips_probe_when_path_missing(self, cfg_memory):
|
||||||
# If the file doesn't exist, no probe runs (the stub would have
|
# If the file doesn't exist, no probe runs (the stub would have
|
||||||
# injected 1080p — its absence proves the skip).
|
# injected 1080p — its absence proves the skip).
|
||||||
from alfred.application.filesystem import resolve_destination as rd
|
|
||||||
|
|
||||||
monkeypatch.setattr(rd, "_PROBER", _StubProber(_stereo_movie_info()))
|
|
||||||
out = resolve_movie_destination(
|
out = resolve_movie_destination(
|
||||||
"Inception.2010.BluRay.x264-GROUP",
|
"Inception.2010.BluRay.x264-GROUP",
|
||||||
"/nowhere/m.mkv",
|
"/nowhere/m.mkv",
|
||||||
"Inception",
|
"Inception",
|
||||||
2010,
|
2010,
|
||||||
|
prober=_StubProber(_stereo_movie_info()),
|
||||||
)
|
)
|
||||||
assert out.status == "ok"
|
assert out.status == "ok"
|
||||||
assert "1080p" not in out.movie_folder_name
|
assert "1080p" not in out.movie_folder_name
|
||||||
|
|
||||||
def test_season_picks_up_probe_via_source_path(
|
def test_season_picks_up_probe_via_source_path(self, cfg_memory, tmp_path):
|
||||||
self, cfg_memory, tmp_path, monkeypatch
|
|
||||||
):
|
|
||||||
from alfred.application.filesystem import resolve_destination as rd
|
|
||||||
|
|
||||||
monkeypatch.setattr(rd, "_PROBER", _StubProber(_stereo_movie_info()))
|
|
||||||
# Season pack name missing quality token; probe must add it.
|
# Season pack name missing quality token; probe must add it.
|
||||||
bare_name = "Oz.S03.BluRay.x265-KONTRAST"
|
bare_name = "Oz.S03.BluRay.x265-KONTRAST"
|
||||||
release_dir = tmp_path / bare_name
|
release_dir = tmp_path / bare_name
|
||||||
@@ -412,7 +446,11 @@ class TestProbeEnrichmentWiring:
|
|||||||
(release_dir / "episode.mkv").write_bytes(b"")
|
(release_dir / "episode.mkv").write_bytes(b"")
|
||||||
|
|
||||||
out = resolve_season_destination(
|
out = resolve_season_destination(
|
||||||
bare_name, "Oz", 1997, source_path=str(release_dir)
|
bare_name,
|
||||||
|
"Oz",
|
||||||
|
1997,
|
||||||
|
source_path=str(release_dir),
|
||||||
|
prober=_StubProber(_stereo_movie_info()),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert out.status == "ok"
|
assert out.status == "ok"
|
||||||
|
|||||||
Reference in New Issue
Block a user