From 13da986b6f982a383e84b4037b2ca2b2f52d2aa9 Mon Sep 17 00:00:00 2001 From: eliranwong Date: Tue, 22 Oct 2024 15:44:35 +0000 Subject: [PATCH] added stream mode --- setup.py | 2 +- uniquebible/latest_changes.txt | 12 +++ uniquebible/main.py | 6 +- uniquebible/startup/nonGui.py | 17 ++++ uniquebible/uba.py | 2 +- uniquebible/util/CatalogUtil.py | 3 +- uniquebible/util/ConfigUtil.py | 13 ++- uniquebible/util/LocalCliHandler.py | 122 ++++++++++++++---------- uniquebible/util/RemoteHttpHandler.py | 37 +++---- uniquebible/util/checkup.py | 4 +- uniquebible/util/text_editor_checkup.py | 2 +- 11 files changed, 143 insertions(+), 77 deletions(-) diff --git a/setup.py b/setup.py index 2ad4434e9a..4c2d57a165 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,7 @@ # https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/ setup( name=package, - version="0.1.07", + version="0.1.12", python_requires=">=3.8, <3.13", description=f"UniqueBible App is a cross-platform & offline bible application, integrated with high-quality resources and unique features. Developers: Eliran Wong and Oliver Tseng", long_description=long_description, diff --git a/uniquebible/latest_changes.txt b/uniquebible/latest_changes.txt index 140a8b1ffe..0dedf836e7 100755 --- a/uniquebible/latest_changes.txt +++ b/uniquebible/latest_changes.txt @@ -1,5 +1,17 @@ PIP package: +0.1.09-12 + +* added a 'stream' mode to work with stdin and stdout, e.g. + +> uniquebible stream John 3:16-18 + +> echo 3:16-18 | uniquebible stream John + +0.1.03-0.1.08 + +* integrated with Toolmate AI, read https://github.com/eliranwong/toolmate + 0.1.02 * fixed plugin menu diff --git a/uniquebible/main.py b/uniquebible/main.py index c53d6883cf..0142ca0284 100755 --- a/uniquebible/main.py +++ b/uniquebible/main.py @@ -39,7 +39,7 @@ os.environ[key] = value # check runmode and initial command -config.noQt = True if config.runMode in ("terminal", "ssh-server", "telnet-server", "http-server", "api-server", "execute-macro") else False +config.noQt = True if config.runMode in ("stream", "terminal", "ssh-server", "telnet-server", "http-server", "api-server", "execute-macro") or os.path.isdir("/data/data/com.termux/files/home") else False config.cli = True if config.runMode == "cli" else False config.enableCli = True if config.runMode in ("cli", "gui", "docker") else False config.enableApiServer = True if config.runMode == "api-server" else False @@ -121,7 +121,9 @@ # ("terminal", "ssh-server", "telnet-server", "http-server", "api-server", "execute-macro") if config.noQt: from uniquebible.startup.nonGui import * - if config.runMode == "terminal": + if config.runMode == "stream": + run_stream_mode() + elif config.runMode == "terminal": run_terminal_mode() elif config.runMode == "ssh-server": run_ssh_server(host=config.sshServerHost, port=config.sshServerPort, server_host_keys=config.sshServerHostKeys, passphrase=config.sshServerPassphrase) diff --git a/uniquebible/startup/nonGui.py b/uniquebible/startup/nonGui.py index 12cc5ed88b..8cb0fddeae 100755 --- a/uniquebible/startup/nonGui.py +++ b/uniquebible/startup/nonGui.py @@ -225,6 +225,23 @@ def run_terminal_mode(): clear_title() sys.exit(0) +# raw mode +def run_stream_mode(): + from uniquebible.util.LocalCliHandler import LocalCliHandler + + input_text = sys.stdin.read() if not sys.stdin.isatty() else "" + + # Set initial command + command = config.initial_command if config.initial_command else " ".join(sys.argv[2:]).strip() + if input_text: + command = f"{command} {input_text}" + if command.strip(): + config.mainWindow = LocalCliHandler() + output_text = config.mainWindow.getContent(command) + else: + output_text = "Command not given!" + print(output_text, file=sys.stdout) + # ssh-server # read setup guide at https://github.com/eliranwong/UniqueBible/wiki/Run-SSH-Server def run_ssh_server(host="", port=2222, server_host_keys="", passphrase="the_best_bible_app"): diff --git a/uniquebible/uba.py b/uniquebible/uba.py index ec91046b45..ec93616bd5 100755 --- a/uniquebible/uba.py +++ b/uniquebible/uba.py @@ -16,7 +16,7 @@ def main(): # check running mode and initial command runMode = sys.argv[1] if len(sys.argv) > 1 else "" - enableCli = True if runMode.lower() in ("cli", "cli.py", "gui", "terminal", "docker", "telnet-server", "http-server", "execute-macro", "api-server") else False + enableCli = True if runMode.lower() in ("stream", "cli", "cli.py", "gui", "terminal", "docker", "telnet-server", "http-server", "execute-macro", "api-server") else False initialCommand = input("Enter command: ").strip() if runMode == "-i" else " ".join(sys.argv[1:]).strip() initialCommand = initialCommand.strip() diff --git a/uniquebible/util/CatalogUtil.py b/uniquebible/util/CatalogUtil.py index 755637532b..359dfad0f2 100644 --- a/uniquebible/util/CatalogUtil.py +++ b/uniquebible/util/CatalogUtil.py @@ -2,8 +2,6 @@ from uniquebible import config from uniquebible.util.FileUtil import FileUtil from uniquebible.util.GitHubRepoInfo import GitHubRepoInfo -if not config.noQt: - from uniquebible.util.GithubUtil import GithubUtil class CatalogUtil: @@ -93,6 +91,7 @@ def loadRemoteCatalog(): @staticmethod def loadRemoteFiles(type, repo): + from uniquebible.util.GithubUtil import GithubUtil data = [] github = GithubUtil(repo[0]) repoData = github.getRepoData() diff --git a/uniquebible/util/ConfigUtil.py b/uniquebible/util/ConfigUtil.py index f2a90e2746..7ed7b7e72d 100644 --- a/uniquebible/util/ConfigUtil.py +++ b/uniquebible/util/ConfigUtil.py @@ -62,8 +62,15 @@ def setup(noQt=None, cli=None, enableCli=None, enableApiServer=None, enableHttpS config.ubaDir = os.getcwd() # check running mode - config.runMode = sys.argv[1].lower() if len(sys.argv) > 1 else "" - if config.runMode and not config.runMode in ("setup-only", "cli", "gui", "terminal", "docker", "telnet-server", "http-server", "execute-macro", "api-server"): + config.runMode = sys.argv[1] if len(sys.argv) > 1 else "" + if " " in config.runMode: + import re + config.runMode, config.initial_command = config.runMode.split(" ", 1) + else: + config.initial_command = "" + config.runMode = config.runMode.lower() + + if config.runMode and not config.runMode in ("stream", "setup-only", "cli", "gui", "terminal", "docker", "telnet-server", "http-server", "execute-macro", "api-server"): config.runMode = "" # Check current version @@ -136,7 +143,7 @@ def getCurrentVenvDir(): []) setConfig("disabled", """ # Disabled modules""", - []) + ['Pygithub', 'Textract', 'Pydnsbl', 'Pocketsphinx'] if os.path.isdir("/data/data/com.termux/files/home") else []) venvDir = getCurrentVenvDir() setConfig("venvDir", """ # virtual environment directory""", diff --git a/uniquebible/util/LocalCliHandler.py b/uniquebible/util/LocalCliHandler.py index 25e826ad0e..8a0f210ec2 100644 --- a/uniquebible/util/LocalCliHandler.py +++ b/uniquebible/util/LocalCliHandler.py @@ -8,6 +8,8 @@ from base64 import b64decode #import urllib.request from ast import literal_eval +from haversine import haversine + from uniquebible.db.BiblesSqlite import Bible from uniquebible.db.JournalSqlite import JournalSqlite from uniquebible.db.ToolsSqlite import Book @@ -31,23 +33,25 @@ from uniquebible.util.HBN import HBN from uniquebible.util.terminal_text_editor import TextEditor from uniquebible.util.terminal_system_command_prompt import SystemCommandPrompt -from uniquebible.util.terminal_mode_dialogs import TerminalModeDialogs -from uniquebible.util.get_path_prompt import GetPath +if not config.runMode == "stream": + from uniquebible.util.terminal_mode_dialogs import TerminalModeDialogs + from uniquebible.util.get_path_prompt import GetPath from uniquebible.util.PromptValidator import NumberValidator, NoAlphaValidator from uniquebible.util.prompt_shared_key_bindings import prompt_shared_key_bindings from uniquebible.util.prompt_multiline_shared_key_bindings import prompt_multiline_shared_key_bindings from uniquebible.util.ConfigUtil import ConfigUtil from uniquebible.util.exlbl import allLocations -from prompt_toolkit import PromptSession, prompt, print_formatted_text, HTML -from prompt_toolkit.shortcuts import clear, confirm -from prompt_toolkit.filters import Condition -#from prompt_toolkit.application import run_in_terminal -from prompt_toolkit.key_binding import KeyBindings, merge_key_bindings -from prompt_toolkit.completion import WordCompleter, NestedCompleter, ThreadedCompleter, FuzzyCompleter -from prompt_toolkit.history import FileHistory -from prompt_toolkit.styles import Style -#from prompt_toolkit.auto_suggest import AutoSuggestFromHistory -from haversine import haversine + +if not config.runMode == "stream": + from prompt_toolkit import PromptSession, prompt, print_formatted_text, HTML + from prompt_toolkit.shortcuts import clear, confirm + from prompt_toolkit.filters import Condition + #from prompt_toolkit.application import run_in_terminal + from prompt_toolkit.key_binding import KeyBindings, merge_key_bindings + from prompt_toolkit.completion import WordCompleter, NestedCompleter, ThreadedCompleter, FuzzyCompleter + from prompt_toolkit.history import FileHistory + from prompt_toolkit.styles import Style + #from prompt_toolkit.auto_suggest import AutoSuggestFromHistory class LocalCliHandler: @@ -61,12 +65,14 @@ def __init__(self, command="John 3:16"): self.crossPlatform.setupResourceLists() self.html = "Unique Bible App" self.plainText = "Unique Bible App" - self.setupDialogs() + if not config.runMode == "stream": + self.setupDialogs() self.audioPlayer = None self.command = command - self.dotCommands = self.getDotCommands() + self.dotCommands = {} if config.runMode == "stream" else self.getDotCommands() self.addShortcuts() - self.initPromptElements() + if not config.runMode == "stream": + self.initPromptElements() self.setOsOpenCmd() self.ttsLanguages = self.getTtsLanguages() self.ttsLanguageCodes = list(self.ttsLanguages.keys()) @@ -85,14 +91,15 @@ def __init__(self, command="John 3:16"): self.startupException2 = "^(_setconfig:::|\.edit|\.change|\.toggle|\.stop|\.exec|mp3:::|mp4:::|cmd:::|\.backup|\.restore|gtts:::|speak:::|download:::|read:::|readsync:::|semantic:::)" #config.cliTtsProcess = None config.audio_playing_file = os.path.join("temp", "000_audio_playing.txt") - self.getPath = GetPath( - cancel_entry=config.terminal_cancel_action, - promptIndicatorColor=config.terminalPromptIndicatorColor2, - promptEntryColor=config.terminalCommandEntryColor2, - subHeadingColor=config.terminalHeadingTextColor, - itemColor=config.terminalResourceLinkColor, - ) - self.shareKeyBindings() + if not config.runMode == "stream": + self.getPath = GetPath( + cancel_entry=config.terminal_cancel_action, + promptIndicatorColor=config.terminalPromptIndicatorColor2, + promptEntryColor=config.terminalCommandEntryColor2, + subHeadingColor=config.terminalHeadingTextColor, + itemColor=config.terminalResourceLinkColor, + ) + self.shareKeyBindings() def setupDialogs(self): self.dialogs = TerminalModeDialogs(self) @@ -357,7 +364,7 @@ def addShortcuts(self): self.dotCommands[key] = (f"an alias to '{value}'", partial(self.getContent, value)) def getDotCommands(self): - return { + return {} if config.runMode == "stream" else { config.terminal_cancel_action: ("cancel action in current prompt", self.cancelAction), ".togglecolorbrightness": ("toggle color brightness", self.togglecolorbrightness), ".togglecolourbrightness": ("an alias to '.togglecolorbrightness'", self.togglecolorbrightness), @@ -629,7 +636,7 @@ def getDotCommands(self): ".portablepython": ("build portable python", self.buildPortablePython), ".system": ("system command prompt", SystemCommandPrompt().run), ".sys": ("an alias to '.system'", SystemCommandPrompt().run), - ".clear": ("clear screen", clear), + ".clear": ("clear screen", self.clear_screen), ".wordnet": ("wordnet dictionary", self.wordnet), ".customize": ("an alias to '.customise'", self.customise), ".mp3": ("play mp3 files in music folder", self.mp3), @@ -646,6 +653,9 @@ def getDotCommands(self): #".image": ("bible chat", self.generateImage), } + def clear_screen(self): + clear() + def calculate(self): userInput = "" self.print("Calculate:") @@ -1421,7 +1431,11 @@ def showdata(self): def showdownloads(self): content = "" from uniquebible.util.DatafileLocation import DatafileLocation - from uniquebible.util.GithubUtil import GithubUtil + try: + from uniquebible.util.GithubUtil import GithubUtil + githubutilEnabled = True + except: + githubutilEnabled = False # ["marveldata", "marvelbible", "marvelcommentary", "GitHubBible", "GitHubCommentary", "GitHubBook", "GitHubMap", "GitHubPdf", "GitHubEpub"] resources = ( ("Marvel Datasets", DatafileLocation.marvelData, "marveldata"), @@ -1435,21 +1449,22 @@ def showdownloads(self): content += """[ {1} ] {0}
""".format(k, config.thisTranslation["installed"]) else: content += """[DOWNLOAD:::{0}:::{1} ]
""".format(keyword, k) - resources = ( - ("GitHub Bibles", "GitHubBible", GitHubRepoInfo.bibles[0], (config.marvelData, "bibles"), ".bible"), - ("GitHub Commentaries", "GitHubCommentary", GitHubRepoInfo.commentaries[0], (config.marvelData, "commentaries"), ".commentary"), - ("GitHub Books", "GitHubBook", GitHubRepoInfo.books[0], (config.marvelData, "books"), ".book"), - ("GitHub Maps", "GitHubMap", GitHubRepoInfo.maps[0], (config.marvelData, "books"), ".book"), - ("GitHub PDF", "GitHubPdf", GitHubRepoInfo.pdf[0], (config.marvelData, "pdf"), ".pdf"), - ("GitHub EPUB", "GitHubEpub", GitHubRepoInfo.epub[0], (config.marvelData, "epub"), ".epub"), - ) - for collection, type, repo, location, extension in resources: - content += "

{0}

".format(collection) - for file in GithubUtil(repo).getRepoData(): - if os.path.isfile(os.path.join(*location, file)): - content += """[ {1} ] {0}
""".format(file.replace(extension, ""), config.thisTranslation["installed"]) - else: - content += """[DOWNLOAD:::{1}:::{0} ]
""".format(file.replace(extension, ""), type) + if githubutilEnabled: + resources = ( + ("GitHub Bibles", "GitHubBible", GitHubRepoInfo.bibles[0], (config.marvelData, "bibles"), ".bible"), + ("GitHub Commentaries", "GitHubCommentary", GitHubRepoInfo.commentaries[0], (config.marvelData, "commentaries"), ".commentary"), + ("GitHub Books", "GitHubBook", GitHubRepoInfo.books[0], (config.marvelData, "books"), ".book"), + ("GitHub Maps", "GitHubMap", GitHubRepoInfo.maps[0], (config.marvelData, "books"), ".book"), + ("GitHub PDF", "GitHubPdf", GitHubRepoInfo.pdf[0], (config.marvelData, "pdf"), ".pdf"), + ("GitHub EPUB", "GitHubEpub", GitHubRepoInfo.epub[0], (config.marvelData, "epub"), ".epub"), + ) + for collection, kind, repo, location, extension in resources: + content += "

{0}

".format(collection) + for file in GithubUtil(repo).getRepoData(): + if os.path.isfile(os.path.join(*location, file)): + content += """[ {1} ] {0}
""".format(file.replace(extension, ""), config.thisTranslation["installed"]) + else: + content += """[DOWNLOAD:::{1}:::{0} ]
""".format(file.replace(extension, ""), kind) content += "

Third-party Resources

Read https://github.com/eliranwong/UniqueBible/wiki/Third-party-resources about third-party resources.

" self.html = content self.plainText = TextUtil.htmlToPlainText(content).strip() @@ -1701,8 +1716,8 @@ def printMultilineNote(self): def getclipboardtext(self, confirmMessage=True): try: - if config.terminalEnableTermuxAPI: - clipboardText = self.getCliOutput("termux-clipboard-get") + if shutil.which("termux-clipboard-get"): + clipboardText = subprocess.run("termux-clipboard-get", shell=True, capture_output=True, text=True).stdout elif ("Pyperclip" in config.enabled): import pyperclip clipboardText = pyperclip.paste() @@ -1798,8 +1813,11 @@ def share(self, command=""): pydoc.pipepager(plainText, cmd="termux-share -a send") return "" else: - import pyperclip - pyperclip.copy(weblink) + if shutil.which("termux-clipboard-set"): + pydoc.pipepager(weblink, cmd="termux-clipboard-set") + else: + import pyperclip + pyperclip.copy(weblink) self.print(f"The following link is copied to clipboard:\n") self.print(weblink) self.print("\nOpen it in a web browser or share with others.") @@ -1814,8 +1832,11 @@ def copy(self, content="", confirmMessage=True): if config.terminalEnableTermuxAPI: pydoc.pipepager(content, cmd="termux-clipboard-set") else: - import pyperclip - pyperclip.copy(content) + if shutil.which("termux-clipboard-set"): + pydoc.pipepager(weblink, cmd="termux-clipboard-set") + else: + import pyperclip + pyperclip.copy(content) if confirmMessage: self.print("Content is copied to clipboard.") return "" @@ -1829,8 +1850,11 @@ def copyHtml(self, content="", confirmMessage=True): if config.terminalEnableTermuxAPI: pydoc.pipepager(content, cmd="termux-clipboard-set") else: - import pyperclip - pyperclip.copy(content) + if shutil.which("termux-clipboard-set"): + pydoc.pipepager(weblink, cmd="termux-clipboard-set") + else: + import pyperclip + pyperclip.copy(content) if confirmMessage: self.print("HTML content is copied to clipboard.") return "" diff --git a/uniquebible/util/RemoteHttpHandler.py b/uniquebible/util/RemoteHttpHandler.py index 404c0c78fd..54a3b6e62c 100644 --- a/uniquebible/util/RemoteHttpHandler.py +++ b/uniquebible/util/RemoteHttpHandler.py @@ -1591,7 +1591,11 @@ def helpContent(self): def downloadContent(self): content = "" from uniquebible.util.DatafileLocation import DatafileLocation - from uniquebible.util.GithubUtil import GithubUtil + try: + from uniquebible.util.GithubUtil import GithubUtil + githubutilEnabled = True + except: + githubutilEnabled = False resources = ( ("Marvel Datasets", DatafileLocation.marvelData, "marveldata"), ("Marvel Bibles", DatafileLocation.marvelBibles, "marvelbible"), @@ -1605,21 +1609,22 @@ def downloadContent(self): else: content += """{2}
"""\ .format(keyword, k.replace("'", "\\'"), k) - resources = ( - ("GitHub Bibles", "GitHubBible", GitHubRepoInfo.bibles[0], (config.marvelData, "bibles"), ".bible"), - ("GitHub Commentaries", "GitHubCommentary", GitHubRepoInfo.commentaries[0], (config.marvelData, "commentaries"), ".commentary"), - ("GitHub Books", "GitHubBook", GitHubRepoInfo.books[0], (config.marvelData, "books"), ".book"), - ("GitHub Maps", "GitHubMap", GitHubRepoInfo.maps[0], (config.marvelData, "books"), ".book"), - ("GitHub PDF", "GitHubPdf", GitHubRepoInfo.pdf[0], (config.marvelData, "pdf"), ".pdf"), - ("GitHub EPUB", "GitHubEpub", GitHubRepoInfo.epub[0], (config.marvelData, "epub"), ".epub"), - ) - for collection, type, repo, location, extension in resources: - content += "

{0}

".format(collection) - for file in GithubUtil(repo).getRepoData(): - if os.path.isfile(os.path.join(*location, file)): - content += """{0} [{1}]
""".format(file.replace(extension, ""), config.thisTranslation["installed"]) - else: - content += """{0}
""".format(file.replace(extension, ""), type) + if githubutilEnabled: + resources = ( + ("GitHub Bibles", "GitHubBible", GitHubRepoInfo.bibles[0], (config.marvelData, "bibles"), ".bible"), + ("GitHub Commentaries", "GitHubCommentary", GitHubRepoInfo.commentaries[0], (config.marvelData, "commentaries"), ".commentary"), + ("GitHub Books", "GitHubBook", GitHubRepoInfo.books[0], (config.marvelData, "books"), ".book"), + ("GitHub Maps", "GitHubMap", GitHubRepoInfo.maps[0], (config.marvelData, "books"), ".book"), + ("GitHub PDF", "GitHubPdf", GitHubRepoInfo.pdf[0], (config.marvelData, "pdf"), ".pdf"), + ("GitHub EPUB", "GitHubEpub", GitHubRepoInfo.epub[0], (config.marvelData, "epub"), ".epub"), + ) + for collection, kind, repo, location, extension in resources: + content += "

{0}

".format(collection) + for file in GithubUtil(repo).getRepoData(): + if os.path.isfile(os.path.join(*location, file)): + content += """{0} [{1}]
""".format(file.replace(extension, ""), config.thisTranslation["installed"]) + else: + content += """{0}
""".format(file.replace(extension, ""), kind) content += "

Third-party Resources

Click this link to read our wiki page about third-party resources.

" return content diff --git a/uniquebible/util/checkup.py b/uniquebible/util/checkup.py index a4c9deadbb..5fc06f40f8 100644 --- a/uniquebible/util/checkup.py +++ b/uniquebible/util/checkup.py @@ -731,7 +731,7 @@ def runTerminalMode(): ("word-forms", "Generate English Word Forms", isWordformsInstalled), ("lemmagen3", "Lemmatizer", isLemmagen3Installed), ("chinese-english-lookup", "Chinese-to-English word definition", isChineseEnglishLookupInstalled), - ("textract", "Extract text from document", isTextractInstalled), + #("textract", "Extract text from document", isTextractInstalled), ("tabulate", "Pretty-print tabular data", isTabulateInstalled), #("apsw", "Another Python SQLite Wrapper", isApswInstalled), ("pyluach", "Hebrew (Jewish) calendar dates", isPyluachInstalled), @@ -785,7 +785,7 @@ def runTerminalMode(): ("word-forms", "Generate English Word Forms", isWordformsInstalled), ("lemmagen3", "Lemmatizer", isLemmagen3Installed), ("chinese-english-lookup", "Chinese-to-English word definition", isChineseEnglishLookupInstalled), - ("textract", "Extract text from document", isTextractInstalled), + #("textract", "Extract text from document", isTextractInstalled), ("tabulate", "Pretty-print tabular data", isTabulateInstalled), #("apsw", "Another Python SQLite Wrapper", isApswInstalled), ("pyluach", "Hebrew (Jewish) calendar dates", isPyluachInstalled), diff --git a/uniquebible/util/text_editor_checkup.py b/uniquebible/util/text_editor_checkup.py index 1cb8b1e765..163b7a9a9d 100644 --- a/uniquebible/util/text_editor_checkup.py +++ b/uniquebible/util/text_editor_checkup.py @@ -239,7 +239,7 @@ def updateModules(module, isInstalled): ("pyperclip", "Cross-platform clipboard utilities", isPyperclipInstalled), ("Pygments", "Syntax highlighting package", isPygmentsInstalled), ("translate", "Google Translate", isTranslateInstalled), - ("textract", "Extract text from document", isTextractInstalled), + #("textract", "Extract text from document", isTextractInstalled), ] for module, feature, isInstalled in optional: checkModule = re.sub("-|_", "", module)