Repository: qarmin/czkawka (language: rust)

Issue:
Visible dupes not getting detected
I found some files, which couldn't be detected no matter the setting even though they are clearly visible dupes. It's thousands of files, which are like this. Potential bug?

I uploaded some of those random pictures to a file sharing service so it's not changed by github, maybe you guys can take a look for yourself - very intriguing!

https://easyupload.io/m/d6x8ox
pw = admin123


Discussion / hints:
The problem in that pictures is caused by using rotate property in exif data(one in group).

GTK/Image-rs seems to not care about exif data, so looks that I will need to check for this property manually
This seems a duplicate of issue #675. Another important missing feature: in addition to rotated images, **flipped images are not detected**.

Fix patch:
diff --git a/.github/workflows/minimial_version.yml b/.github/workflows/minimial_version.yml
index 5ab7f24af..7c26036b0 100644
--- a/.github/workflows/minimial_version.yml
+++ b/.github/workflows/minimial_version.yml
@@ -18,7 +18,7 @@ jobs:
         run: sudo apt update || true; sudo apt install libgtk-4-dev libheif-dev libraw-dev ffmpeg -y
 
       - name: Setup rust version
-        run: rustup default 1.79.0
+        run: rustup default 1.80.0
 
       - name: Build
         run: |
diff --git a/Cargo.lock b/Cargo.lock
index a23c31d85..e1e42059b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -178,6 +178,15 @@ version = "1.0.89"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
 
+[[package]]
+name = "approx"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
+dependencies = [
+ "num-traits",
+]
+
 [[package]]
 name = "arbitrary"
 version = "1.3.2"
@@ -1355,6 +1364,7 @@ dependencies = [
  "lofty",
  "log",
  "mime_guess",
+ "nom-exif",
  "once_cell",
  "os_info",
  "pdf",
@@ -2318,6 +2328,17 @@ dependencies = [
  "version_check",
 ]
 
+[[package]]
+name = "geo-types"
+version = "0.7.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ff16065e5720f376fbced200a5ae0f47ace85fd70b7e54269790281353b6d61"
+dependencies = [
+ "approx",
+ "num-traits",
+ "serde",
+]
+
 [[package]]
 name = "gethostname"
 version = "0.4.3"
@@ -3364,6 +3385,16 @@ version = "1.70.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
 
+[[package]]
+name = "iso6709parse"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e8731493611ee073436908f372020038da3715865a68d024f4fa3d800f13ac7"
+dependencies = [
+ "geo-types",
+ "nom",
+]
+
 [[package]]
 name = "istring"
 version = "0.3.4"
@@ -4156,6 +4187,21 @@ dependencies = [
  "minimal-lexical",
 ]
 
+[[package]]
+name = "nom-exif"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de573fe84056ba8233f8f541aed143d98587e7986b8da2220acf463cd181a7f"
+dependencies = [
+ "bytes",
+ "chrono",
+ "iso6709parse",
+ "nom",
+ "regex",
+ "thiserror",
+ "tracing",
+]
+
 [[package]]
 name = "noop_proc_macro"
 version = "0.3.0"
diff --git a/czkawka_cli/Cargo.toml b/czkawka_cli/Cargo.toml
index c93db852c..da588d02c 100644
--- a/czkawka_cli/Cargo.toml
+++ b/czkawka_cli/Cargo.toml
@@ -3,7 +3,7 @@ name = "czkawka_cli"
 version = "8.0.0"
 authors = ["Rafał Mikrut <mikrutrafal@protonmail.com>"]
 edition = "2021"
-rust-version = "1.79.0"
+rust-version = "1.80.0"
 description = "CLI frontend of Czkawka"
 license = "MIT"
 homepage = "https://github.com/qarmin/czkawka"
diff --git a/czkawka_core/Cargo.toml b/czkawka_core/Cargo.toml
index 76e7671fc..4f89532c5 100644
--- a/czkawka_core/Cargo.toml
+++ b/czkawka_core/Cargo.toml
@@ -3,7 +3,7 @@ name = "czkawka_core"
 version = "8.0.0"
 authors = ["Rafał Mikrut <mikrutrafal@protonmail.com>"]
 edition = "2021"
-rust-version = "1.79.0"
+rust-version = "1.80.0"
 description = "Core of Czkawka app"
 license = "MIT"
 homepage = "https://github.com/qarmin/czkawka"
@@ -76,6 +76,7 @@ libheif-rs = { version = "=0.18.0", optional = true } # Do not upgrade now, sinc
 libheif-sys = { version = "=1.14.2", optional = true } # 1.14.3 brake compilation on Ubuntu 22.04, so pin it to this version
 anyhow = { version = "1.0.89" }
 
+nom-exif = "2.1.0"
 
 state = "0.6"
 trash = "5.1"
diff --git a/czkawka_core/src/common_cache.rs b/czkawka_core/src/common_cache.rs
index 77b80c4c9..9faa8c935 100644
--- a/czkawka_core/src/common_cache.rs
+++ b/czkawka_core/src/common_cache.rs
@@ -15,7 +15,7 @@ use crate::duplicate::HashType;
 use crate::similar_images::{convert_algorithm_to_string, convert_filters_to_string};
 
 const CACHE_VERSION: &str = "70";
-const CACHE_IMAGE_VERSION: &str = "80";
+const CACHE_IMAGE_VERSION: &str = "90";
 
 pub fn get_broken_files_cache_file() -> String {
     format!("cache_broken_files_{CACHE_VERSION}.bin")
diff --git a/czkawka_core/src/common_image.rs b/czkawka_core/src/common_image.rs
index 73f7be417..f192b8190 100644
--- a/czkawka_core/src/common_image.rs
+++ b/czkawka_core/src/common_image.rs
@@ -25,6 +25,7 @@ use libheif_rs::{ColorSpace, HeifContext, RgbChroma};
 #[cfg(feature = "libraw")]
 use libraw::Processor;
 use log::{debug, error, info, warn, LevelFilter, Record};
+use nom_exif::{ExifIter, ExifTag, MediaParser, MediaSource};
 use rawloader::RawLoader;
 use symphonia::core::conv::IntoSample;
 
@@ -87,7 +88,19 @@ pub fn get_dynamic_image_from_path(path: &str) -> Result<DynamicImage, String> {
 
     if let Ok(res) = res {
         match res {
-            Ok(t) => Ok(t),
+            Ok(t) => {
+                let rotation = get_rotation_from_exif(path).unwrap_or(None);
+                match rotation {
+                    Some(ExifOrientation::Normal) | None => Ok(t),
+                    Some(ExifOrientation::MirrorHorizontal) => Ok(t.fliph()),
+                    Some(ExifOrientation::Rotate180) => Ok(t.rotate180()),
+                    Some(ExifOrientation::MirrorVertical) => Ok(t.flipv()),
+                    Some(ExifOrientation::MirrorHorizontalAndRotate270CW) => Ok(t.fliph().rotate270()),
+                    Some(ExifOrientation::Rotate90CW) => Ok(t.rotate90()),
+                    Some(ExifOrientation::MirrorHorizontalAndRotate90CW) => Ok(t.fliph().rotate90()),
+                    Some(ExifOrientation::Rotate270CW) => Ok(t.rotate270()),
+                }
+            }
             Err(e) => Err(format!("Cannot open image file \"{path}\": {e}")),
         }
     } else {
@@ -185,3 +198,43 @@ pub fn check_if_can_display_image(path: &str) -> bool {
 
     allowed_extensions.iter().any(|ext| extension_str.ends_with(ext))
 }
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ExifOrientation {
+    Normal,
+    MirrorHorizontal,
+    Rotate180,
+    MirrorVertical,
+    MirrorHorizontalAndRotate270CW,
+    Rotate90CW,
+    MirrorHorizontalAndRotate90CW,
+    Rotate270CW,
+}
+
+pub fn get_rotation_from_exif(path: &str) -> Result<Option<ExifOrientation>, nom_exif::Error> {
+    let mut parser = MediaParser::new();
+    let ms = MediaSource::file_path(path)?;
+    if !ms.has_exif() {
+        return Ok(None);
+    }
+    let exif_iter: ExifIter = parser.parse(ms)?;
+    for exif_entry in exif_iter {
+        if exif_entry.tag() == Some(ExifTag::Orientation) {
+            if let Some(value) = exif_entry.get_value() {
+                return match value.to_string().as_str() {
+                    "1" => Ok(Some(ExifOrientation::Normal)),
+                    "2" => Ok(Some(ExifOrientation::MirrorHorizontal)),
+                    "3" => Ok(Some(ExifOrientation::Rotate180)),
+                    "4" => Ok(Some(ExifOrientation::MirrorVertical)),
+                    "5" => Ok(Some(ExifOrientation::MirrorHorizontalAndRotate270CW)),
+                    "6" => Ok(Some(ExifOrientation::Rotate90CW)),
+                    "7" => Ok(Some(ExifOrientation::MirrorHorizontalAndRotate90CW)),
+                    "8" => Ok(Some(ExifOrientation::Rotate270CW)),
+                    _ => Ok(None),
+                };
+            }
+        }
+    }
+
+    Ok(None)
+}
diff --git a/czkawka_gui/Cargo.toml b/czkawka_gui/Cargo.toml
index 833ee1b25..3066cacf9 100644
--- a/czkawka_gui/Cargo.toml
+++ b/czkawka_gui/Cargo.toml
@@ -3,7 +3,7 @@ name = "czkawka_gui"
 version = "8.0.0"
 authors = ["Rafał Mikrut <mikrutrafal@protonmail.com>"]
 edition = "2021"
-rust-version = "1.79.0"
+rust-version = "1.80.0"
 description = "GTK frontend of Czkawka"
 license = "MIT"
 homepage = "https://github.com/qarmin/czkawka"
diff --git a/czkawka_gui/README.md b/czkawka_gui/README.md
index 90e8d486c..ac66bc6bd 100644
--- a/czkawka_gui/README.md
+++ b/czkawka_gui/README.md
@@ -71,7 +71,7 @@ lot build and runtime dependencies.
 
 | Program | Minimal version |
 |:-------:|:---------------:|
-|  Rust   |     1.79.0      |
+|  Rust   |     1.80.0      |
 |   GTK   |       4.6       |
 
 ### Linux (Ubuntu, but on other OS should work similar)
diff --git a/instructions/Compilation.md b/instructions/Compilation.md
index 7ff753641..41eee1301 100644
--- a/instructions/Compilation.md
+++ b/instructions/Compilation.md
@@ -20,7 +20,7 @@ New versions of GTK fixes some bugs, so e.g. middle button selection will work o
 
 | Program | Min    | What for                                                                             |
 |---------|--------|--------------------------------------------------------------------------------------|
-| Rust    | 1.79.0 | The minimum version of rust does not depend on anything, so it can change frequently |
+| Rust    | 1.80.0 | The minimum version of rust does not depend on anything, so it can change frequently |
 | GTK     | 4.6    | Only for the `GTK` backend                                                           |
 
 #### Debian / Ubuntu
diff --git a/krokiet/Cargo.toml b/krokiet/Cargo.toml
index f59408e88..5408c66e0 100644
--- a/krokiet/Cargo.toml
+++ b/krokiet/Cargo.toml
@@ -3,7 +3,7 @@ name = "krokiet"
 version = "8.0.0"
 authors = ["Rafał Mikrut <mikrutrafal@protonmail.com>"]
 edition = "2021"
-rust-version = "1.79.0"
+rust-version = "1.80.0"
 description = "Slint frontend of Czkawka Core"
 license = "GPL-3.0-only"
 homepage = "https://github.com/qarmin/czkawka"
diff --git a/krokiet/ui/gui_state.slint b/krokiet/ui/gui_state.slint
index 9c9952fb7..59dc4a469 100644
--- a/krokiet/ui/gui_state.slint
+++ b/krokiet/ui/gui_state.slint
@@ -18,7 +18,7 @@ export global GuiState {
     in-out property <bool> visible_tool_settings;
 
     in-out property <bool> available_subsettings: active_tab == CurrentTab.SimilarImages || active_tab == CurrentTab.DuplicateFiles || active_tab == CurrentTab.SimilarVideos || active_tab == CurrentTab.SimilarMusic || active_tab == CurrentTab.BigFiles || active_tab == CurrentTab.BrokenFiles;
-    in-out property <CurrentTab> active_tab: CurrentTab.SimilarImages;
+    in-out property <CurrentTab> active_tab: CurrentTab.DuplicateFiles;
     in-out property <bool> is_tool_tab_active: active_tab != CurrentTab.Settings && active_tab != CurrentTab.About;
     in-out property <[SelectModel]> select_results_list: [{data: SelectMode.SelectAll, name: "Select All"}, {data: SelectMode.UnselectAll, name: "Deselect All"}, {data: SelectMode.SelectTheSmallestResolution, name: "Select the smallest resolution"}];
 


Test patch: