Updates, bugfixes. You name it.
This commit is contained in:
		
							parent
							
								
									9798a178d7
								
							
						
					
					
						commit
						b7c85455ce
					
				| @ -143,7 +143,13 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu | ||||
|     } else { | ||||
|       // Create the session, and handle potential errors
 | ||||
|       if let Err(why) = session_manager | ||||
|         .create_session(&ctx, guild.id, channel_id, command.user.id) | ||||
|         .create_session( | ||||
|           &ctx, | ||||
|           guild.id, | ||||
|           channel_id, | ||||
|           command.channel_id, | ||||
|           command.user.id, | ||||
|         ) | ||||
|         .await | ||||
|       { | ||||
|         // Need to link first
 | ||||
| @ -188,7 +194,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu | ||||
|         .icon_url("https://spoticord.com/static/image/speaker.png") | ||||
|         .description(format!("Come listen along in <#{}>", channel_id)) | ||||
|         .footer("Spotify will automatically start playing on Spoticord") | ||||
|         .status(Status::Success) | ||||
|         .status(Status::Info) | ||||
|         .build(), | ||||
|     ) | ||||
|     .await; | ||||
|  | ||||
| @ -1,3 +1,7 @@ | ||||
| pub const VERSION: &str = env!("CARGO_PKG_VERSION"); | ||||
| pub const MOTD: &str = "OPEN BETA (v2)"; | ||||
| 
 | ||||
| /// The time it takes for Spoticord to disconnect when no music is being played
 | ||||
| pub const DISCONNECT_TIME: u64 = 5 * 60; | ||||
| 
 | ||||
| // pub const MOTD: &str = "some good 'ol music";
 | ||||
|  | ||||
| @ -41,14 +41,6 @@ async fn main() { | ||||
| 
 | ||||
|   env_logger::init(); | ||||
| 
 | ||||
|   let orig_hook = std::panic::take_hook(); | ||||
|   std::panic::set_hook(Box::new(move |panic_info| { | ||||
|     error!("Panic: {}", panic_info); | ||||
| 
 | ||||
|     orig_hook(panic_info); | ||||
|     std::process::exit(1); | ||||
|   })); | ||||
| 
 | ||||
|   let args: Vec<String> = env::args().collect(); | ||||
| 
 | ||||
|   if args.len() > 2 { | ||||
| @ -143,6 +135,7 @@ async fn main() { | ||||
|         } | ||||
| 
 | ||||
|         _ = async { | ||||
|           #[cfg(unix)] | ||||
|           match term { | ||||
|             Some(ref mut term) => { | ||||
|               let term = term.downcast_mut::<tokio::signal::unix::Signal>().unwrap(); | ||||
|  | ||||
| @ -66,7 +66,10 @@ impl SpoticordPlayer { | ||||
|     self.session = Some(session.clone()); | ||||
| 
 | ||||
|     // Volume mixer
 | ||||
|     let mixer = (mixer::find(Some("softvol")).unwrap())(MixerConfig::default()); | ||||
|     let mixer = (mixer::find(Some("softvol")).unwrap())(MixerConfig { | ||||
|       volume_ctrl: librespot::playback::config::VolumeCtrl::Linear, | ||||
|       ..MixerConfig::default() | ||||
|     }); | ||||
| 
 | ||||
|     let client = self.client.clone(); | ||||
| 
 | ||||
| @ -47,10 +47,12 @@ impl SessionManager { | ||||
|     ctx: &Context, | ||||
|     guild_id: GuildId, | ||||
|     channel_id: ChannelId, | ||||
|     text_channel_id: ChannelId, | ||||
|     owner_id: UserId, | ||||
|   ) -> Result<(), SessionCreateError> { | ||||
|     // Create session first to make sure locks are kept for as little time as possible
 | ||||
|     let session = SpoticordSession::new(ctx, guild_id, channel_id, owner_id).await?; | ||||
|     let session = | ||||
|       SpoticordSession::new(ctx, guild_id, channel_id, text_channel_id, owner_id).await?; | ||||
| 
 | ||||
|     let mut sessions = self.sessions.write().await; | ||||
|     let mut owner_map = self.owner_map.write().await; | ||||
|  | ||||
| @ -1,14 +1,19 @@ | ||||
| use self::manager::{SessionCreateError, SessionManager}; | ||||
| use self::{ | ||||
|   manager::{SessionCreateError, SessionManager}, | ||||
|   pbi::PlaybackInfo, | ||||
| }; | ||||
| use crate::{ | ||||
|   consts::DISCONNECT_TIME, | ||||
|   database::{Database, DatabaseError}, | ||||
|   ipc::{self, packet::IpcPacket, Client}, | ||||
|   utils::{self, spotify}, | ||||
|   utils::{embed::Status, spotify}, | ||||
| }; | ||||
| use ipc_channel::ipc::{IpcError, TryRecvError}; | ||||
| use librespot::core::spotify_id::{SpotifyAudioType, SpotifyId}; | ||||
| use log::*; | ||||
| use serenity::{ | ||||
|   async_trait, | ||||
|   http::Http, | ||||
|   model::prelude::{ChannelId, GuildId, UserId}, | ||||
|   prelude::{Context, RwLock}, | ||||
| }; | ||||
| @ -26,114 +31,16 @@ use std::{ | ||||
| use tokio::sync::Mutex; | ||||
| 
 | ||||
| pub mod manager; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct PlaybackInfo { | ||||
|   last_updated: u128, | ||||
|   position_ms: u32, | ||||
| 
 | ||||
|   pub track: Option<spotify::Track>, | ||||
|   pub episode: Option<spotify::Episode>, | ||||
|   pub spotify_id: Option<SpotifyId>, | ||||
| 
 | ||||
|   pub duration_ms: u32, | ||||
|   pub is_playing: bool, | ||||
| } | ||||
| 
 | ||||
| impl PlaybackInfo { | ||||
|   fn new(duration_ms: u32, position_ms: u32, is_playing: bool) -> Self { | ||||
|     Self { | ||||
|       last_updated: utils::get_time_ms(), | ||||
|       track: None, | ||||
|       episode: None, | ||||
|       spotify_id: None, | ||||
|       duration_ms, | ||||
|       position_ms, | ||||
|       is_playing, | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Update position, duration and playback state
 | ||||
|   async fn update_pos_dur(&mut self, position_ms: u32, duration_ms: u32, is_playing: bool) { | ||||
|     self.position_ms = position_ms; | ||||
|     self.duration_ms = duration_ms; | ||||
|     self.is_playing = is_playing; | ||||
| 
 | ||||
|     self.last_updated = utils::get_time_ms(); | ||||
|   } | ||||
| 
 | ||||
|   // Update spotify id, track and episode
 | ||||
|   fn update_track_episode( | ||||
|     &mut self, | ||||
|     spotify_id: SpotifyId, | ||||
|     track: Option<spotify::Track>, | ||||
|     episode: Option<spotify::Episode>, | ||||
|   ) { | ||||
|     self.spotify_id = Some(spotify_id); | ||||
|     self.track = track; | ||||
|     self.episode = episode; | ||||
|   } | ||||
| 
 | ||||
|   pub fn get_position(&self) -> u32 { | ||||
|     if self.is_playing { | ||||
|       let now = utils::get_time_ms(); | ||||
|       let diff = now - self.last_updated; | ||||
| 
 | ||||
|       self.position_ms + diff as u32 | ||||
|     } else { | ||||
|       self.position_ms | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   pub fn get_name(&self) -> Option<String> { | ||||
|     if let Some(track) = &self.track { | ||||
|       Some(track.name.clone()) | ||||
|     } else if let Some(episode) = &self.episode { | ||||
|       Some(episode.name.clone()) | ||||
|     } else { | ||||
|       None | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   pub fn get_artists(&self) -> Option<String> { | ||||
|     if let Some(track) = &self.track { | ||||
|       Some( | ||||
|         track | ||||
|           .artists | ||||
|           .iter() | ||||
|           .map(|a| a.name.clone()) | ||||
|           .collect::<Vec<String>>() | ||||
|           .join(", "), | ||||
|       ) | ||||
|     } else if let Some(episode) = &self.episode { | ||||
|       Some(episode.show.name.clone()) | ||||
|     } else { | ||||
|       None | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   pub fn get_thumbnail_url(&self) -> Option<String> { | ||||
|     if let Some(track) = &self.track { | ||||
|       let mut images = track.album.images.clone(); | ||||
|       images.sort_by(|a, b| b.width.cmp(&a.width)); | ||||
| 
 | ||||
|       Some(images.get(0).unwrap().url.clone()) | ||||
|     } else if let Some(episode) = &self.episode { | ||||
|       let mut images = episode.show.images.clone(); | ||||
|       images.sort_by(|a, b| b.width.cmp(&a.width)); | ||||
| 
 | ||||
|       Some(images.get(0).unwrap().url.clone()) | ||||
|     } else { | ||||
|       None | ||||
|     } | ||||
|   } | ||||
| } | ||||
| mod pbi; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct SpoticordSession { | ||||
|   owner: Arc<RwLock<Option<UserId>>>, | ||||
|   guild_id: GuildId, | ||||
|   channel_id: ChannelId, | ||||
|   text_channel_id: ChannelId, | ||||
| 
 | ||||
|   http: Arc<Http>, | ||||
| 
 | ||||
|   session_manager: SessionManager, | ||||
| 
 | ||||
| @ -150,6 +57,7 @@ impl SpoticordSession { | ||||
|     ctx: &Context, | ||||
|     guild_id: GuildId, | ||||
|     channel_id: ChannelId, | ||||
|     text_channel_id: ChannelId, | ||||
|     owner_id: UserId, | ||||
|   ) -> Result<SpoticordSession, SessionCreateError> { | ||||
|     // Get the Spotify token of the owner
 | ||||
| @ -237,6 +145,8 @@ impl SpoticordSession { | ||||
|       owner: Arc::new(RwLock::new(Some(owner_id.clone()))), | ||||
|       guild_id, | ||||
|       channel_id, | ||||
|       text_channel_id, | ||||
|       http: ctx.http.clone(), | ||||
|       session_manager: session_manager.clone(), | ||||
|       call: call.clone(), | ||||
|       track: track_handle.clone(), | ||||
| @ -336,6 +246,8 @@ impl SpoticordSession { | ||||
|           } | ||||
| 
 | ||||
|           IpcPacket::Paused(track, position_ms, duration_ms) => { | ||||
|             ipc_instance.start_disconnect_timer().await; | ||||
| 
 | ||||
|             // Convert to SpotifyId
 | ||||
|             let track_id = SpotifyId::from_uri(&track).unwrap(); | ||||
| 
 | ||||
| @ -547,6 +459,28 @@ impl SpoticordSession { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /// Disconnect from voice channel with a message
 | ||||
|   pub async fn disconnect_with_message(&self, content: &str) { | ||||
|     self.disconnect().await; | ||||
| 
 | ||||
|     if let Err(why) = self | ||||
|       .text_channel_id | ||||
|       .send_message(&self.http, |message| { | ||||
|         message.embed(|embed| { | ||||
|           embed.title("Disconnected from voice channel"); | ||||
|           embed.description(content); | ||||
|           embed.color(Status::Warning as u64); | ||||
| 
 | ||||
|           embed | ||||
|         }) | ||||
|       }) | ||||
|       .await | ||||
|       .map(|_| ()) | ||||
|     { | ||||
|       error!("Failed to send disconnect message: {:?}", why); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Update playback info (duration, position, playing state)
 | ||||
|   async fn update_playback(&self, duration_ms: u32, position_ms: u32, playing: bool) -> bool { | ||||
|     let is_none = { | ||||
| @ -572,6 +506,44 @@ impl SpoticordSession { | ||||
|     is_none | ||||
|   } | ||||
| 
 | ||||
|   /// Start the disconnect timer, which will disconnect the bot from the voice channel after a
 | ||||
|   /// certain amount of time
 | ||||
|   async fn start_disconnect_timer(&self) { | ||||
|     let pbi = self.playback_info.clone(); | ||||
|     let instance = self.clone(); | ||||
| 
 | ||||
|     tokio::spawn(async move { | ||||
|       let mut timer = tokio::time::interval(Duration::from_secs(DISCONNECT_TIME)); | ||||
| 
 | ||||
|       // Ignore first (immediate) tick
 | ||||
|       timer.tick().await; | ||||
| 
 | ||||
|       loop { | ||||
|         timer.tick().await; | ||||
| 
 | ||||
|         let is_playing = { | ||||
|           let pbi = pbi.read().await; | ||||
| 
 | ||||
|           if let Some(pbi) = &*pbi { | ||||
|             pbi.is_playing | ||||
|           } else { | ||||
|             false | ||||
|           } | ||||
|         }; | ||||
| 
 | ||||
|         if !is_playing { | ||||
|           info!("Player is not playing, disconnecting"); | ||||
|           instance | ||||
|             .disconnect_with_message( | ||||
|               "The player has been inactive for too long, and has been disconnected.", | ||||
|             ) | ||||
|             .await; | ||||
|           break; | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   // Get the playback info for the current track
 | ||||
|   pub async fn get_playback_info(&self) -> Option<PlaybackInfo> { | ||||
|     self.playback_info.read().await.clone() | ||||
|  | ||||
							
								
								
									
										110
									
								
								src/session/pbi.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/session/pbi.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | ||||
| use librespot::core::spotify_id::SpotifyId; | ||||
| 
 | ||||
| use crate::utils::{self, spotify}; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct PlaybackInfo { | ||||
|   last_updated: u128, | ||||
|   position_ms: u32, | ||||
| 
 | ||||
|   pub track: Option<spotify::Track>, | ||||
|   pub episode: Option<spotify::Episode>, | ||||
|   pub spotify_id: Option<SpotifyId>, | ||||
| 
 | ||||
|   pub duration_ms: u32, | ||||
|   pub is_playing: bool, | ||||
| } | ||||
| 
 | ||||
| impl PlaybackInfo { | ||||
|   /// Create a new instance of PlaybackInfo
 | ||||
|   pub fn new(duration_ms: u32, position_ms: u32, is_playing: bool) -> Self { | ||||
|     Self { | ||||
|       last_updated: utils::get_time_ms(), | ||||
|       track: None, | ||||
|       episode: None, | ||||
|       spotify_id: None, | ||||
|       duration_ms, | ||||
|       position_ms, | ||||
|       is_playing, | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /// Update position, duration and playback state
 | ||||
|   pub async fn update_pos_dur(&mut self, position_ms: u32, duration_ms: u32, is_playing: bool) { | ||||
|     self.position_ms = position_ms; | ||||
|     self.duration_ms = duration_ms; | ||||
|     self.is_playing = is_playing; | ||||
| 
 | ||||
|     self.last_updated = utils::get_time_ms(); | ||||
|   } | ||||
| 
 | ||||
|   /// Update spotify id, track and episode
 | ||||
|   pub fn update_track_episode( | ||||
|     &mut self, | ||||
|     spotify_id: SpotifyId, | ||||
|     track: Option<spotify::Track>, | ||||
|     episode: Option<spotify::Episode>, | ||||
|   ) { | ||||
|     self.spotify_id = Some(spotify_id); | ||||
|     self.track = track; | ||||
|     self.episode = episode; | ||||
|   } | ||||
| 
 | ||||
|   /// Get the current playback position
 | ||||
|   pub fn get_position(&self) -> u32 { | ||||
|     if self.is_playing { | ||||
|       let now = utils::get_time_ms(); | ||||
|       let diff = now - self.last_updated; | ||||
| 
 | ||||
|       self.position_ms + diff as u32 | ||||
|     } else { | ||||
|       self.position_ms | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /// Get the name of the track or episode
 | ||||
|   pub fn get_name(&self) -> Option<String> { | ||||
|     if let Some(track) = &self.track { | ||||
|       Some(track.name.clone()) | ||||
|     } else if let Some(episode) = &self.episode { | ||||
|       Some(episode.name.clone()) | ||||
|     } else { | ||||
|       None | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /// Get the artist(s) or show name of the current track
 | ||||
|   pub fn get_artists(&self) -> Option<String> { | ||||
|     if let Some(track) = &self.track { | ||||
|       Some( | ||||
|         track | ||||
|           .artists | ||||
|           .iter() | ||||
|           .map(|a| a.name.clone()) | ||||
|           .collect::<Vec<String>>() | ||||
|           .join(", "), | ||||
|       ) | ||||
|     } else if let Some(episode) = &self.episode { | ||||
|       Some(episode.show.name.clone()) | ||||
|     } else { | ||||
|       None | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /// Get the album art url
 | ||||
|   pub fn get_thumbnail_url(&self) -> Option<String> { | ||||
|     if let Some(track) = &self.track { | ||||
|       let mut images = track.album.images.clone(); | ||||
|       images.sort_by(|a, b| b.width.cmp(&a.width)); | ||||
| 
 | ||||
|       Some(images.get(0).unwrap().url.clone()) | ||||
|     } else if let Some(episode) = &self.episode { | ||||
|       let mut images = episode.show.images.clone(); | ||||
|       images.sort_by(|a, b| b.width.cmp(&a.width)); | ||||
| 
 | ||||
|       Some(images.get(0).unwrap().url.clone()) | ||||
|     } else { | ||||
|       None | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -1,6 +1,5 @@ | ||||
| use serenity::builder::CreateEmbed; | ||||
| 
 | ||||
| #[allow(dead_code)] | ||||
| pub enum Status { | ||||
|   Info = 0x0773D6, | ||||
|   Success = 0x3BD65D, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DaXcess
						DaXcess