diff --git a/halloween_player/Cargo.toml b/halloween_player/Cargo.toml new file mode 100644 index 0000000..d915d2b --- /dev/null +++ b/halloween_player/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "halloween_player" +version = "0.1.0" +edition = "2021" + +[dependencies] +actix-web = "4" +actix-rt = "2" +serde = { version = "1.0", features = ["derive"] } \ No newline at end of file diff --git a/halloween_player/audio/evil_laugh.wav b/halloween_player/audio/evil_laugh.wav new file mode 100644 index 0000000..945767a Binary files /dev/null and b/halloween_player/audio/evil_laugh.wav differ diff --git a/halloween_player/src/main.rs b/halloween_player/src/main.rs new file mode 100644 index 0000000..59e10c7 --- /dev/null +++ b/halloween_player/src/main.rs @@ -0,0 +1,494 @@ +use actix_web::{web, App, HttpResponse, HttpServer, Result}; +use std::process::Command; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::io::Write; + +// WAV-Datei direkt ins Binary einbetten +static EVIL_LAUGH_WAV: &[u8] = include_bytes!("../audio/evil_laugh.wav"); + +#[derive(Deserialize)] +struct PlayRequest { + file: Option, +} + +#[derive(Serialize)] +struct SystemStatus { + cpu_temp: String, + cpu_usage: String, + memory_usage: String, + disk_usage: String, + uptime: String, + hostname: String, +} + +// Systemstatus abrufen +async fn get_status() -> Result { + let status = SystemStatus { + cpu_temp: get_cpu_temp(), + cpu_usage: get_cpu_usage(), + memory_usage: get_memory_usage(), + disk_usage: get_disk_usage(), + uptime: get_uptime(), + hostname: get_hostname(), + }; + + Ok(HttpResponse::Ok().json(status)) +} + +fn get_cpu_temp() -> String { + fs::read_to_string("/sys/class/thermal/thermal_zone0/temp") + .ok() + .and_then(|s| s.trim().parse::().ok()) + .map(|t| format!("{:.1}°C", t / 1000.0)) + .unwrap_or_else(|| "N/A".to_string()) +} + +fn get_cpu_usage() -> String { + Command::new("sh") + .arg("-c") + .arg("top -bn1 | grep 'Cpu(s)' | awk '{print $2}' | cut -d'%' -f1") + .output() + .ok() + .and_then(|o| String::from_utf8(o.stdout).ok()) + .map(|s| format!("{}%", s.trim())) + .unwrap_or_else(|| "N/A".to_string()) +} + +fn get_memory_usage() -> String { + Command::new("sh") + .arg("-c") + .arg("free -m | awk 'NR==2{printf \"%.0f/%.0fMB (%.0f%%)\", $3,$2,$3*100/$2}'") + .output() + .ok() + .and_then(|o| String::from_utf8(o.stdout).ok()) + .map(|s| s.trim().to_string()) + .unwrap_or_else(|| "N/A".to_string()) +} + +fn get_disk_usage() -> String { + Command::new("sh") + .arg("-c") + .arg("df -h / | awk 'NR==2{printf \"%s/%s (%s)\", $3,$2,$5}'") + .output() + .ok() + .and_then(|o| String::from_utf8(o.stdout).ok()) + .map(|s| s.trim().to_string()) + .unwrap_or_else(|| "N/A".to_string()) +} + +fn get_uptime() -> String { + Command::new("uptime") + .arg("-p") + .output() + .ok() + .and_then(|o| String::from_utf8(o.stdout).ok()) + .map(|s| s.trim().replace("up ", "")) + .unwrap_or_else(|| "N/A".to_string()) +} + +fn get_hostname() -> String { + Command::new("hostname") + .output() + .ok() + .and_then(|o| String::from_utf8(o.stdout).ok()) + .map(|s| s.trim().to_string()) + .unwrap_or_else(|| "raspberry-pi".to_string()) +} + +// Hauptendpunkt zum Abspielen des eingebetteten gruseligen Lachens +async fn play_laugh() -> Result { + // Temporäre Datei erstellen + let temp_path = "/tmp/evil_laugh.wav"; + + match fs::File::create(temp_path) { + Ok(mut file) => { + if let Err(e) = file.write_all(EVIL_LAUGH_WAV) { + return Ok(HttpResponse::InternalServerError() + .body(format!("Fehler beim Schreiben: {}", e))); + } + } + Err(e) => { + return Ok(HttpResponse::InternalServerError() + .body(format!("Fehler beim Erstellen der Datei: {}", e))); + } + } + + // WAV-Datei abspielen + match Command::new("aplay") + .arg(temp_path) + .spawn() + { + Ok(_) => { + println!("🎃 Spiele gruseliges Lachen ab!"); + Ok(HttpResponse::Ok() + .body("🎃 Muahahaha!")) + }, + Err(e) => Ok(HttpResponse::InternalServerError() + .body(format!("Fehler: {}", e))), + } +} + +// Flexibler Endpunkt zum Abspielen beliebiger Dateien aus /home/pi/audio +async fn play_custom(info: web::Query) -> Result { + let filename = match &info.file { + Some(f) => f, + None => return Ok(HttpResponse::BadRequest() + .body("Bitte Dateinamen angeben")), + }; + + let wav_path = format!("/home/pi/audio/{}", filename); + + if !std::path::Path::new(&wav_path).exists() { + return Ok(HttpResponse::NotFound() + .body(format!("Datei nicht gefunden: {}", filename))); + } + + match Command::new("aplay") + .arg(&wav_path) + .spawn() + { + Ok(_) => { + println!("🎃 Spiele ab: {}", filename); + Ok(HttpResponse::Ok() + .body(format!("Spiele ab: {}", filename))) + }, + Err(e) => Ok(HttpResponse::InternalServerError() + .body(format!("Fehler: {}", e))), + } +} + +// Endpunkt zum Auflisten verfügbarer Halloween-Sounds aus /home/pi/audio +async fn list_sounds() -> Result { + let audio_dir = "/home/pi/audio"; + + match std::fs::read_dir(audio_dir) { + Ok(entries) => { + let files: Vec = entries + .filter_map(|e| e.ok()) + .filter(|e| { + e.path() + .extension() + .and_then(|s| s.to_str()) + .map(|s| s.eq_ignore_ascii_case("wav")) + .unwrap_or(false) + }) + .filter_map(|e| e.file_name().into_string().ok()) + .collect(); + + Ok(HttpResponse::Ok().json(files)) + } + Err(_) => { + // Wenn Ordner nicht existiert, leere Liste zurückgeben + Ok(HttpResponse::Ok().json(Vec::::new())) + } + } +} + +// Weboberfläche +async fn index() -> Result { + let html = r#" + + + + + + 🎃 Halloween Player + + + +
+

🎃 HALLOWEEN PLAYER 🎃

+ +
+ +
+
+

👻 Sounds abspielen

+ +
+ ⚡ Das gruselige Lachen ist direkt im Programm eingebettet! +
+ +
+
+ +
+

📊 Raspberry Pi Status

+
Lade Status...
+ +
+
+
+ + + + + "#; + + Ok(HttpResponse::Ok() + .content_type("text/html; charset=utf-8") + .body(html)) +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + println!("🎃 === Halloween Player Webserver === 🎃"); + println!("Server läuft auf 0.0.0.0:8080"); + println!("Weboberfläche: http://localhost:8080"); + println!("WAV-Datei ist eingebettet: {} bytes", EVIL_LAUGH_WAV.len()); + println!(); + + HttpServer::new(|| { + App::new() + .route("/", web::get().to(index)) + .route("/laugh", web::get().to(play_laugh)) + .route("/play", web::get().to(play_custom)) + .route("/sounds", web::get().to(list_sounds)) + .route("/status", web::get().to(get_status)) + }) + .bind("0.0.0.0:8080")? + .run() + .await +}