use std::{fs, path::Path}; use image::EncodableLayout; use webp::Encoder; use axum::Router; use tower_http::services::ServeDir; use crate::music::metadata::CoverData; pub fn get_cover_base_path() -> String { dotenvy::dotenv().ok(); std::env::var("COVER_URL").expect("Error getting cover directory URL") } pub fn get_all_cover_hashes() -> Vec { let path = get_cover_base_path(); let base_path = Path::new(&path); if !base_path.exists() { let _ = fs::create_dir_all(base_path); } let walkdir = walkdir::WalkDir::new(path).min_depth(1).max_depth(1); let hashes: Vec = walkdir .into_iter() .map(|e| { let entry = e.unwrap(); let file_name = entry.file_name().to_str().unwrap(); // len - 5 because the file names end with .webp let hash = file_name[0..(file_name.len() - 5)].to_string(); return hash; }) .collect(); hashes } pub fn write_cover(hash: &str, cover: &CoverData, base_path: &str) -> Result<(), Box> { if cover.mime_type != "image/jpeg" && cover.mime_type != "image/png" && cover.mime_type != "image/webp" { return Err(format!("Invalid cover MIME type: {}", cover.mime_type).into()); } let path = Path::new(&base_path).join(format!("{hash}.webp")); let dynamic_image = image::load_from_memory_with_format( &cover.bytes, match cover.mime_type.as_str() { "image/png" => image::ImageFormat::Png, "image/jpeg" => image::ImageFormat::Jpeg, "image/webp" => image::ImageFormat::WebP, _ => panic!("Invalid cover MIME type (this should never happen)"), }, )?; let resized_image = if dynamic_image.width() > 640 || dynamic_image.height() > 640 { let a = dynamic_image.resize_to_fill(640, 640, image::imageops::FilterType::Lanczos3); drop(dynamic_image); a } else { dynamic_image }; let webp = Encoder::from_image(&resized_image)?.encode_lossless(); fs::write(path, webp.as_bytes())?; Ok(()) } pub async fn create_cover_server() -> Result<(), Box> { let path = get_cover_base_path(); let service = ServeDir::new(path); let app = Router::new().nest_service("/", service); let listener = tokio::net::TcpListener::bind("[::1]:39994").await?; Ok(axum::serve(listener, app).await?) }