progress
This commit is contained in:
parent
106f5178a2
commit
18f2cac879
8 changed files with 4176 additions and 25 deletions
|
|
@ -1 +1 @@
|
|||
{"access_token": "BQAjMPjb13VCQ73SsbWrU9xW8jJmPaJScM6MpLge7nblR7Uve24yXsjltxc-n0PbMp5PENCcLvpJfRqEV627LcUPfTsOwbKyNiEz37DxIwWRiuUEbgZtWznOBF0V0D6ukIS_GPwl1kGmmzpD_nl088_oKB4SmZS4HEKntNvDgoG90H9TAxRJDNHqD3pF7rzVzPKubsML9mv-0g5TgajaiiPbqowaOUYCzVskJSiNdZPAmZsV2iiGvZ2ugI3uQOAt", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email", "expires_at": 1752276843, "refresh_token": "AQDmfQkPCGObfJeTUIbW1hAAwhSqkuHRA3Qh2dqVYMRh0eCkFMQgPNJDDzF8y-BiaVbj80zePkK_XSfYH1aJutMtNbnsqRKWuxP31BTrMc7pdUdbE7Fma4oH8wpDUKdG3MM"}
|
||||
{"access_token": "BQC2H3Gtur2UVBolbGlrlVtwEq7r-0GaGWTmBQZERyH-UVWKOOu8g2nDf-pKwL7w2yf5j9uolii2YBXpEoCOZyaa2rVbLbzbERDfw__hJ9JDhxwWT7OuIf3gxxCPkZs9d7UEaonxyPY7B8bLh49_uhvYpq7azOAJE40bPknaHR230KGsLQpqQbdo4In07iCLaD-PFGYHC4Me7c0QRvyTC1-gx9qgfvlDF-BvIEHsBpDW6dibzSO0AQjzaR4-Okb-", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email", "expires_at": 1752286148, "refresh_token": "AQDmfQkPCGObfJeTUIbW1hAAwhSqkuHRA3Qh2dqVYMRh0eCkFMQgPNJDDzF8y-BiaVbj80zePkK_XSfYH1aJutMtNbnsqRKWuxP31BTrMc7pdUdbE7Fma4oH8wpDUKdG3MM"}
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -254,7 +254,7 @@ class SoulseekClient:
|
|||
logger.debug(f"Response status: {response.status}")
|
||||
logger.debug(f"Response text: {response_text[:500]}...") # First 500 chars
|
||||
|
||||
if response.status in [200, 201]: # Accept both 200 OK and 201 Created
|
||||
if response.status in [200, 201, 204]: # Accept 200 OK, 201 Created, and 204 No Content
|
||||
try:
|
||||
if response_text.strip(): # Only parse if there's content
|
||||
return await response.json()
|
||||
|
|
@ -836,18 +836,72 @@ class SoulseekClient:
|
|||
logger.error(f"Error getting downloads: {e}")
|
||||
return []
|
||||
|
||||
async def cancel_download(self, download_id: str) -> bool:
|
||||
async def cancel_download(self, download_id: str, username: str = None, remove: bool = False) -> bool:
|
||||
if not self.base_url:
|
||||
return False
|
||||
|
||||
# If username is not provided, try to extract it from stored transfer data
|
||||
if not username:
|
||||
logger.debug(f"No username provided for download_id {download_id}, attempting to find it")
|
||||
try:
|
||||
downloads = await self.get_all_downloads()
|
||||
for download in downloads:
|
||||
if download.id == download_id:
|
||||
username = download.username
|
||||
logger.debug(f"Found username {username} for download_id {download_id}")
|
||||
break
|
||||
|
||||
if not username:
|
||||
logger.error(f"Could not find username for download_id {download_id}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"Error finding username for download: {e}")
|
||||
return False
|
||||
|
||||
try:
|
||||
response = await self._make_request('DELETE', f'transfers/downloads/{download_id}')
|
||||
# Use correct slskd API format: DELETE /transfers/downloads/{username}/{download_id}?remove={true/false}
|
||||
# remove=false: Cancel download but keep in transfer list
|
||||
# remove=true: Remove download completely from transfer list
|
||||
endpoint = f'transfers/downloads/{username}/{download_id}?remove={str(remove).lower()}'
|
||||
action = "Removing" if remove else "Cancelling"
|
||||
logger.debug(f"{action} download with endpoint: {endpoint}")
|
||||
response = await self._make_request('DELETE', endpoint)
|
||||
return response is not None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error cancelling download: {e}")
|
||||
return False
|
||||
|
||||
async def clear_all_completed_downloads(self) -> bool:
|
||||
"""Clear all completed/finished downloads from slskd backend
|
||||
|
||||
Uses the /api/v0/transfers/downloads/all/completed endpoint to remove
|
||||
all downloads with completed, cancelled, or failed status from slskd.
|
||||
|
||||
Returns:
|
||||
bool: True if clearing was successful, False otherwise
|
||||
"""
|
||||
if not self.base_url:
|
||||
logger.error("Soulseek client not configured")
|
||||
return False
|
||||
|
||||
try:
|
||||
endpoint = 'transfers/downloads/all/completed'
|
||||
logger.debug(f"Clearing all completed downloads with endpoint: {endpoint}")
|
||||
response = await self._make_request('DELETE', endpoint)
|
||||
success = response is not None
|
||||
|
||||
if success:
|
||||
logger.info("Successfully cleared all completed downloads from slskd")
|
||||
else:
|
||||
logger.error("Failed to clear completed downloads from slskd")
|
||||
|
||||
return success
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error clearing completed downloads: {e}")
|
||||
return False
|
||||
|
||||
async def search_and_download_best(self, query: str, preferred_quality: str = 'flac') -> Optional[str]:
|
||||
results = await self.search(query)
|
||||
|
||||
|
|
|
|||
3849
logs/app.log
3849
logs/app.log
File diff suppressed because it is too large
Load diff
Binary file not shown.
Binary file not shown.
|
|
@ -2081,8 +2081,8 @@ class CompactDownloadItem(QFrame):
|
|||
"""Compact download item optimized for queue display"""
|
||||
def __init__(self, title: str, artist: str, status: str = "queued",
|
||||
progress: int = 0, file_size: int = 0, download_speed: int = 0,
|
||||
file_path: str = "", download_id: str = "", soulseek_client=None,
|
||||
queue_type: str = "active", parent=None):
|
||||
file_path: str = "", download_id: str = "", username: str = "",
|
||||
soulseek_client=None, queue_type: str = "active", parent=None):
|
||||
super().__init__(parent)
|
||||
self.title = title
|
||||
self.artist = artist
|
||||
|
|
@ -2092,6 +2092,7 @@ class CompactDownloadItem(QFrame):
|
|||
self.download_speed = download_speed
|
||||
self.file_path = file_path
|
||||
self.download_id = download_id
|
||||
self.username = username
|
||||
self.soulseek_client = soulseek_client
|
||||
self.queue_type = queue_type # "active" or "finished"
|
||||
self.setup_ui()
|
||||
|
|
@ -2178,15 +2179,76 @@ class CompactDownloadItem(QFrame):
|
|||
progress_layout.addWidget(self.progress_bar)
|
||||
progress_layout.addWidget(self.status_label)
|
||||
|
||||
# Right section: Action button (fixed width)
|
||||
self.action_btn = QPushButton()
|
||||
self.action_btn.setFixedSize(70, 32) # Smaller button
|
||||
self.setup_action_button()
|
||||
# Right section: Action button(s) (fixed width)
|
||||
if self.queue_type == "finished":
|
||||
# For finished downloads, create a container with two buttons
|
||||
button_container = QWidget()
|
||||
button_container.setFixedWidth(150) # Wider to fit two buttons
|
||||
button_layout = QHBoxLayout(button_container)
|
||||
button_layout.setContentsMargins(0, 0, 0, 0)
|
||||
button_layout.setSpacing(4)
|
||||
|
||||
# Open button
|
||||
self.open_btn = QPushButton("📂")
|
||||
self.open_btn.setFixedSize(32, 32)
|
||||
self.open_btn.setToolTip("Open download folder")
|
||||
self.open_btn.clicked.connect(self.open_download_location)
|
||||
self.open_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 rgba(40, 167, 69, 0.8),
|
||||
stop:1 rgba(40, 167, 69, 1.0));
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 rgba(20, 147, 49, 0.9),
|
||||
stop:1 rgba(20, 147, 49, 1.0));
|
||||
}
|
||||
""")
|
||||
|
||||
# Remove button
|
||||
self.remove_btn = QPushButton("🗑️")
|
||||
self.remove_btn.setFixedSize(32, 32)
|
||||
self.remove_btn.setToolTip("Remove from downloads")
|
||||
self.remove_btn.clicked.connect(self.remove_download)
|
||||
self.remove_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 rgba(220, 53, 69, 0.8),
|
||||
stop:1 rgba(220, 53, 69, 1.0));
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 rgba(200, 33, 49, 0.9),
|
||||
stop:1 rgba(200, 33, 49, 1.0));
|
||||
}
|
||||
""")
|
||||
|
||||
button_layout.addWidget(self.open_btn)
|
||||
button_layout.addWidget(self.remove_btn)
|
||||
|
||||
action_widget = button_container
|
||||
else:
|
||||
# For active downloads, single action button
|
||||
self.action_btn = QPushButton()
|
||||
self.action_btn.setFixedSize(70, 32) # Smaller button
|
||||
self.setup_action_button()
|
||||
action_widget = self.action_btn
|
||||
|
||||
# Add everything to main layout
|
||||
layout.addLayout(left_section, 1) # Flexible
|
||||
layout.addWidget(progress_widget) # Fixed width
|
||||
layout.addWidget(self.action_btn) # Fixed width
|
||||
layout.addWidget(action_widget) # Fixed width
|
||||
|
||||
def get_display_filename(self):
|
||||
"""Extract just the filename with extension for display"""
|
||||
|
|
@ -2225,6 +2287,7 @@ class CompactDownloadItem(QFrame):
|
|||
if self.status.lower() in ["downloading", "queued"]:
|
||||
self.action_btn.setText("Cancel")
|
||||
self.action_btn.clicked.connect(self.cancel_download)
|
||||
print(f"[DEBUG] Connected cancel button for: {self.title}")
|
||||
self.action_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
|
|
@ -2279,6 +2342,7 @@ class CompactDownloadItem(QFrame):
|
|||
# Finished downloads: Always show Open button
|
||||
self.action_btn.setText("📂 Open")
|
||||
self.action_btn.clicked.connect(self.open_download_location)
|
||||
print(f"[DEBUG] Connected open button for: {self.title} (path: {self.file_path})")
|
||||
self.action_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
|
|
@ -2317,15 +2381,73 @@ class CompactDownloadItem(QFrame):
|
|||
# Update action button if needed
|
||||
self.setup_action_button()
|
||||
|
||||
def remove_download(self):
|
||||
"""Remove the download completely from transfer list"""
|
||||
print(f"[DEBUG] Remove button clicked - download_id: {self.download_id}, username: {self.username}, title: {self.title}")
|
||||
if self.soulseek_client and self.download_id:
|
||||
print(f"🗑️ Removing download: {self.download_id}")
|
||||
try:
|
||||
import asyncio
|
||||
|
||||
# Get or create event loop for async operation
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
except RuntimeError:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
# Run the removal with remove=true
|
||||
result = loop.run_until_complete(self.soulseek_client.cancel_download(self.download_id, self.username, remove=True))
|
||||
print(f"[DEBUG] Remove result: {result}")
|
||||
|
||||
if result:
|
||||
print(f"✅ Successfully removed download: {self.title}")
|
||||
print(f"[DEBUG] Download should be removed from slskd - waiting for next status update to reflect removal")
|
||||
# Force an immediate status update to check if it's been removed
|
||||
print(f"[DEBUG] Triggering immediate status poll to detect removal")
|
||||
# Find the downloads page and trigger status update
|
||||
downloads_page = self
|
||||
while downloads_page and not hasattr(downloads_page, 'update_download_status'):
|
||||
downloads_page = downloads_page.parent()
|
||||
if downloads_page:
|
||||
downloads_page.update_download_status()
|
||||
else:
|
||||
print(f"❌ Failed to remove download: {self.title}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to remove download: {e}")
|
||||
else:
|
||||
print(f"[DEBUG] Remove failed - soulseek_client: {self.soulseek_client}, download_id: {self.download_id}")
|
||||
|
||||
def cancel_download(self):
|
||||
"""Cancel the download using soulseek client"""
|
||||
print(f"[DEBUG] Cancel button clicked - download_id: {self.download_id}, username: {self.username}, title: {self.title}")
|
||||
if self.soulseek_client and self.download_id:
|
||||
print(f"🚫 Cancelling download: {self.download_id}")
|
||||
try:
|
||||
self.soulseek_client.cancel_download(self.download_id)
|
||||
self.update_status("cancelled")
|
||||
import asyncio
|
||||
|
||||
# Get or create event loop for async operation
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
except RuntimeError:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
# Run the cancellation with username
|
||||
result = loop.run_until_complete(self.soulseek_client.cancel_download(self.download_id, self.username))
|
||||
print(f"[DEBUG] Cancel result: {result}")
|
||||
|
||||
if result:
|
||||
print(f"✅ Successfully cancelled download: {self.title}")
|
||||
self.update_status("cancelled")
|
||||
else:
|
||||
print(f"❌ Failed to cancel download: {self.title}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to cancel download: {e}")
|
||||
else:
|
||||
print(f"[DEBUG] Cancel failed - soulseek_client: {self.soulseek_client}, download_id: {self.download_id}")
|
||||
|
||||
def retry_download(self):
|
||||
"""Retry a failed download"""
|
||||
|
|
@ -2340,13 +2462,35 @@ class CompactDownloadItem(QFrame):
|
|||
import platform
|
||||
from pathlib import Path
|
||||
|
||||
print(f"[DEBUG] Open button clicked - file_path: {self.file_path}, title: {self.title}")
|
||||
|
||||
if not self.file_path:
|
||||
print(f"[DEBUG] No file_path set for download: {self.title}")
|
||||
# Fallback to opening the general downloads folder
|
||||
try:
|
||||
from config.settings import config_manager
|
||||
download_path = config_manager.get('soulseek.download_path', './downloads')
|
||||
|
||||
system = platform.system()
|
||||
if system == "Windows":
|
||||
os.startfile(download_path)
|
||||
elif system == "Darwin": # macOS
|
||||
os.system(f'open "{download_path}"')
|
||||
else: # Linux
|
||||
os.system(f'xdg-open "{download_path}"')
|
||||
|
||||
print(f"📂 Opened downloads folder: {download_path}")
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to open downloads folder: {e}")
|
||||
return
|
||||
|
||||
try:
|
||||
file_path = Path(self.file_path)
|
||||
print(f"[DEBUG] Checking file existence: {file_path}")
|
||||
|
||||
if file_path.exists():
|
||||
folder_path = file_path.parent
|
||||
print(f"[DEBUG] Opening folder: {folder_path}")
|
||||
|
||||
system = platform.system()
|
||||
if system == "Windows":
|
||||
|
|
@ -2359,6 +2503,50 @@ class CompactDownloadItem(QFrame):
|
|||
print(f"📂 Opened folder: {folder_path}")
|
||||
else:
|
||||
print(f"❌ File not found: {file_path}")
|
||||
# Try to find the file in the downloads directory using the filename
|
||||
filename = os.path.basename(self.file_path)
|
||||
print(f"[DEBUG] Searching for file: {filename}")
|
||||
|
||||
from config.settings import config_manager
|
||||
download_path = config_manager.get('soulseek.download_path', './downloads')
|
||||
|
||||
# Search for the file in the downloads directory tree
|
||||
found_file = None
|
||||
for root, dirs, files in os.walk(download_path):
|
||||
for file in files:
|
||||
if file == filename:
|
||||
found_file = os.path.join(root, file)
|
||||
print(f"[DEBUG] Found file at: {found_file}")
|
||||
break
|
||||
if found_file:
|
||||
break
|
||||
|
||||
if found_file:
|
||||
folder_path = os.path.dirname(found_file)
|
||||
print(f"[DEBUG] Opening found folder: {folder_path}")
|
||||
|
||||
system = platform.system()
|
||||
if system == "Windows":
|
||||
os.startfile(folder_path)
|
||||
elif system == "Darwin": # macOS
|
||||
os.system(f'open "{folder_path}"')
|
||||
else: # Linux
|
||||
os.system(f'xdg-open "{folder_path}"')
|
||||
|
||||
print(f"📂 Opened folder: {folder_path}")
|
||||
else:
|
||||
print(f"❌ Could not find file {filename} in downloads directory")
|
||||
# Fallback to opening the downloads folder
|
||||
system = platform.system()
|
||||
if system == "Windows":
|
||||
os.startfile(download_path)
|
||||
elif system == "Darwin": # macOS
|
||||
os.system(f'open "{download_path}"')
|
||||
else: # Linux
|
||||
os.system(f'xdg-open "{download_path}"')
|
||||
|
||||
print(f"📂 Opened downloads folder as fallback: {download_path}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to open download location: {e}")
|
||||
|
||||
|
|
@ -2453,14 +2641,15 @@ class DownloadQueue(QFrame):
|
|||
|
||||
def add_download_item(self, title: str, artist: str, status: str = "queued",
|
||||
progress: int = 0, file_size: int = 0, download_speed: int = 0,
|
||||
file_path: str = "", download_id: str = "", soulseek_client=None):
|
||||
file_path: str = "", download_id: str = "", username: str = "",
|
||||
soulseek_client=None):
|
||||
"""Add a new download item to the queue"""
|
||||
# Hide empty message if this is the first item
|
||||
if len(self.download_items) == 0:
|
||||
self.empty_message.hide()
|
||||
|
||||
# Create new compact download item with queue type
|
||||
item = CompactDownloadItem(title, artist, status, progress, file_size, download_speed, file_path, download_id, soulseek_client, self.queue_type)
|
||||
# Create new compact download item with queue type
|
||||
item = CompactDownloadItem(title, artist, status, progress, file_size, download_speed, file_path, download_id, username, soulseek_client, self.queue_type)
|
||||
self.download_items.append(item)
|
||||
|
||||
# Insert before the stretch (which is always last)
|
||||
|
|
@ -2559,11 +2748,12 @@ class TabbedDownloadManager(QTabWidget):
|
|||
|
||||
def add_download_item(self, title: str, artist: str, status: str = "queued",
|
||||
progress: int = 0, file_size: int = 0, download_speed: int = 0,
|
||||
file_path: str = "", download_id: str = "", soulseek_client=None):
|
||||
file_path: str = "", download_id: str = "", username: str = "",
|
||||
soulseek_client=None):
|
||||
"""Add a new download item to the active queue"""
|
||||
item = self.active_queue.add_download_item(
|
||||
title, artist, status, progress, file_size, download_speed,
|
||||
file_path, download_id, soulseek_client
|
||||
file_path, download_id, username, soulseek_client
|
||||
)
|
||||
self.update_tab_counts()
|
||||
return item
|
||||
|
|
@ -2590,6 +2780,7 @@ class TabbedDownloadManager(QTabWidget):
|
|||
download_speed=download_item.download_speed,
|
||||
file_path=download_item.file_path,
|
||||
download_id=download_item.download_id,
|
||||
username=download_item.username,
|
||||
soulseek_client=download_item.soulseek_client
|
||||
)
|
||||
|
||||
|
|
@ -2610,9 +2801,15 @@ class TabbedDownloadManager(QTabWidget):
|
|||
self.parent().update_download_manager_stats(active_count, finished_count)
|
||||
|
||||
def clear_completed_downloads(self):
|
||||
"""Clear completed downloads from the finished queue"""
|
||||
self.finished_queue.clear_completed_downloads()
|
||||
self.update_tab_counts()
|
||||
"""Clear completed downloads from both slskd backend and local queues"""
|
||||
# Delegate to parent (DownloadsPage) which has access to soulseek_client
|
||||
if hasattr(self.parent(), 'clear_completed_downloads'):
|
||||
self.parent().clear_completed_downloads()
|
||||
else:
|
||||
# Fallback to local clearing if parent method not available
|
||||
print("[DEBUG] No parent clear method found, clearing locally only")
|
||||
self.finished_queue.clear_completed_downloads()
|
||||
self.update_tab_counts()
|
||||
|
||||
@property
|
||||
def download_items(self):
|
||||
|
|
@ -3823,6 +4020,7 @@ class DownloadsPage(QWidget):
|
|||
progress=0,
|
||||
file_size=search_result.size,
|
||||
download_id=download_id,
|
||||
username=search_result.username,
|
||||
file_path=full_filename, # Store the full path for matching
|
||||
soulseek_client=self.soulseek_client
|
||||
)
|
||||
|
|
@ -4259,8 +4457,37 @@ class DownloadsPage(QWidget):
|
|||
print(f"Error cleaning up finished download thread: {e}")
|
||||
|
||||
def clear_completed_downloads(self):
|
||||
"""Clear completed downloads from the queue"""
|
||||
self.download_queue.clear_completed_downloads()
|
||||
"""Clear completed downloads from both slskd backend and local queue"""
|
||||
if not self.soulseek_client:
|
||||
print("[ERROR] No soulseek client available for clearing downloads")
|
||||
return
|
||||
|
||||
# Run async clear operation
|
||||
import asyncio
|
||||
try:
|
||||
# Get the current event loop or create a new one
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
except RuntimeError:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
print("[DEBUG] 🗑️ Clearing all completed downloads from slskd backend...")
|
||||
success = loop.run_until_complete(self.soulseek_client.clear_all_completed_downloads())
|
||||
|
||||
if success:
|
||||
print("[DEBUG] ✅ Successfully cleared completed downloads from backend")
|
||||
# Also clear from local UI
|
||||
self.download_queue.clear_completed_downloads()
|
||||
# Trigger immediate status update to refresh UI
|
||||
self.update_download_status()
|
||||
else:
|
||||
print("[ERROR] ❌ Failed to clear completed downloads from backend")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Exception during clear completed downloads: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def update_download_status(self):
|
||||
"""Poll slskd API for download status updates (QTimer callback) - FIXED VERSION"""
|
||||
|
|
@ -4410,12 +4637,22 @@ class DownloadsPage(QWidget):
|
|||
new_status = 'downloading'
|
||||
elif 'Completed' in state or 'Succeeded' in state:
|
||||
new_status = 'completed'
|
||||
# Construct absolute file path for completed downloads
|
||||
api_filename = matching_transfer.get('filename', '')
|
||||
if api_filename and self.soulseek_client and self.soulseek_client.download_path:
|
||||
from pathlib import Path
|
||||
# Convert API filename to absolute path using configured download directory
|
||||
absolute_file_path = str(Path(self.soulseek_client.download_path) / api_filename)
|
||||
print(f"[DEBUG] Constructed absolute path: {absolute_file_path}")
|
||||
else:
|
||||
absolute_file_path = download_item.file_path
|
||||
|
||||
# Update the download item status and progress BEFORE moving
|
||||
download_item.update_status(
|
||||
status=new_status,
|
||||
progress=100, # Force 100% for completed downloads
|
||||
download_speed=int(avg_speed),
|
||||
file_path=matching_transfer.get('filename', download_item.file_path)
|
||||
file_path=absolute_file_path
|
||||
)
|
||||
# Move completed items to finished queue
|
||||
print(f"[DEBUG] Moving completed download '{download_item.title}' to finished queue")
|
||||
|
|
@ -4471,7 +4708,18 @@ class DownloadsPage(QWidget):
|
|||
print(f"[DEBUG] Updated download ID for '{download_item.title}': {download_item.download_id} -> {transfer_id}")
|
||||
download_item.download_id = transfer_id
|
||||
|
||||
# (Matching transfer not found - debug message already printed above)
|
||||
# If no matching transfer found, the download might have been removed from slskd
|
||||
else:
|
||||
# Check if this download was in finished state and might have been removed
|
||||
if download_item in self.download_queue.finished_queue.download_items:
|
||||
print(f"[DEBUG] 🗑️ Download '{download_item.title}' not found in API - likely removed from slskd")
|
||||
print(f"[DEBUG] Removing '{download_item.title}' from finished downloads UI")
|
||||
# Remove from finished queue since it's no longer in slskd
|
||||
self.download_queue.finished_queue.remove_download_item(download_item)
|
||||
continue
|
||||
|
||||
# After processing all download items, check for any that weren't found in the API
|
||||
# This handles the case where downloads were removed from slskd externally
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Error processing transfer status update: {e}")
|
||||
|
|
|
|||
Loading…
Reference in a new issue