RMusicBot/src/player/commands/play.rs

309 lines
8.1 KiB
Rust

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::<Media>() {
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::<String>()?;
for arg in args.iter::<String>() {
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<LockedAudio>) -> 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<Song>,
) -> 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<String>) {
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<String>) {
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<String>) {
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(())
}