click

feat: load sounds from a directory and let requests specify which sound they want to use

ptr.pet f84ac379 434eefeb

verified
+61 -15
+1
.gitignore
··· 1 1 /target 2 2 result 3 + sounds
+60 -15
src/main.rs
··· 1 1 use std::{ 2 + collections::HashMap, 2 3 io::{BufRead, BufReader, Write}, 3 - net::TcpListener, 4 + net::{TcpListener, TcpStream}, 4 5 }; 5 6 6 7 use quad_snd::*; 7 - 8 - const CLICK: &[u8] = include_bytes!("./sound.ogg"); 9 8 10 9 fn main() { 11 10 let ctx = AudioContext::new(); 12 - let click = Sound::load(&ctx, CLICK); 11 + let sounds = std::fs::read_dir("sounds") 12 + .expect("cant read sounds") 13 + .flat_map(|f| { 14 + let p = f.ok()?.path(); 15 + let n = p.file_stem()?.to_string_lossy().into_owned(); 16 + Some(( 17 + n, 18 + Sound::load(&ctx, &std::fs::read(p).expect("can't load sound")), 19 + )) 20 + }) 21 + .collect::<HashMap<String, Sound>>(); 22 + 23 + let handle_request = |mut stream: TcpStream| { 24 + let mut lines = BufReader::new(&stream) 25 + .lines() 26 + .flatten() 27 + .take_while(|line| !line.is_empty()); 28 + 29 + let request_line = lines.next()?; 30 + let query = parse_query_from_request(request_line.as_str()); 31 + let kind = query.get("kind").cloned().unwrap_or("sound"); 32 + 33 + if let Some(sound) = sounds.get(kind) { 34 + sound.play(&ctx, PlaySoundParams::default()); 35 + } 36 + 37 + // exhaust connection to not get "connection reset by peer" 38 + lines.for_each(drop); 39 + // this never "fails" 40 + let response = "HTTP/1.1 200 OK\r\n\r\n"; 41 + let _ = stream.write_all(response.as_bytes()); 42 + 43 + Some(()) 44 + }; 13 45 14 46 let port = std::env::var("PORT") 15 47 .ok() ··· 18 50 let listener = TcpListener::bind(("0.0.0.0", port)).expect("cant bind"); 19 51 20 52 for stream in listener.incoming() { 21 - let Ok(mut stream) = stream else { 53 + let Ok(stream) = stream else { 22 54 continue; 23 55 }; 24 - click.play(&ctx, PlaySoundParams::default()); 25 - // exhaust connection to not get "connection reset by peer" 26 - BufReader::new(&stream) 27 - .lines() 28 - .flatten() 29 - .take_while(|line| !line.is_empty()) 30 - .for_each(drop); 31 - // this never "fails" 32 - let response = "HTTP/1.1 200 OK\r\n\r\n"; 33 - let _ = stream.write_all(response.as_bytes()); 56 + handle_request(stream); 34 57 } 35 58 } 59 + 60 + fn parse_query_from_request(request_line: &str) -> HashMap<&str, &str> { 61 + request_line 62 + .split_whitespace() 63 + .nth(1) 64 + .and_then(|path| path.split_once('?')) 65 + .map(|(_, query)| { 66 + query 67 + .split('&') 68 + .filter_map(|param| { 69 + if let Some((key, value)) = param.split_once('=') { 70 + Some((key, value)) 71 + } else if !param.is_empty() { 72 + Some((param, "")) 73 + } else { 74 + None 75 + } 76 + }) 77 + .collect() 78 + }) 79 + .unwrap_or_default() 80 + }
src/sound.ogg sounds/sound.ogg