Multiple small changes (#490)

This commit is contained in:
Rafał Mikrut 2021-12-05 21:01:16 +01:00 committed by GitHub
parent e2d31d4f98
commit a8aa8a2a23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 101 additions and 77 deletions

9
Cargo.lock generated
View File

@ -504,6 +504,7 @@ dependencies = [
"rayon",
"rodio",
"serde",
"serde_json",
"tempfile",
"vid_dup_finder_lib",
"xxhash-rust",
@ -1183,9 +1184,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.108"
version = "0.2.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01"
[[package]]
name = "libloading"
@ -1232,9 +1233,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "memoffset"
version = "0.6.4"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]

View File

@ -44,8 +44,11 @@ tempfile = "3.2.0"
# Video Duplactes
vid_dup_finder_lib = "0.1.0"
ffmpeg_cmdline_utils = "0.1.0"
# Saving/Loading Cache
serde = "1.0.130"
bincode = "1.3.3"
serde_json = "1.0.72"
[features]

View File

@ -423,7 +423,7 @@ impl SimilarVideos {
Ok(t) => t,
Err(e) => {
return {
file_entry.error = format!("Failed to hash file, {}", e);
file_entry.error = format!("Failed to hash file, reason {}", e);
Some(file_entry)
}
}
@ -592,14 +592,6 @@ impl PrintResults for SimilarVideos {
pub fn save_hashes_to_file(hashmap: &BTreeMap<String, FileEntry>, text_messages: &mut Messages) {
if let Some(proj_dirs) = ProjectDirs::from("pl", "Qarmin", "Czkawka") {
// Lin: /home/username/.cache/czkawka
// Win: C:\Users\Username\AppData\Local\Qarmin\Czkawka\cache
// Mac: /Users/Username/Library/Caches/pl.Qarmin.Czkawka
// Saves data
// path//file_size//modified_date//num_frames//duration//hash1//hash2 etc.
// number of hashes is equal to HASH_QWORDS(19 at this moment)
let cache_dir = PathBuf::from(proj_dirs.cache_dir());
if cache_dir.exists() {
if !cache_dir.is_dir() {
@ -610,7 +602,8 @@ pub fn save_hashes_to_file(hashmap: &BTreeMap<String, FileEntry>, text_messages:
text_messages.messages.push(format!("Cannot create config dir {}, reason {}", cache_dir.display(), e));
return;
}
let cache_file = cache_dir.join("cache_similar_videos.bin");
let cache_file = cache_dir.join(cache_dir.join(get_cache_file()));
let file_handler = match OpenOptions::new().truncate(true).write(true).create(true).open(&cache_file) {
Ok(t) => t,
Err(e) => {
@ -620,25 +613,43 @@ pub fn save_hashes_to_file(hashmap: &BTreeMap<String, FileEntry>, text_messages:
};
let writer = BufWriter::new(file_handler);
#[cfg(not(debug_assertions))]
if let Err(e) = bincode::serialize_into(writer, hashmap) {
text_messages.messages.push(format!("cannot write data to cache file {}, reason {}", cache_file.display(), e));
text_messages.messages.push(format!("Cannot write data to cache file {}, reason {}", cache_file.display(), e));
return;
}
#[cfg(debug_assertions)]
if let Err(e) = serde_json::to_writer(writer, hashmap) {
text_messages.messages.push(format!("Cannot write data to cache file {}, reason {}", cache_file.display(), e));
return;
}
text_messages.messages.push(format!("Properly saved to file {} cache entries.", hashmap.len()));
}
}
pub fn load_hashes_from_file(text_messages: &mut Messages, delete_outdated_cache: bool) -> Option<BTreeMap<String, FileEntry>> {
if let Some(proj_dirs) = ProjectDirs::from("pl", "Qarmin", "Czkawka") {
let cache_dir = PathBuf::from(proj_dirs.cache_dir());
let cache_file = cache_dir.join("cache_similar_videos.bin");
let cache_file = cache_dir.join(get_cache_file());
let file_handler = match OpenOptions::new().read(true).open(&cache_file) {
Ok(t) => t,
Err(_inspected) => {
// text_messages.messages.push(format!("Cannot find or open cache file {}", cache_file.display())); // This shouldn't be write to output
// text_messages.messages.push(format!("Cannot find or open cache file {}", cache_file.display())); // No error warning
return None;
}
};
let reader = BufReader::new(file_handler);
#[cfg(debug_assertions)]
let mut hashmap_loaded_entries: BTreeMap<String, FileEntry> = match serde_json::from_reader(reader) {
Ok(t) => t,
Err(e) => {
text_messages.warnings.push(format!("Failed to load data from cache file {}, reason {}", cache_file.display(), e));
return None;
}
};
#[cfg(not(debug_assertions))]
let mut hashmap_loaded_entries: BTreeMap<String, FileEntry> = match bincode::deserialize_from(reader) {
Ok(t) => t,
Err(e) => {
@ -648,15 +659,33 @@ pub fn load_hashes_from_file(text_messages: &mut Messages, delete_outdated_cache
};
// Don't load cache data if destination file not exists
hashmap_loaded_entries.retain(|src_path, _file_entry| Path::new(src_path).exists() && !delete_outdated_cache);
if delete_outdated_cache {
hashmap_loaded_entries.retain(|src_path, _file_entry| Path::new(src_path).exists());
}
text_messages.messages.push(format!("Properly loaded {} cache entries.", hashmap_loaded_entries.len()));
return Some(hashmap_loaded_entries);
}
text_messages.messages.push("Cannot find or open system config dir to save cache file".to_string());
text_messages.messages.push("Cannot find or open system config dir to save cache file.".to_string());
None
}
fn get_cache_file() -> String {
let extension;
#[cfg(debug_assertions)]
{
extension = "json";
}
#[cfg(not(debug_assertions))]
{
extension = "bin";
}
format!("cache_similar_videos.{}", extension)
}
pub fn check_if_ffmpeg_is_installed() -> bool {
let vid = "999999999999999999.txt";
if let Err(DetermineVideo { src_path: _a, error: FfmpegNotFound }) = VideoHash::from_path(&vid) {

View File

@ -11,15 +11,18 @@ use crate::help_functions::*;
use crate::notebook_enums::*;
pub fn connect_button_hardlink_symlink(gui_data: &GuiData) {
// Hardlinking
{
let buttons_hardlink = gui_data.bottom_buttons.buttons_hardlink.clone();
let gui_data = gui_data.clone();
buttons_hardlink.connect_clicked(move |_| {
glib::MainContext::default().spawn_local(sym_hard_link_things(gui_data.clone(), false));
glib::MainContext::default().spawn_local(sym_hard_link_things(gui_data.clone(), true));
});
}
// Symlinking
{
let buttons_symlink = gui_data.bottom_buttons.buttons_symlink.clone();
@ -46,7 +49,7 @@ pub async fn sym_hard_link_things(gui_data: GuiData, hardlinking: bool) {
let tree_view = &main_tree_views[nb_number as usize];
let nb_object = &NOTEBOOKS_INFOS[nb_number as usize];
let column_color = nb_object.column_color.expect("Symlinking can be only used for tree views with grouped results");
let column_color = nb_object.column_color.expect("Linking can be only used for tree views with grouped results");
let check_button_settings_confirm_link = gui_data.settings.check_button_settings_confirm_link.clone();
@ -57,6 +60,7 @@ pub async fn sym_hard_link_things(gui_data: GuiData, hardlinking: bool) {
if !check_if_can_link_files(&check_button_settings_confirm_link, &window_main).await {
return;
}
if !check_if_changing_one_item_in_group_and_continue(tree_view, column_color, nb_object.column_selection, &window_main).await {
return;
}

View File

@ -1,41 +1,23 @@
use std::collections::HashMap;
use gtk::prelude::*;
use crate::gui_data::GuiData;
use crate::gui_popovers::GuiPopovers;
use crate::help_functions::PopoverTypes;
use crate::help_functions::{PopoverTypes, NOTEBOOKS_INFOS};
use crate::notebook_enums::*;
pub fn connect_button_select(gui_data: &GuiData) {
let mut hashmap: HashMap<NotebookMainEnum, Vec<PopoverTypes>> = Default::default();
{
hashmap.insert(NotebookMainEnum::SimilarImages, vec![PopoverTypes::All, PopoverTypes::Size, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::Date]);
hashmap.insert(NotebookMainEnum::SimilarVideos, vec![PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::Date, PopoverTypes::Size]);
hashmap.insert(NotebookMainEnum::Duplicate, vec![PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::Date]);
hashmap.insert(NotebookMainEnum::SameMusic, vec![PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::Date, PopoverTypes::Size]);
hashmap.insert(NotebookMainEnum::EmptyFiles, vec![PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom]);
hashmap.insert(NotebookMainEnum::EmptyDirectories, vec![PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom]);
hashmap.insert(NotebookMainEnum::BigFiles, vec![PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom]);
hashmap.insert(NotebookMainEnum::Symlinks, vec![PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom]);
hashmap.insert(NotebookMainEnum::Temporary, vec![PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom]);
hashmap.insert(NotebookMainEnum::BrokenFiles, vec![PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom]);
}
assert_eq!(hashmap.len(), NUMBER_OF_NOTEBOOK_MAIN_TABS);
let popovers = gui_data.popovers.clone();
let notebook_main = gui_data.main_notebook.notebook_main.clone();
let popover_select = gui_data.popovers.popover_select.clone();
let buttons_select = gui_data.bottom_buttons.buttons_select.clone();
buttons_select.connect_clicked(move |_| {
show_required_popovers(&popovers, &to_notebook_main_enum(notebook_main.current_page().unwrap()), &hashmap);
show_required_popovers(&popovers, &to_notebook_main_enum(notebook_main.current_page().unwrap()));
popover_select.popup();
});
}
fn show_required_popovers(popovers: &GuiPopovers, current_mode: &NotebookMainEnum, hashmap: &HashMap<NotebookMainEnum, Vec<PopoverTypes>>) {
fn show_required_popovers(popovers: &GuiPopovers, current_mode: &NotebookMainEnum) {
let buttons_popover_select_all = popovers.buttons_popover_select_all.clone();
let buttons_popover_unselect_all = popovers.buttons_popover_unselect_all.clone();
let buttons_popover_reverse = popovers.buttons_popover_reverse.clone();
@ -53,9 +35,9 @@ fn show_required_popovers(popovers: &GuiPopovers, current_mode: &NotebookMainEnu
let separator_select_image_size = popovers.separator_select_image_size.clone();
let separator_select_reverse = popovers.separator_select_reverse.clone();
let vec = hashmap.get(current_mode).unwrap();
let arr = &NOTEBOOKS_INFOS[current_mode.clone() as usize].available_modes;
if vec.contains(&PopoverTypes::All) {
if arr.contains(&PopoverTypes::All) {
buttons_popover_select_all.show();
buttons_popover_unselect_all.show();
} else {
@ -63,7 +45,7 @@ fn show_required_popovers(popovers: &GuiPopovers, current_mode: &NotebookMainEnu
buttons_popover_unselect_all.hide();
}
if vec.contains(&PopoverTypes::Size) {
if arr.contains(&PopoverTypes::Size) {
buttons_popover_select_all_images_except_biggest.show();
buttons_popover_select_all_images_except_smallest.show();
separator_select_image_size.show();
@ -73,7 +55,7 @@ fn show_required_popovers(popovers: &GuiPopovers, current_mode: &NotebookMainEnu
separator_select_image_size.hide();
}
if vec.contains(&PopoverTypes::Reverse) {
if arr.contains(&PopoverTypes::Reverse) {
buttons_popover_reverse.show();
separator_select_reverse.show();
} else {
@ -81,7 +63,7 @@ fn show_required_popovers(popovers: &GuiPopovers, current_mode: &NotebookMainEnu
separator_select_reverse.hide();
}
if vec.contains(&PopoverTypes::Custom) {
if arr.contains(&PopoverTypes::Custom) {
buttons_popover_select_custom.show();
buttons_popover_unselect_custom.show();
separator_select_custom.show();
@ -91,7 +73,7 @@ fn show_required_popovers(popovers: &GuiPopovers, current_mode: &NotebookMainEnu
separator_select_custom.hide();
}
if vec.contains(&PopoverTypes::Date) {
if arr.contains(&PopoverTypes::Date) {
buttons_popover_select_all_except_oldest.show();
buttons_popover_select_all_except_newest.show();
buttons_popover_select_one_oldest.show();

View File

@ -42,7 +42,7 @@ pub enum PopoverTypes {
pub struct NotebookObject {
pub notebook_type: NotebookMainEnum,
pub available_modes: [PopoverTypes; 4],
pub available_modes: [PopoverTypes; 5],
pub column_activatable_button: Option<i32>,
pub column_path: i32,
pub column_name: i32,
@ -57,7 +57,7 @@ pub struct NotebookObject {
pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
NotebookObject {
notebook_type: NotebookMainEnum::Duplicate,
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::Date],
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::Date, PopoverTypes::None],
column_activatable_button: Some(ColumnsDuplicates::ActivatableSelectButton as i32),
column_path: ColumnsDuplicates::Path as i32,
column_name: ColumnsDuplicates::Name as i32,
@ -70,7 +70,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
},
NotebookObject {
notebook_type: NotebookMainEnum::EmptyDirectories,
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::None],
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::None, PopoverTypes::None],
column_activatable_button: None,
column_path: ColumnsEmptyFolders::Path as i32,
column_name: ColumnsEmptyFolders::Name as i32,
@ -83,7 +83,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
},
NotebookObject {
notebook_type: NotebookMainEnum::BigFiles,
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::None],
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::None, PopoverTypes::None],
column_activatable_button: None,
column_path: ColumnsBigFiles::Path as i32,
column_name: ColumnsBigFiles::Name as i32,
@ -96,7 +96,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
},
NotebookObject {
notebook_type: NotebookMainEnum::EmptyFiles,
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::None],
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::None, PopoverTypes::None],
column_activatable_button: None,
column_path: ColumnsEmptyFiles::Path as i32,
column_name: ColumnsEmptyFiles::Name as i32,
@ -109,7 +109,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
},
NotebookObject {
notebook_type: NotebookMainEnum::Temporary,
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::None],
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::None, PopoverTypes::None],
column_activatable_button: None,
column_path: ColumnsTemporaryFiles::Path as i32,
column_name: ColumnsTemporaryFiles::Name as i32,
@ -122,7 +122,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
},
NotebookObject {
notebook_type: NotebookMainEnum::SimilarImages,
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::Date],
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::Date, PopoverTypes::Size],
column_activatable_button: Some(ColumnsSimilarImages::ActivatableSelectButton as i32),
column_path: ColumnsSimilarImages::Path as i32,
column_name: ColumnsSimilarImages::Name as i32,
@ -135,7 +135,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
},
NotebookObject {
notebook_type: NotebookMainEnum::SimilarVideos,
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::Date],
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::Date, PopoverTypes::Size],
column_activatable_button: Some(ColumnsSimilarVideos::ActivatableSelectButton as i32),
column_path: ColumnsSimilarVideos::Path as i32,
column_name: ColumnsSimilarVideos::Name as i32,
@ -148,7 +148,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
},
NotebookObject {
notebook_type: NotebookMainEnum::SameMusic,
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::Date],
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::Date, PopoverTypes::Size],
column_activatable_button: Some(ColumnsSameMusic::ActivatableSelectButton as i32),
column_path: ColumnsSameMusic::Path as i32,
column_name: ColumnsSameMusic::Name as i32,
@ -161,7 +161,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
},
NotebookObject {
notebook_type: NotebookMainEnum::Symlinks,
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::None],
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::None, PopoverTypes::None],
column_activatable_button: None,
column_path: ColumnsInvalidSymlinks::Path as i32,
column_name: ColumnsInvalidSymlinks::Name as i32,
@ -174,7 +174,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
},
NotebookObject {
notebook_type: NotebookMainEnum::BrokenFiles,
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::None],
available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::None, PopoverTypes::None],
column_activatable_button: None,
column_path: ColumnsBrokenFiles::Path as i32,
column_name: ColumnsBrokenFiles::Name as i32,
@ -357,33 +357,37 @@ pub fn split_path(path: &Path) -> (String, String) {
pub fn print_text_messages_to_text_view(text_messages: &Messages, text_view: &gtk::TextView) {
let mut messages: String = String::from("");
if !text_messages.messages.is_empty() {
messages += "############### MESSAGES ###############\n";
messages += format!("############### MESSAGES({}) ###############\n", text_messages.messages.len()).as_str();
}
for text in &text_messages.messages {
messages += text.as_str();
messages += "\n";
}
if !text_messages.messages.is_empty() {
messages += "\n";
}
// if !text_messages.messages.is_empty() {
// messages += "\n";
// }
if !text_messages.warnings.is_empty() {
messages += "############### WARNINGS ###############\n";
messages += format!("############### WARNINGS({}) ###############\n", text_messages.warnings.len()).as_str();
}
for text in &text_messages.warnings {
messages += text.as_str();
messages += "\n";
}
if !text_messages.warnings.is_empty() {
messages += "\n";
}
// if !text_messages.warnings.is_empty() {
// messages += "\n";
// }
if !text_messages.errors.is_empty() {
messages += "############### ERRORS ###############\n";
messages += format!("############### ERRORS({}) ###############\n", text_messages.errors.len()).as_str();
}
for text in &text_messages.errors {
messages += text.as_str();
messages += "\n";
}
if !text_messages.errors.is_empty() {
// if !text_messages.errors.is_empty() {
// messages += "\n";
// }
if !text_messages.messages.is_empty() || !text_messages.warnings.is_empty() || !text_messages.errors.is_empty() {
messages += "\n";
}

View File

@ -1,10 +1,10 @@
# Compiling Czkawka from sources
## Requirements
Program | Min | What for
---------|------|------------------------------------------------------------
Rust | 1.53 | Czkawka, aims to support the latest available version of Rust on Ubuntu 20.04
GTK | 3.22 | Only for the `GTK` backend
| Program | Min | What for |
|---------|------|-------------------------------------------------------------------------------|
| Rust | 1.53 | Czkawka, aims to support the latest available version of Rust on Ubuntu 20.04 |
| GTK | 3.24 | Only for the `GTK` backend |
If you only want the terminal version without a GUI, just skip all the packages with `gtk` in their names.

View File

@ -3,7 +3,7 @@
### Linux
If you use Snap, Flatpak or Appimage, you need to only install ffmpeg if you want to use Similar Videos tool.
For Czkawka GUI you are required to have at least `GTK 3.22` and also `Alsa` installed (for finding broken music files, but it is disabled by default).
For Czkawka GUI you are required to have at least `GTK 3.24` and also `Alsa` installed (for finding broken music files, but it is disabled by default).
`FFmpeg` in Similar Videos is non required dependency - app will work, but this tool will throw errors, so I recommend to install it.
It should be installed by default on all the most popular distros.
#### Ubuntu/Debian/Linux Mint
@ -78,7 +78,7 @@ You can update the package with the same command.
```
sudo snap install czkawka
```
By default, Snap can only access the files in your home directory. You have to allow czkawka access to all the drives:
By default, Snap can only access the files in your home directory. You have to allow Czkawka access to all the drives:
```
sudo snap connect czkawka:removable-media

View File

@ -70,6 +70,7 @@ Currently, Czkawka stores few config and cache files on disk:
- `cache_similar_image_SIZE_HASH_FILTER.txt` - stores cache data and hashes which may be used later without needing to compute image hash again - editing this file manually is not recommended, but it is allowed. Each algorithms uses its own file, because hashes are completely different in each.
- `cache_broken_files.txt` - stores cache data of broken files
- `cache_duplicates_Blake3.txt` - stores cache data of duplicated files, to not suffer too big of a performance hit when saving/loading file, only already fully hashed files bigger than 5MB are stored. Similar files with replaced `Blake3` to e.g. `SHA256` may be shown, when support for new hashes will be introduced in Czkawka.
- `cache_similar_videos.bin/json` - stores cache data of video files. Depending on usage(debug/release build) it will produce bin file(fast, but unable to change) or json(slow, but well formatted).
Config files are located in this path:
@ -203,7 +204,7 @@ It is a tool for finding similar images that differ e.g. in watermark, size etc.
The tool first collects images with specific extensions that can be checked - `[".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".tif", ".pnm", ".tga", ".ff", ".gif", ".jif", ".jfi", ".ico", ".webp", ".avif"]`.
Next cached data is loaded from file to prevent hashing twice the same file.
The cache which points to non existing data is deleted automatically.
The cache which points to non existing data, by default is deleted automatically.
Then a perceptual hash is created for each image which isn't available in cache.
@ -229,7 +230,7 @@ Before calculating hashes usually images are resized with specific algorithm(`La
Each configuration saves results to different cache files to save users from invalid results.
Some images broke hash functions and create hashes full of `0` or `255`, so these images are silently excluded(probably proper error reporting should be provided).
Some images broke hash functions and create hashes full of `0` or `255`, so these images are silently excluded from end results(but still are saved to cache).
You can test each algorithm with provided CLI tool, just put to folder `test.jpg` file and run inside this command `czkawka_cli tester -i`