|
|
|
@ -23,15 +23,13 @@ use shuffle::SHUFFLE_COMMAND;
|
|
|
|
|
use skip::SKIP_COMMAND;
|
|
|
|
|
|
|
|
|
|
use crate::providers::music::queue::{MusicQueue, Song};
|
|
|
|
|
use crate::providers::music::responses::VideoInformation;
|
|
|
|
|
use crate::providers::music::{
|
|
|
|
|
get_video_information, get_videos_for_playlist, search_video_information,
|
|
|
|
|
};
|
|
|
|
|
use crate::utils::error::{BotError, BotResult};
|
|
|
|
|
use crate::utils::store::Store;
|
|
|
|
|
use futures::future::BoxFuture;
|
|
|
|
|
use futures::FutureExt;
|
|
|
|
|
use regex::Regex;
|
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
|
|
mod clear;
|
|
|
|
|
mod current;
|
|
|
|
@ -121,7 +119,9 @@ struct SongEndNotifier {
|
|
|
|
|
#[async_trait]
|
|
|
|
|
impl VoiceEventHandler for SongEndNotifier {
|
|
|
|
|
async fn act(&self, _ctx: &EventContext<'_>) -> Option<Event> {
|
|
|
|
|
play_next_in_queue(&self.http, &self.channel_id, &self.queue, &self.handler).await;
|
|
|
|
|
while !play_next_in_queue(&self.http, &self.channel_id, &self.queue, &self.handler).await {
|
|
|
|
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
@ -133,17 +133,29 @@ async fn play_next_in_queue(
|
|
|
|
|
channel_id: &ChannelId,
|
|
|
|
|
queue: &Arc<Mutex<MusicQueue>>,
|
|
|
|
|
handler: &Arc<Mutex<Call>>,
|
|
|
|
|
) {
|
|
|
|
|
) -> bool {
|
|
|
|
|
let mut queue_lock = queue.lock().await;
|
|
|
|
|
|
|
|
|
|
if let Some(next) = queue_lock.next() {
|
|
|
|
|
let source = match songbird::ytdl(&next.url).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;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let source = match songbird::ytdl(&url).await {
|
|
|
|
|
Ok(s) => s,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
let _ = channel_id
|
|
|
|
|
.say(&http, format!("Failed to enqueue {}: {:?}", next.title, e))
|
|
|
|
|
.say(
|
|
|
|
|
&http,
|
|
|
|
|
format!("Failed to enqueue {}: {:?}", next.title(), e),
|
|
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
return;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let mut handler_lock = handler.lock().await;
|
|
|
|
@ -152,6 +164,7 @@ async fn play_next_in_queue(
|
|
|
|
|
} else {
|
|
|
|
|
queue_lock.clear_current();
|
|
|
|
|
}
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the list of songs for a given url
|
|
|
|
@ -177,65 +190,46 @@ async fn get_songs_for_query(ctx: &Context, msg: &Message, query: &str) -> BotRe
|
|
|
|
|
|
|
|
|
|
// if no songs were found fetch the song as a video
|
|
|
|
|
if songs.len() == 0 {
|
|
|
|
|
let song: Song = get_video_information(query).await?.into();
|
|
|
|
|
added_one_msg(&ctx, msg, &song).await?;
|
|
|
|
|
let mut song: Song = get_video_information(query).await?.into();
|
|
|
|
|
added_one_msg(&ctx, msg, &mut song).await?;
|
|
|
|
|
songs.push(song);
|
|
|
|
|
} else {
|
|
|
|
|
added_multiple_msg(&ctx, msg, &mut songs).await?;
|
|
|
|
|
}
|
|
|
|
|
} else if SPOTIFY_PLAYLIST_REGEX.is_match(query) {
|
|
|
|
|
// search for all songs in the playlist and search for them on youtube
|
|
|
|
|
let song_names = store.spotify_api.get_songs_in_playlist(query).await?;
|
|
|
|
|
songs = parallel_search_youtube(song_names).await;
|
|
|
|
|
songs = store.spotify_api.get_songs_in_playlist(query).await?;
|
|
|
|
|
added_multiple_msg(&ctx, msg, &mut songs).await?;
|
|
|
|
|
} else if SPOTIFY_ALBUM_REGEX.is_match(query) {
|
|
|
|
|
// fetch all songs in the album and search for them on youtube
|
|
|
|
|
let song_names = store.spotify_api.get_songs_in_album(query).await?;
|
|
|
|
|
songs = parallel_search_youtube(song_names).await;
|
|
|
|
|
songs = store.spotify_api.get_songs_in_album(query).await?;
|
|
|
|
|
added_multiple_msg(&ctx, msg, &mut songs).await?;
|
|
|
|
|
} else if SPOTIFY_SONG_REGEX.is_match(query) {
|
|
|
|
|
// fetch the song name and search it on youtube
|
|
|
|
|
let name = store.spotify_api.get_song_name(query).await?;
|
|
|
|
|
let song: Song = search_video_information(name.clone())
|
|
|
|
|
.await?
|
|
|
|
|
.ok_or(BotError::Msg(format!("Noting found for {}", name)))?
|
|
|
|
|
.into();
|
|
|
|
|
added_one_msg(ctx, msg, &song).await?;
|
|
|
|
|
let mut song = store.spotify_api.get_song_name(query).await?;
|
|
|
|
|
added_one_msg(ctx, msg, &mut song).await?;
|
|
|
|
|
songs.push(song);
|
|
|
|
|
} else {
|
|
|
|
|
let song: Song = search_video_information(query.to_string())
|
|
|
|
|
let mut song: Song = search_video_information(query.to_string())
|
|
|
|
|
.await?
|
|
|
|
|
.ok_or(BotError::Msg(format!("Noting found for {}", query)))?
|
|
|
|
|
.into();
|
|
|
|
|
|
|
|
|
|
added_one_msg(&ctx, msg, &song).await?;
|
|
|
|
|
added_one_msg(&ctx, msg, &mut song).await?;
|
|
|
|
|
songs.push(song);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(songs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Searches songs on youtube in parallel
|
|
|
|
|
async fn parallel_search_youtube(song_names: Vec<String>) -> Vec<Song> {
|
|
|
|
|
let search_futures: Vec<BoxFuture<BotResult<Option<VideoInformation>>>> = song_names
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|s| search_video_information(s).boxed())
|
|
|
|
|
.collect();
|
|
|
|
|
let information: Vec<BotResult<Option<VideoInformation>>> =
|
|
|
|
|
futures::future::join_all(search_futures).await;
|
|
|
|
|
information
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter_map(|i| i.ok().and_then(|s| s).map(Song::from))
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Message when one song was added to the queue
|
|
|
|
|
async fn added_one_msg(ctx: &Context, msg: &Message, song: &Song) -> BotResult<()> {
|
|
|
|
|
async fn added_one_msg(ctx: &Context, msg: &Message, song: &mut Song) -> BotResult<()> {
|
|
|
|
|
let url = song.url().await.ok_or(BotError::from("Song not found"))?;
|
|
|
|
|
msg.channel_id
|
|
|
|
|
.send_message(&ctx.http, |m| {
|
|
|
|
|
m.embed(|mut e| {
|
|
|
|
|
e = e.description(format!("Added [{}]({}) to the queue", song.title, song.url));
|
|
|
|
|
if let Some(thumb) = &song.thumbnail {
|
|
|
|
|
e = e.description(format!("Added [{}]({}) to the queue", song.title(), url));
|
|
|
|
|
if let Some(thumb) = &song.thumbnail() {
|
|
|
|
|
e = e.thumbnail(thumb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|