Browse Source

Implement fair queueing

Frans Bergman 2 years ago
parent
commit
ac11c8079b
4 changed files with 74 additions and 33 deletions
  1. 1 1
      src/audio/audio.rs
  2. 7 7
      src/audio/audio_state.rs
  3. 6 2
      src/audio/song.rs
  4. 60 23
      src/audio/song_queue.rs

+ 1 - 1
src/audio/audio.rs

@@ -156,7 +156,7 @@ async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
         None => return Ok(()),
     };
 
-    match Song::from_query(query).await {
+    match Song::from_query(msg.author.clone(), query).await {
         Ok(song) => {
             AudioState::add_audio(audio_state, song).await;
             message_react(ctx, msg, "✅").await;

+ 7 - 7
src/audio/audio_state.rs

@@ -74,7 +74,7 @@ impl AudioState {
             let mut current_song = audio_state.current_song.lock().await;
             current_song.take()
         } else {
-            queue.pop().await
+            queue.pop()
         };
         drop(is_looping);
 
@@ -113,7 +113,7 @@ impl AudioState {
             panic!("Err AudioState::play_audio: {:?}", why);
         }
         {
-            let text = song.get_string().await;
+            let text = song.get_string();
             let channel_id = audio_state.channel_id.lock().await;
             let http = audio_state.http.lock().await;
             send_embed_http(
@@ -131,7 +131,7 @@ impl AudioState {
 
     pub async fn add_audio(audio_state: Arc<AudioState>, song: Song) {
         let mut queue = audio_state.queue.lock().await;
-        queue.push(vec![song]).await;
+        queue.push(vec![song]);
         let current_song = audio_state.current_song.lock().await;
         if current_song.is_none() {
             let audio_state = audio_state.clone();
@@ -157,12 +157,12 @@ impl AudioState {
 
     pub async fn shuffle(audio_state: Arc<AudioState>) -> Result<(), String> {
         let mut queue = audio_state.queue.lock().await;
-        queue.shuffle().await
+        queue.shuffle()
     }
 
     pub async fn clear(audio_state: Arc<AudioState>) -> Result<(), String> {
         let mut queue = audio_state.queue.lock().await;
-        queue.clear().await
+        queue.clear()
     }
 
     // on success, returns a bool that specifies whether the queue is now being looped
@@ -181,14 +181,14 @@ impl AudioState {
     pub async fn get_string(audio_state: Arc<AudioState>) -> String {
         let current_song = audio_state.current_song.lock().await;
         let current_song = match &*current_song {
-            Some(song) => song.get_string().await,
+            Some(song) => song.get_string(),
             None => "*Not playing*\n".to_string(),
         };
         let queue = audio_state.queue.lock().await;
         format!(
             "**Current Song:**\n{}\n\n**Queue:**\n{}",
             current_song,
-            queue.get_string().await
+            queue.get_string()
         )
     }
 }

+ 6 - 2
src/audio/song.rs

@@ -1,14 +1,17 @@
 use super::subprocess::ytdl;
+use serenity::model::user::User;
 
+#[derive(Clone)]
 pub struct Song {
     pub url: String,
     pub artist: Option<String>,
     pub title: Option<String>,
     pub duration: Option<u64>,
+    pub queuer: User,
 }
 
 impl Song {
-    pub async fn from_query(query: &str) -> Result<Song, std::io::Error> {
+    pub async fn from_query(user: User, query: &str) -> Result<Song, std::io::Error> {
         let query = if query.contains("watch?v=") {
             query.to_string()
         } else {
@@ -22,11 +25,12 @@ impl Song {
             title: video["title"].as_str().map(|t| t.to_string()),
             url: video["url"].as_str().unwrap().to_string(),
             duration: video["duration"].as_u64(),
+            queuer: user,
         };
         Ok(song)
     }
 
-    pub async fn get_string(&self) -> String {
+    pub fn get_string(&self) -> String {
         let artist = self.artist.clone().unwrap_or("unknown".to_string());
         let title = self.title.clone().unwrap_or("unknown".to_string());
         let duration = match self.duration {

+ 60 - 23
src/audio/song_queue.rs

@@ -1,57 +1,94 @@
 use super::song::Song;
 use rand::seq::SliceRandom;
+use serenity::model::id::UserId;
+use std::collections::HashMap;
 use std::{cmp::min, collections::VecDeque};
 
+#[derive(Clone)]
 pub struct SongQueue {
-    queue: VecDeque<Song>,
+    queues: HashMap<UserId, VecDeque<Song>>,
+    users: VecDeque<UserId>,
 }
 
 impl SongQueue {
     pub fn new() -> SongQueue {
         SongQueue {
-            queue: VecDeque::new(),
+            queues: HashMap::new(),
+            users: VecDeque::new(),
         }
     }
-    pub async fn push(&mut self, songs: Vec<Song>) {
-        for item in songs.into_iter() {
-            self.queue.push_back(item);
+    fn new_user_queue(&mut self, user: UserId) {
+        if self.queues.get(&user).is_none() {
+            self.queues.insert(user, VecDeque::new());
+        }
+        if !self.users.contains(&user) {
+            self.users.push_back(user);
+        }
+    }
+    pub fn push(&mut self, songs: Vec<Song>) {
+        for song in songs.into_iter() {
+            self.new_user_queue(song.queuer.id);
+            let deque = self.queues.get_mut(&song.queuer.id).unwrap();
+            deque.push_back(song);
         }
     }
-    pub async fn pop(&mut self) -> Option<Song> {
-        self.queue.pop_front()
+    pub fn pop(&mut self) -> Option<Song> {
+        let user = match self.users.pop_front() {
+            Some(user) => user,
+            None => return None,
+        };
+        let deque = self.queues.get_mut(&user).unwrap();
+        let song = deque.pop_front();
+        if deque.len() > 0 {
+            self.users.push_back(user);
+        }
+        song
     }
-    pub async fn shuffle(&mut self) -> Result<(), String> {
-        if self.queue.len() == 0 {
+    pub fn shuffle(&mut self) -> Result<(), String> {
+        if self.users.len() == 0 {
             return Err("queue is empty".to_string());
         }
-        self.queue
-            .make_contiguous()
-            .shuffle(&mut rand::thread_rng());
+        for deque in self.queues.values_mut() {
+            deque.make_contiguous().shuffle(&mut rand::thread_rng());
+        }
 
         Ok(())
     }
-    pub async fn clear(&mut self) -> Result<(), String> {
-        if self.queue.len() == 0 {
+    pub fn clear(&mut self) -> Result<(), String> {
+        if self.users.len() == 0 {
             return Err("queue is empty".to_string());
         };
-        self.queue.clear();
+        self.queues.clear();
+        self.users.clear();
         Ok(())
     }
-    pub async fn get_string(&self) -> String {
-        if self.queue.len() == 0 {
+    pub fn get_string(&self) -> String {
+        if self.users.len() == 0 {
             return "*empty*".to_string();
         };
         let mut s = String::new();
+        let queue = self.as_vec();
         s.push_str(&format!(
             "*Showing {} of {} songs*\n",
-            min(20, self.queue.len()),
-            self.queue.len()
+            min(20, queue.len()),
+            queue.len()
         ));
-        for (i, song) in self.queue.iter().take(20).enumerate() {
-            s += &format!("{}: ", i);
-            s += &song.get_string().await;
-            s += "\n";
+        for (i, song) in queue.iter().take(20).enumerate() {
+            s += &format!(
+                "{}: {} (queued by {})\n",
+                i + 1,
+                &song.get_string(),
+                song.queuer.name
+            );
         }
         s
     }
+    fn as_vec(&self) -> Vec<Song> {
+        let mut clone = self.clone();
+        let mut queue = vec![];
+        while let Some(song) = clone.pop() {
+            queue.push(song);
+        }
+        queue
+    }
 }