📡 Documentation API REST — Webradio Manager v2.14.0
Version : 2.8.0 | Base URL : http://localhost:5000 | Préfixe : /api/v1 | Format : JSON
Table des matières
- Authentification
- Utilisateurs
- Streaming
- Decks
- Mixer
- Playlist
- Playlists nommées
- Bibliothèque
- Scheduler
- Spots
- TTS (Text-to-Speech)
- Recherche
- Spotify
- Deezer
- YouTube
- Apple Music
- SoundCloud
- Scrobbler (Last.fm)
- Icecast
- Paramètres
- Préférences utilisateur (sessions)
- Upload
- Système de fichiers
- WebDAV
- Synchronisation réseau (FTP / SFTP / Samba)
- WebSocket
- Monitoring & Métriques
- HLS
- Modèles de données
- Codes d'erreur
- Versionnage & Rate Limiting
- Favoris
- Historique d'écoute
- Webradios et radios personnalisées
- Pistes en ligne, titre en cours, loudness
- Sauvegardes
- Scan asynchrone
- Planification de radios
1. Authentification
Préfixe : /api/v1/auth
Le système d'authentification utilise JWT (access + refresh tokens). Un compte admin par défaut est créé au premier démarrage : admin / admin.
POST /auth/login
Connexion utilisateur (limité à 5 tentatives/minute).
Body :
{
"username": "admin",
"password": "admin"
}
Champs optionnels pour la protection CAPTCHA :
- captcha_token — token CAPTCHA (si activé)
- session_id — ID de session CAPTCHA
- captcha_answer — réponse au défi mathématique
Réponse (200) :
{
"message": "Login successful",
"token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"role": "admin",
"is_active": true
}
}
Erreurs :
- 400 — VALIDATION_ERROR / CAPTCHA_FAILED / MISSING_CREDENTIALS
- 401 — INVALID_CREDENTIALS
- 403 — ACCOUNT_DISABLED
- 429 — TOO_MANY_ATTEMPTS (header Retry-After: 300)
POST /auth/logout
Déconnexion (invalide le refresh token).
Body : {} (vide)
POST /auth/refresh
Rafraîchit le token d'accès JWT.
Body :
{
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}
Réponse (200) :
{
"token": "eyJhbGciOiJIUzI1NiIs..."
}
GET /auth/me
Retourne les informations de l'utilisateur connecté.
Headers : Authorization: Bearer <token>
POST /auth/forgot-password
Demande de réinitialisation de mot de passe (envoie un email).
Body :
{
"email": "user@example.com"
}
POST /auth/reset-password
Réinitialise le mot de passe avec un token.
Body :
{
"token": "token-de-reinitialisation",
"new_password": "nouveau_mot_de_passe"
}
GET /auth/health
Health check spécifique au module auth.
2. Utilisateurs
Préfixe : /api/v1/users
Gestion des utilisateurs (admin uniquement).
GET /users
Liste tous les utilisateurs.
Headers : Authorization: Bearer <admin_token>
Réponse (200) :
{
"users": [
{"id": 1, "username": "admin", "email": "admin@example.com", "role": "admin", "is_active": true}
]
}
GET /users/{user_id}
Récupère un utilisateur par son ID.
POST /users
Crée un nouvel utilisateur.
Body :
{
"username": "dj1",
"email": "dj1@example.com",
"password": "motdepasse",
"role": "dj"
}
Rôles valides : admin, dj, viewer
PUT /users/{user_id}
Met à jour un utilisateur.
Body : champs optionnels role, email, is_active
DELETE /users/{user_id}
Supprime un utilisateur. Impossible de se supprimer soi-même.
POST /users/{user_id}/reset-password
Réinitialise le mot de passe d'un utilisateur (admin).
Body : {"new_password": "nouveau_mdp"}
GET /users/{user_id}/permissions
Liste les permissions d'un utilisateur.
POST /users/{user_id}/permissions
Ajoute une permission.
Body : {"permission": "library.edit"}
DELETE /users/{user_id}/permissions/{perm_id}
Supprime une permission.
POST /users/register
Inscription publique (avec validation).
Body :
{
"username": "nouveau",
"email": "nouveau@example.com",
"password": "motdepasse"
}
GET /auth/audit-logs
Journal d'audit des connexions (admin). Query : page, per_page, action, user_id
3. Streaming
Préfixe : /api/v1/streaming
GET /streaming/status
Retourne l'état actuel du stream.
Réponse (200) :
{
"is_live": true,
"bitrate": 128,
"format": "mp3",
"listeners": 5,
"protocol": "icecast",
"pid": 12345,
"uptime": "01:23:45",
"icecast_host": "localhost",
"icecast_port": 8000
}
POST /streaming/start
Démarre le flux Icecast via FFmpeg.
Query : ?protocol=icecast|shoutcast|rtmp (optionnel)
Body (optionnel) : config override JSON
Réponse (200) :
{"success": true, "message": "Stream started", "pid": 12345}
POST /streaming/stop
Arrête le flux.
GET /streaming/protocol
Retourne le protocole de streaming actuel et les options disponibles.
POST /streaming/protocol
Change le protocole de streaming.
Body : {"protocol": "icecast"}
GET /streaming/hls
Retourne les informations de streaming HLS (URL de la playlist m3u8).
GET /streaming/mounts
Liste les points de montage Icecast configurés.
POST /streaming/mounts
Met à jour les points de montage.
4. Decks
Préfixe : /api/v1/decks
GET /decks
Retourne l'état des deux decks A et B.
Réponse (200) :
{
"decks": {
"A": {
"deck": "A",
"current_track_id": "abc123",
"is_playing": true,
"is_paused": false,
"volume": 0.8,
"speed": 1.0,
"eq_low": 0.0,
"eq_mid": 0.0,
"eq_high": 0.0,
"cue_position": 30.5,
"loop_enabled": false,
"playback_position": 45.2
},
"B": { "deck": "B", "is_playing": false, "volume": 0.8 }
}
}
GET /decks/{A|B}
Retourne l'état d'un deck spécifique.
POST /decks/{A|B}/play
Démarre la lecture sur le deck.
POST /decks/{A|B}/pause
Met en pause le deck.
POST /decks/{A|B}/stop
Arrête la lecture.
POST /decks/{A|B}/clear
Vide le deck (décharge la piste).
POST /decks/{A|B}/cue
Définit un point cue.
Body : {"position": 30.5}
POST /decks/{A|B}/cue/jump
Saute au point cue.
POST /decks/{A|B}/loop
Définit une boucle.
Body :
{
"enabled": true,
"start": 10.0,
"end": 45.0
}
POST /decks/{A|B}/volume
Règle le volume (0.0 à 1.0).
Body : {"volume": 0.75}
POST /decks/{A|B}/eq
Règle l'égaliseur 3 bandes (-1.0 à 1.0).
Body :
{
"low": 0.3,
"mid": 0.0,
"high": -0.2
}
POST /decks/{A|B}/speed
Règle la vitesse/pitch (0.5 à 2.0).
Body : {"speed": 1.2}
POST /decks/{A|B}/load
Charge une piste sur le deck.
Body : {"track_id": "abc123"}
5. Mixer
Préfixe : /api/v1/mixer
GET /mixer
Retourne l'état complet du mixer.
Réponse (200) :
{
"crossfader": {"position": 0.5},
"master_volume": 0.8
}
POST /mixer/crossfader
Définit la position du crossfader. - 0.0 = 100% Deck A - 0.5 = 50% A + 50% B (centre) - 1.0 = 100% Deck B
Body : {"position": 0.3}
POST /mixer/master
Définit le volume master (0.0 à 1.0).
Body : {"volume": 0.8}
6. Playlist
Préfixe : /api/v1/playlist
GET /playlist
Liste la playlist (file d'attente active) avec pagination.
Query : page (défaut 1), per_page (défaut 50, max 200)
POST /playlist
Ajoute une piste à la playlist.
Body : {"track_id": "abc123"}
Options :
- position (int) — insérer à une position spécifique
- next (bool) — insérer en tête de file
DELETE /playlist/{track_id}
Retire une piste de la playlist.
POST /playlist/reorder
Réordonne la playlist.
Body : {"track_ids": [3, 1, 2]}
DELETE /playlist
Vide entièrement la playlist.
GET /playlist/next
Retourne la prochaine piste de la file d'attente.
7. Playlists nommées
Préfixe : /api/v1/playlists
GET /playlists
Liste toutes les playlists nommées.
POST /playlists
Crée une nouvelle playlist.
Body :
{
"name": "Ma playlist jazz",
"description": "Mes titres jazz préférés"
}
GET /playlists/{playlist_id}
Récupère les détails d'une playlist.
PUT /playlists/{playlist_id}
Met à jour le nom/description d'une playlist.
DELETE /playlists/{playlist_id}
Supprime une playlist.
GET /playlists/{playlist_id}/tracks
Liste les pistes d'une playlist.
POST /playlists/{playlist_id}/tracks
Ajoute des pistes à une playlist.
Body :
{
"track_ids": ["abc123", "def456"],
"position": 0
}
DELETE /playlists/{playlist_id}/tracks/{track_id}
Retire une piste d'une playlist.
DELETE /playlists/{playlist_id}/tracks
Retire plusieurs pistes d'une playlist (bulk).
Body : {"track_ids": [1, 2, 3]}
PUT /playlists/{playlist_id}/reorder
Réordonne les pistes d'une playlist.
Body : {"track_ids": [3, 1, 2]}
GET /playlists/{playlist_id}/export
Exporte une playlist au format M3U.
8. Bibliothèque
Préfixe : /api/v1/library
GET /library/tracks
Liste les pistes avec pagination, recherche, tri et filtres.
Query params :
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
page |
int | 1 | Numéro de page |
per_page |
int | 50 | Éléments par page (max 200) |
search |
string | — | Recherche par titre/artiste/album |
sort |
string | track_name |
Champ de tri |
sort_dir |
string | asc |
asc ou desc |
genre |
string | — | Filtrer par genre |
source |
string | — | Filtrer par source (local/spotify/deezer/youtube) |
Réponse (200) :
{
"tracks": [...],
"total": 150,
"page": 1,
"per_page": 50,
"total_pages": 3
}
POST /library/tracks
Ajoute une piste manuellement à la bibliothèque.
Body :
{
"track_name": "Mon titre",
"artist": "Mon artiste",
"album": "Mon album",
"genre": "Jazz",
"file_path": "/path/to/file.mp3"
}
GET /library/tracks/{track_id}
Récupère une piste par son track_id (chaîne).
PUT /library/tracks/{track_id}
Édite les métadonnées d'une piste.
Body : champs optionnels track_name, artist, album, genre, bpm, year, rating
DELETE /library/tracks/{track_id}
Supprime une piste de la bibliothèque.
POST /library/scan
Scanne un dossier pour importer des fichiers audio.
Body :
{
"path": "/chemin/vers/musique"
}
Si path est omis, utilise library.scan_paths ou library.path de la config.
Réponse (200) :
{
"message": "Scan complete",
"files_found": 120,
"tracks_added": 115,
"tracks_updated": 5,
"errors": [],
"scanned_path": "/home/user/Music"
}
Formats supportés : MP3, OGG, FLAC, WAV, M4A
GET /library/tracks/{track_id}/stream
Stream un fichier audio (pour lecture depuis la bibliothèque).
GET /library/tracks/{track_id}/cover
Sert la pochette d'une piste (image). Réponse (200) : image (JPG/PNG/WebP). 404 si pas de pochette.
PUT /library/tracks/{track_id}/cover
Définit la pochette d'une piste. Authentification requise (admin).
Option A — Upload multipart (fichier image) :
Content-Type: multipart/form-data
Champ : file (image/jpeg, image/png, image/webp, image/gif)
Option B — URL :
{ "cover_url": "https://example.com/cover.jpg" }
Réponse (200) :
{ "message": "Cover updated", "cover": "b6616a64-....png" }
POST /library/tracks/{track_id}/enrich
Enrichit les métadonnées d'une piste via MusicBrainz + 6 sources.
Réponse (200) :
{
"message": "Enrichment complete",
"metadata": {"musicbrainz": {...}, "cover_url": "..."}
}
POST /library/tracks/enrich-bulk
Enrichissement en masse de plusieurs pistes.
Body : {"track_ids": ["abc123", "def456"]}
POST /library/enrich-async
Lance un job d'enrichissement asynchrone.
Body : {"track_ids": ["abc123", "def456"]}
Réponse (201) : {"job_id": "uuid", "message": "Enrichment job started"}
GET /library/enrich-job/{job_id}
Vérifie l'état d'un job d'enrichissement.
Réponse (200) :
{
"job_id": "uuid",
"status": "running",
"progress": 3,
"total": 10
}
GET /library/tracks/{track_id}/similar
Trouve des pistes similaires (basé sur le genre et les tags).
GET /library/tracks/{track_id}/cover
Retourne la pochette d'album d'une piste.
9. Scheduler
Préfixe : /api/v1/scheduler
GET /scheduler/events
Liste les événements planifiés.
Query params :
- enabled_only (bool) — filtrer les événements activés uniquement
- recurrence (string) — filtrer par type de récurrence
- from_time (ISO datetime) — filtrer à partir d'une date
Réponse (200) :
{
"events": [...],
"count": 5
}
POST /scheduler/events
Crée un événement planifié.
Body :
{
"name": "Spot météo matin",
"start_time": "2026-07-10T08:00:00",
"track_id": 1,
"spot_id": null,
"recurrence": "daily",
"priority": 1,
"enabled": true
}
Récurrences valides : none, every_15min, every_30min, hourly, daily, weekly
Soit track_id soit spot_id doit être fourni (pas les deux).
PUT /scheduler/events/{event_id}
Met à jour un événement.
DELETE /scheduler/events/{event_id}
Supprime un événement.
GET /scheduler/next
Retourne le prochain événement planifié.
10. Spots
Préfixe : /api/v1/spots
GET /spots
Liste les spots générés (paginé, filtrable par type).
Query params : type (weather/promo/nextup/traffic/generic), page, per_page
Réponse (200) :
{
"spots": [...],
"count": 10,
"total": 50,
"page": 1,
"per_page": 50,
"pages": 1
}
POST /spots/generate
Génère un spot audio via TTS.
Body :
{
"type": "promo",
"text": "Bienvenue sur notre webradio !",
"city": "Paris",
"engine": "gtts"
}
GET /spots/{spot_id}
Détails d'un spot.
DELETE /spots/{spot_id}
Supprime un spot et son fichier audio associé.
POST /spots/bulk-delete
Supprime plusieurs spots en une fois.
Body : {"spot_ids": [1, 2, 3]}
POST /spots/weather
Génère un spot météo. Utilise Open-Meteo (gratuit).
Body :
{
"city": "Paris",
"engine": "gtts"
}
POST /spots/nextup
Génère une annonce « next up » (annonce du titre suivant).
Body :
{
"track_name": "Bohemian Rhapsody",
"artist": "Queen"
}
Types de spots disponibles :
| Type | Description |
|---|---|
weather |
Bulletin météo (Open-Meteo, gratuit) |
nextup |
Annonce du titre suivant |
promo |
Spot promotionnel personnalisé |
traffic |
Information trafic |
generic |
Spot texte libre |
11. TTS (Text-to-Speech)
Préfixe : /api/v1/tts
GET /tts/voices
Liste les voix TTS disponibles.
Réponse (200) :
{
"voices": ["fr-FR", "en-US", "es-ES", ...],
"engine": "gtts"
}
Moteurs supportés :
| Moteur | Type | Format | Prérequis |
|---|---|---|---|
gTTS |
Online | MP3 | Connexion Internet |
espeak |
Offline | WAV | Binaire espeak installé |
fallback_silent |
Offline | WAV | Toujours disponible |
12. Recherche
Préfixe : /api/v1
GET /search
Recherche multi-plateformes (local + Spotify + Deezer + YouTube).
Query params :
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
q |
string | requis | Terme de recherche |
source |
string | all |
all, local, spotify, deezer, youtube |
limit |
int | 10 | Nombre max de résultats (max 10) |
Réponse (200) :
{
"local": [...],
"spotify": [...],
"deezer": [...],
"youtube": [...]
}
13. Spotify
Préfixe : /api/v1/spotify
GET /spotify/search
Recherche sur Spotify.
Query : q (requis), limit (défaut 5)
GET /spotify/search/artists
Recherche d'artistes sur Spotify.
Query : q (requis), limit (défaut 5)
GET /spotify/track/{track_id}
Récupère les infos d'une piste Spotify.
GET /spotify/recommendations
Recommandations basées sur des seeds.
Query : seed_artists, seed_tracks, seed_genres, limit
GET /spotify/playlists
Liste les playlists Spotify de l'utilisateur.
GET /spotify/playlist/{playlist_id}
Récupère les détails d'une playlist Spotify.
GET /spotify/playlist/{playlist_id}/tracks
Liste les pistes d'une playlist Spotify.
GET /spotify/player
Retourne la piste en cours de lecture sur Spotify.
POST /spotify/player/play
Lance la lecture sur l'appareil Spotify connecté.
Body : {"device_id": "...", "track_uri": "spotify:track:..."}
POST /spotify/player/pause
Met en pause le player Spotify.
POST /spotify/player/next
Piste suivante sur Spotify.
POST /spotify/player/previous
Piste précédente sur Spotify.
POST /spotify/player/volume
Règle le volume Spotify.
Body : {"volume_percent": 50}
14. Deezer
Préfixe : /api/v1/deezer
GET /deezer/search
Recherche sur Deezer.
Query : q (requis), limit (défaut 5)
GET /deezer/playlist/{playlist_id}
Récupère les détails d'une playlist Deezer.
GET /deezer/playlist/{playlist_id}/tracks
Liste les pistes d'une playlist Deezer.
GET /deezer/user/{user_id}/playlists
Liste les playlists d'un utilisateur Deezer.
GET /deezer/me
Retourne les infos de l'utilisateur Deezer connecté.
GET /deezer/me/playlists
Liste les playlists de l'utilisateur connecté.
GET /deezer/oauth/url
Génère l'URL d'autorisation OAuth Deezer.
GET /deezer/oauth/callback
Callback OAuth Deezer.
POST /deezer/login
Connexion via jeton ARL (méthode alternative).
Body : {"arl": "cookie_arl_de_192_caracteres"}
POST /deezer/login/arl
Connexion par ARL (identique à /login).
POST /deezer/premium-check
Vérifie si le compte Deezer est Premium.
POST /deezer/import
Importe une piste Deezer dans la bibliothèque.
Body :
{
"track_id": "deezer_track_id",
"track_name": "Titre",
"artist": "Artiste",
"album": "Album"
}
15. YouTube
Préfixe : /api/v1/youtube
GET /youtube/search
Recherche sur YouTube.
Query : q (requis)
GET /youtube/track/{video_id}
Récupère les infos d'une vidéo YouTube.
POST /youtube/preview
Prévisualise l'import d'une vidéo YouTube (avec yt-dlp).
Body : {"url": "https://youtube.com/watch?v=..."}
GET /youtube/playlist
Récupère les infos d'une playlist YouTube.
Query : url (requis)
POST /youtube/import
Importe une piste ou playlist YouTube dans la bibliothèque.
Body :
{
"url": "https://youtube.com/watch?v=...",
"track_name": "Titre",
"artist": "Artiste",
"album": "Album"
}
GET /youtube/user-playlists
Liste les playlists YouTube de l'utilisateur.
GET /youtube/status
Statut du service YouTube (yt-dlp).
16. Apple Music
Préfixe : /api/v1/apple-music
GET /apple-music/search
Recherche sur Apple Music.
Query : q (requis), limit (défaut 5)
GET /apple-music/track/{track_id}
Récupère les infos d'une piste Apple Music.
GET /apple-music/playlists
Liste les playlists Apple Music de l'utilisateur.
GET /apple-music/playlist/{playlist_id}/tracks
Liste les pistes d'une playlist Apple Music.
GET /apple-music/auth-url
Génère l'URL d'autorisation Apple Music.
POST /apple-music/callback
Callback d'autorisation Apple Music.
POST /apple-music/add
Importe une piste Apple Music dans la bibliothèque.
17. SoundCloud
Préfixe : /api/v1/soundcloud
GET /soundcloud/search
Recherche sur SoundCloud.
Query : q (requis), limit (défaut 5)
GET /soundcloud/track/{track_id}
Récupère les infos d'une piste SoundCloud.
POST /soundcloud/resolve
Résout une URL SoundCloud en piste.
Body : {"url": "https://soundcloud.com/..."}
GET /soundcloud/playlists
Liste les playlists SoundCloud.
GET /soundcloud/playlist/{playlist_id}/tracks
Liste les pistes d'une playlist SoundCloud.
GET /soundcloud/auth-url
Génère l'URL d'autorisation SoundCloud.
POST /soundcloud/callback
Callback d'autorisation SoundCloud.
POST /soundcloud/add
Importe une piste SoundCloud dans la bibliothèque.
18. Scrobbler (Last.fm)
Préfixe : /api/v1/scrobbler
POST /scrobbler/scrobble
Scrobble une piste sur Last.fm.
Body :
{
"track": "Bohemian Rhapsody",
"artist": "Queen",
"album": "A Night at the Opera",
"timestamp": 1719500000,
"duration": 355
}
POST /scrobbler/nowplaying
Met à jour « Now Playing » sur Last.fm.
Body : {"track": "...", "artist": "...", "album": "..."}
GET /scrobbler/status
Vérifie si Last.fm est configuré.
Réponse (200) :
{
"configured": true,
"username": "mon_username",
"scrobble_enabled": true
}
GET /scrobbler/track
Récupère les infos d'une piste via Last.fm.
Query : track (requis), artist (optionnel)
GET /scrobbler/search
Recherche Last.fm.
Query : q (requis), limit (défaut 10, max 50)
GET /scrobbler/enrich
Enrichit les métadonnées d'une piste via Last.fm.
Query : track (requis), artist (optionnel)
POST /scrobbler/scrobble/batch
Scrobble plusieurs pistes en une fois (max 50).
Body :
{
"scrobbles": [
{"track": "Titre 1", "artist": "Artiste 1"},
{"track": "Titre 2", "artist": "Artiste 2"}
]
}
GET /scrobbler/config
Récupère la configuration Last.fm.
POST /scrobbler/config
Met à jour la configuration Last.fm.
Body : {"username": "...", "api_key": "...", "session_key": "...", "scrobble_enabled": true}
GET /scrobbler/history
Historique des scrobbles (paginé).
Query : page, per_page
POST /scrobbler/clear-history
Efface l'historique des scrobbles.
19. Icecast
Préfixe : /api/v1/icecast
GET /icecast/status
Statut d'Icecast (installé, en cours d'exécution, port).
POST /icecast/install
Installe Icecast automatiquement.
POST /icecast/start
Démarre le serveur Icecast.
POST /icecast/stop
Arrête le serveur Icecast.
POST /icecast/record/start
Démarre l'enregistrement du stream.
Body : {"format": "mp3"}
POST /icecast/record/stop
Arrête l'enregistrement.
GET /icecast/record/status
Statut de l'enregistrement.
20. Paramètres
Préfixe : /api/v1
GET /health
Health check pour monitoring/Docker.
Réponse (200) :
{"status": "ok", "service": "webradio-manager"}
GET /health/detailed
Health check détaillé (uptime, mémoire, DB, WebSocket, scheduler).
GET /settings
Liste tous les paramètres.
Réponse (200) :
{
"settings": {
"weather.city": "Paris",
"tts.engine": "gtts",
"library.path": "/home/user/Music"
},
"count": 15
}
Les clés sensibles (
*_api_key,*_token,*_secret,password,*_arl) sont déchiffrées automatiquement.
GET /settings/{key}
Récupère un paramètre par sa clé.
PUT /settings/{key}
Met à jour un paramètre.
Body : {"value": "nouvelle_valeur"}
POST /settings
Mise à jour en masse (recommandée).
Body :
{
"weather.city": "Lyon",
"tts.engine": "gtts",
"library.path": "/home/user/Music",
"icecast.port": 8000
}
GET /openapi.json
Spécification OpenAPI 3.0 complète.
21. Préférences utilisateur (sessions)
Préfixe : /api/v1/preferences
Préférences propres à chaque utilisateur (session) : thème, langue, configuration de l'espace de travail. Indépendantes des settings globales de l'application.
GET /preferences
Retourne toutes les préférences de l'utilisateur connecté.
Réponse (200) :
{ "preferences": { "theme": "dark", "lang": "fr" }, "count": 2 }
PUT /preferences/
Définit une préférence.
Body : { "value": "dark" }
Réponse (200) :
{ "key": "theme", "value": "dark" }
POST /preferences
Mise à jour groupée.
Body : { "theme": "dark", "lang": "fr" }
Réponse (200) :
{ "message": "Preferences updated", "updated": ["theme", "lang"], "count": 2 }
Authentification requise (JWT) — chaque compte a ses propres préférences.
22. Upload
Préfixe : /api/v1
POST /upload
Upload un fichier audio via multipart/form-data.
Content-Type : multipart/form-data
Champ : file (fichier binaire audio)
Formats acceptés : MP3, OGG, FLAC, WAV, M4A
Réponse (201) :
{
"message": "Upload successful",
"track": {
"track_id": "abc123",
"track_name": "Mon titre",
"artist": "Artiste",
"file_path": "/path/to/file.mp3"
}
}
23. Système de fichiers
Préfixe : /api/v1/filesystem
GET /filesystem/roots
Retourne les racines de fichiers accessibles (drives sous Windows, / sous Linux).
GET /filesystem/browse
Parcourt un dossier.
Query : path (chemin du dossier à explorer)
24. WebDAV
Préfixe : /api/v1/webdav
GET /webdav/status
État de la connexion WebDAV. Retourne configured, url, remote_path.
POST /webdav/sync
Synchronise la bibliothèque vers le serveur WebDAV.
Body : vide (utilise la configuration des Paramètres : URL, utilisateur, mot de passe, dossier distant webdav_remote_path)
Réponse (200) :
{ "message": "Sync complete", "files_found": 12, "synced": 5, "errors": [] }
POST /webdav/test
Teste la connexion WebDAV (PROPFIND sur l'URL + dossier distant configuré).
Body : vide (configuration depuis les Paramètres)
Réponse (200) :
{ "success": true, "message": "Connexion WebDAV OK", "info": { "url": "http://nas:5005/music/", "status": 207 } }
Échec possible : success: false avec message explicite (401 auth refusée, 404 dossier introuvable, etc.)
25. Synchronisation réseau (FTP / SFTP / Samba)
Préfixe : /api/v1
GET /ftp/status
État de la configuration FTP/SFTP. Retourne configured, host, port, protocol, remote_path, username_set.
POST /ftp/sync
Upload de la bibliothèque vers un serveur FTP/SFTP.
Body : vide (configuration depuis les Paramètres : ftp_host, ftp_port, ftp_protocol (ftp|sftp), ftp_username, ftp_password, ftp_remote_path, ftp_passive)
Réponse (200) :
{ "message": "FTP sync complete", "files_found": 12, "synced": 5, "errors": [] }
GET /samba/status
État de la configuration Samba. Retourne configured, server, share, remote_path, port, username_set.
POST /samba/sync
Upload de la bibliothèque vers un partage SMB.
Body : vide (configuration depuis les Paramètres : samba_server, samba_share, samba_port, samba_username, samba_password, samba_remote_path)
Réponse (200) :
{ "message": "Samba sync complete", "files_found": 12, "synced": 5, "errors": [] }
POST /ftp/test
Teste la connexion FTP/SFTP (login + accès au dossier distant).
Body : vide (configuration depuis les Paramètres)
Réponse (200) :
{ "success": true, "message": "Connexion FTP OK", "info": { "host": "nas", "port": 21, "protocol": "ftp", "cwd": "/", "remote_path_ok": true } }
POST /samba/test
Teste la connexion Samba/SMB (connect + list du partage + dossier distant).
Body : vide (configuration depuis les Paramètres)
Réponse (200) :
{ "success": true, "message": "Connexion Samba OK", "info": { "server": "192.168.0.10", "share": "music", "share_ok": true, "remote_path_ok": true } }
26. WebSocket
Préfixe : /socket.io
Connexion SocketIO pour les événements temps réel.
Endpoint REST associé : /api/v1/ws
GET /ws/status
Statut de la connexion WebSocket.
GET /ws/clients
Nombre de clients connectés.
POST /ws/broadcast
Envoie un événement personnalisé à tous les clients.
Body :
{
"event": "custom_event",
"data": {"message": "Bonjour à tous !"}
}
GET /ws/health
Health check WebSocket.
Événements temps réel :
| Événement | Direction | Description |
|---|---|---|
deck_update |
Serveur → Client | Changement d'état d'un deck |
mixer_update |
Serveur → Client | Changement crossfader/volume |
stream_status |
Serveur → Client | Statut du flux Icecast |
playlist_update |
Serveur → Client | Modification de la playlist |
now_playing |
Serveur → Client | Changement de piste en cours |
enrich:progress |
Serveur → Client | Progression enrichissement |
enrich:done |
Serveur → Client | Enrichissement terminé |
27. Monitoring & Métriques
Préfixe : /api/v1
GET /monitoring/dashboard
Tableau de bord complet (uptime, streams, DB, WebSocket, CPU/RAM).
GET /monitoring/system
Métriques système (CPU, RAM, disque).
GET /monitoring/streams
Métriques des flux (bitrate, listeners, uptime).
GET /monitoring/db
Santé de la base de données.
GET /monitoring/ws
Métriques WebSocket (clients connectés, messages).
GET /metrics
Métriques au format Prometheus.
28. HLS
Préfixe : /api/v1/hls
GET /hls/radio.m3u8
Playlist HLS principale (m3u8).
GET /hls/segment_{num}.ts
Segment HLS individuel.
29. Modèles de données
Track
{
"id": 1,
"track_id": "abc123def456",
"track_name": "Bohemian Rhapsody",
"artist": "Queen",
"album": "A Night at the Opera",
"genre": "Rock",
"duration": 355,
"file_path": "/home/user/Music/queen.mp3",
"source_platform": "local",
"track_order": 1,
"status": "queued",
"bpm": 72.0,
"rating": 5,
"year": 1975,
"metadata_json": {
"musicbrainz": {
"cover_url": "https://coverartarchive.org/...",
"year": 1975,
"tags": ["rock", "classic rock"]
}
},
"created_at": "2026-06-27T10:00:00"
}
Statuts : pending, queued, playing, active
Sources : local, spotify, deezer, youtube, apple_music, soundcloud
Spot
{
"id": 1,
"spot_type": "weather",
"city": "Paris",
"generated_text": "Météo pour Paris : 22°C, ensoleillé...",
"audio_path": "/tmp/webradio/spot_abc123.mp3",
"duration": 15.0,
"status": "ready",
"scheduled_at": null,
"created_at": "2026-06-27T10:00:00"
}
Types : weather, promo, nextup, traffic, generic
Statuts : scheduled, ready, played
Schedule
{
"id": 1,
"name": "Spot météo matin",
"track_id": null,
"spot_id": 1,
"start_time": "2026-07-10T08:00:00",
"end_time": null,
"recurrence": "daily",
"priority": 1,
"enabled": true
}
DeckState
{
"deck": "A",
"current_track_id": "abc123",
"is_playing": true,
"is_paused": false,
"volume": 0.8,
"speed": 1.0,
"loop_enabled": false,
"loop_start": null,
"loop_end": null,
"cue_position": 30.5,
"eq_low": 0.0,
"eq_mid": 0.0,
"eq_high": 0.0,
"playback_position": 45.2,
"updated_at": "2026-06-27T10:05:00"
}
User
{
"id": 1,
"username": "admin",
"email": "admin@example.com",
"role": "admin",
"is_active": true,
"last_login": "2026-07-10T08:00:00",
"created_at": "2026-06-27T10:00:00"
}
Rôles : admin, dj, viewer
30. Codes d'erreur
| Code | Signification | Causes fréquentes |
|---|---|---|
| 200 | Succès | Opération réussie |
| 201 | Créé | Ressource créée (POST) |
| 400 | Requête invalide | Paramètres manquants ou invalides |
| 401 | Non authentifié | Token manquant ou expiré |
| 403 | Interdit | Rôle insuffisant |
| 404 | Non trouvé | Ressource inexistante |
| 409 | Conflit | Ressource déjà existante |
| 429 | Trop de requêtes | Rate limit dépassé |
| 500 | Erreur serveur | Exception non gérée |
| 502 | Bad Gateway | Échec service externe (TTS) |
| 503 | Service indisponible | Service externe down |
Format d'erreur standard
{
"error": "Description de l'erreur",
"details": [{"field": "...", "message": "..."}]
}
31. Versionnage & Rate Limiting
Versionnage
L'API supporte le versionnage via le header Accept-Version :
- Accept-Version: v1 — API v1 (préfixe /api/v1)
- Accept-Version: v2 — API v2
- Versions inconnues → 400
Rate Limiting
- Global : 600 requêtes/minute
- Login : 5 tentatives/minute
- MusicBrainz : 1 requête/seconde avec retry sur 403
- Discogs/Last.fm : Backoff exponentiel
Swagger UI
- Swagger UI :
/static/swagger.html - OpenAPI JSON :
/api/v1/openapi.json
Dernière mise à jour : 2026-08-10
Version API : v1 (préfixe /api/v1) + v2
Compatibilité : Webradio Manager v2.0.0 à v2.8.0
Sauf mention contraire, ces routes exigent
Authorization: Bearer <token>.
32. Favoris
| Méthode | Route | Description |
|---|---|---|
| GET | /favorites |
Liste {favorites: [...]} |
| POST | /favorites |
Ajoute : {track_id} (piste existante) ou {source, external_id, title, artist?, album?, duration?, cover_url?, preview_url?} (piste en ligne créée à la volée). Réponse 201, ou 200 + already_favorite |
| DELETE | /favorites/<track_id> |
Retire (track_id = identifiant public, ex. radio:abc) |
33. Historique d'écoute
| Méthode | Route | Description |
|---|---|---|
| POST | /history |
{track_id} — enregistre un démarrage de lecture (doublon < 30 s ignoré : recorded:false) |
| GET | /history?limit=30 |
Pistes récentes dédoublonnées |
| GET | /history/stats |
{total_plays_7d, top: [{track_id, track_name, artist, plays, ...}]} |
34. Webradios et radios personnalisées
| Méthode | Route | Description |
|---|---|---|
| GET | /radio/search?q=&country=&limit=&custom=1 |
Radios radio-browser.info + radios perso en tête ; custom=1 : radios perso seules (public, sans token) |
| GET | /radio/status |
Disponibilité |
| GET | /radio/custom |
Liste des radios perso |
| POST | /radio/custom |
{name, url, logo_url?} |
| PUT | /radio/custom/<id> |
Modifie name, url, logo_url |
| DELETE | /radio/custom/<id> |
Supprime |
| POST | /radio/custom/import-csv |
Multipart file (colonnes name,url,logo ; , ou ;) → {added, skipped} (URL déjà présentes ignorées) |
| GET | /radio/custom/export |
CSV (;, BOM UTF-8) |
35. Pistes en ligne, titre en cours, loudness
| Méthode | Route | Description |
|---|---|---|
| POST | /playlists/<id>/tracks/online |
Ajoute une piste en ligne (source : youtube, radio, …) ; réponse {track} |
| GET | /library/tracks/<track_id>/stream |
Flux audio (fichier local, ou proxy same-origin pour les pistes en ligne ; sans token, lu par <audio>) |
| GET | /library/tracks/<track_id>/nowplaying |
Webradio : {available, artist, title, raw, station} (métadonnées ICY relevées par le proxy) |
| POST | /library/tracks/<track_id>/download |
Rend locale une piste YouTube en ligne |
| POST | /library/tracks/<track_id>/loudness |
Mesure/relit le loudness EBU R128 → {loudness_lufs, target_lufs, gain_db} |
| POST | /library/loudness/analyze |
Analyse toutes les pistes locales (arrière-plan) |
| GET | /library/loudness/status |
{running, done, total} |
| POST | /youtube/import |
Renvoie {duplicate: true, track_id} sans télécharger si la vidéo est déjà importée |
36. Sauvegardes
Administrateur uniquement, SQLite.
| Méthode | Route | Description |
|---|---|---|
| GET | /admin/backups |
Liste {backups: [{name, size, modified}], supported} |
| POST | /admin/backups |
Crée une sauvegarde manuelle |
| GET | /admin/backups/<name> |
Télécharge |
| POST | /admin/backups/<name>/restore |
Body {confirm: true} — vérifie l'intégrité, crée webradio_prerestore_*, restaure à chaud → {restored, safety_backup, relogin} |
Une sauvegarde automatique quotidienne (7 conservées) est faite par le serveur.
37. Scan asynchrone
| Méthode | Route | Description |
|---|---|---|
| POST | /library/scan |
{async: true} → 202 {status: started\|running} ; sans async, scan synchrone (limite 120/h) |
| GET | /library/scan/status |
{running, path, elapsed, error, result: {files_found, tracks_added, tracks_updated, tracks_removed, errors[]}} |
38. Planification de radios
POST /scheduler/events et PUT /scheduler/events/<id> acceptent en plus :
end_time: fin de l'émission (doit être >start_time) ; pour une radio, le deck s'arrête à cette heure et reprend la piste précédenteradio:{id?, name, url, logo?}— crée/retrouve la piste « en ligne » et l'utilise commetrack_idrecurrence: en plus des valeurs existantes,weekdays(lun–ven),weekends, et tout intervalleevery_<N>min|h|song
Les événements pilotent le deck de l'utilisateur propriétaire de l'événement.