- 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
138 lines
4.1 KiB
Rust
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};
|