first commit
This commit is contained in:
commit
0cb536b42b
38 changed files with 9044 additions and 0 deletions
263
src/commands/midi/play.rs
Normal file
263
src/commands/midi/play.rs
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
use crate::client::Client;
|
||||
use crate::client::ClientEvent;
|
||||
use crate::client::Note;
|
||||
use crate::client::Player;
|
||||
use crate::commands::Command;
|
||||
use crate::commands::MidiState;
|
||||
use crate::commands::argument::{ArgumentSpec, ArgumentType, ParsedArguments};
|
||||
use crate::commands::number_to_midi;
|
||||
use crate::commands::play_midi_file;
|
||||
use crate::commands::simple_hash;
|
||||
use crate::midi_helper::MidiEvent;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct PlayCommand {
|
||||
midi_state: Arc<Mutex<MidiState>>,
|
||||
}
|
||||
|
||||
impl PlayCommand {
|
||||
pub fn new(midi_state: Arc<Mutex<MidiState>>) -> Self {
|
||||
Self { midi_state }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Command for PlayCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"play"
|
||||
}
|
||||
fn aliases(&self) -> &[&'static str] {
|
||||
&["p"]
|
||||
}
|
||||
fn category(&self) -> &'static str {
|
||||
"midi"
|
||||
}
|
||||
fn description(&self) -> &'static str {
|
||||
"plays a midi file from a local path, URL, or files.sad.ovh"
|
||||
}
|
||||
fn argument_spec(&self) -> &'static [ArgumentSpec] {
|
||||
&[ArgumentSpec {
|
||||
name: "file",
|
||||
arg_type: ArgumentType::String,
|
||||
required: true,
|
||||
default: None,
|
||||
}]
|
||||
}
|
||||
|
||||
async fn constructed(&mut self, client: Client) {
|
||||
let ntm = number_to_midi();
|
||||
let midi_rx = {
|
||||
let midi_state = self.midi_state.lock().await;
|
||||
midi_state.midi_rx.clone()
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Ok(event) = midi_rx.recv_async().await {
|
||||
match event {
|
||||
MidiEvent::NoteOn { key, velocity } => {
|
||||
client
|
||||
.add_note_buffer(Note {
|
||||
n: ntm.get(&key).unwrap_or(&"").to_string(),
|
||||
v: Some(velocity as f64 / 127.0),
|
||||
d: None,
|
||||
s: None,
|
||||
})
|
||||
.await;
|
||||
}
|
||||
MidiEvent::NoteOff { key } => {
|
||||
client
|
||||
.add_note_buffer(Note {
|
||||
n: ntm.get(&key).unwrap_or(&"").to_string(),
|
||||
v: None,
|
||||
d: None,
|
||||
s: Some(1),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
MidiEvent::Info {
|
||||
num_tracks,
|
||||
time_div: _,
|
||||
events_count,
|
||||
note_count,
|
||||
total_ticks: _,
|
||||
minutes,
|
||||
seconds,
|
||||
millis,
|
||||
parse_time,
|
||||
} => {
|
||||
client
|
||||
.message(format!(
|
||||
"Tracks: `{}` Events: `{}` Total Duration: `{:02}:{:02}.{:03}` Note Count: `{}` Parse time: `{:.2?}`",
|
||||
num_tracks, events_count, minutes, seconds, millis, note_count, parse_time
|
||||
))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn event(&mut self, _: Client, _: ClientEvent) {}
|
||||
|
||||
async fn execute(&mut self, client: Client, _: Player, args: ParsedArguments) {
|
||||
let file_arg = match args.get("file") {
|
||||
Some(crate::commands::argument::ParsedArgument::String(s)) => s,
|
||||
_ => "",
|
||||
};
|
||||
|
||||
let joined_args = file_arg.to_string();
|
||||
let mut filename_to_play: String = "".to_string();
|
||||
let mut filename_beautiful: String = "".to_string();
|
||||
|
||||
if joined_args.starts_with("https://") {
|
||||
let hashed = simple_hash(joined_args.as_str());
|
||||
filename_to_play = format!("midis/{}.mid", hashed);
|
||||
filename_beautiful = joined_args.clone();
|
||||
|
||||
let file_exists = tokio::fs::try_exists(&filename_to_play)
|
||||
.await
|
||||
.unwrap_or(false);
|
||||
if !file_exists {
|
||||
match reqwest::get(&joined_args).await {
|
||||
Ok(resp) => {
|
||||
if resp.status().is_success() {
|
||||
match resp.bytes().await {
|
||||
Ok(bytes) => {
|
||||
match tokio::fs::write(&filename_to_play, &bytes).await {
|
||||
Ok(_) => {
|
||||
client
|
||||
.message(format!(
|
||||
"Downloaded midi from {}, into: {}",
|
||||
joined_args, filename_to_play
|
||||
))
|
||||
.await;
|
||||
}
|
||||
Err(e) => {
|
||||
client
|
||||
.message(format!("Failed to write file: {}", e))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
client
|
||||
.message(format!("Failed to read response bytes: {}", e))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client
|
||||
.message(format!("Failed to download file: HTTP {}", resp.status()))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
client
|
||||
.message(format!("Failed to download file: {}", e))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if joined_args.starts_with("files:") {
|
||||
if let Some(caps) = joined_args.strip_prefix("files:") {
|
||||
let first_capture = caps.trim();
|
||||
|
||||
if first_capture.ends_with('/') {
|
||||
client
|
||||
.message("Use the 'playlist' command to play a directory as a playlist.")
|
||||
.await;
|
||||
return;
|
||||
} else {
|
||||
let hashed = simple_hash(first_capture);
|
||||
filename_to_play = format!("midis/{}.mid", hashed);
|
||||
filename_beautiful = first_capture.to_string();
|
||||
|
||||
let url = format!("https://files.sad.ovh/public/midis/{}", first_capture);
|
||||
|
||||
let file_exists = tokio::fs::try_exists(&filename_to_play)
|
||||
.await
|
||||
.unwrap_or(false);
|
||||
if !file_exists {
|
||||
match reqwest::get(&url).await {
|
||||
Ok(resp) => {
|
||||
if resp.status().is_success() {
|
||||
match resp.bytes().await {
|
||||
Ok(bytes) => {
|
||||
match tokio::fs::write(&filename_to_play, &bytes).await
|
||||
{
|
||||
Ok(_) => {
|
||||
client
|
||||
.message(format!(
|
||||
"Downloaded {} from files.sad.ovh.",
|
||||
filename_beautiful
|
||||
))
|
||||
.await;
|
||||
}
|
||||
Err(e) => {
|
||||
client
|
||||
.message(format!(
|
||||
"Failed to write file: {}",
|
||||
e
|
||||
))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
client
|
||||
.message(format!(
|
||||
"Failed to read response bytes: {}",
|
||||
e
|
||||
))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client
|
||||
.message(format!(
|
||||
"Failed to download file: HTTP {}",
|
||||
resp.status()
|
||||
))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
client
|
||||
.message(format!("Failed to download file: {}", e))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
filename_to_play = joined_args.clone();
|
||||
filename_beautiful = joined_args.clone();
|
||||
}
|
||||
|
||||
play_midi_file(
|
||||
filename_to_play,
|
||||
filename_beautiful.clone(),
|
||||
false,
|
||||
self.midi_state.clone(),
|
||||
client.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
client
|
||||
.message(format!("Started playing {}.", filename_beautiful))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue