refactor(subtitles): introduce SubtitleKnowledge Protocol port
Domain services (SubtitleIdentifier, PatternDetector) used to import the concrete SubtitleKnowledgeBase class directly from infrastructure for their type hint. With this commit they depend on a structural Protocol in alfred/domain/subtitles/ports/knowledge.py declaring just the 7 read-only query methods the domain actually consumes. The concrete YAML-backed SubtitleKnowledgeBase in infrastructure remains the sole adapter — no rename, no shim. With this change alfred/domain/subtitles/ has zero imports from alfred/infrastructure/. Also extend the changelog entry covering the full domain-io-extraction branch.
This commit is contained in:
@@ -143,6 +143,17 @@ callers).
|
|||||||
an explicit `default_rules: SubtitleMatchingRules` parameter. The
|
an explicit `default_rules: SubtitleMatchingRules` parameter. The
|
||||||
`ManageSubtitles` use case loads defaults from the KB once and
|
`ManageSubtitles` use case loads defaults from the KB once and
|
||||||
passes them in.
|
passes them in.
|
||||||
|
- **`SubtitleKnowledge` Protocol port** at
|
||||||
|
`alfred/domain/subtitles/ports/knowledge.py` declares the read-only
|
||||||
|
query surface domain services consume (7 methods:
|
||||||
|
`known_extensions`, `format_for_extension`, `language_for_token`,
|
||||||
|
`is_known_lang_token`, `type_for_token`, `is_known_type_token`,
|
||||||
|
`patterns`). `SubtitleIdentifier` and `PatternDetector` depend on
|
||||||
|
this Protocol instead of the concrete `SubtitleKnowledgeBase` from
|
||||||
|
infrastructure — `domain/subtitles/` now has zero imports from
|
||||||
|
`infrastructure/`. The remaining domain → infra leak
|
||||||
|
(`domain/release/` loading separator YAML at import-time) is
|
||||||
|
documented in tech-debt and scheduled for its own branch.
|
||||||
- **`to_dot_folder_name(title)` helper** in
|
- **`to_dot_folder_name(title)` helper** in
|
||||||
`alfred/domain/shared/value_objects.py` — extracts the
|
`alfred/domain/shared/value_objects.py` — extracts the
|
||||||
`re.sub(r"[^\w\s\.\-]", "", title).replace(" ", ".")` pattern that was
|
`re.sub(r"[^\w\s\.\-]", "", title).replace(" ", ".")` pattern that was
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
"""Domain ports for the subtitles domain — Protocol-based abstractions
|
||||||
|
that decouple domain services from concrete infrastructure adapters."""
|
||||||
|
|
||||||
|
from .knowledge import SubtitleKnowledge
|
||||||
|
|
||||||
|
__all__ = ["SubtitleKnowledge"]
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
"""SubtitleKnowledge port — the query surface domain services need from the
|
||||||
|
subtitle knowledge base, expressed as a Protocol so the domain never imports
|
||||||
|
the infrastructure adapter that backs it.
|
||||||
|
|
||||||
|
The concrete implementation lives in
|
||||||
|
``alfred/infrastructure/knowledge/subtitles/base.py`` (the YAML-backed
|
||||||
|
``SubtitleKnowledgeBase``). Tests can supply any object that satisfies this
|
||||||
|
structural contract.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Protocol
|
||||||
|
|
||||||
|
from ..value_objects import SubtitleFormat, SubtitleLanguage, SubtitlePattern, SubtitleType
|
||||||
|
|
||||||
|
|
||||||
|
class SubtitleKnowledge(Protocol):
|
||||||
|
"""Read-only query surface for subtitle knowledge consumed by the domain.
|
||||||
|
|
||||||
|
Only the methods that domain services actually call belong here — anything
|
||||||
|
else (defaults loading, reload, pattern groups, raw dicts) stays on the
|
||||||
|
concrete class and is reserved for the application layer.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def known_extensions(self) -> set[str]: ...
|
||||||
|
|
||||||
|
def format_for_extension(self, ext: str) -> SubtitleFormat | None: ...
|
||||||
|
|
||||||
|
def language_for_token(self, token: str) -> SubtitleLanguage | None: ...
|
||||||
|
|
||||||
|
def is_known_lang_token(self, token: str) -> bool: ...
|
||||||
|
|
||||||
|
def type_for_token(self, token: str) -> SubtitleType | None: ...
|
||||||
|
|
||||||
|
def is_known_type_token(self, token: str) -> bool: ...
|
||||||
|
|
||||||
|
def patterns(self) -> dict[str, SubtitlePattern]: ...
|
||||||
@@ -4,9 +4,8 @@ import logging
|
|||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from alfred.infrastructure.knowledge.subtitles.base import SubtitleKnowledgeBase
|
|
||||||
|
|
||||||
from ...shared.ports import FilesystemScanner, MediaProber
|
from ...shared.ports import FilesystemScanner, MediaProber
|
||||||
|
from ..ports import SubtitleKnowledge
|
||||||
from ...shared.value_objects import ImdbId
|
from ...shared.value_objects import ImdbId
|
||||||
from ..entities import MediaSubtitleMetadata, SubtitleCandidate
|
from ..entities import MediaSubtitleMetadata, SubtitleCandidate
|
||||||
from ..value_objects import ScanStrategy, SubtitlePattern, SubtitleType
|
from ..value_objects import ScanStrategy, SubtitlePattern, SubtitleType
|
||||||
@@ -59,7 +58,7 @@ class SubtitleIdentifier:
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
kb: SubtitleKnowledgeBase,
|
kb: SubtitleKnowledge,
|
||||||
prober: MediaProber,
|
prober: MediaProber,
|
||||||
scanner: FilesystemScanner,
|
scanner: FilesystemScanner,
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -3,9 +3,8 @@
|
|||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from alfred.infrastructure.knowledge.subtitles.base import SubtitleKnowledgeBase
|
|
||||||
|
|
||||||
from ...shared.ports import FilesystemScanner, MediaProber
|
from ...shared.ports import FilesystemScanner, MediaProber
|
||||||
|
from ..ports import SubtitleKnowledge
|
||||||
from ..value_objects import ScanStrategy, SubtitlePattern
|
from ..value_objects import ScanStrategy, SubtitlePattern
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -22,7 +21,7 @@ class PatternDetector:
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
kb: SubtitleKnowledgeBase,
|
kb: SubtitleKnowledge,
|
||||||
prober: MediaProber,
|
prober: MediaProber,
|
||||||
scanner: FilesystemScanner,
|
scanner: FilesystemScanner,
|
||||||
):
|
):
|
||||||
|
|||||||
Reference in New Issue
Block a user