mirror of
https://github.com/Dvorinka/SpotifyRecAlg.git
synced 2026-06-03 20:13:03 +00:00
356 lines
11 KiB
Python
356 lines
11 KiB
Python
"""
|
|
Spotify Downloader Settings API endpoints
|
|
"""
|
|
|
|
from typing import Any
|
|
|
|
from flask import jsonify
|
|
from flask_jwt_extended import get_jwt_identity
|
|
from flask_openapi3 import APIBlueprint
|
|
from pydantic import BaseModel, Field
|
|
|
|
from swingmusic import logger
|
|
from swingmusic.config import UserConfig
|
|
from swingmusic.services.download_jobs import download_job_manager
|
|
from swingmusic.utils.auth import get_current_userid
|
|
|
|
spotify_settings_bp = APIBlueprint(
|
|
"spotify_settings",
|
|
import_name="spotify_settings",
|
|
url_prefix="/api/settings/spotify",
|
|
)
|
|
|
|
|
|
def _current_userid() -> int:
|
|
try:
|
|
identity = get_jwt_identity()
|
|
if isinstance(identity, dict) and identity.get("id") is not None:
|
|
return int(identity["id"])
|
|
except Exception:
|
|
pass
|
|
|
|
return get_current_userid()
|
|
|
|
|
|
class SpotifySettingsRequest(BaseModel):
|
|
defaultQuality: str = Field("flac", description="Default download quality")
|
|
downloadFolder: str | None = 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: list | None = 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: dict[str, Any] | None = None
|
|
message: str | None = 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 pending/active download jobs for current user.
|
|
"""
|
|
try:
|
|
userid = _current_userid()
|
|
cancelled = download_job_manager.clear_queue(userid)
|
|
return jsonify(
|
|
{
|
|
"success": True,
|
|
"cancelled_jobs": cancelled,
|
|
"message": f"Cleared queue ({cancelled} job(s) cancelled)",
|
|
}
|
|
)
|
|
|
|
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 completed/failed/cancelled download history for current user.
|
|
"""
|
|
try:
|
|
userid = _current_userid()
|
|
deleted = download_job_manager.clear_history(userid)
|
|
return jsonify(
|
|
{
|
|
"success": True,
|
|
"deleted_jobs": deleted,
|
|
"message": f"Download history cleared ({deleted} job(s) removed)",
|
|
}
|
|
)
|
|
|
|
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
|