diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 96ff368b..f93ce14e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,13 +66,14 @@ jobs: run: | sudo apt-get update sudo apt-get install -y libunarr-dev libgl-dev libgles2-mesa-dev \ - libfontconfig1-dev libfreetype-dev libxkbcommon-dev libpoppler-qt6-dev + libfontconfig1-dev libfreetype-dev libxkbcommon-dev libpoppler-qt6-dev \ + libspeechd-dev - name: Install Qt uses: jurplel/install-qt-action@v4 with: version: '6.9.3' - modules: 'qt5compat qtmultimedia qtimageformats qtshadertools' + modules: 'qt5compat qtmultimedia qtimageformats qtshadertools qtspeech' cache: true - name: Build @@ -98,13 +99,14 @@ jobs: run: | sudo apt-get update sudo apt-get install -y libgl-dev libgles2-mesa-dev \ - libfontconfig1-dev libfreetype-dev libxkbcommon-dev libpoppler-qt6-dev + libfontconfig1-dev libfreetype-dev libxkbcommon-dev libpoppler-qt6-dev \ + libspeechd-dev - name: Install Qt uses: jurplel/install-qt-action@v4 with: version: '6.9.3' - modules: 'qt5compat qtmultimedia qtimageformats qtshadertools' + modules: 'qt5compat qtmultimedia qtimageformats qtshadertools qtspeech' cache: true - name: Build @@ -134,7 +136,7 @@ jobs: - name: Install dependencies run: | pip3 install --break-system-packages aqtinstall - python3 -m aqt install-qt mac desktop 6.9.3 -m qt5compat qtmultimedia qtimageformats qtshadertools + python3 -m aqt install-qt mac desktop 6.9.3 -m qt5compat qtmultimedia qtimageformats qtshadertools qtspeech echo "${{ github.workspace }}/6.9.3/macos/bin" >> $GITHUB_PATH npm install -g appdmg @@ -189,7 +191,7 @@ jobs: pip install -U pip pip install aqtinstall mkdir C:\Qt - python -m aqt install-qt windows desktop 6.9.3 win64_msvc2022_64 -O c:\Qt -m qt5compat qtmultimedia qtimageformats qtshadertools + python -m aqt install-qt windows desktop 6.9.3 win64_msvc2022_64 -O c:\Qt -m qt5compat qtmultimedia qtimageformats qtshadertools qtspeech dir C:\Qt\6.9.3\msvc2022_64\bin curl.exe -L --retry 5 --retry-delay 5 --retry-all-errors "https://aka.ms/vs/17/release/vc_redist.x64.exe" -o "%GITHUB_WORKSPACE%\vc_redist.x64.exe" where iscc @@ -333,8 +335,8 @@ jobs: run: | pip install aqtinstall mkdir C:\Qt - python -m aqt install-qt windows desktop 6.9.3 win64_msvc2022_64 -O c:\Qt -m qt5compat qtmultimedia qtimageformats qtshadertools - python -m aqt install-qt windows desktop 6.9.3 win64_msvc2022_arm64_cross_compiled -O c:\Qt -m qt5compat qtmultimedia qtimageformats qtshadertools + python -m aqt install-qt windows desktop 6.9.3 win64_msvc2022_64 -O c:\Qt -m qt5compat qtmultimedia qtimageformats qtshadertools qtspeech + python -m aqt install-qt windows desktop 6.9.3 win64_msvc2022_arm64_cross_compiled -O c:\Qt -m qt5compat qtmultimedia qtimageformats qtshadertools qtspeech dir C:\Qt\6.9.3\msvc2022_arm64\bin curl.exe -L --retry 5 --retry-delay 5 --retry-all-errors "https://aka.ms/vs/17/release/vc_redist.arm64.exe" -o "%GITHUB_WORKSPACE%\vc_redist.arm64.exe" where iscc diff --git a/CMakeLists.txt b/CMakeLists.txt index badd9f25..7e3012b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,7 @@ else() Sql Svg Test + TextToSpeech Widgets ) endif() diff --git a/YACReader/CMakeLists.txt b/YACReader/CMakeLists.txt index 62cf5864..f5e9eeec 100644 --- a/YACReader/CMakeLists.txt +++ b/YACReader/CMakeLists.txt @@ -89,6 +89,7 @@ target_link_libraries(YACReader PRIVATE Qt::Network Qt::Widgets Qt::Multimedia + Qt::TextToSpeech Qt::Svg Qt::Core5Compat comic_backend diff --git a/YACReader/translator.cpp b/YACReader/translator.cpp index a2524706..4822b55c 100644 --- a/YACReader/translator.cpp +++ b/YACReader/translator.cpp @@ -1,6 +1,6 @@ -#include - -#include +#include +#include +#include #include #include @@ -26,8 +26,6 @@ #include -#define APPID "417CEAD93449502CC3C9B69FED26C54118E62BCC" - YACReaderTranslator::YACReaderTranslator(Viewer *parent) : QWidget(parent), drag(false) { @@ -155,7 +153,7 @@ YACReaderTranslator::YACReaderTranslator(Viewer *parent) connect(speakButton, &QAbstractButton::pressed, this, &YACReaderTranslator::play); connect(clearButton, &QAbstractButton::pressed, this, &YACReaderTranslator::clear); - player = new QMediaPlayer; + tts = new QTextToSpeech(this); } void YACReaderTranslator::hideResults() @@ -179,20 +177,16 @@ void YACReaderTranslator::translate() QString from = this->from->itemData(this->from->currentIndex()).toString(); QString to = this->to->itemData(this->to->currentIndex()).toString(); + speakText = text; + speakLocale = from; + TranslationLoader *translationLoader = new TranslationLoader(text, from, to); connect(translationLoader, &TranslationLoader::requestFinished, this, &YACReaderTranslator::setTranslation); connect(translationLoader, &TranslationLoader::error, this, &YACReaderTranslator::error); connect(translationLoader, &TranslationLoader::timeOut, this, &YACReaderTranslator::error); connect(translationLoader, &QThread::finished, translationLoader, &QObject::deleteLater); - TextToSpeachLoader *tts = new TextToSpeachLoader(text, from); - connect(tts, &TextToSpeachLoader::requestFinished, this, &YACReaderTranslator::setSpeak); - connect(tts, &TextToSpeachLoader::error, this, &YACReaderTranslator::error); - connect(tts, &TextToSpeachLoader::timeOut, this, &YACReaderTranslator::error); - connect(tts, &QThread::finished, tts, &QObject::deleteLater); - translationLoader->start(); - tts->start(); resultsTitle->setText(tr("Translation")); @@ -208,19 +202,12 @@ void YACReaderTranslator::error() busyIndicator->hide(); } -void YACReaderTranslator::setSpeak(const QUrl &url) -{ - resultsTitle->setHidden(false); - speakButton->setHidden(false); - - ttsSource = url; -} - void YACReaderTranslator::setTranslation(const QString &string) { resultText->setText(string); resultsTitle->setHidden(false); + speakButton->setHidden(false); resultText->setHidden(false); busyIndicator->hide(); } @@ -236,8 +223,8 @@ void YACReaderTranslator::populateCombos() combo->addItem("Arabic", "ar"); combo->addItem("Bulgarian", "bg"); combo->addItem("Catalan", "ca"); - combo->addItem("Chinese Simplified", "zh-CHS"); - combo->addItem("Chinese Traditional", "zh-CHT"); + combo->addItem("Chinese Simplified", "zh-CN"); + combo->addItem("Chinese Traditional", "zh-TW"); combo->addItem("Czech", "cs"); combo->addItem("Danish", "da"); combo->addItem("Dutch", "nl"); @@ -277,10 +264,8 @@ void YACReaderTranslator::populateCombos() void YACReaderTranslator::play() { - - player->setSource(ttsSource); - - player->play(); + tts->setLocale(QLocale(speakLocale)); + tts->say(speakText); } void YACReaderTranslator::mousePressEvent(QMouseEvent *event) @@ -324,10 +309,10 @@ void TranslationLoader::run() connect(&tT, &QTimer::timeout, &q, &QEventLoop::quit); connect(&manager, &QNetworkAccessManager::finished, &q, &QEventLoop::quit); - QString url = "http://api.microsofttranslator.com/V2/Ajax.svc/Translate?appid=%1&from=%2&to=%3&text=%4&contentType=text/plain"; - url = url.arg(APPID, from, to, text); + QString urlStr = QString("https://api.mymemory.translated.net/get?q=%1&langpair=%2|%3") + .arg(QString::fromUtf8(QUrl::toPercentEncoding(text)), from, to); - QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(url))); + QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(urlStr))); tT.start(5000); // 5s timeout q.exec(); @@ -335,55 +320,12 @@ void TranslationLoader::run() if (tT.isActive()) { // download complete if (reply->error() == QNetworkReply::NoError) { - QString utf8 = QString::fromUtf8(reply->readAll()); - utf8 = utf8.remove(0, 1); - utf8 = utf8.remove(utf8.count() - 1, 1); - - QString translated(utf8); - emit requestFinished(translated); - } else - emit error(); - } else { - emit timeOut(); - } -} - -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- - -TextToSpeachLoader::TextToSpeachLoader(QString text, QString language) - : QThread(), text(text), language(language) -{ -} - -void TextToSpeachLoader::run() -{ - QNetworkAccessManager manager; - QEventLoop q; - QTimer tT; - - tT.setSingleShot(true); - connect(&tT, &QTimer::timeout, &q, &QEventLoop::quit); - connect(&manager, &QNetworkAccessManager::finished, &q, &QEventLoop::quit); - - QString url = "http://api.microsofttranslator.com/V2/Ajax.svc/Speak?appid=%1&language=%2&text=%3&contentType=text/plain"; - url = url.arg(APPID, language, text); - - QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(url))); - - tT.start(5000); // 5s timeout - q.exec(); - - if (tT.isActive()) { - // download complete - if (reply->error() == QNetworkReply::NoError) { - QString utf8 = QString::fromUtf8(reply->readAll()); - utf8 = utf8.remove(0, 1); - utf8 = utf8.remove(utf8.count() - 1, 1); - utf8 = utf8.replace("\\", ""); - - emit requestFinished(QUrl(utf8)); + QJsonDocument doc = QJsonDocument::fromJson(reply->readAll()); + QString translated = doc["responseData"]["translatedText"].toString(); + if (!translated.isEmpty()) + emit requestFinished(translated); + else + emit error(); } else emit error(); } else { diff --git a/YACReader/translator.h b/YACReader/translator.h index be50f8b6..b26b2480 100644 --- a/YACReader/translator.h +++ b/YACReader/translator.h @@ -1,7 +1,6 @@ #ifndef __TRANSLATOR_H #define __TRANSLATOR_H -class QUrl; class QMouseEvent; class QPoint; class QTextEdit; @@ -9,14 +8,13 @@ class QComboBox; class QLabel; class QPushButton; class YACReaderBusyWidget; +class QTextToSpeech; #include #include #include #include "viewer.h" -class QMediaPlayer; - class YACReaderTranslator : public QWidget { Q_OBJECT @@ -28,7 +26,6 @@ public slots: protected slots: void translate(); - void setSpeak(const QUrl &url); void setTranslation(const QString &string); void error(); void clear(); @@ -44,7 +41,9 @@ protected: QPoint click; private: - QMediaPlayer *player; + QTextToSpeech *tts; + QString speakText; + QString speakLocale; QTextEdit *text; QComboBox *from; @@ -53,7 +52,6 @@ private: QPushButton *speakButton; QLabel *resultText; YACReaderBusyWidget *busyIndicator; - QUrl ttsSource; QPushButton *clearButton; }; @@ -74,19 +72,4 @@ private: void run() override; }; -class TextToSpeachLoader : public QThread -{ - Q_OBJECT -public: - TextToSpeachLoader(QString text, QString language); -signals: - void requestFinished(QUrl); - void timeOut(); - void error(); - -private: - QString text; - QString language; - void run() override; -}; #endif diff --git a/ci/win/build_installer_qt6.iss b/ci/win/build_installer_qt6.iss index 87e03c4d..06930e90 100644 --- a/ci/win/build_installer_qt6.iss +++ b/ci/win/build_installer_qt6.iss @@ -60,6 +60,7 @@ Source: Qt6QuickTemplates2.dll; DestDir: {app} Source: Qt6QuickWidgets.dll; DestDir: {app} Source: Qt6Sql.dll; DestDir: {app} Source: Qt6Svg.dll; DestDir: {app} +Source: Qt6TextToSpeech.dll; DestDir: {app} Source: opengl32sw.dll; DestDir: {app}; Flags: skipifsourcedoesntexist Source: D3Dcompiler_47.dll; DestDir: {app}; Flags: skipifsourcedoesntexist @@ -74,6 +75,7 @@ Source:qml\*; DestDir: {app}\qml\; Flags: recursesubdirs Source:qmltooling\*; DestDir: {app}\qmltooling\ Source:sqldrivers\qsqlite.dll; DestDir: {app}\sqldrivers\ Source:styles\*; DestDir: {app}\styles\ +Source:texttospeech\*; DestDir: {app}\texttospeech\ Source:tls\*; DestDir: {app}\tls\ Source:translations\*; DestDir: {app}\translations\