def _on_website(self): try: QDesktopServices.openUrl(QUrl("https://kokimame.github.io/joytan/")) except Exception as e: showCritical("Error occured while accessing to the Internet.\n" "Please report this bug to the developers via GitHub.\n" "(%s)" % e)
def _translate(self): if self.mw.entrylist.count() == 0: showCritical("No entries found in your entry list.", title="Error") return ewkeys = [] # Get language code of target language to translate to from the library destcode = LANGCODES[self.form.langCombo.currentText().lower()] # Check which section to translate _list = self.form.keyList for i in range(_list.count()): ch = _list.itemWidget(_list.item(i)) if ch.isChecked(): ewkeys.append(ch.text()) if self.form.onlyCheck.isChecked(): targets = self.mw.entrylist.get_entry_selected() else: targets = self.mw.entrylist.get_entry_all() self.form.progressBar.setRange(0, len(targets)) def _on_progress(name): self.form.pgMsg.setText("Translating %s." % name) val = self.form.progressBar.value() self.form.progressBar.setValue(val + 1) self.tt = TranslateThread(targets, ewkeys, destcode) self.tt.prog.connect(_on_progress) self.tt.step.connect(self.mw.entrylist.update_entry) self._thread_start() self.tt.finished.connect(self.reject)
def _on_fail(msg): """ This slot gets called if the dubbing thread encounters an exception, then shows a critical error message and kills the thread. """ if self.thread: self.thread.terminate() showCritical(msg)
def _ensure_base_exists(self, base): try: self._ensure_exists(base) except: from gui.utils import showCritical showCritical("""\ Joytan could not create the folder %s. Please ensure that location is not \ read-only and you have permission to write to it.""" % base, title="Error")
def on_audiodialog(mw): # Check if the user install dependencies for pydub if getattr(sys, 'frozen', False): pass elif not pydub.utils.which("ffmpeg"): showCritical("Error: Dependencies not found. " "Please install 'ffmpeg' to create audiobooks") return gui.dialogs.open("AudioDialog", mw)
def _on_download(self): url = self.form.urlEdit.text() if not self.URL_FORMAT.match(url): showCritical("Wrong URL format is detected.\nPlease choose other URL.") return self.thread = MemriseThread(url) self.thread.logger.connect(showCritical) self.thread.bar_size.connect(self._set_bar_size) self.thread.prog.connect(self._on_progress) self.thread.finished.connect(self._completed) self.thread.start() self.form.dlBtn.setEnabled(False) self.form.stopBtn.setEnabled(True)
def _on_open(self): if not self.path: showCritical("File not specified.") return self.thread = OpenCsvThread(self.path) self.thread.reshape.connect( lambda x: self.mw.entrylist.set_config('reshape', x)) self.thread.new_entry.connect(self._on_progress) self.thread.finished.connect(self._completed) self.form.progressBar.setRange(0, self._count_row()) self.form.openBtn.setEnabled(False) self.form.fileBtn.setEnabled(False) self.thread.start()
def _on_open(self): if not self.path: showCritical("File not specified.") return self.thread = OpenCsvThread(self.path) self.thread.reshape.connect( lambda x: self.mw.entrylist.set_config('reshape', x)) self.thread.new_entry.connect(self._on_progress) self.thread.finished.connect(self._completed) self.form.progressBar.setRange(0, self._count_row()) self.form.openBtn.setEnabled(False) self.form.fileBtn.setEnabled(False) if self.form.nameCheck.isChecked(): mpath = self.form.fileLbl.text().replace(".csv", "") midx = mpath.find('/') if midx > 0: mpath = mpath[midx + 1:len(midx) - 1] self.mw.config['title'] = mpath self.thread.start() if self.form.shuffleCheck.isChecked(): self.thread.finished.connect(lambda: self._shuffle())
def _lookup(self): if self.mw.entrylist.count() == 0: showCritical("No entries found in your entry list.", title="Error") return source_name = self.form.sourceCombo.currentText() service = DictionaryService[source_name]() if self.form.onlyCheck.isChecked(): targets = self.mw.entrylist.get_entry_selected() else: targets = self.mw.entrylist.get_entry_all() self.form.progressBar.setRange(0, len(targets)) def _on_progress(name): self.form.pgMsg.setText("Looking up %s." % name) val = self.form.progressBar.value() self.form.progressBar.setValue(val + 1) self.lt = LookupThread(targets, service) self.lt.prog.connect(_on_progress) self.lt.step.connect(self.mw.entrylist.update_entry) self._thread_start() self.lt.finished.connect(self.reject)
def _on_create(self): # Validation before starting to create audiobooks if self.mw.entrylist.count() == 0: showCritical("No entries found in your entry list.") return self.form.pauseBtn.setEnabled(True) # entries: The Entry to be included in the upcoming audiobook. if self.form.allBtn.isChecked(): entries = self.mw.entrylist.get_entry_all() else: _from, _to = self.form.fromSpin.value(), self.form.toSpin.value() if _from > _to: showCritical("The value in 'to' field is out of range.") self._ui_spin() return else: try: entries = self.mw.entrylist.get_entry_all()[_from - 1:_to] except IndexError: showCritical("Index is out of range.") self._ui_spin() return _piecesNum = self.form.pieceSpin.value() # Open TTS setting dialog if TTS setting is incomplete. _list = self.form.flowList undefs = self.mw.entrylist.get_config('undefined') for i in range(_list.count()): fi = _list.itemWidget(_list.item(i)) if isinstance(fi, EwkeyObject) and fi.ewkey in undefs: showCritical( "Please choose Text-to-speech voice for every Entry section to be read." ) gui.dialogs.open("Preferences", self.mw, back_to=self, tab="TTS") return if os.path.isdir(self._destdir()): # When dubbing thread gets interrupted by exceptions that it fails to find dependencies # (e.g, ffmpeg) while creating audio files, it may leave these files, like index.mp3, open. # This situation causes permission error on Windows if user starts the thread again and # tries to remove existing folders, because some of files in the folders are in use. # Therefore, let's ignore errors from shutil below and let pydub raise an error later. shutil.rmtree(self._destdir(), ignore_errors=True) os.makedirs(self._destdir(), exist_ok=True) setting = self._get_setting() class DubbingThread(QThread): prog = pyqtSignal(str) fail = pyqtSignal(str) def __init__(self, mw, worker): QThread.__init__(self) self.mw = mw self.worker = worker def run(self): self.completed = False self.prog.emit( "Setting up audio files. This may take a few minutes") self.worker.setup_audio() self.wait = 0 entryNum = len(entries) pieceSize = entryNum / _piecesNum for i in range(_piecesNum): numStr = str(i) if _piecesNum > 1 else "" sidx = int(i * pieceSize) eidx = int((i + 1) * pieceSize) for ew in entries[sidx:eidx]: self.prog.emit("Creating audio file of Entry #%d" % (ew.row + 1)) os.makedirs(os.path.join(setting['dest'], ew.str_index()), exist_ok=True) # self.worker.onepass(ew) tryNum = 0 while tryNum < 3: if self.wait > 0: if (self.wait == 1): self.prog.emit("Paused.") self.wait = 2 self.sleep(1) tryNum = 0 continue try: self.worker.onepass(ew) tryNum = 3 except Exception as e: msg = "Error:" + str(e).replace( '\n', '').replace('/r', '').strip() self.prog.emit(msg) tryNum += 1 if tryNum == 2: showWarning( "Error occurs while creating audiobook at Entry" " No.%d. System stops with exception '%s'\n" "Would retry after 100 seconds." % (ew.row + 1, e)) if tryNum == 3: self.fail.emit( "Error occurs while creating audiobook at Entry" " No.%d. System stops with exception '%s'" % (ew.row + 1, e)) self.sleep(100) self.prog.emit( "Mixing with BGM. This may take a few minutes.") acapella = sum(self.worker.acapellas) # Is this good for memory efficiency? # del self.worker.acapellas if len(setting['loop']) != 0: try: finalmp3 = acapella.overlay( self.worker.get_bgmloop(len(acapella))) finalmp3.export(setting['dest'] + numStr + ".mp3") except Exception as e: self.fail.emit( "Error occurs while making a looped BGM. System stops " "with exception '%s'" % e) else: acapella.export(setting['dest'] + numStr + ".mp3") if setting['lrc']: self.worker.make_lyrics(setting['dest'] + numStr + ".lrc") self.worker.cleanData() self.completed = True self.quit() def _on_progress(msg): self.form.pgMsg.setText(msg) val = self.form.progressBar.value() if msg.find("Error:") < 0 or msg.find("Paus") < 0: self.form.progressBar.setValue(val + 1) elif msg.find("Error:") > 0: self.form.progressBar.setValue(val - 1) def _on_fail(msg): """ This slot gets called if the dubbing thread encounters an exception, then shows a critical error message and kills the thread. """ if self.thread: self.thread.terminate() showCritical(msg) from joytan.routine.dubbing import DubbingWorker worker = DubbingWorker(setting) self.thread = DubbingThread(self.mw, worker) self.thread.prog.connect(_on_progress) self.thread.fail.connect(_on_fail) self.thread.start() self.thread.finished.connect(lambda: self._completed(setting['dest'])) self.form.createBtn.setEnabled(False) self.form.stopBtn.setEnabled(True) self.form.pauseBtn.setEnabled(True) # Progress contains fixed 2 step; setting up audio files, mixing with BGM self.form.progressBar.setRange(0, len(entries) + 2 + _piecesNum)