From e99d19ece0027979a3b3c110b424f5ee0c01271e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Thu, 7 May 2026 08:48:59 +0200 Subject: [PATCH] automatyzacje-comit3 --- pytorrent/services/smart_queue.py | 14 +++++++++----- pytorrent/static/styles.css | 27 +++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/pytorrent/services/smart_queue.py b/pytorrent/services/smart_queue.py index 7a1b6d2..c1558f6 100644 --- a/pytorrent/services/smart_queue.py +++ b/pytorrent/services/smart_queue.py @@ -172,7 +172,9 @@ def _without_smart_queue_label(value: str | None) -> str: def _has_stalled_label(value: str | None) -> bool: - return SMART_QUEUE_STALLED_LABEL in _label_names(value) + # Note: Stalled is treated case-insensitively so manually edited labels still block Smart Queue. + target = SMART_QUEUE_STALLED_LABEL.casefold() + return any(label.casefold() == target for label in _label_names(value)) def _without_queue_technical_labels(value: str | None) -> str: @@ -182,7 +184,7 @@ def _without_queue_technical_labels(value: str | None) -> str: def _ensure_stalled_label(client: Any, torrent_hash: str, current_label: str = '') -> bool: labels = [label for label in _label_names(current_label) if label != SMART_QUEUE_LABEL] changed = False - if SMART_QUEUE_STALLED_LABEL not in labels: + if not any(label.casefold() == SMART_QUEUE_STALLED_LABEL.casefold() for label in labels): labels.append(SMART_QUEUE_STALLED_LABEL) changed = True if SMART_QUEUE_LABEL in _label_names(current_label): @@ -476,7 +478,9 @@ def check(profile: dict | None = None, user_id: int | None = None, force: bool = return {'ok': True, 'enabled': False, 'paused': [], 'resumed': [], 'labels_restored': restored, 'message': 'Smart Queue disabled'} torrents = rtorrent.list_torrents(profile) - excluded = _excluded_hashes(profile_id, user_id) + # Note: Torrents marked as Stalled are treated as queue-blocked even when there are no other pending downloads. + stalled_label_hashes = {str(t.get('hash') or '') for t in torrents if _has_stalled_label(str(t.get('label') or '')) and t.get('hash')} + excluded = _excluded_hashes(profile_id, user_id) | stalled_label_hashes manage_stopped = bool(settings.get('manage_stopped')) def is_managed_hold(t: dict[str, Any]) -> bool: return _has_smart_queue_label(str(t.get('label') or '')) @@ -622,6 +626,6 @@ def check(profile: dict | None = None, user_id: int | None = None, force: bool = | {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(resumed)} ) 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, 'stalled_label': SMART_QUEUE_STALLED_LABEL, 'stalled_labeled': stalled_labeled, '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), 'excluded_stalled': len(stalled_label_hashes), 'enabled': bool(settings.get('enabled')), 'auto_label': SMART_QUEUE_LABEL, 'stalled_label': SMART_QUEUE_STALLED_LABEL, 'stalled_labeled': stalled_labeled, '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} 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), 'stalled_labeled': stalled_labeled, '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(to_label_waiting), 'stalled_labeled': stalled_labeled, 'excluded_stalled': len(stalled_label_hashes), '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} diff --git a/pytorrent/static/styles.css b/pytorrent/static/styles.css index 72cb13d..ad8e434 100644 --- a/pytorrent/static/styles.css +++ b/pytorrent/static/styles.css @@ -1317,49 +1317,67 @@ body.mobile-mode .mobile-card { border-radius: 999px; background: var(--bs-secondary-bg); font-size: 0.78rem; + overflow-wrap: anywhere; white-space: normal; + word-break: break-word; } .automation-history-toolbar { display: flex; justify-content: flex-end; margin-bottom: 0.5rem; } +/* Note: Automation history has fixed compact metadata columns and a flexible Actions column, so long JSON cannot overlap Time/Rule. */ .automation-history-table { width: 100%; table-layout: fixed; + white-space: normal; } .automation-history-table th, .automation-history-table td { + min-width: 0; vertical-align: top; } .automation-history-table th:nth-child(1), .automation-history-table td:nth-child(1) { - width: 10.5rem; + width: 9rem; + overflow: hidden; + text-overflow: ellipsis; white-space: nowrap; } .automation-history-table th:nth-child(2), .automation-history-table td:nth-child(2) { - width: 13rem; + width: 11rem; + overflow: hidden; overflow-wrap: anywhere; + word-break: break-word; } .automation-history-table th:nth-child(3), .automation-history-table td:nth-child(3) { - width: 14rem; + width: 12rem; + overflow: hidden; overflow-wrap: anywhere; + word-break: break-word; } .automation-history-table th:nth-child(4), .automation-history-table td:nth-child(4) { width: auto; - min-width: 0; + overflow: hidden; overflow-wrap: anywhere; word-break: break-word; } .automation-history-details { + display: block; + min-width: 0; max-width: 100%; } .automation-history-details summary { + display: block; + max-width: 100%; cursor: pointer; list-style-position: inside; + overflow-wrap: anywhere; + white-space: normal; + word-break: break-word; } .automation-history-details pre, .automation-history-raw { @@ -1371,6 +1389,7 @@ body.mobile-mode .mobile-card { border: 1px solid var(--bs-border-color); border-radius: 0.5rem; background: var(--bs-tertiary-bg); + overflow-wrap: anywhere; white-space: pre-wrap; word-break: break-word; }