refactor(subtitles): move SubtitlePlacer to application layer

The placer performs filesystem I/O (os.link) — it belongs in the
application layer, not the domain. Domain services should be pure.

- Move alfred/domain/subtitles/services/placer.py to
  alfred/application/subtitles/placer.py
- Move tests/domain/test_subtitle_placer.py to
  tests/application/test_subtitle_placer.py
- Update all callers (manage_subtitles use case, metadata store, tests)
- Drop placer re-exports from domain.subtitles.services.__init__
This commit is contained in:
2026-05-19 15:07:39 +02:00
parent 9556bf9e08
commit 903e9e7117
8 changed files with 9 additions and 13 deletions
@@ -8,7 +8,7 @@ from alfred.domain.subtitles.entities import SubtitleCandidate
from alfred.domain.subtitles.services.identifier import SubtitleIdentifier from alfred.domain.subtitles.services.identifier import SubtitleIdentifier
from alfred.domain.subtitles.services.matcher import SubtitleMatcher from alfred.domain.subtitles.services.matcher import SubtitleMatcher
from alfred.domain.subtitles.services.pattern_detector import PatternDetector from alfred.domain.subtitles.services.pattern_detector import PatternDetector
from alfred.domain.subtitles.services.placer import ( from alfred.application.subtitles.placer import (
PlacedTrack, PlacedTrack,
SubtitlePlacer, SubtitlePlacer,
_build_dest_name, _build_dest_name,
@@ -5,8 +5,8 @@ import os
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from ..entities import SubtitleCandidate from alfred.domain.subtitles.entities import SubtitleCandidate
from ..value_objects import SubtitleType from alfred.domain.subtitles.value_objects import SubtitleType
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -1,13 +1,9 @@
from .identifier import SubtitleIdentifier from .identifier import SubtitleIdentifier
from .matcher import SubtitleMatcher from .matcher import SubtitleMatcher
from .pattern_detector import PatternDetector from .pattern_detector import PatternDetector
from .placer import PlacedTrack, PlaceResult, SubtitlePlacer
__all__ = [ __all__ = [
"SubtitleIdentifier", "SubtitleIdentifier",
"SubtitleMatcher", "SubtitleMatcher",
"PatternDetector", "PatternDetector",
"SubtitlePlacer",
"PlacedTrack",
"PlaceResult",
] ]
@@ -14,7 +14,7 @@ from pathlib import Path
from typing import Any from typing import Any
from alfred.domain.subtitles.entities import SubtitleCandidate from alfred.domain.subtitles.entities import SubtitleCandidate
from alfred.domain.subtitles.services.placer import PlacedTrack from alfred.application.subtitles.placer import PlacedTrack
from alfred.infrastructure.metadata.store import MetadataStore from alfred.infrastructure.metadata.store import MetadataStore
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
+1 -1
View File
@@ -41,7 +41,7 @@ from alfred.application.filesystem.manage_subtitles import (
_to_unresolved_dto, _to_unresolved_dto,
) )
from alfred.domain.subtitles.entities import MediaSubtitleMetadata, SubtitleCandidate from alfred.domain.subtitles.entities import MediaSubtitleMetadata, SubtitleCandidate
from alfred.domain.subtitles.services.placer import PlacedTrack, PlaceResult from alfred.application.subtitles.placer import PlacedTrack, PlaceResult
from alfred.domain.subtitles.value_objects import ( from alfred.domain.subtitles.value_objects import (
ScanStrategy, ScanStrategy,
SubtitleFormat, SubtitleFormat,
@@ -1,4 +1,4 @@
"""Tests for ``alfred.domain.subtitles.services.placer.SubtitlePlacer``. """Tests for ``alfred.application.subtitles.placer.SubtitlePlacer``.
The placer hard-links subtitle files next to a destination video, naming The placer hard-links subtitle files next to a destination video, naming
them ``{video_stem}.{lang}[.sdh|.forced].{ext}``. them ``{video_stem}.{lang}[.sdh|.forced].{ext}``.
@@ -22,7 +22,7 @@ from unittest.mock import patch
import pytest import pytest
from alfred.domain.subtitles.entities import SubtitleCandidate from alfred.domain.subtitles.entities import SubtitleCandidate
from alfred.domain.subtitles.services.placer import ( from alfred.application.subtitles.placer import (
PlacedTrack, PlacedTrack,
PlaceResult, PlaceResult,
SubtitlePlacer, SubtitlePlacer,
@@ -198,7 +198,7 @@ class TestOSError:
video.write_bytes(b"") video.write_bytes(b"")
track = _track(src) track = _track(src)
with patch( with patch(
"alfred.domain.subtitles.services.placer.os.link", "alfred.application.subtitles.placer.os.link",
side_effect=OSError("cross-device link"), side_effect=OSError("cross-device link"),
): ):
result = placer.place([track], video) result = placer.place([track], video)
@@ -17,7 +17,7 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
from alfred.domain.subtitles.entities import SubtitleCandidate from alfred.domain.subtitles.entities import SubtitleCandidate
from alfred.domain.subtitles.services.placer import PlacedTrack from alfred.application.subtitles.placer import PlacedTrack
from alfred.domain.subtitles.value_objects import ( from alfred.domain.subtitles.value_objects import (
SubtitleFormat, SubtitleFormat,
SubtitleLanguage, SubtitleLanguage,