mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 15:04:57 +00:00
169 lines
5.0 KiB
C++
169 lines
5.0 KiB
C++
// This is the source code of AyuGram for Desktop.
|
|
//
|
|
// We do not and cannot prevent the use of our code,
|
|
// but be respectful and credit the original author.
|
|
//
|
|
// Copyright @Radolyn, 2026
|
|
#include "ayu/features/translator/implementations/google.h"
|
|
|
|
#include "ayu/features/translator/html_parser.h"
|
|
|
|
#include <memory>
|
|
#include <QtCore/QJsonArray>
|
|
#include <QtCore/QJsonDocument>
|
|
#include <QtCore/QJsonObject>
|
|
#include <QtCore/QJsonValue>
|
|
#include <QtCore/QPointer>
|
|
#include <QtCore/QStringList>
|
|
#include <QtCore/QTimer>
|
|
#include <QtCore/QUrl>
|
|
#include <QtGui/QTextDocument>
|
|
#include <QtNetwork/QNetworkReply>
|
|
#include <QtNetwork/QNetworkRequest>
|
|
|
|
namespace Ayu::Translator {
|
|
|
|
namespace {
|
|
|
|
constexpr auto kGoogleTranslateUrl = "https://translate-pa.googleapis.com/v1/translateHtml";
|
|
constexpr auto kGoogleContentType = "application/json+protobuf";
|
|
constexpr auto kGoogleDefaultApiKey = "AIzaSyATBXajvzQLTDHEQbcpq0Ihe0vWDHmO520";
|
|
|
|
QString decodeHtmlEntities(const QString &text) {
|
|
QTextDocument doc;
|
|
doc.setHtml(text);
|
|
return doc.toPlainText();
|
|
}
|
|
|
|
QStringList collectStrings(const QJsonValue &value) {
|
|
QStringList result;
|
|
if (value.isString()) {
|
|
result.push_back(value.toString());
|
|
return result;
|
|
}
|
|
if (value.isArray()) {
|
|
const auto arr = value.toArray();
|
|
for (const auto &item : arr) {
|
|
result.append(collectStrings(item));
|
|
}
|
|
return result;
|
|
}
|
|
if (value.isObject()) {
|
|
const auto obj = value.toObject();
|
|
if (obj.contains(QStringLiteral("text"))) {
|
|
result.append(collectStrings(obj.value(QStringLiteral("text"))));
|
|
}
|
|
if (obj.contains(QStringLiteral("trans"))) {
|
|
result.append(collectStrings(obj.value(QStringLiteral("trans"))));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
GoogleTranslator &GoogleTranslator::instance() {
|
|
static GoogleTranslator inst;
|
|
return inst;
|
|
}
|
|
|
|
GoogleTranslator::GoogleTranslator(QObject *parent)
|
|
: MultiThreadTranslator(parent) {
|
|
}
|
|
|
|
QPointer<QNetworkReply> GoogleTranslator::startSingleTranslation(
|
|
const MultiThreadArgs &args
|
|
) {
|
|
const auto &text = args.parsedData.text;
|
|
const auto &fromLang = args.parsedData.fromLang;
|
|
const auto &toLang = args.parsedData.toLang;
|
|
const auto onSuccess = args.onSuccess;
|
|
const auto onFail = args.onFail;
|
|
|
|
if (text.empty() || toLang.isEmpty()) {
|
|
if (onFail) onFail();
|
|
return nullptr;
|
|
}
|
|
|
|
const auto from = fromLang.trimmed().isEmpty() ? QStringLiteral("auto") : fromLang.trimmed();
|
|
const auto to = toLang.trimmed();
|
|
|
|
auto textToTranslate = text.text;
|
|
textToTranslate = textToTranslate.replace(qsl("\n"), qsl("<br>"));
|
|
|
|
const auto preparedText = textToTranslate;
|
|
QJsonArray requestRoot;
|
|
QJsonArray requestPayload;
|
|
QJsonArray requestText;
|
|
requestText.append(preparedText);
|
|
requestPayload.append(requestText);
|
|
requestPayload.append(from);
|
|
requestPayload.append(to);
|
|
requestRoot.append(requestPayload);
|
|
requestRoot.append(QStringLiteral("wt_lib"));
|
|
const auto body = QJsonDocument(requestRoot).toJson(QJsonDocument::Compact);
|
|
|
|
QNetworkRequest req(QUrl(QString::fromLatin1(kGoogleTranslateUrl)));
|
|
const auto userAgent = randomDesktopUserAgent();
|
|
req.setHeader(QNetworkRequest::UserAgentHeader, userAgent);
|
|
req.setHeader(QNetworkRequest::ContentTypeHeader, QString::fromLatin1(kGoogleContentType));
|
|
req.setRawHeader(QByteArrayLiteral("Accept"), QByteArrayLiteral("application/json"));
|
|
req.setRawHeader(QByteArrayLiteral("X-Goog-Api-Key"), QByteArray(kGoogleDefaultApiKey));
|
|
|
|
QPointer<QNetworkReply> reply = _nam.post(req, body);
|
|
|
|
auto timer = new QTimer(reply);
|
|
timer->setSingleShot(true);
|
|
timer->setInterval(15000);
|
|
QObject::connect(timer,
|
|
&QTimer::timeout,
|
|
reply,
|
|
[reply]
|
|
{
|
|
if (!reply) return;
|
|
if (reply->isRunning()) reply->abort();
|
|
});
|
|
timer->start();
|
|
|
|
QObject::connect(reply,
|
|
&QNetworkReply::finished,
|
|
reply,
|
|
[reply, onSuccess = onSuccess, onFail = onFail, timer]
|
|
{
|
|
if (!reply) return;
|
|
timer->stop();
|
|
const auto guard = std::unique_ptr<QNetworkReply, void(*)(QNetworkReply *)>(
|
|
reply,
|
|
[](QNetworkReply *r) { r->deleteLater(); });
|
|
if (reply->error() != QNetworkReply::NoError) {
|
|
if (onFail) onFail();
|
|
return;
|
|
}
|
|
const auto body = reply->readAll();
|
|
QJsonParseError parseError{};
|
|
const auto doc = QJsonDocument::fromJson(body, &parseError);
|
|
if (parseError.error != QJsonParseError::NoError || !doc.isArray()) {
|
|
if (onFail) onFail();
|
|
return;
|
|
}
|
|
const auto root = doc.array();
|
|
if (root.isEmpty()) {
|
|
if (onFail) onFail();
|
|
return;
|
|
}
|
|
const auto translatedItems = collectStrings(root.at(0));
|
|
const auto textOutCombined = translatedItems.join(QStringLiteral(" "));
|
|
if (textOutCombined.trimmed().isEmpty()) {
|
|
if (onFail) onFail();
|
|
return;
|
|
}
|
|
const auto decodedText = decodeHtmlEntities(textOutCombined);
|
|
if (onSuccess) onSuccess(shouldWrapInHtml()
|
|
? Html::htmlToEntities(decodedText)
|
|
: TextWithEntities{decodedText});
|
|
});
|
|
|
|
return reply;
|
|
}
|
|
|
|
} |