fix: progress part 1. percentage is working
This commit is contained in:
parent
34019b7e65
commit
029abb9be2
@ -13,8 +13,8 @@
|
|||||||
"status": "cancelled",
|
"status": "cancelled",
|
||||||
"priority": "NORMAL",
|
"priority": "NORMAL",
|
||||||
"added_at": "2025-11-20T17:12:34.485225Z",
|
"added_at": "2025-11-20T17:12:34.485225Z",
|
||||||
"started_at": "2025-11-20T18:09:04.527701Z",
|
"started_at": "2025-11-20T18:15:48.216607Z",
|
||||||
"completed_at": "2025-11-20T18:09:27.852314Z",
|
"completed_at": "2025-11-20T18:16:38.929297Z",
|
||||||
"progress": null,
|
"progress": null,
|
||||||
"error": null,
|
"error": null,
|
||||||
"retry_count": 0,
|
"retry_count": 0,
|
||||||
@ -943,5 +943,5 @@
|
|||||||
],
|
],
|
||||||
"active": [],
|
"active": [],
|
||||||
"failed": [],
|
"failed": [],
|
||||||
"timestamp": "2025-11-20T18:09:27.852598+00:00"
|
"timestamp": "2025-11-20T18:16:38.929570+00:00"
|
||||||
}
|
}
|
||||||
@ -260,6 +260,7 @@ class SeriesApp:
|
|||||||
progress=(downloaded / total_bytes) * 100 if total_bytes else 0,
|
progress=(downloaded / total_bytes) * 100 if total_bytes else 0,
|
||||||
eta=eta,
|
eta=eta,
|
||||||
mbper_sec=mbper_sec,
|
mbper_sec=mbper_sec,
|
||||||
|
item_id=item_id,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# Perform download in thread to avoid blocking event loop
|
# Perform download in thread to avoid blocking event loop
|
||||||
|
|||||||
@ -37,6 +37,7 @@ class AnimeService:
|
|||||||
self._app = series_app
|
self._app = series_app
|
||||||
self._directory = series_app.directory_to_search
|
self._directory = series_app.directory_to_search
|
||||||
self._progress_service = progress_service or get_progress_service()
|
self._progress_service = progress_service or get_progress_service()
|
||||||
|
self._event_loop: Optional[asyncio.AbstractEventLoop] = None
|
||||||
# Subscribe to SeriesApp events
|
# Subscribe to SeriesApp events
|
||||||
# Note: Events library uses assignment (=), not += operator
|
# Note: Events library uses assignment (=), not += operator
|
||||||
try:
|
try:
|
||||||
@ -54,13 +55,17 @@ class AnimeService:
|
|||||||
args: DownloadStatusEventArgs from SeriesApp
|
args: DownloadStatusEventArgs from SeriesApp
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Check if there's a running event loop
|
# Get event loop - try running loop first, then stored loop
|
||||||
|
loop = None
|
||||||
try:
|
try:
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
# No running loop - log and skip
|
# No running loop in this thread - use stored loop
|
||||||
|
loop = self._event_loop
|
||||||
|
|
||||||
|
if not loop:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"No running event loop for download status event",
|
"No event loop available for download status event",
|
||||||
status=args.status
|
status=args.status
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@ -74,38 +79,42 @@ class AnimeService:
|
|||||||
|
|
||||||
# Map SeriesApp download events to progress service
|
# Map SeriesApp download events to progress service
|
||||||
if args.status == "started":
|
if args.status == "started":
|
||||||
loop.create_task(
|
asyncio.run_coroutine_threadsafe(
|
||||||
self._progress_service.start_progress(
|
self._progress_service.start_progress(
|
||||||
progress_id=progress_id,
|
progress_id=progress_id,
|
||||||
progress_type=ProgressType.DOWNLOAD,
|
progress_type=ProgressType.DOWNLOAD,
|
||||||
title=f"Downloading {args.serie_folder}",
|
title=f"Downloading {args.serie_folder}",
|
||||||
message=f"S{args.season:02d}E{args.episode:02d}",
|
message=f"S{args.season:02d}E{args.episode:02d}",
|
||||||
metadata={"item_id": args.item_id} if args.item_id else None,
|
metadata={"item_id": args.item_id} if args.item_id else None,
|
||||||
)
|
),
|
||||||
|
loop
|
||||||
)
|
)
|
||||||
elif args.status == "progress":
|
elif args.status == "progress":
|
||||||
loop.create_task(
|
asyncio.run_coroutine_threadsafe(
|
||||||
self._progress_service.update_progress(
|
self._progress_service.update_progress(
|
||||||
progress_id=progress_id,
|
progress_id=progress_id,
|
||||||
current=int(args.progress),
|
current=int(args.progress),
|
||||||
total=100,
|
total=100,
|
||||||
message=args.message or "Downloading...",
|
message=args.message or "Downloading...",
|
||||||
metadata={"item_id": args.item_id} if args.item_id else None,
|
metadata={"item_id": args.item_id} if args.item_id else None,
|
||||||
)
|
),
|
||||||
|
loop
|
||||||
)
|
)
|
||||||
elif args.status == "completed":
|
elif args.status == "completed":
|
||||||
loop.create_task(
|
asyncio.run_coroutine_threadsafe(
|
||||||
self._progress_service.complete_progress(
|
self._progress_service.complete_progress(
|
||||||
progress_id=progress_id,
|
progress_id=progress_id,
|
||||||
message="Download completed",
|
message="Download completed",
|
||||||
)
|
),
|
||||||
|
loop
|
||||||
)
|
)
|
||||||
elif args.status == "failed":
|
elif args.status == "failed":
|
||||||
loop.create_task(
|
asyncio.run_coroutine_threadsafe(
|
||||||
self._progress_service.fail_progress(
|
self._progress_service.fail_progress(
|
||||||
progress_id=progress_id,
|
progress_id=progress_id,
|
||||||
error_message=args.message or str(args.error),
|
error_message=args.message or str(args.error),
|
||||||
)
|
),
|
||||||
|
loop
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error(
|
logger.error(
|
||||||
@ -122,56 +131,65 @@ class AnimeService:
|
|||||||
try:
|
try:
|
||||||
scan_id = "library_scan"
|
scan_id = "library_scan"
|
||||||
|
|
||||||
# Check if there's a running event loop
|
# Get event loop - try running loop first, then stored loop
|
||||||
|
loop = None
|
||||||
try:
|
try:
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
# No running loop - log and skip
|
# No running loop in this thread - use stored loop
|
||||||
|
loop = self._event_loop
|
||||||
|
|
||||||
|
if not loop:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"No running event loop for scan status event",
|
"No event loop available for scan status event",
|
||||||
status=args.status
|
status=args.status
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Map SeriesApp scan events to progress service
|
# Map SeriesApp scan events to progress service
|
||||||
if args.status == "started":
|
if args.status == "started":
|
||||||
loop.create_task(
|
asyncio.run_coroutine_threadsafe(
|
||||||
self._progress_service.start_progress(
|
self._progress_service.start_progress(
|
||||||
progress_id=scan_id,
|
progress_id=scan_id,
|
||||||
progress_type=ProgressType.SCAN,
|
progress_type=ProgressType.SCAN,
|
||||||
title="Scanning anime library",
|
title="Scanning anime library",
|
||||||
message=args.message or "Initializing scan...",
|
message=args.message or "Initializing scan...",
|
||||||
)
|
),
|
||||||
|
loop
|
||||||
)
|
)
|
||||||
elif args.status == "progress":
|
elif args.status == "progress":
|
||||||
loop.create_task(
|
asyncio.run_coroutine_threadsafe(
|
||||||
self._progress_service.update_progress(
|
self._progress_service.update_progress(
|
||||||
progress_id=scan_id,
|
progress_id=scan_id,
|
||||||
current=args.current,
|
current=args.current,
|
||||||
total=args.total,
|
total=args.total,
|
||||||
message=args.message or f"Scanning: {args.folder}",
|
message=args.message or f"Scanning: {args.folder}",
|
||||||
)
|
),
|
||||||
|
loop
|
||||||
)
|
)
|
||||||
elif args.status == "completed":
|
elif args.status == "completed":
|
||||||
loop.create_task(
|
asyncio.run_coroutine_threadsafe(
|
||||||
self._progress_service.complete_progress(
|
self._progress_service.complete_progress(
|
||||||
progress_id=scan_id,
|
progress_id=scan_id,
|
||||||
message=args.message or "Scan completed",
|
message=args.message or "Scan completed",
|
||||||
)
|
),
|
||||||
|
loop
|
||||||
)
|
)
|
||||||
elif args.status == "failed":
|
elif args.status == "failed":
|
||||||
loop.create_task(
|
asyncio.run_coroutine_threadsafe(
|
||||||
self._progress_service.fail_progress(
|
self._progress_service.fail_progress(
|
||||||
progress_id=scan_id,
|
progress_id=scan_id,
|
||||||
error_message=args.message or str(args.error),
|
error_message=args.message or str(args.error),
|
||||||
)
|
),
|
||||||
|
loop
|
||||||
)
|
)
|
||||||
elif args.status == "cancelled":
|
elif args.status == "cancelled":
|
||||||
loop.create_task(
|
asyncio.run_coroutine_threadsafe(
|
||||||
self._progress_service.fail_progress(
|
self._progress_service.fail_progress(
|
||||||
progress_id=scan_id,
|
progress_id=scan_id,
|
||||||
error_message=args.message or "Scan cancelled",
|
error_message=args.message or "Scan cancelled",
|
||||||
)
|
),
|
||||||
|
loop
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error("Error handling scan status event", error=str(exc))
|
logger.error("Error handling scan status event", error=str(exc))
|
||||||
@ -228,6 +246,9 @@ class AnimeService:
|
|||||||
forwarded to the ProgressService through event handlers.
|
forwarded to the ProgressService through event handlers.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
# Store event loop for event handlers
|
||||||
|
self._event_loop = asyncio.get_running_loop()
|
||||||
|
|
||||||
# SeriesApp.rescan is now async and handles events internally
|
# SeriesApp.rescan is now async and handles events internally
|
||||||
await self._app.rescan()
|
await self._app.rescan()
|
||||||
|
|
||||||
@ -247,21 +268,33 @@ class AnimeService:
|
|||||||
season: int,
|
season: int,
|
||||||
episode: int,
|
episode: int,
|
||||||
key: str,
|
key: str,
|
||||||
|
item_id: Optional[str] = None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Start a download.
|
"""Start a download.
|
||||||
|
|
||||||
The SeriesApp now handles progress tracking via events which are
|
The SeriesApp now handles progress tracking via events which are
|
||||||
forwarded to the ProgressService through event handlers.
|
forwarded to the ProgressService through event handlers.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
serie_folder: Serie folder name
|
||||||
|
season: Season number
|
||||||
|
episode: Episode number
|
||||||
|
key: Serie key
|
||||||
|
item_id: Optional download queue item ID for tracking
|
||||||
|
|
||||||
Returns True on success or raises AnimeServiceError on failure.
|
Returns True on success or raises AnimeServiceError on failure.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
# Store event loop for event handlers
|
||||||
|
self._event_loop = asyncio.get_running_loop()
|
||||||
|
|
||||||
# SeriesApp.download is now async and handles events internally
|
# SeriesApp.download is now async and handles events internally
|
||||||
return await self._app.download(
|
return await self._app.download(
|
||||||
serie_folder=serie_folder,
|
serie_folder=serie_folder,
|
||||||
season=season,
|
season=season,
|
||||||
episode=episode,
|
episode=episode,
|
||||||
key=key,
|
key=key,
|
||||||
|
item_id=item_id,
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.exception("download failed")
|
logger.exception("download failed")
|
||||||
|
|||||||
@ -808,6 +808,7 @@ class DownloadService:
|
|||||||
season=item.episode.season,
|
season=item.episode.season,
|
||||||
episode=item.episode.episode,
|
episode=item.episode.episode,
|
||||||
key=item.serie_id,
|
key=item.serie_id,
|
||||||
|
item_id=item.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Handle result
|
# Handle result
|
||||||
|
|||||||
@ -247,6 +247,7 @@ class TestDownload:
|
|||||||
season=1,
|
season=1,
|
||||||
episode=1,
|
episode=1,
|
||||||
key="test_key",
|
key="test_key",
|
||||||
|
item_id=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@ -272,6 +273,7 @@ class TestDownload:
|
|||||||
season=1,
|
season=1,
|
||||||
episode=1,
|
episode=1,
|
||||||
key="test_key",
|
key="test_key",
|
||||||
|
item_id=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user