2021-01-28 17:52:09 +01:00
use rusqlite ::{ named_params , Connection , Result , NO_PARAMS } ;
use std ::error ::Error ;
use std ::fs ::File ;
use std ::io ::BufReader ;
2021-01-29 10:44:07 +01:00
use xml ::reader ::{ EventReader , ParserConfig , XmlEvent } ;
2021-01-28 17:52:09 +01:00
use zip ::{ read ::ZipFile , ZipArchive } ;
fn get_root_file ( container : ZipFile ) -> Result < Option < String > , Box < dyn Error > > {
let parser = EventReader ::new ( container ) ;
for e in parser {
match e {
Ok ( XmlEvent ::StartElement {
name , attributes , ..
} ) if name . local_name = = " rootfile " = > {
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 > {
2021-01-29 10:44:07 +01:00
let parser = ParserConfig ::new ( )
. trim_whitespace ( true )
. ignore_comments ( true )
. coalesce_characters ( true )
. create_reader ( opf ) ;
let mut refines_found = false ;
let mut is_epub3 = false ;
let mut creator_id = String ::new ( ) ;
2021-01-28 17:52:09 +01:00
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 " {
2021-01-29 10:44:07 +01:00
if attr . value . starts_with ( " 3 " ) = = true {
is_epub3 = true ;
2021-01-28 17:52:09 +01:00
}
}
}
}
Ok ( XmlEvent ::StartElement {
name , attributes , ..
} ) if name . local_name = = " creator " = > {
for attr in attributes {
if attr . name . local_name = = " file-as " {
2021-01-29 10:44:07 +01:00
println! ( " File-as: {} " , & attr . value ) ;
2021-01-28 17:52:09 +01:00
return Some ( attr . value ) ;
}
2021-01-29 10:44:07 +01:00
if is_epub3 & & attr . name . local_name = = " id " {
creator_id = attr . value ;
}
}
}
Ok ( XmlEvent ::StartElement {
name , attributes , ..
} ) if name . local_name = = " meta " = > {
if attributes . iter ( ) . any ( | attr | {
attr . name . local_name = = " refines "
& & ( attr . value = = creator_id
| | attr . value = = " # " . to_owned ( ) + creator_id . as_str ( ) )
} ) & & attributes
. iter ( )
. any ( | attr | attr . name . local_name = = " property " & & attr . value = = " file-as " )
{
refines_found = true ;
}
}
Ok ( XmlEvent ::Characters ( value ) ) = > {
if refines_found = = true {
return Some ( value ) ;
}
}
Ok ( XmlEvent ::StartElement { .. } ) = > {
if refines_found = = true {
refines_found = false ;
2021-01-28 17:52:09 +01:00
}
}
Err ( _e ) = > {
break ;
}
_ = > { }
}
}
None
}
struct BookEntry {
id : i32 ,
filepath : String ,
}
fn main ( ) {
let mut conn = Connection ::open ( " /mnt/ext1/system/explorer-3/explorer-3.db " ) . unwrap ( ) ;
let tx = conn . transaction ( ) . unwrap ( ) ;
{
2021-01-29 10:51:20 +01:00
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 ( ) ;
2021-01-28 17:52:09 +01:00
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 ( ) ) ;
}
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 ,
} ) ;
}
}
//println!("Number of entries found: {}", bookentries.len());
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 ( ) ;
}
}
}
}
tx . commit ( ) . unwrap ( ) ;
}