aboutsummaryrefslogtreecommitdiffstats
path: root/epy.py
diff options
context:
space:
mode:
authorbenadha <benawiadha@gmail.com>2022-01-09 10:43:31 +0700
committerbenadha <benawiadha@gmail.com>2022-01-09 10:43:31 +0700
commit8c4c11c08a57996fbc59c1ad11ab96f3a7e86d33 (patch)
tree877017a90c136cda4a2c930edd4c083b4ea1f437 /epy.py
parente6276268dba32bf6fbd53602303835e62a0f6726 (diff)
downloadepy-8c4c11c08a57996fbc59c1ad11ab96f3a7e86d33.tar.gz
Add vim config
Diffstat (limited to 'epy.py')
-rwxr-xr-xepy.py237
1 files changed, 148 insertions, 89 deletions
diff --git a/epy.py b/epy.py
index 5e7aedb..0e46331 100755
--- a/epy.py
+++ b/epy.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
+# vim:tabstop=4:shiftwidth=4:softtabstop=4:smarttab:expandtab:foldmethod=marker
"""\
Usages:
epy read last ebook
@@ -22,6 +23,8 @@ __email__ = "benawiadha@gmail.com"
__url__ = "https://github.com/wustho/epy"
+# Imports {{{
+
import base64
import contextlib
import curses
@@ -58,6 +61,10 @@ try:
except ModuleNotFoundError:
MOBI_SUPPORT = False
+# }}}
+
+
+# Debug Utils {{{
try:
# Debug swith
@@ -83,9 +90,10 @@ try:
except ValueError:
DEBUG = False
+# }}}
-# ----------------------- MODELS ---------------------------
+# Data Models {{{
# add image viewers here
# sorted by most widely used
@@ -112,92 +120,6 @@ class DoubleSpreadPadding(Enum):
RIGHT = 10
-class SpeakerBaseModel:
- cmd: str = "tts_engine_binary"
- available: bool = False
-
- def __init__(self, args: List[str] = []):
- self.args = args
-
- def speak(self, text: str) -> None:
- raise NotImplementedError("Speaker.speak() not implemented")
-
- def is_done(self) -> bool:
- raise NotImplementedError("Speaker.is_done() not implemented")
-
- def stop(self) -> None:
- raise NotImplementedError("Speaker.stop() not implemented")
-
- def cleanup(self) -> None:
- raise NotImplementedError("Speaker.cleanup() not implemented")
-
-
-class SpeakerMimic(SpeakerBaseModel):
- cmd = "mimic"
- available = bool(shutil.which("mimic"))
-
- def speak(self, text: str) -> None:
- self.process = subprocess.Popen(
- [self.cmd, *self.args],
- text=True,
- stdin=subprocess.PIPE,
- stdout=subprocess.DEVNULL,
- stderr=subprocess.STDOUT,
- )
- assert self.process.stdin
- self.process.stdin.write(text)
- self.process.stdin.close()
-
- def is_done(self) -> bool:
- return self.process.poll() is not None
-
- def stop(self) -> None:
- self.process.terminate()
- # self.process.kill()
-
- def cleanup(self) -> None:
- pass
-
-
-class SpeakerPico(SpeakerBaseModel):
- cmd = "pico2wave"
- available = all([shutil.which(dep) for dep in ["pico2wave", "play"]])
-
- def speak(self, text: str) -> None:
- _, self.tmp_path = tempfile.mkstemp(suffix=".wav")
-
- try:
- subprocess.run(
- [self.cmd, *self.args, "-w", self.tmp_path, text],
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- text=True,
- check=True,
- )
- except subprocess.CalledProcessError as e:
- if "invalid pointer" not in e.output:
- sys.exit(e.output)
-
- self.process = subprocess.Popen(
- ["play", self.tmp_path],
- stdout=subprocess.DEVNULL,
- stderr=subprocess.DEVNULL,
- )
-
- def is_done(self) -> bool:
- return self.process.poll() is not None
-
- def stop(self) -> None:
- self.process.terminate()
- # self.process.kill()
-
- def cleanup(self) -> None:
- os.remove(self.tmp_path)
-
-
-SPEAKERS: List[Type[SpeakerBaseModel]] = [SpeakerMimic, SpeakerPico]
-
-
@dataclass(frozen=True)
class BookMetadata:
title: Optional[str] = None
@@ -493,6 +415,104 @@ class Keymap:
TableOfContents: Tuple[Key, ...]
+# }}}
+
+
+# Speaker / TTS Engine Wrappers {{{
+
+
+class SpeakerBaseModel:
+ cmd: str = "tts_engine_binary"
+ available: bool = False
+
+ def __init__(self, args: List[str] = []):
+ self.args = args
+
+ def speak(self, text: str) -> None:
+ raise NotImplementedError("Speaker.speak() not implemented")
+
+ def is_done(self) -> bool:
+ raise NotImplementedError("Speaker.is_done() not implemented")
+
+ def stop(self) -> None:
+ raise NotImplementedError("Speaker.stop() not implemented")
+
+ def cleanup(self) -> None:
+ raise NotImplementedError("Speaker.cleanup() not implemented")
+
+
+class SpeakerMimic(SpeakerBaseModel):
+ cmd = "mimic"
+ available = bool(shutil.which("mimic"))
+
+ def speak(self, text: str) -> None:
+ self.process = subprocess.Popen(
+ [self.cmd, *self.args],
+ text=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.STDOUT,
+ )
+ assert self.process.stdin
+ self.process.stdin.write(text)
+ self.process.stdin.close()
+
+ def is_done(self) -> bool:
+ return self.process.poll() is not None
+
+ def stop(self) -> None:
+ self.process.terminate()
+ # self.process.kill()
+
+ def cleanup(self) -> None:
+ pass
+
+
+class SpeakerPico(SpeakerBaseModel):
+ cmd = "pico2wave"
+ available = all([shutil.which(dep) for dep in ["pico2wave", "play"]])
+
+ def speak(self, text: str) -> None:
+ _, self.tmp_path = tempfile.mkstemp(suffix=".wav")
+
+ try:
+ subprocess.run(
+ [self.cmd, *self.args, "-w", self.tmp_path, text],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ text=True,
+ check=True,
+ )
+ except subprocess.CalledProcessError as e:
+ if "invalid pointer" not in e.output:
+ sys.exit(e.output)
+
+ self.process = subprocess.Popen(
+ ["play", self.tmp_path],
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ )
+
+ def is_done(self) -> bool:
+ return self.process.poll() is not None
+
+ def stop(self) -> None:
+ self.process.terminate()
+ # self.process.kill()
+
+ def cleanup(self) -> None:
+ os.remove(self.tmp_path)
+
+
+# register wrappers here
+SPEAKERS: List[Type[SpeakerBaseModel]] = [SpeakerMimic, SpeakerPico]
+
+# }}}
+
+
+# Ebooks {{{
+
+
class Ebook:
def __init__(self, fileepub: str):
raise NotImplementedError("Ebook.__init__() not implemented")
@@ -870,6 +890,12 @@ class FictionBook(Ebook):
return
+# }}}
+
+
+# HTML & Text Parser {{{
+
+
class HTMLtoLines(HTMLParser):
para = {"p", "div"}
inde = {"q", "dt", "dd", "blockquote"}
@@ -1255,6 +1281,12 @@ class HTMLtoLines(HTMLParser):
)
+# }}}
+
+
+# App Configuration {{{
+
+
class AppData:
@property
def prefix(self) -> Optional[str]:
@@ -1538,6 +1570,12 @@ class State(AppData):
conn.close()
+# }}}
+
+
+# Text Board {{{
+
+
class InfiniBoard:
"""
Wrapper for curses screen to render infinite texts.
@@ -1680,7 +1718,10 @@ class InfiniBoard:
)
-# ----------------------- HELPERS --------------------------
+# }}}
+
+
+# Helpers Function {{{
def construct_speaker(
@@ -2124,7 +2165,10 @@ def text_win(textfunc):
return wrapper
-# ----------------------- MAIN -----------------------------
+# }}}
+
+
+# Main Reading Interface {{{
class Reader:
@@ -3554,6 +3598,12 @@ class Reader:
sys.exit()
+# }}}
+
+
+# Reading Init {{{
+
+
def preread(stdscr, filepath: str):
global STDSCR
if DEBUG:
@@ -3583,6 +3633,12 @@ def preread(stdscr, filepath: str):
reader.cleanup()
+# }}}
+
+
+# Commandline {{{
+
+
def parse_cli_args() -> str:
"""
Try parsing cli args and return filepath of ebook to read
@@ -3722,5 +3778,8 @@ def main():
curses.wrapper(preread, filepath)
+# }}}
+
+
if __name__ == "__main__":
main()