implemented new image saving

This commit is contained in:
pandadev 2024-07-18 14:57:29 +02:00
parent 720f10bdbe
commit 08f79ece60
No known key found for this signature in database
GPG key ID: C39629DACB8E762F
9 changed files with 198 additions and 128 deletions

View file

@ -3,19 +3,40 @@ use base64::engine::general_purpose::STANDARD;
use base64::Engine;
use image::io::Reader as ImageReader;
use image::{DynamicImage, ImageBuffer, ImageFormat, Rgba};
use lazy_static::lazy_static;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use rdev::{simulate, EventType, Key};
use regex::Regex;
use reqwest::Client;
use sha2::{Sha256, Digest};
use sqlx::SqlitePool;
use std::fs;
use std::io::Cursor;
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
use tauri::Manager;
use tauri::{AppHandle, Manager};
use tokio::runtime::Runtime;
use url::Url;
lazy_static! {
static ref APP_DATA_DIR: Mutex<Option<std::path::PathBuf>> = Mutex::new(None);
}
pub fn set_app_data_dir(path: std::path::PathBuf) {
let mut dir = APP_DATA_DIR.lock().unwrap();
*dir = Some(path);
}
#[tauri::command]
pub fn read_image(filename: String) -> Result<Vec<u8>, String> {
let app_data_dir = APP_DATA_DIR.lock().unwrap();
let app_data_dir = app_data_dir.as_ref().expect("App data directory not set");
let image_path = app_data_dir.join("images").join(filename);
fs::read(image_path).map_err(|e| e.to_string())
}
#[tauri::command]
pub fn simulate_paste() {
let mut events = vec![
@ -33,10 +54,18 @@ pub fn simulate_paste() {
}
}
#[tauri::command]
pub fn get_image_path(app_handle: tauri::AppHandle, filename: String) -> String {
let app_data_dir = app_handle.path().app_data_dir().expect("Failed to get app data directory");
let image_path = app_data_dir.join("images").join(filename);
image_path.to_str().unwrap_or("").to_string()
}
pub fn setup(app_handle: tauri::AppHandle) {
let is_processing = std::sync::Arc::new(std::sync::Mutex::new(false));
std::thread::spawn({
let app_handle = app_handle.clone();
let is_processing = std::sync::Arc::clone(&is_processing);
move || {
let mut clipboard = Clipboard::new().unwrap();
@ -53,7 +82,7 @@ pub fn setup(app_handle: tauri::AppHandle) {
if content != last_text {
last_text = content.clone();
rt.block_on(async {
insert_content_if_not_exists(&pool, "text", content).await;
insert_content_if_not_exists(&app_handle, &pool, "text", content).await;
});
}
}
@ -63,7 +92,7 @@ pub fn setup(app_handle: tauri::AppHandle) {
Ok(png_image) => {
let base64_image = STANDARD.encode(&png_image);
rt.block_on(async {
insert_content_if_not_exists(&pool, "image", base64_image).await;
insert_content_if_not_exists(&app_handle, &pool, "image", base64_image).await;
});
}
Err(e) => {
@ -97,7 +126,7 @@ fn process_clipboard_image(
Ok(png_bytes)
}
async fn insert_content_if_not_exists(pool: &SqlitePool, content_type: &str, content: String) {
async fn insert_content_if_not_exists(app_handle: &AppHandle, pool: &SqlitePool, content_type: &str, content: String) {
let last_content: Option<String> = sqlx::query_scalar(
"SELECT content FROM history WHERE content_type = ? ORDER BY timestamp DESC LIMIT 1",
)
@ -106,6 +135,18 @@ async fn insert_content_if_not_exists(pool: &SqlitePool, content_type: &str, con
.await
.unwrap_or(None);
let content = if content_type == "image" {
match save_image(app_handle, &content).await {
Ok(path) => path,
Err(e) => {
println!("Failed to save image: {}", e);
content
}
}
} else {
content
};
if last_content.as_deref() != Some(&content) {
let id: String = thread_rng()
.sample_iter(&Alphanumeric)
@ -113,7 +154,7 @@ async fn insert_content_if_not_exists(pool: &SqlitePool, content_type: &str, con
.map(char::from)
.collect();
let url_regex = Regex::new(r"^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$").unwrap();
let url_regex = Regex::new(r"^https?://(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*)$").unwrap();
let favicon_base64 = if content_type == "text" && url_regex.is_match(&content) {
match Url::parse(&content) {
Ok(url) => match fetch_favicon_as_base64(url).await {
@ -138,13 +179,32 @@ async fn insert_content_if_not_exists(pool: &SqlitePool, content_type: &str, con
)
.bind(id)
.bind(content_type)
.bind(content)
.bind(&content)
.bind(favicon_base64)
.execute(pool)
.await;
}
}
async fn save_image(app_handle: &AppHandle, base64_image: &str) -> Result<String, Box<dyn std::error::Error>> {
let image_data = STANDARD.decode(base64_image)?;
let mut hasher = Sha256::new();
hasher.update(&image_data);
let hash = hasher.finalize();
let filename = format!("{:x}.png", hash);
let app_data_dir = app_handle.path().app_data_dir().expect("Failed to get app data directory");
let images_dir = app_data_dir.join("images");
let path = images_dir.join(&filename);
if !path.exists() {
fs::create_dir_all(&images_dir)?;
fs::write(&path, &image_data)?;
}
Ok(path.to_str().unwrap().to_string())
}
async fn fetch_favicon_as_base64(url: Url) -> Result<Option<String>, Box<dyn std::error::Error>> {
let client = Client::new();
let favicon_url = format!("https://icon.horse/icon/{}", url.host_str().unwrap());