refactored callback
This commit is contained in:
@@ -27,11 +27,7 @@ from src.server.models.download import (
|
||||
QueueStatus,
|
||||
)
|
||||
from src.server.services.anime_service import AnimeService, AnimeServiceError
|
||||
from src.server.services.progress_service import (
|
||||
ProgressService,
|
||||
ProgressType,
|
||||
get_progress_service,
|
||||
)
|
||||
from src.server.services.progress_service import ProgressService, get_progress_service
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -92,10 +88,18 @@ class DownloadService:
|
||||
# Statistics tracking
|
||||
self._total_downloaded_mb: float = 0.0
|
||||
self._download_speeds: deque[float] = deque(maxlen=10)
|
||||
|
||||
|
||||
# Subscribe to SeriesApp download events for progress tracking
|
||||
if hasattr(anime_service, '_app') and hasattr(
|
||||
anime_service._app, 'download_status'
|
||||
):
|
||||
anime_service._app.download_status += (
|
||||
self._on_seriesapp_download_status
|
||||
)
|
||||
|
||||
# Load persisted queue
|
||||
self._load_queue()
|
||||
|
||||
|
||||
logger.info(
|
||||
"DownloadService initialized",
|
||||
max_retries=max_retries,
|
||||
@@ -146,6 +150,69 @@ class DownloadService:
|
||||
self._broadcast_callback = callback
|
||||
logger.debug("Broadcast callback registered")
|
||||
|
||||
def _on_seriesapp_download_status(self, args) -> None:
|
||||
"""Handle download status events from SeriesApp.
|
||||
|
||||
Updates the active download item with progress information.
|
||||
|
||||
Args:
|
||||
args: DownloadStatusEventArgs from SeriesApp
|
||||
"""
|
||||
try:
|
||||
# Only process if we have an active download
|
||||
if not self._active_download:
|
||||
return
|
||||
|
||||
# Match the event to the active download item
|
||||
# SeriesApp events include serie_folder, season, episode
|
||||
if (
|
||||
self._active_download.serie_folder == args.serie_folder
|
||||
and self._active_download.episode.season == args.season
|
||||
and self._active_download.episode.episode == args.episode
|
||||
):
|
||||
if args.status == "progress":
|
||||
# Update item progress
|
||||
self._active_download.progress = DownloadProgress(
|
||||
percent=args.progress,
|
||||
downloaded_mb=(
|
||||
args.progress * args.mbper_sec / 100
|
||||
if args.mbper_sec
|
||||
else 0.0
|
||||
),
|
||||
total_mb=None, # Not provided by SeriesApp
|
||||
speed_mbps=args.mbper_sec,
|
||||
eta_seconds=args.eta,
|
||||
)
|
||||
|
||||
# Track speed
|
||||
if args.mbper_sec:
|
||||
self._download_speeds.append(args.mbper_sec)
|
||||
|
||||
# Broadcast update
|
||||
asyncio.create_task(
|
||||
self._broadcast_update(
|
||||
"download_progress",
|
||||
{
|
||||
"download_id": self._active_download.id,
|
||||
"item_id": self._active_download.id,
|
||||
"serie_name": self._active_download.serie_name,
|
||||
"season": args.season,
|
||||
"episode": args.episode,
|
||||
"progress": (
|
||||
self._active_download.progress.model_dump(
|
||||
mode="json"
|
||||
)
|
||||
),
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
"Error handling SeriesApp download status",
|
||||
error=str(exc)
|
||||
)
|
||||
|
||||
async def _broadcast_update(self, update_type: str, data: dict) -> None:
|
||||
"""Broadcast update to connected WebSocket clients.
|
||||
|
||||
@@ -689,107 +756,6 @@ class DownloadService:
|
||||
f"Failed to retry: {str(e)}"
|
||||
) from e
|
||||
|
||||
def _create_progress_callback(self, item: DownloadItem) -> Callable:
|
||||
"""Create a progress callback for a download item.
|
||||
|
||||
Args:
|
||||
item: Download item to track progress for
|
||||
|
||||
Returns:
|
||||
Callback function for progress updates
|
||||
"""
|
||||
logger.info(
|
||||
f"Creating progress callback for item {item.id}"
|
||||
)
|
||||
|
||||
def progress_callback(progress_data: dict) -> None:
|
||||
"""Update progress and broadcast to clients."""
|
||||
try:
|
||||
logger.debug(
|
||||
f"Progress callback received: {progress_data}"
|
||||
)
|
||||
|
||||
# Update item progress
|
||||
item.progress = DownloadProgress(
|
||||
percent=progress_data.get("percent", 0.0),
|
||||
downloaded_mb=progress_data.get("downloaded_mb", 0.0),
|
||||
total_mb=progress_data.get("total_mb"),
|
||||
speed_mbps=progress_data.get("speed_mbps"),
|
||||
eta_seconds=progress_data.get("eta_seconds"),
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
f"Updated item progress: percent={item.progress.percent:.1f}%, "
|
||||
f"downloaded={item.progress.downloaded_mb:.1f}MB, "
|
||||
f"total={item.progress.total_mb}MB, "
|
||||
f"speed={item.progress.speed_mbps}MB/s"
|
||||
)
|
||||
|
||||
# Track speed for statistics
|
||||
if item.progress.speed_mbps:
|
||||
self._download_speeds.append(item.progress.speed_mbps)
|
||||
|
||||
# Update progress service
|
||||
# Schedule coroutines in a thread-safe manner
|
||||
# (callback may be called from executor thread)
|
||||
if item.progress.total_mb and item.progress.total_mb > 0:
|
||||
current_mb = int(item.progress.downloaded_mb)
|
||||
total_mb = int(item.progress.total_mb)
|
||||
|
||||
logger.debug(
|
||||
f"Updating progress service: current={current_mb}MB, "
|
||||
f"total={total_mb}MB"
|
||||
)
|
||||
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
self._progress_service.update_progress(
|
||||
progress_id=f"download_{item.id}",
|
||||
current=current_mb,
|
||||
total=total_mb,
|
||||
metadata={
|
||||
"speed_mbps": item.progress.speed_mbps,
|
||||
"eta_seconds": item.progress.eta_seconds,
|
||||
},
|
||||
),
|
||||
loop
|
||||
)
|
||||
except RuntimeError as e:
|
||||
logger.warning(
|
||||
f"Could not schedule progress update: {e}"
|
||||
)
|
||||
|
||||
# Broadcast update (fire and forget)
|
||||
logger.debug(
|
||||
f"Broadcasting download_progress event for item {item.id}"
|
||||
)
|
||||
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
self._broadcast_update(
|
||||
"download_progress",
|
||||
{
|
||||
"download_id": item.id,
|
||||
"item_id": item.id,
|
||||
"serie_name": item.serie_name,
|
||||
"season": item.episode.season,
|
||||
"episode": item.episode.episode,
|
||||
"progress": item.progress.model_dump(mode="json"),
|
||||
},
|
||||
),
|
||||
loop
|
||||
)
|
||||
except RuntimeError as e:
|
||||
logger.warning(
|
||||
f"Could not schedule broadcast: {e}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("Progress callback error", error=str(e))
|
||||
|
||||
return progress_callback
|
||||
|
||||
async def _process_download(self, item: DownloadItem) -> None:
|
||||
"""Process a single download item.
|
||||
|
||||
@@ -809,31 +775,10 @@ class DownloadService:
|
||||
season=item.episode.season,
|
||||
episode=item.episode.episode,
|
||||
)
|
||||
|
||||
# Start progress tracking
|
||||
await self._progress_service.start_progress(
|
||||
progress_id=f"download_{item.id}",
|
||||
progress_type=ProgressType.DOWNLOAD,
|
||||
title=f"Downloading {item.serie_name}",
|
||||
message=(
|
||||
f"S{item.episode.season:02d}E{item.episode.episode:02d}"
|
||||
),
|
||||
metadata={
|
||||
"item_id": item.id,
|
||||
"serie_name": item.serie_name,
|
||||
"season": item.episode.season,
|
||||
"episode": item.episode.episode,
|
||||
},
|
||||
)
|
||||
|
||||
# Create progress callback
|
||||
progress_callback = self._create_progress_callback(item)
|
||||
logger.info(
|
||||
f"Passing callback {progress_callback} to anime_service for "
|
||||
f"item {item.id}"
|
||||
)
|
||||
|
||||
|
||||
# Execute download via anime service
|
||||
# Note: AnimeService handles progress via SeriesApp events
|
||||
# Progress updates received via _on_seriesapp_download_status
|
||||
# Use serie_folder if available, otherwise fall back to serie_id
|
||||
# for backwards compatibility with old queue items
|
||||
folder = item.serie_folder if item.serie_folder else item.serie_id
|
||||
@@ -842,7 +787,6 @@ class DownloadService:
|
||||
season=item.episode.season,
|
||||
episode=item.episode.episode,
|
||||
key=item.serie_id,
|
||||
callback=progress_callback,
|
||||
)
|
||||
|
||||
# Handle result
|
||||
@@ -860,17 +804,7 @@ class DownloadService:
|
||||
"Download completed successfully", item_id=item.id
|
||||
)
|
||||
|
||||
# Complete progress tracking
|
||||
await self._progress_service.complete_progress(
|
||||
progress_id=f"download_{item.id}",
|
||||
message="Download completed successfully",
|
||||
metadata={
|
||||
"downloaded_mb": item.progress.downloaded_mb
|
||||
if item.progress
|
||||
else 0,
|
||||
},
|
||||
)
|
||||
|
||||
# Broadcast completion (progress already handled by events)
|
||||
await self._broadcast_update(
|
||||
"download_complete",
|
||||
{
|
||||
@@ -901,13 +835,7 @@ class DownloadService:
|
||||
retry_count=item.retry_count,
|
||||
)
|
||||
|
||||
# Fail progress tracking
|
||||
await self._progress_service.fail_progress(
|
||||
progress_id=f"download_{item.id}",
|
||||
error_message=str(e),
|
||||
metadata={"retry_count": item.retry_count},
|
||||
)
|
||||
|
||||
# Broadcast failure (progress already handled by events)
|
||||
await self._broadcast_update(
|
||||
"download_failed",
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user