Add feedback button and improve song results

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/15/head
trivernis 3 years ago
parent 77fd292763
commit 658bc7fdc5
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -30,7 +30,7 @@ async fn current(ctx: &Context, msg: &Message) -> CommandResult {
queue_lock.current().clone()
};
if let Some(current) = current {
if let Some((current, _)) = current {
let metadata = current.metadata().clone();
log::trace!("Metadata is {:?}", metadata);
let np_msg = create_now_playing_msg(ctx, queue.clone(), msg.channel_id).await?;

@ -41,7 +41,7 @@ async fn leave(ctx: &Context, msg: &Message) -> CommandResult {
}
if manager.get(guild.id).is_some() {
if let Some(current) = queue_lock.current() {
if let Some((current, _)) = queue_lock.current() {
current.stop()?;
}
manager.remove(guild.id).await?;

@ -23,7 +23,7 @@ async fn lyrics(ctx: &Context, msg: &Message) -> CommandResult {
);
let queue_lock = queue.lock().await;
if let Some(current) = queue_lock.current() {
if let Some((current, _)) = queue_lock.current() {
log::debug!("Playing music. Fetching lyrics for currently playing song...");
let metadata = current.metadata();
let title = metadata.title.clone().unwrap();

@ -125,7 +125,7 @@ impl VoiceEventHandler for ChannelDurationNotifier {
let mut handler_lock = self.handler.lock().await;
handler_lock.remove_all_global_events();
}
if let Some(current) = queue_lock.current() {
if let Some((current, _)) = queue_lock.current() {
let _ = current.stop();
}
let _ = self.manager.remove(self.guild_id).await;
@ -270,7 +270,7 @@ async fn play_next_in_queue(
log::error!("Failed to update now playing message: {:?}", e);
}
}
queue_lock.set_current(track);
queue_lock.set_current(track, next);
} else {
if let Some(np) = mem::take(&mut queue_lock.now_playing_msg) {
let np = np.read().await;
@ -366,7 +366,7 @@ async fn get_songs_for_query(ctx: &Context, msg: &Message, query: &str) -> BotRe
} else if SPOTIFY_SONG_REGEX.is_match(&query) {
// fetch the song name and search it on youtube
log::debug!("Query is a spotify song");
let track = store.spotify_api.get_song_name(&query).await?;
let track = store.spotify_api.get_track_for_url(&query).await?;
let mut song = get_youtube_song_for_track(&database, track.clone())
.await?
.unwrap_or(track.into());

@ -37,7 +37,8 @@ async fn pause(ctx: &Context, msg: &Message) -> CommandResult {
m.content("⏸️ Paused playback")
})
.await?;
if let (Some(menu), Some(current)) = (&queue_lock.now_playing_msg, queue_lock.current())
if let (Some(menu), Some((current, _))) =
(&queue_lock.now_playing_msg, queue_lock.current())
{
update_now_playing_msg(&ctx.http, menu, current.metadata(), true).await?;
}
@ -47,7 +48,8 @@ async fn pause(ctx: &Context, msg: &Message) -> CommandResult {
m.content("▶ Resumed playback")
})
.await?;
if let (Some(menu), Some(current)) = (&queue_lock.now_playing_msg, queue_lock.current())
if let (Some(menu), Some((current, _))) =
(&queue_lock.now_playing_msg, queue_lock.current())
{
update_now_playing_msg(&ctx.http, menu, current.metadata(), true).await?;
}

@ -28,7 +28,7 @@ async fn skip(ctx: &Context, msg: &Message) -> CommandResult {
);
let queue_lock = queue.lock().await;
if let Some(current) = queue_lock.current() {
if let Some((current, _)) = queue_lock.current() {
current.stop()?;
}

@ -6,7 +6,9 @@ use serenity::model::prelude::ChannelId;
use songbird::input::Metadata;
use crate::commands::music::{get_queue_for_guild, get_voice_manager, is_dj};
use crate::providers::music::add_youtube_song_to_database;
use crate::providers::music::queue::MusicQueue;
use crate::utils::context_data::{DatabaseContainer, Store};
use crate::utils::error::*;
use bot_serenityutils::core::MessageHandle;
use bot_serenityutils::error::SerenityUtilsResult;
@ -21,6 +23,7 @@ use tokio::sync::{Mutex, RwLock};
static PAUSE_BUTTON: &str = "⏯️";
static SKIP_BUTTON: &str = "⏭️";
static STOP_BUTTON: &str = "⏹️";
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(
@ -39,6 +42,9 @@ pub async fn create_now_playing_msg(
.add_control(2, SKIP_BUTTON, |c, m, r| {
Box::pin(skip_button_action(c, m, r))
})
.add_control(3, GOOD_PICK_BUTTON, |c, m, r| {
Box::pin(good_pick_action(c, m, r))
})
.add_page(Page::new_builder(move || {
let queue = Arc::clone(&queue);
Box::pin(async move {
@ -47,7 +53,7 @@ pub async fn create_now_playing_msg(
log::debug!("Queue locked");
let mut page = CreateMessage::default();
if let Some(current) = queue.current() {
if let Some((current, _)) = queue.current() {
page.embed(|e| create_now_playing_embed(current.metadata(), e, queue.paused()));
} else {
page.embed(|e| e.description("Queue is empty"));
@ -142,7 +148,7 @@ async fn play_pause_button_action(
};
log::debug!("Queue is unlocked");
if let Some(current) = current {
if let Some((current, _)) = current {
update_now_playing_msg(&ctx.http, &message, current.metadata(), paused).await?;
}
}
@ -169,7 +175,7 @@ async fn skip_button_action(
queue.current().clone()
};
if let Some(current) = current {
if let Some((current, _)) = current {
let _ = current.stop();
}
}
@ -201,7 +207,7 @@ async fn stop_button_action(
handler_lock.remove_all_global_events();
}
if let Some(current) = queue.current() {
current.stop().map_err(BotError::from)?;
current.0.stop().map_err(BotError::from)?;
}
if manager.get(guild_id).is_some() {
@ -221,3 +227,22 @@ async fn stop_button_action(
Ok(())
}
async fn good_pick_action(
ctx: &Context,
_menu: &mut Menu<'_>,
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;
if let Some((_, song)) = queue.current() {
let data = ctx.data.read().await;
let store = data.get::<Store>().unwrap();
let database = data.get::<DatabaseContainer>().unwrap();
add_youtube_song_to_database(store, database, &mut song.clone()).await?;
}
Ok(())
}

@ -1,4 +1,4 @@
use crate::providers::music::queue::Song;
use crate::providers::music::queue::{Song, SongSource};
use crate::utils::context_data::StoreData;
use crate::utils::error::BotResult;
use aspotify::{ArtistSimplified, Track};
@ -20,6 +20,7 @@ pub(crate) async fn song_to_youtube_video(song: &Song) -> BotResult<Option<Video
let match_query = format!("{} - {}", artist, title);
let queries = vec![
format! {"{} - {} topic", artist, title},
format!("{} - {} lyrics", artist, title),
format!("{} - {} audio only", artist, title),
format!("{} by {}", title, artist),
@ -50,20 +51,25 @@ pub async fn add_youtube_song_to_database(
database: &Database,
song: &mut Song,
) -> BotResult<()> {
match search_for_song_variations(store, song).await {
Ok(Some(track)) => {
log::debug!("Song found on spotify. Inserting metadata");
let artists = artists_to_string(track.artists);
let url = song.url().await.unwrap();
if let Some(id) = track.id {
database
.add_song(&id, &artists, &track.name, &track.album.name, &url)
.await?;
let track = match song.source() {
SongSource::Spotify(track) => track.clone(),
SongSource::YouTube(_) => match search_for_song_variations(store, song).await {
Ok(Some(track)) => track,
Err(e) => {
log::error!("Failed to search for song on spotify {:?}", e);
return Ok(());
}
}
Err(e) => log::error!("Failed to search for song on spotify {:?}", e),
_ => {}
_ => return Ok(()),
},
};
log::debug!("Song found on spotify. Inserting metadata");
let artists = artists_to_string(track.artists);
let url = song.url().await.unwrap();
if let Some(id) = track.id {
database
.add_song(&id, &artists, &track.name, &track.album.name, &url)
.await?;
}
Ok(())

@ -1,6 +1,6 @@
use std::collections::VecDeque;
use aspotify::{Track, TrackSimplified};
use aspotify::Track;
use songbird::tracks::TrackHandle;
use bot_coreutils::shuffle::Shuffle;
@ -15,7 +15,7 @@ use tokio::sync::RwLock;
#[derive(Clone)]
pub struct MusicQueue {
inner: VecDeque<Song>,
current: Option<TrackHandle>,
current: Option<(TrackHandle, Song)>,
paused: bool,
pub now_playing_msg: Option<Arc<RwLock<MessageHandle>>>,
pub leave_flag: bool,
@ -58,8 +58,8 @@ impl MusicQueue {
}
/// Sets the currently playing song
pub fn set_current(&mut self, handle: TrackHandle) {
self.current = Some(handle)
pub fn set_current(&mut self, handle: TrackHandle, song: Song) {
self.current = Some((handle, song))
}
/// Clears the currently playing song
@ -68,7 +68,7 @@ impl MusicQueue {
}
/// Returns the reference to the currently playing song
pub fn current(&self) -> &Option<TrackHandle> {
pub fn current(&self) -> &Option<(TrackHandle, Song)> {
&self.current
}
@ -93,9 +93,9 @@ impl MusicQueue {
pub fn pause(&mut self) {
if let Some(current) = &self.current {
if self.paused {
let _ = current.play();
let _ = current.0.play();
} else {
let _ = current.pause();
let _ = current.0.pause();
}
self.paused = !self.paused;
@ -110,12 +110,19 @@ impl MusicQueue {
}
}
#[derive(Clone, Debug)]
pub enum SongSource {
Spotify(Track),
YouTube(String),
}
#[derive(Clone, Debug)]
pub struct Song {
url: Option<String>,
title: String,
author: String,
thumbnail: Option<String>,
source: SongSource,
}
impl Song {
@ -150,15 +157,21 @@ impl Song {
pub fn thumbnail(&self) -> &Option<String> {
&self.thumbnail
}
/// The source of the song
pub fn source(&self) -> &SongSource {
&self.source
}
}
impl From<VideoInformation> for Song {
fn from(info: VideoInformation) -> Self {
Self {
url: Some(info.webpage_url),
url: Some(info.webpage_url.clone()),
title: info.title,
author: info.uploader,
thumbnail: info.thumbnail,
source: SongSource::YouTube(info.webpage_url),
}
}
}
@ -170,6 +183,7 @@ impl From<PlaylistEntry> for Song {
title: entry.title,
author: entry.uploader,
thumbnail: None,
source: SongSource::YouTube(format!("https://www.youtube.com/watch?v={}", entry.url)),
}
}
}
@ -177,31 +191,17 @@ impl From<PlaylistEntry> for Song {
impl From<Track> for Song {
fn from(track: Track) -> Self {
Self {
title: track.name,
author: track
.artists
.into_iter()
.map(|a| a.name)
.collect::<Vec<String>>()
.join(" & "),
url: None,
thumbnail: None,
}
}
}
impl From<TrackSimplified> for Song {
fn from(track: TrackSimplified) -> Self {
Self {
title: track.name,
title: track.name.clone(),
author: track
.clone()
.artists
.into_iter()
.map(|a| a.name)
.map(|a| a.name.clone())
.collect::<Vec<String>>()
.join(" & "),
url: None,
thumbnail: None,
source: SongSource::Spotify(track),
}
}
}
@ -211,8 +211,9 @@ impl From<YoutubeSong> for Song {
Self {
title: song.title,
author: song.artist,
url: Some(song.url),
url: Some(song.url.clone()),
thumbnail: None,
source: SongSource::YouTube(song.url),
}
}
}

@ -123,7 +123,7 @@ impl SpotifyApi {
}
/// Returns song entity for a given spotify url
pub async fn get_song_name(&self, url: &str) -> BotResult<Track> {
pub async fn get_track_for_url(&self, url: &str) -> BotResult<Track> {
log::debug!("Getting song for {}", url);
let id = self.get_id_for_url(url)?;
let track = self.client.tracks().get_track(&*id, None).await?.data;

Loading…
Cancel
Save