Cleanup parsers
This commit is contained in:
@@ -37,32 +37,17 @@ class EPUB:
|
||||
def __init__(self, book_filename, temp_dir):
|
||||
self.book_filename = book_filename
|
||||
self.temp_dir = temp_dir
|
||||
|
||||
self.zip_file = None
|
||||
self.file_list = None
|
||||
self.opf_dict = None
|
||||
self.book = {}
|
||||
self.split_chapters = {}
|
||||
|
||||
self.metadata = None
|
||||
self.content = []
|
||||
|
||||
self.generate_references()
|
||||
|
||||
def find_file(self, filename):
|
||||
# Get rid of special characters
|
||||
filename = unquote(filename)
|
||||
|
||||
# First, look for the file in the root of the book
|
||||
if filename in self.file_list:
|
||||
return filename
|
||||
|
||||
# Then search for it elsewhere
|
||||
else:
|
||||
file_basename = os.path.basename(filename)
|
||||
for i in self.file_list:
|
||||
if os.path.basename(i) == file_basename:
|
||||
return i
|
||||
|
||||
# If the file isn't found
|
||||
logging.error(filename + ' not found in ' + self.book_filename)
|
||||
return False
|
||||
|
||||
def generate_references(self):
|
||||
self.zip_file = zipfile.ZipFile(
|
||||
self.book_filename, mode='r', allowZip64=True)
|
||||
@@ -88,9 +73,26 @@ class EPUB:
|
||||
packagefile_data = self.zip_file.read(packagefile)
|
||||
self.opf_dict = xmltodict.parse(packagefile_data)
|
||||
|
||||
def generate_toc(self):
|
||||
self.book['content'] = []
|
||||
def find_file(self, filename):
|
||||
# Get rid of special characters
|
||||
filename = unquote(filename)
|
||||
|
||||
# First, look for the file in the root of the book
|
||||
if filename in self.file_list:
|
||||
return filename
|
||||
|
||||
# Then search for it elsewhere
|
||||
else:
|
||||
file_basename = os.path.basename(filename)
|
||||
for i in self.file_list:
|
||||
if os.path.basename(i) == file_basename:
|
||||
return i
|
||||
|
||||
# If the file isn't found
|
||||
logging.error(filename + ' not found in ' + self.book_filename)
|
||||
return False
|
||||
|
||||
def generate_toc(self):
|
||||
def find_alternative_toc():
|
||||
toc_filename = None
|
||||
toc_filename_alternative = None
|
||||
@@ -134,14 +136,14 @@ class EPUB:
|
||||
level + 1,
|
||||
i['navLabel']['text'],
|
||||
i['content']['@src']] for i in nav_node]
|
||||
self.book['content'].extend(these_contents)
|
||||
self.content.extend(these_contents)
|
||||
return
|
||||
|
||||
if 'navPoint' in nav_node.keys():
|
||||
recursor(level, nav_node['navPoint'])
|
||||
|
||||
else:
|
||||
self.book['content'].append([
|
||||
self.content.append([
|
||||
level + 1,
|
||||
nav_node['navLabel']['text'],
|
||||
nav_node['content']['@src']])
|
||||
@@ -150,14 +152,14 @@ class EPUB:
|
||||
for top_level_nav in navpoints:
|
||||
# Just one chapter
|
||||
if isinstance(top_level_nav, str):
|
||||
self.book['content'].append([
|
||||
self.content.append([
|
||||
1,
|
||||
navpoints['navLabel']['text'],
|
||||
navpoints['content']['@src']])
|
||||
break
|
||||
|
||||
# Multiple chapters
|
||||
self.book['content'].append([
|
||||
self.content.append([
|
||||
1,
|
||||
top_level_nav['navLabel']['text'],
|
||||
top_level_nav['content']['@src']])
|
||||
@@ -183,14 +185,12 @@ class EPUB:
|
||||
return 'Possible parse error: ' + chapter_file
|
||||
|
||||
def parse_split_chapters(self, chapters_with_split_content):
|
||||
self.book['split_chapters'] = {}
|
||||
|
||||
# For split chapters, get the whole chapter first, then split
|
||||
# between ids using their anchors, then "heal" the resultant text
|
||||
# by creating a BeautifulSoup object. Write its str to the content
|
||||
for i in chapters_with_split_content.items():
|
||||
chapter_file = i[0]
|
||||
self.book['split_chapters'][chapter_file] = {}
|
||||
self.split_chapters[chapter_file] = {}
|
||||
|
||||
chapter_content = self.get_chapter_content(chapter_file)
|
||||
soup = BeautifulSoup(chapter_content, 'lxml')
|
||||
@@ -208,10 +208,10 @@ class EPUB:
|
||||
if this_tag:
|
||||
this_markup = BeautifulSoup(
|
||||
str(this_tag).strip() + markup_split[1], 'lxml')
|
||||
self.book['split_chapters'][chapter_file][this_anchor] = str(this_markup)
|
||||
self.split_chapters[chapter_file][this_anchor] = str(this_markup)
|
||||
|
||||
# Remaining markup is assigned here
|
||||
self.book['split_chapters'][chapter_file]['top_level'] = str(soup)
|
||||
self.split_chapters[chapter_file]['top_level'] = str(soup)
|
||||
|
||||
def generate_content(self):
|
||||
# Find all the chapters mentioned in the opf spine
|
||||
@@ -238,7 +238,7 @@ class EPUB:
|
||||
|
||||
chapter_title = 1
|
||||
toc_chapters = [
|
||||
unquote(i[2].split('#')[0]) for i in self.book['content']]
|
||||
unquote(i[2].split('#')[0]) for i in self.content]
|
||||
|
||||
last_valid_index = -2 # Yes, but why?
|
||||
for i in spine_final:
|
||||
@@ -251,7 +251,7 @@ class EPUB:
|
||||
except ValueError:
|
||||
last_valid_index += 1
|
||||
|
||||
self.book['content'].insert(
|
||||
self.content.insert(
|
||||
last_valid_index + 1,
|
||||
[1, str(chapter_title), i])
|
||||
chapter_title += 1
|
||||
@@ -259,7 +259,7 @@ class EPUB:
|
||||
# Parse split chapters as below
|
||||
# They can be picked up during the iteration through the toc
|
||||
chapters_with_split_content = {}
|
||||
for i in self.book['content']:
|
||||
for i in self.content:
|
||||
if '#' in i[2]:
|
||||
this_split = i[2].split('#')
|
||||
chapter = this_split[0]
|
||||
@@ -278,8 +278,7 @@ class EPUB:
|
||||
# In case a split chapter is encountered, get its content
|
||||
# from the split_chapters dictionary
|
||||
# What could possibly go wrong?
|
||||
split_chapters = self.book['split_chapters']
|
||||
toc_copy = self.book['content'][:]
|
||||
toc_copy = self.content[:]
|
||||
|
||||
# Put the book into the book
|
||||
for count, i in enumerate(toc_copy):
|
||||
@@ -293,7 +292,7 @@ class EPUB:
|
||||
|
||||
try:
|
||||
chapter_content = (
|
||||
split_chapters[chapter_file_proper][this_anchor])
|
||||
self.split_chapters[chapter_file_proper][this_anchor])
|
||||
except KeyError:
|
||||
chapter_content = 'Parse Error'
|
||||
error_string = (
|
||||
@@ -301,9 +300,9 @@ class EPUB:
|
||||
logger.error(error_string)
|
||||
|
||||
# Get content that remained at the end of the pillaging above
|
||||
elif chapter_file in split_chapters.keys():
|
||||
elif chapter_file in self.split_chapters.keys():
|
||||
try:
|
||||
chapter_content = split_chapters[chapter_file]['top_level']
|
||||
chapter_content = self.split_chapters[chapter_file]['top_level']
|
||||
except KeyError:
|
||||
chapter_content = 'Parse Error'
|
||||
error_string = (
|
||||
@@ -314,26 +313,26 @@ class EPUB:
|
||||
else:
|
||||
chapter_content = self.get_chapter_content(chapter_file)
|
||||
|
||||
self.book['content'][count][2] = chapter_content
|
||||
self.content[count][2] = chapter_content
|
||||
|
||||
# Cleanup content by removing null chapters
|
||||
self.book['content'] = [
|
||||
i for i in self.book['content'] if i[2]]
|
||||
self.content = [
|
||||
i for i in self.content if i[2]]
|
||||
|
||||
self.generate_book_cover()
|
||||
if self.book['cover']:
|
||||
cover_image = self.generate_book_cover()
|
||||
if cover_image:
|
||||
cover_path = os.path.join(
|
||||
self.temp_dir, os.path.basename(self.book_filename)) + '- cover'
|
||||
with open(cover_path, 'wb') as cover_temp:
|
||||
cover_temp.write(self.book['cover'])
|
||||
cover_temp.write(cover_image)
|
||||
|
||||
# There's probably some rationale to doing an insert here
|
||||
# But a replacement seems... neater
|
||||
self.book['content'].insert(
|
||||
self.content.insert(
|
||||
0, (1, 'Cover', f'<center><img src="{cover_path}" alt="Cover"></center>'))
|
||||
|
||||
def generate_metadata(self):
|
||||
metadata = self.opf_dict['package']['metadata']
|
||||
book_metadata = self.opf_dict['package']['metadata']
|
||||
|
||||
def flattener(this_object):
|
||||
if isinstance(this_object, collections.OrderedDict):
|
||||
@@ -354,67 +353,76 @@ class EPUB:
|
||||
|
||||
# Book title
|
||||
try:
|
||||
self.book['title'] = flattener(metadata['dc:title'])
|
||||
title = flattener(book_metadata['dc:title'])
|
||||
except:
|
||||
self.book['title'] = os.path.splitext(
|
||||
logger.warning('Title not found: ' + self.book_filename)
|
||||
title = os.path.splitext(
|
||||
os.path.basename(self.book_filename))[0]
|
||||
|
||||
# Book author
|
||||
try:
|
||||
self.book['author'] = flattener(metadata['dc:creator'])
|
||||
author = flattener(book_metadata['dc:creator'])
|
||||
except:
|
||||
self.book['author'] = 'Unknown'
|
||||
logger.warning('Author not found: ' + self.book_filename)
|
||||
author = 'Unknown'
|
||||
|
||||
# Book year
|
||||
try:
|
||||
self.book['year'] = int(flattener(metadata['dc:date'])[:4])
|
||||
year = int(flattener(book_metadata['dc:date'])[:4])
|
||||
except:
|
||||
self.book['year'] = 9999
|
||||
logger.warning('Year not found: ' + self.book_filename)
|
||||
year = 9999
|
||||
|
||||
# Book isbn
|
||||
# Both one and multiple schema
|
||||
self.book['isbn'] = None
|
||||
isbn = None
|
||||
try:
|
||||
scheme = metadata['dc:identifier']['@opf:scheme'].lower()
|
||||
scheme = book_metadata['dc:identifier']['@opf:scheme'].lower()
|
||||
if scheme.lower() == 'isbn':
|
||||
self.book['isbn'] = metadata['dc:identifier']['#text']
|
||||
isbn = book_metadata['dc:identifier']['#text']
|
||||
|
||||
except (TypeError, KeyError):
|
||||
try:
|
||||
for i in metadata['dc:identifier']:
|
||||
for i in book_metadata['dc:identifier']:
|
||||
if i['@opf:scheme'].lower() == 'isbn':
|
||||
self.book['isbn'] = i['#text']
|
||||
isbn = i['#text']
|
||||
break
|
||||
except:
|
||||
logger.warning('ISBN not found: ' + self.book_filename)
|
||||
pass
|
||||
|
||||
# Book tags
|
||||
try:
|
||||
self.book['tags'] = metadata['dc:subject']
|
||||
if isinstance(self.book['tags'], str):
|
||||
self.book['tags'] = [self.book['tags']]
|
||||
tags = book_metadata['dc:subject']
|
||||
if isinstance(tags, str):
|
||||
tags = [tags]
|
||||
except:
|
||||
self.book['tags'] = []
|
||||
tags = []
|
||||
|
||||
# Book cover
|
||||
self.generate_book_cover()
|
||||
cover = self.generate_book_cover()
|
||||
|
||||
# Named tuple? Named tuple.
|
||||
Metadata = collections.namedtuple(
|
||||
'Metadata', ['title', 'author', 'year', 'isbn', 'tags', 'cover'])
|
||||
self.metadata = Metadata(title, author, year, isbn, tags, cover)
|
||||
|
||||
def generate_book_cover(self):
|
||||
# This is separate because the book cover needs to
|
||||
# be found and extracted both during addition / reading
|
||||
self.book['cover'] = None
|
||||
book_cover = None
|
||||
|
||||
try:
|
||||
cover_image = [
|
||||
i['@href'] for i in self.opf_dict['package']['manifest']['item']
|
||||
if i['@media-type'].split('/')[0] == 'image' and
|
||||
'cover' in i['@id']][0]
|
||||
self.book['cover'] = self.zip_file.read(
|
||||
self.find_file(cover_image))
|
||||
book_cover = self.zip_file.read(self.find_file(cover_image))
|
||||
except:
|
||||
pass
|
||||
|
||||
# Find book cover the hard way
|
||||
if not self.book['cover']:
|
||||
if not book_cover:
|
||||
biggest_image_size = 0
|
||||
biggest_image = None
|
||||
for j in self.zip_file.filelist:
|
||||
@@ -424,5 +432,10 @@ class EPUB:
|
||||
biggest_image_size = j.file_size
|
||||
|
||||
if biggest_image:
|
||||
self.book['cover'] = self.zip_file.read(
|
||||
book_cover = self.zip_file.read(
|
||||
self.find_file(biggest_image))
|
||||
|
||||
if not book_cover:
|
||||
logger.warning('Cover not found: ' + self.book_filename)
|
||||
|
||||
return book_cover
|
||||
|
Reference in New Issue
Block a user