From 8e4e1f58cbc32be9851c959b8ef82f30eb5af1a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mikrut?= <41945903+qarmin@users.noreply.github.com> Date: Fri, 22 Apr 2022 21:46:33 +0200 Subject: [PATCH] Finding invalid extensions (#678) * Find invalid extensions * Another day * Libc * Basic Renames * Ending words * Better renaming * Stonks because works --- .gitignore | 1 + Cargo.lock | 180 +++-- Changelog.md | 15 + README.md | 2 +- czkawka_core/Cargo.toml | 3 + czkawka_core/src/bad_extensions.rs | 478 +++++++++++++ czkawka_core/src/lib.rs | 1 + czkawka_core/src/similar_images.rs | 2 +- czkawka_gui/Cargo.toml | 2 +- czkawka_gui/i18n/en/czkawka_gui.ftl | 5 + czkawka_gui/src/compute_results.rs | 69 ++ .../src/connect_things/connect_button_save.rs | 6 + .../connect_things/connect_button_search.rs | 26 + .../connect_things/connect_progress_window.rs | 67 +- czkawka_gui/src/gui_structs/gui_data.rs | 4 + .../src/gui_structs/gui_main_notebook.rs | 23 + czkawka_gui/src/help_functions.rs | 26 + czkawka_gui/src/initialize_gui.rs | 39 +- czkawka_gui/src/main.rs | 10 +- czkawka_gui/src/notebook_enums.rs | 5 +- czkawka_gui/ui/czkawka.cmb | 14 +- czkawka_gui/ui/main_window.ui | 637 +++++++++--------- instructions/Compilation.md | 2 +- instructions/Instruction.md | 16 +- 24 files changed, 1236 insertions(+), 397 deletions(-) create mode 100644 czkawka_core/src/bad_extensions.rs diff --git a/.gitignore b/.gitignore index b8a2aef..d9c6ec3 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ results*.txt TestSuite* *.snap flatpak/ +*.zip \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 505b051..5bfbad8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" +checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" [[package]] name = "arrayref" @@ -160,9 +160,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.56.0" +version = "0.59.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239" +checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" dependencies = [ "bitflags", "cexpr", @@ -338,13 +338,23 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cexpr" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ "nom", ] +[[package]] +name = "cfb" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f89d248799e3f15f91b70917f65381062a01bb8e222700ea0e5a7ff9785f9c" +dependencies = [ + "byteorder", + "uuid", +] + [[package]] name = "cfg-expr" version = "0.10.2" @@ -460,9 +470,9 @@ dependencies = [ [[package]] name = "coreaudio-sys" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca4679a59dbd8c15f064c012dfe8c1163b9453224238b59bb9328c142b8b248b" +checksum = "3dff444d80630d7073077d38d40b4501fd518bd2b922c2a55edcc8b0f7be57e6" dependencies = [ "bindgen", ] @@ -604,7 +614,9 @@ dependencies = [ "image 0.24.1", "image_hasher", "imagepipe", + "infer", "lofty", + "mime_guess", "once_cell", "rawloader", "rayon", @@ -769,9 +781,9 @@ dependencies = [ [[package]] name = "exr" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4badb9489a465cb2c555af1f00f0bfd8cecd6fc12ac11da9d5b40c5dd5f0200" +checksum = "14cc0e06fb5f67e5d6beadf3a382fec9baca1aa751c6d5368fdeee7e5932c215" dependencies = [ "bit_field", "deflate 1.0.0", @@ -813,7 +825,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" dependencies = [ "memoffset", - "rustc_version", + "rustc_version 0.3.3", ] [[package]] @@ -827,14 +839,14 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af" dependencies = [ "cfg-if 1.0.0", "crc32fast", "libc", - "miniz_oxide 0.4.4", + "miniz_oxide 0.5.1", ] [[package]] @@ -1408,7 +1420,7 @@ dependencies = [ "num-traits", "png 0.17.5", "scoped_threadpool", - "tiff 0.7.1", + "tiff 0.7.2", ] [[package]] @@ -1453,6 +1465,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "infer" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20b2b533137b9cad970793453d4f921c2e91312a6d88b1085c07bc15fc51bb3b" +dependencies = [ + "cfb", +] + [[package]] name = "inflate" version = "0.4.5" @@ -1584,9 +1605,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.122" +version = "0.2.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259" +checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50" [[package]] name = "libloading" @@ -1629,9 +1650,9 @@ dependencies = [ [[package]] name = "lofty" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a944022a74ee371dbf1f36839f4a589822e8c980d1265f086eb540648c78083a" +checksum = "45967792fc531850b544d0e50065ffda8ce814a0d2142a6cbe7009961964e17c" dependencies = [ "base64 0.13.0", "byteorder", @@ -1684,6 +1705,28 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "minimp3" version = "0.5.1" @@ -1765,15 +1808,15 @@ dependencies = [ [[package]] name = "ndk-context" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5cc68637e21fe8f077f6a1c9e0b9ca495bb74895226b476310f613325884" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-glue" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9ffb7443daba48349d545028777ca98853b018b4c16624aa01223bc29e078da" +checksum = "0d0c4a7b83860226e6b4183edac21851f05d5a51756e97a1144b7f5a6b63e65f" dependencies = [ "lazy_static", "libc", @@ -1821,12 +1864,12 @@ dependencies = [ [[package]] name = "nom" -version = "5.1.2" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" dependencies = [ "memchr", - "version_check", + "minimal-lexical", ] [[package]] @@ -2278,9 +2321,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] @@ -2317,24 +2360,24 @@ dependencies = [ [[package]] name = "rawloader" -version = "0.37.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d23e8122c7619b7d3a1286ed328e4f5c0cf3dcdadd9adff508f7364d83a7734" +checksum = "4d8c6f168c492ffd326537b3aa5a8d5fe07f0d8a3970c5957f286bcd13f888aa" dependencies = [ "byteorder", "enumn", "glob", "lazy_static", "rayon", - "rustc_version", + "rustc_version 0.4.0", "toml", ] [[package]] name = "rayon" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221" dependencies = [ "autocfg", "crossbeam-deque", @@ -2344,14 +2387,13 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "lazy_static", "num_cpus", ] @@ -2416,9 +2458,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "6.3.0" +version = "6.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40377bff8cceee81e28ddb73ac97f5c2856ce5522f0b260b763f434cdfae602" +checksum = "9a17e5ac65b318f397182ae94e532da0ba56b88dd1200b774715d36c4943b1c3" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -2440,9 +2482,9 @@ dependencies = [ [[package]] name = "rust-embed-utils" -version = "7.1.0" +version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad22c7226e4829104deab21df575e995bfbc4adfad13a595e387477f238c1aec" +checksum = "756feca3afcbb1487a1d01f4ecd94cf8ec98ea074c55a69e7136d29fb6166029" dependencies = [ "sha2 0.9.9", "walkdir", @@ -2460,7 +2502,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" dependencies = [ - "semver", + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.7", ] [[package]] @@ -2550,6 +2601,12 @@ dependencies = [ "semver-parser", ] +[[package]] +name = "semver" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" + [[package]] name = "semver-parser" version = "0.10.2" @@ -2639,9 +2696,9 @@ dependencies = [ [[package]] name = "shlex" -version = "0.1.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "slab" @@ -2668,9 +2725,9 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" [[package]] name = "spin" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +checksum = "c530c2b0d0bf8b69304b39fe2001993e267461948b890cd037d8ad4293fa1a0d" dependencies = [ "lock_api", ] @@ -2818,12 +2875,12 @@ dependencies = [ [[package]] name = "tiff" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0247608e998cb6ce39dfc8f4a16c50361ce71e5b52e6d24ea1227ea8ea8ee0b2" +checksum = "7cfada0986f446a770eca461e8c6566cb879682f7d687c8348aa0c857bd52286" dependencies = [ "flate2", - "jpeg-decoder 0.1.22", + "jpeg-decoder 0.2.4", "weezl", ] @@ -2879,9 +2936,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "toml" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] @@ -2951,6 +3008,15 @@ dependencies = [ "tinystr", ] +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-segmentation" version = "1.9.0" @@ -2969,6 +3035,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" + [[package]] name = "vec_map" version = "0.8.2" @@ -3087,9 +3159,9 @@ dependencies = [ [[package]] name = "weezl" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e" +checksum = "9c97e489d8f836838d497091de568cf16b117486d529ec5579233521065bd5e4" [[package]] name = "winapi" @@ -3167,9 +3239,9 @@ checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" [[package]] name = "xxhash-rust" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a16b7b403377d61184bb601d8349a4ff2c4cec08a305d004f710b7eaafef24" +checksum = "074914ea4eec286eb8d1fd745768504f420a1f7b7919185682a4a267bed7d2e7" [[package]] name = "yaml-rust" diff --git a/Changelog.md b/Changelog.md index 23e6ae1..6654229 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,18 @@ +## Version 4.1.0 - .04.2022r +- New mode - finding files whose content not match with their extension - [#678](https://github.com/qarmin/czkawka/pull/678) +- Builtin icons - no more invalid, theme/OS dependant icons - [#659](https://github.com/qarmin/czkawka/pull/659) +- Big(usually 2x) speedup of showing previews of images(both previews in scan and compare window) - [#660](https://github.com/qarmin/czkawka/pull/660) +- Fix selecting records by custom selection popup - [#632](https://github.com/qarmin/czkawka/pull/632) +- Support more tags when comparing music files - [#590](https://github.com/qarmin/czkawka/pull/590) +- Fix not proper selecting path - [#656](https://github.com/qarmin/czkawka/pull/656) +- No more popups during scan for similar videos on Windows - [#656](https://github.com/qarmin/czkawka/pull/656) - external change [4056](https://github.com/Farmadupe/ffmpeg_cmdline_utils/commit/405687514f9d9e8984cbe2547c53e85b71e08b27) +- Custom selecting is now case-insensitive by default - [#657](https://github.com/qarmin/czkawka/pull/657) +- Better approximate comparison of tags - [#641](https://github.com/qarmin/czkawka/pull/641) +- Fix search problem due accumulated stop events - [#623](https://github.com/qarmin/czkawka/pull/623) +- Fix file hardlinking on Windows - [#668](https://github.com/qarmin/czkawka/pull/668) +- Support for case-insensitive name grouping of files - [#669](https://github.com/qarmin/czkawka/pull/669) +- Directories for search GUI can be passed by CLI - [#677](https://github.com/qarmin/czkawka/pull/677) + ## Version 4.0.0 - 20.01.2022r - Multithreading support for collecting files to check(2/3x speedup on 4 thread processor and SSD) - [#502](https://github.com/qarmin/czkawka/pull/502), [#504](https://github.com/qarmin/czkawka/pull/504) - Add multiple translations - Polish, Italian, French, German, Russian ... - [#469](https://github.com/qarmin/czkawka/pull/469), [#508](https://github.com/qarmin/czkawka/pull/508), [5be](https://github.com/qarmin/czkawka/commit/5be801e76395855f07ab1da43cdbb8bd0b843834) diff --git a/README.md b/README.md index 9042ff6..e776f65 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ - Similar Videos - Looks for visually similar videos - Same Music - Searches for music with the same artist, album etc. - Invalid Symbolic Links - Shows symbolic links which point to non-existent files/directories - - Broken Files - Finds files with an invalid extension or that are corrupted + - Broken Files - Finds files that are invalid or corrupted ![Czkawka](https://user-images.githubusercontent.com/41945903/145280350-506f7e94-4db0-4de7-a68d-6e7c26bbd2bf.gif) diff --git a/czkawka_core/Cargo.toml b/czkawka_core/Cargo.toml index 407f91b..798801b 100644 --- a/czkawka_core/Cargo.toml +++ b/czkawka_core/Cargo.toml @@ -60,6 +60,9 @@ once_cell = "1.10.0" rawloader = "0.37.0" imagepipe = "0.4.0" +# Checking for invalid extensions +mime_guess = "2.0.4" +infer = "0.7.0" [features] default = [] diff --git a/czkawka_core/src/bad_extensions.rs b/czkawka_core/src/bad_extensions.rs new file mode 100644 index 0000000..fa97eeb --- /dev/null +++ b/czkawka_core/src/bad_extensions.rs @@ -0,0 +1,478 @@ +use std::collections::BTreeSet; +use std::fs::File; +use std::io::prelude::*; +use std::io::BufWriter; +use std::path::PathBuf; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::Arc; +use std::thread::sleep; +use std::time::{Duration, SystemTime}; +use std::{mem, thread}; + +use crossbeam_channel::Receiver; +use mime_guess::get_mime_extensions; +use rayon::prelude::*; + +use crate::common::{Common, LOOP_DURATION}; +use crate::common_dir_traversal::{CheckingMethod, DirTraversalBuilder, DirTraversalResult, FileEntry, ProgressData}; +use crate::common_directory::Directories; +use crate::common_extensions::Extensions; +use crate::common_items::ExcludedItems; +use crate::common_messages::Messages; +use crate::common_traits::*; + +// This adds several workarounds for bugs/invalid recognizing types by external libraries +// ("real_content_extension", "current_file_extension") +static WORKAROUNDS: &[(&str, &str)] = &[ + // Wine/Windows + ("exe", "acm"), + ("exe", "ax"), + ("exe", "bck"), + ("exe", "com"), + ("exe", "cpl"), + ("exe", "cpl"), + ("exe", "dll"), + ("exe", "dll16"), + ("exe", "drv"), + ("exe", "drv16"), + ("exe", "ds"), + ("exe", "exe16"), + ("exe", "fon"), // Type of font or something else + ("exe", "msstyles"), + ("exe", "orig"), + ("exe", "sys"), + ("exe", "tlb"), + ("exe", "vxd"), + // Other + ("exe", "doc"), // Not sure whe docx is not recognized + ("exe", "sys"), + ("gz", "blend"), + ("gz", "crate"), + ("gz", "svgz"), + ("gz", "tgz"), + ("html", "md"), + ("html", "svg"), // Quite strange, but yes it works + ("jpg", "jfif"), + ("obj", "o"), + ("odp", "otp"), + ("odt", "ott"), + ("ogg", "ogv"), + ("pptx", "ppsx"), + ("sh", "bash"), + ("sh", "py"), + ("sh", "rs"), + ("xml", "cmb"), + ("xml", "conf"), + ("xml", "dae"), + ("xml", "docbook"), + ("xml", "gir"), + ("xml", "glade"), + ("xml", "html"), + ("xml", "kdenlive"), + ("xml", "lang"), + ("xml", "svg"), + ("xml", "ui"), + ("xml", "vcproj"), + ("zip", "apk"), + ("zip", "dat"), + ("zip", "jar"), + ("zip", "kra"), + ("zip", "nupkg"), + ("zip", "pptx"), + ("zip", "whl"), + ("zip", "xpi"), +]; + +#[derive(Clone)] +pub struct BadFileEntry { + pub path: PathBuf, + pub modified_date: u64, + pub size: u64, + pub current_extension: String, + pub proper_extensions: String, +} +/// Info struck with helpful information's about results +#[derive(Default)] +pub struct Info { + pub number_of_files_with_bad_extension: usize, +} + +impl Info { + pub fn new() -> Self { + Default::default() + } +} + +pub struct BadExtensions { + text_messages: Messages, + information: Info, + files_to_check: Vec, + bad_extensions_files: Vec, + directories: Directories, + allowed_extensions: Extensions, + excluded_items: ExcludedItems, + minimal_file_size: u64, + maximal_file_size: u64, + recursive_search: bool, + stopped_search: bool, + save_also_as_json: bool, + include_files_without_extension: bool, +} + +impl BadExtensions { + pub fn new() -> Self { + Self { + text_messages: Messages::new(), + information: Info::new(), + recursive_search: true, + allowed_extensions: Extensions::new(), + directories: Directories::new(), + excluded_items: ExcludedItems::new(), + files_to_check: Default::default(), + stopped_search: false, + minimal_file_size: 8192, + maximal_file_size: u64::MAX, + bad_extensions_files: Default::default(), + save_also_as_json: false, + include_files_without_extension: true, + } + } + + pub fn find_bad_extensions_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender>) { + self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); + if !self.check_files(stop_receiver, progress_sender) { + self.stopped_search = true; + return; + } + if !self.look_for_bad_extensions_files(stop_receiver, progress_sender) { + self.stopped_search = true; + return; + } + self.debug_print(); + } + + pub fn get_stopped_search(&self) -> bool { + self.stopped_search + } + + pub const fn get_bad_extensions_files(&self) -> &Vec { + &self.bad_extensions_files + } + + pub fn set_maximal_file_size(&mut self, maximal_file_size: u64) { + self.maximal_file_size = match maximal_file_size { + 0 => 1, + t => t, + }; + } + pub fn set_minimal_file_size(&mut self, minimal_file_size: u64) { + self.minimal_file_size = match minimal_file_size { + 0 => 1, + t => t, + }; + } + + pub const fn get_text_messages(&self) -> &Messages { + &self.text_messages + } + + pub const fn get_information(&self) -> &Info { + &self.information + } + + pub fn set_save_also_as_json(&mut self, save_also_as_json: bool) { + self.save_also_as_json = save_also_as_json; + } + + pub fn set_recursive_search(&mut self, recursive_search: bool) { + self.recursive_search = recursive_search; + } + + pub fn set_included_directory(&mut self, included_directory: Vec) -> bool { + self.directories.set_included_directory(included_directory, &mut self.text_messages) + } + + pub fn set_excluded_directory(&mut self, excluded_directory: Vec) { + self.directories.set_excluded_directory(excluded_directory, &mut self.text_messages); + } + pub fn set_allowed_extensions(&mut self, allowed_extensions: String) { + self.allowed_extensions.set_allowed_extensions(allowed_extensions, &mut self.text_messages); + } + + pub fn set_excluded_items(&mut self, excluded_items: Vec) { + 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>) -> bool { + let result = DirTraversalBuilder::new() + .root_dirs(self.directories.included_directories.clone()) + .group_by(|_fe| ()) + .stop_receiver(stop_receiver) + .progress_sender(progress_sender) + .minimal_file_size(self.minimal_file_size) + .maximal_file_size(self.maximal_file_size) + .directories(self.directories.clone()) + .allowed_extensions(self.allowed_extensions.clone()) + .excluded_items(self.excluded_items.clone()) + .recursive_search(self.recursive_search) + .build() + .run(); + match result { + DirTraversalResult::SuccessFiles { + start_time, + grouped_file_entries, + warnings, + } => { + if let Some(files_to_check) = grouped_file_entries.get(&()) { + self.files_to_check = files_to_check.clone(); + } + self.text_messages.warnings.extend(warnings); + Common::print_time(start_time, SystemTime::now(), "check_files".to_string()); + true + } + DirTraversalResult::SuccessFolders { .. } => { + unreachable!() + } + DirTraversalResult::Stopped => false, + } + } + + fn look_for_bad_extensions_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender>) -> bool { + let system_time = SystemTime::now(); + + let include_files_without_extension = self.include_files_without_extension; + + let check_was_breaked = AtomicBool::new(false); // Used for breaking from GUI and ending check thread + + //// PROGRESS THREAD START + let progress_thread_run = Arc::new(AtomicBool::new(true)); + let atomic_file_counter = Arc::new(AtomicUsize::new(0)); + + let progress_thread_handle = if let Some(progress_sender) = progress_sender { + let progress_send = progress_sender.clone(); + let progress_thread_run = progress_thread_run.clone(); + let atomic_file_counter = atomic_file_counter.clone(); + let entries_to_check = self.files_to_check.len(); + thread::spawn(move || loop { + progress_send + .unbounded_send(ProgressData { + checking_method: CheckingMethod::None, + current_stage: 1, + max_stage: 1, + entries_checked: atomic_file_counter.load(Ordering::Relaxed) as usize, + entries_to_check, + }) + .unwrap(); + if !progress_thread_run.load(Ordering::Relaxed) { + break; + } + sleep(Duration::from_millis(LOOP_DURATION as u64)); + }) + } else { + thread::spawn(|| {}) + }; + + let mut files_to_check = Default::default(); + mem::swap(&mut files_to_check, &mut self.files_to_check); + + //// PROGRESS THREAD END + self.bad_extensions_files = files_to_check + .into_par_iter() // TODO into par iter after + .map(|file_entry| { + atomic_file_counter.fetch_add(1, Ordering::Relaxed); + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { + check_was_breaked.store(true, Ordering::Relaxed); + return None; + } + + let current_extension; + + // Check what exactly content file contains + let kind = match infer::get_from_path(&file_entry.path) { + Ok(k) => match k { + Some(t) => t, + None => return Some(None), + }, + Err(_) => return Some(None), + }; + let proper_extension = kind.extension(); + + // Extract current extension from file + if let Some(extension) = file_entry.path.extension() { + let extension = extension.to_string_lossy().to_lowercase(); + // Text longer than 10 characters is not considered as extension + if extension.len() > 10 { + current_extension = "".to_string(); + } else { + current_extension = extension; + } + } else { + current_extension = "".to_string(); + } + + // Already have proper extension, no need to do more things + if current_extension == proper_extension { + return Some(None); + } + + // Check for all extensions that file can use(not sure if it is worth to do it) + let mut all_available_extensions: BTreeSet<_> = Default::default(); + let think_extension = match current_extension.is_empty() { + true => "".to_string(), + false => { + for mim in mime_guess::from_ext(proper_extension) { + if let Some(all_ext) = get_mime_extensions(&mim) { + for ext in all_ext { + all_available_extensions.insert(ext); + } + } + } + + // Workarounds + for (pre, post) in WORKAROUNDS { + if post == ¤t_extension.as_str() && all_available_extensions.contains(&pre) { + all_available_extensions.insert(post); + } + } + + let mut guessed_multiple_extensions = "".to_string(); + for ext in &all_available_extensions { + guessed_multiple_extensions.push_str(ext); + guessed_multiple_extensions.push(','); + } + guessed_multiple_extensions.pop(); + + guessed_multiple_extensions + } + }; + + if all_available_extensions.is_empty() { + // Not found any extension + return Some(None); + } else if current_extension.is_empty() { + if !include_files_without_extension { + println!("Empty extension which is disabled by settings"); + return Some(None); + } + } else if all_available_extensions.take(¤t_extension.as_str()).is_some() { + // Found proper extension + return Some(None); + } + + Some(Some(BadFileEntry { + path: file_entry.path, + modified_date: file_entry.modified_date, + size: file_entry.size, + current_extension, + proper_extensions: think_extension, + })) + }) + .while_some() + .filter(|file_entry| file_entry.is_some()) + .map(|file_entry| file_entry.unwrap()) + .collect::>(); + + // End thread which send info to gui + progress_thread_run.store(false, Ordering::Relaxed); + progress_thread_handle.join().unwrap(); + + // Break if stop was clicked + if check_was_breaked.load(Ordering::Relaxed) { + return false; + } + + self.information.number_of_files_with_bad_extension = self.bad_extensions_files.len(); + + Common::print_time(system_time, SystemTime::now(), "sort_images - reading data from files in parallel".to_string()); + + // Clean unused data + self.files_to_check = Default::default(); + + true + } +} + +impl Default for BadExtensions { + fn default() -> Self { + Self::new() + } +} + +impl DebugPrint for BadExtensions { + #[allow(dead_code)] + #[allow(unreachable_code)] + /// Debugging printing - only available on debug build + fn debug_print(&self) { + #[cfg(not(debug_assertions))] + { + return; + } + println!("---------------DEBUG PRINT---------------"); + println!("### Information's"); + + println!("Errors size - {}", self.text_messages.errors.len()); + println!("Warnings size - {}", self.text_messages.warnings.len()); + println!("Messages size - {}", self.text_messages.messages.len()); + + println!("### Other"); + + println!("Excluded items - {:?}", self.excluded_items.items); + println!("Included directories - {:?}", self.directories.included_directories); + println!("Excluded directories - {:?}", self.directories.excluded_directories); + println!("Recursive search - {}", self.recursive_search); + println!("-----------------------------------------"); + } +} + +impl SaveResults for BadExtensions { + fn save_results_to_file(&mut self, file_name: &str) -> bool { + let start_time: SystemTime = SystemTime::now(); + let file_name: String = match file_name { + "" => "results.txt".to_string(), + k => k.to_string(), + }; + + let file_handler = match File::create(&file_name) { + Ok(t) => t, + Err(e) => { + self.text_messages.errors.push(format!("Failed to create file {}, reason {}", file_name, e)); + return false; + } + }; + let mut writer = BufWriter::new(file_handler); + + if let Err(e) = writeln!( + writer, + "Results of searching {:?} with excluded directories {:?} and excluded items {:?}", + self.directories.included_directories, self.directories.excluded_directories, self.excluded_items.items + ) { + self.text_messages.errors.push(format!("Failed to save results to file {}, reason {}", file_name, e)); + return false; + } + + if !self.bad_extensions_files.is_empty() { + writeln!(writer, "Found {} files with invalid extension.", self.information.number_of_files_with_bad_extension).unwrap(); + for file_entry in self.bad_extensions_files.iter() { + writeln!(writer, "{}", file_entry.path.display()).unwrap(); + } + } else { + write!(writer, "Not found any files with invalid extension.").unwrap(); + } + Common::print_time(start_time, SystemTime::now(), "save_results_to_file".to_string()); + true + } +} + +impl PrintResults for BadExtensions { + /// Print information's about duplicated entries + /// Only needed for CLI + fn print_results(&self) { + let start_time: SystemTime = SystemTime::now(); + println!("Found {} files with invalid extension.\n", self.information.number_of_files_with_bad_extension); + for file_entry in self.bad_extensions_files.iter() { + println!("{}", file_entry.path.display()); + } + + Common::print_time(start_time, SystemTime::now(), "print_entries".to_string()); + } +} diff --git a/czkawka_core/src/lib.rs b/czkawka_core/src/lib.rs index 8de40ea..e37b1f5 100644 --- a/czkawka_core/src/lib.rs +++ b/czkawka_core/src/lib.rs @@ -16,6 +16,7 @@ pub mod similar_images; pub mod similar_videos; pub mod temporary; +pub mod bad_extensions; pub mod common; pub mod common_dir_traversal; pub mod common_directory; diff --git a/czkawka_core/src/similar_images.rs b/czkawka_core/src/similar_images.rs index 29d698e..3db45d4 100644 --- a/czkawka_core/src/similar_images.rs +++ b/czkawka_core/src/similar_images.rs @@ -41,7 +41,7 @@ pub const IMAGE_RS_BROKEN_FILES_EXTENSIONS: [&str; 10] = [ ]; pub const ZIP_FILES_EXTENSIONS: [&str; 1] = [".zip"]; -pub const AUDIO_FILES_EXTENSIONS: [&str; 4] = [".mp3", ".flac", ".wav", ".ogg"]; +pub const AUDIO_FILES_EXTENSIONS: [&str; 8] = [".mp3", ".flac", ".wav", ".ogg", ".m4a", ".aac", ".aiff", ".pcm"]; pub const VIDEO_FILES_EXTENSIONS: [&str; 16] = [ ".mp4", ".mpv", ".flv", ".mp4a", ".webm", ".mpg", ".mp2", ".mpeg", ".m4p", ".m4v", ".avi", ".wmv", ".qt", ".mov", ".swf", ".mkv", diff --git a/czkawka_gui/Cargo.toml b/czkawka_gui/Cargo.toml index 658c5de..e53528f 100644 --- a/czkawka_gui/Cargo.toml +++ b/czkawka_gui/Cargo.toml @@ -46,7 +46,7 @@ fs_extra = "1.2.0" # Language i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-requester"] } i18n-embed-fl = "0.6.4" -rust-embed = "6.3.0" +rust-embed = "6.4.0" once_cell = "1.10.0" [target.'cfg(windows)'.dependencies] diff --git a/czkawka_gui/i18n/en/czkawka_gui.ftl b/czkawka_gui/i18n/en/czkawka_gui.ftl index 88f6034..f67325f 100644 --- a/czkawka_gui/i18n/en/czkawka_gui.ftl +++ b/czkawka_gui/i18n/en/czkawka_gui.ftl @@ -79,6 +79,7 @@ main_notebook_similar_videos = Similar Videos main_notebook_same_music = Music Duplicates main_notebook_symlinks = Invalid Symlinks main_notebook_broken_files = Broken Files +main_notebook_bad_extensions = Bad Extensions main_tree_view_column_file_name = File Name main_tree_view_column_folder_name = Folder Name @@ -97,6 +98,8 @@ main_tree_view_column_symlink_file_name = Symlink File Name main_tree_view_column_symlink_folder = Symlnik Folder main_tree_view_column_destination_path = Destination Path main_tree_view_column_type_of_error = Type Of Error +main_tree_view_column_current_extension = Current Extension +main_tree_view_column_proper_extensions = Proper Extension main_label_check_method = Check method main_label_hash_type = Hash type @@ -379,10 +382,12 @@ compute_found_videos = Found { $number_files } similar videos in { $number_group compute_found_music = Found { $number_files } similar music files in { $number_groups } groups compute_found_invalid_symlinks = Found { $number_files } invalid symlinks compute_found_broken_files = Found { $number_files } broken files +compute_found_bad_extensions = Found { $number_files } files with invalid extensions # Progress window progress_scanning_general_file = Scanning {$file_number} file +progress_scanning_extension_of_files = Checking extension of {$file_checked}/{$all_files} file progress_scanning_broken_files = Checking {$file_checked}/{$all_files} file progress_scanning_video = Hashing of {$file_checked}/{$all_files} video progress_scanning_image = Hashing of {$file_checked}/{$all_files} image diff --git a/czkawka_gui/src/compute_results.rs b/czkawka_gui/src/compute_results.rs index d8c055e..f407207 100644 --- a/czkawka_gui/src/compute_results.rs +++ b/czkawka_gui/src/compute_results.rs @@ -43,9 +43,11 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver< let shared_big_files_state = gui_data.shared_big_files_state.clone(); let shared_same_invalid_symlinks = gui_data.shared_same_invalid_symlinks.clone(); let tree_view_temporary_files_finder = gui_data.main_notebook.tree_view_temporary_files_finder.clone(); + let tree_view_bad_extensions = gui_data.main_notebook.tree_view_bad_extensions.clone(); let shared_temporary_files_state = gui_data.shared_temporary_files_state.clone(); let shared_similar_images_state = gui_data.shared_similar_images_state.clone(); let shared_similar_videos_state = gui_data.shared_similar_videos_state.clone(); + let shared_bad_extensions_state = gui_data.shared_bad_extensions_state.clone(); let tree_view_same_music_finder = gui_data.main_notebook.tree_view_same_music_finder.clone(); let shared_same_music_state = gui_data.shared_same_music_state.clone(); let buttons_names = gui_data.bottom_buttons.buttons_names; @@ -1505,6 +1507,73 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver< } } } + Message::BadExtensions(be) => { + if be.get_stopped_search() { + entry_info.set_text(&flg!("compute_stopped_by_user")); + } else { + let information = be.get_information(); + let text_messages = be.get_text_messages(); + + let bad_extensions_number: usize = information.number_of_files_with_bad_extension; + entry_info.set_text( + flg!( + "compute_found_bad_extensions", + generate_translation_hashmap(vec![("number_files", bad_extensions_number.to_string()),]) + ) + .as_str(), + ); + + // Create GUI + { + let list_store = get_list_store(&tree_view_bad_extensions); + + let vector = be.get_bad_extensions_files(); + + // Sort + let mut vector = vector.clone(); + vector.sort_by_key(|e| { + let t = split_path(e.path.as_path()); + (t.0, t.1) + }); + + for file_entry in vector { + let (directory, file) = split_path(&file_entry.path); + let values: [(u32, &dyn ToValue); 7] = [ + (ColumnsBadExtensions::SelectionButton as u32, &false), + (ColumnsBadExtensions::Name as u32, &file), + (ColumnsBadExtensions::Path as u32, &directory), + (ColumnsBadExtensions::CurrentExtension as u32, &file_entry.current_extension), + (ColumnsBadExtensions::ValidExtensions as u32, &file_entry.proper_extensions), + ( + ColumnsBadExtensions::Modification as u32, + &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string()), + ), + (ColumnsBadExtensions::ModificationAsSecs as u32, &(file_entry.modified_date as i64)), + ]; + list_store.set(&list_store.append(), &values); + } + print_text_messages_to_text_view(text_messages, &text_view_errors); + } + + // Set state + { + *shared_bad_extensions_state.borrow_mut() = be; + + set_specific_buttons_as_active( + &shared_buttons, + &NotebookMainEnum::Temporary, + &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], + bad_extensions_number > 0, + ); + + set_buttons( + &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::Temporary).unwrap(), + &buttons_array, + &buttons_names, + ); + } + } + } } // Returning false here would close the receiver and have senders fail glib::Continue(true) diff --git a/czkawka_gui/src/connect_things/connect_button_save.rs b/czkawka_gui/src/connect_things/connect_button_save.rs index 4fd7970..ae32e8f 100644 --- a/czkawka_gui/src/connect_things/connect_button_save.rs +++ b/czkawka_gui/src/connect_things/connect_button_save.rs @@ -26,6 +26,7 @@ pub fn connect_button_save(gui_data: &GuiData) { let shared_same_music_state = gui_data.shared_same_music_state.clone(); let shared_same_invalid_symlinks = gui_data.shared_same_invalid_symlinks.clone(); let shared_broken_files_state = gui_data.shared_broken_files_state.clone(); + let shared_bad_extensions_state = gui_data.shared_bad_extensions_state.clone(); let shared_buttons = gui_data.shared_buttons.clone(); let entry_info = gui_data.entry_info.clone(); let notebook_main = gui_data.main_notebook.notebook_main.clone(); @@ -83,6 +84,11 @@ pub fn connect_button_save(gui_data: &GuiData) { shared_broken_files_state.borrow_mut().save_results_to_file(file_name); } + NotebookMainEnum::BadExtensions => { + file_name = "results_bad_extensions.txt"; + + shared_bad_extensions_state.borrow_mut().save_results_to_file(file_name); + } } post_save_things( file_name, diff --git a/czkawka_gui/src/connect_things/connect_button_search.rs b/czkawka_gui/src/connect_things/connect_button_search.rs index 9e1084e..036180c 100644 --- a/czkawka_gui/src/connect_things/connect_button_search.rs +++ b/czkawka_gui/src/connect_things/connect_button_search.rs @@ -5,6 +5,7 @@ use std::thread; use glib::Sender; use gtk::prelude::*; +use czkawka_core::bad_extensions::BadExtensions; use czkawka_core::big_file::BigFile; use czkawka_core::broken_files::BrokenFiles; use czkawka_core::common_dir_traversal; @@ -41,6 +42,7 @@ pub fn connect_button_search( futures_sender_temporary: futures::channel::mpsc::UnboundedSender, futures_sender_invalid_symlinks: futures::channel::mpsc::UnboundedSender, futures_sender_broken_files: futures::channel::mpsc::UnboundedSender, + futures_sender_bad_extensions: futures::channel::mpsc::UnboundedSender, ) { let combo_box_image_hash_size = gui_data.main_notebook.combo_box_image_hash_size.clone(); let combo_box_image_hash_algorithm = gui_data.main_notebook.combo_box_image_hash_algorithm.clone(); @@ -99,6 +101,7 @@ pub fn connect_button_search( let tree_view_similar_images_finder = gui_data.main_notebook.tree_view_similar_images_finder.clone(); let tree_view_similar_videos_finder = gui_data.main_notebook.tree_view_similar_videos_finder.clone(); let tree_view_temporary_files_finder = gui_data.main_notebook.tree_view_temporary_files_finder.clone(); + let tree_view_bad_extensions = gui_data.main_notebook.tree_view_bad_extensions.clone(); let window_progress = gui_data.progress_window.window_progress.clone(); let entry_info = gui_data.entry_info.clone(); let button_settings = gui_data.header.button_settings.clone(); @@ -482,6 +485,29 @@ pub fn connect_button_search( let _ = glib_stop_sender.send(Message::BrokenFiles(br)); }); } + NotebookMainEnum::BadExtensions => { + label_stage.show(); + grid_progress_stages.show_all(); + window_progress.resize(1, 1); + + get_list_store(&tree_view_bad_extensions).clear(); + + let futures_sender_bad_extensions = futures_sender_bad_extensions.clone(); + // Find Similar music + thread::spawn(move || { + let mut be = BadExtensions::new(); + + be.set_included_directory(included_directories); + be.set_excluded_directory(excluded_directories); + be.set_excluded_items(excluded_items); + be.set_minimal_file_size(minimal_file_size); + be.set_maximal_file_size(maximal_file_size); + be.set_allowed_extensions(allowed_extensions); + be.set_recursive_search(recursive_search); + be.find_bad_extensions_files(Some(&stop_receiver), Some(&futures_sender_bad_extensions)); + let _ = glib_stop_sender.send(Message::BadExtensions(be)); + }); + } } // Show progress dialog diff --git a/czkawka_gui/src/connect_things/connect_progress_window.rs b/czkawka_gui/src/connect_things/connect_progress_window.rs index a7223d6..6e547fb 100644 --- a/czkawka_gui/src/connect_things/connect_progress_window.rs +++ b/czkawka_gui/src/connect_things/connect_progress_window.rs @@ -1,6 +1,8 @@ +use futures::channel::mpsc::UnboundedReceiver; use futures::StreamExt; use gtk::prelude::*; +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; @@ -11,16 +13,17 @@ use crate::taskbar_progress::tbp_flags::TBPF_INDETERMINATE; #[allow(clippy::too_many_arguments)] pub fn connect_progress_window( gui_data: &GuiData, - mut futures_receiver_duplicate_files: futures::channel::mpsc::UnboundedReceiver, - mut futures_receiver_empty_files: futures::channel::mpsc::UnboundedReceiver, - mut futures_receiver_empty_folder: futures::channel::mpsc::UnboundedReceiver, - mut futures_receiver_big_files: futures::channel::mpsc::UnboundedReceiver, - mut futures_receiver_same_music: futures::channel::mpsc::UnboundedReceiver, - mut futures_receiver_similar_images: futures::channel::mpsc::UnboundedReceiver, - mut futures_receiver_similar_videos: futures::channel::mpsc::UnboundedReceiver, - mut futures_receiver_temporary: futures::channel::mpsc::UnboundedReceiver, - mut futures_receiver_invalid_symlinks: futures::channel::mpsc::UnboundedReceiver, - mut futures_receiver_broken_files: futures::channel::mpsc::UnboundedReceiver, + mut futures_receiver_duplicate_files: UnboundedReceiver, + mut futures_receiver_empty_files: UnboundedReceiver, + mut futures_receiver_empty_folder: UnboundedReceiver, + mut futures_receiver_big_files: UnboundedReceiver, + mut futures_receiver_same_music: UnboundedReceiver, + mut futures_receiver_similar_images: UnboundedReceiver, + mut futures_receiver_similar_videos: UnboundedReceiver, + mut futures_receiver_temporary: UnboundedReceiver, + mut futures_receiver_invalid_symlinks: UnboundedReceiver, + mut futures_receiver_broken_files: UnboundedReceiver, + mut futures_receiver_bad_extensions: UnboundedReceiver, ) { let main_context = glib::MainContext::default(); let _guard = main_context.acquire().unwrap(); @@ -411,4 +414,48 @@ pub fn connect_progress_window( }; main_context.spawn_local(future); } + { + // Broken Files + let label_stage = gui_data.progress_window.label_stage.clone(); + let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone(); + let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone(); + let taskbar_state = gui_data.taskbar_state.clone(); + let future = async move { + while let Some(item) = futures_receiver_bad_extensions.next().await { + match item.current_stage { + 0 => { + progress_bar_current_stage.hide(); + label_stage.set_text(&flg!( + "progress_scanning_general_file", + generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())]) + )); + taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE); + } + 1 => { + progress_bar_current_stage.show(); + if item.entries_to_check != 0 { + 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.entries_checked) as f64 / item.entries_to_check as f64); + taskbar_state.borrow().set_progress_value( + (item.entries_to_check + item.entries_checked) as u64, + item.entries_to_check as u64 * (item.max_stage + 1) as u64, + ); + } else { + progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64); + progress_bar_current_stage.set_fraction(0f64); + taskbar_state.borrow().set_progress_value(1, (item.max_stage + 1) as u64); + } + label_stage.set_text(&flg!( + "progress_scanning_extension_of_files", + generate_translation_hashmap(vec![("file_checked", item.entries_checked.to_string()), ("all_files", item.entries_to_check.to_string())]) + )); + } + _ => { + panic!(); + } + } + } + }; + main_context.spawn_local(future); + } } diff --git a/czkawka_gui/src/gui_structs/gui_data.rs b/czkawka_gui/src/gui_structs/gui_data.rs index d56532f..43318ce 100644 --- a/czkawka_gui/src/gui_structs/gui_data.rs +++ b/czkawka_gui/src/gui_structs/gui_data.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use std::rc::Rc; use crossbeam_channel::bounded; +use czkawka_core::bad_extensions::BadExtensions; use gdk::gdk_pixbuf::Pixbuf; use gtk::prelude::*; use gtk::Builder; @@ -88,6 +89,7 @@ pub struct GuiData { pub shared_same_music_state: Rc>, pub shared_same_invalid_symlinks: Rc>, pub shared_broken_files_state: Rc>, + pub shared_bad_extensions_state: Rc>, pub preview_path: Rc>, @@ -162,6 +164,7 @@ impl GuiData { let shared_same_music_state: Rc> = Rc::new(RefCell::new(SameMusic::new())); let shared_same_invalid_symlinks: Rc> = Rc::new(RefCell::new(InvalidSymlinks::new())); let shared_broken_files_state: Rc> = Rc::new(RefCell::new(BrokenFiles::new())); + let shared_bad_extensions_state: Rc> = Rc::new(RefCell::new(BadExtensions::new())); let preview_path: Rc> = Rc::new(RefCell::new("".to_string())); @@ -201,6 +204,7 @@ impl GuiData { shared_same_music_state, shared_same_invalid_symlinks, shared_broken_files_state, + shared_bad_extensions_state, preview_path, entry_info, text_view_errors, diff --git a/czkawka_gui/src/gui_structs/gui_main_notebook.rs b/czkawka_gui/src/gui_structs/gui_main_notebook.rs index 57aa0d9..d265811 100644 --- a/czkawka_gui/src/gui_structs/gui_main_notebook.rs +++ b/czkawka_gui/src/gui_structs/gui_main_notebook.rs @@ -23,6 +23,7 @@ pub struct GuiMainNotebook { pub scrolled_window_same_music_finder: gtk::ScrolledWindow, pub scrolled_window_invalid_symlinks: gtk::ScrolledWindow, pub scrolled_window_broken_files: gtk::ScrolledWindow, + pub scrolled_window_bad_extensions: gtk::ScrolledWindow, pub tree_view_duplicate_finder: gtk::TreeView, pub tree_view_empty_folder_finder: gtk::TreeView, @@ -34,6 +35,7 @@ pub struct GuiMainNotebook { pub tree_view_same_music_finder: gtk::TreeView, pub tree_view_invalid_symlinks: gtk::TreeView, pub tree_view_broken_files: gtk::TreeView, + pub tree_view_bad_extensions: gtk::TreeView, // TODO, in GTK4 this can be changed to e.g. add_controller which is not 100% compatible with this - https://discourse.gnome.org/t/how-to-convert-code-to-use-eventcontrollerkey/8198/2 pub evk_tree_view_duplicate_finder: gtk::EventControllerKey, @@ -46,6 +48,7 @@ pub struct GuiMainNotebook { pub evk_tree_view_same_music_finder: gtk::EventControllerKey, pub evk_tree_view_invalid_symlinks: gtk::EventControllerKey, pub evk_tree_view_broken_files: gtk::EventControllerKey, + pub evk_tree_view_bad_extensions: gtk::EventControllerKey, // pub gc_tree_view_duplicate_finder: gtk4::GestureClick, // pub gc_tree_view_empty_folder_finder: gtk::GestureClick, @@ -57,6 +60,7 @@ pub struct GuiMainNotebook { // pub gc_tree_view_same_music_finder: gtk::GestureClick, // pub gc_tree_view_invalid_symlinks: gtk::GestureClick, // pub gc_tree_view_broken_files: gtk::GestureClick, + // pub gc_tree_view_bad_extensions: gtk::GestureClick, // General @@ -126,6 +130,7 @@ impl GuiMainNotebook { let scrolled_window_same_music_finder: gtk::ScrolledWindow = builder.object("scrolled_window_same_music_finder").unwrap(); let scrolled_window_invalid_symlinks: gtk::ScrolledWindow = builder.object("scrolled_window_invalid_symlinks").unwrap(); let scrolled_window_broken_files: gtk::ScrolledWindow = builder.object("scrolled_window_broken_files").unwrap(); + let scrolled_window_bad_extensions: gtk::ScrolledWindow = builder.object("scrolled_window_bad_extensions").unwrap(); let tree_view_duplicate_finder: gtk::TreeView = TreeView::new(); tree_view_duplicate_finder.set_widget_name("PIERD"); @@ -138,6 +143,7 @@ impl GuiMainNotebook { let tree_view_same_music_finder: gtk::TreeView = TreeView::new(); let tree_view_invalid_symlinks: gtk::TreeView = TreeView::new(); let tree_view_broken_files: gtk::TreeView = TreeView::new(); + let tree_view_bad_extensions: gtk::TreeView = TreeView::new(); let evk_tree_view_duplicate_finder: gtk::EventControllerKey = EventControllerKey::new(&tree_view_duplicate_finder); let evk_tree_view_empty_folder_finder: gtk::EventControllerKey = EventControllerKey::new(&tree_view_empty_folder_finder); @@ -149,6 +155,7 @@ impl GuiMainNotebook { let evk_tree_view_same_music_finder: gtk::EventControllerKey = EventControllerKey::new(&tree_view_same_music_finder); let evk_tree_view_invalid_symlinks: gtk::EventControllerKey = EventControllerKey::new(&tree_view_invalid_symlinks); let evk_tree_view_broken_files: gtk::EventControllerKey = EventControllerKey::new(&tree_view_broken_files); + let evk_tree_view_bad_extensions: gtk::EventControllerKey = EventControllerKey::new(&tree_view_bad_extensions); // TODO GTK 4 // let evk_tree_view_duplicate_finder: gtk4::EventControllerKey = EventControllerKey::new(); @@ -171,6 +178,8 @@ impl GuiMainNotebook { // tree_view_invalid_symlinks.add_controller(&evk_tree_view_invalid_symlinks); // let evk_tree_view_broken_files: gtk4::EventControllerKey = EventControllerKey::new(); // tree_view_broken_files.add_controller(&evk_tree_view_broken_files); + // let evk_tree_view_bad_extensions: gtk4::EventControllerKey = EventControllerKey::new(); + // tree_view_bad_extensions.add_controller(&evk_tree_view_bad_extensions); // let gc_tree_view_duplicate_finder: gtk4::GestureClick = GestureClick::new(); // tree_view_duplicate_finder.add_controller(&gc_tree_view_duplicate_finder); @@ -192,6 +201,8 @@ impl GuiMainNotebook { // tree_view_invalid_symlinks.add_controller(&gc_tree_view_invalid_symlinks); // let gc_tree_view_broken_files: gtk4::GestureClick = GestureClick::new(); // tree_view_broken_files.add_controller(&gc_tree_view_broken_files); + // let gc_tree_view_bad_extensions: gtk4::GestureClick = GestureClick::new(); + // tree_view_bad_extensions.add_controller(&gc_tree_view_bad_extensions); let combo_box_duplicate_check_method: gtk::ComboBoxText = builder.object("combo_box_duplicate_check_method").unwrap(); let combo_box_duplicate_hash_type: gtk::ComboBoxText = builder.object("combo_box_duplicate_hash_type").unwrap(); @@ -251,6 +262,7 @@ impl GuiMainNotebook { scrolled_window_same_music_finder, scrolled_window_invalid_symlinks, scrolled_window_broken_files, + scrolled_window_bad_extensions, tree_view_duplicate_finder, tree_view_empty_folder_finder, tree_view_empty_files_finder, @@ -261,6 +273,7 @@ impl GuiMainNotebook { tree_view_same_music_finder, tree_view_invalid_symlinks, tree_view_broken_files, + tree_view_bad_extensions, evk_tree_view_duplicate_finder, evk_tree_view_empty_folder_finder, evk_tree_view_empty_files_finder, @@ -304,6 +317,7 @@ impl GuiMainNotebook { check_button_video_ignore_same_size, check_button_image_fast_compare, check_button_duplicate_case_sensitive_name, + evk_tree_view_bad_extensions, } } @@ -319,6 +333,7 @@ impl GuiMainNotebook { self.tree_view_same_music_finder.clone(), self.tree_view_invalid_symlinks.clone(), self.tree_view_broken_files.clone(), + self.tree_view_bad_extensions.clone(), ] } @@ -415,6 +430,7 @@ impl GuiMainNotebook { (NotebookMainEnum::SameMusic as usize, flg!("main_notebook_same_music")), (NotebookMainEnum::Symlinks as usize, flg!("main_notebook_symlinks")), (NotebookMainEnum::BrokenFiles as usize, flg!("main_notebook_broken_files")), + (NotebookMainEnum::BrokenFiles as usize, flg!("main_notebook_bad_extensions")), ] { self.notebook_main .tab_label(&vec_children[main_enum]) @@ -492,6 +508,13 @@ impl GuiMainNotebook { flg!("main_tree_view_column_type_of_error"), flg!("main_tree_view_column_modification"), ], // Broken Files + vec![ + flg!("main_tree_view_column_file_name"), + flg!("main_tree_view_column_path"), + flg!("main_tree_view_column_current_extension"), + flg!("main_tree_view_column_proper_extensions"), + flg!("main_tree_view_column_modification"), + ], // Broken Files ]; for (notebook_index, tree_view) in self.get_main_tree_views().iter().enumerate() { diff --git a/czkawka_gui/src/help_functions.rs b/czkawka_gui/src/help_functions.rs index 5d7198b..f0b9b6a 100644 --- a/czkawka_gui/src/help_functions.rs +++ b/czkawka_gui/src/help_functions.rs @@ -3,6 +3,7 @@ use std::cmp::Ordering; use std::collections::HashMap; use std::path::{Path, PathBuf}; +use czkawka_core::bad_extensions::BadExtensions; use gtk::prelude::*; use gtk::{Bin, ListStore, TextView, TreeView, Widget}; @@ -205,6 +206,19 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [ column_size_as_bytes: None, column_modification_as_secs: None, }, + NotebookObject { + notebook_type: NotebookMainEnum::BadExtensions, + available_modes: [PopoverTypes::All, PopoverTypes::Reverse, PopoverTypes::Custom, PopoverTypes::None, PopoverTypes::None], + column_activatable_button: None, + column_path: ColumnsBadExtensions::Path as i32, + column_name: ColumnsBadExtensions::Name as i32, + column_selection: ColumnsBadExtensions::SelectionButton as i32, + column_color: None, + column_dimensions: None, + column_size: None, + column_size_as_bytes: None, + column_modification_as_secs: None, + }, ]; pub enum Message { @@ -218,6 +232,7 @@ pub enum Message { SameMusic(SameMusic), InvalidSymlinks(InvalidSymlinks), BrokenFiles(BrokenFiles), + BadExtensions(BadExtensions), } pub enum ColumnsDuplicates { @@ -345,6 +360,16 @@ pub enum ColumnsBrokenFiles { ModificationAsSecs, } +pub enum ColumnsBadExtensions { + SelectionButton = 0, + Name, + Path, + CurrentExtension, + ValidExtensions, + Modification, + ModificationAsSecs, +} + pub const TEXT_COLOR: &str = "#ffffff"; pub const MAIN_ROW_COLOR: &str = "#343434"; pub const HEADER_ROW_COLOR: &str = "#272727"; @@ -497,6 +522,7 @@ pub fn get_notebook_enum_from_tree_view(tree_view: >k::TreeView) -> NotebookMa "tree_view_same_music_finder" => NotebookMainEnum::SameMusic, "tree_view_invalid_symlinks" => NotebookMainEnum::Symlinks, "tree_view_broken_files" => NotebookMainEnum::BrokenFiles, + "tree_view_bad_extensions" => NotebookMainEnum::BadExtensions, e => { panic!("{}", e) } diff --git a/czkawka_gui/src/initialize_gui.rs b/czkawka_gui/src/initialize_gui.rs index 6d445e5..503ebd8 100644 --- a/czkawka_gui/src/initialize_gui.rs +++ b/czkawka_gui/src/initialize_gui.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::ops::Deref; +use std::path::Path; use std::rc::Rc; use gdk::gdk_pixbuf::Pixbuf; @@ -8,7 +9,7 @@ use gtk::prelude::*; use gtk::{CheckButton, Image, SelectionMode, TextView, TreeView}; use crate::flg; -use czkawka_core::similar_images::SIMILAR_VALUES; +use czkawka_core::similar_images::{IMAGE_RS_EXTENSIONS, RAW_IMAGE_EXTENSIONS, SIMILAR_VALUES}; use czkawka_core::similar_videos::MAX_TOLERANCE; use crate::create_tree_view::*; @@ -387,6 +388,31 @@ pub fn initialize_gui(gui_data: &mut GuiData) { scrolled_window.add(&tree_view); scrolled_window.show_all(); } + // Bad Extensions + { + let scrolled_window = gui_data.main_notebook.scrolled_window_bad_extensions.clone(); + let tree_view = gui_data.main_notebook.tree_view_bad_extensions.clone(); + + let col_types: [glib::types::Type; 7] = [ + glib::types::Type::BOOL, // SelectionButton + glib::types::Type::STRING, // Name + glib::types::Type::STRING, // Path + glib::types::Type::STRING, // CurrentExtension + glib::types::Type::STRING, // ProperExtensions + glib::types::Type::STRING, // Modification + glib::types::Type::U64, // ModificationAsSecs + ]; + let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); + + tree_view.set_model(Some(&list_store)); + tree_view.selection().set_mode(SelectionMode::Multiple); + + create_tree_view_broken_files(&tree_view); + + tree_view.set_widget_name("tree_view_bad_extensions"); + scrolled_window.add(&tree_view); + scrolled_window.show_all(); + } } } @@ -545,6 +571,7 @@ fn connect_event_mouse(gui_data: &GuiData) { // gui_data.main_notebook.gc_tree_view_same_music_finder.clone(), // gui_data.main_notebook.gc_tree_view_invalid_symlinks.clone(), // gui_data.main_notebook.gc_tree_view_broken_files.clone(), + // gui_data.main_notebook.gc_tree_view_bad_extensions.clone(), // ] { // gc.set_button(0); // gc.connect_pressed(opening_double_click_function); @@ -595,6 +622,7 @@ fn connect_event_buttons(gui_data: &GuiData) { gui_data.main_notebook.evk_tree_view_same_music_finder.clone(), gui_data.main_notebook.evk_tree_view_invalid_symlinks.clone(), gui_data.main_notebook.evk_tree_view_broken_files.clone(), + gui_data.main_notebook.evk_tree_view_bad_extensions.clone(), ] { let gui_data_clone = gui_data.clone(); evk.connect_key_pressed(opening_enter_function_ported); @@ -696,6 +724,15 @@ fn show_preview( } } + if let Some(extension) = Path::new(&name).extension() { + let extension = format!(".{}", extension.to_string_lossy()); + if !RAW_IMAGE_EXTENSIONS.contains(&extension.as_str()) && !IMAGE_RS_EXTENSIONS.contains(&extension.as_str()) { + break 'dir; + } + } else { + break 'dir; + } + let mut pixbuf = match Pixbuf::from_file(file_name) { Ok(pixbuf) => pixbuf, Err(e) => { diff --git a/czkawka_gui/src/main.rs b/czkawka_gui/src/main.rs index afbe48a..e0b1658 100644 --- a/czkawka_gui/src/main.rs +++ b/czkawka_gui/src/main.rs @@ -85,7 +85,7 @@ fn build_ui(application: &Application, arguments: Vec) { futures::channel::mpsc::UnboundedSender, futures::channel::mpsc::UnboundedReceiver, ) = futures::channel::mpsc::unbounded(); - let (futures_sender_big_file, futures_receiver_big_file): ( + let (futures_sender_big_file, futures_receiver_big_files): ( futures::channel::mpsc::UnboundedSender, futures::channel::mpsc::UnboundedReceiver, ) = futures::channel::mpsc::unbounded(); @@ -113,6 +113,10 @@ fn build_ui(application: &Application, arguments: Vec) { futures::channel::mpsc::UnboundedSender, futures::channel::mpsc::UnboundedReceiver, ) = futures::channel::mpsc::unbounded(); + let (futures_sender_bad_extensions, futures_receiver_bad_extensions): ( + futures::channel::mpsc::UnboundedSender, + futures::channel::mpsc::UnboundedReceiver, + ) = futures::channel::mpsc::unbounded(); initialize_gui(&mut gui_data); validate_notebook_data(&gui_data); // Must be run after initialization of gui, to check if everything was properly setup @@ -146,6 +150,7 @@ fn build_ui(application: &Application, arguments: Vec) { futures_sender_temporary, futures_sender_invalid_symlinks, futures_sender_broken_files, + futures_sender_bad_extensions, ); connect_button_select(&gui_data); connect_button_stop(&gui_data); @@ -163,13 +168,14 @@ fn build_ui(application: &Application, arguments: Vec) { futures_receiver_duplicate_files, futures_receiver_empty_files, futures_receiver_empty_folder, - futures_receiver_big_file, + futures_receiver_big_files, futures_receiver_same_music, futures_receiver_similar_images, futures_receiver_similar_videos, futures_receiver_temporary, futures_receiver_invalid_symlinks, futures_receiver_broken_files, + futures_receiver_bad_extensions, ); connect_show_hide_ui(&gui_data); connect_settings(&gui_data); diff --git a/czkawka_gui/src/notebook_enums.rs b/czkawka_gui/src/notebook_enums.rs index 87c9113..03eeb81 100644 --- a/czkawka_gui/src/notebook_enums.rs +++ b/czkawka_gui/src/notebook_enums.rs @@ -1,4 +1,4 @@ -pub const NUMBER_OF_NOTEBOOK_MAIN_TABS: usize = 10; +pub const NUMBER_OF_NOTEBOOK_MAIN_TABS: usize = 11; // pub const NUMBER_OF_NOTEBOOK_UPPER_TABS: usize = 3; // Needs to be updated when changed order of notebook tabs @@ -14,6 +14,7 @@ pub enum NotebookMainEnum { SameMusic, Symlinks, BrokenFiles, + BadExtensions, } pub fn to_notebook_main_enum(notebook_number: u32) -> NotebookMainEnum { @@ -28,6 +29,7 @@ pub fn to_notebook_main_enum(notebook_number: u32) -> NotebookMainEnum { 7 => NotebookMainEnum::SameMusic, 8 => NotebookMainEnum::Symlinks, 9 => NotebookMainEnum::BrokenFiles, + 10 => NotebookMainEnum::BadExtensions, _ => panic!("Invalid Notebook Tab"), } } @@ -44,6 +46,7 @@ pub fn get_all_main_tabs() -> [NotebookMainEnum; NUMBER_OF_NOTEBOOK_MAIN_TABS] { to_notebook_main_enum(7), to_notebook_main_enum(8), to_notebook_main_enum(9), + to_notebook_main_enum(10), ] } diff --git a/czkawka_gui/ui/czkawka.cmb b/czkawka_gui/ui/czkawka.cmb index 6e7bbfd..0771c3d 100644 --- a/czkawka_gui/ui/czkawka.cmb +++ b/czkawka_gui/ui/czkawka.cmb @@ -2,13 +2,13 @@ - (5,None,"Czkawka","main_window.ui","Czkawka is simple and fast app to find duplicates, empty folders, similar images etc.",None,"Rafa\\305\\202 Mikrut","mit_x11",None," Generated with glade 3.39.0 \n\nThe MIT License (MIT)\n\nCopyright (c) \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\nAuthor: Rafał Mikrut\n\n"), - (6,None,"Czkawka","about_dialog.cmb.ui","Czkawka is simple and fast app to find duplicates, empty folders, similar images etc.",None,"Rafa\\305\\202 Mikrut","mit_x11",None," Generated with glade 3.39.0 \n\nThe MIT License (MIT)\n\nCopyright (c) \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\nAuthor: Rafał Mikrut\n\n"), - (7,None,"compare_images.ui","compare_images.ui",None,None,None,None,None," Generated with glade 3.39.0 "), - (8,None,"Czkawka","popover_right_click.ui","Czkawka is simple and fast app to find duplicates, empty folders, similar images etc.",None,"Rafa\\305\\202 Mikrut","mit_x11",None," Generated with glade 3.38.2 \n\nThe MIT License (MIT)\n\nCopyright (c) \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\nAuthor: Rafał Mikrut\n\n"), - (9,None,"Czkawka","popover_select.ui","Czkawka is simple and fast app to find duplicates, empty folders, similar images etc.",None,"Rafa\\305\\202 Mikrut","mit_x11",None," Generated with glade 3.38.2 \n\nThe MIT License (MIT)\n\nCopyright (c) \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\nAuthor: Rafał Mikrut\n\n"), - (10,None,"Czkawka","progress.ui","Czkawka is simple and fast app to find duplicates, empty folders, similar images etc.",None,"Rafa\\305\\202 Mikrut","mit_x11",None," Generated with glade 3.39.0 \n\nThe MIT License (MIT)\n\nCopyright (c) \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\nAuthor: Rafał Mikrut\n\n"), - (11,None,"Czkawka","settings.ui","Czkawka is simple and fast app to find duplicates, empty folders, similar images etc.",None,"Rafa\\305\\202 Mikrut","mit_x11",None," Generated with glade 3.39.0 \n\nThe MIT License (MIT)\n\nCopyright (c) \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\nAuthor: Rafał Mikrut\n\n") + (5,None,"Czkawka","main_window.ui",None,None,None,None,None,None), + (6,None,"Czkawka","about_dialog.cmb.ui",None,None,None,None,None,None), + (7,None,"Czkawka","compare_images.ui",None,None,None,None,None,None), + (8,None,"Czkawka","popover_right_click.ui",None,None,None,None,None,None), + (9,None,"Czkawka","popover_select.ui",None,None,None,None,None,None), + (10,None,"Czkawka","progress.ui",None,None,None,None,None,None), + (11,None,"Czkawka","settings.ui",None,None,None,None,None,None) (5,"gtk+","3.24",None), diff --git a/czkawka_gui/ui/main_window.ui b/czkawka_gui/ui/main_window.ui index 489cbbe..6f0d068 100644 --- a/czkawka_gui/ui/main_window.ui +++ b/czkawka_gui/ui/main_window.ui @@ -1,91 +1,60 @@ - - - + + - - - - - 10 - 1 100 + 1 + 10 False - 800 1100 + 800 + True False vertical - True + True True vertical - True - True True + True - False - 5 - 5 - 5 True + False + 5 + 5 + 5 + True False 5 vertical 1 - True + True True True - True + True False center 4 - True + True False image-missing - True False @@ -95,9 +64,9 @@ Author: Rafał Mikrut + True False Add - True False @@ -116,20 +85,20 @@ Author: Rafał Mikrut + True True True - True + True False center 4 - True + True False image-missing - True False @@ -139,9 +108,9 @@ Author: Rafał Mikrut + True False Remove - True False @@ -160,20 +129,20 @@ Author: Rafał Mikrut + True True True - True + True False center 4 - True + True False image-missing - True False @@ -183,9 +152,9 @@ Author: Rafał Mikrut + True False Manual Add - True False @@ -211,9 +180,9 @@ Author: Rafał Mikrut + True True in - True True @@ -223,13 +192,13 @@ Author: Rafał Mikrut - True - True - True Recursive - 5 - False True + True + False + 5 + True + True False @@ -244,9 +213,9 @@ Author: Rafał Mikrut + True False Included Directories - True False @@ -254,34 +223,34 @@ Author: Rafał Mikrut - False - 5 - 5 - 6 True + False + 5 + 5 + 6 + True False 5 vertical 1 - True + True True True - True + True False center 4 - True + True False image-missing - True False @@ -291,9 +260,9 @@ Author: Rafał Mikrut + True False Add - True False @@ -312,20 +281,20 @@ Author: Rafał Mikrut + True True True - True + True False center 4 - True + True False image-missing - True False @@ -335,9 +304,9 @@ Author: Rafał Mikrut + True False Remove - True False @@ -356,20 +325,20 @@ Author: Rafał Mikrut + True True True - True + True False center 4 - True + True False image-missing - True False @@ -379,9 +348,9 @@ Author: Rafał Mikrut + True False Manual Add - True False @@ -407,9 +376,9 @@ Author: Rafał Mikrut + True True in - True True @@ -424,9 +393,9 @@ Author: Rafał Mikrut + True False Excluded Directories - True 1 @@ -435,22 +404,22 @@ Author: Rafał Mikrut - False - vertical - center True + False + center + vertical - False - 5 - 5 - 5 True + False + 5 + 5 + 5 + True False Excluded items - True False @@ -460,9 +429,9 @@ Author: Rafał Mikrut + True True */.git,*/node_modules,*/lost+found - True True @@ -479,16 +448,16 @@ Author: Rafał Mikrut - False - 5 - 5 - 5 True + False + 5 + 5 + 5 + True False Allowed Extensions - True False @@ -498,8 +467,8 @@ Author: Rafał Mikrut - True True + True True @@ -516,16 +485,16 @@ Author: Rafał Mikrut - False - 5 - 5 - 8 True + False + 5 + 5 + 8 + True False File Size(bytes) - True False @@ -535,9 +504,9 @@ Author: Rafał Mikrut + True False Min: - True False @@ -547,12 +516,12 @@ Author: Rafał Mikrut + True True - False - number 15 8192 - True + False + number True @@ -562,9 +531,9 @@ Author: Rafał Mikrut + True False Max: - True False @@ -574,12 +543,12 @@ Author: Rafał Mikrut + True True - False - number 15 1099512000000 - True + False + number True @@ -601,9 +570,9 @@ Author: Rafał Mikrut + True False Items Configuration - True 2 @@ -618,32 +587,32 @@ Author: Rafał Mikrut - True - True - left True + True + left + True - False True + False + True False vertical - True - False - 5 - 5 - 2 True + False + 5 + 5 + 2 - False - Check method - 3 True + False + 3 + Check method False @@ -653,8 +622,8 @@ Author: Rafał Mikrut - False True + False False @@ -664,11 +633,11 @@ Author: Rafał Mikrut - False - Hash type - 2 - 5 True + False + 5 + 2 + Hash type False @@ -678,8 +647,8 @@ Author: Rafał Mikrut - False True + False False @@ -690,8 +659,13 @@ Author: Rafał Mikrut Case sensitive + False + False + False + False + True 4 @@ -704,10 +678,10 @@ Author: Rafał Mikrut + True True 5 in - True True @@ -724,11 +698,11 @@ Author: Rafał Mikrut - False - 80 - center - True 100 + 80 + True + False + center False @@ -740,9 +714,9 @@ Author: Rafał Mikrut + True False Duplicates files - True False @@ -750,9 +724,9 @@ Author: Rafał Mikrut + True True in - True 1 @@ -760,9 +734,9 @@ Author: Rafał Mikrut + True False Empty Directories - True 1 @@ -771,22 +745,22 @@ Author: Rafał Mikrut + True False vertical - True + True False - 5 5 + 5 2 8 - True + True False Number of shown files - True False @@ -796,12 +770,12 @@ Author: Rafał Mikrut + True True - False - number 15 50 - True + False + number True @@ -818,9 +792,9 @@ Author: Rafał Mikrut + True True in - True True @@ -835,9 +809,9 @@ Author: Rafał Mikrut + True False Big Files - True 2 @@ -846,9 +820,9 @@ Author: Rafał Mikrut + True True in - True 3 @@ -856,9 +830,9 @@ Author: Rafał Mikrut + True False Empty Files - True 3 @@ -867,9 +841,9 @@ Author: Rafał Mikrut + True True in - True 4 @@ -877,9 +851,9 @@ Author: Rafał Mikrut + True False Temporary Files - True 4 @@ -888,26 +862,26 @@ Author: Rafał Mikrut - False True + False + True False vertical - True - False - 5 - 5 - 2 True + False + 5 + 5 + 2 - False - Resize algorithm - 2 True + False + 2 + Resize algorithm False @@ -917,8 +891,8 @@ Author: Rafał Mikrut - False True + False False @@ -928,11 +902,11 @@ Author: Rafał Mikrut - False - Hash size: - 2 - 5 True + False + 5 + 2 + Hash size: False @@ -942,8 +916,8 @@ Author: Rafał Mikrut - False True + False False @@ -953,11 +927,11 @@ Author: Rafał Mikrut - False - Hash type: - 2 - 5 True + False + 5 + 2 + Hash type: False @@ -967,8 +941,8 @@ Author: Rafał Mikrut - False True + False False @@ -985,16 +959,16 @@ Author: Rafał Mikrut - False - 2 - 5 - 5 True + False + 5 + 5 + 2 + True False Similarity - True False @@ -1004,9 +978,9 @@ Author: Rafał Mikrut + True False Very High - True False @@ -1016,12 +990,12 @@ Author: Rafał Mikrut + True True - 0 100 1 + 0 right - True True @@ -1031,11 +1005,11 @@ Author: Rafał Mikrut - False - Minimal - 5 - 5 True + False + 5 + 5 + Minimal False @@ -1045,12 +1019,12 @@ Author: Rafał Mikrut - True - True Ignore same size - 7 - False True + True + False + 7 + True False @@ -1060,12 +1034,12 @@ Author: Rafał Mikrut - True - True Fast compare - 7 - False True + True + False + 7 + True False @@ -1082,10 +1056,10 @@ Author: Rafał Mikrut + True True 5 in - True True @@ -1102,11 +1076,11 @@ Author: Rafał Mikrut - False - 80 - center - True 100 + 80 + True + False + center False @@ -1121,9 +1095,9 @@ Author: Rafał Mikrut + True False Similar Images - True 5 @@ -1132,21 +1106,21 @@ Author: Rafał Mikrut + True False vertical - True - False - 2 - 5 - 5 True + False + 5 + 5 + 2 + True False Similarity - True False @@ -1156,9 +1130,9 @@ Author: Rafał Mikrut + True False Very High - True False @@ -1168,12 +1142,12 @@ Author: Rafał Mikrut + True True - 0 100 0 + 0 right - True True @@ -1183,11 +1157,11 @@ Author: Rafał Mikrut - False - Minimal - 5 - 5 True + False + 5 + 5 + Minimal False @@ -1197,12 +1171,12 @@ Author: Rafał Mikrut - True - True Ignore same size - 7 - False True + True + False + 7 + True False @@ -1219,9 +1193,9 @@ Author: Rafał Mikrut + True True in - True True @@ -1236,9 +1210,9 @@ Author: Rafał Mikrut + True False Similar Videos - True 6 @@ -1247,24 +1221,24 @@ Author: Rafał Mikrut + True False vertical - True - False - 5 - 5 - 8 True + False + 5 + 5 + 8 - True - True - True Title - False True + True + False + True + True False @@ -1274,12 +1248,12 @@ Author: Rafał Mikrut - True - True - True Artist - False True + True + False + True + True False @@ -1289,12 +1263,12 @@ Author: Rafał Mikrut - True - True - start Year - False True + True + False + start + True False @@ -1304,11 +1278,11 @@ Author: Rafał Mikrut - True - True Bitrate - False True + True + False + True False @@ -1318,11 +1292,11 @@ Author: Rafał Mikrut - True - True Genre - False True + True + False + True False @@ -1332,14 +1306,19 @@ Author: Rafał Mikrut - True - True Length - False True - 1.0 - 0.9 + True + False + 1 + 0.89999997615814209 + True + + False + True + 5 + @@ -1350,18 +1329,18 @@ Author: Rafał Mikrut - False - 2 - 5 - 5 True + False + 5 + 5 + 2 - True - True Approximate Comparison - False True + True + False + True False @@ -1378,10 +1357,10 @@ Author: Rafał Mikrut + True True 5 in - True True @@ -1396,9 +1375,9 @@ Author: Rafał Mikrut + True False Music Duplicates - True 7 @@ -1407,9 +1386,9 @@ Author: Rafał Mikrut + True True in - True 8 @@ -1417,9 +1396,9 @@ Author: Rafał Mikrut + True False Invalid Symlinks - True 8 @@ -1428,9 +1407,9 @@ Author: Rafał Mikrut + True True in - True 9 @@ -1438,15 +1417,39 @@ Author: Rafał Mikrut + True False Broken Files - True 9 False + + + True + True + in + + + + + + 10 + + + + + True + False + Bad Extensions + + + 10 + False + + True @@ -1462,33 +1465,33 @@ Author: Rafał Mikrut - False - 5 - 5 - 5 - 2 True + False + 5 + 5 + 2 + 5 + True False 2 - True + True True True - True + True False center 2 - True + True False image-missing - True False @@ -1498,9 +1501,9 @@ Author: Rafał Mikrut + True False Search - True False @@ -1526,15 +1529,15 @@ Author: Rafał Mikrut + True True True - True + True False center image-missing - True @@ -1547,15 +1550,15 @@ Author: Rafał Mikrut + True True True - True + True False center image-missing - True @@ -1568,26 +1571,26 @@ Author: Rafał Mikrut + True False 2 - True + True True False True - True + True False center 2 - True + True False image-missing - True False @@ -1597,9 +1600,9 @@ Author: Rafał Mikrut + True False Select - True False @@ -1618,20 +1621,20 @@ Author: Rafał Mikrut + True True True - True + True False center 2 - True + True False image-missing - True False @@ -1641,9 +1644,9 @@ Author: Rafał Mikrut + True False Compare - True False @@ -1662,20 +1665,20 @@ Author: Rafał Mikrut + True True True - True + True False center 2 - True + True False image-missing - True False @@ -1685,9 +1688,9 @@ Author: Rafał Mikrut + True False Delete - True False @@ -1706,20 +1709,20 @@ Author: Rafał Mikrut + True True True - True + True False center 2 - True + True False image-missing - True False @@ -1729,9 +1732,9 @@ Author: Rafał Mikrut + True False Move - True False @@ -1750,20 +1753,20 @@ Author: Rafał Mikrut + True True True - True + True False center 2 - True + True False image-missing - True False @@ -1773,9 +1776,9 @@ Author: Rafał Mikrut + True False Save - True False @@ -1794,20 +1797,20 @@ Author: Rafał Mikrut + True True True - True + True False center 2 - True + True False image-missing - True False @@ -1817,9 +1820,9 @@ Author: Rafał Mikrut + True False Symlink - True False @@ -1838,20 +1841,20 @@ Author: Rafał Mikrut + True True True - True + True False center 2 - True + True False image-missing - True False @@ -1861,9 +1864,9 @@ Author: Rafał Mikrut + True False Hardlink - True False @@ -1897,19 +1900,19 @@ Author: Rafał Mikrut - False - 5 - 5 - 5 - 5 True + False + 5 + 5 + 5 + 5 + True True False False none - True True @@ -1919,14 +1922,14 @@ Author: Rafał Mikrut + True True - False False False - none Czkawka 4.0.0 - True 1 + none + False False @@ -1944,17 +1947,17 @@ Author: Rafał Mikrut + True True - 100 queue in - True + 100 + True True False True - True @@ -1969,24 +1972,24 @@ Author: Rafał Mikrut + True False True - True + True False 5 - True + True True True - True + True False image-missing - True @@ -1998,14 +2001,14 @@ Author: Rafał Mikrut + True True True - True + True False image-missing - True diff --git a/instructions/Compilation.md b/instructions/Compilation.md index 8d5f7cb..51ee4bd 100644 --- a/instructions/Compilation.md +++ b/instructions/Compilation.md @@ -9,7 +9,7 @@ FFmpeg is not included here, because is not needed to build because it is dynami | Program | Min | What for | |---------|------|-------------------------------------------------------------------------------| -| Rust | 1.56 | Czkawka, aims to support the latest available version of Rust on Ubuntu 20.04 | +| Rust | 1.57 | Czkawka, aims to support the latest available version of Rust on Ubuntu 20.04 | | GTK | 3.24 | Only for the `GTK` backend | #### Debian / Ubuntu diff --git a/instructions/Instruction.md b/instructions/Instruction.md index bff254c..86d14f4 100644 --- a/instructions/Instruction.md +++ b/instructions/Instruction.md @@ -231,6 +231,8 @@ Some images broke hash functions and create hashes full of `0` or `255`, so thes 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` +Faster compare option allows to only once compare results, so checking should works a lot of faster when using higher number of similarity. + Some tidbits: - Smaller hash size not always means that calculating it will take more time - `Blockhash` is the only algorithm that don't resize images before hashing @@ -253,8 +255,20 @@ Next, with provided by user tolerance, they are compared to each other and group ### Broken Files This tool finds files which are corrupted or have an invalid extension. -At first files from specific group (image,archive,audio) are collected and then these files are opened(due to additional dependencies, audio files are disabled by default). +At first app collects image and archive files(only this two types are supported now, but also I plan to support audio, but this is currently blocked by https://github.com/RustAudio/rodio/issues/349) and then these files are simply opened. If an error happens when opening such file it means that this file is corrupted or unsupported. Only some file extensions are handled, because I rely on external crates. Also, some false positives may be shown(e.g. https://github.com/image-rs/jpeg-decoder/issues/130) so always open file to check if it is really broken. + +### Bad Extensions +Mode allows to find files with content that not match with their extensions. + +It works in this way: +- Takes current file extension e.g. `źrebię.zip` -> `zip` +- Read few bytes of file +- Matches bytes with signatures to guess file extension e.g. `7z` +- Takes mime type(may return more than 1) from extension e.g. `Mime::Archive` +- Returns all file extensions that are connected to this mime type e.g. `rar,7z,zip,p7` +- Basing on file extension, adds more elements to list from above(needed because some files e.g. `exe` and `dll` begins with similar/same bytes) +- If current file extensions is inside list then probably have proper extension, if is not inside, then is shown as file with invalid extension \ No newline at end of file