Files
MoviePilot/app/application/subscription/search.py
T

83 lines
2.5 KiB
Python

"""手工订阅搜索应用用例。"""
from dataclasses import dataclass
from typing import Callable, Protocol
from app.application.subscription.delete import SubscribeDeletionCandidate
@dataclass(frozen=True, slots=True)
class SubscribeSearchActor:
"""执行手工订阅搜索的用户身份。"""
username: str
is_superuser: bool
class SubscribeSearchRepository(Protocol):
"""手工订阅搜索所需的最小读取端口。"""
async def get_candidate(
self,
subscribe_id: int,
) -> SubscribeDeletionCandidate | None:
"""读取单条订阅的归属信息。"""
...
async def list_search_ids(self, username: str, state: str) -> list[int]:
"""返回用户当前可搜索状态下的订阅编号。"""
...
SubscribeSearchScheduler = Callable[[tuple[int, ...] | None, str | None], None]
class SearchSubscriptionsCommand:
"""按用户权限生成并提交手工订阅搜索任务。"""
def __init__(
self,
repository: SubscribeSearchRepository,
schedule_search: SubscribeSearchScheduler,
) -> None:
"""注入订阅读取端口和后台任务提交端口。"""
self._repository = repository
self._schedule_search = schedule_search
async def execute(
self,
actor: SubscribeSearchActor,
subscribe_id: int | None = None,
) -> bool:
"""提交单条或当前用户全部可搜索订阅,返回目标是否存在。"""
if subscribe_id is not None:
candidate = await self._repository.get_candidate(subscribe_id)
if not self._can_access(candidate, actor):
return False
self._schedule_search((subscribe_id,), None)
return True
if actor.is_superuser:
self._schedule_search(None, "R")
return True
subscribe_ids = await self._repository.list_search_ids(
actor.username,
"R",
)
if subscribe_ids:
self._schedule_search(tuple(subscribe_ids), None)
return True
@staticmethod
def _can_access(
candidate: SubscribeDeletionCandidate | None,
actor: SubscribeSearchActor,
) -> bool:
"""沿用订阅读取接口的超级用户和归属用户权限语义。"""
if candidate is None:
return False
if actor.is_superuser:
return True
return bool(candidate.username) and candidate.username == actor.username