A script that fixes the metadata from a google photos export and converts it to jpegxl
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Woah, init i think

MrSnowy 43693395

+4088
+1
.envrc
··· 1 + use flake
+18
.gitignore
··· 1 + /target 2 + /fixed 3 + /fucked 4 + /converted_fucked 5 + /converted 6 + */F 7 + 8 + # Devenv 9 + .devenv* 10 + devenv.local.nix 11 + 12 + # direnv 13 + .direnv 14 + 15 + # pre-commit 16 + .pre-commit-config.yaml 17 + 18 + temp.dat
+1053
Cargo.lock
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 4 4 + 5 + [[package]] 6 + name = "aho-corasick" 7 + version = "1.1.3" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 + dependencies = [ 11 + "memchr", 12 + ] 13 + 14 + [[package]] 15 + name = "android-tzdata" 16 + version = "0.1.1" 17 + source = "registry+https://github.com/rust-lang/crates.io-index" 18 + checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 19 + 20 + [[package]] 21 + name = "android_system_properties" 22 + version = "0.1.5" 23 + source = "registry+https://github.com/rust-lang/crates.io-index" 24 + checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 25 + dependencies = [ 26 + "libc", 27 + ] 28 + 29 + [[package]] 30 + name = "anstream" 31 + version = "0.6.20" 32 + source = "registry+https://github.com/rust-lang/crates.io-index" 33 + checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" 34 + dependencies = [ 35 + "anstyle", 36 + "anstyle-parse", 37 + "anstyle-query", 38 + "anstyle-wincon", 39 + "colorchoice", 40 + "is_terminal_polyfill", 41 + "utf8parse", 42 + ] 43 + 44 + [[package]] 45 + name = "anstyle" 46 + version = "1.0.11" 47 + source = "registry+https://github.com/rust-lang/crates.io-index" 48 + checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" 49 + 50 + [[package]] 51 + name = "anstyle-parse" 52 + version = "0.2.7" 53 + source = "registry+https://github.com/rust-lang/crates.io-index" 54 + checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 55 + dependencies = [ 56 + "utf8parse", 57 + ] 58 + 59 + [[package]] 60 + name = "anstyle-query" 61 + version = "1.1.4" 62 + source = "registry+https://github.com/rust-lang/crates.io-index" 63 + checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" 64 + dependencies = [ 65 + "windows-sys 0.60.2", 66 + ] 67 + 68 + [[package]] 69 + name = "anstyle-wincon" 70 + version = "3.0.10" 71 + source = "registry+https://github.com/rust-lang/crates.io-index" 72 + checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" 73 + dependencies = [ 74 + "anstyle", 75 + "once_cell_polyfill", 76 + "windows-sys 0.60.2", 77 + ] 78 + 79 + [[package]] 80 + name = "anyhow" 81 + version = "1.0.99" 82 + source = "registry+https://github.com/rust-lang/crates.io-index" 83 + checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" 84 + 85 + [[package]] 86 + name = "autocfg" 87 + version = "1.5.0" 88 + source = "registry+https://github.com/rust-lang/crates.io-index" 89 + checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 90 + 91 + [[package]] 92 + name = "bitflags" 93 + version = "2.9.2" 94 + source = "registry+https://github.com/rust-lang/crates.io-index" 95 + checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" 96 + 97 + [[package]] 98 + name = "bumpalo" 99 + version = "3.19.0" 100 + source = "registry+https://github.com/rust-lang/crates.io-index" 101 + checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 102 + 103 + [[package]] 104 + name = "byteorder" 105 + version = "1.5.0" 106 + source = "registry+https://github.com/rust-lang/crates.io-index" 107 + checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 108 + 109 + [[package]] 110 + name = "cc" 111 + version = "1.2.33" 112 + source = "registry+https://github.com/rust-lang/crates.io-index" 113 + checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" 114 + dependencies = [ 115 + "shlex", 116 + ] 117 + 118 + [[package]] 119 + name = "cfb" 120 + version = "0.7.3" 121 + source = "registry+https://github.com/rust-lang/crates.io-index" 122 + checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" 123 + dependencies = [ 124 + "byteorder", 125 + "fnv", 126 + "uuid", 127 + ] 128 + 129 + [[package]] 130 + name = "cfg-if" 131 + version = "1.0.1" 132 + source = "registry+https://github.com/rust-lang/crates.io-index" 133 + checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 134 + 135 + [[package]] 136 + name = "chrono" 137 + version = "0.4.41" 138 + source = "registry+https://github.com/rust-lang/crates.io-index" 139 + checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" 140 + dependencies = [ 141 + "android-tzdata", 142 + "iana-time-zone", 143 + "js-sys", 144 + "num-traits", 145 + "serde", 146 + "wasm-bindgen", 147 + "windows-link", 148 + ] 149 + 150 + [[package]] 151 + name = "clap" 152 + version = "4.5.45" 153 + source = "registry+https://github.com/rust-lang/crates.io-index" 154 + checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" 155 + dependencies = [ 156 + "clap_builder", 157 + "clap_derive", 158 + ] 159 + 160 + [[package]] 161 + name = "clap_builder" 162 + version = "4.5.44" 163 + source = "registry+https://github.com/rust-lang/crates.io-index" 164 + checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" 165 + dependencies = [ 166 + "anstream", 167 + "anstyle", 168 + "clap_lex", 169 + "strsim", 170 + ] 171 + 172 + [[package]] 173 + name = "clap_derive" 174 + version = "4.5.45" 175 + source = "registry+https://github.com/rust-lang/crates.io-index" 176 + checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" 177 + dependencies = [ 178 + "heck", 179 + "proc-macro2", 180 + "quote", 181 + "syn", 182 + ] 183 + 184 + [[package]] 185 + name = "clap_lex" 186 + version = "0.7.5" 187 + source = "registry+https://github.com/rust-lang/crates.io-index" 188 + checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" 189 + 190 + [[package]] 191 + name = "colorchoice" 192 + version = "1.0.4" 193 + source = "registry+https://github.com/rust-lang/crates.io-index" 194 + checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 195 + 196 + [[package]] 197 + name = "converter-client" 198 + version = "0.1.0" 199 + dependencies = [ 200 + "anyhow", 201 + "clap", 202 + "converter-lib", 203 + "exiftool", 204 + ] 205 + 206 + [[package]] 207 + name = "converter-lib" 208 + version = "0.1.0" 209 + dependencies = [ 210 + "anyhow", 211 + ] 212 + 213 + [[package]] 214 + name = "converter-server" 215 + version = "0.1.0" 216 + dependencies = [ 217 + "anyhow", 218 + "converter-lib", 219 + "infer", 220 + ] 221 + 222 + [[package]] 223 + name = "core-foundation-sys" 224 + version = "0.8.7" 225 + source = "registry+https://github.com/rust-lang/crates.io-index" 226 + checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 227 + 228 + [[package]] 229 + name = "crossbeam-deque" 230 + version = "0.8.6" 231 + source = "registry+https://github.com/rust-lang/crates.io-index" 232 + checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" 233 + dependencies = [ 234 + "crossbeam-epoch", 235 + "crossbeam-utils", 236 + ] 237 + 238 + [[package]] 239 + name = "crossbeam-epoch" 240 + version = "0.9.18" 241 + source = "registry+https://github.com/rust-lang/crates.io-index" 242 + checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 243 + dependencies = [ 244 + "crossbeam-utils", 245 + ] 246 + 247 + [[package]] 248 + name = "crossbeam-utils" 249 + version = "0.8.21" 250 + source = "registry+https://github.com/rust-lang/crates.io-index" 251 + checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 252 + 253 + [[package]] 254 + name = "either" 255 + version = "1.15.0" 256 + source = "registry+https://github.com/rust-lang/crates.io-index" 257 + checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 258 + 259 + [[package]] 260 + name = "env_filter" 261 + version = "0.1.3" 262 + source = "registry+https://github.com/rust-lang/crates.io-index" 263 + checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" 264 + dependencies = [ 265 + "log", 266 + ] 267 + 268 + [[package]] 269 + name = "env_logger" 270 + version = "0.11.8" 271 + source = "registry+https://github.com/rust-lang/crates.io-index" 272 + checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" 273 + dependencies = [ 274 + "anstream", 275 + "anstyle", 276 + "env_filter", 277 + "jiff", 278 + "log", 279 + ] 280 + 281 + [[package]] 282 + name = "errno" 283 + version = "0.3.13" 284 + source = "registry+https://github.com/rust-lang/crates.io-index" 285 + checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" 286 + dependencies = [ 287 + "libc", 288 + "windows-sys 0.60.2", 289 + ] 290 + 291 + [[package]] 292 + name = "exiftool" 293 + version = "0.2.6" 294 + source = "registry+https://github.com/rust-lang/crates.io-index" 295 + checksum = "d9110f386a3755c161f4c585e5f5b98702dfd7fb8e4b9e92f71d61605ecdbc60" 296 + dependencies = [ 297 + "chrono", 298 + "log", 299 + "serde", 300 + "serde_json", 301 + "serde_path_to_error", 302 + "tempfile", 303 + "thiserror", 304 + ] 305 + 306 + [[package]] 307 + name = "fastrand" 308 + version = "2.3.0" 309 + source = "registry+https://github.com/rust-lang/crates.io-index" 310 + checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 311 + 312 + [[package]] 313 + name = "fnv" 314 + version = "1.0.7" 315 + source = "registry+https://github.com/rust-lang/crates.io-index" 316 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 317 + 318 + [[package]] 319 + name = "getrandom" 320 + version = "0.3.3" 321 + source = "registry+https://github.com/rust-lang/crates.io-index" 322 + checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 323 + dependencies = [ 324 + "cfg-if", 325 + "libc", 326 + "r-efi", 327 + "wasi", 328 + ] 329 + 330 + [[package]] 331 + name = "gfoto-metadata-fixer" 332 + version = "0.1.0" 333 + dependencies = [ 334 + "anyhow", 335 + "clap", 336 + "env_logger", 337 + "exiftool", 338 + "infer", 339 + "lazy-regex", 340 + "log", 341 + "rayon", 342 + "regex", 343 + "serde", 344 + "serde_json", 345 + "snowy_libs", 346 + ] 347 + 348 + [[package]] 349 + name = "heck" 350 + version = "0.5.0" 351 + source = "registry+https://github.com/rust-lang/crates.io-index" 352 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 353 + 354 + [[package]] 355 + name = "iana-time-zone" 356 + version = "0.1.63" 357 + source = "registry+https://github.com/rust-lang/crates.io-index" 358 + checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" 359 + dependencies = [ 360 + "android_system_properties", 361 + "core-foundation-sys", 362 + "iana-time-zone-haiku", 363 + "js-sys", 364 + "log", 365 + "wasm-bindgen", 366 + "windows-core", 367 + ] 368 + 369 + [[package]] 370 + name = "iana-time-zone-haiku" 371 + version = "0.1.2" 372 + source = "registry+https://github.com/rust-lang/crates.io-index" 373 + checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 374 + dependencies = [ 375 + "cc", 376 + ] 377 + 378 + [[package]] 379 + name = "infer" 380 + version = "0.19.0" 381 + source = "registry+https://github.com/rust-lang/crates.io-index" 382 + checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" 383 + dependencies = [ 384 + "cfb", 385 + ] 386 + 387 + [[package]] 388 + name = "is_terminal_polyfill" 389 + version = "1.70.1" 390 + source = "registry+https://github.com/rust-lang/crates.io-index" 391 + checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 392 + 393 + [[package]] 394 + name = "itoa" 395 + version = "1.0.15" 396 + source = "registry+https://github.com/rust-lang/crates.io-index" 397 + checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 398 + 399 + [[package]] 400 + name = "jiff" 401 + version = "0.2.15" 402 + source = "registry+https://github.com/rust-lang/crates.io-index" 403 + checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" 404 + dependencies = [ 405 + "jiff-static", 406 + "log", 407 + "portable-atomic", 408 + "portable-atomic-util", 409 + "serde", 410 + ] 411 + 412 + [[package]] 413 + name = "jiff-static" 414 + version = "0.2.15" 415 + source = "registry+https://github.com/rust-lang/crates.io-index" 416 + checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" 417 + dependencies = [ 418 + "proc-macro2", 419 + "quote", 420 + "syn", 421 + ] 422 + 423 + [[package]] 424 + name = "js-sys" 425 + version = "0.3.77" 426 + source = "registry+https://github.com/rust-lang/crates.io-index" 427 + checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 428 + dependencies = [ 429 + "once_cell", 430 + "wasm-bindgen", 431 + ] 432 + 433 + [[package]] 434 + name = "lazy-regex" 435 + version = "3.4.1" 436 + source = "registry+https://github.com/rust-lang/crates.io-index" 437 + checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126" 438 + dependencies = [ 439 + "lazy-regex-proc_macros", 440 + "once_cell", 441 + "regex", 442 + ] 443 + 444 + [[package]] 445 + name = "lazy-regex-proc_macros" 446 + version = "3.4.1" 447 + source = "registry+https://github.com/rust-lang/crates.io-index" 448 + checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1" 449 + dependencies = [ 450 + "proc-macro2", 451 + "quote", 452 + "regex", 453 + "syn", 454 + ] 455 + 456 + [[package]] 457 + name = "libc" 458 + version = "0.2.175" 459 + source = "registry+https://github.com/rust-lang/crates.io-index" 460 + checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" 461 + 462 + [[package]] 463 + name = "linux-raw-sys" 464 + version = "0.9.4" 465 + source = "registry+https://github.com/rust-lang/crates.io-index" 466 + checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" 467 + 468 + [[package]] 469 + name = "log" 470 + version = "0.4.27" 471 + source = "registry+https://github.com/rust-lang/crates.io-index" 472 + checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 473 + 474 + [[package]] 475 + name = "memchr" 476 + version = "2.7.5" 477 + source = "registry+https://github.com/rust-lang/crates.io-index" 478 + checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 479 + 480 + [[package]] 481 + name = "num-traits" 482 + version = "0.2.19" 483 + source = "registry+https://github.com/rust-lang/crates.io-index" 484 + checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 485 + dependencies = [ 486 + "autocfg", 487 + ] 488 + 489 + [[package]] 490 + name = "once_cell" 491 + version = "1.21.3" 492 + source = "registry+https://github.com/rust-lang/crates.io-index" 493 + checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 494 + 495 + [[package]] 496 + name = "once_cell_polyfill" 497 + version = "1.70.1" 498 + source = "registry+https://github.com/rust-lang/crates.io-index" 499 + checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 500 + 501 + [[package]] 502 + name = "portable-atomic" 503 + version = "1.11.1" 504 + source = "registry+https://github.com/rust-lang/crates.io-index" 505 + checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 506 + 507 + [[package]] 508 + name = "portable-atomic-util" 509 + version = "0.2.4" 510 + source = "registry+https://github.com/rust-lang/crates.io-index" 511 + checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" 512 + dependencies = [ 513 + "portable-atomic", 514 + ] 515 + 516 + [[package]] 517 + name = "proc-macro2" 518 + version = "1.0.101" 519 + source = "registry+https://github.com/rust-lang/crates.io-index" 520 + checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" 521 + dependencies = [ 522 + "unicode-ident", 523 + ] 524 + 525 + [[package]] 526 + name = "quote" 527 + version = "1.0.40" 528 + source = "registry+https://github.com/rust-lang/crates.io-index" 529 + checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 530 + dependencies = [ 531 + "proc-macro2", 532 + ] 533 + 534 + [[package]] 535 + name = "r-efi" 536 + version = "5.3.0" 537 + source = "registry+https://github.com/rust-lang/crates.io-index" 538 + checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 539 + 540 + [[package]] 541 + name = "rayon" 542 + version = "1.11.0" 543 + source = "registry+https://github.com/rust-lang/crates.io-index" 544 + checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" 545 + dependencies = [ 546 + "either", 547 + "rayon-core", 548 + ] 549 + 550 + [[package]] 551 + name = "rayon-core" 552 + version = "1.13.0" 553 + source = "registry+https://github.com/rust-lang/crates.io-index" 554 + checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" 555 + dependencies = [ 556 + "crossbeam-deque", 557 + "crossbeam-utils", 558 + ] 559 + 560 + [[package]] 561 + name = "regex" 562 + version = "1.11.1" 563 + source = "registry+https://github.com/rust-lang/crates.io-index" 564 + checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 565 + dependencies = [ 566 + "aho-corasick", 567 + "memchr", 568 + "regex-automata", 569 + "regex-syntax", 570 + ] 571 + 572 + [[package]] 573 + name = "regex-automata" 574 + version = "0.4.9" 575 + source = "registry+https://github.com/rust-lang/crates.io-index" 576 + checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 577 + dependencies = [ 578 + "aho-corasick", 579 + "memchr", 580 + "regex-syntax", 581 + ] 582 + 583 + [[package]] 584 + name = "regex-syntax" 585 + version = "0.8.5" 586 + source = "registry+https://github.com/rust-lang/crates.io-index" 587 + checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 588 + 589 + [[package]] 590 + name = "rustix" 591 + version = "1.0.8" 592 + source = "registry+https://github.com/rust-lang/crates.io-index" 593 + checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" 594 + dependencies = [ 595 + "bitflags", 596 + "errno", 597 + "libc", 598 + "linux-raw-sys", 599 + "windows-sys 0.60.2", 600 + ] 601 + 602 + [[package]] 603 + name = "rustversion" 604 + version = "1.0.22" 605 + source = "registry+https://github.com/rust-lang/crates.io-index" 606 + checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 607 + 608 + [[package]] 609 + name = "ryu" 610 + version = "1.0.20" 611 + source = "registry+https://github.com/rust-lang/crates.io-index" 612 + checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 613 + 614 + [[package]] 615 + name = "serde" 616 + version = "1.0.219" 617 + source = "registry+https://github.com/rust-lang/crates.io-index" 618 + checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 619 + dependencies = [ 620 + "serde_derive", 621 + ] 622 + 623 + [[package]] 624 + name = "serde_derive" 625 + version = "1.0.219" 626 + source = "registry+https://github.com/rust-lang/crates.io-index" 627 + checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 628 + dependencies = [ 629 + "proc-macro2", 630 + "quote", 631 + "syn", 632 + ] 633 + 634 + [[package]] 635 + name = "serde_json" 636 + version = "1.0.142" 637 + source = "registry+https://github.com/rust-lang/crates.io-index" 638 + checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" 639 + dependencies = [ 640 + "itoa", 641 + "memchr", 642 + "ryu", 643 + "serde", 644 + ] 645 + 646 + [[package]] 647 + name = "serde_path_to_error" 648 + version = "0.1.17" 649 + source = "registry+https://github.com/rust-lang/crates.io-index" 650 + checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" 651 + dependencies = [ 652 + "itoa", 653 + "serde", 654 + ] 655 + 656 + [[package]] 657 + name = "shlex" 658 + version = "1.3.0" 659 + source = "registry+https://github.com/rust-lang/crates.io-index" 660 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 661 + 662 + [[package]] 663 + name = "snowy_libs" 664 + version = "0.1.3" 665 + dependencies = [ 666 + "termsize", 667 + ] 668 + 669 + [[package]] 670 + name = "strsim" 671 + version = "0.11.1" 672 + source = "registry+https://github.com/rust-lang/crates.io-index" 673 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 674 + 675 + [[package]] 676 + name = "syn" 677 + version = "2.0.106" 678 + source = "registry+https://github.com/rust-lang/crates.io-index" 679 + checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" 680 + dependencies = [ 681 + "proc-macro2", 682 + "quote", 683 + "unicode-ident", 684 + ] 685 + 686 + [[package]] 687 + name = "tempfile" 688 + version = "3.20.0" 689 + source = "registry+https://github.com/rust-lang/crates.io-index" 690 + checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" 691 + dependencies = [ 692 + "fastrand", 693 + "getrandom", 694 + "once_cell", 695 + "rustix", 696 + "windows-sys 0.59.0", 697 + ] 698 + 699 + [[package]] 700 + name = "termsize" 701 + version = "0.1.9" 702 + source = "registry+https://github.com/rust-lang/crates.io-index" 703 + checksum = "6f11ff5c25c172608d5b85e2fb43ee9a6d683a7f4ab7f96ae07b3d8b590368fd" 704 + dependencies = [ 705 + "libc", 706 + "winapi", 707 + ] 708 + 709 + [[package]] 710 + name = "thiserror" 711 + version = "2.0.15" 712 + source = "registry+https://github.com/rust-lang/crates.io-index" 713 + checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850" 714 + dependencies = [ 715 + "thiserror-impl", 716 + ] 717 + 718 + [[package]] 719 + name = "thiserror-impl" 720 + version = "2.0.15" 721 + source = "registry+https://github.com/rust-lang/crates.io-index" 722 + checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0" 723 + dependencies = [ 724 + "proc-macro2", 725 + "quote", 726 + "syn", 727 + ] 728 + 729 + [[package]] 730 + name = "unicode-ident" 731 + version = "1.0.18" 732 + source = "registry+https://github.com/rust-lang/crates.io-index" 733 + checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 734 + 735 + [[package]] 736 + name = "utf8parse" 737 + version = "0.2.2" 738 + source = "registry+https://github.com/rust-lang/crates.io-index" 739 + checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 740 + 741 + [[package]] 742 + name = "uuid" 743 + version = "1.18.0" 744 + source = "registry+https://github.com/rust-lang/crates.io-index" 745 + checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" 746 + dependencies = [ 747 + "js-sys", 748 + "wasm-bindgen", 749 + ] 750 + 751 + [[package]] 752 + name = "wasi" 753 + version = "0.14.2+wasi-0.2.4" 754 + source = "registry+https://github.com/rust-lang/crates.io-index" 755 + checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 756 + dependencies = [ 757 + "wit-bindgen-rt", 758 + ] 759 + 760 + [[package]] 761 + name = "wasm-bindgen" 762 + version = "0.2.100" 763 + source = "registry+https://github.com/rust-lang/crates.io-index" 764 + checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 765 + dependencies = [ 766 + "cfg-if", 767 + "once_cell", 768 + "rustversion", 769 + "wasm-bindgen-macro", 770 + ] 771 + 772 + [[package]] 773 + name = "wasm-bindgen-backend" 774 + version = "0.2.100" 775 + source = "registry+https://github.com/rust-lang/crates.io-index" 776 + checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 777 + dependencies = [ 778 + "bumpalo", 779 + "log", 780 + "proc-macro2", 781 + "quote", 782 + "syn", 783 + "wasm-bindgen-shared", 784 + ] 785 + 786 + [[package]] 787 + name = "wasm-bindgen-macro" 788 + version = "0.2.100" 789 + source = "registry+https://github.com/rust-lang/crates.io-index" 790 + checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 791 + dependencies = [ 792 + "quote", 793 + "wasm-bindgen-macro-support", 794 + ] 795 + 796 + [[package]] 797 + name = "wasm-bindgen-macro-support" 798 + version = "0.2.100" 799 + source = "registry+https://github.com/rust-lang/crates.io-index" 800 + checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 801 + dependencies = [ 802 + "proc-macro2", 803 + "quote", 804 + "syn", 805 + "wasm-bindgen-backend", 806 + "wasm-bindgen-shared", 807 + ] 808 + 809 + [[package]] 810 + name = "wasm-bindgen-shared" 811 + version = "0.2.100" 812 + source = "registry+https://github.com/rust-lang/crates.io-index" 813 + checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 814 + dependencies = [ 815 + "unicode-ident", 816 + ] 817 + 818 + [[package]] 819 + name = "winapi" 820 + version = "0.3.9" 821 + source = "registry+https://github.com/rust-lang/crates.io-index" 822 + checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 823 + dependencies = [ 824 + "winapi-i686-pc-windows-gnu", 825 + "winapi-x86_64-pc-windows-gnu", 826 + ] 827 + 828 + [[package]] 829 + name = "winapi-i686-pc-windows-gnu" 830 + version = "0.4.0" 831 + source = "registry+https://github.com/rust-lang/crates.io-index" 832 + checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 833 + 834 + [[package]] 835 + name = "winapi-x86_64-pc-windows-gnu" 836 + version = "0.4.0" 837 + source = "registry+https://github.com/rust-lang/crates.io-index" 838 + checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 839 + 840 + [[package]] 841 + name = "windows-core" 842 + version = "0.61.2" 843 + source = "registry+https://github.com/rust-lang/crates.io-index" 844 + checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 845 + dependencies = [ 846 + "windows-implement", 847 + "windows-interface", 848 + "windows-link", 849 + "windows-result", 850 + "windows-strings", 851 + ] 852 + 853 + [[package]] 854 + name = "windows-implement" 855 + version = "0.60.0" 856 + source = "registry+https://github.com/rust-lang/crates.io-index" 857 + checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 858 + dependencies = [ 859 + "proc-macro2", 860 + "quote", 861 + "syn", 862 + ] 863 + 864 + [[package]] 865 + name = "windows-interface" 866 + version = "0.59.1" 867 + source = "registry+https://github.com/rust-lang/crates.io-index" 868 + checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 869 + dependencies = [ 870 + "proc-macro2", 871 + "quote", 872 + "syn", 873 + ] 874 + 875 + [[package]] 876 + name = "windows-link" 877 + version = "0.1.3" 878 + source = "registry+https://github.com/rust-lang/crates.io-index" 879 + checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 880 + 881 + [[package]] 882 + name = "windows-result" 883 + version = "0.3.4" 884 + source = "registry+https://github.com/rust-lang/crates.io-index" 885 + checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 886 + dependencies = [ 887 + "windows-link", 888 + ] 889 + 890 + [[package]] 891 + name = "windows-strings" 892 + version = "0.4.2" 893 + source = "registry+https://github.com/rust-lang/crates.io-index" 894 + checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 895 + dependencies = [ 896 + "windows-link", 897 + ] 898 + 899 + [[package]] 900 + name = "windows-sys" 901 + version = "0.59.0" 902 + source = "registry+https://github.com/rust-lang/crates.io-index" 903 + checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 904 + dependencies = [ 905 + "windows-targets 0.52.6", 906 + ] 907 + 908 + [[package]] 909 + name = "windows-sys" 910 + version = "0.60.2" 911 + source = "registry+https://github.com/rust-lang/crates.io-index" 912 + checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 913 + dependencies = [ 914 + "windows-targets 0.53.3", 915 + ] 916 + 917 + [[package]] 918 + name = "windows-targets" 919 + version = "0.52.6" 920 + source = "registry+https://github.com/rust-lang/crates.io-index" 921 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 922 + dependencies = [ 923 + "windows_aarch64_gnullvm 0.52.6", 924 + "windows_aarch64_msvc 0.52.6", 925 + "windows_i686_gnu 0.52.6", 926 + "windows_i686_gnullvm 0.52.6", 927 + "windows_i686_msvc 0.52.6", 928 + "windows_x86_64_gnu 0.52.6", 929 + "windows_x86_64_gnullvm 0.52.6", 930 + "windows_x86_64_msvc 0.52.6", 931 + ] 932 + 933 + [[package]] 934 + name = "windows-targets" 935 + version = "0.53.3" 936 + source = "registry+https://github.com/rust-lang/crates.io-index" 937 + checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" 938 + dependencies = [ 939 + "windows-link", 940 + "windows_aarch64_gnullvm 0.53.0", 941 + "windows_aarch64_msvc 0.53.0", 942 + "windows_i686_gnu 0.53.0", 943 + "windows_i686_gnullvm 0.53.0", 944 + "windows_i686_msvc 0.53.0", 945 + "windows_x86_64_gnu 0.53.0", 946 + "windows_x86_64_gnullvm 0.53.0", 947 + "windows_x86_64_msvc 0.53.0", 948 + ] 949 + 950 + [[package]] 951 + name = "windows_aarch64_gnullvm" 952 + version = "0.52.6" 953 + source = "registry+https://github.com/rust-lang/crates.io-index" 954 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 955 + 956 + [[package]] 957 + name = "windows_aarch64_gnullvm" 958 + version = "0.53.0" 959 + source = "registry+https://github.com/rust-lang/crates.io-index" 960 + checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 961 + 962 + [[package]] 963 + name = "windows_aarch64_msvc" 964 + version = "0.52.6" 965 + source = "registry+https://github.com/rust-lang/crates.io-index" 966 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 967 + 968 + [[package]] 969 + name = "windows_aarch64_msvc" 970 + version = "0.53.0" 971 + source = "registry+https://github.com/rust-lang/crates.io-index" 972 + checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 973 + 974 + [[package]] 975 + name = "windows_i686_gnu" 976 + version = "0.52.6" 977 + source = "registry+https://github.com/rust-lang/crates.io-index" 978 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 979 + 980 + [[package]] 981 + name = "windows_i686_gnu" 982 + version = "0.53.0" 983 + source = "registry+https://github.com/rust-lang/crates.io-index" 984 + checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 985 + 986 + [[package]] 987 + name = "windows_i686_gnullvm" 988 + version = "0.52.6" 989 + source = "registry+https://github.com/rust-lang/crates.io-index" 990 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 991 + 992 + [[package]] 993 + name = "windows_i686_gnullvm" 994 + version = "0.53.0" 995 + source = "registry+https://github.com/rust-lang/crates.io-index" 996 + checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 997 + 998 + [[package]] 999 + name = "windows_i686_msvc" 1000 + version = "0.52.6" 1001 + source = "registry+https://github.com/rust-lang/crates.io-index" 1002 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1003 + 1004 + [[package]] 1005 + name = "windows_i686_msvc" 1006 + version = "0.53.0" 1007 + source = "registry+https://github.com/rust-lang/crates.io-index" 1008 + checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 1009 + 1010 + [[package]] 1011 + name = "windows_x86_64_gnu" 1012 + version = "0.52.6" 1013 + source = "registry+https://github.com/rust-lang/crates.io-index" 1014 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1015 + 1016 + [[package]] 1017 + name = "windows_x86_64_gnu" 1018 + version = "0.53.0" 1019 + source = "registry+https://github.com/rust-lang/crates.io-index" 1020 + checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 1021 + 1022 + [[package]] 1023 + name = "windows_x86_64_gnullvm" 1024 + version = "0.52.6" 1025 + source = "registry+https://github.com/rust-lang/crates.io-index" 1026 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1027 + 1028 + [[package]] 1029 + name = "windows_x86_64_gnullvm" 1030 + version = "0.53.0" 1031 + source = "registry+https://github.com/rust-lang/crates.io-index" 1032 + checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 1033 + 1034 + [[package]] 1035 + name = "windows_x86_64_msvc" 1036 + version = "0.52.6" 1037 + source = "registry+https://github.com/rust-lang/crates.io-index" 1038 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1039 + 1040 + [[package]] 1041 + name = "windows_x86_64_msvc" 1042 + version = "0.53.0" 1043 + source = "registry+https://github.com/rust-lang/crates.io-index" 1044 + checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 1045 + 1046 + [[package]] 1047 + name = "wit-bindgen-rt" 1048 + version = "0.39.0" 1049 + source = "registry+https://github.com/rust-lang/crates.io-index" 1050 + checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 1051 + dependencies = [ 1052 + "bitflags", 1053 + ]
+3
Cargo.toml
··· 1 + [workspace] 2 + resolver = "3" 3 + members = ["converter-client", "converter-lib", "converter-server", "gfoto-fixer"]
+10
converter-client/Cargo.toml
··· 1 + [package] 2 + name = "converter-client" 3 + version = "0.1.0" 4 + edition = "2024" 5 + 6 + [dependencies] 7 + anyhow = "1.0.99" 8 + clap = { version = "4.5.45", features = ["derive"] } 9 + converter-lib = { version = "0.1.0", path = "../converter-lib" } 10 + exiftool = "0.2.6"
+12
converter-client/src/cli.rs
··· 1 + use std::net::IpAddr; 2 + 3 + use clap::Parser; 4 + 5 + /// Google takeout metadata fixer 6 + #[derive(Parser, Debug)] 7 + #[command(version, about, long_about = None)] 8 + pub struct Args { 9 + /// Define the IP address for the relay server 10 + #[arg(long, default_value_t = IpAddr::from([127, 0, 0, 1]))] 11 + pub ip: IpAddr, 12 + }
+253
converter-client/src/main.rs
··· 1 + use std::{ 2 + fs, 3 + io::{Read, Write}, 4 + net::{SocketAddr, TcpStream}, 5 + path::PathBuf, 6 + process::Command, 7 + }; 8 + 9 + use anyhow::{Context, bail}; 10 + use clap::Parser; 11 + use converter_lib::{ClientMessageType, JobMessage}; 12 + use exiftool::ExifTool; 13 + 14 + mod cli; 15 + 16 + fn main() -> anyhow::Result<()> { 17 + println!("Hello, world!"); 18 + 19 + let mut exiftool = ExifTool::new()?; 20 + 21 + let args = cli::Args::parse(); 22 + 23 + let mut stream = TcpStream::connect(SocketAddr::new(args.ip, 33693))?; 24 + 25 + let message = ClientMessageType::Ready.into_bytes(); 26 + 27 + stream.write_all(&message)?; 28 + 29 + let mut completed_jobs: u64 = 0; 30 + let mut saved_bytes: i64 = 0; 31 + 32 + loop { 33 + // println!("loop"); 34 + let payload_size = { 35 + let mut buffer = [0; 8]; 36 + 37 + stream.read_exact(&mut buffer)?; 38 + 39 + u64::from_be_bytes(buffer) as usize 40 + }; 41 + 42 + // Read the returned data 43 + let mut payload = vec![0u8; payload_size]; 44 + stream.read_exact(&mut payload)?; 45 + 46 + // Get the message and pass it over to the main thread 47 + let message = JobMessage::from_bytes(payload).context("context")?; 48 + 49 + println!("\nGot a job from server! {message}",); 50 + 51 + let job = (|| -> anyhow::Result<(PathBuf, i64)> { 52 + match message { 53 + JobMessage::ImageJob(image_bytes) => { 54 + fs::write("./temp.dat", image_bytes)?; 55 + let (converted_path, size_diff) = convert_image()?; 56 + saved_bytes += size_diff; 57 + 58 + restore_exif(&converted_path, &mut exiftool)?; 59 + Ok((converted_path, size_diff)) 60 + } 61 + JobMessage::VideoJob(video_bytes) => { 62 + fs::write("./temp.dat", video_bytes)?; 63 + let (converted_path, size_diff) = convert_video()?; 64 + saved_bytes += size_diff; 65 + 66 + restore_exif(&converted_path, &mut exiftool)?; 67 + Ok((converted_path, size_diff)) 68 + } 69 + } 70 + })(); 71 + 72 + match job { 73 + Ok((converted_path, size_diff)) => { 74 + let bytes = fs::read(converted_path)?; 75 + let message = ClientMessageType::CompletedJob((bytes, size_diff)); 76 + stream.write_all(&message.into_bytes())?; 77 + 78 + completed_jobs += 1; 79 + 80 + println!( 81 + "Completed job succesfully! {completed_jobs} in total! And {saved_bytes} Bytes saved!" 82 + ); 83 + } 84 + Err(err) => { 85 + let message = ClientMessageType::FailedJob(err.to_string()); 86 + stream.write_all(&message.into_bytes())?; 87 + 88 + println!("Job failed! 10 dabloons have been substracted! ({err})"); 89 + } 90 + } 91 + 92 + if let Err(err) = fs::remove_file("./temp.dat") { 93 + match err.kind() { 94 + std::io::ErrorKind::NotFound => (), 95 + _ => panic!("Couldn't remove file ./temp.dat"), 96 + } 97 + } 98 + 99 + if let Err(err) = fs::remove_file("./temp.jxl") { 100 + match err.kind() { 101 + std::io::ErrorKind::NotFound => (), 102 + _ => panic!("Couldn't remove file ./temp.jxl"), 103 + } 104 + } 105 + 106 + if let Err(err) = fs::remove_file("./temp.mp4") { 107 + match err.kind() { 108 + std::io::ErrorKind::NotFound => (), 109 + _ => panic!("Couldn't remove file ./temp.mp4"), 110 + } 111 + } 112 + } 113 + 114 + // Ok(()) 115 + } 116 + 117 + fn convert_image() -> anyhow::Result<(PathBuf, i64)> { 118 + let base_path = PathBuf::from("./temp.dat"); 119 + let converted_path = PathBuf::from("./temp.jxl"); 120 + 121 + let mut command = &mut Command::new("cjxl"); 122 + 123 + command = command.args([ 124 + base_path 125 + .to_str() 126 + .context("Couldnt convert base_path to str")?, 127 + converted_path 128 + .to_str() 129 + .context("Couldnt convert converted_path to str")?, 130 + "-e", 131 + "11", // Pretty high effort (max is 11) 132 + "-d", 133 + "0", // Lossless 134 + "--allow_expert_options", 135 + "--container", // Encode it with an container format for exif 136 + "1", // confirm 137 + "--brotli_effort", 138 + "11", 139 + // "--lossless_jpeg", // Make it so jpeg can have a little bit of loss 140 + // "1", 141 + // "--num_threads", 142 + // "24", 143 + ]); 144 + 145 + // if kind.extension() != "gif" { 146 + // command = command.arg("--progressive"); // Enable progresive decoding 147 + // } 148 + 149 + let output = command.output()?; 150 + 151 + if !output.status.success() { 152 + bail!("Error while converting image to jxl: {output:?}") 153 + // std::process::exit(0); 154 + } else { 155 + let og_size = match fs::metadata(&base_path) { 156 + Ok(ok) => ok.len(), 157 + Err(err) => { 158 + eprintln!("Error while getting metadata for {base_path:?}: ({err}), ignoring."); 159 + 0 160 + } 161 + }; 162 + 163 + let conv_size = match fs::metadata(&converted_path) { 164 + Ok(ok) => ok.len(), 165 + Err(err) => { 166 + eprintln!( 167 + "Error while getting metadata for {converted_path:?}: ({err}), ignoring." 168 + ); 169 + 0 170 + } 171 + }; 172 + 173 + let diff: i64 = og_size as i64 - conv_size as i64; 174 + 175 + println!( 176 + "Converted image successfully! -{diff} ~{}%", 177 + (conv_size as f64 / og_size as f64 * 100f64) as u64 178 + ); 179 + 180 + Ok((converted_path, diff)) 181 + } 182 + } 183 + 184 + fn convert_video() -> anyhow::Result<(PathBuf, i64)> { 185 + let base_path = PathBuf::from("./temp.dat"); 186 + let converted_path = PathBuf::from("./temp.mp4"); 187 + 188 + let output = Command::new("ffmpeg") 189 + .args([ 190 + "-y", 191 + "-i", 192 + base_path 193 + .to_str() 194 + .context("Couldnt convert base_path to str")?, 195 + "-c:v", 196 + "librav1e", 197 + "-qp", 198 + "40", 199 + "-speed", 200 + "2", 201 + converted_path 202 + .to_str() 203 + .context("Couldnt convert converted_path to str")?, 204 + ]) 205 + .output()?; 206 + 207 + if !output.status.success() { 208 + bail!("Error while converting {base_path:?} to av1: {output:?}"); 209 + } else { 210 + let og_size = fs::metadata(&base_path)?.len(); 211 + let conv_size = fs::metadata(&converted_path)?.len(); 212 + 213 + let diff: i64 = og_size as i64 - conv_size as i64; 214 + 215 + println!( 216 + "Converted video successfully! -{diff} ~{}%", 217 + (conv_size as f64 / og_size as f64 * 100f64) as u64 218 + ); 219 + 220 + Ok((converted_path, diff)) 221 + } 222 + } 223 + 224 + fn restore_exif(converted_path: &PathBuf, exiftool: &mut ExifTool) -> anyhow::Result<()> { 225 + let base_path = PathBuf::from("./temp.dat"); 226 + 227 + // Copy over da exif 228 + let args = &[ 229 + // "-T", 230 + "-overwrite_original", // Don't make a copy with _original appended to it 231 + "-m", // Ignore minor errors like: Error: [minor] IFD1 pointer references previous BrotliEXIF directory - ./temp.jxl 232 + "-TagsFromFile", 233 + base_path 234 + .to_str() 235 + .context("Couldnt convert base_path to str")?, 236 + "-all:all", 237 + "-FileModifyDate<FileModifyDate", // Also copy the File Modification Date/Time 238 + converted_path 239 + .to_str() 240 + .context("Couldnt convert converted_path to str")?, 241 + ]; 242 + 243 + let out = exiftool.execute_lines(args); 244 + 245 + // Make sure exiftool did its thing correctly 246 + if let Err(err) = out { 247 + bail!("Error while restoring exif data! {err}"); 248 + } else { 249 + println!("Restored exif data to {converted_path:?} succesfully!"); 250 + } 251 + 252 + Ok(()) 253 + }
+7
converter-lib/Cargo.toml
··· 1 + [package] 2 + name = "converter-lib" 3 + version = "0.1.0" 4 + edition = "2024" 5 + 6 + [dependencies] 7 + anyhow = "1.0.99"
+199
converter-lib/src/lib.rs
··· 1 + // pub fn add(left: u64, right: u64) -> u64 { 2 + // left + right 3 + // } 4 + 5 + // #[cfg(test)] 6 + // mod tests { 7 + // use super::*; 8 + 9 + // #[test] 10 + // fn it_works() { 11 + // let result = add(2, 2); 12 + // assert_eq!(result, 4); 13 + // } 14 + // } 15 + 16 + use std::{fmt, net::SocketAddr, vec}; 17 + 18 + #[derive(Debug)] 19 + pub enum JobMessage { 20 + ImageJob(Vec<u8>), 21 + VideoJob(Vec<u8>), 22 + // StopJob 23 + } 24 + 25 + impl std::fmt::Display for JobMessage { 26 + // This trait requires `fmt` with this exact signature. 27 + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 28 + // Write strictly the first element into the supplied output 29 + // stream: `f`. Returns `fmt::Result` which indicates whether the 30 + // operation succeeded or failed. Note that `write!` uses syntax which 31 + // is very similar to `println!`. 32 + match self { 33 + JobMessage::ImageJob(items) => { 34 + write!(f, "JobMessage::ImageJob({} bytes)", items.len()) 35 + } 36 + JobMessage::VideoJob(items) => { 37 + write!(f, "JobMessage::VideoJob({} bytes)", items.len()) 38 + } 39 + } 40 + } 41 + } 42 + 43 + impl JobMessage { 44 + pub fn into_bytes(self) -> Vec<u8> { 45 + let mut buffer = vec![]; 46 + 47 + match self { 48 + JobMessage::ImageJob(mut data) => { 49 + buffer.push(1); // messsage type 50 + buffer.push(0); // padding 51 + 52 + buffer.append(&mut data); // message 53 + } 54 + JobMessage::VideoJob(mut data) => { 55 + buffer.push(2); // messsage type 56 + buffer.push(0); // padding 57 + 58 + buffer.append(&mut data); // message 59 + } 60 + } 61 + 62 + // Append size to the beginning of the buffer 63 + let size: [u8; 8] = (buffer.len() as u64).to_be_bytes(); 64 + buffer.splice(..0, size); 65 + 66 + buffer 67 + } 68 + 69 + pub fn from_bytes(buffer: Vec<u8>) -> Option<Self> { 70 + // Check if the buffer is big enough 71 + let buf_size = buffer.len(); 72 + if buf_size < 2 { 73 + return None; 74 + } 75 + 76 + // Get payload 77 + let payload = buffer[2..buf_size].to_vec(); 78 + 79 + match buffer.first()? { 80 + 1 => Some(JobMessage::ImageJob(payload)), 81 + 2 => Some(JobMessage::VideoJob(payload)), 82 + _ => None, 83 + } 84 + } 85 + } 86 + 87 + #[derive(Debug)] 88 + pub struct ClientMessage { 89 + pub message: ClientMessageType, 90 + pub addr: SocketAddr, 91 + } 92 + 93 + // #[derive(Debug)] 94 + pub enum ClientMessageType { 95 + CompletedJob((Vec<u8>, i64)), 96 + FailedJob(String), 97 + AbortedJob(String), 98 + Ready, 99 + // Busy, 100 + } 101 + 102 + impl std::fmt::Debug for ClientMessageType { 103 + fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { 104 + match self { 105 + ClientMessageType::CompletedJob((items, saved_bytes)) => { 106 + write!( 107 + f, 108 + "ClientMessageType::CompletedJob({} Bytes, {saved_bytes} Bytes)", 109 + items.len() 110 + ) 111 + } 112 + ClientMessageType::FailedJob(reason) => { 113 + write!(f, "ClientMessageType::FailedJob(\"{reason}\")") 114 + } 115 + ClientMessageType::AbortedJob(reason) => { 116 + write!(f, "ClientMessageType::AbortedJob(\"{reason}\")") 117 + } 118 + ClientMessageType::Ready => { 119 + write!(f, "ClientMessageType::Ready") 120 + } 121 + } 122 + } 123 + } 124 + 125 + impl ClientMessageType { 126 + pub fn into_bytes(self) -> Vec<u8> { 127 + let mut buffer = vec![]; 128 + 129 + match self { 130 + ClientMessageType::CompletedJob((mut data, saved_bytes)) => { 131 + buffer.push(10); // messsage type 132 + buffer.push(0); // padding 133 + 134 + let saved_bytes: [u8; 8] = saved_bytes.to_be_bytes(); 135 + buffer.extend_from_slice(&saved_bytes); 136 + 137 + buffer.append(&mut data); 138 + } 139 + ClientMessageType::FailedJob(data) => { 140 + buffer.push(110); // messsage type 141 + buffer.push(0); // padding 142 + 143 + buffer.append(&mut data.into_bytes()); // message 144 + } 145 + ClientMessageType::AbortedJob(data) => { 146 + buffer.push(120); // messsage type 147 + buffer.push(0); // padding 148 + 149 + buffer.append(&mut data.into_bytes()); // message 150 + } 151 + ClientMessageType::Ready => { 152 + buffer.push(200); 153 + buffer.push(0); // padding 154 + } 155 + } 156 + 157 + // Append size to the beginning of the buffer 158 + let size: [u8; 8] = (buffer.len() as u64).to_be_bytes(); 159 + buffer.splice(..0, size); 160 + 161 + buffer 162 + } 163 + 164 + pub fn from_bytes(buffer: Vec<u8>) -> Option<Self> { 165 + // Check if the buffer is big enough 166 + let buf_size = buffer.len(); 167 + if buf_size < 2 { 168 + return None; 169 + } 170 + 171 + // Get payload 172 + let payload = buffer[2..buf_size].to_vec(); 173 + 174 + match buffer.first()? { 175 + 10 => { 176 + let saved_bytes = { 177 + let buffer: [u8; 8] = payload[0..8].try_into().ok()?; 178 + 179 + i64::from_be_bytes(buffer) 180 + }; 181 + 182 + Some(ClientMessageType::CompletedJob(( 183 + payload[8..].to_vec(), 184 + saved_bytes, 185 + ))) 186 + } 187 + 110 => { 188 + let string = String::from_utf8(payload).ok()?; 189 + Some(ClientMessageType::FailedJob(string)) 190 + } 191 + 120 => { 192 + let string = String::from_utf8(payload).ok()?; 193 + Some(ClientMessageType::AbortedJob(string)) 194 + } 195 + 200 => Some(ClientMessageType::Ready), 196 + _ => None, 197 + } 198 + } 199 + }
+9
converter-server/Cargo.toml
··· 1 + [package] 2 + name = "converter-server" 3 + version = "0.1.0" 4 + edition = "2024" 5 + 6 + [dependencies] 7 + anyhow = "1.0.99" 8 + converter-lib = { version = "0.1.0", path = "../converter-lib" } 9 + infer = "0.19.0"
+69
converter-server/src/client.rs
··· 1 + use std::{ 2 + io::{Read, Write}, 3 + net::{SocketAddr, TcpStream}, 4 + sync::mpsc, 5 + thread, 6 + }; 7 + 8 + use anyhow::{Context, bail}; 9 + use converter_lib::{ClientMessage, ClientMessageType, JobMessage}; 10 + 11 + pub fn client_loop( 12 + stream: TcpStream, 13 + job_recv: mpsc::Receiver<JobMessage>, 14 + message_sender: mpsc::Sender<ClientMessage>, 15 + addr: SocketAddr, 16 + ) -> anyhow::Result<()> { 17 + println!("Created client thread"); 18 + 19 + let mut reader = stream.try_clone()?; 20 + let mut writer = stream; 21 + 22 + // Writer thread 23 + thread::spawn(move || -> anyhow::Result<()> { 24 + loop { 25 + // Check if there are any jobs that need to be send, and send them. 26 + match job_recv.recv() { 27 + Ok(job) => { 28 + println!("Sent new job ({job}) to {addr}"); 29 + // Wait on a new job and send it to the client 30 + let bytes = job.into_bytes(); 31 + 32 + writer.write_all(&bytes)?; 33 + } 34 + Err(err) => bail!("Error while recieving job! {err}"), 35 + } 36 + } 37 + }); 38 + 39 + // Reader thread 40 + loop { 41 + let payload_size = { 42 + let mut buffer = [0; 8]; 43 + 44 + reader.read_exact(&mut buffer)?; 45 + 46 + u64::from_be_bytes(buffer) as usize 47 + }; 48 + 49 + // Read the returned data 50 + if payload_size > 4000000000 { 51 + // Might cause reads to become fucked 52 + println!("payload size of more than 4GB!"); 53 + continue; 54 + } 55 + 56 + let mut payload = vec![0u8; payload_size]; 57 + reader.read_exact(&mut payload)?; 58 + 59 + // Get the message and pass it over to the main thread 60 + let message = ClientMessageType::from_bytes(payload).context("context")?; 61 + println!("Got a message from {addr} with: {message:?}"); 62 + 63 + let client_message = ClientMessage { message, addr }; 64 + message_sender.send(client_message)?; 65 + // println!("Message forwarded succesfully"); 66 + } 67 + 68 + // Ok(()) 69 + }
+73
converter-server/src/data_structs.rs
··· 1 + use std::{ 2 + collections::HashMap, 3 + net::SocketAddr, 4 + sync::{ 5 + RwLock, 6 + atomic::{AtomicI64, AtomicU64}, 7 + mpsc::Sender, 8 + }, 9 + vec, 10 + }; 11 + 12 + use converter_lib::JobMessage; 13 + 14 + use crate::media::Media; 15 + 16 + pub struct AppState { 17 + pub clients: RwLock<Vec<Client>>, 18 + pub media: RwLock<Vec<Media>>, 19 + pub processing: RwLock<HashMap<SocketAddr, Media>>, 20 + pub total_files: AtomicU64, 21 + pub completed_files: AtomicU64, 22 + pub saved_bytes: AtomicI64, 23 + } 24 + 25 + // pub struct ProcessingMedia { 26 + // data: HashMap<SocketAddr, Media>, 27 + // } 28 + 29 + // impl ProcessingMedia { 30 + // pub fn new() -> ProcessingMedia { 31 + // ProcessingMedia { 32 + // data: HashMap::new(), 33 + // } 34 + // } 35 + 36 + // pub fn take_media(&mut self, addr: &SocketAddr) -> Option<Media> { 37 + // self.data.remove(addr) 38 + // } 39 + 40 + // pub fn add_media(&mut self, addr: &SocketAddr) { 41 + // self.data.ins 42 + // } 43 + // } 44 + 45 + impl AppState { 46 + pub fn new() -> AppState { 47 + AppState { 48 + clients: RwLock::new(vec![]), 49 + media: RwLock::new(vec![]), 50 + processing: RwLock::new(HashMap::new()), 51 + total_files: AtomicU64::new(0), 52 + completed_files: AtomicU64::new(0), 53 + saved_bytes: AtomicI64::new(0), 54 + } 55 + } 56 + 57 + // pub fn remove_processing_media() {} 58 + } 59 + 60 + #[derive(Debug)] 61 + pub struct Client { 62 + pub state: ClientState, 63 + pub addr: SocketAddr, 64 + pub sender: Sender<JobMessage>, 65 + } 66 + 67 + #[derive(Debug)] 68 + pub enum ClientState { 69 + Ready, 70 + UnReady, 71 + Busy, 72 + Initializing, 73 + }
+266
converter-server/src/main.rs
··· 1 + mod client; 2 + mod data_structs; 3 + mod media; 4 + use std::{ 5 + fs, 6 + net::TcpListener, 7 + sync::{ 8 + Arc, 9 + atomic::Ordering, 10 + mpsc::{self, Receiver, Sender}, 11 + }, 12 + thread, 13 + time::Instant, 14 + }; 15 + 16 + use anyhow::bail; 17 + use converter_lib::{ClientMessage, JobMessage}; 18 + 19 + use crate::data_structs::{AppState, Client, ClientState}; 20 + 21 + fn main() -> anyhow::Result<()> { 22 + println!("Hello, world!"); 23 + 24 + let state = Arc::new(AppState::new()); 25 + 26 + // Index images 27 + { 28 + let (mut media, converted_images, saved_bytes) = media::collect_media("./fixed".into())?; 29 + state.media.write().unwrap().append(&mut media); 30 + 31 + state.saved_bytes.fetch_add(saved_bytes, Ordering::Relaxed); 32 + state 33 + .completed_files 34 + .fetch_add(converted_images, Ordering::Relaxed); 35 + 36 + state.total_files.fetch_add( 37 + state.media.read().unwrap().len() as u64 + converted_images, 38 + Ordering::Relaxed, 39 + ); 40 + } 41 + 42 + // For recieving jobs back 43 + let (message_sender, message_recv): (Sender<ClientMessage>, Receiver<ClientMessage>) = 44 + mpsc::channel(); 45 + 46 + // Client messages reciever 47 + { 48 + let state = state.clone(); 49 + 50 + thread::spawn(move || -> anyhow::Result<()> { 51 + let started_at = Instant::now(); 52 + 53 + loop { 54 + let client_message = message_recv.recv()?; 55 + let total_clients = state.clients.read().unwrap().len(); 56 + let addr = client_message.addr; 57 + // println!("Got message!"); 58 + 59 + if let Some(client) = state 60 + .clients 61 + .write() 62 + .unwrap() 63 + .iter_mut() 64 + .find(|x| x.addr == addr) 65 + { 66 + match client_message.message { 67 + converter_lib::ClientMessageType::CompletedJob((data, saved_bytes)) => { 68 + client.state = ClientState::Ready; 69 + // Get the completed job and save it to disk! 70 + 71 + let media = state.processing.write().unwrap().remove(&addr).unwrap(); 72 + fs::create_dir_all(&media.converted_dir)?; 73 + 74 + let mut converted_path = 75 + media.converted_dir.join(media.path.file_name().unwrap()); 76 + 77 + match media.media_type { 78 + media::MediaType::Image => converted_path.set_extension("jxl"), 79 + media::MediaType::Video => converted_path.set_extension("mp4"), 80 + }; 81 + 82 + fs::write(&converted_path, data)?; 83 + 84 + let total_saved_bytes = 85 + state.saved_bytes.fetch_add(saved_bytes, Ordering::Relaxed); 86 + let completed_images = 87 + state.completed_files.fetch_add(1, Ordering::Relaxed); 88 + 89 + let total_images = state.total_files.load(Ordering::Relaxed); 90 + 91 + let time_string = { 92 + let going_for = started_at.elapsed().as_secs_f32(); 93 + 94 + // Guesstimate remaining time 95 + let time_per_num = going_for / completed_images as f32; 96 + let remaining_time = (time_per_num 97 + * (total_images - completed_images) as f32) 98 + .ceil() 99 + as u64; 100 + 101 + let hours = remaining_time / 3600; 102 + let minutes = (remaining_time % 3600) / 60; 103 + let seconds = remaining_time % 60; 104 + 105 + if hours > 0 { 106 + format!("{hours}h {minutes}m {seconds}s") 107 + } else if minutes > 0 { 108 + format!("{minutes}m {seconds}s") 109 + } else { 110 + format!("{seconds}s") 111 + } 112 + }; 113 + 114 + println!( 115 + "Wrote {converted_path:?} from {addr}, Saving {saved_bytes} bytes!\n({total_clients} clients) {total_images}/{completed_images} done! {total_saved_bytes} saved in total! {time_string} remaining!\n", 116 + ); 117 + 118 + // Get a new media and send it to the client 119 + let media = state.media.write().unwrap().pop().unwrap(); 120 + let bytes = fs::read(&media.path).unwrap(); 121 + 122 + let job = match media.media_type { 123 + media::MediaType::Image => JobMessage::ImageJob(bytes), 124 + media::MediaType::Video => JobMessage::VideoJob(bytes), 125 + }; 126 + 127 + client.sender.send(job)?; 128 + 129 + // Set media to processing and set client to busy 130 + state.processing.write().unwrap().insert(client.addr, media); 131 + client.state = ClientState::Busy; 132 + } 133 + converter_lib::ClientMessageType::FailedJob(reason) => { 134 + // Remove media from processing 135 + let media = state.processing.write().unwrap().remove(&addr).unwrap(); 136 + 137 + println!("Job ({:?}) failed with reason ({reason})!", media.path); 138 + 139 + // Move file to fucked path 140 + let fuckedpath = media.fucked_dir.join(media.path.file_name().unwrap()); 141 + 142 + println!("Moving {:?} into fucked_dir to {fuckedpath:?}!", media.path); 143 + 144 + fs::create_dir_all(&media.fucked_dir)?; 145 + fs::rename(media.path, fuckedpath)?; 146 + 147 + // Give new job to client 148 + let media = state.media.write().unwrap().pop().unwrap(); 149 + let bytes = fs::read(&media.path).unwrap(); 150 + 151 + let job = match media.media_type { 152 + media::MediaType::Image => JobMessage::ImageJob(bytes), 153 + media::MediaType::Video => JobMessage::VideoJob(bytes), 154 + }; 155 + 156 + client.sender.send(job)?; 157 + 158 + // Set media to processing and set client to busy 159 + state.processing.write().unwrap().insert(client.addr, media); 160 + client.state = ClientState::Busy; 161 + } 162 + converter_lib::ClientMessageType::AbortedJob(reason) => { 163 + client.state = ClientState::UnReady; 164 + 165 + // Remove media from processing 166 + let media = state.processing.write().unwrap().remove(&addr).unwrap(); 167 + 168 + println!( 169 + "{} aborted their job with reason ({})! Making {:?} available.", 170 + client.addr, reason, media.path 171 + ); 172 + 173 + // Make it available again 174 + state.media.write().unwrap().push(media) 175 + } 176 + converter_lib::ClientMessageType::Ready => { 177 + client.state = ClientState::Ready; 178 + 179 + // Get a new media and send it to the client 180 + let media = state.media.write().unwrap().pop().unwrap(); 181 + let bytes = fs::read(&media.path).unwrap(); 182 + 183 + let job = match media.media_type { 184 + media::MediaType::Image => JobMessage::ImageJob(bytes), 185 + media::MediaType::Video => JobMessage::VideoJob(bytes), 186 + }; 187 + 188 + client.sender.send(job)?; 189 + // println!("Job sent to {addr} succefully!"); 190 + 191 + state.processing.write().unwrap().insert(client.addr, media); 192 + client.state = ClientState::Busy; 193 + } 194 + }; 195 + } else { 196 + println!( 197 + "Couldnt find a client with address {addr} in {:?}!", 198 + state.clients.read().unwrap() 199 + ); 200 + 201 + continue; 202 + } 203 + } 204 + }); 205 + } 206 + 207 + // Listen loop 208 + let listener = TcpListener::bind("0.0.0.0:33693")?; 209 + 210 + for stream in listener.incoming() { 211 + println!("New connection incoming!"); 212 + 213 + let res = (|| -> anyhow::Result<()> { 214 + match stream { 215 + Ok(stream) => { 216 + // Channels 217 + let (job_sender, job_recv): (Sender<JobMessage>, Receiver<JobMessage>) = 218 + mpsc::channel(); 219 + let message_sender = message_sender.clone(); 220 + let addr = stream.peer_addr()?; 221 + 222 + // Create the client 223 + let client = Client { 224 + state: ClientState::Initializing, 225 + addr, 226 + sender: job_sender, 227 + }; 228 + 229 + state.clients.write().unwrap().push(client); 230 + 231 + let state = state.clone(); 232 + 233 + // Create the handle loop for the client 234 + thread::spawn(move || { 235 + let client_loop = 236 + client::client_loop(stream, job_recv, message_sender, addr); 237 + 238 + match client_loop { 239 + Ok(_) => println!("Client {addr} exited, cleaning up client."), 240 + Err(err) => eprintln!( 241 + "Error in the client loop for {addr}, cleaning up client! ({err})" 242 + ), 243 + } 244 + 245 + // Remove the client 246 + state.clients.write().unwrap().retain(|x| x.addr != addr); 247 + 248 + // Move any processing media back into available media 249 + if let Some(media) = state.processing.write().unwrap().remove(&addr) { 250 + state.media.write().unwrap().push(media); 251 + } 252 + }); 253 + 254 + Ok(()) 255 + } 256 + Err(err) => bail!("It must've been a ghost in the machine... {err:?}"), 257 + } 258 + })(); 259 + 260 + if let Err(err) = res { 261 + eprintln!("Error while handling a tcp connection! {err}") 262 + } 263 + } 264 + 265 + Ok(()) 266 + }
+122
converter-server/src/media.rs
··· 1 + use std::{ 2 + fs, 3 + path::{Path, PathBuf}, 4 + }; 5 + 6 + use anyhow::bail; 7 + 8 + pub struct Media { 9 + pub path: PathBuf, 10 + pub converted_dir: PathBuf, 11 + pub fucked_dir: PathBuf, 12 + pub media_type: MediaType, 13 + } 14 + 15 + pub enum MediaType { 16 + Image, 17 + Video, 18 + } 19 + 20 + pub fn collect_media(path: PathBuf) -> anyhow::Result<(Vec<Media>, u64, i64)> { 21 + if !path.is_dir() { 22 + println!("{:?}", path.canonicalize()); 23 + bail!("Please provide an directory!") 24 + } 25 + 26 + println!("Starting scanning for media"); 27 + // let files = fs::read_dir(path)?; 28 + 29 + println!("[D]:: {path:?}"); 30 + 31 + let mut images = wurm( 32 + &path, 33 + Path::new("./converted/"), 34 + Path::new("./converted_fucked/"), 35 + )?; 36 + 37 + let mut saved_bytes: i64 = 0; 38 + let mut completed_media: u64 = 0; 39 + 40 + images.retain(|image| { 41 + let mut converted_path = image.converted_dir.join(image.path.file_name().unwrap()); 42 + 43 + match image.media_type { 44 + MediaType::Image => converted_path.set_extension("jxl"), 45 + MediaType::Video => converted_path.set_extension("mp4"), 46 + }; 47 + 48 + // Check if the file exists and set how many bytes are saved. 49 + if let Ok(val) = fs::metadata(converted_path) { 50 + let conv_size = val.len(); 51 + let og_size = fs::metadata(&image.path).unwrap().len(); 52 + 53 + // Set how many bytes are saved 54 + let diff: i64 = og_size as i64 - conv_size as i64; 55 + saved_bytes += diff; 56 + 57 + completed_media += 1; 58 + 59 + false // Converted image found 60 + } else { 61 + true // No converted image found 62 + } 63 + }); 64 + 65 + println!( 66 + "Found {} files!\n{completed_media} Files were already converted! Saving {saved_bytes} Bytes!", 67 + images.len() 68 + ); 69 + 70 + Ok((images, completed_media, saved_bytes)) 71 + } 72 + 73 + fn wurm(dir: &Path, converted_dir: &Path, fucked_dir: &Path) -> anyhow::Result<Vec<Media>> { 74 + // Create the dirs 75 + // let _ = fs::create_dir(converted_dir); 76 + // let _ = fs::create_dir(fucked_dir); 77 + 78 + if !dir.is_dir() { 79 + bail!("The dir isnt a directory?? \"{:?}\"", dir) 80 + } 81 + 82 + let mut images = vec![]; 83 + 84 + for entry in fs::read_dir(dir)? { 85 + let entry = entry?; 86 + let path = entry.path(); 87 + 88 + if path.is_dir() { 89 + let dir = path.file_name().unwrap(); 90 + let converted_dir = converted_dir.join(dir); 91 + let fucked_dir = fucked_dir.join(dir); 92 + 93 + println!("[D]:: {path:?}"); 94 + 95 + match wurm(&path, &converted_dir, &fucked_dir) { 96 + Ok(val) => images.extend(val), 97 + Err(err) => eprintln!("Error while worming into path \"{:?}\": {}", &path, err), 98 + } 99 + } else { 100 + let kind = infer::get_from_path(&path)?.expect("file type is unknown"); 101 + 102 + let media_type = match kind.matcher_type() { 103 + infer::MatcherType::Image => MediaType::Image, 104 + infer::MatcherType::Video => MediaType::Video, 105 + _ => { 106 + println!("Skipping {path:?}, Neither a image or video!"); 107 + continue; 108 + } 109 + }; 110 + 111 + images.push(Media { 112 + path, 113 + converted_dir: converted_dir.to_owned(), 114 + fucked_dir: fucked_dir.to_owned(), 115 + media_type, 116 + }); 117 + // info!("Converties"); 118 + } 119 + } 120 + 121 + Ok(images) 122 + }
+44
flake.lock
··· 1 + { 2 + "nodes": { 3 + "exiftool-fix": { 4 + "locked": { 5 + "lastModified": 1754129530, 6 + "narHash": "sha256-Ny64nGB5xDnDdjbTPjabx4da0CnsMr+azskZUODW4eQ=", 7 + "owner": "anthonyroussel", 8 + "repo": "nixpkgs", 9 + "rev": "12569159cde8cd9a89a1bd947f104a10626f5be4", 10 + "type": "github" 11 + }, 12 + "original": { 13 + "owner": "anthonyroussel", 14 + "ref": "update-fix-exiftool", 15 + "repo": "nixpkgs", 16 + "type": "github" 17 + } 18 + }, 19 + "nixpkgs": { 20 + "locked": { 21 + "lastModified": 1755186698, 22 + "narHash": "sha256-wNO3+Ks2jZJ4nTHMuks+cxAiVBGNuEBXsT29Bz6HASo=", 23 + "owner": "nixos", 24 + "repo": "nixpkgs", 25 + "rev": "fbcf476f790d8a217c3eab4e12033dc4a0f6d23c", 26 + "type": "github" 27 + }, 28 + "original": { 29 + "owner": "nixos", 30 + "ref": "nixos-unstable", 31 + "repo": "nixpkgs", 32 + "type": "github" 33 + } 34 + }, 35 + "root": { 36 + "inputs": { 37 + "exiftool-fix": "exiftool-fix", 38 + "nixpkgs": "nixpkgs" 39 + } 40 + } 41 + }, 42 + "root": "root", 43 + "version": 7 44 + }
+71
flake.nix
··· 1 + { 2 + description = "Flake for developing snowstorm!"; 3 + 4 + inputs = { 5 + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 6 + exiftool-fix.url = "github:anthonyroussel/nixpkgs/update-fix-exiftool"; 7 + }; 8 + 9 + outputs = 10 + { 11 + nixpkgs, 12 + exiftool-fix, 13 + ... 14 + }: 15 + let 16 + system = "x86_64-linux"; 17 + pkgs = import nixpkgs { 18 + inherit system; 19 + }; 20 + project = "gfoto-metadata-fixer"; 21 + in 22 + { 23 + devShells."${system}" = { 24 + default = 25 + let 26 + exiftool = (import exiftool-fix { inherit system; }).exiftool; 27 + in 28 + pkgs.mkShell { 29 + 30 + packages = [ 31 + pkgs.ffmpeg-full 32 + pkgs.libjxl 33 + exiftool 34 + ]; 35 + 36 + shellHook = '' 37 + echo -e "\n\x1b[36;1m📦 Welcome to the default flake for \x1b[32;1m${project}\x1b[0m\x1b[36m!\x1b[0m" 38 + ''; 39 + }; 40 + 41 + windows = 42 + let 43 + windows-mingw32 = import nixpkgs { 44 + localSystem = "${system}"; 45 + crossSystem = { 46 + config = "x86_64-w64-mingw32"; 47 + }; 48 + }; 49 + in 50 + pkgs.mkShell { 51 + packages = with pkgs; [ 52 + windows-mingw32.buildPackages.gcc 53 + rustPlatform.bindgenHook 54 + ]; 55 + 56 + LD_LIBRARY_PATH = 57 + with pkgs; 58 + lib.makeLibraryPath [ 59 + windows-mingw32.buildPackages.gcc 60 + ]; 61 + 62 + # Needed to fix issues with pthread 63 + CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS = "-L native=${pkgs.pkgsCross.mingwW64.windows.pthreads}/lib"; 64 + 65 + shellHook = '' 66 + echo "\n\x1b[36;1m📦 Welcome to the windows flake for \x1b[32;1m${project}\x1b[0m\x1b[36;1m!\x1b[0m 67 + ''; 68 + }; 69 + }; 70 + }; 71 + }
+953
gfoto-fixer/Cargo.lock
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 4 4 + 5 + [[package]] 6 + name = "aho-corasick" 7 + version = "1.1.3" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 + dependencies = [ 11 + "memchr", 12 + ] 13 + 14 + [[package]] 15 + name = "android-tzdata" 16 + version = "0.1.1" 17 + source = "registry+https://github.com/rust-lang/crates.io-index" 18 + checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 19 + 20 + [[package]] 21 + name = "android_system_properties" 22 + version = "0.1.5" 23 + source = "registry+https://github.com/rust-lang/crates.io-index" 24 + checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 25 + dependencies = [ 26 + "libc", 27 + ] 28 + 29 + [[package]] 30 + name = "anstream" 31 + version = "0.6.19" 32 + source = "registry+https://github.com/rust-lang/crates.io-index" 33 + checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" 34 + dependencies = [ 35 + "anstyle", 36 + "anstyle-parse", 37 + "anstyle-query", 38 + "anstyle-wincon", 39 + "colorchoice", 40 + "is_terminal_polyfill", 41 + "utf8parse", 42 + ] 43 + 44 + [[package]] 45 + name = "anstyle" 46 + version = "1.0.11" 47 + source = "registry+https://github.com/rust-lang/crates.io-index" 48 + checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" 49 + 50 + [[package]] 51 + name = "anstyle-parse" 52 + version = "0.2.7" 53 + source = "registry+https://github.com/rust-lang/crates.io-index" 54 + checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 55 + dependencies = [ 56 + "utf8parse", 57 + ] 58 + 59 + [[package]] 60 + name = "anstyle-query" 61 + version = "1.1.3" 62 + source = "registry+https://github.com/rust-lang/crates.io-index" 63 + checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" 64 + dependencies = [ 65 + "windows-sys", 66 + ] 67 + 68 + [[package]] 69 + name = "anstyle-wincon" 70 + version = "3.0.9" 71 + source = "registry+https://github.com/rust-lang/crates.io-index" 72 + checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" 73 + dependencies = [ 74 + "anstyle", 75 + "once_cell_polyfill", 76 + "windows-sys", 77 + ] 78 + 79 + [[package]] 80 + name = "anyhow" 81 + version = "1.0.98" 82 + source = "registry+https://github.com/rust-lang/crates.io-index" 83 + checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 84 + 85 + [[package]] 86 + name = "autocfg" 87 + version = "1.5.0" 88 + source = "registry+https://github.com/rust-lang/crates.io-index" 89 + checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 90 + 91 + [[package]] 92 + name = "bitflags" 93 + version = "2.9.1" 94 + source = "registry+https://github.com/rust-lang/crates.io-index" 95 + checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 96 + 97 + [[package]] 98 + name = "bumpalo" 99 + version = "3.19.0" 100 + source = "registry+https://github.com/rust-lang/crates.io-index" 101 + checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 102 + 103 + [[package]] 104 + name = "byteorder" 105 + version = "1.5.0" 106 + source = "registry+https://github.com/rust-lang/crates.io-index" 107 + checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 108 + 109 + [[package]] 110 + name = "cc" 111 + version = "1.2.27" 112 + source = "registry+https://github.com/rust-lang/crates.io-index" 113 + checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" 114 + dependencies = [ 115 + "shlex", 116 + ] 117 + 118 + [[package]] 119 + name = "cfb" 120 + version = "0.7.3" 121 + source = "registry+https://github.com/rust-lang/crates.io-index" 122 + checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" 123 + dependencies = [ 124 + "byteorder", 125 + "fnv", 126 + "uuid", 127 + ] 128 + 129 + [[package]] 130 + name = "cfg-if" 131 + version = "1.0.1" 132 + source = "registry+https://github.com/rust-lang/crates.io-index" 133 + checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 134 + 135 + [[package]] 136 + name = "chrono" 137 + version = "0.4.41" 138 + source = "registry+https://github.com/rust-lang/crates.io-index" 139 + checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" 140 + dependencies = [ 141 + "android-tzdata", 142 + "iana-time-zone", 143 + "js-sys", 144 + "num-traits", 145 + "serde", 146 + "wasm-bindgen", 147 + "windows-link", 148 + ] 149 + 150 + [[package]] 151 + name = "clap" 152 + version = "4.5.40" 153 + source = "registry+https://github.com/rust-lang/crates.io-index" 154 + checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" 155 + dependencies = [ 156 + "clap_builder", 157 + "clap_derive", 158 + ] 159 + 160 + [[package]] 161 + name = "clap_builder" 162 + version = "4.5.40" 163 + source = "registry+https://github.com/rust-lang/crates.io-index" 164 + checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" 165 + dependencies = [ 166 + "anstream", 167 + "anstyle", 168 + "clap_lex", 169 + "strsim", 170 + ] 171 + 172 + [[package]] 173 + name = "clap_derive" 174 + version = "4.5.40" 175 + source = "registry+https://github.com/rust-lang/crates.io-index" 176 + checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" 177 + dependencies = [ 178 + "heck", 179 + "proc-macro2", 180 + "quote", 181 + "syn", 182 + ] 183 + 184 + [[package]] 185 + name = "clap_lex" 186 + version = "0.7.5" 187 + source = "registry+https://github.com/rust-lang/crates.io-index" 188 + checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" 189 + 190 + [[package]] 191 + name = "colorchoice" 192 + version = "1.0.4" 193 + source = "registry+https://github.com/rust-lang/crates.io-index" 194 + checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 195 + 196 + [[package]] 197 + name = "core-foundation-sys" 198 + version = "0.8.7" 199 + source = "registry+https://github.com/rust-lang/crates.io-index" 200 + checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 201 + 202 + [[package]] 203 + name = "crossbeam-deque" 204 + version = "0.8.6" 205 + source = "registry+https://github.com/rust-lang/crates.io-index" 206 + checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" 207 + dependencies = [ 208 + "crossbeam-epoch", 209 + "crossbeam-utils", 210 + ] 211 + 212 + [[package]] 213 + name = "crossbeam-epoch" 214 + version = "0.9.18" 215 + source = "registry+https://github.com/rust-lang/crates.io-index" 216 + checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 217 + dependencies = [ 218 + "crossbeam-utils", 219 + ] 220 + 221 + [[package]] 222 + name = "crossbeam-utils" 223 + version = "0.8.21" 224 + source = "registry+https://github.com/rust-lang/crates.io-index" 225 + checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 226 + 227 + [[package]] 228 + name = "either" 229 + version = "1.15.0" 230 + source = "registry+https://github.com/rust-lang/crates.io-index" 231 + checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 232 + 233 + [[package]] 234 + name = "env_filter" 235 + version = "0.1.3" 236 + source = "registry+https://github.com/rust-lang/crates.io-index" 237 + checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" 238 + dependencies = [ 239 + "log", 240 + ] 241 + 242 + [[package]] 243 + name = "env_logger" 244 + version = "0.11.8" 245 + source = "registry+https://github.com/rust-lang/crates.io-index" 246 + checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" 247 + dependencies = [ 248 + "anstream", 249 + "anstyle", 250 + "env_filter", 251 + "jiff", 252 + "log", 253 + ] 254 + 255 + [[package]] 256 + name = "errno" 257 + version = "0.3.13" 258 + source = "registry+https://github.com/rust-lang/crates.io-index" 259 + checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" 260 + dependencies = [ 261 + "libc", 262 + "windows-sys", 263 + ] 264 + 265 + [[package]] 266 + name = "exiftool" 267 + version = "0.2.5" 268 + source = "registry+https://github.com/rust-lang/crates.io-index" 269 + checksum = "376731c80e403b70e189602f1f69fac6f6daf3e5028d40708abf1a2bb8dd6a4e" 270 + dependencies = [ 271 + "chrono", 272 + "log", 273 + "serde", 274 + "serde_json", 275 + "serde_path_to_error", 276 + "tempfile", 277 + "thiserror", 278 + ] 279 + 280 + [[package]] 281 + name = "fastrand" 282 + version = "2.3.0" 283 + source = "registry+https://github.com/rust-lang/crates.io-index" 284 + checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 285 + 286 + [[package]] 287 + name = "fnv" 288 + version = "1.0.7" 289 + source = "registry+https://github.com/rust-lang/crates.io-index" 290 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 291 + 292 + [[package]] 293 + name = "getrandom" 294 + version = "0.3.3" 295 + source = "registry+https://github.com/rust-lang/crates.io-index" 296 + checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 297 + dependencies = [ 298 + "cfg-if", 299 + "libc", 300 + "r-efi", 301 + "wasi", 302 + ] 303 + 304 + [[package]] 305 + name = "gfoto-metadata-fixer" 306 + version = "0.1.0" 307 + dependencies = [ 308 + "anyhow", 309 + "clap", 310 + "env_logger", 311 + "exiftool", 312 + "infer", 313 + "lazy-regex", 314 + "log", 315 + "rayon", 316 + "regex", 317 + "serde", 318 + "serde_json", 319 + "snowy_libs", 320 + ] 321 + 322 + [[package]] 323 + name = "heck" 324 + version = "0.5.0" 325 + source = "registry+https://github.com/rust-lang/crates.io-index" 326 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 327 + 328 + [[package]] 329 + name = "iana-time-zone" 330 + version = "0.1.63" 331 + source = "registry+https://github.com/rust-lang/crates.io-index" 332 + checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" 333 + dependencies = [ 334 + "android_system_properties", 335 + "core-foundation-sys", 336 + "iana-time-zone-haiku", 337 + "js-sys", 338 + "log", 339 + "wasm-bindgen", 340 + "windows-core", 341 + ] 342 + 343 + [[package]] 344 + name = "iana-time-zone-haiku" 345 + version = "0.1.2" 346 + source = "registry+https://github.com/rust-lang/crates.io-index" 347 + checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 348 + dependencies = [ 349 + "cc", 350 + ] 351 + 352 + [[package]] 353 + name = "infer" 354 + version = "0.19.0" 355 + source = "registry+https://github.com/rust-lang/crates.io-index" 356 + checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" 357 + dependencies = [ 358 + "cfb", 359 + ] 360 + 361 + [[package]] 362 + name = "is_terminal_polyfill" 363 + version = "1.70.1" 364 + source = "registry+https://github.com/rust-lang/crates.io-index" 365 + checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 366 + 367 + [[package]] 368 + name = "itoa" 369 + version = "1.0.15" 370 + source = "registry+https://github.com/rust-lang/crates.io-index" 371 + checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 372 + 373 + [[package]] 374 + name = "jiff" 375 + version = "0.2.15" 376 + source = "registry+https://github.com/rust-lang/crates.io-index" 377 + checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" 378 + dependencies = [ 379 + "jiff-static", 380 + "log", 381 + "portable-atomic", 382 + "portable-atomic-util", 383 + "serde", 384 + ] 385 + 386 + [[package]] 387 + name = "jiff-static" 388 + version = "0.2.15" 389 + source = "registry+https://github.com/rust-lang/crates.io-index" 390 + checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" 391 + dependencies = [ 392 + "proc-macro2", 393 + "quote", 394 + "syn", 395 + ] 396 + 397 + [[package]] 398 + name = "js-sys" 399 + version = "0.3.77" 400 + source = "registry+https://github.com/rust-lang/crates.io-index" 401 + checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 402 + dependencies = [ 403 + "once_cell", 404 + "wasm-bindgen", 405 + ] 406 + 407 + [[package]] 408 + name = "lazy-regex" 409 + version = "3.4.1" 410 + source = "registry+https://github.com/rust-lang/crates.io-index" 411 + checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126" 412 + dependencies = [ 413 + "lazy-regex-proc_macros", 414 + "once_cell", 415 + "regex", 416 + ] 417 + 418 + [[package]] 419 + name = "lazy-regex-proc_macros" 420 + version = "3.4.1" 421 + source = "registry+https://github.com/rust-lang/crates.io-index" 422 + checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1" 423 + dependencies = [ 424 + "proc-macro2", 425 + "quote", 426 + "regex", 427 + "syn", 428 + ] 429 + 430 + [[package]] 431 + name = "libc" 432 + version = "0.2.174" 433 + source = "registry+https://github.com/rust-lang/crates.io-index" 434 + checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" 435 + 436 + [[package]] 437 + name = "linux-raw-sys" 438 + version = "0.9.4" 439 + source = "registry+https://github.com/rust-lang/crates.io-index" 440 + checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" 441 + 442 + [[package]] 443 + name = "log" 444 + version = "0.4.27" 445 + source = "registry+https://github.com/rust-lang/crates.io-index" 446 + checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 447 + 448 + [[package]] 449 + name = "memchr" 450 + version = "2.7.5" 451 + source = "registry+https://github.com/rust-lang/crates.io-index" 452 + checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 453 + 454 + [[package]] 455 + name = "num-traits" 456 + version = "0.2.19" 457 + source = "registry+https://github.com/rust-lang/crates.io-index" 458 + checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 459 + dependencies = [ 460 + "autocfg", 461 + ] 462 + 463 + [[package]] 464 + name = "once_cell" 465 + version = "1.21.3" 466 + source = "registry+https://github.com/rust-lang/crates.io-index" 467 + checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 468 + 469 + [[package]] 470 + name = "once_cell_polyfill" 471 + version = "1.70.1" 472 + source = "registry+https://github.com/rust-lang/crates.io-index" 473 + checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 474 + 475 + [[package]] 476 + name = "portable-atomic" 477 + version = "1.11.1" 478 + source = "registry+https://github.com/rust-lang/crates.io-index" 479 + checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 480 + 481 + [[package]] 482 + name = "portable-atomic-util" 483 + version = "0.2.4" 484 + source = "registry+https://github.com/rust-lang/crates.io-index" 485 + checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" 486 + dependencies = [ 487 + "portable-atomic", 488 + ] 489 + 490 + [[package]] 491 + name = "proc-macro2" 492 + version = "1.0.95" 493 + source = "registry+https://github.com/rust-lang/crates.io-index" 494 + checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 495 + dependencies = [ 496 + "unicode-ident", 497 + ] 498 + 499 + [[package]] 500 + name = "quote" 501 + version = "1.0.40" 502 + source = "registry+https://github.com/rust-lang/crates.io-index" 503 + checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 504 + dependencies = [ 505 + "proc-macro2", 506 + ] 507 + 508 + [[package]] 509 + name = "r-efi" 510 + version = "5.3.0" 511 + source = "registry+https://github.com/rust-lang/crates.io-index" 512 + checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 513 + 514 + [[package]] 515 + name = "rayon" 516 + version = "1.10.0" 517 + source = "registry+https://github.com/rust-lang/crates.io-index" 518 + checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 519 + dependencies = [ 520 + "either", 521 + "rayon-core", 522 + ] 523 + 524 + [[package]] 525 + name = "rayon-core" 526 + version = "1.12.1" 527 + source = "registry+https://github.com/rust-lang/crates.io-index" 528 + checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 529 + dependencies = [ 530 + "crossbeam-deque", 531 + "crossbeam-utils", 532 + ] 533 + 534 + [[package]] 535 + name = "regex" 536 + version = "1.11.1" 537 + source = "registry+https://github.com/rust-lang/crates.io-index" 538 + checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 539 + dependencies = [ 540 + "aho-corasick", 541 + "memchr", 542 + "regex-automata", 543 + "regex-syntax", 544 + ] 545 + 546 + [[package]] 547 + name = "regex-automata" 548 + version = "0.4.9" 549 + source = "registry+https://github.com/rust-lang/crates.io-index" 550 + checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 551 + dependencies = [ 552 + "aho-corasick", 553 + "memchr", 554 + "regex-syntax", 555 + ] 556 + 557 + [[package]] 558 + name = "regex-syntax" 559 + version = "0.8.5" 560 + source = "registry+https://github.com/rust-lang/crates.io-index" 561 + checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 562 + 563 + [[package]] 564 + name = "rustix" 565 + version = "1.0.7" 566 + source = "registry+https://github.com/rust-lang/crates.io-index" 567 + checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" 568 + dependencies = [ 569 + "bitflags", 570 + "errno", 571 + "libc", 572 + "linux-raw-sys", 573 + "windows-sys", 574 + ] 575 + 576 + [[package]] 577 + name = "rustversion" 578 + version = "1.0.21" 579 + source = "registry+https://github.com/rust-lang/crates.io-index" 580 + checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" 581 + 582 + [[package]] 583 + name = "ryu" 584 + version = "1.0.20" 585 + source = "registry+https://github.com/rust-lang/crates.io-index" 586 + checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 587 + 588 + [[package]] 589 + name = "serde" 590 + version = "1.0.219" 591 + source = "registry+https://github.com/rust-lang/crates.io-index" 592 + checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 593 + dependencies = [ 594 + "serde_derive", 595 + ] 596 + 597 + [[package]] 598 + name = "serde_derive" 599 + version = "1.0.219" 600 + source = "registry+https://github.com/rust-lang/crates.io-index" 601 + checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 602 + dependencies = [ 603 + "proc-macro2", 604 + "quote", 605 + "syn", 606 + ] 607 + 608 + [[package]] 609 + name = "serde_json" 610 + version = "1.0.140" 611 + source = "registry+https://github.com/rust-lang/crates.io-index" 612 + checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 613 + dependencies = [ 614 + "itoa", 615 + "memchr", 616 + "ryu", 617 + "serde", 618 + ] 619 + 620 + [[package]] 621 + name = "serde_path_to_error" 622 + version = "0.1.17" 623 + source = "registry+https://github.com/rust-lang/crates.io-index" 624 + checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" 625 + dependencies = [ 626 + "itoa", 627 + "serde", 628 + ] 629 + 630 + [[package]] 631 + name = "shlex" 632 + version = "1.3.0" 633 + source = "registry+https://github.com/rust-lang/crates.io-index" 634 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 635 + 636 + [[package]] 637 + name = "snowy_libs" 638 + version = "0.1.3" 639 + dependencies = [ 640 + "termsize", 641 + ] 642 + 643 + [[package]] 644 + name = "strsim" 645 + version = "0.11.1" 646 + source = "registry+https://github.com/rust-lang/crates.io-index" 647 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 648 + 649 + [[package]] 650 + name = "syn" 651 + version = "2.0.104" 652 + source = "registry+https://github.com/rust-lang/crates.io-index" 653 + checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" 654 + dependencies = [ 655 + "proc-macro2", 656 + "quote", 657 + "unicode-ident", 658 + ] 659 + 660 + [[package]] 661 + name = "tempfile" 662 + version = "3.20.0" 663 + source = "registry+https://github.com/rust-lang/crates.io-index" 664 + checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" 665 + dependencies = [ 666 + "fastrand", 667 + "getrandom", 668 + "once_cell", 669 + "rustix", 670 + "windows-sys", 671 + ] 672 + 673 + [[package]] 674 + name = "termsize" 675 + version = "0.1.9" 676 + source = "registry+https://github.com/rust-lang/crates.io-index" 677 + checksum = "6f11ff5c25c172608d5b85e2fb43ee9a6d683a7f4ab7f96ae07b3d8b590368fd" 678 + dependencies = [ 679 + "libc", 680 + "winapi", 681 + ] 682 + 683 + [[package]] 684 + name = "thiserror" 685 + version = "2.0.12" 686 + source = "registry+https://github.com/rust-lang/crates.io-index" 687 + checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 688 + dependencies = [ 689 + "thiserror-impl", 690 + ] 691 + 692 + [[package]] 693 + name = "thiserror-impl" 694 + version = "2.0.12" 695 + source = "registry+https://github.com/rust-lang/crates.io-index" 696 + checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 697 + dependencies = [ 698 + "proc-macro2", 699 + "quote", 700 + "syn", 701 + ] 702 + 703 + [[package]] 704 + name = "unicode-ident" 705 + version = "1.0.18" 706 + source = "registry+https://github.com/rust-lang/crates.io-index" 707 + checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 708 + 709 + [[package]] 710 + name = "utf8parse" 711 + version = "0.2.2" 712 + source = "registry+https://github.com/rust-lang/crates.io-index" 713 + checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 714 + 715 + [[package]] 716 + name = "uuid" 717 + version = "1.17.0" 718 + source = "registry+https://github.com/rust-lang/crates.io-index" 719 + checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" 720 + dependencies = [ 721 + "js-sys", 722 + "wasm-bindgen", 723 + ] 724 + 725 + [[package]] 726 + name = "wasi" 727 + version = "0.14.2+wasi-0.2.4" 728 + source = "registry+https://github.com/rust-lang/crates.io-index" 729 + checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 730 + dependencies = [ 731 + "wit-bindgen-rt", 732 + ] 733 + 734 + [[package]] 735 + name = "wasm-bindgen" 736 + version = "0.2.100" 737 + source = "registry+https://github.com/rust-lang/crates.io-index" 738 + checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 739 + dependencies = [ 740 + "cfg-if", 741 + "once_cell", 742 + "rustversion", 743 + "wasm-bindgen-macro", 744 + ] 745 + 746 + [[package]] 747 + name = "wasm-bindgen-backend" 748 + version = "0.2.100" 749 + source = "registry+https://github.com/rust-lang/crates.io-index" 750 + checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 751 + dependencies = [ 752 + "bumpalo", 753 + "log", 754 + "proc-macro2", 755 + "quote", 756 + "syn", 757 + "wasm-bindgen-shared", 758 + ] 759 + 760 + [[package]] 761 + name = "wasm-bindgen-macro" 762 + version = "0.2.100" 763 + source = "registry+https://github.com/rust-lang/crates.io-index" 764 + checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 765 + dependencies = [ 766 + "quote", 767 + "wasm-bindgen-macro-support", 768 + ] 769 + 770 + [[package]] 771 + name = "wasm-bindgen-macro-support" 772 + version = "0.2.100" 773 + source = "registry+https://github.com/rust-lang/crates.io-index" 774 + checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 775 + dependencies = [ 776 + "proc-macro2", 777 + "quote", 778 + "syn", 779 + "wasm-bindgen-backend", 780 + "wasm-bindgen-shared", 781 + ] 782 + 783 + [[package]] 784 + name = "wasm-bindgen-shared" 785 + version = "0.2.100" 786 + source = "registry+https://github.com/rust-lang/crates.io-index" 787 + checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 788 + dependencies = [ 789 + "unicode-ident", 790 + ] 791 + 792 + [[package]] 793 + name = "winapi" 794 + version = "0.3.9" 795 + source = "registry+https://github.com/rust-lang/crates.io-index" 796 + checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 797 + dependencies = [ 798 + "winapi-i686-pc-windows-gnu", 799 + "winapi-x86_64-pc-windows-gnu", 800 + ] 801 + 802 + [[package]] 803 + name = "winapi-i686-pc-windows-gnu" 804 + version = "0.4.0" 805 + source = "registry+https://github.com/rust-lang/crates.io-index" 806 + checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 807 + 808 + [[package]] 809 + name = "winapi-x86_64-pc-windows-gnu" 810 + version = "0.4.0" 811 + source = "registry+https://github.com/rust-lang/crates.io-index" 812 + checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 813 + 814 + [[package]] 815 + name = "windows-core" 816 + version = "0.61.2" 817 + source = "registry+https://github.com/rust-lang/crates.io-index" 818 + checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 819 + dependencies = [ 820 + "windows-implement", 821 + "windows-interface", 822 + "windows-link", 823 + "windows-result", 824 + "windows-strings", 825 + ] 826 + 827 + [[package]] 828 + name = "windows-implement" 829 + version = "0.60.0" 830 + source = "registry+https://github.com/rust-lang/crates.io-index" 831 + checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 832 + dependencies = [ 833 + "proc-macro2", 834 + "quote", 835 + "syn", 836 + ] 837 + 838 + [[package]] 839 + name = "windows-interface" 840 + version = "0.59.1" 841 + source = "registry+https://github.com/rust-lang/crates.io-index" 842 + checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 843 + dependencies = [ 844 + "proc-macro2", 845 + "quote", 846 + "syn", 847 + ] 848 + 849 + [[package]] 850 + name = "windows-link" 851 + version = "0.1.3" 852 + source = "registry+https://github.com/rust-lang/crates.io-index" 853 + checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 854 + 855 + [[package]] 856 + name = "windows-result" 857 + version = "0.3.4" 858 + source = "registry+https://github.com/rust-lang/crates.io-index" 859 + checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 860 + dependencies = [ 861 + "windows-link", 862 + ] 863 + 864 + [[package]] 865 + name = "windows-strings" 866 + version = "0.4.2" 867 + source = "registry+https://github.com/rust-lang/crates.io-index" 868 + checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 869 + dependencies = [ 870 + "windows-link", 871 + ] 872 + 873 + [[package]] 874 + name = "windows-sys" 875 + version = "0.59.0" 876 + source = "registry+https://github.com/rust-lang/crates.io-index" 877 + checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 878 + dependencies = [ 879 + "windows-targets", 880 + ] 881 + 882 + [[package]] 883 + name = "windows-targets" 884 + version = "0.52.6" 885 + source = "registry+https://github.com/rust-lang/crates.io-index" 886 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 887 + dependencies = [ 888 + "windows_aarch64_gnullvm", 889 + "windows_aarch64_msvc", 890 + "windows_i686_gnu", 891 + "windows_i686_gnullvm", 892 + "windows_i686_msvc", 893 + "windows_x86_64_gnu", 894 + "windows_x86_64_gnullvm", 895 + "windows_x86_64_msvc", 896 + ] 897 + 898 + [[package]] 899 + name = "windows_aarch64_gnullvm" 900 + version = "0.52.6" 901 + source = "registry+https://github.com/rust-lang/crates.io-index" 902 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 903 + 904 + [[package]] 905 + name = "windows_aarch64_msvc" 906 + version = "0.52.6" 907 + source = "registry+https://github.com/rust-lang/crates.io-index" 908 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 909 + 910 + [[package]] 911 + name = "windows_i686_gnu" 912 + version = "0.52.6" 913 + source = "registry+https://github.com/rust-lang/crates.io-index" 914 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 915 + 916 + [[package]] 917 + name = "windows_i686_gnullvm" 918 + version = "0.52.6" 919 + source = "registry+https://github.com/rust-lang/crates.io-index" 920 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 921 + 922 + [[package]] 923 + name = "windows_i686_msvc" 924 + version = "0.52.6" 925 + source = "registry+https://github.com/rust-lang/crates.io-index" 926 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 927 + 928 + [[package]] 929 + name = "windows_x86_64_gnu" 930 + version = "0.52.6" 931 + source = "registry+https://github.com/rust-lang/crates.io-index" 932 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 933 + 934 + [[package]] 935 + name = "windows_x86_64_gnullvm" 936 + version = "0.52.6" 937 + source = "registry+https://github.com/rust-lang/crates.io-index" 938 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 939 + 940 + [[package]] 941 + name = "windows_x86_64_msvc" 942 + version = "0.52.6" 943 + source = "registry+https://github.com/rust-lang/crates.io-index" 944 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 945 + 946 + [[package]] 947 + name = "wit-bindgen-rt" 948 + version = "0.39.0" 949 + source = "registry+https://github.com/rust-lang/crates.io-index" 950 + checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 951 + dependencies = [ 952 + "bitflags", 953 + ]
+25
gfoto-fixer/Cargo.toml
··· 1 + [package] 2 + name = "gfoto-metadata-fixer" 3 + version = "0.1.0" 4 + edition = "2024" 5 + 6 + [dependencies] 7 + anyhow = "1.0.99" 8 + # little_exif = "0.6.7" 9 + env_logger = { version = "0.11.8", features = [ 10 + "auto-color", 11 + "humantime", 12 + ], default-features = false } 13 + log = "0.4.27" 14 + clap = { version = "4.5.45", features = ["derive"] } 15 + serde_json = "1.0.142" 16 + serde = { version = "1.0.219", features = ["derive"] } 17 + # blake3 = "1.8.2" 18 + regex = "1.11.1" 19 + lazy-regex = "3.4.1" 20 + # chrono = "0.4.41" 21 + rayon = "1.11.0" 22 + # snowy_libs = { git = "https://git.killuaa.dev/TheSnowEcosystem/snowy_libs", version = "0.1.3" } 23 + snowy_libs = { path = "../../snowy_libs" } 24 + exiftool = "0.2.6" 25 + infer = "0.19.0"
+314
gfoto-fixer/src/convert.rs
··· 1 + use std::{ 2 + fs, 3 + path::{Path, PathBuf}, 4 + process::Command, 5 + sync::{ 6 + Arc, 7 + atomic::{AtomicU64, Ordering}, 8 + }, 9 + time::Duration, 10 + }; 11 + 12 + use anyhow::bail; 13 + use exiftool::ExifTool; 14 + use infer::MatcherType; 15 + use log::{info, warn}; 16 + use rayon::vec; 17 + use snowy_libs::multi_line::MultilineSession; 18 + 19 + pub fn converty(silly: MultilineSession, path: PathBuf) -> anyhow::Result<()> { 20 + if !path.is_dir() { 21 + println!("{:?}", path.canonicalize()); 22 + bail!("Please provide an directory!") 23 + } 24 + 25 + info!("Starting sillies"); 26 + // let files = fs::read_dir(path)?; 27 + 28 + info!("[D]:: {:?}", path); 29 + // Image, Converted_dir, Fucked_convert_dir 30 + let images = wurm( 31 + &path, 32 + Path::new("./converted/"), 33 + Path::new("./fucked_convert/"), 34 + )?; 35 + 36 + let count = Arc::new(AtomicU64::new(0)); 37 + let total_count: u64 = images.len() as u64; 38 + 39 + let bar = snowy_libs::start_progress_bar( 40 + &count, 41 + total_count, 42 + 6, 43 + "Converting media", 44 + Duration::from_millis(100), 45 + silly.clone(), 46 + ); 47 + 48 + let count = count.clone(); 49 + // let chunk_vec = chunk.to_vec(); 50 + 51 + let mut exiftool = ExifTool::new().unwrap(); 52 + 53 + images.iter().for_each(|(path, fixed_dir, fucked_dir)| { 54 + match convert_image(path, fixed_dir, fucked_dir, &mut exiftool) { 55 + Ok(_) => (), 56 + Err(_err) => { 57 + count.fetch_add(1, Ordering::Relaxed); 58 + todo!(); 59 + } 60 + } 61 + count.fetch_add(1, Ordering::Relaxed); 62 + }); 63 + 64 + bar.join().unwrap(); 65 + 66 + // threads.push(thread); 67 + 68 + // let chuncks = images.chunks(2000); 69 + 70 + // let mut threads = vec![]; 71 + 72 + // for chunk in chuncks { 73 + // let count = count.clone(); 74 + // let chunk_vec = chunk.to_vec(); 75 + 76 + // let thread = thread::spawn(move || { 77 + // let mut exiftool = ExifTool::new().unwrap(); 78 + 79 + // chunk_vec 80 + // .iter() 81 + // .for_each(|(metadata, path, fixed_dir, fucked_dir)| { 82 + // match handle_image(metadata, path, fixed_dir, fucked_dir, &mut exiftool) { 83 + // Ok(_) => (), 84 + // Err(_err) => { 85 + // count.fetch_add(1, Ordering::Relaxed); 86 + // todo!(); 87 + // } 88 + // } 89 + // count.fetch_add(1, Ordering::Relaxed); 90 + // }); 91 + // }); 92 + 93 + // threads.push(thread); 94 + // } 95 + 96 + // for thread in threads { 97 + // if thread.is_finished() { 98 + // continue; 99 + // } 100 + 101 + // thread.join().unwrap() 102 + // } 103 + 104 + println!("Hello, world!"); 105 + Ok(()) 106 + } 107 + 108 + fn wurm( 109 + dir: &Path, 110 + converted_dir: &Path, 111 + fucked_dir: &Path, 112 + ) -> anyhow::Result<Vec<(PathBuf, PathBuf, PathBuf)>> { 113 + 114 + // Create the dirs 115 + let _ = fs::create_dir(converted_dir); 116 + let _ = fs::create_dir(fucked_dir); 117 + 118 + if !dir.is_dir() { 119 + bail!("The dir isnt a directory?? \"{:?}\"", dir) 120 + } 121 + 122 + let mut images = vec![]; 123 + 124 + for entry in fs::read_dir(dir)? { 125 + let entry = entry?; 126 + let path = entry.path(); 127 + 128 + if path.is_dir() { 129 + let dir = path.file_name().unwrap(); 130 + let converted_dir = converted_dir.join(dir); 131 + let fucked_dir = fucked_dir.join(dir); 132 + 133 + info!("[D]:: {path:?}"); 134 + 135 + match wurm(&path, &converted_dir, &fucked_dir) { 136 + Ok(val) => images.extend(val), 137 + Err(err) => warn!("Error while worming into path \"{:?}\": {}", &path, err), 138 + } 139 + } else { 140 + images.push((path, converted_dir.to_owned(), fucked_dir.to_owned())); 141 + // info!("Converties"); 142 + } 143 + } 144 + 145 + Ok(images) 146 + } 147 + 148 + fn convert_image( 149 + image: &Path, 150 + converted_dir: &Path, 151 + fucked_dir: &Path, 152 + exif_tool: &mut ExifTool, 153 + ) -> anyhow::Result<()> { 154 + let kind = infer::get_from_path(image)?.expect("file type is unknown"); 155 + 156 + let mut converted_path = converted_dir.join(image.file_name().unwrap()); 157 + let mut fucked_path = fucked_dir.join(image.file_name().unwrap()); 158 + 159 + let media_type = if kind.extension() == "gif" { 160 + // return Ok(()); 161 + kind.matcher_type() 162 + // MatcherType::Video 163 + } else { 164 + kind.matcher_type() 165 + }; 166 + 167 + match media_type { 168 + MatcherType::Image => { 169 + return Ok(()); 170 + converted_path.set_extension("jxl"); 171 + 172 + if converted_path.exists() { 173 + log::info!("Skipping image {converted_path:?}"); 174 + return Ok(()); 175 + } 176 + 177 + let mut command = &mut Command::new("cjxl"); 178 + 179 + command = command.args([ 180 + image.to_str().unwrap(), 181 + converted_path.to_str().unwrap(), 182 + "-e", 183 + "1", // Pretty high effort (max is 11) 184 + "-d", 185 + "0", // Lossless 186 + "--allow_expert_options", 187 + "--container", // Encode it with an container format for exif 188 + "1", // confirm 189 + // "--lossless_jpeg", // Make it so jpeg can have a little bit of loss 190 + // "1", 191 + // "--num_threads", 192 + // "24", 193 + ]); 194 + 195 + // if kind.extension() != "gif" { 196 + // command = command.arg("--progressive"); // Enable progresive decoding 197 + // } 198 + 199 + let output = command.output().expect("Failed to convert image to jxl!"); 200 + 201 + // .output() 202 + // .expect("failed to execute process"); 203 + 204 + if !output.status.success() { 205 + log::error!("Error while converting {image:?} to jxl: {output:?}"); 206 + 207 + if !fucked_dir.exists() { 208 + fs::create_dir_all(fucked_dir).unwrap(); 209 + } 210 + 211 + fs::copy(image, fucked_path).unwrap(); 212 + return Ok(()); 213 + // std::process::exit(0); 214 + } else { 215 + log::info!( 216 + "Converted {image:?} successfully. {} => {}", 217 + fs::metadata(image)?.len(), 218 + fs::metadata(&converted_path)?.len() 219 + ) 220 + } 221 + } 222 + MatcherType::Video => { 223 + converted_path.set_extension("mp4"); 224 + // Exiftool doesnt support matroska (mkv), so mp4 it is 225 + 226 + if converted_path.exists() { 227 + log::info!("Skipping video {converted_path:?}"); 228 + return Ok(()); 229 + } 230 + 231 + let out = Command::new("ffmpeg") 232 + .args([ 233 + "-y", 234 + "-i", 235 + image.to_str().unwrap(), 236 + "-c:v", 237 + "librav1e", 238 + "-qp", 239 + "40", 240 + "-speed", 241 + "2", 242 + "/tmp/ffmpeg-converting.mp4", 243 + ]) 244 + .output() 245 + .expect("failed to execute process"); 246 + 247 + if !out.status.success() { 248 + log::error!("{out:?}"); 249 + std::process::exit(0); 250 + } else { 251 + // Make it so it puts files temp in /tmp 252 + let out = Command::new("mv") 253 + .args([ 254 + "/tmp/ffmpeg-converting.mp4", 255 + converted_path.to_str().unwrap(), 256 + ]) 257 + .output() 258 + .expect("failed to move file"); 259 + 260 + if !out.status.success() { 261 + log::error!("{out:?}"); 262 + std::process::exit(0); 263 + } 264 + 265 + let og_size = fs::metadata(image)?.len(); 266 + let conv_size = fs::metadata(&converted_path)?.len(); 267 + 268 + log::info!( 269 + "Converted {image:?} successfully. -{} ~{}%", 270 + (og_size as i64 - conv_size as i64) as i64, 271 + (conv_size / og_size * 100) 272 + ) 273 + } 274 + } 275 + _ => { 276 + log::warn!("This is not an image or video file, huh? {image:?}"); 277 + } 278 + } 279 + 280 + // Copy over da exif 281 + let args = &[ 282 + // "-T", 283 + "-overwrite_original", // Don't make a copy with _original appended to it 284 + "-TagsFromFile", 285 + image.to_str().unwrap(), 286 + "-all:all", 287 + "-FileModifyDate<FileModifyDate", // Also copy the File Modification Date/Time 288 + converted_path.to_str().unwrap(), 289 + ]; 290 + 291 + let out = exif_tool.execute_lines(args); 292 + 293 + // log::info!("{args:?} {out:?}"); 294 + 295 + // Make sure exiftool did its thing correctly 296 + if let Err(err) = out { 297 + println!("{}", err.to_string().trim().replace("\n", "")); 298 + 299 + if !fucked_dir.exists() { 300 + fs::create_dir_all(fucked_dir).unwrap(); 301 + } 302 + 303 + fucked_path.set_extension(converted_path.extension().unwrap()); 304 + fs::rename(&converted_path, &fucked_path).unwrap(); 305 + } else { 306 + // log::error!("{:?}", out); 307 + } 308 + 309 + // std::process::exit(0); 310 + 311 + Ok(()) 312 + 313 + // Copy back exif metadata 314 + }
+165
gfoto-fixer/src/image_metadata.rs
··· 1 + use std::{ 2 + ffi::OsString, 3 + fs, 4 + path::{Path, PathBuf}, 5 + }; 6 + 7 + use anyhow::bail; 8 + use regex::Regex; 9 + use serde::Deserialize; 10 + 11 + /// Struct that contains the relevant metadata of the metadata json files from google takeout 12 + #[derive(Debug, Deserialize, Clone)] 13 + pub struct GfotoMetadata { 14 + pub title: String, 15 + // description: String, 16 + // imageViews: String, 17 + #[serde(rename = "creationTime")] 18 + pub creation_time: TimeInfo, 19 + #[serde(rename = "photoTakenTime")] 20 + pub photo_taken_time: TimeInfo, 21 + // #[serde(rename = "geoData")] 22 + // pub geo_data: GeoData, 23 + // geoDataExif: Option<GeoData>, 24 + } 25 + 26 + /// Struct that co 27 + #[derive(Debug, Deserialize, Clone)] 28 + pub struct TimeInfo { 29 + pub timestamp: String, 30 + // #[allow(dead_code)] 31 + // formatted: String, 32 + } 33 + 34 + // #[derive(Debug, Deserialize, PartialEq, Clone)] 35 + // pub struct GeoData { 36 + // pub latitude: f64, 37 + // pub longitude: f64, 38 + // pub altitude: f64, 39 + // #[serde(rename = "latitudeSpan")] 40 + // pub latitude_span: f64, 41 + // #[serde(rename = "longitudeSpan")] 42 + // pub longitude_span: f64, 43 + // } 44 + 45 + /// Regex to see if it has the (num) sillieness in the image name. 46 + /// 47 + /// It has a group for the base path, the num and the file extension 48 + pub static IMAGE_NAME_REGEX: lazy_regex::Lazy<Regex> = 49 + lazy_regex::lazy_regex!(r"^(.*)\((\d+)\)(\.[^.]+)$"); 50 + 51 + /// Function that attempts to generate a metadata filename based on googles weird format 52 + /// 53 + /// Google takeout limits file names to 46 chars for some reason 54 + fn generate_metadata_filename(path: &Path) -> anyhow::Result<OsString> { 55 + let mut file_name = path.file_name().unwrap().to_os_string(); 56 + 57 + // Add this 58 + file_name.push(".supplemental-metadata"); 59 + 60 + // Google takeout limits file names to 46 chars for some reason 61 + // Not fully sure if this works safely, but it works for me... Might not work with weird chars. 62 + file_name = match file_name.into_string() { 63 + Ok(mut string) => { 64 + if string.chars().count() > 46 { 65 + while string.chars().count() > 46 { 66 + string.pop(); 67 + } 68 + OsString::from(string) 69 + } else { 70 + OsString::from(string) 71 + } 72 + } 73 + Err(err) => bail!( 74 + "Couldnt turn file_name into string. ({:?}) : {:?}", 75 + path, 76 + err 77 + ), 78 + }; 79 + 80 + // Add .json file extension 81 + file_name.push(".json"); 82 + 83 + Ok(file_name) 84 + } 85 + 86 + /// This function attempts to find the json metadata for the image 87 + /// Returns the metadata 88 + pub fn get_image_metadata(path: &Path) -> anyhow::Result<GfotoMetadata> { 89 + // Generate the metadata file name 90 + let metadata_string = match generate_metadata_filename(path) { 91 + Ok(val) => val, 92 + Err(err) => bail!("{err}"), 93 + }; 94 + 95 + // Generate the full path of the metadata file 96 + let metadata_path = match path.parent() { 97 + Some(parent) => parent.join(&metadata_string), 98 + None => bail!("Couldnt get the folder of the file. ({:?})", path), 99 + }; 100 + 101 + // Try to read the metadata file 102 + let file_content = match fs::read_to_string(metadata_path.clone()) { 103 + Ok(val) => val, 104 + Err(_err) => { 105 + // Google likes to be silly when you make a copy of a file (i think?) 106 + // If the original image was called "IMG_20180214_171835513.jpg" than the metadata file will be "IMG_20180214_171835513.jpg.supplemental-metada.json" 107 + // Makes sense right? So what if I told you that copies do the following: 108 + // The copy "IMG_20180214_171835513(1).jpg" has the metadata file "IMG_20180214_171835513.jpg.supplemental-metada(1).json" 109 + // WHY??? 110 + 111 + let str = path.file_name().unwrap().to_str().unwrap(); 112 + 113 + // If it captures anything with the IMAGE_NAME_REGEX const 114 + if let Some(caps) = IMAGE_NAME_REGEX.captures(str) { 115 + let base = caps.get(1).unwrap().as_str(); 116 + let num = caps.get(2).unwrap().as_str(); 117 + let ext = caps.get(3).unwrap().as_str(); 118 + 119 + let base_image: PathBuf = path.parent().unwrap().join(format!("{}{}", base, ext)); 120 + 121 + let metadata_string = match generate_metadata_filename(&base_image) { 122 + Ok(val) => val, 123 + Err(err) => bail!("{err}"), 124 + }; 125 + 126 + let silly = metadata_string 127 + .into_string() 128 + .unwrap() 129 + .replace(".json", &format!("({}).json", num)); 130 + 131 + let new_path = path.parent().unwrap().join(silly); 132 + 133 + match fs::read_to_string(&new_path) { 134 + Ok(val) => val, 135 + Err(_err) => { 136 + bail!( 137 + "(2) Couldnt read the contents of the metadata file ({:?})", 138 + &new_path, 139 + // err 140 + ); 141 + } 142 + } 143 + } else { 144 + bail!( 145 + "Couldnt read the contents of the metadata file ({:?})", 146 + &metadata_path, 147 + // err 148 + ); 149 + } 150 + } 151 + }; 152 + 153 + let metadata: GfotoMetadata = match serde_json::from_str(&file_content) { 154 + Ok(val) => val, 155 + Err(err) => { 156 + bail!( 157 + "Couldnt parse the metadata of the file ({:?}) : [{:?}]", 158 + &metadata_path, 159 + err 160 + ); 161 + } 162 + }; 163 + 164 + Ok(metadata) 165 + }
+281
gfoto-fixer/src/main.rs
··· 1 + use std::{ 2 + fs, 3 + path::{Path, PathBuf}, 4 + sync::{ 5 + Arc, 6 + atomic::{AtomicU64, Ordering}, 7 + }, 8 + thread, 9 + time::Duration, 10 + vec, 11 + }; 12 + 13 + use anyhow::{Context, bail}; 14 + use clap::Parser; 15 + use env_logger::Env; 16 + use exiftool::ExifTool; 17 + use log::{info, warn}; 18 + 19 + use crate::image_metadata::GfotoMetadata; 20 + 21 + mod convert; 22 + mod image_metadata; 23 + 24 + const LOGGING_LEVEL: &str = "info"; 25 + 26 + /// Google takeout metadata fixer 27 + #[derive(Parser, Debug)] 28 + #[command(version, about, long_about = None)] 29 + struct Args { 30 + /// Path to the images 31 + #[arg( 32 + short, 33 + long, 34 + default_value = "/mnt/SnowData/snowy/__STAGING__/Takeout/Test" 35 + )] 36 + images_folder: PathBuf, 37 + 38 + // // Overwrite existing time metadata 39 + // #[arg(short, long, default_value_t = false)] 40 + // overwrite_time: bool, 41 + 42 + // Only convert images in the fixed directory (handy incase you want to convert them later) 43 + #[arg(short, long, default_value_t = false)] 44 + only_conversion: bool, 45 + } 46 + 47 + // IMG_20180929_172248549.jpg.supplemental-metada.json 48 + fn main() -> anyhow::Result<()> { 49 + let mut silly = snowy_libs::multi_line::start_multiline_session(); 50 + let temp_silly = silly.clone(); 51 + 52 + env_logger::Builder::from_env(Env::default().default_filter_or(LOGGING_LEVEL)) 53 + .format(move |buf, record| { 54 + let msg = format!("[{}] - {}\n", record.level(), record.args()); 55 + // log_pb.println(msg).unwrap(); // Print above the progress bar 56 + temp_silly.println(msg); 57 + // println!(); 58 + Ok(()) 59 + }) 60 + .init(); 61 + 62 + // env_logger::Builder::from_default_env().format(format) 63 + 64 + let args = Args::parse(); 65 + let path = args.images_folder; 66 + 67 + silly.reserve_line(); 68 + silly.reserve_line(); 69 + silly.reserve_line(); 70 + silly.reserve_line(); 71 + 72 + if args.only_conversion { 73 + convert::converty(silly.clone(), Path::new("./fixed/").to_path_buf())?; 74 + return Ok(()); 75 + } 76 + 77 + if !path.is_dir() { 78 + println!("{:?}", path.canonicalize()); 79 + bail!("Please provide an directory!") 80 + } 81 + info!("Starting sillies"); 82 + // let files = fs::read_dir(path)?; 83 + 84 + info!("[D]:: {:?}", path); 85 + let images: Vec<(GfotoMetadata, PathBuf, PathBuf, PathBuf)> = 86 + wurm(&path, Path::new("./fixed/"), Path::new("./fucked/"))?; 87 + 88 + let count = Arc::new(AtomicU64::new(0)); 89 + let total_count: u64 = images.len() as u64; 90 + 91 + let bar = snowy_libs::start_progress_bar( 92 + &count, 93 + total_count, 94 + 6, 95 + "Writing images", 96 + Duration::from_millis(100), 97 + silly.clone(), 98 + ); 99 + 100 + let chuncks = images.chunks(2000); 101 + 102 + let mut threads = vec![]; 103 + 104 + for chunk in chuncks { 105 + let count = count.clone(); 106 + let chunk_vec = chunk.to_vec(); 107 + 108 + let thread = thread::spawn(move || { 109 + let mut exiftool = ExifTool::new().unwrap(); 110 + 111 + chunk_vec 112 + .iter() 113 + .for_each(|(metadata, path, fixed_dir, fucked_dir)| { 114 + match handle_image(metadata, path, fixed_dir, fucked_dir, &mut exiftool) { 115 + Ok(_) => (), 116 + Err(_err) => { 117 + count.fetch_add(1, Ordering::Relaxed); 118 + todo!(); 119 + } 120 + } 121 + count.fetch_add(1, Ordering::Relaxed); 122 + }); 123 + }); 124 + 125 + threads.push(thread); 126 + } 127 + 128 + for thread in threads { 129 + if thread.is_finished() { 130 + continue; 131 + } 132 + 133 + thread.join().unwrap() 134 + } 135 + 136 + bar.join().unwrap(); 137 + 138 + println!("Hello, world!"); 139 + Ok(()) 140 + } 141 + 142 + fn wurm( 143 + dir: &Path, 144 + fixed_dir: &Path, 145 + fucked_dir: &Path, 146 + ) -> anyhow::Result<Vec<(GfotoMetadata, PathBuf, PathBuf, PathBuf)>> { 147 + // println!("{:?}", putto_dir); 148 + 149 + // Create the dirs 150 + fs::create_dir(fixed_dir).unwrap(); 151 + // fs::create_dir(fucked_dir).unwrap(); 152 + 153 + if !dir.is_dir() { 154 + bail!("The dir isnt a directory?? \"{:?}\"", dir) 155 + } 156 + 157 + let mut images: Vec<(GfotoMetadata, PathBuf, PathBuf, PathBuf)> = vec![]; 158 + 159 + for entry in fs::read_dir(dir)? { 160 + let entry = entry?; 161 + let path = entry.path(); 162 + 163 + if path.is_dir() { 164 + let dir = path.file_name().unwrap(); 165 + let fixed_dir = fixed_dir.join(dir); 166 + let fucked_dir = fucked_dir.join(dir); 167 + 168 + info!("[D]:: {:?}", path); 169 + 170 + match wurm(&path, &fixed_dir, &fucked_dir) { 171 + Ok(val) => images.extend(val), 172 + Err(err) => warn!("Error while worming into path \"{:?}\": {}", &path, err), 173 + } 174 + } else { 175 + // info!("Fixing exif"); 176 + 177 + if path 178 + .extension() 179 + .context("Couldn't get the extensions of the path")? 180 + == "json" 181 + { 182 + // info!("Skipping json file! {:?}", path); 183 + continue; 184 + } 185 + 186 + match image_metadata::get_image_metadata(&path) { 187 + Ok(val) => { 188 + // Image is good, push it! 189 + images.push((val, path, fixed_dir.to_owned(), fucked_dir.to_owned())); 190 + } 191 + Err(err) => { 192 + warn!("{} Skipping!", err); 193 + 194 + if !fucked_dir.exists() { 195 + fs::create_dir_all(fucked_dir)?; 196 + } 197 + 198 + // Copy the image to the fucked directory so people can sort them out themselves 199 + fs::copy( 200 + &path, 201 + fucked_dir.join( 202 + path.file_name() 203 + .context("Couldn't get the file_name of the path")?, 204 + ), 205 + )?; 206 + 207 + continue; 208 + } 209 + }; 210 + } 211 + } 212 + 213 + Ok(images) 214 + } 215 + 216 + fn handle_image( 217 + metadata: &GfotoMetadata, 218 + path: &Path, 219 + fixed_dir: &Path, 220 + fucked_dir: &Path, 221 + exiftool: &mut ExifTool, 222 + ) -> anyhow::Result<()> { 223 + // Extract needed data from the metadata 224 + // let geodata = &metadata.geo_data; 225 + let date_time_original_unix = &metadata.photo_taken_time.timestamp; 226 + let create_date_unix = &metadata.creation_time.timestamp; 227 + let name = &metadata.title; 228 + 229 + // Create paths for image 230 + let mut fixed_path = fixed_dir.join(name); 231 + let mut fucked_path = fucked_dir.join(name); 232 + 233 + // Read the raw image 234 + let image = fs::read(path)?; 235 + 236 + { 237 + // Fixup file extensions of images being incorrect 238 + let kind = infer::get(&image).expect("file type is unknown"); 239 + fixed_path.set_extension(kind.extension()); 240 + fucked_path.set_extension(kind.extension()); 241 + } 242 + 243 + // Write the raw image back 244 + fs::write(&fixed_path, image)?; 245 + 246 + // Arguments for exiftool 247 + let args = &[ 248 + // "-T", 249 + "-m", 250 + "-d", 251 + "%s", 252 + // The timestamp tags 253 + "-overwrite_original", 254 + &format!("-DateTimeOriginal={}", date_time_original_unix), 255 + &format!("-CreateDate={}", create_date_unix), 256 + &format!("-ModifyDate={}", create_date_unix), 257 + &format!("-FileModifyDate={}", create_date_unix), 258 + // &format!("-GPSLatitude={}", geodata.latitude), 259 + // &format!("-GPSLongitude={}", geodata.longitude), 260 + // &format!("-GPSAltitude={}", geodata.altitude), 261 + // File path :3 262 + fixed_path.to_str().unwrap(), 263 + ]; 264 + 265 + let out = exiftool.execute_lines(args); 266 + 267 + // Todo! gps data 268 + 269 + // Make sure exiftool did its thing correctly 270 + if let Err(err) = out { 271 + log::error!("{}", err.to_string().trim().replace("\n", "")); 272 + 273 + if !fucked_dir.exists() { 274 + fs::create_dir_all(fucked_dir).unwrap(); 275 + } 276 + 277 + fs::rename(&fixed_path, &fucked_path).unwrap(); 278 + } 279 + 280 + Ok(()) 281 + }
+15
justfile
··· 1 + 2 + server: 3 + cargo run --bin converter-server 4 + 5 + server-release: 6 + cargo run --bin converter-server --release 7 + 8 + client: 9 + cargo run --bin converter-client 10 + 11 + client-release: 12 + cargo run --bin converter-client 13 + 14 + client-windows: 15 + nix develop .#windows --command cargo build --bin converter-client --release --target x86_64-pc-windows-gnu
+23
readme.md
··· 1 + # gfoto-metadata-fixer 2 + ## The script 3 + I havent been able to find a script that handles the image restoration correctly, so i decided to write my own. 4 + This cli app is very fast, however it still depends on how fast your drive is! 5 + 6 + This script does the following: 7 + - Restores original file names 8 + - Fixes incorrect file extensions (needed for fixing exif data) 9 + - Restores EXIF OriginallyCreated data 10 + - It copies the files and the into a new directory (with the same structure) instead of overwriting the existing files. 11 + - If it cant find the metadata for a file it will put it in a *seperate directory* 12 + 13 + It handles some bullshit that google does that I have encountered myself: 14 + - Google trims the metadata json to 46 chars 15 + - Duplicate files that google decides to give a weird metadata file. 16 + - "IMG_20180214_171835513(1).jpg" has the metadata file "IMG_20180214_171835513.jpg.supplemental-metada(1).json" 17 + 18 + 19 + ## The converter 20 + This repo also includes a jxl and av1 conversion project. It takes all the images and convertes them to jxl/av1 spread out over seperate computers. 21 + 22 + There is a server component that distrebutes the files, and a client component that converts it to jxl/av1, restores the exif, and sents it back to the server. 23 + The server than stores it in two folders, converted or converted_fucked, depending if the conversion succeeded or not :P.
+102
test_script.sh
··· 1 + #! /usr/bin/env nix-shell 2 + #! nix-shell -i bash -p coreutils bash exiftool jq 3 + # set -x 4 + base_dir=$1 5 + #echo $image_dir 6 + 7 + # Goes trought the current directory, lists everything and goes through every file 8 + # It will go 9 + wurm() { 10 + local image_dir=$1 11 + 12 + for file in "$image_dir"/*; do 13 + 14 + if [[ -d "$file" ]]; then 15 + 16 + # Found a new directory, worming in! 17 + echo "[D]:: $file" 18 + wurm "$file" 19 + elif [[ -f "$file" ]]; then 20 + # echo "[F]:: $file" 21 + process_file "$file" 22 + # echo -n 23 + else 24 + echo "WHAT THE SIGMA $file" 25 + fi 26 + 27 + done 28 + } 29 + 30 + 31 + process_file() { 32 + if [[ "$1" == *.json ]]; then 33 + return 1 34 + fi 35 + 36 + 37 + 38 + # Get the file name of the file 39 + # ./F/IMG_20170703_210233231.jpg 40 + # file="$1" 41 + file_name=$(basename "$1") 42 + file_dir=$(dirname "$1") 43 + # echo $file_dir 44 + 45 + 46 + # now to_save_dir=$() == file_dir - base_dir from beginnign 47 + 48 + 49 + # to_save_dir=$(realpath --relative-to="$base_dir" "$file_dir") 50 + # echo $to_save_dir_fixed 51 + 52 + # exit 0 53 + 54 + json_file="${file_name}.supplemental-metadata" 55 + 56 + json_file=${json_file:0:46} # Truncate to 46 chars if needed 57 + 58 + json_file="${file_dir}/${json_file}.json" 59 + 60 + if [[ -f $json_file ]]; then 61 + # f 62 + # echo -n 63 + temp_file=$(cat "$json_file") 64 + # IMG_20180822_182349199_BURST011-EFFECTS.jpg.su.json 65 + # IMG_20180822_182349199_BURST011-EFFECTS-bewerkt.jpg 66 + new_name=$(echo -n $temp_file | jq .title) 67 + geo_lati=$(echo -n $temp_file | jq .geoData.latitude) 68 + geo_long=$(echo -n $temp_file | jq .geoData.longitude) 69 + geo_alti=$(echo -n $temp_file | jq .geoData.altitude) 70 + 71 + geo_alti_span=$(echo -n $temp_file | jq .geoData.latitudeSpan) 72 + geo_long_span=$(echo -n $temp_file | jq .geoData.longitudeSpan) 73 + 74 + date_time_original_unix=$(echo -n $temp_file | jq .photoTakenTime.timestamp) 75 + 76 + # to_save_dir="./fixed/${file_dir#"$base_dir"/}" 77 + # mkdir -p "$to_save_dir" 78 + # cp "$1" "$to_save_dir/$new_name" 79 + # exit 1 80 + 81 + else 82 + echo "[E]:: Json metadata not found for: \"$1\"" 83 + # to_save_dir="./fucked/${file_dir#"$base_dir"/}" 84 + # mkdir -p "$to_save_dir" 85 + # cp "$1" "$to_save_dir/$file_name" 86 + # exit 1 87 + fi 88 + 89 + 90 + 91 + # new_path 92 + 93 + # echo "$1 :: ${file_name} :: ${json_file}" 94 + # exit 0 95 + } 96 + 97 + # Make dirs 98 + mkdir ./fixed 99 + mkdir ./fucked 100 + 101 + # echo $base_dir 102 + wurm "$base_dir"