diff --git a/czkawka_core/src/temporary.rs b/czkawka_core/src/temporary.rs index c0f16fd..649f257 100644 --- a/czkawka_core/src/temporary.rs +++ b/czkawka_core/src/temporary.rs @@ -32,11 +32,20 @@ const TEMP_EXTENSIONS: &[&str] = &[ ]; #[derive(Clone, Serialize, Debug)] -pub struct FileEntry { +pub struct TemporaryFileEntry { pub path: PathBuf, pub modified_date: u64, } +impl TemporaryFileEntry { + pub fn get_path(&self) -> &PathBuf { + &self.path + } + pub fn get_modified_date(&self) -> u64 { + self.modified_date + } +} + #[derive(Default)] pub struct Info { pub number_of_temporary_files: usize, @@ -45,7 +54,7 @@ pub struct Info { pub struct Temporary { common_data: CommonToolData, information: Info, - temporary_files: Vec, + temporary_files: Vec, } impl Temporary { @@ -138,7 +147,7 @@ impl Temporary { true } - pub fn get_file_entry(&self, atomic_counter: &Arc, entry_data: &DirEntry, warnings: &mut Vec) -> Option { + pub fn get_file_entry(&self, atomic_counter: &Arc, entry_data: &DirEntry, warnings: &mut Vec) -> Option { atomic_counter.fetch_add(1, Ordering::Relaxed); let current_file_name = entry_data.path(); @@ -158,7 +167,7 @@ impl Temporary { }; // Creating new file entry - Some(FileEntry { + Some(TemporaryFileEntry { modified_date: get_modified_time(&metadata, warnings, ¤t_file_name, false), path: current_file_name, }) @@ -234,7 +243,7 @@ impl CommonData for Temporary { } impl Temporary { - pub const fn get_temporary_files(&self) -> &Vec { + pub const fn get_temporary_files(&self) -> &Vec { &self.temporary_files } diff --git a/krokiet/src/common.rs b/krokiet/src/common.rs index fc6b609..89c100d 100644 --- a/krokiet/src/common.rs +++ b/krokiet/src/common.rs @@ -73,12 +73,9 @@ pub enum StrDataEmptyFiles { pub enum IntDataTemporaryFiles { ModificationDatePart1, ModificationDatePart2, - SizePart1, - SizePart2, } #[repr(u8)] pub enum StrDataTemporaryFiles { - Size, Name, Path, ModificationDate, @@ -253,13 +250,12 @@ pub fn get_int_size_idx(active_tab: CurrentTab) -> usize { CurrentTab::SimilarImages => IntDataSimilarImages::SizePart1 as usize, CurrentTab::DuplicateFiles => IntDataDuplicateFiles::SizePart1 as usize, CurrentTab::BigFiles => IntDataBigFiles::SizePart1 as usize, - CurrentTab::TemporaryFiles => IntDataTemporaryFiles::SizePart1 as usize, CurrentTab::SimilarVideos => IntDataSimilarVideos::SizePart1 as usize, CurrentTab::SimilarMusic => IntDataSimilarMusic::SizePart1 as usize, CurrentTab::BrokenFiles => IntDataBrokenFiles::SizePart1 as usize, CurrentTab::BadExtensions => IntDataBadExtensions::SizePart1 as usize, CurrentTab::Settings | CurrentTab::About => panic!("Button should be disabled"), - CurrentTab::EmptyFolders | CurrentTab::InvalidSymlinks => panic!("Unable to get size from this tab"), + CurrentTab::EmptyFolders | CurrentTab::InvalidSymlinks | CurrentTab::TemporaryFiles => panic!("Unable to get size from this tab"), } } diff --git a/krokiet/src/connect_scan.rs b/krokiet/src/connect_scan.rs index 86eafb8..f7a04b7 100644 --- a/krokiet/src/connect_scan.rs +++ b/krokiet/src/connect_scan.rs @@ -22,6 +22,7 @@ use czkawka_core::same_music::{MusicEntry, SameMusic}; use czkawka_core::similar_images; use czkawka_core::similar_images::{ImagesEntry, SimilarImages}; use czkawka_core::similar_videos::{SimilarVideos, VideosEntry}; +use czkawka_core::temporary::{Temporary, TemporaryFileEntry}; use crate::common::split_u64_into_i32s; use crate::settings::{collect_settings, SettingsCustom, ALLOWED_HASH_TYPE_VALUES, ALLOWED_RESIZE_ALGORITHM_VALUES}; @@ -74,8 +75,10 @@ pub fn connect_scan_button(app: &MainWindow, progress_sender: Sender { scan_broken_files(a, progress_sender, stop_receiver, custom_settings); } + CurrentTab::TemporaryFiles => { + scan_temporary_files(a, progress_sender, stop_receiver, custom_settings); + } CurrentTab::Settings | CurrentTab::About => panic!("Button should be disabled"), - _ => unimplemented!(), } }); } @@ -168,7 +171,7 @@ fn write_duplicate_results(app: &MainWindow, vector: Vec<(Option insert_data_to_model(&items, data_model_str, data_model_int, false); } } - app.set_similar_images_model(items.into()); + app.set_duplicate_files_model(items.into()); app.invoke_scan_ended(format!("Found {items_found} similar duplicates files").into()); app.global::().set_info_text(messages.into()); } @@ -257,7 +260,7 @@ fn write_big_files_results(app: &MainWindow, vector: Vec, messages: S let (data_model_str, data_model_int) = prepare_data_model_big_files(&fe); insert_data_to_model(&items, data_model_str, data_model_int, false); } - app.set_empty_folder_model(items.into()); + app.set_big_files_model(items.into()); app.invoke_scan_ended(format!("Found {items_found} files").into()); app.global::().set_info_text(messages.into()); } @@ -592,6 +595,48 @@ fn prepare_data_model_invalid_symlinks(fe: &SymlinksFileEntry) -> (ModelRc, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut finder = Temporary::new(); + set_common_settings(&mut finder, &custom_settings); + finder.find_temporary_files(Some(&stop_receiver), Some(&progress_sender)); + + let mut vector = finder.get_temporary_files().clone(); + let messages = finder.get_text_messages().create_messages_text(); + + vector.par_sort_unstable_by(|a, b| split_path_compare(a.path.as_path(), b.path.as_path())); + + a.upgrade_in_event_loop(move |app| { + crate::connect_scan::write_temporary_files_results(&app, vector, messages); + }) + }) + .unwrap(); +} +fn write_temporary_files_results(app: &MainWindow, vector: Vec, messages: String) { + let items_found = vector.len(); + let items = Rc::new(VecModel::default()); + for fe in vector { + let (data_model_str, data_model_int) = crate::connect_scan::prepare_data_model_temporary_files(&fe); + insert_data_to_model(&items, data_model_str, data_model_int, false); + } + app.set_temporary_files_model(items.into()); + app.invoke_scan_ended(format!("Found {items_found} files").into()); + app.global::().set_info_text(messages.into()); +} + +fn prepare_data_model_temporary_files(fe: &TemporaryFileEntry) -> (ModelRc, ModelRc) { + let (directory, file) = split_path(&fe.path); + let data_model_str = VecModel::from_slice(&[ + file.into(), + directory.into(), + NaiveDateTime::from_timestamp_opt(fe.modified_date as i64, 0).unwrap().to_string().into(), + ]); + let modification_split = split_u64_into_i32s(fe.get_modified_date()); + let data_model_int = VecModel::from_slice(&[modification_split.0, modification_split.1]); + (data_model_str, data_model_int) } ////////////////////////////////////////// Broken Files fn scan_broken_files(a: Weak, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { @@ -620,7 +665,7 @@ fn write_broken_files_results(app: &MainWindow, vector: Vec, messag let (data_model_str, data_model_int) = crate::connect_scan::prepare_data_model_broken_files(&fe); insert_data_to_model(&items, data_model_str, data_model_int, false); } - app.set_empty_folder_model(items.into()); + app.set_broken_files_model(items.into()); app.invoke_scan_ended(format!("Found {items_found} files").into()); app.global::().set_info_text(messages.into()); } @@ -666,7 +711,7 @@ fn write_bad_extensions_results(app: &MainWindow, vector: Vec, mes let (data_model_str, data_model_int) = prepare_data_model_bad_extensions(&fe); insert_data_to_model(&items, data_model_str, data_model_int, false); } - app.set_empty_folder_model(items.into()); + app.set_bad_extensions_model(items.into()); app.invoke_scan_ended(format!("Found {items_found} files with bad extensions").into()); app.global::().set_info_text(messages.into()); } diff --git a/krokiet/src/main.rs b/krokiet/src/main.rs index 49807d9..c9f66e2 100644 --- a/krokiet/src/main.rs +++ b/krokiet/src/main.rs @@ -93,4 +93,12 @@ pub fn zeroing_all_models(app: &MainWindow) { app.set_empty_folder_model(Rc::new(VecModel::default()).into()); app.set_empty_files_model(Rc::new(VecModel::default()).into()); app.set_similar_images_model(Rc::new(VecModel::default()).into()); + app.set_duplicate_files_model(Rc::new(VecModel::default()).into()); + app.set_similar_music_model(Rc::new(VecModel::default()).into()); + app.set_big_files_model(Rc::new(VecModel::default()).into()); + app.set_bad_extensions_model(Rc::new(VecModel::default()).into()); + app.set_broken_files_model(Rc::new(VecModel::default()).into()); + app.set_similar_videos_model(Rc::new(VecModel::default()).into()); + app.set_invalid_symlinks_model(Rc::new(VecModel::default()).into()); + app.set_temporary_files_model(Rc::new(VecModel::default()).into()); } diff --git a/krokiet/ui/gui_state.slint b/krokiet/ui/gui_state.slint index 5610a94..59dc4a4 100644 --- a/krokiet/ui/gui_state.slint +++ b/krokiet/ui/gui_state.slint @@ -17,7 +17,7 @@ export global GuiState { in-out property choosing_include_directories; in-out property visible_tool_settings; - in-out property available_subsettings: active_tab == CurrentTab.SimilarImages; + in-out property available_subsettings: active_tab == CurrentTab.SimilarImages || active_tab == CurrentTab.DuplicateFiles || active_tab == CurrentTab.SimilarVideos || active_tab == CurrentTab.SimilarMusic || active_tab == CurrentTab.BigFiles || active_tab == CurrentTab.BrokenFiles; in-out property active_tab: CurrentTab.DuplicateFiles; in-out property is_tool_tab_active: active_tab != CurrentTab.Settings && active_tab != CurrentTab.About; in-out property <[SelectModel]> select_results_list: [{data: SelectMode.SelectAll, name: "Select All"}, {data: SelectMode.UnselectAll, name: "Deselect All"}, {data: SelectMode.SelectTheSmallestResolution, name: "Select the smallest resolution"}]; diff --git a/krokiet/ui/left_side_panel.slint b/krokiet/ui/left_side_panel.slint index f6408d2..2587758 100644 --- a/krokiet/ui/left_side_panel.slint +++ b/krokiet/ui/left_side_panel.slint @@ -68,7 +68,7 @@ export component LeftSidePanel { callback changed_current_tab(); width: 120px; VerticalLayout { - spacing: 10px; + spacing: 2px; Rectangle { visible: GuiState.active_tab != CurrentTab.About; height: 80px; @@ -110,7 +110,6 @@ export component LeftSidePanel { curr_tab: r.tab; changed_current_tab() => {root.changed_current_tab();} } - } Rectangle { diff --git a/krokiet/ui/main_lists.slint b/krokiet/ui/main_lists.slint index 7f57c0c..8777dd9 100644 --- a/krokiet/ui/main_lists.slint +++ b/krokiet/ui/main_lists.slint @@ -88,8 +88,8 @@ export component MainList { visible: GuiState.active_tab == CurrentTab.TemporaryFiles; min-width: 200px; height: parent.height; - columns: ["Selection", "Size", "File Name", "Path", "Modification Date"]; - column-sizes: [35px, size_px, name_px, path_px, mod_px]; + columns: ["Selection", "File Name", "Path", "Modification Date"]; + column-sizes: [35px, name_px, path_px, mod_px]; values <=> temporary_files_model; parentPathIdx: 3; fileNameIdx: 2; @@ -143,8 +143,8 @@ export component MainList { visible: GuiState.active_tab == CurrentTab.BrokenFiles; min-width: 200px; height: parent.height; - columns: ["Selection", "Size", "File Name", "Type of Error", "Path", "Modification Date"]; - column-sizes: [35px, size_px, name_px, 200px, path_px, mod_px]; + columns: ["Selection", "File Name", "Path", "Type of Error", "Size", "Modification Date"]; + column-sizes: [35px, name_px, path_px, 200px, size_px, mod_px]; values <=> broken_files_model; parentPathIdx: 4; fileNameIdx: 2; diff --git a/krokiet/ui/popup_new_directories.slint b/krokiet/ui/popup_new_directories.slint index a920dbf..1de76e5 100644 --- a/krokiet/ui/popup_new_directories.slint +++ b/krokiet/ui/popup_new_directories.slint @@ -72,14 +72,7 @@ export component PopupNewDirectories inherits Rectangle { } } } - - // Button { - // text:"KKK"; - // clicked => { - // show-popup(); - // } - // } - + show_popup() => { popup_window.show(); } diff --git a/krokiet/ui/settings.slint b/krokiet/ui/settings.slint index 8634238..e35d74f 100644 --- a/krokiet/ui/settings.slint +++ b/krokiet/ui/settings.slint @@ -40,7 +40,7 @@ export global Settings { // Allowed subsettings - // Duplicate + // Similar Images in-out property <[string]> similar_images_sub_available_hash_size: ["8", "16", "32", "64"]; in-out property similar_images_sub_hash_size_index: 0; in-out property <[string]> similar_images_sub_available_resize_algorithm: ["Lanczos3", "Nearest", "Triangle", "Gaussian", "CatmullRom"]; @@ -50,4 +50,37 @@ export global Settings { in-out property similar_images_sub_max_similarity: 40; in-out property similar_images_sub_current_similarity: 20; in-out property similar_images_sub_ignore_same_size; + + // Duplicates + in-out property <[string]> duplicates_sub_check_method: ["Hash", "Size", "Name", "Size and Name"]; + in-out property duplicates_sub_check_method_index: 0; + in-out property <[string]> duplicates_sub_available_hash_type: ["Blake3", "CRC32", "XXH3"]; + in-out property duplicates_sub_available_hash_type_index: 0; + + // Big files + in-out property <[string]> biggest_files_sub_method: ["The Biggest", "The Smallest"]; + in-out property biggest_files_sub_method_index: 0; + in-out property biggest_files_sub_number_of_files: 50; + + // Similar Videos + in-out property similar_videos_sub_ignore_same_size; + in-out property similar_videos_sub_max_similarity: 20; + in-out property similar_videos_sub_current_similarity: 15; + + // Same Music + in-out property <[string]> similar_music_sub_audio_check_type: ["Tags", "Fingerprint"]; + in-out property similar_music_sub_audio_check_type_index: 0; + in-out property similar_music_sub_approximate_comparison; + in-out property similar_music_sub_title: true; + in-out property similar_music_sub_artist: true; + in-out property similar_music_sub_year: false; + in-out property similar_music_sub_bitrate: false; + in-out property similar_music_sub_genre: false; + in-out property similar_music_sub_length: false; + + // Broken Files + in-out property broken_files_sub_audio: true; + in-out property broken_files_sub_pdf: false; + in-out property broken_files_sub_archive: false; + in-out property broken_files_sub_image: false; } diff --git a/krokiet/ui/settings_list.slint b/krokiet/ui/settings_list.slint index d0352cc..2bd20f8 100644 --- a/krokiet/ui/settings_list.slint +++ b/krokiet/ui/settings_list.slint @@ -9,7 +9,7 @@ global SettingsSize { out property item_height: 30px; } -component TextComponent inherits HorizontalLayout { +export component TextComponent inherits HorizontalLayout { in-out property model; in property name; spacing: 5px; diff --git a/krokiet/ui/tool_settings.slint b/krokiet/ui/tool_settings.slint index 84b28ef..0f9537a 100644 --- a/krokiet/ui/tool_settings.slint +++ b/krokiet/ui/tool_settings.slint @@ -13,6 +13,7 @@ import {ColorPalette} from "color_palette.slint"; import {GuiState} from "gui_state.slint"; import { Preview } from "preview.slint"; import {PopupNewDirectories} from "popup_new_directories.slint"; +import {TextComponent} from "settings_list.slint"; component ComboBoxWrapper inherits HorizontalLayout { in-out property text; @@ -62,7 +63,8 @@ component SliderWrapper inherits HorizontalLayout { export component ToolSettings { ScrollView { - if GuiState.active_tab == CurrentTab.SimilarImages: VerticalLayout { + VerticalLayout { + visible: GuiState.active_tab == CurrentTab.SimilarImages; spacing: 5px; padding: 10px; SubsettingsHeader { } @@ -94,5 +96,128 @@ export component ToolSettings { } Rectangle {} } + VerticalLayout { + visible: GuiState.active_tab == CurrentTab.DuplicateFiles; + spacing: 5px; + padding: 10px; + SubsettingsHeader { } + ComboBoxWrapper { + text: "Check method"; + model: Settings.duplicates_sub_check_method; + current_index <=> Settings.duplicates_sub_check_method_index; + } + ComboBoxWrapper { + text: "Hash type"; + model: Settings.duplicates_sub_available_hash_type; + current_index <=> Settings.duplicates_sub_available_hash_type_index; + } + Rectangle {} + } + VerticalLayout { + visible: GuiState.active_tab == CurrentTab.BigFiles; + spacing: 5px; + padding: 10px; + SubsettingsHeader { } + ComboBoxWrapper { + text: "Checked files"; + model: Settings.biggest_files_sub_method; + current_index <=> Settings.biggest_files_sub_method_index; + } + TextComponent { + name: "Number of files"; + model <=> Settings.biggest_files_sub_number_of_files; + } + Rectangle {} + } + VerticalLayout { + visible: GuiState.active_tab == CurrentTab.SimilarVideos; + spacing: 5px; + padding: 10px; + SubsettingsHeader { } + SliderWrapper { + text: "Max difference"; + end_text: "(" + round(Settings.similar_videos_sub_current_similarity) + "/" + round(Settings.similar_videos_sub_max_similarity) + ")"; + end_text_size: 40px; + maximum <=> Settings.similar_videos_sub_max_similarity; + value <=> Settings.similar_videos_sub_current_similarity; + } + CheckBoxWrapper { + text: "Ignore same size"; + checked <=> Settings.similar_images_sub_ignore_same_size; + } + Rectangle {} + } + VerticalLayout { + visible: GuiState.active_tab == CurrentTab.SimilarMusic; + spacing: 5px; + padding: 10px; + SubsettingsHeader { } + ComboBoxWrapper { + text: "Audio check type"; + model: Settings.similar_music_sub_audio_check_type; + current_index <=> Settings.similar_music_sub_audio_check_type_index; + } + CheckBoxWrapper { + text: "Approximate Tag Comparison"; + checked <=> Settings.similar_music_sub_approximate_comparison; + } + Text { + text: "Compared tags"; + font-size: 12px; + } + CheckBoxWrapper { + text: "Title"; + checked <=> Settings.similar_music_sub_title; + } + CheckBoxWrapper { + text: "Artist"; + checked <=> Settings.similar_music_sub_artist; + } + CheckBoxWrapper { + text: "Bitrate"; + checked <=> Settings.similar_music_sub_bitrate; + } + CheckBoxWrapper { + text: "Genre"; + checked <=> Settings.similar_music_sub_genre; + } + CheckBoxWrapper { + text: "Year"; + checked <=> Settings.similar_music_sub_year; + } + CheckBoxWrapper { + text: "Length"; + checked <=> Settings.similar_music_sub_length; + } + Rectangle {} + } + VerticalLayout { + visible: GuiState.active_tab == CurrentTab.BrokenFiles; + spacing: 5px; + padding: 10px; + SubsettingsHeader { } + Text { + text: "Type of files to check"; + font-size: 12px; + } + CheckBoxWrapper { + text: "Audio"; + checked <=> Settings.broken_files_sub_audio; + } + CheckBoxWrapper { + text: "Pdf"; + checked <=> Settings.broken_files_sub_pdf; + } + CheckBoxWrapper { + text: "Archive"; + checked <=> Settings.broken_files_sub_archive; + } + CheckBoxWrapper { + text: "Image"; + checked <=> Settings.broken_files_sub_image; + } + + Rectangle {} + } } } \ No newline at end of file