diff options
| -rw-r--r-- | Cargo.lock | 12 | ||||
| -rw-r--r-- | Cargo.toml | 4 | ||||
| -rw-r--r-- | src/bot.rs | 8 | ||||
| -rw-r--r-- | src/commands/playback.rs | 58 | ||||
| -rw-r--r-- | src/commands/today/mod.rs | 21 |
5 files changed, 87 insertions, 16 deletions
@@ -1076,6 +1076,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] name = "hyper" version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3017,6 +3023,7 @@ dependencies = [ "envconfig_derive", "fnv", "grate", + "humantime", "itertools", "lazy_static", "poise", @@ -3037,6 +3044,7 @@ dependencies = [ "tokio", "tokio-postgres", "url", + "uuid", "windows 0.58.0", ] @@ -3567,9 +3575,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", ] @@ -57,6 +57,10 @@ timeago = "0.4" fnv = "1.0" dashmap = "6.0" +humantime = "2.1" + +uuid = "1.10" + clap = { version = "4.5", features = ["derive"] } envconfig = "0.10" @@ -69,6 +69,12 @@ impl TypeMapKey for VolumeKey { type Value = DashMap<GuildId, f64>; } +pub struct PlaybackKey; + +impl TypeMapKey for PlaybackKey { + type Value = Arc<DashMap<uuid::Uuid, songbird::input::AuxMetadata>>; +} + #[cfg(debug_assertions)] const BOTNAME: &str = "thulani (dev)"; @@ -127,6 +133,7 @@ struct SongbirdHandler(Arc<Mutex<Call>>); impl songbird::events::EventHandler for SongbirdHandler { async fn act(&self, _ctx: &EventContext<'_>) -> Option<Event> { let mut call = self.0.lock().await; + if call.queue().is_empty() { let _ = call.leave().await; } @@ -416,6 +423,7 @@ pub async fn run() -> anyhow::Result<()> { .register_songbird_from_config(sb_config) .type_map_insert::<HttpKey>(reqwest::Client::new()) .type_map_insert::<VolumeKey>(DashMap::new()) + .type_map_insert::<PlaybackKey>(Arc::new(DashMap::new())) .framework(framework().await) .await?; diff --git a/src/commands/playback.rs b/src/commands/playback.rs index 4d6d0be..e7bf830 100644 --- a/src/commands/playback.rs +++ b/src/commands/playback.rs @@ -1,4 +1,7 @@ -use std::sync::Arc; +use std::{ + fmt::Debug, + sync::Arc, +}; use grate::tracing; use serenity::prelude::*; @@ -10,7 +13,10 @@ use songbird::{ use tap::Conv; use crate::{ - bot::HttpKey, + bot::{ + HttpKey, + PlaybackKey, + }, util, PoiseContext, PoiseData, @@ -21,9 +27,7 @@ pub fn commands() -> impl IntoIterator<Item = poise::Command<PoiseData, anyhow:: } pub async fn songbird(ctx: PoiseContext<'_>) -> anyhow::Result<(Arc<Songbird>, Arc<Mutex<Call>>)> { - let Some(gid) = ctx.guild_id() else { - return Err(anyhow::anyhow!("no guild id").into()); - }; + let gid = util::guild_id(ctx)?; let sb = songbird::get(ctx.serenity_context()).await.expect("acquiring songbird handle"); let call = sb.get_or_insert(gid); @@ -196,19 +200,51 @@ pub async fn die(ctx: PoiseContext<'_>) -> anyhow::Result<()> { /// List queued audio. #[poise::command(prefix_command, guild_only, category = "playback", aliases("queue"))] pub async fn list(ctx: PoiseContext<'_>) -> anyhow::Result<()> { - let (_sb, call) = songbird(ctx).await?; + let current_queue = { + let (_sb, call) = songbird(ctx).await?; - let call = call.lock().await; - let queue = call.queue(); + let call = call.lock().await; + let queue = call.queue(); - if queue.current_queue().is_empty() { + queue.current_queue() + }; + + let playback_data = { + let data = ctx.serenity_context().data.read().await; + data.get::<PlaybackKey>().cloned().unwrap() + }; + + if current_queue.is_empty() { util::reply(ctx, "nothing queued").await?; } - for track in queue.current_queue().into_iter() { + fn fmt_option<T>(name: &str, opt: Option<T>) -> String + where + T: std::fmt::Display, + { + if let Some(opt) = opt { + format!("{opt}") + } else { + format!("<no {name}>") + } + } + + for track in current_queue.into_iter() { let info = track.get_info().await?; - let fmt = format!("track playing for {:?}", info.play_time); + let meta = playback_data.get(&track.uuid()).map(|x| x.clone()); + let meta = meta.as_ref(); + + let fmt = format!( + "{}: {:?} / {}", + fmt_option("title", meta.and_then(|x| x.title.as_ref())), + humantime::format_duration(info.position), + fmt_option( + "duration", + meta.and_then(|x| x.duration.as_ref().map(|&dur| humantime::format_duration(dur))) + ) + ); + util::reply(ctx, fmt).await?; } diff --git a/src/commands/today/mod.rs b/src/commands/today/mod.rs index f8f38c7..2c47e83 100644 --- a/src/commands/today/mod.rs +++ b/src/commands/today/mod.rs @@ -5,11 +5,17 @@ use rand::{ seq::SliceRandom, thread_rng, }; -use songbird::input::YoutubeDl; +use songbird::input::{ + Compose, + YoutubeDl, +}; use tap::Conv; use crate::{ - bot::HttpKey, + bot::{ + HttpKey, + PlaybackKey, + }, commands::playback::songbird, util, PoiseContext, @@ -117,12 +123,21 @@ pub async fn today(ctx: PoiseContext<'_>, #[rest] _rest: Option<String>) -> anyh data.get::<HttpKey>().unwrap().clone() }; - let input = + let mut input = YoutubeDl::new_ytdl_like("yt-dlp", client.clone(), play_args.url.conv::<String>()); + let meta = input.aux_metadata().await?; + let handle = call.enqueue_input(input.into()).await; handle.set_volume(volume as _)?; + let playback = { + let data = ctx.serenity_context().data.read().await; + data.get::<PlaybackKey>().unwrap().clone() + }; + + playback.insert(handle.uuid(), meta); + let q = call.queue(); q.pause()?; q.modify_queue(move |q| { |
