From a52b8a82883b9fcb136b8c5924a643c9561edef1 Mon Sep 17 00:00:00 2001 From: Martin Brodbeck Date: Mon, 15 Feb 2021 13:05:32 +0100 Subject: [PATCH] fix missing series information --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 2 ++ src/epub.rs | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++--- src/main.rs | 24 +++++++++++++- 5 files changed, 114 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c65061..fccaaa4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -148,7 +148,7 @@ dependencies = [ [[package]] name = "pbdbfixer" -version = "0.6.0" +version = "0.7.0" dependencies = [ "quick-xml", "rusqlite", diff --git a/Cargo.toml b/Cargo.toml index 05c3b23..1f36995 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pbdbfixer" -version = "0.6.0" +version = "0.7.0" authors = ["Martin Brodbeck "] edition = "2018" diff --git a/README.md b/README.md index b348db3..8839722 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,12 @@ The app tries to fix the following issues in the database - 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) ## Compatibility This program is tested on a PocketBook - *Touch HD 3* (software version 6.1.900) +- *Inkpad 3 Pro* (software version 6.0.1067) - *Touch Lux 4* (software version 6.0.1118) 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!). diff --git a/src/epub.rs b/src/epub.rs index ed63f75..759c91f 100644 --- a/src/epub.rs +++ b/src/epub.rs @@ -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, pub genre: String, + pub series: Series, } impl EpubMetadata { @@ -24,6 +40,7 @@ impl EpubMetadata { EpubMetadata { authors: Vec::new(), genre: String::new(), + series: Series::new(), } } } @@ -87,6 +104,8 @@ pub fn get_epub_metadata(filename: &str) -> Option { 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)] @@ -185,8 +204,63 @@ pub fn get_epub_metadata(filename: &str) -> Option { }) { 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::() + .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,6 +274,19 @@ pub fn get_epub_metadata(filename: &str) -> Option { 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; } @@ -212,8 +299,6 @@ pub fn get_epub_metadata(filename: &str) -> Option { } } - //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 +308,5 @@ pub fn get_epub_metadata(filename: &str) -> Option { }) .collect(); - //println!("Meta: {:?}", &epub_meta); - Some(epub_meta) } diff --git a/src/main.rs b/src/main.rs index 97056b7..b2cc8b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ struct BookEntry { has_drm: bool, genre: String, first_author_letter: String, + series: String, } fn get_epubs_from_database(tx: &Transaction) -> Vec { @@ -21,7 +22,7 @@ fn get_epubs_from_database(tx: &Transaction) -> Vec { .prepare( r#" SELECT books.id, folders.name, files.filename, books.firstauthor, - books.author, genres.name, first_author_letter + books.author, genres.name, first_author_letter, series FROM books_impl books JOIN files ON books.id = files.book_id JOIN folders @@ -50,6 +51,7 @@ fn get_epubs_from_database(tx: &Transaction) -> Vec { }; 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, @@ -59,6 +61,7 @@ fn get_epubs_from_database(tx: &Transaction) -> Vec { has_drm, genre, first_author_letter, + series, }; book_entries.push(entry); @@ -119,6 +122,7 @@ struct Statistics { drm_skipped: usize, genres_fixed: usize, sorting_fixed: usize, + series_fixed: usize, } impl Statistics { @@ -127,6 +131,7 @@ impl Statistics { || &self.genres_fixed > &0 || &self.ghost_books_cleaned > &0 || &self.sorting_fixed > &0 + || &self.series_fixed > &0 } } @@ -137,6 +142,7 @@ fn fix_db_entries(tx: &Transaction, book_entries: &Vec) -> Statistics drm_skipped: 0, genres_fixed: 0, sorting_fixed: 0, + series_fixed: 0, }; for entry in book_entries { @@ -224,6 +230,18 @@ fn fix_db_entries(tx: &Transaction, book_entries: &Vec) -> Statistics .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( + 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; + } } } @@ -290,11 +308,13 @@ fn main() { "Authors fixed: {}\n\ Sorting fixed: {}\n\ Genres fixed: {}\n\ + Series fixed: {}\n\ Books skipped (DRM): {}\n\ Books cleaned from DB: {}", &stat.authors_fixed, &stat.sorting_fixed, &stat.genres_fixed, + &stat.series_fixed, &stat.drm_skipped, &stat.ghost_books_cleaned ), @@ -306,11 +326,13 @@ fn main() { "Authors fixed: {}\n\ Sorting fixed: {}\n\ Genres fixed: {}\n\ + Series fixed: {}\n\ Books skipped (DRM): {}\n\ Books cleaned from DB: {}", &stat.authors_fixed, &stat.sorting_fixed, &stat.genres_fixed, + &stat.series_fixed, &stat.drm_skipped, &stat.ghost_books_cleaned );