From c24df944c51affa606877357f4d2a05e0f54a8c1 Mon Sep 17 00:00:00 2001 From: Florian THIERRY Date: Tue, 14 Jul 2026 00:13:47 +0200 Subject: [PATCH] WIP create a handler to download a file chunk by chunk. --- backend/domain/src/video/error.rs | 6 + backend/domain/src/video/mod.rs | 4 +- backend/domain/src/video/model.rs | 10 +- backend/domain/src/video/port.rs | 16 ++ backend/exposition/Cargo.lock | 321 +++++++++++++++++++--- backend/exposition/Cargo.toml | 4 + backend/exposition/src/lib.rs | 15 - backend/exposition/src/video/handler.rs | 17 ++ backend/exposition/src/video/mod.rs | 1 + backend/infrastructure/Cargo.toml | 1 + backend/infrastructure/src/video/mod.rs | 1 + backend/infrastructure/src/video/model.rs | 13 + backend/launcher/Cargo.lock | 39 +++ backend/launcher/src/main.rs | 48 ++-- 14 files changed, 413 insertions(+), 83 deletions(-) create mode 100644 backend/domain/src/video/error.rs create mode 100644 backend/domain/src/video/port.rs create mode 100644 backend/exposition/src/video/handler.rs create mode 100644 backend/infrastructure/src/video/model.rs diff --git a/backend/domain/src/video/error.rs b/backend/domain/src/video/error.rs new file mode 100644 index 0000000..1ebd4a6 --- /dev/null +++ b/backend/domain/src/video/error.rs @@ -0,0 +1,6 @@ +pub enum VideoError { + VideoDoesNotExists, + FolderDoesNotExists, + FolderBrowsingTechnicalIssue, + FileReadingTechnicalIssue +} \ No newline at end of file diff --git a/backend/domain/src/video/mod.rs b/backend/domain/src/video/mod.rs index 99bbd12..832ba93 100644 --- a/backend/domain/src/video/mod.rs +++ b/backend/domain/src/video/mod.rs @@ -1 +1,3 @@ -pub mod model; \ No newline at end of file +pub mod error; +pub mod model; +pub mod port; \ No newline at end of file diff --git a/backend/domain/src/video/model.rs b/backend/domain/src/video/model.rs index e1e6fdc..aaf4516 100644 --- a/backend/domain/src/video/model.rs +++ b/backend/domain/src/video/model.rs @@ -1,3 +1,5 @@ +use uuid::Uuid; + pub enum VideoFormat { AVI, MKV, @@ -6,7 +8,11 @@ pub enum VideoFormat { UNKNOWN } +pub trait VideoFile {} + pub struct Video { - filename: String, - format: VideoFormat + pub id: Uuid, + pub filename: String, + pub file: Box, + pub format: VideoFormat, } \ No newline at end of file diff --git a/backend/domain/src/video/port.rs b/backend/domain/src/video/port.rs new file mode 100644 index 0000000..02b7550 --- /dev/null +++ b/backend/domain/src/video/port.rs @@ -0,0 +1,16 @@ +use uuid::Uuid; +use crate::video::error::VideoError; +use crate::video::model::Video; + +pub trait VideoPort: Send + Sync { + fn list_root(&self) -> Result, VideoError>; + fn list_folder(&self, folder_id: Uuid) -> Result, VideoError>; + fn get_video_stream(&self, video_id: Uuid) -> Result; + fn clone_box(&self) -> Box; +} + +impl Clone for Box { + fn clone(&self) -> Self { + self.clone_box() + } +} \ No newline at end of file diff --git a/backend/exposition/Cargo.lock b/backend/exposition/Cargo.lock index b2a7e47..3b9502c 100644 --- a/backend/exposition/Cargo.lock +++ b/backend/exposition/Cargo.lock @@ -41,7 +41,7 @@ dependencies = [ "foldhash", "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", @@ -266,12 +266,92 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[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" @@ -740,12 +820,32 @@ dependencies = [ "percent-encoding", ] +[[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.32" @@ -765,6 +865,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "slab", @@ -840,7 +941,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", "indexmap", "slab", "tokio", @@ -898,6 +999,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" @@ -910,6 +1044,41 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[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" @@ -1190,6 +1359,55 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "lumiere-application" +version = "1.0.0" +dependencies = [ + "axum", + "bcrypt", + "chrono", + "jsonwebtoken", + "log", + "lumiere-domain", + "mime", + "rand 0.10.1", + "tokio", + "uuid", +] + +[[package]] +name = "lumiere-domain" +version = "1.0.0" +dependencies = [ + "chrono", + "serde", + "uuid", +] + +[[package]] +name = "lumiere-exposition" +version = "1.0.0" +dependencies = [ + "actix-web", + "actix-web-httpauth", + "async-stream", + "chrono", + "futures-util", + "jsonwebtoken", + "lumiere-application", + "lumiere-domain", + "serde", + "tokio", + "tokio-util", + "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" @@ -1757,6 +1975,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_urlencoded" version = "0.7.1" @@ -1895,42 +2124,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "lumiere-application" -version = "1.0.0" -dependencies = [ - "bcrypt", - "chrono", - "jsonwebtoken", - "log", - "rand 0.10.1", - "lumiere-domain", - "uuid", -] - -[[package]] -name = "lumiere-domain" -version = "1.0.0" -dependencies = [ - "chrono", - "serde", - "uuid", -] - -[[package]] -name = "lumiere-exposition" -version = "1.0.0" -dependencies = [ - "actix-web", - "actix-web-httpauth", - "chrono", - "jsonwebtoken", - "serde", - "lumiere-application", - "lumiere-domain", - "uuid", -] - [[package]] name = "syn" version = "2.0.117" @@ -1942,6 +2135,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" @@ -2016,9 +2215,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.52.0" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91135f59b1cbf38c91e73cf3386fca9bb77915c45ce2771460c9d92f0f3d776" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -2027,9 +2226,21 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "socket2 0.6.3", + "tokio-macros", "windows-sys 0.61.2", ] +[[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" @@ -2053,6 +2264,34 @@ dependencies = [ "tokio", ] +[[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/exposition/Cargo.toml b/backend/exposition/Cargo.toml index 812e028..33948ea 100644 --- a/backend/exposition/Cargo.toml +++ b/backend/exposition/Cargo.toml @@ -13,7 +13,11 @@ lumiere-application = { path = "../application" } actix-web = { version = "4.13.0", features = ["rustls-0_23"] } actix-web-httpauth = "0.8.2" +async-stream = "0.3.6" +futures-util = "0.3.32" chrono = "0.4.44" jsonwebtoken = { version = "10.3.0", features = ["rust_crypto"] } serde = { version = "1.0.228", features = ["derive"] } uuid = { version = "1.23.0", features = ["v4", "serde"] } +tokio = { version = "1.52.3", features = ["fs"] } +tokio-util = "0.7.18" diff --git a/backend/exposition/src/lib.rs b/backend/exposition/src/lib.rs index 148ec54..1b5db8c 100644 --- a/backend/exposition/src/lib.rs +++ b/backend/exposition/src/lib.rs @@ -1,6 +1,5 @@ extern crate core; -use crate::user::handler::{create_new_user, delete_by_id, get_all_users, initialise, login, update_user, update_user_password}; use crate::user::validator::validator; use actix_web::web::ServiceConfig; use actix_web::{get, web, HttpResponse, Responder}; @@ -15,20 +14,6 @@ async fn health() -> impl Responder { } pub fn init(cfg: &mut ServiceConfig) { - cfg.service(health) - .service(health) - .service(initialise) - .service(login) - .service( - web::scope("") - .wrap(HttpAuthentication::bearer(validator)) - .service(create_new_user) - .service(update_user) - .service(update_user_password) - .service(get_all_users) - .service(delete_by_id) - ); - cfg.default_service(web::to(not_found)); } diff --git a/backend/exposition/src/video/handler.rs b/backend/exposition/src/video/handler.rs new file mode 100644 index 0000000..d91966d --- /dev/null +++ b/backend/exposition/src/video/handler.rs @@ -0,0 +1,17 @@ +use actix_web::{get, HttpResponse}; +use tokio::fs::File; +use tokio_util::codec::{BytesCodec, FramedRead}; +use futures_util::stream::StreamExt; + +#[get("/download")] +pub async fn download_file() -> HttpResponse { + HttpResponse::Ok() + .content_type("application/octet-stream") + .streaming(async_stream::stream! { + let file = File::open("/home/florian/Video/2019-06-02 17-09-30.flv").await.expect("Failed to open file"); + let mut stream = FramedRead::new(file, BytesCodec::new()); + while let Some(chunk) = stream.next().await { + yield chunk.map(|bytes| bytes.freeze()); + } + }) +} \ No newline at end of file diff --git a/backend/exposition/src/video/mod.rs b/backend/exposition/src/video/mod.rs index 089fda1..6ed9738 100644 --- a/backend/exposition/src/video/mod.rs +++ b/backend/exposition/src/video/mod.rs @@ -1 +1,2 @@ +pub mod handler; pub mod video_format_content_type_mapper; \ No newline at end of file diff --git a/backend/infrastructure/Cargo.toml b/backend/infrastructure/Cargo.toml index 6867e18..bd9eff5 100644 --- a/backend/infrastructure/Cargo.toml +++ b/backend/infrastructure/Cargo.toml @@ -17,4 +17,5 @@ log = "0.4.29" rand = "0.10.1" rusqlite = { version = "0.39.0", features = ["bundled"] } sysinfo = "0.39.1" +tokio = { version = "1.52.3", features = ["fs"] } uuid = { version = "1.23.1", features = ["v4"] } \ No newline at end of file diff --git a/backend/infrastructure/src/video/mod.rs b/backend/infrastructure/src/video/mod.rs index 53d6d11..cf6b588 100644 --- a/backend/infrastructure/src/video/mod.rs +++ b/backend/infrastructure/src/video/mod.rs @@ -1 +1,2 @@ +pub mod model; pub mod video_format_detector; \ No newline at end of file diff --git a/backend/infrastructure/src/video/model.rs b/backend/infrastructure/src/video/model.rs new file mode 100644 index 0000000..22070c7 --- /dev/null +++ b/backend/infrastructure/src/video/model.rs @@ -0,0 +1,13 @@ +use std::fs::File; + +pub struct TokioVideoFile { + pub file: File +} + +impl TokioVideoFile { + pub fn new(file_path: &String) -> Self { + Self { + file: File::open(file_path).expect(format!("Failed to open file {file_path}").as_str()) + } + } +} \ No newline at end of file diff --git a/backend/launcher/Cargo.lock b/backend/launcher/Cargo.lock index 26cbae6..b8cbf5a 100644 --- a/backend/launcher/Cargo.lock +++ b/backend/launcher/Cargo.lock @@ -316,6 +316,28 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -1198,6 +1220,17 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.32" @@ -1217,6 +1250,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "slab", @@ -1836,11 +1870,15 @@ version = "1.0.0" dependencies = [ "actix-web", "actix-web-httpauth", + "async-stream", "chrono", + "futures-util", "jsonwebtoken", "lumiere-application", "lumiere-domain", "serde", + "tokio", + "tokio-util", "uuid", ] @@ -1856,6 +1894,7 @@ dependencies = [ "rand 0.10.1", "rusqlite", "sysinfo", + "tokio", "uuid", ] diff --git a/backend/launcher/src/main.rs b/backend/launcher/src/main.rs index b478206..6d642c2 100644 --- a/backend/launcher/src/main.rs +++ b/backend/launcher/src/main.rs @@ -16,30 +16,30 @@ use surveillant_systeme_exposition::video::video_format_content_type_mapper::map use surveillant_systeme_infrastructure::video::video_format_detector::get_content_type; use tokio_cron_scheduler::{Job, JobScheduler}; -// #[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 +#[actix_web::main] +async fn main() -> std::io::Result<()> { + // 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 {