Browse Source

Clean up

Remove Spotify and the YouTube loader.

Add YouTube titles to queue.
Frans Bergman 3 years ago
parent
commit
e6640bf347

+ 74 - 180
src/audio/audio.rs

@@ -1,40 +1,36 @@
-use std::{
-    sync::Arc,
-    collections::HashMap,
-};
+use super::audio_state::AudioState;
+use lazy_static::lazy_static;
 use serenity::{
 use serenity::{
-    client::Context, 
+    client::Context,
     framework::standard::{
     framework::standard::{
-        Args, CommandResult,
         macros::{command, group},
         macros::{command, group},
+        Args, CommandResult,
     },
     },
-    model::{channel::Message, id::GuildId}
+    model::{channel::Message, id::GuildId},
 };
 };
 use songbird::tracks::TrackCommand;
 use songbird::tracks::TrackCommand;
-use tokio::{
-    sync::Mutex,
-};
-use lazy_static::lazy_static;
-use super::{
-    audio_state::AudioState,
-};
+use std::{collections::HashMap, sync::Arc};
+use tokio::sync::Mutex;
 
 
-use super::config::audio as config_audio;
-
-use crate::util::{
-    send_message,
-    send_embed,
-    message_react,
-};
+use crate::util::{message_react, send_embed};
 
 
 #[group]
 #[group]
-#[commands(join,disconnect,play,splay,cure,extend,skip,pause,resume,change_loop,shuffle,clear,queue)]
+#[commands(
+    join,
+    disconnect,
+    play,
+    skip,
+    pause,
+    resume,
+    change_loop,
+    shuffle,
+    clear,
+    queue
+)]
 struct Audio;
 struct Audio;
 
 
 lazy_static! {
 lazy_static! {
-    static ref AUDIO_STATES: Mutex<HashMap<GuildId, Arc<AudioState>>> = {
-        Mutex::new(HashMap::new())
-    };
+    static ref AUDIO_STATES: Mutex<HashMap<GuildId, Arc<AudioState>>> = Mutex::new(HashMap::new());
 }
 }
 
 
 async fn get_audio_state(ctx: &Context, msg: &Message) -> Option<Arc<AudioState>> {
 async fn get_audio_state(ctx: &Context, msg: &Message) -> Option<Arc<AudioState>> {
@@ -54,19 +50,16 @@ async fn get_audio_state(ctx: &Context, msg: &Message) -> Option<Arc<AudioState>
                 .voice_states
                 .voice_states
                 .get(&msg.author.id)
                 .get(&msg.author.id)
                 .and_then(|voice_state| voice_state.channel_id);
                 .and_then(|voice_state| voice_state.channel_id);
-            let channel_id = match channel_id{
+            let channel_id = match channel_id {
                 Some(channel_id) => channel_id,
                 Some(channel_id) => channel_id,
                 None => {
                 None => {
                     send_embed(ctx, msg, "Error: please be in a voice channel").await;
                     send_embed(ctx, msg, "Error: please be in a voice channel").await;
                     return None;
                     return None;
                 }
                 }
             };
             };
-            let manager = songbird::get(ctx)
-                .await
-                .unwrap()
-                .clone();
+            let manager = songbird::get(ctx).await.unwrap().clone();
             let (handle_lock, success) = manager.join(guild_id, channel_id).await;
             let (handle_lock, success) = manager.join(guild_id, channel_id).await;
-            if let Err(err) = success{
+            if let Err(err) = success {
                 println!("Error: {:?}", err);
                 println!("Error: {:?}", err);
                 return None;
                 return None;
             }
             }
@@ -84,19 +77,30 @@ async fn remove_audio_state(ctx: &Context, msg: &Message) -> Result<(), String>
     let guild_id = guild.id;
     let guild_id = guild.id;
 
 
     let mut audio_states = AUDIO_STATES.lock().await;
     let mut audio_states = AUDIO_STATES.lock().await;
+    let manager = songbird::get(ctx).await.unwrap();
+    if manager
+        .get(guild_id)
+        .unwrap()
+        .lock()
+        .await
+        .leave()
+        .await
+        .is_err()
+    {
+        return Err("Could not leave channel".to_string());
+    }
 
 
-    if let Some(state) = audio_states.remove(&guild_id){
-        AudioState::cleanup(state).await;
+    if audio_states.remove(&guild_id).is_some() {
         Ok(())
         Ok(())
-    }else{
+    } else {
         Err("bot is not currently active".to_string())
         Err("bot is not currently active".to_string())
     }
     }
 }
 }
 
 
 #[command]
 #[command]
-async fn join(ctx: &Context, msg: &Message) -> CommandResult{
+async fn join(ctx: &Context, msg: &Message) -> CommandResult {
     let audio_state = get_audio_state(ctx, msg).await;
     let audio_state = get_audio_state(ctx, msg).await;
-    if audio_state.is_some(){
+    if audio_state.is_some() {
         message_react(ctx, msg, "🥳").await;
         message_react(ctx, msg, "🥳").await;
     }
     }
 
 
@@ -105,8 +109,8 @@ async fn join(ctx: &Context, msg: &Message) -> CommandResult{
 
 
 #[command]
 #[command]
 #[aliases("leave")]
 #[aliases("leave")]
-async fn disconnect(ctx: &Context, msg: &Message) -> CommandResult{
-    match remove_audio_state(ctx, msg).await{
+async fn disconnect(ctx: &Context, msg: &Message) -> CommandResult {
+    match remove_audio_state(ctx, msg).await {
         Ok(()) => message_react(ctx, msg, "👋").await,
         Ok(()) => message_react(ctx, msg, "👋").await,
         Err(why) => send_embed(ctx, msg, &format!("Error: {}", why)).await,
         Err(why) => send_embed(ctx, msg, &format!("Error: {}", why)).await,
     };
     };
@@ -115,175 +119,99 @@ async fn disconnect(ctx: &Context, msg: &Message) -> CommandResult{
 }
 }
 
 
 #[command]
 #[command]
-async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult{
-    let query = args.rest();
-
-    let audio_state = get_audio_state(ctx, msg).await;
-    let audio_state = match audio_state{
-        Some(audio_state) => audio_state,
-        None => return Ok(())
-    };
-
-    AudioState::add_audio(audio_state, query, false).await;
-
-    message_react(ctx, msg, "🎶").await;
-
-    Ok(())
-}
-
-#[command]
-async fn splay(ctx: &Context, msg: &Message, args: Args) -> CommandResult{
+async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
     let query = args.rest();
     let query = args.rest();
 
 
     let audio_state = get_audio_state(ctx, msg).await;
     let audio_state = get_audio_state(ctx, msg).await;
-    let audio_state = match audio_state{
-        Some(audio_state) => audio_state,
-        None => return Ok(())
-    };
-
-    AudioState::add_audio(audio_state, query, true).await;
-
-    message_react(ctx, msg, "🔀").await;
-    message_react(ctx, msg, "🎶").await;
-
-    Ok(())
-}
-
-#[command]
-async fn cure(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult{
-    let query= match args.single::<String>(){
-        Ok(query) => query,
-        Err(_) => {
-            send_embed(ctx, msg, "Error: invalid Spotify playlist").await;
-            return Ok(())
-        }
-    };
-    let amount = match args.single::<usize>(){
-        Ok(amount) => amount,
-        Err(_) => {
-            20
-        }
-    };
-
-    let audio_state = get_audio_state(ctx, msg).await;
-    let audio_state = match audio_state{
+    let audio_state = match audio_state {
         Some(audio_state) => audio_state,
         Some(audio_state) => audio_state,
-        None => return Ok(())
+        None => return Ok(()),
     };
     };
 
 
-    AudioState::add_recommended_songs(audio_state, &query, amount).await;
-
-    message_react(ctx, msg, "🍻").await;
     message_react(ctx, msg, "🎶").await;
     message_react(ctx, msg, "🎶").await;
 
 
-    Ok(())
-}
-
-#[command]
-async fn extend(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult{
-    let query= match args.single::<String>(){
-        Ok(query) => query,
-        Err(_) => {
-            send_embed(ctx, msg, "Error: invalid Spotify playlist").await;
-            return Ok(())
-        }
-    };
-    let extend_ratio = match args.single::<f64>(){
-        Ok(amount) => amount,
-        Err(_) => {
-            config_audio::EXTEND_RATIO
-        }
-    };
-
-    let audio_state = get_audio_state(ctx, msg).await;
-    let audio_state = match audio_state{
-        Some(audio_state) => audio_state,
-        None => return Ok(())
-    };
+    AudioState::add_audio(audio_state, query).await;
 
 
-    AudioState::extend_songs(audio_state, &query, extend_ratio).await;
-
-    message_react(ctx, msg, "🍻").await;
-    message_react(ctx, msg, "🎶").await;
+    message_react(ctx, msg, "✅").await;
 
 
     Ok(())
     Ok(())
 }
 }
 
 
 #[command]
 #[command]
-async fn skip(ctx: &Context, msg: &Message) -> CommandResult{
+async fn skip(ctx: &Context, msg: &Message) -> CommandResult {
     let audio_state = get_audio_state(ctx, msg).await;
     let audio_state = get_audio_state(ctx, msg).await;
-    let audio_state = match audio_state{
+    let audio_state = match audio_state {
         Some(audio_state) => audio_state,
         Some(audio_state) => audio_state,
-        None => return Ok(())
+        None => return Ok(()),
     };
     };
 
 
     if let Err(why) = AudioState::send_track_command(audio_state, TrackCommand::Stop).await {
     if let Err(why) = AudioState::send_track_command(audio_state, TrackCommand::Stop).await {
         send_embed(ctx, msg, &format!("Error: {}", why)).await;
         send_embed(ctx, msg, &format!("Error: {}", why)).await;
-    }else {
+    } else {
         message_react(ctx, msg, "↪").await;
         message_react(ctx, msg, "↪").await;
     };
     };
     Ok(())
     Ok(())
 }
 }
 
 
 #[command]
 #[command]
-async fn pause(ctx: &Context, msg: &Message) -> CommandResult{
+async fn pause(ctx: &Context, msg: &Message) -> CommandResult {
     let audio_state = get_audio_state(ctx, msg).await;
     let audio_state = get_audio_state(ctx, msg).await;
-    let audio_state = match audio_state{
+    let audio_state = match audio_state {
         Some(audio_state) => audio_state,
         Some(audio_state) => audio_state,
-        None => return Ok(())
+        None => return Ok(()),
     };
     };
 
 
     if let Err(why) = AudioState::send_track_command(audio_state, TrackCommand::Pause).await {
     if let Err(why) = AudioState::send_track_command(audio_state, TrackCommand::Pause).await {
         send_embed(ctx, msg, &format!("Error: {}", why)).await;
         send_embed(ctx, msg, &format!("Error: {}", why)).await;
-    }else {
+    } else {
         message_react(ctx, msg, "⏸").await;
         message_react(ctx, msg, "⏸").await;
     };
     };
     Ok(())
     Ok(())
 }
 }
 
 
 #[command]
 #[command]
-async fn resume(ctx: &Context, msg: &Message) -> CommandResult{
+async fn resume(ctx: &Context, msg: &Message) -> CommandResult {
     let audio_state = get_audio_state(ctx, msg).await;
     let audio_state = get_audio_state(ctx, msg).await;
-    let audio_state = match audio_state{
+    let audio_state = match audio_state {
         Some(audio_state) => audio_state,
         Some(audio_state) => audio_state,
-        None => return Ok(())
+        None => return Ok(()),
     };
     };
 
 
     if let Err(why) = AudioState::send_track_command(audio_state, TrackCommand::Play).await {
     if let Err(why) = AudioState::send_track_command(audio_state, TrackCommand::Play).await {
         send_embed(ctx, msg, &format!("Error: {}", why)).await;
         send_embed(ctx, msg, &format!("Error: {}", why)).await;
-    }else {
+    } else {
         message_react(ctx, msg, "▶").await;
         message_react(ctx, msg, "▶").await;
     };
     };
     Ok(())
     Ok(())
 }
 }
 
 
 #[command]
 #[command]
-async fn shuffle(ctx: &Context, msg: &Message) -> CommandResult{
+async fn shuffle(ctx: &Context, msg: &Message) -> CommandResult {
     let audio_state = get_audio_state(ctx, msg).await;
     let audio_state = get_audio_state(ctx, msg).await;
-    let audio_state = match audio_state{
+    let audio_state = match audio_state {
         Some(audio_state) => audio_state,
         Some(audio_state) => audio_state,
-        None => return Ok(())
+        None => return Ok(()),
     };
     };
 
 
     if let Err(why) = AudioState::shuffle(audio_state).await {
     if let Err(why) = AudioState::shuffle(audio_state).await {
         send_embed(ctx, msg, &format!("Error: {}", why)).await;
         send_embed(ctx, msg, &format!("Error: {}", why)).await;
-    } else{
+    } else {
         message_react(ctx, msg, "🔀").await;
         message_react(ctx, msg, "🔀").await;
     };
     };
     Ok(())
     Ok(())
 }
 }
 
 
 #[command]
 #[command]
-async fn clear(ctx: &Context, msg: &Message) -> CommandResult{
+async fn clear(ctx: &Context, msg: &Message) -> CommandResult {
     let audio_state = get_audio_state(ctx, msg).await;
     let audio_state = get_audio_state(ctx, msg).await;
-    let audio_state = match audio_state{
+    let audio_state = match audio_state {
         Some(audio_state) => audio_state,
         Some(audio_state) => audio_state,
-        None => return Ok(())
+        None => return Ok(()),
     };
     };
 
 
     if let Err(why) = AudioState::clear(audio_state.clone()).await {
     if let Err(why) = AudioState::clear(audio_state.clone()).await {
         send_embed(ctx, msg, &format!("Error: {}", why)).await;
         send_embed(ctx, msg, &format!("Error: {}", why)).await;
-    } else{
+    } else {
         message_react(ctx, msg, "🗑").await;
         message_react(ctx, msg, "🗑").await;
     };
     };
 
 
@@ -292,11 +220,11 @@ async fn clear(ctx: &Context, msg: &Message) -> CommandResult{
 
 
 #[command]
 #[command]
 #[aliases("loop")]
 #[aliases("loop")]
-async fn change_loop(ctx: &Context, msg: &Message) -> CommandResult{
+async fn change_loop(ctx: &Context, msg: &Message) -> CommandResult {
     let audio_state = get_audio_state(ctx, msg).await;
     let audio_state = get_audio_state(ctx, msg).await;
-    let audio_state = match audio_state{
+    let audio_state = match audio_state {
         Some(audio_state) => audio_state,
         Some(audio_state) => audio_state,
-        None => return Ok(())
+        None => return Ok(()),
     };
     };
 
 
     match AudioState::change_looping(audio_state).await {
     match AudioState::change_looping(audio_state).await {
@@ -308,48 +236,14 @@ async fn change_loop(ctx: &Context, msg: &Message) -> CommandResult{
 }
 }
 
 
 #[command]
 #[command]
-async fn queue(ctx: &Context, msg: &Message) -> CommandResult{
+async fn queue(ctx: &Context, msg: &Message) -> CommandResult {
     let audio_state = get_audio_state(ctx, msg).await;
     let audio_state = get_audio_state(ctx, msg).await;
-    let audio_state = match audio_state{
+    let audio_state = match audio_state {
         Some(audio_state) => audio_state,
         Some(audio_state) => audio_state,
-        None => return Ok(())
+        None => return Ok(()),
     };
     };
 
 
-    send_embed(ctx, msg,&AudioState::get_string(audio_state).await).await;
+    send_embed(ctx, msg, &AudioState::get_string(audio_state).await).await;
 
 
     Ok(())
     Ok(())
 }
 }
-/*
-#[command]
-async fn start(ctx: &Context, msg: &Message) -> CommandResult{
-    let c_id = msg.channel_id;
-    let b = ctx.clone();
-    println!("start command");
-    //let a= a.clone();
-    {
-        let b = b.clone();
-        tokio::spawn(async move {
-                spam(c_id, b, 3).await
-            }
-        );
-    }
-    {
-        let b = b.clone();
-        tokio::spawn(async move {
-                spam(c_id, b, 5).await
-            }
-        );
-    }
-    Ok(())
-}
-
-async fn spam(c_id: ChannelId, ctx: Context, duration: u64){
-    loop {
-        let result = c_id.say(&ctx.http, format!("{} seconds passed", duration)).await;
-        if let Err(why) = result {
-            println!("Error sending message {:?}", why);
-        }
-        sleep(Duration::from_secs(duration)).await;
-    }
-}  
-*/

+ 56 - 112
src/audio/audio_state.rs

@@ -1,46 +1,21 @@
-use std::{
-    sync::Arc,
-    mem::drop,
-};
-use rand::seq::SliceRandom;
-use super::{
-    song_queue::SongQueue,
-    song::{
-        Song,
-    },
-    song_searcher::{
-        process_query,
-        song_recommender,
-    },
-    subprocess::ffmpeg_pcm,
-};
+use super::{song::Song, song_queue::SongQueue, subprocess::ffmpeg_pcm};
 use crate::util::send_embed_http;
 use crate::util::send_embed_http;
-use songbird::{Call, Event, EventContext, EventHandler as VoiceEventHandler, TrackEvent, 
-    input::{
-        self,
-        reader::Reader,
-    }, 
-    tracks::{
-        TrackHandle,
-        TrackCommand,
-    }
-
-};
-use tokio::sync::Mutex;
 use serenity::{
 use serenity::{
     async_trait,
     async_trait,
-    prelude::{
-        Mutex as SerenityMutex,
-    },
-    http::Http,
     client::Context,
     client::Context,
-    model::{
-        id::ChannelId,
-        channel::Message,
-    },
+    http::Http,
+    model::{channel::Message, id::ChannelId},
+    prelude::Mutex as SerenityMutex,
 };
 };
+use songbird::{
+    input::{self, reader::Reader},
+    tracks::{TrackCommand, TrackHandle},
+    Call, Event, EventContext, EventHandler as VoiceEventHandler, TrackEvent,
+};
+use std::{mem::drop, sync::Arc};
+use tokio::sync::Mutex;
 
 
-pub struct AudioState{
+pub struct AudioState {
     queue: SongQueue,
     queue: SongQueue,
     handler: Arc<SerenityMutex<Call>>,
     handler: Arc<SerenityMutex<Call>>,
     current_song: Mutex<Option<Song>>,
     current_song: Mutex<Option<Song>>,
@@ -51,9 +26,9 @@ pub struct AudioState{
     http: Mutex<Arc<Http>>,
     http: Mutex<Arc<Http>>,
 }
 }
 
 
-impl AudioState{
-    pub fn new(handler: Arc<SerenityMutex<Call>>, ctx: &Context, msg: &Message) -> Arc<AudioState>{
-        let audio_state = AudioState{
+impl AudioState {
+    pub fn new(handler: Arc<SerenityMutex<Call>>, ctx: &Context, msg: &Message) -> Arc<AudioState> {
+        let audio_state = AudioState {
             queue: SongQueue::new(),
             queue: SongQueue::new(),
             handler,
             handler,
             current_song: Mutex::new(None),
             current_song: Mutex::new(None),
@@ -73,7 +48,7 @@ impl AudioState{
         audio_state
         audio_state
     }
     }
 
 
-    pub async fn set_context(audio_state: Arc<AudioState>, ctx: &Context, msg: &Message){
+    pub async fn set_context(audio_state: Arc<AudioState>, ctx: &Context, msg: &Message) {
         {
         {
             let mut channel_id = audio_state.channel_id.lock().await;
             let mut channel_id = audio_state.channel_id.lock().await;
             *channel_id = msg.channel_id;
             *channel_id = msg.channel_id;
@@ -84,48 +59,51 @@ impl AudioState{
         }
         }
     }
     }
 
 
-    async fn play_audio(audio_state: Arc<AudioState>){
+    async fn play_audio(audio_state: Arc<AudioState>) {
         let is_looping = audio_state.is_looping.lock().await;
         let is_looping = audio_state.is_looping.lock().await;
-        let mut song = if *is_looping{
+        let song = if *is_looping {
             let mut current_song = audio_state.current_song.lock().await;
             let mut current_song = audio_state.current_song.lock().await;
-            current_song.take().expect("logical error: expected current_song to be non-empty")
-        }else{
+            current_song
+                .take()
+                .expect("logical error: expected current_song to be non-empty")
+        } else {
             audio_state.queue.pop().await
             audio_state.queue.pop().await
         };
         };
         drop(is_looping);
         drop(is_looping);
 
 
-        
-        let url = song.get_url().await;
-        let source = ffmpeg_pcm(url).await;
+        let source = ffmpeg_pcm(&song.url).await;
         let source = match source {
         let source = match source {
             Ok(source) => source,
             Ok(source) => source,
             Err(why) => {
             Err(why) => {
                 println!("Error in AudioState::play_audio: {}", why);
                 println!("Error in AudioState::play_audio: {}", why);
-                return
+                return;
             }
             }
         };
         };
         let reader = Reader::Extension(source);
         let reader = Reader::Extension(source);
         let source = input::Input::float_pcm(true, reader);
         let source = input::Input::float_pcm(true, reader);
 
 
         let mut handler = audio_state.handler.lock().await;
         let mut handler = audio_state.handler.lock().await;
-        
+
         let handle = handler.play_source(source);
         let handle = handler.play_source(source);
 
 
         if let Err(why) = handle.add_event(
         if let Err(why) = handle.add_event(
             Event::Track(TrackEvent::End),
             Event::Track(TrackEvent::End),
-            SongEndNotifier{
+            SongEndNotifier {
                 audio_state: audio_state.clone(),
                 audio_state: audio_state.clone(),
-            }
-        ){
+            },
+        ) {
             panic!("Err AudioState::play_audio: {:?}", why);
             panic!("Err AudioState::play_audio: {:?}", why);
         }
         }
         {
         {
             let text = song.get_string().await;
             let text = song.get_string().await;
             let channel_id = audio_state.channel_id.lock().await;
             let channel_id = audio_state.channel_id.lock().await;
             let http = audio_state.http.lock().await;
             let http = audio_state.http.lock().await;
-            send_embed_http(*channel_id, http.clone(), &format!(
-                "Now playing:\n\n {}", text
-            )).await;
+            send_embed_http(
+                *channel_id,
+                http.clone(),
+                &format!("Now playing:\n\n {}", text),
+            )
+            .await;
         }
         }
         let mut current_song = audio_state.current_song.lock().await;
         let mut current_song = audio_state.current_song.lock().await;
         *current_song = Some(song);
         *current_song = Some(song);
@@ -133,75 +111,41 @@ impl AudioState{
         *track_handle = Some(handle);
         *track_handle = Some(handle);
     }
     }
 
 
-    pub async fn add_audio(audio_state: Arc<AudioState>, query: &str, shuffle: bool){
-        let mut songs = match process_query(query).await{
-            Ok(songs) => songs,
+    pub async fn add_audio(audio_state: Arc<AudioState>, query: &str) {
+        let song = match Song::from_query(query).await {
+            Ok(song) => song,
             Err(why) => {
             Err(why) => {
                 println!("Error add_audio: {}", why);
                 println!("Error add_audio: {}", why);
                 return;
                 return;
-            },
-        };
-        if shuffle {
-            songs.shuffle(&mut rand::thread_rng());
-        }
-        audio_state.queue.push(songs).await;
-    }
-
-    pub async fn add_recommended_songs(audio_state: Arc<AudioState>, query: &str, amount: usize){
-        let mut songs = match song_recommender(query, amount).await{
-            Ok(songs) => songs,
-            Err(why) => {
-                println!("Error add_recommended_songs: {}", why);
-                return;
-            },
-        };
-        songs.shuffle(&mut rand::thread_rng());
-        audio_state.queue.push(songs).await;
-    }
-
-    pub async fn extend_songs(audio_state: Arc<AudioState>, query: &str, extend_ratio: f64){
-        let mut songs = match process_query(query).await{
-            Ok(songs) => songs,
-            Err(why) => {
-                println!("Error extend_songs: {}", why);
-                return;
-            },
-        };
-        let recommended_songs = match song_recommender(query, (songs.len() as f64 * extend_ratio) as usize).await{
-            Ok(songs) => songs,
-            Err(why) => {
-                println!("Error add_recommended_songs: {}", why);
-                return;
-            },
+            }
         };
         };
-        songs.extend(recommended_songs);
-        songs.shuffle(&mut rand::thread_rng());
-        audio_state.queue.push(songs).await;
+        audio_state.queue.push(vec![song]).await;
     }
     }
 
 
-    pub async fn send_track_command(audio_state: Arc<AudioState>, cmd: TrackCommand) -> Result<(), String>{
+    pub async fn send_track_command(
+        audio_state: Arc<AudioState>,
+        cmd: TrackCommand,
+    ) -> Result<(), String> {
         let track_handle = audio_state.track_handle.lock().await;
         let track_handle = audio_state.track_handle.lock().await;
         match track_handle.as_ref() {
         match track_handle.as_ref() {
-            Some(track_handle) => {
-                match track_handle.send(cmd){
-                    Ok(()) => Ok(()),
-                    Err(why) => Err(format!("{:?}",why))
-                }
+            Some(track_handle) => match track_handle.send(cmd) {
+                Ok(()) => Ok(()),
+                Err(why) => Err(format!("{:?}", why)),
             },
             },
-            None => Err("no song currently playing".to_string())
+            None => Err("no song currently playing".to_string()),
         }
         }
     }
     }
 
 
-    pub async fn shuffle(audio_state: Arc<AudioState>) -> Result<(), String>{
+    pub async fn shuffle(audio_state: Arc<AudioState>) -> Result<(), String> {
         audio_state.queue.shuffle().await
         audio_state.queue.shuffle().await
     }
     }
 
 
-    pub async fn clear(audio_state: Arc<AudioState>) -> Result<(), String>{
+    pub async fn clear(audio_state: Arc<AudioState>) -> Result<(), String> {
         audio_state.queue.clear().await
         audio_state.queue.clear().await
     }
     }
 
 
     // on success, returns a bool that specifies whether the queue is now being looped
     // on success, returns a bool that specifies whether the queue is now being looped
-    pub async fn change_looping(audio_state: Arc<AudioState>) -> Result<bool, String>{
+    pub async fn change_looping(audio_state: Arc<AudioState>) -> Result<bool, String> {
         {
         {
             let current_song = audio_state.current_song.lock().await;
             let current_song = audio_state.current_song.lock().await;
             if current_song.is_none() {
             if current_song.is_none() {
@@ -229,17 +173,17 @@ impl AudioState{
         }*/
         }*/
     }
     }
 
 
-    pub async fn cleanup(audio_state: Arc<AudioState>) {
-        audio_state.queue.cleanup().await;
-    }
-
     pub async fn get_string(audio_state: Arc<AudioState>) -> String {
     pub async fn get_string(audio_state: Arc<AudioState>) -> String {
         let current_song = audio_state.current_song.lock().await;
         let current_song = audio_state.current_song.lock().await;
         let current_song = match &*current_song {
         let current_song = match &*current_song {
             Some(song) => song.get_string().await,
             Some(song) => song.get_string().await,
             None => "*Not playing*\n".to_string(),
             None => "*Not playing*\n".to_string(),
         };
         };
-        format!("**Current Song:**\n{}\n\n**Queue:**\n{}", current_song, audio_state.queue.get_string().await)
+        format!(
+            "**Current Song:**\n{}\n\n**Queue:**\n{}",
+            current_song,
+            audio_state.queue.get_string().await
+        )
     }
     }
 }
 }
 
 
@@ -254,4 +198,4 @@ impl VoiceEventHandler for SongEndNotifier {
 
 
         None
         None
     }
     }
-}
+}

+ 0 - 9
src/audio/config.rs

@@ -1,10 +1 @@
 
 
-pub mod spotify_recommend {
-    pub const SAME_ARTIST: u32 = 1;
-    pub const EXPLORE_ARTIST: u32 = 1;
-    pub const EXPLORE_ALBUM: u32 = 0;
-}
-
-pub mod audio {
-    pub const EXTEND_RATIO: f64 = 1.5;
-}

+ 2 - 6
src/audio/mod.rs

@@ -1,13 +1,9 @@
 pub mod audio;
 pub mod audio;
 pub mod audio_state;
 pub mod audio_state;
 
 
+mod config;
 mod song;
 mod song;
-mod youtube_loader;
 mod song_queue;
 mod song_queue;
 mod subprocess;
 mod subprocess;
-mod work;
-mod spotify;
-mod song_searcher;
-mod config;
 
 
-pub use audio::*;
+pub use audio::*;

+ 30 - 109
src/audio/song.rs

@@ -1,131 +1,52 @@
-use std::{
-    sync::{Arc},
-};
-use tokio::sync::{mpsc, Mutex};
-use super::{
-    work::Work,
-};
+use super::subprocess::{ytdl, ytdl_title};
 
 
-pub enum SongUrlState{
-    Proc{
-        is_loaded: Arc<Mutex<bool>>,
-        receiver: mpsc::Receiver<String>,
-        work: Work,
-    }
-}
-
-pub struct SongMetadata{
+pub struct SongMetadata {
     pub artist: Option<String>,
     pub artist: Option<String>,
     pub title: Option<String>,
     pub title: Option<String>,
-    pub search_query: Option<String>,
-    pub youtube_url: Option<String>,
     pub duration: Option<u64>,
     pub duration: Option<u64>,
 }
 }
 
 
-pub struct Song{
-    pub url_state: SongUrlState,
-    url: Arc<Mutex<Option<String>>>,
+pub struct Song {
+    pub url: String,
     metadata: SongMetadata,
     metadata: SongMetadata,
 }
 }
 
 
-impl Song{
-    pub fn new_load(metadata: SongMetadata) -> Option<(Song, Option<Work>)>{
-        let query = match metadata.youtube_url.clone(){
-            Some(url) => url,
-            None => format!("ytsearch:{}", metadata.search_query.clone()?),
+impl Song {
+    pub async fn from_query(query: &str) -> Result<Song, std::io::Error> {
+        let query = if query.contains("watch?v=") {
+            query.to_string()
+        } else {
+            format!("ytsearch:{}", query)
         };
         };
 
 
-        let (tx, rx) = mpsc::channel(1);
-        let is_loaded = Arc::new(Mutex::new(false));
-        let work = Work{
-            sender: tx,
-            is_loaded: is_loaded.clone(),
-            query,
-        };
-        let url_state = SongUrlState::Proc{
-            is_loaded,
-            receiver: rx,
-            work: work.clone(),
-        };
-        
-        let song = Song{
-            url_state,
-            url: Arc::new(Mutex::new(None)),
-            metadata,
-        };
-        Some((song, Some(work)))
-    }
-    pub async fn get_url(&mut self) -> String{
-        match &mut self.url_state{
-            SongUrlState::Proc{receiver,..} => {
-                let mut url = self.url.lock().await;
-                match url.clone(){
-                    Some(url)=>{
-                        return url;
-                    }
-                    None=>{
-                        //drop(url);
-                        let source = receiver.recv().await.unwrap();
-                        //let mut url = song.url.lock().unwrap();
-                        *url = Some(source);
-                        return url.clone().unwrap();
-                    }
-                }
-            },
+        let title = ytdl_title(&query).await?;
+        let url = ytdl(&query).await?;
+
+        let metadata = SongMetadata {
+            artist: None,
+            title: Some(title),
+            duration: None,
         };
         };
+
+        let song = Song { url, metadata };
+        Ok(song)
     }
     }
-    pub async fn get_string(&self) -> String{
-        let metadata = &self.metadata;
-        let artist = match &metadata.artist{
-            Some(artist) => artist,
-            None => "unknown",
-        };
-        let title = match &metadata.title{
-            Some(title) => title,
-            None => "unknown",
-        };
-        let duration = match &metadata.duration{
+
+    pub async fn get_string(&self) -> String {
+        let artist = self
+            .metadata
+            .artist
+            .clone()
+            .unwrap_or("unknown".to_string());
+        let title = self.metadata.title.clone().unwrap_or("unknown".to_string());
+        let duration = match self.metadata.duration {
             Some(duration) => {
             Some(duration) => {
                 let mins = duration / 60;
                 let mins = duration / 60;
                 let secs = duration - mins * 60;
                 let secs = duration - mins * 60;
                 format!("{}:{:0>2}", mins, secs)
                 format!("{}:{:0>2}", mins, secs)
-            },
+            }
             None => "unknown duration".to_string(),
             None => "unknown duration".to_string(),
         };
         };
         format!("{} by {} | {}", title, artist, &duration)
         format!("{} by {} | {}", title, artist, &duration)
     }
     }
 }
 }
-
-/*
-async fn send_url(state: &SongUrlState, url: String) {
-    let SongUrlState::Proc{is_loaded, sender, ..} = state;
-    let is_loaded = is_loaded.lock().unwrap();
-    if *is_loaded{
-        return
-    }
-    sender.send(url).await.unwrap();
-}*/
-/*
-async fn hi() -> Song{
-    let (tx, rx) = mpsc::channel::<String>(100);
-    Song{
-        url: "".to_string(),
-        url_state: SongUrlState::Proc{
-            is_loaded: Arc::new(Mutex::new(false)),
-            is_waited: Arc::new(Mutex::new(false)),
-            receiver: rx,
-            //sender: tx,
-        }
-    }
-}*/
-
-/*
-async fn get_url(song: &mut Song) -> &str{
-    let is_waited = song.is_loaded.lock().unwrap();
-    if *is_waited{
-        return &song.url;
-    }
-    let wait_chan = &mut song.wait_chan;
-    song.url = wait_chan.await.unwrap();
-    &song.url
-}*/

+ 26 - 53
src/audio/song_queue.rs

@@ -1,51 +1,38 @@
-use std::{
-    sync::Arc,
-    collections::VecDeque,
-    mem::drop,
-    cmp::min,
-};
-use tokio::sync::{Semaphore, Mutex};
+use super::song::Song;
 use rand::seq::SliceRandom;
 use rand::seq::SliceRandom;
-use super::{
-    song::{
-        Song,
-        SongUrlState,
-    },
-    youtube_loader::YoutubeLoader,
-    work::Work,
-};
-pub struct SongQueue{
-    loader: Arc<Mutex<YoutubeLoader>>,
+use std::{cmp::min, collections::VecDeque, mem::drop, sync::Arc};
+use tokio::sync::{Mutex, Semaphore};
+pub struct SongQueue {
     queue: Arc<Mutex<VecDeque<Song>>>,
     queue: Arc<Mutex<VecDeque<Song>>>,
     queue_sem: Semaphore,
     queue_sem: Semaphore,
 }
 }
 
 
-impl SongQueue{
+impl SongQueue {
     pub fn new() -> SongQueue {
     pub fn new() -> SongQueue {
-        SongQueue{
-            loader: Arc::new(Mutex::new(YoutubeLoader::new())),
+        SongQueue {
             queue: Arc::new(Mutex::new(VecDeque::new())),
             queue: Arc::new(Mutex::new(VecDeque::new())),
             queue_sem: Semaphore::new(0),
             queue_sem: Semaphore::new(0),
         }
         }
     }
     }
-    pub async fn push(&self, songs: Vec<(Song, Option<Work>)>){
+    pub async fn push(&self, songs: Vec<Song>) {
         let mut queue = self.queue.lock().await;
         let mut queue = self.queue.lock().await;
         let count = songs.len();
         let count = songs.len();
-        let loader = self.loader.lock().await;
-        for item in songs.into_iter(){
-            queue.push_back(item.0);
-            if let Some(work) = item.1{
-                loader.add_work(work).await;
-            };
-            //self.loader.add_work(item.1).await;
+        for item in songs.into_iter() {
+            queue.push_back(item);
         }
         }
         drop(queue);
         drop(queue);
         self.queue_sem.add_permits(count);
         self.queue_sem.add_permits(count);
     }
     }
     pub async fn pop(&self) -> Song {
     pub async fn pop(&self) -> Song {
-        self.queue_sem.acquire().await.expect("Error SongQueue.pop: semaphore acquire() failed").forget();
+        self.queue_sem
+            .acquire()
+            .await
+            .expect("Error SongQueue.pop: semaphore acquire() failed")
+            .forget();
         let mut queue = self.queue.lock().await;
         let mut queue = self.queue.lock().await;
-        queue.pop_front().expect("Error SongQueue.pop: semaphore sync failure")
+        queue
+            .pop_front()
+            .expect("Error SongQueue.pop: semaphore sync failure")
     }
     }
     pub async fn shuffle(&self) -> Result<(), String> {
     pub async fn shuffle(&self) -> Result<(), String> {
         let mut queue = self.queue.lock().await;
         let mut queue = self.queue.lock().await;
@@ -54,18 +41,9 @@ impl SongQueue{
         }
         }
         queue.make_contiguous().shuffle(&mut rand::thread_rng());
         queue.make_contiguous().shuffle(&mut rand::thread_rng());
 
 
-        self.reset_loader().await;
-        let loader = self.loader.lock().await;
-
-        for song in queue.iter(){
-            match &song.url_state{
-                SongUrlState::Proc{work,..} => loader.add_work(work.clone()).await,
-            };
-        };
-
         Ok(())
         Ok(())
     }
     }
-    pub async fn clear(&self) -> Result<(), String>{
+    pub async fn clear(&self) -> Result<(), String> {
         let mut queue = self.queue.lock().await;
         let mut queue = self.queue.lock().await;
         if queue.len() == 0 {
         if queue.len() == 0 {
             return Err("queue is empty".to_string());
             return Err("queue is empty".to_string());
@@ -73,27 +51,22 @@ impl SongQueue{
         queue.clear();
         queue.clear();
         Ok(())
         Ok(())
     }
     }
-    async fn reset_loader(&self) {
-        let mut loader = self.loader.lock().await;
-        loader.cleanup().await;
-        *loader = YoutubeLoader::new();
-    }
-    pub async fn cleanup(&self) {
-        let mut loader = self.loader.lock().await;
-        loader.cleanup().await;
-    }
-    pub async fn get_string(&self) -> String{
+    pub async fn get_string(&self) -> String {
         let queue = self.queue.lock().await;
         let queue = self.queue.lock().await;
         if queue.len() == 0 {
         if queue.len() == 0 {
             return "*empty*".to_string();
             return "*empty*".to_string();
         };
         };
         let mut s = String::new();
         let mut s = String::new();
-        s.push_str(&format!("*Showing {} of {} songs*\n", min(20, queue.len()), queue.len()));
-        for (i, song) in queue.iter().take(20).enumerate(){
+        s.push_str(&format!(
+            "*Showing {} of {} songs*\n",
+            min(20, queue.len()),
+            queue.len()
+        ));
+        for (i, song) in queue.iter().take(20).enumerate() {
             s += &format!("{}: ", i);
             s += &format!("{}: ", i);
             s += &song.get_string().await;
             s += &song.get_string().await;
             s += "\n";
             s += "\n";
         }
         }
         s
         s
     }
     }
-}
+}

+ 0 - 85
src/audio/song_searcher.rs

@@ -1,85 +0,0 @@
-use super::{
-    spotify::SpotifyClient,
-    song::{
-        Song,
-        SongMetadata,
-    },
-    work::Work,
-};
-
-use std::sync::Arc;
-
-pub async fn process_query(query: &str) -> Result<Vec<(Song, Option<Work>)>, String>{
-    if query.contains("spotify") && query.contains("/playlist/"){
-        let split: Vec<&str> = query
-            .split("/playlist/")
-            .filter(|s| !s.is_empty())
-            .collect();
-        if split.len() != 2 {
-            return Err("invalid spotify playlist URL".to_string());
-        }
-        let playlist_id = split[1];
-        let playlist_id = playlist_id
-            .split('?')
-            .find(|s| !s.is_empty())
-            .expect("Logical error: process_query's playlist_id contains items?");
-            
-        let client = SpotifyClient::new().await;
-        let client = match client {
-            Ok(client) => client,
-            Err(why) => return Err(why),
-        };
-        let tracks = match client.get_playlist(playlist_id).await{
-            Ok(tracks) => tracks,
-            Err(why) => return Err(why),
-        };
-        return Ok(SpotifyClient::process_track_objects(tracks));
-        /*
-        
-        */
-    } else {
-        let data = if query.contains("watch?v=") {
-            (Some(query.to_string()), None)
-        } else {
-            (None, Some(query.to_string()))
-        };
-        let metadata = SongMetadata{
-            artist: None,
-            title: None,
-            duration: None,
-            search_query: data.1,
-            youtube_url: data.0,
-        };
-        let song = match Song::new_load(metadata){
-            Some(song) => song,
-            None => return Err("failed to get song from YouTube".to_string()),
-        };
-        return Ok(vec![song]);
-    };
-}
-
-pub async fn song_recommender(query: &str, amount: usize) -> Result<Vec<(Song, Option<Work>)>, String>{
-    let split: Vec<&str> = query
-        .split("/playlist/")
-        .filter(|s| !s.is_empty())
-        .collect();
-    if split.len() != 2 {
-        return Err("invalid spotify playlist URL".to_string());
-    }
-    let playlist_id = split[1];
-    let playlist_id = playlist_id
-        .split('?')
-        .find(|s| !s.is_empty())
-        .expect("Logical error: process_query's playlist_id contains items?");
-    
-    let client = SpotifyClient::new().await;
-    let client = match client {
-        Ok(client) => Arc::new(client),
-        Err(why) => return Err(why),
-    };
-    let tracks = match SpotifyClient::recommend_playlist(client, amount, playlist_id).await{
-        Ok(tracks) => tracks,
-        Err(why) => return Err(why),
-    };
-    return Ok(SpotifyClient::process_track_objects(tracks));
-}

+ 0 - 270
src/audio/spotify.rs

@@ -1,270 +0,0 @@
-use rspotify::{
-    client::{
-        Spotify,
-        SpotifyBuilder,
-    },
-    model::{
-        Id,
-        PlayableItem,
-        Market,
-        Country,
-        FullTrack,
-        SimplifiedTrack,
-    },
-    oauth2::CredentialsBuilder,
-};
-
-use rand::{
-    seq::IteratorRandom,
-    distributions::{
-        WeightedIndex,
-        Distribution,
-    },
-    Rng,
-};
-
-use tokio::{
-    self,
-    time::{
-        sleep,
-        Duration,
-    },
-};
-
-use super::{
-    song::{
-        Song,
-        SongMetadata,
-    },
-    work::Work,
-    config::spotify_recommend as sr,
-};
-
-use std::sync::Arc;
-
-pub enum TrackObject{
-    FullTrack(FullTrack),
-    SimplifiedTrack(SimplifiedTrack),
-}
-
-impl TrackObject{
-    fn artist(&self) -> &str{
-        match self{
-            TrackObject::FullTrack(track) => &track.artists[0].name,
-            TrackObject::SimplifiedTrack(track) => &track.artists[0].name,
-        }
-    }
-    fn title(&self) -> &str{
-        match self{
-            TrackObject::FullTrack(track) => &track.name,
-            TrackObject::SimplifiedTrack(track) => &track.name,
-        }
-    }
-    fn duration(&self) -> u64{
-        match self{
-            TrackObject::FullTrack(track) => track.duration.as_secs(),
-            TrackObject::SimplifiedTrack(track) => track.duration.as_secs(),
-        }
-    }
-    fn album_id(&self) -> Option<&str>{
-        match self{
-            TrackObject::FullTrack(track) => track.album.id.as_deref(),
-            TrackObject::SimplifiedTrack(_) => None,
-        }
-    }
-    fn artist_id(&self) -> Option<&str>{
-        match self{
-            TrackObject::FullTrack(track) => track.artists[0].id.as_deref(),
-            TrackObject::SimplifiedTrack(track) => track.artists[0].id.as_deref(),
-        }
-    }
-}
-
-pub struct SpotifyClient{
-    client: Spotify,
-}
-
-impl SpotifyClient{
-    pub async fn new() -> Result<SpotifyClient, String> {
-        let creds = CredentialsBuilder::default()
-            .id("5f573c9620494bae87890c0f08a60293")
-            .secret("212476d9b0f3472eaa762d90b19b0ba8")
-            .build();
-        let creds = match creds{
-            Ok(creds) => creds,
-            Err(why) => return Err(why.to_string()),
-        };
-        let mut spotify = SpotifyBuilder::default()
-            .credentials(creds)
-            //.oauth(oauth)
-            .build()
-            .unwrap();
-        if let Err(why) = spotify.request_client_token().await{
-            return Err(why.to_string());
-        };
-        Ok(SpotifyClient{
-            client: spotify,
-        })
-    }
-    pub fn process_track_objects(tracks: Vec<TrackObject>) -> Vec<(Song, Option<Work>)> {
-        let mut songs = vec![];
-        for track in tracks.into_iter(){
-            let artist = track.artist();
-            let title = track.title();
-            let metadata = SongMetadata{
-                search_query: Some(SpotifyClient::get_query_string(artist, title)),
-                artist: Some(artist.to_string()),
-                title: Some(title.to_string()),
-                youtube_url: None,
-                duration: Some(track.duration()),
-            };
-            match Song::new_load(metadata){
-                Some(data) => songs.push(data),
-                None => continue,
-            };
-        }
-        songs
-    }
-    pub async fn get_playlist(&self, playlist_id: &str) -> Result<Vec<TrackObject>, String>{
-        let playlist_id = Id::from_id(playlist_id);
-        let playlist_id = match playlist_id{
-            Ok(playlist_id) => playlist_id,
-            Err(why) => {
-                return Err(format!("spotify::get_playlist: {:?}", why));
-            }
-        };
-        let tracks = self.client.playlist(playlist_id, None, None).await;
-        let tracks = match tracks{
-            Ok(tracks) => tracks,
-            Err(why)=>{
-                println!("Error in spotify.get_playlist: {:?}", why);
-                return Err(format!("spotify::get_playlist: {:?}", why));
-            }
-        };
-        let items = tracks.tracks.items;
-        let mut tracks = vec![];
-        for data in items.into_iter() {
-            let track = match data.track{
-                Some(PlayableItem::Track(track)) => track,
-                Some(_) => continue,
-                None => continue,
-            };
-            tracks.push(TrackObject::FullTrack(track));
-        }
-        Ok(tracks)
-    }
-    async fn random_from_artist(&self, id: &str) -> Option<TrackObject>{
-        let id = match Id::from_id(id){
-            Ok(id) => id,
-            Err(why) => {
-                println!("Error {:?}",why);
-                return None;
-            }
-        };
-        let tracks = self.client.artist_top_tracks(id, &Market::Country(Country::Japan)).await;
-        match tracks {
-            Ok(tracks) => Some(TrackObject::FullTrack(tracks.into_iter().choose(&mut rand::thread_rng())?)),
-            Err(why) => {
-                println!("Error SpotifyClient::random_from_artist: {:?}", why);
-                None
-            },
-        }
-    }
-    async fn random_from_album(&self, id: &str) -> Option<TrackObject>{
-        let id = match Id::from_id(id){
-            Ok(id) => id,
-            Err(why) => {
-                println!("Error {:?}",why);
-                return None;
-            }
-        };
-        let album = self.client.album(id).await;
-        match album {
-            Ok(album) => {
-                let tracks = album.tracks.items;
-                Some(TrackObject::SimplifiedTrack(tracks.into_iter().choose(&mut rand::thread_rng())?))
-            },
-            Err(why) => {
-                println!("Error SpotifyClient::random_from_album: {:?}", why);
-                None
-            },
-        }
-    }
-    //  -> Result<Vec<(Song, Option<Work>)>, String>
-    pub async fn recommend_playlist(client: Arc<SpotifyClient>, amount: usize, playlist_id: &str) -> Result<Vec<TrackObject>, String>{
-        let mut tasks = vec![];
-        let tracks = match client.get_playlist(playlist_id).await{
-            Ok(tracks) => tracks,
-            Err(why) => return Err(why),
-        };
-        let tracks = Arc::new(tracks);
-        //let tracks = tracks.sample(&mut rand::thread_rng(), amount);
-        for _ in 0..amount{
-            let tracks = tracks.clone();
-            let ind = rand::thread_rng().gen::<u32>() as usize % tracks.len();
-            
-            let client = client.clone();
-            let task = tokio::spawn(
-                async move {
-                    let track = &tracks[ind];
-
-                    let weights = [sr::SAME_ARTIST, sr::EXPLORE_ALBUM, sr::EXPLORE_ARTIST];
-                    let option = WeightedIndex::new(&weights).unwrap().sample(&mut rand::thread_rng());
-
-                    match option{
-                        // find random song from track artist
-                        0 => {
-                            let artist = match track.artist_id(){
-                                Some(artist) => artist,
-                                None => return None,
-                            };
-                            client.random_from_artist(artist).await
-                        },
-                        // find random song from track album
-                        1 => {
-                            let album = match track.album_id(){
-                                Some(album) => album,
-                                None => {
-                                    println!("album not found");
-                                    return None
-                                },
-                            };
-                            client.random_from_album(album).await
-                        },
-                        // find random song from a random similar artist
-                        _ => {
-                            let artist = match track.artist_id(){
-                                Some(artist) => artist,
-                                None => return None,
-                            };
-                            let id = Id::from_id(artist).unwrap();
-                            let artists = client.client.artist_related_artists(id).await;
-                            let artists = match artists{
-                                Ok(artists) => artists[..5].to_vec(),
-                                Err(why) => {
-                                    println!("Error related artists: {:?}", why);
-                                    return None
-                                }
-                            };
-                            let id = &artists.iter().choose(&mut rand::thread_rng()).unwrap().id;
-                            client.random_from_artist(id).await
-                        },
-                    }
-                }
-            );
-            tasks.push(task);
-            sleep(Duration::from_millis(100)).await;
-        };
-        let mut tracks = vec![];
-        for task in tasks.into_iter(){
-            match task.await.unwrap(){
-                Some(track) => tracks.push(track),
-                None => continue,
-            }
-        }
-        Ok(tracks)
-    }
-    fn get_query_string(artist: &str, title: &str) -> String{
-        format!("{} {} lyrics", artist, title)
-    }
-}

+ 35 - 32
src/audio/subprocess.rs

@@ -1,59 +1,62 @@
 use std::{
 use std::{
     io::{BufReader, Read},
     io::{BufReader, Read},
-    process::{
-        Command,
-        Stdio,
-    },
+    process::{Command, Stdio},
 };
 };
 use tokio::process::Command as TokioCommand;
 use tokio::process::Command as TokioCommand;
 
 
-pub async fn ytdl(query: &str) -> String{
+pub async fn ytdl(query: &str) -> Result<String, std::io::Error> {
     let mut cmd = TokioCommand::new("youtube-dl");
     let mut cmd = TokioCommand::new("youtube-dl");
     let cmd = cmd
     let cmd = cmd
         .arg("-x")
         .arg("-x")
         .arg("--skip-download")
         .arg("--skip-download")
         .arg("--get-url")
         .arg("--get-url")
-        .arg("--audio-quality").arg("128k")
+        .arg("--audio-quality")
+        .arg("128k")
         .arg(query);
         .arg(query);
-    let out = cmd.output().await.unwrap();
-    String::from_utf8(out.stdout).unwrap()
+    let out = cmd.output().await?;
+    Ok(String::from_utf8(out.stdout).unwrap())
 }
 }
-//  -> Result<Box<dyn Read + Send>, String>
-pub async fn ffmpeg_pcm(url: String) -> Result<Box<dyn Read + Send>, String>{
-    /*let res = tokio::task::spawn_blocking(move ||{
-        
-    }).await.unwrap();
-    res*/
+
+pub async fn ytdl_title(query: &str) -> Result<String, std::io::Error> {
+    let mut cmd = TokioCommand::new("youtube-dl");
+    let cmd = cmd.arg("--get-title").arg(query);
+    let out = cmd.output().await?;
+    Ok(String::from_utf8(out.stdout).unwrap())
+}
+
+pub async fn ffmpeg_pcm(url: &str) -> Result<Box<dyn Read + Send>, String> {
     let mut cmd = Command::new("ffmpeg");
     let mut cmd = Command::new("ffmpeg");
     let cmd = cmd
     let cmd = cmd
-        .arg("-reconnect").arg("1")
-        .arg("-reconnect_streamed").arg("1")
-        .arg("-reconnect_delay_max").arg("5")
-        .arg("-i").arg(url)
-        .arg("-f").arg("s16le")
-        .arg("-ar").arg("48000")
-        .arg("-ac").arg("2")
-        .arg("-acodec").arg("pcm_f32le")
+        .arg("-reconnect")
+        .arg("1")
+        .arg("-reconnect_streamed")
+        .arg("1")
+        .arg("-reconnect_delay_max")
+        .arg("5")
+        .arg("-i")
+        .arg(url)
+        .arg("-f")
+        .arg("s16le")
+        .arg("-ar")
+        .arg("48000")
+        .arg("-ac")
+        .arg("2")
+        .arg("-acodec")
+        .arg("pcm_f32le")
         .arg("pipe:1")
         .arg("pipe:1")
         .stdout(Stdio::piped())
         .stdout(Stdio::piped())
         .stderr(Stdio::null());
         .stderr(Stdio::null());
-    let child = match cmd.spawn(){
+    let child = match cmd.spawn() {
         Ok(child) => child,
         Ok(child) => child,
         Err(error) => {
         Err(error) => {
-            return Err(format!("{}",error));
+            return Err(format!("{}", error));
         }
         }
     };
     };
-    let out = match child.stdout{
+    let out = match child.stdout {
         Some(out) => out,
         Some(out) => out,
         None => return Err("subprocess::ffmpeg_pcm: failed to get child stdout".to_string()),
         None => return Err("subprocess::ffmpeg_pcm: failed to get child stdout".to_string()),
     };
     };
-    let buf = BufReader::with_capacity(16384*8, out);
+    let buf = BufReader::with_capacity(16384 * 8, out);
     let buf: Box<dyn Read + Send> = Box::new(buf);
     let buf: Box<dyn Read + Send> = Box::new(buf);
     Ok(buf)
     Ok(buf)
 }
 }
-
-
-/*
-fn ffmpeg(url: &str){
-    
-}*/

+ 0 - 11
src/audio/work.rs

@@ -1,11 +0,0 @@
-use std::{
-    sync::{Arc},
-};
-use tokio::sync::{mpsc, Mutex};
-
-#[derive(Clone)]
-pub struct Work{
-    pub sender: mpsc::Sender<String>,
-    pub query: String,
-    pub is_loaded: Arc<Mutex<bool>>,
-}

+ 0 - 61
src/audio/youtube_loader.rs

@@ -1,61 +0,0 @@
-use tokio::{
-    sync::{mpsc},
-};
-use super::{
-    work::Work,
-    subprocess::ytdl,
-};
-
-pub struct YoutubeLoader {
-    work: mpsc::Sender<Work>,
-    kill: mpsc::Sender<()>,
-}
-
-impl YoutubeLoader{
-    pub async fn add_work(& self, work: Work){
-        if let Err(err) = self.work.send(work).await{
-            println!("Error in Loader::add_work: {}", err.to_string());
-        };
-    }
-    async fn loader_loop(mut work: mpsc::Receiver<Work>,){
-        while let Some(work) = work.recv().await {
-            let url = ytdl(&work.query).await;
-    
-            if let Err(err) = work.sender.send(url).await{
-                println!("Error in Loader::loader_loop: {:?}", err);
-            };
-
-            {
-                let mut is_loaded = work.is_loaded.lock().await;
-                assert!(!*is_loaded);
-                *is_loaded = true;
-            }
-        }
-    }
-
-    pub async fn cleanup(&mut self) {
-        if let Err(why) = self.kill.send(()).await{
-            println!("Error on Loader::cleanup: {}",why);
-        };
-    }
-    
-    pub fn new() -> YoutubeLoader{
-        let (work_tx, work_sx) = mpsc::channel(200);
-        let (kill_tx, kill_sx) = mpsc::channel(1);
-        tokio::spawn(async move{
-            YoutubeLoader::start_loader_loop(work_sx, kill_sx).await
-        });
-        YoutubeLoader{
-            work: work_tx,
-            kill: kill_tx,
-        }
-    }
-    
-    async fn start_loader_loop(work: mpsc::Receiver<Work>, mut kill: mpsc::Receiver<()>){
-        let f = tokio::spawn(async move {
-            YoutubeLoader::loader_loop(work).await
-        });
-        kill.recv().await;
-        f.abort();
-    }
-}

+ 13 - 15
src/main.rs

@@ -1,11 +1,12 @@
+use serenity::{
+    async_trait,
+    client::{Client, Context, EventHandler},
+    framework::StandardFramework,
+    model::gateway::Ready,
+};
 use std::env;
 use std::env;
-use serenity::{async_trait, client::{Client, Context, EventHandler}, framework::{
-        StandardFramework,
-    }, model::gateway::Ready};
 
 
-use songbird::{
-    SerenityInit,
-};
+use songbird::SerenityInit;
 
 
 mod audio;
 mod audio;
 mod util;
 mod util;
@@ -13,22 +14,19 @@ mod util;
 struct Handler;
 struct Handler;
 
 
 #[async_trait]
 #[async_trait]
-impl EventHandler for Handler{
-    async fn ready(&self, _: Context, _: Ready){
+impl EventHandler for Handler {
+    async fn ready(&self, _: Context, _: Ready) {
         println!("bot connected");
         println!("bot connected");
     }
     }
 }
 }
 
 
-
 #[tokio::main]
 #[tokio::main]
 async fn main() {
 async fn main() {
     let token = env::var("OCTAVE_BOT_TOKEN").expect("Error: token not found");
     let token = env::var("OCTAVE_BOT_TOKEN").expect("Error: token not found");
     let framework = StandardFramework::new()
     let framework = StandardFramework::new()
-        .configure(|c|{
-            c.prefix("a.")
-        })
+        .configure(|c| c.prefix("!"))
         .group(&audio::AUDIO_GROUP);
         .group(&audio::AUDIO_GROUP);
-    
+
     let mut client = Client::builder(token)
     let mut client = Client::builder(token)
         .event_handler(Handler)
         .event_handler(Handler)
         .framework(framework)
         .framework(framework)
@@ -36,7 +34,7 @@ async fn main() {
         .await
         .await
         .expect("Error creating client");
         .expect("Error creating client");
 
 
-    if let Err(why) = client.start().await{
+    if let Err(why) = client.start().await {
         println!("Error starting client: {:?}", why);
         println!("Error starting client: {:?}", why);
     }
     }
-}
+}

+ 33 - 40
src/util/util.rs

@@ -1,57 +1,50 @@
 use serenity::{
 use serenity::{
-    client::Context, 
-    model::{
-        channel::Message, 
-        prelude::ReactionType,
-        id::ChannelId,
-    },
+    client::Context,
     http::Http,
     http::Http,
+    model::{channel::Message, id::ChannelId, prelude::ReactionType},
 };
 };
 
 
 use std::sync::Arc;
 use std::sync::Arc;
 
 
-pub async fn send_message(ctx: &Context, msg: &Message, text: &str){
-    let res = msg.channel_id.send_message(&ctx.http, |m| {
-        m.content(text);
-        m
-    }).await;
-    if let Err(why) = res{
+pub async fn send_embed(ctx: &Context, msg: &Message, text: &str) {
+    let res = msg
+        .channel_id
+        .send_message(&ctx.http, |m| {
+            m.embed(|e| {
+                e.colour(0xf542bf);
+                e.description(text);
+                e
+            });
+            m
+        })
+        .await;
+    if let Err(why) = res {
         println!("Error sending embed: {:?}", why);
         println!("Error sending embed: {:?}", why);
     };
     };
 }
 }
 
 
-pub async fn send_embed(ctx: &Context, msg: &Message, text: &str){
-    let res = msg.channel_id.send_message(&ctx.http, |m| {
-        m.embed(|e| {
-            e.colour(0xf542bf);
-            e.description(text);
-            e
-        });
-        m
-    }).await;
-    if let Err(why) = res{
-        println!("Error sending embed: {:?}", why);
-    };
-}
+pub async fn send_embed_http(channel_id: ChannelId, http: Arc<Http>, text: &str) {
+    let res = channel_id
+        .send_message(http, |m| {
+            m.embed(|e| {
+                e.colour(0xf542bf);
+                e.description(text);
+                e
+            });
+            m
+        })
+        .await;
 
 
-pub async fn send_embed_http(channel_id: ChannelId, http: Arc<Http>, text: &str){
-    let res = channel_id.send_message(http, |m| {
-        m.embed(|e| {
-            e.colour(0xf542bf);
-            e.description(text);
-            e
-        });
-        m
-    }).await;
-
-    if let Err(why) = res{
+    if let Err(why) = res {
         println!("Error sending embed: {:?}", why);
         println!("Error sending embed: {:?}", why);
     };
     };
 }
 }
 
 
-pub async fn message_react(ctx: &Context, msg: &Message, emoji: &str){
-    let res = msg.react(&ctx.http, ReactionType::Unicode(emoji.to_string())).await;
-    if let Err(why) = res{
+pub async fn message_react(ctx: &Context, msg: &Message, emoji: &str) {
+    let res = msg
+        .react(&ctx.http, ReactionType::Unicode(emoji.to_string()))
+        .await;
+    if let Err(why) = res {
         println!("Error reacting to message: {:?}", why);
         println!("Error reacting to message: {:?}", why);
     }
     }
-}
+}