mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-02 21:34:32 +08:00
139 lines
5.3 KiB
Python
139 lines
5.3 KiB
Python
"""
|
|
订阅执行状态的请求级异步 SQLAlchemy 适配器。
|
|
"""
|
|
|
|
from typing import Optional
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.application.subscription.execution import SearchBatchSnapshot, SearchTaskSnapshot
|
|
from app.db.models.subscribeactivity import SubscribeActivity
|
|
from app.db.models.subscriptionsearch import SubscriptionSearchBatch, SubscriptionSearchTask
|
|
|
|
|
|
def _task(record: SubscriptionSearchTask, activity: Optional[SubscribeActivity] = None) -> SearchTaskSnapshot:
|
|
"""
|
|
复制可脱离 AsyncSession 使用的搜索任务快照。
|
|
"""
|
|
return SearchTaskSnapshot(
|
|
download_counts={key: value for key, value in (activity.counts if activity else {}).items()
|
|
if key in {"submitted", "initial_submitted", "upgrade_submitted", "full_upgrade_submitted"}
|
|
and isinstance(value, int)},
|
|
downloads=tuple({**resource, "subscription_id": record.subscription_id, "task_id": record.task_id}
|
|
for resource in (activity.details.get("resources", []) if activity else [])
|
|
if isinstance(resource, dict) and resource.get("decision") == "submitted"),
|
|
task_id=record.task_id,
|
|
batch_id=record.batch_id,
|
|
subscription_id=record.subscription_id,
|
|
source=record.source,
|
|
priority=record.priority,
|
|
position=record.position,
|
|
state=record.state,
|
|
phase=record.phase,
|
|
attempt_count=record.attempt_count,
|
|
cancel_requested=bool(record.cancel_requested),
|
|
lease_token=record.lease_token,
|
|
created_at=record.created_at,
|
|
updated_at=record.updated_at,
|
|
available_at=record.available_at,
|
|
started_at=record.started_at,
|
|
finished_at=record.finished_at,
|
|
last_error=record.last_error,
|
|
current_site_id=record.current_site_id,
|
|
)
|
|
|
|
|
|
def _batch(record: SubscriptionSearchBatch) -> SearchBatchSnapshot:
|
|
"""
|
|
复制可脱离 AsyncSession 使用的搜索批次快照。
|
|
"""
|
|
return SearchBatchSnapshot(
|
|
batch_id=record.batch_id,
|
|
source=record.source,
|
|
state=record.state,
|
|
priority=record.priority,
|
|
total_count=record.total_count,
|
|
finished_count=record.finished_count,
|
|
failed_count=record.failed_count,
|
|
cancelled_count=record.cancelled_count,
|
|
skipped_count=record.skipped_count,
|
|
cancel_requested=bool(record.cancel_requested),
|
|
created_at=record.created_at,
|
|
updated_at=record.updated_at,
|
|
started_at=record.started_at,
|
|
finished_at=record.finished_at,
|
|
last_error=record.last_error,
|
|
)
|
|
|
|
|
|
class SessionSubscriptionExecutionStatusRepository:
|
|
"""
|
|
复用请求 AsyncSession 批量读取搜索执行事实。
|
|
"""
|
|
|
|
def __init__(self, session: AsyncSession) -> None:
|
|
"""
|
|
绑定请求持有的异步会话。
|
|
"""
|
|
self._session = session
|
|
|
|
async def latest_search_tasks(
|
|
self,
|
|
subscription_ids: tuple[int, ...],
|
|
) -> dict[int, SearchTaskSnapshot]:
|
|
"""
|
|
按更新时间倒序读取并在内存中保留每条订阅首项。
|
|
"""
|
|
result = await self._session.execute(
|
|
select(SubscriptionSearchTask)
|
|
.where(SubscriptionSearchTask.subscription_id.in_(subscription_ids))
|
|
.order_by(
|
|
SubscriptionSearchTask.updated_at.desc(),
|
|
SubscriptionSearchTask.id.desc(),
|
|
)
|
|
)
|
|
snapshots: dict[int, SearchTaskSnapshot] = {}
|
|
for record in result.scalars().all():
|
|
snapshots.setdefault(record.subscription_id, _task(record))
|
|
return snapshots
|
|
|
|
async def list_batches(self, *, limit: int) -> list[SearchBatchSnapshot]:
|
|
"""
|
|
返回最近更新的批次,访问范围由应用服务依据任务校验。
|
|
"""
|
|
result = await self._session.execute(
|
|
select(SubscriptionSearchBatch)
|
|
.where(SubscriptionSearchBatch.total_count > 0)
|
|
.order_by(
|
|
SubscriptionSearchBatch.updated_at.desc(),
|
|
SubscriptionSearchBatch.id.desc(),
|
|
)
|
|
.limit(limit)
|
|
)
|
|
return [_batch(record) for record in result.scalars().all()]
|
|
|
|
async def get_batch(self, batch_id: str) -> Optional[SearchBatchSnapshot]:
|
|
"""
|
|
按稳定批次 ID 返回状态快照。
|
|
"""
|
|
result = await self._session.execute(
|
|
select(SubscriptionSearchBatch).where(
|
|
SubscriptionSearchBatch.batch_id == batch_id
|
|
)
|
|
)
|
|
record = result.scalars().first()
|
|
return _batch(record) if record else None
|
|
|
|
async def list_batch_tasks(self, batch_id: str) -> list[SearchTaskSnapshot]:
|
|
"""
|
|
按持久位置返回批次任务,供访问校验和当前阶段投影。
|
|
"""
|
|
result = await self._session.execute(
|
|
select(SubscriptionSearchTask, SubscribeActivity)
|
|
.outerjoin(SubscribeActivity, SubscribeActivity.event_id == SubscriptionSearchTask.task_id)
|
|
.where(SubscriptionSearchTask.batch_id == batch_id)
|
|
.order_by(SubscriptionSearchTask.position, SubscriptionSearchTask.id)
|
|
)
|
|
return [_task(record, activity) for record, activity in result.all()]
|