Compare commits

..

43 commits

Author SHA1 Message Date
b02ede5182 get_epub_metadata() simplified. 2024-11-12 10:57:03 +01:00
da77983228 get_rootfile() simplified 2024-11-12 10:37:01 +01:00
8b38cee7b8 README updated 2024-11-11 10:15:03 +01:00
e9fa11b700 push to new version 2024-11-08 14:29:28 +01:00
18873c4b3e Update description for compiling the stuff 2024-11-08 14:10:51 +01:00
85f7dd0e45 Merge branch 'wip' 2024-11-08 14:04:04 +01:00
2cf198e52b Cargo.lock updated 2024-11-08 14:03:41 +01:00
445662727e strip release builds 2024-11-07 14:26:52 +01:00
660ff77dfc some tweaks, email added 2024-11-05 14:19:59 +01:00
23c126ec9a deps updated
particularly quick_xml which caused some code changes. Lots of testing should be done.
2024-11-05 14:01:48 +01:00
d5c2b277dc cargo update 2024-11-05 13:00:08 +01:00
4c425dbddf Add Debian as an example. 2024-08-12 12:07:13 +02:00
339e8534ec README.md aktualisiert 2024-05-17 08:33:28 +02:00
fb30301da8 note added 2024-05-13 07:44:34 +02:00
e1d0ac3200 README.md aktualisiert 2024-04-15 11:11:58 +02:00
9f0484f4a8 User feedback added 2024-01-10 10:53:40 +01:00
aa0f1f6999 Tested firmware versions updated 2023-06-14 10:32:03 +02:00
acdeb3d19a dependencies updated 2022-07-12 13:21:59 +02:00
b30209c62f Tested versions 6.4 -> 6.5 2022-02-08 12:36:31 +01:00
5af608f45b Rust edition 2018 → 2021 2021-10-25 08:40:50 +02:00
6db590f333 Push to new version. 2021-08-06 15:14:50 +02:00
56624427d5 Adapted to the new database schema. 2021-08-06 15:10:55 +02:00
8ef3238ac3 Push to new version. 2021-08-06 11:03:10 +02:00
49853f9d2f Do not rely on database column ext.
Since firmware version 6.4 seems to ignore this column, we also look at the file name suffix.
2021-08-06 11:00:16 +02:00
e1beced72c Merge branch 'master' of ssh://git.rustysoft.de:60022/martin/pbdbfixer 2021-05-26 13:36:22 +02:00
995bc5e3ed updated to rusqlite 0.25 2021-05-26 13:29:43 +02:00
3a794874dc software versions updated 2021-04-27 07:51:12 +02:00
ef7710cca6 Calibre emphasized 2021-03-29 15:25:10 +02:00
f50e5e09e6 firmware versions updated 2021-03-29 15:24:28 +02:00
2fcb2ac3e7 Considering the altered database structure
fixing #2
2021-03-29 15:19:03 +02:00
ad179ac9e0
push to new dependency versions 2021-03-04 12:57:23 +01:00
d683804623 feedback part added 2021-03-03 14:04:50 +01:00
ec11c16477 build instructions improved 2021-03-03 13:29:35 +01:00
65007fdf50 push to new version 2021-02-26 13:47:56 +01:00
a6594d4abc Also fetch metadata from Adobe DRM encoded books 2021-02-26 13:28:29 +01:00
9baff090a8 push to new version 2021-02-24 14:05:03 +01:00
94e1dedb1e set foreign_key pragma correctly 2021-02-24 14:03:35 +01:00
da405064d4 update dependencies 2021-02-19 13:14:52 +01:00
e2e2468543 more on usage and compatibility 2021-02-19 13:12:28 +01:00
735335941e db stuff moved to new database module 2021-02-17 16:20:57 +01:00
624987326d Also remove "false positive" authors
If PB adds an author who doesn't have the "aut"-role, make sure to
remove this false-positive entry
2021-02-15 14:13:38 +01:00
a52b8a8288 fix missing series information 2021-02-15 13:05:32 +01:00
7d8ab91d85 readme updated 2021-02-12 10:10:10 +01:00
6 changed files with 1000 additions and 417 deletions

651
Cargo.lock generated
View file

@ -1,34 +1,77 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler32"
version = "1.2.0"
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "aes"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "ahash"
version = "0.4.7"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "arbitrary"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "775a8770d29db3dadcb858482cc240af7b2ffde4ac4de67d1d4955728103f0e2"
dependencies = [
"derive_arbitrary",
]
[[package]]
name = "bitflags"
version = "1.2.1"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "byteorder"
version = "1.4.2"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bzip2"
version = "0.3.3"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b"
checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
dependencies = [
"bzip2-sys",
"libc",
@ -36,9 +79,9 @@ dependencies = [
[[package]]
name = "bzip2-sys"
version = "0.1.9+1.0.8"
version = "0.1.11+1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad3b39a260062fca31f7b0b12f207e8f2590a67d32ec7d59c20484b07ea7285e"
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
dependencies = [
"cc",
"libc",
@ -47,15 +90,14 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.66"
version = "1.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
checksum = "67b9470d453346108f93a59222a9a1a5724db32d0a4727b7ab7ace4b4d822dc9"
dependencies = [
"jobserver",
"libc",
"shlex",
]
[[package]]
name = "cfg-if"
@ -64,19 +106,129 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crc32fast"
version = "1.2.1"
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"cfg-if 1.0.0",
"crypto-common",
"inout",
]
[[package]]
name = "fallible-iterator"
version = "0.2.0"
name = "constant_time_eq"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
[[package]]
name = "cpufeatures"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
dependencies = [
"libc",
]
[[package]]
name = "crc"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "deflate64"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b"
[[package]]
name = "deranged"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
]
[[package]]
name = "derive_arbitrary"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d475dfebcb4854d596b17b09f477616f80f17a550517f2b3615d8c205d5c802b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fallible-iterator"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "fallible-streaming-iterator"
@ -86,45 +238,107 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "flate2"
version = "1.0.14"
version = "1.0.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42"
checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
dependencies = [
"cfg-if 0.1.10",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]]
name = "hashbrown"
version = "0.9.1"
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
]
[[package]]
name = "hashlink"
version = "0.6.0"
name = "hashbrown"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d99cf782f0dc4372d26846bec3de7804ceb5df083c2d4462c0b8d2330e894fa8"
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
[[package]]
name = "hashlink"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
dependencies = [
"hashbrown",
"hashbrown 0.14.5",
]
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "indexmap"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [
"equivalent",
"hashbrown 0.15.1",
]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"generic-array",
]
[[package]]
name = "jobserver"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
dependencies = [
"libc",
]
[[package]]
name = "libc"
version = "0.2.83"
version = "0.2.162"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb0c4e9c72ee9d69b767adebc5f4788462a3b45624acd919475c92597bcaf4f"
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
[[package]]
name = "libsqlite3-sys"
version = "0.20.1"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64d31059f22935e6c31830db5249ba2b7ecd54fd73a9909286f0a67aa55c2fbd"
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
dependencies = [
"cc",
"pkg-config",
@ -132,23 +346,57 @@ dependencies = [
]
[[package]]
name = "memchr"
version = "2.3.4"
name = "lockfree-object-pool"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e"
[[package]]
name = "miniz_oxide"
version = "0.3.7"
name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "lzma-rs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
dependencies = [
"adler32",
"byteorder",
"crc",
]
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
dependencies = [
"adler2",
]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "once_cell"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "pbdbfixer"
version = "0.6.0"
version = "0.9.0"
dependencies = [
"quick-xml",
"rusqlite",
@ -156,84 +404,187 @@ dependencies = [
]
[[package]]
name = "pkg-config"
version = "0.3.19"
name = "pbkdf2"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
dependencies = [
"digest",
"hmac",
]
[[package]]
name = "pkg-config"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.24"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "quick-xml"
version = "0.21.0"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0452695941410a58c8ce4391707ba9bad26a247173bd9886a05a5e8a8babec75"
checksum = "ffbfb3ddf5364c9cfcd65549a1e7b801d0e8d1b14c1a1590a6408aa93cfbfa84"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "1.0.8"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rusqlite"
version = "0.24.2"
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f38ee71cbab2c827ec0ac24e76f82eca723cee92c509a65f67dee393c25112"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rusqlite"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
dependencies = [
"bitflags",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"memchr",
"smallvec",
]
[[package]]
name = "smallvec"
version = "1.6.1"
name = "serde"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
dependencies = [
"serde_derive",
]
[[package]]
name = "syn"
version = "1.0.60"
name = "serde_derive"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"syn",
]
[[package]]
name = "sha1"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.23"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146"
checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.23"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e"
dependencies = [
"proc-macro2",
"quote",
@ -242,65 +593,161 @@ dependencies = [
[[package]]
name = "time"
version = "0.1.44"
version = "0.3.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"libc",
"wasi",
"winapi",
"deranged",
"num-conv",
"powerfmt",
"serde",
"time-core",
]
[[package]]
name = "unicode-xid"
version = "0.2.1"
name = "time-core"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "vcpkg"
version = "0.2.11"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
"byteorder",
"zerocopy-derive",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
dependencies = [
"zeroize_derive",
]
[[package]]
name = "zeroize_derive"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zip"
version = "0.5.9"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc2896475a242c41366941faa27264df2cb935185a92e059a004d0048feb2ac5"
checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494"
dependencies = [
"byteorder",
"aes",
"arbitrary",
"bzip2",
"constant_time_eq",
"crc32fast",
"crossbeam-utils",
"deflate64",
"displaydoc",
"flate2",
"hmac",
"indexmap",
"lzma-rs",
"memchr",
"pbkdf2",
"rand",
"sha1",
"thiserror",
"time",
"zeroize",
"zopfli",
"zstd",
]
[[package]]
name = "zopfli"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946"
dependencies = [
"bumpalo",
"crc32fast",
"lockfree-object-pool",
"log",
"once_cell",
"simd-adler32",
]
[[package]]
name = "zstd"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.13+zstd.1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
dependencies = [
"cc",
"pkg-config",
]

View file

@ -1,15 +1,18 @@
[package]
name = "pbdbfixer"
version = "0.6.0"
version = "0.9.0"
authors = ["Martin Brodbeck <martin@brodbeck-online.de>"]
edition = "2018"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
zip = "0.5"
quick-xml = "0.21"
zip = "2.2.0"
quick-xml = "0.37"
[dependencies.rusqlite]
version = "0.24"
version = "0.32.1"
features = ["bundled"]
[profile.release]
strip = true

View file

@ -1,28 +1,31 @@
# PbDbFixer
## Motivation
Since Pocketbook has some problems with extracting metadata correctly from
EPUB files, this program tries fix these issues. It tries to identify
wrong database entries and fix it by reading the corresponding epub
metadata.
Since Pocketbook has some problems with extracting metadata correctly from EPUB files, this program tries fix these issues. It tries to identify wrong database entries and fix it by reading the corresponding epub metadata.
## Features
The app tries to fix the following issues in the database
PbDbFixer 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
This program is tested on a PocketBook
- *Touch HD 3* (software version 6.1.900)
- *Touch Lux 4* (software version 6.0.1118)
This program has been tested on a PocketBook
- *Touch Lux 4* (software version 6.5)
- *Touch HD 3* (software version 6.7)
- *Era* (software version 6.8) [^1]
- *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!).
## Installation and Usage
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.
---
**WARNING**:
@ -30,13 +33,27 @@ Use at your own risk. In case of doubt it is not a mistake to make a backup of t
---
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 Chat [@beedaddy:matrix.rustysoft.de](https://matrix.to/#/@beedaddy:matrix.rustysoft.de)
- [PbDbFixer-Thread](https://www.e-reader-forum.de/t/pbdbfixer-noch-ein-tool-zum-korrigieren-von-metadaten.156702/) of the German *E-Reader Forum*.
- E-Mail: info@rustysoft.de
## Build
If you want to build PbDbFixer yourself, 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 had to edit `~/.cargo/config`:
If you want to build PbDbFixer yourself, make sure that you have Rust's toolchain target `armv7-unknown-linux-gnueabi` installed. Additionaly, you should use [PocketBook's SDK](https://github.com/pocketbook/SDK_6.3.0/tree/6.5) for being able to cross-compile specific for these devices. Don't forget to tell `cargo` which compiler/linker it has to invoke. In my case, I had to edit `~/.cargo/config.toml`:
```
[target.arm-unknown-linux-gnueabi]
linker = "arm-linux-gnueabi-gcc"
[target.armv7-unknown-linux-gnueabi]
linker = "SDKs/PB/SDK_6.3.0/SDK-B288/usr/bin/arm-obreey-linux-gnueabi-gcc"
```
Now you can easily compile the stuff by invoking
Now you can easily compile the stuff. Note that you have to tell where the libraries are located (`LD_LIBRARY_PATH`):
```
cargo build --release --target=arm-unknown-linux-gnueabi
LD_LIBRARY_PATH="$HOME/SDKs/PB/SDK_6.3.0/SDK-B288/usr/lib" cargo build --release --target=armv7-unknown-linux-gnueabi
```
[^1]: User feedback

272
src/database.rs Normal file
View file

@ -0,0 +1,272 @@
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
}

View file

@ -13,10 +13,26 @@ pub struct Author {
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 {
@ -24,6 +40,7 @@ impl EpubMetadata {
EpubMetadata {
authors: Vec::new(),
genre: String::new(),
series: Series::new(),
}
}
}
@ -35,24 +52,25 @@ fn get_rootfile(archive: &mut ZipArchive<File>) -> String {
container.read_to_string(&mut xml_str_buffer).unwrap();
let mut reader = Reader::from_str(&xml_str_buffer);
reader.trim_text(true);
reader.config_mut().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();
match reader.read_event_into(&mut buf) {
Ok(Event::Start(ref e)) | Ok(Event::Empty(ref e))
if e.name().as_ref() == b"rootfile" =>
{
opf_filename = e
.attributes()
.filter(|attr| attr.as_ref().unwrap().key.as_ref() == b"full-path")
.next()
.unwrap()
.unwrap()
.unescape_value()
.unwrap()
.to_string();
break;
}
Ok(Event::Eof) => break,
@ -87,6 +105,8 @@ pub fn get_epub_metadata(filename: &str) -> Option<EpubMetadata> {
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)]
@ -109,22 +129,22 @@ pub fn get_epub_metadata(filename: &str) -> Option<EpubMetadata> {
let mut xml_authors = HashMap::new();
loop {
match reader.read_event(&mut buf) {
match reader.read_event_into(&mut buf) {
// See if we have EPUB3 or EPUB2
Ok(Event::Start(ref e)) if e.local_name() == b"package" => {
Ok(Event::Start(ref e)) if e.name().as_ref() == b"package" => {
if e.attributes().any(|attr| {
attr.as_ref().unwrap().key == b"version"
attr.as_ref().unwrap().key.as_ref() == 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" => {
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"creator" => {
creator_found = true;
if is_epub3 {
if let Some(idval) = e
.attributes()
.filter(|attr| attr.as_ref().unwrap().key == b"id")
.filter(|attr| attr.as_ref().unwrap().key.as_ref() == b"id")
.next()
{
curr_id = "#".to_string()
@ -132,23 +152,34 @@ pub fn get_epub_metadata(filename: &str) -> Option<EpubMetadata> {
.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"))
.filter(|attr| {
attr.as_ref()
.unwrap()
.key
.as_ref()
.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();
.unescape_value()
.unwrap_or_default()
.to_string();
entry.role = "aut".to_string();
} else if let Some(_role_val) = e
.attributes()
.filter(|attr| attr.as_ref().unwrap().key.ends_with(b"role"))
.filter(|attr| attr.as_ref().unwrap().key.as_ref().ends_with(b"role"))
.next()
{
curr_id = "none".to_string() + xml_authors.len().to_string().as_str();
@ -167,26 +198,82 @@ pub fn get_epub_metadata(filename: &str) -> Option<EpubMetadata> {
creator_found = false;
}
Ok(Event::Start(ref e)) if e.local_name() == b"meta" && is_epub3 => {
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"meta" && is_epub3 => {
if let Some(refines) = e
.attributes()
.filter(|attr| attr.as_ref().unwrap().key == b"refines")
.filter(|attr| attr.as_ref().unwrap().key.as_ref() == b"refines")
.next()
{
if e.attributes().any(|attr| {
attr.as_ref().unwrap().key == b"property"
attr.as_ref().unwrap().key.as_ref() == 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().key.as_ref() == 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.as_ref() == 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.as_ref() == 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().as_ref() == b"meta" && !is_epub3 => {
if e.attributes().any(|attr| {
attr.as_ref().unwrap().key.as_ref() == b"name"
&& attr
.as_ref()
.unwrap()
.unescape_value()
.unwrap()
.ends_with("series")
}) {
epub_meta.series.name = e
.attributes()
.filter(|attr| attr.as_ref().unwrap().key.as_ref() == b"content")
.next()
.unwrap()
.unwrap()
.unescape_value()
.unwrap_or_default()
.to_string();
} else if e.attributes().any(|attr| {
attr.as_ref().unwrap().key.as_ref() == b"name"
&& attr
.as_ref()
.unwrap()
.unescape_value()
.unwrap()
.ends_with("series_index")
}) {
let index_float = e
.attributes()
.filter(|attr| attr.as_ref().unwrap().key.as_ref() == b"content")
.next()
.unwrap()
.unwrap()
.unescape_value()
.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());
@ -200,11 +287,24 @@ pub fn get_epub_metadata(filename: &str) -> Option<EpubMetadata> {
role_found = false;
}
Ok(Event::Start(ref e)) if e.local_name() == b"subject" => {
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().as_ref() == b"subject" => {
genre_found = true;
}
Ok(Event::Text(ref e)) if genre_found => {
epub_meta.genre = e.unescape_and_decode(&reader).unwrap();
epub_meta.genre = e.unescape().unwrap().to_string();
genre_found = false;
}
Ok(Event::Eof) => break,
@ -212,8 +312,6 @@ pub fn get_epub_metadata(filename: &str) -> Option<EpubMetadata> {
}
}
//println!("Meta: {:?}", &xml_authors);
epub_meta.authors = xml_authors
.into_iter()
.filter(|&(_, ref xml_author)| &xml_author.role == "aut" && &xml_author.name.len() > &0)
@ -223,7 +321,5 @@ pub fn get_epub_metadata(filename: &str) -> Option<EpubMetadata> {
})
.collect();
//println!("Meta: {:?}", &epub_meta);
Some(epub_meta)
}

View file

@ -1,239 +1,7 @@
mod database;
mod epub;
mod pocketbook;
use rusqlite::{named_params, Connection, Transaction, NO_PARAMS};
use std::usize;
struct BookEntry {
id: i32,
filepath: String,
author: String,
firstauthor: String,
has_drm: bool,
genre: String,
first_author_letter: String,
}
fn get_epubs_from_database(tx: &Transaction) -> Vec<BookEntry> {
let mut book_entries = Vec::new();
let mut stmt = tx
.prepare(
r#"
SELECT books.id, folders.name, files.filename, books.firstauthor,
books.author, genres.name, first_author_letter
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 books.ext = 'epub'
ORDER BY books.id"#,
)
.unwrap();
let mut rows = stmt.query(NO_PARAMS).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();
let author: String = row.get(4).unwrap();
let has_drm = match prefix.as_str() {
"/mnt/ext1/Digital Editions" => true,
_ => false,
};
let genre: String = row.get(5).unwrap_or_default();
let first_author_letter = row.get(6).unwrap_or_default();
let entry = BookEntry {
id: book_id,
filepath,
firstauthor,
author,
has_drm,
genre,
first_author_letter,
};
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(NO_PARAMS).unwrap();
tx.execute(
r#"DELETE FROM books_settings WHERE bookid NOT IN ( SELECT id FROM books_impl )"#,
NO_PARAMS,
)
.unwrap();
tx.execute(
r#"DELETE FROM books_uids WHERE book_id NOT IN ( SELECT id FROM books_impl )"#,
NO_PARAMS,
)
.unwrap();
tx.execute(
r#"DELETE FROM bookshelfs_books WHERE bookid NOT IN ( SELECT id FROM books_impl )"#,
NO_PARAMS,
)
.unwrap();
tx.execute(
r#"DELETE FROM booktogenre WHERE bookid NOT IN ( SELECT id FROM books_impl )"#,
NO_PARAMS,
)
.unwrap();
tx.execute(
r#"DELETE FROM social WHERE bookid NOT IN ( SELECT id FROM books_impl )"#,
NO_PARAMS,
)
.unwrap();
num
}
struct Statistics {
authors_fixed: i32,
ghost_books_cleaned: usize,
drm_skipped: usize,
genres_fixed: usize,
sorting_fixed: usize,
}
impl Statistics {
fn anything_fixed(&self) -> bool {
&self.authors_fixed > &0
|| &self.genres_fixed > &0
|| &self.ghost_books_cleaned > &0
|| &self.sorting_fixed > &0
}
}
fn fix_db_entries(tx: &Transaction, book_entries: &Vec<BookEntry>) -> Statistics {
let mut stat = Statistics {
authors_fixed: 0,
ghost_books_cleaned: 0,
drm_skipped: 0,
genres_fixed: 0,
sorting_fixed: 0,
};
for entry in book_entries {
if entry.has_drm {
stat.drm_skipped = stat.drm_skipped + 1;
continue;
}
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(
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 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(
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)) {
let mut stmt = tx
.prepare("UPDATE books_impl SET author = :authors WHERE id = :book_id")
.unwrap();
stmt.execute_named(
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(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(
named_params![":bookid": &entry.id, ":genre": &epub_metadata.genre],
)
.unwrap();
stat.genres_fixed = stat.genres_fixed + 1;
}
}
}
// ghost books
let num = remove_ghost_books_from_db(tx);
stat.ghost_books_cleaned = num;
stat
}
fn main() {
if cfg!(target_arch = "arm") {
let res = pocketbook::dialog(
@ -253,36 +21,16 @@ fn main() {
}
}
let mut conn = Connection::open("/mnt/ext1/system/explorer-3/explorer-3.db").unwrap();
conn.execute("PRAGMA foreign_keys = 0", NO_PARAMS).unwrap();
let tx = conn.transaction().unwrap();
let book_entries = get_epubs_from_database(&tx);
let stat = fix_db_entries(&tx, &book_entries);
tx.commit().unwrap();
let stat = database::fix_db_entries();
if cfg!(target_arch = "arm") {
if stat.anything_fixed() == false {
if stat.drm_skipped == 0 {
pocketbook::dialog(
pocketbook::Icon::Info,
"The database seems to be ok.\n\
pocketbook::dialog(
pocketbook::Icon::Info,
"The database seems to be ok.\n\
Nothing had to be fixed.",
&["OK"],
);
} else {
pocketbook::dialog(
pocketbook::Icon::Info,
&format!(
"The database seems to be ok.\n\
Nothing had to be fixed.\n\
(Books skipped (DRM): {})",
&stat.drm_skipped
),
&["OK"],
);
}
&["OK"],
);
} else {
pocketbook::dialog(
pocketbook::Icon::Info,
@ -290,12 +38,12 @@ fn main() {
"Authors fixed: {}\n\
Sorting fixed: {}\n\
Genres fixed: {}\n\
Books skipped (DRM): {}\n\
Series fixed: {}\n\
Books cleaned from DB: {}",
&stat.authors_fixed,
&stat.sorting_fixed,
&stat.genres_fixed,
&stat.drm_skipped,
&stat.series_fixed,
&stat.ghost_books_cleaned
),
&["OK"],
@ -306,12 +54,12 @@ fn main() {
"Authors fixed: {}\n\
Sorting fixed: {}\n\
Genres fixed: {}\n\
Books skipped (DRM): {}\n\
Series fixed: {}\n\
Books cleaned from DB: {}",
&stat.authors_fixed,
&stat.sorting_fixed,
&stat.genres_fixed,
&stat.drm_skipped,
&stat.series_fixed,
&stat.ghost_books_cleaned
);
}