1
0
Fork 0
mirror of synced 2024-04-27 09:12:06 +12:00

Finding invalid extensions (#678)

* Find invalid extensions

* Another day

* Libc

* Basic Renames

* Ending words

* Better renaming

* Stonks because works
This commit is contained in:
Rafał Mikrut 2022-04-22 21:46:33 +02:00 committed by GitHub
parent c88d347e00
commit 8e4e1f58cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1236 additions and 397 deletions

1
.gitignore vendored
View file

@ -7,3 +7,4 @@ results*.txt
TestSuite*
*.snap
flatpak/
*.zip

180
Cargo.lock generated
View file

@ -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"

View file

@ -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)

View file

@ -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
<!-- The GIF thingy -->
![Czkawka](https://user-images.githubusercontent.com/41945903/145280350-506f7e94-4db0-4de7-a68d-6e7c26bbd2bf.gif)

View file

@ -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 = []

View file

@ -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<FileEntry>,
bad_extensions_files: Vec<BadFileEntry>,
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<ProgressData>>) {
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<BadFileEntry> {
&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<PathBuf>) -> bool {
self.directories.set_included_directory(included_directory, &mut self.text_messages)
}
pub fn set_excluded_directory(&mut self, excluded_directory: Vec<PathBuf>) {
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<String>) {
self.excluded_items.set_excluded_items(excluded_items, &mut self.text_messages);
}
fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool {
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<ProgressData>>) -> 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 == &current_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(&current_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::<Vec<_>>();
// 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());
}
}

View file

@ -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;

View file

@ -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",

View file

@ -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]

View file

@ -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

View file

@ -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)

View file

@ -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,

View file

@ -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<temporary::ProgressData>,
futures_sender_invalid_symlinks: futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures_sender_broken_files: futures::channel::mpsc::UnboundedSender<broken_files::ProgressData>,
futures_sender_bad_extensions: futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
) {
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

View file

@ -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<common_dir_traversal::ProgressData>,
mut futures_receiver_empty_files: futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
mut futures_receiver_empty_folder: futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
mut futures_receiver_big_files: futures::channel::mpsc::UnboundedReceiver<big_file::ProgressData>,
mut futures_receiver_same_music: futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
mut futures_receiver_similar_images: futures::channel::mpsc::UnboundedReceiver<similar_images::ProgressData>,
mut futures_receiver_similar_videos: futures::channel::mpsc::UnboundedReceiver<similar_videos::ProgressData>,
mut futures_receiver_temporary: futures::channel::mpsc::UnboundedReceiver<temporary::ProgressData>,
mut futures_receiver_invalid_symlinks: futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
mut futures_receiver_broken_files: futures::channel::mpsc::UnboundedReceiver<broken_files::ProgressData>,
mut futures_receiver_duplicate_files: UnboundedReceiver<common_dir_traversal::ProgressData>,
mut futures_receiver_empty_files: UnboundedReceiver<common_dir_traversal::ProgressData>,
mut futures_receiver_empty_folder: UnboundedReceiver<common_dir_traversal::ProgressData>,
mut futures_receiver_big_files: UnboundedReceiver<big_file::ProgressData>,
mut futures_receiver_same_music: UnboundedReceiver<common_dir_traversal::ProgressData>,
mut futures_receiver_similar_images: UnboundedReceiver<similar_images::ProgressData>,
mut futures_receiver_similar_videos: UnboundedReceiver<similar_videos::ProgressData>,
mut futures_receiver_temporary: UnboundedReceiver<temporary::ProgressData>,
mut futures_receiver_invalid_symlinks: UnboundedReceiver<common_dir_traversal::ProgressData>,
mut futures_receiver_broken_files: UnboundedReceiver<broken_files::ProgressData>,
mut futures_receiver_bad_extensions: UnboundedReceiver<ProgressData>,
) {
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);
}
}

View file

@ -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<RefCell<SameMusic>>,
pub shared_same_invalid_symlinks: Rc<RefCell<InvalidSymlinks>>,
pub shared_broken_files_state: Rc<RefCell<BrokenFiles>>,
pub shared_bad_extensions_state: Rc<RefCell<BadExtensions>>,
pub preview_path: Rc<RefCell<String>>,
@ -162,6 +164,7 @@ impl GuiData {
let shared_same_music_state: Rc<RefCell<_>> = Rc::new(RefCell::new(SameMusic::new()));
let shared_same_invalid_symlinks: Rc<RefCell<_>> = Rc::new(RefCell::new(InvalidSymlinks::new()));
let shared_broken_files_state: Rc<RefCell<_>> = Rc::new(RefCell::new(BrokenFiles::new()));
let shared_bad_extensions_state: Rc<RefCell<_>> = Rc::new(RefCell::new(BadExtensions::new()));
let preview_path: Rc<RefCell<_>> = 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,

View file

@ -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() {

View file

@ -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: &gtk::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)
}

View file

@ -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) => {

View file

@ -85,7 +85,7 @@ fn build_ui(application: &Application, arguments: Vec<OsString>) {
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = 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<big_file::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<big_file::ProgressData>,
) = futures::channel::mpsc::unbounded();
@ -113,6 +113,10 @@ fn build_ui(application: &Application, arguments: Vec<OsString>) {
futures::channel::mpsc::UnboundedSender<broken_files::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<broken_files::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_bad_extensions, futures_receiver_bad_extensions): (
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = futures::channel::mpsc::unbounded();
initialize_gui(&mut gui_data);
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<OsString>) {
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<OsString>) {
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);

View file

@ -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),
]
}

View file

@ -2,13 +2,13 @@
<!DOCTYPE cambalache-project SYSTEM "cambalache-project.dtd">
<cambalache-project version="0.9.0" target_tk="gtk+-3.0">
<ui>
(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)
</ui>
<ui_library>
(5,"gtk+","3.24",None),

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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