Things dedup (#1183)

* Deduplicate cli settings

* Save

* Nice

* TODO even bigger generalization

* Simplification

* Bad
pull/1190/head
Rafał Mikrut 4 months ago committed by GitHub
parent 0defcbd253
commit 6cde5ab7a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

57
Cargo.lock generated

@ -953,6 +953,19 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "console"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"unicode-width",
"windows-sys 0.52.0",
]
[[package]]
name = "const-field-offset"
version = "0.1.3"
@ -1179,6 +1192,16 @@ dependencies = [
"syn 2.0.48",
]
[[package]]
name = "ctrlc"
version = "3.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b"
dependencies = [
"nix 0.27.1",
"windows-sys 0.52.0",
]
[[package]]
name = "cursor-icon"
version = "1.1.0"
@ -1190,10 +1213,13 @@ name = "czkawka_cli"
version = "6.1.0"
dependencies = [
"clap",
"crossbeam-channel",
"ctrlc",
"czkawka_core",
"fun_time",
"handsome_logger",
"image_hasher",
"indicatif",
"log",
]
@ -1574,6 +1600,12 @@ version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "encoding_rs"
version = "0.8.33"
@ -3081,6 +3113,19 @@ dependencies = [
"hashbrown 0.14.3",
]
[[package]]
name = "indicatif"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
dependencies = [
"console",
"instant",
"number_prefix",
"portable-atomic",
"unicode-width",
]
[[package]]
name = "infer"
version = "0.15.0"
@ -3898,6 +3943,12 @@ dependencies = [
"libc",
]
[[package]]
name = "number_prefix"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "objc"
version = "0.2.7"
@ -6102,6 +6153,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "untrusted"
version = "0.9.0"

@ -10,6 +10,9 @@
### CLI
- Providing full static rust binary with [Eyra](https://github.com/sunfishcode/eyra) - [#1102](https://github.com/qarmin/czkawka/pull/1102)
- Fixed duplicated `-c` argument, now saving as compact json is handled via `-C` - [#1153](https://github.com/qarmin/czkawka/pull/1153)
- Added progress bar - [#TODO]()
- Clean and safe cancelling of scan - [#TODO]()
- Unification of CLI arguments - [#TODO]()
### Krokiet GUI
- Initial release of new gui - [#1102](https://github.com/qarmin/czkawka/pull/1102)

@ -19,6 +19,9 @@ log = "0.4.20"
handsome_logger = "0.8"
fun_time = { version = "0.3", features = ["log"] }
czkawka_core = { path = "../czkawka_core", version = "6.1.0", features = [] }
indicatif = "0.17"
crossbeam-channel = { version = "0.5", features = [] }
ctrlc = { version = "3.4", features = ["termination"] }
[features]
default = []

@ -87,13 +87,7 @@ pub enum Commands {
#[derive(Debug, clap::Args)]
pub struct DuplicatesArgs {
#[clap(flatten)]
pub thread_number: ThreadNumber,
#[clap(flatten)]
pub directories: Directories,
#[clap(flatten)]
pub excluded_directories: ExcludedDirectories,
#[clap(flatten)]
pub excluded_items: ExcludedItems,
pub common_cli_items: CommonCliItems,
#[clap(
short,
long,
@ -121,8 +115,6 @@ pub struct DuplicatesArgs {
long_help = "Minimum size of cached files in bytes, assigning bigger value may speed up the scan but loading the cache will be slower, assigning smaller value may slow down the scan and some files may need to be hashed again but loading the cache will be faster"
)]
pub minimal_cached_file_size: u64,
#[clap(flatten)]
pub allowed_extensions: AllowedExtensions,
#[clap(
short,
long,
@ -143,18 +135,7 @@ pub struct DuplicatesArgs {
)]
pub hash_type: HashType,
#[clap(flatten)]
pub file_to_save: FileToSave,
#[clap(flatten)]
pub json_compact_file_to_save: JsonCompactFileToSave,
#[clap(flatten)]
pub json_pretty_file_to_save: JsonPrettyFileToSave,
#[clap(flatten)]
pub not_recursive: NotRecursive,
#[clap(flatten)]
pub case_sensitive_name_comparison: CaseSensitiveNameComparison,
#[cfg(target_family = "unix")]
#[clap(flatten)]
pub exclude_other_filesystems: ExcludeOtherFilesystems,
#[clap(flatten)]
pub allow_hard_links: AllowHardLinks,
#[clap(flatten)]
@ -164,117 +145,43 @@ pub struct DuplicatesArgs {
#[derive(Debug, clap::Args)]
pub struct EmptyFoldersArgs {
#[clap(flatten)]
pub thread_number: ThreadNumber,
#[clap(flatten)]
pub directories: Directories,
#[clap(flatten)]
pub excluded_directories: ExcludedDirectories,
#[clap(flatten)]
pub excluded_items: ExcludedItems,
pub common_cli_items: CommonCliItems,
#[clap(short = 'D', long, help = "Delete found folders")]
pub delete_folders: bool,
#[clap(flatten)]
pub file_to_save: FileToSave,
#[clap(flatten)]
pub json_compact_file_to_save: JsonCompactFileToSave,
#[clap(flatten)]
pub json_pretty_file_to_save: JsonPrettyFileToSave,
#[cfg(target_family = "unix")]
#[clap(flatten)]
pub exclude_other_filesystems: ExcludeOtherFilesystems,
}
#[derive(Debug, clap::Args)]
pub struct BiggestFilesArgs {
#[clap(flatten)]
pub thread_number: ThreadNumber,
#[clap(flatten)]
pub directories: Directories,
#[clap(flatten)]
pub excluded_directories: ExcludedDirectories,
#[clap(flatten)]
pub excluded_items: ExcludedItems,
#[clap(flatten)]
pub allowed_extensions: AllowedExtensions,
pub common_cli_items: CommonCliItems,
#[clap(short, long, default_value = "50", help = "Number of files to be shown")]
pub number_of_files: usize,
#[clap(short = 'D', long, help = "Delete found files")]
pub delete_files: bool,
#[clap(flatten)]
pub file_to_save: FileToSave,
#[clap(flatten)]
pub json_compact_file_to_save: JsonCompactFileToSave,
#[clap(flatten)]
pub json_pretty_file_to_save: JsonPrettyFileToSave,
#[clap(flatten)]
pub not_recursive: NotRecursive,
#[clap(short = 'J', long, help = "Finds the smallest files instead the biggest")]
pub smallest_mode: bool,
#[cfg(target_family = "unix")]
#[clap(flatten)]
pub exclude_other_filesystems: ExcludeOtherFilesystems,
}
#[derive(Debug, clap::Args)]
pub struct EmptyFilesArgs {
#[clap(flatten)]
pub thread_number: ThreadNumber,
#[clap(flatten)]
pub directories: Directories,
#[clap(flatten)]
pub excluded_directories: ExcludedDirectories,
#[clap(flatten)]
pub excluded_items: ExcludedItems,
#[clap(flatten)]
pub allowed_extensions: AllowedExtensions,
pub common_cli_items: CommonCliItems,
#[clap(short = 'D', long, help = "Delete found files")]
pub delete_files: bool,
#[clap(flatten)]
pub file_to_save: FileToSave,
#[clap(flatten)]
pub json_compact_file_to_save: JsonCompactFileToSave,
#[clap(flatten)]
pub json_pretty_file_to_save: JsonPrettyFileToSave,
#[clap(flatten)]
pub not_recursive: NotRecursive,
#[cfg(target_family = "unix")]
#[clap(flatten)]
pub exclude_other_filesystems: ExcludeOtherFilesystems,
}
#[derive(Debug, clap::Args)]
pub struct TemporaryArgs {
#[clap(flatten)]
pub thread_number: ThreadNumber,
#[clap(flatten)]
pub directories: Directories,
#[clap(flatten)]
pub excluded_directories: ExcludedDirectories,
#[clap(flatten)]
pub excluded_items: ExcludedItems,
pub common_cli_items: CommonCliItems,
#[clap(short = 'D', long, help = "Delete found files")]
pub delete_files: bool,
#[clap(flatten)]
pub file_to_save: FileToSave,
#[clap(flatten)]
pub json_compact_file_to_save: JsonCompactFileToSave,
#[clap(flatten)]
pub json_pretty_file_to_save: JsonPrettyFileToSave,
#[clap(flatten)]
pub not_recursive: NotRecursive,
#[cfg(target_family = "unix")]
#[clap(flatten)]
pub exclude_other_filesystems: ExcludeOtherFilesystems,
}
#[derive(Debug, clap::Args)]
pub struct SimilarImagesArgs {
#[clap(flatten)]
pub thread_number: ThreadNumber,
#[clap(flatten)]
pub directories: Directories,
#[clap(flatten)]
pub excluded_directories: ExcludedDirectories,
pub common_cli_items: CommonCliItems,
#[clap(
short,
long,
@ -303,22 +210,9 @@ pub struct SimilarImagesArgs {
)]
pub similarity_preset: SimilarityPreset,
#[clap(flatten)]
pub excluded_items: ExcludedItems,
#[clap(flatten)]
pub file_to_save: FileToSave,
#[clap(flatten)]
pub delete_method: DMethod,
#[clap(flatten)]
pub dry_run: DryRun,
#[clap(flatten)]
pub json_compact_file_to_save: JsonCompactFileToSave,
#[clap(flatten)]
pub json_pretty_file_to_save: JsonPrettyFileToSave,
#[clap(flatten)]
pub not_recursive: NotRecursive,
#[cfg(target_family = "unix")]
#[clap(flatten)]
pub exclude_other_filesystems: ExcludeOtherFilesystems,
#[clap(
short = 'g',
long,
@ -348,13 +242,7 @@ pub struct SimilarImagesArgs {
#[derive(Debug, clap::Args)]
pub struct SameMusicArgs {
#[clap(flatten)]
pub thread_number: ThreadNumber,
#[clap(flatten)]
pub directories: Directories,
#[clap(flatten)]
pub excluded_directories: ExcludedDirectories,
#[clap(flatten)]
pub excluded_items: ExcludedItems,
pub common_cli_items: CommonCliItems,
#[clap(flatten)]
pub delete_method: DMethod,
#[clap(flatten)]
@ -377,17 +265,6 @@ pub struct SameMusicArgs {
long_help = "Methods to search files.\nCONTENT - finds similar audio files by content, TAGS - finds similar images by tags, needs to set"
)]
pub search_method: CheckingMethod,
#[clap(flatten)]
pub file_to_save: FileToSave,
#[clap(flatten)]
pub json_compact_file_to_save: JsonCompactFileToSave,
#[clap(flatten)]
pub json_pretty_file_to_save: JsonPrettyFileToSave,
#[clap(flatten)]
pub not_recursive: NotRecursive,
#[cfg(target_family = "unix")]
#[clap(flatten)]
pub exclude_other_filesystems: ExcludeOtherFilesystems,
#[clap(
short,
long,
@ -458,84 +335,27 @@ fn parse_minimum_segment_duration(src: &str) -> Result<f32, String> {
#[derive(Debug, clap::Args)]
pub struct InvalidSymlinksArgs {
#[clap(flatten)]
pub thread_number: ThreadNumber,
#[clap(flatten)]
pub directories: Directories,
#[clap(flatten)]
pub excluded_directories: ExcludedDirectories,
#[clap(flatten)]
pub excluded_items: ExcludedItems,
#[clap(flatten)]
pub allowed_extensions: AllowedExtensions,
pub common_cli_items: CommonCliItems,
#[clap(short = 'D', long, help = "Delete found files")]
pub delete_files: bool,
#[clap(flatten)]
pub file_to_save: FileToSave,
#[clap(flatten)]
pub json_compact_file_to_save: JsonCompactFileToSave,
#[clap(flatten)]
pub json_pretty_file_to_save: JsonPrettyFileToSave,
#[clap(flatten)]
pub not_recursive: NotRecursive,
#[cfg(target_family = "unix")]
#[clap(flatten)]
pub exclude_other_filesystems: ExcludeOtherFilesystems,
}
#[derive(Debug, clap::Args)]
pub struct BrokenFilesArgs {
#[clap(flatten)]
pub thread_number: ThreadNumber,
#[clap(flatten)]
pub directories: Directories,
#[clap(flatten)]
pub excluded_directories: ExcludedDirectories,
#[clap(flatten)]
pub excluded_items: ExcludedItems,
#[clap(flatten)]
pub allowed_extensions: AllowedExtensions,
pub common_cli_items: CommonCliItems,
#[clap(short = 'D', long, help = "Delete found files")]
pub delete_files: bool,
#[clap(flatten)]
pub file_to_save: FileToSave,
#[clap(flatten)]
pub json_compact_file_to_save: JsonCompactFileToSave,
#[clap(flatten)]
pub json_pretty_file_to_save: JsonPrettyFileToSave,
#[clap(flatten)]
pub not_recursive: NotRecursive,
#[cfg(target_family = "unix")]
#[clap(flatten)]
pub exclude_other_filesystems: ExcludeOtherFilesystems,
}
#[derive(Debug, clap::Args)]
pub struct SimilarVideosArgs {
#[clap(flatten)]
pub thread_number: ThreadNumber,
#[clap(flatten)]
pub directories: Directories,
#[clap(flatten)]
pub excluded_directories: ExcludedDirectories,
#[clap(flatten)]
pub excluded_items: ExcludedItems,
pub common_cli_items: CommonCliItems,
#[clap(flatten)]
pub delete_method: DMethod,
#[clap(flatten)]
pub dry_run: DryRun,
#[clap(flatten)]
pub file_to_save: FileToSave,
#[clap(flatten)]
pub json_compact_file_to_save: JsonCompactFileToSave,
#[clap(flatten)]
pub json_pretty_file_to_save: JsonPrettyFileToSave,
#[clap(flatten)]
pub allowed_extensions: AllowedExtensions,
#[clap(flatten)]
pub not_recursive: NotRecursive,
#[cfg(target_family = "unix")]
#[clap(flatten)]
pub exclude_other_filesystems: ExcludeOtherFilesystems,
#[clap(
short,
long,
@ -568,43 +388,13 @@ pub struct SimilarVideosArgs {
#[derive(Debug, clap::Args)]
pub struct BadExtensionsArgs {
#[clap(flatten)]
pub thread_number: ThreadNumber,
#[clap(flatten)]
pub directories: Directories,
#[clap(flatten)]
pub excluded_directories: ExcludedDirectories,
#[clap(flatten)]
pub excluded_items: ExcludedItems,
#[clap(flatten)]
pub allowed_extensions: AllowedExtensions,
#[clap(flatten)]
pub file_to_save: FileToSave,
#[clap(flatten)]
pub json_compact_file_to_save: JsonCompactFileToSave,
#[clap(flatten)]
pub json_pretty_file_to_save: JsonPrettyFileToSave,
#[clap(flatten)]
pub not_recursive: NotRecursive,
#[cfg(target_family = "unix")]
#[clap(flatten)]
pub exclude_other_filesystems: ExcludeOtherFilesystems,
pub common_cli_items: CommonCliItems,
}
#[derive(Debug, clap::Args)]
pub struct DMethod {
#[clap(
short = 'D',
long,
default_value = "NONE",
value_parser = parse_delete_method,
help = "Delete method (AEN, AEO, ON, OO, HARD)",
long_help = "Methods to delete the files.\nAEN - All files except the newest,\nAEO - All files except the oldest,\nON - Only 1 file, the newest,\nOO - Only 1 file, the oldest\nHARD - create hard link\nNONE - not delete files"
)]
pub delete_method: DeleteMethod,
}
#[derive(Debug, clap::Args)]
pub struct Directories {
pub struct CommonCliItems {
#[clap(short = 'T', long, default_value = "0", help = "Limits thread number, 0(default) will use all available threads")]
pub thread_number: usize,
#[clap(
short,
long,
@ -613,10 +403,6 @@ pub struct Directories {
long_help = "List of directorie(s) which will be searched(absolute path)"
)]
pub directories: Vec<PathBuf>,
}
#[derive(Debug, clap::Args)]
pub struct ExcludedDirectories {
#[clap(
short,
long,
@ -624,10 +410,6 @@ pub struct ExcludedDirectories {
long_help = "List of directorie(s) which will be excluded from search(absolute path)"
)]
pub excluded_directories: Vec<PathBuf>,
}
#[derive(Debug, clap::Args)]
pub struct ExcludedItems {
#[clap(
short = 'E',
long,
@ -635,10 +417,6 @@ pub struct ExcludedItems {
long_help = "List of excluded item(s) which contains * wildcard(may be slow, so use -e where possible)"
)]
pub excluded_items: Vec<String>,
}
#[derive(Debug, clap::Args)]
pub struct AllowedExtensions {
#[clap(
short = 'x',
long,
@ -646,25 +424,30 @@ pub struct AllowedExtensions {
long_help = "List of checked files with provided extension(s). There are also helpful macros which allow to easy use a typical extensions like:\nIMAGE(\"jpg,kra,gif,png,bmp,tiff,hdr,svg\"),\nTEXT(\"txt,doc,docx,odt,rtf\"),\nVIDEO(\"mp4,flv,mkv,webm,vob,ogv,gifv,avi,mov,wmv,mpg,m4v,m4p,mpeg,3gp\") or\nMUSIC(\"mp3,flac,ogg,tta,wma,webm\")\n "
)]
pub allowed_extensions: Vec<String>,
}
#[derive(Debug, clap::Args)]
pub struct NotRecursive {
#[clap(flatten)]
pub file_to_save: FileToSave,
#[clap(flatten)]
pub json_compact_file_to_save: JsonCompactFileToSave,
#[clap(flatten)]
pub json_pretty_file_to_save: JsonPrettyFileToSave,
#[clap(short = 'R', long, help = "Prevents from recursive check of folders")]
pub not_recursive: bool,
#[cfg(target_family = "unix")]
#[clap(short = 'X', long, help = "Exclude files on other filesystems")]
pub exclude_other_filesystems: bool,
}
#[derive(Debug, clap::Args)]
pub struct ThreadNumber {
#[clap(short = 'T', long, default_value = "0", help = "Limits thread number, 0(default) will use all available threads")]
pub thread_number: usize,
}
#[cfg(target_family = "unix")]
#[derive(Debug, clap::Args)]
pub struct ExcludeOtherFilesystems {
#[clap(short = 'X', long, help = "Exclude files on other filesystems")]
pub exclude_other_filesystems: bool,
pub struct DMethod {
#[clap(
short = 'D',
long,
default_value = "NONE",
value_parser = parse_delete_method,
help = "Delete method (AEN, AEO, ON, OO, HARD)",
long_help = "Methods to delete the files.\nAEN - All files except the newest,\nAEO - All files except the oldest,\nON - Only 1 file, the newest,\nOO - Only 1 file, the oldest\nHARD - create hard link\nNONE - not delete files"
)]
pub delete_method: DeleteMethod,
}
#[derive(Debug, clap::Args)]

@ -1,6 +1,9 @@
#![allow(clippy::needless_late_init)]
use std::thread;
use clap::Parser;
use crossbeam_channel::{bounded, unbounded, Receiver, Sender};
use log::error;
use commands::Commands;
@ -8,6 +11,7 @@ use czkawka_core::bad_extensions::BadExtensions;
use czkawka_core::big_file::{BigFile, SearchMode};
use czkawka_core::broken_files::BrokenFiles;
use czkawka_core::common::{print_version_mode, set_number_of_threads, setup_logger};
use czkawka_core::common_dir_traversal::ProgressData;
use czkawka_core::common_tool::{CommonData, DeleteMethod};
#[allow(unused_imports)] // It is used in release for print_results_to_output().
use czkawka_core::common_traits::*;
@ -21,11 +25,13 @@ use czkawka_core::similar_videos::SimilarVideos;
use czkawka_core::temporary::Temporary;
use crate::commands::{
Args, BadExtensionsArgs, BiggestFilesArgs, BrokenFilesArgs, DuplicatesArgs, EmptyFilesArgs, EmptyFoldersArgs, InvalidSymlinksArgs, SameMusicArgs, SimilarImagesArgs,
SimilarVideosArgs, TemporaryArgs,
Args, BadExtensionsArgs, BiggestFilesArgs, BrokenFilesArgs, CommonCliItems, DuplicatesArgs, EmptyFilesArgs, EmptyFoldersArgs, InvalidSymlinksArgs, SameMusicArgs,
SimilarImagesArgs, SimilarVideosArgs, TemporaryArgs,
};
use crate::progress::connect_progress;
mod commands;
mod progress;
fn main() {
let command = Args::parse().command;
@ -37,146 +43,95 @@ fn main() {
println!("{command:?}");
}
match command {
Commands::Duplicates(duplicates_args) => duplicates(duplicates_args),
Commands::EmptyFolders(empty_folders_args) => empty_folders(empty_folders_args),
Commands::BiggestFiles(biggest_files_args) => biggest_files(biggest_files_args),
Commands::EmptyFiles(empty_files_args) => empty_files(empty_files_args),
Commands::Temporary(temporary_args) => temporary(temporary_args),
Commands::SimilarImages(similar_images_args) => similar_images(similar_images_args),
Commands::SameMusic(same_music_args) => same_music(same_music_args),
Commands::InvalidSymlinks(invalid_symlinks_args) => invalid_symlinks(invalid_symlinks_args),
Commands::BrokenFiles(broken_files_args) => broken_files(broken_files_args),
Commands::SimilarVideos(similar_videos_args) => similar_videos(similar_videos_args),
Commands::BadExtensions(bad_extensions_args) => bad_extensions(bad_extensions_args),
let (progress_sender, progress_receiver): (Sender<ProgressData>, Receiver<ProgressData>) = unbounded();
let (stop_sender, stop_receiver): (Sender<()>, Receiver<()>) = bounded(1);
let calculate_thread = thread::spawn(move || match command {
Commands::Duplicates(duplicates_args) => duplicates(duplicates_args, &stop_receiver, &progress_sender),
Commands::EmptyFolders(empty_folders_args) => empty_folders(empty_folders_args, &stop_receiver, &progress_sender),
Commands::BiggestFiles(biggest_files_args) => biggest_files(biggest_files_args, &stop_receiver, &progress_sender),
Commands::EmptyFiles(empty_files_args) => empty_files(empty_files_args, &stop_receiver, &progress_sender),
Commands::Temporary(temporary_args) => temporary(temporary_args, &stop_receiver, &progress_sender),
Commands::SimilarImages(similar_images_args) => similar_images(similar_images_args, &stop_receiver, &progress_sender),
Commands::SameMusic(same_music_args) => same_music(same_music_args, &stop_receiver, &progress_sender),
Commands::InvalidSymlinks(invalid_symlinks_args) => invalid_symlinks(invalid_symlinks_args, &stop_receiver, &progress_sender),
Commands::BrokenFiles(broken_files_args) => broken_files(broken_files_args, &stop_receiver, &progress_sender),
Commands::SimilarVideos(similar_videos_args) => similar_videos(similar_videos_args, &stop_receiver, &progress_sender),
Commands::BadExtensions(bad_extensions_args) => bad_extensions(bad_extensions_args, &stop_receiver, &progress_sender),
Commands::Tester {} => {
test_image_conversion_speed();
}
}
});
ctrlc::set_handler(move || {
println!("Get Sender");
stop_sender.send(()).expect("Could not send signal on channel.");
})
.expect("Error setting Ctrl-C handler");
connect_progress(&progress_receiver);
calculate_thread.join().unwrap();
}
fn duplicates(duplicates: DuplicatesArgs) {
fn duplicates(duplicates: DuplicatesArgs, stop_receiver: &Receiver<()>, progress_sender: &Sender<ProgressData>) {
let DuplicatesArgs {
thread_number,
directories,
excluded_directories,
excluded_items,
common_cli_items,
minimal_file_size,
maximal_file_size,
minimal_cached_file_size,
allowed_extensions,
search_method,
delete_method,
hash_type,
file_to_save,
json_compact_file_to_save,
json_pretty_file_to_save,
not_recursive,
#[cfg(target_family = "unix")]
exclude_other_filesystems,
allow_hard_links,
dry_run,
case_sensitive_name_comparison,
} = duplicates;
set_number_of_threads(thread_number.thread_number);
let mut item = DuplicateFinder::new();
item.set_included_directory(directories.directories);
item.set_excluded_directory(excluded_directories.excluded_directories);
item.set_excluded_items(excluded_items.excluded_items);
set_common_settings(&mut item, &common_cli_items);
item.set_minimal_file_size(minimal_file_size);
item.set_maximal_file_size(maximal_file_size);
item.set_minimal_cache_file_size(minimal_cached_file_size);
item.set_allowed_extensions(allowed_extensions.allowed_extensions.join(","));
item.set_check_method(search_method);
item.set_delete_method(delete_method.delete_method);
item.set_hash_type(hash_type);
item.set_recursive_search(!not_recursive.not_recursive);
#[cfg(target_family = "unix")]
item.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems);
item.set_ignore_hard_links(!allow_hard_links.allow_hard_links);
item.set_dry_run(dry_run.dry_run);
item.set_case_sensitive_name_comparison(case_sensitive_name_comparison.case_sensitive_name_comparison);
item.find_duplicates(None, None);
save_results_to_files(file_to_save.file_name(), json_compact_file_to_save.file_name(), json_pretty_file_to_save.file_name(), &item);
item.find_duplicates(Some(stop_receiver), Some(progress_sender));
if !cfg!(debug_assertions) {
item.print_results_to_output();
}
item.get_text_messages().print_messages();
save_and_print_results(&mut item, &common_cli_items);
}
fn empty_folders(empty_folders: EmptyFoldersArgs) {
let EmptyFoldersArgs {
thread_number,
directories,
delete_folders,
file_to_save,
json_compact_file_to_save,
json_pretty_file_to_save,
excluded_directories,
excluded_items,
#[cfg(target_family = "unix")]
exclude_other_filesystems,
} = empty_folders;
set_number_of_threads(thread_number.thread_number);
fn empty_folders(empty_folders: EmptyFoldersArgs, stop_receiver: &Receiver<()>, progress_sender: &Sender<ProgressData>) {
let EmptyFoldersArgs { common_cli_items, delete_folders } = empty_folders;
let mut item = EmptyFolder::new();
item.set_included_directory(directories.directories);
item.set_excluded_directory(excluded_directories.excluded_directories);
item.set_excluded_items(excluded_items.excluded_items);
set_common_settings(&mut item, &common_cli_items);
if delete_folders {
item.set_delete_method(DeleteMethod::Delete);
}
#[cfg(target_family = "unix")]
item.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems);
item.find_empty_folders(None, None);
save_results_to_files(file_to_save.file_name(), json_compact_file_to_save.file_name(), json_pretty_file_to_save.file_name(), &item);
item.find_empty_folders(Some(stop_receiver), Some(progress_sender));
if !cfg!(debug_assertions) {
item.print_results_to_output();
}
item.get_text_messages().print_messages();
save_and_print_results(&mut item, &common_cli_items);
}
fn biggest_files(biggest_files: BiggestFilesArgs) {
fn biggest_files(biggest_files: BiggestFilesArgs, stop_receiver: &Receiver<()>, progress_sender: &Sender<ProgressData>) {
let BiggestFilesArgs {
thread_number,
directories,
excluded_directories,
excluded_items,
allowed_extensions,
common_cli_items,
number_of_files,
file_to_save,
json_compact_file_to_save,
json_pretty_file_to_save,
not_recursive,
#[cfg(target_family = "unix")]
exclude_other_filesystems,
delete_files,
smallest_mode,
} = biggest_files;
set_number_of_threads(thread_number.thread_number);
let mut item = BigFile::new();
item.set_included_directory(directories.directories);
item.set_excluded_directory(excluded_directories.excluded_directories);
item.set_excluded_items(excluded_items.excluded_items);
item.set_allowed_extensions(allowed_extensions.allowed_extensions.join(","));
set_common_settings(&mut item, &common_cli_items);
item.set_number_of_files_to_check(number_of_files);
item.set_recursive_search(!not_recursive.not_recursive);
#[cfg(target_family = "unix")]
item.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems);
if delete_files {
item.set_delete_method(DeleteMethod::Delete);
}
@ -184,113 +139,47 @@ fn biggest_files(biggest_files: BiggestFilesArgs) {
item.set_search_mode(SearchMode::SmallestFiles);
}
item.find_big_files(None, None);
save_results_to_files(file_to_save.file_name(), json_compact_file_to_save.file_name(), json_pretty_file_to_save.file_name(), &item);
item.find_big_files(Some(stop_receiver), Some(progress_sender));
if !cfg!(debug_assertions) {
item.print_results_to_output();
}
item.get_text_messages().print_messages();
save_and_print_results(&mut item, &common_cli_items);
}
fn empty_files(empty_files: EmptyFilesArgs) {
let EmptyFilesArgs {
thread_number,
directories,
excluded_directories,
excluded_items,
allowed_extensions,
delete_files,
file_to_save,
json_compact_file_to_save,
json_pretty_file_to_save,
not_recursive,
#[cfg(target_family = "unix")]
exclude_other_filesystems,
} = empty_files;
set_number_of_threads(thread_number.thread_number);
fn empty_files(empty_files: EmptyFilesArgs, stop_receiver: &Receiver<()>, progress_sender: &Sender<ProgressData>) {
let EmptyFilesArgs { common_cli_items, delete_files } = empty_files;
let mut item = EmptyFiles::new();
item.set_included_directory(directories.directories);
item.set_excluded_directory(excluded_directories.excluded_directories);
item.set_excluded_items(excluded_items.excluded_items);
item.set_allowed_extensions(allowed_extensions.allowed_extensions.join(","));
item.set_recursive_search(!not_recursive.not_recursive);
#[cfg(target_family = "unix")]
item.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems);
set_common_settings(&mut item, &common_cli_items);
if delete_files {
item.set_delete_method(DeleteMethod::Delete);
}
item.find_empty_files(None, None);
save_results_to_files(file_to_save.file_name(), json_compact_file_to_save.file_name(), json_pretty_file_to_save.file_name(), &item);
item.find_empty_files(Some(stop_receiver), Some(progress_sender));
if !cfg!(debug_assertions) {
item.print_results_to_output();
}
item.get_text_messages().print_messages();
save_and_print_results(&mut item, &common_cli_items);
}
fn temporary(temporary: TemporaryArgs) {
let TemporaryArgs {
thread_number,
directories,
excluded_directories,
excluded_items,
#[cfg(target_family = "unix")]
exclude_other_filesystems,
delete_files,
file_to_save,
json_compact_file_to_save,
json_pretty_file_to_save,
not_recursive,
} = temporary;
set_number_of_threads(thread_number.thread_number);
fn temporary(temporary: TemporaryArgs, stop_receiver: &Receiver<()>, progress_sender: &Sender<ProgressData>) {
let TemporaryArgs { common_cli_items, delete_files } = temporary;
let mut item = Temporary::new();
item.set_included_directory(directories.directories);
item.set_excluded_directory(excluded_directories.excluded_directories);
item.set_excluded_items(excluded_items.excluded_items);
item.set_recursive_search(!not_recursive.not_recursive);
#[cfg(target_family = "unix")]
item.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems);
set_common_settings(&mut item, &common_cli_items);
if delete_files {
item.set_delete_method(DeleteMethod::Delete);
}
item.find_temporary_files(None, None);
save_results_to_files(file_to_save.file_name(), json_compact_file_to_save.file_name(), json_pretty_file_to_save.file_name(), &item);
item.find_temporary_files(Some(stop_receiver), Some(progress_sender));
if !cfg!(debug_assertions) {
item.print_results_to_output();
}
item.get_text_messages().print_messages();
save_and_print_results(&mut item, &common_cli_items);
}
fn similar_images(similar_images: SimilarImagesArgs) {
fn similar_images(similar_images: SimilarImagesArgs, stop_receiver: &Receiver<()>, progress_sender: &Sender<ProgressData>) {
let SimilarImagesArgs {
thread_number,
directories,
excluded_directories,
excluded_items,
file_to_save,
json_compact_file_to_save,
json_pretty_file_to_save,
common_cli_items,
minimal_file_size,
maximal_file_size,
similarity_preset,
not_recursive,
#[cfg(target_family = "unix")]
exclude_other_filesystems,
hash_alg,
image_filter,
hash_size,
@ -298,49 +187,27 @@ fn similar_images(similar_images: SimilarImagesArgs) {
dry_run,
} = similar_images;
set_number_of_threads(thread_number.thread_number);
let mut item = SimilarImages::new();
item.set_included_directory(directories.directories);
item.set_excluded_directory(excluded_directories.excluded_directories);
item.set_excluded_items(excluded_items.excluded_items);
set_common_settings(&mut item, &common_cli_items);
item.set_minimal_file_size(minimal_file_size);
item.set_maximal_file_size(maximal_file_size);
item.set_recursive_search(!not_recursive.not_recursive);
#[cfg(target_family = "unix")]
item.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems);
item.set_image_filter(image_filter);
item.set_hash_alg(hash_alg);
item.set_hash_size(hash_size);
item.set_delete_method(delete_method.delete_method);
item.set_dry_run(dry_run.dry_run);
item.set_similarity(return_similarity_from_similarity_preset(&similarity_preset, hash_size));
item.find_similar_images(None, None);
save_results_to_files(file_to_save.file_name(), json_compact_file_to_save.file_name(), json_pretty_file_to_save.file_name(), &item);
item.find_similar_images(Some(stop_receiver), Some(progress_sender));
if !cfg!(debug_assertions) {
item.print_results_to_output();
}
item.get_text_messages().print_messages();
save_and_print_results(&mut item, &common_cli_items);
}
fn same_music(same_music: SameMusicArgs) {
fn same_music(same_music: SameMusicArgs, stop_receiver: &Receiver<()>, progress_sender: &Sender<ProgressData>) {
let SameMusicArgs {
thread_number,
directories,
excluded_directories,
excluded_items,
common_cli_items,
delete_method,
file_to_save,
json_compact_file_to_save,
json_pretty_file_to_save,
not_recursive,
#[cfg(target_family = "unix")]
exclude_other_filesystems,
minimal_file_size,
maximal_file_size,
music_similarity,
@ -350,18 +217,11 @@ fn same_music(same_music: SameMusicArgs) {
search_method,
} = same_music;
set_number_of_threads(thread_number.thread_number);
let mut item = SameMusic::new();
item.set_included_directory(directories.directories);
item.set_excluded_directory(excluded_directories.excluded_directories);
item.set_excluded_items(excluded_items.excluded_items);
set_common_settings(&mut item, &common_cli_items);
item.set_minimal_file_size(minimal_file_size);
item.set_maximal_file_size(maximal_file_size);
item.set_recursive_search(!not_recursive.not_recursive);
#[cfg(target_family = "unix")]
item.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems);
item.set_music_similarity(music_similarity);
item.set_delete_method(delete_method.delete_method);
item.set_dry_run(dry_run.dry_run);
@ -369,197 +229,111 @@ fn same_music(same_music: SameMusicArgs) {
item.set_maximum_difference(maximum_difference);
item.set_check_type(search_method);
item.find_same_music(None, None);
save_results_to_files(file_to_save.file_name(), json_compact_file_to_save.file_name(), json_pretty_file_to_save.file_name(), &item);
item.find_same_music(Some(stop_receiver), Some(progress_sender));
if !cfg!(debug_assertions) {
item.print_results_to_output();
}
item.get_text_messages().print_messages();
save_and_print_results(&mut item, &common_cli_items);
}
fn invalid_symlinks(invalid_symlinks: InvalidSymlinksArgs) {
let InvalidSymlinksArgs {
thread_number,
directories,
excluded_directories,
excluded_items,
allowed_extensions,
file_to_save,
json_compact_file_to_save,
json_pretty_file_to_save,
not_recursive,
#[cfg(target_family = "unix")]
exclude_other_filesystems,
delete_files,
} = invalid_symlinks;
set_number_of_threads(thread_number.thread_number);
fn invalid_symlinks(invalid_symlinks: InvalidSymlinksArgs, stop_receiver: &Receiver<()>, progress_sender: &Sender<ProgressData>) {
let InvalidSymlinksArgs { common_cli_items, delete_files } = invalid_symlinks;
let mut item = InvalidSymlinks::new();
item.set_included_directory(directories.directories);
item.set_excluded_directory(excluded_directories.excluded_directories);
item.set_excluded_items(excluded_items.excluded_items);
item.set_allowed_extensions(allowed_extensions.allowed_extensions.join(","));
item.set_recursive_search(!not_recursive.not_recursive);
#[cfg(target_family = "unix")]
item.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems);
set_common_settings(&mut item, &common_cli_items);
if delete_files {
item.set_delete_method(DeleteMethod::Delete);
}
item.find_invalid_links(None, None);
save_results_to_files(file_to_save.file_name(), json_compact_file_to_save.file_name(), json_pretty_file_to_save.file_name(), &item);
item.find_invalid_links(Some(stop_receiver), Some(progress_sender));
if !cfg!(debug_assertions) {
item.print_results_to_output();
}
item.get_text_messages().print_messages();
save_and_print_results(&mut item, &common_cli_items);
}
fn broken_files(broken_files: BrokenFilesArgs) {
let BrokenFilesArgs {
thread_number,
directories,
excluded_directories,
excluded_items,
allowed_extensions,
delete_files,
file_to_save,
json_compact_file_to_save,
json_pretty_file_to_save,
not_recursive,
#[cfg(target_family = "unix")]
exclude_other_filesystems,
} = broken_files;
set_number_of_threads(thread_number.thread_number);
fn broken_files(broken_files: BrokenFilesArgs, stop_receiver: &Receiver<()>, progress_sender: &Sender<ProgressData>) {
let BrokenFilesArgs { common_cli_items, delete_files } = broken_files;
let mut item = BrokenFiles::new();
item.set_included_directory(directories.directories);
item.set_excluded_directory(excluded_directories.excluded_directories);
item.set_excluded_items(excluded_items.excluded_items);
item.set_allowed_extensions(allowed_extensions.allowed_extensions.join(","));
item.set_recursive_search(!not_recursive.not_recursive);
#[cfg(target_family = "unix")]
item.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems);
set_common_settings(&mut item, &common_cli_items);
if delete_files {
item.set_delete_method(DeleteMethod::Delete);
}
item.find_broken_files(None, None);
save_results_to_files(file_to_save.file_name(), json_compact_file_to_save.file_name(), json_pretty_file_to_save.file_name(), &item);
item.find_broken_files(Some(stop_receiver), Some(progress_sender));
if !cfg!(debug_assertions) {
item.print_results_to_output();
}
item.get_text_messages().print_messages();
save_and_print_results(&mut item, &common_cli_items);
}
fn similar_videos(similar_videos: SimilarVideosArgs) {
fn similar_videos(similar_videos: SimilarVideosArgs, stop_receiver: &Receiver<()>, progress_sender: &Sender<ProgressData>) {
let SimilarVideosArgs {
thread_number,
directories,
excluded_directories,
excluded_items,
file_to_save,
json_compact_file_to_save,
json_pretty_file_to_save,
not_recursive,
#[cfg(target_family = "unix")]
exclude_other_filesystems,
common_cli_items,
tolerance,
minimal_file_size,
maximal_file_size,
allowed_extensions,
delete_method,
dry_run,
} = similar_videos;
set_number_of_threads(thread_number.thread_number);
let mut item = SimilarVideos::new();
item.set_included_directory(directories.directories);
item.set_excluded_directory(excluded_directories.excluded_directories);
item.set_excluded_items(excluded_items.excluded_items);
item.set_allowed_extensions(allowed_extensions.allowed_extensions.join(","));
item.set_recursive_search(!not_recursive.not_recursive);
#[cfg(target_family = "unix")]
item.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems);
set_common_settings(&mut item, &common_cli_items);
item.set_minimal_file_size(minimal_file_size);
item.set_maximal_file_size(maximal_file_size);
item.set_tolerance(tolerance);
item.set_delete_method(delete_method.delete_method);
item.set_dry_run(dry_run.dry_run);
item.find_similar_videos(None, None);
save_results_to_files(file_to_save.file_name(), json_compact_file_to_save.file_name(), json_pretty_file_to_save.file_name(), &item);
item.find_similar_videos(Some(stop_receiver), Some(progress_sender));
if !cfg!(debug_assertions) {
item.print_results_to_output();
}
item.get_text_messages().print_messages();
save_and_print_results(&mut item, &common_cli_items);
}
fn bad_extensions(bad_extensions: BadExtensionsArgs) {
let BadExtensionsArgs {
thread_number,
directories,
excluded_directories,
excluded_items,
file_to_save,
json_compact_file_to_save,
json_pretty_file_to_save,
not_recursive,
#[cfg(target_family = "unix")]
exclude_other_filesystems,
allowed_extensions,
} = bad_extensions;
set_number_of_threads(thread_number.thread_number);
fn bad_extensions(bad_extensions: BadExtensionsArgs, stop_receiver: &Receiver<()>, progress_sender: &Sender<ProgressData>) {
let BadExtensionsArgs { common_cli_items } = bad_extensions;
let mut item = BadExtensions::new();
item.set_included_directory(directories.directories);
item.set_excluded_directory(excluded_directories.excluded_directories);
item.set_excluded_items(excluded_items.excluded_items);
item.set_allowed_extensions(allowed_extensions.allowed_extensions.join(","));
item.set_recursive_search(!not_recursive.not_recursive);
#[cfg(target_family = "unix")]
item.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems);
item.find_bad_extensions_files(None, None);
set_common_settings(&mut item, &common_cli_items);
save_results_to_files(file_to_save.file_name(), json_compact_file_to_save.file_name(), json_pretty_file_to_save.file_name(), &item);
item.find_bad_extensions_files(Some(stop_receiver), Some(progress_sender));
if !cfg!(debug_assertions) {
item.print_results_to_output();
}
item.get_text_messages().print_messages();
save_and_print_results(&mut item, &common_cli_items);
}
fn save_results_to_files<T: PrintResults>(txt_file_name: Option<&str>, compact_json_file_name: Option<&str>, pretty_json_file_name: Option<&str>, item: &T) {
if let Some(file_name) = txt_file_name {
if let Err(e) = item.print_results_to_file(file_name) {
fn save_and_print_results<T: CommonData + PrintResults>(component: &mut T, common_cli_items: &CommonCliItems) {
if let Some(file_name) = common_cli_items.file_to_save.file_name() {
if let Err(e) = component.print_results_to_file(file_name) {
error!("Failed to save results to file {e}");
}
}
if let Some(file_name) = compact_json_file_name {
if let Err(e) = item.save_results_to_file_as_json(file_name, false) {
if let Some(file_name) = common_cli_items.json_compact_file_to_save.file_name() {
if let Err(e) = component.save_results_to_file_as_json(file_name, false) {
error!("Failed to save compact json results to file {e}");
}
}
if let Some(file_name) = pretty_json_file_name {
if let Err(e) = item.save_results_to_file_as_json(file_name, true) {
if let Some(file_name) = common_cli_items.json_pretty_file_to_save.file_name() {
if let Err(e) = component.save_results_to_file_as_json(file_name, true) {
error!("Failed to save pretty json results to file {e}");
}
}
if !cfg!(debug_assertions) {
component.print_results_to_output();
}
component.get_text_messages().print_messages();
}
fn set_common_settings<T>(component: &mut T, common_cli_items: &CommonCliItems)
where
T: CommonData + PrintResults,
{
set_number_of_threads(common_cli_items.thread_number);
component.set_included_directory(common_cli_items.directories.clone());
component.set_excluded_directory(common_cli_items.excluded_directories.clone());
component.set_excluded_items(common_cli_items.excluded_items.clone());
component.set_recursive_search(!common_cli_items.not_recursive);
#[cfg(target_family = "unix")]
component.set_exclude_other_filesystems(common_cli_items.exclude_other_filesystems);
component.set_allowed_extensions(common_cli_items.allowed_extensions.clone().join(","));
}

@ -0,0 +1,93 @@
use std::time::Duration;
use crossbeam_channel::Receiver;
use indicatif::{ProgressBar, ProgressStyle};
use czkawka_core::common_dir_traversal::{CheckingMethod, ProgressData, ToolType};
pub fn connect_progress(progress_receiver: &Receiver<ProgressData>) {
let mut pb = ProgressBar::new(1);
let mut latest_id = None;
while let Ok(progress_data) = progress_receiver.recv() {
if latest_id != Some(progress_data.current_stage) {
pb.finish_and_clear();
if progress_data.current_stage == 0 {
pb = get_progress_bar_for_collect_files();
} else if check_if_saving_cache(&progress_data) || check_if_loading_cache(&progress_data) {
pb = get_progress_loading_saving_cache(check_if_loading_cache(&progress_data));
} else {
pb = get_progress_known_values(progress_data.entries_to_check, &get_progress_message(&progress_data));
}
latest_id = Some(progress_data.current_stage);
}
pb.set_position(progress_data.entries_checked as u64);
if progress_data.current_stage == 0 && progress_data.tool_type != ToolType::EmptyFolders {
pb.set_message(format!("Collecting files: {}", progress_data.entries_checked));
} else if progress_data.current_stage == 0 {
pb.set_message(format!("Collecting folders: {}", progress_data.entries_checked));
}
}
pb.finish();
}
pub fn get_progress_message(progress_data: &ProgressData) -> String {
match (progress_data.tool_type, progress_data.current_stage, progress_data.checking_method) {
(ToolType::SameMusic, 2, CheckingMethod::AudioTags) | (ToolType::SameMusic, 5, CheckingMethod::AudioContent) => "Reading tags",
(ToolType::SameMusic, 2, CheckingMethod::AudioContent) => "Calculating fingerprint",
(ToolType::SameMusic, 4, CheckingMethod::AudioTags) => "Comparing tags",
(ToolType::SameMusic, 4, CheckingMethod::AudioContent) => "Comparing fingerprint",
(ToolType::Duplicate, 2, CheckingMethod::Hash) => "Reading prehashes",
(ToolType::Duplicate, 5, CheckingMethod::Hash) => "Reading hashes",
(ToolType::SimilarImages, 1, _) => "Reading images",
(ToolType::SimilarImages, 2, _) => "Comparing image hashes",
(ToolType::SimilarVideos, 1, _) => "Reading similar values",
(ToolType::BrokenFiles, 1, _) => "Checking broken files",
(ToolType::BadExtensions, 1, _) => "Checking extensions of files",
_ => unreachable!(),
}
.to_string()
}
pub fn check_if_loading_cache(progress_data: &ProgressData) -> bool {
matches!(
(progress_data.tool_type, progress_data.current_stage),
(ToolType::SameMusic, 1) | (ToolType::Duplicate, 1 | 4)
)
}
pub fn check_if_saving_cache(progress_data: &ProgressData) -> bool {
matches!(
(progress_data.tool_type, progress_data.current_stage),
(ToolType::SameMusic, 3) | (ToolType::Duplicate, 3 | 6)
)
}
pub fn get_progress_bar_for_collect_files() -> ProgressBar {
let pb = ProgressBar::new_spinner();
pb.enable_steady_tick(Duration::from_millis(120));
pb.set_style(
ProgressStyle::with_template("{msg} {spinner:.blue}")
.unwrap()
.tick_strings(&["▹▹▹▹▹", "▸▹▹▹▹", "▹▸▹▹▹", "▹▹▸▹▹", "▹▹▹▸▹", "▹▹▹▹▸", "▪▪▪▪▪"]),
);
pb
}
pub fn get_progress_known_values(max_value: usize, msg: &str) -> ProgressBar {
let pb = ProgressBar::new(max_value as u64);
pb.set_style(ProgressStyle::with_template(&format!("{msg} [{{bar}}] {{pos}}/{{len}} ")).unwrap().progress_chars("=> "));
pb
}
pub fn get_progress_loading_saving_cache(loading: bool) -> ProgressBar {
let msg = if loading { "Loading cache" } else { "Saving cache" };
let pb = ProgressBar::new_spinner();
pb.enable_steady_tick(Duration::from_millis(120));
pb.set_style(
ProgressStyle::with_template(&format!("{msg} {{spinner:.blue}}"))
.unwrap()
.tick_strings(&["▹▹▹▹▹", "▸▹▹▹▹", "▹▸▹▹▹", "▹▹▸▹▹", "▹▹▹▸▹", "▹▹▹▹▸", "▪▪▪▪▪"]),
);
pb
}

@ -156,17 +156,17 @@ impl BrokenFiles {
match result {
DirTraversalResult::SuccessFiles { grouped_file_entries, warnings } => {
self.broken_files = grouped_file_entries
self.files_to_check = grouped_file_entries
.into_values()
.flatten()
.map(|fe| {
let mut broken_entry = fe.into_broken_entry();
broken_entry.type_of_file = check_extension_availability(broken_entry.get_path(), &images_extensions, &zip_extensions, &audio_extensions, &pdf_extensions);
broken_entry
(broken_entry.path.to_string_lossy().to_string(), broken_entry)
})
.collect();
self.common_data.text_messages.warnings.extend(warnings);
debug!("check_files - Found {} image files.", self.broken_files.len());
debug!("check_files - Found {} files to check.", self.files_to_check.len());
true
}
@ -451,16 +451,18 @@ fn check_extension_availability(
debug_assert!(false, "Extension not really fully str");
return TypeOfFile::Unknown;
};
let extension_lowercase = extension_str.to_ascii_lowercase();
if images_extensions.contains(&extension_str) {
if images_extensions.contains(&extension_lowercase.as_str()) {
TypeOfFile::Image
} else if zip_extensions.contains(&extension_str) {
} else if zip_extensions.contains(&extension_lowercase.as_str()) {
TypeOfFile::ArchiveZip
} else if audio_extensions.contains(&extension_str) {
} else if audio_extensions.contains(&extension_lowercase.as_str()) {
TypeOfFile::Audio
} else if pdf_extensions.contains(&extension_str) {
} else if pdf_extensions.contains(&extension_lowercase.as_str()) {
TypeOfFile::PDF
} else {
eprintln!("File with unknown extension: {full_name:?} - {extension_lowercase}");
debug_assert!(false, "File with unknown extension");
TypeOfFile::Unknown
}

@ -562,7 +562,8 @@ pub fn prepare_thread_handler_common(
let progress_thread_run = progress_thread_run.clone();
let atomic_counter = atomic_counter.clone();
thread::spawn(move || {
let mut time_since_last_send = SystemTime::now();
// Use earlier time, to send immediately first message
let mut time_since_last_send = SystemTime::now() - Duration::from_secs(10u64);
loop {
if time_since_last_send.elapsed().unwrap().as_millis() > SEND_PROGRESS_DATA_TIME_BETWEEN as u128 {

@ -268,7 +268,7 @@ impl DuplicateFinder {
.group_by(group_by_func)
.stop_receiver(stop_receiver)
.progress_sender(progress_sender)
.checking_method(CheckingMethod::Name)
.checking_method(CheckingMethod::SizeName)
.build()
.run();

@ -193,6 +193,7 @@ impl SameMusic {
.stop_receiver(stop_receiver)
.progress_sender(progress_sender)
.common_data(&self.common_data)
.checking_method(self.check_type)
.max_stage(max_stage)
.build()
.run();

@ -19,6 +19,58 @@ use crate::localizer_core::generate_translation_hashmap;
use crate::taskbar_progress::tbp_flags::TBPF_INDETERMINATE;
use crate::taskbar_progress::TaskbarProgress;
// Empty files
// 0 - Collecting files
// Empty folders
// 0 - Collecting folders
// Big files
// 0 - Collecting files
// Same music
// 0 - Collecting files
// 1 - Loading cache
// 2 - Checking tags / content
// 3 - Saving cache
// 4 - Checking tags / content - progress
// 5 - Only content - ending
// Similar images
// 0 - Collecting files
// 1 - Scanning images
// 2 - Comparing hashes
// Similar videos
// 0 - Collecting files
// 1 - Scanning videos
// Temporary files
// 0 - Collecting files
// Invalid symlinks
// 0 - Collecting files
// Broken files
// 0 - Collecting files
// 1 - Scanning files
// Bad extensions
// 0 - Collecting files
// 1 - Scanning files
// Duplicates - Hash
// 0 - Collecting files
// 1 - Loading cache
// 2 - Hash - first 1KB file
// 3 - Saving cache
// 4 - Loading cache
// 5 - Hash - normal hash
// 6 - Saving cache
// Duplicates - Name or SizeName or Size
// 0 - Collecting files
#[allow(clippy::too_many_arguments)]
pub fn connect_progress_window(gui_data: &GuiData, progress_receiver: Receiver<ProgressData>) {
let main_context = MainContext::default();
@ -31,19 +83,12 @@ pub fn connect_progress_window(gui_data: &GuiData, progress_receiver: Receiver<P
loop {
let item = progress_receiver.try_recv();
if let Ok(item) = item {
match item.tool_type {
ToolType::Duplicate => process_bar_duplicates(&gui_data, &item),
ToolType::EmptyFiles => process_bar_empty_files(&gui_data, &item),
ToolType::EmptyFolders => process_bar_empty_folder(&gui_data, &item),
ToolType::BigFile => process_bar_big_files(&gui_data, &item),
ToolType::SameMusic => process_bar_same_music(&gui_data, &item),
ToolType::SimilarImages => process_bar_similar_images(&gui_data, &item),
ToolType::SimilarVideos => process_bar_similar_videos(&gui_data, &item),
ToolType::TemporaryFiles => process_bar_temporary(&gui_data, &item),
ToolType::InvalidSymlinks => process_bar_invalid_symlinks(&gui_data, &item),
ToolType::BrokenFiles => process_bar_broken_files(&gui_data, &item),
ToolType::BadExtensions => process_bar_bad_extensions(&gui_data, &item),
ToolType::None => panic!(),
if item.current_stage == 0 {
progress_collect_items(&gui_data, &item, item.tool_type != ToolType::EmptyFolders);
} else if check_if_loading_saving_cache(&item) {
progress_save_load_cache(&gui_data, &item);
} else {
progress_default(&gui_data, &item);
}
} else {
break;
@ -55,278 +100,123 @@ pub fn connect_progress_window(gui_data: &GuiData, progress_receiver: Receiver<P
main_context.spawn_local(future);
}
fn process_bar_empty_files(gui_data: &GuiData, item: &ProgressData) {
let label_stage = gui_data.progress_window.label_stage.clone();
let taskbar_state = gui_data.taskbar_state.clone();
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(item)));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
pub fn check_if_loading_saving_cache(progress_data: &ProgressData) -> bool {
matches!(
(progress_data.tool_type, progress_data.current_stage),
(ToolType::SameMusic, 1 | 3) | (ToolType::Duplicate, 1 | 3 | 4 | 6)
)
}
fn process_bar_empty_folder(gui_data: &GuiData, item: &ProgressData) {
let label_stage = gui_data.progress_window.label_stage.clone();
let taskbar_state = gui_data.taskbar_state.clone();
label_stage.set_text(&flg!(
"progress_scanning_empty_folders",
generate_translation_hashmap(vec![("folder_number", item.entries_checked.to_string())])
));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
}
fn process_bar_big_files(gui_data: &GuiData, item: &ProgressData) {
let label_stage = gui_data.progress_window.label_stage.clone();
let taskbar_state = gui_data.taskbar_state.clone();
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(item)));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
}
fn process_bar_same_music(gui_data: &GuiData, item: &ProgressData) {
fn progress_save_load_cache(gui_data: &GuiData, item: &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 taskbar_state = gui_data.taskbar_state.clone();
match item.current_stage {
0 => {
progress_bar_current_stage.hide();
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(item)));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
progress_bar_current_stage.hide();
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
let text = match (item.tool_type, item.checking_method, item.current_stage) {
(ToolType::SameMusic, CheckingMethod::AudioTags | CheckingMethod::AudioContent, 1) => {
flg!("progress_cache_loading")
}
// Loading cache
1 => {
progress_bar_current_stage.hide();
common_set_data(item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
label_stage.set_text(&flg!("progress_cache_loading"));
(ToolType::SameMusic, CheckingMethod::AudioTags | CheckingMethod::AudioContent, 3) => {
flg!("progress_cache_saving")
}
2 => {
progress_bar_current_stage.show();
common_set_data(item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
match item.checking_method {
CheckingMethod::AudioTags => label_stage.set_text(&flg!("progress_scanning_music_tags", progress_ratio_tm(item))),
CheckingMethod::AudioContent => label_stage.set_text(&flg!("progress_scanning_music_content", progress_ratio_tm(item))),
_ => panic!(),
}
(ToolType::Duplicate, CheckingMethod::Hash, 1) => {
flg!("progress_prehash_cache_loading")
}
// Saving cache
3 => {
progress_bar_current_stage.hide();
common_set_data(item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
label_stage.set_text(&flg!("progress_cache_saving"));
(ToolType::Duplicate, CheckingMethod::Hash, 3) => {
flg!("progress_prehash_cache_saving")
}
4 => {
progress_bar_current_stage.show();
common_set_data(item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
match item.checking_method {
CheckingMethod::AudioTags => label_stage.set_text(&flg!("progress_scanning_music_tags_end", progress_ratio_tm(item))),
CheckingMethod::AudioContent => label_stage.set_text(&flg!("progress_scanning_music_content_end", progress_ratio_tm(item))),
_ => panic!(),
}
(ToolType::Duplicate, CheckingMethod::Hash, 4) => {
flg!("progress_hash_cache_loading")
}
5 => {
progress_bar_current_stage.show();
common_set_data(item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
if item.checking_method == CheckingMethod::AudioContent {
label_stage.set_text(&flg!("progress_scanning_music_tags", progress_ratio_tm(item)));
} else {
panic!();
}
(ToolType::Duplicate, CheckingMethod::Hash, 6) => {
flg!("progress_hash_cache_saving")
}
_ => panic!(),
}
};
label_stage.set_text(&text);
}
fn process_bar_similar_images(gui_data: &GuiData, item: &ProgressData) {
fn progress_collect_items(gui_data: &GuiData, item: &ProgressData, files: bool) {
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 taskbar_state = gui_data.taskbar_state.clone();
match item.current_stage {
0 => {
progress_bar_current_stage.hide();
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(item)));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
progress_bar_current_stage.hide();
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
match (item.tool_type, item.checking_method) {
(ToolType::Duplicate, CheckingMethod::Name) => {
label_stage.set_text(&flg!("progress_scanning_name", file_number_tm(item)));
return;
}
1 => {
progress_bar_current_stage.show();
common_set_data(item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
label_stage.set_text(&flg!("progress_scanning_image", progress_ratio_tm(item)));
(ToolType::Duplicate, CheckingMethod::SizeName) => {
label_stage.set_text(&flg!("progress_scanning_size_name", file_number_tm(item)));
return;
}
2 => {
progress_bar_current_stage.show();
common_set_data(item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
label_stage.set_text(&flg!("progress_comparing_image_hashes", progress_ratio_tm(item)));
(ToolType::Duplicate, CheckingMethod::Size | CheckingMethod::Hash) => {
label_stage.set_text(&flg!("progress_scanning_size", file_number_tm(item)));
return;
}
_ => panic!(),
_ => {}
}
}
fn process_bar_similar_videos(gui_data: &GuiData, item: &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 taskbar_state = gui_data.taskbar_state.clone();
match item.current_stage {
0 => {
progress_bar_current_stage.hide();
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(item)));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
}
1 => {
progress_bar_current_stage.show();
common_set_data(item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
label_stage.set_text(&flg!("progress_scanning_video", progress_ratio_tm(item)));
}
_ => panic!(),
if files {
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(item)));
} else {
label_stage.set_text(&flg!(
"progress_scanning_empty_folders",
generate_translation_hashmap(vec![("folder_number", item.entries_checked.to_string())])
));
}
}
fn process_bar_temporary(gui_data: &GuiData, item: &ProgressData) {
let label_stage = gui_data.progress_window.label_stage.clone();
let taskbar_state = gui_data.taskbar_state.clone();
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(item)));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
}
fn process_bar_invalid_symlinks(gui_data: &GuiData, item: &ProgressData) {
let label_stage = gui_data.progress_window.label_stage.clone();
let taskbar_state = gui_data.taskbar_state.clone();
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(item)));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
}
fn process_bar_broken_files(gui_data: &GuiData, item: &ProgressData) {
fn progress_default(gui_data: &GuiData, item: &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 taskbar_state = gui_data.taskbar_state.clone();
match item.current_stage {
0 => {
progress_bar_current_stage.hide();
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(item)));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
progress_bar_current_stage.show();
common_set_data(item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
match (item.tool_type, item.checking_method, item.current_stage) {
(ToolType::SameMusic, CheckingMethod::AudioTags, 2) | (ToolType::SameMusic, CheckingMethod::AudioContent, 5) => {
label_stage.set_text(&flg!("progress_scanning_music_tags", progress_ratio_tm(item)));
}
1 => {
progress_bar_current_stage.show();
common_set_data(item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
label_stage.set_text(&flg!("progress_scanning_broken_files", progress_ratio_tm(item)));
(ToolType::SameMusic, CheckingMethod::AudioContent, 2) => {
label_stage.set_text(&flg!("progress_scanning_music_content", progress_ratio_tm(item)));
}
_ => panic!(),
}
}
fn process_bar_bad_extensions(gui_data: &GuiData, item: &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 taskbar_state = gui_data.taskbar_state.clone();
match item.current_stage {
0 => {
progress_bar_current_stage.hide();
label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(item)));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
(ToolType::SameMusic, CheckingMethod::AudioTags, 4) => {
label_stage.set_text(&flg!("progress_scanning_music_tags_end", progress_ratio_tm(item)));
}
1 => {
progress_bar_current_stage.show();
common_set_data(item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
label_stage.set_text(&flg!("progress_scanning_extension_of_files", progress_ratio_tm(item)));
(ToolType::SameMusic, CheckingMethod::AudioContent, 4) => {
label_stage.set_text(&flg!("progress_scanning_music_content_end", progress_ratio_tm(item)));
}
_ => panic!(),
}
}
fn process_bar_duplicates(gui_data: &GuiData, item: &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();
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.set_fraction(0f64);
label_stage.set_text(&flg!("progress_scanning_size", file_number_tm(item)));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
}
// Loading cache
1 | 4 => {
progress_bar_current_stage.hide();
common_set_data(item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
if item.current_stage == 1 {
label_stage.set_text(&flg!("progress_prehash_cache_loading"));
} else {
label_stage.set_text(&flg!("progress_hash_cache_loading"));
}
}
// Saving cache
3 | 6 => {
progress_bar_current_stage.hide();
common_set_data(item, &progress_bar_all_stages, &progress_bar_current_stage, &taskbar_state);
if item.current_stage == 3 {
label_stage.set_text(&flg!("progress_prehash_cache_saving"));
} else {
label_stage.set_text(&flg!("progress_hash_cache_saving"));
}
}
// Hash - first 1KB file
2 => {
progress_bar_current_stage.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
5 => {
progress_bar_current_stage.show();
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");
}
}
(ToolType::SimilarImages, _, 1) => {
label_stage.set_text(&flg!("progress_scanning_image", progress_ratio_tm(item)));
}
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);
(ToolType::SimilarImages, _, 2) => {
label_stage.set_text(&flg!("progress_comparing_image_hashes", progress_ratio_tm(item)));
}
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);
(ToolType::SimilarVideos, _, 1) => {
label_stage.set_text(&flg!("progress_scanning_video", progress_ratio_tm(item)));
}
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);
(ToolType::BrokenFiles, _, 1) => {
label_stage.set_text(&flg!("progress_scanning_broken_files", progress_ratio_tm(item)));
}
_ => panic!(),
};
(ToolType::BadExtensions, _, 1) => {
label_stage.set_text(&flg!("progress_scanning_extension_of_files", progress_ratio_tm(item)));
}
(ToolType::Duplicate, CheckingMethod::Hash, 2) => {
label_stage.set_text(&flg!("progress_analyzed_partial_hash", progress_ratio_tm(item)));
}
(ToolType::Duplicate, CheckingMethod::Hash, 5) => {
label_stage.set_text(&flg!("progress_analyzed_full_hash", progress_ratio_tm(item)));
}
_ => unreachable!(),
}
}
fn common_set_data(item: &ProgressData, progress_bar_all_stages: &ProgressBar, progress_bar_current_stage: &ProgressBar, taskbar_state: &Rc<RefCell<TaskbarProgress>>) {

Loading…
Cancel
Save