Multiple small changes (#490)
This commit is contained in:
parent
e2d31d4f98
commit
a8aa8a2a23
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -504,6 +504,7 @@ dependencies = [
|
||||||
"rayon",
|
"rayon",
|
||||||
"rodio",
|
"rodio",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"vid_dup_finder_lib",
|
"vid_dup_finder_lib",
|
||||||
"xxhash-rust",
|
"xxhash-rust",
|
||||||
|
@ -1183,9 +1184,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.108"
|
version = "0.2.109"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
|
checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
|
@ -1232,9 +1233,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "memoffset"
|
||||||
version = "0.6.4"
|
version = "0.6.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
|
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
|
@ -44,8 +44,11 @@ tempfile = "3.2.0"
|
||||||
# Video Duplactes
|
# Video Duplactes
|
||||||
vid_dup_finder_lib = "0.1.0"
|
vid_dup_finder_lib = "0.1.0"
|
||||||
ffmpeg_cmdline_utils = "0.1.0"
|
ffmpeg_cmdline_utils = "0.1.0"
|
||||||
|
|
||||||
|
# Saving/Loading Cache
|
||||||
serde = "1.0.130"
|
serde = "1.0.130"
|
||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
|
serde_json = "1.0.72"
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
|
@ -423,7 +423,7 @@ impl SimilarVideos {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return {
|
return {
|
||||||
file_entry.error = format!("Failed to hash file, {}", e);
|
file_entry.error = format!("Failed to hash file, reason {}", e);
|
||||||
Some(file_entry)
|
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) {
|
pub fn save_hashes_to_file(hashmap: &BTreeMap<String, FileEntry>, text_messages: &mut Messages) {
|
||||||
if let Some(proj_dirs) = ProjectDirs::from("pl", "Qarmin", "Czkawka") {
|
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());
|
let cache_dir = PathBuf::from(proj_dirs.cache_dir());
|
||||||
if cache_dir.exists() {
|
if cache_dir.exists() {
|
||||||
if !cache_dir.is_dir() {
|
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));
|
text_messages.messages.push(format!("Cannot create config dir {}, reason {}", cache_dir.display(), e));
|
||||||
return;
|
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) {
|
let file_handler = match OpenOptions::new().truncate(true).write(true).create(true).open(&cache_file) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -620,25 +613,43 @@ pub fn save_hashes_to_file(hashmap: &BTreeMap<String, FileEntry>, text_messages:
|
||||||
};
|
};
|
||||||
|
|
||||||
let writer = BufWriter::new(file_handler);
|
let writer = BufWriter::new(file_handler);
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
if let Err(e) = bincode::serialize_into(writer, hashmap) {
|
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>> {
|
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") {
|
if let Some(proj_dirs) = ProjectDirs::from("pl", "Qarmin", "Czkawka") {
|
||||||
let cache_dir = PathBuf::from(proj_dirs.cache_dir());
|
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) {
|
let file_handler = match OpenOptions::new().read(true).open(&cache_file) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(_inspected) => {
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let reader = BufReader::new(file_handler);
|
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) {
|
let mut hashmap_loaded_entries: BTreeMap<String, FileEntry> = match bincode::deserialize_from(reader) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
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
|
// 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);
|
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
|
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 {
|
pub fn check_if_ffmpeg_is_installed() -> bool {
|
||||||
let vid = "999999999999999999.txt";
|
let vid = "999999999999999999.txt";
|
||||||
if let Err(DetermineVideo { src_path: _a, error: FfmpegNotFound }) = VideoHash::from_path(&vid) {
|
if let Err(DetermineVideo { src_path: _a, error: FfmpegNotFound }) = VideoHash::from_path(&vid) {
|
||||||
|
|
|
@ -11,15 +11,18 @@ use crate::help_functions::*;
|
||||||
use crate::notebook_enums::*;
|
use crate::notebook_enums::*;
|
||||||
|
|
||||||
pub fn connect_button_hardlink_symlink(gui_data: &GuiData) {
|
pub fn connect_button_hardlink_symlink(gui_data: &GuiData) {
|
||||||
|
// Hardlinking
|
||||||
{
|
{
|
||||||
let buttons_hardlink = gui_data.bottom_buttons.buttons_hardlink.clone();
|
let buttons_hardlink = gui_data.bottom_buttons.buttons_hardlink.clone();
|
||||||
|
|
||||||
let gui_data = gui_data.clone();
|
let gui_data = gui_data.clone();
|
||||||
|
|
||||||
buttons_hardlink.connect_clicked(move |_| {
|
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();
|
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 tree_view = &main_tree_views[nb_number as usize];
|
||||||
let nb_object = &NOTEBOOKS_INFOS[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();
|
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 {
|
if !check_if_can_link_files(&check_button_settings_confirm_link, &window_main).await {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !check_if_changing_one_item_in_group_and_continue(tree_view, column_color, nb_object.column_selection, &window_main).await {
|
if !check_if_changing_one_item_in_group_and_continue(tree_view, column_color, nb_object.column_selection, &window_main).await {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +1,23 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
||||||
use crate::gui_data::GuiData;
|
use crate::gui_data::GuiData;
|
||||||
use crate::gui_popovers::GuiPopovers;
|
use crate::gui_popovers::GuiPopovers;
|
||||||
use crate::help_functions::PopoverTypes;
|
use crate::help_functions::{PopoverTypes, NOTEBOOKS_INFOS};
|
||||||
use crate::notebook_enums::*;
|
use crate::notebook_enums::*;
|
||||||
|
|
||||||
pub fn connect_button_select(gui_data: &GuiData) {
|
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 popovers = gui_data.popovers.clone();
|
||||||
let notebook_main = gui_data.main_notebook.notebook_main.clone();
|
let notebook_main = gui_data.main_notebook.notebook_main.clone();
|
||||||
let popover_select = gui_data.popovers.popover_select.clone();
|
let popover_select = gui_data.popovers.popover_select.clone();
|
||||||
let buttons_select = gui_data.bottom_buttons.buttons_select.clone();
|
let buttons_select = gui_data.bottom_buttons.buttons_select.clone();
|
||||||
|
|
||||||
buttons_select.connect_clicked(move |_| {
|
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();
|
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_select_all = popovers.buttons_popover_select_all.clone();
|
||||||
let buttons_popover_unselect_all = popovers.buttons_popover_unselect_all.clone();
|
let buttons_popover_unselect_all = popovers.buttons_popover_unselect_all.clone();
|
||||||
let buttons_popover_reverse = popovers.buttons_popover_reverse.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_image_size = popovers.separator_select_image_size.clone();
|
||||||
let separator_select_reverse = popovers.separator_select_reverse.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_select_all.show();
|
||||||
buttons_popover_unselect_all.show();
|
buttons_popover_unselect_all.show();
|
||||||
} else {
|
} else {
|
||||||
|
@ -63,7 +45,7 @@ fn show_required_popovers(popovers: &GuiPopovers, current_mode: &NotebookMainEnu
|
||||||
buttons_popover_unselect_all.hide();
|
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_biggest.show();
|
||||||
buttons_popover_select_all_images_except_smallest.show();
|
buttons_popover_select_all_images_except_smallest.show();
|
||||||
separator_select_image_size.show();
|
separator_select_image_size.show();
|
||||||
|
@ -73,7 +55,7 @@ fn show_required_popovers(popovers: &GuiPopovers, current_mode: &NotebookMainEnu
|
||||||
separator_select_image_size.hide();
|
separator_select_image_size.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
if vec.contains(&PopoverTypes::Reverse) {
|
if arr.contains(&PopoverTypes::Reverse) {
|
||||||
buttons_popover_reverse.show();
|
buttons_popover_reverse.show();
|
||||||
separator_select_reverse.show();
|
separator_select_reverse.show();
|
||||||
} else {
|
} else {
|
||||||
|
@ -81,7 +63,7 @@ fn show_required_popovers(popovers: &GuiPopovers, current_mode: &NotebookMainEnu
|
||||||
separator_select_reverse.hide();
|
separator_select_reverse.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
if vec.contains(&PopoverTypes::Custom) {
|
if arr.contains(&PopoverTypes::Custom) {
|
||||||
buttons_popover_select_custom.show();
|
buttons_popover_select_custom.show();
|
||||||
buttons_popover_unselect_custom.show();
|
buttons_popover_unselect_custom.show();
|
||||||
separator_select_custom.show();
|
separator_select_custom.show();
|
||||||
|
@ -91,7 +73,7 @@ fn show_required_popovers(popovers: &GuiPopovers, current_mode: &NotebookMainEnu
|
||||||
separator_select_custom.hide();
|
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_oldest.show();
|
||||||
buttons_popover_select_all_except_newest.show();
|
buttons_popover_select_all_except_newest.show();
|
||||||
buttons_popover_select_one_oldest.show();
|
buttons_popover_select_one_oldest.show();
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub enum PopoverTypes {
|
||||||
|
|
||||||
pub struct NotebookObject {
|
pub struct NotebookObject {
|
||||||
pub notebook_type: NotebookMainEnum,
|
pub notebook_type: NotebookMainEnum,
|
||||||
pub available_modes: [PopoverTypes; 4],
|
pub available_modes: [PopoverTypes; 5],
|
||||||
pub column_activatable_button: Option<i32>,
|
pub column_activatable_button: Option<i32>,
|
||||||
pub column_path: i32,
|
pub column_path: i32,
|
||||||
pub column_name: i32,
|
pub column_name: i32,
|
||||||
|
@ -57,7 +57,7 @@ pub struct NotebookObject {
|
||||||
pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
|
pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
|
||||||
NotebookObject {
|
NotebookObject {
|
||||||
notebook_type: NotebookMainEnum::Duplicate,
|
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_activatable_button: Some(ColumnsDuplicates::ActivatableSelectButton as i32),
|
||||||
column_path: ColumnsDuplicates::Path as i32,
|
column_path: ColumnsDuplicates::Path as i32,
|
||||||
column_name: ColumnsDuplicates::Name as i32,
|
column_name: ColumnsDuplicates::Name as i32,
|
||||||
|
@ -70,7 +70,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
|
||||||
},
|
},
|
||||||
NotebookObject {
|
NotebookObject {
|
||||||
notebook_type: NotebookMainEnum::EmptyDirectories,
|
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_activatable_button: None,
|
||||||
column_path: ColumnsEmptyFolders::Path as i32,
|
column_path: ColumnsEmptyFolders::Path as i32,
|
||||||
column_name: ColumnsEmptyFolders::Name as i32,
|
column_name: ColumnsEmptyFolders::Name as i32,
|
||||||
|
@ -83,7 +83,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
|
||||||
},
|
},
|
||||||
NotebookObject {
|
NotebookObject {
|
||||||
notebook_type: NotebookMainEnum::BigFiles,
|
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_activatable_button: None,
|
||||||
column_path: ColumnsBigFiles::Path as i32,
|
column_path: ColumnsBigFiles::Path as i32,
|
||||||
column_name: ColumnsBigFiles::Name as i32,
|
column_name: ColumnsBigFiles::Name as i32,
|
||||||
|
@ -96,7 +96,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
|
||||||
},
|
},
|
||||||
NotebookObject {
|
NotebookObject {
|
||||||
notebook_type: NotebookMainEnum::EmptyFiles,
|
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_activatable_button: None,
|
||||||
column_path: ColumnsEmptyFiles::Path as i32,
|
column_path: ColumnsEmptyFiles::Path as i32,
|
||||||
column_name: ColumnsEmptyFiles::Name as i32,
|
column_name: ColumnsEmptyFiles::Name as i32,
|
||||||
|
@ -109,7 +109,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
|
||||||
},
|
},
|
||||||
NotebookObject {
|
NotebookObject {
|
||||||
notebook_type: NotebookMainEnum::Temporary,
|
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_activatable_button: None,
|
||||||
column_path: ColumnsTemporaryFiles::Path as i32,
|
column_path: ColumnsTemporaryFiles::Path as i32,
|
||||||
column_name: ColumnsTemporaryFiles::Name as i32,
|
column_name: ColumnsTemporaryFiles::Name as i32,
|
||||||
|
@ -122,7 +122,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
|
||||||
},
|
},
|
||||||
NotebookObject {
|
NotebookObject {
|
||||||
notebook_type: NotebookMainEnum::SimilarImages,
|
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_activatable_button: Some(ColumnsSimilarImages::ActivatableSelectButton as i32),
|
||||||
column_path: ColumnsSimilarImages::Path as i32,
|
column_path: ColumnsSimilarImages::Path as i32,
|
||||||
column_name: ColumnsSimilarImages::Name as i32,
|
column_name: ColumnsSimilarImages::Name as i32,
|
||||||
|
@ -135,7 +135,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
|
||||||
},
|
},
|
||||||
NotebookObject {
|
NotebookObject {
|
||||||
notebook_type: NotebookMainEnum::SimilarVideos,
|
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_activatable_button: Some(ColumnsSimilarVideos::ActivatableSelectButton as i32),
|
||||||
column_path: ColumnsSimilarVideos::Path as i32,
|
column_path: ColumnsSimilarVideos::Path as i32,
|
||||||
column_name: ColumnsSimilarVideos::Name as i32,
|
column_name: ColumnsSimilarVideos::Name as i32,
|
||||||
|
@ -148,7 +148,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
|
||||||
},
|
},
|
||||||
NotebookObject {
|
NotebookObject {
|
||||||
notebook_type: NotebookMainEnum::SameMusic,
|
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_activatable_button: Some(ColumnsSameMusic::ActivatableSelectButton as i32),
|
||||||
column_path: ColumnsSameMusic::Path as i32,
|
column_path: ColumnsSameMusic::Path as i32,
|
||||||
column_name: ColumnsSameMusic::Name as i32,
|
column_name: ColumnsSameMusic::Name as i32,
|
||||||
|
@ -161,7 +161,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
|
||||||
},
|
},
|
||||||
NotebookObject {
|
NotebookObject {
|
||||||
notebook_type: NotebookMainEnum::Symlinks,
|
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_activatable_button: None,
|
||||||
column_path: ColumnsInvalidSymlinks::Path as i32,
|
column_path: ColumnsInvalidSymlinks::Path as i32,
|
||||||
column_name: ColumnsInvalidSymlinks::Name as i32,
|
column_name: ColumnsInvalidSymlinks::Name as i32,
|
||||||
|
@ -174,7 +174,7 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
|
||||||
},
|
},
|
||||||
NotebookObject {
|
NotebookObject {
|
||||||
notebook_type: NotebookMainEnum::BrokenFiles,
|
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_activatable_button: None,
|
||||||
column_path: ColumnsBrokenFiles::Path as i32,
|
column_path: ColumnsBrokenFiles::Path as i32,
|
||||||
column_name: ColumnsBrokenFiles::Name 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: >k::TextView) {
|
pub fn print_text_messages_to_text_view(text_messages: &Messages, text_view: >k::TextView) {
|
||||||
let mut messages: String = String::from("");
|
let mut messages: String = String::from("");
|
||||||
if !text_messages.messages.is_empty() {
|
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 {
|
for text in &text_messages.messages {
|
||||||
messages += text.as_str();
|
messages += text.as_str();
|
||||||
messages += "\n";
|
messages += "\n";
|
||||||
}
|
}
|
||||||
if !text_messages.messages.is_empty() {
|
// if !text_messages.messages.is_empty() {
|
||||||
messages += "\n";
|
// messages += "\n";
|
||||||
}
|
// }
|
||||||
if !text_messages.warnings.is_empty() {
|
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 {
|
for text in &text_messages.warnings {
|
||||||
messages += text.as_str();
|
messages += text.as_str();
|
||||||
messages += "\n";
|
messages += "\n";
|
||||||
}
|
}
|
||||||
if !text_messages.warnings.is_empty() {
|
// if !text_messages.warnings.is_empty() {
|
||||||
messages += "\n";
|
// messages += "\n";
|
||||||
}
|
// }
|
||||||
if !text_messages.errors.is_empty() {
|
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 {
|
for text in &text_messages.errors {
|
||||||
messages += text.as_str();
|
messages += text.as_str();
|
||||||
messages += "\n";
|
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";
|
messages += "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# Compiling Czkawka from sources
|
# Compiling Czkawka from sources
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
Program | Min | What for
|
| Program | Min | What for |
|
||||||
---------|------|------------------------------------------------------------
|
|---------|------|-------------------------------------------------------------------------------|
|
||||||
Rust | 1.53 | Czkawka, aims to support the latest available version of Rust on Ubuntu 20.04
|
| 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
|
| 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.
|
If you only want the terminal version without a GUI, just skip all the packages with `gtk` in their names.
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
### Linux
|
### Linux
|
||||||
If you use Snap, Flatpak or Appimage, you need to only install ffmpeg if you want to use Similar Videos tool.
|
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.
|
`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.
|
It should be installed by default on all the most popular distros.
|
||||||
#### Ubuntu/Debian/Linux Mint
|
#### Ubuntu/Debian/Linux Mint
|
||||||
|
@ -78,7 +78,7 @@ You can update the package with the same command.
|
||||||
```
|
```
|
||||||
sudo snap install czkawka
|
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
|
sudo snap connect czkawka:removable-media
|
||||||
|
|
|
@ -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_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_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_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:
|
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"]`.
|
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.
|
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.
|
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.
|
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`
|
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`
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue