feat: add typed qBittorrent scheduled polling
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
"""Endpoints for typed scheduled-action status and history."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
|
||||
from media_library_viewer_api.dependencies import get_settings_store
|
||||
from media_library_viewer_api.models.scheduler import ( # type: ignore[reportMissingImports]
|
||||
SchedulerManualRunResponse,
|
||||
SchedulerRun,
|
||||
SchedulerRunsResponse,
|
||||
SchedulerSamplesResponse,
|
||||
SchedulerStatus,
|
||||
)
|
||||
from media_library_viewer_api.services.qbittorrent_store import QbittorrentSampleStore
|
||||
from media_library_viewer_api.services.scheduler import ( # type: ignore[reportMissingImports]
|
||||
SchedulerBusyError,
|
||||
get_scheduler,
|
||||
)
|
||||
from media_library_viewer_api.services.scheduler_actions import (
|
||||
QBITTORRENT_SPEED_ACTION, # type: ignore[reportMissingImports]
|
||||
)
|
||||
from media_library_viewer_api.services.scheduler_store import SchedulerRunStore # type: ignore[reportMissingImports]
|
||||
from media_library_viewer_api.services.settings_store import SettingsStore
|
||||
|
||||
router = APIRouter(prefix="/api/scheduler", tags=["scheduler"])
|
||||
|
||||
|
||||
def _safe_int(value: Any, default: int = 0) -> int:
|
||||
try:
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
|
||||
def _require_qbittorrent(service_id: str, store: SettingsStore) -> dict[str, Any]:
|
||||
service = store.get_service(service_id)
|
||||
if not service or service.get("service_type") != "qbittorrent":
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="qBittorrent service not found")
|
||||
return service
|
||||
|
||||
|
||||
@router.get("/services/{service_id}/status", response_model=SchedulerStatus)
|
||||
def get_scheduler_status(
|
||||
service_id: str,
|
||||
store: SettingsStore = Depends(get_settings_store),
|
||||
) -> SchedulerStatus:
|
||||
_require_qbittorrent(service_id, store)
|
||||
try:
|
||||
return SchedulerStatus(**get_scheduler().status(service_id, store))
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.get("/services/{service_id}/runs", response_model=SchedulerRunsResponse)
|
||||
def get_scheduler_runs(
|
||||
service_id: str,
|
||||
status_filter: str | None = Query(default=None, alias="status"),
|
||||
trigger: str | None = None,
|
||||
limit: int = Query(default=50, ge=1, le=100),
|
||||
offset: int = Query(default=0, ge=0),
|
||||
store: SettingsStore = Depends(get_settings_store),
|
||||
) -> SchedulerRunsResponse:
|
||||
_require_qbittorrent(service_id, store)
|
||||
items, total = SchedulerRunStore().list_runs(
|
||||
service_id,
|
||||
QBITTORRENT_SPEED_ACTION,
|
||||
status=status_filter,
|
||||
trigger=trigger,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
)
|
||||
return SchedulerRunsResponse(
|
||||
items=[SchedulerRun(**item) for item in items],
|
||||
total=total,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/services/{service_id}/run", response_model=SchedulerManualRunResponse)
|
||||
def run_scheduler_action(
|
||||
service_id: str,
|
||||
store: SettingsStore = Depends(get_settings_store),
|
||||
) -> SchedulerManualRunResponse:
|
||||
_require_qbittorrent(service_id, store)
|
||||
try:
|
||||
result = get_scheduler().run_now(service_id, store)
|
||||
except SchedulerBusyError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(exc)) from exc
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
|
||||
return SchedulerManualRunResponse(
|
||||
run=SchedulerRun(**result["run"]),
|
||||
status=SchedulerStatus(**result["status"]),
|
||||
)
|
||||
|
||||
|
||||
@router.get("/services/{service_id}/samples", response_model=SchedulerSamplesResponse)
|
||||
def get_scheduler_samples(
|
||||
service_id: str,
|
||||
window_seconds: int = Query(default=1_800, ge=60, le=86_400),
|
||||
store: SettingsStore = Depends(get_settings_store),
|
||||
) -> SchedulerSamplesResponse:
|
||||
_require_qbittorrent(service_id, store)
|
||||
since_ts = _safe_int(time.time()) - window_seconds
|
||||
samples = QbittorrentSampleStore().window(service_id, since_ts=since_ts)
|
||||
return SchedulerSamplesResponse(
|
||||
service_id=service_id,
|
||||
window_seconds=window_seconds,
|
||||
samples=samples,
|
||||
)
|
||||
|
||||
|
||||
__all__ = ["router"]
|
||||
Reference in New Issue
Block a user