fix queue
This commit is contained in:
@@ -274,11 +274,25 @@ def _read_live_start_state(client: Any, torrent_hash: str) -> dict[str, Any]:
|
|||||||
result[key] = int(value or 0) if key in {'state', 'active', 'open', 'priority'} else str(value or '')
|
result[key] = int(value or 0) if key in {'state', 'active', 'open', 'priority'} else str(value or '')
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
result[f'{key}_error'] = str(exc)
|
result[f'{key}_error'] = str(exc)
|
||||||
# Note: Nie uznajemy d.is_open ani state=1 za wznowienie; Paused też potrafi mieć te wartości.
|
# Note: Realny slot liczymy po d.is_active=1. Dodatkowo zwracamy state/open/priority,
|
||||||
# Smart Queue zalicza start dopiero po d.is_active=1, czyli po realnym zdjęciu pauzy.
|
# bo przy masowym resume rTorrent czasem przyjmuje start, ale aktywuje transfer dopiero w kolejnym ticku.
|
||||||
result['started'] = bool(int(result.get('active') or 0))
|
result['started'] = bool(int(result.get('active') or 0))
|
||||||
|
result['start_accepted'] = bool(int(result.get('state') or 0) or int(result.get('open') or 0))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _refresh_active_slots(profile: dict, excluded: set[str], manage_stopped: bool) -> tuple[int, list[dict[str, Any]]]:
|
||||||
|
"""Read a fresh torrent snapshot and count real active Smart Queue slots."""
|
||||||
|
fresh = rtorrent.list_torrents(profile)
|
||||||
|
active = [
|
||||||
|
t for t in fresh
|
||||||
|
if str(t.get('hash') or '') not in excluded
|
||||||
|
and _is_running_download_slot(t)
|
||||||
|
]
|
||||||
|
# Note: Po batchowym resume nie ufamy staremu snapshotowi; odświeżenie z rTorrent
|
||||||
|
# pozwala dobić kolejkę także wtedy, gdy aktywacja nastąpiła z opóźnieniem.
|
||||||
|
return len(active), fresh
|
||||||
|
|
||||||
def _set_smart_queue_label(client: Any, torrent_hash: str, attempts: int = 3) -> bool:
|
def _set_smart_queue_label(client: Any, torrent_hash: str, attempts: int = 3) -> bool:
|
||||||
for attempt in range(max(1, attempts)):
|
for attempt in range(max(1, attempts)):
|
||||||
try:
|
try:
|
||||||
@@ -508,18 +522,23 @@ def check(profile: dict | None = None, user_id: int | None = None, force: bool =
|
|||||||
|
|
||||||
candidate_queue = [t for t in candidates if str(t.get('hash') or '') and str(t.get('hash') or '') not in pause_hashes]
|
candidate_queue = [t for t in candidates if str(t.get('hash') or '') and str(t.get('hash') or '') not in pause_hashes]
|
||||||
active_slots = active_after_pause
|
active_slots = active_after_pause
|
||||||
|
max_resume_attempts = max(len(candidate_queue), max_active * 3)
|
||||||
|
|
||||||
# Note: Resume dziala teraz w petli do pelnego limitu z ustawien. Gdy batch nie przejdzie
|
# Note: Resume działa w rundach aż do pełnego limitu z ustawień. Po każdej rundzie
|
||||||
# na d.is_active=1, Smart Queue nie zatrzymuje sie, tylko probuje nastepnych kandydatow.
|
# pobieramy świeży snapshot z rTorrent, bo masowe d.resume/d.start nie zawsze widać
|
||||||
while candidate_queue and active_slots < max_active:
|
# natychmiast w d.is_active na pojedynczym RPC.
|
||||||
|
while candidate_queue and active_slots < max_active and len(attempted_hashes) < max_resume_attempts:
|
||||||
slots_left = max_active - active_slots
|
slots_left = max_active - active_slots
|
||||||
batch = candidate_queue[:slots_left]
|
# Note: Bierzemy mały nadmiar kandydatów tylko wtedy, gdy poprzednie resume nie zwiększyło
|
||||||
candidate_queue = candidate_queue[slots_left:]
|
# liczby aktywnych slotów; to naprawia przypadek, gdy część pauzowanych nie wstaje po komendzie.
|
||||||
|
batch_size = min(len(candidate_queue), max(1, slots_left))
|
||||||
|
batch = candidate_queue[:batch_size]
|
||||||
|
candidate_queue = candidate_queue[batch_size:]
|
||||||
batch_requested: list[str] = []
|
batch_requested: list[str] = []
|
||||||
|
|
||||||
for t in batch:
|
for t in batch:
|
||||||
h = str(t.get('hash') or '')
|
h = str(t.get('hash') or '')
|
||||||
if not h:
|
if not h or h in attempted_hashes:
|
||||||
continue
|
continue
|
||||||
attempted_hashes.add(h)
|
attempted_hashes.add(h)
|
||||||
try:
|
try:
|
||||||
@@ -529,6 +548,10 @@ def check(profile: dict | None = None, user_id: int | None = None, force: bool =
|
|||||||
batch_requested.append(h)
|
batch_requested.append(h)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
start_failed.append({'hash': h, 'error': str(exc)})
|
start_failed.append({'hash': h, 'error': str(exc)})
|
||||||
|
time.sleep(0.03)
|
||||||
|
|
||||||
|
if not batch_requested:
|
||||||
|
continue
|
||||||
|
|
||||||
active_verified, batch_no_effect = _verify_started_downloads(c, batch_requested)
|
active_verified, batch_no_effect = _verify_started_downloads(c, batch_requested)
|
||||||
start_no_effect.extend(batch_no_effect)
|
start_no_effect.extend(batch_no_effect)
|
||||||
@@ -536,10 +559,24 @@ def check(profile: dict | None = None, user_id: int | None = None, force: bool =
|
|||||||
if h not in resumed:
|
if h not in resumed:
|
||||||
_restore_auto_label(c, profile_id, h, None)
|
_restore_auto_label(c, profile_id, h, None)
|
||||||
resumed.append(h)
|
resumed.append(h)
|
||||||
active_slots += len([h for h in active_verified if h])
|
|
||||||
|
|
||||||
if not batch_requested:
|
fresh_active_slots, fresh_torrents = _refresh_active_slots(profile, excluded, manage_stopped)
|
||||||
break
|
active_slots = max(active_slots, fresh_active_slots)
|
||||||
|
|
||||||
|
# Note: Jeżeli rTorrent wznowił torrent dopiero po odświeżeniu listy, dopisujemy go
|
||||||
|
# do resumed i zdejmujemy techniczny label Smart Queue.
|
||||||
|
fresh_by_hash = {str(t.get('hash') or ''): t for t in fresh_torrents}
|
||||||
|
for h in batch_requested:
|
||||||
|
live_t = fresh_by_hash.get(h)
|
||||||
|
if live_t and _is_running_download_slot(live_t) and h not in resumed:
|
||||||
|
_restore_auto_label(c, profile_id, h, None)
|
||||||
|
resumed.append(h)
|
||||||
|
|
||||||
|
if active_slots < max_active and not candidate_queue:
|
||||||
|
# Note: Ostatnia próba dla pozycji, które przyjęły start, ale jeszcze nie pokazały active=1.
|
||||||
|
time.sleep(0.75)
|
||||||
|
fresh_active_slots, fresh_torrents = _refresh_active_slots(profile, excluded, manage_stopped)
|
||||||
|
active_slots = max(active_slots, fresh_active_slots)
|
||||||
|
|
||||||
resumed_set = set(resumed)
|
resumed_set = set(resumed)
|
||||||
waiting_hashes = {
|
waiting_hashes = {
|
||||||
|
|||||||
Reference in New Issue
Block a user