Compare commits

..

3 Commits

Author SHA1 Message Date
084488a88c chore: bump version 2026-07-31 08:45:27 +02:00
270da18543 fix: emit download_progress events during direct stream downloads
When _try_direct_stream() succeeded, it streamed chunks directly via
requests.get() without firing any progress events. This caused the WebSocket
frontend to only see 'started' (0%) and 'completed' (100%) — no incremental
updates.

Add per-chunk progress events to _try_direct_stream that mirror yt-dlp's
hook format (downloaded_bytes, total_bytes, speed, eta, status), emitted at
~1% intervals. These flow through the existing download_progress handler chain
(SeriesApp → anime_service → progress_service → WebSocket → frontend).
2026-07-31 08:42:59 +02:00
163532b1ef fix: use data.key instead of data.data in series_updated handler
The websocket-client strips the outer {type, data, ...} wrapper before
emitting to handlers, so handlers receive the inner series data object
directly (e.g. {key, name, missing_episodes}) — not {type, data: {...}}.

The series_updated handler was checking data.data which always failed,
causing every update to fall back to a full loadSeries() call instead of
calling updateSingleSeries() directly. This prevented the missing
episodes count from updating in real-time after a download completed.

Fix: check data.key directly and pass data (not data.data) to
updateSingleSeries().
2026-07-31 08:37:44 +02:00
5 changed files with 46 additions and 7 deletions

View File

@@ -1 +1 @@
v1.5.2
v1.5.3

View File

@@ -1,6 +1,6 @@
{
"name": "aniworld-web",
"version": "1.5.2",
"version": "1.5.3",
"description": "Aniworld Anime Download Manager - Web Frontend",
"type": "module",
"scripts": {

View File

@@ -5,6 +5,7 @@ import logging
import os
import re
import shutil
import time
import threading
from pathlib import Path
from urllib.parse import quote
@@ -383,6 +384,12 @@ class AniworldLoader(Loader):
"Direct stream download starting (type=%s)",
content_type
)
total_size = int(response.headers.get(
"Content-Length", 0
))
received = 0
last_emit = 0
start_time = time.monotonic()
with open(output_path, "wb") as fh:
for chunk in response.iter_content(chunk_size=1024 * 1024):
if self._cancel_flag.is_set():
@@ -391,7 +398,33 @@ class AniworldLoader(Loader):
)
return False
if chunk:
received += len(chunk)
fh.write(chunk)
# Emit progress events at ~1% intervals
if total_size > 0:
pct = (received / total_size) * 100
if pct - last_emit >= 1.0 or received == total_size:
elapsed = time.monotonic() - start_time
speed_bps = (
received / elapsed
if elapsed > 0 else 0
)
eta = (
int((total_size - received) / speed_bps)
if speed_bps > 0 else None
)
self.events.download_progress({
"downloaded_bytes": received,
"total_bytes": total_size,
"speed": speed_bps,
"eta": eta,
"status": (
"finished"
if received >= total_size
else "downloading"
),
})
last_emit = pct
return True
except requests.RequestException as exc:
logger.warning("Direct stream download failed: %s", exc)

View File

@@ -136,13 +136,16 @@ AniWorld.IndexSocketHandler = (function() {
// Series events
socket.on(WS_EVENTS.SERIES_UPDATED, function(data) {
console.log('Series updated:', data);
// Use the data directly to update the series instead of full refresh
if (data && data.data && AniWorld.SeriesManager && AniWorld.SeriesManager.updateSingleSeries) {
AniWorld.SeriesManager.updateSingleSeries(data.data);
// NOTE: websocket-client.js strips the outer {type, data, ...} wrapper
// before emitting, so `data` here is the inner series data object
// (e.g. {key, name, missing_episodes, ...}) — NOT {type, data, ...}.
// AniWorld.SeriesManager.updateSingleSeries() expects this flat object.
if (data && data.key && AniWorld.SeriesManager && AniWorld.SeriesManager.updateSingleSeries) {
AniWorld.SeriesManager.updateSingleSeries(data);
} else {
// Fallback to full reload if data is incomplete
console.warn('Incomplete series update data, falling back to full reload');
console.warn('Incomplete series update data, falling back to full reload', data);
if (AniWorld.SeriesManager && AniWorld.SeriesManager.loadSeries) {
AniWorld.SeriesManager.loadSeries();
}

3
uv.lock generated Normal file
View File

@@ -0,0 +1,3 @@
version = 1
revision = 3
requires-python = ">=3.12"