| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 | '''   (C) 2019 Raryel C. Souza    This program is free software: you can redistribute it and/or modify    it under the terms of the GNU General Public License as published by    the Free Software Foundation, either version 3 of the License, or    (at your option) any later version.    This program is distributed in the hope that it will be useful,    but WITHOUT ANY WARRANTY; without even the implied warranty of    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    GNU General Public License for more details.    You should have received a copy of the GNU General Public License    along with this program.  If not, see <https://www.gnu.org/licenses/>.'''from PyQt5 import QtCore, QtWidgetsfrom PyQt5.QtWidgets import QFileDialog, QMessageBoxfrom PyQt5.QtCore import Qtfrom pathlib import Pathfrom pytranscriber.model.param_autosub import Param_Autosubfrom pytranscriber.util.util import MyUtilfrom pytranscriber.control.thread_exec_autosub import Thread_Exec_Autosubfrom pytranscriber.control.thread_cancel_autosub import Thread_Cancel_Autosubfrom pytranscriber.gui.gui import Ui_windowimport osclass Ctr_Main():    def __init__(self):        import sys        app = QtWidgets.QApplication(sys.argv)        window = QtWidgets.QMainWindow()        self.objGUI = Ui_window()        self.objGUI.setupUi(window)        self.__initGUI()        window.setFixedSize(window.size())        window.show()        sys.exit(app.exec_())    def __initGUI(self):        #language selection list        list_languages =  [ "en-US - English (United States)",                            "en-AU - English (Australia)",                            "en-CA - English (Canada)",                            "en-GB - English (United Kingdom)",                            "en-HK - English (Hong Kong)",                            "en-IN - English (India)",                            "en-GB - English (Ireland)",                            "en-NZ - English (New Zealand)",                            "en-PH - English (Philippines)",                            "en-SG - English (Singapore)",                            "af - Afrikaans",                            "ar - Arabic",                            'ar-DZ - Arabic (Algeria)',                            'ar-EG - Arabic (Egypt)',                            'ar-IQ - Arabic (Iraq)',                            'ar-IS - Arabic (Israel)',                            'ar-JO - Arabic (Jordan)',                            'ar-KW - Arabic (Kuwait)',                            'ar-LB - Arabic (Lebanon)',                            'ar-MA - Arabic (Morocco)',                            'ar-OM - Arabic (Oman)',                            'ar-QA - Arabic (Qatar)',                            'ar-SA - Arabic (Saudi Arabia)',                            'ar-PS - Arabic (State of Palestine)',                            'ar-TN - Arabic (Tunisia)',                            'ar-AE - Arabic (United Arab Emirates)',                            'ar-YE - Arabic (Yemen)',                            "az - Azerbaijani",                            "be - Belarusian",                            "bg - Bulgarian",                            "bn - Bengali",                            "bs - Bosnian",                            "ca - Catalan",                            "ceb -Cebuano",                            "cs - Czech",                            "cy - Welsh",                            "da - Danish",                            "de - German",                            'de-AT - German (Austria)',                            'de-CH - German (Switzerland)',                            "el - Greek",                            "eo - Esperanto",                            'es-ES - Spanish (Spain)',                            'es-AR - Spanish (Argentina)',                            'es-BO - Spanish (Bolivia)',                            'es-CL - Spanish (Chile)',                            'es-CO - Spanish (Colombia)',                            'es-CR - Spanish (Costa Rica)',                            'es-DO - Spanish (Dominican Republic)',                            'es-EC - Spanish (Ecuador)',                            'es-GT - Spanish (Guatemala)',                            'es-HN - Spanish (Honduras)',                            'es-MX - Spanish (Mexico)',                            'es-NI - Spanish (Nicaragua)',                            'es-PA - Spanish (Panama)',                            'es-PE - Spanish (Peru)',                            'es-PR - Spanish (Puerto Rico)',                            'es-PY - Spanish (Paraguay)',                            'es-SV - Spanish (El Salvador)',                            'es-UY - Spanish (Uruguay)',                            'es-US - Spanish (United States)',                            'es-VE - Spanish (Venezuela)',                            "et - Estonian",                            "eu - Basque",                            "fa - Persian",                            'fil-PH - Filipino (Philippines)',                            "fi - Finnish",                            "fr - French",                            'fr-BE - French (Belgium)',                            'fr-CA - French (Canada)',                            'fr-CH - French (Switzerland)',                            "ga - Irish",                            "gl - Galician",                            "gu -Gujarati",                            "ha - Hausa",                            "hi - Hindi",                            "hmn - Hmong",                            "hr - Croatian",                            "ht - Haitian Creole",                            "hu - Hungarian",                            "hy - Armenian",                            "id - Indonesian",                            "ig - Igbo",                            "is - Icelandic",                            "it - Italian",                            'it-CH - Italian (Switzerland)',                            "iw - Hebrew",                            "ja - Japanese",                            "jw - Javanese",                            "ka - Georgian",                            "kk - Kazakh",                            "km - Khmer",                            "kn - Kannada",                            "ko - Korean",                            "la - Latin",                            "lo - Lao",                            "lt - Lithuanian",                            "lv - Latvian",                            "mg - Malagasy",                            "mi - Maori",                            "mk - Macedonian",                            "ml - Malayalam",                            "mn - Mongolian",                            "mr - Marathi",                            "ms - Malay",                            "mt - Maltese",                            "my - Myanmar (Burmese)",                            "ne - Nepali",                            "nl - Dutch",                            "no - Norwegian",                            "ny - Chichewa",                            "pa - Punjabi",                            "pl - Polish",                            "pt-BR - Portuguese (Brazil)",                            "pt-PT - Portuguese (Portugal)",                            "ro - Romanian",                            "ru - Russian",                            "si - Sinhala",                            "sk - Slovak",                            "sl - Slovenian",                            "so - Somali",                            "sq - Albanian",                            "sr - Serbian",                            "st - Sesotho",                            "su - Sudanese",                            "sv - Swedish",                            "sw - Swahili",                            "ta - Tamil",                            'ta-IN - Tamil (India)',                            'ta-MY - Tamil (Malaysia)',                            'ta-SG - Tamil (Singapore)',                            'ta-LK - Tamil (Sri Lanka)',                            "te - Telugu",                            "tg - Tajik",                            "th - Thai",                            "tl - Filipino",                            "tr - Turkish",                            "uk - Ukrainian",                            "ur - Urdu",                            "uz - Uzbek",                            "vi - Vietnamese",                            "yi - Yiddish",                            "yo - Yoruba",                            "yue-Hant-HK - Cantonese (Traditional, HK)",                            "zh - Chinese (Simplified, China)",                            "zh-HK - Chinese (Simplified, Hong Kong)",                            "zh-TW - Chinese (Traditional, Taiwan)",                            "zu - Zulu" ]        self.objGUI.cbSelectLang.addItems(list_languages)        self.__listenerProgress("", 0)        #default output folder at user desktop        userHome = Path.home()        pathOutputFolder = userHome / 'Desktop' / 'pyTranscriber'        self.objGUI.qleOutputFolder.setText(str(pathOutputFolder))        self.objGUI.bRemoveFile.setEnabled(False)        self.objGUI.bCancel.hide()        #button listeners        self.objGUI.bConvert.clicked.connect(self.__listenerBExec)        self.objGUI.bCancel.clicked.connect(self.__listenerBCancel)        self.objGUI.bRemoveFile.clicked.connect(self.__listenerBRemove)        self.objGUI.bSelectOutputFolder.clicked.connect(self.__listenerBSelectOuputFolder)        self.objGUI.bOpenOutputFolder.clicked.connect(self.__listenerBOpenOutputFolder)        self.objGUI.bSelectMedia.clicked.connect(self.__listenerBSelectMedia)        self.objGUI.actionLicense.triggered.connect(self.__listenerBLicense)        self.objGUI.actionDonation.triggered.connect(self.__listenerBDonation)        self.objGUI.actionAbout_pyTranscriber.triggered.connect(self.__listenerBAboutpyTranscriber)    def __resetGUIAfterSuccess(self):        self.__resetGUIAfterCancel()        self.objGUI.qlwListFilesSelected.clear()        self.objGUI.bConvert.setEnabled(False)        self.objGUI.bRemoveFile.setEnabled(False)    def __resetGUIAfterCancel(self):        self.__resetProgressBar()        self.objGUI.bSelectMedia.setEnabled(True)        self.objGUI.bSelectOutputFolder.setEnabled(True)        self.objGUI.cbSelectLang.setEnabled(True)        self.objGUI.chbxOpenOutputFilesAuto.setEnabled(True)        self.objGUI.bCancel.hide()        self.objGUI.bConvert.setEnabled(True)        self.objGUI.bRemoveFile.setEnabled(True)    def __lockButtonsDuringOperation(self):        self.objGUI.bConvert.setEnabled(False)        self.objGUI.bRemoveFile.setEnabled(False)        self.objGUI.bSelectMedia.setEnabled(False)        self.objGUI.bSelectOutputFolder.setEnabled(False)        self.objGUI.cbSelectLang.setEnabled(False)        self.objGUI.chbxOpenOutputFilesAuto.setEnabled(False)        QtCore.QCoreApplication.processEvents()    def __listenerProgress(self, str, percent):        self.objGUI.labelCurrentOperation.setText(str)        self.objGUI.progressBar.setProperty("value", percent)        QtCore.QCoreApplication.processEvents()    def __setProgressBarIndefinite(self):        self.objGUI.progressBar.setMinimum(0)        self.objGUI.progressBar.setMaximum(0)        self.objGUI.progressBar.setValue(0)    def __resetProgressBar(self):        self.objGUI.progressBar.setMinimum(0)        self.objGUI.progressBar.setMaximum(100)        self.objGUI.progressBar.setValue(0)        self.__listenerProgress("", 0)    def __updateProgressFileYofN(self, str):        self.objGUI.labelProgressFileIndex.setText(str)        QtCore.QCoreApplication.processEvents()    def __listenerBSelectOuputFolder(self):        fSelectDir = QFileDialog.getExistingDirectory(self.objGUI.centralwidget)        if fSelectDir:            self.objGUI.qleOutputFolder.setText(fSelectDir)    def __listenerBSelectMedia(self):        #options = QFileDialog.Options()        options = QFileDialog.DontUseNativeDialog        files, _ = QFileDialog.getOpenFileNames(self.objGUI.centralwidget, "Select media", "","All Media Files (*.mp3 *.mp4 *.wav *.m4a *.wma)")        if files:            self.objGUI.qlwListFilesSelected.addItems(files)            #enable the convert button only if list of files is not empty            self.objGUI.bConvert.setEnabled(True)            self.objGUI.bRemoveFile.setEnabled(True)    def __listenerBExec(self):        if not MyUtil.is_internet_connected():            self.__showErrorMessage("Error! Cannot reach Google Speech Servers. \n\n1) Please make sure you are connected to the internet. \n2) If you are in China or other place that blocks access to Google servers: please install and enable a desktop-wide VPN app like Windscribe before trying to use pyTranscriber!")        else:            #extracts the two letter lang_code from the string on language selection            selectedLanguage = self.objGUI.cbSelectLang.currentText()            indexSpace = selectedLanguage.index(" ")            langCode = selectedLanguage[:indexSpace]            listFiles = []            for i in range(self.objGUI.qlwListFilesSelected.count()):                listFiles.append(str(self.objGUI.qlwListFilesSelected.item(i).text()))            outputFolder = self.objGUI.qleOutputFolder.text()            if self.objGUI.chbxOpenOutputFilesAuto.checkState() == Qt.Checked:                boolOpenOutputFilesAuto = True            else:                boolOpenOutputFilesAuto = False            objParamAutosub = Param_Autosub(listFiles, outputFolder, langCode,                                            boolOpenOutputFilesAuto)            #execute the main process in separate thread to avoid gui lock            self.thread_exec = Thread_Exec_Autosub(objParamAutosub)            #connect signals from work thread to gui controls            self.thread_exec.signalLockGUI.connect(self.__lockButtonsDuringOperation)            self.thread_exec.signalResetGUIAfterSuccess.connect(self.__resetGUIAfterSuccess)            self.thread_exec.signalResetGUIAfterCancel.connect(self.__resetGUIAfterCancel)            self.thread_exec.signalProgress.connect(self.__listenerProgress)            self.thread_exec.signalProgressFileYofN.connect(self.__updateProgressFileYofN)            self.thread_exec.signalErrorMsg.connect(self.__showErrorMessage)            self.thread_exec.start()            #Show the cancel button            self.objGUI.bCancel.show()            self.objGUI.bCancel.setEnabled(True)    def __listenerBCancel(self):        self.objGUI.bCancel.setEnabled(False)        self.thread_cancel = Thread_Cancel_Autosub(self.thread_exec)        #Only if worker thread is running        if self.thread_exec and self.thread_exec.isRunning():            #reset progress indicator            self.__listenerProgress("Cancelling", 0)            self.__setProgressBarIndefinite()            self.__updateProgressFileYofN("")            #connect the terminate signal to resetGUI            self.thread_cancel.signalTerminated.connect(self.__resetGUIAfterCancel)            #run the cancel autosub operation in new thread            #to avoid progressbar freezing            self.thread_cancel.start()            self.thread_exec = None    def __listenerBRemove(self):        indexSelected = self.objGUI.qlwListFilesSelected.currentRow()        if indexSelected >= 0:            self.objGUI.qlwListFilesSelected.takeItem(indexSelected)        #if no items left disables the remove and convert button        if self.objGUI.qlwListFilesSelected.count() == 0:            self.objGUI.bRemoveFile.setEnabled(False)            self.objGUI.bConvert.setEnabled(False)    def __listenerBOpenOutputFolder(self):        pathOutputFolder = Path(self.objGUI.qleOutputFolder.text())        #if folder exists and is valid directory        if os.path.exists(pathOutputFolder) and os.path.isdir(pathOutputFolder):            MyUtil.open_file(pathOutputFolder)        else:            self.__showErrorMessage("Error! Invalid output folder.")    def __listenerBLicense(self):        self.__showInfoMessage("<html><body><a href=\"https://www.gnu.org/licenses/gpl-3.0.html\">GPL License</a><br><br>"                + "Copyright (C) 2019 Raryel C. Souza <raryel.costa at gmail.com><br>"                + "<br>This program is free software: you can redistribute it and/or modify<br>"                + "it under the terms of the GNU General Public License as published by<br>"                + "the Free Software Foundation, either version 3 of the License, or<br>"                + " any later version<br>"                + "<br>"                + "This program is distributed in the hope that it will be useful,<br>"                + "but WITHOUT ANY WARRANTY; without even the implied warranty of<br>"                + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>"                + "GNU General Public License for more details.<br>"                + "<br>"                + "You should have received a copy of the GNU General Public License<br>"                + "along with this program.  If not, see <a href=\"https://www.gnu.org/licenses\">www.gnu.org/licenses</a>."                + "</body></html>", "License")    def __listenerBDonation(self):        self.__showInfoMessage("<html><body>"                + "pyTranscriber is developed as a hobby, so donations of any value are welcomed and essential for further improvements and fixes."                + "<br><br>If you feel that this software has been useful and would like to contribute for it to continue improve and have more features and fixes you can <a href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=YHB854YHPJCU8&item_name=Donation+pyTranscriber¤cy_code=BRL\">DONATE VIA PAYPAL</a> or <a href=\"https://blockchain.com/btc/payment_request?address=153LcqV59paxEEJX7riLrEHQbE54vhcko9&amount=0.00026351&message=Donation to support pyTranscriber development\"> DONATE US$5 VIA BITCOIN</a>."                + "<br><br>Thanks in advance!"                + "</body></html>", "DONATIONS")    def __listenerBAboutpyTranscriber(self):        self.__showInfoMessage("<html><body>"                + "<a href=\"https://github.com/raryelcostasouza/pyTranscriber\">pyTranscriber</a> is an application that can be used "                + "to generate <b>automatic transcription / automatic subtitles </b>"                + "for audio/video files through a friendly graphical user interface. "                + "<br><br>"                + "The hard work of speech recognition is made by the <a href=\"https://cloud.google.com/speech/\">Google Speech Recognition API</a> "                + "using <a href=\"https://github.com/agermanidis/autosub\">Autosub</a>"                + "<br><br>pyTranscriber is developed as a hobby, so donations of any value are welcomed and essential for further improvements and fixes."                + "<br><br>If you feel that this software has been useful and would like to contribute for it to continue improve and have more features and fixes you can <a href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=YHB854YHPJCU8&item_name=Donation+pyTranscriber¤cy_code=BRL\">DONATE VIA PAYPAL</a> or <a href=\"https://blockchain.com/btc/payment_request?address=153LcqV59paxEEJX7riLrEHQbE54vhcko9&amount=0.00026351&message=Donation to support pyTranscriber development\"> DONATE US$5 VIA BITCOIN</a>."                + "<br><br>Thanks in advance!"                + "</body></html>", "About pyTranscriber")    def __showInfoMessage(self, info_msg, title):        msg = QMessageBox()        msg.setIcon(QMessageBox.Information)        msg.setWindowTitle(title)        msg.setText(info_msg)        msg.exec()    def __showErrorMessage(self, errorMsg):        msg = QMessageBox()        msg.setIcon(QMessageBox.Critical)        msg.setWindowTitle("Error!")        msg.setText(errorMsg)        msg.exec()
 |