fix queue
This commit is contained in:
@@ -469,19 +469,19 @@ def check(profile: dict | None = None, user_id: int | None = None, force: bool =
|
|||||||
to_pause: list[dict[str, Any]] = pause_rank[:max(0, len(downloading) - max_active)]
|
to_pause: list[dict[str, Any]] = pause_rank[:max(0, len(downloading) - max_active)]
|
||||||
pause_hashes = {str(t.get('hash') or '') for t in to_pause}
|
pause_hashes = {str(t.get('hash') or '') for t in to_pause}
|
||||||
|
|
||||||
# Note: Rotacja stalled działa tylko przy pełnej kolejce. Gdy brakuje slotów, Smart Queue ma
|
# Note: Stalled jest wymieniany nie tylko przy pelnej kolejce. Najpierw wypelniamy wolne sloty,
|
||||||
# najpierw dobrać brakujące pozycje, a nie pauzować już istniejące lub błędnie uznane za stalled.
|
# a dopiero nadmiar kandydatow zuzywamy na rotacje martwych/stalled pobran.
|
||||||
if candidates and len(downloading) >= max_active:
|
free_slots_before_pause = max(0, max_active - max(0, len(downloading) - len(to_pause)))
|
||||||
replaceable_stalled = [t for t in stalled if str(t.get('hash') or '') not in pause_hashes]
|
stalled_rotation_slots = max(0, len(candidates) - free_slots_before_pause)
|
||||||
for t in replaceable_stalled[:max(0, len(candidates) - len(to_pause))]:
|
for t in stalled:
|
||||||
|
h = str(t.get('hash') or '')
|
||||||
|
if not h or h in pause_hashes or stalled_rotation_slots <= 0:
|
||||||
|
continue
|
||||||
to_pause.append(t)
|
to_pause.append(t)
|
||||||
pause_hashes.add(str(t.get('hash') or ''))
|
pause_hashes.add(h)
|
||||||
|
stalled_rotation_slots -= 1
|
||||||
|
|
||||||
active_after_pause = max(0, len(downloading) - len(to_pause))
|
active_after_pause = max(0, len(downloading) - len(to_pause))
|
||||||
available_slots = max(0, max_active - active_after_pause)
|
|
||||||
to_resume = candidates[:available_slots]
|
|
||||||
# Note: Pozycje poza bieżącą pulą startu zostają jawnie oznaczone jako oczekujące Smart Queue.
|
|
||||||
to_label_waiting = candidates[available_slots:]
|
|
||||||
|
|
||||||
c = rtorrent.client_for(profile)
|
c = rtorrent.client_for(profile)
|
||||||
rtorrent_cap = _ensure_rtorrent_download_cap(c, max_active)
|
rtorrent_cap = _ensure_rtorrent_download_cap(c, max_active)
|
||||||
@@ -492,50 +492,80 @@ def check(profile: dict | None = None, user_id: int | None = None, force: bool =
|
|||||||
start_no_effect: list[dict[str, Any]] = []
|
start_no_effect: list[dict[str, Any]] = []
|
||||||
resume_requested: list[str] = []
|
resume_requested: list[str] = []
|
||||||
start_results: list[dict[str, Any]] = []
|
start_results: list[dict[str, Any]] = []
|
||||||
|
attempted_hashes: set[str] = set()
|
||||||
|
|
||||||
for t in to_pause:
|
for t in to_pause:
|
||||||
try:
|
|
||||||
c.call('d.pause', t['hash'])
|
|
||||||
if not _mark_auto_paused(c, profile_id, t):
|
|
||||||
label_failed.append(t['hash'])
|
|
||||||
paused.append(t['hash'])
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
for t in to_label_waiting:
|
|
||||||
h = str(t.get('hash') or '')
|
|
||||||
if not h or h in pause_hashes:
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
if not _mark_auto_paused(c, profile_id, t):
|
|
||||||
label_failed.append(h)
|
|
||||||
except Exception:
|
|
||||||
label_failed.append(h)
|
|
||||||
|
|
||||||
# Note: Startujemy całą pulę kandydatów w jednej rundzie. Label zdejmujemy po zaakceptowanym RPC,
|
|
||||||
# bo rTorrent może trzymać część pozycji w swojej kolejce z active=0 mimo poprawnego d.start/d.resume.
|
|
||||||
for t in to_resume:
|
|
||||||
h = str(t.get('hash') or '')
|
h = str(t.get('hash') or '')
|
||||||
if not h:
|
if not h:
|
||||||
continue
|
continue
|
||||||
|
try:
|
||||||
|
c.call('d.pause', h)
|
||||||
|
if not _mark_auto_paused(c, profile_id, t):
|
||||||
|
label_failed.append(h)
|
||||||
|
paused.append(h)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# Note: Resume dziala teraz w petli do pelnego limitu z ustawien. Gdy batch nie przejdzie
|
||||||
|
# na d.is_active=1, Smart Queue nie zatrzymuje sie, tylko probuje nastepnych kandydatow.
|
||||||
|
while candidate_queue and active_slots < max_active:
|
||||||
|
slots_left = max_active - active_slots
|
||||||
|
batch = candidate_queue[:slots_left]
|
||||||
|
candidate_queue = candidate_queue[slots_left:]
|
||||||
|
batch_requested: list[str] = []
|
||||||
|
|
||||||
|
for t in batch:
|
||||||
|
h = str(t.get('hash') or '')
|
||||||
|
if not h:
|
||||||
|
continue
|
||||||
|
attempted_hashes.add(h)
|
||||||
try:
|
try:
|
||||||
result = _start_download(c, t)
|
result = _start_download(c, t)
|
||||||
start_results.append(result)
|
start_results.append(result)
|
||||||
resume_requested.append(h)
|
resume_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)})
|
||||||
|
|
||||||
active_verified, start_no_effect = _verify_started_downloads(c, resume_requested)
|
active_verified, batch_no_effect = _verify_started_downloads(c, batch_requested)
|
||||||
|
start_no_effect.extend(batch_no_effect)
|
||||||
for h in active_verified:
|
for h in active_verified:
|
||||||
|
if h not in resumed:
|
||||||
_restore_auto_label(c, profile_id, h, None)
|
_restore_auto_label(c, profile_id, h, None)
|
||||||
# Note: Historia pokazuje tylko torrenty faktycznie zdjęte z pauzy, a nie samą liczbę wysłanych komend.
|
resumed.append(h)
|
||||||
resumed = list(active_verified)
|
active_slots += len([h for h in active_verified if h])
|
||||||
|
|
||||||
|
if not batch_requested:
|
||||||
|
break
|
||||||
|
|
||||||
|
resumed_set = set(resumed)
|
||||||
|
waiting_hashes = {
|
||||||
|
str(t.get('hash') or '')
|
||||||
|
for t in candidates
|
||||||
|
if str(t.get('hash') or '') and str(t.get('hash') or '') not in pause_hashes and str(t.get('hash') or '') not in resumed_set
|
||||||
|
}
|
||||||
|
|
||||||
|
# Note: Kazdy kandydat niewznowiony w tej rundzie zostaje oznaczony jako oczekujacy,
|
||||||
|
# dzieki czemu kolejne cykle nadal dobieraja go z pauzy/labela Smart Queue.
|
||||||
|
for t in candidates:
|
||||||
|
h = str(t.get('hash') or '')
|
||||||
|
if not h or h not in waiting_hashes:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
if not _mark_auto_paused(c, profile_id, t):
|
||||||
|
label_failed.append(h)
|
||||||
|
except Exception:
|
||||||
|
label_failed.append(h)
|
||||||
|
|
||||||
keep_labels = (
|
keep_labels = (
|
||||||
set(paused)
|
set(paused)
|
||||||
| {str(t.get('hash') or '') for t in to_label_waiting}
|
| waiting_hashes
|
||||||
| {str(t.get('hash') or '') for t in stopped if str(t.get('label') or '') == SMART_QUEUE_LABEL and str(t.get('hash') or '') not in set(resumed)}
|
| {str(t.get('hash') or '') for t in stopped if str(t.get('label') or '') == SMART_QUEUE_LABEL and str(t.get('hash') or '') not in resumed_set}
|
||||||
)
|
)
|
||||||
restored = _cleanup_auto_labels(c, profile_id, torrents, keep_labels, manage_stopped)
|
restored = _cleanup_auto_labels(c, profile_id, torrents, keep_labels, manage_stopped)
|
||||||
details = {'excluded': len(excluded), 'enabled': bool(settings.get('enabled')), 'auto_label': SMART_QUEUE_LABEL, 'labels_restored': restored, 'labels_failed': label_failed, 'start_failed': start_failed, 'start_no_effect': start_no_effect, 'start_results': start_results, 'resume_requested': resume_requested, 'active_verified': active_verified, 'waiting_labeled': len(to_label_waiting), 'manage_stopped': manage_stopped, 'max_active_downloads': max_active, 'active_before': len(downloading), 'active_after_expected': active_after_pause + len(resumed), 'paused_planned': len(to_pause), 'resumed_planned': len(to_resume), 'rtorrent_cap': rtorrent_cap}
|
details = {'excluded': len(excluded), 'enabled': bool(settings.get('enabled')), 'auto_label': SMART_QUEUE_LABEL, 'labels_restored': restored, 'labels_failed': label_failed, 'start_failed': start_failed, 'start_no_effect': start_no_effect, 'start_results': start_results, 'resume_requested': resume_requested, 'active_verified': resumed, 'attempted_count': len(attempted_hashes), 'waiting_labeled': len(waiting_hashes), 'manage_stopped': manage_stopped, 'max_active_downloads': max_active, 'active_before': len(downloading), 'active_after_expected': active_slots, 'paused_planned': len(to_pause), 'resumed_planned': len(attempted_hashes), 'stalled_detected': len(stalled), 'stalled_paused': len([h for h in paused if h in stalled_hashes]), 'rtorrent_cap': rtorrent_cap}
|
||||||
add_history(profile_id, 'force_check' if force else 'auto_check', paused, resumed, len(torrents), details, user_id)
|
add_history(profile_id, 'force_check' if force else 'auto_check', paused, resumed, len(torrents), details, user_id)
|
||||||
return {'ok': True, 'enabled': bool(settings.get('enabled')), 'paused': paused, 'resumed': resumed, 'resume_requested': resume_requested, 'waiting_labeled': len(to_label_waiting), 'labels_restored': restored, 'labels_failed': label_failed, 'start_failed': start_failed, 'start_no_effect': start_no_effect, 'active_verified': active_verified, 'rtorrent_cap': rtorrent_cap, 'checked': len(torrents), 'excluded': len(excluded), 'settings': settings}
|
return {'ok': True, 'enabled': bool(settings.get('enabled')), 'paused': paused, 'resumed': resumed, 'resume_requested': resume_requested, 'waiting_labeled': len(waiting_hashes), 'labels_restored': restored, 'labels_failed': label_failed, 'start_failed': start_failed, 'start_no_effect': start_no_effect, 'active_verified': resumed, 'rtorrent_cap': rtorrent_cap, 'checked': len(torrents), 'excluded': len(excluded), 'settings': settings}
|
||||||
|
|||||||
Reference in New Issue
Block a user