From 0bf04d3b08cfbb11d942d594997db8dd66300e92 Mon Sep 17 00:00:00 2001 From: Louis Beaumont Date: Sat, 20 Jul 2024 09:57:59 +0200 Subject: [PATCH] chore: remove warning, restore cli release --- .github/workflows/release-app.yml | 2 +- .github/workflows/release-cli.yml | 82 +++++++++++++++++++++++++++++ screenpipe-vision/src/core.rs | 71 ++++++++++++++++--------- screenpipe-vision/src/utils.rs | 87 +++++++++++-------------------- 4 files changed, 160 insertions(+), 82 deletions(-) create mode 100644 .github/workflows/release-cli.yml diff --git a/.github/workflows/release-app.yml b/.github/workflows/release-app.yml index 2c5acb7d6..43e7dc7ae 100644 --- a/.github/workflows/release-app.yml +++ b/.github/workflows/release-app.yml @@ -5,7 +5,7 @@ # # Run for Linux # act -W .github/workflows/release-app.yml --container-architecture linux/amd64 -j publish-tauri -P ubuntu-24.04 -name: Release +name: Release App on: push: diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml new file mode 100644 index 000000000..577bc4b3c --- /dev/null +++ b/.github/workflows/release-cli.yml @@ -0,0 +1,82 @@ +name: Release CLI + +on: + push: + tags: + - "v*" + workflow_dispatch: + +jobs: + build-ubuntu: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y libavformat-dev libavfilter-dev libavdevice-dev ffmpeg libasound2-dev + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + + - name: Build with RPATH + run: | + export PKG_CONFIG_PATH="/usr/lib/x86_64-linux-gnu/pkgconfig:$PKG_CONFIG_PATH" + export RUSTFLAGS="-C link-arg=-Wl,-rpath,\$ORIGIN/lib" + cargo build --release + + - name: Copy FFmpeg libraries + run: | + mkdir -p target/release/lib + cp /usr/lib/x86_64-linux-gnu/libavcodec.so* target/release/lib/ + cp /usr/lib/x86_64-linux-gnu/libavformat.so* target/release/lib/ + cp /usr/lib/x86_64-linux-gnu/libavutil.so* target/release/lib/ + cp /usr/lib/x86_64-linux-gnu/libswresample.so* target/release/lib/ + cp /usr/lib/x86_64-linux-gnu/libswscale.so* target/release/lib/ + cp /usr/lib/x86_64-linux-gnu/libavfilter.so* target/release/lib/ + cp /usr/lib/x86_64-linux-gnu/libavdevice.so* target/release/lib/ + + - name: Create deployment package + run: | + mkdir -p screenpipe-linux + cp target/release/screenpipe screenpipe-linux/screenpipe + cp -r target/release/lib screenpipe-linux/ + chmod +x screenpipe-linux/screenpipe + tar -czvf screenpipe-linux.tar.gz screenpipe-linux + + - name: Upload Artifact + uses: actions/upload-artifact@v2 + with: + name: screenpipe-linux + path: screenpipe-linux.tar.gz + + release: + runs-on: ubuntu-latest + needs: [build-ubuntu] + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Download Artifacts + uses: actions/download-artifact@v2 + with: + name: screenpipe-linux + + - name: Set Version + shell: bash + run: | + echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV + echo "RELEASE_VERSION=$(echo ${GITHUB_REF_NAME} | cut -f1 -d-)" >> $GITHUB_ENV + + - name: Create or update Release + env: + GH_TOKEN: ${{ secrets.PAT }} + run: | + gh release create ${{ env.RELEASE_VERSION }} --title ${{ env.RELEASE_VERSION }} --generate-notes + gh release upload ${{ env.RELEASE_VERSION }} screenpipe-linux.tar.gz \ No newline at end of file diff --git a/screenpipe-vision/src/core.rs b/screenpipe-vision/src/core.rs index d636337ce..17e979448 100644 --- a/screenpipe-vision/src/core.rs +++ b/screenpipe-vision/src/core.rs @@ -1,14 +1,21 @@ use image::DynamicImage; -use log::{debug, error, info}; +use log::{debug, error}; use rusty_tesseract::DataOutput; -use tokio::sync::{mpsc::{Receiver, Sender}, Mutex}; // Corrected import for Mutex use serde_json; -use std::{collections::{HashMap, HashSet}, sync::Arc, time::{Duration, Instant}}; -use xcap::Monitor; -use strsim::levenshtein; use std::sync::atomic::{AtomicBool, Ordering}; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, + time::{Duration, Instant}, +}; +use strsim::levenshtein; +use tokio::sync::{ + mpsc::{Receiver, Sender}, + Mutex, +}; // Corrected import for Mutex +use xcap::Monitor; -use crate::utils::{perform_ocr, save_text_files, capture_screenshot, compare_with_previous_image}; +use crate::utils::{capture_screenshot, compare_with_previous_image, perform_ocr, save_text_files}; pub enum ControlMessage { Pause, @@ -32,16 +39,13 @@ pub struct OcrTaskData { pub result_tx: Sender, } -const MAX_THREADS: usize = 1; - pub async fn continuous_capture( _control_rx: &mut Receiver, result_tx: Sender, interval: Duration, - save_text_files_flag: bool, + save_text_files_flag: bool, ) { let monitor = Monitor::all().unwrap().first().unwrap().clone(); // Simplified monitor retrieval - let cache = Arc::new(Mutex::new(HashMap::::new())); let previous_text_json = Arc::new(Mutex::new(None)); let ocr_task_running = Arc::new(AtomicBool::new(false)); let mut frame_counter: u64 = 0; @@ -52,7 +56,14 @@ pub async fn continuous_capture( loop { let (image, image_hash, _capture_duration) = capture_screenshot(&monitor).await; - let current_average = compare_with_previous_image(&previous_image, &image, &mut max_average, frame_counter, &mut max_avg_value).await; + let current_average = compare_with_previous_image( + &previous_image, + &image, + &mut max_average, + frame_counter, + &mut max_avg_value, + ) + .await; if current_average > max_avg_value { max_average = Some(MaxAverageFrame { image: Arc::new(image.clone()), @@ -70,7 +81,8 @@ pub async fn continuous_capture( if !ocr_task_running.load(Ordering::SeqCst) { // debug!("max_avg_frame {} before if let Some(", max_avg_value); - if let Some(max_avg_frame) = max_average.take() { // Use take() to move out the value + if let Some(max_avg_frame) = max_average.take() { + // Use take() to move out the value let ocr_task_data = OcrTaskData { image: max_avg_frame.image.clone(), frame_number: max_avg_frame.frame_number, @@ -78,7 +90,6 @@ pub async fn continuous_capture( result_tx: result_tx.clone(), }; - let cache_clone = cache.clone(); let previous_text_json_clone = previous_text_json.clone(); let ocr_task_running_clone = ocr_task_running.clone(); @@ -90,10 +101,11 @@ pub async fn continuous_capture( ocr_task_data.frame_number, ocr_task_data.timestamp, ocr_task_data.result_tx, - &cache_clone, &previous_text_json_clone, save_text_files_flag, // Pass the flag here - ).await { + ) + .await + { error!("Error processing OCR task: {}", e); } ocr_task_running_clone.store(false, Ordering::SeqCst); @@ -103,7 +115,6 @@ pub async fn continuous_capture( // Reset max_average and max_avg_value after spawning the OCR task max_avg_value = 0.0; // debug!("max_avg_value {}", max_avg_value); - } } @@ -135,7 +146,6 @@ async fn process_ocr_task( frame_number: u64, timestamp: Instant, result_tx: Sender, - cache: &Arc>>, previous_text_json: &Arc>>>>, save_text_files_flag: bool, // Add this parameter ) -> Result<(), std::io::Error> { @@ -144,10 +154,11 @@ async fn process_ocr_task( debug!("Performing OCR for frame {}", frame_number); let (text, data_output, json_output) = perform_ocr(&image_arc); - let current_text_json: Vec> = serde_json::from_str(&json_output).unwrap_or_else(|e| { - error!("Failed to parse JSON output: {}", e); - Vec::new() - }); + let current_text_json: Vec> = serde_json::from_str(&json_output) + .unwrap_or_else(|e| { + error!("Failed to parse JSON output: {}", e); + Vec::new() + }); let mut previous_text_json = previous_text_json.lock().await; let mut new_text_json = Vec::new(); @@ -166,7 +177,8 @@ async fn process_ocr_task( } } } else { - new_text_json = current_text_json.iter() + new_text_json = current_text_json + .iter() .filter(|record| record["confidence"].parse::().unwrap_or(0.0) > 60.0) .cloned() .collect(); @@ -176,7 +188,13 @@ async fn process_ocr_task( new_text_json.retain(|record| seen_texts.insert(record["text"].clone())); if save_text_files_flag { - save_text_files(frame_number, &new_text_json, ¤t_text_json, &previous_text_json).await; + save_text_files( + frame_number, + &new_text_json, + ¤t_text_json, + &previous_text_json, + ) + .await; } *previous_text_json = Some(current_text_json.clone()); @@ -193,7 +211,10 @@ async fn process_ocr_task( .await { error!("Failed to send OCR result: {}", e); - return Err(std::io::Error::new(std::io::ErrorKind::Other, "Failed to send OCR result")); + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "Failed to send OCR result", + )); } let _duration = start_time.elapsed(); debug!( @@ -201,4 +222,4 @@ async fn process_ocr_task( frame_number, _duration ); Ok(()) -} \ No newline at end of file +} diff --git a/screenpipe-vision/src/utils.rs b/screenpipe-vision/src/utils.rs index a7c8d3b08..72f419899 100644 --- a/screenpipe-vision/src/utils.rs +++ b/screenpipe-vision/src/utils.rs @@ -1,18 +1,15 @@ +use crate::core::MaxAverageFrame; // Assuming core.rs is in the same crate under the `core` module use image::DynamicImage; +use image_compare::{Algorithm, Metric, Similarity}; // Added import for Similarity +use log::{debug, error}; +use rusty_tesseract::{Args, DataOutput, Image}; // Added import for Args, Image, DataOutput +use serde_json; use std::collections::HashMap; -use std::fs::{self, File}; +use std::fs::File; use std::hash::{DefaultHasher, Hash, Hasher}; use std::io::Write; -use std::path::Path; -use serde_json; -use image_compare::{Algorithm, Metric, Similarity}; // Added import for Similarity -use rusty_tesseract::{Args, Image, DataOutput}; // Added import for Args, Image, DataOutput -use log::{debug, error}; use std::sync::Arc; use std::time::{Duration, Instant}; -use tokio::sync::Mutex; -use tokio::sync::mpsc::Receiver; -use crate::core::{ControlMessage, MaxAverageFrame}; // Assuming core.rs is in the same crate under the `core` module use xcap::Monitor; pub fn calculate_hash(image: &DynamicImage) -> u64 { @@ -21,31 +18,21 @@ pub fn calculate_hash(image: &DynamicImage) -> u64 { hasher.finish() } -pub fn save_json_to_file(json_data: &Vec>, file_name: &str) { - let folder_path = Path::new("text_json"); - if !folder_path.exists() { - fs::create_dir(folder_path).unwrap(); - } - - let file_path = folder_path.join(file_name); - let mut file = File::create(file_path).unwrap(); - let json_string = serde_json::to_string_pretty(json_data).unwrap(); - file.write_all(json_string.as_bytes()).unwrap(); -} - pub fn compare_images_histogram(image1: &DynamicImage, image2: &DynamicImage) -> f64 { let image_one = image1.to_luma8(); let image_two = image2.to_luma8(); - let result = image_compare::gray_similarity_histogram(Metric::Hellinger, &image_one, &image_two) - .expect("Images had different dimensions"); + let result = + image_compare::gray_similarity_histogram(Metric::Hellinger, &image_one, &image_two) + .expect("Images had different dimensions"); result } pub fn compare_images_ssim(image1: &DynamicImage, image2: &DynamicImage) -> f64 { let image_one = image1.to_luma8(); let image_two = image2.to_luma8(); - let result: Similarity = image_compare::gray_similarity_structure(&Algorithm::MSSIMSimple, &image_one, &image_two) - .expect("Images had different dimensions"); + let result: Similarity = + image_compare::gray_similarity_structure(&Algorithm::MSSIMSimple, &image_one, &image_two) + .expect("Images had different dimensions"); result.score } @@ -53,9 +40,7 @@ pub fn perform_ocr(image: &DynamicImage) -> (String, DataOutput, String) { // debug!("inside perform_ocr"); let args = Args { lang: "eng".to_string(), - config_variables: HashMap::from([ - ("tessedit_create_tsv".into(), "1".into()), - ]), + config_variables: HashMap::from([("tessedit_create_tsv".into(), "1".into())]), dpi: Some(600), // 150 is a balanced option, 600 seems faster surprisingly, the bigger the number the more granualar result psm: Some(1), // PSM 1: Automatic page segmentation with OSD. PSM 3: Automatic page segmentation with OSD oem: Some(1), //1: Neural nets LSTM engine only, 3: Default, based on what is available. (Default) @@ -89,7 +74,11 @@ pub fn perform_ocr(image: &DynamicImage) -> (String, DataOutput, String) { "line_position".to_string(), format!( "level{}page_num{}block_num{}par_num{}line_num{}", - record.level, record.page_num, record.block_num, record.par_num, record.line_num + record.level, + record.page_num, + record.block_num, + record.par_num, + record.line_num ), ); lines.push(line_data); @@ -136,23 +125,6 @@ fn data_output_to_text(data_output: &DataOutput) -> String { text } - -pub async fn handle_control_messages( - control_rx: &mut Receiver, - is_paused: &Arc>, - should_stop: &Arc>, -) { - if let Ok(message) = control_rx.try_recv() { - match message { - ControlMessage::Pause => *is_paused.lock().await = true, - ControlMessage::Resume => *is_paused.lock().await = false, - ControlMessage::Stop => { - *should_stop.lock().await = true; - } - } - } -} - pub async fn capture_screenshot(monitor: &Monitor) -> (DynamicImage, u64, Duration) { let capture_start = Instant::now(); let buffer = monitor.capture_image().unwrap(); @@ -194,13 +166,15 @@ pub async fn save_text_files( let id = frame_number; debug!("Saving text files for frame {}", frame_number); - let new_text_lines: Vec = new_text_json.iter().map(|record| { - record.get("text").cloned().unwrap_or_default() - }).collect(); + let new_text_lines: Vec = new_text_json + .iter() + .map(|record| record.get("text").cloned().unwrap_or_default()) + .collect(); - let current_text_lines: Vec = current_text_json.iter().map(|record| { - record.get("text").cloned().unwrap_or_default() - }).collect(); + let current_text_lines: Vec = current_text_json + .iter() + .map(|record| record.get("text").cloned().unwrap_or_default()) + .collect(); let new_text_file_path = format!("text_json/new_text_{}.txt", id); let mut new_text_file = match File::create(&new_text_file_path) { @@ -227,9 +201,10 @@ pub async fn save_text_files( } if let Some(prev_json) = previous_text_json { - let prev_text_lines: Vec = prev_json.iter().map(|record| { - record.get("text").cloned().unwrap_or_default() - }).collect(); + let prev_text_lines: Vec = prev_json + .iter() + .map(|record| record.get("text").cloned().unwrap_or_default()) + .collect(); let prev_text_file_path = format!("text_json/previous_text_{}.txt", id); let mut prev_text_file = match File::create(&prev_text_file_path) { Ok(file) => file, @@ -245,4 +220,4 @@ pub async fn save_text_files( } } } -} \ No newline at end of file +}