From 698b1fa4f333a7bcd8d248ead461e8d35d73ebe9 Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Fri, 27 Feb 2026 11:18:13 -0800 Subject: [PATCH] clear current downloads --- core/soulseek_client.py | 27 +++++++++++++++++++++++++++ web_server.py | 22 ++++++++++++++++++++++ webui/index.html | 1 + webui/static/script.js | 26 ++++++++++++++++++++++++++ webui/static/style.css | 17 +++++++++++++++++ 5 files changed, 93 insertions(+) diff --git a/core/soulseek_client.py b/core/soulseek_client.py index 36f1f750..d275fff1 100644 --- a/core/soulseek_client.py +++ b/core/soulseek_client.py @@ -1187,6 +1187,33 @@ class SoulseekClient: )) return results + async def cancel_all_downloads(self) -> bool: + """Cancel and remove ALL downloads (active + completed) from slskd. + + Returns: + bool: True if successful, False otherwise + """ + if not self.base_url: + logger.error("Soulseek client not configured") + return False + + try: + endpoint = 'transfers/downloads/all' + logger.debug(f"Cancelling all downloads with endpoint: {endpoint}") + response = await self._make_request('DELETE', endpoint) + success = response is not None + + if success: + logger.info("Successfully cancelled all downloads from slskd") + else: + logger.error("Failed to cancel all downloads from slskd") + + return success + + except Exception as e: + logger.error(f"Error cancelling all downloads: {e}") + return False + async def clear_all_completed_downloads(self) -> bool: """Clear all completed/finished downloads from slskd backend diff --git a/web_server.py b/web_server.py index 37ce5fd5..b77a2bbb 100644 --- a/web_server.py +++ b/web_server.py @@ -4981,6 +4981,28 @@ def cancel_download(): print(f"Error cancelling download: {e}") return jsonify({"success": False, "error": str(e)}), 500 +@app.route('/api/downloads/cancel-all', methods=['POST']) +def cancel_all_downloads(): + """ + Cancel all active downloads from slskd, then clear completed ones. + """ + try: + # First cancel all active downloads + cancel_success = run_async(soulseek_client.cancel_all_downloads()) + if not cancel_success: + return jsonify({"success": False, "error": "Failed to cancel active downloads."}), 500 + + # Then clear the now-cancelled/completed downloads + clear_success = run_async(soulseek_client.clear_all_completed_downloads()) + + # Sweep empty directories + _sweep_empty_download_directories() + + return jsonify({"success": True, "message": "All downloads cancelled and cleared."}) + except Exception as e: + print(f"Error cancelling all downloads: {e}") + return jsonify({"success": False, "error": str(e)}), 500 + @app.route('/api/downloads/clear-finished', methods=['POST']) def clear_finished_downloads(): """ diff --git a/webui/index.html b/webui/index.html index 4e62dd3c..44fef070 100644 --- a/webui/index.html +++ b/webui/index.html @@ -1718,6 +1718,7 @@
+
diff --git a/webui/static/script.js b/webui/static/script.js index 21b0675b..16b8def8 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -9887,10 +9887,14 @@ async function loadDownloadsData() { // Event listeners are already set up in initializeSearch() - don't duplicate them const clearButton = document.querySelector('.controls-panel__clear-btn'); + const cancelAllButton = document.querySelector('.controls-panel__cancel-all-btn'); if (clearButton) { clearButton.addEventListener('click', clearFinishedDownloads); } + if (cancelAllButton) { + cancelAllButton.addEventListener('click', cancelAllDownloads); + } // Start sophisticated polling system (1-second interval like GUI) startDownloadPolling(); @@ -10157,6 +10161,28 @@ async function clearFinishedDownloads() { } } +async function cancelAllDownloads() { + if (!confirm('Cancel ALL active downloads and clear the transfer list? This cannot be undone.')) { + return; + } + + try { + const response = await fetch('/api/downloads/cancel-all', { + method: 'POST' + }); + const result = await response.json(); + + if (result.success) { + showToast('All downloads cancelled and cleared', 'success'); + } else { + showToast(`Failed to cancel: ${result.error}`, 'error'); + } + } catch (error) { + console.error('Error cancelling all downloads:', error); + showToast('Error cancelling downloads', 'error'); + } +} + // REPLACE the old performDownloadsSearch function with this new one. async function performDownloadsSearch() { const query = document.getElementById('downloads-search-input').value.trim(); diff --git a/webui/static/style.css b/webui/static/style.css index fc732964..78c02561 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -2641,6 +2641,23 @@ body { background: linear-gradient(to bottom, rgba(220, 53, 69, 0.5), rgba(220, 53, 69, 0.35)); } +.controls-panel__cancel-all-btn { + width: 100%; + height: 28px; + margin-top: 4px; + background: linear-gradient(to bottom, rgba(255, 152, 0, 0.4), rgba(255, 152, 0, 0.25)); + border: 1px solid rgba(255, 152, 0, 0.8); + color: #ff9800; + border-radius: 14px; + font-size: 10px; + font-weight: 600; + cursor: pointer; + transition: background 0.2s ease; +} + +.controls-panel__cancel-all-btn:hover { + background: linear-gradient(to bottom, rgba(255, 152, 0, 0.5), rgba(255, 152, 0, 0.35)); +} /* Download Manager (Tabs & Queue) */ .download-manager {