Implement finding duplicated music by tags/artist/year etc. (#95)
This commit is contained in:
parent
8ba780ded6
commit
ecebb3a1af
407
Cargo.lock
generated
407
Cargo.lock
generated
|
@ -1,5 +1,11 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.2.0"
|
||||
|
@ -38,9 +44,9 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
|||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "atk"
|
||||
|
@ -79,6 +85,25 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "audiotags"
|
||||
version = "0.2.7182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b87d111118f42416bb5f13c5dd2e2d879925964702a435be711d4f78fa9ce6d8"
|
||||
dependencies = [
|
||||
"audiotags-dev-macro",
|
||||
"id3",
|
||||
"metaflac",
|
||||
"mp4ameta",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "audiotags-dev-macro"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b79298591161f312f06327df7963063ee07466be303dcc3084a44ec293cb36e"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "0.1.7"
|
||||
|
@ -123,9 +148,9 @@ checksum = "5488039ea2c6de8668351415e39a0218a8955bffadcff0cf01d1293a20854584"
|
|||
|
||||
[[package]]
|
||||
name = "blake2b_simd"
|
||||
version = "0.5.10"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
|
||||
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
|
@ -141,7 +166,7 @@ dependencies = [
|
|||
"arrayref",
|
||||
"arrayvec",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"constant_time_eq",
|
||||
"crypto-mac",
|
||||
"digest",
|
||||
|
@ -218,6 +243,12 @@ version = "0.1.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
|
@ -267,10 +298,16 @@ version = "0.1.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const_fn"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.5"
|
||||
|
@ -279,11 +316,11 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
|||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
||||
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -292,32 +329,41 @@ version = "0.4.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
"crossbeam-utils 0.7.2",
|
||||
"maybe-uninit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.7.3"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
|
||||
checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
"maybe-uninit",
|
||||
"crossbeam-utils 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.8.2"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
|
||||
checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"cfg-if 1.0.0",
|
||||
"const_fn",
|
||||
"crossbeam-utils 0.8.0",
|
||||
"lazy_static",
|
||||
"maybe-uninit",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
@ -329,7 +375,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"cfg-if 1.0.0",
|
||||
"const_fn",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
|
@ -355,9 +413,11 @@ dependencies = [
|
|||
name = "czkawka_core"
|
||||
version = "1.2.1"
|
||||
dependencies = [
|
||||
"audiotags",
|
||||
"bitflags",
|
||||
"bk-tree",
|
||||
"blake3",
|
||||
"crossbeam-channel",
|
||||
"crossbeam-channel 0.4.4",
|
||||
"hamming",
|
||||
"humansize",
|
||||
"image",
|
||||
|
@ -369,7 +429,7 @@ name = "czkawka_gui"
|
|||
version = "1.2.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossbeam-channel",
|
||||
"crossbeam-channel 0.4.4",
|
||||
"czkawka_core",
|
||||
"gdk",
|
||||
"gio",
|
||||
|
@ -431,7 +491,7 @@ checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
|
|||
dependencies = [
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.44",
|
||||
"syn 1.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -490,6 +550,70 @@ version = "1.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "encoding"
|
||||
version = "0.2.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec"
|
||||
dependencies = [
|
||||
"encoding-index-japanese",
|
||||
"encoding-index-korean",
|
||||
"encoding-index-simpchinese",
|
||||
"encoding-index-singlebyte",
|
||||
"encoding-index-tradchinese",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding-index-japanese"
|
||||
version = "1.20141219.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91"
|
||||
dependencies = [
|
||||
"encoding_index_tests",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding-index-korean"
|
||||
version = "1.20141219.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81"
|
||||
dependencies = [
|
||||
"encoding_index_tests",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding-index-simpchinese"
|
||||
version = "1.20141219.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7"
|
||||
dependencies = [
|
||||
"encoding_index_tests",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding-index-singlebyte"
|
||||
version = "1.20141219.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a"
|
||||
dependencies = [
|
||||
"encoding_index_tests",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding-index-tradchinese"
|
||||
version = "1.20141219.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18"
|
||||
dependencies = [
|
||||
"encoding_index_tests",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_index_tests"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.20.14"
|
||||
|
@ -499,6 +623,18 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crc32fast",
|
||||
"libc",
|
||||
"miniz_oxide 0.4.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
|
@ -507,9 +643,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
|||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d8e3078b7b2a8a671cb7a3d17b4760e4181ea243227776ba83fd043b4ca034e"
|
||||
checksum = "95314d38584ffbfda215621d723e0a3906f032e03ae5551e650058dac83d4797"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
|
@ -522,9 +658,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a4d35f7401e948629c9c3d6638fb9bf94e0b2121e96c3b428cc4e631f3eb74"
|
||||
checksum = "0448174b01148032eed37ac4aed28963aaaa8cfa93569a08e5b479bbc6c2c151"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
|
@ -532,15 +668,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d674eaa0056896d5ada519900dbf97ead2e46a7b6621e8160d79e2f2e1e2784b"
|
||||
checksum = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc709ca1da6f66143b8c9bec8e6260181869893714e9b5a490b169b0414144ab"
|
||||
checksum = "f5f8e0c9258abaea85e78ebdda17ef9666d390e987f006be6080dfe354b708cb"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
|
@ -549,42 +685,42 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fc94b64bb39543b4e432f1790b6bf18e3ee3b74653c5449f63310e9a74b123c"
|
||||
checksum = "6e1798854a4727ff944a7b12aa999f58ce7aa81db80d2dfaaf2ba06f065ddd2b"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f57ed14da4603b2554682e9f2ff3c65d7567b53188db96cb71538217fc64581b"
|
||||
checksum = "e36fccf3fc58563b4a14d265027c627c3b665d7fed489427e88e7cc929559efe"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.44",
|
||||
"syn 1.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d8764258ed64ebc5d9ed185cf86a95db5cac810269c5d20ececb32e0088abbd"
|
||||
checksum = "0e3ca3f17d6e8804ae5d3df7a7d35b2b3a6fe89dac84b31872720fc3060a0b11"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dd26820a9f3637f1302da8bceba3ff33adbe53464b54ca24d4e2d4f1db30f94"
|
||||
checksum = "96d502af37186c4fef99453df03e374683f8a1eec9dcc1e66b3b82dc8278ce3c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a894a0acddba51a2d49a6f4263b1e64b8c579ece8af50fa86503d52cd1eea34"
|
||||
checksum = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
|
@ -681,7 +817,7 @@ version = "0.1.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
@ -762,7 +898,7 @@ dependencies = [
|
|||
"proc-macro-error",
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.44",
|
||||
"syn 1.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -856,6 +992,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
|
||||
|
||||
[[package]]
|
||||
name = "humansize"
|
||||
version = "1.1.0"
|
||||
|
@ -863,13 +1005,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e"
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.23.10"
|
||||
name = "id3"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "985fc06b1304d19c28d5c562ed78ef5316183f2b0053b46763a0b94862373c34"
|
||||
checksum = "02c11bb50ce1568516aefbe4b6564c3feaf15a8e5ccbea90fa652012446ae9bf"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"encoding",
|
||||
"flate2",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.23.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4f0a8345b33b082aedec2f4d7d4a926b845cee184cbe78b703413066564431b"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"gif",
|
||||
"jpeg-decoder",
|
||||
"num-iter",
|
||||
|
@ -935,17 +1091,17 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.79"
|
||||
version = "0.2.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
|
||||
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3557c9384f7f757f6d139cd3a4c62ef4e850696c16bf27924a5538c8a09717a1"
|
||||
checksum = "1090080fe06ec2648d0da3881d9453d97e71a45f00eb179af7fdd7e3f686fdb0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
|
@ -955,7 +1111,7 @@ version = "0.4.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -969,12 +1125,6 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lzw"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084"
|
||||
|
||||
[[package]]
|
||||
name = "maybe-uninit"
|
||||
version = "2.0.0"
|
||||
|
@ -983,9 +1133,9 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
|||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
version = "2.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
|
@ -996,6 +1146,17 @@ dependencies = [
|
|||
"autocfg 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "metaflac"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4685bf0039a9d2919c2dbb281cba1c58d168dce58f519cbd70c468ce2c36a748"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"hex",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minifb"
|
||||
version = "0.17.0"
|
||||
|
@ -1025,6 +1186,32 @@ dependencies = [
|
|||
"adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"autocfg 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mp4ameta"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ef7a2ba51feaa9e7be2209b6e5d7472d08427ef0ce88616ed93c66dae9601b2"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"mp4ameta_proc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mp4ameta_proc"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64ae83441f6b67e3b7f009295618e90f03228b0f1a149f56ee8cd0f6bb5602c5"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.17.0"
|
||||
|
@ -1033,7 +1220,7 @@ checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
|
|||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"void",
|
||||
]
|
||||
|
@ -1061,9 +1248,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.43"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
|
||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"num-traits",
|
||||
|
@ -1071,9 +1258,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.41"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f"
|
||||
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"num-integer",
|
||||
|
@ -1082,9 +1269,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5b4d7360f362cfb50dde8143501e6940b22f644be75a4cc90b2d81968908138"
|
||||
checksum = "e5fa6d5f418879385b213d905f7cf5bf4aa553d4c380f0152d1d4f2749186fa9"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"num-integer",
|
||||
|
@ -1093,9 +1280,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.12"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
]
|
||||
|
@ -1185,7 +1372,7 @@ dependencies = [
|
|||
"case",
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.44",
|
||||
"syn 1.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1315,22 +1502,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "0.4.27"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15"
|
||||
checksum = "ee41d838744f60d959d7074e3afb6b35c7456d0f61cad38a24e35e6553f73841"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "0.4.27"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895"
|
||||
checksum = "81a4ffa594b66bff340084d4081df649a7dc049ac8d7fc458d8e628bfbbb2f86"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.44",
|
||||
"syn 1.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1366,14 +1553,14 @@ dependencies = [
|
|||
"bitflags",
|
||||
"crc32fast",
|
||||
"deflate 0.8.6",
|
||||
"miniz_oxide",
|
||||
"miniz_oxide 0.3.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.9"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
|
@ -1393,7 +1580,7 @@ dependencies = [
|
|||
"proc-macro-error-attr",
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.44",
|
||||
"syn 1.0.48",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
|
@ -1410,9 +1597,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.18"
|
||||
version = "0.5.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
|
||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-nested"
|
||||
|
@ -1627,9 +1814,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.4.1"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf6960dc9a5b4ee8d3e4c5787b4a112a8818e0290a42ff664ad60692fdf2032"
|
||||
checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"crossbeam-deque",
|
||||
|
@ -1639,13 +1826,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.8.1"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf"
|
||||
checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-channel 0.5.0",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"crossbeam-utils 0.8.0",
|
||||
"lazy_static",
|
||||
"num_cpus",
|
||||
]
|
||||
|
@ -1705,7 +1892,7 @@ dependencies = [
|
|||
"base64 0.12.3",
|
||||
"blake2b_simd",
|
||||
"constant_time_eq",
|
||||
"crossbeam-utils",
|
||||
"crossbeam-utils 0.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1804,7 +1991,7 @@ version = "0.32.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34e71125077d297d57e4c1acfe8981b5bdfbf5a20e7b589abfdcb33bf1127f86"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
@ -1840,7 +2027,7 @@ checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
|
|||
dependencies = [
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.44",
|
||||
"syn 1.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1917,7 +2104,7 @@ dependencies = [
|
|||
"quote 1.0.7",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"syn 1.0.44",
|
||||
"syn 1.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1933,7 +2120,7 @@ dependencies = [
|
|||
"serde_derive",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
"syn 1.0.44",
|
||||
"syn 1.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1975,7 +2162,7 @@ dependencies = [
|
|||
"proc-macro-error",
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.44",
|
||||
"syn 1.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1993,7 +2180,7 @@ dependencies = [
|
|||
"heck",
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.44",
|
||||
"syn 1.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2021,9 +2208,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.44"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e03e57e4fcbfe7749842d53e24ccb9aa12b7252dbe5e91d2acad31834c8b8fdd"
|
||||
checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.7",
|
||||
|
@ -2051,7 +2238,7 @@ version = "3.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"rand 0.7.3",
|
||||
"redox_syscall",
|
||||
|
@ -2085,18 +2272,18 @@ checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab"
|
|||
dependencies = [
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.44",
|
||||
"syn 1.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f3b8a87c4da944c3f27e5943289171ac71a6150a79ff6bacfff06d159dfff2f"
|
||||
checksum = "abeb4e3f32a8973722c0254189e6890358e72b1bf11becb287ee0b23c595a41d"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"lzw",
|
||||
"miniz_oxide",
|
||||
"jpeg-decoder",
|
||||
"miniz_oxide 0.4.3",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2213,7 +2400,7 @@ version = "0.2.68"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
|
@ -2228,7 +2415,7 @@ dependencies = [
|
|||
"log",
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.44",
|
||||
"syn 1.0.48",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
@ -2250,7 +2437,7 @@ checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe"
|
|||
dependencies = [
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.44",
|
||||
"syn 1.0.48",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
@ -2333,9 +2520,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0e26e7a4d998e3d7949c69444b8b4916bac810da0d3a82ae612c89e952782f4"
|
||||
checksum = "8795d6e0e17485803cc10ef126bb8c0d59b7c61b219d66cfe0b3216dd0e8580a"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
|
|
|
@ -30,6 +30,7 @@ But the most important thing for me was to learn Rust and create a program usefu
|
|||
- Temporary Files - Allows finding temporary files
|
||||
- Similar Files - Finds files which are not exactly the same
|
||||
- Zeroed Files - Find files which are filled with zeros(usually corrupted)
|
||||
- Same Musc - Search for music with same artist, album etc.
|
||||
|
||||
## Usage and requirements
|
||||
|
||||
|
@ -168,7 +169,7 @@ So still is a big room for improvements.
|
|||
| Big files | X | | |
|
||||
| Similar images | X | | X |
|
||||
| Zeroed Files| X | | |
|
||||
| Checking files EXIF| | | X |
|
||||
| Music duplicates(EXIF) | X | | X |
|
||||
| Installed packages | | X | |
|
||||
| Invalid names | | X | |
|
||||
| Names conflict | | X | |
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use czkawka_core::duplicate::{CheckingMethod, DeleteMethod};
|
||||
use czkawka_core::same_music::MusicSimilarity;
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(name = "czkawka", help_message = HELP_MESSAGE, template = HELP_TEMPLATE)]
|
||||
pub enum Commands {
|
||||
|
@ -123,6 +125,25 @@ pub enum Commands {
|
|||
#[structopt(short, long, parse(try_from_str = parse_minimal_file_size), default_value = "1024", help = "Minimum size in bytes", long_help = "Minimum size of checked files in bytes, assigning bigger value may speed up searching")]
|
||||
minimal_file_size: u64,
|
||||
},
|
||||
#[structopt(name = "music", about = "Finds same music by tags", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka music -d /home/rafal -f results.txt")]
|
||||
SameMusic {
|
||||
#[structopt(flatten)]
|
||||
directories: Directories,
|
||||
#[structopt(flatten)]
|
||||
excluded_directories: ExcludedDirectories,
|
||||
#[structopt(flatten)]
|
||||
excluded_items: ExcludedItems,
|
||||
// #[structopt(short = "D", long, help = "Delete found files")]
|
||||
// delete_files: bool, TODO
|
||||
#[structopt(short = "z", long, default_value = "artist,title", parse(try_from_str = parse_music_duplicate_type), help = "Search method (title, artist, album_title, album_artist, year)", long_help = "Sets which rows must be equal to set this files as duplicates(may be mixed, but must be divided by commas).")]
|
||||
music_similarity: MusicSimilarity,
|
||||
#[structopt(flatten)]
|
||||
file_to_save: FileToSave,
|
||||
#[structopt(flatten)]
|
||||
not_recursive: NotRecursive,
|
||||
#[structopt(short, long, parse(try_from_str = parse_minimal_file_size), default_value = "1024", help = "Minimum size in bytes", long_help = "Minimum size of checked files in bytes, assigning bigger value may speed up searching")]
|
||||
minimal_file_size: u64,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
|
@ -210,6 +231,38 @@ fn parse_minimal_file_size(src: &str) -> Result<u64, String> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_music_duplicate_type(src: &str) -> Result<MusicSimilarity, String> {
|
||||
if src.is_empty() {
|
||||
return Ok(MusicSimilarity::NONE);
|
||||
}
|
||||
|
||||
let mut similarity: MusicSimilarity = MusicSimilarity::NONE;
|
||||
|
||||
let parts: Vec<&str> = src.split(',').collect();
|
||||
|
||||
if parts.iter().any(|e| e.to_lowercase().contains("title") && !e.to_lowercase().contains("album")) {
|
||||
similarity |= MusicSimilarity::TITLE;
|
||||
}
|
||||
if parts.iter().any(|e| e.to_lowercase().contains("artist") && !e.to_lowercase().contains("album")) {
|
||||
similarity |= MusicSimilarity::ARTIST;
|
||||
}
|
||||
if parts.iter().any(|e| e.to_lowercase().contains("title") && e.to_lowercase().contains("album")) {
|
||||
similarity |= MusicSimilarity::ALBUM_TITLE;
|
||||
}
|
||||
if parts.iter().any(|e| e.to_lowercase().contains("artist") && e.to_lowercase().contains("album")) {
|
||||
similarity |= MusicSimilarity::ALBUM_ARTIST;
|
||||
}
|
||||
if parts.iter().any(|e| e.to_lowercase().contains("year")) {
|
||||
similarity |= MusicSimilarity::YEAR;
|
||||
}
|
||||
|
||||
if similarity == MusicSimilarity::NONE {
|
||||
return Err("Couldn't parse the music search method (allowed: title,artist,album_title,album_artist,year)".to_string());
|
||||
}
|
||||
|
||||
Ok(similarity)
|
||||
}
|
||||
|
||||
static HELP_MESSAGE: &str = "Prints help information (--help will give more information)";
|
||||
|
||||
const HELP_TEMPLATE: &str = r#"
|
||||
|
@ -233,4 +286,5 @@ EXAMPLES:
|
|||
{bin} empty-files -d /home/rafal /home/szczekacz -e /home/rafal/Pulpit -R -f results.txt
|
||||
{bin} temp -d /home/rafal/ -E */.git */tmp* *Pulpit -f results.txt -D
|
||||
{bin} image -d /home/rafal -e /home/rafal/Pulpit -f results.txt
|
||||
{bin} zeroed -d /home/rafal -e /home/rafal/Pulpit -f results.txt"#;
|
||||
{bin} zeroed -d /home/rafal -e /home/krzak -f results.txt"
|
||||
{bin} music -d /home/rafal -e /home/rafal/Pulpit -z "artist,year, ARTISTALBUM, ALBUM___tiTlE" -f results.txt"#;
|
||||
|
|
|
@ -10,6 +10,7 @@ use czkawka_core::{
|
|||
duplicate::DuplicateFinder,
|
||||
empty_files::{self, EmptyFiles},
|
||||
empty_folder::EmptyFolder,
|
||||
same_music::SameMusic,
|
||||
similar_files::SimilarImages,
|
||||
temporary::{self, Temporary},
|
||||
zeroed::{self, ZeroedFiles},
|
||||
|
@ -221,7 +222,6 @@ fn main() {
|
|||
sf.print_results();
|
||||
sf.get_text_messages().print_messages();
|
||||
}
|
||||
|
||||
Commands::ZeroedFiles {
|
||||
directories,
|
||||
excluded_directories,
|
||||
|
@ -258,5 +258,41 @@ fn main() {
|
|||
zf.print_results();
|
||||
zf.get_text_messages().print_messages();
|
||||
}
|
||||
Commands::SameMusic {
|
||||
directories,
|
||||
excluded_directories,
|
||||
excluded_items,
|
||||
// delete_files,
|
||||
file_to_save,
|
||||
not_recursive,
|
||||
minimal_file_size,
|
||||
music_similarity,
|
||||
} => {
|
||||
let mut mf = SameMusic::new();
|
||||
|
||||
mf.set_included_directory(path_list_to_str(directories.directories));
|
||||
mf.set_excluded_directory(path_list_to_str(excluded_directories.excluded_directories));
|
||||
mf.set_excluded_items(path_list_to_str(excluded_items.excluded_items));
|
||||
mf.set_minimal_file_size(minimal_file_size);
|
||||
mf.set_recursive_search(!not_recursive.not_recursive);
|
||||
mf.set_music_similarity(music_similarity);
|
||||
|
||||
// if delete_files {
|
||||
// // TODO mf.set_delete_method(same_music::DeleteMethod::Delete);
|
||||
// }
|
||||
|
||||
mf.find_same_music(None);
|
||||
|
||||
if let Some(file_name) = file_to_save.file_name() {
|
||||
if !mf.save_results_to_file(file_name) {
|
||||
mf.get_text_messages().print_messages();
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))] // This will show too much probably unnecessary data to debug, comment line only if needed
|
||||
mf.print_results();
|
||||
mf.get_text_messages().print_messages();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,3 +21,7 @@ img_hash = "3.1"
|
|||
bk-tree = "0.3"
|
||||
image = "0.23"
|
||||
hamming = "0.1"
|
||||
|
||||
# Needed by same music
|
||||
bitflags = "1.2.1"
|
||||
audiotags = "0.2.7182"
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
pub mod big_file;
|
||||
pub mod duplicate;
|
||||
pub mod empty_files;
|
||||
|
@ -10,6 +13,7 @@ pub mod common_extensions;
|
|||
pub mod common_items;
|
||||
pub mod common_messages;
|
||||
pub mod common_traits;
|
||||
pub mod same_music;
|
||||
pub mod similar_files;
|
||||
pub mod zeroed;
|
||||
|
||||
|
|
557
czkawka_core/src/same_music.rs
Normal file
557
czkawka_core/src/same_music.rs
Normal file
|
@ -0,0 +1,557 @@
|
|||
use std::fs;
|
||||
use std::fs::{File, Metadata};
|
||||
use std::io::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use crate::common::Common;
|
||||
use crate::common_directory::Directories;
|
||||
use crate::common_items::ExcludedItems;
|
||||
use crate::common_messages::Messages;
|
||||
use crate::common_traits::*;
|
||||
use audiotags::Tag;
|
||||
use crossbeam_channel::Receiver;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub enum DeleteMethod {
|
||||
None,
|
||||
Delete,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct MusicSimilarity : u32 {
|
||||
const NONE = 0;
|
||||
|
||||
const TITLE = 0b1;
|
||||
const ARTIST = 0b10;
|
||||
|
||||
const ALBUM_TITLE = 0b100;
|
||||
const ALBUM_ARTIST = 0b1000;
|
||||
|
||||
const YEAR = 0b10000;
|
||||
// const Time = 0b100000;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FileEntry {
|
||||
pub size: u64,
|
||||
|
||||
pub path: PathBuf,
|
||||
pub modified_date: u64,
|
||||
|
||||
pub title: String,
|
||||
pub artist: String,
|
||||
|
||||
pub album_title: String,
|
||||
pub album_artist: String,
|
||||
|
||||
pub year: i32,
|
||||
// pub time: u32,
|
||||
}
|
||||
|
||||
/// Info struck with helpful information's about results
|
||||
#[derive(Default)]
|
||||
pub struct Info {
|
||||
pub number_of_checked_files: usize,
|
||||
pub number_of_checked_folders: usize,
|
||||
pub number_of_ignored_files: usize,
|
||||
pub number_of_ignored_things: usize,
|
||||
pub number_of_music_entries: usize,
|
||||
pub number_of_removed_files: usize,
|
||||
pub number_of_failed_to_remove_files: usize,
|
||||
pub number_of_duplicates_music_files: usize,
|
||||
}
|
||||
impl Info {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct with required information's to work
|
||||
pub struct SameMusic {
|
||||
text_messages: Messages,
|
||||
information: Info,
|
||||
music_entries: Vec<FileEntry>,
|
||||
duplicated_music_entries: Vec<Vec<FileEntry>>,
|
||||
directories: Directories,
|
||||
excluded_items: ExcludedItems,
|
||||
minimal_file_size: u64,
|
||||
recursive_search: bool,
|
||||
delete_method: DeleteMethod,
|
||||
music_similarity: MusicSimilarity,
|
||||
stopped_search: bool,
|
||||
}
|
||||
|
||||
impl SameMusic {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
text_messages: Messages::new(),
|
||||
information: Info::new(),
|
||||
recursive_search: true,
|
||||
directories: Directories::new(),
|
||||
excluded_items: ExcludedItems::new(),
|
||||
music_entries: vec![],
|
||||
delete_method: DeleteMethod::None,
|
||||
music_similarity: MusicSimilarity::NONE,
|
||||
stopped_search: false,
|
||||
minimal_file_size: 1024,
|
||||
duplicated_music_entries: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_same_music(&mut self, rx: Option<&Receiver<()>>) {
|
||||
self.directories.optimize_directories(self.recursive_search, &mut self.text_messages);
|
||||
if !self.check_files(rx) {
|
||||
self.stopped_search = true;
|
||||
return;
|
||||
}
|
||||
if !self.check_for_duplicates(rx) {
|
||||
self.stopped_search = true;
|
||||
return;
|
||||
}
|
||||
self.delete_files();
|
||||
self.debug_print();
|
||||
}
|
||||
|
||||
pub fn get_stopped_search(&self) -> bool {
|
||||
self.stopped_search
|
||||
}
|
||||
|
||||
pub const fn get_duplicated_music_entries(&self) -> &Vec<Vec<FileEntry>> {
|
||||
&self.duplicated_music_entries
|
||||
}
|
||||
pub const fn get_music_similarity(&self) -> &MusicSimilarity {
|
||||
&self.music_similarity
|
||||
}
|
||||
|
||||
pub const fn get_text_messages(&self) -> &Messages {
|
||||
&self.text_messages
|
||||
}
|
||||
|
||||
pub const fn get_information(&self) -> &Info {
|
||||
&self.information
|
||||
}
|
||||
|
||||
pub fn set_delete_method(&mut self, delete_method: DeleteMethod) {
|
||||
self.delete_method = delete_method;
|
||||
}
|
||||
|
||||
pub fn set_recursive_search(&mut self, recursive_search: bool) {
|
||||
self.recursive_search = recursive_search;
|
||||
}
|
||||
|
||||
pub fn set_included_directory(&mut self, included_directory: String) -> bool {
|
||||
self.directories.set_included_directory(included_directory, &mut self.text_messages)
|
||||
}
|
||||
|
||||
pub fn set_excluded_directory(&mut self, excluded_directory: String) {
|
||||
self.directories.set_excluded_directory(excluded_directory, &mut self.text_messages);
|
||||
}
|
||||
|
||||
pub fn set_excluded_items(&mut self, excluded_items: String) {
|
||||
self.excluded_items.set_excluded_items(excluded_items, &mut self.text_messages);
|
||||
}
|
||||
|
||||
pub fn set_music_similarity(&mut self, music_similarity: MusicSimilarity) {
|
||||
self.music_similarity = music_similarity;
|
||||
}
|
||||
|
||||
/// Check files for any with size == 0
|
||||
fn check_files(&mut self, rx: Option<&Receiver<()>>) -> bool {
|
||||
let start_time: SystemTime = SystemTime::now();
|
||||
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
|
||||
|
||||
// Add root folders for finding
|
||||
for id in &self.directories.included_directories {
|
||||
folders_to_check.push(id.clone());
|
||||
}
|
||||
self.information.number_of_checked_folders += folders_to_check.len();
|
||||
|
||||
while !folders_to_check.is_empty() {
|
||||
if rx.is_some() && rx.unwrap().try_recv().is_ok() {
|
||||
return false;
|
||||
}
|
||||
let current_folder = folders_to_check.pop().unwrap();
|
||||
|
||||
// Read current dir, if permission are denied just go to next
|
||||
let read_dir = match fs::read_dir(¤t_folder) {
|
||||
Ok(t) => t,
|
||||
Err(_) => {
|
||||
self.text_messages.warnings.push(format!("Cannot open dir {}", current_folder.display()));
|
||||
continue;
|
||||
} // Permissions denied
|
||||
};
|
||||
|
||||
// Check every sub folder/file/link etc.
|
||||
'dir: for entry in read_dir {
|
||||
let entry_data = match entry {
|
||||
Ok(t) => t,
|
||||
Err(_) => {
|
||||
self.text_messages.warnings.push(format!("Cannot read entry in dir {}", current_folder.display()));
|
||||
continue 'dir;
|
||||
} //Permissions denied
|
||||
};
|
||||
let metadata: Metadata = match entry_data.metadata() {
|
||||
Ok(t) => t,
|
||||
Err(_) => {
|
||||
self.text_messages.warnings.push(format!("Cannot read metadata in dir {}", current_folder.display()));
|
||||
continue 'dir;
|
||||
} //Permissions denied
|
||||
};
|
||||
if metadata.is_dir() {
|
||||
self.information.number_of_checked_folders += 1;
|
||||
|
||||
if !self.recursive_search {
|
||||
continue 'dir;
|
||||
}
|
||||
|
||||
let next_folder = current_folder.join(entry_data.file_name());
|
||||
if self.directories.is_excluded(&next_folder) || self.excluded_items.is_excluded(&next_folder) {
|
||||
continue 'dir;
|
||||
}
|
||||
|
||||
folders_to_check.push(next_folder);
|
||||
} else if metadata.is_file() {
|
||||
// Checking files
|
||||
if metadata.len() >= self.minimal_file_size {
|
||||
let current_file_name = current_folder.join(entry_data.file_name());
|
||||
if self.excluded_items.is_excluded(¤t_file_name) {
|
||||
self.information.number_of_ignored_files += 1;
|
||||
continue 'dir;
|
||||
}
|
||||
|
||||
let allowed_extensions = [".mp3", ".flac"];
|
||||
|
||||
if !allowed_extensions.iter().any(|r| current_file_name.to_string_lossy().ends_with(r)) {
|
||||
self.information.number_of_ignored_files += 1;
|
||||
continue 'dir;
|
||||
}
|
||||
|
||||
let tag = Tag::new().read_from_path(¤t_file_name).unwrap();
|
||||
|
||||
// Creating new file entry
|
||||
let fe: FileEntry = FileEntry {
|
||||
size: metadata.len(),
|
||||
path: current_file_name.clone(),
|
||||
modified_date: match metadata.modified() {
|
||||
Ok(t) => match t.duration_since(UNIX_EPOCH) {
|
||||
Ok(d) => d.as_secs(),
|
||||
Err(_) => {
|
||||
self.text_messages.warnings.push(format!("File {} seems to be modified before Unix Epoch.", current_file_name.display()));
|
||||
0
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
self.text_messages.warnings.push(format!("Unable to get modification date from file {}", current_file_name.display()));
|
||||
continue 'dir;
|
||||
} // Permissions Denied
|
||||
},
|
||||
title: match tag.title() {
|
||||
Some(t) => t.to_string(),
|
||||
None => "".to_string(),
|
||||
},
|
||||
artist: match tag.artist() {
|
||||
Some(t) => t.to_string(),
|
||||
None => "".to_string(),
|
||||
},
|
||||
album_title: match tag.album_title() {
|
||||
Some(t) => t.to_string(),
|
||||
None => "".to_string(),
|
||||
},
|
||||
album_artist: match tag.album_artist() {
|
||||
Some(t) => t.to_string(),
|
||||
None => "".to_string(),
|
||||
},
|
||||
year: match tag.year() {
|
||||
Some(t) => t,
|
||||
None => 0,
|
||||
},
|
||||
};
|
||||
|
||||
// Adding files to Vector
|
||||
self.music_entries.push(fe);
|
||||
|
||||
self.information.number_of_checked_files += 1;
|
||||
} else {
|
||||
self.information.number_of_ignored_files += 1;
|
||||
}
|
||||
} else {
|
||||
// Probably this is symbolic links so we are free to ignore this
|
||||
self.information.number_of_ignored_things += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.information.number_of_music_entries = self.music_entries.len();
|
||||
|
||||
Common::print_time(start_time, SystemTime::now(), "check_files_size".to_string());
|
||||
true
|
||||
}
|
||||
|
||||
fn check_for_duplicates(&mut self, rx: Option<&Receiver<()>>) -> bool {
|
||||
if MusicSimilarity::NONE == self.music_similarity {
|
||||
panic!("This can't be none");
|
||||
}
|
||||
let start_time: SystemTime = SystemTime::now();
|
||||
|
||||
let mut old_duplicates: Vec<Vec<FileEntry>> = vec![self.music_entries.clone()];
|
||||
let mut new_duplicates: Vec<Vec<FileEntry>> = Vec::new();
|
||||
|
||||
if (self.music_similarity & MusicSimilarity::TITLE) == MusicSimilarity::TITLE {
|
||||
for vec_file_entry in old_duplicates {
|
||||
if rx.is_some() && rx.unwrap().try_recv().is_ok() {
|
||||
return false;
|
||||
}
|
||||
let mut hash_map: HashMap<String, Vec<FileEntry>> = Default::default();
|
||||
for file_entry in vec_file_entry {
|
||||
let title = file_entry.title.to_lowercase().trim().to_string();
|
||||
if title != "" {
|
||||
hash_map.entry(title.clone()).or_insert_with(Vec::new);
|
||||
hash_map.get_mut(title.as_str()).unwrap().push(file_entry);
|
||||
}
|
||||
}
|
||||
for (_title, vec_file_entry) in hash_map {
|
||||
if vec_file_entry.len() > 1 {
|
||||
new_duplicates.push(vec_file_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
old_duplicates = new_duplicates;
|
||||
new_duplicates = Vec::new();
|
||||
}
|
||||
|
||||
if (self.music_similarity & MusicSimilarity::ARTIST) == MusicSimilarity::ARTIST {
|
||||
for vec_file_entry in old_duplicates {
|
||||
if rx.is_some() && rx.unwrap().try_recv().is_ok() {
|
||||
return false;
|
||||
}
|
||||
let mut hash_map: HashMap<String, Vec<FileEntry>> = Default::default();
|
||||
for file_entry in vec_file_entry {
|
||||
let artist = file_entry.artist.to_lowercase().trim().to_string();
|
||||
if artist != "" {
|
||||
hash_map.entry(artist.clone()).or_insert_with(Vec::new);
|
||||
hash_map.get_mut(artist.as_str()).unwrap().push(file_entry);
|
||||
}
|
||||
}
|
||||
for (_artist, vec_file_entry) in hash_map {
|
||||
if vec_file_entry.len() > 1 {
|
||||
new_duplicates.push(vec_file_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
old_duplicates = new_duplicates;
|
||||
new_duplicates = Vec::new();
|
||||
}
|
||||
|
||||
if (self.music_similarity & MusicSimilarity::ALBUM_TITLE) == MusicSimilarity::ALBUM_TITLE {
|
||||
for vec_file_entry in old_duplicates {
|
||||
if rx.is_some() && rx.unwrap().try_recv().is_ok() {
|
||||
return false;
|
||||
}
|
||||
let mut hash_map: HashMap<String, Vec<FileEntry>> = Default::default();
|
||||
for file_entry in vec_file_entry {
|
||||
let album_title = file_entry.album_title.to_lowercase().trim().to_string();
|
||||
if album_title != "" {
|
||||
hash_map.entry(album_title.clone()).or_insert_with(Vec::new);
|
||||
hash_map.get_mut(album_title.as_str()).unwrap().push(file_entry);
|
||||
}
|
||||
}
|
||||
for (_album_title, vec_file_entry) in hash_map {
|
||||
if vec_file_entry.len() > 1 {
|
||||
new_duplicates.push(vec_file_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
old_duplicates = new_duplicates;
|
||||
new_duplicates = Vec::new();
|
||||
}
|
||||
|
||||
if (self.music_similarity & MusicSimilarity::ALBUM_ARTIST) == MusicSimilarity::ALBUM_ARTIST {
|
||||
for vec_file_entry in old_duplicates {
|
||||
if rx.is_some() && rx.unwrap().try_recv().is_ok() {
|
||||
return false;
|
||||
}
|
||||
let mut hash_map: HashMap<String, Vec<FileEntry>> = Default::default();
|
||||
for file_entry in vec_file_entry {
|
||||
let album_artist = file_entry.album_artist.to_lowercase().trim().to_string();
|
||||
if album_artist != "" {
|
||||
hash_map.entry(album_artist.clone()).or_insert_with(Vec::new);
|
||||
hash_map.get_mut(album_artist.as_str()).unwrap().push(file_entry);
|
||||
}
|
||||
}
|
||||
for (_album_artist, vec_file_entry) in hash_map {
|
||||
if vec_file_entry.len() > 1 {
|
||||
new_duplicates.push(vec_file_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
old_duplicates = new_duplicates;
|
||||
new_duplicates = Vec::new();
|
||||
}
|
||||
|
||||
if (self.music_similarity & MusicSimilarity::YEAR) == MusicSimilarity::YEAR {
|
||||
for vec_file_entry in old_duplicates {
|
||||
if rx.is_some() && rx.unwrap().try_recv().is_ok() {
|
||||
return false;
|
||||
}
|
||||
let mut hash_map: HashMap<i32, Vec<FileEntry>> = Default::default();
|
||||
for file_entry in vec_file_entry {
|
||||
let year = file_entry.year;
|
||||
if year != 0 {
|
||||
hash_map.entry(year).or_insert_with(Vec::new);
|
||||
hash_map.get_mut(&year).unwrap().push(file_entry);
|
||||
}
|
||||
}
|
||||
for (_year, vec_file_entry) in hash_map {
|
||||
if vec_file_entry.len() > 1 {
|
||||
new_duplicates.push(vec_file_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
old_duplicates = new_duplicates;
|
||||
// new_duplicates = Vec::new();
|
||||
}
|
||||
|
||||
self.duplicated_music_entries = old_duplicates;
|
||||
|
||||
for vec in &self.duplicated_music_entries {
|
||||
self.information.number_of_duplicates_music_files += vec.len() - 1;
|
||||
}
|
||||
|
||||
Common::print_time(start_time, SystemTime::now(), "check_for_duplicates".to_string());
|
||||
true
|
||||
}
|
||||
|
||||
pub fn set_minimal_file_size(&mut self, minimal_file_size: u64) {
|
||||
self.minimal_file_size = match minimal_file_size {
|
||||
0 => 1,
|
||||
t => t,
|
||||
};
|
||||
}
|
||||
|
||||
/// Function to delete files, from filed Vector
|
||||
fn delete_files(&mut self) {
|
||||
let start_time: SystemTime = SystemTime::now();
|
||||
// TODO
|
||||
// match self.delete_method {
|
||||
// DeleteMethod::Delete => {
|
||||
// for file_entry in &self.music_entries {
|
||||
// if fs::remove_file(file_entry.path.clone()).is_err() {
|
||||
// self.text_messages.warnings.push(file_entry.path.display().to_string());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// DeleteMethod::None => {
|
||||
// //Just do nothing
|
||||
// }
|
||||
// }
|
||||
|
||||
Common::print_time(start_time, SystemTime::now(), "delete_files".to_string());
|
||||
}
|
||||
}
|
||||
impl Default for SameMusic {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugPrint for SameMusic {
|
||||
#[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!("Number of checked files - {}", self.information.number_of_checked_files);
|
||||
println!("Number of checked folders - {}", self.information.number_of_checked_folders);
|
||||
println!("Number of ignored files - {}", self.information.number_of_ignored_files);
|
||||
println!("Number of ignored things(like symbolic links) - {}", self.information.number_of_ignored_things);
|
||||
println!("Number of removed files - {}", self.information.number_of_removed_files);
|
||||
println!("Number of failed to remove files - {}", self.information.number_of_failed_to_remove_files);
|
||||
println!("Number of duplicated music files - {}", self.information.number_of_duplicates_music_files);
|
||||
|
||||
println!("### Other");
|
||||
|
||||
println!("Excluded items - {:?}", self.excluded_items.items);
|
||||
println!("Minimum file size - {:?}", self.minimal_file_size);
|
||||
println!("Found files music - {}", self.music_entries.len());
|
||||
println!("Found duplicated files music - {}", self.duplicated_music_entries.len());
|
||||
println!("Included directories - {:?}", self.directories.included_directories);
|
||||
println!("Excluded directories - {:?}", self.directories.excluded_directories);
|
||||
println!("Recursive search - {}", self.recursive_search.to_string());
|
||||
println!("Delete Method - {:?}", self.delete_method);
|
||||
println!("-----------------------------------------");
|
||||
}
|
||||
}
|
||||
impl SaveResults for SameMusic {
|
||||
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 mut file = match File::create(&file_name) {
|
||||
Ok(t) => t,
|
||||
Err(_) => {
|
||||
self.text_messages.errors.push(format!("Failed to create file {}", file_name));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
if writeln!(
|
||||
file,
|
||||
"Results of searching {:?} with excluded directories {:?} and excluded items {:?}",
|
||||
self.directories.included_directories, self.directories.excluded_directories, self.excluded_items.items
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
self.text_messages.errors.push(format!("Failed to save results to file {}", file_name));
|
||||
return false;
|
||||
}
|
||||
|
||||
if !self.music_entries.is_empty() {
|
||||
writeln!(file, "Found {} same music files.", self.information.number_of_music_entries).unwrap();
|
||||
for file_entry in self.music_entries.iter() {
|
||||
writeln!(file, "{}", file_entry.path.display()).unwrap();
|
||||
}
|
||||
} else {
|
||||
write!(file, "Not found any empty files.").unwrap();
|
||||
}
|
||||
Common::print_time(start_time, SystemTime::now(), "save_results_to_file".to_string());
|
||||
true
|
||||
}
|
||||
}
|
||||
impl PrintResults for SameMusic {
|
||||
/// Print information's about duplicated entries
|
||||
/// Only needed for CLI
|
||||
fn print_results(&self) {
|
||||
let start_time: SystemTime = SystemTime::now();
|
||||
println!("Found {} similar music files.\n", self.duplicated_music_entries.len());
|
||||
for vec_file_entry in self.duplicated_music_entries.iter() {
|
||||
for file_entry in vec_file_entry {
|
||||
println!(
|
||||
"T: {} - A: {} - AT: {} - AA: {} - Y: {} - P: {}",
|
||||
file_entry.title,
|
||||
file_entry.artist,
|
||||
file_entry.album_title,
|
||||
file_entry.album_artist,
|
||||
file_entry.year,
|
||||
file_entry.path.display()
|
||||
);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
Common::print_time(start_time, SystemTime::now(), "print_entries".to_string());
|
||||
}
|
||||
}
|
|
@ -1097,6 +1097,165 @@ Author: Rafał Mikrut
|
|||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="notebook_main_same_music_finder">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Minimal file size(in bytes)</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry_same_music_minimal_size">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="max_length">15</property>
|
||||
<property name="text" translatable="yes">1024</property>
|
||||
<property name="caps_lock_warning">False</property>
|
||||
<property name="input_purpose">number</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="check_button_music_title">
|
||||
<property name="label" translatable="yes">Title</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="check_button_music_artist">
|
||||
<property name="label" translatable="yes">Artist</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="check_button_music_album_title">
|
||||
<property name="label" translatable="yes">Album Title</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="check_button_music_album_artist">
|
||||
<property name="label" translatable="yes">Album Artist</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="check_button_music_year">
|
||||
<property name="label" translatable="yes">Year</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolled_window_same_music_finder">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Music Duplicates</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">6</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="notebook_main_zeroed_files_finder">
|
||||
<property name="visible">True</property>
|
||||
|
@ -1158,7 +1317,7 @@ Author: Rafał Mikrut
|
|||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">6</property>
|
||||
<property name="position">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
|
@ -1168,7 +1327,7 @@ Author: Rafał Mikrut
|
|||
<property name="label" translatable="yes">Zeroed Files</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">6</property>
|
||||
<property name="position">7</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
|
|
@ -19,6 +19,7 @@ pub fn connect_button_delete(gui_data: &GuiData) {
|
|||
let scrolled_window_main_temporary_files_finder = gui_data.scrolled_window_main_temporary_files_finder.clone();
|
||||
let scrolled_window_similar_images_finder = gui_data.scrolled_window_similar_images_finder.clone();
|
||||
let scrolled_window_zeroed_files_finder = gui_data.scrolled_window_zeroed_files_finder.clone();
|
||||
let scrolled_window_same_music_finder = gui_data.scrolled_window_same_music_finder.clone();
|
||||
|
||||
buttons_delete.connect_clicked(move |_| {
|
||||
if *shared_confirmation_dialog_delete_dialog_showing_state.borrow_mut() {
|
||||
|
@ -396,7 +397,38 @@ pub fn connect_button_delete(gui_data: &GuiData) {
|
|||
text_view_errors.get_buffer().unwrap().set_text(messages.as_str());
|
||||
selection.unselect_all();
|
||||
}
|
||||
"notebook_main_same_music_finder" => {
|
||||
let tree_view = scrolled_window_same_music_finder.get_children().get(0).unwrap().clone().downcast::<gtk::TreeView>().unwrap();
|
||||
let selection = tree_view.get_selection();
|
||||
|
||||
let (selection_rows, tree_model) = selection.get_selected_rows();
|
||||
if selection_rows.is_empty() {
|
||||
return;
|
||||
}
|
||||
let list_store = tree_model.clone().downcast::<gtk::ListStore>().unwrap();
|
||||
|
||||
// let new_tree_model = TreeModel::new(); // TODO - maybe create new model when inserting a new data, because this seems to be not optimal when using thousands of rows
|
||||
|
||||
let mut messages: String = "".to_string();
|
||||
|
||||
// Must be deleted from end to start, because when deleting entries, TreePath(and also TreeIter) will points to invalid data
|
||||
for tree_path in selection_rows.iter().rev() {
|
||||
let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsSameMusic::Name as i32).get::<String>().unwrap().unwrap();
|
||||
let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsSameMusic::Path as i32).get::<String>().unwrap().unwrap();
|
||||
|
||||
match fs::remove_file(format!("{}/{}", path, name)) {
|
||||
Ok(_) => {
|
||||
list_store.remove(&list_store.get_iter(tree_path).unwrap());
|
||||
}
|
||||
Err(_) => messages += format!("Failed to remove file {}/{} because file doesn't exists or you don't have permissions.\n", path, name).as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
text_view_errors.get_buffer().unwrap().set_text(messages.as_str());
|
||||
selection.unselect_all();
|
||||
}
|
||||
e => panic!("Not existent {}", e),
|
||||
}
|
||||
});
|
||||
}
|
||||
// fn basic_remove(tree_view: gtk::TreeView, column_name: i32, column_path: i32) {} // TODO, will replace simple remove of things
|
||||
|
|
|
@ -14,6 +14,7 @@ pub fn connect_button_save(gui_data: &GuiData) {
|
|||
let shared_temporary_files_state = gui_data.shared_temporary_files_state.clone();
|
||||
let shared_empty_files_state = gui_data.shared_empty_files_state.clone();
|
||||
let shared_similar_images_state = gui_data.shared_similar_images_state.clone();
|
||||
let shared_same_music_state = gui_data.shared_same_music_state.clone();
|
||||
let shared_zeroed_files_state = gui_data.shared_zeroed_files_state.clone();
|
||||
let notebook_main_children_names = gui_data.notebook_main_children_names.clone();
|
||||
let notebook_main = gui_data.notebook_main.clone();
|
||||
|
@ -96,7 +97,7 @@ pub fn connect_button_save(gui_data: &GuiData) {
|
|||
*shared_buttons.borrow_mut().get_mut("similar_images").unwrap().get_mut("save").unwrap() = false;
|
||||
}
|
||||
}
|
||||
"notebook_main_zeroed_files_finder_label" => {
|
||||
"notebook_main_zeroed_files_finder" => {
|
||||
let file_name = "results_zeroed_files.txt";
|
||||
|
||||
let mut zf = shared_zeroed_files_state.borrow_mut();
|
||||
|
@ -109,6 +110,19 @@ pub fn connect_button_save(gui_data: &GuiData) {
|
|||
*shared_buttons.borrow_mut().get_mut("zeroed_files").unwrap().get_mut("save").unwrap() = false;
|
||||
}
|
||||
}
|
||||
"notebook_main_same_music_finder" => {
|
||||
let file_name = "results_same_music.txt";
|
||||
|
||||
let mut mf = shared_same_music_state.borrow_mut();
|
||||
mf.save_results_to_file(file_name);
|
||||
|
||||
entry_info.set_text(format!("Saved results to file {}", file_name).as_str());
|
||||
// Set state
|
||||
{
|
||||
buttons_save.hide();
|
||||
*shared_buttons.borrow_mut().get_mut("zeroed_files").unwrap().get_mut("save").unwrap() = false;
|
||||
}
|
||||
}
|
||||
e => panic!("Not existent {}", e),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use czkawka_core::big_file::BigFile;
|
|||
use czkawka_core::duplicate::DuplicateFinder;
|
||||
use czkawka_core::empty_files::EmptyFiles;
|
||||
use czkawka_core::empty_folder::EmptyFolder;
|
||||
use czkawka_core::same_music::{MusicSimilarity, SameMusic};
|
||||
use czkawka_core::similar_files::SimilarImages;
|
||||
use czkawka_core::temporary::Temporary;
|
||||
use czkawka_core::zeroed::ZeroedFiles;
|
||||
|
@ -24,6 +25,7 @@ pub fn connect_button_search(gui_data: &GuiData, sender: Sender<Message>) {
|
|||
let buttons_array = gui_data.buttons_array.clone();
|
||||
let check_button_recursive = gui_data.check_button_recursive.clone();
|
||||
let entry_excluded_items = gui_data.entry_excluded_items.clone();
|
||||
let entry_same_music_minimal_size = gui_data.entry_same_music_minimal_size.clone();
|
||||
let entry_allowed_extensions = gui_data.entry_allowed_extensions.clone();
|
||||
let buttons_names = gui_data.buttons_names.clone();
|
||||
let radio_button_name = gui_data.radio_button_name.clone();
|
||||
|
@ -31,10 +33,16 @@ pub fn connect_button_search(gui_data: &GuiData, sender: Sender<Message>) {
|
|||
let radio_button_hashmb = gui_data.radio_button_hashmb.clone();
|
||||
let radio_button_hash = gui_data.radio_button_hash.clone();
|
||||
let entry_duplicate_minimal_size = gui_data.entry_duplicate_minimal_size.clone();
|
||||
// let sender = gui_data.sender.clone();
|
||||
let rx = gui_data.rx.clone();
|
||||
let entry_big_files_number = gui_data.entry_big_files_number.clone();
|
||||
let entry_similar_images_minimal_size = gui_data.entry_similar_images_minimal_size.clone();
|
||||
let check_button_music_title: gtk::CheckButton = gui_data.check_button_music_title.clone();
|
||||
let check_button_music_artist: gtk::CheckButton = gui_data.check_button_music_artist.clone();
|
||||
let check_button_music_album_title: gtk::CheckButton = gui_data.check_button_music_album_title.clone();
|
||||
let check_button_music_album_artist: gtk::CheckButton = gui_data.check_button_music_album_artist.clone();
|
||||
let check_button_music_year: gtk::CheckButton = gui_data.check_button_music_year.clone();
|
||||
let shared_buttons = gui_data.shared_buttons.clone();
|
||||
|
||||
buttons_search_clone.connect_clicked(move |_| {
|
||||
let included_directories = get_string_from_list_store(&scrolled_window_included_directories);
|
||||
let excluded_directories = get_string_from_list_store(&scrolled_window_excluded_directories);
|
||||
|
@ -82,7 +90,7 @@ pub fn connect_button_search(gui_data: &GuiData, sender: Sender<Message>) {
|
|||
df.set_minimal_file_size(minimal_file_size);
|
||||
df.set_check_method(check_method);
|
||||
df.set_delete_method(delete_method);
|
||||
df.find_duplicates(Option::from(&receiver_stop)); //&rc_stop_signal.borrow().1);
|
||||
df.find_duplicates(Option::from(&receiver_stop));
|
||||
let _ = sender.send(Message::Duplicates(df));
|
||||
});
|
||||
}
|
||||
|
@ -94,7 +102,6 @@ pub fn connect_button_search(gui_data: &GuiData, sender: Sender<Message>) {
|
|||
thread::spawn(move || {
|
||||
let mut ef = EmptyFolder::new();
|
||||
ef.set_included_directory(included_directories);
|
||||
ef.set_delete_folder(false);
|
||||
ef.find_empty_folders(Option::from(&receiver_stop));
|
||||
let _ = sender.send(Message::EmptyFolders(ef));
|
||||
});
|
||||
|
@ -181,7 +188,7 @@ pub fn connect_button_search(gui_data: &GuiData, sender: Sender<Message>) {
|
|||
let sender = sender.clone();
|
||||
let receiver_stop = rx.clone();
|
||||
|
||||
// Find temporary files
|
||||
// Find zeroed files
|
||||
thread::spawn(move || {
|
||||
let mut zf = ZeroedFiles::new();
|
||||
|
||||
|
@ -189,10 +196,57 @@ pub fn connect_button_search(gui_data: &GuiData, sender: Sender<Message>) {
|
|||
zf.set_excluded_directory(excluded_directories);
|
||||
zf.set_recursive_search(recursive_search);
|
||||
zf.set_excluded_items(excluded_items);
|
||||
zf.set_allowed_extensions(allowed_extensions);
|
||||
zf.find_zeroed_files(Option::from(&receiver_stop));
|
||||
let _ = sender.send(Message::ZeroedFiles(zf));
|
||||
});
|
||||
}
|
||||
"notebook_main_same_music_finder" => {
|
||||
let minimal_file_size = match entry_same_music_minimal_size.get_text().as_str().parse::<u64>() {
|
||||
Ok(t) => t,
|
||||
Err(_) => 1024, // By default
|
||||
};
|
||||
let mut music_similarity: MusicSimilarity = MusicSimilarity::NONE;
|
||||
|
||||
if check_button_music_title.get_active() {
|
||||
music_similarity |= MusicSimilarity::TITLE;
|
||||
}
|
||||
if check_button_music_artist.get_active() {
|
||||
music_similarity |= MusicSimilarity::ARTIST;
|
||||
}
|
||||
if check_button_music_album_title.get_active() {
|
||||
music_similarity |= MusicSimilarity::ALBUM_TITLE;
|
||||
}
|
||||
if check_button_music_album_artist.get_active() {
|
||||
music_similarity |= MusicSimilarity::ALBUM_ARTIST;
|
||||
}
|
||||
if check_button_music_year.get_active() {
|
||||
music_similarity |= MusicSimilarity::YEAR;
|
||||
}
|
||||
|
||||
if music_similarity != MusicSimilarity::NONE {
|
||||
let sender = sender.clone();
|
||||
let receiver_stop = rx.clone();
|
||||
|
||||
// Find temporary files
|
||||
thread::spawn(move || {
|
||||
let mut mf = SameMusic::new();
|
||||
|
||||
mf.set_included_directory(included_directories);
|
||||
mf.set_excluded_directory(excluded_directories);
|
||||
mf.set_excluded_items(excluded_items);
|
||||
mf.set_minimal_file_size(minimal_file_size);
|
||||
mf.set_recursive_search(recursive_search);
|
||||
mf.set_music_similarity(music_similarity);
|
||||
mf.find_same_music(Option::from(&receiver_stop));
|
||||
let _ = sender.send(Message::SameMusic(mf));
|
||||
});
|
||||
} else {
|
||||
notebook_main.set_sensitive(true);
|
||||
set_buttons(&mut *shared_buttons.borrow_mut().get_mut("same_music").unwrap(), &buttons_array, &buttons_names);
|
||||
entry_info.set_text("ERROR: You must select at least one checkbox with music searching types.");
|
||||
}
|
||||
}
|
||||
e => panic!("Not existent {}", e),
|
||||
}
|
||||
});
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::gui_data::GuiData;
|
|||
use crate::help_functions::*;
|
||||
use chrono::NaiveDateTime;
|
||||
use czkawka_core::duplicate::CheckingMethod;
|
||||
use czkawka_core::same_music::MusicSimilarity;
|
||||
use glib::Receiver;
|
||||
use gtk::prelude::*;
|
||||
|
||||
|
@ -30,6 +31,8 @@ pub fn connect_compute_results(gui_data: &GuiData, receiver: Receiver<Message>)
|
|||
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_zeroed_files_state = gui_data.shared_zeroed_files_state.clone();
|
||||
let scrolled_window_same_music_finder = gui_data.scrolled_window_same_music_finder.clone();
|
||||
let shared_same_music_state = gui_data.shared_same_music_state.clone();
|
||||
let buttons_names = gui_data.buttons_names.clone();
|
||||
|
||||
receiver.attach(None, move |msg| {
|
||||
|
@ -586,7 +589,6 @@ pub fn connect_compute_results(gui_data: &GuiData, receiver: Receiver<Message>)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Message::ZeroedFiles(zf) => {
|
||||
if zf.get_stopped_search() {
|
||||
entry_info.set_text("Searching for zeroed files was stopped by user");
|
||||
|
@ -659,6 +661,128 @@ pub fn connect_compute_results(gui_data: &GuiData, receiver: Receiver<Message>)
|
|||
}
|
||||
}
|
||||
}
|
||||
Message::SameMusic(mf) => {
|
||||
if mf.get_stopped_search() {
|
||||
entry_info.set_text("Searching for empty files was stopped by user");
|
||||
|
||||
//Also clear list
|
||||
scrolled_window_same_music_finder
|
||||
.get_children()
|
||||
.get(0)
|
||||
.unwrap()
|
||||
.clone()
|
||||
.downcast::<gtk::TreeView>()
|
||||
.unwrap()
|
||||
.get_model()
|
||||
.unwrap()
|
||||
.downcast::<gtk::ListStore>()
|
||||
.unwrap()
|
||||
.clear();
|
||||
} else {
|
||||
let information = mf.get_information();
|
||||
let text_messages = mf.get_text_messages();
|
||||
|
||||
let same_music_number: usize = information.number_of_duplicates_music_files;
|
||||
|
||||
entry_info.set_text(format!("Found {} duplicated music files.", same_music_number).as_str());
|
||||
|
||||
// Create GUI
|
||||
{
|
||||
let list_store = scrolled_window_same_music_finder
|
||||
.get_children()
|
||||
.get(0)
|
||||
.unwrap()
|
||||
.clone()
|
||||
.downcast::<gtk::TreeView>()
|
||||
.unwrap()
|
||||
.get_model()
|
||||
.unwrap()
|
||||
.downcast::<gtk::ListStore>()
|
||||
.unwrap();
|
||||
list_store.clear();
|
||||
|
||||
let col_indices = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
||||
|
||||
let vector = mf.get_duplicated_music_entries();
|
||||
|
||||
let music_similarity = *mf.get_music_similarity();
|
||||
|
||||
let is_title = (MusicSimilarity::TITLE & music_similarity) != MusicSimilarity::NONE;
|
||||
let is_artist = (MusicSimilarity::ARTIST & music_similarity) != MusicSimilarity::NONE;
|
||||
let is_album_title = (MusicSimilarity::ALBUM_TITLE & music_similarity) != MusicSimilarity::NONE;
|
||||
let is_album_artist = (MusicSimilarity::ALBUM_ARTIST & music_similarity) != MusicSimilarity::NONE;
|
||||
let is_year = (MusicSimilarity::YEAR & music_similarity) != MusicSimilarity::NONE;
|
||||
|
||||
let text: String = "-----".to_string();
|
||||
|
||||
for vec_file_entry in vector {
|
||||
let values: [&dyn ToValue; 12] = [
|
||||
&"".to_string(),
|
||||
&"".to_string(),
|
||||
&"".to_string(),
|
||||
&(match is_title {
|
||||
true => text.clone(),
|
||||
false => "".to_string(),
|
||||
}),
|
||||
&(match is_artist {
|
||||
true => text.clone(),
|
||||
false => "".to_string(),
|
||||
}),
|
||||
&(match is_album_title {
|
||||
true => text.clone(),
|
||||
false => "".to_string(),
|
||||
}),
|
||||
&(match is_album_artist {
|
||||
true => text.clone(),
|
||||
false => "".to_string(),
|
||||
}),
|
||||
&(match is_year {
|
||||
true => text.clone(),
|
||||
false => "".to_string(),
|
||||
}),
|
||||
&"".to_string(),
|
||||
&"".to_string(),
|
||||
&(HEADER_ROW_COLOR.to_string()),
|
||||
&(TEXT_COLOR.to_string()),
|
||||
];
|
||||
list_store.set(&list_store.append(), &col_indices, &values);
|
||||
for file_entry in vec_file_entry {
|
||||
let (directory, file) = split_path(&file_entry.path);
|
||||
let values: [&dyn ToValue; 12] = [
|
||||
&file_entry.size.file_size(options::BINARY).unwrap(),
|
||||
&file,
|
||||
&directory,
|
||||
&file_entry.title,
|
||||
&file_entry.artist,
|
||||
&file_entry.album_title,
|
||||
&file_entry.album_artist,
|
||||
&file_entry.year.to_string(),
|
||||
&(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string()),
|
||||
&(file_entry.modified_date),
|
||||
&(MAIN_ROW_COLOR.to_string()),
|
||||
&(TEXT_COLOR.to_string()),
|
||||
];
|
||||
list_store.set(&list_store.append(), &col_indices, &values);
|
||||
}
|
||||
}
|
||||
print_text_messages_to_text_view(text_messages, &text_view_errors);
|
||||
}
|
||||
|
||||
// Set state
|
||||
{
|
||||
*shared_same_music_state.borrow_mut() = mf;
|
||||
|
||||
if same_music_number > 0 {
|
||||
*shared_buttons.borrow_mut().get_mut("same_music").unwrap().get_mut("save").unwrap() = true;
|
||||
*shared_buttons.borrow_mut().get_mut("same_music").unwrap().get_mut("delete").unwrap() = true;
|
||||
} else {
|
||||
*shared_buttons.borrow_mut().get_mut("same_music").unwrap().get_mut("save").unwrap() = false;
|
||||
*shared_buttons.borrow_mut().get_mut("same_music").unwrap().get_mut("delete").unwrap() = false;
|
||||
}
|
||||
set_buttons(&mut *shared_buttons.borrow_mut().get_mut("same_music").unwrap(), &buttons_array, &buttons_names);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Returning false here would close the receiver and have senders fail
|
||||
glib::Continue(true)
|
||||
|
|
|
@ -29,6 +29,7 @@ pub fn connect_notebook_tabs(gui_data: &GuiData) {
|
|||
"notebook_big_main_file_finder" => page = "big_file",
|
||||
"notebook_main_similar_images_finder_label" => page = "similar_images",
|
||||
"notebook_main_zeroed_files_finder" => page = "zeroed_files",
|
||||
"notebook_main_same_music_finder" => page = "same_music",
|
||||
e => {
|
||||
panic!("Not existent page {}", e);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ pub fn create_tree_view_duplicates(tree_view: &mut gtk::TreeView) {
|
|||
column.pack_start(&renderer, true);
|
||||
column.set_title("Path");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(100);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsDuplicates::Path as i32);
|
||||
column.add_attribute(&renderer, "background", ColumnsDuplicates::Color as i32);
|
||||
column.add_attribute(&renderer, "foreground", ColumnsDuplicates::TextColor as i32);
|
||||
|
@ -30,7 +30,7 @@ pub fn create_tree_view_duplicates(tree_view: &mut gtk::TreeView) {
|
|||
column.pack_start(&renderer, true);
|
||||
column.set_title("Modification Date");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(100);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsDuplicates::Modification as i32);
|
||||
column.add_attribute(&renderer, "background", ColumnsDuplicates::Color as i32);
|
||||
column.add_attribute(&renderer, "foreground", ColumnsDuplicates::TextColor as i32);
|
||||
|
@ -54,7 +54,7 @@ pub fn create_tree_view_empty_folders(tree_view: &mut gtk::TreeView) {
|
|||
column.pack_start(&renderer, true);
|
||||
column.set_title("Path");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(100);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsEmptyFolders::Path as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
|
@ -63,7 +63,7 @@ pub fn create_tree_view_empty_folders(tree_view: &mut gtk::TreeView) {
|
|||
column.pack_start(&renderer, true);
|
||||
column.set_title("Modification Date");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(100);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsEmptyFolders::Modification as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
|
@ -94,7 +94,7 @@ pub fn create_tree_view_big_files(tree_view: &mut gtk::TreeView) {
|
|||
column.pack_start(&renderer, true);
|
||||
column.set_title("Path");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(100);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsBigFiles::Path as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
|
@ -103,7 +103,7 @@ pub fn create_tree_view_big_files(tree_view: &mut gtk::TreeView) {
|
|||
column.pack_start(&renderer, true);
|
||||
column.set_title("Modification Date");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(100);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsBigFiles::Modification as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
|
@ -125,7 +125,7 @@ pub fn create_tree_view_temporary_files(tree_view: &mut gtk::TreeView) {
|
|||
column.pack_start(&renderer, true);
|
||||
column.set_title("Path");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(100);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsTemporaryFiles::Path as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
|
@ -134,7 +134,7 @@ pub fn create_tree_view_temporary_files(tree_view: &mut gtk::TreeView) {
|
|||
column.pack_start(&renderer, true);
|
||||
column.set_title("Modification Date");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(100);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsTemporaryFiles::Modification as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
|
@ -156,7 +156,7 @@ pub fn create_tree_view_empty_files(tree_view: &mut gtk::TreeView) {
|
|||
column.pack_start(&renderer, true);
|
||||
column.set_title("Path");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(100);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsEmptyFiles::Path as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
|
@ -165,7 +165,7 @@ pub fn create_tree_view_empty_files(tree_view: &mut gtk::TreeView) {
|
|||
column.pack_start(&renderer, true);
|
||||
column.set_title("Modification Date");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(100);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsEmptyFiles::Modification as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
|
@ -222,7 +222,7 @@ pub fn create_tree_view_similar_images(tree_view: &mut gtk::TreeView) {
|
|||
column.pack_start(&renderer, true);
|
||||
column.set_title("Path");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(100);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsSimilarImages::Path as i32);
|
||||
column.add_attribute(&renderer, "background", ColumnsSimilarImages::Color as i32);
|
||||
column.add_attribute(&renderer, "foreground", ColumnsSimilarImages::TextColor as i32);
|
||||
|
@ -233,7 +233,7 @@ pub fn create_tree_view_similar_images(tree_view: &mut gtk::TreeView) {
|
|||
column.pack_start(&renderer, true);
|
||||
column.set_title("Modification Date");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(100);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsSimilarImages::Modification as i32);
|
||||
column.add_attribute(&renderer, "background", ColumnsSimilarImages::Color as i32);
|
||||
column.add_attribute(&renderer, "foreground", ColumnsSimilarImages::TextColor as i32);
|
||||
|
@ -276,7 +276,7 @@ pub fn create_tree_view_zeroed_files(tree_view: &mut gtk::TreeView) {
|
|||
column.pack_start(&renderer, true);
|
||||
column.set_title("Path");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(100);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsZeroedFiles::Path as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
|
@ -285,9 +285,112 @@ pub fn create_tree_view_zeroed_files(tree_view: &mut gtk::TreeView) {
|
|||
column.pack_start(&renderer, true);
|
||||
column.set_title("Modification Date");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(100);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsZeroedFiles::Modification as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
tree_view.set_vexpand(true);
|
||||
}
|
||||
|
||||
pub fn create_tree_view_same_music(tree_view: &mut gtk::TreeView) {
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
let column: gtk::TreeViewColumn = TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.set_title("Size");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsSameMusic::Size as i32);
|
||||
column.add_attribute(&renderer, "background", ColumnsSameMusic::Color as i32);
|
||||
column.add_attribute(&renderer, "foreground", ColumnsSameMusic::TextColor as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
let column: gtk::TreeViewColumn = TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.set_title("File Name");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsSameMusic::Name as i32);
|
||||
column.add_attribute(&renderer, "background", ColumnsSameMusic::Color as i32);
|
||||
column.add_attribute(&renderer, "foreground", ColumnsSameMusic::TextColor as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
let column: gtk::TreeViewColumn = TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.set_title("Path");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsSameMusic::Path as i32);
|
||||
column.add_attribute(&renderer, "background", ColumnsSameMusic::Color as i32);
|
||||
column.add_attribute(&renderer, "foreground", ColumnsSameMusic::TextColor as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
let column: gtk::TreeViewColumn = TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.set_title("Title");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsSameMusic::Title as i32);
|
||||
column.add_attribute(&renderer, "background", ColumnsSameMusic::Color as i32);
|
||||
column.add_attribute(&renderer, "foreground", ColumnsSameMusic::TextColor as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
let column: gtk::TreeViewColumn = TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.set_title("Artist");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsSameMusic::Artist as i32);
|
||||
column.add_attribute(&renderer, "background", ColumnsSameMusic::Color as i32);
|
||||
column.add_attribute(&renderer, "foreground", ColumnsSameMusic::TextColor as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
let column: gtk::TreeViewColumn = TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.set_title("Album Title");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsSameMusic::AlbumTitle as i32);
|
||||
column.add_attribute(&renderer, "background", ColumnsSameMusic::Color as i32);
|
||||
column.add_attribute(&renderer, "foreground", ColumnsSameMusic::TextColor as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
let column: gtk::TreeViewColumn = TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.set_title("Album Artist");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsSameMusic::AlbumArtist as i32);
|
||||
column.add_attribute(&renderer, "background", ColumnsSameMusic::Color as i32);
|
||||
column.add_attribute(&renderer, "foreground", ColumnsSameMusic::TextColor as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
let column: gtk::TreeViewColumn = TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.set_title("Year");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsSameMusic::Year as i32);
|
||||
column.add_attribute(&renderer, "background", ColumnsSameMusic::Color as i32);
|
||||
column.add_attribute(&renderer, "foreground", ColumnsSameMusic::TextColor as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
let column: gtk::TreeViewColumn = TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.set_title("Modification Date");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsSameMusic::Modification as i32);
|
||||
column.add_attribute(&renderer, "background", ColumnsSameMusic::Color as i32);
|
||||
column.add_attribute(&renderer, "foreground", ColumnsSameMusic::TextColor as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
tree_view.set_vexpand(true);
|
||||
}
|
||||
|
|
|
@ -117,3 +117,20 @@ pub fn opening_double_click_function_zeroed_files(tree_view: >k::TreeView, eve
|
|||
}
|
||||
gtk::Inhibit(false)
|
||||
}
|
||||
|
||||
pub fn opening_double_click_function_same_music(tree_view: >k::TreeView, event: &gdk::EventButton) -> gtk::Inhibit {
|
||||
if event.get_event_type() == gdk::EventType::DoubleButtonPress {
|
||||
let selection = tree_view.get_selection();
|
||||
let (selection_rows, tree_model) = selection.get_selected_rows();
|
||||
|
||||
for tree_path in selection_rows.iter().rev() {
|
||||
let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsSameMusic::Name as i32).get::<String>().unwrap().unwrap();
|
||||
let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsSameMusic::Path as i32).get::<String>().unwrap().unwrap();
|
||||
|
||||
if open::that(format!("{}/{}", path, name)).is_err() {
|
||||
println!("Failed to open {}/{}", path, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
gtk::Inhibit(false)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
extern crate gdk;
|
||||
extern crate gtk;
|
||||
use crossbeam_channel::unbounded;
|
||||
use czkawka_core::big_file::BigFile;
|
||||
use czkawka_core::duplicate::DuplicateFinder;
|
||||
use czkawka_core::empty_files::EmptyFiles;
|
||||
use czkawka_core::empty_folder::EmptyFolder;
|
||||
use czkawka_core::same_music::SameMusic;
|
||||
use czkawka_core::similar_files::SimilarImages;
|
||||
use czkawka_core::temporary::Temporary;
|
||||
use czkawka_core::zeroed::ZeroedFiles;
|
||||
|
@ -15,12 +17,15 @@ use std::rc::Rc;
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct GuiData {
|
||||
// Glade builder
|
||||
pub glade_src: String,
|
||||
pub builder: Builder,
|
||||
|
||||
// Windows
|
||||
pub window_main: gtk::Window,
|
||||
|
||||
// States
|
||||
pub main_notebooks_labels: [String; 7],
|
||||
pub main_notebooks_labels: [String; 8],
|
||||
pub upper_notebooks_labels: [String; 4],
|
||||
pub buttons_labels: [String; 7],
|
||||
// Buttons state
|
||||
|
@ -37,6 +42,7 @@ pub struct GuiData {
|
|||
pub shared_big_files_state: Rc<RefCell<BigFile>>,
|
||||
pub shared_similar_images_state: Rc<RefCell<SimilarImages>>,
|
||||
pub shared_zeroed_files_state: Rc<RefCell<ZeroedFiles>>,
|
||||
pub shared_same_music_state: Rc<RefCell<SameMusic>>,
|
||||
|
||||
// State of confirmation dialogs
|
||||
pub shared_confirmation_dialog_delete_dialog_showing_state: Rc<RefCell<bool>>,
|
||||
|
@ -47,6 +53,7 @@ pub struct GuiData {
|
|||
pub entry_allowed_extensions: gtk::Entry,
|
||||
pub entry_excluded_items: gtk::Entry,
|
||||
pub entry_big_files_number: gtk::Entry,
|
||||
pub entry_same_music_minimal_size: gtk::Entry,
|
||||
|
||||
//// GUI Buttons
|
||||
pub buttons_search: gtk::Button,
|
||||
|
@ -78,7 +85,14 @@ pub struct GuiData {
|
|||
//// Check Buttons
|
||||
pub check_button_recursive: gtk::CheckButton,
|
||||
|
||||
pub check_button_music_title: gtk::CheckButton,
|
||||
pub check_button_music_artist: gtk::CheckButton,
|
||||
pub check_button_music_album_title: gtk::CheckButton,
|
||||
pub check_button_music_album_artist: gtk::CheckButton,
|
||||
pub check_button_music_year: gtk::CheckButton,
|
||||
|
||||
//// Radio Buttons
|
||||
// Duplicates
|
||||
pub radio_button_name: gtk::RadioButton,
|
||||
pub radio_button_size: gtk::RadioButton,
|
||||
pub radio_button_hashmb: gtk::RadioButton,
|
||||
|
@ -106,6 +120,7 @@ pub struct GuiData {
|
|||
pub scrolled_window_big_files_finder: gtk::ScrolledWindow,
|
||||
pub scrolled_window_similar_images_finder: gtk::ScrolledWindow,
|
||||
pub scrolled_window_zeroed_files_finder: gtk::ScrolledWindow,
|
||||
pub scrolled_window_same_music_finder: gtk::ScrolledWindow,
|
||||
|
||||
// Upper notebook
|
||||
pub scrolled_window_included_directories: gtk::ScrolledWindow,
|
||||
|
@ -139,6 +154,7 @@ impl GuiData {
|
|||
"big_file".to_string(),
|
||||
"similar_images".to_string(),
|
||||
"zeroed_files".to_string(),
|
||||
"same_music".to_string(),
|
||||
];
|
||||
let upper_notebooks_labels = [
|
||||
/*"general",*/ "included_directories".to_string(),
|
||||
|
@ -189,6 +205,7 @@ impl GuiData {
|
|||
let shared_big_files_state: Rc<RefCell<_>> = Rc::new(RefCell::new(BigFile::new()));
|
||||
let shared_similar_images_state: Rc<RefCell<_>> = Rc::new(RefCell::new(SimilarImages::new()));
|
||||
let shared_zeroed_files_state: Rc<RefCell<_>> = Rc::new(RefCell::new(ZeroedFiles::new()));
|
||||
let shared_same_music_state: Rc<RefCell<_>> = Rc::new(RefCell::new(SameMusic::new()));
|
||||
|
||||
// State of confirmation dialogs
|
||||
let shared_confirmation_dialog_delete_dialog_showing_state: Rc<RefCell<_>> = Rc::new(RefCell::new(true));
|
||||
|
@ -201,6 +218,7 @@ impl GuiData {
|
|||
let entry_allowed_extensions: gtk::Entry = builder.get_object("entry_allowed_extensions").unwrap();
|
||||
let entry_excluded_items: gtk::Entry = builder.get_object("entry_excluded_items").unwrap();
|
||||
let entry_big_files_number: gtk::Entry = builder.get_object("entry_big_files_number").unwrap();
|
||||
let entry_same_music_minimal_size: gtk::Entry = builder.get_object("entry_same_music_minimal_size").unwrap();
|
||||
|
||||
//// GUI Buttons
|
||||
let buttons_search: gtk::Button = builder.get_object("buttons_search").unwrap();
|
||||
|
@ -241,6 +259,11 @@ impl GuiData {
|
|||
|
||||
//// Check Buttons
|
||||
let check_button_recursive: gtk::CheckButton = builder.get_object("check_button_recursive").unwrap();
|
||||
let check_button_music_title: gtk::CheckButton = builder.get_object("check_button_music_title").unwrap();
|
||||
let check_button_music_artist: gtk::CheckButton = builder.get_object("check_button_music_artist").unwrap();
|
||||
let check_button_music_album_title: gtk::CheckButton = builder.get_object("check_button_music_album_title").unwrap();
|
||||
let check_button_music_album_artist: gtk::CheckButton = builder.get_object("check_button_music_album_artist").unwrap();
|
||||
let check_button_music_year: gtk::CheckButton = builder.get_object("check_button_music_year").unwrap();
|
||||
|
||||
//// Radio Buttons
|
||||
let radio_button_name: gtk::RadioButton = builder.get_object("radio_button_name").unwrap();
|
||||
|
@ -277,6 +300,7 @@ impl GuiData {
|
|||
let scrolled_window_big_files_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_big_files_finder").unwrap();
|
||||
let scrolled_window_similar_images_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_similar_images_finder").unwrap();
|
||||
let scrolled_window_zeroed_files_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_zeroed_files_finder").unwrap();
|
||||
let scrolled_window_same_music_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_same_music_finder").unwrap();
|
||||
|
||||
// Upper notebook
|
||||
let scrolled_window_included_directories: gtk::ScrolledWindow = builder.get_object("scrolled_window_included_directories").unwrap();
|
||||
|
@ -304,12 +328,14 @@ impl GuiData {
|
|||
shared_big_files_state,
|
||||
shared_similar_images_state,
|
||||
shared_zeroed_files_state,
|
||||
shared_same_music_state,
|
||||
shared_confirmation_dialog_delete_dialog_showing_state,
|
||||
entry_similar_images_minimal_size,
|
||||
entry_duplicate_minimal_size,
|
||||
entry_allowed_extensions,
|
||||
entry_excluded_items,
|
||||
entry_big_files_number,
|
||||
entry_same_music_minimal_size,
|
||||
buttons_search,
|
||||
buttons_stop,
|
||||
buttons_resume,
|
||||
|
@ -332,6 +358,11 @@ impl GuiData {
|
|||
buttons_popover_select_one_newest,
|
||||
popover_select,
|
||||
check_button_recursive,
|
||||
check_button_music_title,
|
||||
check_button_music_artist,
|
||||
check_button_music_album_title,
|
||||
check_button_music_album_artist,
|
||||
check_button_music_year,
|
||||
radio_button_name,
|
||||
radio_button_size,
|
||||
radio_button_hashmb,
|
||||
|
@ -349,6 +380,7 @@ impl GuiData {
|
|||
scrolled_window_big_files_finder,
|
||||
scrolled_window_similar_images_finder,
|
||||
scrolled_window_zeroed_files_finder,
|
||||
scrolled_window_same_music_finder,
|
||||
scrolled_window_included_directories,
|
||||
scrolled_window_excluded_directories,
|
||||
sx,
|
||||
|
|
|
@ -3,6 +3,7 @@ use czkawka_core::common_messages::Messages;
|
|||
use czkawka_core::duplicate::DuplicateFinder;
|
||||
use czkawka_core::empty_files::EmptyFiles;
|
||||
use czkawka_core::empty_folder::EmptyFolder;
|
||||
use czkawka_core::same_music::SameMusic;
|
||||
use czkawka_core::similar_files::{SimilarImages, Similarity};
|
||||
use czkawka_core::temporary::Temporary;
|
||||
use czkawka_core::zeroed::ZeroedFiles;
|
||||
|
@ -18,6 +19,7 @@ pub enum Message {
|
|||
Temporary(Temporary),
|
||||
SimilarImages(SimilarImages),
|
||||
ZeroedFiles(ZeroedFiles),
|
||||
SameMusic(SameMusic),
|
||||
}
|
||||
|
||||
pub enum ColumnsDuplicates {
|
||||
|
@ -72,6 +74,20 @@ pub enum ColumnsZeroedFiles {
|
|||
Path,
|
||||
Modification,
|
||||
}
|
||||
pub enum ColumnsSameMusic {
|
||||
Size = 0,
|
||||
Name,
|
||||
Path,
|
||||
Title,
|
||||
Artist,
|
||||
AlbumTitle,
|
||||
AlbumArtist,
|
||||
Year,
|
||||
Modification,
|
||||
_ModificationAsSecs,
|
||||
Color,
|
||||
TextColor,
|
||||
}
|
||||
|
||||
pub const TEXT_COLOR: &str = "#ffffff";
|
||||
pub const MAIN_ROW_COLOR: &str = "#343434";
|
||||
|
@ -157,6 +173,15 @@ pub fn select_function_duplicates(_tree_selection: >k::TreeSelection, tree_mod
|
|||
|
||||
true
|
||||
}
|
||||
pub fn select_function_same_music(_tree_selection: >k::TreeSelection, tree_model: >k::TreeModel, tree_path: >k::TreePath, _is_path_currently_selected: bool) -> bool {
|
||||
let color = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsSameMusic::Color as i32).get::<String>().unwrap().unwrap();
|
||||
|
||||
if color == HEADER_ROW_COLOR {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn set_buttons(hashmap: &mut HashMap<String, bool>, buttons_array: &[gtk::Button], button_names: &[String]) {
|
||||
for (index, button) in buttons_array.iter().enumerate() {
|
||||
|
|
|
@ -23,6 +23,7 @@ pub fn startup_configuration(gui_data: &GuiData) {
|
|||
let scrolled_window_main_temporary_files_finder = gui_data.scrolled_window_main_temporary_files_finder.clone();
|
||||
let scrolled_window_big_files_finder = gui_data.scrolled_window_big_files_finder.clone();
|
||||
let scrolled_window_similar_images_finder = gui_data.scrolled_window_similar_images_finder.clone();
|
||||
let scrolled_window_same_music_finder = gui_data.scrolled_window_same_music_finder.clone();
|
||||
let scrolled_window_zeroed_files_finder = gui_data.scrolled_window_zeroed_files_finder.clone();
|
||||
let scrolled_window_included_directories = gui_data.scrolled_window_included_directories.clone();
|
||||
let scrolled_window_excluded_directories = gui_data.scrolled_window_excluded_directories.clone();
|
||||
|
@ -171,6 +172,36 @@ pub fn startup_configuration(gui_data: &GuiData) {
|
|||
scrolled_window_zeroed_files_finder.add(&tree_view);
|
||||
scrolled_window_zeroed_files_finder.show_all();
|
||||
}
|
||||
// Same Files
|
||||
{
|
||||
let col_types: [glib::types::Type; 12] = [
|
||||
glib::types::Type::String,
|
||||
glib::types::Type::String,
|
||||
glib::types::Type::String,
|
||||
glib::types::Type::String,
|
||||
glib::types::Type::String,
|
||||
glib::types::Type::String,
|
||||
glib::types::Type::String,
|
||||
glib::types::Type::String,
|
||||
glib::types::Type::String,
|
||||
glib::types::Type::String,
|
||||
glib::types::Type::String,
|
||||
glib::types::Type::String,
|
||||
];
|
||||
let list_store: gtk::ListStore = gtk::ListStore::new(&col_types);
|
||||
|
||||
let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store);
|
||||
|
||||
tree_view.get_selection().set_mode(SelectionMode::Multiple);
|
||||
|
||||
create_tree_view_same_music(&mut tree_view);
|
||||
tree_view.get_selection().set_select_function(Some(Box::new(select_function_same_music)));
|
||||
|
||||
tree_view.connect_button_press_event(opening_double_click_function_same_music);
|
||||
|
||||
scrolled_window_same_music_finder.add(&tree_view);
|
||||
scrolled_window_same_music_finder.show_all();
|
||||
}
|
||||
}
|
||||
|
||||
// Set Included Directory
|
||||
|
|
Loading…
Reference in a new issue