Skip to content

Commit 8543d49

Browse files
committed
fix google translate and devtools deepl translate, add chrome file selector, add garbage filter
1 parent 5537679 commit 8543d49

13 files changed

+114
-77
lines changed

GUI/extenwindow.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ namespace
110110
QAction addExtension(ADD_EXTENSION), removeExtension(REMOVE_EXTENSION);
111111
if (auto action = QMenu::exec({ &addExtension, &removeExtension }, ui.extenList->mapToGlobal(point), nullptr, This))
112112
if (action == &removeExtension) Delete();
113-
else if (QString extenFile = QFileDialog::getOpenFileName(This, ADD_EXTENSION, ".", EXTENSIONS + QString(" (*.xdll)\nLibraries (*.dll)")); !extenFile.isEmpty()) Add(extenFile);
113+
else if (QString extenFile = QFileDialog::getOpenFileName(This, ADD_EXTENSION, ".", EXTENSIONS + QString(" (*.xdll);;Libraries (*.dll)")); !extenFile.isEmpty()) Add(extenFile);
114114
}
115115
}
116116

GUI/host/textthread.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ void TextThread::Flush()
104104
for (auto& sentence : sentences)
105105
{
106106
totalSize += sentence.size();
107-
sentence.erase(std::remove(sentence.begin(), sentence.end(), L'\0'), sentence.end());
107+
sentence.erase(std::remove(sentence.begin(), sentence.end(), 0), sentence.end());
108108
if (Output(*this, sentence)) storage->append(sentence);
109109
}
110110

extensions/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ target_link_libraries(Regex\ Filter Qt5::Widgets)
4747
target_link_libraries(Styler Qt5::Widgets)
4848
target_link_libraries(Thread\ Linker Qt5::Widgets)
4949

50+
add_custom_target(Cleaner ALL COMMAND del *.xdll WORKING_DIRECTORY ${CMAKE_FINAL_OUTPUT_DIRECTORY})
51+
5052
if (NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5WebSockets.dll AND NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5WebSocketsd.dll)
5153
add_custom_command(TARGET DevTools\ DeepL\ Translate
5254
POST_BUILD

extensions/bingtranslate.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ QStringList languages
8080
};
8181
std::wstring autoDetectLanguage = L"auto-detect";
8282

83-
bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true;
83+
bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true, useFilter = true;
8484
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
8585

8686
std::pair<bool, std::wstring> Translate(const std::wstring& text)

extensions/deepltranslate.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const char* TRANSLATION_PROVIDER = "DeepL Translate";
1111
const char* GET_API_KEY_FROM = "https://www.deepl.com/pro.html";
1212
QStringList languages
1313
{
14-
"Chinese (simplified): ZH",
14+
"Chinese: ZH",
1515
"Dutch: NL",
1616
"English: EN",
1717
"French: FR",
@@ -25,7 +25,7 @@ QStringList languages
2525
};
2626
std::wstring autoDetectLanguage = L"auto";
2727

28-
bool translateSelectedOnly = true, rateLimitAll = true, rateLimitSelected = true, useCache = true;
28+
bool translateSelectedOnly = true, rateLimitAll = true, rateLimitSelected = true, useCache = true, useFilter = true;
2929
int tokenCount = 10, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
3030

3131
enum KeyType { CAT, REST };

extensions/devtools.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ namespace
1414
auto _ = ([]
1515
{
1616
QObject::connect(&webSocket, &QWebSocket::stateChanged,
17-
[](QAbstractSocket::SocketState state) { OnStatusChanged(QMetaEnum::fromType<QAbstractSocket::SocketState>().valueToKey(state)); }
18-
);
17+
[](QAbstractSocket::SocketState state) { OnStatusChanged(QMetaEnum::fromType<QAbstractSocket::SocketState>().valueToKey(state)); });
1918
QObject::connect(&webSocket, &QWebSocket::textMessageReceived, [](QString message)
2019
{
2120
auto result = JSON::Parse(S(message));
@@ -84,7 +83,7 @@ namespace DevTools
8483
if (GetExitCodeProcess(processInfo.hProcess, &exitCode) && exitCode == STILL_ACTIVE)
8584
{
8685
TerminateProcess(processInfo.hProcess, 0);
87-
WaitForSingleObject(processInfo.hProcess, 100);
86+
WaitForSingleObject(processInfo.hProcess, 2000);
8887
CloseHandle(processInfo.hProcess);
8988
CloseHandle(processInfo.hThread);
9089
}
@@ -98,11 +97,11 @@ namespace DevTools
9897
return webSocket.state() == QAbstractSocket::ConnectedState;
9998
}
10099

101-
JSON::Value<wchar_t> SendRequest(const std::wstring& method, const std::wstring& params)
100+
JSON::Value<wchar_t> SendRequest(const char* method, const std::wstring& params)
102101
{
103102
concurrency::task_completion_event<JSON::Value<wchar_t>> response;
104103
int id = idCounter += 1;
105-
auto message = FormatString(LR"({"id":%d,"method":"%s","params":%s})", id, method, params);
104+
auto message = FormatString(LR"({"id":%d,"method":"%S","params":%s})", id, method, params);
106105
{
107106
std::scoped_lock lock(devToolsMutex);
108107
if (webSocket.state() != QAbstractSocket::ConnectedState) return {};

extensions/devtools.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ namespace DevTools
66
void Start(const std::wstring& path, std::function<void(QString)> statusChanged, bool headless);
77
void Close();
88
bool Connected();
9-
JSON::Value<wchar_t> SendRequest(const std::wstring& method, const std::wstring& params = L"{}");
9+
JSON::Value<wchar_t> SendRequest(const char* method, const std::wstring& params = L"{}");
1010
}

extensions/devtoolsdeepltranslate.cpp

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
#include "qtcommon.h"
22
#include "devtools.h"
3+
#include <QFileDialog>
4+
#include <QMouseEvent>
35
#include <ShlObj.h>
46

57
extern const wchar_t* TRANSLATION_ERROR;
6-
extern Synchronized<std::wstring> translateTo, translateFrom;
7-
extern QFormLayout* display;
8-
extern Settings settings;
9-
10-
const char* TRANSLATION_PROVIDER = "DevTools DeepL Translate";
11-
const char* GET_API_KEY_FROM = nullptr;
12-
bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true;
13-
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500;
14-
158
extern const char* CHROME_LOCATION;
169
extern const char* START_DEVTOOLS;
1710
extern const char* STOP_DEVTOOLS;
@@ -20,9 +13,18 @@ extern const char* DEVTOOLS_STATUS;
2013
extern const char* AUTO_START;
2114
extern const wchar_t* ERROR_START_CHROME;
2215

16+
extern Synchronized<std::wstring> translateTo, translateFrom;
17+
extern QFormLayout* display;
18+
extern Settings settings;
19+
20+
const char* TRANSLATION_PROVIDER = "DevTools DeepL Translate";
21+
const char* GET_API_KEY_FROM = nullptr;
22+
bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true, useFilter = true;
23+
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500;
24+
2325
QStringList languages
2426
{
25-
"Chinese (simplified): zh",
27+
"Chinese: zh",
2628
"Dutch: nl",
2729
"English: en",
2830
"French: fr",
@@ -51,14 +53,27 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
5153
if (std::filesystem::exists(programFiles)) chromePath = S(programFiles);
5254
}
5355
auto chromePathEdit = new QLineEdit(chromePath);
56+
static struct : QObject
57+
{
58+
bool eventFilter(QObject* object, QEvent* event)
59+
{
60+
if (auto mouseEvent = dynamic_cast<QMouseEvent*>(event))
61+
if (mouseEvent->button() == Qt::LeftButton)
62+
if (QString chromePath = QFileDialog::getOpenFileName(nullptr, TRANSLATION_PROVIDER, "/", "Chrome (*.exe)"); !chromePath.isEmpty())
63+
((QLineEdit*)object)->setText(chromePath);
64+
return false;
65+
}
66+
} chromeSelector;
67+
chromePathEdit->installEventFilter(&chromeSelector);
5468
QObject::connect(chromePathEdit, &QLineEdit::textChanged, [chromePathEdit](QString path) { settings.setValue(CHROME_LOCATION, path); });
5569
display->addRow(CHROME_LOCATION, chromePathEdit);
5670
auto statusLabel = new QLabel("Stopped");
5771
auto startButton = new QPushButton(START_DEVTOOLS), stopButton = new QPushButton(STOP_DEVTOOLS);
5872
auto headlessCheck = new QCheckBox();
5973
headlessCheck->setChecked(settings.value(HEADLESS_MODE, true).toBool());
6074
QObject::connect(headlessCheck, &QCheckBox::clicked, [](bool headless) { settings.setValue(HEADLESS_MODE, headless); });
61-
QObject::connect(startButton, &QPushButton::clicked, [statusLabel, chromePathEdit, headlessCheck] {
75+
QObject::connect(startButton, &QPushButton::clicked, [statusLabel, chromePathEdit, headlessCheck]
76+
{
6277
DevTools::Start(
6378
S(chromePathEdit->text()),
6479
[statusLabel](QString status)
@@ -79,8 +94,10 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
7994
})
8095
if (auto userAgent = Copy(JSON::Parse(httpRequest.response)[L"User-Agent"].String()))
8196
if (userAgent->find(L"Headless") != std::string::npos)
82-
DevTools::SendRequest(L"Network.setUserAgentOverride",
83-
FormatString(LR"({"userAgent":"%s"})", userAgent->replace(userAgent->find(L"Headless"), 8, L"")));
97+
DevTools::SendRequest(
98+
"Network.setUserAgentOverride",
99+
FormatString(LR"({"userAgent":"%s"})", userAgent->replace(userAgent->find(L"Headless"), 8, L""))
100+
);
84101
}).detach();
85102
},
86103
headlessCheck->isChecked()
@@ -91,14 +108,14 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
91108
buttons->addWidget(startButton);
92109
buttons->addWidget(stopButton);
93110
display->addRow(HEADLESS_MODE, headlessCheck);
94-
auto autoStartButton = new QCheckBox();
95-
autoStartButton->setChecked(settings.value(AUTO_START, false).toBool());
96-
QObject::connect(autoStartButton, &QCheckBox::clicked, [](bool autoStart) { settings.setValue(AUTO_START, autoStart); });
97-
display->addRow(AUTO_START, autoStartButton);
111+
auto autoStartCheck = new QCheckBox();
112+
autoStartCheck->setChecked(settings.value(AUTO_START, false).toBool());
113+
QObject::connect(autoStartCheck, &QCheckBox::clicked, [](bool autoStart) { settings.setValue(AUTO_START, autoStart); });
114+
display->addRow(AUTO_START, autoStartCheck);
98115
display->addRow(buttons);
99116
statusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);
100117
display->addRow(DEVTOOLS_STATUS, statusLabel);
101-
if (autoStartButton->isChecked()) QMetaObject::invokeMethod(startButton, &QPushButton::click, Qt::QueuedConnection);
118+
if (autoStartCheck->isChecked()) QMetaObject::invokeMethod(startButton, &QPushButton::click, Qt::QueuedConnection);
102119
}
103120
break;
104121
case DLL_PROCESS_DETACH:
@@ -116,16 +133,20 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text)
116133
// DevTools can't handle concurrent translations yet
117134
static std::mutex translationMutex;
118135
std::scoped_lock lock(translationMutex);
119-
DevTools::SendRequest(L"Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/translator#%s/%s/%s"})", translateFrom.Copy(), translateTo.Copy(), Escape(text)));
136+
DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/translator#any/%s/%s"})", translateTo.Copy(), Escape(text)));
137+
138+
if (translateFrom.Copy() != autoDetectLanguage)
139+
DevTools::SendRequest("Runtime.evaluate", FormatString(LR"({"expression":"
140+
document.querySelector('.lmt__language_select--source').querySelector('button').click(),
141+
document.evaluate(`//button[contains(text(),'%s')]`,document.querySelector('.lmt__language_select__menu'),null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue.click()
142+
"})", S(std::find_if(languages.begin(), languages.end(), [end = S(translateFrom.Copy())](const QString& language) { return language.endsWith(end); })->split(":")[0])));
143+
120144
for (int retry = 0; ++retry < 100; Sleep(100))
121-
if (auto translation = Copy(
122-
DevTools::SendRequest(L"Runtime.evaluate", LR"({"expression":"document.querySelector('#target-dummydiv').innerHTML","returnByValue":true})")[L"result"][L"value"].String()
123-
)) if (!translation->empty()) return { true, translation.value() };
124-
if (auto errorMessage = Copy(
125-
DevTools::SendRequest(
126-
L"Runtime.evaluate",
127-
LR"({"expression":"document.querySelector('div.lmt__system_notification').innerHTML","returnByValue":true})"
128-
)[L"result"][L"value"].String()
129-
)) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, errorMessage.value()) };
145+
if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate",
146+
LR"({"expression":"document.querySelector('#target-dummydiv').innerHTML.trim() ","returnByValue":true})"
147+
)[L"result"][L"value"].String())) if (!translation->empty()) return { true, translation.value() };
148+
if (auto errorMessage = Copy(DevTools::SendRequest("Runtime.evaluate",
149+
LR"({"expression":"document.querySelector('div.lmt__system_notification').innerHTML","returnByValue":true})"
150+
)[L"result"][L"value"].String())) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, errorMessage.value()) };
130151
return { false, TRANSLATION_ERROR };
131152
}

extensions/googletranslate.cpp

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ QStringList languages
122122
};
123123
std::wstring autoDetectLanguage = L"auto";
124124

125-
bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true;
125+
bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true, useFilter = true;
126126
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
127127

128128
std::pair<bool, std::wstring> Translate(const std::wstring& text)
@@ -145,34 +145,12 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text)
145145
if (HttpRequest httpRequest{
146146
L"Mozilla/5.0 Textractor",
147147
L"translate.google.com",
148-
L"POST",
149-
L"/_/TranslateWebserverUi/data/batchexecute?rpcids=MkEWBc",
150-
"f.req=" + Escape(WideStringToString(
151-
FormatString(LR"([[["MkEWBc","[[\"%s\",\"%s\",\"%s\",true],[null]]",null,"generic"]]])", JSON::Escape((JSON::Escape(text))), translateFrom.Copy(), translateTo.Copy())
152-
)),
153-
L"Content-Type: application/x-www-form-urlencoded"
148+
L"GET",
149+
FormatString(L"/m?sl=%s&tl=%s&q=%s", translateFrom.Copy(), translateTo.Copy(), Escape(text)).c_str()
154150
})
155151
{
156-
if (auto start = httpRequest.response.find(L"[["); start != std::string::npos)
157-
{
158-
if (auto blob = Copy(JSON::Parse(httpRequest.response.substr(start))[0][2].String())) if (auto translations = Copy(JSON::Parse(blob.value())[1][0].Array()))
159-
{
160-
std::wstring translation;
161-
if (translations->size() == 1)
162-
{
163-
if (translations = Copy(translations.value()[0][5].Array()))
164-
for (const auto& sentence : translations.value())
165-
if (sentence[0].String()) (translation += *sentence[0].String()) += L" ";
166-
}
167-
else
168-
{
169-
for (const auto& conjugation : translations.value())
170-
if (auto sentence = conjugation[0].String()) if (auto gender = conjugation[2].String()) translation += FormatString(L"%s %s\n", *sentence, *gender);
171-
}
172-
if (!translation.empty()) return { true, translation };
173-
return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, blob.value()) };
174-
}
175-
}
152+
auto start = httpRequest.response.find(L"result-container\">") + 18, end = httpRequest.response.find(L'<', start);
153+
if (start != end) return { true, HTML::Unescape(httpRequest.response.substr(start, end - start)) };
176154
return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) };
177155
}
178156
else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) };

extensions/network.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,36 @@ struct HttpRequest
3232
std::wstring Escape(const std::wstring& text);
3333
std::string Escape(const std::string& text);
3434

35+
namespace HTML
36+
{
37+
template <typename C>
38+
std::basic_string<C> Unescape(std::basic_string<C> text)
39+
{
40+
constexpr C
41+
lt[] = { '&', 'l', 't', ';' },
42+
gt[] = { '&', 'g', 't', ';' },
43+
apos1[] = { '&', 'a', 'p', 'o', 's', ';' },
44+
apos2[] = { '&', '#', '3', '9', ';' },
45+
apos3[] = { '&', '#', 'x', '2', '7', ';' },
46+
apos4[] = { '&', '#', 'X', '2', '7', ';' },
47+
quot[] = { '&', 'q', 'u', 'o', 't', ';' },
48+
amp[] = { '&', 'a', 'm', 'p', ';' };
49+
for (int i = 0; i < text.size(); ++i)
50+
if (text[i] == '&')
51+
for (auto [original, length, replacement] : Array<const C*, size_t, C>{
52+
{ lt, std::size(lt), '<' },
53+
{ gt, std::size(gt), '>' },
54+
{ apos1, std::size(apos1), '\'' },
55+
{ apos2, std::size(apos2), '\'' },
56+
{ apos3, std::size(apos3), '\'' },
57+
{ apos4, std::size(apos4), '\'' },
58+
{ quot, std::size(quot), '"' },
59+
{ amp, std::size(amp), '&' }
60+
}) if (std::char_traits<C>::compare(text.data() + i, original, length) == 0) text.replace(i, length, 1, replacement);
61+
return text;
62+
}
63+
}
64+
3565
namespace JSON
3666
{
3767
template <typename C>
@@ -136,7 +166,7 @@ namespace JSON
136166

137167
if (SkipWhitespace()) return {};
138168

139-
static C nullStr[] = { 'n', 'u', 'l', 'l' }, trueStr[] = { 't', 'r', 'u', 'e' }, falseStr[] = { 'f', 'a', 'l', 's', 'e' };
169+
constexpr C nullStr[] = { 'n', 'u', 'l', 'l' }, trueStr[] = { 't', 'r', 'u', 'e' }, falseStr[] = { 'f', 'a', 'l', 's', 'e' };
140170
if (ch == nullStr[0])
141171
if (std::char_traits<C>::compare(text.data() + i, nullStr, std::size(nullStr)) == 0) return i += std::size(nullStr), nullptr;
142172
else return {};

0 commit comments

Comments
 (0)