Skip to content

Instantly share code, notes, and snippets.

@tyrelsouza
Created June 1, 2022 05:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tyrelsouza/9c6681850fc00bf5d9f35568faf611d4 to your computer and use it in GitHub Desktop.
Save tyrelsouza/9c6681850fc00bf5d9f35568faf611d4 to your computer and use it in GitHub Desktop.
Epub Reader
import re
from typing import TypeVar, Dict, Any, List, Optional
import dearpygui.dearpygui as dpg
import ebooklib
from bs4 import BeautifulSoup
from ebooklib import epub
WIDTH: int = 700
class BookBase:
pass
Book = TypeVar('Book', bound=BookBase)
class Epub(BookBase):
def __init__(self, book_path: str) -> None:
self.contents: ebooklib.epub.EpubBook = epub.read_epub(book_path)
self.title: str = self.contents.title
self.toc: List[ebooklib.epub.Link] = self.contents.toc
self.current_index = 0
self.current_text: Optional[str] = None
self.chapters: List[Dict[str, Any]] = []
self.parse_chapters()
def parse_chapters(self) -> None:
idx = 0
for _item in self.toc:
if isinstance(_item, tuple): # In case is section tuple(section, [link, ...])
for link in _item[1]:
self._parse_link(idx, link)
idx += 1
else:
self._parse_link(idx, _item)
idx += 1
def _parse_link(self, idx, link):
title = link.title
self.chapters.append(dict(
index=idx,
title=title,
item=self.contents.get_item_with_href(link.href)
))
def load_view(self) -> None:
item = self.chapters[self.current_index]['item']
soup = BeautifulSoup(item.get_body_content(), "html.parser")
text = [para.get_text() for para in soup.find_all("p")]
self.current_text = "\n".join(text)
self.fix_quotes()
def fix_quotes(self):
self.current_text = re.sub(u"[\u201c\u201d]", '"', self.current_text)
self.current_text = re.sub(u"’", "'", self.current_text)
self.current_text = re.sub(u"—", "--", self.current_text)
def show_chapter(self, index) -> None:
self.current_index = index
self.load_view()
class Readz:
file_name: str = None
book: Optional[Book] = None
# Elements
lbl_title: int = None
btn_open_close: int = None
list_btn_chapter: List[int] = []
w_window: int = None
txt_body: int = None
tbl_main_table: int = None
def __init__(self):
pass
def cb_open_file(self, sender, app_data):
self.file_name = app_data['file_path_name']
self.book = Epub(book_path=self.file_name)
dpg.set_value(self.lbl_title, self.book.title)
self.load_chapter_table()
# dpg.set_item_callback(self.btn_open_close, self.cb_close_file)
# dpg.set_item_label(self.btn_open_close, "Close File")
dpg.hide_item(sender)
self.book.load_view()
dpg.set_value(self.txt_body, self.book.current_text)
def cb_close_file(self):
self.book = None
dpg.set_value(self.lbl_title, "No Book")
dpg.configure_item("chapter_titles", resizable=False)
dpg.set_item_callback(self.btn_open_close, lambda: dpg.show_item("file_dialog_id"))
dpg.set_item_label(self.btn_open_close, "Open File")
dpg.delete_item(item="chapter_titles", children_only=False)
# self.tbl_chapter_titles = dpg.table(tag="chapter_titles", resizable=False)
with dpg.table(tag="chapter_titles", resizable=False):
dpg.add_table_column(label="-Chapter Titles-")
def cb_change_chapter(self, sender, app_data, user_data) -> None:
# print(user_data, self.list_btn_chapter)
self.book.show_chapter(user_data['chapter'])
dpg.set_value(self.txt_body, self.book.current_text)
for chapter in self.list_btn_chapter:
dpg.enable_item(chapter)
dpg.disable_item(sender)
def load_chapter_table(self):
dpg.configure_item("chapter_titles", resizable=False)
for chapter in self.book.chapters:
with dpg.table_row(parent="chapter_titles"):
title = chapter['title']
if title.lower().startswith("chapter "):
title = f"Chp. {title[len('chapter '):]}"
self.list_btn_chapter.append(
dpg.add_button(
label=title,
callback=self.cb_change_chapter,
user_data={"chapter": chapter['index']}))
def dpg_set_themes(self) -> None:
with dpg.theme() as disabled_theme:
with dpg.theme_component(dpg.mvButton, enabled_state=False):
dpg.add_theme_color(dpg.mvThemeCol_Text, [255, 255, 255])
dpg.add_theme_color(dpg.mvThemeCol_Button, [0, 0, 100])
dpg.bind_theme(disabled_theme)
def start(self):
dpg.create_context()
self.dpg_set_themes()
# Configure File Dialog
with dpg.file_dialog(directory_selector=False, show=False, callback=self.cb_open_file, id="file_dialog_id"):
dpg.add_file_extension(".epub", color=(0, 255, 0, 255), custom_text="[Epub]")
# Setup initial window.
self.dpg_main_window()
dpg.create_viewport(title='Readz', width=WIDTH, height=768)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.set_primary_window("MAIN_WINDOW", True)
dpg.start_dearpygui()
dpg.destroy_context()
def dpg_main_window(self):
# Setup initial window.
self.w_window = dpg.window(label="Readz", tag="MAIN_WINDOW", width=WIDTH, height=768)
with self.w_window:
self.tbl_main_table = dpg.table(tag="main_layout", header_row=False)
with self.tbl_main_table:
dpg.add_table_column(width=120, width_fixed=True)
dpg.add_table_column()
with dpg.table_row():
self.lbl_title = dpg.add_text(label="<Title>")
self.btn_open_close = dpg.add_button(
label="Open File",
callback=lambda: dpg.show_item("file_dialog_id"))
with dpg.table_row():
with dpg.table(tag="chapter_titles", resizable=False):
dpg.add_table_column(label="Chapter Titles")
self.txt_body = dpg.add_text("...", tracked=True, wrap=WIDTH-120)
if __name__ == '__main__':
r = Readz()
r.start()
# TODO: Reset the chapter list
# TODO: Make chapter list load a view
# TODO: Switch away from DPG
ebooklib
beautifulsoup4
dearpygui
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment