Browse and listen to thousands of radio stations across the globe right from your terminal ๐ŸŒŽ ๐Ÿ“ป ๐ŸŽตโœจ
radio rust tokio web-radio command-line-tool tui
at main 2.3 kB view raw
1use std::time::Duration; 2 3use anyhow::Error; 4use serde::Deserialize; 5use surf::{Client, Config, Url}; 6 7#[derive(Deserialize)] 8pub struct Header { 9 #[serde(rename = "Title")] 10 pub title: String, 11 #[serde(rename = "Subtitle")] 12 pub subtitle: String, 13} 14 15#[derive(Deserialize)] 16pub struct NowPlaying { 17 #[serde(rename = "Header")] 18 pub header: Header, 19} 20 21pub async fn extract_stream_url(url: &str, playlist_type: Option<String>) -> Result<String, Error> { 22 match playlist_type { 23 Some(playlist_type) => match playlist_type.as_str() { 24 "pls" => { 25 let client: Client = Config::new() 26 .set_timeout(Some(Duration::from_secs(5))) 27 .try_into() 28 .unwrap(); 29 let response = client 30 .get(Url::parse(url)?) 31 .recv_string() 32 .await 33 .map_err(|e| Error::msg(e.to_string()))?; 34 35 let mut response = response.replace("[Playlist]", "[playlist]"); 36 if !response.contains("NumberOfEntries") { 37 response = format!("{}\nNumberOfEntries=1", response); 38 } 39 40 let url = pls::parse(&mut response.as_bytes()) 41 .map_err(|e| Error::msg(e.to_string()))? 42 .first() 43 .map(|e| e.path.clone()) 44 .unwrap(); 45 Ok(url.to_string()) 46 } 47 _ => Err(Error::msg(format!( 48 "Playlist type {} not supported", 49 playlist_type 50 ))), 51 }, 52 None => Ok(url.to_string()), 53 } 54} 55 56pub async fn get_currently_playing(station: &str) -> Result<String, Error> { 57 let client = Client::new(); 58 let url = format!( 59 "https://feed.tunein.com/profiles/{}/nowPlaying?partnerId=RadioTime", 60 station 61 ); 62 let response: NowPlaying = client 63 .get(Url::parse(&url)?) 64 .recv_json() 65 .await 66 .map_err(|e| Error::msg(e.to_string()))?; 67 68 let subtitle = response.header.subtitle.trim(); 69 if subtitle.is_empty() { 70 Ok(response.header.title.trim().to_string()) 71 } else { 72 Ok(subtitle.to_string()) 73 } 74}