Files
MoviePilot/app/chain/search/cache.py

200 lines
6.6 KiB
Python

"""
搜索参数与结果缓存适配 owner。
"""
from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast
from app.application.search.state import (
SearchStateService,
normalize_search_params,
stringify_sites,
)
from app.chain.search.contract import _SearchOwnerBase
from app.domain.context import Context, SubtitleInfo
from app.schemas.types import (
MediaSource,
MediaType,
)
if TYPE_CHECKING:
from app.schemas.search import SearchLastContextData
class SearchCacheOwner(_SearchOwnerBase):
"""
搜索参数与结果缓存适配 owner。
"""
@staticmethod
def _stringify_sites(sites: Optional[List[int]]) -> str:
"""
将站点ID列表转换为前端可直接复用的查询字符串。
"""
return stringify_sites(sites)
@staticmethod
def _normalize_search_params(params: Optional[Dict[str, Any]]) -> Optional[Dict[str, str]]:
"""
规范化上次搜索参数,供前端结果页重新搜索使用;旧复合关键字仅在
缓存读取边界转换为独立的媒体来源和原生 ID。
"""
return normalize_search_params(params)
def _search_state(self) -> SearchStateService:
"""
构造绑定当前 Chain 缓存端口的搜索状态服务。
"""
return SearchStateService(
save_cache=self.save_cache,
load_cache=self.load_cache,
async_save_cache=self.async_save_cache,
async_load_cache=self.async_load_cache,
params_key=self._SEARCH_PARAMS_CACHE_KEY,
result_key=self._RESULT_CACHE_KEY,
subtitle_result_key=self._SUBTITLE_RESULT_CACHE_KEY,
)
def save_last_search_params(
self,
*,
keyword: Optional[str] = None,
media_source: Optional[MediaSource] = None,
media_id: Optional[str] = None,
mtype: Optional[MediaType] = None,
area: Optional[str] = "title",
title: Optional[str] = None,
year: Optional[str] = None,
season: Optional[int] = None,
episode: Optional[int] = None,
sites: Optional[List[int]] = None,
music_type: Optional[str] = None,
result_type: Optional[str] = "torrent",
include_candidates: bool = False,
) -> None:
"""
保存最后一次资源搜索参数,标题搜索与精确身份搜索使用互斥字段。
"""
self._search_state().save_params(
keyword=keyword,
media_source=media_source,
media_id=media_id,
mtype=mtype,
area=area,
title=title,
year=year,
season=season,
episode=episode,
sites=sites,
music_type=music_type,
result_type=result_type,
include_candidates=include_candidates,
)
async def async_save_last_search_params(
self,
*,
keyword: Optional[str] = None,
media_source: Optional[MediaSource] = None,
media_id: Optional[str] = None,
mtype: Optional[MediaType] = None,
area: Optional[str] = "title",
title: Optional[str] = None,
year: Optional[str] = None,
season: Optional[int] = None,
episode: Optional[int] = None,
sites: Optional[List[int]] = None,
music_type: Optional[str] = None,
result_type: Optional[str] = "torrent",
include_candidates: bool = False,
) -> None:
"""
异步保存最后一次资源搜索参数,标题搜索与精确身份搜索使用互斥字段。
"""
await self._search_state().async_save_params(
keyword=keyword,
media_source=media_source,
media_id=media_id,
mtype=mtype,
area=area,
title=title,
year=year,
season=season,
episode=episode,
sites=sites,
music_type=music_type,
result_type=result_type,
include_candidates=include_candidates,
)
def last_search_params(self) -> Optional[Dict[str, str]]:
"""
获取上次搜索使用的参数。
"""
return self._search_state().load_params()
async def async_last_search_params(self) -> Optional[Dict[str, str]]:
"""
异步获取上次搜索使用的参数。
"""
return await self._search_state().async_load_params()
async def async_last_search_context(self, user_id: int) -> "SearchLastContextData":
"""
读取已认证用户的原子搜索上下文,不与旧全局结果拼接。
:param user_id: 当前认证用户主键
:return: 该用户最近完成的完整搜索快照,缺失时为空
"""
return await self._search_state().async_load_context(user_id)
async def async_save_search_context(self, user_id: int, context: "SearchLastContextData") -> None:
"""
通过状态服务一次发布参数、结果和诊断。
:param user_id: 当前认证用户主键
:param context: 正常完成的搜索完整快照
"""
await self._search_state().async_save_context(user_id, context)
def last_search_results(self) -> Optional[List[Context]]:
"""
获取上次搜索结果
"""
return cast(Optional[List[Context]], self._search_state().load_results())
async def async_last_search_results(self) -> Optional[List[Context]]:
"""
异步获取上次搜索结果
"""
return cast(
Optional[List[Context]],
await self._search_state().async_load_results(),
)
async def async_last_subtitle_search_results(self) -> Optional[List[SubtitleInfo]]:
"""
异步获取上次字幕搜索结果。
"""
return cast(
Optional[List[SubtitleInfo]],
await self._search_state().async_load_subtitle_results(),
)
def _save_results(self, results: List[Context]) -> None:
"""
通过搜索状态 owner 保存同步资源结果,避免业务入口直写缓存键。
"""
self._search_state().save_results(results)
async def _async_save_results(self, results: List[Context]) -> None:
"""
通过搜索状态 owner 保存异步资源结果,统一结果发布边界。
"""
await self._search_state().async_save_results(results)
async def _async_save_subtitles(self, subtitles: List[SubtitleInfo]) -> None:
"""
通过搜索状态 owner 保存字幕结果,统一字幕发布边界。
"""
await self._search_state().async_save_subtitle_results(subtitles)