diff --git a/.gitignore b/.gitignore index e162307..900d1c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ **/*.conf.toml +**/*.facf /output.log /fractus-atlas /src-front/wasm/ diff --git a/Cargo.lock b/Cargo.lock index 22aa441..7528dc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,6 +62,12 @@ version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + [[package]] name = "async-recursion" version = "1.1.1" @@ -374,7 +380,7 @@ dependencies = [ [[package]] name = "common" -version = "2.1.0" +version = "2.2.0" dependencies = [ "serde", ] @@ -560,6 +566,17 @@ dependencies = [ "serde", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -573,6 +590,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "destructure_traitobject" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" + [[package]] name = "digest" version = "0.10.7" @@ -583,6 +606,15 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -593,6 +625,18 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -798,12 +842,15 @@ dependencies = [ [[package]] name = "fractus-atlas" -version = "2.1.0" +version = "2.2.0" dependencies = [ "async-recursion", "common", + "dirs", "encoding_rs", "futures", + "log", + "log4rs", "notify", "open", "rand 0.8.5", @@ -1322,6 +1369,12 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "1.3.1" @@ -1704,6 +1757,43 @@ name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +dependencies = [ + "serde", +] + +[[package]] +name = "log-mdc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" + +[[package]] +name = "log4rs" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0816135ae15bd0391cf284eab37e6e3ee0a6ee63d2ceeb659862bd8d0a984ca6" +dependencies = [ + "anyhow", + "arc-swap", + "chrono", + "derivative", + "fnv", + "humantime", + "libc", + "log", + "log-mdc", + "once_cell", + "parking_lot", + "rand 0.8.5", + "serde", + "serde-value", + "serde_json", + "serde_yaml", + "thiserror", + "thread-id", + "typemap-ors", + "winapi", +] [[package]] name = "loom" @@ -1996,6 +2086,21 @@ dependencies = [ "pathdiff", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + [[package]] name = "os_pipe" version = "1.1.5" @@ -2680,6 +2785,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + [[package]] name = "serde-wasm-bindgen" version = "0.6.5" @@ -2786,6 +2901,19 @@ dependencies = [ "syn 2.0.60", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.2.6", + "itoa 1.0.11", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "serialize-to-javascript" version = "0.1.1" @@ -3362,6 +3490,16 @@ dependencies = [ "syn 2.0.60", ] +[[package]] +name = "thread-id" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ec81c46e9eb50deaa257be2f148adf052d1fb7701cfd55ccfab2525280b70b" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "thread_local" version = "1.1.8" @@ -3671,6 +3809,15 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typemap-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867" +dependencies = [ + "unsafe-any-ors", +] + [[package]] name = "typenum" version = "1.17.0" @@ -3679,7 +3826,7 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ui" -version = "2.1.0" +version = "2.2.0" dependencies = [ "common", "console_error_panic_hook", @@ -3760,6 +3907,21 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unsafe-any-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad" +dependencies = [ + "destructure_traitobject", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "url" version = "2.5.0" diff --git a/README.md b/README.md index 9e27124..0f2d936 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,6 @@ [![issues](https://img.shields.io/github/issues-raw/Jimskapt/fractus-atlas?color=blue)](https://github.com/Jimskapt/fractus-atlas/issues) [![Rust cargo multi-platform compilation](https://github.com/Jimskapt/fractus-atlas/workflows/Rust%20cargo%20multi-platform%20compilation/badge.svg)](https://github.com/Jimskapt/fractus-atlas/actions/) -> Fractus Atlas is an image viewer which allows you to quickly move them in folders. +> Fractus Atlas is an image viewer that lets you move images quickly into folders. 💾 You can download this program on [releases tab](https://github.com/Jimskapt/fractus-atlas/releases). diff --git a/common/Cargo.toml b/common/Cargo.toml index 3526b72..04c2f73 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "common" -version = "2.1.0" +version = "2.2.0" edition = "2021" publish = false diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 3a2b044..f2ae0c4 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fractus-atlas" -version = "2.1.0" -description = "An image viewer which allows you to quickly move them in folders." +version = "2.2.0" +description = "An image viewer that lets you move images quickly into folders" homepage = "https://github.com/Jimskapt/fractus-atlas" repository = "https://github.com/Jimskapt/fractus-atlas" keywords = ["image", "images", "fast", "browsing", "viewer", "folder", "folders", "sort", "sorting"] @@ -36,6 +36,9 @@ async-recursion = "1.1.1" notify = "6.1.1" futures = "0.3.30" open = "5.1.2" +log4rs = "1.3.0" +log = "0.4.21" +dirs = "5.0.1" [features] # this feature is used for production builds or when `devPath` points to the filesystem diff --git a/src-tauri/src/lib/action.rs b/src-tauri/src/lib/action.rs index e46581f..4edb2d6 100644 --- a/src-tauri/src/lib/action.rs +++ b/src-tauri/src/lib/action.rs @@ -66,7 +66,15 @@ pub async fn apply_action( if let Some(output) = output_folder { if let Some(image) = state.read().await.images.get(position) { let old_path = image.get_current(); - new_path = output.path.join::( + + let output_path = super::build_absolute_path( + &output.path, + rstate.settings_path.clone(), + ); + + std::fs::create_dir_all(&output_path).unwrap(); + + new_path = output_path.join::( image .origin .file_name() diff --git a/src-tauri/src/lib/mod.rs b/src-tauri/src/lib/mod.rs index efd71d6..0c94a84 100644 --- a/src-tauri/src/lib/mod.rs +++ b/src-tauri/src/lib/mod.rs @@ -14,7 +14,7 @@ pub async fn run(init_settings: InitSettings) { Ok(content) => match toml::from_str(&content) { Ok(settings) => (Some(settings_path), settings), Err(err) => { - eprintln!("{err}"); + // eprintln!("{err}"); (None, common::Settings::default()) } }, @@ -29,6 +29,55 @@ pub async fn run(init_settings: InitSettings) { InitSettings::Standalone(settings) => (None, settings), }; + let global_log_path = + dirs::data_local_dir().map(|path| path.join(env!("CARGO_PKG_NAME")).join("global.log")); + + let stdout = log4rs::append::console::ConsoleAppender::builder() + .target(log4rs::append::console::Target::Stdout) + .build(); + + let level_filter = log::LevelFilter::Trace; + + let config = if let Some(global_log) = global_log_path { + let global_logger = log4rs::append::file::FileAppender::builder() + .encoder(Box::new(log4rs::encode::pattern::PatternEncoder::new( + "{d} - {m}{n}", + ))) + .build(global_log) + .unwrap(); + + log4rs::config::Config::builder() + .appender( + log4rs::config::Appender::builder().build("globalfile", Box::new(global_logger)), + ) + .appender( + log4rs::config::Appender::builder() + .filter(Box::new(log4rs::filter::threshold::ThresholdFilter::new( + level_filter, + ))) + .build("stdout", Box::new(stdout)), + ) + .build( + log4rs::config::Root::builder() + .appender("globalfile") + .appender("stdout") + .build(level_filter), + ) + .unwrap() + } else { + log4rs::config::Config::builder() + .appender(log4rs::config::Appender::builder().build("stdout", Box::new(stdout))) + .logger(log4rs::config::Logger::builder().build("stdout", level_filter)) + .build( + log4rs::config::Root::builder() + .appender("stdout") + .build(level_filter), + ) + .unwrap() + }; + + let _handle = log4rs::init_config(config).unwrap(); + let mut shortcuts = BTreeMap::new(); for (id, output) in settings.output_folders.iter().enumerate() { for shortcut in &output.shortcuts_or { @@ -52,7 +101,7 @@ pub async fn run(init_settings: InitSettings) { // shortcuts.insert(String::from("backspace"), action::AppAction::RestoreImage); shortcuts.insert(String::from(" "), action::AppAction::ChangeRandomPosition); - let (sender, mut receiver) = tokio::sync::mpsc::channel(100); + let (sender, mut receiver) = tokio::sync::mpsc::unbounded_channel(); let state = Arc::new(RwLock::new(AppState { settings_path, @@ -73,10 +122,7 @@ pub async fn run(init_settings: InitSettings) { let sender_for_task = sender_for_watcher.clone(); let path_for_task = path.clone(); futures::executor::block_on(async { - sender_for_task - .send(FileEvent::Add(path_for_task)) - .await - .unwrap(); + sender_for_task.send(FileEvent::Add(path_for_task)).unwrap(); }); } } else if let notify::EventKind::Modify(notify::event::ModifyKind::Name( @@ -89,7 +135,6 @@ pub async fn run(init_settings: InitSettings) { futures::executor::block_on(async { sender_for_task .send(FileEvent::Rename(path_for_task)) - .await .unwrap(); }); } @@ -108,23 +153,19 @@ pub async fn run(init_settings: InitSettings) { tokio::task::spawn(async move { while let Some(event) = receiver.recv().await { match event { - FileEvent::Add(add_path) => { - println!("{}", add_path.display()); - receive(state_for_file_channel.clone(), add_path).await - }, + FileEvent::Add(add_path) => receive(state_for_file_channel.clone(), add_path).await, FileEvent::Rename(add_path) => { receive(state_for_file_channel.clone(), add_path).await } - } - - match sort { - common::SortingOrder::FileName => { - state_for_file_channel - .write() - .await - .images - .sort_by(|a, b| a.origin.cmp(&b.origin)); - } + FileEvent::DoSort => match sort { + common::SortingOrder::FileName => { + state_for_file_channel + .write() + .await + .images + .sort_by(|a, b| a.origin.cmp(&b.origin)); + } + }, } } }); @@ -393,12 +434,24 @@ async fn get_settings_path( #[tauri::command] async fn update_files_list( state: tauri::State<'_, Arc>>, - sender: tauri::State<'_, tokio::sync::mpsc::Sender>, + sender: tauri::State<'_, tokio::sync::mpsc::UnboundedSender>, ) -> Result<(), ()> { let state_for_input = state.inner().clone(); let folders = state_for_input.read().await.settings.input_folders.clone(); + let settings_path = state_for_input.read().await.settings_path.clone(); + + let absolute_folders: Vec = folders + .iter() + .map(|folder| { + let mut clone = folder.clone(); - for input in &folders { + clone.path = build_absolute_path(clone.path, settings_path.clone()); + + clone + }) + .collect(); + + for input in &absolute_folders { notify::Watcher::unwatch( state_for_input.write().await.watcher.as_mut().unwrap(), &input.path, @@ -430,6 +483,8 @@ async fn update_files_list( }, ) .unwrap(); + + sender_for_task.send(FileEvent::DoSort).unwrap(); }); } @@ -517,16 +572,20 @@ fn get_backend_version() -> String { #[async_recursion::async_recursion] async fn browse_dir( path: &std::path::Path, - sender: tokio::sync::mpsc::Sender, + sender: tokio::sync::mpsc::UnboundedSender, recursivity: bool, ) { let mut temp = tokio::fs::read_dir(&path).await.unwrap(); while let Ok(Some(entry)) = temp.next_entry().await { if entry.path().is_file() { + log::debug!("send : {:?}", path.join(entry.file_name())); + sender .send(FileEvent::Add(path.join(entry.file_name()))) - .await .unwrap(); + + // needed for big folders with a lot of files + tokio::time::sleep(tokio::time::Duration::from_millis(10)).await; } else if entry.path().is_dir() && recursivity { browse_dir(&path.join(entry.file_name()), sender.clone(), recursivity).await; } @@ -678,12 +737,38 @@ async fn get_image( .unwrap() } +pub fn build_absolute_path( + input: impl Into, + settings_path: Option, +) -> std::path::PathBuf { + let input_paths = input.into(); + + if input_paths.is_absolute() { + input_paths.clone() + } else if let Some(some_settings_path) = &settings_path { + some_settings_path.parent().unwrap().join(&input_paths) + } else if let Ok(current_dir) = std::env::current_dir() { + current_dir.join(&input_paths) + } else if let Some(exec) = std::env::args().next() { + std::path::PathBuf::from(exec) + .parent() + .unwrap() + .join(&input_paths) + } else { + panic!(); + } +} + async fn receive(state: Arc>, add_path: std::path::PathBuf) { + let settings_path = state.read().await.settings_path.clone(); let input_folders = state.write().await.settings.input_folders.clone(); + 'inputs: for input_folder in input_folders { + let input_folder_path = build_absolute_path(&input_folder.path, settings_path.clone()); + if add_path.is_file() && input_folder.filter(&add_path) - && add_path.starts_with(input_folder.path) + && add_path.starts_with(input_folder_path) { { let images = &mut state.write().await.images; @@ -697,6 +782,8 @@ async fn receive(state: Arc>, add_path: std::path::PathBuf) { false } }) { + log::debug!("receive : {:?}", add_path); + images.push(Image { origin: add_path.clone(), moved: None, @@ -729,6 +816,7 @@ pub enum InitSettings { enum FileEvent { Add(std::path::PathBuf), Rename(std::path::PathBuf), + DoSort, } pub struct AppState { diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 6ab0dd8..6437b50 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -10,7 +10,7 @@ async fn main() { let settings_path = args .next() - .unwrap_or_else(|| format!("{}.conf.toml", env!("CARGO_PKG_NAME"))); + .unwrap_or_else(|| format!("{}.facf", env!("CARGO_PKG_NAME"))); let settings_pathbuf = std::path::PathBuf::from(settings_path); let absolute_settings_path = if settings_pathbuf.is_absolute() { diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index cd6b998..5f6ddb5 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "productName": "fractus-atlas", - "version": "2.1.0", + "version": "2.2.0", "identifier": "com.jimskapt.fractus-atlas", "build": { "frontendDist": "../src-front" diff --git a/src-wasm/Cargo.toml b/src-wasm/Cargo.toml index 8cf8041..c03aa20 100644 --- a/src-wasm/Cargo.toml +++ b/src-wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ui" -version = "2.1.0" +version = "2.2.0" authors = ["Jimskapt "] edition = "2021"