From 78ca22e1b7c8646914f8419a28bc854d6eaf2e18 Mon Sep 17 00:00:00 2001 From: TolaMironcenko Date: Sun, 15 Feb 2026 17:40:32 +0700 Subject: [PATCH] added audio and video messages --- Cargo.lock | 208 ++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 +- src/api/upload.rs | 62 +++++++++++-- web/css/index.css | 47 ++++++++++ web/index.html | 17 ++++ web/js/recording.js | 162 ++++++++++++++++++++++++++++++++++ 6 files changed, 492 insertions(+), 7 deletions(-) create mode 100644 web/js/recording.js diff --git a/Cargo.lock b/Cargo.lock index 503e423..f55d71d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,6 +47,12 @@ dependencies = [ "libc", ] +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "async-trait" version = "0.1.89" @@ -527,6 +533,12 @@ dependencies = [ "zune-inflate", ] +[[package]] +name = "extended" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365" + [[package]] name = "fastrand" version = "2.3.0" @@ -1262,6 +1274,7 @@ dependencies = [ "serde", "serde_json", "sqlx", + "symphonia", "tokio", "tower-http", "tracing", @@ -2188,6 +2201,201 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "symphonia" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5773a4c030a19d9bfaa090f49746ff35c75dfddfa700df7a5939d5e076a57039" +dependencies = [ + "lazy_static", + "symphonia-bundle-flac", + "symphonia-bundle-mp3", + "symphonia-codec-aac", + "symphonia-codec-adpcm", + "symphonia-codec-alac", + "symphonia-codec-pcm", + "symphonia-codec-vorbis", + "symphonia-core", + "symphonia-format-caf", + "symphonia-format-isomp4", + "symphonia-format-mkv", + "symphonia-format-ogg", + "symphonia-format-riff", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-bundle-flac" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91565e180aea25d9b80a910c546802526ffd0072d0b8974e3ebe59b686c9976" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-bundle-mp3" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4872dd6bb56bf5eac799e3e957aa1981086c3e613b27e0ac23b176054f7c57ed" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-codec-aac" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c263845aa86881416849c1729a54c7f55164f8b96111dba59de46849e73a790" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-codec-adpcm" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dddc50e2bbea4cfe027441eece77c46b9f319748605ab8f3443350129ddd07f" +dependencies = [ + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-codec-alac" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8413fa754942ac16a73634c9dfd1500ed5c61430956b33728567f667fdd393ab" +dependencies = [ + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-codec-pcm" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e89d716c01541ad3ebe7c91ce4c8d38a7cf266a3f7b2f090b108fb0cb031d95" +dependencies = [ + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-codec-vorbis" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f025837c309cd69ffef572750b4a2257b59552c5399a5e49707cc5b1b85d1c73" +dependencies = [ + "log", + "symphonia-core", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea00cc4f79b7f6bb7ff87eddc065a1066f3a43fe1875979056672c9ef948c2af" +dependencies = [ + "arrayvec", + "bitflags 1.3.2", + "bytemuck", + "lazy_static", + "log", +] + +[[package]] +name = "symphonia-format-caf" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8faf379316b6b6e6bbc274d00e7a592e0d63ff1a7e182ce8ba25e24edd3d096" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-format-isomp4" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243739585d11f81daf8dac8d9f3d18cc7898f6c09a259675fc364b382c30e0a5" +dependencies = [ + "encoding_rs", + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-format-mkv" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122d786d2c43a49beb6f397551b4a050d8229eaa54c7ddf9ee4b98899b8742d0" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-format-ogg" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b4955c67c1ed3aa8ae8428d04ca8397fbef6a19b2b051e73b5da8b1435639cb" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-format-riff" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d7c3df0e7d94efb68401d81906eae73c02b40d5ec1a141962c592d0f11a96f" +dependencies = [ + "extended", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-metadata" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36306ff42b9ffe6e5afc99d49e121e0bd62fe79b9db7b9681d48e29fa19e6b16" +dependencies = [ + "encoding_rs", + "lazy_static", + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-utils-xiph" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27c85ab799a338446b68eec77abf42e1a6f1bb490656e121c6e27bfbab9f16" +dependencies = [ + "symphonia-core", + "symphonia-metadata", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/Cargo.toml b/Cargo.toml index d21e70a..899806e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,4 +18,5 @@ rand = "0.8" chrono = { version = "0.4", features = ["serde"] } uuid = { version = "1.0", features = ["v4"] } image = "0.24" -config = "0.13" \ No newline at end of file +config = "0.13" +symphonia = { version = "0.5", features = ["all"] } diff --git a/src/api/upload.rs b/src/api/upload.rs index a81eaee..877dc78 100644 --- a/src/api/upload.rs +++ b/src/api/upload.rs @@ -6,6 +6,9 @@ use axum::response::Json; use image::GenericImageView; use std::path::Path; use std::sync::Arc; +use symphonia::core::formats::FormatOptions; +use symphonia::core::meta::MetadataOptions; +use symphonia::core::probe::Hint; use tokio::fs; use tokio::io::AsyncWriteExt; @@ -180,13 +183,14 @@ pub async fn upload_file_handler( })) } FileType::Video => { - // For now, we'll just return basic info for videos - // In a real implementation, you might want to extract duration using ffmpeg + // Extract duration for video files + let duration = extract_duration(&filepath).await; + let video_info = VideoInfo { filename: filename.clone(), filepath: filepathweb.clone(), size, - duration: None, // Duration extraction would require additional libraries + duration, mime_type: content_type.clone(), }; @@ -197,13 +201,14 @@ pub async fn upload_file_handler( })) } FileType::Audio => { - // For now, we'll just return basic info for audio - // In a real implementation, you might want to extract duration using ffmpeg + // Extract duration for audio files + let duration = extract_duration(&filepath).await; + let audio_info = AudioInfo { filename: filename.clone(), filepath: filepathweb.clone(), size, - duration: None, // Duration extraction would require additional libraries + duration, mime_type: content_type.clone(), }; @@ -242,6 +247,51 @@ enum FileType { Generic, } +// Function to extract duration from audio/video files +async fn extract_duration(filepath: &str) -> Option { + use symphonia::core::io::MediaSourceStream; + + let file = match std::fs::File::open(filepath) { + Ok(file) => file, + Err(_) => return None, + }; + + let mss = MediaSourceStream::new(Box::new(file), Default::default()); + + let mut hint = Hint::new(); + if let Some(ext) = Path::new(filepath).extension().and_then(|ext| ext.to_str()) { + hint.with_extension(ext); + } + + let format_opts = FormatOptions::default(); + let metadata_opts = MetadataOptions::default(); + + let probed = + match symphonia::default::get_probe().format(&hint, mss, &format_opts, &metadata_opts) { + Ok(probed) => probed, + Err(_) => return None, + }; + + let track = match probed + .format + .tracks() + .iter() + .find(|t| t.codec_params.codec != symphonia::core::codecs::CODEC_TYPE_NULL) + { + Some(track) => track, + None => return None, + }; + + let duration = track.codec_params.time_base.and_then(|tb| { + track + .codec_params + .n_frames + .and_then(|frames| Some(tb.calc_time(frames).seconds as f64)) + }); + + duration +} + fn determine_file_type(content_type: &str, filename: &str) -> Option { let lower_ct = content_type.to_lowercase(); let lower_fn = filename.to_lowercase(); diff --git a/web/css/index.css b/web/css/index.css index 03dd500..9ab49bb 100644 --- a/web/css/index.css +++ b/web/css/index.css @@ -662,6 +662,53 @@ body { box-shadow: none; } +.record-button { + padding: 12px 20px; + background: linear-gradient(135deg, #e74c3c, #c0392b); + color: white; + border: none; + border-radius: 25px; + cursor: pointer; + font-size: 14px; + font-weight: 600; + transition: all 0.3s ease; + flex-shrink: 0; + min-height: 44px; + display: flex; + align-items: center; + justify-content: center; +} + +.record-button:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(231, 76, 60, 0.4); +} + +.record-button:disabled { + background: var(--bg-tertiary); + color: var(--text-secondary); + cursor: not-allowed; + transform: none; + box-shadow: none; +} + +.record-button.recording { + background: linear-gradient(135deg, #ff6b6b, #ff5252); + animation: pulse 1s infinite; +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0 rgba(231, 76, 60, 0.7); + } + 70% { + box-shadow: 0 0 0 10px rgba(231, 76, 60, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(231, 76, 60, 0); + } +} + .system-message { text-align: center; color: var(--text-secondary); diff --git a/web/index.html b/web/index.html index 1e1df19..859a5e7 100644 --- a/web/index.html +++ b/web/index.html @@ -206,6 +206,22 @@ > + + +