From 24b1719d3593fdf295383fc89679143fc10415cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mikrut?= Date: Thu, 15 Feb 2024 16:27:38 +0100 Subject: [PATCH] Music --- czkawka_gui/src/compute_results.rs | 2 +- krokiet/src/connect_scan.rs | 179 ++++++++++++++++++++++++++--- 2 files changed, 162 insertions(+), 19 deletions(-) diff --git a/czkawka_gui/src/compute_results.rs b/czkawka_gui/src/compute_results.rs index 273eff7..e77fdbb 100644 --- a/czkawka_gui/src/compute_results.rs +++ b/czkawka_gui/src/compute_results.rs @@ -1282,7 +1282,7 @@ fn compute_duplicate_finder( fn vector_sort_unstable_entry_by_path(vector: &[T]) -> Vec where T: ResultEntry + Clone, - T: std::marker::Send, + T: Send, { if vector.len() >= 2 { let mut vector = vector.to_vec(); diff --git a/krokiet/src/connect_scan.rs b/krokiet/src/connect_scan.rs index de39601..2060a4b 100644 --- a/krokiet/src/connect_scan.rs +++ b/krokiet/src/connect_scan.rs @@ -15,8 +15,10 @@ use czkawka_core::common_traits::ResultEntry; use czkawka_core::duplicate::{DuplicateEntry, DuplicateFinder}; use czkawka_core::empty_files::EmptyFiles; use czkawka_core::empty_folder::{EmptyFolder, FolderEntry}; +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 crate::common::split_u64_into_i32s; use crate::settings::{collect_settings, SettingsCustom, ALLOWED_HASH_TYPE_VALUES, ALLOWED_RESIZE_ALGORITHM_VALUES}; @@ -45,15 +47,15 @@ pub fn connect_scan_button(app: &MainWindow, progress_sender: Sender { scan_empty_folders(a, progress_sender, stop_receiver, custom_settings); } + CurrentTab::BigFiles => { + scan_big_files(a, progress_sender, stop_receiver, custom_settings); + } CurrentTab::EmptyFiles => { scan_empty_files(a, progress_sender, stop_receiver, custom_settings); } CurrentTab::SimilarImages => { scan_similar_images(a, progress_sender, stop_receiver, custom_settings); } - CurrentTab::BigFiles => { - scan_big_files(a, progress_sender, stop_receiver, custom_settings); - } CurrentTab::Settings | CurrentTab::About => panic!("Button should be disabled"), _ => unimplemented!(), } @@ -255,6 +257,51 @@ fn prepare_data_model_big_files(fe: &FileEntry) -> (ModelRc, Model let data_model_int = VecModel::from_slice(&[modification_split.0, modification_split.1, size_split.0, size_split.1]); (data_model_str, data_model_int) } + +///////////////////////////////// Empty Files +fn scan_empty_files(a: Weak, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut finder = EmptyFiles::new(); + set_common_settings(&mut finder, &custom_settings); + finder.find_empty_files(Some(&stop_receiver), Some(&progress_sender)); + + let mut vector = finder.get_empty_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| { + write_empty_files_results(&app, vector, messages); + }) + }) + .unwrap(); +} +fn write_empty_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) = prepare_data_model_empty_files(&fe); + insert_data_to_model(&items, data_model_str, data_model_int, false); + } + app.set_empty_files_model(items.into()); + app.invoke_scan_ended(format!("Found {items_found} empty files").into()); + app.global::().set_info_text(messages.into()); +} + +fn prepare_data_model_empty_files(fe: &FileEntry) -> (ModelRc, ModelRc) { + let (directory, file) = split_path(fe.get_path()); + let data_model_str = VecModel::from_slice(&[ + file.into(), + directory.into(), + NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(), + ]); + let modification_split = split_u64_into_i32s(fe.get_modified_date()); + let size_split = split_u64_into_i32s(fe.size); + let data_model_int = VecModel::from_slice(&[modification_split.0, modification_split.1, size_split.0, size_split.1]); + (data_model_str, data_model_int) +} // Scan Similar Images fn scan_similar_images(a: Weak, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { @@ -341,41 +388,65 @@ fn prepare_data_model_similar_images(fe: &ImagesEntry, hash_size: u8) -> (ModelR (data_model_str, data_model_int) } -///////////////////////////////// Empty Files -fn scan_empty_files(a: Weak, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { +// Scan Similar Videos + +fn scan_similar_videos(a: Weak, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) .spawn(move || { - let mut finder = EmptyFiles::new(); + let mut finder = SimilarVideos::new(); set_common_settings(&mut finder, &custom_settings); - finder.find_empty_files(Some(&stop_receiver), Some(&progress_sender)); - let mut vector = finder.get_empty_files().clone(); + // TODO set rest of settings + finder.find_similar_videos(Some(&stop_receiver), Some(&progress_sender)); + 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())); + let mut vector; + if finder.get_use_reference() { + vector = finder + .get_similar_videos_referenced() + .iter() + .cloned() + .map(|(original, others)| (Some(original), others)) + .collect::>(); + } else { + vector = finder.get_similar_videos().iter().cloned().map(|items| (None, items)).collect::>(); + } + for (_first_entry, vec_fe) in &mut vector { + vec_fe.par_sort_unstable_by(|a, b| split_path_compare(a.path.as_path(), b.path.as_path())); + } a.upgrade_in_event_loop(move |app| { - write_empty_files_results(&app, vector, messages); + write_similar_videos_results(&app, vector, messages); }) }) .unwrap(); } -fn write_empty_files_results(app: &MainWindow, vector: Vec, messages: String) { +fn write_similar_videos_results(app: &MainWindow, vector: Vec<(Option, 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) = prepare_data_model_empty_files(&fe); - insert_data_to_model(&items, data_model_str, data_model_int, false); + for (ref_fe, vec_fe) in vector { + if let Some(ref_fe) = ref_fe { + let (data_model_str, data_model_int) = prepare_data_model_similar_videos(&ref_fe); + insert_data_to_model(&items, data_model_str, data_model_int, true); + } else { + insert_data_to_model(&items, ModelRc::new(VecModel::default()), ModelRc::new(VecModel::default()), true); + } + + for fe in vec_fe { + let (data_model_str, data_model_int) = prepare_data_model_similar_videos(&fe); + insert_data_to_model(&items, data_model_str, data_model_int, false); + } } - app.set_empty_files_model(items.into()); - app.invoke_scan_ended(format!("Found {items_found} empty files").into()); + app.set_similar_videos_model(items.into()); + app.invoke_scan_ended(format!("Found {items_found} similar videos files").into()); app.global::().set_info_text(messages.into()); } - -fn prepare_data_model_empty_files(fe: &FileEntry) -> (ModelRc, ModelRc) { +fn prepare_data_model_similar_videos(fe: &VideosEntry) -> (ModelRc, ModelRc) { let (directory, file) = split_path(fe.get_path()); let data_model_str = VecModel::from_slice(&[ + format_size(fe.size, BINARY).into(), file.into(), directory.into(), NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(), @@ -385,7 +456,79 @@ fn prepare_data_model_empty_files(fe: &FileEntry) -> (ModelRc, Mod let data_model_int = VecModel::from_slice(&[modification_split.0, modification_split.1, size_split.0, size_split.1]); (data_model_str, data_model_int) } +// Scan Similar Music +fn scan_similar_music(a: Weak, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut finder = SameMusic::new(); + set_common_settings(&mut finder, &custom_settings); + // TODO set rest of settings + finder.find_similar_music(Some(&stop_receiver), Some(&progress_sender)); + + let messages = finder.get_text_messages().create_messages_text(); + + let mut vector; + if finder.get_use_reference() { + vector = finder + .get_similar_music_referenced() + .iter() + .cloned() + .map(|(original, others)| (Some(original), others)) + .collect::>(); + } else { + vector = finder.get_similar_music().iter().cloned().map(|items| (None, items)).collect::>(); + } + for (_first_entry, vec_fe) in &mut vector { + vec_fe.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_similar_music_results(&app, vector, messages); + }) + }) + .unwrap(); +} +fn write_similar_music_results(app: &MainWindow, vector: Vec<(Option, Vec)>, messages: String) { + let items_found = vector.len(); + let items = Rc::new(VecModel::default()); + for (ref_fe, vec_fe) in vector { + if let Some(ref_fe) = ref_fe { + let (data_model_str, data_model_int) = crate::connect_scan::prepare_data_model_similar_music(&ref_fe); + insert_data_to_model(&items, data_model_str, data_model_int, true); + } else { + insert_data_to_model(&items, ModelRc::new(VecModel::default()), ModelRc::new(VecModel::default()), true); + } + + for fe in vec_fe { + let (data_model_str, data_model_int) = crate::connect_scan::prepare_data_model_similar_music(&fe); + insert_data_to_model(&items, data_model_str, data_model_int, false); + } + } + app.set_similar_music_model(items.into()); + app.invoke_scan_ended(format!("Found {items_found} similar music files").into()); + app.global::().set_info_text(messages.into()); +} +fn prepare_data_model_similar_music(fe: &MusicEntry) -> (ModelRc, ModelRc) { + let (directory, file) = split_path(fe.get_path()); + let data_model_str = VecModel::from_slice(&[ + format_size(fe.size, BINARY).into(), + file.into(), + fe.track_title.clone().into(), + fe.track_artist.clone().into(), + fe.year.clone().into(), + fe.bitrate.to_string().into(), + fe.length.clone().into(), + fe.genre.clone().into(), + directory.into(), + NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(), + ]); + let modification_split = split_u64_into_i32s(fe.get_modified_date()); + let size_split = split_u64_into_i32s(fe.size); + let data_model_int = VecModel::from_slice(&[modification_split.0, modification_split.1, size_split.0, size_split.1]); + (data_model_str, data_model_int) +} ////////////////////////////////////////// Common fn insert_data_to_model(items: &Rc>, data_model_str: ModelRc, data_model_int: ModelRc, header_row: bool) { let main = MainListModel {