use std::vec; use radiobrowser::RadioBrowserAPI; use reqwest::Client; use songbird::input::Input; use songbird::input::HttpRequest; use super::super::connect; use crate::utils::poise_context_extension::ContextExt; use crate::types::{Context, Error}; use crate::commands::voice::voice_utils::autocomplete_channel; use super::radio_utils::paginate_search; use super::super::voice_utils::MAX_ENTRIES; #[poise::command( slash_command, description_localized("en-US", "Plays music from URL source"), subcommands("search", "play") )] pub async fn radio(_ctx: Context<'_>) -> Result<(), Error> { Ok(()) } // TODO: autocomplete radio stream URLs #[poise::command( slash_command, description_localized("en-US", "Plays music from URL source") )] pub async fn play(ctx: Context<'_>, #[autocomplete = "autocomplete_channel"] #[description = "Voice channel name: "] channel: Option, #[autocomplete = "autocomplete_radio"] #[description = "Radio station: "] name: String, ) -> Result<(), Error> { if ctx.guild().is_none() { ctx.reply_ephemeral("Can't use this outside of guild").await?; return Ok(()); } let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird Voice client placed in at initialisation.") .clone(); let Some(guild_id) = ctx.guild_id() else { ctx.reply_ephemeral("Guild id not found").await?; return Ok(()) }; if manager.get(guild_id).is_none() { if let Err(e) = connect(&ctx, guild_id, channel).await { ctx.reply_ephemeral(&e.to_string()).await?; return Ok(()) } } if let Some(handler_lock) = manager.get(guild_id) { let mut handler = handler_lock.lock().await; let client = Client::new(); let request = HttpRequest::new(client, name); let input = Input::from(request); handler.play_only_input(input); } else { ctx.reply_ephemeral("Not in a voice channel").await?; return Ok(()) } ctx.reply_ephemeral("Done!").await?; Ok(()) } async fn autocomplete_radio( ctx: Context<'_>, partial: &str ) -> Vec { let api = { match &ctx.data().radio_browser { Some(v) => v.clone(), None => { let Ok(radio_browser) = RadioBrowserAPI::new().await else { return vec!["There was an error with radio API!".to_owned()] }; radio_browser } } }; let stations: Vec = match api.get_stations() .limit(MAX_ENTRIES) .name(partial) .hidebroken(true) .send().await { Ok(stations) => { stations.iter().map(|station| format!("Name: {} Language: {} Bitrate: {}", station.name, station.countrycode, station.bitrate) ).collect() }, Err(_) => return vec!["".to_owned()] }; dbg!(&stations); return stations // return vec![]; } // TODO // autocomplete // search with buttons and list in embed with direct links to stream // embed showing currently playing song // TODO: autocomplete radio stream URLs #[poise::command( slash_command, description_localized("en-US", "Search for a radio") )] pub async fn search(ctx: Context<'_>, #[description = "Radio station: "] name: String, #[description = "Country: "] country: Option, #[description = "Tag: "] tag: Option ) -> Result<(), Error> { let api = match &ctx.data().radio_browser { Some(v) => v.clone(), None => { let Ok(radio_browser) = RadioBrowserAPI::new().await else { let _ = ctx.reply_ephemeral("There was an error with radio API!").await; return Ok(()) }; radio_browser } }; let station_search_builder = api.get_stations() .name(name) .tag(tag.unwrap_or("".to_owned())) .country(country.unwrap_or("".to_owned())) .hidebroken(true); paginate_search(ctx, station_search_builder); Ok(()) }