Reimplement all music related functionality in MusicPlayer struct

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/25/head
trivernis 3 years ago
parent 6876a1bb1a
commit b2ba31a9e9
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -13,7 +13,9 @@ use songbird::SerenityInit;
use crate::commands::*;
use crate::handler::Handler;
use crate::providers::music::lavalink::{Lavalink, LavalinkHandler};
use crate::utils::context_data::{get_database_from_context, DatabaseContainer, Store, StoreData};
use crate::utils::context_data::{
get_database_from_context, DatabaseContainer, MusicPlayers, Store, StoreData,
};
use crate::utils::error::{BotError, BotResult};
use bot_serenityutils::menu::EventDrivenMessageContainer;
use lavalink_rs::LavalinkClient;
@ -35,19 +37,20 @@ pub async fn get_client() -> BotResult<Client> {
.register_songbird()
.await?;
let data = client.data.clone();
let http = client.cache_and_http.http.clone();
let lava_client = LavalinkClient::builder(current_application.id.0)
.set_host(env::var("LAVALINK_HOST").unwrap_or("172.0.0.1".to_string()))
.set_password(env::var("LAVALINK_PORT").expect("Missing lavalink port"))
.set_password(env::var("LAVALINK_PASSWORD").expect("Missing lavalink password"))
.build(LavalinkHandler { data, http })
.build(LavalinkHandler { data })
.await?;
{
let mut data = client.data.write().await;
data.insert::<Store>(StoreData::new());
data.insert::<DatabaseContainer>(database);
data.insert::<EventDrivenMessageContainer>(Arc::new(Mutex::new(HashMap::new())));
data.insert::<Lavalink>(lava_client);
data.insert::<MusicPlayers>(HashMap::new());
data.insert::<Lavalink>(Arc::new(lava_client));
}
Ok(client)

@ -9,9 +9,7 @@ use serenity::prelude::*;
use sysinfo::{ProcessExt, SystemExt};
use crate::commands::common::handle_autodelete;
use crate::providers::music::queue::MusicQueue;
use crate::utils::context_data::{get_database_from_context, Store};
use std::sync::Arc;
use crate::utils::context_data::{get_database_from_context, MusicPlayers};
#[command]
#[description("Shows some statistics about the bot")]
@ -94,23 +92,7 @@ async fn stats(ctx: &Context, msg: &Message) -> CommandResult {
/// Returns the total number of queues that are not
/// flagged to leave
async fn get_queue_count(ctx: &Context) -> usize {
let queues: Vec<Arc<Mutex<MusicQueue>>> = {
let data = ctx.data.read().await;
let store = data.get::<Store>().unwrap();
store
.music_queues
.iter()
.map(|(_, q)| Arc::clone(q))
.collect()
};
let mut count = 0;
for queue in queues {
let queue = queue.lock().await;
if !queue.leave_flag {
count += 1;
}
}
count
let data = ctx.data.read().await;
let players = data.get::<MusicPlayers>().unwrap();
players.len()
}

@ -1,12 +1,13 @@
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::CommandResult;
use serenity::framework::standard::{CommandResult, CommandError};
use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete;
use crate::commands::music::{get_queue_for_guild, DJ_CHECK};
use crate::commands::music::{get_music_player_for_guild, DJ_CHECK};
use bot_serenityutils::core::SHORT_TIMEOUT;
use bot_serenityutils::ephemeral_message::EphemeralMessage;
use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
#[command]
#[only_in(guilds)]
@ -19,14 +20,16 @@ async fn clear_queue(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap();
log::debug!("Clearing queue for guild {}", guild.id);
let queue = forward_error!(
ctx,
msg.channel_id,
get_queue_for_guild(ctx, &guild.id).await
);
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {
player
} else {
return create_no_voicechannel_message(&ctx.http, msg.channel_id)
.await
.map_err(CommandError::from);
};
{
let mut queue_lock = queue.lock().await;
queue_lock.clear();
let mut player = player.lock().await;
player.queue().clear();
}
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {

@ -1,12 +1,11 @@
use std::mem;
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::CommandResult;
use serenity::framework::standard::{CommandError, CommandResult};
use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete;
use crate::commands::music::get_queue_for_guild;
use crate::commands::music::get_music_player_for_guild;
use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
use crate::messages::music::now_playing::create_now_playing_msg;
#[command]
@ -19,27 +18,22 @@ async fn current(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap();
log::debug!("Displaying current song for queue in {}", guild.id);
let queue = forward_error!(
ctx,
msg.channel_id,
get_queue_for_guild(ctx, &guild.id).await
);
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {
player
} else {
return create_no_voicechannel_message(&ctx.http, msg.channel_id)
.await
.map_err(CommandError::from);
};
let current = {
let queue_lock = queue.lock().await;
queue_lock.current().clone()
let mut player = player.lock().await;
player.queue().current().clone()
};
if let Some(_) = current {
let np_msg = create_now_playing_msg(ctx, queue.clone(), msg.channel_id).await?;
let mut queue_lock = queue.lock().await;
if let Some(old_np) = mem::replace(&mut queue_lock.now_playing_msg, Some(np_msg)) {
let old_np = old_np.read().await;
if let Ok(message) = old_np.get_message(&ctx.http).await {
let _ = message.delete(ctx).await;
}
}
let np_msg = create_now_playing_msg(ctx, player.clone(), msg.channel_id).await?;
let mut player = player.lock().await;
player.set_now_playing(np_msg).await;
}
handle_autodelete(ctx, msg).await?;

@ -4,7 +4,8 @@ use serenity::framework::standard::{Args, CommandResult};
use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete;
use crate::commands::music::{get_channel_for_author, is_dj, join_channel};
use crate::commands::music::{get_channel_for_author, is_dj};
use crate::providers::music::player::MusicPlayer;
use bot_serenityutils::core::SHORT_TIMEOUT;
use bot_serenityutils::ephemeral_message::EphemeralMessage;
use serenity::model::id::ChannelId;
@ -34,7 +35,7 @@ async fn join(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
)
};
log::debug!("Joining channel {} for guild {}", channel_id, guild.id);
join_channel(ctx, channel_id, guild.id).await;
MusicPlayer::join(ctx, guild.id, channel_id).await?;
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {
m.content("🎤 Joined the Voice Channel")
})

@ -4,8 +4,8 @@ use serenity::framework::standard::CommandResult;
use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete;
use crate::commands::music::{get_voice_manager, DJ_CHECK};
use crate::providers::music::lavalink::Lavalink;
use crate::commands::music::DJ_CHECK;
use crate::utils::context_data::MusicPlayers;
use bot_serenityutils::core::SHORT_TIMEOUT;
use bot_serenityutils::ephemeral_message::EphemeralMessage;
@ -20,31 +20,21 @@ async fn leave(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap();
log::debug!("Leave request received for guild {}", guild.id);
let manager = get_voice_manager(ctx).await;
let handler = manager.get(guild.id);
if let Some(handler) = handler {
let mut handler_lock = handler.lock().await;
handler_lock.remove_all_global_events();
let mut data = ctx.data.write().await;
let players = data.get_mut::<MusicPlayers>().unwrap();
match players.remove(&guild.id.0) {
None => {
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {
m.content("‼️ I'm not in a Voice Channel")
})
.await?;
}
Some(player) => {
let mut player = player.lock().await;
player.stop().await?;
}
}
if manager.get(guild.id).is_some() {
manager.remove(guild.id).await?;
let data = ctx.data.read().await;
let player = data.get::<Lavalink>().unwrap();
player.destroy(guild.id.0).await?;
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {
m.content("👋 Left the Voice Channel")
})
.await?;
log::debug!("Left the voice channel");
} else {
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {
m.content("‼️ I'm not in a Voice Channel")
})
.await?;
log::debug!("Not in a voice channel");
}
handle_autodelete(ctx, msg).await?;
Ok(())

@ -1,11 +1,11 @@
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::CommandResult;
use serenity::framework::standard::{CommandError, CommandResult};
use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete;
use crate::commands::music::get_queue_for_guild;
use crate::providers::music::lyrics::get_lyrics;
use crate::commands::music::get_music_player_for_guild;
use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
#[command]
#[only_in(guilds)]
@ -16,39 +16,40 @@ async fn lyrics(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap();
log::debug!("Fetching lyrics for song playing in {}", guild.id);
let queue = forward_error!(
ctx,
msg.channel_id,
get_queue_for_guild(ctx, &guild.id).await
);
let queue_lock = queue.lock().await;
if let Some(song) = queue_lock.current() {
log::debug!("Playing music. Fetching lyrics for currently playing song...");
let title = song.title().clone();
let author = song.author().clone();
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {
player
} else {
return create_no_voicechannel_message(&ctx.http, msg.channel_id)
.await
.map_err(CommandError::from);
};
if let Some(lyrics) = get_lyrics(&*author, &*title).await? {
log::trace!("Lyrics for '{}' are {}", title, lyrics);
let (lyrics, current) = {
let mut player = player.lock().await;
let current = player.queue().current().clone();
(player.lyrics().await?, current)
};
msg.channel_id
.send_message(ctx, |m| {
m.embed(|e| {
e.title(format!("Lyrics for {} by {}", title, author))
.description(lyrics)
.footer(|f| f.text("Powered by lyricsovh"))
})
})
.await?;
} else {
log::debug!("No lyrics found");
msg.channel_id.say(ctx, "No lyrics found").await?;
}
} else {
if let Some(lyrics) = lyrics {
let current = current.unwrap();
msg.channel_id
.say(ctx, "I'm not playing music right now")
.send_message(ctx, |m| {
m.embed(|e| {
e.title(format!(
"Lyrics for {} by {}",
current.title(),
current.author()
))
.description(lyrics)
.footer(|f| f.text("Powered by lyricsovh"))
})
})
.await?;
} else {
log::debug!("No lyrics found");
msg.channel_id.say(ctx, "No lyrics found").await?;
}
handle_autodelete(ctx, msg).await?;
Ok(())

@ -1,21 +1,14 @@
use std::mem;
use std::sync::atomic::{AtomicIsize, AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
use aspotify::Track;
use regex::Regex;
use serenity::async_trait;
use serenity::client::Context;
use serenity::framework::standard::macros::{check, group};
use serenity::http::Http;
use serenity::model::channel::Message;
use serenity::model::guild::Guild;
use serenity::model::id::{ChannelId, GuildId, UserId};
use serenity::model::user::User;
use songbird::{
Call, Event, EventContext, EventHandler as VoiceEventHandler, Songbird, TrackEvent,
};
use songbird::Songbird;
use tokio::sync::Mutex;
use clear_queue::CLEAR_QUEUE_COMMAND;
@ -34,19 +27,16 @@ use save_playlist::SAVE_PLAYLIST_COMMAND;
use shuffle::SHUFFLE_COMMAND;
use skip::SKIP_COMMAND;
use crate::messages::music::now_playing::update_now_playing_msg;
use crate::providers::music::lavalink::Lavalink;
use crate::providers::music::queue::{MusicQueue, Song};
use crate::providers::music::player::MusicPlayer;
use crate::providers::music::queue::Song;
use crate::providers::music::{add_youtube_song_to_database, youtube_dl};
use crate::providers::settings::{get_setting, Setting};
use crate::utils::context_data::{DatabaseContainer, Store};
use crate::utils::context_data::{DatabaseContainer, MusicPlayers, Store};
use crate::utils::error::{BotError, BotResult};
use bot_database::Database;
use futures::future::BoxFuture;
use futures::FutureExt;
use lavalink_rs::LavalinkClient;
use serenity::framework::standard::{Args, CommandOptions, Reason};
use serenity::prelude::{RwLock, TypeMap};
use youtube_metadata::get_video_information;
mod clear_queue;
@ -85,131 +75,12 @@ mod skip;
)]
pub struct Music;
struct SongEndNotifier {
channel_id: ChannelId,
guild_id: GuildId,
http: Arc<Http>,
queue: Arc<Mutex<MusicQueue>>,
data: Arc<RwLock<TypeMap>>,
}
#[async_trait]
impl VoiceEventHandler for SongEndNotifier {
async fn act(&self, _ctx: &EventContext<'_>) -> Option<Event> {
log::debug!("Song ended in {}. Playing next one", self.channel_id);
let data = self.data.read().await;
let player = data.get::<Lavalink>().unwrap();
while !play_next_in_queue(
&self.http,
&self.channel_id,
&self.guild_id,
&self.queue,
player,
)
.await
{
tokio::time::sleep(Duration::from_millis(100)).await;
}
None
}
}
struct ChannelDurationNotifier {
channel_id: ChannelId,
guild_id: GuildId,
count: Arc<AtomicUsize>,
queue: Arc<Mutex<MusicQueue>>,
leave_in: Arc<AtomicIsize>,
handler: Arc<Mutex<Call>>,
manager: Arc<Songbird>,
}
#[async_trait]
impl VoiceEventHandler for ChannelDurationNotifier {
async fn act(&self, _ctx: &EventContext<'_>) -> Option<Event> {
let count_before = self.count.fetch_add(1, Ordering::Relaxed);
log::debug!(
"Playing in channel {} for {} minutes",
self.channel_id,
count_before
);
let queue_lock = self.queue.lock().await;
if queue_lock.leave_flag {
log::debug!("Waiting to leave");
if self.leave_in.fetch_sub(1, Ordering::Relaxed) <= 0 {
log::debug!("Leaving voice channel");
{
let mut handler_lock = self.handler.lock().await;
handler_lock.remove_all_global_events();
}
let _ = self.manager.remove(self.guild_id).await;
log::debug!("Left the voice channel");
}
} else {
log::debug!("Resetting leave value");
self.leave_in.store(5, Ordering::Relaxed)
}
None
}
}
/// Joins a voice channel
async fn join_channel(ctx: &Context, channel_id: ChannelId, guild_id: GuildId) -> Arc<Mutex<Call>> {
log::debug!(
"Attempting to join channel {} in guild {}",
channel_id,
guild_id
);
let manager = songbird::get(ctx)
/// Returns the voice manager from the context
pub async fn get_voice_manager(ctx: &Context) -> Arc<Songbird> {
songbird::get(ctx)
.await
.expect("Songbird Voice client placed in at initialisation.")
.clone();
let (handler, connection) = manager.join_gateway(guild_id, channel_id).await;
let connection = connection.expect("Failed to join gateway");
let mut data = ctx.data.write().await;
let lava_client = data.get::<Lavalink>().unwrap();
lava_client
.create_session(&connection)
.await
.expect("Failed to create lava session");
let store = data.get_mut::<Store>().unwrap();
log::debug!("Creating new queue");
let queue = Arc::new(Mutex::new(MusicQueue::new(channel_id)));
store.music_queues.insert(guild_id, queue.clone());
{
let mut handler_lock = handler.lock().await;
log::debug!("Registering track end handler");
handler_lock.add_global_event(
Event::Track(TrackEvent::End),
SongEndNotifier {
channel_id,
guild_id,
http: ctx.http.clone(),
queue: Arc::clone(&queue),
data: ctx.data.clone(),
},
);
handler_lock.add_global_event(
Event::Periodic(Duration::from_secs(60), None),
ChannelDurationNotifier {
channel_id,
guild_id,
count: Default::default(),
queue: Arc::clone(&queue),
handler: handler.clone(),
leave_in: Arc::new(AtomicIsize::new(5)),
manager: manager.clone(),
},
);
}
handler
.clone()
}
/// Returns the voice channel the author is in
@ -221,93 +92,15 @@ fn get_channel_for_author(author_id: &UserId, guild: &Guild) -> BotResult<Channe
.ok_or(BotError::from("You're not in a Voice Channel"))
}
/// Returns the voice manager from the context
pub async fn get_voice_manager(ctx: &Context) -> Arc<Songbird> {
songbird::get(ctx)
.await
.expect("Songbird Voice client placed in at initialisation.")
.clone()
}
/// Returns a reference to a guilds music queue
pub(crate) async fn get_queue_for_guild(
/// Returns the music player for a given guild
pub async fn get_music_player_for_guild(
ctx: &Context,
guild_id: &GuildId,
) -> BotResult<Arc<Mutex<MusicQueue>>> {
guild_id: GuildId,
) -> Option<Arc<Mutex<MusicPlayer>>> {
let data = ctx.data.read().await;
let store = data.get::<Store>().unwrap();
let queue = store
.music_queues
.get(guild_id)
.ok_or(BotError::from("I'm not in a Voice Channel"))?
.clone();
Ok(queue)
}
let players = data.get::<MusicPlayers>().unwrap();
/// Plays the next song in the queue
pub async fn play_next_in_queue(
http: &Arc<Http>,
channel_id: &ChannelId,
guild_id: &GuildId,
queue: &Arc<Mutex<MusicQueue>>,
player: &LavalinkClient,
) -> bool {
let mut queue_lock = queue.lock().await;
if let Some(mut next) = queue_lock.next() {
let url = match next.url().await {
Some(url) => url,
None => {
let _ = channel_id
.say(&http, format!("'{}' not found", next.title()))
.await;
return false;
}
};
log::debug!("Getting source for song '{}'", url);
let query_information = match player.auto_search_tracks(url).await {
Ok(s) => s,
Err(e) => {
let _ = channel_id
.say(
&http,
format!("Failed to enqueue {}: {:?}", next.title(), e),
)
.await;
return false;
}
};
if let Err(e) = player
.play(guild_id.0, query_information.tracks[0].clone())
.start()
.await
{
log::error!("Failed to play song: {:?}", e);
}
log::trace!("Track is {:?}", query_information.tracks[0]);
if queue_lock.paused() {
let _ = player.pause(guild_id.0).await;
}
if let Some(np) = &queue_lock.now_playing_msg {
if let Err(e) = update_now_playing_msg(http, np, &mut next, queue_lock.paused()).await {
log::error!("Failed to update now playing message: {:?}", e);
}
}
queue_lock.set_current(next);
} else {
if let Some(np) = mem::take(&mut queue_lock.now_playing_msg) {
let np = np.read().await;
if let Ok(message) = np.get_message(http).await {
let _ = message.delete(http).await;
}
}
queue_lock.clear_current();
}
true
players.get(&guild_id.0).cloned()
}
/// Returns the list of songs for a given url

@ -1,10 +1,11 @@
use crate::commands::common::handle_autodelete;
use crate::commands::music::{get_queue_for_guild, DJ_CHECK};
use crate::commands::music::{get_music_player_for_guild, DJ_CHECK};
use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
use bot_serenityutils::core::SHORT_TIMEOUT;
use bot_serenityutils::ephemeral_message::EphemeralMessage;
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::{Args, CommandResult};
use serenity::framework::standard::{Args, CommandError, CommandResult};
use serenity::model::channel::Message;
#[command]
@ -22,15 +23,17 @@ async fn move_song(ctx: &Context, msg: &Message, mut args: Args) -> CommandResul
let pos1 = args.single::<usize>()?;
let pos2 = args.single::<usize>()?;
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {
player
} else {
return create_no_voicechannel_message(&ctx.http, msg.channel_id)
.await
.map_err(CommandError::from);
};
{
let queue = forward_error!(
ctx,
msg.channel_id,
get_queue_for_guild(ctx, &guild.id).await
);
let mut queue_lock = queue.lock().await;
queue_lock.move_position(pos1, pos2);
let mut player = player.lock().await;
player.queue().move_position(pos1, pos2);
}
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {
m.content(format!(

@ -1,12 +1,11 @@
use serenity::framework::standard::macros::command;
use serenity::framework::standard::CommandResult;
use serenity::framework::standard::{CommandError, CommandResult};
use serenity::model::channel::Message;
use serenity::prelude::*;
use crate::commands::common::handle_autodelete;
use crate::commands::music::{get_queue_for_guild, DJ_CHECK};
use crate::messages::music::now_playing::update_now_playing_msg;
use crate::providers::music::lavalink::Lavalink;
use crate::commands::music::{get_music_player_for_guild, DJ_CHECK};
use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
use bot_serenityutils::core::SHORT_TIMEOUT;
use bot_serenityutils::ephemeral_message::EphemeralMessage;
@ -20,39 +19,33 @@ async fn pause(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap();
log::debug!("Pausing playback for guild {}", guild.id);
let queue = forward_error!(
ctx,
msg.channel_id,
get_queue_for_guild(ctx, &guild.id).await
);
let mut queue_lock = queue.lock().await;
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {
player
} else {
return create_no_voicechannel_message(&ctx.http, msg.channel_id)
.await
.map_err(CommandError::from);
};
let mut player = player.lock().await;
if let Some(_) = player.queue().current() {
player.toggle_paused().await?;
let is_paused = player.is_paused();
if let Some(_) = queue_lock.current() {
let is_paused = {
let data = ctx.data.read().await;
let player = data.get::<Lavalink>().unwrap();
player.set_pause(guild.id.0, !queue_lock.paused()).await?;
!queue_lock.paused()
};
queue_lock.set_paused(is_paused);
if is_paused {
log::debug!("Paused");
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {
m.content("⏸️ Paused playback")
})
.await?;
if let (Some(menu), Some(song)) = (&queue_lock.now_playing_msg, queue_lock.current()) {
update_now_playing_msg(&ctx.http, menu, &mut song.clone(), true).await?;
}
player.update_now_playing().await?;
} else {
log::debug!("Resumed");
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {
m.content("▶ Resumed playback")
})
.await?;
if let (Some(menu), Some(song)) = (&queue_lock.now_playing_msg, queue_lock.current()) {
update_now_playing_msg(&ctx.http, menu, &mut song.clone(), true).await?;
}
player.update_now_playing().await?;
}
} else {
msg.channel_id.say(ctx, "Nothing to pause").await?;

@ -5,12 +5,12 @@ use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete;
use crate::commands::music::{
get_channel_for_author, get_queue_for_guild, get_songs_for_query, get_voice_manager,
join_channel, play_next_in_queue,
get_channel_for_author, get_music_player_for_guild, get_songs_for_query,
};
use crate::messages::music::now_playing::create_now_playing_msg;
use crate::providers::music::lavalink::Lavalink;
use crate::providers::music::player::MusicPlayer;
use crate::providers::settings::{get_setting, Setting};
use std::sync::Arc;
#[command]
#[only_in(guilds)]
@ -25,25 +25,22 @@ async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap();
log::debug!("Play request received for guild {}", guild.id);
let manager = get_voice_manager(ctx).await;
let handler = manager.get(guild.id);
let mut player = get_music_player_for_guild(ctx, guild.id).await;
if handler.is_none() {
if player.is_none() {
log::debug!("Not in a channel. Joining authors channel...");
msg.guild(&ctx.cache).await.unwrap();
let channel_id = get_channel_for_author(&msg.author.id, &guild)?;
join_channel(ctx, channel_id, guild.id).await;
let music_player = MusicPlayer::join(ctx, guild.id, channel_id).await?;
player = Some(music_player);
}
let player = player.unwrap();
let songs = get_songs_for_query(&ctx, msg, query).await?;
let queue = get_queue_for_guild(ctx, &guild.id).await?;
let (play_first, create_now_playing) = {
log::debug!("Adding song to queue");
let mut queue_lock = queue.lock().await;
let mut player_lock = player.lock().await;
for song in songs {
queue_lock.add(song);
player_lock.queue().add(song);
}
let autoshuffle = get_setting(ctx, guild.id, Setting::MusicAutoShuffle)
.await?
@ -51,26 +48,23 @@ async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
if autoshuffle {
log::debug!("Autoshuffeling");
queue_lock.shuffle();
player_lock.queue().shuffle();
}
(
queue_lock.current().is_none(),
queue_lock.now_playing_msg.is_none(),
player_lock.queue().current().is_none(),
player_lock.now_playing_message().is_none(),
)
};
if play_first {
log::debug!("Playing first song in queue");
let data = ctx.data.read().await;
let lava_player = data.get::<Lavalink>().unwrap();
while !play_next_in_queue(&ctx.http, &msg.channel_id, &guild.id, &queue, &lava_player).await
{
}
let mut player_lock = player.lock().await;
player_lock.play_next().await?;
}
if create_now_playing {
let handle = create_now_playing_msg(ctx, queue.clone(), msg.channel_id).await?;
let mut queue_lock = queue.lock().await;
queue_lock.now_playing_msg = Some(handle);
let handle = create_now_playing_msg(ctx, Arc::clone(&player), msg.channel_id).await?;
let mut player_lock = player.lock().await;
player_lock.set_now_playing(handle).await;
}
handle_autodelete(ctx, msg).await?;

@ -5,11 +5,11 @@ use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete;
use crate::commands::music::{
get_channel_for_author, get_queue_for_guild, get_songs_for_query, get_voice_manager,
join_channel, play_next_in_queue, DJ_CHECK,
get_channel_for_author, get_music_player_for_guild, get_songs_for_query, DJ_CHECK,
};
use crate::messages::music::now_playing::create_now_playing_msg;
use crate::providers::music::lavalink::Lavalink;
use crate::providers::music::player::MusicPlayer;
use std::sync::Arc;
#[command]
#[only_in(guilds)]
@ -24,42 +24,41 @@ async fn play_next(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap();
log::debug!("Playing song as next song for guild {}", guild.id);
let manager = get_voice_manager(ctx).await;
let handler = manager.get(guild.id);
if handler.is_none() {
log::debug!("Not in a voice channel. Joining authors channel");
msg.guild(&ctx.cache).await.unwrap();
let mut player = get_music_player_for_guild(ctx, guild.id).await;
if player.is_none() {
log::debug!("Not in a channel. Joining authors channel...");
let channel_id = get_channel_for_author(&msg.author.id, &guild)?;
join_channel(ctx, channel_id, guild.id).await;
let music_player = MusicPlayer::join(ctx, guild.id, channel_id).await?;
player = Some(music_player);
}
let player = player.unwrap();
let mut songs = get_songs_for_query(&ctx, msg, query).await?;
let queue = get_queue_for_guild(ctx, &guild.id).await?;
let (play_first, create_now_playing) = {
let mut queue_lock = queue.lock().await;
let mut player_lock = player.lock().await;
songs.reverse();
log::debug!("Enqueueing songs as next songs in the queue");
for song in songs {
queue_lock.add_next(song);
player_lock.queue().add_next(song);
}
(
queue_lock.current().is_none(),
queue_lock.now_playing_msg.is_none(),
player_lock.queue().current().is_none(),
player_lock.now_playing_message().is_none(),
)
};
if play_first {
let data = ctx.data.read().await;
let player = data.get::<Lavalink>().unwrap();
while !play_next_in_queue(&ctx.http, &msg.channel_id, &guild.id, &queue, &player).await {}
let mut player_lock = player.lock().await;
player_lock.play_next().await?;
}
if create_now_playing {
let handle = create_now_playing_msg(ctx, queue.clone(), msg.channel_id).await?;
let mut queue_lock = queue.lock().await;
queue_lock.now_playing_msg = Some(handle);
let handle = create_now_playing_msg(ctx, Arc::clone(&player), msg.channel_id).await?;
let mut player_lock = player.lock().await;
player_lock.set_now_playing(handle).await;
}
handle_autodelete(ctx, msg).await?;

@ -1,10 +1,11 @@
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::{Args, CommandResult};
use serenity::framework::standard::{Args, CommandError, CommandResult};
use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete;
use crate::commands::music::get_queue_for_guild;
use crate::commands::music::get_music_player_for_guild;
use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
use crate::messages::music::queue::create_queue_menu;
use crate::providers::music::queue::Song;
@ -23,13 +24,16 @@ async fn queue(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
.map(|s| s.unwrap().to_lowercase())
.collect::<Vec<String>>();
let queue = forward_error!(
ctx,
msg.channel_id,
get_queue_for_guild(ctx, &guild.id).await
);
let queue_lock = queue.lock().await;
let songs: Vec<(usize, Song)> = queue_lock
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {
player
} else {
return create_no_voicechannel_message(&ctx.http, msg.channel_id)
.await
.map_err(CommandError::from);
};
let mut player = player.lock().await;
let songs: Vec<(usize, Song)> = player
.queue()
.entries()
.into_iter()
.enumerate()

@ -1,10 +1,11 @@
use crate::commands::common::handle_autodelete;
use crate::commands::music::{get_queue_for_guild, DJ_CHECK};
use crate::commands::music::{get_music_player_for_guild, DJ_CHECK};
use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
use bot_serenityutils::core::SHORT_TIMEOUT;
use bot_serenityutils::ephemeral_message::EphemeralMessage;
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::{Args, CommandResult};
use serenity::framework::standard::{Args, CommandError, CommandResult};
use serenity::model::channel::Message;
#[command]
@ -21,15 +22,17 @@ async fn remove_song(ctx: &Context, msg: &Message, mut args: Args) -> CommandRes
log::debug!("Moving song for guild {}", guild.id);
let pos = args.single::<usize>()?;
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {
player
} else {
return create_no_voicechannel_message(&ctx.http, msg.channel_id)
.await
.map_err(CommandError::from);
};
{
let queue = forward_error!(
ctx,
msg.channel_id,
get_queue_for_guild(ctx, &guild.id).await
);
let mut queue_lock = queue.lock().await;
queue_lock.remove(pos);
let mut player = player.lock().await;
player.queue().remove(pos);
}
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {

@ -1,10 +1,11 @@
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::CommandResult;
use serenity::framework::standard::{CommandError, CommandResult};
use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete;
use crate::commands::music::{get_queue_for_guild, DJ_CHECK};
use crate::commands::music::{get_music_player_for_guild, DJ_CHECK};
use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
use bot_serenityutils::core::SHORT_TIMEOUT;
use bot_serenityutils::ephemeral_message::EphemeralMessage;
@ -19,14 +20,16 @@ async fn shuffle(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap();
log::debug!("Shuffling queue for guild {}", guild.id);
let queue = forward_error!(
ctx,
msg.channel_id,
get_queue_for_guild(ctx, &guild.id).await
);
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {
player
} else {
return create_no_voicechannel_message(&ctx.http, msg.channel_id)
.await
.map_err(CommandError::from);
};
{
let mut queue_lock = queue.lock().await;
queue_lock.shuffle();
let mut player = player.lock().await;
player.queue().shuffle();
}
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {

@ -1,11 +1,11 @@
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::CommandResult;
use serenity::framework::standard::{CommandError, CommandResult};
use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete;
use crate::commands::music::DJ_CHECK;
use crate::providers::music::lavalink::Lavalink;
use crate::commands::music::{get_music_player_for_guild, DJ_CHECK};
use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
use bot_serenityutils::core::SHORT_TIMEOUT;
use bot_serenityutils::ephemeral_message::EphemeralMessage;
@ -20,10 +20,16 @@ async fn skip(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap();
log::debug!("Skipping song for guild {}", guild.id);
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {
player
} else {
return create_no_voicechannel_message(&ctx.http, msg.channel_id)
.await
.map_err(CommandError::from);
};
{
let data = ctx.data.read().await;
let player = data.get::<Lavalink>().unwrap();
player.stop(guild.id.0).await?;
let mut player = player.lock().await;
player.skip().await?;
}
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {

@ -8,7 +8,7 @@ use serenity::model::id::{ChannelId, GuildId, MessageId};
use serenity::model::voice::VoiceState;
use serenity::prelude::*;
use crate::commands::music::get_queue_for_guild;
use crate::commands::music::get_music_player_for_guild;
use crate::utils::delete_messages_from_database;
use bot_serenityutils::menu::{
handle_message_delete, handle_message_delete_bulk, handle_reaction_add, handle_reaction_remove,
@ -120,10 +120,10 @@ impl EventHandler for Handler {
if let Some(count) = member_count {
log::debug!("{} Members in channel", count);
if let Ok(queue) = get_queue_for_guild(&ctx, &guild_id).await {
let mut queue_lock = queue.lock().await;
if let Some(player) = get_music_player_for_guild(&ctx, guild_id).await {
let mut player = player.lock().await;
log::debug!("Setting leave flag to {}", count == 0);
queue_lock.leave_flag = count == 0;
player.set_leave_flag(count == 0);
}
}
}

@ -1,2 +1,3 @@
pub mod no_voicechannel;
pub mod now_playing;
pub mod queue;

@ -0,0 +1,19 @@
use crate::utils::error::BotResult;
use bot_serenityutils::core::SHORT_TIMEOUT;
use bot_serenityutils::ephemeral_message::EphemeralMessage;
use serenity::http::Http;
use serenity::model::prelude::ChannelId;
use std::sync::Arc;
/// Creates a not in a voicechannel message
pub async fn create_no_voicechannel_message(
http: &Arc<Http>,
channel_id: ChannelId,
) -> BotResult<()> {
EphemeralMessage::create(http, channel_id, SHORT_TIMEOUT, |m| {
m.content("‼️ I'm not in a Voice Channel")
})
.await?;
Ok(())
}

@ -4,11 +4,11 @@ use serenity::builder::CreateEmbed;
use serenity::http::Http;
use serenity::model::prelude::ChannelId;
use crate::commands::music::{get_queue_for_guild, get_voice_manager, is_dj};
use crate::commands::music::{get_music_player_for_guild, get_voice_manager, is_dj};
use crate::messages::add_ephemeral_handle_to_database;
use crate::providers::music::add_youtube_song_to_database;
use crate::providers::music::lavalink::Lavalink;
use crate::providers::music::queue::{MusicQueue, Song};
use crate::providers::music::player::MusicPlayer;
use crate::providers::music::queue::Song;
use crate::utils::context_data::{DatabaseContainer, Store};
use crate::utils::error::*;
use bot_serenityutils::core::MessageHandle;
@ -30,7 +30,7 @@ static GOOD_PICK_BUTTON: &str = "👍";
/// Creates a new now playing message and returns the embed for that message
pub async fn create_now_playing_msg(
ctx: &Context,
queue: Arc<Mutex<MusicQueue>>,
player: Arc<Mutex<MusicPlayer>>,
channel_id: ChannelId,
) -> BotResult<Arc<RwLock<MessageHandle>>> {
log::debug!("Creating now playing menu");
@ -61,16 +61,17 @@ pub async fn create_now_playing_msg(
)
.show_help()
.add_page(Page::new_builder(move || {
let queue = Arc::clone(&queue);
let player = Arc::clone(&player);
Box::pin(async move {
log::debug!("Creating now playing embed for page");
let queue = queue.lock().await;
log::debug!("Queue locked");
let mut player = player.lock().await;
log::debug!("player locked");
let mut page = CreateMessage::default();
if let Some(mut current) = queue.current().clone() {
if let Some(mut current) = player.queue().current().clone() {
let mut embed = CreateEmbed::default();
create_now_playing_embed(&mut current, &mut embed, queue.paused(), nsfw).await;
create_now_playing_embed(&mut current, &mut embed, player.is_paused(), nsfw)
.await;
page.embed(|e| {
e.0.clone_from(&embed.0);
e
@ -166,25 +167,16 @@ async fn play_pause_button_action(
return Ok(());
}
{
let queue = get_queue_for_guild(ctx, &guild_id).await?;
let player = get_music_player_for_guild(ctx, guild_id).await.unwrap();
let (current, message, paused) = {
log::debug!("Queue is locked");
let mut queue = queue.lock().await;
{
let pause = !queue.paused();
let data = ctx.data.read().await;
let player = data.get::<Lavalink>().unwrap();
player
.set_pause(guild_id.0, pause)
.await
.map_err(BotError::from)?;
queue.set_paused(pause);
}
let mut player = player.lock().await;
player.toggle_paused().await?;
(
queue.current().clone(),
queue.now_playing_msg.clone().unwrap(),
queue.paused(),
player.queue().current().clone(),
player.now_playing_message().clone().unwrap(),
player.is_paused(),
)
};
log::debug!("Queue is unlocked");
@ -211,9 +203,9 @@ async fn skip_button_action(
}
{
let data = ctx.data.read().await;
let player = data.get::<Lavalink>().unwrap();
player.stop(guild_id.0).await.map_err(BotError::from)?;
let player = get_music_player_for_guild(ctx, guild_id).await.unwrap();
let mut player = player.lock().await;
player.skip().await?;
}
Ok(())
@ -243,9 +235,9 @@ async fn stop_button_action(
if manager.get(guild_id).is_some() {
manager.remove(guild_id).await.map_err(BotError::from)?;
let data = ctx.data.read().await;
let player = data.get::<Lavalink>().unwrap();
player.destroy(guild_id.0).await.map_err(BotError::from)?;
let player = get_music_player_for_guild(ctx, guild_id).await.unwrap();
let mut player = player.lock().await;
player.stop().await?;
log::debug!("Left the voice channel");
} else {
log::debug!("Not in a voice channel");
@ -268,10 +260,10 @@ async fn good_pick_action(
reaction: Reaction,
) -> SerenityUtilsResult<()> {
let guild_id = reaction.guild_id.unwrap();
let queue = get_queue_for_guild(ctx, &guild_id).await?;
let queue = queue.lock().await;
let player = get_music_player_for_guild(ctx, guild_id).await.unwrap();
let mut player = player.lock().await;
if let Some(song) = queue.current() {
if let Some(song) = player.queue().current() {
let data = ctx.data.read().await;
let store = data.get::<Store>().unwrap();
let database = data.get::<DatabaseContainer>().unwrap();
@ -292,9 +284,9 @@ async fn delete_action(
handle.clone()
};
{
let queue = get_queue_for_guild(ctx, &guild_id).await?;
let mut queue = queue.lock().await;
queue.now_playing_msg = None;
let player = get_music_player_for_guild(ctx, guild_id).await.unwrap();
let mut player = player.lock().await;
player.clear_now_playing();
}
ctx.http
.delete_message(handle.channel_id, handle.message_id)

@ -1,11 +1,8 @@
use crate::commands::music::play_next_in_queue;
use crate::utils::context_data::Store;
use crate::utils::context_data::MusicPlayers;
use lavalink_rs::gateway::LavalinkEventHandler;
use lavalink_rs::model::{TrackFinish, TrackStart};
use lavalink_rs::LavalinkClient;
use serenity::async_trait;
use serenity::http::Http;
use serenity::model::id::GuildId;
use serenity::prelude::TypeMapKey;
use std::sync::Arc;
use tokio::sync::RwLock;
@ -13,7 +10,6 @@ use typemap_rev::TypeMap;
pub struct LavalinkHandler {
pub data: Arc<RwLock<TypeMap>>,
pub http: Arc<Http>,
}
#[async_trait]
@ -21,36 +17,28 @@ impl LavalinkEventHandler for LavalinkHandler {
async fn track_start(&self, _client: LavalinkClient, event: TrackStart) {
log::info!("Track started!\nGuild: {}", event.guild_id);
}
async fn track_finish(&self, client: LavalinkClient, event: TrackFinish) {
async fn track_finish(&self, _: LavalinkClient, event: TrackFinish) {
log::info!("Track finished!\nGuild: {}", event.guild_id);
let queue = {
let player = {
let data = self.data.read().await;
let store = data.get::<Store>().unwrap();
let players = data.get::<MusicPlayers>().unwrap();
store
.music_queues
.get(&GuildId(event.guild_id))
.unwrap()
.clone()
players.get(&event.guild_id).cloned()
};
let channel_id = {
let queue = queue.lock().await;
queue.channel_id()
};
while !play_next_in_queue(
&self.http,
&channel_id,
&GuildId(event.guild_id),
&queue,
&client,
)
.await
{}
if let Some(player) = player {
let mut player = player.lock().await;
if let Err(e) = player.play_next().await {
log::error!("Failed to play next song: {:?}", e);
}
if let Err(e) = player.update_now_playing().await {
log::error!("Failed to update now playing embed: {:?}", e);
}
}
}
}
pub struct Lavalink;
impl TypeMapKey for Lavalink {
type Value = LavalinkClient;
type Value = Arc<LavalinkClient>;
}

@ -7,12 +7,13 @@ use regex::Regex;
use responses::VideoInformation;
use youtube_dl::search_video_information;
pub(crate) mod lavalink;
pub(crate) mod lyrics;
pub(crate) mod queue;
pub(crate) mod responses;
pub(crate) mod spotify;
pub(crate) mod youtube_dl;
pub mod lavalink;
pub mod lyrics;
pub mod player;
pub mod queue;
pub mod responses;
pub mod spotify;
pub mod youtube_dl;
/// Searches for a youtube video for the specified song
pub(crate) async fn song_to_youtube_video(song: &Song) -> BotResult<Option<VideoInformation>> {

@ -0,0 +1,181 @@
use crate::messages::music::now_playing::update_now_playing_msg;
use crate::providers::music::lavalink::Lavalink;
use crate::providers::music::lyrics::get_lyrics;
use crate::providers::music::queue::MusicQueue;
use crate::utils::context_data::MusicPlayers;
use crate::utils::error::BotResult;
use bot_serenityutils::core::MessageHandle;
use lavalink_rs::LavalinkClient;
use serenity::client::Context;
use serenity::http::Http;
use serenity::model::id::{ChannelId, GuildId};
use std::mem;
use std::sync::Arc;
use tokio::sync::{Mutex, RwLock};
pub struct MusicPlayer {
client: Arc<LavalinkClient>,
http: Arc<Http>,
queue: MusicQueue,
guild_id: GuildId,
now_playing_msg: Option<Arc<RwLock<MessageHandle>>>,
leave_flag: bool,
paused: bool,
}
impl MusicPlayer {
/// Creates a new music player
pub fn new(client: Arc<LavalinkClient>, http: Arc<Http>, guild_id: GuildId) -> Self {
Self {
client,
http,
guild_id,
queue: MusicQueue::new(),
now_playing_msg: None,
leave_flag: false,
paused: false,
}
}
/// Joins a given voice channel
pub async fn join(
ctx: &Context,
guild_id: GuildId,
voice_channel_id: ChannelId,
) -> BotResult<Arc<Mutex<MusicPlayer>>> {
let manager = songbird::get(ctx).await.unwrap();
let (_, connection) = manager.join_gateway(guild_id, voice_channel_id).await;
let connection = connection?;
let player = {
let mut data = ctx.data.write().await;
let client = data.get::<Lavalink>().unwrap();
client.create_session(&connection).await?;
let player = MusicPlayer::new(Arc::clone(client), Arc::clone(&ctx.http), guild_id);
let player = Arc::new(Mutex::new(player));
let players = data.get_mut::<MusicPlayers>().unwrap();
players.insert(guild_id.0, Arc::clone(&player));
player
};
Ok(player)
}
/// Returns a mutable reference to the inner queue
pub fn queue(&mut self) -> &mut MusicQueue {
&mut self.queue
}
/// Skips to the next song
pub async fn skip(&mut self) -> BotResult<()> {
self.client.stop(self.guild_id.0).await?;
Ok(())
}
/// Stops playback and leaves the channel
pub async fn stop(&mut self) -> BotResult<()> {
self.queue.clear();
self.client.stop(self.guild_id.0).await?;
Ok(())
}
/// Returns the lyrics for the currently playing song
pub async fn lyrics(&self) -> BotResult<Option<String>> {
if let Some(current) = self.queue.current() {
let title = current.title();
let artist = current.author();
get_lyrics(artist, title).await
} else {
Ok(None)
}
}
/// Plays the next song in the queue
pub async fn play_next(&mut self) -> BotResult<()> {
while !self.try_play_next().await? {}
Ok(())
}
/// Tries to play the next song
pub async fn try_play_next(&mut self) -> BotResult<bool> {
let mut next = if let Some(n) = self.queue.next() {
n
} else {
return Ok(true);
};
let url = if let Some(url) = next.url().await {
url
} else {
return Ok(false);
};
let query_information = match self.client.auto_search_tracks(url).await {
Ok(i) => i,
Err(e) => {
log::error!("Failed to search for song: {}", e);
return Ok(false);
}
};
let track = query_information.tracks[0].clone();
self.client.play(self.guild_id.0, track).start().await?;
self.queue.set_current(next);
Ok(true)
}
/// Sets the new now playing message of the queue
pub async fn set_now_playing(&mut self, message: Arc<RwLock<MessageHandle>>) {
let _ = self.delete_now_playing().await;
self.now_playing_msg = Some(message)
}
/// Updates the now playing message
pub async fn update_now_playing(&self) -> BotResult<()> {
if let (Some(current), Some(np)) = (self.queue.current(), &self.now_playing_msg) {
update_now_playing_msg(&self.http, np, &mut current.clone(), self.is_paused()).await?;
}
Ok(())
}
/// Deletes the now playing message
pub async fn delete_now_playing(&mut self) -> BotResult<()> {
if let Some(np) = mem::take(&mut self.now_playing_msg) {
let np = np.read().await;
let msg = np.get_message(&self.http).await?;
msg.delete(&self.http).await?;
}
Ok(())
}
/// Pauses playback
pub async fn toggle_paused(&mut self) -> BotResult<()> {
self.paused = !self.paused;
self.client.set_pause(self.guild_id.0, self.paused).await?;
Ok(())
}
/// Returns if playback is paused
pub fn is_paused(&self) -> bool {
self.paused
}
/// Returns the now playing message of the player
pub fn now_playing_message(&self) -> &Option<Arc<RwLock<MessageHandle>>> {
&self.now_playing_msg
}
/// Deletes the now playing message from the player
pub fn clear_now_playing(&mut self) {
self.now_playing_msg = None;
}
/// Sets the leave flag to the given value
pub fn set_leave_flag(&mut self, flag: bool) {
self.leave_flag = flag;
}
}

@ -7,30 +7,20 @@ use bot_coreutils::shuffle::Shuffle;
use crate::providers::music::responses::{PlaylistEntry, VideoInformation};
use crate::providers::music::song_to_youtube_video;
use bot_database::models::YoutubeSong;
use bot_serenityutils::core::MessageHandle;
use serenity::model::id::ChannelId;
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Clone)]
pub struct MusicQueue {
inner: VecDeque<Song>,
current: Option<Song>,
paused: bool,
pub now_playing_msg: Option<Arc<RwLock<MessageHandle>>>,
pub leave_flag: bool,
channel_id: ChannelId,
}
impl MusicQueue {
pub fn new(channel_id: ChannelId) -> Self {
pub fn new() -> Self {
Self {
inner: VecDeque::new(),
current: None,
paused: false,
leave_flag: false,
now_playing_msg: None,
channel_id,
}
}
@ -64,26 +54,11 @@ impl MusicQueue {
self.current = Some(song)
}
/// Clears the currently playing song
pub fn clear_current(&mut self) {
self.current = None;
}
/// Returns the reference to the currently playing song
pub fn current(&self) -> &Option<Song> {
&self.current
}
/// Returns if the queue is paused
pub fn paused(&self) -> bool {
self.paused
}
/// Sets if the queue is paused
pub fn set_paused(&mut self, paused: bool) {
self.paused = paused;
}
/// Clears the queue
pub fn clear(&mut self) {
self.inner.clear();
@ -100,11 +75,6 @@ impl MusicQueue {
pub fn remove(&mut self, index: usize) {
self.inner.remove(index);
}
/// The channel id where the music messages should be sent to
pub fn channel_id(&self) -> ChannelId {
self.channel_id
}
}
#[derive(Clone, Debug)]

@ -5,18 +5,16 @@ use std::sync::Arc;
use bot_database::Database;
use sauce_api::prelude::SauceNao;
use serenity::client::Context;
use serenity::model::id::GuildId;
use serenity::prelude::TypeMapKey;
use tokio::sync::Mutex;
use crate::providers::music::queue::MusicQueue;
use crate::providers::music::player::MusicPlayer;
use crate::providers::music::spotify::SpotifyApi;
pub struct Store;
pub struct StoreData {
pub minecraft_data_api: minecraft_data_rs::api::Api,
pub music_queues: HashMap<GuildId, Arc<Mutex<MusicQueue>>>,
pub spotify_api: SpotifyApi,
pub sauce_nao: SauceNao,
}
@ -31,7 +29,6 @@ impl StoreData {
minecraft_data_api: minecraft_data_rs::api::Api::new(
minecraft_data_rs::api::versions::latest_stable().unwrap(),
),
music_queues: HashMap::new(),
spotify_api: SpotifyApi::new(),
sauce_nao,
}
@ -57,3 +54,9 @@ pub async fn get_database_from_context(ctx: &Context) -> Database {
database.clone()
}
pub struct MusicPlayers;
impl TypeMapKey for MusicPlayers {
type Value = HashMap<u64, Arc<Mutex<MusicPlayer>>>;
}

Loading…
Cancel
Save