diff --git a/.env.example b/.env.example index 16abd67..00af629 100644 --- a/.env.example +++ b/.env.example @@ -1,31 +1,42 @@ - -# Docker Compose environment variables# Delete all comments of this when deploying (everything that is ) +### +### Main configuration file of the server. If you +### plan to have this only for personal use, you +### can leave the defaults as they are. +### +### If you plan on using for a server, +### see [insert docs url] +### # Interface to bind to. Unless you know what you're doing, don't change this HOST=0.0.0.0 -# Redis connection (external or internal) +# Redis connection (external or internal). REDIS_HOST=redis REDIS_PORT=6379 REDIS_DB=0 REDIS_PASSWORD=CHANGE_ME -# Set to true to filter out explicit content +# Set to true to filter out explicit content. EXPLICIT_FILTER=false -# User ID for the container -PUID=1000 -# Group ID for the container + +# User and group ID for the container. Sets the owner of the downloaded files. +PUID=1000 PGID=1000 -# Optional: Sets the default file permissions for newly created files within the container. +# Optional: Sets the default file permissions for newly created files within the container. UMASK=0022 -# Enable authentication -ENABLE_AUTH=true -# Basic Authentication settings. CHANGE THE JWT_SECRET +### +### Multi-user settings, disabled by default. +### + +# Enable authentication (i.e. multi-user mode). +ENABLE_AUTH=false + +# Basic Authentication settings. JWT_SECRET=long-random-text # How much a session persists, in hours. 720h = 30 days. @@ -49,4 +60,4 @@ GOOGLE_CLIENT_SECRET= # GitHub SSO (get from GitHub Developer Settings) GITHUB_CLIENT_ID= -GITHUB_CLIENT_SECRET= +GITHUB_CLIENT_SECRET= \ No newline at end of file diff --git a/API_DOCUMENTATION.md b/API_DOCUMENTATION.md new file mode 100644 index 0000000..5746202 --- /dev/null +++ b/API_DOCUMENTATION.md @@ -0,0 +1,644 @@ +# Spotizerr API Documentation + +A comprehensive music download service API built with FastAPI that supports Spotify content downloading, playlist/artist watching, and user authentication. + +## 🚀 Base URL +``` +http://localhost:7171/api +``` + +## 🔐 Authentication + +### Authentication System +- **Type**: JWT-based authentication with optional SSO (Google/GitHub) +- **Token**: Bearer token in Authorization header +- **When Disabled**: System user with admin privileges automatically applied + +### Auth Status +Check authentication configuration and current user status. + +#### `GET /auth/status` +**Response:** +```json +{ + "auth_enabled": true, + "authenticated": false, + "user": null, + "registration_enabled": true, + "sso_enabled": true, + "sso_providers": ["google", "github"] +} +``` + +### Login & Registration + +#### `POST /auth/login` +**Request:** +```json +{ + "username": "string", + "password": "string" +} +``` +**Response:** +```json +{ + "access_token": "jwt-token", + "token_type": "bearer", + "user": { + "username": "string", + "email": "string", + "role": "user|admin", + "created_at": "2024-01-01T00:00:00", + "last_login": "2024-01-01T00:00:00", + "sso_provider": null, + "is_sso_user": false + } +} +``` + +#### `POST /auth/register` +**Request:** +```json +{ + "username": "string", + "password": "string", + "email": "string" +} +``` + +#### `POST /auth/logout` +Logs out the current user. + +### User Management (Admin Only) + +#### `GET /auth/users` +List all users. + +#### `POST /auth/users/create` +**Request:** +```json +{ + "username": "string", + "password": "string", + "email": "string", + "role": "user|admin" +} +``` + +#### `DELETE /auth/users/{username}` +Delete a user. + +#### `PUT /auth/users/{username}/role` +**Request:** +```json +{ + "role": "user|admin" +} +``` + +#### `PUT /auth/users/{username}/password` +Admin password reset. +**Request:** +```json +{ + "new_password": "string" +} +``` + +### Profile Management + +#### `GET /auth/profile` +Get current user profile. + +#### `PUT /auth/profile/password` +Change own password. +**Request:** +```json +{ + "current_password": "string", + "new_password": "string" +} +``` + +### SSO Authentication + +#### `GET /auth/sso/status` +Get SSO configuration and available providers. + +#### `GET /auth/sso/login/google` +Redirect to Google OAuth. + +#### `GET /auth/sso/login/github` +Redirect to GitHub OAuth. + +#### `GET /auth/sso/callback/google` +Google OAuth callback. + +#### `GET /auth/sso/callback/github` +GitHub OAuth callback. + +#### `POST /auth/sso/unlink/{provider}` +Unlink SSO provider from account. + +## 🎵 Content Download Endpoints + +### Track Downloads + +#### `GET /track/download/{track_id}` +Download a single track. +**Response:** +```json +{ + "task_id": "uuid" +} +``` +**Status Code:** 202 (Accepted) + +#### `GET /track/download/cancel` +**Query Parameters:** +- `task_id`: Task ID to cancel + +#### `GET /track/info` +Get track metadata. +**Query Parameters:** +- `id`: Spotify track ID + +**Response:** +```json +{ + "id": "string", + "name": "string", + "artists": [{"name": "string"}], + "album": {"name": "string"}, + "duration_ms": 180000, + "explicit": false, + "external_urls": {"spotify": "url"} +} +``` + +### Album Downloads + +#### `GET /album/download/{album_id}` +Download an entire album. +**Response:** +```json +{ + "task_id": "uuid" +} +``` + +#### `GET /album/download/cancel` +**Query Parameters:** +- `task_id`: Task ID to cancel + +#### `GET /album/info` +Get album metadata. +**Query Parameters:** +- `id`: Spotify album ID + +### Playlist Downloads + +#### `GET /playlist/download/{playlist_id}` +Download an entire playlist. +**Response:** +```json +{ + "task_id": "uuid" +} +``` + +#### `GET /playlist/download/cancel` +**Query Parameters:** +- `task_id`: Task ID to cancel + +#### `GET /playlist/info` +Get playlist metadata. +**Query Parameters:** +- `id`: Spotify playlist ID + +#### `GET /playlist/metadata` +Get detailed playlist metadata including tracks. +**Query Parameters:** +- `id`: Spotify playlist ID + +#### `GET /playlist/tracks` +Get playlist tracks. +**Query Parameters:** +- `id`: Spotify playlist ID + +### Artist Downloads + +#### `GET /artist/download/{artist_id}` +Download artist's discography. +**Query Parameters:** +- `album_type`: Comma-separated values (`album,single,compilation,appears_on`) + +**Response:** +```json +{ + "status": "complete", + "message": "Artist discography processing initiated. X albums queued.", + "queued_albums": ["task_id1", "task_id2"], + "duplicate_albums": ["existing_task_id"] +} +``` + +#### `GET /artist/download/cancel` +**Query Parameters:** +- `task_id`: Task ID to cancel + +#### `GET /artist/info` +Get artist metadata. +**Query Parameters:** +- `id`: Spotify artist ID + +## 📺 Watch Functionality + +Monitor playlists and artists for new content and automatically download updates. + +### Playlist Watching + +#### `PUT /playlist/watch/{playlist_spotify_id}` +Add playlist to watch list. +**Request:** +```json +{ + "watch_new_additions": true, + "download_existing": false +} +``` + +#### `GET /playlist/watch/{playlist_spotify_id}/status` +Get playlist watch status. + +#### `DELETE /playlist/watch/{playlist_spotify_id}` +Remove playlist from watch list. + +#### `POST /playlist/watch/{playlist_spotify_id}/tracks` +Add specific tracks to watch for a playlist. +**Request:** +```json +{ + "track_ids": ["track_id1", "track_id2"] +} +``` + +#### `DELETE /playlist/watch/{playlist_spotify_id}/tracks` +Remove specific tracks from watch. +**Request:** +```json +{ + "track_ids": ["track_id1", "track_id2"] +} +``` + +#### `GET /playlist/watch/list` +List all watched playlists. + +#### `POST /playlist/watch/trigger_check` +Manually trigger watch check for all playlists. + +#### `POST /playlist/watch/trigger_check/{playlist_spotify_id}` +Manually trigger watch check for specific playlist. + +### Artist Watching + +#### `PUT /artist/watch/{artist_spotify_id}` +Add artist to watch list. +**Request:** +```json +{ + "watch_new_releases": true, + "album_types": ["album", "single"] +} +``` + +#### `GET /artist/watch/{artist_spotify_id}/status` +Get artist watch status. + +#### `DELETE /artist/watch/{artist_spotify_id}` +Remove artist from watch list. + +#### `POST /artist/watch/{artist_spotify_id}/albums` +Add specific albums to watch for an artist. +**Request:** +```json +{ + "album_ids": ["album_id1", "album_id2"] +} +``` + +#### `DELETE /artist/watch/{artist_spotify_id}/albums` +Remove specific albums from watch. + +#### `GET /artist/watch/list` +List all watched artists. + +#### `POST /artist/watch/trigger_check` +Manually trigger watch check for all artists. + +#### `POST /artist/watch/trigger_check/{artist_spotify_id}` +Manually trigger watch check for specific artist. + +## 🔍 Search + +#### `GET /search/` +Search Spotify content. +**Query Parameters:** +- `q`: Search query (required) +- `search_type` or `type`: Content type (`track`, `album`, `artist`, `playlist`, `episode`, `show`) +- `limit`: Results limit (default: 20) +- `main`: Account context + +**Response:** +```json +{ + "items": [ + { + "id": "string", + "name": "string", + "type": "track", + "artists": [{"name": "string"}], + "external_urls": {"spotify": "url"} + } + ] +} +``` + +## 📊 Progress & Task Management + +### Task Monitoring + +#### `GET /prgs/list` +List all tasks with optional filtering. +**Query Parameters:** +- `status`: Filter by status (`pending`, `running`, `completed`, `failed`) +- `download_type`: Filter by type (`track`, `album`, `playlist`) +- `limit`: Results limit + +#### `GET /prgs/{task_id}` +Get specific task details and progress. + +#### `GET /prgs/updates` +Get task updates since last check. +**Query Parameters:** +- `since`: Timestamp to get updates since + +#### `GET /prgs/stream` +**Server-Sent Events (SSE)** endpoint for real-time progress updates. +**Response:** Continuous stream of task updates. + +### Task Control + +#### `POST /prgs/cancel/{task_id}` +Cancel a specific task. + +#### `POST /prgs/cancel/all` +Cancel all running tasks. + +#### `DELETE /prgs/delete/{task_id}` +Delete completed/failed task from history. + +## 📜 Download History + +### History Retrieval + +#### `GET /history/` +Get download history with pagination. +**Query Parameters:** +- `limit`: Max records (default: 100, max: 500) +- `offset`: Records to skip (default: 0) +- `download_type`: Filter by type (`track`, `album`, `playlist`) +- `status`: Filter by status (`completed`, `failed`, `skipped`, `in_progress`) + +**Response:** +```json +{ + "downloads": [ + { + "task_id": "uuid", + "download_type": "track", + "url": "spotify_url", + "name": "Track Name", + "artist": "Artist Name", + "status": "completed", + "created_at": "2024-01-01T00:00:00", + "completed_at": "2024-01-01T00:05:00", + "file_path": "/path/to/file.mp3" + } + ], + "pagination": { + "limit": 100, + "offset": 0, + "returned_count": 50 + } +} +``` + +#### `GET /history/{task_id}` +Get specific download history entry. + +#### `GET /history/{task_id}/children` +Get child tasks (for album/playlist downloads). + +#### `GET /history/stats` +Get download statistics. + +#### `GET /history/search` +Search download history. +**Query Parameters:** +- `q`: Search query +- `field`: Field to search (`name`, `artist`, `url`) + +#### `GET /history/recent` +Get recent downloads. +**Query Parameters:** +- `hours`: Hours to look back (default: 24) + +#### `GET /history/failed` +Get failed downloads. + +#### `POST /history/cleanup` +Clean up old history entries. +**Request:** +```json +{ + "older_than_days": 30, + "keep_failed": true +} +``` + +## ⚙️ System Configuration + +### Configuration Management + +#### `GET /config/` +Get current system configuration. + +#### `POST /config/` / `PUT /config/` +Update system configuration. +**Request:** +```json +{ + "maxConcurrentDownloads": 3, + "service": "spotify", + "fallback": true, + "spotifyQuality": "high", + "deezerQuality": "flac", + "realTime": true, + "downloadPath": "/downloads", + "fileFormat": "mp3" +} +``` + +#### `GET /config/check` +Validate current configuration. + +#### `POST /config/validate` +Validate provided configuration. + +### Watch Configuration + +#### `GET /config/watch` +Get watch system configuration. + +#### `POST /config/watch` / `PUT /config/watch` +Update watch configuration. +**Request:** +```json +{ + "enabled": true, + "check_interval": 3600, + "max_concurrent_checks": 2, + "retry_failed_after": 1800 +} +``` + +#### `POST /config/watch/validate` +Validate watch configuration. + +## 🔑 Credentials Management + +### Service Credentials + +#### `GET /credentials/{service}` +List credentials for service (`spotify` or `deezer`). + +#### `GET /credentials/{service}/{name}` +Get specific credential set. + +#### `POST /credentials/{service}/{name}` +Create new credential set. +**Request:** +```json +{ + "client_id": "string", + "client_secret": "string" +} +``` + +#### `PUT /credentials/{service}/{name}` +Update credential set. + +#### `DELETE /credentials/{service}/{name}` +Delete credential set. + +#### `GET /credentials/all/{service}` +Get all credentials for service. + +### Spotify API Configuration + +#### `GET /credentials/spotify_api_config` +Get Spotify API configuration. + +#### `PUT /credentials/spotify_api_config` +Update Spotify API configuration. + +#### `GET /credentials/markets` +Get available Spotify markets. + +## 🚨 Error Handling + +### HTTP Status Codes +- **200**: Success +- **202**: Accepted (async operations) +- **400**: Bad Request (validation errors) +- **401**: Unauthorized (auth required) +- **403**: Forbidden (insufficient permissions) +- **404**: Not Found +- **409**: Conflict (duplicate downloads) +- **500**: Internal Server Error + +### Error Response Format +```json +{ + "error": "Error description", + "details": "Additional details", + "traceback": "Stack trace (dev mode)" +} +``` + +### Common Error Scenarios +- **Duplicate Downloads**: 409 status with existing task ID +- **Missing Spotify Metadata**: 404 when track/album/playlist not found +- **Invalid Credentials**: Authentication errors when service credentials are wrong +- **Rate Limiting**: Temporary failures when hitting Spotify API limits + +## 💡 Usage Examples + +### Download a Track +```bash +curl -X GET "http://localhost:7171/api/track/download/4iV5W9uYEdYUVa79Axb7Rh" \ + -H "Authorization: Bearer your-jwt-token" +``` + +### Search for Music +```bash +curl -X GET "http://localhost:7171/api/search/?q=bohemian%20rhapsody&search_type=track" \ + -H "Authorization: Bearer your-jwt-token" +``` + +### Monitor Progress with SSE +```javascript +const eventSource = new EventSource('/api/prgs/stream'); +eventSource.onmessage = function(event) { + const data = JSON.parse(event.data); + console.log('Progress update:', data); +}; +``` + +### Add Playlist to Watch +```bash +curl -X PUT "http://localhost:7171/api/playlist/watch/37i9dQZF1DXcBWIGoYBM5M" \ + -H "Authorization: Bearer your-jwt-token" \ + -H "Content-Type: application/json" \ + -d '{"watch_new_additions": true, "download_existing": false}' +``` + +## 🔧 Development Notes + +### Authentication Middleware +- Routes are protected by `require_auth_from_state` dependency +- Admin routes use `require_admin_from_state` dependency +- When auth is disabled, system returns mock admin user + +### Task System +- All downloads are async using Celery +- Progress tracked via Redis +- Real-time updates via SSE +- Task cancellation supported + +### File Structure +- Downloads stored in configurable directory +- Metadata stored in JSON files +- History persisted in database + +### Rate Limiting +- Spotify API rate limits respected +- Concurrent download limits configurable +- Retry logic for failed requests + +--- + +*This documentation covers all endpoints discovered in the Spotizerr routes directory. The API is designed for high-throughput music downloading with comprehensive monitoring and management capabilities.* \ No newline at end of file