mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 15:38:19 +08:00
refactor(chain): type download and media server ports
This commit is contained in:
+39
-22
@@ -3,8 +3,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any, cast
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.application.download.failures import (
|
||||
DownloadFailureSnapshot,
|
||||
DownloadFailureWrite,
|
||||
)
|
||||
from app.db.oper.downloadfailure import DownloadFailureOper
|
||||
from app.db.uow import SqlAlchemyUnitOfWork
|
||||
|
||||
@@ -12,7 +17,7 @@ from app.db.uow import SqlAlchemyUnitOfWork
|
||||
class TransactionalDownloadFailureRepository:
|
||||
"""为 Chain 下载失败读写创建短生命周期会话并显式收口事务。"""
|
||||
|
||||
def __init__(self, session_factory: Callable[[], Any]) -> None:
|
||||
def __init__(self, session_factory: Callable[[], Session]) -> None:
|
||||
"""保存由启动组合根提供的同步会话工厂。"""
|
||||
self._session_factory = session_factory
|
||||
|
||||
@@ -20,36 +25,48 @@ class TransactionalDownloadFailureRepository:
|
||||
self,
|
||||
fingerprints: list[str],
|
||||
now_time: str,
|
||||
) -> dict[str, Any]:
|
||||
) -> dict[str, DownloadFailureSnapshot]:
|
||||
"""在独立只读会话中查询仍处于冷却期的失败记录。"""
|
||||
with self._session_factory() as session:
|
||||
return cast(
|
||||
dict[str, Any],
|
||||
DownloadFailureOper(db=session).get_active_by_fingerprints(
|
||||
fingerprints=fingerprints,
|
||||
now_time=now_time,
|
||||
),
|
||||
records = DownloadFailureOper(db=session).get_active_by_fingerprints(
|
||||
fingerprints=fingerprints,
|
||||
now_time=now_time,
|
||||
)
|
||||
return {
|
||||
fingerprint: DownloadFailureSnapshot(
|
||||
fingerprint=record.fingerprint,
|
||||
error_message=record.error_message,
|
||||
next_retry_at=record.next_retry_at,
|
||||
)
|
||||
for fingerprint, record in records.items()
|
||||
}
|
||||
|
||||
def record_failure(
|
||||
self,
|
||||
fingerprint: str,
|
||||
now_time: str,
|
||||
next_retry_at: str,
|
||||
**kwargs: object,
|
||||
) -> Any:
|
||||
def record_failure(self, failure: DownloadFailureWrite) -> None:
|
||||
"""在一个显式 UoW 中新增或更新下载失败记录。"""
|
||||
with self._session_factory() as session:
|
||||
transaction = SqlAlchemyUnitOfWork(session)
|
||||
try:
|
||||
failure = DownloadFailureOper(db=session).record_failure(
|
||||
fingerprint=fingerprint,
|
||||
now_time=now_time,
|
||||
next_retry_at=next_retry_at,
|
||||
**kwargs,
|
||||
DownloadFailureOper(db=session).record_failure(
|
||||
fingerprint=failure.fingerprint,
|
||||
now_time=failure.failed_at,
|
||||
next_retry_at=failure.next_retry_at,
|
||||
type=failure.media_type,
|
||||
title=failure.title,
|
||||
year=failure.year,
|
||||
media_source=failure.media_source,
|
||||
media_id=failure.media_id,
|
||||
seasons=failure.seasons,
|
||||
episodes=failure.episodes,
|
||||
site=failure.site,
|
||||
site_name=failure.site_name,
|
||||
torrent_id=failure.torrent_id,
|
||||
torrent_name=failure.torrent_name,
|
||||
torrent_size=failure.torrent_size,
|
||||
downloader=failure.downloader,
|
||||
source=failure.source,
|
||||
error_message=failure.error_message,
|
||||
)
|
||||
transaction.commit()
|
||||
return failure
|
||||
except Exception:
|
||||
transaction.rollback()
|
||||
raise
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
"""媒体服务器本地缓存的显式短会话与事务适配器。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from collections.abc import Callable
|
||||
from typing import Optional, TypeVar, Union
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.application.mediaserver import MediaServerSyncItem
|
||||
from app.db.oper.mediaserver import MediaServerOper
|
||||
from app.db.uow import SqlAlchemyUnitOfWork
|
||||
from app.schemas.types import MediaSource
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class TransactionalMediaServerRepository:
|
||||
"""让每次媒体库缓存查询或变更各自拥有一个短生命周期 Session。"""
|
||||
|
||||
def __init__(self, session_factory: Callable[[], Session]) -> None:
|
||||
"""保存由启动组合根提供的同步会话工厂。"""
|
||||
self._session_factory = session_factory
|
||||
|
||||
def _read(self, operation: Callable[[MediaServerOper], T]) -> T:
|
||||
"""在独立只读会话中执行媒体库缓存查询。"""
|
||||
with self._session_factory() as session:
|
||||
return operation(MediaServerOper(db=session))
|
||||
|
||||
def _write(self, operation: Callable[[MediaServerOper], T]) -> T:
|
||||
"""在独立 UoW 中执行并提交一项媒体库缓存变更。"""
|
||||
with self._session_factory() as session:
|
||||
unit_of_work = SqlAlchemyUnitOfWork(session)
|
||||
try:
|
||||
result = operation(MediaServerOper(db=session))
|
||||
unit_of_work.commit()
|
||||
return result
|
||||
except Exception:
|
||||
unit_of_work.rollback()
|
||||
raise
|
||||
|
||||
def get_item_id(
|
||||
self,
|
||||
*,
|
||||
title: Optional[str] = None,
|
||||
year: Optional[Union[str, int]] = None,
|
||||
mtype: Optional[str] = None,
|
||||
media_source: Optional[MediaSource] = None,
|
||||
media_id: Optional[str] = None,
|
||||
season: Optional[int] = None,
|
||||
) -> Optional[str]:
|
||||
"""在短会话中返回匹配条目的服务器 item_id。"""
|
||||
item_id: Optional[str] = self._read(
|
||||
lambda repository: repository.get_item_id(
|
||||
title=title,
|
||||
year=year,
|
||||
mtype=mtype,
|
||||
media_source=media_source,
|
||||
media_id=media_id,
|
||||
season=season,
|
||||
)
|
||||
)
|
||||
return item_id
|
||||
|
||||
def upsert(self, item: MediaServerSyncItem) -> bool:
|
||||
"""在单个短事务中新增或更新一个媒体库同步条目。"""
|
||||
payload = {
|
||||
"server": item.server,
|
||||
"library": item.library,
|
||||
"item_id": item.item_id,
|
||||
"item_type": item.item_type,
|
||||
"title": item.title,
|
||||
"original_title": item.original_title,
|
||||
"year": item.year,
|
||||
"media_source": item.media_source,
|
||||
"media_id": item.media_id,
|
||||
"path": item.path,
|
||||
"seasoninfo": {
|
||||
season: list(episodes)
|
||||
for season, episodes in item.seasoninfo
|
||||
},
|
||||
"note": (
|
||||
json.loads(item.note_json)
|
||||
if item.note_json is not None
|
||||
else None
|
||||
),
|
||||
"lst_mod_date": item.lst_mod_date,
|
||||
}
|
||||
created: bool = self._write(
|
||||
lambda repository: repository.upsert(**payload)
|
||||
)
|
||||
return created
|
||||
|
||||
def delete_stale(self, *, server: str, sync_time: str) -> int:
|
||||
"""在短事务中删除指定服务器本轮未更新的条目。"""
|
||||
deleted: int = self._write(
|
||||
lambda repository: repository.delete_stale(
|
||||
server=server,
|
||||
sync_time=sync_time,
|
||||
),
|
||||
)
|
||||
return deleted
|
||||
|
||||
def delete_excluded_servers(self, servers: list[str]) -> int:
|
||||
"""在短事务中删除已停用或已移除服务器的条目。"""
|
||||
deleted: int = self._write(
|
||||
lambda repository: repository.delete_excluded_servers(servers)
|
||||
)
|
||||
return deleted
|
||||
Reference in New Issue
Block a user