1
0
Fork 0
mirror of synced 2024-06-01 18:19:46 +12:00
This commit is contained in:
Rafał Mikrut 2023-05-02 20:52:02 +02:00
parent b89d7ea00b
commit 3be13a914b
22 changed files with 311 additions and 488 deletions

View file

@ -10,6 +10,7 @@ use std::mem;
use std::time::SystemTime; use std::time::SystemTime;
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use futures::channel::mpsc::UnboundedSender;
use mime_guess::get_mime_extensions; use mime_guess::get_mime_extensions;
use rayon::prelude::*; use rayon::prelude::*;
@ -208,7 +209,7 @@ impl BadExtensions {
} }
} }
pub fn find_bad_extensions_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) { pub fn find_bad_extensions_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) {
self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); self.directories.optimize_directories(self.recursive_search, &mut self.text_messages);
if !self.check_files(stop_receiver, progress_sender) { if !self.check_files(stop_receiver, progress_sender) {
self.stopped_search = true; self.stopped_search = true;
@ -283,7 +284,7 @@ impl BadExtensions {
self.excluded_items.set_excluded_items(excluded_items, &mut self.text_messages); self.excluded_items.set_excluded_items(excluded_items, &mut self.text_messages);
} }
fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
let result = DirTraversalBuilder::new() let result = DirTraversalBuilder::new()
.root_dirs(self.directories.included_directories.clone()) .root_dirs(self.directories.included_directories.clone())
.group_by(|_fe| ()) .group_by(|_fe| ())
@ -317,14 +318,13 @@ impl BadExtensions {
} }
} }
fn look_for_bad_extensions_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn look_for_bad_extensions_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
let system_time = SystemTime::now(); let system_time = SystemTime::now();
let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread
let progress_thread_run = Arc::new(AtomicBool::new(true)); let progress_thread_run = Arc::new(AtomicBool::new(true));
let atomic_counter = Arc::new(AtomicUsize::new(0)); let atomic_counter = Arc::new(AtomicUsize::new(0));
let progress_thread_handle = prepare_thread_handler_common( let progress_thread_handle = prepare_thread_handler_common(
progress_sender, progress_sender,
&progress_thread_run, &progress_thread_run,

View file

@ -2,33 +2,28 @@ use std::collections::BTreeMap;
use std::fs::{DirEntry, File, Metadata}; use std::fs::{DirEntry, File, Metadata};
use std::io::{BufWriter, Write}; use std::io::{BufWriter, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::atomic::Ordering; use std::sync::atomic::AtomicBool;
use std::sync::atomic::{AtomicBool, AtomicU64}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::thread::{sleep, JoinHandle};
use std::time::Duration; use std::fs;
use std::time::SystemTime; use std::time::SystemTime;
use std::{fs, thread};
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use futures::channel::mpsc::UnboundedSender;
use humansize::format_size; use humansize::format_size;
use humansize::BINARY; use humansize::BINARY;
use rayon::prelude::*; use rayon::prelude::*;
use crate::common::{check_folder_children, send_info_and_wait_for_ending_all_threads, split_path}; use crate::common::Common;
use crate::common::{Common, LOOP_DURATION}; use crate::common::{check_folder_children, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, split_path};
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time}; use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
use crate::common_directory::Directories; use crate::common_directory::Directories;
use crate::common_extensions::Extensions; use crate::common_extensions::Extensions;
use crate::common_items::ExcludedItems; use crate::common_items::ExcludedItems;
use crate::common_messages::Messages; use crate::common_messages::Messages;
use crate::common_traits::{DebugPrint, PrintResults, SaveResults}; use crate::common_traits::{DebugPrint, PrintResults, SaveResults};
#[derive(Debug)]
pub struct ProgressData {
pub files_checked: usize,
}
#[derive(Clone)] #[derive(Clone)]
pub struct FileEntry { pub struct FileEntry {
pub path: PathBuf, pub path: PathBuf,
@ -94,7 +89,7 @@ impl BigFile {
} }
} }
pub fn find_big_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) { pub fn find_big_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) {
self.optimize_directories(); self.optimize_directories();
if !self.look_for_big_files(stop_receiver, progress_sender) { if !self.look_for_big_files(stop_receiver, progress_sender) {
self.stopped_search = true; self.stopped_search = true;
@ -147,33 +142,7 @@ impl BigFile {
self.allowed_extensions.set_allowed_extensions(allowed_extensions, &mut self.text_messages); self.allowed_extensions.set_allowed_extensions(allowed_extensions, &mut self.text_messages);
} }
pub fn prepare_thread_handler( fn look_for_big_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
&self,
progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>,
progress_thread_run: &Arc<AtomicBool>,
atomic_counter: &Arc<AtomicU64>,
) -> JoinHandle<()> {
if let Some(progress_sender) = progress_sender {
let progress_send = progress_sender.clone();
let progress_thread_run = progress_thread_run.clone();
let atomic_counter = atomic_counter.clone();
thread::spawn(move || loop {
progress_send
.unbounded_send(ProgressData {
files_checked: atomic_counter.load(Ordering::Relaxed) as usize,
})
.unwrap();
if !progress_thread_run.load(Ordering::Relaxed) {
break;
}
sleep(Duration::from_millis(LOOP_DURATION as u64));
})
} else {
thread::spawn(|| {})
}
}
fn look_for_big_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool {
let start_time: SystemTime = SystemTime::now(); let start_time: SystemTime = SystemTime::now();
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
let mut old_map: BTreeMap<u64, Vec<FileEntry>> = Default::default(); let mut old_map: BTreeMap<u64, Vec<FileEntry>> = Default::default();
@ -184,8 +153,8 @@ impl BigFile {
} }
let progress_thread_run = Arc::new(AtomicBool::new(true)); let progress_thread_run = Arc::new(AtomicBool::new(true));
let atomic_counter = Arc::new(AtomicU64::new(0)); let atomic_counter = Arc::new(AtomicUsize::new(0));
let progress_thread_handle = self.prepare_thread_handler(progress_sender, &progress_thread_run, &atomic_counter); let progress_thread_handle = prepare_thread_handler_common(progress_sender, &progress_thread_run, &atomic_counter, 0, 0, 0, CheckingMethod::None);
while !folders_to_check.is_empty() { while !folders_to_check.is_empty() {
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
@ -251,7 +220,7 @@ impl BigFile {
pub fn collect_file_entry( pub fn collect_file_entry(
&self, &self,
atomic_counter: &Arc<AtomicU64>, atomic_counter: &Arc<AtomicUsize>,
metadata: &Metadata, metadata: &Metadata,
entry_data: &DirEntry, entry_data: &DirEntry,
fe_result: &mut Vec<(u64, FileEntry)>, fe_result: &mut Vec<(u64, FileEntry)>,

View file

@ -5,11 +5,12 @@ use std::io::{BufReader, BufWriter};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::thread::{sleep, JoinHandle};
use std::time::{Duration, SystemTime}; use std::time::SystemTime;
use std::{fs, mem, panic, thread}; use std::{fs, mem, panic};
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use futures::channel::mpsc::UnboundedSender;
use pdf::file::FileOptions; use pdf::file::FileOptions;
use pdf::object::ParseOptions; use pdf::object::ParseOptions;
use pdf::PdfError; use pdf::PdfError;
@ -17,23 +18,17 @@ use pdf::PdfError::Try;
use rayon::prelude::*; use rayon::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::common::{check_folder_children, create_crash_message, open_cache_folder, send_info_and_wait_for_ending_all_threads, Common, LOOP_DURATION, PDF_FILES_EXTENSIONS}; use crate::common::{
check_folder_children, create_crash_message, open_cache_folder, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, Common, PDF_FILES_EXTENSIONS,
};
use crate::common::{AUDIO_FILES_EXTENSIONS, IMAGE_RS_BROKEN_FILES_EXTENSIONS, ZIP_FILES_EXTENSIONS}; use crate::common::{AUDIO_FILES_EXTENSIONS, IMAGE_RS_BROKEN_FILES_EXTENSIONS, ZIP_FILES_EXTENSIONS};
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time}; use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
use crate::common_directory::Directories; use crate::common_directory::Directories;
use crate::common_extensions::Extensions; use crate::common_extensions::Extensions;
use crate::common_items::ExcludedItems; use crate::common_items::ExcludedItems;
use crate::common_messages::Messages; use crate::common_messages::Messages;
use crate::common_traits::*; use crate::common_traits::*;
#[derive(Debug)]
pub struct ProgressData {
pub current_stage: u8,
pub max_stage: u8,
pub files_checked: usize,
pub files_to_check: usize,
}
#[derive(Eq, PartialEq, Clone, Debug, Copy)] #[derive(Eq, PartialEq, Clone, Debug, Copy)]
pub enum DeleteMethod { pub enum DeleteMethod {
None, None,
@ -121,7 +116,7 @@ impl BrokenFiles {
} }
} }
pub fn find_broken_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) { pub fn find_broken_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) {
self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); self.directories.optimize_directories(self.recursive_search, &mut self.text_messages);
if !self.check_files(stop_receiver, progress_sender) { if !self.check_files(stop_receiver, progress_sender) {
self.stopped_search = true; self.stopped_search = true;
@ -197,39 +192,7 @@ impl BrokenFiles {
self.excluded_items.set_excluded_items(excluded_items, &mut self.text_messages); self.excluded_items.set_excluded_items(excluded_items, &mut self.text_messages);
} }
pub fn prepare_thread_handler_broken_files( fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
&self,
progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>,
progress_thread_run: &Arc<AtomicBool>,
atomic_counter: &Arc<AtomicUsize>,
current_stage: u8,
max_stage: u8,
max_value: usize,
) -> JoinHandle<()> {
if let Some(progress_sender) = progress_sender {
let progress_send = progress_sender.clone();
let progress_thread_run = progress_thread_run.clone();
let atomic_counter = atomic_counter.clone();
thread::spawn(move || loop {
progress_send
.unbounded_send(ProgressData {
current_stage,
max_stage,
files_checked: atomic_counter.load(Ordering::Relaxed),
files_to_check: max_value,
})
.unwrap();
if !progress_thread_run.load(Ordering::Relaxed) {
break;
}
sleep(Duration::from_millis(LOOP_DURATION as u64));
})
} else {
thread::spawn(|| {})
}
}
fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool {
let start_time: SystemTime = SystemTime::now(); let start_time: SystemTime = SystemTime::now();
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
@ -240,7 +203,7 @@ impl BrokenFiles {
let progress_thread_run = Arc::new(AtomicBool::new(true)); let progress_thread_run = Arc::new(AtomicBool::new(true));
let atomic_counter = Arc::new(AtomicUsize::new(0)); let atomic_counter = Arc::new(AtomicUsize::new(0));
let progress_thread_handle = self.prepare_thread_handler_broken_files(progress_sender, &progress_thread_run, &atomic_counter, 0, 1, 0); let progress_thread_handle = prepare_thread_handler_common(progress_sender, &progress_thread_run, &atomic_counter, 0, 1, 0, CheckingMethod::None);
while !folders_to_check.is_empty() { while !folders_to_check.is_empty() {
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
@ -441,7 +404,7 @@ impl BrokenFiles {
} }
} }
fn look_for_broken_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn look_for_broken_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
let system_time = SystemTime::now(); let system_time = SystemTime::now();
let loaded_hash_map; let loaded_hash_map;
@ -481,7 +444,15 @@ impl BrokenFiles {
let progress_thread_run = Arc::new(AtomicBool::new(true)); let progress_thread_run = Arc::new(AtomicBool::new(true));
let atomic_counter = Arc::new(AtomicUsize::new(0)); let atomic_counter = Arc::new(AtomicUsize::new(0));
let progress_thread_handle = self.prepare_thread_handler_broken_files(progress_sender, &progress_thread_run, &atomic_counter, 1, 1, non_cached_files_to_check.len()); let progress_thread_handle = prepare_thread_handler_common(
progress_sender,
&progress_thread_run,
&atomic_counter,
1,
1,
non_cached_files_to_check.len(),
CheckingMethod::None,
);
let mut vec_file_entry: Vec<FileEntry> = non_cached_files_to_check let mut vec_file_entry: Vec<FileEntry> = non_cached_files_to_check
.into_par_iter() .into_par_iter()

View file

@ -11,6 +11,7 @@ use std::{fs, thread};
#[cfg(feature = "heif")] #[cfg(feature = "heif")]
use anyhow::Result; use anyhow::Result;
use directories_next::ProjectDirs; use directories_next::ProjectDirs;
use futures::channel::mpsc::UnboundedSender;
use image::{DynamicImage, ImageBuffer, Rgb}; use image::{DynamicImage, ImageBuffer, Rgb};
use imagepipe::{ImageSource, Pipeline}; use imagepipe::{ImageSource, Pipeline};
// #[cfg(feature = "heif")] // #[cfg(feature = "heif")]
@ -360,7 +361,7 @@ pub fn check_folder_children(
} }
pub fn prepare_thread_handler_common( pub fn prepare_thread_handler_common(
progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>, progress_sender: Option<&UnboundedSender<ProgressData>>,
progress_thread_run: &Arc<AtomicBool>, progress_thread_run: &Arc<AtomicBool>,
atomic_counter: &Arc<AtomicUsize>, atomic_counter: &Arc<AtomicUsize>,
current_stage: u8, current_stage: u8,

View file

@ -7,6 +7,7 @@ use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use futures::channel::mpsc::UnboundedSender;
use rayon::prelude::*; use rayon::prelude::*;
use crate::common::{prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads}; use crate::common::{prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads};
@ -99,7 +100,7 @@ pub struct DirTraversalBuilder<'a, 'b, F> {
group_by: Option<F>, group_by: Option<F>,
root_dirs: Vec<PathBuf>, root_dirs: Vec<PathBuf>,
stop_receiver: Option<&'a Receiver<()>>, stop_receiver: Option<&'a Receiver<()>>,
progress_sender: Option<&'b futures::channel::mpsc::UnboundedSender<ProgressData>>, progress_sender: Option<&'b UnboundedSender<ProgressData>>,
minimal_file_size: Option<u64>, minimal_file_size: Option<u64>,
maximal_file_size: Option<u64>, maximal_file_size: Option<u64>,
checking_method: CheckingMethod, checking_method: CheckingMethod,
@ -115,7 +116,7 @@ pub struct DirTraversal<'a, 'b, F> {
group_by: F, group_by: F,
root_dirs: Vec<PathBuf>, root_dirs: Vec<PathBuf>,
stop_receiver: Option<&'a Receiver<()>>, stop_receiver: Option<&'a Receiver<()>>,
progress_sender: Option<&'b futures::channel::mpsc::UnboundedSender<ProgressData>>, progress_sender: Option<&'b UnboundedSender<ProgressData>>,
recursive_search: bool, recursive_search: bool,
directories: Directories, directories: Directories,
excluded_items: ExcludedItems, excluded_items: ExcludedItems,
@ -168,7 +169,7 @@ impl<'a, 'b, F> DirTraversalBuilder<'a, 'b, F> {
} }
#[must_use] #[must_use]
pub fn progress_sender(mut self, progress_sender: Option<&'b futures::channel::mpsc::UnboundedSender<ProgressData>>) -> Self { pub fn progress_sender(mut self, progress_sender: Option<&'b UnboundedSender<ProgressData>>) -> Self {
self.progress_sender = progress_sender; self.progress_sender = progress_sender;
self self
} }
@ -333,16 +334,8 @@ where
folders_to_check.extend(self.root_dirs); folders_to_check.extend(self.root_dirs);
let progress_thread_run = Arc::new(AtomicBool::new(true)); let progress_thread_run = Arc::new(AtomicBool::new(true));
let atomic_entry_counter = Arc::new(AtomicUsize::new(0)); let atomic_counter = Arc::new(AtomicUsize::new(0));
let progress_thread_handle = prepare_thread_handler_common( let progress_thread_handle = prepare_thread_handler_common(self.progress_sender, &progress_thread_run, &atomic_counter, 0, self.max_stage, 0, self.checking_method);
self.progress_sender,
&progress_thread_run,
&atomic_entry_counter,
0,
self.max_stage,
0,
self.checking_method,
);
let DirTraversal { let DirTraversal {
collect, collect,
@ -386,7 +379,7 @@ where
process_dir_in_file_symlink_mode(recursive_search, current_folder, entry_data, &directories, &mut dir_result, &mut warnings, &excluded_items); process_dir_in_file_symlink_mode(recursive_search, current_folder, entry_data, &directories, &mut dir_result, &mut warnings, &excluded_items);
} }
(EntryType::Dir, Collect::EmptyFolders) => { (EntryType::Dir, Collect::EmptyFolders) => {
atomic_entry_counter.fetch_add(1, Ordering::Relaxed); atomic_counter.fetch_add(1, Ordering::Relaxed);
process_dir_in_dir_mode( process_dir_in_dir_mode(
&metadata, &metadata,
current_folder, current_folder,
@ -400,7 +393,7 @@ where
); );
} }
(EntryType::File, Collect::Files) => { (EntryType::File, Collect::Files) => {
atomic_entry_counter.fetch_add(1, Ordering::Relaxed); atomic_counter.fetch_add(1, Ordering::Relaxed);
process_file_in_file_mode( process_file_in_file_mode(
&metadata, &metadata,
entry_data, entry_data,
@ -427,10 +420,10 @@ where
set_as_not_empty_folder_list.push(current_folder.clone()); set_as_not_empty_folder_list.push(current_folder.clone());
} }
(EntryType::File, Collect::InvalidSymlinks) => { (EntryType::File, Collect::InvalidSymlinks) => {
atomic_entry_counter.fetch_add(1, Ordering::Relaxed); atomic_counter.fetch_add(1, Ordering::Relaxed);
} }
(EntryType::Symlink, Collect::InvalidSymlinks) => { (EntryType::Symlink, Collect::InvalidSymlinks) => {
atomic_entry_counter.fetch_add(1, Ordering::Relaxed); atomic_counter.fetch_add(1, Ordering::Relaxed);
process_symlink_in_symlink_mode( process_symlink_in_symlink_mode(
&metadata, &metadata,
entry_data, entry_data,

View file

@ -15,9 +15,11 @@ use std::time::SystemTime;
use std::{fs, mem}; use std::{fs, mem};
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use futures::channel::mpsc::UnboundedSender;
use humansize::format_size; use humansize::format_size;
use humansize::BINARY; use humansize::BINARY;
use rayon::prelude::*; use rayon::prelude::*;
use xxhash_rust::xxh3::Xxh3;
use crate::common::{open_cache_folder, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, Common}; use crate::common::{open_cache_folder, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, Common};
use crate::common_dir_traversal::{CheckingMethod, DirTraversalBuilder, DirTraversalResult, FileEntry, ProgressData}; use crate::common_dir_traversal::{CheckingMethod, DirTraversalBuilder, DirTraversalResult, FileEntry, ProgressData};
@ -43,7 +45,7 @@ impl HashType {
match self { match self {
HashType::Blake3 => Box::new(blake3::Hasher::new()), HashType::Blake3 => Box::new(blake3::Hasher::new()),
HashType::Crc32 => Box::new(crc32fast::Hasher::new()), HashType::Crc32 => Box::new(crc32fast::Hasher::new()),
HashType::Xxh3 => Box::new(xxhash_rust::xxh3::Xxh3::new()), HashType::Xxh3 => Box::new(Xxh3::new()),
} }
} }
} }
@ -147,7 +149,7 @@ impl DuplicateFinder {
} }
} }
pub fn find_duplicates(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) { pub fn find_duplicates(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) {
self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); self.directories.optimize_directories(self.recursive_search, &mut self.text_messages);
self.use_reference_folders = !self.directories.reference_directories.is_empty(); self.use_reference_folders = !self.directories.reference_directories.is_empty();
@ -340,7 +342,7 @@ impl DuplicateFinder {
&self.files_with_identical_size_names_referenced &self.files_with_identical_size_names_referenced
} }
fn check_files_name(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn check_files_name(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
let group_by_func = if self.case_sensitive_name_comparison { let group_by_func = if self.case_sensitive_name_comparison {
|fe: &FileEntry| fe.path.file_name().unwrap().to_string_lossy().to_string() |fe: &FileEntry| fe.path.file_name().unwrap().to_string_lossy().to_string()
} else { } else {
@ -435,7 +437,7 @@ impl DuplicateFinder {
} }
} }
fn check_files_size_name(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn check_files_size_name(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
let group_by_func = if self.case_sensitive_name_comparison { let group_by_func = if self.case_sensitive_name_comparison {
|fe: &FileEntry| (fe.size, fe.path.file_name().unwrap().to_string_lossy().to_string()) |fe: &FileEntry| (fe.size, fe.path.file_name().unwrap().to_string_lossy().to_string())
} else { } else {
@ -535,7 +537,7 @@ impl DuplicateFinder {
/// Read file length and puts it to different boxes(each for different lengths) /// Read file length and puts it to different boxes(each for different lengths)
/// If in box is only 1 result, then it is removed /// If in box is only 1 result, then it is removed
fn check_files_size(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn check_files_size(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
let max_stage = match self.check_method { let max_stage = match self.check_method {
CheckingMethod::Size => 0, CheckingMethod::Size => 0,
CheckingMethod::Hash => 2, CheckingMethod::Hash => 2,
@ -718,7 +720,7 @@ impl DuplicateFinder {
fn prehashing( fn prehashing(
&mut self, &mut self,
stop_receiver: Option<&Receiver<()>>, stop_receiver: Option<&Receiver<()>>,
progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>, progress_sender: Option<&UnboundedSender<ProgressData>>,
pre_checked_map: &mut BTreeMap<u64, Vec<FileEntry>>, pre_checked_map: &mut BTreeMap<u64, Vec<FileEntry>>,
) -> Option<()> { ) -> Option<()> {
let start_time: SystemTime = SystemTime::now(); let start_time: SystemTime = SystemTime::now();
@ -886,7 +888,7 @@ impl DuplicateFinder {
fn full_hashing( fn full_hashing(
&mut self, &mut self,
stop_receiver: Option<&Receiver<()>>, stop_receiver: Option<&Receiver<()>>,
progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>, progress_sender: Option<&UnboundedSender<ProgressData>>,
pre_checked_map: BTreeMap<u64, Vec<FileEntry>>, pre_checked_map: BTreeMap<u64, Vec<FileEntry>>,
) -> Option<()> { ) -> Option<()> {
let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread
@ -1018,7 +1020,7 @@ impl DuplicateFinder {
} }
/// The slowest checking type, which must be applied after checking for size /// The slowest checking type, which must be applied after checking for size
fn check_files_hash(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn check_files_hash(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
assert_eq!(self.check_method, CheckingMethod::Hash); assert_eq!(self.check_method, CheckingMethod::Hash);
let mut pre_checked_map: BTreeMap<u64, Vec<FileEntry>> = Default::default(); let mut pre_checked_map: BTreeMap<u64, Vec<FileEntry>> = Default::default();
@ -1617,7 +1619,7 @@ impl MyHasher for crc32fast::Hasher {
} }
} }
impl MyHasher for xxhash_rust::xxh3::Xxh3 { impl MyHasher for Xxh3 {
fn update(&mut self, bytes: &[u8]) { fn update(&mut self, bytes: &[u8]) {
self.write(bytes); self.write(bytes);
} }

View file

@ -6,6 +6,7 @@ use std::path::PathBuf;
use std::time::SystemTime; use std::time::SystemTime;
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use futures::channel::mpsc::UnboundedSender;
use crate::common::Common; use crate::common::Common;
use crate::common_dir_traversal::{DirTraversalBuilder, DirTraversalResult, FileEntry, ProgressData}; use crate::common_dir_traversal::{DirTraversalBuilder, DirTraversalResult, FileEntry, ProgressData};
@ -64,7 +65,7 @@ impl EmptyFiles {
} }
/// Finding empty files, save results to internal struct variables /// Finding empty files, save results to internal struct variables
pub fn find_empty_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) { pub fn find_empty_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) {
self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); self.directories.optimize_directories(self.recursive_search, &mut self.text_messages);
if !self.check_files(stop_receiver, progress_sender) { if !self.check_files(stop_receiver, progress_sender) {
self.stopped_search = true; self.stopped_search = true;
@ -125,7 +126,7 @@ impl EmptyFiles {
} }
/// Check files for any with size == 0 /// Check files for any with size == 0
fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
let result = DirTraversalBuilder::new() let result = DirTraversalBuilder::new()
.root_dirs(self.directories.included_directories.clone()) .root_dirs(self.directories.included_directories.clone())
.group_by(|_fe| ()) .group_by(|_fe| ())

View file

@ -6,6 +6,7 @@ use std::path::PathBuf;
use std::time::SystemTime; use std::time::SystemTime;
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use futures::channel::mpsc::UnboundedSender;
use crate::common::Common; use crate::common::Common;
use crate::common_dir_traversal::{Collect, DirTraversalBuilder, DirTraversalResult, FolderEmptiness, FolderEntry, ProgressData}; use crate::common_dir_traversal::{Collect, DirTraversalBuilder, DirTraversalResult, FolderEmptiness, FolderEntry, ProgressData};
@ -88,7 +89,7 @@ impl EmptyFolder {
self.directories.set_excluded_directory(excluded_directory, &mut self.text_messages); self.directories.set_excluded_directory(excluded_directory, &mut self.text_messages);
} }
/// Public function used by CLI to search for empty folders /// Public function used by CLI to search for empty folders
pub fn find_empty_folders(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) { pub fn find_empty_folders(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) {
self.directories.optimize_directories(true, &mut self.text_messages); self.directories.optimize_directories(true, &mut self.text_messages);
if !self.check_for_empty_folders(stop_receiver, progress_sender) { if !self.check_for_empty_folders(stop_receiver, progress_sender) {
self.stopped_search = true; self.stopped_search = true;
@ -128,7 +129,7 @@ impl EmptyFolder {
/// Function to check if folder are empty. /// Function to check if folder are empty.
/// Parameter `initial_checking` for second check before deleting to be sure that checked folder is still empty /// Parameter `initial_checking` for second check before deleting to be sure that checked folder is still empty
fn check_for_empty_folders(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn check_for_empty_folders(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
let result = DirTraversalBuilder::new() let result = DirTraversalBuilder::new()
.root_dirs(self.directories.included_directories.clone()) .root_dirs(self.directories.included_directories.clone())
.group_by(|_fe| ()) .group_by(|_fe| ())

View file

@ -6,6 +6,7 @@ use std::path::PathBuf;
use std::time::SystemTime; use std::time::SystemTime;
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use futures::channel::mpsc::UnboundedSender;
use crate::common::Common; use crate::common::Common;
use crate::common_dir_traversal::{Collect, DirTraversalBuilder, DirTraversalResult, ErrorType, FileEntry, ProgressData}; use crate::common_dir_traversal::{Collect, DirTraversalBuilder, DirTraversalResult, ErrorType, FileEntry, ProgressData};
@ -63,7 +64,7 @@ impl InvalidSymlinks {
} }
} }
pub fn find_invalid_links(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) { pub fn find_invalid_links(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) {
self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); self.directories.optimize_directories(self.recursive_search, &mut self.text_messages);
if !self.check_files(stop_receiver, progress_sender) { if !self.check_files(stop_receiver, progress_sender) {
self.stopped_search = true; self.stopped_search = true;
@ -124,7 +125,7 @@ impl InvalidSymlinks {
} }
/// Check files for any with size == 0 /// Check files for any with size == 0
fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
let result = DirTraversalBuilder::new() let result = DirTraversalBuilder::new()
.root_dirs(self.directories.included_directories.clone()) .root_dirs(self.directories.included_directories.clone())
.group_by(|_fe| ()) .group_by(|_fe| ())

View file

@ -9,6 +9,7 @@ use std::time::SystemTime;
use std::{mem, panic}; use std::{mem, panic};
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use futures::channel::mpsc::UnboundedSender;
use lofty::TaggedFileExt; use lofty::TaggedFileExt;
use lofty::{read_from, AudioFile, ItemKey}; use lofty::{read_from, AudioFile, ItemKey};
use rayon::prelude::*; use rayon::prelude::*;
@ -148,7 +149,7 @@ impl SameMusic {
} }
} }
pub fn find_same_music(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) { pub fn find_same_music(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) {
self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); self.directories.optimize_directories(self.recursive_search, &mut self.text_messages);
self.use_reference_folders = !self.directories.reference_directories.is_empty(); self.use_reference_folders = !self.directories.reference_directories.is_empty();
if !self.check_files(stop_receiver, progress_sender) { if !self.check_files(stop_receiver, progress_sender) {
@ -276,7 +277,7 @@ impl SameMusic {
self.use_reference_folders self.use_reference_folders
} }
fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
if !self.allowed_extensions.using_custom_extensions() { if !self.allowed_extensions.using_custom_extensions() {
self.allowed_extensions.extend_allowed_extensions(AUDIO_FILES_EXTENSIONS); self.allowed_extensions.extend_allowed_extensions(AUDIO_FILES_EXTENSIONS);
} else { } else {
@ -367,7 +368,7 @@ impl SameMusic {
save_cache_to_file(&all_results, &mut self.text_messages, self.save_also_as_json); save_cache_to_file(&all_results, &mut self.text_messages, self.save_also_as_json);
} }
fn read_tags(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn read_tags(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
let start_time: SystemTime = SystemTime::now(); let start_time: SystemTime = SystemTime::now();
let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.read_tags_load_cache(); let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.read_tags_load_cache();
@ -515,7 +516,7 @@ impl SameMusic {
Some(music_entry) Some(music_entry)
} }
fn check_for_duplicate_tags(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn check_for_duplicate_tags(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
assert_ne!(MusicSimilarity::NONE, self.music_similarity, "This can't be none"); assert_ne!(MusicSimilarity::NONE, self.music_similarity, "This can't be none");
let start_time: SystemTime = SystemTime::now(); let start_time: SystemTime = SystemTime::now();

View file

@ -6,12 +6,13 @@ use std::panic;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::thread::{sleep, JoinHandle};
use std::time::{Duration, SystemTime}; use std::mem;
use std::{mem, thread}; use std::time::SystemTime;
use bk_tree::BKTree; use bk_tree::BKTree;
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use futures::channel::mpsc::UnboundedSender;
use humansize::format_size; use humansize::format_size;
use humansize::BINARY; use humansize::BINARY;
use image::GenericImageView; use image::GenericImageView;
@ -22,10 +23,10 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "heif")] #[cfg(feature = "heif")]
use crate::common::get_dynamic_image_from_heic; use crate::common::get_dynamic_image_from_heic;
use crate::common::{ use crate::common::{
check_folder_children, create_crash_message, get_dynamic_image_from_raw_image, get_number_of_threads, open_cache_folder, send_info_and_wait_for_ending_all_threads, Common, check_folder_children, create_crash_message, get_dynamic_image_from_raw_image, get_number_of_threads, open_cache_folder, prepare_thread_handler_common,
HEIC_EXTENSIONS, IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS, LOOP_DURATION, RAW_IMAGE_EXTENSIONS, send_info_and_wait_for_ending_all_threads, Common, HEIC_EXTENSIONS, IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS, RAW_IMAGE_EXTENSIONS,
}; };
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time}; use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
use crate::common_directory::Directories; use crate::common_directory::Directories;
use crate::common_extensions::Extensions; use crate::common_extensions::Extensions;
use crate::common_items::ExcludedItems; use crate::common_items::ExcludedItems;
@ -42,14 +43,6 @@ pub const SIMILAR_VALUES: [[u32; 6]; 4] = [
[6, 20, 40, 40, 40, 40], // 64 [6, 20, 40, 40, 40, 40], // 64
]; ];
#[derive(Debug)]
pub struct ProgressData {
pub current_stage: u8,
pub max_stage: u8,
pub images_checked: usize,
pub images_to_check: usize,
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FileEntry { pub struct FileEntry {
pub path: PathBuf, pub path: PathBuf,
@ -254,7 +247,7 @@ impl SimilarImages {
} }
/// Public function used by CLI to search for empty folders /// Public function used by CLI to search for empty folders
pub fn find_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) { pub fn find_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) {
self.directories.optimize_directories(true, &mut self.text_messages); self.directories.optimize_directories(true, &mut self.text_messages);
self.use_reference_folders = !self.directories.reference_directories.is_empty(); self.use_reference_folders = !self.directories.reference_directories.is_empty();
if !self.check_for_similar_images(stop_receiver, progress_sender) { if !self.check_for_similar_images(stop_receiver, progress_sender) {
@ -279,41 +272,9 @@ impl SimilarImages {
// self.delete_folders = delete_folder; // self.delete_folders = delete_folder;
// } // }
pub fn prepare_thread_handler_similar_images(
&self,
progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>,
progress_thread_run: &Arc<AtomicBool>,
atomic_counter: &Arc<AtomicUsize>,
current_stage: u8,
max_stage: u8,
max_value: usize,
) -> JoinHandle<()> {
if let Some(progress_sender) = progress_sender {
let progress_send = progress_sender.clone();
let progress_thread_run = progress_thread_run.clone();
let atomic_counter = atomic_counter.clone();
thread::spawn(move || loop {
progress_send
.unbounded_send(ProgressData {
current_stage,
max_stage,
images_checked: atomic_counter.load(Ordering::Relaxed),
images_to_check: max_value,
})
.unwrap();
if !progress_thread_run.load(Ordering::Relaxed) {
break;
}
sleep(Duration::from_millis(LOOP_DURATION as u64));
})
} else {
thread::spawn(|| {})
}
}
/// Function to check if folder are empty. /// Function to check if folder are empty.
/// Parameter `initial_checking` for second check before deleting to be sure that checked folder is still empty /// Parameter `initial_checking` for second check before deleting to be sure that checked folder is still empty
fn check_for_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn check_for_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
let start_time: SystemTime = SystemTime::now(); let start_time: SystemTime = SystemTime::now();
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
@ -337,7 +298,7 @@ impl SimilarImages {
let progress_thread_run = Arc::new(AtomicBool::new(true)); let progress_thread_run = Arc::new(AtomicBool::new(true));
let atomic_counter = Arc::new(AtomicUsize::new(0)); let atomic_counter = Arc::new(AtomicUsize::new(0));
let progress_thread_handle = self.prepare_thread_handler_similar_images(progress_sender, &progress_thread_run, &atomic_counter, 0, 3, 0); let progress_thread_handle = prepare_thread_handler_common(progress_sender, &progress_thread_run, &atomic_counter, 0, 2, 0, CheckingMethod::None);
while !folders_to_check.is_empty() { while !folders_to_check.is_empty() {
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
@ -466,7 +427,7 @@ impl SimilarImages {
// - Join already read hashes with hashes which were read from file // - Join already read hashes with hashes which were read from file
// - Join all hashes and save it to file // - Join all hashes and save it to file
fn hash_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn hash_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
let hash_map_modification = SystemTime::now(); let hash_map_modification = SystemTime::now();
let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.hash_images_load_cache(); let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.hash_images_load_cache();
@ -477,7 +438,15 @@ impl SimilarImages {
let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread
let progress_thread_run = Arc::new(AtomicBool::new(true)); let progress_thread_run = Arc::new(AtomicBool::new(true));
let atomic_counter = Arc::new(AtomicUsize::new(0)); let atomic_counter = Arc::new(AtomicUsize::new(0));
let progress_thread_handle = self.prepare_thread_handler_similar_images(progress_sender, &progress_thread_run, &atomic_counter, 1, 3, non_cached_files_to_check.len()); let progress_thread_handle = prepare_thread_handler_common(
progress_sender,
&progress_thread_run,
&atomic_counter,
1,
2,
non_cached_files_to_check.len(),
CheckingMethod::None,
);
let mut vec_file_entry: Vec<(FileEntry, ImHash)> = non_cached_files_to_check let mut vec_file_entry: Vec<(FileEntry, ImHash)> = non_cached_files_to_check
.into_par_iter() .into_par_iter()
@ -610,7 +579,7 @@ impl SimilarImages {
fn compare_hashes( fn compare_hashes(
&self, &self,
hashes_to_check: &[ImHash], hashes_to_check: &[ImHash],
atomic_mode_counter: &Arc<AtomicUsize>, atomic_counter: &Arc<AtomicUsize>,
stop_receiver: Option<&Receiver<()>>, stop_receiver: Option<&Receiver<()>>,
check_was_stopped: &AtomicBool, check_was_stopped: &AtomicBool,
tolerance: u32, tolerance: u32,
@ -632,7 +601,7 @@ impl SimilarImages {
// Also don't add too often data to atomic variable // Also don't add too often data to atomic variable
const CYCLES_COUNTER: usize = 0b11_1111; const CYCLES_COUNTER: usize = 0b11_1111;
if ((index & CYCLES_COUNTER) == CYCLES_COUNTER) && index != 0 { if ((index & CYCLES_COUNTER) == CYCLES_COUNTER) && index != 0 {
atomic_mode_counter.fetch_add(CYCLES_COUNTER, Ordering::Relaxed); atomic_counter.fetch_add(CYCLES_COUNTER, Ordering::Relaxed);
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
check_was_stopped.store(true, Ordering::Relaxed); check_was_stopped.store(true, Ordering::Relaxed);
return None; return None;
@ -839,7 +808,7 @@ impl SimilarImages {
self.collect_hash_compare_result(hashes_parents, hashes_with_multiple_images, all_hashed_images, collected_similar_images, hashes_similarity); self.collect_hash_compare_result(hashes_parents, hashes_with_multiple_images, all_hashed_images, collected_similar_images, hashes_similarity);
} }
fn find_similar_hashes(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn find_similar_hashes(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
if self.image_hashes.is_empty() { if self.image_hashes.is_empty() {
return true; return true;
} }
@ -865,8 +834,8 @@ impl SimilarImages {
} else { } else {
let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread
let progress_thread_run = Arc::new(AtomicBool::new(true)); let progress_thread_run = Arc::new(AtomicBool::new(true));
let atomic_mode_counter = Arc::new(AtomicUsize::new(0)); let atomic_counter = Arc::new(AtomicUsize::new(0));
let progress_thread_handle = self.prepare_thread_handler_similar_images(progress_sender, &progress_thread_run, &atomic_mode_counter, 2, 2, all_hashes.len()); let progress_thread_handle = prepare_thread_handler_common(progress_sender, &progress_thread_run, &atomic_counter, 2, 2, all_hashes.len(), CheckingMethod::None);
// Don't use hashes with multiple images in bktree, because they will always be master of group and cannot be find by other hashes // Don't use hashes with multiple images in bktree, because they will always be master of group and cannot be find by other hashes
@ -877,7 +846,7 @@ impl SimilarImages {
.map(|hashes_to_check| { .map(|hashes_to_check| {
self.compare_hashes( self.compare_hashes(
&hashes_to_check, &hashes_to_check,
&atomic_mode_counter, &atomic_counter,
stop_receiver, stop_receiver,
&check_was_stopped, &check_was_stopped,
tolerance, tolerance,

View file

@ -5,12 +5,13 @@ use std::io::*;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::thread::{sleep, JoinHandle};
use std::time::{Duration, SystemTime}; use std::mem;
use std::{mem, thread}; use std::time::SystemTime;
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use ffmpeg_cmdline_utils::FfmpegErrorKind::FfmpegNotFound; use ffmpeg_cmdline_utils::FfmpegErrorKind::FfmpegNotFound;
use futures::channel::mpsc::UnboundedSender;
use humansize::format_size; use humansize::format_size;
use humansize::BINARY; use humansize::BINARY;
use rayon::prelude::*; use rayon::prelude::*;
@ -18,9 +19,9 @@ use serde::{Deserialize, Serialize};
use vid_dup_finder_lib::HashCreationErrorKind::DetermineVideo; use vid_dup_finder_lib::HashCreationErrorKind::DetermineVideo;
use vid_dup_finder_lib::{NormalizedTolerance, VideoHash}; use vid_dup_finder_lib::{NormalizedTolerance, VideoHash};
use crate::common::{check_folder_children, send_info_and_wait_for_ending_all_threads, VIDEO_FILES_EXTENSIONS}; use crate::common::{check_folder_children, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, VIDEO_FILES_EXTENSIONS};
use crate::common::{open_cache_folder, Common, LOOP_DURATION}; use crate::common::{open_cache_folder, Common};
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time}; use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
use crate::common_directory::Directories; use crate::common_directory::Directories;
use crate::common_extensions::Extensions; use crate::common_extensions::Extensions;
use crate::common_items::ExcludedItems; use crate::common_items::ExcludedItems;
@ -31,14 +32,6 @@ use crate::localizer_core::generate_translation_hashmap;
pub const MAX_TOLERANCE: i32 = 20; pub const MAX_TOLERANCE: i32 = 20;
#[derive(Debug)]
pub struct ProgressData {
pub current_stage: u8,
pub max_stage: u8,
pub videos_checked: usize,
pub videos_to_check: usize,
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FileEntry { pub struct FileEntry {
pub path: PathBuf, pub path: PathBuf,
@ -215,7 +208,7 @@ impl SimilarVideos {
} }
/// Public function used by CLI to search for empty folders /// Public function used by CLI to search for empty folders
pub fn find_similar_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) { pub fn find_similar_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) {
if !check_if_ffmpeg_is_installed() { if !check_if_ffmpeg_is_installed() {
self.text_messages.errors.push(flc!("core_ffmpeg_not_found")); self.text_messages.errors.push(flc!("core_ffmpeg_not_found"));
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
@ -247,41 +240,9 @@ impl SimilarVideos {
// self.delete_folders = delete_folder; // self.delete_folders = delete_folder;
// } // }
pub fn prepare_thread_handler_similar_video(
&self,
progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>,
progress_thread_run: &Arc<AtomicBool>,
atomic_counter: &Arc<AtomicUsize>,
current_stage: u8,
max_stage: u8,
max_value: usize,
) -> JoinHandle<()> {
if let Some(progress_sender) = progress_sender {
let progress_send = progress_sender.clone();
let progress_thread_run = progress_thread_run.clone();
let atomic_counter = atomic_counter.clone();
thread::spawn(move || loop {
progress_send
.unbounded_send(ProgressData {
current_stage,
max_stage,
videos_checked: atomic_counter.load(Ordering::Relaxed),
videos_to_check: max_value,
})
.unwrap();
if !progress_thread_run.load(Ordering::Relaxed) {
break;
}
sleep(Duration::from_millis(LOOP_DURATION as u64));
})
} else {
thread::spawn(|| {})
}
}
/// Function to check if folder are empty. /// Function to check if folder are empty.
/// Parameter `initial_checking` for second check before deleting to be sure that checked folder is still empty /// Parameter `initial_checking` for second check before deleting to be sure that checked folder is still empty
fn check_for_similar_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn check_for_similar_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
let start_time: SystemTime = SystemTime::now(); let start_time: SystemTime = SystemTime::now();
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
@ -301,7 +262,7 @@ impl SimilarVideos {
let progress_thread_run = Arc::new(AtomicBool::new(true)); let progress_thread_run = Arc::new(AtomicBool::new(true));
let atomic_counter = Arc::new(AtomicUsize::new(0)); let atomic_counter = Arc::new(AtomicUsize::new(0));
let progress_thread_handle = self.prepare_thread_handler_similar_video(progress_sender, &progress_thread_run, &atomic_counter, 0, 1, 0); let progress_thread_handle = prepare_thread_handler_common(progress_sender, &progress_thread_run, &atomic_counter, 0, 1, 0, CheckingMethod::None);
while !folders_to_check.is_empty() { while !folders_to_check.is_empty() {
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {
@ -424,7 +385,7 @@ impl SimilarVideos {
(loaded_hash_map, records_already_cached, non_cached_files_to_check) (loaded_hash_map, records_already_cached, non_cached_files_to_check)
} }
fn sort_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool { fn sort_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
let hash_map_modification = SystemTime::now(); let hash_map_modification = SystemTime::now();
let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.load_cache_at_start(); let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.load_cache_at_start();
@ -436,7 +397,15 @@ impl SimilarVideos {
let progress_thread_run = Arc::new(AtomicBool::new(true)); let progress_thread_run = Arc::new(AtomicBool::new(true));
let atomic_counter = Arc::new(AtomicUsize::new(0)); let atomic_counter = Arc::new(AtomicUsize::new(0));
let progress_thread_handle = self.prepare_thread_handler_similar_video(progress_sender, &progress_thread_run, &atomic_counter, 1, 1, non_cached_files_to_check.len()); let progress_thread_handle = prepare_thread_handler_common(
progress_sender,
&progress_thread_run,
&atomic_counter,
1,
1,
non_cached_files_to_check.len(),
CheckingMethod::None,
);
let mut vec_file_entry: Vec<FileEntry> = non_cached_files_to_check let mut vec_file_entry: Vec<FileEntry> = non_cached_files_to_check
.par_iter() .par_iter()

View file

@ -4,15 +4,16 @@ use std::io::BufWriter;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::thread::{sleep, JoinHandle};
use std::time::{Duration, SystemTime}; use std::fs;
use std::{fs, thread}; use std::time::SystemTime;
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use futures::channel::mpsc::UnboundedSender;
use rayon::prelude::*; use rayon::prelude::*;
use crate::common::{check_folder_children, send_info_and_wait_for_ending_all_threads, Common, LOOP_DURATION}; use crate::common::{check_folder_children, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads, Common};
use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time}; use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_dir, get_lowercase_name, get_modified_time, CheckingMethod, ProgressData};
use crate::common_directory::Directories; use crate::common_directory::Directories;
use crate::common_items::ExcludedItems; use crate::common_items::ExcludedItems;
use crate::common_messages::Messages; use crate::common_messages::Messages;
@ -34,13 +35,6 @@ const TEMP_EXTENSIONS: &[&str] = &[
".partial", ".partial",
]; ];
#[derive(Debug)]
pub struct ProgressData {
pub current_stage: u8,
pub max_stage: u8,
pub files_checked: usize,
}
#[derive(Eq, PartialEq, Clone, Debug, Copy)] #[derive(Eq, PartialEq, Clone, Debug, Copy)]
pub enum DeleteMethod { pub enum DeleteMethod {
None, None,
@ -94,7 +88,7 @@ impl Temporary {
} }
/// Finding temporary files, save results to internal struct variables /// Finding temporary files, save results to internal struct variables
pub fn find_temporary_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) { pub fn find_temporary_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) {
self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); self.directories.optimize_directories(self.recursive_search, &mut self.text_messages);
if !self.check_files(stop_receiver, progress_sender) { if !self.check_files(stop_receiver, progress_sender) {
self.stopped_search = true; self.stopped_search = true;
@ -149,35 +143,7 @@ impl Temporary {
self.excluded_items.set_excluded_items(excluded_items, &mut self.text_messages); self.excluded_items.set_excluded_items(excluded_items, &mut self.text_messages);
} }
fn prepare_thread_handler_temporary( fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender<ProgressData>>) -> bool {
&self,
progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>,
progress_thread_run: &Arc<AtomicBool>,
atomic_counter: &Arc<AtomicUsize>,
) -> JoinHandle<()> {
if let Some(progress_sender) = progress_sender {
let progress_send = progress_sender.clone();
let progress_thread_run = progress_thread_run.clone();
let atomic_counter = atomic_counter.clone();
thread::spawn(move || loop {
progress_send
.unbounded_send(ProgressData {
current_stage: 0,
max_stage: 0,
files_checked: atomic_counter.load(Ordering::Relaxed),
})
.unwrap();
if !progress_thread_run.load(Ordering::Relaxed) {
break;
}
sleep(Duration::from_millis(LOOP_DURATION as u64));
})
} else {
thread::spawn(|| {})
}
}
fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool {
let start_time: SystemTime = SystemTime::now(); let start_time: SystemTime = SystemTime::now();
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
@ -188,7 +154,7 @@ impl Temporary {
let progress_thread_run = Arc::new(AtomicBool::new(true)); let progress_thread_run = Arc::new(AtomicBool::new(true));
let atomic_counter = Arc::new(AtomicUsize::new(0)); let atomic_counter = Arc::new(AtomicUsize::new(0));
let progress_thread_handle = self.prepare_thread_handler_temporary(progress_sender, &progress_thread_run, &atomic_counter); let progress_thread_handle = prepare_thread_handler_common(progress_sender, &progress_thread_run, &atomic_counter, 0, 0, 0, CheckingMethod::None);
while !folders_to_check.is_empty() { while !folders_to_check.is_empty() {
if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() {

View file

@ -1,3 +1,4 @@
use fs_extra::dir::CopyOptions;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use gtk4::prelude::*; use gtk4::prelude::*;
@ -199,7 +200,7 @@ fn move_files_common(
let thing = get_full_name_from_path_name(&path, &file_name); let thing = get_full_name_from_path_name(&path, &file_name);
let destination_file = destination_folder.join(file_name); let destination_file = destination_folder.join(file_name);
if Path::new(&thing).is_dir() { if Path::new(&thing).is_dir() {
if let Err(e) = fs_extra::dir::move_dir(&thing, &destination_file, &fs_extra::dir::CopyOptions::new()) { if let Err(e) = fs_extra::dir::move_dir(&thing, &destination_file, &CopyOptions::new()) {
messages += flg!("move_folder_failed", generate_translation_hashmap(vec![("name", thing), ("reason", e.to_string())])).as_str(); messages += flg!("move_folder_failed", generate_translation_hashmap(vec![("name", thing), ("reason", e.to_string())])).as_str();
messages += "\n"; messages += "\n";
continue 'next_result; continue 'next_result;

View file

@ -1,3 +1,4 @@
use futures::channel::mpsc::UnboundedSender;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::thread; use std::thread;
@ -8,7 +9,8 @@ use gtk4::prelude::*;
use czkawka_core::bad_extensions::BadExtensions; use czkawka_core::bad_extensions::BadExtensions;
use czkawka_core::big_file::BigFile; use czkawka_core::big_file::BigFile;
use czkawka_core::broken_files::{BrokenFiles, CheckedTypes}; use czkawka_core::broken_files::{BrokenFiles, CheckedTypes};
use czkawka_core::common_dir_traversal;
use czkawka_core::common_dir_traversal::ProgressData;
use czkawka_core::duplicate::DuplicateFinder; use czkawka_core::duplicate::DuplicateFinder;
use czkawka_core::empty_files::EmptyFiles; use czkawka_core::empty_files::EmptyFiles;
use czkawka_core::empty_folder::EmptyFolder; use czkawka_core::empty_folder::EmptyFolder;
@ -17,7 +19,6 @@ use czkawka_core::same_music::{MusicSimilarity, SameMusic};
use czkawka_core::similar_images::SimilarImages; use czkawka_core::similar_images::SimilarImages;
use czkawka_core::similar_videos::SimilarVideos; use czkawka_core::similar_videos::SimilarVideos;
use czkawka_core::temporary::Temporary; use czkawka_core::temporary::Temporary;
use czkawka_core::*;
use crate::gui_structs::gui_data::GuiData; use crate::gui_structs::gui_data::GuiData;
use crate::help_combo_box::{ use crate::help_combo_box::{
@ -33,17 +34,17 @@ use crate::{flg, DEFAULT_MAXIMAL_FILE_SIZE, DEFAULT_MINIMAL_CACHE_SIZE, DEFAULT_
pub fn connect_button_search( pub fn connect_button_search(
gui_data: &GuiData, gui_data: &GuiData,
glib_stop_sender: Sender<Message>, glib_stop_sender: Sender<Message>,
futures_sender_duplicate_files: futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>, futures_sender_duplicate_files: UnboundedSender<ProgressData>,
futures_sender_empty_files: futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>, futures_sender_empty_files: UnboundedSender<ProgressData>,
futures_sender_empty_folder: futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>, futures_sender_empty_folder: UnboundedSender<ProgressData>,
futures_sender_big_file: futures::channel::mpsc::UnboundedSender<big_file::ProgressData>, futures_sender_big_file: UnboundedSender<ProgressData>,
futures_sender_same_music: futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>, futures_sender_same_music: UnboundedSender<ProgressData>,
futures_sender_similar_images: futures::channel::mpsc::UnboundedSender<similar_images::ProgressData>, futures_sender_similar_images: UnboundedSender<ProgressData>,
futures_sender_similar_videos: futures::channel::mpsc::UnboundedSender<similar_videos::ProgressData>, futures_sender_similar_videos: UnboundedSender<ProgressData>,
futures_sender_temporary: futures::channel::mpsc::UnboundedSender<temporary::ProgressData>, futures_sender_temporary: UnboundedSender<ProgressData>,
futures_sender_invalid_symlinks: futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>, futures_sender_invalid_symlinks: UnboundedSender<ProgressData>,
futures_sender_broken_files: futures::channel::mpsc::UnboundedSender<broken_files::ProgressData>, futures_sender_broken_files: UnboundedSender<ProgressData>,
futures_sender_bad_extensions: futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>, futures_sender_bad_extensions: UnboundedSender<ProgressData>,
) { ) {
let check_button_settings_one_filesystem = gui_data.settings.check_button_settings_one_filesystem.clone(); let check_button_settings_one_filesystem = gui_data.settings.check_button_settings_one_filesystem.clone();
let combo_box_image_hash_size = gui_data.main_notebook.combo_box_image_hash_size.clone(); let combo_box_image_hash_size = gui_data.main_notebook.combo_box_image_hash_size.clone();

View file

@ -119,6 +119,7 @@ pub fn connect_popover_sort(gui_data: &GuiData) {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use glib::types::Type;
use gtk4::prelude::*; use gtk4::prelude::*;
use gtk4::{Popover, TreeView}; use gtk4::{Popover, TreeView};
@ -126,7 +127,7 @@ mod test {
#[gtk4::test] #[gtk4::test]
fn test_sort_iters() { fn test_sort_iters() {
let columns_types: &[glib::types::Type] = &[glib::types::Type::U32, glib::types::Type::STRING]; let columns_types: &[Type] = &[Type::U32, Type::STRING];
let list_store = gtk4::ListStore::new(columns_types); let list_store = gtk4::ListStore::new(columns_types);
let values_to_add: &[&[(u32, &dyn ToValue)]] = &[&[(0, &2), (1, &"AAA")], &[(0, &3), (1, &"CCC")], &[(0, &1), (1, &"BBB")]]; let values_to_add: &[&[(u32, &dyn ToValue)]] = &[&[(0, &2), (1, &"AAA")], &[(0, &3), (1, &"CCC")], &[(0, &1), (1, &"BBB")]];
@ -156,7 +157,7 @@ mod test {
#[gtk4::test] #[gtk4::test]
pub fn test_popover_sort_general_simple() { pub fn test_popover_sort_general_simple() {
let columns_types: &[glib::types::Type] = &[glib::types::Type::BOOL, glib::types::Type::STRING]; let columns_types: &[Type] = &[Type::BOOL, Type::STRING];
let list_store = gtk4::ListStore::new(columns_types); let list_store = gtk4::ListStore::new(columns_types);
let tree_view = TreeView::builder().model(&list_store).build(); let tree_view = TreeView::builder().model(&list_store).build();
let popover = Popover::new(); let popover = Popover::new();
@ -179,7 +180,7 @@ mod test {
#[gtk4::test] #[gtk4::test]
pub fn test_popover_sort_general() { pub fn test_popover_sort_general() {
let columns_types: &[glib::types::Type] = &[glib::types::Type::BOOL, glib::types::Type::STRING]; let columns_types: &[Type] = &[Type::BOOL, Type::STRING];
let list_store = gtk4::ListStore::new(columns_types); let list_store = gtk4::ListStore::new(columns_types);
let tree_view = TreeView::builder().model(&list_store).build(); let tree_view = TreeView::builder().model(&list_store).build();
let popover = Popover::new(); let popover = Popover::new();

View file

@ -2,8 +2,8 @@ use futures::channel::mpsc::UnboundedReceiver;
use futures::StreamExt; use futures::StreamExt;
use gtk4::prelude::*; use gtk4::prelude::*;
use czkawka_core::common_dir_traversal;
use czkawka_core::common_dir_traversal::ProgressData; use czkawka_core::common_dir_traversal::ProgressData;
use czkawka_core::{big_file, broken_files, common_dir_traversal, similar_images, similar_videos, temporary};
use crate::flg; use crate::flg;
use crate::gui_structs::gui_data::GuiData; use crate::gui_structs::gui_data::GuiData;
@ -16,13 +16,13 @@ pub fn connect_progress_window(
mut futures_receiver_duplicate_files: UnboundedReceiver<ProgressData>, mut futures_receiver_duplicate_files: UnboundedReceiver<ProgressData>,
mut futures_receiver_empty_files: UnboundedReceiver<ProgressData>, mut futures_receiver_empty_files: UnboundedReceiver<ProgressData>,
mut futures_receiver_empty_folder: UnboundedReceiver<ProgressData>, mut futures_receiver_empty_folder: UnboundedReceiver<ProgressData>,
mut futures_receiver_big_files: UnboundedReceiver<big_file::ProgressData>, mut futures_receiver_big_files: UnboundedReceiver<ProgressData>,
mut futures_receiver_same_music: UnboundedReceiver<ProgressData>, mut futures_receiver_same_music: UnboundedReceiver<ProgressData>,
mut futures_receiver_similar_images: UnboundedReceiver<similar_images::ProgressData>, mut futures_receiver_similar_images: UnboundedReceiver<ProgressData>,
mut futures_receiver_similar_videos: UnboundedReceiver<similar_videos::ProgressData>, mut futures_receiver_similar_videos: UnboundedReceiver<ProgressData>,
mut futures_receiver_temporary: UnboundedReceiver<temporary::ProgressData>, mut futures_receiver_temporary: UnboundedReceiver<ProgressData>,
mut futures_receiver_invalid_symlinks: UnboundedReceiver<ProgressData>, mut futures_receiver_invalid_symlinks: UnboundedReceiver<ProgressData>,
mut futures_receiver_broken_files: UnboundedReceiver<broken_files::ProgressData>, mut futures_receiver_broken_files: UnboundedReceiver<ProgressData>,
mut futures_receiver_bad_extensions: UnboundedReceiver<ProgressData>, mut futures_receiver_bad_extensions: UnboundedReceiver<ProgressData>,
) { ) {
let main_context = glib::MainContext::default(); let main_context = glib::MainContext::default();
@ -175,7 +175,7 @@ pub fn connect_progress_window(
while let Some(item) = futures_receiver_big_files.next().await { while let Some(item) = futures_receiver_big_files.next().await {
label_stage.set_text(&flg!( label_stage.set_text(&flg!(
"progress_scanning_general_file", "progress_scanning_general_file",
generate_translation_hashmap(vec![("file_number", item.files_checked.to_string())]) generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
)); ));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE); taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
} }
@ -257,18 +257,18 @@ pub fn connect_progress_window(
progress_bar_current_stage.hide(); progress_bar_current_stage.hide();
label_stage.set_text(&flg!( label_stage.set_text(&flg!(
"progress_scanning_general_file", "progress_scanning_general_file",
generate_translation_hashmap(vec![("file_number", item.images_checked.to_string())]) generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
)); ));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE); taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
} }
1 => { 1 => {
progress_bar_current_stage.show(); progress_bar_current_stage.show();
if item.images_to_check != 0 { if item.entries_to_check != 0 {
progress_bar_all_stages.set_fraction((1f64 + (item.images_checked) as f64 / item.images_to_check as f64) / (item.max_stage + 1) as f64); progress_bar_all_stages.set_fraction((1f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64);
progress_bar_current_stage.set_fraction((item.images_checked) as f64 / item.images_to_check as f64); progress_bar_current_stage.set_fraction((item.entries_checked) as f64 / item.entries_to_check as f64);
taskbar_state.borrow().set_progress_value( taskbar_state.borrow().set_progress_value(
(item.images_to_check + item.images_checked) as u64, (item.entries_to_check + item.entries_checked) as u64,
item.images_to_check as u64 * (item.max_stage + 1) as u64, item.entries_to_check as u64 * (item.max_stage + 1) as u64,
); );
} else { } else {
progress_bar_all_stages.set_fraction((item.current_stage as f64) / (item.max_stage + 1) as f64); progress_bar_all_stages.set_fraction((item.current_stage as f64) / (item.max_stage + 1) as f64);
@ -277,17 +277,17 @@ pub fn connect_progress_window(
} }
label_stage.set_text(&flg!( label_stage.set_text(&flg!(
"progress_scanning_image", "progress_scanning_image",
generate_translation_hashmap(vec![("file_checked", item.images_checked.to_string()), ("all_files", item.images_to_check.to_string())]) generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())])
)); ));
} }
2 => { 2 => {
progress_bar_current_stage.show(); progress_bar_current_stage.show();
if item.images_to_check != 0 { if item.entries_to_check != 0 {
progress_bar_all_stages.set_fraction((2f64 + (item.images_checked) as f64 / item.images_to_check as f64) / (item.max_stage + 1) as f64); progress_bar_all_stages.set_fraction((2f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64);
progress_bar_current_stage.set_fraction((item.images_checked) as f64 / item.images_to_check as f64); progress_bar_current_stage.set_fraction((item.entries_checked) as f64 / item.entries_to_check as f64);
taskbar_state.borrow().set_progress_value( taskbar_state.borrow().set_progress_value(
(item.images_to_check + item.images_checked) as u64, (item.entries_to_check + item.entries_checked) as u64,
item.images_to_check as u64 * (item.max_stage + 1) as u64, item.entries_to_check as u64 * (item.max_stage + 1) as u64,
); );
} else { } else {
progress_bar_all_stages.set_fraction((item.current_stage as f64) / (item.max_stage + 1) as f64); progress_bar_all_stages.set_fraction((item.current_stage as f64) / (item.max_stage + 1) as f64);
@ -296,7 +296,7 @@ pub fn connect_progress_window(
} }
label_stage.set_text(&flg!( label_stage.set_text(&flg!(
"progress_comparing_image_hashes", "progress_comparing_image_hashes",
generate_translation_hashmap(vec![("file_checked", item.images_checked.to_string()), ("all_files", item.images_to_check.to_string())]) generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())])
)); ));
} }
_ => { _ => {
@ -320,18 +320,18 @@ pub fn connect_progress_window(
progress_bar_current_stage.hide(); progress_bar_current_stage.hide();
label_stage.set_text(&flg!( label_stage.set_text(&flg!(
"progress_scanning_general_file", "progress_scanning_general_file",
generate_translation_hashmap(vec![("file_number", item.videos_checked.to_string())]) generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
)); ));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE); taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
} }
1 => { 1 => {
progress_bar_current_stage.show(); progress_bar_current_stage.show();
if item.videos_to_check != 0 { if item.entries_to_check != 0 {
progress_bar_all_stages.set_fraction((1f64 + (item.videos_checked) as f64 / item.videos_to_check as f64) / (item.max_stage + 1) as f64); progress_bar_all_stages.set_fraction((1f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64);
progress_bar_current_stage.set_fraction((item.videos_checked) as f64 / item.videos_to_check as f64); progress_bar_current_stage.set_fraction((item.entries_checked) as f64 / item.entries_to_check as f64);
taskbar_state.borrow().set_progress_value( taskbar_state.borrow().set_progress_value(
(item.videos_to_check + item.videos_checked) as u64, (item.entries_to_check + item.entries_checked) as u64,
item.videos_to_check as u64 * (item.max_stage + 1) as u64, item.entries_to_check as u64 * (item.max_stage + 1) as u64,
); );
} else { } else {
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64); progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
@ -340,7 +340,7 @@ pub fn connect_progress_window(
} }
label_stage.set_text(&flg!( label_stage.set_text(&flg!(
"progress_scanning_video", "progress_scanning_video",
generate_translation_hashmap(vec![("file_checked", item.videos_checked.to_string()), ("all_files", item.videos_to_check.to_string())]) generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())])
)); ));
} }
_ => { _ => {
@ -359,7 +359,7 @@ pub fn connect_progress_window(
while let Some(item) = futures_receiver_temporary.next().await { while let Some(item) = futures_receiver_temporary.next().await {
label_stage.set_text(&flg!( label_stage.set_text(&flg!(
"progress_scanning_general_file", "progress_scanning_general_file",
generate_translation_hashmap(vec![("file_number", item.files_checked.to_string())]) generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
)); ));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE); taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
} }
@ -394,18 +394,19 @@ pub fn connect_progress_window(
progress_bar_current_stage.hide(); progress_bar_current_stage.hide();
label_stage.set_text(&flg!( label_stage.set_text(&flg!(
"progress_scanning_general_file", "progress_scanning_general_file",
generate_translation_hashmap(vec![("file_number", item.files_checked.to_string())]) generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())])
)); ));
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE); taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
} }
1 => { 1 => {
progress_bar_current_stage.show(); progress_bar_current_stage.show();
if item.files_to_check != 0 { if item.entries_to_check != 0 {
progress_bar_all_stages.set_fraction((1f64 + (item.files_checked) as f64 / item.files_to_check as f64) / (item.max_stage + 1) as f64); progress_bar_all_stages.set_fraction((1f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64);
progress_bar_current_stage.set_fraction((item.files_checked) as f64 / item.files_to_check as f64); progress_bar_current_stage.set_fraction((item.entries_checked) as f64 / item.entries_to_check as f64);
taskbar_state taskbar_state.borrow().set_progress_value(
.borrow() (item.entries_to_check + item.entries_checked) as u64,
.set_progress_value((item.files_to_check + item.files_checked) as u64, item.files_to_check as u64 * (item.max_stage + 1) as u64); item.entries_to_check as u64 * (item.max_stage + 1) as u64,
);
} else { } else {
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64); progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
progress_bar_current_stage.set_fraction(0f64); progress_bar_current_stage.set_fraction(0f64);
@ -413,7 +414,7 @@ pub fn connect_progress_window(
} }
label_stage.set_text(&flg!( label_stage.set_text(&flg!(
"progress_scanning_broken_files", "progress_scanning_broken_files",
generate_translation_hashmap(vec![("file_checked", item.files_checked.to_string()), ("all_files", item.files_to_check.to_string())]) generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())])
)); ));
} }
_ => { _ => {

View file

@ -1,5 +1,6 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::BufReader;
use std::rc::Rc; use std::rc::Rc;
use crossbeam_channel::bounded; use crossbeam_channel::bounded;
@ -122,7 +123,7 @@ impl GuiData {
window_main.set_title(Some(&flg!("window_main_title"))); window_main.set_title(Some(&flg!("window_main_title")));
window_main.show(); window_main.show();
let pixbuf = Pixbuf::from_read(std::io::BufReader::new(ICON_ABOUT)).unwrap(); let pixbuf = Pixbuf::from_read(BufReader::new(ICON_ABOUT)).unwrap();
window_main.set_application(Some(application)); window_main.set_application(Some(application));

View file

@ -1,5 +1,6 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::BufReader;
use std::path::PathBuf; use std::path::PathBuf;
use gdk4::gdk_pixbuf::{InterpType, Pixbuf}; use gdk4::gdk_pixbuf::{InterpType, Pixbuf};
@ -714,7 +715,7 @@ const TYPE_OF_INTERPOLATION: InterpType = InterpType::Tiles;
pub fn set_icon_of_button<P: IsA<Widget>>(button: &P, data: &'static [u8]) { pub fn set_icon_of_button<P: IsA<Widget>>(button: &P, data: &'static [u8]) {
let image = get_custom_image_from_widget(&button.clone()); let image = get_custom_image_from_widget(&button.clone());
let pixbuf = Pixbuf::from_read(std::io::BufReader::new(data)).unwrap(); let pixbuf = Pixbuf::from_read(BufReader::new(data)).unwrap();
let pixbuf = pixbuf.scale_simple(SIZE_OF_ICON, SIZE_OF_ICON, TYPE_OF_INTERPOLATION).unwrap(); let pixbuf = pixbuf.scale_simple(SIZE_OF_ICON, SIZE_OF_ICON, TYPE_OF_INTERPOLATION).unwrap();
image.set_from_pixbuf(Some(&pixbuf)); image.set_from_pixbuf(Some(&pixbuf));
} }
@ -778,6 +779,7 @@ pub fn scale_step_function(scale: &gtk4::Scale, _scroll_type: ScrollType, value:
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use glib::types::Type;
use gtk4::prelude::*; use gtk4::prelude::*;
use gtk4::Orientation; use gtk4::Orientation;
use image::DynamicImage; use image::DynamicImage;
@ -789,7 +791,7 @@ mod test {
#[gtk4::test] #[gtk4::test]
fn test_check_if_list_store_column_have_all_same_values() { fn test_check_if_list_store_column_have_all_same_values() {
let columns_types: &[glib::types::Type] = &[glib::types::Type::BOOL]; let columns_types: &[Type] = &[Type::BOOL];
let list_store = gtk4::ListStore::new(columns_types); let list_store = gtk4::ListStore::new(columns_types);
list_store.clear(); list_store.clear();
@ -823,7 +825,7 @@ mod test {
#[gtk4::test] #[gtk4::test]
fn test_check_if_value_is_in_list_store() { fn test_check_if_value_is_in_list_store() {
let columns_types: &[glib::types::Type] = &[glib::types::Type::STRING]; let columns_types: &[Type] = &[Type::STRING];
let list_store = gtk4::ListStore::new(columns_types); let list_store = gtk4::ListStore::new(columns_types);
let values_to_add: &[(u32, &dyn ToValue)] = &[(0, &"Koczkodan"), (0, &"Kachir")]; let values_to_add: &[(u32, &dyn ToValue)] = &[(0, &"Koczkodan"), (0, &"Kachir")];
for i in values_to_add { for i in values_to_add {
@ -833,7 +835,7 @@ mod test {
assert!(check_if_value_is_in_list_store(&list_store, 0, "Kachir")); assert!(check_if_value_is_in_list_store(&list_store, 0, "Kachir"));
assert!(!check_if_value_is_in_list_store(&list_store, 0, "Koczkodan2")); assert!(!check_if_value_is_in_list_store(&list_store, 0, "Koczkodan2"));
let columns_types: &[glib::types::Type] = &[glib::types::Type::STRING, glib::types::Type::STRING]; let columns_types: &[Type] = &[Type::STRING, Type::STRING];
let list_store = gtk4::ListStore::new(columns_types); let list_store = gtk4::ListStore::new(columns_types);
let values_to_add: &[&[(u32, &dyn ToValue)]] = &[&[(0, &"Koczkodan"), (1, &"Krakus")], &[(0, &"Kachir"), (1, &"Wodnica")]]; let values_to_add: &[&[(u32, &dyn ToValue)]] = &[&[(0, &"Koczkodan"), (1, &"Krakus")], &[(0, &"Kachir"), (1, &"Wodnica")]];
for i in values_to_add { for i in values_to_add {

View file

@ -3,6 +3,7 @@ use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
use gdk4::gdk_pixbuf::Pixbuf; use gdk4::gdk_pixbuf::Pixbuf;
use glib::types::Type;
use gtk4::gdk_pixbuf::InterpType; use gtk4::gdk_pixbuf::InterpType;
use gtk4::prelude::*; use gtk4::prelude::*;
use gtk4::{CheckButton, Image, SelectionMode, TextView, TreeView}; use gtk4::{CheckButton, Image, SelectionMode, TextView, TreeView};
@ -304,9 +305,9 @@ pub fn initialize_gui(gui_data: &mut GuiData) {
let evk = gui_data.upper_notebook.evk_tree_view_included_directories.clone(); let evk = gui_data.upper_notebook.evk_tree_view_included_directories.clone();
let gc = gui_data.upper_notebook.gc_tree_view_included_directories.clone(); let gc = gui_data.upper_notebook.gc_tree_view_included_directories.clone();
let col_types: [glib::types::Type; 2] = [ let col_types: [Type; 2] = [
glib::types::Type::STRING, // Path Type::STRING, // Path
glib::types::Type::BOOL, // ReferenceButton Type::BOOL, // ReferenceButton
]; ];
let list_store: gtk4::ListStore = gtk4::ListStore::new(&col_types); let list_store: gtk4::ListStore = gtk4::ListStore::new(&col_types);
@ -341,7 +342,7 @@ pub fn initialize_gui(gui_data: &mut GuiData) {
let evk = gui_data.upper_notebook.evk_tree_view_excluded_directories.clone(); let evk = gui_data.upper_notebook.evk_tree_view_excluded_directories.clone();
let gc = gui_data.upper_notebook.gc_tree_view_excluded_directories.clone(); let gc = gui_data.upper_notebook.gc_tree_view_excluded_directories.clone();
let col_types: [glib::types::Type; 1] = [glib::types::Type::STRING]; let col_types: [Type; 1] = [Type::STRING];
let list_store: gtk4::ListStore = gtk4::ListStore::new(&col_types); let list_store: gtk4::ListStore = gtk4::ListStore::new(&col_types);
tree_view.set_model(Some(&list_store)); tree_view.set_model(Some(&list_store));

View file

@ -5,6 +5,8 @@
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
#![allow(clippy::needless_late_init)] #![allow(clippy::needless_late_init)]
use futures::channel::mpsc;
use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
use std::env; use std::env;
use std::ffi::OsString; use std::ffi::OsString;
@ -32,6 +34,7 @@ use connect_things::connect_settings::*;
use connect_things::connect_show_hide_ui::*; use connect_things::connect_show_hide_ui::*;
use connect_things::connect_similar_image_size_change::*; use connect_things::connect_similar_image_size_change::*;
use czkawka_core::common::{get_number_of_threads, set_number_of_threads}; use czkawka_core::common::{get_number_of_threads, set_number_of_threads};
use czkawka_core::common_dir_traversal::ProgressData;
use czkawka_core::*; use czkawka_core::*;
use gui_structs::gui_data::*; use gui_structs::gui_data::*;
@ -80,50 +83,17 @@ fn build_ui(application: &Application, arguments: &[OsString]) {
let (glib_stop_sender, glib_stop_receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); let (glib_stop_sender, glib_stop_receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
// Futures progress report // Futures progress report
let (futures_sender_duplicate_files, futures_receiver_duplicate_files): ( let (futures_sender_duplicate_files, futures_receiver_duplicate_files): (UnboundedSender<ProgressData>, UnboundedReceiver<ProgressData>) = mpsc::unbounded();
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>, let (futures_sender_empty_files, futures_receiver_empty_files): (UnboundedSender<ProgressData>, UnboundedReceiver<ProgressData>) = mpsc::unbounded();
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>, let (futures_sender_empty_folder, futures_receiver_empty_folder): (UnboundedSender<ProgressData>, UnboundedReceiver<ProgressData>) = mpsc::unbounded();
) = futures::channel::mpsc::unbounded(); let (futures_sender_big_file, futures_receiver_big_files): (UnboundedSender<ProgressData>, UnboundedReceiver<ProgressData>) = mpsc::unbounded();
let (futures_sender_empty_files, futures_receiver_empty_files): ( let (futures_sender_same_music, futures_receiver_same_music): (UnboundedSender<ProgressData>, UnboundedReceiver<ProgressData>) = mpsc::unbounded();
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>, let (futures_sender_similar_images, futures_receiver_similar_images): (UnboundedSender<ProgressData>, UnboundedReceiver<ProgressData>) = mpsc::unbounded();
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>, let (futures_sender_similar_videos, futures_receiver_similar_videos): (UnboundedSender<ProgressData>, UnboundedReceiver<ProgressData>) = mpsc::unbounded();
) = futures::channel::mpsc::unbounded(); let (futures_sender_temporary, futures_receiver_temporary): (UnboundedSender<ProgressData>, UnboundedReceiver<ProgressData>) = mpsc::unbounded();
let (futures_sender_empty_folder, futures_receiver_empty_folder): ( let (futures_sender_invalid_symlinks, futures_receiver_invalid_symlinks): (UnboundedSender<ProgressData>, UnboundedReceiver<ProgressData>) = mpsc::unbounded();
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>, let (futures_sender_broken_files, futures_receiver_broken_files): (UnboundedSender<ProgressData>, UnboundedReceiver<ProgressData>) = mpsc::unbounded();
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>, let (futures_sender_bad_extensions, futures_receiver_bad_extensions): (UnboundedSender<ProgressData>, UnboundedReceiver<ProgressData>) = mpsc::unbounded();
) = futures::channel::mpsc::unbounded();
let (futures_sender_big_file, futures_receiver_big_files): (
futures::channel::mpsc::UnboundedSender<big_file::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<big_file::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_same_music, futures_receiver_same_music): (
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_similar_images, futures_receiver_similar_images): (
futures::channel::mpsc::UnboundedSender<similar_images::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<similar_images::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_similar_videos, futures_receiver_similar_videos): (
futures::channel::mpsc::UnboundedSender<similar_videos::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<similar_videos::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_temporary, futures_receiver_temporary): (
futures::channel::mpsc::UnboundedSender<temporary::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<temporary::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_invalid_symlinks, futures_receiver_invalid_symlinks): (
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_broken_files, futures_receiver_broken_files): (
futures::channel::mpsc::UnboundedSender<broken_files::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<broken_files::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_bad_extensions, futures_receiver_bad_extensions): (
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = futures::channel::mpsc::unbounded();
initialize_gui(&mut gui_data); initialize_gui(&mut gui_data);
validate_notebook_data(&gui_data); // Must be run after initialization of gui, to check if everything was properly setup validate_notebook_data(&gui_data); // Must be run after initialization of gui, to check if everything was properly setup

View file

@ -3,6 +3,7 @@ use crate::help_functions::{
ColumnsSameMusic, ColumnsSimilarImages, ColumnsSimilarVideos, ColumnsTemporaryFiles, PopoverTypes, ColumnsSameMusic, ColumnsSimilarImages, ColumnsSimilarVideos, ColumnsTemporaryFiles, PopoverTypes,
}; };
use crate::notebook_enums::{NotebookMainEnum, NUMBER_OF_NOTEBOOK_MAIN_TABS}; use crate::notebook_enums::{NotebookMainEnum, NUMBER_OF_NOTEBOOK_MAIN_TABS};
use glib::types::Type;
pub struct NotebookObject { pub struct NotebookObject {
pub notebook_type: NotebookMainEnum, pub notebook_type: NotebookMainEnum,
@ -16,7 +17,7 @@ pub struct NotebookObject {
pub column_size: Option<i32>, pub column_size: Option<i32>,
pub column_size_as_bytes: Option<i32>, pub column_size_as_bytes: Option<i32>,
pub column_modification_as_secs: Option<i32>, pub column_modification_as_secs: Option<i32>,
pub columns_types: &'static [glib::types::Type], pub columns_types: &'static [Type],
pub bottom_buttons: &'static [BottomButtonsEnum], pub bottom_buttons: &'static [BottomButtonsEnum],
} }
@ -41,17 +42,17 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
column_size_as_bytes: Some(ColumnsDuplicates::SizeAsBytes as i32), column_size_as_bytes: Some(ColumnsDuplicates::SizeAsBytes as i32),
column_modification_as_secs: Some(ColumnsDuplicates::ModificationAsSecs as i32), column_modification_as_secs: Some(ColumnsDuplicates::ModificationAsSecs as i32),
columns_types: &[ columns_types: &[
glib::types::Type::BOOL, // ActivatableSelectButton Type::BOOL, // ActivatableSelectButton
glib::types::Type::BOOL, // SelectionButton Type::BOOL, // SelectionButton
glib::types::Type::STRING, // Size Type::STRING, // Size
glib::types::Type::U64, // SizeAsBytes Type::U64, // SizeAsBytes
glib::types::Type::STRING, // Name Type::STRING, // Name
glib::types::Type::STRING, // Path Type::STRING, // Path
glib::types::Type::STRING, // Modification Type::STRING, // Modification
glib::types::Type::U64, // ModificationAsSecs Type::U64, // ModificationAsSecs
glib::types::Type::STRING, // Color Type::STRING, // Color
glib::types::Type::BOOL, // IsHeader Type::BOOL, // IsHeader
glib::types::Type::STRING, // TextColor Type::STRING, // TextColor
], ],
bottom_buttons: &[ bottom_buttons: &[
BottomButtonsEnum::Save, BottomButtonsEnum::Save,
@ -76,11 +77,11 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
column_size_as_bytes: None, column_size_as_bytes: None,
column_modification_as_secs: None, column_modification_as_secs: None,
columns_types: &[ columns_types: &[
glib::types::Type::BOOL, // SelectionButton Type::BOOL, // SelectionButton
glib::types::Type::STRING, // Name Type::STRING, // Name
glib::types::Type::STRING, // Path Type::STRING, // Path
glib::types::Type::STRING, // Modification Type::STRING, // Modification
glib::types::Type::U64, // ModificationAsSecs Type::U64, // ModificationAsSecs
], ],
bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move],
}, },
@ -97,13 +98,13 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
column_size_as_bytes: None, column_size_as_bytes: None,
column_modification_as_secs: None, column_modification_as_secs: None,
columns_types: &[ columns_types: &[
glib::types::Type::BOOL, // SelectionButton Type::BOOL, // SelectionButton
glib::types::Type::STRING, // Size Type::STRING, // Size
glib::types::Type::STRING, // Name Type::STRING, // Name
glib::types::Type::STRING, // Path Type::STRING, // Path
glib::types::Type::STRING, // Modification Type::STRING, // Modification
glib::types::Type::U64, // SizeAsBytes Type::U64, // SizeAsBytes
glib::types::Type::U64, // ModificationAsSecs Type::U64, // ModificationAsSecs
], ],
bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move],
}, },
@ -120,11 +121,11 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
column_size_as_bytes: None, column_size_as_bytes: None,
column_modification_as_secs: None, column_modification_as_secs: None,
columns_types: &[ columns_types: &[
glib::types::Type::BOOL, // SelectionButton Type::BOOL, // SelectionButton
glib::types::Type::STRING, // Name Type::STRING, // Name
glib::types::Type::STRING, // Path Type::STRING, // Path
glib::types::Type::STRING, // Modification Type::STRING, // Modification
glib::types::Type::U64, // ModificationAsSecs Type::U64, // ModificationAsSecs
], ],
bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move],
}, },
@ -141,11 +142,11 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
column_size_as_bytes: None, column_size_as_bytes: None,
column_modification_as_secs: None, column_modification_as_secs: None,
columns_types: &[ columns_types: &[
glib::types::Type::BOOL, // SelectionButton Type::BOOL, // SelectionButton
glib::types::Type::STRING, // Name Type::STRING, // Name
glib::types::Type::STRING, // Path Type::STRING, // Path
glib::types::Type::STRING, // Modification Type::STRING, // Modification
glib::types::Type::U64, // ModificationAsSecs Type::U64, // ModificationAsSecs
], ],
bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move],
}, },
@ -162,19 +163,19 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
column_size_as_bytes: Some(ColumnsSimilarImages::SizeAsBytes as i32), column_size_as_bytes: Some(ColumnsSimilarImages::SizeAsBytes as i32),
column_modification_as_secs: Some(ColumnsSimilarImages::ModificationAsSecs as i32), column_modification_as_secs: Some(ColumnsSimilarImages::ModificationAsSecs as i32),
columns_types: &[ columns_types: &[
glib::types::Type::BOOL, // ActivatableSelectButton Type::BOOL, // ActivatableSelectButton
glib::types::Type::BOOL, // SelectionButton Type::BOOL, // SelectionButton
glib::types::Type::STRING, // Similarity Type::STRING, // Similarity
glib::types::Type::STRING, // Size Type::STRING, // Size
glib::types::Type::U64, // SizeAsBytes Type::U64, // SizeAsBytes
glib::types::Type::STRING, // Dimensions Type::STRING, // Dimensions
glib::types::Type::STRING, // Name Type::STRING, // Name
glib::types::Type::STRING, // Path Type::STRING, // Path
glib::types::Type::STRING, // Modification Type::STRING, // Modification
glib::types::Type::U64, // ModificationAsSecs Type::U64, // ModificationAsSecs
glib::types::Type::STRING, // Color Type::STRING, // Color
glib::types::Type::BOOL, // IsHeader Type::BOOL, // IsHeader
glib::types::Type::STRING, // TextColor Type::STRING, // TextColor
], ],
bottom_buttons: &[ bottom_buttons: &[
BottomButtonsEnum::Save, BottomButtonsEnum::Save,
@ -200,17 +201,17 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
column_size_as_bytes: Some(ColumnsSimilarVideos::SizeAsBytes as i32), column_size_as_bytes: Some(ColumnsSimilarVideos::SizeAsBytes as i32),
column_modification_as_secs: Some(ColumnsSimilarVideos::ModificationAsSecs as i32), column_modification_as_secs: Some(ColumnsSimilarVideos::ModificationAsSecs as i32),
columns_types: &[ columns_types: &[
glib::types::Type::BOOL, // ActivatableSelectButton Type::BOOL, // ActivatableSelectButton
glib::types::Type::BOOL, // SelectionButton Type::BOOL, // SelectionButton
glib::types::Type::STRING, // Size Type::STRING, // Size
glib::types::Type::U64, // SizeAsBytes Type::U64, // SizeAsBytes
glib::types::Type::STRING, // Name Type::STRING, // Name
glib::types::Type::STRING, // Path Type::STRING, // Path
glib::types::Type::STRING, // Modification Type::STRING, // Modification
glib::types::Type::U64, // ModificationAsSecs Type::U64, // ModificationAsSecs
glib::types::Type::STRING, // Color Type::STRING, // Color
glib::types::Type::BOOL, // IsHeader Type::BOOL, // IsHeader
glib::types::Type::STRING, // TextColor Type::STRING, // TextColor
], ],
bottom_buttons: &[ bottom_buttons: &[
BottomButtonsEnum::Save, BottomButtonsEnum::Save,
@ -235,24 +236,24 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
column_size_as_bytes: Some(ColumnsSameMusic::SizeAsBytes as i32), column_size_as_bytes: Some(ColumnsSameMusic::SizeAsBytes as i32),
column_modification_as_secs: Some(ColumnsSameMusic::ModificationAsSecs as i32), column_modification_as_secs: Some(ColumnsSameMusic::ModificationAsSecs as i32),
columns_types: &[ columns_types: &[
glib::types::Type::BOOL, // ActivatableSelectButton Type::BOOL, // ActivatableSelectButton
glib::types::Type::BOOL, // SelectionButton Type::BOOL, // SelectionButton
glib::types::Type::STRING, // Size Type::STRING, // Size
glib::types::Type::U64, // SizeAsBytes Type::U64, // SizeAsBytes
glib::types::Type::STRING, // Name Type::STRING, // Name
glib::types::Type::STRING, // Path Type::STRING, // Path
glib::types::Type::STRING, // Title Type::STRING, // Title
glib::types::Type::STRING, // Artist Type::STRING, // Artist
glib::types::Type::STRING, // Year Type::STRING, // Year
glib::types::Type::STRING, // Bitrate Type::STRING, // Bitrate
glib::types::Type::U64, // BitrateAsNumber Type::U64, // BitrateAsNumber
glib::types::Type::STRING, // Length Type::STRING, // Length
glib::types::Type::STRING, // Genre Type::STRING, // Genre
glib::types::Type::STRING, // Modification Type::STRING, // Modification
glib::types::Type::U64, // ModificationAsSecs Type::U64, // ModificationAsSecs
glib::types::Type::STRING, // Color Type::STRING, // Color
glib::types::Type::BOOL, // IsHeader Type::BOOL, // IsHeader
glib::types::Type::STRING, // TextColor Type::STRING, // TextColor
], ],
bottom_buttons: &[ bottom_buttons: &[
BottomButtonsEnum::Save, BottomButtonsEnum::Save,
@ -277,13 +278,13 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
column_size_as_bytes: None, column_size_as_bytes: None,
column_modification_as_secs: None, column_modification_as_secs: None,
columns_types: &[ columns_types: &[
glib::types::Type::BOOL, // SelectionButton Type::BOOL, // SelectionButton
glib::types::Type::STRING, // Name Type::STRING, // Name
glib::types::Type::STRING, // Path Type::STRING, // Path
glib::types::Type::STRING, // DestinationPath Type::STRING, // DestinationPath
glib::types::Type::STRING, // TypeOfError Type::STRING, // TypeOfError
glib::types::Type::STRING, // Modification Type::STRING, // Modification
glib::types::Type::U64, // ModificationAsSecs Type::U64, // ModificationAsSecs
], ],
bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move],
}, },
@ -300,12 +301,12 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
column_size_as_bytes: None, column_size_as_bytes: None,
column_modification_as_secs: None, column_modification_as_secs: None,
columns_types: &[ columns_types: &[
glib::types::Type::BOOL, // SelectionButton Type::BOOL, // SelectionButton
glib::types::Type::STRING, // Name Type::STRING, // Name
glib::types::Type::STRING, // Path Type::STRING, // Path
glib::types::Type::STRING, // ErrorType Type::STRING, // ErrorType
glib::types::Type::STRING, // Modification Type::STRING, // Modification
glib::types::Type::U64, // ModificationAsSecs Type::U64, // ModificationAsSecs
], ],
bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move],
}, },
@ -322,13 +323,13 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
column_size_as_bytes: None, column_size_as_bytes: None,
column_modification_as_secs: None, column_modification_as_secs: None,
columns_types: &[ columns_types: &[
glib::types::Type::BOOL, // SelectionButton Type::BOOL, // SelectionButton
glib::types::Type::STRING, // Name Type::STRING, // Name
glib::types::Type::STRING, // Path Type::STRING, // Path
glib::types::Type::STRING, // CurrentExtension Type::STRING, // CurrentExtension
glib::types::Type::STRING, // ProperExtensions Type::STRING, // ProperExtensions
glib::types::Type::STRING, // Modification Type::STRING, // Modification
glib::types::Type::U64, // ModificationAsSecs Type::U64, // ModificationAsSecs
], ],
bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move],
}, },