diff --git a/backend/application/Cargo.toml b/backend/application/Cargo.toml index 833fc9e..cb2963e 100644 --- a/backend/application/Cargo.toml +++ b/backend/application/Cargo.toml @@ -14,6 +14,9 @@ path = "tests/user_service_test.rs" [dependencies] lumiere-domain = { path = "../domain" } +axum = "0.8.9" +tokio = "1.52.3" +mime = "0.3.17" bcrypt = "0.19.0" chrono = { version = "0.4.44", features = ["serde"] } log = "0.4.29" diff --git a/backend/application/src/video/api.rs b/backend/application/src/video/api.rs index 2d41d87..175c9d7 100644 --- a/backend/application/src/video/api.rs +++ b/backend/application/src/video/api.rs @@ -67,12 +67,50 @@ fn sanitize_and_resolve_path(video_path: &str) -> Result &'static str { + use std::io::Read; + + // Read first few bytes to detect magic numbers + let mut file = match File::open(path) { + Ok(f) => f, + Err(_) => return "application/octet-stream", + }; + + let mut buffer = [0u8; 16]; // Read enough bytes to detect common formats + let bytes_read = match file.read(&mut buffer) { + Ok(n) if n > 0 => n, + _ => return "application/octet-stream", + }; + + // Detect AVI format (RIFF header with AVI) + if bytes_read >= 12 && buffer[0..4] == [82, 73, 70, 70] && buffer[8..12] == [65, 86, 73, 32] { + return "video/x-msvideo"; + } + + // Detect MKV format (EB ML header) + if bytes_read >= 4 && buffer[0..4] == [0x1A, 0x45, 0xDF, 0xA3] { + return "video/x-matroska"; // Proper MIME type for MKV + } + + // Detect MP4/MOV format (ftyp box) + if bytes_read >= 8 && buffer[4..8] == [102, 116, 121, 112] { + return "video/mp4"; + } + + // Detect WebM format (EB ML header like MKV but with different signature) + if bytes_read >= 8 && buffer[0..4] == [0x1A, 0x45, 0xDF, 0xA3] { + // Check for WebM signature at offset 4 + if bytes_read >= 12 && buffer[4..8] == [119, 101, 98, 109] { + return "video/webm"; + } + } + + // Fallback to extension-based detection for unknown formats match path.extension().and_then(|ext| ext.to_str()) { - Some("avi") => "video/x-msvideo", - Some("mkv") | Some("mp4") | Some("mov") | Some("webm") => "video/mp4", // Generic video type - _ => "application/octet-stream", // Fallback for unknown types + Some("mp4") | Some("mov") => "video/mp4", + Some("webm") => "video/webm", + _ => "application/octet-stream", } } diff --git a/backend/application/src/video/mod.rs b/backend/application/src/video/mod.rs index cc097b5..6c73b40 100644 --- a/backend/application/src/video/mod.rs +++ b/backend/application/src/video/mod.rs @@ -1 +1,2 @@ -pub mod service; \ No newline at end of file +pub mod service; +//pub mod api; \ No newline at end of file diff --git a/backend/domain/src/video/model.rs b/backend/domain/src/video/model.rs index 3fa774b..e1e6fdc 100644 --- a/backend/domain/src/video/model.rs +++ b/backend/domain/src/video/model.rs @@ -1,4 +1,12 @@ +pub enum VideoFormat { + AVI, + MKV, + MP4, + WEBM, + UNKNOWN +} + pub struct Video { filename: String, - + format: VideoFormat } \ No newline at end of file diff --git a/backend/exposition/src/lib.rs b/backend/exposition/src/lib.rs index a6c2960..148ec54 100644 --- a/backend/exposition/src/lib.rs +++ b/backend/exposition/src/lib.rs @@ -7,6 +7,7 @@ use actix_web::{get, web, HttpResponse, Responder}; use actix_web_httpauth::middleware::HttpAuthentication; pub mod user; +pub mod video; #[get("/health")] async fn health() -> impl Responder { diff --git a/backend/exposition/src/video/mod.rs b/backend/exposition/src/video/mod.rs new file mode 100644 index 0000000..089fda1 --- /dev/null +++ b/backend/exposition/src/video/mod.rs @@ -0,0 +1 @@ +pub mod video_format_content_type_mapper; \ No newline at end of file diff --git a/backend/exposition/src/video/video_format_content_type_mapper.rs b/backend/exposition/src/video/video_format_content_type_mapper.rs new file mode 100644 index 0000000..fc6eb3e --- /dev/null +++ b/backend/exposition/src/video/video_format_content_type_mapper.rs @@ -0,0 +1,11 @@ +use surveillant_systeme_domain::video::model::VideoFormat; + +pub fn map_to_content_type(video_format: &VideoFormat) -> &'static str { + match video_format { + VideoFormat::AVI => "video/x-msvideo", + VideoFormat::MKV => "video/x-matroska", + VideoFormat::MP4 => "video/mp4", + VideoFormat::WEBM => "video/webm", + VideoFormat::UNKNOWN => "application/octet-stream" + } +} \ No newline at end of file diff --git a/backend/infrastructure/src/lib.rs b/backend/infrastructure/src/lib.rs index 1681016..b239b1a 100644 --- a/backend/infrastructure/src/lib.rs +++ b/backend/infrastructure/src/lib.rs @@ -2,3 +2,4 @@ pub mod common; pub mod configuration; pub mod schema; pub mod user; +pub mod video; diff --git a/backend/infrastructure/src/video/mod.rs b/backend/infrastructure/src/video/mod.rs new file mode 100644 index 0000000..53d6d11 --- /dev/null +++ b/backend/infrastructure/src/video/mod.rs @@ -0,0 +1 @@ +pub mod video_format_detector; \ No newline at end of file diff --git a/backend/infrastructure/src/video/video_format_detector.rs b/backend/infrastructure/src/video/video_format_detector.rs new file mode 100644 index 0000000..20c82c2 --- /dev/null +++ b/backend/infrastructure/src/video/video_format_detector.rs @@ -0,0 +1,51 @@ +use std::fs::File; +use std::path::Path; +use surveillant_systeme_domain::video::model::VideoFormat; +use surveillant_systeme_domain::video::model::VideoFormat::{AVI, MKV, MP4, UNKNOWN, WEBM}; + +/// Determine Content-Type by reading file headers (magic numbers) +pub fn get_content_type(path: &Path) -> VideoFormat { + use std::io::Read; + + // Read first few bytes to detect magic numbers + let mut file = match File::open(path) { + Ok(f) => f, + Err(_) => return UNKNOWN, + }; + + let mut buffer = [0u8; 16]; // Read enough bytes to detect common formats + let bytes_read = match file.read(&mut buffer) { + Ok(n) if n > 0 => n, + _ => return UNKNOWN, + }; + + // Detect AVI format (RIFF header with AVI) + if bytes_read >= 12 && buffer[0..4] == [82, 73, 70, 70] && buffer[8..12] == [65, 86, 73, 32] { + return AVI; + } + + // Detect MKV format (EB ML header) + if bytes_read >= 4 && buffer[0..4] == [0x1A, 0x45, 0xDF, 0xA3] { + return MKV; // Proper MIME type for MKV + } + + // Detect MP4/MOV format (ftyp box) + if bytes_read >= 8 && buffer[4..8] == [102, 116, 121, 112] { + return MP4; + } + + // Detect WebM format (EB ML header like MKV but with different signature) + if bytes_read >= 8 && buffer[0..4] == [0x1A, 0x45, 0xDF, 0xA3] { + // Check for WebM signature at offset 4 + if bytes_read >= 12 && buffer[4..8] == [119, 101, 98, 109] { + return WEBM; + } + } + + // Fallback to extension-based detection for unknown formats + match path.extension().and_then(|ext| ext.to_str()) { + Some("mp4") | Some("mov") => MP4, + Some("webm") => WEBM, + _ => UNKNOWN, + } +} \ No newline at end of file diff --git a/backend/launcher/Cargo.lock b/backend/launcher/Cargo.lock index 697a6c9..26cbae6 100644 --- a/backend/launcher/Cargo.lock +++ b/backend/launcher/Cargo.lock @@ -41,7 +41,7 @@ dependencies = [ "foldhash 0.1.5", "futures-core", "h2", - "http", + "http 0.2.12", "httparse", "httpdate", "itoa", @@ -77,7 +77,7 @@ checksum = "14f8c75c51892f18d9c46150c5ac7beb81c95f78c8b83a634d49f4ca32551fe7" dependencies = [ "bytestring", "cfg-if", - "http", + "http 0.2.12", "regex", "regex-lite", "serde", @@ -316,6 +316,12 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.5.0" @@ -344,6 +350,58 @@ dependencies = [ "fs_extra", ] +[[package]] +name = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http 1.4.2", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.2", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -1125,6 +1183,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" version = "0.3.32" @@ -1225,7 +1292,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", "indexmap", "slab", "tokio", @@ -1301,6 +1368,39 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http 1.4.2", +] + +[[package]] +name = "http-body-util" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.2", + "http-body", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.10.1" @@ -1322,6 +1422,41 @@ dependencies = [ "typenum", ] +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http 1.4.2", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "bytes", + "http 1.4.2", + "http-body", + "hyper", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "iana-time-zone" version = "0.1.65" @@ -1674,12 +1809,15 @@ dependencies = [ name = "lumiere-application" version = "1.0.0" dependencies = [ + "axum", "bcrypt", "chrono", "jsonwebtoken", "log", "lumiere-domain", + "mime", "rand 0.10.1", + "tokio", "uuid", ] @@ -1721,6 +1859,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "memchr" version = "2.8.0" @@ -2433,6 +2577,17 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + [[package]] name = "serde_spanned" version = "1.1.1" @@ -2636,6 +2791,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + [[package]] name = "synstructure" version = "0.13.2" @@ -2736,6 +2897,7 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "socket2 0.6.3", + "tokio-macros", "windows-sys 0.61.2", ] @@ -2755,6 +2917,17 @@ dependencies = [ "uuid", ] +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio-rustls" version = "0.26.4" @@ -2809,6 +2982,34 @@ dependencies = [ "winnow 1.0.3", ] +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.44" diff --git a/backend/launcher/src/main.rs b/backend/launcher/src/main.rs index bc3739b..b478206 100644 --- a/backend/launcher/src/main.rs +++ b/backend/launcher/src/main.rs @@ -9,28 +9,37 @@ use rustls::ServerConfig; use std::env; use std::fs::File; use std::io::{BufReader, Error}; +use std::path::Path; use std::sync::Arc; use surveillant_systeme_exposition::init; +use surveillant_systeme_exposition::video::video_format_content_type_mapper::map_to_content_type; +use surveillant_systeme_infrastructure::video::video_format_detector::get_content_type; use tokio_cron_scheduler::{Job, JobScheduler}; -#[actix_web::main] -async fn main() -> std::io::Result<()>{ - dotenv().ok(); - unsafe { env::set_var("RUST_LOG", "debug"); } - check_all_variables_are_set(); - env_logger::init(); - - let application_context = ApplicationContext::init()?; - - let tls_config = init_tls_configuration()?; - let server = HttpServer::new( - move || App::new().configure(init) - .app_data(web::Data::new(Arc::new(application_context.user_service.clone()))) - ) - .bind_rustls_0_23(("0.0.0.0", 19000), tls_config) - .expect("Unable to start server"); - - server.run().await +// #[actix_web::main] +fn main() { + let path_str = "/home/florian/Videos/AirFly/GH010015.MP4"; + let path = Path::new(path_str); + let file_type = get_content_type(&path); + let file_content_type = map_to_content_type(&file_type); + println!("file_type={file_content_type}"); + () + // dotenv().ok(); + // unsafe { env::set_var("RUST_LOG", "debug"); } + // check_all_variables_are_set(); + // env_logger::init(); + // + // let application_context = ApplicationContext::init()?; + // + // let tls_config = init_tls_configuration()?; + // let server = HttpServer::new( + // move || App::new().configure(init) + // .app_data(web::Data::new(Arc::new(application_context.user_service.clone()))) + // ) + // .bind_rustls_0_23(("0.0.0.0", 19000), tls_config) + // .expect("Unable to start server"); + // + // server.run().await } fn init_tls_configuration() -> Result { @@ -60,4 +69,4 @@ fn check_all_variables_are_set() { env::var(variable).expect(format!("{} should be defined!", variable).as_str()); }); info!("All variables are set correctly."); -} +} \ No newline at end of file