Updates, bugfixes. You name it.
This commit is contained in:
1 parent
9798a178d7
commit
b7c85455ce
9 files changed
+206
-117
No files matched your search
@@ -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;
|
||||
|
||||
+76
-104
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user