diff --git a/web_server.py b/web_server.py index 37202025..ec823f3d 100644 --- a/web_server.py +++ b/web_server.py @@ -6845,6 +6845,7 @@ def _atomic_cancel_task(playlist_id, track_index): return False, "Task already cancelled", {'task_id': task_id, 'status': 'cancelled'} current_status = task.get('status', 'unknown') + original_status = current_status # Store original status before changing it batch_id = task.get('batch_id') print(f"đŸŽ¯ [Atomic Cancel] Starting atomic cancel: playlist={playlist_id}, track={track_index}, task={task_id}, status={current_status}") @@ -6890,6 +6891,7 @@ def _atomic_cancel_task(playlist_id, track_index): task_info = { 'task_id': task_id, 'status': 'cancelled', + 'original_status': original_status, # Pass original status for slskd cancellation 'track_name': task.get('track_info', {}).get('name', 'Unknown'), 'playlist_id': playlist_id, 'track_index': track_index, @@ -6957,16 +6959,16 @@ def cancel_task_v2(): download_id = task.get('download_id') username = task.get('username') current_status = task.get('status') + original_status = task_info.get('original_status', current_status) # Get original status from task_info - print(f"🔍 [Atomic Cancel] Task {task_id} state: status='{current_status}', download_id='{download_id}', username='{username}'") + print(f"🔍 [Atomic Cancel] Task {task_id} state: status='{current_status}', original_status='{original_status}', download_id='{download_id}', username='{username}'") print(f"🔍 [Atomic Cancel] Download ID type: {type(download_id)}, length: {len(str(download_id)) if download_id else 0}") print(f"🔍 [Atomic Cancel] Download ID looks like filename: {download_id and ('/' in str(download_id) or '\\' in str(download_id))}") if download_id and username: - # Only try to cancel if task was actually downloading (has real slskd download) - # For pending/searching tasks, the cancellation is already handled by status='cancelled' - if current_status in ['downloading', 'queued']: # Remove 'searching' - those aren't in slskd yet - try: + # Always try to cancel in slskd - doesn't matter what status it was + # If it's not there or already done, the DELETE request will just fail harmlessly + try: print(f"đŸšĢ [Atomic Cancel] Attempting to cancel Soulseek download:") print(f" Username: {username}") print(f" Download ID: {download_id}") @@ -7005,21 +7007,32 @@ def cancel_task_v2(): if real_download_id: print(f"🔄 [Atomic Cancel] Attempting cancel with real ID: {real_download_id}") try: - # Use both approaches that work - # Approach A: Simple DELETE (like sync.py) - response = asyncio.run(soulseek_client._make_request('DELETE', f'transfers/downloads/{real_download_id}')) + # Use EXACT format from slskd web UI: DELETE /api/v0/transfers/downloads/{username}/{download_id}?remove=false + endpoint = f'transfers/downloads/{username}/{real_download_id}?remove=true' + print(f"🌐 [Atomic Cancel] Using slskd web UI format: {endpoint}") + + response = asyncio.run(soulseek_client._make_request('DELETE', endpoint)) if response is not None: - print(f"✅ [Atomic Cancel] Successfully cancelled with simple DELETE: {real_download_id}") + print(f"✅ [Atomic Cancel] Successfully cancelled with slskd web UI format: {real_download_id}") success = True else: - # Approach B: Complex method (like downloads.py) - print(f"🔄 [Atomic Cancel] Simple DELETE failed, trying complex method") - complex_success = asyncio.run(soulseek_client.cancel_download(real_download_id, username, remove=True)) - if complex_success: - print(f"✅ [Atomic Cancel] Successfully cancelled with complex method: {real_download_id}") + print(f"âš ī¸ [Atomic Cancel] Web UI format failed, trying alternative formats") + + # Fallback: Try without remove parameter + endpoint2 = f'transfers/downloads/{username}/{real_download_id}' + response2 = asyncio.run(soulseek_client._make_request('DELETE', endpoint2)) + if response2 is not None: + print(f"✅ [Atomic Cancel] Successfully cancelled without remove param: {real_download_id}") success = True else: - print(f"âš ī¸ [Atomic Cancel] Both methods failed for real ID: {real_download_id}") + # Final fallback: Try simple format (sync.py style) + endpoint3 = f'transfers/downloads/{real_download_id}' + response3 = asyncio.run(soulseek_client._make_request('DELETE', endpoint3)) + if response3 is not None: + print(f"✅ [Atomic Cancel] Successfully cancelled with simple format: {real_download_id}") + success = True + else: + print(f"âš ī¸ [Atomic Cancel] All DELETE formats failed for real ID: {real_download_id}") except Exception as cancel_error: print(f"âš ī¸ [Atomic Cancel] Exception cancelling real ID {real_download_id}: {cancel_error}") else: @@ -7030,17 +7043,13 @@ def cancel_task_v2(): if not success: print(f"❌ [Atomic Cancel] Failed to cancel download in slskd API") - except Exception as e: - print(f"âš ī¸ [Atomic Cancel] Exception cancelling Soulseek download {download_id}: {e}") - # Print more details about the error - import traceback - print(f"âš ī¸ [Atomic Cancel] Cancel error traceback: {traceback.format_exc()}") - else: - print(f"â­ī¸ [Atomic Cancel] Skipping Soulseek cancel - task was '{current_status}' (not actively downloading)") - elif current_status in ['downloading', 'queued', 'searching']: - print(f"âš ī¸ [Atomic Cancel] Task was '{current_status}' but missing download_id or username for Soulseek cancel") + except Exception as e: + print(f"âš ī¸ [Atomic Cancel] Exception cancelling Soulseek download {download_id}: {e}") + # Print more details about the error + import traceback + print(f"âš ī¸ [Atomic Cancel] Cancel error traceback: {traceback.format_exc()}") else: - print(f"â„šī¸ [Atomic Cancel] No Soulseek cancel needed - task was '{current_status}'") + print(f"â„šī¸ [Atomic Cancel] No download_id or username available - skipping slskd cancel") # Add to wishlist (non-blocking, best effort) try: