fixed percentage and mb/s view

This commit is contained in:
Lukas 2025-11-01 16:49:12 +01:00
parent f0b9d50f85
commit b76ffbf656
3 changed files with 119 additions and 28 deletions

View File

@ -1,24 +1,5 @@
{
"pending": [
{
"id": "54533241-ed24-482f-85d7-c9218352ae7f",
"serie_id": "highschool-dxd",
"serie_name": "Highschool DxD",
"episode": {
"season": 4,
"episode": 7,
"title": null
},
"status": "pending",
"priority": "normal",
"added_at": "2025-11-01T14:30:56.882358Z",
"started_at": null,
"completed_at": null,
"progress": null,
"error": null,
"retry_count": 0,
"source_url": null
},
{
"id": "7e80b3c4-6837-4af7-bea3-ca037df594ce",
"serie_id": "highschool-dxd",
@ -1485,5 +1466,5 @@
],
"active": [],
"failed": [],
"timestamp": "2025-11-01T15:42:52.117823+00:00"
"timestamp": "2025-11-01T15:48:49.007428+00:00"
}

View File

@ -253,15 +253,48 @@ class SeriesApp:
if isinstance(progress_info, dict):
# Calculate percentage based on downloaded/total bytes
downloaded = progress_info.get('downloaded_bytes', 0)
total = progress_info.get('total_bytes') or progress_info.get('total_bytes_estimate', 0)
total_bytes = (
progress_info.get('total_bytes')
or progress_info.get('total_bytes_estimate', 0)
)
if total > 0:
progress = (downloaded / total) * 100
if total_bytes > 0:
progress = (downloaded / total_bytes) * 100
else:
progress = 0
# Extract speed and ETA from yt-dlp progress dict
speed = progress_info.get('speed', 0) # bytes/sec
eta = progress_info.get('eta') # seconds
# Convert to expected format for web API callback
# Web API expects: percent, downloaded_mb, total_mb,
# speed_mbps, eta_seconds
web_progress_dict = {
'percent': progress,
# Convert bytes to MB
'downloaded_mb': downloaded / (1024 * 1024),
'total_mb': (
total_bytes / (1024 * 1024)
if total_bytes > 0
else None
),
# Convert bytes/sec to MB/sec
'speed_mbps': (
speed / (1024 * 1024) if speed else None
),
'eta_seconds': eta,
}
else:
# Fallback for old-style float progress
progress = float(progress_info)
web_progress_dict = {
'percent': progress,
'downloaded_mb': 0.0,
'total_mb': None,
'speed_mbps': None,
'eta_seconds': None,
}
# Notify progress via new callback system
self._callback_manager.notify_progress(
@ -281,13 +314,14 @@ class SeriesApp:
)
)
# Call legacy callback if provided
# Call callback with web API format
# (dict with detailed progress info)
if callback:
callback(progress)
callback(web_progress_dict)
# Propagate progress into the legacy callback chain so existing
# UI surfaces continue to receive updates without rewriting the
# old interfaces.
# Propagate progress into the legacy callback chain so
# existing UI surfaces continue to receive updates without
# rewriting the old interfaces.
# Call legacy progress_callback if provided
if self.progress_callback:
self.progress_callback(ProgressInfo(

View File

@ -470,6 +470,82 @@ class TestBroadcastCallbacks:
# Verify callback was called
mock_callback.assert_called()
@pytest.mark.asyncio
async def test_progress_callback_format(self, download_service):
"""Test that progress callback receives correct data format."""
# Set up a mock callback to capture progress updates
progress_updates = []
def capture_progress(progress_data: dict):
progress_updates.append(progress_data)
# Mock download to simulate progress
async def mock_download_with_progress(*args, **kwargs):
# Get the callback from kwargs
callback = kwargs.get('callback')
if callback:
# Simulate progress updates with the expected format
callback({
'percent': 50.0,
'downloaded_mb': 250.5,
'total_mb': 501.0,
'speed_mbps': 5.2,
'eta_seconds': 48,
})
return True
download_service._anime_service.download = mock_download_with_progress
# Add an item to the queue
await download_service.add_to_queue(
serie_id="series-1",
serie_name="Test Series",
episodes=[EpisodeIdentifier(season=1, episode=1)],
)
# Process the download
item = download_service._pending_queue.popleft()
del download_service._pending_items_by_id[item.id]
# Replace the progress callback with our capture function
original_callback = download_service._create_progress_callback
def wrapper(item):
callback = original_callback(item)
def wrapped_callback(data):
capture_progress(data)
callback(data)
return wrapped_callback
download_service._create_progress_callback = wrapper
await download_service._process_download(item)
# Verify progress callback was called with correct format
assert len(progress_updates) > 0
progress_data = progress_updates[0]
# Check all expected keys are present
assert 'percent' in progress_data
assert 'downloaded_mb' in progress_data
assert 'total_mb' in progress_data
assert 'speed_mbps' in progress_data
assert 'eta_seconds' in progress_data
# Verify values are of correct type
assert isinstance(progress_data['percent'], (int, float))
assert isinstance(progress_data['downloaded_mb'], (int, float))
assert (
progress_data['total_mb'] is None
or isinstance(progress_data['total_mb'], (int, float))
)
assert (
progress_data['speed_mbps'] is None
or isinstance(progress_data['speed_mbps'], (int, float))
)
class TestServiceLifecycle:
"""Test service start and stop operations."""