Add finding similar audio by content (#970)
* In prehashing check for user clicks, less often * Similar audio * Remove ugly time checking * Fix using cache * Fix cache and improve performance of validating items * Remove cache type - cache should be saved to two different cache files(because ) * Working * Simple multithreading * Basic Generalization * Reference folder and swap cleaning * Split into multiple files * Commons, improved GUI message * Simplifying thread run * Check was stopped * Fix checking same files * Make read single file tag more general * Remove unnnecessary clone * Reading tags * Base * Search * Gui Fix * Gui Fix * Tooltip
This commit is contained in:
parent
14ccb49e45
commit
78d00eeb99
32 changed files with 1900 additions and 1612 deletions
52
Cargo.lock
generated
52
Cargo.lock
generated
|
@ -389,9 +389,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.2.6"
|
version = "4.2.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d70680e56dc65cb226c361aaa4e4a16d1f7e082bfed9ffceaee39c2012384ec"
|
checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
@ -400,9 +400,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.2.6"
|
version = "4.2.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fad499d5e07338414687350c5fdb82b1ab0001e9b26aa6275deccb684b14164"
|
checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
|
@ -744,13 +744,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.3"
|
version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886"
|
checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 2.0.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1780,9 +1780,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.142"
|
version = "0.2.143"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
checksum = "edc207893e85c5d6be840e969b496b53d94cec8be2d501b214f50daa97fa8024"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libheif-rs"
|
name = "libheif-rs"
|
||||||
|
@ -1827,9 +1827,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b64f40e5e03e0d54f03845c8197d0291253cdbedfb1cb46b13c2c117554a9f4c"
|
checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "locale_config"
|
name = "locale_config"
|
||||||
|
@ -2293,9 +2293,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.26"
|
version = "0.3.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "png"
|
name = "png"
|
||||||
|
@ -2633,9 +2633,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.37.18"
|
version = "0.37.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8bbfc1d1c7c40c01715f47d71444744a81669ca84e8b63e25a55e169b1f86433"
|
checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"errno",
|
"errno",
|
||||||
|
@ -2708,18 +2708,18 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.160"
|
version = "1.0.162"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
|
checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.160"
|
version = "1.0.162"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
|
checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -3178,9 +3178,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.20"
|
version = "0.3.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
|
checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"time-core",
|
"time-core",
|
||||||
|
@ -3188,9 +3188,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-core"
|
name = "time-core"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinystr"
|
name = "tinystr"
|
||||||
|
@ -3770,9 +3770,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zip"
|
name = "zip"
|
||||||
version = "0.6.4"
|
version = "0.6.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef"
|
checksum = "7e92305c174683d78035cbf1b70e18db6329cc0f1b9cae0a52ca90bf5bfe7125"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes 0.7.5",
|
"aes 0.7.5",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -3784,7 +3784,7 @@ dependencies = [
|
||||||
"hmac",
|
"hmac",
|
||||||
"pbkdf2",
|
"pbkdf2",
|
||||||
"sha1",
|
"sha1",
|
||||||
"time 0.3.20",
|
"time 0.3.21",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -38,7 +38,7 @@ pdf = "0.8"
|
||||||
|
|
||||||
# Needed by audio similarity feature
|
# Needed by audio similarity feature
|
||||||
rusty-chromaprint = "0.1"
|
rusty-chromaprint = "0.1"
|
||||||
symphonia = { version = "0.5", features = ["mp3", "aac", "alac", "flac", "isomp4", "mkv", "ogg", "pcm", "vorbis", "wav"] }
|
symphonia = { version = "0.5", features = ["all"] }
|
||||||
|
|
||||||
# Hashes for duplicate files
|
# Hashes for duplicate files
|
||||||
blake3 = "1.3"
|
blake3 = "1.3"
|
||||||
|
@ -74,10 +74,10 @@ num_cpus = "1.15"
|
||||||
|
|
||||||
# Heif/Heic
|
# Heif/Heic
|
||||||
libheif-rs = { version = "0.18.0", optional = true } # Do not upgrade now, since Ubuntu 22.04 not works with newer version
|
libheif-rs = { version = "0.18.0", optional = true } # Do not upgrade now, since Ubuntu 22.04 not works with newer version
|
||||||
anyhow = { version = "1.0", optional = true }
|
anyhow = { version = "1.0" }
|
||||||
|
|
||||||
state = "0.5"
|
state = "0.5"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
heif = ["dep:libheif-rs", "dep:anyhow"]
|
heif = ["dep:libheif-rs"]
|
||||||
|
|
|
@ -6,14 +6,13 @@ use std::mem;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use futures::channel::mpsc::UnboundedSender;
|
use futures::channel::mpsc::UnboundedSender;
|
||||||
use mime_guess::get_mime_extensions;
|
use mime_guess::get_mime_extensions;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
use crate::common::{prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, Common};
|
use crate::common::{prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads};
|
||||||
use crate::common_dir_traversal::{CheckingMethod, DirTraversalBuilder, DirTraversalResult, FileEntry, ProgressData};
|
use crate::common_dir_traversal::{CheckingMethod, DirTraversalBuilder, DirTraversalResult, FileEntry, ProgressData};
|
||||||
use crate::common_directory::Directories;
|
use crate::common_directory::Directories;
|
||||||
use crate::common_extensions::Extensions;
|
use crate::common_extensions::Extensions;
|
||||||
|
@ -298,16 +297,12 @@ impl BadExtensions {
|
||||||
.build()
|
.build()
|
||||||
.run();
|
.run();
|
||||||
match result {
|
match result {
|
||||||
DirTraversalResult::SuccessFiles {
|
DirTraversalResult::SuccessFiles { grouped_file_entries, warnings } => {
|
||||||
start_time,
|
|
||||||
grouped_file_entries,
|
|
||||||
warnings,
|
|
||||||
} => {
|
|
||||||
if let Some(files_to_check) = grouped_file_entries.get(&()) {
|
if let Some(files_to_check) = grouped_file_entries.get(&()) {
|
||||||
self.files_to_check = files_to_check.clone();
|
self.files_to_check = files_to_check.clone();
|
||||||
}
|
}
|
||||||
self.text_messages.warnings.extend(warnings);
|
self.text_messages.warnings.extend(warnings);
|
||||||
Common::print_time(start_time, SystemTime::now(), "check_files");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
DirTraversalResult::SuccessFolders { .. } => {
|
DirTraversalResult::SuccessFolders { .. } => {
|
||||||
|
@ -318,24 +313,10 @@ impl BadExtensions {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn look_for_bad_extensions_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
fn look_for_bad_extensions_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
||||||
let system_time = SystemTime::now();
|
let (progress_thread_handle, progress_thread_run, atomic_counter, check_was_stopped) =
|
||||||
|
prepare_thread_handler_common(progress_sender, 1, 1, self.files_to_check.len(), CheckingMethod::None);
|
||||||
|
|
||||||
let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread
|
let files_to_check = mem::take(&mut self.files_to_check);
|
||||||
|
|
||||||
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
|
||||||
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
|
||||||
let progress_thread_handle = prepare_thread_handler_common(
|
|
||||||
progress_sender,
|
|
||||||
&progress_thread_run,
|
|
||||||
&atomic_counter,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
self.files_to_check.len(),
|
|
||||||
CheckingMethod::None,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut files_to_check = Default::default();
|
|
||||||
mem::swap(&mut files_to_check, &mut self.files_to_check);
|
|
||||||
|
|
||||||
let mut hashmap_workarounds: HashMap<&str, Vec<&str>> = Default::default();
|
let mut hashmap_workarounds: HashMap<&str, Vec<&str>> = Default::default();
|
||||||
for (proper, found) in WORKAROUNDS {
|
for (proper, found) in WORKAROUNDS {
|
||||||
|
@ -357,8 +338,6 @@ impl BadExtensions {
|
||||||
|
|
||||||
self.information.number_of_files_with_bad_extension = self.bad_extensions_files.len();
|
self.information.number_of_files_with_bad_extension = self.bad_extensions_files.len();
|
||||||
|
|
||||||
Common::print_time(system_time, SystemTime::now(), "bad extension finding");
|
|
||||||
|
|
||||||
// Clean unused data
|
// Clean unused data
|
||||||
self.files_to_check = Default::default();
|
self.files_to_check = Default::default();
|
||||||
|
|
||||||
|
@ -525,7 +504,6 @@ impl DebugPrint for BadExtensions {
|
||||||
|
|
||||||
impl SaveResults for BadExtensions {
|
impl SaveResults for BadExtensions {
|
||||||
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let file_name: String = match file_name {
|
let file_name: String = match file_name {
|
||||||
"" => "results.txt".to_string(),
|
"" => "results.txt".to_string(),
|
||||||
k => k.to_string(),
|
k => k.to_string(),
|
||||||
|
@ -557,7 +535,7 @@ impl SaveResults for BadExtensions {
|
||||||
} else {
|
} else {
|
||||||
write!(writer, "Not found any files with invalid extension.").unwrap();
|
write!(writer, "Not found any files with invalid extension.").unwrap();
|
||||||
}
|
}
|
||||||
Common::print_time(start_time, SystemTime::now(), "save_results_to_file");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -566,12 +544,9 @@ impl PrintResults for BadExtensions {
|
||||||
/// Print information's about duplicated entries
|
/// Print information's about duplicated entries
|
||||||
/// Only needed for CLI
|
/// Only needed for CLI
|
||||||
fn print_results(&self) {
|
fn print_results(&self) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
println!("Found {} files with invalid extension.\n", self.information.number_of_files_with_bad_extension);
|
println!("Found {} files with invalid extension.\n", self.information.number_of_files_with_bad_extension);
|
||||||
for file_entry in &self.bad_extensions_files {
|
for file_entry in &self.bad_extensions_files {
|
||||||
println!("{} ----- {}", file_entry.path.display(), file_entry.proper_extensions);
|
println!("{} ----- {}", file_entry.path.display(), file_entry.proper_extensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "print_entries");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,9 @@ use std::fs;
|
||||||
use std::fs::{DirEntry, File, Metadata};
|
use std::fs::{DirEntry, File, Metadata};
|
||||||
use std::io::{BufWriter, Write};
|
use std::io::{BufWriter, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::atomic::AtomicBool;
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use futures::channel::mpsc::UnboundedSender;
|
use futures::channel::mpsc::UnboundedSender;
|
||||||
|
@ -14,7 +13,6 @@ use humansize::format_size;
|
||||||
use humansize::BINARY;
|
use humansize::BINARY;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
use crate::common::Common;
|
|
||||||
use crate::common::{check_folder_children, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, split_path};
|
use crate::common::{check_folder_children, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, split_path};
|
||||||
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
|
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
|
||||||
use crate::common_directory::Directories;
|
use crate::common_directory::Directories;
|
||||||
|
@ -142,7 +140,6 @@ impl BigFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn look_for_big_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
fn look_for_big_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
|
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
|
||||||
let mut old_map: BTreeMap<u64, Vec<FileEntry>> = Default::default();
|
let mut old_map: BTreeMap<u64, Vec<FileEntry>> = Default::default();
|
||||||
|
|
||||||
|
@ -151,9 +148,7 @@ impl BigFile {
|
||||||
folders_to_check.push(id.clone());
|
folders_to_check.push(id.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
let (progress_thread_handle, progress_thread_run, atomic_counter, _check_was_stopped) = prepare_thread_handler_common(progress_sender, 0, 0, 0, CheckingMethod::None);
|
||||||
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
|
||||||
let progress_thread_handle = prepare_thread_handler_common(progress_sender, &progress_thread_run, &atomic_counter, 0, 0, 0, CheckingMethod::None);
|
|
||||||
|
|
||||||
while !folders_to_check.is_empty() {
|
while !folders_to_check.is_empty() {
|
||||||
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
|
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
|
||||||
|
@ -213,7 +208,6 @@ impl BigFile {
|
||||||
|
|
||||||
self.extract_n_biggest_files(old_map);
|
self.extract_n_biggest_files(old_map);
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "look_for_big_files");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,8 +302,6 @@ impl BigFile {
|
||||||
|
|
||||||
/// Function to delete files, from filed Vector
|
/// Function to delete files, from filed Vector
|
||||||
fn delete_files(&mut self) {
|
fn delete_files(&mut self) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
|
|
||||||
match self.delete_method {
|
match self.delete_method {
|
||||||
DeleteMethod::Delete => {
|
DeleteMethod::Delete => {
|
||||||
for (_, file_entry) in &self.big_files {
|
for (_, file_entry) in &self.big_files {
|
||||||
|
@ -322,8 +314,6 @@ impl BigFile {
|
||||||
//Just do nothing
|
//Just do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "delete_files");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +355,6 @@ impl DebugPrint for BigFile {
|
||||||
impl SaveResults for BigFile {
|
impl SaveResults for BigFile {
|
||||||
/// Saving results to provided file
|
/// Saving results to provided file
|
||||||
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let file_name: String = match file_name {
|
let file_name: String = match file_name {
|
||||||
"" => "results.txt".to_string(),
|
"" => "results.txt".to_string(),
|
||||||
k => k.to_string(),
|
k => k.to_string(),
|
||||||
|
@ -401,14 +390,13 @@ impl SaveResults for BigFile {
|
||||||
} else {
|
} else {
|
||||||
write!(writer, "Not found any files.").unwrap();
|
write!(writer, "Not found any files.").unwrap();
|
||||||
}
|
}
|
||||||
Common::print_time(start_time, SystemTime::now(), "save_results_to_file");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrintResults for BigFile {
|
impl PrintResults for BigFile {
|
||||||
fn print_results(&self) {
|
fn print_results(&self) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
if self.search_mode == SearchMode::BiggestFiles {
|
if self.search_mode == SearchMode::BiggestFiles {
|
||||||
println!("{} the biggest files.\n\n", self.information.number_of_real_files);
|
println!("{} the biggest files.\n\n", self.information.number_of_real_files);
|
||||||
} else {
|
} else {
|
||||||
|
@ -417,6 +405,5 @@ impl PrintResults for BigFile {
|
||||||
for (size, file_entry) in &self.big_files {
|
for (size, file_entry) in &self.big_files {
|
||||||
println!("{} ({}) - {}", format_size(*size, BINARY), size, file_entry.path.display());
|
println!("{} ({}) - {}", format_size(*size, BINARY), size, file_entry.path.display());
|
||||||
}
|
}
|
||||||
Common::print_time(start_time, SystemTime::now(), "print_entries");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@ use std::fs::{DirEntry, File, Metadata};
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::{BufReader, BufWriter};
|
use std::io::{BufReader, BufWriter};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::SystemTime;
|
|
||||||
use std::{fs, mem, panic};
|
use std::{fs, mem, panic};
|
||||||
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
|
@ -18,7 +18,7 @@ use rayon::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
check_folder_children, create_crash_message, open_cache_folder, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, Common, PDF_FILES_EXTENSIONS,
|
check_folder_children, create_crash_message, open_cache_folder, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, PDF_FILES_EXTENSIONS,
|
||||||
};
|
};
|
||||||
use crate::common::{AUDIO_FILES_EXTENSIONS, IMAGE_RS_BROKEN_FILES_EXTENSIONS, ZIP_FILES_EXTENSIONS};
|
use crate::common::{AUDIO_FILES_EXTENSIONS, IMAGE_RS_BROKEN_FILES_EXTENSIONS, ZIP_FILES_EXTENSIONS};
|
||||||
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
|
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
|
||||||
|
@ -193,7 +193,6 @@ impl BrokenFiles {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
|
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
|
||||||
|
|
||||||
// Add root folders for finding
|
// Add root folders for finding
|
||||||
|
@ -201,9 +200,7 @@ impl BrokenFiles {
|
||||||
folders_to_check.push(id.clone());
|
folders_to_check.push(id.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
let (progress_thread_handle, progress_thread_run, atomic_counter, _check_was_stopped) = prepare_thread_handler_common(progress_sender, 0, 1, 0, CheckingMethod::None);
|
||||||
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
|
||||||
let progress_thread_handle = prepare_thread_handler_common(progress_sender, &progress_thread_run, &atomic_counter, 0, 1, 0, CheckingMethod::None);
|
|
||||||
|
|
||||||
while !folders_to_check.is_empty() {
|
while !folders_to_check.is_empty() {
|
||||||
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
|
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
|
||||||
|
@ -263,7 +260,6 @@ impl BrokenFiles {
|
||||||
|
|
||||||
send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle);
|
send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle);
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "check_files");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
fn get_file_entry(
|
fn get_file_entry(
|
||||||
|
@ -405,14 +401,11 @@ impl BrokenFiles {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn look_for_broken_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
fn look_for_broken_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
||||||
let system_time = SystemTime::now();
|
|
||||||
|
|
||||||
let loaded_hash_map;
|
let loaded_hash_map;
|
||||||
|
|
||||||
let mut records_already_cached: BTreeMap<String, FileEntry> = Default::default();
|
let mut records_already_cached: BTreeMap<String, FileEntry> = Default::default();
|
||||||
let mut non_cached_files_to_check: BTreeMap<String, FileEntry> = Default::default();
|
let mut non_cached_files_to_check: BTreeMap<String, FileEntry> = Default::default();
|
||||||
let mut files_to_check = Default::default();
|
let files_to_check = mem::take(&mut self.files_to_check);
|
||||||
mem::swap(&mut self.files_to_check, &mut files_to_check);
|
|
||||||
|
|
||||||
if self.use_cache {
|
if self.use_cache {
|
||||||
loaded_hash_map = match load_cache_from_file(&mut self.text_messages, self.delete_outdated_cache) {
|
loaded_hash_map = match load_cache_from_file(&mut self.text_messages, self.delete_outdated_cache) {
|
||||||
|
@ -442,17 +435,8 @@ impl BrokenFiles {
|
||||||
non_cached_files_to_check = files_to_check;
|
non_cached_files_to_check = files_to_check;
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
let (progress_thread_handle, progress_thread_run, atomic_counter, _check_was_stopped) =
|
||||||
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
prepare_thread_handler_common(progress_sender, 1, 1, non_cached_files_to_check.len(), CheckingMethod::None);
|
||||||
let progress_thread_handle = prepare_thread_handler_common(
|
|
||||||
progress_sender,
|
|
||||||
&progress_thread_run,
|
|
||||||
&atomic_counter,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
non_cached_files_to_check.len(),
|
|
||||||
CheckingMethod::None,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut vec_file_entry: Vec<FileEntry> = non_cached_files_to_check
|
let mut vec_file_entry: Vec<FileEntry> = non_cached_files_to_check
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
|
@ -479,9 +463,7 @@ impl BrokenFiles {
|
||||||
send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle);
|
send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle);
|
||||||
|
|
||||||
// Just connect loaded results with already calculated
|
// Just connect loaded results with already calculated
|
||||||
for (_name, file_entry) in records_already_cached {
|
vec_file_entry.extend(records_already_cached.into_values());
|
||||||
vec_file_entry.push(file_entry.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.use_cache {
|
if self.use_cache {
|
||||||
// Must save all results to file, old loaded from file with all currently counted results
|
// Must save all results to file, old loaded from file with all currently counted results
|
||||||
|
@ -503,8 +485,6 @@ impl BrokenFiles {
|
||||||
|
|
||||||
self.information.number_of_broken_files = self.broken_files.len();
|
self.information.number_of_broken_files = self.broken_files.len();
|
||||||
|
|
||||||
Common::print_time(system_time, SystemTime::now(), "sort_images - reading data from files in parallel");
|
|
||||||
|
|
||||||
// Clean unused data
|
// Clean unused data
|
||||||
self.files_to_check = Default::default();
|
self.files_to_check = Default::default();
|
||||||
|
|
||||||
|
@ -513,8 +493,6 @@ impl BrokenFiles {
|
||||||
|
|
||||||
/// Function to delete files, from filed Vector
|
/// Function to delete files, from filed Vector
|
||||||
fn delete_files(&mut self) {
|
fn delete_files(&mut self) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
|
|
||||||
match self.delete_method {
|
match self.delete_method {
|
||||||
DeleteMethod::Delete => {
|
DeleteMethod::Delete => {
|
||||||
for file_entry in &self.broken_files {
|
for file_entry in &self.broken_files {
|
||||||
|
@ -527,8 +505,6 @@ impl BrokenFiles {
|
||||||
//Just do nothing
|
//Just do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "delete_files");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,7 +545,6 @@ impl DebugPrint for BrokenFiles {
|
||||||
|
|
||||||
impl SaveResults for BrokenFiles {
|
impl SaveResults for BrokenFiles {
|
||||||
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let file_name: String = match file_name {
|
let file_name: String = match file_name {
|
||||||
"" => "results.txt".to_string(),
|
"" => "results.txt".to_string(),
|
||||||
k => k.to_string(),
|
k => k.to_string(),
|
||||||
|
@ -601,7 +576,7 @@ impl SaveResults for BrokenFiles {
|
||||||
} else {
|
} else {
|
||||||
write!(writer, "Not found any broken files.").unwrap();
|
write!(writer, "Not found any broken files.").unwrap();
|
||||||
}
|
}
|
||||||
Common::print_time(start_time, SystemTime::now(), "save_results_to_file");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -610,13 +585,10 @@ impl PrintResults for BrokenFiles {
|
||||||
/// Print information's about duplicated entries
|
/// Print information's about duplicated entries
|
||||||
/// Only needed for CLI
|
/// Only needed for CLI
|
||||||
fn print_results(&self) {
|
fn print_results(&self) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
println!("Found {} broken files.\n", self.information.number_of_broken_files);
|
println!("Found {} broken files.\n", self.information.number_of_broken_files);
|
||||||
for file_entry in &self.broken_files {
|
for file_entry in &self.broken_files {
|
||||||
println!("{} - {}", file_entry.path.display(), file_entry.error_string);
|
println!("{} - {}", file_entry.path.display(), file_entry.error_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "print_entries");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ use libheif_rs::{ColorSpace, HeifContext, RgbChroma};
|
||||||
use crate::common_dir_traversal::{CheckingMethod, ProgressData};
|
use crate::common_dir_traversal::{CheckingMethod, ProgressData};
|
||||||
use crate::common_directory::Directories;
|
use crate::common_directory::Directories;
|
||||||
use crate::common_items::ExcludedItems;
|
use crate::common_items::ExcludedItems;
|
||||||
|
use crate::common_traits::ResultEntry;
|
||||||
|
|
||||||
static NUMBER_OF_THREADS: state::Storage<usize> = state::Storage::new();
|
static NUMBER_OF_THREADS: state::Storage<usize> = state::Storage::new();
|
||||||
|
|
||||||
|
@ -364,16 +365,38 @@ pub fn check_folder_children(
|
||||||
dir_result.push(next_folder);
|
dir_result.push(next_folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn filter_reference_folders_generic<T>(entries_to_check: Vec<Vec<T>>, directories: &Directories) -> Vec<(T, Vec<T>)>
|
||||||
|
where
|
||||||
|
T: ResultEntry,
|
||||||
|
{
|
||||||
|
entries_to_check
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|vec_file_entry| {
|
||||||
|
let (mut files_from_referenced_folders, normal_files): (Vec<_>, Vec<_>) =
|
||||||
|
vec_file_entry.into_iter().partition(|e| directories.is_in_referenced_directory(e.get_path()));
|
||||||
|
|
||||||
|
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some((files_from_referenced_folders.pop().unwrap(), normal_files))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<(T, Vec<T>)>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn prepare_thread_handler_common(
|
pub fn prepare_thread_handler_common(
|
||||||
progress_sender: Option<&UnboundedSender<ProgressData>>,
|
progress_sender: Option<&UnboundedSender<ProgressData>>,
|
||||||
progress_thread_run: &Arc<AtomicBool>,
|
|
||||||
atomic_counter: &Arc<AtomicUsize>,
|
|
||||||
current_stage: u8,
|
current_stage: u8,
|
||||||
max_stage: u8,
|
max_stage: u8,
|
||||||
max_value: usize,
|
max_value: usize,
|
||||||
checking_method: CheckingMethod,
|
checking_method: CheckingMethod,
|
||||||
) -> JoinHandle<()> {
|
) -> (JoinHandle<()>, Arc<AtomicBool>, Arc<AtomicUsize>, AtomicBool) {
|
||||||
if let Some(progress_sender) = progress_sender {
|
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
||||||
|
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
||||||
|
let check_was_stopped = AtomicBool::new(false);
|
||||||
|
let progress_thread_sender = if let Some(progress_sender) = progress_sender {
|
||||||
let progress_send = progress_sender.clone();
|
let progress_send = progress_sender.clone();
|
||||||
let progress_thread_run = progress_thread_run.clone();
|
let progress_thread_run = progress_thread_run.clone();
|
||||||
let atomic_counter = atomic_counter.clone();
|
let atomic_counter = atomic_counter.clone();
|
||||||
|
@ -394,7 +417,8 @@ pub fn prepare_thread_handler_common(
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
thread::spawn(|| {})
|
thread::spawn(|| {})
|
||||||
}
|
};
|
||||||
|
(progress_thread_sender, progress_thread_run, atomic_counter, check_was_stopped)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_info_and_wait_for_ending_all_threads(progress_thread_run: &Arc<AtomicBool>, progress_thread_handle: JoinHandle<()>) {
|
pub fn send_info_and_wait_for_ending_all_threads(progress_thread_run: &Arc<AtomicBool>, progress_thread_handle: JoinHandle<()>) {
|
||||||
|
|
|
@ -2,9 +2,9 @@ use std::collections::BTreeMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::{DirEntry, Metadata, ReadDir};
|
use std::fs::{DirEntry, Metadata, ReadDir};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::UNIX_EPOCH;
|
||||||
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use futures::channel::mpsc::UnboundedSender;
|
use futures::channel::mpsc::UnboundedSender;
|
||||||
|
@ -14,6 +14,7 @@ use crate::common::{prepare_thread_handler_common, send_info_and_wait_for_ending
|
||||||
use crate::common_directory::Directories;
|
use crate::common_directory::Directories;
|
||||||
use crate::common_extensions::Extensions;
|
use crate::common_extensions::Extensions;
|
||||||
use crate::common_items::ExcludedItems;
|
use crate::common_items::ExcludedItems;
|
||||||
|
use crate::common_traits::ResultEntry;
|
||||||
use crate::flc;
|
use crate::flc;
|
||||||
use crate::localizer_core::generate_translation_hashmap;
|
use crate::localizer_core::generate_translation_hashmap;
|
||||||
|
|
||||||
|
@ -33,6 +34,8 @@ pub enum CheckingMethod {
|
||||||
SizeName,
|
SizeName,
|
||||||
Size,
|
Size,
|
||||||
Hash,
|
Hash,
|
||||||
|
AudioTags,
|
||||||
|
AudioContent,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||||
|
@ -43,6 +46,11 @@ pub struct FileEntry {
|
||||||
pub hash: String,
|
pub hash: String,
|
||||||
pub symlink_info: Option<SymlinkInfo>,
|
pub symlink_info: Option<SymlinkInfo>,
|
||||||
}
|
}
|
||||||
|
impl ResultEntry for FileEntry {
|
||||||
|
fn get_path(&self) -> &Path {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Symlinks
|
// Symlinks
|
||||||
|
|
||||||
|
@ -280,12 +288,10 @@ impl<'a, 'b, F> DirTraversalBuilder<'a, 'b, F> {
|
||||||
|
|
||||||
pub enum DirTraversalResult<T: Ord + PartialOrd> {
|
pub enum DirTraversalResult<T: Ord + PartialOrd> {
|
||||||
SuccessFiles {
|
SuccessFiles {
|
||||||
start_time: SystemTime,
|
|
||||||
warnings: Vec<String>,
|
warnings: Vec<String>,
|
||||||
grouped_file_entries: BTreeMap<T, Vec<FileEntry>>,
|
grouped_file_entries: BTreeMap<T, Vec<FileEntry>>,
|
||||||
},
|
},
|
||||||
SuccessFolders {
|
SuccessFolders {
|
||||||
start_time: SystemTime,
|
|
||||||
warnings: Vec<String>,
|
warnings: Vec<String>,
|
||||||
folder_entries: BTreeMap<PathBuf, FolderEntry>, // Path, FolderEntry
|
folder_entries: BTreeMap<PathBuf, FolderEntry>, // Path, FolderEntry
|
||||||
},
|
},
|
||||||
|
@ -314,7 +320,6 @@ where
|
||||||
let mut all_warnings = vec![];
|
let mut all_warnings = vec![];
|
||||||
let mut grouped_file_entries: BTreeMap<T, Vec<FileEntry>> = BTreeMap::new();
|
let mut grouped_file_entries: BTreeMap<T, Vec<FileEntry>> = BTreeMap::new();
|
||||||
let mut folder_entries: BTreeMap<PathBuf, FolderEntry> = BTreeMap::new();
|
let mut folder_entries: BTreeMap<PathBuf, FolderEntry> = BTreeMap::new();
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
|
|
||||||
// Add root folders into result (only for empty folder collection)
|
// Add root folders into result (only for empty folder collection)
|
||||||
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
|
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
|
||||||
|
@ -333,9 +338,8 @@ where
|
||||||
// Add root folders for finding
|
// Add root folders for finding
|
||||||
folders_to_check.extend(self.root_dirs);
|
folders_to_check.extend(self.root_dirs);
|
||||||
|
|
||||||
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
let (progress_thread_handle, progress_thread_run, atomic_counter, _check_was_stopped) =
|
||||||
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
prepare_thread_handler_common(self.progress_sender, 0, self.max_stage, 0, self.checking_method);
|
||||||
let progress_thread_handle = prepare_thread_handler_common(self.progress_sender, &progress_thread_run, &atomic_counter, 0, self.max_stage, 0, self.checking_method);
|
|
||||||
|
|
||||||
let DirTraversal {
|
let DirTraversal {
|
||||||
collect,
|
collect,
|
||||||
|
@ -468,12 +472,10 @@ where
|
||||||
|
|
||||||
match collect {
|
match collect {
|
||||||
Collect::Files | Collect::InvalidSymlinks => DirTraversalResult::SuccessFiles {
|
Collect::Files | Collect::InvalidSymlinks => DirTraversalResult::SuccessFiles {
|
||||||
start_time,
|
|
||||||
grouped_file_entries,
|
grouped_file_entries,
|
||||||
warnings: all_warnings,
|
warnings: all_warnings,
|
||||||
},
|
},
|
||||||
Collect::EmptyFolders => DirTraversalResult::SuccessFolders {
|
Collect::EmptyFolders => DirTraversalResult::SuccessFolders {
|
||||||
start_time,
|
|
||||||
folder_entries,
|
folder_entries,
|
||||||
warnings: all_warnings,
|
warnings: all_warnings,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::SystemTime;
|
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
use std::{fs, os::unix::fs::MetadataExt};
|
use std::{fs, os::unix::fs::MetadataExt};
|
||||||
|
|
||||||
|
@ -30,8 +30,6 @@ impl Directories {
|
||||||
|
|
||||||
/// Setting included directories, at least one must be provided or scan won't start
|
/// Setting included directories, at least one must be provided or scan won't start
|
||||||
pub fn set_included_directory(&mut self, included_directory: Vec<PathBuf>, text_messages: &mut Messages) -> bool {
|
pub fn set_included_directory(&mut self, included_directory: Vec<PathBuf>, text_messages: &mut Messages) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
|
|
||||||
if included_directory.is_empty() {
|
if included_directory.is_empty() {
|
||||||
text_messages.errors.push(flc!("core_missing_no_chosen_included_directory"));
|
text_messages.errors.push(flc!("core_missing_no_chosen_included_directory"));
|
||||||
return false;
|
return false;
|
||||||
|
@ -90,13 +88,11 @@ impl Directories {
|
||||||
|
|
||||||
self.included_directories = checked_directories;
|
self.included_directories = checked_directories;
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "set_included_directory");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Setting absolute path to exclude from search
|
/// Setting absolute path to exclude from search
|
||||||
pub fn set_excluded_directory(&mut self, excluded_directory: Vec<PathBuf>, text_messages: &mut Messages) {
|
pub fn set_excluded_directory(&mut self, excluded_directory: Vec<PathBuf>, text_messages: &mut Messages) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
if excluded_directory.is_empty() {
|
if excluded_directory.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -148,8 +144,6 @@ impl Directories {
|
||||||
checked_directories.push(directory);
|
checked_directories.push(directory);
|
||||||
}
|
}
|
||||||
self.excluded_directories = checked_directories;
|
self.excluded_directories = checked_directories;
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "set_excluded_directory");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
|
@ -159,8 +153,6 @@ impl Directories {
|
||||||
|
|
||||||
/// Remove unused entries when included or excluded overlaps with each other or are duplicated etc.
|
/// Remove unused entries when included or excluded overlaps with each other or are duplicated etc.
|
||||||
pub fn optimize_directories(&mut self, recursive_search: bool, text_messages: &mut Messages) -> bool {
|
pub fn optimize_directories(&mut self, recursive_search: bool, text_messages: &mut Messages) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
|
|
||||||
let mut optimized_included: Vec<PathBuf> = Vec::new();
|
let mut optimized_included: Vec<PathBuf> = Vec::new();
|
||||||
let mut optimized_excluded: Vec<PathBuf> = Vec::new();
|
let mut optimized_excluded: Vec<PathBuf> = Vec::new();
|
||||||
|
|
||||||
|
@ -295,7 +287,6 @@ impl Directories {
|
||||||
// Not needed, but better is to have sorted everything
|
// Not needed, but better is to have sorted everything
|
||||||
self.excluded_directories.sort_unstable();
|
self.excluded_directories.sort_unstable();
|
||||||
self.included_directories.sort_unstable();
|
self.included_directories.sort_unstable();
|
||||||
Common::print_time(start_time, SystemTime::now(), "optimize_directories");
|
|
||||||
|
|
||||||
// Get device IDs for included directories
|
// Get device IDs for included directories
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
|
@ -314,6 +305,11 @@ impl Directories {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_in_referenced_directory(&self, path: &Path) -> bool {
|
||||||
|
self.reference_directories.iter().any(|e| path.starts_with(e))
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks whether a specified directory is excluded from searching
|
/// Checks whether a specified directory is excluded from searching
|
||||||
pub fn is_excluded(&self, path: impl AsRef<Path>) -> bool {
|
pub fn is_excluded(&self, path: impl AsRef<Path>) -> bool {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
use crate::common::Common;
|
|
||||||
use crate::common_messages::Messages;
|
use crate::common_messages::Messages;
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
|
@ -16,7 +13,6 @@ impl Extensions {
|
||||||
/// List of allowed extensions, only files with this extensions will be checking if are duplicates
|
/// List of allowed extensions, only files with this extensions will be checking if are duplicates
|
||||||
/// After, extensions cannot contains any dot, commas etc.
|
/// After, extensions cannot contains any dot, commas etc.
|
||||||
pub fn set_allowed_extensions(&mut self, mut allowed_extensions: String, text_messages: &mut Messages) {
|
pub fn set_allowed_extensions(&mut self, mut allowed_extensions: String, text_messages: &mut Messages) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
if allowed_extensions.trim().is_empty() {
|
if allowed_extensions.trim().is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +53,6 @@ impl Extensions {
|
||||||
.messages
|
.messages
|
||||||
.push("No valid extensions were provided, so allowing all extensions by default.".to_string());
|
.push("No valid extensions were provided, so allowing all extensions by default.".to_string());
|
||||||
}
|
}
|
||||||
Common::print_time(start_time, SystemTime::now(), "set_allowed_extensions");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
use crate::common::Common;
|
use crate::common::Common;
|
||||||
use crate::common_messages::Messages;
|
use crate::common_messages::Messages;
|
||||||
|
@ -17,8 +16,6 @@ impl ExcludedItems {
|
||||||
/// Setting excluded items which needs to contains * wildcard
|
/// Setting excluded items which needs to contains * wildcard
|
||||||
/// Are a lot of slower than absolute path, so it should be used to heavy
|
/// Are a lot of slower than absolute path, so it should be used to heavy
|
||||||
pub fn set_excluded_items(&mut self, excluded_items: Vec<String>, text_messages: &mut Messages) {
|
pub fn set_excluded_items(&mut self, excluded_items: Vec<String>, text_messages: &mut Messages) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
|
|
||||||
if excluded_items.is_empty() {
|
if excluded_items.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -55,7 +52,6 @@ impl ExcludedItems {
|
||||||
checked_expressions.push(expression);
|
checked_expressions.push(expression);
|
||||||
}
|
}
|
||||||
self.items = checked_expressions;
|
self.items = checked_expressions;
|
||||||
Common::print_time(start_time, SystemTime::now(), "set_excluded_items");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether a specified path is excluded from searching
|
/// Checks whether a specified path is excluded from searching
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
pub trait DebugPrint {
|
pub trait DebugPrint {
|
||||||
fn debug_print(&self);
|
fn debug_print(&self);
|
||||||
}
|
}
|
||||||
|
@ -9,3 +11,7 @@ pub trait SaveResults {
|
||||||
pub trait PrintResults {
|
pub trait PrintResults {
|
||||||
fn print_results(&self);
|
fn print_results(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ResultEntry {
|
||||||
|
fn get_path(&self) -> &Path;
|
||||||
|
}
|
||||||
|
|
|
@ -9,9 +9,8 @@ use std::io::{BufReader, BufWriter};
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::SystemTime;
|
|
||||||
use std::{fs, mem};
|
use std::{fs, mem};
|
||||||
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
|
@ -21,7 +20,7 @@ use humansize::BINARY;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use xxhash_rust::xxh3::Xxh3;
|
use xxhash_rust::xxh3::Xxh3;
|
||||||
|
|
||||||
use crate::common::{open_cache_folder, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, Common};
|
use crate::common::{open_cache_folder, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads};
|
||||||
use crate::common_dir_traversal::{CheckingMethod, DirTraversalBuilder, DirTraversalResult, FileEntry, ProgressData};
|
use crate::common_dir_traversal::{CheckingMethod, DirTraversalBuilder, DirTraversalResult, FileEntry, ProgressData};
|
||||||
use crate::common_directory::Directories;
|
use crate::common_directory::Directories;
|
||||||
use crate::common_extensions::Extensions;
|
use crate::common_extensions::Extensions;
|
||||||
|
@ -182,9 +181,7 @@ impl DuplicateFinder {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CheckingMethod::None => {
|
_ => panic!(),
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.delete_files();
|
self.delete_files();
|
||||||
self.debug_print();
|
self.debug_print();
|
||||||
|
@ -364,11 +361,7 @@ impl DuplicateFinder {
|
||||||
.build()
|
.build()
|
||||||
.run();
|
.run();
|
||||||
match result {
|
match result {
|
||||||
DirTraversalResult::SuccessFiles {
|
DirTraversalResult::SuccessFiles { grouped_file_entries, warnings } => {
|
||||||
start_time,
|
|
||||||
grouped_file_entries,
|
|
||||||
warnings,
|
|
||||||
} => {
|
|
||||||
self.files_with_identical_names = grouped_file_entries;
|
self.files_with_identical_names = grouped_file_entries;
|
||||||
self.text_messages.warnings.extend(warnings);
|
self.text_messages.warnings.extend(warnings);
|
||||||
|
|
||||||
|
@ -384,21 +377,11 @@ impl DuplicateFinder {
|
||||||
|
|
||||||
// Reference - only use in size, because later hash will be counted differently
|
// Reference - only use in size, because later hash will be counted differently
|
||||||
if self.use_reference_folders {
|
if self.use_reference_folders {
|
||||||
let mut btree_map = Default::default();
|
let vec = mem::take(&mut self.files_with_identical_names)
|
||||||
mem::swap(&mut self.files_with_identical_names, &mut btree_map);
|
|
||||||
let reference_directories = self.directories.reference_directories.clone();
|
|
||||||
let vec = btree_map
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(_size, vec_file_entry)| {
|
.filter_map(|(_name, vec_file_entry)| {
|
||||||
let mut files_from_referenced_folders = Vec::new();
|
let (mut files_from_referenced_folders, normal_files): (Vec<_>, Vec<_>) =
|
||||||
let mut normal_files = Vec::new();
|
vec_file_entry.into_iter().partition(|e| self.directories.is_in_referenced_directory(e.get_path()));
|
||||||
for file_entry in vec_file_entry {
|
|
||||||
if reference_directories.iter().any(|e| file_entry.path.starts_with(e)) {
|
|
||||||
files_from_referenced_folders.push(file_entry);
|
|
||||||
} else {
|
|
||||||
normal_files.push(file_entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
||||||
None
|
None
|
||||||
|
@ -413,7 +396,6 @@ impl DuplicateFinder {
|
||||||
}
|
}
|
||||||
self.calculate_name_stats();
|
self.calculate_name_stats();
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "check_files_name");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
DirTraversalResult::SuccessFolders { .. } => {
|
DirTraversalResult::SuccessFolders { .. } => {
|
||||||
|
@ -459,11 +441,7 @@ impl DuplicateFinder {
|
||||||
.build()
|
.build()
|
||||||
.run();
|
.run();
|
||||||
match result {
|
match result {
|
||||||
DirTraversalResult::SuccessFiles {
|
DirTraversalResult::SuccessFiles { grouped_file_entries, warnings } => {
|
||||||
start_time,
|
|
||||||
grouped_file_entries,
|
|
||||||
warnings,
|
|
||||||
} => {
|
|
||||||
self.files_with_identical_size_names = grouped_file_entries;
|
self.files_with_identical_size_names = grouped_file_entries;
|
||||||
self.text_messages.warnings.extend(warnings);
|
self.text_messages.warnings.extend(warnings);
|
||||||
|
|
||||||
|
@ -479,21 +457,11 @@ impl DuplicateFinder {
|
||||||
|
|
||||||
// Reference - only use in size, because later hash will be counted differently
|
// Reference - only use in size, because later hash will be counted differently
|
||||||
if self.use_reference_folders {
|
if self.use_reference_folders {
|
||||||
let mut btree_map = Default::default();
|
let vec = mem::take(&mut self.files_with_identical_size_names)
|
||||||
mem::swap(&mut self.files_with_identical_size_names, &mut btree_map);
|
|
||||||
let reference_directories = self.directories.reference_directories.clone();
|
|
||||||
let vec = btree_map
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(_size, vec_file_entry)| {
|
.filter_map(|(_size, vec_file_entry)| {
|
||||||
let mut files_from_referenced_folders = Vec::new();
|
let (mut files_from_referenced_folders, normal_files): (Vec<_>, Vec<_>) =
|
||||||
let mut normal_files = Vec::new();
|
vec_file_entry.into_iter().partition(|e| self.directories.is_in_referenced_directory(e.get_path()));
|
||||||
for file_entry in vec_file_entry {
|
|
||||||
if reference_directories.iter().any(|e| file_entry.path.starts_with(e)) {
|
|
||||||
files_from_referenced_folders.push(file_entry);
|
|
||||||
} else {
|
|
||||||
normal_files.push(file_entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
||||||
None
|
None
|
||||||
|
@ -509,7 +477,6 @@ impl DuplicateFinder {
|
||||||
}
|
}
|
||||||
self.calculate_size_name_stats();
|
self.calculate_size_name_stats();
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "check_files_size_name");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
DirTraversalResult::SuccessFolders { .. } => {
|
DirTraversalResult::SuccessFolders { .. } => {
|
||||||
|
@ -559,17 +526,12 @@ impl DuplicateFinder {
|
||||||
.build()
|
.build()
|
||||||
.run();
|
.run();
|
||||||
match result {
|
match result {
|
||||||
DirTraversalResult::SuccessFiles {
|
DirTraversalResult::SuccessFiles { grouped_file_entries, warnings } => {
|
||||||
start_time,
|
|
||||||
grouped_file_entries,
|
|
||||||
warnings,
|
|
||||||
} => {
|
|
||||||
self.files_with_identical_size = grouped_file_entries;
|
self.files_with_identical_size = grouped_file_entries;
|
||||||
self.text_messages.warnings.extend(warnings);
|
self.text_messages.warnings.extend(warnings);
|
||||||
|
|
||||||
// Create new BTreeMap without single size entries(files have not duplicates)
|
// Create new BTreeMap without single size entries(files have not duplicates)
|
||||||
let mut old_map: BTreeMap<u64, Vec<FileEntry>> = Default::default();
|
let old_map: BTreeMap<u64, Vec<FileEntry>> = mem::take(&mut self.files_with_identical_size);
|
||||||
mem::swap(&mut old_map, &mut self.files_with_identical_size);
|
|
||||||
|
|
||||||
for (size, vec) in old_map {
|
for (size, vec) in old_map {
|
||||||
if vec.len() <= 1 {
|
if vec.len() <= 1 {
|
||||||
|
@ -586,7 +548,6 @@ impl DuplicateFinder {
|
||||||
self.filter_reference_folders_by_size();
|
self.filter_reference_folders_by_size();
|
||||||
self.calculate_size_stats();
|
self.calculate_size_stats();
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "check_files_size");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
DirTraversalResult::SuccessFolders { .. } => {
|
DirTraversalResult::SuccessFolders { .. } => {
|
||||||
|
@ -616,21 +577,11 @@ impl DuplicateFinder {
|
||||||
/// This is needed, because later reference folders looks for hashes, not size
|
/// This is needed, because later reference folders looks for hashes, not size
|
||||||
fn filter_reference_folders_by_size(&mut self) {
|
fn filter_reference_folders_by_size(&mut self) {
|
||||||
if self.use_reference_folders && self.check_method == CheckingMethod::Size {
|
if self.use_reference_folders && self.check_method == CheckingMethod::Size {
|
||||||
let mut btree_map = Default::default();
|
let vec = mem::take(&mut self.files_with_identical_size)
|
||||||
mem::swap(&mut self.files_with_identical_size, &mut btree_map);
|
|
||||||
let reference_directories = self.directories.reference_directories.clone();
|
|
||||||
let vec = btree_map
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(_size, vec_file_entry)| {
|
.filter_map(|(_size, vec_file_entry)| {
|
||||||
let mut files_from_referenced_folders = Vec::new();
|
let (mut files_from_referenced_folders, normal_files): (Vec<_>, Vec<_>) =
|
||||||
let mut normal_files = Vec::new();
|
vec_file_entry.into_iter().partition(|e| self.directories.is_in_referenced_directory(e.get_path()));
|
||||||
for file_entry in vec_file_entry {
|
|
||||||
if reference_directories.iter().any(|e| file_entry.path.starts_with(e)) {
|
|
||||||
files_from_referenced_folders.push(file_entry);
|
|
||||||
} else {
|
|
||||||
normal_files.push(file_entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
||||||
None
|
None
|
||||||
|
@ -723,21 +674,9 @@ impl DuplicateFinder {
|
||||||
progress_sender: Option<&UnboundedSender<ProgressData>>,
|
progress_sender: Option<&UnboundedSender<ProgressData>>,
|
||||||
pre_checked_map: &mut BTreeMap<u64, Vec<FileEntry>>,
|
pre_checked_map: &mut BTreeMap<u64, Vec<FileEntry>>,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let check_type = self.hash_type;
|
let check_type = self.hash_type;
|
||||||
let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread
|
let (progress_thread_handle, progress_thread_run, atomic_counter, check_was_stopped) =
|
||||||
|
prepare_thread_handler_common(progress_sender, 1, 2, self.files_with_identical_size.values().map(Vec::len).sum(), self.check_method);
|
||||||
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
|
||||||
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
|
||||||
let progress_thread_handle = prepare_thread_handler_common(
|
|
||||||
progress_sender,
|
|
||||||
&progress_thread_run,
|
|
||||||
&atomic_counter,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
self.files_with_identical_size.values().map(Vec::len).sum(),
|
|
||||||
self.check_method,
|
|
||||||
);
|
|
||||||
|
|
||||||
let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.prehash_load_cache_at_start();
|
let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.prehash_load_cache_at_start();
|
||||||
|
|
||||||
|
@ -750,11 +689,11 @@ impl DuplicateFinder {
|
||||||
let mut buffer = [0u8; 1024 * 2];
|
let mut buffer = [0u8; 1024 * 2];
|
||||||
|
|
||||||
atomic_counter.fetch_add(vec_file_entry.len(), Ordering::Relaxed);
|
atomic_counter.fetch_add(vec_file_entry.len(), Ordering::Relaxed);
|
||||||
for file_entry in vec_file_entry {
|
|
||||||
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
|
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
|
||||||
check_was_stopped.store(true, Ordering::Relaxed);
|
check_was_stopped.store(true, Ordering::Relaxed);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
for file_entry in vec_file_entry {
|
||||||
match hash_calculation(&mut buffer, file_entry, &check_type, 0) {
|
match hash_calculation(&mut buffer, file_entry, &check_type, 0) {
|
||||||
Ok(hash_string) => {
|
Ok(hash_string) => {
|
||||||
hashmap_with_hash.entry(hash_string.clone()).or_insert_with(Vec::new).push(file_entry.clone());
|
hashmap_with_hash.entry(hash_string.clone()).or_insert_with(Vec::new).push(file_entry.clone());
|
||||||
|
@ -791,8 +730,6 @@ impl DuplicateFinder {
|
||||||
|
|
||||||
self.prehash_save_cache_at_exit(loaded_hash_map, &pre_hash_results);
|
self.prehash_save_cache_at_exit(loaded_hash_map, &pre_hash_results);
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "check_files_hash - prehash");
|
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -891,23 +828,10 @@ impl DuplicateFinder {
|
||||||
progress_sender: Option<&UnboundedSender<ProgressData>>,
|
progress_sender: Option<&UnboundedSender<ProgressData>>,
|
||||||
pre_checked_map: BTreeMap<u64, Vec<FileEntry>>,
|
pre_checked_map: BTreeMap<u64, Vec<FileEntry>>,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread
|
|
||||||
|
|
||||||
let check_type = self.hash_type;
|
let check_type = self.hash_type;
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
|
|
||||||
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
let (progress_thread_handle, progress_thread_run, atomic_counter, check_was_stopped) =
|
||||||
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
prepare_thread_handler_common(progress_sender, 2, 2, pre_checked_map.values().map(Vec::len).sum(), self.check_method);
|
||||||
|
|
||||||
let progress_thread_handle = prepare_thread_handler_common(
|
|
||||||
progress_sender,
|
|
||||||
&progress_thread_run,
|
|
||||||
&atomic_counter,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
pre_checked_map.values().map(Vec::len).sum(),
|
|
||||||
self.check_method,
|
|
||||||
);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////// HASHING START
|
///////////////////////////////////////////////////////////////////////////// HASHING START
|
||||||
{
|
{
|
||||||
|
@ -958,30 +882,20 @@ impl DuplicateFinder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Common::print_time(start_time, SystemTime::now(), "delete_files");
|
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_reference_folders(&mut self) {
|
fn hash_reference_folders(&mut self) {
|
||||||
// Reference - only use in size, because later hash will be counted differently
|
// Reference - only use in size, because later hash will be counted differently
|
||||||
if self.use_reference_folders {
|
if self.use_reference_folders {
|
||||||
let mut btree_map = Default::default();
|
let vec = mem::take(&mut self.files_with_identical_hashes)
|
||||||
mem::swap(&mut self.files_with_identical_hashes, &mut btree_map);
|
|
||||||
let reference_directories = self.directories.reference_directories.clone();
|
|
||||||
let vec = btree_map
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(_size, vec_vec_file_entry)| {
|
.filter_map(|(_size, vec_vec_file_entry)| {
|
||||||
let mut all_results_with_same_size = Vec::new();
|
let mut all_results_with_same_size = Vec::new();
|
||||||
for vec_file_entry in vec_vec_file_entry {
|
for vec_file_entry in vec_vec_file_entry {
|
||||||
let mut files_from_referenced_folders = Vec::new();
|
let (mut files_from_referenced_folders, normal_files): (Vec<_>, Vec<_>) =
|
||||||
let mut normal_files = Vec::new();
|
vec_file_entry.into_iter().partition(|e| self.directories.is_in_referenced_directory(e.get_path()));
|
||||||
for file_entry in vec_file_entry {
|
|
||||||
if reference_directories.iter().any(|e| file_entry.path.starts_with(e)) {
|
|
||||||
files_from_referenced_folders.push(file_entry);
|
|
||||||
} else {
|
|
||||||
normal_files.push(file_entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
|
@ -1045,7 +959,6 @@ impl DuplicateFinder {
|
||||||
/// Function to delete files, from filed before `BTreeMap`
|
/// Function to delete files, from filed before `BTreeMap`
|
||||||
/// Using another function to delete files to avoid duplicates data
|
/// Using another function to delete files to avoid duplicates data
|
||||||
fn delete_files(&mut self) {
|
fn delete_files(&mut self) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
if self.delete_method == DeleteMethod::None {
|
if self.delete_method == DeleteMethod::None {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1073,14 +986,9 @@ impl DuplicateFinder {
|
||||||
let _tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages, self.dryrun);
|
let _tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages, self.dryrun);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CheckingMethod::None => {
|
_ => panic!(),
|
||||||
//Just do nothing
|
|
||||||
panic!("Checking method should never be none.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "delete_files");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DuplicateFinder {
|
impl Default for DuplicateFinder {
|
||||||
|
@ -1146,7 +1054,6 @@ impl DebugPrint for DuplicateFinder {
|
||||||
|
|
||||||
impl SaveResults for DuplicateFinder {
|
impl SaveResults for DuplicateFinder {
|
||||||
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let file_name: String = match file_name {
|
let file_name: String = match file_name {
|
||||||
"" => "results.txt".to_string(),
|
"" => "results.txt".to_string(),
|
||||||
k => k.to_string(),
|
k => k.to_string(),
|
||||||
|
@ -1270,11 +1177,9 @@ impl SaveResults for DuplicateFinder {
|
||||||
write!(writer, "Not found any duplicates.").unwrap();
|
write!(writer, "Not found any duplicates.").unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CheckingMethod::None => {
|
_ => panic!(),
|
||||||
panic!();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "save_results_to_file");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1283,7 +1188,6 @@ impl PrintResults for DuplicateFinder {
|
||||||
/// Print information's about duplicated entries
|
/// Print information's about duplicated entries
|
||||||
/// Only needed for CLI
|
/// Only needed for CLI
|
||||||
fn print_results(&self) {
|
fn print_results(&self) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let mut number_of_files: u64 = 0;
|
let mut number_of_files: u64 = 0;
|
||||||
let mut number_of_groups: u64 = 0;
|
let mut number_of_groups: u64 = 0;
|
||||||
|
|
||||||
|
@ -1359,12 +1263,9 @@ impl PrintResults for DuplicateFinder {
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CheckingMethod::None => {
|
_ => panic!(),
|
||||||
panic!("Checking Method shouldn't be ever set to None");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Common::print_time(start_time, SystemTime::now(), "print_entries");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Functions to remove slice(vector) of files with provided method
|
/// Functions to remove slice(vector) of files with provided method
|
||||||
|
|
|
@ -3,12 +3,10 @@ use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::BufWriter;
|
use std::io::BufWriter;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use futures::channel::mpsc::UnboundedSender;
|
use futures::channel::mpsc::UnboundedSender;
|
||||||
|
|
||||||
use crate::common::Common;
|
|
||||||
use crate::common_dir_traversal::{DirTraversalBuilder, DirTraversalResult, FileEntry, ProgressData};
|
use crate::common_dir_traversal::{DirTraversalBuilder, DirTraversalResult, FileEntry, ProgressData};
|
||||||
use crate::common_directory::Directories;
|
use crate::common_directory::Directories;
|
||||||
use crate::common_extensions::Extensions;
|
use crate::common_extensions::Extensions;
|
||||||
|
@ -141,17 +139,13 @@ impl EmptyFiles {
|
||||||
.build()
|
.build()
|
||||||
.run();
|
.run();
|
||||||
match result {
|
match result {
|
||||||
DirTraversalResult::SuccessFiles {
|
DirTraversalResult::SuccessFiles { grouped_file_entries, warnings } => {
|
||||||
start_time,
|
|
||||||
grouped_file_entries,
|
|
||||||
warnings,
|
|
||||||
} => {
|
|
||||||
if let Some(empty_files) = grouped_file_entries.get(&()) {
|
if let Some(empty_files) = grouped_file_entries.get(&()) {
|
||||||
self.empty_files = empty_files.clone();
|
self.empty_files = empty_files.clone();
|
||||||
}
|
}
|
||||||
self.information.number_of_empty_files = self.empty_files.len();
|
self.information.number_of_empty_files = self.empty_files.len();
|
||||||
self.text_messages.warnings.extend(warnings);
|
self.text_messages.warnings.extend(warnings);
|
||||||
Common::print_time(start_time, SystemTime::now(), "check_files_name");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
DirTraversalResult::SuccessFolders { .. } => {
|
DirTraversalResult::SuccessFolders { .. } => {
|
||||||
|
@ -163,8 +157,6 @@ impl EmptyFiles {
|
||||||
|
|
||||||
/// Function to delete files, from filed Vector
|
/// Function to delete files, from filed Vector
|
||||||
fn delete_files(&mut self) {
|
fn delete_files(&mut self) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
|
|
||||||
match self.delete_method {
|
match self.delete_method {
|
||||||
DeleteMethod::Delete => {
|
DeleteMethod::Delete => {
|
||||||
for file_entry in &self.empty_files {
|
for file_entry in &self.empty_files {
|
||||||
|
@ -177,8 +169,6 @@ impl EmptyFiles {
|
||||||
//Just do nothing
|
//Just do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "delete_files");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +210,6 @@ impl DebugPrint for EmptyFiles {
|
||||||
|
|
||||||
impl SaveResults for EmptyFiles {
|
impl SaveResults for EmptyFiles {
|
||||||
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let file_name: String = match file_name {
|
let file_name: String = match file_name {
|
||||||
"" => "results.txt".to_string(),
|
"" => "results.txt".to_string(),
|
||||||
k => k.to_string(),
|
k => k.to_string(),
|
||||||
|
@ -252,7 +241,7 @@ impl SaveResults for EmptyFiles {
|
||||||
} else {
|
} else {
|
||||||
write!(writer, "Not found any empty files.").unwrap();
|
write!(writer, "Not found any empty files.").unwrap();
|
||||||
}
|
}
|
||||||
Common::print_time(start_time, SystemTime::now(), "save_results_to_file");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,12 +250,9 @@ impl PrintResults for EmptyFiles {
|
||||||
/// Print information's about duplicated entries
|
/// Print information's about duplicated entries
|
||||||
/// Only needed for CLI
|
/// Only needed for CLI
|
||||||
fn print_results(&self) {
|
fn print_results(&self) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
println!("Found {} empty files.\n", self.information.number_of_empty_files);
|
println!("Found {} empty files.\n", self.information.number_of_empty_files);
|
||||||
for file_entry in &self.empty_files {
|
for file_entry in &self.empty_files {
|
||||||
println!("{}", file_entry.path.display());
|
println!("{}", file_entry.path.display());
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "print_entries");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,10 @@ use std::fs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufWriter, Write};
|
use std::io::{BufWriter, Write};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use futures::channel::mpsc::UnboundedSender;
|
use futures::channel::mpsc::UnboundedSender;
|
||||||
|
|
||||||
use crate::common::Common;
|
|
||||||
use crate::common_dir_traversal::{Collect, DirTraversalBuilder, DirTraversalResult, FolderEmptiness, FolderEntry, ProgressData};
|
use crate::common_dir_traversal::{Collect, DirTraversalBuilder, DirTraversalResult, FolderEmptiness, FolderEntry, ProgressData};
|
||||||
use crate::common_directory::Directories;
|
use crate::common_directory::Directories;
|
||||||
use crate::common_items::ExcludedItems;
|
use crate::common_items::ExcludedItems;
|
||||||
|
@ -145,11 +143,7 @@ impl EmptyFolder {
|
||||||
DirTraversalResult::SuccessFiles { .. } => {
|
DirTraversalResult::SuccessFiles { .. } => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
DirTraversalResult::SuccessFolders {
|
DirTraversalResult::SuccessFolders { folder_entries, warnings } => {
|
||||||
start_time,
|
|
||||||
folder_entries,
|
|
||||||
warnings,
|
|
||||||
} => {
|
|
||||||
// We need to set empty folder list
|
// We need to set empty folder list
|
||||||
#[allow(unused_mut)] // Used is later by Windows build
|
#[allow(unused_mut)] // Used is later by Windows build
|
||||||
for (mut name, folder_entry) in folder_entries {
|
for (mut name, folder_entry) in folder_entries {
|
||||||
|
@ -160,7 +154,6 @@ impl EmptyFolder {
|
||||||
|
|
||||||
self.text_messages.warnings.extend(warnings);
|
self.text_messages.warnings.extend(warnings);
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "check_for_empty_folder");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
DirTraversalResult::Stopped => false,
|
DirTraversalResult::Stopped => false,
|
||||||
|
@ -169,7 +162,6 @@ impl EmptyFolder {
|
||||||
|
|
||||||
/// Deletes earlier found empty folders
|
/// Deletes earlier found empty folders
|
||||||
fn delete_empty_folders(&mut self) {
|
fn delete_empty_folders(&mut self) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
// Folders may be deleted or require too big privileges
|
// Folders may be deleted or require too big privileges
|
||||||
for name in self.empty_folder_list.keys() {
|
for name in self.empty_folder_list.keys() {
|
||||||
match fs::remove_dir_all(name) {
|
match fs::remove_dir_all(name) {
|
||||||
|
@ -177,8 +169,6 @@ impl EmptyFolder {
|
||||||
Err(e) => self.text_messages.warnings.push(format!("Failed to remove folder {}, reason {}", name.display(), e)),
|
Err(e) => self.text_messages.warnings.push(format!("Failed to remove folder {}, reason {}", name.display(), e)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "delete_files");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set included dir which needs to be relative, exists etc.
|
/// Set included dir which needs to be relative, exists etc.
|
||||||
|
@ -211,7 +201,6 @@ impl DebugPrint for EmptyFolder {
|
||||||
|
|
||||||
impl SaveResults for EmptyFolder {
|
impl SaveResults for EmptyFolder {
|
||||||
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let file_name: String = match file_name {
|
let file_name: String = match file_name {
|
||||||
"" => "results.txt".to_string(),
|
"" => "results.txt".to_string(),
|
||||||
k => k.to_string(),
|
k => k.to_string(),
|
||||||
|
@ -248,7 +237,7 @@ impl SaveResults for EmptyFolder {
|
||||||
} else {
|
} else {
|
||||||
write!(writer, "Not found any empty folders.").unwrap();
|
write!(writer, "Not found any empty folders.").unwrap();
|
||||||
}
|
}
|
||||||
Common::print_time(start_time, SystemTime::now(), "save_results_to_file");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,10 @@ use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::BufWriter;
|
use std::io::BufWriter;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use futures::channel::mpsc::UnboundedSender;
|
use futures::channel::mpsc::UnboundedSender;
|
||||||
|
|
||||||
use crate::common::Common;
|
|
||||||
use crate::common_dir_traversal::{Collect, DirTraversalBuilder, DirTraversalResult, ErrorType, FileEntry, ProgressData};
|
use crate::common_dir_traversal::{Collect, DirTraversalBuilder, DirTraversalResult, ErrorType, FileEntry, ProgressData};
|
||||||
use crate::common_directory::Directories;
|
use crate::common_directory::Directories;
|
||||||
use crate::common_extensions::Extensions;
|
use crate::common_extensions::Extensions;
|
||||||
|
@ -139,17 +137,12 @@ impl InvalidSymlinks {
|
||||||
.build()
|
.build()
|
||||||
.run();
|
.run();
|
||||||
match result {
|
match result {
|
||||||
DirTraversalResult::SuccessFiles {
|
DirTraversalResult::SuccessFiles { grouped_file_entries, warnings } => {
|
||||||
start_time,
|
|
||||||
grouped_file_entries,
|
|
||||||
warnings,
|
|
||||||
} => {
|
|
||||||
if let Some(((), invalid_symlinks)) = grouped_file_entries.into_iter().next() {
|
if let Some(((), invalid_symlinks)) = grouped_file_entries.into_iter().next() {
|
||||||
self.invalid_symlinks = invalid_symlinks;
|
self.invalid_symlinks = invalid_symlinks;
|
||||||
}
|
}
|
||||||
self.information.number_of_invalid_symlinks = self.invalid_symlinks.len();
|
self.information.number_of_invalid_symlinks = self.invalid_symlinks.len();
|
||||||
self.text_messages.warnings.extend(warnings);
|
self.text_messages.warnings.extend(warnings);
|
||||||
Common::print_time(start_time, SystemTime::now(), "check_files_name");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
DirTraversalResult::SuccessFolders { .. } => unreachable!(),
|
DirTraversalResult::SuccessFolders { .. } => unreachable!(),
|
||||||
|
@ -159,8 +152,6 @@ impl InvalidSymlinks {
|
||||||
|
|
||||||
/// Function to delete files, from filed Vector
|
/// Function to delete files, from filed Vector
|
||||||
fn delete_files(&mut self) {
|
fn delete_files(&mut self) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
|
|
||||||
match self.delete_method {
|
match self.delete_method {
|
||||||
DeleteMethod::Delete => {
|
DeleteMethod::Delete => {
|
||||||
for file_entry in &self.invalid_symlinks {
|
for file_entry in &self.invalid_symlinks {
|
||||||
|
@ -173,8 +164,6 @@ impl InvalidSymlinks {
|
||||||
//Just do nothing
|
//Just do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "delete_files");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +205,6 @@ impl DebugPrint for InvalidSymlinks {
|
||||||
|
|
||||||
impl SaveResults for InvalidSymlinks {
|
impl SaveResults for InvalidSymlinks {
|
||||||
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let file_name: String = match file_name {
|
let file_name: String = match file_name {
|
||||||
"" => "results.txt".to_string(),
|
"" => "results.txt".to_string(),
|
||||||
k => k.to_string(),
|
k => k.to_string(),
|
||||||
|
@ -258,7 +246,6 @@ impl SaveResults for InvalidSymlinks {
|
||||||
} else {
|
} else {
|
||||||
write!(writer, "Not found any invalid symlinks.").unwrap();
|
write!(writer, "Not found any invalid symlinks.").unwrap();
|
||||||
}
|
}
|
||||||
Common::print_time(start_time, SystemTime::now(), "save_results_to_file");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,7 +254,6 @@ impl PrintResults for InvalidSymlinks {
|
||||||
/// Print information's about duplicated entries
|
/// Print information's about duplicated entries
|
||||||
/// Only needed for CLI
|
/// Only needed for CLI
|
||||||
fn print_results(&self) {
|
fn print_results(&self) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
println!("Found {} invalid symlinks.\n", self.information.number_of_invalid_symlinks);
|
println!("Found {} invalid symlinks.\n", self.information.number_of_invalid_symlinks);
|
||||||
for file_entry in &self.invalid_symlinks {
|
for file_entry in &self.invalid_symlinks {
|
||||||
println!(
|
println!(
|
||||||
|
@ -280,7 +266,5 @@ impl PrintResults for InvalidSymlinks {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "print_entries");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -23,14 +23,14 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::common::get_dynamic_image_from_heic;
|
use crate::common::get_dynamic_image_from_heic;
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
check_folder_children, create_crash_message, get_dynamic_image_from_raw_image, get_number_of_threads, open_cache_folder, prepare_thread_handler_common,
|
check_folder_children, create_crash_message, get_dynamic_image_from_raw_image, get_number_of_threads, open_cache_folder, prepare_thread_handler_common,
|
||||||
send_info_and_wait_for_ending_all_threads, Common, HEIC_EXTENSIONS, IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS, RAW_IMAGE_EXTENSIONS,
|
send_info_and_wait_for_ending_all_threads, HEIC_EXTENSIONS, IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS, RAW_IMAGE_EXTENSIONS,
|
||||||
};
|
};
|
||||||
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
|
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
|
||||||
use crate::common_directory::Directories;
|
use crate::common_directory::Directories;
|
||||||
use crate::common_extensions::Extensions;
|
use crate::common_extensions::Extensions;
|
||||||
use crate::common_items::ExcludedItems;
|
use crate::common_items::ExcludedItems;
|
||||||
use crate::common_messages::Messages;
|
use crate::common_messages::Messages;
|
||||||
use crate::common_traits::{DebugPrint, PrintResults, SaveResults};
|
use crate::common_traits::{DebugPrint, PrintResults, ResultEntry, SaveResults};
|
||||||
use crate::flc;
|
use crate::flc;
|
||||||
|
|
||||||
type ImHash = Vec<u8>;
|
type ImHash = Vec<u8>;
|
||||||
|
@ -51,6 +51,11 @@ pub struct FileEntry {
|
||||||
pub hash: ImHash,
|
pub hash: ImHash,
|
||||||
pub similarity: u32,
|
pub similarity: u32,
|
||||||
}
|
}
|
||||||
|
impl ResultEntry for FileEntry {
|
||||||
|
fn get_path(&self) -> &Path {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Used by CLI tool when we cannot use directly values
|
/// Used by CLI tool when we cannot use directly values
|
||||||
#[derive(Clone, Debug, Copy)]
|
#[derive(Clone, Debug, Copy)]
|
||||||
|
@ -275,7 +280,6 @@ impl SimilarImages {
|
||||||
/// Function to check if folder are empty.
|
/// Function to check if folder are empty.
|
||||||
/// Parameter `initial_checking` for second check before deleting to be sure that checked folder is still empty
|
/// Parameter `initial_checking` for second check before deleting to be sure that checked folder is still empty
|
||||||
fn check_for_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
fn check_for_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
|
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
|
||||||
|
|
||||||
if !self.allowed_extensions.using_custom_extensions() {
|
if !self.allowed_extensions.using_custom_extensions() {
|
||||||
|
@ -296,9 +300,7 @@ impl SimilarImages {
|
||||||
folders_to_check.push(id.clone());
|
folders_to_check.push(id.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
let (progress_thread_handle, progress_thread_run, atomic_counter, _check_was_stopped) = prepare_thread_handler_common(progress_sender, 0, 2, 0, CheckingMethod::None);
|
||||||
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
|
||||||
let progress_thread_handle = prepare_thread_handler_common(progress_sender, &progress_thread_run, &atomic_counter, 0, 2, 0, CheckingMethod::None);
|
|
||||||
|
|
||||||
while !folders_to_check.is_empty() {
|
while !folders_to_check.is_empty() {
|
||||||
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
|
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
|
||||||
|
@ -355,7 +357,7 @@ impl SimilarImages {
|
||||||
}
|
}
|
||||||
|
|
||||||
send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle);
|
send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle);
|
||||||
Common::print_time(start_time, SystemTime::now(), "check_for_similar_images");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,16 +403,18 @@ impl SimilarImages {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (name, file_entry) in &self.images_to_check {
|
for (name, file_entry) in &self.images_to_check {
|
||||||
#[allow(clippy::if_same_then_else)]
|
|
||||||
if !loaded_hash_map.contains_key(name) {
|
if !loaded_hash_map.contains_key(name) {
|
||||||
// If loaded data doesn't contains current image info
|
// If loaded data doesn't contains current image info
|
||||||
non_cached_files_to_check.insert(name.clone(), file_entry.clone());
|
non_cached_files_to_check.insert(name.clone(), file_entry.clone());
|
||||||
} else if file_entry.size != loaded_hash_map.get(name).unwrap().size || file_entry.modified_date != loaded_hash_map.get(name).unwrap().modified_date {
|
} else {
|
||||||
|
let loaded_item = loaded_hash_map.get(name).unwrap();
|
||||||
|
if file_entry.size != loaded_item.size || file_entry.modified_date != loaded_item.modified_date {
|
||||||
// When size or modification date of image changed, then it is clear that is different image
|
// When size or modification date of image changed, then it is clear that is different image
|
||||||
non_cached_files_to_check.insert(name.clone(), file_entry.clone());
|
non_cached_files_to_check.insert(name.clone(), file_entry.clone());
|
||||||
} else {
|
} else {
|
||||||
// Checking may be omitted when already there is entry with same size and modification date
|
// Checking may be omitted when already there is entry with same size and modification date
|
||||||
records_already_cached.insert(name.clone(), loaded_hash_map.get(name).unwrap().clone());
|
records_already_cached.insert(name.clone(), loaded_item.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -422,31 +426,16 @@ impl SimilarImages {
|
||||||
|
|
||||||
// Cache algorithm:
|
// Cache algorithm:
|
||||||
// - Load data from file
|
// - Load data from file
|
||||||
// - Remove from data to search, already loaded entries from cache(size and modified datamust match)
|
// - Remove from data to search, already loaded entries from cache(size and modified date must match)
|
||||||
// - Check hash of files which doesn't have saved entry
|
// - Check hash of files which doesn't have saved entry
|
||||||
// - Join already read hashes with hashes which were read from file
|
// - Join already read hashes with hashes which were read from file
|
||||||
// - Join all hashes and save it to file
|
// - Join all hashes and save it to file
|
||||||
|
|
||||||
fn hash_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
fn hash_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
||||||
let hash_map_modification = SystemTime::now();
|
|
||||||
|
|
||||||
let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.hash_images_load_cache();
|
let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.hash_images_load_cache();
|
||||||
|
|
||||||
Common::print_time(hash_map_modification, SystemTime::now(), "sort_images - reading data from cache and preparing them");
|
let (progress_thread_handle, progress_thread_run, atomic_counter, check_was_stopped) =
|
||||||
let hash_map_modification = SystemTime::now();
|
prepare_thread_handler_common(progress_sender, 1, 2, non_cached_files_to_check.len(), CheckingMethod::None);
|
||||||
|
|
||||||
let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread
|
|
||||||
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
|
||||||
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
|
||||||
let progress_thread_handle = prepare_thread_handler_common(
|
|
||||||
progress_sender,
|
|
||||||
&progress_thread_run,
|
|
||||||
&atomic_counter,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
non_cached_files_to_check.len(),
|
|
||||||
CheckingMethod::None,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut vec_file_entry: Vec<(FileEntry, ImHash)> = non_cached_files_to_check
|
let mut vec_file_entry: Vec<(FileEntry, ImHash)> = non_cached_files_to_check
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
|
@ -465,11 +454,8 @@ impl SimilarImages {
|
||||||
|
|
||||||
send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle);
|
send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle);
|
||||||
|
|
||||||
Common::print_time(hash_map_modification, SystemTime::now(), "sort_images - reading data from files in parallel");
|
|
||||||
let hash_map_modification = SystemTime::now();
|
|
||||||
|
|
||||||
// Just connect loaded results with already calculated hashes
|
// Just connect loaded results with already calculated hashes
|
||||||
for (_name, file_entry) in records_already_cached {
|
for file_entry in records_already_cached.into_values() {
|
||||||
vec_file_entry.push((file_entry.clone(), file_entry.hash));
|
vec_file_entry.push((file_entry.clone(), file_entry.hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,7 +488,6 @@ impl SimilarImages {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(hash_map_modification, SystemTime::now(), "sort_images - saving data to files");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
fn collect_image_file_entry(&self, mut file_entry: FileEntry) -> (FileEntry, ImHash) {
|
fn collect_image_file_entry(&self, mut file_entry: FileEntry) -> (FileEntry, ImHash) {
|
||||||
|
@ -813,14 +798,12 @@ impl SimilarImages {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let hash_map_modification = SystemTime::now();
|
|
||||||
let tolerance = self.similarity;
|
let tolerance = self.similarity;
|
||||||
|
|
||||||
// Results
|
// Results
|
||||||
let mut collected_similar_images: HashMap<ImHash, Vec<FileEntry>> = Default::default();
|
let mut collected_similar_images: HashMap<ImHash, Vec<FileEntry>> = Default::default();
|
||||||
|
|
||||||
let mut all_hashed_images = Default::default();
|
let all_hashed_images = mem::take(&mut self.image_hashes);
|
||||||
mem::swap(&mut all_hashed_images, &mut self.image_hashes);
|
|
||||||
|
|
||||||
let all_hashes: Vec<_> = all_hashed_images.clone().into_keys().collect();
|
let all_hashes: Vec<_> = all_hashed_images.clone().into_keys().collect();
|
||||||
|
|
||||||
|
@ -832,10 +815,8 @@ impl SimilarImages {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread
|
let (progress_thread_handle, progress_thread_run, atomic_counter, check_was_stopped) =
|
||||||
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
prepare_thread_handler_common(progress_sender, 2, 2, all_hashes.len(), CheckingMethod::None);
|
||||||
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
|
||||||
let progress_thread_handle = prepare_thread_handler_common(progress_sender, &progress_thread_run, &atomic_counter, 2, 2, all_hashes.len(), CheckingMethod::None);
|
|
||||||
|
|
||||||
// Don't use hashes with multiple images in bktree, because they will always be master of group and cannot be find by other hashes
|
// Don't use hashes with multiple images in bktree, because they will always be master of group and cannot be find by other hashes
|
||||||
|
|
||||||
|
@ -874,8 +855,6 @@ impl SimilarImages {
|
||||||
|
|
||||||
self.check_for_reference_folders();
|
self.check_for_reference_folders();
|
||||||
|
|
||||||
Common::print_time(hash_map_modification, SystemTime::now(), "sort_images - selecting data from HashMap");
|
|
||||||
|
|
||||||
if self.use_reference_folders {
|
if self.use_reference_folders {
|
||||||
for (_fe, vector) in &self.similar_referenced_vectors {
|
for (_fe, vector) in &self.similar_referenced_vectors {
|
||||||
self.information.number_of_duplicates += vector.len();
|
self.information.number_of_duplicates += vector.len();
|
||||||
|
@ -888,7 +867,7 @@ impl SimilarImages {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean unused data
|
// Clean unused data to save ram
|
||||||
self.image_hashes = Default::default();
|
self.image_hashes = Default::default();
|
||||||
self.images_to_check = Default::default();
|
self.images_to_check = Default::default();
|
||||||
self.bktree = BKTree::new(Hamming);
|
self.bktree = BKTree::new(Hamming);
|
||||||
|
@ -898,9 +877,7 @@ impl SimilarImages {
|
||||||
|
|
||||||
fn exclude_items_with_same_size(&mut self) {
|
fn exclude_items_with_same_size(&mut self) {
|
||||||
if self.exclude_images_with_same_size {
|
if self.exclude_images_with_same_size {
|
||||||
let mut new_vector = Default::default();
|
for vec_file_entry in mem::take(&mut self.similar_vectors) {
|
||||||
mem::swap(&mut self.similar_vectors, &mut new_vector);
|
|
||||||
for vec_file_entry in new_vector {
|
|
||||||
let mut bt_sizes: BTreeSet<u64> = Default::default();
|
let mut bt_sizes: BTreeSet<u64> = Default::default();
|
||||||
let mut vec_values = Vec::new();
|
let mut vec_values = Vec::new();
|
||||||
for file_entry in vec_file_entry {
|
for file_entry in vec_file_entry {
|
||||||
|
@ -918,21 +895,11 @@ impl SimilarImages {
|
||||||
|
|
||||||
fn check_for_reference_folders(&mut self) {
|
fn check_for_reference_folders(&mut self) {
|
||||||
if self.use_reference_folders {
|
if self.use_reference_folders {
|
||||||
let mut similar_vector = Default::default();
|
self.similar_referenced_vectors = mem::take(&mut self.similar_vectors)
|
||||||
mem::swap(&mut self.similar_vectors, &mut similar_vector);
|
|
||||||
let reference_directories = self.directories.reference_directories.clone();
|
|
||||||
self.similar_referenced_vectors = similar_vector
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|vec_file_entry| {
|
.filter_map(|vec_file_entry| {
|
||||||
let mut files_from_referenced_folders = Vec::new();
|
let (mut files_from_referenced_folders, normal_files): (Vec<_>, Vec<_>) =
|
||||||
let mut normal_files = Vec::new();
|
vec_file_entry.into_iter().partition(|e| self.directories.is_in_referenced_directory(e.get_path()));
|
||||||
for file_entry in vec_file_entry {
|
|
||||||
if reference_directories.iter().any(|e| file_entry.path.starts_with(e)) {
|
|
||||||
files_from_referenced_folders.push(file_entry);
|
|
||||||
} else {
|
|
||||||
normal_files.push(file_entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
||||||
None
|
None
|
||||||
|
@ -1077,7 +1044,6 @@ impl DebugPrint for SimilarImages {
|
||||||
|
|
||||||
impl SaveResults for SimilarImages {
|
impl SaveResults for SimilarImages {
|
||||||
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let file_name: String = match file_name {
|
let file_name: String = match file_name {
|
||||||
"" => "results.txt".to_string(),
|
"" => "results.txt".to_string(),
|
||||||
k => k.to_string(),
|
k => k.to_string(),
|
||||||
|
@ -1123,7 +1089,6 @@ impl SaveResults for SimilarImages {
|
||||||
write!(writer, "Not found any similar images.").unwrap();
|
write!(writer, "Not found any similar images.").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "save_results_to_file");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,7 @@ use std::io::Write;
|
||||||
use std::io::*;
|
use std::io::*;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use ffmpeg_cmdline_utils::FfmpegErrorKind::FfmpegNotFound;
|
use ffmpeg_cmdline_utils::FfmpegErrorKind::FfmpegNotFound;
|
||||||
|
@ -18,14 +16,14 @@ use serde::{Deserialize, Serialize};
|
||||||
use vid_dup_finder_lib::HashCreationErrorKind::DetermineVideo;
|
use vid_dup_finder_lib::HashCreationErrorKind::DetermineVideo;
|
||||||
use vid_dup_finder_lib::{NormalizedTolerance, VideoHash};
|
use vid_dup_finder_lib::{NormalizedTolerance, VideoHash};
|
||||||
|
|
||||||
|
use crate::common::open_cache_folder;
|
||||||
use crate::common::{check_folder_children, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, VIDEO_FILES_EXTENSIONS};
|
use crate::common::{check_folder_children, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, VIDEO_FILES_EXTENSIONS};
|
||||||
use crate::common::{open_cache_folder, Common};
|
|
||||||
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
|
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
|
||||||
use crate::common_directory::Directories;
|
use crate::common_directory::Directories;
|
||||||
use crate::common_extensions::Extensions;
|
use crate::common_extensions::Extensions;
|
||||||
use crate::common_items::ExcludedItems;
|
use crate::common_items::ExcludedItems;
|
||||||
use crate::common_messages::Messages;
|
use crate::common_messages::Messages;
|
||||||
use crate::common_traits::{DebugPrint, PrintResults, SaveResults};
|
use crate::common_traits::{DebugPrint, PrintResults, ResultEntry, SaveResults};
|
||||||
use crate::flc;
|
use crate::flc;
|
||||||
use crate::localizer_core::generate_translation_hashmap;
|
use crate::localizer_core::generate_translation_hashmap;
|
||||||
|
|
||||||
|
@ -39,6 +37,11 @@ pub struct FileEntry {
|
||||||
pub vhash: VideoHash,
|
pub vhash: VideoHash,
|
||||||
pub error: String,
|
pub error: String,
|
||||||
}
|
}
|
||||||
|
impl ResultEntry for FileEntry {
|
||||||
|
fn get_path(&self) -> &Path {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Distance metric to use with the BK-tree.
|
/// Distance metric to use with the BK-tree.
|
||||||
struct Hamming;
|
struct Hamming;
|
||||||
|
@ -242,7 +245,6 @@ impl SimilarVideos {
|
||||||
/// Function to check if folder are empty.
|
/// Function to check if folder are empty.
|
||||||
/// Parameter `initial_checking` for second check before deleting to be sure that checked folder is still empty
|
/// Parameter `initial_checking` for second check before deleting to be sure that checked folder is still empty
|
||||||
fn check_for_similar_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
fn check_for_similar_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
|
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
|
||||||
|
|
||||||
if !self.allowed_extensions.using_custom_extensions() {
|
if !self.allowed_extensions.using_custom_extensions() {
|
||||||
|
@ -259,9 +261,7 @@ impl SimilarVideos {
|
||||||
folders_to_check.push(id.clone());
|
folders_to_check.push(id.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
let (progress_thread_handle, progress_thread_run, atomic_counter, _check_was_stopped) = prepare_thread_handler_common(progress_sender, 0, 1, 0, CheckingMethod::None);
|
||||||
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
|
||||||
let progress_thread_handle = prepare_thread_handler_common(progress_sender, &progress_thread_run, &atomic_counter, 0, 1, 0, CheckingMethod::None);
|
|
||||||
|
|
||||||
while !folders_to_check.is_empty() {
|
while !folders_to_check.is_empty() {
|
||||||
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
|
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
|
||||||
|
@ -319,7 +319,7 @@ impl SimilarVideos {
|
||||||
}
|
}
|
||||||
|
|
||||||
send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle);
|
send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle);
|
||||||
Common::print_time(start_time, SystemTime::now(), "check_for_similar_videos");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,16 +365,18 @@ impl SimilarVideos {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (name, file_entry) in &self.videos_to_check {
|
for (name, file_entry) in &self.videos_to_check {
|
||||||
#[allow(clippy::if_same_then_else)]
|
|
||||||
if !loaded_hash_map.contains_key(name) {
|
if !loaded_hash_map.contains_key(name) {
|
||||||
// If loaded data doesn't contains current videos info
|
// If loaded data doesn't contains current videos info
|
||||||
non_cached_files_to_check.insert(name.clone(), file_entry.clone());
|
non_cached_files_to_check.insert(name.clone(), file_entry.clone());
|
||||||
} else if file_entry.size != loaded_hash_map.get(name).unwrap().size || file_entry.modified_date != loaded_hash_map.get(name).unwrap().modified_date {
|
} else {
|
||||||
|
let loaded_item = loaded_hash_map.get(name).unwrap();
|
||||||
|
if file_entry.size != loaded_item.size || file_entry.modified_date != loaded_item.modified_date {
|
||||||
// When size or modification date of video changed, then it is clear that is different video
|
// When size or modification date of video changed, then it is clear that is different video
|
||||||
non_cached_files_to_check.insert(name.clone(), file_entry.clone());
|
non_cached_files_to_check.insert(name.clone(), file_entry.clone());
|
||||||
} else {
|
} else {
|
||||||
// Checking may be omitted when already there is entry with same size and modification date
|
// Checking may be omitted when already there is entry with same size and modification date
|
||||||
records_already_cached.insert(name.clone(), loaded_hash_map.get(name).unwrap().clone());
|
records_already_cached.insert(name.clone(), loaded_item.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -385,26 +387,10 @@ impl SimilarVideos {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sort_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
fn sort_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
||||||
let hash_map_modification = SystemTime::now();
|
|
||||||
|
|
||||||
let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.load_cache_at_start();
|
let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.load_cache_at_start();
|
||||||
|
|
||||||
Common::print_time(hash_map_modification, SystemTime::now(), "sort_videos - reading data from cache and preparing them");
|
let (progress_thread_handle, progress_thread_run, atomic_counter, check_was_stopped) =
|
||||||
let hash_map_modification = SystemTime::now();
|
prepare_thread_handler_common(progress_sender, 1, 1, non_cached_files_to_check.len(), CheckingMethod::None);
|
||||||
|
|
||||||
let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread
|
|
||||||
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
|
||||||
|
|
||||||
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
|
||||||
let progress_thread_handle = prepare_thread_handler_common(
|
|
||||||
progress_sender,
|
|
||||||
&progress_thread_run,
|
|
||||||
&atomic_counter,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
non_cached_files_to_check.len(),
|
|
||||||
CheckingMethod::None,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut vec_file_entry: Vec<FileEntry> = non_cached_files_to_check
|
let mut vec_file_entry: Vec<FileEntry> = non_cached_files_to_check
|
||||||
.par_iter()
|
.par_iter()
|
||||||
|
@ -435,13 +421,8 @@ impl SimilarVideos {
|
||||||
|
|
||||||
send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle);
|
send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle);
|
||||||
|
|
||||||
Common::print_time(hash_map_modification, SystemTime::now(), "sort_videos - reading data from files in parallel");
|
|
||||||
let hash_map_modification = SystemTime::now();
|
|
||||||
|
|
||||||
// Just connect loaded results with already calculated hashes
|
// Just connect loaded results with already calculated hashes
|
||||||
for (_name, file_entry) in records_already_cached {
|
vec_file_entry.extend(records_already_cached.into_values());
|
||||||
vec_file_entry.push(file_entry.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut hashmap_with_file_entries: HashMap<String, FileEntry> = Default::default();
|
let mut hashmap_with_file_entries: HashMap<String, FileEntry> = Default::default();
|
||||||
let mut vector_of_hashes: Vec<VideoHash> = Vec::new();
|
let mut vector_of_hashes: Vec<VideoHash> = Vec::new();
|
||||||
|
@ -469,9 +450,6 @@ impl SimilarVideos {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(hash_map_modification, SystemTime::now(), "sort_videos - saving data to files");
|
|
||||||
let hash_map_modification = SystemTime::now();
|
|
||||||
|
|
||||||
self.match_groups_of_videos(vector_of_hashes, &hashmap_with_file_entries);
|
self.match_groups_of_videos(vector_of_hashes, &hashmap_with_file_entries);
|
||||||
self.remove_from_reference_folders();
|
self.remove_from_reference_folders();
|
||||||
|
|
||||||
|
@ -487,8 +465,6 @@ impl SimilarVideos {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(hash_map_modification, SystemTime::now(), "sort_videos - selecting data from BtreeMap");
|
|
||||||
|
|
||||||
// Clean unused data
|
// Clean unused data
|
||||||
self.videos_hashes = Default::default();
|
self.videos_hashes = Default::default();
|
||||||
self.videos_to_check = Default::default();
|
self.videos_to_check = Default::default();
|
||||||
|
@ -523,21 +499,11 @@ impl SimilarVideos {
|
||||||
|
|
||||||
fn remove_from_reference_folders(&mut self) {
|
fn remove_from_reference_folders(&mut self) {
|
||||||
if self.use_reference_folders {
|
if self.use_reference_folders {
|
||||||
let mut similar_vector = Default::default();
|
self.similar_referenced_vectors = mem::take(&mut self.similar_vectors)
|
||||||
mem::swap(&mut self.similar_vectors, &mut similar_vector);
|
|
||||||
let reference_directories = self.directories.reference_directories.clone();
|
|
||||||
self.similar_referenced_vectors = similar_vector
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|vec_file_entry| {
|
.filter_map(|vec_file_entry| {
|
||||||
let mut files_from_referenced_folders = Vec::new();
|
let (mut files_from_referenced_folders, normal_files): (Vec<_>, Vec<_>) =
|
||||||
let mut normal_files = Vec::new();
|
vec_file_entry.into_iter().partition(|e| self.directories.is_in_referenced_directory(e.get_path()));
|
||||||
for file_entry in vec_file_entry {
|
|
||||||
if reference_directories.iter().any(|e| file_entry.path.starts_with(e)) {
|
|
||||||
files_from_referenced_folders.push(file_entry);
|
|
||||||
} else {
|
|
||||||
normal_files.push(file_entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
||||||
None
|
None
|
||||||
|
@ -590,7 +556,6 @@ impl DebugPrint for SimilarVideos {
|
||||||
|
|
||||||
impl SaveResults for SimilarVideos {
|
impl SaveResults for SimilarVideos {
|
||||||
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let file_name: String = match file_name {
|
let file_name: String = match file_name {
|
||||||
"" => "results.txt".to_string(),
|
"" => "results.txt".to_string(),
|
||||||
k => k.to_string(),
|
k => k.to_string(),
|
||||||
|
@ -628,7 +593,6 @@ impl SaveResults for SimilarVideos {
|
||||||
write!(writer, "Not found any similar videos.").unwrap();
|
write!(writer, "Not found any similar videos.").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "save_results_to_file");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,15 +3,14 @@ use std::fs::{DirEntry, File, Metadata};
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::BufWriter;
|
use std::io::BufWriter;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use futures::channel::mpsc::UnboundedSender;
|
use futures::channel::mpsc::UnboundedSender;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
use crate::common::{check_folder_children, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, Common};
|
use crate::common::{check_folder_children, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads};
|
||||||
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
|
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
|
||||||
use crate::common_directory::Directories;
|
use crate::common_directory::Directories;
|
||||||
use crate::common_items::ExcludedItems;
|
use crate::common_items::ExcludedItems;
|
||||||
|
@ -143,7 +142,6 @@ impl Temporary {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
|
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
|
||||||
|
|
||||||
// Add root folders for finding
|
// Add root folders for finding
|
||||||
|
@ -151,9 +149,7 @@ impl Temporary {
|
||||||
folders_to_check.push(id.clone());
|
folders_to_check.push(id.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
let (progress_thread_handle, progress_thread_run, atomic_counter, _check_was_stopped) = prepare_thread_handler_common(progress_sender, 0, 0, 0, CheckingMethod::None);
|
||||||
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
|
||||||
let progress_thread_handle = prepare_thread_handler_common(progress_sender, &progress_thread_run, &atomic_counter, 0, 0, 0, CheckingMethod::None);
|
|
||||||
|
|
||||||
while !folders_to_check.is_empty() {
|
while !folders_to_check.is_empty() {
|
||||||
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
|
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
|
||||||
|
@ -214,7 +210,6 @@ impl Temporary {
|
||||||
send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle);
|
send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle);
|
||||||
self.information.number_of_temporary_files = self.temporary_files.len();
|
self.information.number_of_temporary_files = self.temporary_files.len();
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "check_files_size");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
pub fn get_file_entry(
|
pub fn get_file_entry(
|
||||||
|
@ -248,8 +243,6 @@ impl Temporary {
|
||||||
|
|
||||||
/// Function to delete files, from filed Vector
|
/// Function to delete files, from filed Vector
|
||||||
fn delete_files(&mut self) {
|
fn delete_files(&mut self) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
|
|
||||||
match self.delete_method {
|
match self.delete_method {
|
||||||
DeleteMethod::Delete => {
|
DeleteMethod::Delete => {
|
||||||
for file_entry in &self.temporary_files {
|
for file_entry in &self.temporary_files {
|
||||||
|
@ -262,8 +255,6 @@ impl Temporary {
|
||||||
//Just do nothing
|
//Just do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "delete_files");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +295,6 @@ impl DebugPrint for Temporary {
|
||||||
|
|
||||||
impl SaveResults for Temporary {
|
impl SaveResults for Temporary {
|
||||||
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
fn save_results_to_file(&mut self, file_name: &str) -> bool {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
let file_name: String = match file_name {
|
let file_name: String = match file_name {
|
||||||
"" => "results.txt".to_string(),
|
"" => "results.txt".to_string(),
|
||||||
k => k.to_string(),
|
k => k.to_string(),
|
||||||
|
@ -336,19 +326,16 @@ impl SaveResults for Temporary {
|
||||||
} else {
|
} else {
|
||||||
write!(writer, "Not found any temporary files.").unwrap();
|
write!(writer, "Not found any temporary files.").unwrap();
|
||||||
}
|
}
|
||||||
Common::print_time(start_time, SystemTime::now(), "save_results_to_file");
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrintResults for Temporary {
|
impl PrintResults for Temporary {
|
||||||
fn print_results(&self) {
|
fn print_results(&self) {
|
||||||
let start_time: SystemTime = SystemTime::now();
|
|
||||||
println!("Found {} temporary files.\n", self.information.number_of_temporary_files);
|
println!("Found {} temporary files.\n", self.information.number_of_temporary_files);
|
||||||
for file_entry in &self.temporary_files {
|
for file_entry in &self.temporary_files {
|
||||||
println!("{}", file_entry.path.display());
|
println!("{}", file_entry.path.display());
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::print_time(start_time, SystemTime::now(), "print_entries");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,23 @@ music_bitrate_checkbox = Bitrate
|
||||||
music_genre_checkbox = Genre
|
music_genre_checkbox = Genre
|
||||||
music_length_checkbox = Length
|
music_length_checkbox = Length
|
||||||
music_comparison_checkbox = Approximate Comparison
|
music_comparison_checkbox = Approximate Comparison
|
||||||
|
music_checking_by_tags = Tags
|
||||||
|
music_checking_by_content = Content
|
||||||
|
same_music_seconds_label = Minimal fragment second duration
|
||||||
|
same_music_similarity_label = Maximum difference
|
||||||
|
|
||||||
|
same_music_tooltip =
|
||||||
|
Searching for similar music files by its content can be configured by setting:
|
||||||
|
|
||||||
|
- The minimum fragment time after which music files can be identified as similar
|
||||||
|
- The maximum difference difference between two tested fragments
|
||||||
|
|
||||||
|
The key to good results is to find sensible combinations of these parameters, for provided.
|
||||||
|
|
||||||
|
Setting the minimum time to 5s and the maximum difference to 1.0, will look for almost identical fragments in the files.
|
||||||
|
A time of 20s and a maximum difference of 6.0, on the other hand, works well for finding remixes/live versions etc.
|
||||||
|
|
||||||
|
By default, each music file is compared to each other and this can take a lot of time when testing many files, so it is usually better to use reference folders and specifying which files are to be compared with each other(with same amount of files, comparing fingerprints will be faster at least 4x than without reference folders).
|
||||||
|
|
||||||
music_comparison_checkbox_tooltip =
|
music_comparison_checkbox_tooltip =
|
||||||
It searches for similar music files using AI, which uses machine learning to remove parentheses from a phrase. For example, with this option enabled, the files in question will be considered duplicates:
|
It searches for similar music files using AI, which uses machine learning to remove parentheses from a phrase. For example, with this option enabled, the files in question will be considered duplicates:
|
||||||
|
@ -446,6 +463,8 @@ progress_scanning_image = Hashing of {$file_checked}/{$all_files} image
|
||||||
progress_comparing_image_hashes = Comparing {$file_checked}/{$all_files} image hash
|
progress_comparing_image_hashes = Comparing {$file_checked}/{$all_files} image hash
|
||||||
progress_scanning_music_tags_end = Comparing tags of {$file_checked}/{$all_files} music file
|
progress_scanning_music_tags_end = Comparing tags of {$file_checked}/{$all_files} music file
|
||||||
progress_scanning_music_tags = Reading tags of {$file_checked}/{$all_files} music file
|
progress_scanning_music_tags = Reading tags of {$file_checked}/{$all_files} music file
|
||||||
|
progress_scanning_music_content_end = Comparing fingerprint of {$file_checked}/{$all_files} music file
|
||||||
|
progress_scanning_music_content = Calculating fingerprint of {$file_checked}/{$all_files} music file
|
||||||
progress_scanning_empty_folders = Scanning {$folder_number} folder
|
progress_scanning_empty_folders = Scanning {$folder_number} folder
|
||||||
progress_scanning_size = Scanning size of {$file_number} file
|
progress_scanning_size = Scanning size of {$file_number} file
|
||||||
progress_scanning_size_name = Scanning name and size of {$file_number} file
|
progress_scanning_size_name = Scanning name and size of {$file_number} file
|
||||||
|
|
|
@ -550,7 +550,7 @@ fn computer_same_music(
|
||||||
} else {
|
} else {
|
||||||
let vector = mf.get_duplicated_music_entries();
|
let vector = mf.get_duplicated_music_entries();
|
||||||
|
|
||||||
let text: &str = "-----";
|
let text: &str = if mf.get_check_type() == CheckingMethod::AudioTags { "-----" } else { "" };
|
||||||
|
|
||||||
for vec_file_entry in vector {
|
for vec_file_entry in vector {
|
||||||
// Sort
|
// Sort
|
||||||
|
@ -1164,9 +1164,7 @@ fn computer_duplicate_finder(
|
||||||
duplicates_size = information.lost_space_by_size;
|
duplicates_size = information.lost_space_by_size;
|
||||||
duplicates_group = information.number_of_groups_by_size_name;
|
duplicates_group = information.number_of_groups_by_size_name;
|
||||||
}
|
}
|
||||||
CheckingMethod::None => {
|
_ => panic!(),
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if duplicates_size == 0 {
|
if duplicates_size == 0 {
|
||||||
entry_info.set_text(
|
entry_info.set_text(
|
||||||
|
@ -1251,9 +1249,7 @@ fn computer_duplicate_finder(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CheckingMethod::None => {
|
_ => panic!(),
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match df.get_check_method() {
|
match df.get_check_method() {
|
||||||
|
@ -1310,9 +1306,7 @@ fn computer_duplicate_finder(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CheckingMethod::None => {
|
_ => panic!(),
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print_text_messages_to_text_view(text_messages, text_view_errors);
|
print_text_messages_to_text_view(text_messages, text_view_errors);
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,14 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use futures::channel::mpsc::UnboundedReceiver;
|
use futures::channel::mpsc::UnboundedReceiver;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
use glib::MainContext;
|
||||||
use gtk4::prelude::*;
|
use gtk4::prelude::*;
|
||||||
|
use gtk4::ProgressBar;
|
||||||
|
|
||||||
|
use common_dir_traversal::CheckingMethod;
|
||||||
use czkawka_core::common_dir_traversal;
|
use czkawka_core::common_dir_traversal;
|
||||||
use czkawka_core::common_dir_traversal::ProgressData;
|
use czkawka_core::common_dir_traversal::ProgressData;
|
||||||
|
|
||||||
|
@ -9,151 +16,50 @@ use crate::flg;
|
||||||
use crate::gui_structs::gui_data::GuiData;
|
use crate::gui_structs::gui_data::GuiData;
|
||||||
use crate::localizer_core::generate_translation_hashmap;
|
use crate::localizer_core::generate_translation_hashmap;
|
||||||
use crate::taskbar_progress::tbp_flags::TBPF_INDETERMINATE;
|
use crate::taskbar_progress::tbp_flags::TBPF_INDETERMINATE;
|
||||||
|
use crate::taskbar_progress::TaskbarProgress;
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn connect_progress_window(
|
pub fn connect_progress_window(
|
||||||
gui_data: &GuiData,
|
gui_data: &GuiData,
|
||||||
mut futures_receiver_duplicate_files: UnboundedReceiver<ProgressData>,
|
futures_receiver_duplicate_files: UnboundedReceiver<ProgressData>,
|
||||||
mut futures_receiver_empty_files: UnboundedReceiver<ProgressData>,
|
futures_receiver_empty_files: UnboundedReceiver<ProgressData>,
|
||||||
mut futures_receiver_empty_folder: UnboundedReceiver<ProgressData>,
|
futures_receiver_empty_folder: UnboundedReceiver<ProgressData>,
|
||||||
mut futures_receiver_big_files: UnboundedReceiver<ProgressData>,
|
futures_receiver_big_files: UnboundedReceiver<ProgressData>,
|
||||||
mut futures_receiver_same_music: UnboundedReceiver<ProgressData>,
|
futures_receiver_same_music: UnboundedReceiver<ProgressData>,
|
||||||
mut futures_receiver_similar_images: UnboundedReceiver<ProgressData>,
|
futures_receiver_similar_images: UnboundedReceiver<ProgressData>,
|
||||||
mut futures_receiver_similar_videos: UnboundedReceiver<ProgressData>,
|
futures_receiver_similar_videos: UnboundedReceiver<ProgressData>,
|
||||||
mut futures_receiver_temporary: UnboundedReceiver<ProgressData>,
|
futures_receiver_temporary: UnboundedReceiver<ProgressData>,
|
||||||
mut futures_receiver_invalid_symlinks: UnboundedReceiver<ProgressData>,
|
futures_receiver_invalid_symlinks: UnboundedReceiver<ProgressData>,
|
||||||
mut futures_receiver_broken_files: UnboundedReceiver<ProgressData>,
|
futures_receiver_broken_files: UnboundedReceiver<ProgressData>,
|
||||||
mut futures_receiver_bad_extensions: UnboundedReceiver<ProgressData>,
|
futures_receiver_bad_extensions: UnboundedReceiver<ProgressData>,
|
||||||
) {
|
) {
|
||||||
let main_context = glib::MainContext::default();
|
let main_context = MainContext::default();
|
||||||
let _guard = main_context.acquire().unwrap();
|
let _guard = main_context.acquire().unwrap();
|
||||||
|
|
||||||
{
|
process_bar_duplicates(gui_data, &main_context, futures_receiver_duplicate_files);
|
||||||
// Duplicate Files
|
process_bar_empty_files(gui_data, &main_context, futures_receiver_empty_files);
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
process_bar_empty_folder(gui_data, &main_context, futures_receiver_empty_folder);
|
||||||
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
process_bar_big_files(gui_data, &main_context, futures_receiver_big_files);
|
||||||
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
process_bar_same_music(gui_data, &main_context, futures_receiver_same_music);
|
||||||
let grid_progress_stages = gui_data.progress_window.grid_progress_stages.clone();
|
process_bar_similar_images(gui_data, &main_context, futures_receiver_similar_images);
|
||||||
let taskbar_state = gui_data.taskbar_state.clone();
|
process_bar_similar_videos(gui_data, &main_context, futures_receiver_similar_videos);
|
||||||
let future = async move {
|
process_bar_temporary(gui_data, &main_context, futures_receiver_temporary);
|
||||||
while let Some(item) = futures_receiver_duplicate_files.next().await {
|
process_bar_invalid_symlinks(gui_data, &main_context, futures_receiver_invalid_symlinks);
|
||||||
match item.checking_method {
|
process_bar_broken_files(gui_data, &main_context, futures_receiver_broken_files);
|
||||||
common_dir_traversal::CheckingMethod::Hash => {
|
process_bar_bad_extensions(gui_data, &main_context, futures_receiver_bad_extensions);
|
||||||
label_stage.show();
|
}
|
||||||
match item.current_stage {
|
fn process_bar_empty_files(gui_data: &GuiData, main_context: &MainContext, mut futures_receiver_empty_files: UnboundedReceiver<ProgressData>) {
|
||||||
// Checking Size
|
|
||||||
0 => {
|
|
||||||
progress_bar_current_stage.hide();
|
|
||||||
// progress_bar_all_stages.hide();
|
|
||||||
progress_bar_all_stages.set_fraction(0 as f64);
|
|
||||||
label_stage.set_text(&flg!(
|
|
||||||
"progress_scanning_size",
|
|
||||||
generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
|
|
||||||
));
|
|
||||||
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
|
||||||
}
|
|
||||||
// Hash - first 1KB file
|
|
||||||
1 => {
|
|
||||||
progress_bar_current_stage.show();
|
|
||||||
// progress_bar_all_stages.show();
|
|
||||||
if item.entries_to_check != 0 {
|
|
||||||
progress_bar_all_stages.set_fraction((1f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64);
|
|
||||||
progress_bar_current_stage.set_fraction((item.entries_checked) as f64 / item.entries_to_check as f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(
|
|
||||||
(item.entries_to_check + item.entries_checked) as u64,
|
|
||||||
item.entries_to_check as u64 * (item.max_stage + 1) as u64,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(1, 1 + item.max_stage as u64);
|
|
||||||
}
|
|
||||||
|
|
||||||
label_stage.set_text(&flg!(
|
|
||||||
"progress_analyzed_partial_hash",
|
|
||||||
generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())])
|
|
||||||
));
|
|
||||||
}
|
|
||||||
// Hash - normal hash
|
|
||||||
2 => {
|
|
||||||
if item.entries_to_check != 0 {
|
|
||||||
progress_bar_all_stages.set_fraction((2f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64);
|
|
||||||
progress_bar_current_stage.set_fraction((item.entries_checked) as f64 / item.entries_to_check as f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(
|
|
||||||
(2 * item.entries_to_check + item.entries_checked) as u64,
|
|
||||||
item.entries_to_check as u64 * (item.max_stage + 1) as u64,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
progress_bar_all_stages.set_fraction((2f64) / (item.max_stage + 1) as f64);
|
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(2, 1 + item.max_stage as u64);
|
|
||||||
}
|
|
||||||
|
|
||||||
label_stage.set_text(&flg!(
|
|
||||||
"progress_analyzed_full_hash",
|
|
||||||
generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())])
|
|
||||||
));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!("Not available current_stage");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
common_dir_traversal::CheckingMethod::Name => {
|
|
||||||
label_stage.show();
|
|
||||||
grid_progress_stages.hide();
|
|
||||||
|
|
||||||
label_stage.set_text(&flg!(
|
|
||||||
"progress_scanning_name",
|
|
||||||
generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
|
|
||||||
));
|
|
||||||
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
|
||||||
}
|
|
||||||
common_dir_traversal::CheckingMethod::SizeName => {
|
|
||||||
label_stage.show();
|
|
||||||
grid_progress_stages.hide();
|
|
||||||
|
|
||||||
label_stage.set_text(&flg!(
|
|
||||||
"progress_scanning_size_name",
|
|
||||||
generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
|
|
||||||
));
|
|
||||||
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
|
||||||
}
|
|
||||||
common_dir_traversal::CheckingMethod::Size => {
|
|
||||||
label_stage.show();
|
|
||||||
grid_progress_stages.hide();
|
|
||||||
|
|
||||||
label_stage.set_text(&flg!(
|
|
||||||
"progress_scanning_size",
|
|
||||||
generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
|
|
||||||
));
|
|
||||||
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
|
||||||
}
|
|
||||||
common_dir_traversal::CheckingMethod::None => {
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
main_context.spawn_local(future);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// Empty Files
|
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
let taskbar_state = gui_data.taskbar_state.clone();
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
while let Some(item) = futures_receiver_empty_files.next().await {
|
while let Some(item) = futures_receiver_empty_files.next().await {
|
||||||
label_stage.set_text(&flg!(
|
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(&item)));
|
||||||
"progress_scanning_general_file",
|
|
||||||
generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
|
|
||||||
));
|
|
||||||
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
}
|
}
|
||||||
{
|
fn process_bar_empty_folder(gui_data: &GuiData, main_context: &MainContext, mut futures_receiver_empty_folder: UnboundedReceiver<ProgressData>) {
|
||||||
// Empty Folder
|
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
let taskbar_state = gui_data.taskbar_state.clone();
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
|
@ -166,24 +72,19 @@ pub fn connect_progress_window(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
}
|
}
|
||||||
{
|
fn process_bar_big_files(gui_data: &GuiData, main_context: &MainContext, mut futures_receiver_big_files: UnboundedReceiver<ProgressData>) {
|
||||||
// Big Files
|
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
let taskbar_state = gui_data.taskbar_state.clone();
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
while let Some(item) = futures_receiver_big_files.next().await {
|
while let Some(item) = futures_receiver_big_files.next().await {
|
||||||
label_stage.set_text(&flg!(
|
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(&item)));
|
||||||
"progress_scanning_general_file",
|
|
||||||
generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
|
|
||||||
));
|
|
||||||
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
}
|
}
|
||||||
{
|
fn process_bar_same_music(gui_data: &GuiData, main_context: &MainContext, mut futures_receiver_same_music: UnboundedReceiver<ProgressData>) {
|
||||||
// Same Music
|
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
||||||
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
||||||
|
@ -193,59 +94,43 @@ pub fn connect_progress_window(
|
||||||
match item.current_stage {
|
match item.current_stage {
|
||||||
0 => {
|
0 => {
|
||||||
progress_bar_current_stage.hide();
|
progress_bar_current_stage.hide();
|
||||||
label_stage.set_text(&flg!(
|
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(&item)));
|
||||||
"progress_scanning_general_file",
|
|
||||||
generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
|
|
||||||
));
|
|
||||||
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
progress_bar_current_stage.show();
|
progress_bar_current_stage.show();
|
||||||
if item.entries_to_check != 0 {
|
common_set_data(&item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
|
||||||
progress_bar_all_stages.set_fraction((1f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64);
|
|
||||||
progress_bar_current_stage.set_fraction((item.entries_checked) as f64 / item.entries_to_check as f64);
|
match item.checking_method {
|
||||||
taskbar_state.borrow().set_progress_value(
|
CheckingMethod::AudioTags => label_stage.set_text(&flg!("progress_scanning_music_tags", progress_ratio_tm(&item))),
|
||||||
(item.entries_to_check + item.entries_checked) as u64,
|
CheckingMethod::AudioContent => label_stage.set_text(&flg!("progress_scanning_music_content", progress_ratio_tm(&item))),
|
||||||
item.entries_to_check as u64 * (item.max_stage + 1) as u64,
|
_ => panic!(),
|
||||||
);
|
|
||||||
} else {
|
|
||||||
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(1, (item.max_stage + 1) as u64);
|
|
||||||
}
|
}
|
||||||
label_stage.set_text(&flg!(
|
|
||||||
"progress_scanning_music_tags",
|
|
||||||
generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())])
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
2 => {
|
2 => {
|
||||||
if item.entries_to_check != 0 {
|
common_set_data(&item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
|
||||||
progress_bar_all_stages.set_fraction((2f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64);
|
|
||||||
progress_bar_current_stage.set_fraction((item.entries_checked) as f64 / item.entries_to_check as f64);
|
match item.checking_method {
|
||||||
taskbar_state.borrow().set_progress_value(
|
CheckingMethod::AudioTags => label_stage.set_text(&flg!("progress_scanning_music_tags_end", progress_ratio_tm(&item))),
|
||||||
(2 * item.entries_to_check + item.entries_checked) as u64,
|
CheckingMethod::AudioContent => label_stage.set_text(&flg!("progress_scanning_music_content_end", progress_ratio_tm(&item))),
|
||||||
item.entries_to_check as u64 * (item.max_stage + 1) as u64,
|
_ => panic!(),
|
||||||
);
|
|
||||||
} else {
|
|
||||||
progress_bar_all_stages.set_fraction((2f64) / (item.max_stage + 1) as f64);
|
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(2, (item.max_stage + 1) as u64);
|
|
||||||
}
|
}
|
||||||
label_stage.set_text(&flg!(
|
|
||||||
"progress_scanning_music_tags_end",
|
|
||||||
generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())])
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
_ => {
|
3 => {
|
||||||
panic!();
|
common_set_data(&item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
|
||||||
|
|
||||||
|
match item.checking_method {
|
||||||
|
CheckingMethod::AudioContent => label_stage.set_text(&flg!("progress_scanning_music_tags", progress_ratio_tm(&item))),
|
||||||
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
}
|
}
|
||||||
{
|
fn process_bar_similar_images(gui_data: &GuiData, main_context: &MainContext, mut futures_receiver_similar_images: UnboundedReceiver<ProgressData>) {
|
||||||
// Similar Images
|
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
||||||
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
||||||
|
@ -255,60 +140,26 @@ pub fn connect_progress_window(
|
||||||
match item.current_stage {
|
match item.current_stage {
|
||||||
0 => {
|
0 => {
|
||||||
progress_bar_current_stage.hide();
|
progress_bar_current_stage.hide();
|
||||||
label_stage.set_text(&flg!(
|
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(&item)));
|
||||||
"progress_scanning_general_file",
|
|
||||||
generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
|
|
||||||
));
|
|
||||||
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
progress_bar_current_stage.show();
|
progress_bar_current_stage.show();
|
||||||
if item.entries_to_check != 0 {
|
common_set_data(&item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
|
||||||
progress_bar_all_stages.set_fraction((1f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64);
|
label_stage.set_text(&flg!("progress_scanning_image", progress_ratio_tm(&item)));
|
||||||
progress_bar_current_stage.set_fraction((item.entries_checked) as f64 / item.entries_to_check as f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(
|
|
||||||
(item.entries_to_check + item.entries_checked) as u64,
|
|
||||||
item.entries_to_check as u64 * (item.max_stage + 1) as u64,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
progress_bar_all_stages.set_fraction((item.current_stage as f64) / (item.max_stage + 1) as f64);
|
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(1, (item.max_stage + 1) as u64);
|
|
||||||
}
|
|
||||||
label_stage.set_text(&flg!(
|
|
||||||
"progress_scanning_image",
|
|
||||||
generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())])
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
2 => {
|
2 => {
|
||||||
progress_bar_current_stage.show();
|
progress_bar_current_stage.show();
|
||||||
if item.entries_to_check != 0 {
|
common_set_data(&item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
|
||||||
progress_bar_all_stages.set_fraction((2f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64);
|
label_stage.set_text(&flg!("progress_comparing_image_hashes", progress_ratio_tm(&item)));
|
||||||
progress_bar_current_stage.set_fraction((item.entries_checked) as f64 / item.entries_to_check as f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(
|
|
||||||
(item.entries_to_check + item.entries_checked) as u64,
|
|
||||||
item.entries_to_check as u64 * (item.max_stage + 1) as u64,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
progress_bar_all_stages.set_fraction((item.current_stage as f64) / (item.max_stage + 1) as f64);
|
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(2, (item.max_stage + 1) as u64);
|
|
||||||
}
|
|
||||||
label_stage.set_text(&flg!(
|
|
||||||
"progress_comparing_image_hashes",
|
|
||||||
generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())])
|
|
||||||
));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!();
|
|
||||||
}
|
}
|
||||||
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
}
|
}
|
||||||
{
|
fn process_bar_similar_videos(gui_data: &GuiData, main_context: &MainContext, mut futures_receiver_similar_videos: UnboundedReceiver<ProgressData>) {
|
||||||
// Similar Videos
|
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
||||||
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
||||||
|
@ -318,71 +169,43 @@ pub fn connect_progress_window(
|
||||||
match item.current_stage {
|
match item.current_stage {
|
||||||
0 => {
|
0 => {
|
||||||
progress_bar_current_stage.hide();
|
progress_bar_current_stage.hide();
|
||||||
label_stage.set_text(&flg!(
|
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(&item)));
|
||||||
"progress_scanning_general_file",
|
|
||||||
generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
|
|
||||||
));
|
|
||||||
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
progress_bar_current_stage.show();
|
progress_bar_current_stage.show();
|
||||||
if item.entries_to_check != 0 {
|
common_set_data(&item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
|
||||||
progress_bar_all_stages.set_fraction((1f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64);
|
label_stage.set_text(&flg!("progress_scanning_video", progress_ratio_tm(&item)));
|
||||||
progress_bar_current_stage.set_fraction((item.entries_checked) as f64 / item.entries_to_check as f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(
|
|
||||||
(item.entries_to_check + item.entries_checked) as u64,
|
|
||||||
item.entries_to_check as u64 * (item.max_stage + 1) as u64,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(1, (item.max_stage + 1) as u64);
|
|
||||||
}
|
|
||||||
label_stage.set_text(&flg!(
|
|
||||||
"progress_scanning_video",
|
|
||||||
generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())])
|
|
||||||
));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!();
|
|
||||||
}
|
}
|
||||||
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
}
|
}
|
||||||
{
|
fn process_bar_temporary(gui_data: &GuiData, main_context: &MainContext, mut futures_receiver_temporary: UnboundedReceiver<ProgressData>) {
|
||||||
// Temporary
|
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
let taskbar_state = gui_data.taskbar_state.clone();
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
while let Some(item) = futures_receiver_temporary.next().await {
|
while let Some(item) = futures_receiver_temporary.next().await {
|
||||||
label_stage.set_text(&flg!(
|
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(&item)));
|
||||||
"progress_scanning_general_file",
|
|
||||||
generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
|
|
||||||
));
|
|
||||||
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
}
|
}
|
||||||
{
|
fn process_bar_invalid_symlinks(gui_data: &GuiData, main_context: &MainContext, mut futures_receiver_invalid_symlinks: UnboundedReceiver<ProgressData>) {
|
||||||
// Invalid Symlinks
|
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
let taskbar_state = gui_data.taskbar_state.clone();
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
while let Some(item) = futures_receiver_invalid_symlinks.next().await {
|
while let Some(item) = futures_receiver_invalid_symlinks.next().await {
|
||||||
label_stage.set_text(&flg!(
|
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(&item)));
|
||||||
"progress_scanning_general_file",
|
|
||||||
generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
|
|
||||||
));
|
|
||||||
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
}
|
}
|
||||||
{
|
fn process_bar_broken_files(gui_data: &GuiData, main_context: &MainContext, mut futures_receiver_broken_files: UnboundedReceiver<ProgressData>) {
|
||||||
// Broken Files
|
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
||||||
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
||||||
|
@ -392,41 +215,21 @@ pub fn connect_progress_window(
|
||||||
match item.current_stage {
|
match item.current_stage {
|
||||||
0 => {
|
0 => {
|
||||||
progress_bar_current_stage.hide();
|
progress_bar_current_stage.hide();
|
||||||
label_stage.set_text(&flg!(
|
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(&item)));
|
||||||
"progress_scanning_general_file",
|
|
||||||
generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
|
|
||||||
));
|
|
||||||
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
progress_bar_current_stage.show();
|
progress_bar_current_stage.show();
|
||||||
if item.entries_to_check != 0 {
|
common_set_data(&item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
|
||||||
progress_bar_all_stages.set_fraction((1f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64);
|
label_stage.set_text(&flg!("progress_scanning_broken_files", progress_ratio_tm(&item)));
|
||||||
progress_bar_current_stage.set_fraction((item.entries_checked) as f64 / item.entries_to_check as f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(
|
|
||||||
(item.entries_to_check + item.entries_checked) as u64,
|
|
||||||
item.entries_to_check as u64 * (item.max_stage + 1) as u64,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(1, (item.max_stage + 1) as u64);
|
|
||||||
}
|
|
||||||
label_stage.set_text(&flg!(
|
|
||||||
"progress_scanning_broken_files",
|
|
||||||
generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())])
|
|
||||||
));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!();
|
|
||||||
}
|
}
|
||||||
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
}
|
}
|
||||||
{
|
fn process_bar_bad_extensions(gui_data: &GuiData, main_context: &MainContext, mut futures_receiver_bad_extensions: UnboundedReceiver<ProgressData>) {
|
||||||
// Broken Files
|
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
||||||
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
||||||
|
@ -436,37 +239,104 @@ pub fn connect_progress_window(
|
||||||
match item.current_stage {
|
match item.current_stage {
|
||||||
0 => {
|
0 => {
|
||||||
progress_bar_current_stage.hide();
|
progress_bar_current_stage.hide();
|
||||||
label_stage.set_text(&flg!(
|
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(&item)));
|
||||||
"progress_scanning_general_file",
|
|
||||||
generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
|
|
||||||
));
|
|
||||||
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
progress_bar_current_stage.show();
|
progress_bar_current_stage.show();
|
||||||
if item.entries_to_check != 0 {
|
common_set_data(&item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
|
||||||
progress_bar_all_stages.set_fraction((1f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64);
|
label_stage.set_text(&flg!("progress_scanning_extension_of_files", progress_ratio_tm(&item)));
|
||||||
progress_bar_current_stage.set_fraction((item.entries_checked) as f64 / item.entries_to_check as f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(
|
|
||||||
(item.entries_to_check + item.entries_checked) as u64,
|
|
||||||
item.entries_to_check as u64 * (item.max_stage + 1) as u64,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
|
||||||
taskbar_state.borrow().set_progress_value(1, (item.max_stage + 1) as u64);
|
|
||||||
}
|
|
||||||
label_stage.set_text(&flg!(
|
|
||||||
"progress_scanning_extension_of_files",
|
|
||||||
generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())])
|
|
||||||
));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!();
|
|
||||||
}
|
}
|
||||||
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
|
}
|
||||||
|
fn process_bar_duplicates(gui_data: &GuiData, main_context: &MainContext, mut futures_receiver_duplicate_files: UnboundedReceiver<ProgressData>) {
|
||||||
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
|
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
||||||
|
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
||||||
|
let grid_progress_stages = gui_data.progress_window.grid_progress_stages.clone();
|
||||||
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
|
let future = async move {
|
||||||
|
while let Some(item) = futures_receiver_duplicate_files.next().await {
|
||||||
|
match item.checking_method {
|
||||||
|
CheckingMethod::Hash => {
|
||||||
|
label_stage.show();
|
||||||
|
match item.current_stage {
|
||||||
|
// Checking Size
|
||||||
|
0 => {
|
||||||
|
progress_bar_current_stage.hide();
|
||||||
|
// progress_bar_all_stages.hide();
|
||||||
|
progress_bar_all_stages.set_fraction(0 as f64);
|
||||||
|
label_stage.set_text(&flg!("progress_scanning_size", file_number_tm(&item)));
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
|
}
|
||||||
|
// Hash - first 1KB file
|
||||||
|
1 => {
|
||||||
|
progress_bar_current_stage.show();
|
||||||
|
// progress_bar_all_stages.show();
|
||||||
|
common_set_data(&item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
|
||||||
|
|
||||||
|
label_stage.set_text(&flg!("progress_analyzed_partial_hash", progress_ratio_tm(&item)));
|
||||||
|
}
|
||||||
|
// Hash - normal hash
|
||||||
|
2 => {
|
||||||
|
common_set_data(&item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
|
||||||
|
label_stage.set_text(&flg!("progress_analyzed_full_hash", progress_ratio_tm(&item)));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Not available current_stage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CheckingMethod::Name => {
|
||||||
|
label_stage.show();
|
||||||
|
grid_progress_stages.hide();
|
||||||
|
|
||||||
|
label_stage.set_text(&flg!("progress_scanning_name", file_number_tm(&item)));
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
|
}
|
||||||
|
CheckingMethod::SizeName => {
|
||||||
|
label_stage.show();
|
||||||
|
grid_progress_stages.hide();
|
||||||
|
|
||||||
|
label_stage.set_text(&flg!("progress_scanning_size_name", file_number_tm(&item)));
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
|
}
|
||||||
|
CheckingMethod::Size => {
|
||||||
|
label_stage.show();
|
||||||
|
grid_progress_stages.hide();
|
||||||
|
|
||||||
|
label_stage.set_text(&flg!("progress_scanning_size", file_number_tm(&item)));
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
|
}
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
main_context.spawn_local(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn common_set_data(item: &ProgressData, progress_bar_all_stages: &ProgressBar, progress_bar_current_stage: &ProgressBar, taskbar_state: &Rc<RefCell<TaskbarProgress>>) {
|
||||||
|
if item.entries_to_check != 0 {
|
||||||
|
progress_bar_all_stages.set_fraction((item.current_stage as f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64);
|
||||||
|
progress_bar_current_stage.set_fraction((item.entries_checked) as f64 / item.entries_to_check as f64);
|
||||||
|
taskbar_state.borrow().set_progress_value(
|
||||||
|
((item.current_stage as usize) * item.entries_to_check + item.entries_checked) as u64,
|
||||||
|
item.entries_to_check as u64 * (item.max_stage + 1) as u64,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
progress_bar_all_stages.set_fraction((item.current_stage as f64) / (item.max_stage + 1) as f64);
|
||||||
|
progress_bar_current_stage.set_fraction(0f64);
|
||||||
|
taskbar_state.borrow().set_progress_value(item.current_stage as u64, 1 + item.max_stage as u64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn file_number_tm(item: &ProgressData) -> HashMap<&'static str, String> {
|
||||||
|
generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
|
||||||
|
}
|
||||||
|
fn progress_ratio_tm(item: &ProgressData) -> HashMap<&'static str, String> {
|
||||||
|
generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())])
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
use gtk4::prelude::*;
|
||||||
|
use gtk4::{CheckButton, Widget};
|
||||||
|
|
||||||
|
use czkawka_core::common_dir_traversal::CheckingMethod;
|
||||||
|
|
||||||
|
use crate::gui_structs::gui_data::GuiData;
|
||||||
|
use crate::help_combo_box::AUDIO_TYPE_CHECK_METHOD_COMBO_BOX;
|
||||||
|
use crate::help_functions::scale_set_min_max_values;
|
||||||
|
|
||||||
|
const MINIMUM_SECONDS: f64 = 0.5;
|
||||||
|
const MAXIMUM_SECONDS: f64 = 180.0;
|
||||||
|
const DEFAULT_SECONDS: f64 = 15.0;
|
||||||
|
const MINIMUM_SIMILARITY: f64 = 0.0;
|
||||||
|
const MAXIMUM_SIMILARITY: f64 = 10.0;
|
||||||
|
const DEFAULT_SIMILARITY: f64 = 5.0;
|
||||||
|
|
||||||
|
pub fn connect_same_music_change_mode(gui_data: &GuiData) {
|
||||||
|
let check_button_music_title = gui_data.main_notebook.check_button_music_title.clone();
|
||||||
|
let check_button_music_approximate_comparison = gui_data.main_notebook.check_button_music_approximate_comparison.clone();
|
||||||
|
let check_button_music_bitrate = gui_data.main_notebook.check_button_music_bitrate.clone();
|
||||||
|
let check_button_music_artist = gui_data.main_notebook.check_button_music_artist.clone();
|
||||||
|
let check_button_music_genre = gui_data.main_notebook.check_button_music_genre.clone();
|
||||||
|
let check_button_music_length = gui_data.main_notebook.check_button_music_length.clone();
|
||||||
|
let check_button_music_year = gui_data.main_notebook.check_button_music_year.clone();
|
||||||
|
let buttons = [
|
||||||
|
check_button_music_title,
|
||||||
|
check_button_music_approximate_comparison,
|
||||||
|
check_button_music_bitrate,
|
||||||
|
check_button_music_artist,
|
||||||
|
check_button_music_genre,
|
||||||
|
check_button_music_year,
|
||||||
|
check_button_music_length,
|
||||||
|
];
|
||||||
|
|
||||||
|
let scale_seconds_same_music = gui_data.main_notebook.scale_seconds_same_music.clone();
|
||||||
|
let scale_similarity_same_music = gui_data.main_notebook.scale_similarity_same_music.clone();
|
||||||
|
let label_same_music_similarity = gui_data.main_notebook.label_same_music_similarity.clone();
|
||||||
|
let label_same_music_seconds = gui_data.main_notebook.label_same_music_seconds.clone();
|
||||||
|
|
||||||
|
scale_set_min_max_values(&scale_seconds_same_music, MINIMUM_SECONDS, MAXIMUM_SECONDS, DEFAULT_SECONDS, None);
|
||||||
|
scale_set_min_max_values(&scale_similarity_same_music, MINIMUM_SIMILARITY, MAXIMUM_SIMILARITY, DEFAULT_SIMILARITY, None);
|
||||||
|
|
||||||
|
let scales_and_labels = [
|
||||||
|
scale_seconds_same_music.into(),
|
||||||
|
scale_similarity_same_music.into(),
|
||||||
|
label_same_music_similarity.into(),
|
||||||
|
label_same_music_seconds.into(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let combo_box_audio_check_type = gui_data.main_notebook.combo_box_audio_check_type.clone();
|
||||||
|
|
||||||
|
let check_method_index = combo_box_audio_check_type.active().unwrap() as usize;
|
||||||
|
let check_method = AUDIO_TYPE_CHECK_METHOD_COMBO_BOX[check_method_index].check_method;
|
||||||
|
|
||||||
|
disable_enable_buttons(&buttons, &scales_and_labels, check_method);
|
||||||
|
combo_box_audio_check_type.connect_changed(move |combo_box_text| {
|
||||||
|
if let Some(active) = combo_box_text.active() {
|
||||||
|
let check_method = AUDIO_TYPE_CHECK_METHOD_COMBO_BOX[active as usize].check_method;
|
||||||
|
|
||||||
|
disable_enable_buttons(&buttons, &scales_and_labels, check_method);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disable_enable_buttons(buttons: &[CheckButton; 7], scales: &[Widget; 4], current_mode: CheckingMethod) {
|
||||||
|
match current_mode {
|
||||||
|
CheckingMethod::AudioTags => {
|
||||||
|
buttons.iter().for_each(WidgetExt::show);
|
||||||
|
scales.iter().for_each(WidgetExt::hide);
|
||||||
|
}
|
||||||
|
CheckingMethod::AudioContent => {
|
||||||
|
buttons.iter().for_each(WidgetExt::hide);
|
||||||
|
scales.iter().for_each(WidgetExt::show);
|
||||||
|
}
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ pub mod connect_notebook_tabs;
|
||||||
pub mod connect_popovers_select;
|
pub mod connect_popovers_select;
|
||||||
pub mod connect_popovers_sort;
|
pub mod connect_popovers_sort;
|
||||||
pub mod connect_progress_window;
|
pub mod connect_progress_window;
|
||||||
|
pub mod connect_same_music_mode_changed;
|
||||||
pub mod connect_selection_of_directories;
|
pub mod connect_selection_of_directories;
|
||||||
pub mod connect_settings;
|
pub mod connect_settings;
|
||||||
pub mod connect_show_hide_ui;
|
pub mod connect_show_hide_ui;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use czkawka_core::localizer_core::{fnc_get_similarity_minimal, fnc_get_similarit
|
||||||
use czkawka_core::similar_images::{get_string_from_similarity, SIMILAR_VALUES};
|
use czkawka_core::similar_images::{get_string_from_similarity, SIMILAR_VALUES};
|
||||||
|
|
||||||
use crate::flg;
|
use crate::flg;
|
||||||
use crate::help_combo_box::{BIG_FILES_CHECK_METHOD_COMBO_BOX, DUPLICATES_CHECK_METHOD_COMBO_BOX, IMAGES_HASH_SIZE_COMBO_BOX};
|
use crate::help_combo_box::{AUDIO_TYPE_CHECK_METHOD_COMBO_BOX, BIG_FILES_CHECK_METHOD_COMBO_BOX, DUPLICATES_CHECK_METHOD_COMBO_BOX, IMAGES_HASH_SIZE_COMBO_BOX};
|
||||||
use crate::help_functions::get_all_direct_children;
|
use crate::help_functions::get_all_direct_children;
|
||||||
use crate::notebook_enums::{NotebookMainEnum, NUMBER_OF_NOTEBOOK_MAIN_TABS};
|
use crate::notebook_enums::{NotebookMainEnum, NUMBER_OF_NOTEBOOK_MAIN_TABS};
|
||||||
|
|
||||||
|
@ -121,6 +121,12 @@ pub struct GuiMainNotebook {
|
||||||
pub check_button_music_genre: CheckButton,
|
pub check_button_music_genre: CheckButton,
|
||||||
pub check_button_music_length: CheckButton,
|
pub check_button_music_length: CheckButton,
|
||||||
pub check_button_music_approximate_comparison: CheckButton,
|
pub check_button_music_approximate_comparison: CheckButton,
|
||||||
|
pub label_audio_check_type: Label,
|
||||||
|
pub combo_box_audio_check_type: ComboBoxText,
|
||||||
|
pub label_same_music_seconds: Label,
|
||||||
|
pub label_same_music_similarity: Label,
|
||||||
|
pub scale_seconds_same_music: Scale,
|
||||||
|
pub scale_similarity_same_music: Scale,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GuiMainNotebook {
|
impl GuiMainNotebook {
|
||||||
|
@ -247,6 +253,13 @@ impl GuiMainNotebook {
|
||||||
let image_preview_similar_images: Image = builder.object("image_preview_similar_images").unwrap();
|
let image_preview_similar_images: Image = builder.object("image_preview_similar_images").unwrap();
|
||||||
let image_preview_duplicates: Image = builder.object("image_preview_duplicates").unwrap();
|
let image_preview_duplicates: Image = builder.object("image_preview_duplicates").unwrap();
|
||||||
|
|
||||||
|
let label_audio_check_type: Label = builder.object("label_audio_check_type").unwrap();
|
||||||
|
let combo_box_audio_check_type: ComboBoxText = builder.object("combo_box_audio_check_type").unwrap();
|
||||||
|
let label_same_music_seconds: Label = builder.object("label_same_music_seconds").unwrap();
|
||||||
|
let label_same_music_similarity: Label = builder.object("label_same_music_similarity").unwrap();
|
||||||
|
let scale_seconds_same_music: Scale = builder.object("scale_seconds_same_music").unwrap();
|
||||||
|
let scale_similarity_same_music: Scale = builder.object("scale_similarity_same_music").unwrap();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
notebook_main,
|
notebook_main,
|
||||||
scrolled_window_duplicate_finder,
|
scrolled_window_duplicate_finder,
|
||||||
|
@ -289,6 +302,7 @@ impl GuiMainNotebook {
|
||||||
check_button_music_genre,
|
check_button_music_genre,
|
||||||
check_button_music_length,
|
check_button_music_length,
|
||||||
check_button_music_approximate_comparison,
|
check_button_music_approximate_comparison,
|
||||||
|
label_audio_check_type,
|
||||||
scale_similarity_similar_images,
|
scale_similarity_similar_images,
|
||||||
scale_similarity_similar_videos,
|
scale_similarity_similar_videos,
|
||||||
check_button_broken_files_audio,
|
check_button_broken_files_audio,
|
||||||
|
@ -331,6 +345,11 @@ impl GuiMainNotebook {
|
||||||
combo_box_big_files_mode,
|
combo_box_big_files_mode,
|
||||||
label_big_files_mode,
|
label_big_files_mode,
|
||||||
check_button_broken_files_image,
|
check_button_broken_files_image,
|
||||||
|
combo_box_audio_check_type,
|
||||||
|
label_same_music_seconds,
|
||||||
|
label_same_music_similarity,
|
||||||
|
scale_seconds_same_music,
|
||||||
|
scale_similarity_same_music,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,6 +426,13 @@ impl GuiMainNotebook {
|
||||||
self.check_button_broken_files_image.set_label(Some(&flg!("main_check_box_broken_files_image")));
|
self.check_button_broken_files_image.set_label(Some(&flg!("main_check_box_broken_files_image")));
|
||||||
self.check_button_broken_files_pdf.set_label(Some(&flg!("main_check_box_broken_files_pdf")));
|
self.check_button_broken_files_pdf.set_label(Some(&flg!("main_check_box_broken_files_pdf")));
|
||||||
|
|
||||||
|
self.label_same_music_seconds.set_label(&flg!("same_music_seconds_label"));
|
||||||
|
self.label_same_music_similarity.set_label(&flg!("same_music_similarity_label"));
|
||||||
|
self.label_same_music_seconds.set_tooltip_text(Some(&flg!("same_music_tooltip")));
|
||||||
|
self.label_same_music_similarity.set_tooltip_text(Some(&flg!("same_music_tooltip")));
|
||||||
|
self.scale_seconds_same_music.set_tooltip_text(Some(&flg!("same_music_tooltip")));
|
||||||
|
self.scale_similarity_similar_videos.set_tooltip_text(Some(&flg!("same_music_tooltip")));
|
||||||
|
|
||||||
{
|
{
|
||||||
let hash_size_index = self.combo_box_image_hash_size.active().unwrap() as usize;
|
let hash_size_index = self.combo_box_image_hash_size.active().unwrap() as usize;
|
||||||
let hash_size = IMAGES_HASH_SIZE_COMBO_BOX[hash_size_index];
|
let hash_size = IMAGES_HASH_SIZE_COMBO_BOX[hash_size_index];
|
||||||
|
@ -541,6 +567,19 @@ impl GuiMainNotebook {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let active = self.combo_box_audio_check_type.active().unwrap_or(0);
|
||||||
|
self.combo_box_audio_check_type.remove_all();
|
||||||
|
for i in &AUDIO_TYPE_CHECK_METHOD_COMBO_BOX {
|
||||||
|
let text = match i.check_method {
|
||||||
|
CheckingMethod::AudioTags => flg!("music_checking_by_tags"),
|
||||||
|
CheckingMethod::AudioContent => flg!("music_checking_by_content"),
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
self.combo_box_audio_check_type.append_text(&text);
|
||||||
|
}
|
||||||
|
self.combo_box_audio_check_type.set_active(Some(active));
|
||||||
|
}
|
||||||
{
|
{
|
||||||
let active = self.combo_box_duplicate_check_method.active().unwrap_or(0);
|
let active = self.combo_box_duplicate_check_method.active().unwrap_or(0);
|
||||||
self.combo_box_duplicate_check_method.remove_all();
|
self.combo_box_duplicate_check_method.remove_all();
|
||||||
|
@ -550,7 +589,7 @@ impl GuiMainNotebook {
|
||||||
CheckingMethod::Size => flg!("duplicate_mode_size_combo_box"),
|
CheckingMethod::Size => flg!("duplicate_mode_size_combo_box"),
|
||||||
CheckingMethod::Name => flg!("duplicate_mode_name_combo_box"),
|
CheckingMethod::Name => flg!("duplicate_mode_name_combo_box"),
|
||||||
CheckingMethod::SizeName => flg!("duplicate_mode_size_name_combo_box"),
|
CheckingMethod::SizeName => flg!("duplicate_mode_size_name_combo_box"),
|
||||||
CheckingMethod::None => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
self.combo_box_duplicate_check_method.append_text(&text);
|
self.combo_box_duplicate_check_method.append_text(&text);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,23 @@ pub const DUPLICATES_CHECK_METHOD_COMBO_BOX: [CheckMethodStruct; 4] = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct AudioTypeStruct {
|
||||||
|
pub eng_name: &'static str,
|
||||||
|
pub check_method: CheckingMethod,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const AUDIO_TYPE_CHECK_METHOD_COMBO_BOX: [AudioTypeStruct; 2] = [
|
||||||
|
AudioTypeStruct {
|
||||||
|
eng_name: "Tags",
|
||||||
|
check_method: CheckingMethod::AudioTags,
|
||||||
|
},
|
||||||
|
AudioTypeStruct {
|
||||||
|
eng_name: "Content",
|
||||||
|
check_method: CheckingMethod::AudioContent,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct SearchModeStruct {
|
pub struct SearchModeStruct {
|
||||||
pub eng_name: &'static str,
|
pub eng_name: &'static str,
|
||||||
|
|
|
@ -7,7 +7,7 @@ use gdk4::gdk_pixbuf::{InterpType, Pixbuf};
|
||||||
use glib::signal::Inhibit;
|
use glib::signal::Inhibit;
|
||||||
use glib::Error;
|
use glib::Error;
|
||||||
use gtk4::prelude::*;
|
use gtk4::prelude::*;
|
||||||
use gtk4::{ListStore, ScrollType, TextView, TreeView, Widget};
|
use gtk4::{ListStore, Scale, ScrollType, TextView, TreeView, Widget};
|
||||||
use image::codecs::jpeg::JpegEncoder;
|
use image::codecs::jpeg::JpegEncoder;
|
||||||
use image::{DynamicImage, EncodableLayout};
|
use image::{DynamicImage, EncodableLayout};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
|
@ -770,7 +770,16 @@ pub fn check_if_list_store_column_have_all_same_values(list_store: &ListStore, c
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scale_step_function(scale: >k4::Scale, _scroll_type: ScrollType, value: f64) -> Inhibit {
|
pub fn scale_set_min_max_values(scale: &Scale, minimum: f64, maximum: f64, current_value: f64, step: Option<f64>) {
|
||||||
|
scale.set_range(minimum, maximum);
|
||||||
|
scale.set_fill_level(maximum);
|
||||||
|
scale.set_value(current_value);
|
||||||
|
if let Some(step) = step {
|
||||||
|
scale.adjustment().set_step_increment(step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scale_step_function(scale: &Scale, _scroll_type: ScrollType, value: f64) -> Inhibit {
|
||||||
scale.set_increments(1_f64, 1_f64);
|
scale.set_increments(1_f64, 1_f64);
|
||||||
scale.set_round_digits(0);
|
scale.set_round_digits(0);
|
||||||
scale.set_fill_level(value.round());
|
scale.set_fill_level(value.round());
|
||||||
|
|
|
@ -42,6 +42,7 @@ use crate::compute_results::*;
|
||||||
use crate::connect_things::connect_button_sort::connect_button_sort;
|
use crate::connect_things::connect_button_sort::connect_button_sort;
|
||||||
use crate::connect_things::connect_popovers_select::connect_popover_select;
|
use crate::connect_things::connect_popovers_select::connect_popover_select;
|
||||||
use crate::connect_things::connect_popovers_sort::connect_popover_sort;
|
use crate::connect_things::connect_popovers_sort::connect_popover_sort;
|
||||||
|
use crate::connect_things::connect_same_music_mode_changed::connect_same_music_change_mode;
|
||||||
use crate::initialize_gui::*;
|
use crate::initialize_gui::*;
|
||||||
use crate::language_functions::LANGUAGES_ALL;
|
use crate::language_functions::LANGUAGES_ALL;
|
||||||
use crate::saving_loading::*;
|
use crate::saving_loading::*;
|
||||||
|
@ -163,6 +164,7 @@ fn build_ui(application: &Application, arguments: &[OsString]) {
|
||||||
connect_button_about(&gui_data);
|
connect_button_about(&gui_data);
|
||||||
connect_about_buttons(&gui_data);
|
connect_about_buttons(&gui_data);
|
||||||
connect_similar_image_size_change(&gui_data);
|
connect_similar_image_size_change(&gui_data);
|
||||||
|
connect_same_music_change_mode(&gui_data);
|
||||||
|
|
||||||
let window_main = gui_data.window_main.clone();
|
let window_main = gui_data.window_main.clone();
|
||||||
let taskbar_state = gui_data.taskbar_state.clone();
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
|
|
|
@ -237,6 +237,10 @@
|
||||||
(5,232,"GtkLabel","label_buttons_sort",230,None,None,None,1),
|
(5,232,"GtkLabel","label_buttons_sort",230,None,None,None,1),
|
||||||
(5,234,"GtkLabel","label_audio_check_type",124,None,None,None,None),
|
(5,234,"GtkLabel","label_audio_check_type",124,None,None,None,None),
|
||||||
(5,235,"GtkComboBoxText","combo_box_audio_check_type",124,None,None,None,1),
|
(5,235,"GtkComboBoxText","combo_box_audio_check_type",124,None,None,None,1),
|
||||||
|
(5,236,"GtkScale","scale_seconds_same_music",117,None,None,None,7),
|
||||||
|
(5,237,"GtkScale","scale_similarity_same_music",117,None,None,None,9),
|
||||||
|
(5,238,"GtkLabel","label_same_music_seconds",117,None,None,None,6),
|
||||||
|
(5,239,"GtkLabel","label_same_music_similarity",117,None,None,None,8),
|
||||||
(6,1,"GtkPopover","popover_right_click",None,None,None,None,None),
|
(6,1,"GtkPopover","popover_right_click",None,None,None,None,None),
|
||||||
(6,2,"GtkBox",None,1,None,None,None,None),
|
(6,2,"GtkBox",None,1,None,None,None,None),
|
||||||
(6,3,"GtkButton","buttons_popover_right_click_open_file",2,None,None,None,None),
|
(6,3,"GtkButton","buttons_popover_right_click_open_file",2,None,None,None,None),
|
||||||
|
@ -769,6 +773,26 @@
|
||||||
(5,232,"GtkLabel","label","SortMenu",None,None,None,None,None),
|
(5,232,"GtkLabel","label","SortMenu",None,None,None,None,None),
|
||||||
(5,234,"GtkLabel","label","Audio check type",None,None,None,None,None),
|
(5,234,"GtkLabel","label","Audio check type",None,None,None,None,None),
|
||||||
(5,234,"GtkWidget","margin-end","2",None,None,None,None,None),
|
(5,234,"GtkWidget","margin-end","2",None,None,None,None,None),
|
||||||
|
(5,236,"GtkRange","fill-level","100",None,None,None,None,None),
|
||||||
|
(5,236,"GtkRange","round-digits","1",None,None,None,None,None),
|
||||||
|
(5,236,"GtkScale","digits","0",None,None,None,None,None),
|
||||||
|
(5,236,"GtkScale","draw-value","1",None,None,None,None,None),
|
||||||
|
(5,236,"GtkScale","value-pos","right",None,None,None,None,None),
|
||||||
|
(5,236,"GtkWidget","focusable","1",None,None,None,None,None),
|
||||||
|
(5,236,"GtkWidget","hexpand","1",None,None,None,None,None),
|
||||||
|
(5,237,"GtkRange","fill-level","100",None,None,None,None,None),
|
||||||
|
(5,237,"GtkRange","round-digits","1",None,None,None,None,None),
|
||||||
|
(5,237,"GtkScale","digits","0",None,None,None,None,None),
|
||||||
|
(5,237,"GtkScale","draw-value","1",None,None,None,None,None),
|
||||||
|
(5,237,"GtkScale","value-pos","right",None,None,None,None,None),
|
||||||
|
(5,237,"GtkWidget","focusable","1",None,None,None,None,None),
|
||||||
|
(5,237,"GtkWidget","hexpand","1",None,None,None,None,None),
|
||||||
|
(5,238,"GtkLabel","label","Minimal fragment second duration",None,None,None,None,None),
|
||||||
|
(5,238,"GtkWidget","margin-end","5",None,None,None,None,None),
|
||||||
|
(5,238,"GtkWidget","margin-start","5",None,None,None,None,None),
|
||||||
|
(5,239,"GtkLabel","label","Max difference",None,None,None,None,None),
|
||||||
|
(5,239,"GtkWidget","margin-end","5",None,None,None,None,None),
|
||||||
|
(5,239,"GtkWidget","margin-start","5",None,None,None,None,None),
|
||||||
(6,1,"GtkPopover","child",None,None,None,None,None,2),
|
(6,1,"GtkPopover","child",None,None,None,None,None,2),
|
||||||
(6,1,"GtkPopover","position","left",None,None,None,None,None),
|
(6,1,"GtkPopover","position","left",None,None,None,None,None),
|
||||||
(6,2,"GtkOrientable","orientation","vertical",None,None,None,None,None),
|
(6,2,"GtkOrientable","orientation","vertical",None,None,None,None,None),
|
||||||
|
@ -991,6 +1015,7 @@
|
||||||
(9,58,"GtkWidget","focusable","1",None,None,None,None,None),
|
(9,58,"GtkWidget","focusable","1",None,None,None,None,None),
|
||||||
(9,58,"GtkWidget","hexpand","1",None,None,None,None,None),
|
(9,58,"GtkWidget","hexpand","1",None,None,None,None,None),
|
||||||
(9,59,"GtkAccessible","accessible-role","menu-item-checkbox",None,None,None,None,None),
|
(9,59,"GtkAccessible","accessible-role","menu-item-checkbox",None,None,None,None,None),
|
||||||
|
(9,59,"GtkLabel","label","Restart Required",None,None,None,None,None),
|
||||||
(9,59,"GtkWidget","margin-bottom","4",None,None,None,None,None),
|
(9,59,"GtkWidget","margin-bottom","4",None,None,None,None,None),
|
||||||
(9,59,"GtkWidget","margin-top","5",None,None,None,None,None),
|
(9,59,"GtkWidget","margin-top","5",None,None,None,None,None),
|
||||||
(10,1,"GtkPopover","child",None,None,None,None,None,2),
|
(10,1,"GtkPopover","child",None,None,None,None,None,2),
|
||||||
|
|
|
@ -721,6 +721,42 @@
|
||||||
<property name="label">Length</property>
|
<property name="label">Length</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label_same_music_seconds">
|
||||||
|
<property name="label">Minimal fragment second duration</property>
|
||||||
|
<property name="margin-end">5</property>
|
||||||
|
<property name="margin-start">5</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScale" id="scale_seconds_same_music">
|
||||||
|
<property name="digits">0</property>
|
||||||
|
<property name="draw-value">1</property>
|
||||||
|
<property name="fill-level">100</property>
|
||||||
|
<property name="focusable">1</property>
|
||||||
|
<property name="hexpand">1</property>
|
||||||
|
<property name="round-digits">1</property>
|
||||||
|
<property name="value-pos">right</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label_same_music_similarity">
|
||||||
|
<property name="label">Max difference</property>
|
||||||
|
<property name="margin-end">5</property>
|
||||||
|
<property name="margin-start">5</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScale" id="scale_similarity_same_music">
|
||||||
|
<property name="digits">0</property>
|
||||||
|
<property name="draw-value">1</property>
|
||||||
|
<property name="fill-level">100</property>
|
||||||
|
<property name="focusable">1</property>
|
||||||
|
<property name="hexpand">1</property>
|
||||||
|
<property name="round-digits">1</property>
|
||||||
|
<property name="value-pos">right</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
|
|
|
@ -142,6 +142,7 @@
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="label_restart_needed">
|
<object class="GtkLabel" id="label_restart_needed">
|
||||||
<property name="accessible-role">menu-item-checkbox</property>
|
<property name="accessible-role">menu-item-checkbox</property>
|
||||||
|
<property name="label">Restart Required</property>
|
||||||
<property name="margin-bottom">4</property>
|
<property name="margin-bottom">4</property>
|
||||||
<property name="margin-top">5</property>
|
<property name="margin-top">5</property>
|
||||||
</object>
|
</object>
|
||||||
|
|
Loading…
Reference in a new issue