fixed percentage and mb/s view
This commit is contained in:
parent
f0b9d50f85
commit
b76ffbf656
@ -1,24 +1,5 @@
|
|||||||
{
|
{
|
||||||
"pending": [
|
"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",
|
"id": "7e80b3c4-6837-4af7-bea3-ca037df594ce",
|
||||||
"serie_id": "highschool-dxd",
|
"serie_id": "highschool-dxd",
|
||||||
@ -1485,5 +1466,5 @@
|
|||||||
],
|
],
|
||||||
"active": [],
|
"active": [],
|
||||||
"failed": [],
|
"failed": [],
|
||||||
"timestamp": "2025-11-01T15:42:52.117823+00:00"
|
"timestamp": "2025-11-01T15:48:49.007428+00:00"
|
||||||
}
|
}
|
||||||
@ -253,15 +253,48 @@ class SeriesApp:
|
|||||||
if isinstance(progress_info, dict):
|
if isinstance(progress_info, dict):
|
||||||
# Calculate percentage based on downloaded/total bytes
|
# Calculate percentage based on downloaded/total bytes
|
||||||
downloaded = progress_info.get('downloaded_bytes', 0)
|
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:
|
if total_bytes > 0:
|
||||||
progress = (downloaded / total) * 100
|
progress = (downloaded / total_bytes) * 100
|
||||||
else:
|
else:
|
||||||
progress = 0
|
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:
|
else:
|
||||||
# Fallback for old-style float progress
|
# Fallback for old-style float progress
|
||||||
progress = float(progress_info)
|
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
|
# Notify progress via new callback system
|
||||||
self._callback_manager.notify_progress(
|
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:
|
if callback:
|
||||||
callback(progress)
|
callback(web_progress_dict)
|
||||||
|
|
||||||
# Propagate progress into the legacy callback chain so existing
|
# Propagate progress into the legacy callback chain so
|
||||||
# UI surfaces continue to receive updates without rewriting the
|
# existing UI surfaces continue to receive updates without
|
||||||
# old interfaces.
|
# rewriting the old interfaces.
|
||||||
# Call legacy progress_callback if provided
|
# Call legacy progress_callback if provided
|
||||||
if self.progress_callback:
|
if self.progress_callback:
|
||||||
self.progress_callback(ProgressInfo(
|
self.progress_callback(ProgressInfo(
|
||||||
|
|||||||
@ -470,6 +470,82 @@ class TestBroadcastCallbacks:
|
|||||||
# Verify callback was called
|
# Verify callback was called
|
||||||
mock_callback.assert_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:
|
class TestServiceLifecycle:
|
||||||
"""Test service start and stop operations."""
|
"""Test service start and stop operations."""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user