Compare commits
No commits in common. "master" and "v0.1.0" have entirely different histories.
8 changed files with 329 additions and 1003 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1 @@
|
||||||
/target
|
/target
|
||||||
.vscode
|
|
445
Cargo.lock
generated
445
Cargo.lock
generated
|
@ -1,68 +1,34 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler"
|
name = "adler32"
|
||||||
version = "1.0.2"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aes"
|
|
||||||
version = "0.7.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cipher",
|
|
||||||
"cpufeatures",
|
|
||||||
"opaque-debug",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.7.6"
|
version = "0.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
|
||||||
dependencies = [
|
|
||||||
"getrandom",
|
|
||||||
"once_cell",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "base64ct"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block-buffer"
|
|
||||||
version = "0.10.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.4.3"
|
version = "1.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bzip2"
|
name = "bzip2"
|
||||||
version = "0.4.3"
|
version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0"
|
checksum = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bzip2-sys",
|
"bzip2-sys",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -70,9 +36,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bzip2-sys"
|
name = "bzip2-sys"
|
||||||
version = "0.1.11+1.0.8"
|
version = "0.1.9+1.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
|
checksum = "ad3b39a260062fca31f7b0b12f207e8f2590a67d32ec7d59c20484b07ea7285e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -81,12 +47,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.73"
|
version = "1.0.66"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
|
||||||
dependencies = [
|
|
||||||
"jobserver",
|
[[package]]
|
||||||
]
|
name = "cfg-if"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
|
@ -94,68 +63,13 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cipher"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "constant_time_eq"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cpufeatures"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.3.2"
|
version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if 1.0.0",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-utils"
|
|
||||||
version = "0.8.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crypto-common"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2ccfd8c0ee4cce11e45b3fd6f9d5e69e0cc62912aa6a0cb1bf4617b0eba5a12f"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
"typenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "digest"
|
|
||||||
version = "0.10.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
|
|
||||||
dependencies = [
|
|
||||||
"block-buffer",
|
|
||||||
"crypto-common",
|
|
||||||
"subtle",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -172,88 +86,45 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.24"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
|
checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"cfg-if 0.1.10",
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
|
"libc",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "generic-array"
|
|
||||||
version = "0.14.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
|
|
||||||
dependencies = [
|
|
||||||
"typenum",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getrandom"
|
|
||||||
version = "0.2.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"wasi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.11.2"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashlink"
|
name = "hashlink"
|
||||||
version = "0.7.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
|
checksum = "d99cf782f0dc4372d26846bec3de7804ceb5df083c2d4462c0b8d2330e894fa8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hmac"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
|
||||||
dependencies = [
|
|
||||||
"digest",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itoa"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jobserver"
|
|
||||||
version = "0.1.24"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.126"
|
version = "0.2.83"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
checksum = "7eb0c4e9c72ee9d69b767adebc5f4788462a3b45624acd919475c92597bcaf4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libsqlite3-sys"
|
name = "libsqlite3-sys"
|
||||||
version = "0.24.2"
|
version = "0.20.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
|
checksum = "64d31059f22935e6c31830db5249ba2b7ecd54fd73a9909286f0a67aa55c2fbd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
|
@ -262,98 +133,57 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.5.0"
|
version = "2.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.5.3"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
|
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler32",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num_threads"
|
|
||||||
version = "0.1.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "opaque-debug"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "password-hash"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8"
|
|
||||||
dependencies = [
|
|
||||||
"base64ct",
|
|
||||||
"rand_core",
|
|
||||||
"subtle",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pbdbfixer"
|
name = "pbdbfixer"
|
||||||
version = "0.8.3"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quick-xml",
|
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
|
"xml-rs",
|
||||||
"zip",
|
"zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pbkdf2"
|
|
||||||
version = "0.10.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7"
|
|
||||||
dependencies = [
|
|
||||||
"digest",
|
|
||||||
"hmac",
|
|
||||||
"password-hash",
|
|
||||||
"sha2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.25"
|
version = "0.3.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "proc-macro2"
|
||||||
version = "0.23.0"
|
version = "1.0.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9279fbdacaad3baf559d8cabe0acc3d06e30ea14931af31af79578ac0946decc"
|
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "quote"
|
||||||
version = "0.6.3"
|
version = "1.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rusqlite"
|
name = "rusqlite"
|
||||||
version = "0.27.0"
|
version = "0.24.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a"
|
checksum = "d5f38ee71cbab2c827ec0ac24e76f82eca723cee92c509a65f67dee393c25112"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"fallible-iterator",
|
"fallible-iterator",
|
||||||
|
@ -364,127 +194,110 @@ dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sha1"
|
|
||||||
version = "0.10.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cpufeatures",
|
|
||||||
"digest",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sha2"
|
|
||||||
version = "0.10.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cpufeatures",
|
|
||||||
"digest",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.9.0"
|
version = "1.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
|
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "syn"
|
||||||
version = "2.4.1"
|
version = "1.0.60"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.11"
|
version = "0.1.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217"
|
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
|
||||||
"libc",
|
"libc",
|
||||||
"num_threads",
|
"wasi",
|
||||||
"time-macros",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-macros"
|
name = "unicode-xid"
|
||||||
version = "0.2.4"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typenum"
|
|
||||||
version = "1.15.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "version_check"
|
|
||||||
version = "0.9.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.10.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xml-rs"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zip"
|
name = "zip"
|
||||||
version = "0.6.2"
|
version = "0.5.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf225bcf73bb52cbb496e70475c7bd7a3f769df699c0020f6c7bd9a96dcf0b8d"
|
checksum = "cc2896475a242c41366941faa27264df2cb935185a92e059a004d0048feb2ac5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bzip2",
|
"bzip2",
|
||||||
"constant_time_eq",
|
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"crossbeam-utils",
|
|
||||||
"flate2",
|
"flate2",
|
||||||
"hmac",
|
"thiserror",
|
||||||
"pbkdf2",
|
|
||||||
"sha1",
|
|
||||||
"time",
|
"time",
|
||||||
"zstd",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zstd"
|
|
||||||
version = "0.10.2+zstd.1.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5f4a6bd64f22b5e3e94b4e238669ff9f10815c27a5180108b849d24174a83847"
|
|
||||||
dependencies = [
|
|
||||||
"zstd-safe",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zstd-safe"
|
|
||||||
version = "4.1.6+zstd.1.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94b61c51bb270702d6167b8ce67340d2754b088d0c091b06e593aa772c3ee9bb"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"zstd-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zstd-sys"
|
|
||||||
version = "1.6.3+zstd.1.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fc49afa5c8d634e75761feda8c592051e7eeb4683ba827211eb0d731d3402ea8"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
]
|
]
|
||||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -1,15 +1,15 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pbdbfixer"
|
name = "pbdbfixer"
|
||||||
version = "0.8.3"
|
version = "0.1.0"
|
||||||
authors = ["Martin Brodbeck <martin@brodbeck-online.de>"]
|
authors = ["Martin Brodbeck <martin@brodbeck-online.de>"]
|
||||||
edition = "2021"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zip = "0.6"
|
zip = "0.5"
|
||||||
quick-xml = "0.23"
|
xml-rs = "0.8"
|
||||||
|
|
||||||
[dependencies.rusqlite]
|
[dependencies.rusqlite]
|
||||||
version = "0.27"
|
version = "0.24"
|
||||||
features = ["bundled"]
|
features = ["bundled"]
|
||||||
|
|
50
README.md
50
README.md
|
@ -6,48 +6,22 @@ EPUB files, this program tries fix these issues. It tries to identify
|
||||||
wrong database entries and fix it by reading the corresponding epub
|
wrong database entries and fix it by reading the corresponding epub
|
||||||
metadata.
|
metadata.
|
||||||
|
|
||||||
## Features
|
|
||||||
The app tries to fix the following issues in the database
|
|
||||||
- Correction of wrong firstauthor entries (books_impl table)
|
|
||||||
- Correction of wrong first_author_letter entries (books_impl table)
|
|
||||||
- Correction of wrong author entries (books_impl table)
|
|
||||||
- Removing deleted e-books from the database (various tables)
|
|
||||||
- Add missing genre if present in epub (genre and booktogenre tables)
|
|
||||||
- Add missing series information (books_impl table)
|
|
||||||
|
|
||||||
The best results are achieved when metadata has been carefully maintained with **Calibre**.
|
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
This program is tested on a PocketBook
|
This program is tested only on a PocketBook Touch HD 3 device (software
|
||||||
- *Touch Lux 4* (software version 6.5)
|
version 6.1.900). It might work with other PocketBook devices/software
|
||||||
- *Touch HD 3* (software version 6.7)
|
versions. Please tell me, if it works for you (and do make a backup of the
|
||||||
- *Era* (software version 6.8) [^1]
|
explorer-3.db file before trying!).
|
||||||
- *InkPad 3 Pro* (software version 6.8) [^1]
|
|
||||||
- *InkPad 4* (software version 6.8)
|
|
||||||
- *Verse Pro* (software version 6.8) [^1]
|
|
||||||
|
|
||||||
It might work with other PocketBook devices/software versions. Please tell me if it works for you (and do make a backup of the explorer-3.db file before trying!).
|
|
||||||
|
|
||||||
<span style="color: red">**Please note:**</span> As I do not currently own a PocketBook device, I am unfortunately unable to react to changes in the database structure. Perhaps you can send me your `explorer-3.db` so that I can take a look at the changes. Just get in touch with me.
|
|
||||||
|
|
||||||
## Installation and Usage
|
## Installation and Usage
|
||||||
---
|
Just copy the executable file into the PocketBook's application directory.
|
||||||
**WARNING**:
|
If you encounter duplicate authors in the PocketBook's library, open the
|
||||||
|
applications screen and tap on the PbDbFixer icon.
|
||||||
Use at your own risk. In case of doubt it is not a mistake to make a backup of the file `/system/explorer-3/explorer-3.db` beforehand.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Just copy the executable file into the PocketBook's application directory. If you encounter duplicate authors or other issues (see "Features" above) in the PocketBook's library, open the applications screen and tap on the PbDbFixer icon.
|
|
||||||
|
|
||||||
If you don't see any changes:
|
|
||||||
There might be an explorer (which shows your library) process already running. Then you should just stop/kill it with the task manager. Putting the device to sleep and then wake it up might also work. Afterwards, the changes should be visible to the explorer.
|
|
||||||
|
|
||||||
## Feedback
|
|
||||||
Feedback is highly appreciated. You can reach me via Matrix [@beedaddy:matrix.rustysoft.de](https://matrix.to/#/@beedaddy:matrix.rustysoft.de) or ask questions in the [PbDbFixer-Thread](https://www.e-reader-forum.de/t/pbdbfixer-noch-ein-tool-zum-korrigieren-von-metadaten.156702/) of the German *E-Reader Forum*.
|
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
If you want to build PbDbFixer yourself, make sure that you have Rust's toolchain target `arm-unknown-linux-gnueabi` as well as the GCC cross compiler for ARM CPUs installed. On **Arch** Linux, the AUR package `arm-linux-gnueabi-gcc75-linaro-bin` does the job. On **Debian**, you have to install the package `gcc-arm-linux-gnueabi`. Don't forget to tell `cargo` which compiler/linker it has to invoke. In my case, I had to edit `~/.cargo/config`:
|
To be able to build PbDbFixer, you have to have the cross compiler for
|
||||||
|
ARM CPUs installed. On Arch Linux, the AUR package `arm-linux-gnueabi-gcc75-linaro-bin`
|
||||||
|
does the job. Don't forget to tell `cargo` which compiler/linker it has to
|
||||||
|
invoke. In my case, I hat to edit `~/.cargo/config`:
|
||||||
```
|
```
|
||||||
[target.arm-unknown-linux-gnueabi]
|
[target.arm-unknown-linux-gnueabi]
|
||||||
linker = "arm-linux-gnueabi-gcc"
|
linker = "arm-linux-gnueabi-gcc"
|
||||||
|
@ -56,5 +30,3 @@ Now you can easily compile the stuff by invoking
|
||||||
```
|
```
|
||||||
cargo build --release --target=arm-unknown-linux-gnueabi
|
cargo build --release --target=arm-unknown-linux-gnueabi
|
||||||
```
|
```
|
||||||
|
|
||||||
[^1]: User feedback
|
|
272
src/database.rs
272
src/database.rs
|
@ -1,272 +0,0 @@
|
||||||
use rusqlite::{named_params, Connection, Transaction};
|
|
||||||
|
|
||||||
use crate::epub;
|
|
||||||
|
|
||||||
const DATABASE_FILE: &str = "/mnt/ext1/system/explorer-3/explorer-3.db";
|
|
||||||
|
|
||||||
pub struct BookEntry {
|
|
||||||
id: i32,
|
|
||||||
filepath: String,
|
|
||||||
author: String,
|
|
||||||
firstauthor: String,
|
|
||||||
genre: String,
|
|
||||||
first_author_letter: String,
|
|
||||||
series: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_epubs_from_database(tx: &Transaction) -> Vec<BookEntry> {
|
|
||||||
let mut book_entries = Vec::new();
|
|
||||||
|
|
||||||
let version: i32 = tx
|
|
||||||
.query_row(r#"SELECT id FROM version"#, [], |r| r.get(0))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let books_or_files = match version {
|
|
||||||
x if x >= 38 => "files",
|
|
||||||
_ => "books",
|
|
||||||
};
|
|
||||||
|
|
||||||
let stmt_str = format!(
|
|
||||||
r#"
|
|
||||||
SELECT books.id, folders.name, files.filename, books.firstauthor,
|
|
||||||
books.author, genres.name, first_author_letter, series
|
|
||||||
FROM books_impl books JOIN files
|
|
||||||
ON books.id = files.book_id
|
|
||||||
JOIN folders
|
|
||||||
ON folders.id = files.folder_id
|
|
||||||
LEFT OUTER JOIN booktogenre btg
|
|
||||||
ON books.id = btg.bookid
|
|
||||||
LEFT OUTER JOIN genres
|
|
||||||
ON genres.id = btg.genreid
|
|
||||||
WHERE files.storageid = 1 AND {}.ext = 'epub'
|
|
||||||
ORDER BY books.id"#,
|
|
||||||
&books_or_files
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut stmt = tx.prepare(&stmt_str).unwrap();
|
|
||||||
|
|
||||||
let mut rows = stmt.query([]).unwrap();
|
|
||||||
|
|
||||||
while let Some(row) = rows.next().unwrap() {
|
|
||||||
let book_id: i32 = row.get(0).unwrap();
|
|
||||||
let prefix: String = row.get(1).unwrap();
|
|
||||||
let filename: String = row.get(2).unwrap();
|
|
||||||
let filepath = format!("{}/{}", prefix, filename);
|
|
||||||
let firstauthor: String = row.get(3).unwrap_or_default();
|
|
||||||
let author: String = row.get(4).unwrap_or_default();
|
|
||||||
let genre: String = row.get(5).unwrap_or_default();
|
|
||||||
let first_author_letter = row.get(6).unwrap_or_default();
|
|
||||||
let series: String = row.get(7).unwrap_or_default();
|
|
||||||
|
|
||||||
let entry = BookEntry {
|
|
||||||
id: book_id,
|
|
||||||
filepath,
|
|
||||||
firstauthor,
|
|
||||||
author,
|
|
||||||
genre,
|
|
||||||
first_author_letter,
|
|
||||||
series,
|
|
||||||
};
|
|
||||||
|
|
||||||
book_entries.push(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
book_entries
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_ghost_books_from_db(tx: &Transaction) -> usize {
|
|
||||||
let mut stmt = tx
|
|
||||||
.prepare(
|
|
||||||
r#"
|
|
||||||
DELETE FROM books_impl
|
|
||||||
WHERE id IN (
|
|
||||||
SELECT books.id
|
|
||||||
FROM books_impl books
|
|
||||||
LEFT OUTER JOIN files
|
|
||||||
ON books.id = files.book_id
|
|
||||||
WHERE files.filename is NULL
|
|
||||||
)"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let num = stmt.execute([]).unwrap();
|
|
||||||
|
|
||||||
tx.execute(
|
|
||||||
r#"DELETE FROM books_settings WHERE bookid NOT IN ( SELECT id FROM books_impl )"#,
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let version: i32 = tx
|
|
||||||
.query_row(r#"SELECT id FROM version"#, [], |r| r.get(0))
|
|
||||||
.unwrap();
|
|
||||||
if version >= 37 {
|
|
||||||
tx.execute(
|
|
||||||
r#"DELETE FROM books_fast_hashes WHERE book_id NOT IN ( SELECT id FROM books_impl )"#,
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
tx.execute(
|
|
||||||
r#"DELETE FROM books_uids WHERE book_id NOT IN ( SELECT id FROM books_impl )"#,
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.execute(
|
|
||||||
r#"DELETE FROM bookshelfs_books WHERE bookid NOT IN ( SELECT id FROM books_impl )"#,
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
tx.execute(
|
|
||||||
r#"DELETE FROM booktogenre WHERE bookid NOT IN ( SELECT id FROM books_impl )"#,
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
tx.execute(
|
|
||||||
r#"DELETE FROM social WHERE bookid NOT IN ( SELECT id FROM books_impl )"#,
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
num
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Statistics {
|
|
||||||
pub authors_fixed: i32,
|
|
||||||
pub ghost_books_cleaned: usize,
|
|
||||||
pub genres_fixed: usize,
|
|
||||||
pub sorting_fixed: usize,
|
|
||||||
pub series_fixed: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Statistics {
|
|
||||||
pub fn anything_fixed(&self) -> bool {
|
|
||||||
&self.authors_fixed > &0
|
|
||||||
|| &self.genres_fixed > &0
|
|
||||||
|| &self.ghost_books_cleaned > &0
|
|
||||||
|| &self.sorting_fixed > &0
|
|
||||||
|| &self.series_fixed > &0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fix_db_entries() -> Statistics {
|
|
||||||
let mut stat = Statistics {
|
|
||||||
authors_fixed: 0,
|
|
||||||
ghost_books_cleaned: 0,
|
|
||||||
genres_fixed: 0,
|
|
||||||
sorting_fixed: 0,
|
|
||||||
series_fixed: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut conn = Connection::open(DATABASE_FILE).unwrap();
|
|
||||||
conn.pragma_update(None, "foreign_keys", &0).unwrap();
|
|
||||||
let tx = conn.transaction().unwrap();
|
|
||||||
|
|
||||||
let book_entries = get_epubs_from_database(&tx);
|
|
||||||
|
|
||||||
for entry in book_entries {
|
|
||||||
if let Some(epub_metadata) = epub::get_epub_metadata(&entry.filepath) {
|
|
||||||
// Fix firstauthor…
|
|
||||||
let mut firstauthors = epub_metadata
|
|
||||||
.authors
|
|
||||||
.iter()
|
|
||||||
.filter(|aut| aut.firstauthor.len() > 0)
|
|
||||||
.map(|aut| aut.firstauthor.clone())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
firstauthors.sort();
|
|
||||||
if !firstauthors.iter().all(|s| entry.firstauthor.contains(s)) {
|
|
||||||
let mut stmt = tx
|
|
||||||
.prepare("UPDATE books_impl SET firstauthor = :file_as WHERE id = :book_id")
|
|
||||||
.unwrap();
|
|
||||||
stmt.execute(
|
|
||||||
named_params![":file_as": firstauthors.join(" & "), ":book_id": entry.id],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
stat.authors_fixed = stat.authors_fixed + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix first_author_letter
|
|
||||||
let first_author_letter = firstauthors
|
|
||||||
.join(" & ")
|
|
||||||
.chars()
|
|
||||||
.next()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.to_string()
|
|
||||||
.to_uppercase();
|
|
||||||
|
|
||||||
if first_author_letter != "\0" && (entry.first_author_letter != first_author_letter) {
|
|
||||||
let mut stmt = tx
|
|
||||||
.prepare("UPDATE books_impl SET first_author_letter = :first_letter WHERE id = :book_id")
|
|
||||||
.unwrap();
|
|
||||||
stmt.execute(
|
|
||||||
named_params![":first_letter": first_author_letter,":book_id": entry.id],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
stat.sorting_fixed = stat.sorting_fixed + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix author names…
|
|
||||||
let authornames = epub_metadata
|
|
||||||
.authors
|
|
||||||
.iter()
|
|
||||||
.map(|aut| aut.name.clone())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
if !authornames.iter().all(|s| entry.author.contains(s))
|
|
||||||
|| authornames.join(", ").len() != entry.author.len()
|
|
||||||
{
|
|
||||||
let mut stmt = tx
|
|
||||||
.prepare("UPDATE books_impl SET author = :authors WHERE id = :book_id")
|
|
||||||
.unwrap();
|
|
||||||
stmt.execute(
|
|
||||||
named_params![":authors": authornames.join(", "), ":book_id": entry.id],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
stat.authors_fixed = stat.authors_fixed + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix genre…
|
|
||||||
if entry.genre.is_empty() && epub_metadata.genre.len() > 0 {
|
|
||||||
let mut stmt = tx
|
|
||||||
.prepare(r#"INSERT INTO genres (name) SELECT :genre ON CONFLICT DO NOTHING"#)
|
|
||||||
.unwrap();
|
|
||||||
stmt.execute(named_params![":genre": &epub_metadata.genre])
|
|
||||||
.unwrap();
|
|
||||||
let mut stmt = tx
|
|
||||||
.prepare(
|
|
||||||
r#"
|
|
||||||
INSERT INTO booktogenre (bookid, genreid)
|
|
||||||
VALUES (:bookid,
|
|
||||||
(SELECT id FROM genres WHERE name = :genre)
|
|
||||||
)
|
|
||||||
ON CONFLICT DO NOTHING"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
stmt.execute(named_params![":bookid": &entry.id, ":genre": &epub_metadata.genre])
|
|
||||||
.unwrap();
|
|
||||||
stat.genres_fixed = stat.genres_fixed + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix series…
|
|
||||||
if !epub_metadata.series.name.is_empty() && entry.series.is_empty() {
|
|
||||||
let mut stmt = tx
|
|
||||||
.prepare("UPDATE books_impl SET series = :series, numinseries = :series_index WHERE id = :book_id")
|
|
||||||
.unwrap();
|
|
||||||
stmt.execute(
|
|
||||||
named_params![":series": &epub_metadata.series.name, ":series_index": &epub_metadata.series.index, ":book_id": entry.id],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
stat.series_fixed = stat.series_fixed + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ghost books
|
|
||||||
let num = remove_ghost_books_from_db(&tx);
|
|
||||||
stat.ghost_books_cleaned = num;
|
|
||||||
|
|
||||||
tx.commit().unwrap();
|
|
||||||
|
|
||||||
stat
|
|
||||||
}
|
|
316
src/epub.rs
316
src/epub.rs
|
@ -1,316 +0,0 @@
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
fs::{self, File},
|
|
||||||
io::Read,
|
|
||||||
};
|
|
||||||
|
|
||||||
use quick_xml::{events::Event, Reader};
|
|
||||||
use zip::ZipArchive;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Author {
|
|
||||||
pub name: String,
|
|
||||||
pub firstauthor: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Series {
|
|
||||||
pub name: String,
|
|
||||||
pub index: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Series {
|
|
||||||
fn new() -> Self {
|
|
||||||
Series {
|
|
||||||
name: String::new(),
|
|
||||||
index: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct EpubMetadata {
|
|
||||||
pub authors: Vec<Author>,
|
|
||||||
pub genre: String,
|
|
||||||
pub series: Series,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EpubMetadata {
|
|
||||||
fn new() -> Self {
|
|
||||||
EpubMetadata {
|
|
||||||
authors: Vec::new(),
|
|
||||||
genre: String::new(),
|
|
||||||
series: Series::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_rootfile(archive: &mut ZipArchive<File>) -> String {
|
|
||||||
let mut container = archive.by_name("META-INF/container.xml").unwrap();
|
|
||||||
let mut xml_str_buffer = String::new();
|
|
||||||
|
|
||||||
container.read_to_string(&mut xml_str_buffer).unwrap();
|
|
||||||
|
|
||||||
let mut reader = Reader::from_str(&xml_str_buffer);
|
|
||||||
reader.trim_text(true);
|
|
||||||
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
let mut opf_filename = String::new();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match reader.read_event(&mut buf) {
|
|
||||||
Ok(Event::Start(ref e)) | Ok(Event::Empty(ref e)) if e.local_name() == b"rootfile" => {
|
|
||||||
opf_filename = String::from_utf8(
|
|
||||||
e.attributes()
|
|
||||||
.filter(|attr| attr.as_ref().unwrap().key == b"full-path")
|
|
||||||
.next()
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
.value
|
|
||||||
.to_vec(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Ok(Event::Eof) => break,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
opf_filename
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_epub_metadata(filename: &str) -> Option<EpubMetadata> {
|
|
||||||
let mut epub_meta = EpubMetadata::new();
|
|
||||||
let file = fs::File::open(&filename);
|
|
||||||
|
|
||||||
let file = match file {
|
|
||||||
Err(_) => return None,
|
|
||||||
Ok(file) => file,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut archive = ZipArchive::new(file).unwrap();
|
|
||||||
|
|
||||||
let opf_filename = get_rootfile(&mut archive);
|
|
||||||
|
|
||||||
let mut xml_str_buffer = String::new();
|
|
||||||
let mut opf = archive.by_name(&opf_filename).unwrap();
|
|
||||||
opf.read_to_string(&mut xml_str_buffer).unwrap();
|
|
||||||
|
|
||||||
let mut reader = Reader::from_str(&xml_str_buffer);
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
|
|
||||||
let mut curr_id = String::new();
|
|
||||||
let mut creator_found = false;
|
|
||||||
let mut file_as_found = false;
|
|
||||||
let mut role_found = false;
|
|
||||||
let mut genre_found = false;
|
|
||||||
let mut series_found = false;
|
|
||||||
let mut series_index_found = false;
|
|
||||||
let mut is_epub3 = false;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct XmlAut {
|
|
||||||
name: String,
|
|
||||||
sort: String,
|
|
||||||
role: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl XmlAut {
|
|
||||||
fn new() -> Self {
|
|
||||||
XmlAut {
|
|
||||||
name: String::new(),
|
|
||||||
sort: String::new(),
|
|
||||||
role: String::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut xml_authors = HashMap::new();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match reader.read_event(&mut buf) {
|
|
||||||
// See if we have EPUB3 or EPUB2
|
|
||||||
Ok(Event::Start(ref e)) if e.local_name() == b"package" => {
|
|
||||||
if e.attributes().any(|attr| {
|
|
||||||
attr.as_ref().unwrap().key == b"version"
|
|
||||||
&& attr.as_ref().unwrap().value.starts_with(b"3")
|
|
||||||
}) {
|
|
||||||
is_epub3 = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Event::Start(ref e)) if e.local_name() == b"creator" => {
|
|
||||||
creator_found = true;
|
|
||||||
if is_epub3 {
|
|
||||||
if let Some(idval) = e
|
|
||||||
.attributes()
|
|
||||||
.filter(|attr| attr.as_ref().unwrap().key == b"id")
|
|
||||||
.next()
|
|
||||||
{
|
|
||||||
curr_id = "#".to_string()
|
|
||||||
+ String::from_utf8(idval.unwrap().value.to_vec())
|
|
||||||
.unwrap()
|
|
||||||
.as_str();
|
|
||||||
xml_authors.insert(curr_id.clone(), XmlAut::new());
|
|
||||||
} else {
|
|
||||||
curr_id = "none".to_string() + xml_authors.len().to_string().as_str();
|
|
||||||
let entry = xml_authors.entry(curr_id.clone()).or_insert(XmlAut::new());
|
|
||||||
entry.role = "aut".to_string();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let Some(file_as_val) = e
|
|
||||||
.attributes()
|
|
||||||
.filter(|attr| attr.as_ref().unwrap().key.ends_with(b"file-as"))
|
|
||||||
.next()
|
|
||||||
{
|
|
||||||
curr_id = "none".to_string() + xml_authors.len().to_string().as_str();
|
|
||||||
let entry = xml_authors.entry(curr_id.clone()).or_insert(XmlAut::new());
|
|
||||||
entry.sort = file_as_val
|
|
||||||
.unwrap()
|
|
||||||
.unescape_and_decode_value(&reader)
|
|
||||||
.unwrap_or_default();
|
|
||||||
entry.role = "aut".to_string();
|
|
||||||
} else if let Some(_role_val) = e
|
|
||||||
.attributes()
|
|
||||||
.filter(|attr| attr.as_ref().unwrap().key.ends_with(b"role"))
|
|
||||||
.next()
|
|
||||||
{
|
|
||||||
curr_id = "none".to_string() + xml_authors.len().to_string().as_str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Event::Text(ref e)) if creator_found => {
|
|
||||||
if is_epub3 {
|
|
||||||
let entry = xml_authors.entry(curr_id.clone()).or_insert(XmlAut::new());
|
|
||||||
entry.name = String::from_utf8(e.to_vec()).unwrap();
|
|
||||||
} else {
|
|
||||||
let entry = xml_authors.entry(curr_id.clone()).or_insert(XmlAut::new());
|
|
||||||
entry.name = String::from_utf8(e.to_vec()).unwrap();
|
|
||||||
entry.role = "aut".to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
creator_found = false;
|
|
||||||
}
|
|
||||||
Ok(Event::Start(ref e)) if e.local_name() == b"meta" && is_epub3 => {
|
|
||||||
if let Some(refines) = e
|
|
||||||
.attributes()
|
|
||||||
.filter(|attr| attr.as_ref().unwrap().key == b"refines")
|
|
||||||
.next()
|
|
||||||
{
|
|
||||||
if e.attributes().any(|attr| {
|
|
||||||
attr.as_ref().unwrap().key == b"property"
|
|
||||||
&& attr.as_ref().unwrap().value.ends_with(b"file-as")
|
|
||||||
}) {
|
|
||||||
curr_id = String::from_utf8(refines.unwrap().value.to_vec()).unwrap();
|
|
||||||
file_as_found = true;
|
|
||||||
} else if e.attributes().any(|attr| {
|
|
||||||
attr.as_ref().unwrap().key == b"property"
|
|
||||||
&& attr.as_ref().unwrap().value.ends_with(b"role")
|
|
||||||
}) {
|
|
||||||
curr_id = String::from_utf8(refines.unwrap().value.to_vec()).unwrap();
|
|
||||||
role_found = true;
|
|
||||||
} else if e.attributes().any(|attr| {
|
|
||||||
attr.as_ref().unwrap().key == b"property"
|
|
||||||
&& attr.as_ref().unwrap().value.ends_with(b"group-position")
|
|
||||||
}) {
|
|
||||||
series_index_found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if e.attributes().any(|attr| {
|
|
||||||
attr.as_ref().unwrap().key == b"property"
|
|
||||||
&& attr
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.value
|
|
||||||
.ends_with(b"belongs-to-collection")
|
|
||||||
}) {
|
|
||||||
series_found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Event::Empty(ref e)) if e.local_name() == b"meta" && !is_epub3 => {
|
|
||||||
if e.attributes().any(|attr| {
|
|
||||||
attr.as_ref().unwrap().key == b"name"
|
|
||||||
&& attr
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.unescaped_value()
|
|
||||||
.unwrap()
|
|
||||||
.ends_with(b"series")
|
|
||||||
}) {
|
|
||||||
epub_meta.series.name = e
|
|
||||||
.attributes()
|
|
||||||
.filter(|attr| attr.as_ref().unwrap().key == b"content")
|
|
||||||
.next()
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
.unescape_and_decode_value(&reader)
|
|
||||||
.unwrap_or_default();
|
|
||||||
} else if e.attributes().any(|attr| {
|
|
||||||
attr.as_ref().unwrap().key == b"name"
|
|
||||||
&& attr
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.unescaped_value()
|
|
||||||
.unwrap()
|
|
||||||
.ends_with(b"series_index")
|
|
||||||
}) {
|
|
||||||
let index_float = e
|
|
||||||
.attributes()
|
|
||||||
.filter(|attr| attr.as_ref().unwrap().key == b"content")
|
|
||||||
.next()
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
.unescape_and_decode_value(&reader)
|
|
||||||
.unwrap_or_default()
|
|
||||||
.parse::<f32>()
|
|
||||||
.unwrap_or_default();
|
|
||||||
epub_meta.series.index = index_float as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Event::Text(ref e)) if file_as_found && is_epub3 => {
|
|
||||||
let entry = xml_authors.entry(curr_id.clone()).or_insert(XmlAut::new());
|
|
||||||
entry.sort = String::from_utf8(e.to_vec()).unwrap();
|
|
||||||
|
|
||||||
file_as_found = false;
|
|
||||||
}
|
|
||||||
Ok(Event::Text(ref e)) if role_found && is_epub3 => {
|
|
||||||
let entry = xml_authors.entry(curr_id.clone()).or_insert(XmlAut::new());
|
|
||||||
entry.role = String::from_utf8(e.to_vec()).unwrap();
|
|
||||||
|
|
||||||
role_found = false;
|
|
||||||
}
|
|
||||||
Ok(Event::Text(ref e)) if series_found && is_epub3 => {
|
|
||||||
epub_meta.series.name = String::from_utf8(e.to_vec()).unwrap();
|
|
||||||
|
|
||||||
series_found = false;
|
|
||||||
}
|
|
||||||
Ok(Event::Text(ref e)) if series_index_found && is_epub3 => {
|
|
||||||
epub_meta.series.index = String::from_utf8(e.to_vec())
|
|
||||||
.unwrap()
|
|
||||||
.parse()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
series_index_found = false;
|
|
||||||
}
|
|
||||||
Ok(Event::Start(ref e)) if e.local_name() == b"subject" => {
|
|
||||||
genre_found = true;
|
|
||||||
}
|
|
||||||
Ok(Event::Text(ref e)) if genre_found => {
|
|
||||||
epub_meta.genre = e.unescape_and_decode(&reader).unwrap();
|
|
||||||
genre_found = false;
|
|
||||||
}
|
|
||||||
Ok(Event::Eof) => break,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
epub_meta.authors = xml_authors
|
|
||||||
.into_iter()
|
|
||||||
.filter(|&(_, ref xml_author)| &xml_author.role == "aut" && &xml_author.name.len() > &0)
|
|
||||||
.map(|(_key, value)| Author {
|
|
||||||
name: value.name,
|
|
||||||
firstauthor: value.sort,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Some(epub_meta)
|
|
||||||
}
|
|
230
src/main.rs
230
src/main.rs
|
@ -1,66 +1,198 @@
|
||||||
mod database;
|
|
||||||
mod epub;
|
|
||||||
mod pocketbook;
|
mod pocketbook;
|
||||||
|
|
||||||
fn main() {
|
use rusqlite::{named_params, Connection, Result, NO_PARAMS};
|
||||||
if cfg!(target_arch = "arm") {
|
use std::error::Error;
|
||||||
let res = pocketbook::dialog(
|
use std::fs::File;
|
||||||
pocketbook::Icon::None,
|
use std::io::BufReader;
|
||||||
"PocketBook has sometimes problems parsing metadata.\n\
|
use xml::reader::{EventReader, ParserConfig, XmlEvent};
|
||||||
This app tries to fix some of these issues.\n\
|
use zip::{read::ZipFile, ZipArchive};
|
||||||
(Note: The database file explore-3.db will be altered!)\n\
|
|
||||||
\n\
|
fn get_root_file(container: ZipFile) -> Result<Option<String>, Box<dyn Error>> {
|
||||||
Please be patient - this might take a while.\n\
|
let parser = EventReader::new(container);
|
||||||
You will see a blank screen during the process.\n\
|
|
||||||
\n\
|
for e in parser {
|
||||||
Proceed?",
|
match e {
|
||||||
&["Cancel", "Yes"],
|
Ok(XmlEvent::StartElement {
|
||||||
);
|
name, attributes, ..
|
||||||
if res == 1 {
|
}) if name.local_name == "rootfile" => {
|
||||||
return;
|
for attr in attributes {
|
||||||
|
if attr.name.local_name == "full-path" {
|
||||||
|
return Ok(Some(attr.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(Box::new(e));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_attribute_file_as(opf: ZipFile) -> Option<String> {
|
||||||
|
let parser = ParserConfig::new()
|
||||||
|
.trim_whitespace(true)
|
||||||
|
.ignore_comments(true)
|
||||||
|
.coalesce_characters(true)
|
||||||
|
.create_reader(opf);
|
||||||
|
|
||||||
|
let mut refines_found = false;
|
||||||
|
let mut refines_entries = Vec::new();
|
||||||
|
let mut is_epub3 = false;
|
||||||
|
let mut creator_ids = Vec::new();
|
||||||
|
|
||||||
|
for e in parser {
|
||||||
|
match e {
|
||||||
|
Ok(XmlEvent::StartElement {
|
||||||
|
name, attributes, ..
|
||||||
|
}) if name.local_name == "package" => {
|
||||||
|
for attr in attributes {
|
||||||
|
if attr.name.local_name == "version" {
|
||||||
|
if attr.value.starts_with("3") == true {
|
||||||
|
is_epub3 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(XmlEvent::StartElement {
|
||||||
|
name, attributes, ..
|
||||||
|
}) if name.local_name == "creator" => {
|
||||||
|
for attr in attributes {
|
||||||
|
if attr.name.local_name == "file-as" {
|
||||||
|
println!("File-as: {}", &attr.value);
|
||||||
|
return Some(attr.value);
|
||||||
|
}
|
||||||
|
if is_epub3 && attr.name.local_name == "id" {
|
||||||
|
creator_ids.push("#".to_owned() + attr.value.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(XmlEvent::StartElement {
|
||||||
|
name, attributes, ..
|
||||||
|
}) if name.local_name == "meta" => {
|
||||||
|
if attributes.iter().any(|attr| {
|
||||||
|
attr.name.local_name == "refines" && creator_ids.contains(&attr.value)
|
||||||
|
}) && attributes
|
||||||
|
.iter()
|
||||||
|
.any(|attr| attr.name.local_name == "property" && attr.value == "file-as")
|
||||||
|
{
|
||||||
|
refines_found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(XmlEvent::Characters(value)) => {
|
||||||
|
if refines_found == true {
|
||||||
|
refines_entries.push(value);
|
||||||
|
refines_found = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(XmlEvent::StartElement { .. }) => {
|
||||||
|
if refines_found == true {
|
||||||
|
refines_found = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_e) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let stat = database::fix_db_entries();
|
if refines_entries.len() == 1 {
|
||||||
|
return Some(refines_entries.remove(0));
|
||||||
|
} else if refines_entries.len() >= 2 {
|
||||||
|
return Some(refines_entries.join(" & "));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BookEntry {
|
||||||
|
id: i32,
|
||||||
|
filepath: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut authors_fixed = 0;
|
||||||
|
|
||||||
|
let mut conn = Connection::open("/mnt/ext1/system/explorer-3/explorer-3.db").unwrap();
|
||||||
|
let tx = conn.transaction().unwrap();
|
||||||
|
{
|
||||||
|
// Get book ids from entries where we have something like "firstname lastname" in author
|
||||||
|
// but no "lastname, firstname" in fistauthor
|
||||||
|
let mut stmt = tx.prepare("SELECT id FROM books_impl WHERE ext LIKE 'epub' AND author LIKE '% %' AND (firstauthor NOT LIKE '%\\,%' ESCAPE '\\' OR firstauthor LIKE '%&%')").unwrap();
|
||||||
|
let mut rows = stmt.query(NO_PARAMS).unwrap();
|
||||||
|
let mut book_ids: Vec<i32> = Vec::new();
|
||||||
|
while let Some(row) = rows.next().unwrap() {
|
||||||
|
book_ids.push(row.get(0).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get also book ids from the special case where we have multiple authors (separated by ", " in authors)
|
||||||
|
// but no ampersand ("&") in firstauthor
|
||||||
|
let mut stmt = tx.prepare("SELECT id FROM books_impl WHERE ext LIKE 'epub' AND author LIKE '%\\, %' ESCAPE '\\' AND firstauthor NOT LIKE '%&%'").unwrap();
|
||||||
|
let mut rows = stmt.query(NO_PARAMS).unwrap();
|
||||||
|
while let Some(row) = rows.next().unwrap() {
|
||||||
|
book_ids.push(row.get(0).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
book_ids.sort();
|
||||||
|
book_ids.dedup();
|
||||||
|
|
||||||
|
let mut bookentries = Vec::new();
|
||||||
|
|
||||||
|
for book_id in book_ids {
|
||||||
|
let mut stmt = tx.prepare("SELECT folders.name,files.filename FROM files,folders WHERE files.book_id = :book_id AND files.storageid = 1 AND files.folder_id = folders.id").unwrap();
|
||||||
|
let mut rows = stmt
|
||||||
|
.query_named(named_params! { ":book_id": book_id })
|
||||||
|
.unwrap();
|
||||||
|
while let Some(row) = rows.next().unwrap() {
|
||||||
|
let prefix: String = row.get(0).unwrap();
|
||||||
|
let filename: String = row.get(1).unwrap();
|
||||||
|
let filepath = format!("{}/{}", prefix, filename);
|
||||||
|
bookentries.push(BookEntry {
|
||||||
|
id: book_id,
|
||||||
|
filepath: filepath,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in bookentries {
|
||||||
|
let file = File::open(entry.filepath.as_str());
|
||||||
|
let file = match file {
|
||||||
|
Err(_) => continue,
|
||||||
|
Ok(file) => file,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut archive = ZipArchive::new(BufReader::new(file)).unwrap();
|
||||||
|
|
||||||
|
let container = archive.by_name("META-INF/container.xml").unwrap();
|
||||||
|
|
||||||
|
if let Some(opf_file) = get_root_file(container).unwrap() {
|
||||||
|
let opf = archive.by_name(opf_file.as_str()).unwrap();
|
||||||
|
if let Some(file_as) = get_attribute_file_as(opf) {
|
||||||
|
let mut stmt = tx
|
||||||
|
.prepare("UPDATE books_impl SET firstauthor = :file_as WHERE id = :book_id")
|
||||||
|
.unwrap();
|
||||||
|
stmt.execute_named(named_params![":file_as": file_as, ":book_id": entry.id])
|
||||||
|
.unwrap();
|
||||||
|
authors_fixed = authors_fixed + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tx.commit().unwrap();
|
||||||
|
|
||||||
if cfg!(target_arch = "arm") {
|
if cfg!(target_arch = "arm") {
|
||||||
if stat.anything_fixed() == false {
|
if authors_fixed == 0 {
|
||||||
pocketbook::dialog(
|
pocketbook::dialog(
|
||||||
pocketbook::Icon::Info,
|
pocketbook::Icon::Info,
|
||||||
"The database seems to be ok.\n\
|
"The database seems to be ok.\nNothing had to be fixed.",
|
||||||
Nothing had to be fixed.",
|
|
||||||
&["OK"],
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
pocketbook::dialog(
|
pocketbook::dialog(
|
||||||
pocketbook::Icon::Info,
|
pocketbook::Icon::Info,
|
||||||
&format!(
|
&format!("Authors fixed: {}", &authors_fixed),
|
||||||
"Authors fixed: {}\n\
|
|
||||||
Sorting fixed: {}\n\
|
|
||||||
Genres fixed: {}\n\
|
|
||||||
Series fixed: {}\n\
|
|
||||||
Books cleaned from DB: {}",
|
|
||||||
&stat.authors_fixed,
|
|
||||||
&stat.sorting_fixed,
|
|
||||||
&stat.genres_fixed,
|
|
||||||
&stat.series_fixed,
|
|
||||||
&stat.ghost_books_cleaned
|
|
||||||
),
|
|
||||||
&["OK"],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
"Authors fixed: {}\n\
|
|
||||||
Sorting fixed: {}\n\
|
|
||||||
Genres fixed: {}\n\
|
|
||||||
Series fixed: {}\n\
|
|
||||||
Books cleaned from DB: {}",
|
|
||||||
&stat.authors_fixed,
|
|
||||||
&stat.sorting_fixed,
|
|
||||||
&stat.genres_fixed,
|
|
||||||
&stat.series_fixed,
|
|
||||||
&stat.ghost_books_cleaned
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub enum Icon {
|
||||||
WLan,
|
WLan,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dialog(icon: Icon, text: &str, buttons: &[&str]) -> i32 {
|
pub fn dialog(icon: Icon, text: &str) {
|
||||||
let iconstr = match icon {
|
let iconstr = match icon {
|
||||||
Icon::None => "0",
|
Icon::None => "0",
|
||||||
Icon::Info => "1",
|
Icon::Info => "1",
|
||||||
|
@ -22,10 +22,8 @@ pub fn dialog(icon: Icon, text: &str, buttons: &[&str]) -> i32 {
|
||||||
Icon::WLan => "5",
|
Icon::WLan => "5",
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = Command::new(DIALOG_PATH)
|
Command::new(DIALOG_PATH)
|
||||||
.args([&[iconstr, "", text], buttons].concat())
|
.args(&[iconstr, "", text, "OK"])
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
res.status.code().unwrap_or(-1)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue