use super::super::prelude::*; use rusqlite::params; use rand::{seq::SliceRandom, thread_rng}; use serenity::prelude::*; use serenity::voice::LockedAudio; use serenity::{ framework::standard::{macros::command, Args, CommandResult}, model::channel::Message, }; use utilities::prelude::*; use std::sync::Once; use std::thread; use std::time::Duration; static WATCHER: Once = Once::new(); #[command] fn play(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { if let Err(err) = channel_contains_author(ctx, msg) { msg.channel_id.say(&ctx.http, err)?; return Ok(()); } let mut data = ctx.data.write(); let media = match data.get_mut::() { Some(media) => media, None => { msg.channel_id.say(&ctx.http, "could not find media data")?; return Ok(()); } }; { let mut media_lock = media.lock().unwrap(); if args.len() == 0 { if !check_for_continue(media_lock.song_mut()) { msg.channel_id .say(&ctx.http, "Must provide a URL to a video or audio")?; } } else { let first_arg = args.current().unwrap(); if first_arg == "--local" { handle_local_request(&mut media_lock, ctx, msg)?; } else if first_arg.starts_with("http") { handle_http_request(&mut media_lock, ctx, msg, first_arg)?; } else { let mut arg_list = args.single::()?; for arg in args.iter::() { arg_list += &format!(" {}", arg?.trim()); } handle_song_request(&mut media_lock, ctx, msg, &arg_list)? } } } WATCHER.call_once(|| { let media_data = media.clone(); let message_clone = msg.clone(); let context_clone = ctx.clone(); thread::spawn(move || loop { { let mut media_lock = media_data.lock().unwrap(); // check if there is a song currently being played let mut is_playing = false; if let Some(song) = media_lock.song() { let song_lock = song.lock(); if !song_lock.finished { is_playing = true; } } if !is_playing { if let Err(err) = MediaData::next_song(&context_clone, &mut media_lock, &message_clone) { println!("{}", err); } } } thread::sleep(Duration::from_millis(1500)); }); }); Ok(()) } fn check_for_continue(song_lock: &Option) -> bool { match song_lock { Some(song) => { let song_clone = song.clone(); let mut audio_lock = song_clone.lock(); audio_lock.play(); true } None => false, } } fn append_songs( media: &mut MediaData, ctx: &Context, msg: &Message, mut source: Vec, ) -> VerboseResult<()> { media.playlist_mut().append(&mut source); println!("start playing"); MediaData::start_playing(ctx, media, msg)?; Ok(()) } fn handle_http_request( media: &mut MediaData, ctx: &serenity::client::Context, msg: &serenity::model::channel::Message, url: &str, ) -> VerboseResult<()> { let mut names = Vec::new(); { let sql = media.db(); let mut stmt = match sql.prepare("SELECT name FROM Vulva3 WHERE link = ?") { Ok(statement) => statement, Err(_) => create_error!("failed preparing data base access"), }; let rows = match stmt.query_map(&[url], |row| row.get(0) as rusqlite::Result) { Ok(rows) => rows, Err(_) => create_error!("failed querying rows"), }; for name_result in rows { let name = match name_result { Ok(name) => name, Err(_) => create_error!("failed getting name from row"), }; names.push(name); } } if names.len() > 0 { msg.channel_id .say(&ctx.http, "song already loaded!") .map_err(|err| format!("{}", err))?; append_songs( media, ctx, msg, names.iter().map(|n| Song { name: n.clone() }).collect(), )?; } else { let source = match youtube_dl(&url) { Ok(source) => source, Err(why) => { println!("Err starting source: {:?}", why); msg.channel_id .say(&ctx.http, format!("Error using youtube-dl: {}", why)) .map_err(|err| format!("{}", err))?; return Ok(()); } }; let mut info = if source.len() == 1 { "Finished downloading song:".to_string() } else { "Finished downloading songs:".to_string() }; { let sql = media.db(); for song in &source { info = format!("{}\n\t{}", info, song.name); if sql .execute( "INSERT INTO Vulva3 (name, link) VALUES (?1, ?2)", params![song.name, url], ) .is_err() { create_error!("failed inserting songs into db"); } } } msg.channel_id .say(&ctx.http, info) .map_err(|err| format!("{}", err))?; append_songs(media, ctx, msg, source)?; } Ok(()) } fn handle_local_request( media: &mut MediaData, ctx: &serenity::client::Context, msg: &serenity::model::channel::Message, ) -> VerboseResult<()> { let mut songs = Vec::new(); { let sql = media.db(); let mut stmt = match sql.prepare("SELECT name FROM Vulva3") { Ok(statement) => statement, Err(_) => create_error!("failed preparing data base access"), }; let rows = match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result) { Ok(rows) => rows, Err(_) => create_error!("failed querying rows"), }; for name_result in rows { let name = match name_result { Ok(name) => name, Err(_) => create_error!("failed getting name from row"), }; songs.push(Song { name }); } } let mut rng = thread_rng(); songs.shuffle(&mut rng); append_songs(media, ctx, msg, songs)?; Ok(()) } fn handle_song_request( media: &mut MediaData, ctx: &serenity::client::Context, msg: &serenity::model::channel::Message, pattern: &str, ) -> VerboseResult<()> { println!("song request ({})", pattern); let mut songs = Vec::new(); { let sql = media.db(); let mut stmt = match sql.prepare(&format!( "SELECT name FROM Vulva3 WHERE name LIKE '%{}%'", pattern )) { Ok(statement) => statement, Err(_) => create_error!("failed preparing data base access"), }; let rows = match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result) { Ok(rows) => rows, Err(_) => create_error!("failed querying rows"), }; for name_result in rows { let name = match name_result { Ok(name) => name, Err(_) => create_error!("failed getting name from row"), }; songs.push(Song { name }); } } println!("{:?}", &songs); if !songs.is_empty() { let mut rng = thread_rng(); songs.shuffle(&mut rng); println!("append songs"); append_songs(media, ctx, msg, songs)?; } else { msg.channel_id .say(&ctx.http, format!("no song found with pattern {}", pattern)) .map_err(|err| format!("{}", err))?; } Ok(()) }