From 143b5cfe3059e930f1ec5c7b3b530783490a22fb Mon Sep 17 00:00:00 2001 From: eliranwong Date: Tue, 28 Nov 2023 17:50:10 +0000 Subject: [PATCH] v35.22; ctrl+q to stop audio in terminal mode --- UniqueBibleAppVersion.txt | 2 +- latest_changes.txt | 3 ++ patches.txt | 12 +++---- util/RemoteCliMainWindow.py | 68 ++++++++++++++++++++++++++++++------- util/TextCommandParser.py | 47 +++++++++++++++++++++++-- 5 files changed, 111 insertions(+), 21 deletions(-) diff --git a/UniqueBibleAppVersion.txt b/UniqueBibleAppVersion.txt index 3e2f904792..c260ac9e89 100755 --- a/UniqueBibleAppVersion.txt +++ b/UniqueBibleAppVersion.txt @@ -1 +1 @@ -35.21 +35.22 diff --git a/latest_changes.txt b/latest_changes.txt index 7481786fa6..c11b322912 100755 --- a/latest_changes.txt +++ b/latest_changes.txt @@ -1,3 +1,6 @@ +Changes in 35.22: +* 'ctrl+q' or 'ctrl+z' to stop bible audio in terminal mode. + Changes in 35.21: * Added subheadings command to api server diff --git a/patches.txt b/patches.txt index 2cbed78972..1b928c904f 100755 --- a/patches.txt +++ b/patches.txt @@ -1239,7 +1239,6 @@ (33.57, "folder", "startup") (33.57, "file", "startup/share.py") (33.61, "file", "gui/Downloader.py") -(33.61, "file", "util/RemoteCliHandler.py") (33.62, "file", "plugins/menu/Bible.py") (33.63, "file", "gui/WebEngineViewPopover.py") (33.63, "file", "gui/TabWidget.py") @@ -1384,12 +1383,13 @@ (35.13, "file", "util/CrossPlatform.py") (35.13, "file", "util/TextEditorUtility.py") (35.16, "file", "util/VlcUtil.py") -(35.18, "file", "util/TextCommandParser.py") (35.19, "file", "startup/nonGui.py") (35.19, "file", "util/ConfigUtil.py") -(35.20, "file", "util/LocalCliHandler.py") (35.20, "file", "util/TextUtil.py") (35.21, "file", "util/RemoteApiHandler.py") -(35.21, "file", "UniqueBibleAppVersion.txt") -(35.21, "file", "latest_changes.txt") -(35.21, "file", "patches.txt") +(35.22, "file", "util/RemoteCliHandler.py") +(35.22, "file", "util/LocalCliHandler.py") +(35.22, "file", "util/TextCommandParser.py") +(35.22, "file", "UniqueBibleAppVersion.txt") +(35.22, "file", "latest_changes.txt") +(35.22, "file", "patches.txt") diff --git a/util/RemoteCliMainWindow.py b/util/RemoteCliMainWindow.py index a91185235f..c71125343c 100644 --- a/util/RemoteCliMainWindow.py +++ b/util/RemoteCliMainWindow.py @@ -1,4 +1,4 @@ -import os, config, zipfile, gdown, shutil, re, platform, subprocess, sys, signal +import os, config, zipfile, gdown, shutil, re, platform, subprocess, sys, signal, threading from util.LanguageUtil import LanguageUtil from util.TextCommandParser import TextCommandParser @@ -148,7 +148,24 @@ def getPlaylistFromHTML(self, html, displayText=False): if os.path.isfile(audioFilePath): playlist.append(audioFilePath) if config.runMode == "terminal": - self.playAudioBibleFilePlayListPlusDisplayText(playlist, textList) if displayText else self.playAudioBibleFilePlayList(playlist) + # Create a new thread for the streaming task + config.playback_finished = False + playback_event = threading.Event() + if displayText: + self.playback_thread = threading.Thread(target=self.playAudioBibleFilePlayListPlusDisplayText, args=(playlist, textList, False, playback_event)) + else: + self.playback_thread = threading.Thread(target=self.playAudioBibleFilePlayList, args=(playlist,)) + # Start the streaming thread + self.playback_thread.start() + + # wait while text output is steaming; capture key combo 'ctrl+q' or 'ctrl+z' to stop the streaming + config.mainWindow.textCommandParser.keyToStopStreaming(playback_event) + + # when streaming is done or when user press "ctrl+q" + self.playback_thread.join() + + # old way + #self.playAudioBibleFilePlayListPlusDisplayText(playlist, textList) if displayText else self.playAudioBibleFilePlayList(playlist) return [] else: searchPattern = """[Rr][Ee][Aa][Dd]([Ww][Oo][Rr][Dd]|[Ll][Ee][Xx][Ee][Mm][Ee]):::([A-Za-z0-9]+?)\.([0-9]+?)\.([0-9]+?)\.([0-9]+?)\.([0-9]+?)["']""" @@ -232,32 +249,56 @@ def playAudioBibleChapterVerseByVerse(self, text, b, c, startVerse=0, displayTex textList.append("") if config.runMode == "terminal": playlist = [filepath for *_, filepath in playlist] - self.playAudioBibleFilePlayListPlusDisplayText(playlist, textList) if displayText else self.playAudioBibleFilePlayList(playlist) + + # Create a new thread for the streaming task + config.playback_finished = False + playback_event = threading.Event() + if displayText: + self.playback_thread = threading.Thread(target=self.playAudioBibleFilePlayListPlusDisplayText, args=(playlist, textList, False, playback_event)) + else: + self.playback_thread = threading.Thread(target=self.playAudioBibleFilePlayList, args=(playlist,)) + # Start the streaming thread + self.playback_thread.start() + + # wait while text output is steaming; capture key combo 'ctrl+q' or 'ctrl+z' to stop the streaming + config.mainWindow.textCommandParser.keyToStopStreaming(playback_event) + + # when streaming is done or when user press "ctrl+q" + self.playback_thread.join() + + # old way + #self.playAudioBibleFilePlayListPlusDisplayText(playlist, textList) if displayText else self.playAudioBibleFilePlayList(playlist) return [] return playlist #return [("NET_1_1_3.mp3", "audio/bibles/NET-UK/default/1_1/NET_1_1_3.mp3"), ("NET_1_1_4.mp3", "audio/bibles/NET-UK/default/1_1/NET_1_1_4.mp3")] - def playAudioBibleFilePlayList(self, playlist, gui=False): + def notifyAudioPlayback(self): + print("--------------------") + print("Playing audio ...") + print("To stop audio playback, press 'ctrl+q' or 'ctrl+z'.") + print("--------------------") + + def playAudioBibleFilePlayList(self, playlist, gui=False, playback_event=None): # do not remove the dummy gui argument for this method self.closeMediaPlayer() if playlist: if config.isVlcAvailable: - print("Playing audio now ...") - print("To stop it, run '.stopaudio' or '.sa' in UBA command prompt.") + self.notifyAudioPlayback() VlcUtil.playMediaFile(playlist, config.vlcSpeed, gui) else: print("No VLC player is found!") + config.playback_finished = True - def playAudioBibleFilePlayListPlusDisplayText(self, playlist, textList, gui=False): + def playAudioBibleFilePlayListPlusDisplayText(self, playlist, textList, gui=False, playback_event=None): # do not remove the dummy gui argument for this method self.closeMediaPlayer() if playlist: - print("Playing audio now with text synchronisation ...") - print("To stop it, run '.stopaudiosync' or '.sas' in UBA command prompt in a separate session.") - if config.runMode == "terminal": - config.mainWindow.createAudioPlayingFile() + self.notifyAudioPlayback() + #if config.runMode == "terminal": + # config.mainWindow.createAudioPlayingFile() for index, audioFile in enumerate(playlist): - if not os.path.isfile(config.audio_playing_file): + #if not os.path.isfile(config.audio_playing_file): + if playback_event is not None and playback_event.is_set(): break try: # display text @@ -284,8 +325,11 @@ def playAudioBibleFilePlayListPlusDisplayText(self, playlist, textList, gui=Fals self.closeMediaPlayer() if config.runMode == "terminal": config.mainWindow.removeAudioPlayingFile() + config.playback_finished = True def closeMediaPlayer(self): + if os.path.isfile(config.audio_playing_file): + os.remove(config.audio_playing_file) if WebtopUtil.isPackageInstalled("pkill"): # close Android media player try: diff --git a/util/TextCommandParser.py b/util/TextCommandParser.py index 152a946e2f..5cdd183407 100644 --- a/util/TextCommandParser.py +++ b/util/TextCommandParser.py @@ -1,6 +1,8 @@ # coding=utf-8 -import glob, pprint, traceback, pydoc +import glob, pprint, traceback, pydoc, threading, asyncio import os, re, webbrowser, platform, zipfile, subprocess, config +from prompt_toolkit.input import create_input +from prompt_toolkit.keys import Keys from datetime import date from util.VlcUtil import VlcUtil from util.exlbl import allLocations, tc_location_names, sc_location_names @@ -1778,6 +1780,30 @@ def downloadYouTubeFile(self, downloadCommand, youTubeLink, outputFolder, noFfmp else: webbrowser.open(wikiPage) + def keyToStopStreaming(self, playback_event): + async def readKeys() -> None: + done = False + input = create_input() + + def keys_ready(): + nonlocal done + for key_press in input.read_keys(): + #print(key_press) + if key_press.key in (Keys.ControlQ, Keys.ControlZ): + print("\nStopping audio playback ...") + self.parent.closeMediaPlayer() + done = True + playback_event.set() + + with input.raw_mode(): + with input.attach(keys_ready): + while not done: + if config.playback_finished: + break + await asyncio.sleep(0.1) + + asyncio.run(readKeys()) + # READSYNC::: def textReadSync(self, command, source): return self.textRead(command, source, True) if config.runMode == "terminal" else ("study", "Currently, only terminal mode supports running READSYNC::: command.", {}) @@ -1821,7 +1847,24 @@ def textRead(self, command, source, displayText=False): target = "" content = "" if config.runMode == "terminal": - self.parent.playAudioBibleFilePlayListPlusDisplayText(allPlayList, allTextList) if displayText else self.parent.playAudioBibleFilePlayList(allPlayList) + # Create a new thread for the streaming task + config.playback_finished = False + playback_event = threading.Event() + if displayText: + self.playback_thread = threading.Thread(target=self.parent.playAudioBibleFilePlayListPlusDisplayText, args=(allPlayList, allTextList, False, playback_event)) + else: + self.playback_thread = threading.Thread(target=self.parent.playAudioBibleFilePlayList, args=(allPlayList,)) + # Start the streaming thread + self.playback_thread.start() + + # wait while text output is steaming; capture key combo 'ctrl+q' or 'ctrl+z' to stop the streaming + self.keyToStopStreaming(playback_event) + + # when streaming is done or when user press "ctrl+q" + self.playback_thread.join() + + # old way + #self.parent.playAudioBibleFilePlayListPlusDisplayText(allPlayList, allTextList) if displayText else self.parent.playAudioBibleFilePlayList(allPlayList) else: self.parent.playAudioBibleFilePlayList(allPlayList) return (target, content, {})