5.6 KiB
Radio Paradise - Architecture
Vue d'ensemble
Cette crate fournit deux architectures pour accéder à Radio Paradise :
- RadioParadiseStreamSource (legacy) : Télécharge les blocs FLAC entiers et découpe manuellement
- RadioParadisePlaylistFeeder (recommandé) : Utilise les URLs gapless individuelles + système de playlist
RadioParadisePlaylistFeeder (Architecture simplifiée)
Principe
Au lieu de télécharger un gros bloc FLAC contenant plusieurs chansons et de calculer manuellement les bornes de chaque chanson, cette architecture :
- Récupère le bloc via l'API
get_block - Filtre les chansons : garde uniquement celles où
sched_time_millis + duration >= now() - Télécharge chaque chanson individuellement via son
gapless_url - Stocke les métadonnées (titre, artiste, album, cover) dans le cache audio
- Push les PKs dans une playlist avec TTL calculé =
sched_end - now() - La playlist est consommée par
PlaylistSourcequi produit le flux audio
Avantages
- Simplicité : Pas de calcul de bornes, pas de découpe manuelle
- Précision : Chaque fichier FLAC = une chanson exactement
- Réutilisabilité : Utilise l'infrastructure existante (pmoplaylist, pmoaudiocache, PlaylistSource)
- TTL automatique : Les chansons expirées sont automatiquement retirées de la playlist
Exemple d'utilisation
use pmoparadise::{RadioParadiseClient, RadioParadisePlaylistFeeder};
use pmoaudiocache::cache::new_cache;
use pmocovers::cache::new_cache as new_covers_cache;
use pmoaudio_ext::PlaylistSource;
use std::sync::Arc;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Créer les caches
let audio_cache = Arc::new(new_cache("./cache/audio", 500)?);
let covers_cache = Arc::new(new_covers_cache("./cache/covers", 500)?);
// Créer le client Radio Paradise
let client = RadioParadiseClient::new().await?;
// Créer le feeder (retourne feeder + read_handle)
let (feeder, read_handle) = RadioParadisePlaylistFeeder::new(
client.clone(),
audio_cache.clone(),
covers_cache.clone(),
"rp-live".to_string(),
Some("radio-paradise".to_string()),
).await?;
// Lancer le feeder dans une tâche
let feeder = Arc::new(feeder);
let feeder_clone = feeder.clone();
tokio::spawn(async move {
if let Err(e) = feeder_clone.run().await {
tracing::error!("Feeder error: {}", e);
}
});
// Enqueue le bloc actuel
let now_playing = client.now_playing().await?;
feeder.push_block_id(now_playing.block.event);
// Créer la source audio depuis la playlist
let playlist_source = PlaylistSource::new(read_handle, audio_cache);
// Utiliser playlist_source dans un pipeline pmoaudio...
Ok(())
}
Radio Paradise API - Référence des URLs
URLs d'artistes
Format : https://radioparadise.com/music/artist/{artist_id}
Format alternatif : https://radioparadise.com/music/artist/{artist_id}/{Artist_Name}
Le champ artist_id est disponible dans song.song_credit_list[].artist_id.
Exemples :
- Sting (ID 4247) : https://radioparadise.com/music/artist/4247
- Pink Martini (ID 3718) : https://radioparadise.com/music/artist/3718/Pink_Martini
URLs de chansons
Format : https://legacy.radioparadise.com/rp3.php?file=songinfo&name=Music&song_id={song_id}
Le champ song_id est disponible dans song.song_id.
URLs gapless (FLAC individuels)
Format : Fourni directement par l'API dans song.gapless_url
Exemple : https://audio-geo.radioparadise.com/chan/1/x/1065/4/g/1065-3.flac
Ces URLs pointent vers des fichiers FLAC contenant une seule chanson, permettant un téléchargement et un traitement simplifiés.
Timestamps (sched_time_millis)
Tous les timestamps de l'API Radio Paradise sont en UTC (Unix timestamp en millisecondes).
Exemple :
"sched_time_millis": 1763272707000 // 2025-11-16 06:16:09 UTC
Pour calculer la fin de diffusion d'une chanson :
let sched_end = song.sched_time_millis + song.duration;
let is_still_playing = sched_end >= now_ms;
Notes d'implémentation future
Ces URLs peuvent être utilisées pour :
- Enrichir les métadonnées avec les biographies d'artistes (scraping des pages artistes)
- Récupérer les paroles (via l'API ou scraping)
- Afficher l'historique de diffusion par chanson
- Lier vers les pages communautaires Radio Paradise pour ratings/commentaires
- Intégration MusicBrainz/Discogs : utiliser
asinou rechercher par artiste+titre+album
Structure des données
Block
Un bloc Radio Paradise contient :
event: ID de début du blocend_event: ID de fin (= event du bloc suivant)length: Durée totale en millisecondesurl: URL du bloc FLAC complet (legacy)song: Map des chansons indexées par position ("0", "1", "2", ...)
Song
Chaque chanson contient :
- Métadonnées :
title,artist,album,year,rating - Timing :
elapsed(position dans le bloc),duration,sched_time_millis - Identifiants :
song_id,audio_id,event - Covers :
cover,cover_large,cover_medium,cover_small - Streaming :
gapless_url(⭐ nouveau, recommandé) - Artiste :
artist_id(pour construire les URLs)
Filtrage des chansons
Pour éviter de télécharger des chansons déjà terminées :
let now_ms = SystemTime::now()
.duration_since(UNIX_EPOCH)?
.as_millis() as u64;
for (idx, song) in block.songs_ordered() {
if song.is_still_playing(now_ms) {
// Télécharger et ajouter à la playlist
}
}