Files
MoviePilot/app/domain/media.py
DDSRem 2de976f6b3 Merge pull request #7050 from DDSRem-Dev/codex-comment-migration
docs: 统一全项目中文注释和结构化契约说明
2026-10-17 19:26:29 +08:00

141 lines
4.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
媒体来源的领域策略。
这里只留「按什么规则挑来源、什么算可订阅的音乐实体」这类策略——它们依赖运行期配置
(注入的 SEARCH_SOURCE 提供者)与业务约定,会随产品决策变化。
身份的**表示规则**(别名归一、ID 去空白拒零、成对写入、媒体键前缀)不随策略变化,
已迁至 app/schemas/media.py,与两个身份 Mixin 作伴。持久化层因此不必为一条表示规则
反向依赖领域层。本模块不做 re-export:同一个符号只应有一条 import 路径。
"""
from typing import Callable, Optional, Tuple, Union
from app.schemas.media import normalize_media_source
from app.schemas.types import (
MUSIC_ENTITY_TYPES,
MUSIC_SUBSCRIBABLE_TYPES,
MediaSource,
MediaSourceSelection,
)
MUSIC_MEDIA_SOURCE_ORDER = (
MediaSource.MusicBrainz,
MediaSource.TheAudioDB,
MediaSource.DoubanMusic,
)
MUSIC_MEDIA_SOURCES = frozenset(MUSIC_MEDIA_SOURCE_ORDER)
_search_source_provider: Callable[[], object] = lambda: None
def music_recognition_sources(configured: str, primary: MediaSource = MediaSource.MusicBrainz) -> tuple[MediaSource, ...]:
"""
只从混合搜索配置中选择内置音乐来源,保留用户顺序;无音乐配置兼容原主来源。
"""
sources: list[MediaSource] = []
for value in str(configured or "").split(","):
source = normalize_media_source(value)
if source is not None and source in MUSIC_MEDIA_SOURCES and source not in sources:
sources.append(source)
return tuple(sources) or (primary,)
def configure_search_source_provider(provider: Callable[[], object]) -> None:
"""
注入默认媒体来源配置,领域选择逻辑不直接依赖平台 settings。
"""
global _search_source_provider
_search_source_provider = provider
def normalize_music_type(
value: Optional[object],
*,
allow_artist: bool = True,
) -> Optional[str]:
"""
规范化音乐实体类型,非法值返回 None。
"""
normalized = str(value or "").strip().lower()
allowed = MUSIC_ENTITY_TYPES if allow_artist else MUSIC_SUBSCRIBABLE_TYPES
return normalized if normalized in allowed else None
def is_music_media_source(
source: Optional[Union[MediaSource, str]],
) -> bool:
"""
判断单个请求级来源是否为内置音乐元数据源。
"""
return normalize_media_source(source) in MUSIC_MEDIA_SOURCES
def parse_media_source_selection(value: Optional[str]) -> Tuple[MediaSource, ...]:
"""
解析 HTTP 查询参数中的逗号分隔来源,并转换为有序枚举集合。
:param value: 逗号分隔的来源值;空值表示未显式选择来源
:return: 去重后的媒体来源枚举元组
:raises ValueError: 包含格式非法的来源标识
"""
if not value:
return ()
sources: list[MediaSource] = []
invalid_sources: list[str] = []
for item in str(value).split(","):
raw_source = item.strip()
if not raw_source:
continue
source = normalize_media_source(raw_source)
if not source:
invalid_sources.append(raw_source)
elif source not in sources:
sources.append(source)
if invalid_sources:
raise ValueError(f"不支持的媒体数据源:{', '.join(invalid_sources)}")
return tuple(sources)
def is_media_source_selected(
media_source: Optional[MediaSourceSelection],
source_key: MediaSource,
) -> bool:
"""
判断请求级媒体数据源集合是否包含当前模块。
:param media_source: 请求级媒体数据源枚举或枚举元组,空表示不作限制
:param source_key: 当前模块对应的数据源标识
:return: 是否包含
"""
if not media_source:
return True
selected_sources = (
(media_source,)
if isinstance(media_source, MediaSource)
else media_source
)
return source_key in selected_sources
def is_media_source_enabled(
media_source: Optional[MediaSourceSelection],
source_key: MediaSource,
) -> bool:
"""
判断媒体搜索时数据源是否启用:请求级来源集合优先,未指定时回退到
全局 SEARCH_SOURCE 多来源配置,两者均未配置时全部启用。
:param media_source: 请求级媒体数据源枚举或枚举元组
:param source_key: 当前模块对应的数据源标识
:return: 是否启用
"""
if media_source:
return is_media_source_selected(media_source, source_key)
configured_search_sources = _search_source_provider()
if configured_search_sources:
configured_sources = {
normalize_media_source(item)
for item in str(configured_search_sources).split(",")
}
return source_key in configured_sources
return True