mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-03 20:13:02 +00:00
aad2f2d421
- Add spotify_downloader service for track/album/playlist downloads - Update Spotify API endpoints with enhanced functionality - Fix pydub utils import issues - Update GitHub workflows for improved CI/CD
372 lines
11 KiB
Python
372 lines
11 KiB
Python
"""
|
|
Spotify Downloader Settings API endpoints
|
|
"""
|
|
|
|
from flask import Blueprint, request, jsonify
|
|
from flask_openapi3 import APIBlueprint, Tag
|
|
from pydantic import BaseModel, Field
|
|
from typing import Optional, Dict, Any
|
|
|
|
from swingmusic import logger
|
|
from swingmusic.config import UserConfig
|
|
|
|
spotify_settings_bp = APIBlueprint(
|
|
'spotify_settings',
|
|
import_name='spotify_settings',
|
|
url_prefix='/api/settings/spotify'
|
|
)
|
|
|
|
|
|
class SpotifySettingsRequest(BaseModel):
|
|
defaultQuality: str = Field('flac', description='Default download quality')
|
|
downloadFolder: Optional[str] = Field(None, description='Download folder path')
|
|
autoAddToLibrary: bool = Field(True, description='Auto-add downloads to library')
|
|
maxConcurrentDownloads: int = Field(3, description='Max concurrent downloads')
|
|
sources: Optional[list] = Field(None, description='Download sources configuration')
|
|
maxRetryAttempts: int = Field(3, description='Max retry attempts')
|
|
cleanupHistoryDays: int = Field(30, description='Auto-cleanup history days')
|
|
showExplicitWarning: bool = Field(True, description='Show explicit content warning')
|
|
|
|
|
|
class SpotifySettingsResponse(BaseModel):
|
|
success: bool
|
|
settings: Optional[Dict[str, Any]] = None
|
|
message: Optional[str] = None
|
|
|
|
|
|
# Default settings
|
|
DEFAULT_SETTINGS = {
|
|
'defaultQuality': 'flac',
|
|
'downloadFolder': '',
|
|
'autoAddToLibrary': True,
|
|
'maxConcurrentDownloads': 3,
|
|
'sources': [
|
|
{
|
|
'name': 'tidal',
|
|
'display_name': 'Tidal',
|
|
'enabled': True,
|
|
'priority': 1,
|
|
'config': {
|
|
'quality_preference': ['lossless', 'high', 'normal'],
|
|
'formats': ['flac', 'mp3']
|
|
}
|
|
},
|
|
{
|
|
'name': 'qobuz',
|
|
'display_name': 'Qobuz',
|
|
'enabled': True,
|
|
'priority': 2,
|
|
'config': {
|
|
'quality_preference': ['lossless', 'high', 'normal'],
|
|
'formats': ['flac', 'mp3']
|
|
}
|
|
},
|
|
{
|
|
'name': 'amazon',
|
|
'display_name': 'Amazon Music',
|
|
'enabled': False,
|
|
'priority': 3,
|
|
'config': {
|
|
'quality_preference': ['high', 'normal'],
|
|
'formats': ['mp3', 'aac']
|
|
}
|
|
}
|
|
],
|
|
'maxRetryAttempts': 3,
|
|
'cleanupHistoryDays': 30,
|
|
'showExplicitWarning': True
|
|
}
|
|
|
|
|
|
def get_spotify_settings():
|
|
"""Get Spotify downloader settings from config"""
|
|
try:
|
|
config = UserConfig()
|
|
spotify_settings = config.spotify_downloads if hasattr(config, 'spotify_downloads') else {}
|
|
|
|
# Merge with defaults
|
|
settings = {**DEFAULT_SETTINGS}
|
|
settings.update(spotify_settings)
|
|
|
|
return settings
|
|
except Exception as e:
|
|
logger.error(f"Error loading Spotify settings: {e}")
|
|
return DEFAULT_SETTINGS
|
|
|
|
|
|
def save_spotify_settings(settings_data: dict):
|
|
"""Save Spotify downloader settings to config"""
|
|
try:
|
|
config = UserConfig()
|
|
|
|
# Update only provided settings
|
|
current_settings = get_spotify_settings()
|
|
current_settings.update(settings_data)
|
|
|
|
# Save to config
|
|
config.spotify_downloads = current_settings
|
|
config.save()
|
|
|
|
logger.info("Spotify settings saved successfully")
|
|
return True
|
|
except Exception as e:
|
|
logger.error(f"Error saving Spotify settings: {e}")
|
|
return False
|
|
|
|
|
|
@spotify_settings_bp.get('/', summary='Get Spotify downloader settings')
|
|
def get_settings():
|
|
"""
|
|
Get current Spotify downloader settings
|
|
|
|
Returns all Spotify downloader configuration including:
|
|
- Default quality settings
|
|
- Download folder configuration
|
|
- Source priorities and enablement
|
|
- Advanced options
|
|
"""
|
|
try:
|
|
settings = get_spotify_settings()
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'settings': settings
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error getting Spotify settings: {e}")
|
|
return jsonify({
|
|
'success': False,
|
|
'message': str(e)
|
|
}), 500
|
|
|
|
|
|
@spotify_settings_bp.post('/', summary='Update Spotify downloader settings')
|
|
def update_settings(body: SpotifySettingsRequest):
|
|
"""
|
|
Update Spotify downloader settings
|
|
|
|
- **defaultQuality**: Default download quality (flac, mp3_320, mp3_128)
|
|
- **downloadFolder**: Custom download folder path
|
|
- **autoAddToLibrary**: Whether to auto-add downloads to library
|
|
- **maxConcurrentDownloads**: Maximum concurrent downloads (1-10)
|
|
- **sources**: Download sources configuration
|
|
- **maxRetryAttempts**: Maximum retry attempts for failed downloads
|
|
- **cleanupHistoryDays**: Days to keep download history (0 = disabled)
|
|
- **showExplicitWarning**: Show warning for explicit content
|
|
|
|
Updates the Spotify downloader configuration and saves to user settings.
|
|
"""
|
|
try:
|
|
# Validate inputs
|
|
if body.defaultQuality not in ['flac', 'mp3_320', 'mp3_128']:
|
|
return jsonify({
|
|
'success': False,
|
|
'message': 'Invalid quality setting'
|
|
}), 400
|
|
|
|
if not 1 <= body.maxConcurrentDownloads <= 10:
|
|
return jsonify({
|
|
'success': False,
|
|
'message': 'Max concurrent downloads must be between 1 and 10'
|
|
}), 400
|
|
|
|
if not 0 <= body.maxRetryAttempts <= 10:
|
|
return jsonify({
|
|
'success': False,
|
|
'message': 'Max retry attempts must be between 0 and 10'
|
|
}), 400
|
|
|
|
if not 0 <= body.cleanupHistoryDays <= 365:
|
|
return jsonify({
|
|
'success': False,
|
|
'message': 'Cleanup days must be between 0 and 365'
|
|
}), 400
|
|
|
|
# Prepare settings data
|
|
settings_data = {
|
|
'defaultQuality': body.defaultQuality,
|
|
'downloadFolder': body.downloadFolder,
|
|
'autoAddToLibrary': body.autoAddToLibrary,
|
|
'maxConcurrentDownloads': body.maxConcurrentDownloads,
|
|
'sources': body.sources,
|
|
'maxRetryAttempts': body.maxRetryAttempts,
|
|
'cleanupHistoryDays': body.cleanupHistoryDays,
|
|
'showExplicitWarning': body.showExplicitWarning
|
|
}
|
|
|
|
# Remove None values
|
|
settings_data = {k: v for k, v in settings_data.items() if v is not None}
|
|
|
|
# Save settings
|
|
if save_spotify_settings(settings_data):
|
|
return jsonify({
|
|
'success': True,
|
|
'message': 'Settings saved successfully'
|
|
})
|
|
else:
|
|
return jsonify({
|
|
'success': False,
|
|
'message': 'Failed to save settings'
|
|
}), 500
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error updating Spotify settings: {e}")
|
|
return jsonify({
|
|
'success': False,
|
|
'message': str(e)
|
|
}), 500
|
|
|
|
|
|
@spotify_settings_bp.post('/reset', summary='Reset Spotify settings to defaults')
|
|
def reset_settings():
|
|
"""
|
|
Reset all Spotify downloader settings to default values
|
|
|
|
Resets all Spotify downloader configuration to factory defaults.
|
|
"""
|
|
try:
|
|
if save_spotify_settings(DEFAULT_SETTINGS):
|
|
return jsonify({
|
|
'success': True,
|
|
'message': 'Settings reset to defaults',
|
|
'settings': DEFAULT_SETTINGS
|
|
})
|
|
else:
|
|
return jsonify({
|
|
'success': False,
|
|
'message': 'Failed to reset settings'
|
|
}), 500
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error resetting Spotify settings: {e}")
|
|
return jsonify({
|
|
'success': False,
|
|
'message': str(e)
|
|
}), 500
|
|
|
|
|
|
@spotify_settings_bp.delete('/queue', summary='Clear download queue')
|
|
def clear_queue():
|
|
"""
|
|
Clear the entire download queue
|
|
|
|
Removes all pending and active downloads from the queue.
|
|
"""
|
|
try:
|
|
from swingmusic.services.spotify_downloader import spotify_downloader
|
|
|
|
# Clear queue
|
|
spotify_downloader.download_queue.clear()
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'message': 'Download queue cleared'
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error clearing download queue: {e}")
|
|
return jsonify({
|
|
'success': False,
|
|
'message': str(e)
|
|
}), 500
|
|
|
|
|
|
@spotify_settings_bp.delete('/history', summary='Clear download history')
|
|
def clear_history():
|
|
"""
|
|
Clear the download history
|
|
|
|
Removes all completed and failed downloads from history.
|
|
"""
|
|
try:
|
|
from swingmusic.services.spotify_downloader import spotify_downloader
|
|
|
|
# Clear history
|
|
spotify_downloader.download_history.clear()
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'message': 'Download history cleared'
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error clearing download history: {e}")
|
|
return jsonify({
|
|
'success': False,
|
|
'message': str(e)
|
|
}), 500
|
|
|
|
|
|
@spotify_settings_bp.get('/sources', summary='Get available download sources')
|
|
def get_available_sources():
|
|
"""
|
|
Get list of available download sources
|
|
|
|
Returns information about supported download sources and their capabilities.
|
|
"""
|
|
try:
|
|
sources = [
|
|
{
|
|
'name': 'tidal',
|
|
'display_name': 'Tidal',
|
|
'description': 'High-quality FLAC downloads from Tidal',
|
|
'quality_options': ['lossless', 'high', 'normal'],
|
|
'formats': ['flac', 'mp3'],
|
|
'available': True,
|
|
'requires_auth': False,
|
|
'max_quality': 'lossless'
|
|
},
|
|
{
|
|
'name': 'qobuz',
|
|
'display_name': 'Qobuz',
|
|
'description': 'Alternative high-quality source with extensive catalog',
|
|
'quality_options': ['lossless', 'high', 'normal'],
|
|
'formats': ['flac', 'mp3'],
|
|
'available': True,
|
|
'requires_auth': True,
|
|
'max_quality': 'lossless'
|
|
},
|
|
{
|
|
'name': 'amazon',
|
|
'display_name': 'Amazon Music',
|
|
'description': 'Fallback source with wide availability',
|
|
'quality_options': ['high', 'normal'],
|
|
'formats': ['mp3', 'aac'],
|
|
'available': False, # Disabled by default
|
|
'requires_auth': True,
|
|
'max_quality': 'high'
|
|
}
|
|
]
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'sources': sources
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error getting available sources: {e}")
|
|
return jsonify({
|
|
'success': False,
|
|
'message': str(e)
|
|
}), 500
|
|
|
|
|
|
# Error handlers
|
|
@spotify_settings_bp.errorhandler(400)
|
|
def bad_request(error):
|
|
return jsonify({
|
|
'error': 'Bad request',
|
|
'message': str(error),
|
|
'success': False
|
|
}), 400
|
|
|
|
|
|
@spotify_settings_bp.errorhandler(500)
|
|
def internal_error(error):
|
|
return jsonify({
|
|
'error': 'Internal server error',
|
|
'message': str(error),
|
|
'success': False
|
|
}), 500
|