fix missing series information

This commit is contained in:
Martin Brodbeck 2021-02-15 13:05:32 +01:00
parent 7d8ab91d85
commit a52b8a8288
5 changed files with 114 additions and 7 deletions

2
Cargo.lock generated
View File

@ -148,7 +148,7 @@ dependencies = [
[[package]]
name = "pbdbfixer"
version = "0.6.0"
version = "0.7.0"
dependencies = [
"quick-xml",
"rusqlite",

View File

@ -1,6 +1,6 @@
[package]
name = "pbdbfixer"
version = "0.6.0"
version = "0.7.0"
authors = ["Martin Brodbeck <martin@brodbeck-online.de>"]
edition = "2018"

View File

@ -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!).

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(),
}
}
}
@ -87,6 +104,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)]
@ -185,8 +204,63 @@ pub fn get_epub_metadata(filename: &str) -> Option<EpubMetadata> {
}) {
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());
@ -200,6 +274,19 @@ pub fn get_epub_metadata(filename: &str) -> Option<EpubMetadata> {
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<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 +308,5 @@ pub fn get_epub_metadata(filename: &str) -> Option<EpubMetadata> {
})
.collect();
//println!("Meta: {:?}", &epub_meta);
Some(epub_meta)
}

View File

@ -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<BookEntry> {
@ -21,7 +22,7 @@ fn get_epubs_from_database(tx: &Transaction) -> Vec<BookEntry> {
.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<BookEntry> {
};
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<BookEntry> {
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<BookEntry>) -> 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<BookEntry>) -> 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
);