Files
pmomusic/pmoradiofrance/src/lib.rs
Eric Coissac 0cca9229ad feat: implement livemeta/pull API and station mapping
- Replace old /api/live endpoint with new livemeta/pull API (api.radiofrance.fr)
- Add hardcoded station ID/stream mappings with runtime-updatable mapping
- Implement station rediscovery on unknown slug or stream failure
- Add persistent caching for station mappings (30-day TTL) via pmoconfig
- Update live_metadata to use numeric IDs and handle ID mismatches
- Add station validation (API + stream accessibility check)
- Refactor metadata caching with fetched_at and fallback TTL
- Update discover_local_radios to scrape SvelteKit page data
- Add new models: PullResponse, PullStep, EmbedImage for livemeta format
- Improve live_metadata.rs example to safely truncate intros
- Enable parallel DIDL generation for playlist groups
2026-03-24 10:01:47 +01:00

138 lines
4.1 KiB
Rust

//! Radio France client library for PMOMusic
//!
//! This crate provides a Rust client for accessing Radio France's public APIs,
//! including live metadata, station discovery, and stream URLs.
//!
//! # Features
//!
//! - **Station Discovery**: Discover all Radio France stations dynamically
//! (main stations, webradios, and local France Bleu radios)
//! - **Live Metadata**: Get current show information, producers, visuals
//! - **Stream URLs**: Get HiFi stream URLs (AAC 192 kbps, HLS)
//! - **Polling Support**: Intelligent refresh delay based on API recommendations
//! - **Configuration Extension**: Cache station lists with configurable TTL
//!
//! # Supported Stations
//!
//! - **Main Stations**: France Inter, France Info, France Culture, France Musique,
//! FIP, Mouv', France Bleu
//! - **Webradios**: FIP Rock, FIP Jazz, France Musique Baroque, etc.
//! - **Local Radios**: ~40 France Bleu local stations
//!
//! # Example
//!
//! ```no_run
//! use pmoradiofrance::{RadioFranceClient, ImageSize};
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let client = RadioFranceClient::new().await?;
//!
//! // Discover all stations
//! let stations = client.discover_all_stations().await?;
//! println!("Found {} stations", stations.len());
//!
//! // Get live metadata
//! let live = client.live_metadata("franceculture").await?;
//! println!("Now: {} - {}",
//! live.now.first_line.title_or_default(),
//! live.now.second_line.title_or_default()
//! );
//!
//! // Get HiFi stream URL
//! let stream_url = client.get_hifi_stream_url("franceculture").await?;
//! println!("Stream: {}", stream_url);
//!
//! Ok(())
//! }
//! ```
//!
//! # Configuration Extension
//!
//! When the `pmoconfig` feature is enabled, this crate provides a configuration
//! extension trait for caching station lists:
//!
//! ```no_run
//! use pmoconfig::get_config;
//! use pmoradiofrance::{RadioFranceConfigExt, RadioFranceClient};
//!
//! # #[tokio::main]
//! # async fn main() -> anyhow::Result<()> {
//! let config = get_config();
//!
//! // Check cached stations (default TTL: 7 days)
//! if let Some(stations) = config.get_radiofrance_stations_cached()? {
//! println!("Using {} cached stations", stations.len());
//! } else {
//! // Cache miss - need to discover
//! let client = RadioFranceClient::new().await?;
//! let stations = client.discover_all_stations().await?;
//! config.set_radiofrance_cached_stations(&stations)?;
//! }
//! # Ok(())
//! # }
//! ```
//!
//! # API Rate Limiting
//!
//! Radio France's APIs don't have documented rate limits, but the `delayToRefresh`
//! field in responses indicates the recommended polling interval. Always use
//! `RadioFranceClient::next_refresh_delay()` to respect this.
//!
//! # Audio Quality
//!
//! This client focuses on HiFi quality only:
//! - **AAC 192 kbps**: Primary format (best quality)
//! - **HLS**: Adaptive streaming fallback
//!
//! Lower quality formats (lofi, midfi) are not prioritized but are available
//! in the `StreamSource` list if needed.
pub mod client;
pub mod error;
pub mod models;
#[cfg(feature = "pmoconfig")]
pub mod config_ext;
#[cfg(feature = "pmoconfig")]
pub mod metadata_cache;
#[cfg(feature = "playlist")]
pub mod playlist;
#[cfg(feature = "server")]
pub mod source;
#[cfg(feature = "server")]
pub mod pmoserver_ext;
#[cfg(feature = "server")]
pub mod pmoserver_impl;
#[cfg(feature = "server")]
pub mod api_rest;
// Re-exports
pub use client::{ClientBuilder, RadioFranceClient};
pub use error::{Error, Result};
pub use models::{
BroadcastType, EmbedImage, ImageSize, Line, LiveResponse, LocalRadio, Media, Release,
ShowMetadata, Song, Station, StreamFormat, StreamSource, Visuals,
};
#[cfg(feature = "pmoconfig")]
pub use config_ext::{RadioFranceConfigExt, StationInfo};
#[cfg(feature = "pmoconfig")]
pub use metadata_cache::{CachedMetadata, MetadataCache, MetadataUpdateCallback};
#[cfg(feature = "playlist")]
pub use playlist::{StationGroup, StationGroups};
#[cfg(feature = "server")]
pub use source::RadioFranceSource;
#[cfg(feature = "server")]
pub use pmoserver_ext::{RadioFranceExt, RadioFranceState};