multilang_and_other #8
@@ -977,13 +977,37 @@ def _refill_underfilled_queue(profile: dict, settings: dict[str, Any], profile_i
|
|||||||
| {str(t.get('hash') or '') for t in stopped if _has_smart_queue_label(str(t.get('label') or '')) and str(t.get('hash') or '') not in set(started_by_queue)}
|
| {str(t.get('hash') or '') for t in stopped if _has_smart_queue_label(str(t.get('label') or '')) and str(t.get('hash') or '') not in set(started_by_queue)}
|
||||||
)
|
)
|
||||||
restored = _cleanup_auto_labels(c, profile_id, torrents, keep_labels, True)
|
restored = _cleanup_auto_labels(c, profile_id, torrents, keep_labels, True)
|
||||||
|
# Note: Cooldown refill uses started incomplete torrents as queue slots. This diagnostic
|
||||||
|
# explains why a refill may legitimately start nothing even when only a few torrents transfer data.
|
||||||
|
active_transferring = sum(1 for t in downloading if int(t.get('down_rate') or 0) > 0 or int(t.get('up_rate') or 0) > 0)
|
||||||
|
active_rtorrent = sum(1 for t in downloading if int(t.get('active') or 0))
|
||||||
|
active_state = sum(1 for t in downloading if int(t.get('state') or 0))
|
||||||
|
active_after_expected = len(downloading) + len(start_requested)
|
||||||
|
if available_slots <= 0:
|
||||||
|
refill_decision = f'Cooldown refill skipped: active slots at limit ({len(downloading)}/{max_active})'
|
||||||
|
refill_blocked_reason = 'active_slots_at_limit'
|
||||||
|
elif not candidates:
|
||||||
|
refill_decision = 'Cooldown refill skipped: no stopped candidates available'
|
||||||
|
refill_blocked_reason = 'no_candidates'
|
||||||
|
elif start_requested:
|
||||||
|
refill_decision = f'Cooldown refill requested {len(start_requested)} start(s)'
|
||||||
|
refill_blocked_reason = ''
|
||||||
|
else:
|
||||||
|
refill_decision = 'Cooldown refill ran but rTorrent did not confirm new starts yet'
|
||||||
|
refill_blocked_reason = 'start_not_confirmed'
|
||||||
details = {
|
details = {
|
||||||
|
'decision': refill_decision,
|
||||||
|
'blocked_reason': refill_blocked_reason,
|
||||||
'enabled': bool(settings.get('enabled')),
|
'enabled': bool(settings.get('enabled')),
|
||||||
'cooldown_refill': True,
|
'cooldown_refill': True,
|
||||||
'cooldown_respected': True,
|
'cooldown_respected': True,
|
||||||
'refill_mode': _refill_mode(settings),
|
'refill_mode': _refill_mode(settings),
|
||||||
'refill_interval_minutes': int(settings.get('refill_interval_minutes') or 0),
|
'refill_interval_minutes': int(settings.get('refill_interval_minutes') or 0),
|
||||||
'active_before': len(downloading),
|
'active_before': len(downloading),
|
||||||
|
'active_after_expected': active_after_expected,
|
||||||
|
'active_transferring_count': active_transferring,
|
||||||
|
'active_rtorrent_count': active_rtorrent,
|
||||||
|
'active_state_count': active_state,
|
||||||
'available_slots': available_slots,
|
'available_slots': available_slots,
|
||||||
'candidates': len(candidates),
|
'candidates': len(candidates),
|
||||||
'start_source_skipped': len(source_skipped),
|
'start_source_skipped': len(source_skipped),
|
||||||
@@ -1014,6 +1038,10 @@ def _refill_underfilled_queue(profile: dict, settings: dict[str, Any], profile_i
|
|||||||
'max_active_downloads': max_active,
|
'max_active_downloads': max_active,
|
||||||
'available_slots': available_slots,
|
'available_slots': available_slots,
|
||||||
'candidates': len(candidates),
|
'candidates': len(candidates),
|
||||||
|
'active_transferring': active_transferring,
|
||||||
|
'active_rtorrent': active_rtorrent,
|
||||||
|
'active_state': active_state,
|
||||||
|
'blocked_reason': refill_blocked_reason,
|
||||||
'start_source_skipped': len(source_skipped),
|
'start_source_skipped': len(source_skipped),
|
||||||
'requested': len(start_requested),
|
'requested': len(start_requested),
|
||||||
'verified': len(active_verified),
|
'verified': len(active_verified),
|
||||||
@@ -1069,7 +1097,11 @@ def _refill_underfilled_queue(profile: dict, settings: dict[str, Any], profile_i
|
|||||||
'start_pending_confirmation': start_pending_confirmation,
|
'start_pending_confirmation': start_pending_confirmation,
|
||||||
'active_verified': active_verified,
|
'active_verified': active_verified,
|
||||||
'active_before': len(downloading),
|
'active_before': len(downloading),
|
||||||
'active_after_expected': len(downloading) + len(started_by_queue),
|
'active_after_expected': active_after_expected,
|
||||||
|
'active_transferring_count': active_transferring,
|
||||||
|
'active_rtorrent_count': active_rtorrent,
|
||||||
|
'active_state_count': active_state,
|
||||||
|
'blocked_reason': refill_blocked_reason,
|
||||||
'available_slots': available_slots,
|
'available_slots': available_slots,
|
||||||
'start_source_skipped': len(source_skipped),
|
'start_source_skipped': len(source_skipped),
|
||||||
'checked': len(torrents),
|
'checked': len(torrents),
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -551,7 +551,7 @@ body.resizing-details {
|
|||||||
min-width: 680px;
|
min-width: 680px;
|
||||||
}
|
}
|
||||||
.smart-history-table {
|
.smart-history-table {
|
||||||
min-width: 760px;
|
min-width: 920px;
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
}
|
}
|
||||||
.smart-history-table th,
|
.smart-history-table th,
|
||||||
@@ -4373,6 +4373,33 @@ body,
|
|||||||
|
|
||||||
|
|
||||||
/* Smart Queue exception picker */
|
/* Smart Queue exception picker */
|
||||||
|
.smart-exclusion-toolbar {
|
||||||
|
align-items: center;
|
||||||
|
display: grid;
|
||||||
|
gap: 0.65rem;
|
||||||
|
grid-template-columns: minmax(16rem, 1fr) auto auto auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smart-exclusion-toolbar-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smart-exclusion-counter {
|
||||||
|
color: var(--bs-secondary-color);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smart-exclusion-toggle {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.45rem;
|
||||||
|
margin: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.smart-exclusion-choice-list {
|
.smart-exclusion-choice-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.45rem;
|
gap: 0.45rem;
|
||||||
@@ -4382,14 +4409,14 @@ body,
|
|||||||
}
|
}
|
||||||
|
|
||||||
.smart-exclusion-choice-row {
|
.smart-exclusion-choice-row {
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 0.65rem;
|
background: rgba(var(--bs-secondary-bg-rgb), 0.24);
|
||||||
padding: 0.65rem 0.75rem;
|
|
||||||
border: 1px solid var(--bs-border-color);
|
border: 1px solid var(--bs-border-color);
|
||||||
border-radius: 0.65rem;
|
border-radius: 0.65rem;
|
||||||
background: rgba(var(--bs-secondary-bg-rgb), 0.24);
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.65rem;
|
||||||
|
padding: 0.65rem 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.smart-exclusion-choice-row:hover {
|
.smart-exclusion-choice-row:hover {
|
||||||
@@ -4413,6 +4440,16 @@ body,
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.smart-exclusion-toolbar {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smart-exclusion-toolbar-actions {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Backup preview data samples */
|
/* Backup preview data samples */
|
||||||
.backup-preview-empty {
|
.backup-preview-empty {
|
||||||
color: var(--bs-secondary-color);
|
color: var(--bs-secondary-color);
|
||||||
|
|||||||
@@ -353,7 +353,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="modal fade" id="smartExclusionModal" tabindex="-1" aria-hidden="true">
|
<div class="modal fade" id="smartExclusionModal" tabindex="-1" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-scrollable modal-lg">
|
<div class="modal-dialog modal-dialog-scrollable modal-xl">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title"><i class="fa-solid fa-ban"></i> Smart Queue exceptions</h5>
|
<h5 class="modal-title"><i class="fa-solid fa-ban"></i> Smart Queue exceptions</h5>
|
||||||
@@ -361,7 +361,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="tool-note mb-3">Select torrents that Smart Queue should ignore. Use search to filter by name, label, status or hash.</div>
|
<div class="tool-note mb-3">Select torrents that Smart Queue should ignore. Use search to filter by name, label, status or hash.</div>
|
||||||
<input id="smartExclusionSearch" class="form-control mb-3" type="search" placeholder="Search torrents to exclude...">
|
<div class="smart-exclusion-toolbar mb-3">
|
||||||
|
<input id="smartExclusionSearch" class="form-control" type="search" placeholder="Search torrents to exclude...">
|
||||||
|
<label class="form-check form-switch smart-exclusion-toggle">
|
||||||
|
<input id="smartExclusionOnlySelected" class="form-check-input" type="checkbox">
|
||||||
|
<span class="form-check-label">Only selected</span>
|
||||||
|
</label>
|
||||||
|
<div class="smart-exclusion-toolbar-actions">
|
||||||
|
<button id="smartExclusionSelectVisibleBtn" type="button" class="btn btn-sm btn-outline-primary">Select visible</button>
|
||||||
|
<button id="smartExclusionClearVisibleBtn" type="button" class="btn btn-sm btn-outline-secondary">Clear visible</button>
|
||||||
|
</div>
|
||||||
|
<span id="smartExclusionCounter" class="smart-exclusion-counter">0 selected</span>
|
||||||
|
</div>
|
||||||
<div id="smartExclusionChoiceList" class="smart-exclusion-choice-list"></div>
|
<div id="smartExclusionChoiceList" class="smart-exclusion-choice-list"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|||||||
Reference in New Issue
Block a user