radio-rs

Place a description here.
git clone git://git.alex.balgavy.eu/reponame.git
Log | Files | Refs

commit 44fd2dfbe476a8f13b067f8fbd583a39d777863a
parent 47bb2484a81e610a81ac977cbb4a19ba5c081871
Author: Alex Balgavy <alex@balgavy.eu>
Date:   Sat,  4 Jun 2022 22:33:36 +0200

Submenus!

Diffstat:
Mconfig.toml | 88++++++++++++++++++-------------------------------------------------------------
Msrc/config.rs | 3++-
Msrc/main.rs | 49+++++++++++++++++++++++++++++++++++++++----------
Msrc/screen.rs | 58+++++++++++++++++++++++++++++++++++++++++++---------------
4 files changed, 104 insertions(+), 94 deletions(-)

diff --git a/config.toml b/config.toml @@ -56,73 +56,25 @@ url = "https://streaming.live365.com/a37043" mpc_load_option = "ADD" [[config.radios]] -name = "SOMA - Groove Salad ambient/downtempo" +name = "SOMA..." website = "https://somafm.com/" -url = "https://somafm.com/groovesalad256.pls" -mpc_load_option = "LOAD" - -[[config.radios]] -name = "SOMA - Mission Control ambient space" -website = "https://somafm.com/" -url = "https://somafm.com/missioncontrol.pls" -mpc_load_option = "LOAD" - -[[config.radios]] -name = "SOMA - The Trip prog house/trance" -website = "https://somafm.com/" -url = "https://somafm.com/thetrip.pls" -mpc_load_option = "LOAD" - -[[config.radios]] -name = "SOMA - Beat Blender deep house downtempo" -website = "https://somafm.com/" -url = "https://somafm.com/beatblender.pls" -mpc_load_option = "LOAD" - -[[config.radios]] -name = "SOMA - Dub Step" -website = "https://somafm.com/" -url = "https://somafm.com/dubstep256.pls" -mpc_load_option = "LOAD" - -[[config.radios]] -name = "SOMA - Defcon" -website = "https://somafm.com/" -url = "https://somafm.com/defcon256.pls" -mpc_load_option = "LOAD" - -[[config.radios]] -name = "SOMA - Deep Space deep ambient electro/experimental" -website = "https://somafm.com/" -url = "https://somafm.com/deepspaceone.pls" -mpc_load_option = "LOAD" - -[[config.radios]] -name = "SOMA - Thistle Radio Celtic" -website = "https://somafm.com/" -url = "https://somafm.com/thistle.pls" -mpc_load_option = "LOAD" - -[[config.radios]] -name = "SOMA - Fluid instr. hip hop liquid trap" -website = "https://somafm.com/" -url = "https://somafm.com/fluid.pls" -mpc_load_option = "LOAD" - -[[config.radios]] -name = "Bluemars" -website = "http://echoesofbluemars.org/" -url = "http://streams.echoesofbluemars.org:8000/bluemars.m3u" -mpc_load_option = "LOAD" - -[[config.radios]] -name = "Cryosleep" +radios = [ + {name = "Groove Salad ambient/downtempo", website = "https://somafm.com/", url = "https://somafm.com/groovesalad256.pls", mpc_load_option = "LOAD"}, + {name = "Mission Control ambient space", website = "https://somafm.com/", url = "https://somafm.com/missioncontrol.pls", mpc_load_option = "LOAD"}, + {name = "The Trip prog house/trance", website = "https://somafm.com/", url = "https://somafm.com/thetrip.pls", mpc_load_option = "LOAD"}, + {name = "Beat Blender deep house downtempo", website = "https://somafm.com/", url = "https://somafm.com/beatblender.pls", mpc_load_option = "LOAD"}, + {name = "Dub Step", website = "https://somafm.com/", url = "https://somafm.com/dubstep256.pls", mpc_load_option = "LOAD"}, + {name = "Defcon", website = "https://somafm.com/", url = "https://somafm.com/defcon256.pls", mpc_load_option = "LOAD"}, + {name = "Deep Space deep ambient electro/experimental", website = "https://somafm.com/", url = "https://somafm.com/deepspaceone.pls", mpc_load_option = "LOAD"}, + {name = "Thistle Radio Celtic", website = "https://somafm.com/", url = "https://somafm.com/thistle.pls", mpc_load_option = "LOAD"}, + {name = "Fluid instr. hip hop liquid trap", website = "https://somafm.com/", url = "https://somafm.com/fluid.pls", mpc_load_option = "LOAD"}, +] + +[[config.radios]] +name = "Echoes of Blue Mars..." website = "http://echoesofbluemars.org/" -url = "http://streams.echoesofbluemars.org:8000/cryosleep.m3u" -mpc_load_option = "LOAD" - -[[config.radios]] -name = "Voices From Within" -website = "http://echoesofbluemars.org/" -url = "http://streams.echoesofbluemars.org:8000/voicesfromwithin.m3u" -mpc_load_option = "LOAD" +radios = [ + {name = "Bluemars", website = "http://echoesofbluemars.org/", url = "http://streams.echoesofbluemars.org:8000/bluemars.m3u", mpc_load_option = "LOAD"}, + {name = "Cryosleep", website = "http://echoesofbluemars.org/", url = "http://streams.echoesofbluemars.org:8000/cryosleep.m3u", mpc_load_option = "LOAD"}, + {name = "Voices From Within", website = "http://echoesofbluemars.org/", url = "http://streams.echoesofbluemars.org:8000/voicesfromwithin.m3u", mpc_load_option = "LOAD"} +] diff --git a/src/config.rs b/src/config.rs @@ -17,8 +17,9 @@ pub struct Config { pub struct Item { pub name: String, pub website: String, - pub url: String, + pub url: Option<String>, pub mpc_load_option: Option<MpcLoadOptions>, + pub radios: Option<Vec<Item>> } pub fn read_config() -> ConfigData { diff --git a/src/main.rs b/src/main.rs @@ -1,21 +1,50 @@ +mod config; mod radio; mod screen; mod tunein; -mod config; use radio::*; use std::process::exit; use tunein::*; +use config::*; +fn choose_radio(radios: &Vec<Item>) -> &Item { + loop { + let choices: Vec<String> = radios + .iter() + .map(|x| format!("{} ({})", x.name, x.website)) + .collect(); + + let (choice_i, _) = match screen::list_menu(&*choices) { + Some((i, s)) => (i, s), + None => continue + }; + let chosen_radio = match radios[choice_i].url { + Some(_) => &radios[choice_i], + None => { + let choices: Vec<String> = radios[choice_i].radios.as_ref().unwrap() + .iter() + .map(|x| format!("{} ({})", x.name, x.website)) + .collect(); + let (choice_i, _) = match screen::list_menu(&*choices) { + Some((i, s)) => (i, s), + None => continue + }; + &radios[choice_i] + } + }; + return chosen_radio; + } +} fn main() { let data = config::read_config(); - let choices: Vec<String> = data - .config - .radios - .iter() - .map(|x| format!("{} ({})", x.name, x.website)) - .collect(); - let (choice_i, _) = screen::list_menu(&choices[..]); - let chosen_radio = &data.config.radios[choice_i]; - let radio = baseradio::BaseRadio::new(&chosen_radio.url); + assert!(data + .config + .radios + .iter() + .all(|x| (x.url.is_some() && x.mpc_load_option.is_some()) || (x.radios.is_some()))); + + let top_radios = &data.config.radios; + let chosen_radio = choose_radio(top_radios); + let radio = baseradio::BaseRadio::new(chosen_radio.url.as_ref().unwrap()); let opts = RadioOptions { method: data.config.player, load_options: chosen_radio.mpc_load_option, diff --git a/src/screen.rs b/src/screen.rs @@ -4,19 +4,13 @@ pub fn clear() { println!("\x1B[H\x1B[2J"); } -pub fn list_menu(list: &[String]) -> (usize, String) { - let mut message = None; +fn prompt(prompt: &str) -> String { + let mut message: Option<&str> = None; loop { - clear(); - println!("== Internet Radio Player =="); - for (i, val) in list.iter().enumerate() { - println!("{} {}", i + 1, val); - } if message.is_some() { println!("Error: {}", message.unwrap()); } - - print!("Enter number or press ^C to go back> "); + print!("{}", prompt); let _ = stdout().flush(); let mut input_text = String::new(); @@ -24,16 +18,50 @@ pub fn list_menu(list: &[String]) -> (usize, String) { message = Some("did not input text."); continue; } - let trimmed = input_text.trim(); + return input_text.trim().to_string(); + } +} + + +pub fn list_menu(list: &[String]) -> Option<(usize, String)> { + let mut message = None; + loop { + clear(); + println!("== Internet Radio Player =="); + for (i, val) in list.iter().enumerate() { + println!("{} {}", i + 1, val); + } + println!("Type 'q' or 'quit' to go back, press ^C to exit."); + if message.is_some() { + println!("Error: {}", message.unwrap()); + } - let index = match trimmed.parse::<usize>() { - Ok(n) if n > 0 && n <= list.len() => n - 1, + // can't use just prompt(..), because match goes on &str, and prompt returns String. + // can't use prompt(..).as_str(), because temporary value dropped while borrowed. + // - i could use a new variable here, but it's clutter. + // So, I reborrow with &*. + let trimmed = &*prompt("Enter number> "); + + let n = match trimmed.parse::<usize>() { + Ok(n) => n, _ => { - message = Some("choice out of range"); - continue; + match trimmed { + "q" | "quit" => { + return None; + } + _ => { + message = Some("Not a number!"); + continue; + } + } } }; + if n == 0 || n > list.len() { + message = Some("choice out of range"); + continue; + } + let index = n - 1; - return (index, list[index].to_string()); + return Some((index, list[index].to_string())); } }