i dont have time for this

This commit is contained in:
architect.in.git
2025-04-22 21:51:06 -06:00
parent b00115792a
commit dbbd8889df
8 changed files with 208 additions and 36 deletions

View File

@@ -6,7 +6,6 @@ from deezspot.deezloader import DeeLogin
from pathlib import Path
def download_album(
service,
url,
main,
fallback=None,
@@ -22,8 +21,24 @@ def download_album(
progress_callback=None
):
try:
# DEBUG: Print parameters
print(f"DEBUG: album.py received - service={service}, main={main}, fallback={fallback}")
# Detect URL source (Spotify or Deezer) from URL
is_spotify_url = 'open.spotify.com' in url.lower()
is_deezer_url = 'deezer.com' in url.lower()
# Determine service exclusively from URL
if is_spotify_url:
service = 'spotify'
elif is_deezer_url:
service = 'deezer'
else:
# If URL can't be detected, raise an error
error_msg = "Invalid URL: Must be from open.spotify.com or deezer.com"
print(f"ERROR: {error_msg}")
raise ValueError(error_msg)
print(f"DEBUG: album.py - URL detection: is_spotify_url={is_spotify_url}, is_deezer_url={is_deezer_url}")
print(f"DEBUG: album.py - Service determined from URL: {service}")
print(f"DEBUG: album.py - Credentials: main={main}, fallback={fallback}")
# Load Spotify client credentials if available
spotify_client_id = None
@@ -49,6 +64,8 @@ def download_album(
except Exception as e:
print(f"Error loading Spotify search credentials: {e}")
# For Spotify URLs: check if fallback is enabled, if so use the fallback logic,
# otherwise download directly from Spotify
if service == 'spotify':
if fallback:
if quality is None:
@@ -186,6 +203,7 @@ def download_album(
max_retries=max_retries
)
print(f"DEBUG: Album download completed successfully using Spotify main")
# For Deezer URLs: download directly from Deezer
elif service == 'deezer':
if quality is None:
quality = 'FLAC'

View File

@@ -87,6 +87,16 @@ def download_artist_albums(url, album_type="album,single,compilation", request_a
logger.info(f"Fetching artist info for ID: {artist_id}")
# Detect URL source (only Spotify is supported for artists)
is_spotify_url = 'open.spotify.com' in url.lower()
is_deezer_url = 'deezer.com' in url.lower()
# Artist functionality only works with Spotify URLs currently
if not is_spotify_url:
error_msg = "Invalid URL: Artist functionality only supports open.spotify.com URLs"
logger.error(error_msg)
raise ValueError(error_msg)
# Get artist info with albums
artist_data = get_spotify_info(artist_id, "artist")
@@ -152,8 +162,7 @@ def download_artist_albums(url, album_type="album,single,compilation", request_a
"name": album_name,
"artist": album_artist,
"type": "album",
"service": "spotify",
# Add reference to parent artist request if needed
# URL source will be automatically detected in the download functions
"parent_artist_url": url,
"parent_request_type": "artist"
}
@@ -162,7 +171,6 @@ def download_artist_albums(url, album_type="album,single,compilation", request_a
task_data = {
"download_type": "album",
"type": "album", # Type for the download task
"service": "spotify", # Default to Spotify since we're using Spotify API
"url": album_url, # Important: use the album URL, not artist URL
"retry_url": album_url, # Use album URL for retry logic, not artist URL
"name": album_name,

View File

@@ -120,9 +120,6 @@ class CeleryDownloadQueueManager:
# Extract original request or use empty dict
original_request = task.get("orig_request", task.get("original_request", {}))
# Determine service (spotify or deezer) from config or request
service = original_request.get("service", config_params.get("service", "spotify"))
# Debug retry_url if present
if "retry_url" in task:
logger.debug(f"Task has retry_url: {task['retry_url']}")
@@ -133,21 +130,20 @@ class CeleryDownloadQueueManager:
"type": task.get("type", download_type),
"name": task.get("name", ""),
"artist": task.get("artist", ""),
"service": service,
"url": task.get("url", ""),
# Preserve retry_url if present
"retry_url": task.get("retry_url", ""),
# Use config values but allow override from request
"main": original_request.get("main",
config_params['spotify'] if service == 'spotify' else config_params['deezer']),
# Use main account from config
"main": original_request.get("main", config_params['deezer']),
# Set fallback if enabled in config
"fallback": original_request.get("fallback",
config_params['spotify'] if config_params['fallback'] and service == 'spotify' else None),
config_params['spotify'] if config_params['fallback'] else None),
"quality": original_request.get("quality",
config_params['spotifyQuality'] if service == 'spotify' else config_params['deezerQuality']),
# Use default quality settings
"quality": original_request.get("quality", config_params['deezerQuality']),
"fall_quality": original_request.get("fall_quality", config_params['spotifyQuality']),

View File

@@ -893,12 +893,11 @@ def download_track(self, **task_data):
custom_track_format = task_data.get("custom_track_format", config_params.get("customTrackFormat", "%tracknum%. %music%"))
pad_tracks = task_data.get("pad_tracks", config_params.get("tracknum_padding", True))
# Execute the download
# Execute the download - service is now determined from URL
download_track_func(
service=service,
url=url,
main=main,
fallback=fallback,
fallback=fallback if fallback_enabled else None,
quality=quality,
fall_quality=fall_quality,
real_time=real_time,
@@ -961,12 +960,11 @@ def download_album(self, **task_data):
custom_track_format = task_data.get("custom_track_format", config_params.get("customTrackFormat", "%tracknum%. %music%"))
pad_tracks = task_data.get("pad_tracks", config_params.get("tracknum_padding", True))
# Execute the download
# Execute the download - service is now determined from URL
download_album_func(
service=service,
url=url,
main=main,
fallback=fallback,
fallback=fallback if fallback_enabled else None,
quality=quality,
fall_quality=fall_quality,
real_time=real_time,
@@ -1034,12 +1032,11 @@ def download_playlist(self, **task_data):
retry_delay_increase = task_data.get("retry_delay_increase", config_params.get("retry_delay_increase", 5))
max_retries = task_data.get("max_retries", config_params.get("maxRetries", 3))
# Execute the download
# Execute the download - service is now determined from URL
download_playlist_func(
service=service,
url=url,
main=main,
fallback=fallback,
fallback=fallback if fallback_enabled else None,
quality=quality,
fall_quality=fall_quality,
real_time=real_time,

View File

@@ -6,7 +6,6 @@ from deezspot.deezloader import DeeLogin
from pathlib import Path
def download_playlist(
service,
url,
main,
fallback=None,
@@ -22,8 +21,24 @@ def download_playlist(
progress_callback=None,
):
try:
# DEBUG: Print parameters
print(f"DEBUG: playlist.py received - service={service}, main={main}, fallback={fallback}")
# Detect URL source (Spotify or Deezer) from URL
is_spotify_url = 'open.spotify.com' in url.lower()
is_deezer_url = 'deezer.com' in url.lower()
# Determine service exclusively from URL
if is_spotify_url:
service = 'spotify'
elif is_deezer_url:
service = 'deezer'
else:
# If URL can't be detected, raise an error
error_msg = "Invalid URL: Must be from open.spotify.com or deezer.com"
print(f"ERROR: {error_msg}")
raise ValueError(error_msg)
print(f"DEBUG: playlist.py - URL detection: is_spotify_url={is_spotify_url}, is_deezer_url={is_deezer_url}")
print(f"DEBUG: playlist.py - Service determined from URL: {service}")
print(f"DEBUG: playlist.py - Credentials: main={main}, fallback={fallback}")
# Load Spotify client credentials if available
spotify_client_id = None
@@ -49,6 +64,8 @@ def download_playlist(
except Exception as e:
print(f"Error loading Spotify search credentials: {e}")
# For Spotify URLs: check if fallback is enabled, if so use the fallback logic,
# otherwise download directly from Spotify
if service == 'spotify':
if fallback:
if quality is None:
@@ -181,6 +198,7 @@ def download_playlist(
max_retries=max_retries
)
print(f"DEBUG: Playlist download completed successfully using Spotify main")
# For Deezer URLs: download directly from Deezer
elif service == 'deezer':
if quality is None:
quality = 'FLAC'

View File

@@ -6,7 +6,6 @@ from deezspot.deezloader import DeeLogin
from pathlib import Path
def download_track(
service,
url,
main,
fallback=None,
@@ -22,8 +21,24 @@ def download_track(
progress_callback=None
):
try:
# DEBUG: Print parameters
print(f"DEBUG: track.py received - service={service}, main={main}, fallback={fallback}")
# Detect URL source (Spotify or Deezer) from URL
is_spotify_url = 'open.spotify.com' in url.lower()
is_deezer_url = 'deezer.com' in url.lower()
# Determine service exclusively from URL
if is_spotify_url:
service = 'spotify'
elif is_deezer_url:
service = 'deezer'
else:
# If URL can't be detected, raise an error
error_msg = "Invalid URL: Must be from open.spotify.com or deezer.com"
print(f"ERROR: {error_msg}")
raise ValueError(error_msg)
print(f"DEBUG: track.py - URL detection: is_spotify_url={is_spotify_url}, is_deezer_url={is_deezer_url}")
print(f"DEBUG: track.py - Service determined from URL: {service}")
print(f"DEBUG: track.py - Credentials: main={main}, fallback={fallback}")
# Load Spotify client credentials if available
spotify_client_id = None
@@ -49,6 +64,8 @@ def download_track(
except Exception as e:
print(f"Error loading Spotify search credentials: {e}")
# For Spotify URLs: check if fallback is enabled, if so use the fallback logic,
# otherwise download directly from Spotify
if service == 'spotify':
if fallback:
if quality is None:
@@ -166,6 +183,7 @@ def download_track(
retry_delay_increase=retry_delay_increase,
max_retries=max_retries
)
# For Deezer URLs: download directly from Deezer
elif service == 'deezer':
if quality is None:
quality = 'FLAC'

View File

@@ -551,6 +551,9 @@ createQueueItem(item, type, prgFile, queueId) {
<div class="queue-item-status">
<div class="log" id="log-${queueId}-${prgFile}">${defaultMessage}</div>
<!-- Error details container (hidden by default) -->
<div class="error-details" id="error-details-${queueId}-${prgFile}" style="display: none;"></div>
<div class="progress-container">
<!-- Track-level progress bar for single track or current track in multi-track items -->
<div class="track-progress-bar-container" id="track-progress-container-${queueId}-${prgFile}">
@@ -620,13 +623,80 @@ createQueueItem(item, type, prgFile, queueId) {
break;
case 'error':
entry.element.classList.add('error');
// Show detailed error information in the error-details container if available
if (entry.lastStatus && entry.element) {
const errorDetailsContainer = entry.element.querySelector(`#error-details-${entry.uniqueId}-${entry.prgFile}`);
if (errorDetailsContainer) {
// Format the error details
let errorDetailsHTML = '';
// Add error message
errorDetailsHTML += `<div class="error-message">${entry.lastStatus.error || entry.lastStatus.message || 'Unknown error'}</div>`;
// Add parent information if available
if (entry.lastStatus.parent) {
const parent = entry.lastStatus.parent;
let parentInfo = '';
if (parent.type === 'album') {
parentInfo = `<div class="parent-info">From album: "${parent.title}" by ${parent.artist || 'Unknown artist'}</div>`;
} else if (parent.type === 'playlist') {
parentInfo = `<div class="parent-info">From playlist: "${parent.name}" by ${parent.owner || 'Unknown creator'}</div>`;
}
if (parentInfo) {
errorDetailsHTML += parentInfo;
}
}
// Add source URL if available
if (entry.lastStatus.url) {
errorDetailsHTML += `<div class="error-url">Source: <a href="${entry.lastStatus.url}" target="_blank" rel="noopener noreferrer">${entry.lastStatus.url}</a></div>`;
}
// Add retry button if this error can be retried
if (entry.lastStatus.can_retry !== false && (!entry.retryCount || entry.retryCount < this.MAX_RETRIES)) {
errorDetailsHTML += `<button class="retry-btn" data-queueid="${entry.uniqueId}">Retry Download</button>`;
}
// Display the error details
errorDetailsContainer.innerHTML = errorDetailsHTML;
errorDetailsContainer.style.display = 'block';
// Add event listener to retry button if present
const retryBtn = errorDetailsContainer.querySelector('.retry-btn');
if (retryBtn) {
retryBtn.addEventListener('click', (e) => {
const queueId = e.target.getAttribute('data-queueid');
if (queueId) {
const logElement = entry.element.querySelector('.log');
this.retryDownload(queueId, logElement);
}
});
}
}
}
break;
case 'complete':
case 'done':
entry.element.classList.add('complete');
// Hide error details if present
if (entry.element) {
const errorDetailsContainer = entry.element.querySelector(`#error-details-${entry.uniqueId}-${entry.prgFile}`);
if (errorDetailsContainer) {
errorDetailsContainer.style.display = 'none';
}
}
break;
case 'cancelled':
entry.element.classList.add('cancelled');
// Hide error details if present
if (entry.element) {
const errorDetailsContainer = entry.element.querySelector(`#error-details-${entry.uniqueId}-${entry.prgFile}`);
if (errorDetailsContainer) {
errorDetailsContainer.style.display = 'none';
}
}
break;
}
}
@@ -1081,7 +1151,15 @@ createQueueItem(item, type, prgFile, queueId) {
return `${trackName}${artist ? ` by ${artist}` : ''} was skipped: ${data.reason || 'Unknown reason'}`;
case 'error':
// Enhanced error message handling using the new format
let errorMsg = `Error: ${data.error || data.message || 'Unknown error'}`;
// Add position information for tracks in collections
if (data.current_track && data.total_tracks) {
errorMsg = `Error on track ${data.current_track}/${data.total_tracks}: ${data.error || data.message || 'Unknown error'}`;
}
// Add retry information if available
if (data.retry_count !== undefined) {
errorMsg += ` (Attempt ${data.retry_count}/${this.MAX_RETRIES})`;
} else if (data.can_retry !== undefined) {
@@ -1091,6 +1169,21 @@ createQueueItem(item, type, prgFile, queueId) {
errorMsg += ` (Max retries reached)`;
}
}
// Add parent information if this is a track with a parent
if (data.type === 'track' && data.parent) {
if (data.parent.type === 'album') {
errorMsg += `\nFrom album: "${data.parent.title}" by ${data.parent.artist || 'Unknown artist'}`;
} else if (data.parent.type === 'playlist') {
errorMsg += `\nFrom playlist: "${data.parent.name}" by ${data.parent.owner || 'Unknown creator'}`;
}
}
// Add URL for troubleshooting if available
if (data.url) {
errorMsg += `\nSource: ${data.url}`;
}
return errorMsg;
case 'retrying':
@@ -1168,8 +1261,30 @@ createQueueItem(item, type, prgFile, queueId) {
entry.isRetrying = true;
logElement.textContent = 'Retrying download...';
// Determine if we should use parent information for retry
let useParent = false;
let parentType = null;
let parentUrl = null;
// Check if we have parent information in the lastStatus
if (entry.lastStatus && entry.lastStatus.parent) {
const parent = entry.lastStatus.parent;
if (parent.type && parent.url) {
useParent = true;
parentType = parent.type;
parentUrl = parent.url;
console.log(`Using parent info for retry: ${parentType} with URL: ${parentUrl}`);
}
}
// Find a retry URL from various possible sources
const getRetryUrl = () => {
// If using parent, return parent URL
if (useParent && parentUrl) {
return parentUrl;
}
// Otherwise use the standard fallback options
if (entry.requestUrl) return entry.requestUrl;
// If we have lastStatus with original_request, check there
@@ -1200,10 +1315,12 @@ createQueueItem(item, type, prgFile, queueId) {
// Close any existing polling interval
this.clearPollingInterval(queueId);
console.log(`Retrying download for ${entry.type} with URL: ${retryUrl}`);
// Determine which type to use for the API endpoint
const apiType = useParent ? parentType : entry.type;
console.log(`Retrying download using type: ${apiType} with URL: ${retryUrl}`);
// Build the API URL based on the entry's type
const apiUrl = `/api/${entry.type}/download?url=${encodeURIComponent(retryUrl)}`;
// Build the API URL based on the determined type
const apiUrl = `/api/${apiType}/download?url=${encodeURIComponent(retryUrl)}`;
// Add name and artist if available for better progress display
let fullRetryUrl = apiUrl;

View File

@@ -116,7 +116,7 @@
<option value="%music%">%music% - Track title</option>
<option value="%artist%">%artist% - Track artist</option>
<option value="%album%">%album% - Album name</option>
<option value="%album_artist%">%album_artist% - Album artist</option>
<option value="%ar_album%">%ar_album% - Album artist</option>
<option value="%tracknum%">%tracknum% - Track number</option>
<option value="%year%">%year% - Year of release</option>
</optgroup>
@@ -169,7 +169,7 @@
<option value="%music%">%music% - Track title</option>
<option value="%artist%">%artist% - Track artist</option>
<option value="%album%">%album% - Album name</option>
<option value="%album_artist%">%album_artist% - Album artist</option>
<option value="%ar_album%">%ar_album% - Album artist</option>
<option value="%tracknum%">%tracknum% - Track number</option>
<option value="%year%">%year% - Year of release</option>
</optgroup>