From 8639e5cdd630b7bf068607d39febd7b9a1cbc958 Mon Sep 17 00:00:00 2001 From: TheMaddax Date: Mon, 27 Jan 2025 08:44:42 -0600 Subject: [PATCH] Fix temp dir for thumbnails --- package.json | 4 +- src-tauri/src/lib.rs | 63 ++++++++++++++------------ src-tauri/src/main.rs | 50 ++++++++++++++++---- src-tauri/tauri.conf.json | 8 ++-- src/App.tsx | 26 ++++++----- src/components/ProcessingAnimation.tsx | 51 +++++++++++++++++++++ src/components/TranscriptionTask.tsx | 46 ++++--------------- src/styles/ProcessingAnimation.css | 33 ++++++++++++++ tailwind.config.js | 16 +++++++ 9 files changed, 204 insertions(+), 93 deletions(-) create mode 100644 src/components/ProcessingAnimation.tsx create mode 100644 src/styles/ProcessingAnimation.css diff --git a/package.json b/package.json index d950efa..0184288 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "subtitle-merge", + "name": "deafblind-video-assistant", "private": true, - "version": "0.1.0", + "version": "1.0.0", "type": "module", "scripts": { "dev": "vite", diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 87d222f..aa37884 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -5,6 +5,7 @@ use std::io::BufRead; use tauri::{Runtime, State, Emitter}; use tempfile::TempDir; use std::fs; +use base64::Engine; #[derive(Debug, thiserror::Error)] pub enum Error { @@ -60,7 +61,6 @@ pub struct TranscriptionArgs { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct TranscriptionResult { - pub visual_description: String, pub narrative: String, } @@ -70,8 +70,27 @@ pub struct Thumbnail { pub time: u32, } -// Note: Removed unused Gemini request/response structs since we're handling -// the API interaction through the Python script +pub struct ThumbnailState { + temp_dir: Option, +} + +impl Default for ThumbnailState { + fn default() -> Self { + Self { + temp_dir: None, + } + } +} + +impl ThumbnailState { + pub fn set_temp_dir(&mut self, dir: TempDir) { + self.temp_dir = Some(dir); + } + + pub fn get_temp_dir(&self) -> Option<&TempDir> { + self.temp_dir.as_ref() + } +} #[derive(Debug, Serialize, Clone)] pub struct ProgressUpdate { @@ -201,24 +220,21 @@ pub async fn test_gemini_api_impl(api_key: &str) -> Result<(), Error> { Ok(()) } -pub async fn extract_thumbnails_impl(video_path: &str, interval: u32) -> Result, Error> { - // Create a directory in the system temp directory - let temp_dir = std::env::temp_dir().join("video_thumbs"); - let thumb_dir = temp_dir.join(format!("session_{}", std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs())); - - std::fs::create_dir_all(&thumb_dir) - .map_err(|e| Error::IoError(e))?; - - println!("Extracting thumbnails to: {:?}", thumb_dir); +pub async fn extract_thumbnails_impl( + video_path: &str, + interval: u32, + thumbnail_state: State<'_, Mutex> +) -> Result, Error> { + // Create a new temporary directory + let temp_dir = TempDir::new().map_err(|e| Error::IoError(e))?; + println!("Extracting thumbnails to temporary directory: {:?}", temp_dir.path()); let duration = get_video_duration(video_path).await? as u32; let mut thumbnails = Vec::new(); + // Process all frames before moving temp_dir into state for time in (0..duration).step_by(interval as usize) { - let output_path = thumb_dir.join(format!("thumb_{}.jpg", time)); + let output_path = temp_dir.path().join(format!("thumb_{}.jpg", time)); match extract_frame(video_path, time, output_path.to_str().unwrap()).await { Ok(_) => { println!("Successfully extracted frame at time {}", time); @@ -238,6 +254,8 @@ pub async fn extract_thumbnails_impl(video_path: &str, interval: u32) -> Result< return Err(Error::FFmpegError("Failed to extract any thumbnails".to_string())); } + // Store the TempDir in the state only after we're done using it + thumbnail_state.lock().unwrap().set_temp_dir(temp_dir); Ok(thumbnails) } @@ -258,7 +276,7 @@ pub async fn process_transcription_impl(args: TranscriptionArgs) -> Result Result>() .join("\n\n"); - // Combine all visual descriptions for the raw visual description output - let visual_description = frame_descriptions.iter() - .map(|(time, desc)| format!("At timestamp {}:\n{}", time, desc)) - .collect::>() - .join("\n\n"); - // Process subtitle content if provided let narrative = if let Some(subtitle_path) = &args.subtitle_path { println!("\nReading and parsing subtitle content..."); @@ -366,24 +378,19 @@ pub async fn process_transcription_impl(args: TranscriptionArgs) -> Result Result<(), Error> { @@ -40,8 +41,12 @@ async fn get_video_duration_command(path: String) -> Result { } #[tauri::command] -async fn extract_thumbnails(path: String, interval: u32) -> Result, Error> { - extract_thumbnails_impl(&path, interval).await +async fn extract_thumbnails( + path: String, + interval: u32, + thumbnail_state: tauri::State<'_, Mutex>, +) -> Result, Error> { + extract_thumbnails_impl(&path, interval, thumbnail_state).await } #[tauri::command] @@ -50,15 +55,41 @@ async fn process_transcription(args: TranscriptionArgs) -> Result Result, Error> { +async fn generate_thumbnails( + video_path: String, + thumbnail_state: tauri::State<'_, Mutex>, +) -> Result, Error> { // Generate thumbnails every 10 seconds - extract_thumbnails_impl(&video_path, 10).await + extract_thumbnails_impl(&video_path, 10, thumbnail_state).await } #[tauri::command] -async fn read_thumbnail(path: String) -> Result { - let data = fs::read(&path).map_err(|e| Error::IoError(e))?; - Ok(BASE64_STANDARD.encode(&data)) +async fn read_thumbnail( + path: String, + thumbnail_state: tauri::State<'_, Mutex>, +) -> Result { + // Verify the path is within our temp directory to prevent unauthorized access + let state = thumbnail_state.lock().unwrap(); + if let Some(temp_dir) = state.get_temp_dir() { + let temp_path = temp_dir.path(); + let requested_path = std::path::Path::new(&path); + + // Check if the requested path is within our temp directory + if !requested_path.starts_with(temp_path) { + return Err(Error::IoError(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "Attempted to access file outside of temporary directory", + ))); + } + + let data = fs::read(&path).map_err(|e| Error::IoError(e))?; + Ok(base64::engine::general_purpose::STANDARD.encode(&data)) + } else { + Err(Error::IoError(std::io::Error::new( + std::io::ErrorKind::NotFound, + "No active thumbnail session", + ))) + } } fn main() { @@ -67,6 +98,7 @@ fn main() { .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_fs::init()) .manage(ProgressState::default()) + .manage(Mutex::new(ThumbnailState::default())) .setup(|_app| { Ok(()) }) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 76cf4c1..11a5aaa 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,8 +1,8 @@ { "$schema": "https://raw.githubusercontent.com/tauri-apps/tauri/dev/tooling/cli/schema.json", - "productName": "Video Subtitle Merger 2.0", - "version": "0.1.0", - "identifier": "com.subtitle-merge.app", + "productName": "DeafBlind Video Assistant", + "version": "1.0.0", + "identifier": "com.deafblind-video-assistant.app", "app": { "security": { "csp": null, @@ -12,7 +12,7 @@ { "fullscreen": false, "resizable": true, - "title": "Video Subtitle Merger 2.0", + "title": "DeafBlind Video Assistant", "width": 800, "height": 600, "visible": true, diff --git a/src/App.tsx b/src/App.tsx index 4127ad7..84dc04c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -41,18 +41,8 @@ function App() { return (
-
-

Video Subtitle Merger 3.0

- +
+

DeafBlind Video Assistant 1.0

{error && ( @@ -64,6 +54,18 @@ function App() { {!selectedTask ? (
+
+ +

Choose a Task