forked from Public-Mirror/Textractor
fix google translate and devtools deepl translate, add chrome file selector, add garbage filter
This commit is contained in:
parent
5537679442
commit
8543d49192
@ -110,7 +110,7 @@ namespace
|
|||||||
QAction addExtension(ADD_EXTENSION), removeExtension(REMOVE_EXTENSION);
|
QAction addExtension(ADD_EXTENSION), removeExtension(REMOVE_EXTENSION);
|
||||||
if (auto action = QMenu::exec({ &addExtension, &removeExtension }, ui.extenList->mapToGlobal(point), nullptr, This))
|
if (auto action = QMenu::exec({ &addExtension, &removeExtension }, ui.extenList->mapToGlobal(point), nullptr, This))
|
||||||
if (action == &removeExtension) Delete();
|
if (action == &removeExtension) Delete();
|
||||||
else if (QString extenFile = QFileDialog::getOpenFileName(This, ADD_EXTENSION, ".", EXTENSIONS + QString(" (*.xdll)\nLibraries (*.dll)")); !extenFile.isEmpty()) Add(extenFile);
|
else if (QString extenFile = QFileDialog::getOpenFileName(This, ADD_EXTENSION, ".", EXTENSIONS + QString(" (*.xdll);;Libraries (*.dll)")); !extenFile.isEmpty()) Add(extenFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ void TextThread::Flush()
|
|||||||
for (auto& sentence : sentences)
|
for (auto& sentence : sentences)
|
||||||
{
|
{
|
||||||
totalSize += sentence.size();
|
totalSize += sentence.size();
|
||||||
sentence.erase(std::remove(sentence.begin(), sentence.end(), L'\0'), sentence.end());
|
sentence.erase(std::remove(sentence.begin(), sentence.end(), 0), sentence.end());
|
||||||
if (Output(*this, sentence)) storage->append(sentence);
|
if (Output(*this, sentence)) storage->append(sentence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,8 @@ target_link_libraries(Regex\ Filter Qt5::Widgets)
|
|||||||
target_link_libraries(Styler Qt5::Widgets)
|
target_link_libraries(Styler Qt5::Widgets)
|
||||||
target_link_libraries(Thread\ Linker Qt5::Widgets)
|
target_link_libraries(Thread\ Linker Qt5::Widgets)
|
||||||
|
|
||||||
|
add_custom_target(Cleaner ALL COMMAND del *.xdll WORKING_DIRECTORY ${CMAKE_FINAL_OUTPUT_DIRECTORY})
|
||||||
|
|
||||||
if (NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5WebSockets.dll AND NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5WebSocketsd.dll)
|
if (NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5WebSockets.dll AND NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5WebSocketsd.dll)
|
||||||
add_custom_command(TARGET DevTools\ DeepL\ Translate
|
add_custom_command(TARGET DevTools\ DeepL\ Translate
|
||||||
POST_BUILD
|
POST_BUILD
|
||||||
|
@ -80,7 +80,7 @@ QStringList languages
|
|||||||
};
|
};
|
||||||
std::wstring autoDetectLanguage = L"auto-detect";
|
std::wstring autoDetectLanguage = L"auto-detect";
|
||||||
|
|
||||||
bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true;
|
bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true, useFilter = true;
|
||||||
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
|
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
|
||||||
|
|
||||||
std::pair<bool, std::wstring> Translate(const std::wstring& text)
|
std::pair<bool, std::wstring> Translate(const std::wstring& text)
|
||||||
|
@ -11,7 +11,7 @@ const char* TRANSLATION_PROVIDER = "DeepL Translate";
|
|||||||
const char* GET_API_KEY_FROM = "https://www.deepl.com/pro.html";
|
const char* GET_API_KEY_FROM = "https://www.deepl.com/pro.html";
|
||||||
QStringList languages
|
QStringList languages
|
||||||
{
|
{
|
||||||
"Chinese (simplified): ZH",
|
"Chinese: ZH",
|
||||||
"Dutch: NL",
|
"Dutch: NL",
|
||||||
"English: EN",
|
"English: EN",
|
||||||
"French: FR",
|
"French: FR",
|
||||||
@ -25,7 +25,7 @@ QStringList languages
|
|||||||
};
|
};
|
||||||
std::wstring autoDetectLanguage = L"auto";
|
std::wstring autoDetectLanguage = L"auto";
|
||||||
|
|
||||||
bool translateSelectedOnly = true, rateLimitAll = true, rateLimitSelected = true, useCache = true;
|
bool translateSelectedOnly = true, rateLimitAll = true, rateLimitSelected = true, useCache = true, useFilter = true;
|
||||||
int tokenCount = 10, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
|
int tokenCount = 10, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
|
||||||
|
|
||||||
enum KeyType { CAT, REST };
|
enum KeyType { CAT, REST };
|
||||||
|
@ -14,8 +14,7 @@ namespace
|
|||||||
auto _ = ([]
|
auto _ = ([]
|
||||||
{
|
{
|
||||||
QObject::connect(&webSocket, &QWebSocket::stateChanged,
|
QObject::connect(&webSocket, &QWebSocket::stateChanged,
|
||||||
[](QAbstractSocket::SocketState state) { OnStatusChanged(QMetaEnum::fromType<QAbstractSocket::SocketState>().valueToKey(state)); }
|
[](QAbstractSocket::SocketState state) { OnStatusChanged(QMetaEnum::fromType<QAbstractSocket::SocketState>().valueToKey(state)); });
|
||||||
);
|
|
||||||
QObject::connect(&webSocket, &QWebSocket::textMessageReceived, [](QString message)
|
QObject::connect(&webSocket, &QWebSocket::textMessageReceived, [](QString message)
|
||||||
{
|
{
|
||||||
auto result = JSON::Parse(S(message));
|
auto result = JSON::Parse(S(message));
|
||||||
@ -84,7 +83,7 @@ namespace DevTools
|
|||||||
if (GetExitCodeProcess(processInfo.hProcess, &exitCode) && exitCode == STILL_ACTIVE)
|
if (GetExitCodeProcess(processInfo.hProcess, &exitCode) && exitCode == STILL_ACTIVE)
|
||||||
{
|
{
|
||||||
TerminateProcess(processInfo.hProcess, 0);
|
TerminateProcess(processInfo.hProcess, 0);
|
||||||
WaitForSingleObject(processInfo.hProcess, 100);
|
WaitForSingleObject(processInfo.hProcess, 2000);
|
||||||
CloseHandle(processInfo.hProcess);
|
CloseHandle(processInfo.hProcess);
|
||||||
CloseHandle(processInfo.hThread);
|
CloseHandle(processInfo.hThread);
|
||||||
}
|
}
|
||||||
@ -98,11 +97,11 @@ namespace DevTools
|
|||||||
return webSocket.state() == QAbstractSocket::ConnectedState;
|
return webSocket.state() == QAbstractSocket::ConnectedState;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON::Value<wchar_t> SendRequest(const std::wstring& method, const std::wstring& params)
|
JSON::Value<wchar_t> SendRequest(const char* method, const std::wstring& params)
|
||||||
{
|
{
|
||||||
concurrency::task_completion_event<JSON::Value<wchar_t>> response;
|
concurrency::task_completion_event<JSON::Value<wchar_t>> response;
|
||||||
int id = idCounter += 1;
|
int id = idCounter += 1;
|
||||||
auto message = FormatString(LR"({"id":%d,"method":"%s","params":%s})", id, method, params);
|
auto message = FormatString(LR"({"id":%d,"method":"%S","params":%s})", id, method, params);
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(devToolsMutex);
|
std::scoped_lock lock(devToolsMutex);
|
||||||
if (webSocket.state() != QAbstractSocket::ConnectedState) return {};
|
if (webSocket.state() != QAbstractSocket::ConnectedState) return {};
|
||||||
|
@ -6,5 +6,5 @@ namespace DevTools
|
|||||||
void Start(const std::wstring& path, std::function<void(QString)> statusChanged, bool headless);
|
void Start(const std::wstring& path, std::function<void(QString)> statusChanged, bool headless);
|
||||||
void Close();
|
void Close();
|
||||||
bool Connected();
|
bool Connected();
|
||||||
JSON::Value<wchar_t> SendRequest(const std::wstring& method, const std::wstring& params = L"{}");
|
JSON::Value<wchar_t> SendRequest(const char* method, const std::wstring& params = L"{}");
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,10 @@
|
|||||||
#include "qtcommon.h"
|
#include "qtcommon.h"
|
||||||
#include "devtools.h"
|
#include "devtools.h"
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QMouseEvent>
|
||||||
#include <ShlObj.h>
|
#include <ShlObj.h>
|
||||||
|
|
||||||
extern const wchar_t* TRANSLATION_ERROR;
|
extern const wchar_t* TRANSLATION_ERROR;
|
||||||
extern Synchronized<std::wstring> translateTo, translateFrom;
|
|
||||||
extern QFormLayout* display;
|
|
||||||
extern Settings settings;
|
|
||||||
|
|
||||||
const char* TRANSLATION_PROVIDER = "DevTools DeepL Translate";
|
|
||||||
const char* GET_API_KEY_FROM = nullptr;
|
|
||||||
bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true;
|
|
||||||
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500;
|
|
||||||
|
|
||||||
extern const char* CHROME_LOCATION;
|
extern const char* CHROME_LOCATION;
|
||||||
extern const char* START_DEVTOOLS;
|
extern const char* START_DEVTOOLS;
|
||||||
extern const char* STOP_DEVTOOLS;
|
extern const char* STOP_DEVTOOLS;
|
||||||
@ -20,9 +13,18 @@ extern const char* DEVTOOLS_STATUS;
|
|||||||
extern const char* AUTO_START;
|
extern const char* AUTO_START;
|
||||||
extern const wchar_t* ERROR_START_CHROME;
|
extern const wchar_t* ERROR_START_CHROME;
|
||||||
|
|
||||||
|
extern Synchronized<std::wstring> translateTo, translateFrom;
|
||||||
|
extern QFormLayout* display;
|
||||||
|
extern Settings settings;
|
||||||
|
|
||||||
|
const char* TRANSLATION_PROVIDER = "DevTools DeepL Translate";
|
||||||
|
const char* GET_API_KEY_FROM = nullptr;
|
||||||
|
bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true, useFilter = true;
|
||||||
|
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500;
|
||||||
|
|
||||||
QStringList languages
|
QStringList languages
|
||||||
{
|
{
|
||||||
"Chinese (simplified): zh",
|
"Chinese: zh",
|
||||||
"Dutch: nl",
|
"Dutch: nl",
|
||||||
"English: en",
|
"English: en",
|
||||||
"French: fr",
|
"French: fr",
|
||||||
@ -51,6 +53,18 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
|
|||||||
if (std::filesystem::exists(programFiles)) chromePath = S(programFiles);
|
if (std::filesystem::exists(programFiles)) chromePath = S(programFiles);
|
||||||
}
|
}
|
||||||
auto chromePathEdit = new QLineEdit(chromePath);
|
auto chromePathEdit = new QLineEdit(chromePath);
|
||||||
|
static struct : QObject
|
||||||
|
{
|
||||||
|
bool eventFilter(QObject* object, QEvent* event)
|
||||||
|
{
|
||||||
|
if (auto mouseEvent = dynamic_cast<QMouseEvent*>(event))
|
||||||
|
if (mouseEvent->button() == Qt::LeftButton)
|
||||||
|
if (QString chromePath = QFileDialog::getOpenFileName(nullptr, TRANSLATION_PROVIDER, "/", "Chrome (*.exe)"); !chromePath.isEmpty())
|
||||||
|
((QLineEdit*)object)->setText(chromePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} chromeSelector;
|
||||||
|
chromePathEdit->installEventFilter(&chromeSelector);
|
||||||
QObject::connect(chromePathEdit, &QLineEdit::textChanged, [chromePathEdit](QString path) { settings.setValue(CHROME_LOCATION, path); });
|
QObject::connect(chromePathEdit, &QLineEdit::textChanged, [chromePathEdit](QString path) { settings.setValue(CHROME_LOCATION, path); });
|
||||||
display->addRow(CHROME_LOCATION, chromePathEdit);
|
display->addRow(CHROME_LOCATION, chromePathEdit);
|
||||||
auto statusLabel = new QLabel("Stopped");
|
auto statusLabel = new QLabel("Stopped");
|
||||||
@ -58,7 +72,8 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
|
|||||||
auto headlessCheck = new QCheckBox();
|
auto headlessCheck = new QCheckBox();
|
||||||
headlessCheck->setChecked(settings.value(HEADLESS_MODE, true).toBool());
|
headlessCheck->setChecked(settings.value(HEADLESS_MODE, true).toBool());
|
||||||
QObject::connect(headlessCheck, &QCheckBox::clicked, [](bool headless) { settings.setValue(HEADLESS_MODE, headless); });
|
QObject::connect(headlessCheck, &QCheckBox::clicked, [](bool headless) { settings.setValue(HEADLESS_MODE, headless); });
|
||||||
QObject::connect(startButton, &QPushButton::clicked, [statusLabel, chromePathEdit, headlessCheck] {
|
QObject::connect(startButton, &QPushButton::clicked, [statusLabel, chromePathEdit, headlessCheck]
|
||||||
|
{
|
||||||
DevTools::Start(
|
DevTools::Start(
|
||||||
S(chromePathEdit->text()),
|
S(chromePathEdit->text()),
|
||||||
[statusLabel](QString status)
|
[statusLabel](QString status)
|
||||||
@ -79,8 +94,10 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
|
|||||||
})
|
})
|
||||||
if (auto userAgent = Copy(JSON::Parse(httpRequest.response)[L"User-Agent"].String()))
|
if (auto userAgent = Copy(JSON::Parse(httpRequest.response)[L"User-Agent"].String()))
|
||||||
if (userAgent->find(L"Headless") != std::string::npos)
|
if (userAgent->find(L"Headless") != std::string::npos)
|
||||||
DevTools::SendRequest(L"Network.setUserAgentOverride",
|
DevTools::SendRequest(
|
||||||
FormatString(LR"({"userAgent":"%s"})", userAgent->replace(userAgent->find(L"Headless"), 8, L"")));
|
"Network.setUserAgentOverride",
|
||||||
|
FormatString(LR"({"userAgent":"%s"})", userAgent->replace(userAgent->find(L"Headless"), 8, L""))
|
||||||
|
);
|
||||||
}).detach();
|
}).detach();
|
||||||
},
|
},
|
||||||
headlessCheck->isChecked()
|
headlessCheck->isChecked()
|
||||||
@ -91,14 +108,14 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
|
|||||||
buttons->addWidget(startButton);
|
buttons->addWidget(startButton);
|
||||||
buttons->addWidget(stopButton);
|
buttons->addWidget(stopButton);
|
||||||
display->addRow(HEADLESS_MODE, headlessCheck);
|
display->addRow(HEADLESS_MODE, headlessCheck);
|
||||||
auto autoStartButton = new QCheckBox();
|
auto autoStartCheck = new QCheckBox();
|
||||||
autoStartButton->setChecked(settings.value(AUTO_START, false).toBool());
|
autoStartCheck->setChecked(settings.value(AUTO_START, false).toBool());
|
||||||
QObject::connect(autoStartButton, &QCheckBox::clicked, [](bool autoStart) { settings.setValue(AUTO_START, autoStart); });
|
QObject::connect(autoStartCheck, &QCheckBox::clicked, [](bool autoStart) { settings.setValue(AUTO_START, autoStart); });
|
||||||
display->addRow(AUTO_START, autoStartButton);
|
display->addRow(AUTO_START, autoStartCheck);
|
||||||
display->addRow(buttons);
|
display->addRow(buttons);
|
||||||
statusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);
|
statusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);
|
||||||
display->addRow(DEVTOOLS_STATUS, statusLabel);
|
display->addRow(DEVTOOLS_STATUS, statusLabel);
|
||||||
if (autoStartButton->isChecked()) QMetaObject::invokeMethod(startButton, &QPushButton::click, Qt::QueuedConnection);
|
if (autoStartCheck->isChecked()) QMetaObject::invokeMethod(startButton, &QPushButton::click, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DLL_PROCESS_DETACH:
|
case DLL_PROCESS_DETACH:
|
||||||
@ -116,16 +133,20 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text)
|
|||||||
// DevTools can't handle concurrent translations yet
|
// DevTools can't handle concurrent translations yet
|
||||||
static std::mutex translationMutex;
|
static std::mutex translationMutex;
|
||||||
std::scoped_lock lock(translationMutex);
|
std::scoped_lock lock(translationMutex);
|
||||||
DevTools::SendRequest(L"Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/translator#%s/%s/%s"})", translateFrom.Copy(), translateTo.Copy(), Escape(text)));
|
DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/translator#any/%s/%s"})", translateTo.Copy(), Escape(text)));
|
||||||
|
|
||||||
|
if (translateFrom.Copy() != autoDetectLanguage)
|
||||||
|
DevTools::SendRequest("Runtime.evaluate", FormatString(LR"({"expression":"
|
||||||
|
document.querySelector('.lmt__language_select--source').querySelector('button').click(),
|
||||||
|
document.evaluate(`//button[contains(text(),'%s')]`,document.querySelector('.lmt__language_select__menu'),null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue.click()
|
||||||
|
"})", S(std::find_if(languages.begin(), languages.end(), [end = S(translateFrom.Copy())](const QString& language) { return language.endsWith(end); })->split(":")[0])));
|
||||||
|
|
||||||
for (int retry = 0; ++retry < 100; Sleep(100))
|
for (int retry = 0; ++retry < 100; Sleep(100))
|
||||||
if (auto translation = Copy(
|
if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate",
|
||||||
DevTools::SendRequest(L"Runtime.evaluate", LR"({"expression":"document.querySelector('#target-dummydiv').innerHTML","returnByValue":true})")[L"result"][L"value"].String()
|
LR"({"expression":"document.querySelector('#target-dummydiv').innerHTML.trim() ","returnByValue":true})"
|
||||||
)) if (!translation->empty()) return { true, translation.value() };
|
)[L"result"][L"value"].String())) if (!translation->empty()) return { true, translation.value() };
|
||||||
if (auto errorMessage = Copy(
|
if (auto errorMessage = Copy(DevTools::SendRequest("Runtime.evaluate",
|
||||||
DevTools::SendRequest(
|
LR"({"expression":"document.querySelector('div.lmt__system_notification').innerHTML","returnByValue":true})"
|
||||||
L"Runtime.evaluate",
|
)[L"result"][L"value"].String())) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, errorMessage.value()) };
|
||||||
LR"({"expression":"document.querySelector('div.lmt__system_notification').innerHTML","returnByValue":true})"
|
|
||||||
)[L"result"][L"value"].String()
|
|
||||||
)) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, errorMessage.value()) };
|
|
||||||
return { false, TRANSLATION_ERROR };
|
return { false, TRANSLATION_ERROR };
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ QStringList languages
|
|||||||
};
|
};
|
||||||
std::wstring autoDetectLanguage = L"auto";
|
std::wstring autoDetectLanguage = L"auto";
|
||||||
|
|
||||||
bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true;
|
bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true, useFilter = true;
|
||||||
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
|
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
|
||||||
|
|
||||||
std::pair<bool, std::wstring> Translate(const std::wstring& text)
|
std::pair<bool, std::wstring> Translate(const std::wstring& text)
|
||||||
@ -145,34 +145,12 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text)
|
|||||||
if (HttpRequest httpRequest{
|
if (HttpRequest httpRequest{
|
||||||
L"Mozilla/5.0 Textractor",
|
L"Mozilla/5.0 Textractor",
|
||||||
L"translate.google.com",
|
L"translate.google.com",
|
||||||
L"POST",
|
L"GET",
|
||||||
L"/_/TranslateWebserverUi/data/batchexecute?rpcids=MkEWBc",
|
FormatString(L"/m?sl=%s&tl=%s&q=%s", translateFrom.Copy(), translateTo.Copy(), Escape(text)).c_str()
|
||||||
"f.req=" + Escape(WideStringToString(
|
|
||||||
FormatString(LR"([[["MkEWBc","[[\"%s\",\"%s\",\"%s\",true],[null]]",null,"generic"]]])", JSON::Escape((JSON::Escape(text))), translateFrom.Copy(), translateTo.Copy())
|
|
||||||
)),
|
|
||||||
L"Content-Type: application/x-www-form-urlencoded"
|
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
if (auto start = httpRequest.response.find(L"[["); start != std::string::npos)
|
auto start = httpRequest.response.find(L"result-container\">") + 18, end = httpRequest.response.find(L'<', start);
|
||||||
{
|
if (start != end) return { true, HTML::Unescape(httpRequest.response.substr(start, end - start)) };
|
||||||
if (auto blob = Copy(JSON::Parse(httpRequest.response.substr(start))[0][2].String())) if (auto translations = Copy(JSON::Parse(blob.value())[1][0].Array()))
|
|
||||||
{
|
|
||||||
std::wstring translation;
|
|
||||||
if (translations->size() == 1)
|
|
||||||
{
|
|
||||||
if (translations = Copy(translations.value()[0][5].Array()))
|
|
||||||
for (const auto& sentence : translations.value())
|
|
||||||
if (sentence[0].String()) (translation += *sentence[0].String()) += L" ";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (const auto& conjugation : translations.value())
|
|
||||||
if (auto sentence = conjugation[0].String()) if (auto gender = conjugation[2].String()) translation += FormatString(L"%s %s\n", *sentence, *gender);
|
|
||||||
}
|
|
||||||
if (!translation.empty()) return { true, translation };
|
|
||||||
return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, blob.value()) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) };
|
return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) };
|
||||||
}
|
}
|
||||||
else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) };
|
else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) };
|
||||||
|
@ -32,6 +32,36 @@ struct HttpRequest
|
|||||||
std::wstring Escape(const std::wstring& text);
|
std::wstring Escape(const std::wstring& text);
|
||||||
std::string Escape(const std::string& text);
|
std::string Escape(const std::string& text);
|
||||||
|
|
||||||
|
namespace HTML
|
||||||
|
{
|
||||||
|
template <typename C>
|
||||||
|
std::basic_string<C> Unescape(std::basic_string<C> text)
|
||||||
|
{
|
||||||
|
constexpr C
|
||||||
|
lt[] = { '&', 'l', 't', ';' },
|
||||||
|
gt[] = { '&', 'g', 't', ';' },
|
||||||
|
apos1[] = { '&', 'a', 'p', 'o', 's', ';' },
|
||||||
|
apos2[] = { '&', '#', '3', '9', ';' },
|
||||||
|
apos3[] = { '&', '#', 'x', '2', '7', ';' },
|
||||||
|
apos4[] = { '&', '#', 'X', '2', '7', ';' },
|
||||||
|
quot[] = { '&', 'q', 'u', 'o', 't', ';' },
|
||||||
|
amp[] = { '&', 'a', 'm', 'p', ';' };
|
||||||
|
for (int i = 0; i < text.size(); ++i)
|
||||||
|
if (text[i] == '&')
|
||||||
|
for (auto [original, length, replacement] : Array<const C*, size_t, C>{
|
||||||
|
{ lt, std::size(lt), '<' },
|
||||||
|
{ gt, std::size(gt), '>' },
|
||||||
|
{ apos1, std::size(apos1), '\'' },
|
||||||
|
{ apos2, std::size(apos2), '\'' },
|
||||||
|
{ apos3, std::size(apos3), '\'' },
|
||||||
|
{ apos4, std::size(apos4), '\'' },
|
||||||
|
{ quot, std::size(quot), '"' },
|
||||||
|
{ amp, std::size(amp), '&' }
|
||||||
|
}) if (std::char_traits<C>::compare(text.data() + i, original, length) == 0) text.replace(i, length, 1, replacement);
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace JSON
|
namespace JSON
|
||||||
{
|
{
|
||||||
template <typename C>
|
template <typename C>
|
||||||
@ -136,7 +166,7 @@ namespace JSON
|
|||||||
|
|
||||||
if (SkipWhitespace()) return {};
|
if (SkipWhitespace()) return {};
|
||||||
|
|
||||||
static C nullStr[] = { 'n', 'u', 'l', 'l' }, trueStr[] = { 't', 'r', 'u', 'e' }, falseStr[] = { 'f', 'a', 'l', 's', 'e' };
|
constexpr C nullStr[] = { 'n', 'u', 'l', 'l' }, trueStr[] = { 't', 'r', 'u', 'e' }, falseStr[] = { 'f', 'a', 'l', 's', 'e' };
|
||||||
if (ch == nullStr[0])
|
if (ch == nullStr[0])
|
||||||
if (std::char_traits<C>::compare(text.data() + i, nullStr, std::size(nullStr)) == 0) return i += std::size(nullStr), nullptr;
|
if (std::char_traits<C>::compare(text.data() + i, nullStr, std::size(nullStr)) == 0) return i += std::size(nullStr), nullptr;
|
||||||
else return {};
|
else return {};
|
||||||
|
@ -6,7 +6,7 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
|||||||
|
|
||||||
std::vector<int> repeatNumbers(sentence.size() + 1, 0);
|
std::vector<int> repeatNumbers(sentence.size() + 1, 0);
|
||||||
int repeatNumber = 1;
|
int repeatNumber = 1;
|
||||||
wchar_t prevChar = L'\0';
|
wchar_t prevChar = 0;
|
||||||
for (auto nextChar : sentence)
|
for (auto nextChar : sentence)
|
||||||
{
|
{
|
||||||
if (nextChar == prevChar)
|
if (nextChar == prevChar)
|
||||||
|
@ -13,6 +13,7 @@ extern const char* TRANSLATE_SELECTED_THREAD_ONLY;
|
|||||||
extern const char* RATE_LIMIT_ALL_THREADS;
|
extern const char* RATE_LIMIT_ALL_THREADS;
|
||||||
extern const char* RATE_LIMIT_SELECTED_THREAD;
|
extern const char* RATE_LIMIT_SELECTED_THREAD;
|
||||||
extern const char* USE_TRANS_CACHE;
|
extern const char* USE_TRANS_CACHE;
|
||||||
|
extern const char* FILTER_GARBAGE;
|
||||||
extern const char* RATE_LIMIT_TOKEN_COUNT;
|
extern const char* RATE_LIMIT_TOKEN_COUNT;
|
||||||
extern const char* RATE_LIMIT_TOKEN_RESTORE_DELAY;
|
extern const char* RATE_LIMIT_TOKEN_RESTORE_DELAY;
|
||||||
extern const char* MAX_SENTENCE_SIZE;
|
extern const char* MAX_SENTENCE_SIZE;
|
||||||
@ -23,7 +24,7 @@ extern const char* TRANSLATION_PROVIDER;
|
|||||||
extern const char* GET_API_KEY_FROM;
|
extern const char* GET_API_KEY_FROM;
|
||||||
extern QStringList languages;
|
extern QStringList languages;
|
||||||
extern std::wstring autoDetectLanguage;
|
extern std::wstring autoDetectLanguage;
|
||||||
extern bool translateSelectedOnly, rateLimitAll, rateLimitSelected, useCache;
|
extern bool translateSelectedOnly, rateLimitAll, rateLimitSelected, useCache, useFilter;
|
||||||
extern int tokenCount, tokenRestoreDelay, maxSentenceSize;
|
extern int tokenCount, tokenRestoreDelay, maxSentenceSize;
|
||||||
std::pair<bool, std::wstring> Translate(const std::wstring& text);
|
std::pair<bool, std::wstring> Translate(const std::wstring& text);
|
||||||
|
|
||||||
@ -84,6 +85,7 @@ public:
|
|||||||
{ rateLimitAll, RATE_LIMIT_ALL_THREADS },
|
{ rateLimitAll, RATE_LIMIT_ALL_THREADS },
|
||||||
{ rateLimitSelected, RATE_LIMIT_SELECTED_THREAD },
|
{ rateLimitSelected, RATE_LIMIT_SELECTED_THREAD },
|
||||||
{ useCache, USE_TRANS_CACHE },
|
{ useCache, USE_TRANS_CACHE },
|
||||||
|
{ useFilter, FILTER_GARBAGE }
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
value = settings.value(label, value).toBool();
|
value = settings.value(label, value).toBool();
|
||||||
@ -165,7 +167,7 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
|||||||
Synchronized<std::vector<DWORD>> tokens;
|
Synchronized<std::vector<DWORD>> tokens;
|
||||||
} rateLimiter;
|
} rateLimiter;
|
||||||
|
|
||||||
auto StripWhitespace = [](std::wstring& text)
|
auto Trim = [](std::wstring& text)
|
||||||
{
|
{
|
||||||
text.erase(text.begin(), std::find_if_not(text.begin(), text.end(), iswspace));
|
text.erase(text.begin(), std::find_if_not(text.begin(), text.end(), iswspace));
|
||||||
text.erase(std::find_if_not(text.rbegin(), text.rend(), iswspace).base(), text.end());
|
text.erase(std::find_if_not(text.rbegin(), text.rend(), iswspace).base(), text.end());
|
||||||
@ -173,7 +175,11 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
|||||||
|
|
||||||
bool cache = false;
|
bool cache = false;
|
||||||
std::wstring translation;
|
std::wstring translation;
|
||||||
StripWhitespace(sentence);
|
if (useFilter)
|
||||||
|
{
|
||||||
|
Trim(sentence);
|
||||||
|
sentence.erase(std::find_if(sentence.begin(), sentence.end(), [](wchar_t ch) { return ch < ' ' && ch != '\n'; }), sentence.end());
|
||||||
|
}
|
||||||
if (useCache)
|
if (useCache)
|
||||||
{
|
{
|
||||||
auto translationCache = ::translationCache.Acquire();
|
auto translationCache = ::translationCache.Acquire();
|
||||||
@ -182,7 +188,7 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
|||||||
if (translation.empty() && (!translateSelectedOnly || sentenceInfo["current select"]))
|
if (translation.empty() && (!translateSelectedOnly || sentenceInfo["current select"]))
|
||||||
if (rateLimiter.Request() || !rateLimitAll || (!rateLimitSelected && sentenceInfo["current select"])) std::tie(cache, translation) = Translate(sentence);
|
if (rateLimiter.Request() || !rateLimitAll || (!rateLimitSelected && sentenceInfo["current select"])) std::tie(cache, translation) = Translate(sentence);
|
||||||
else translation = TOO_MANY_TRANS_REQUESTS;
|
else translation = TOO_MANY_TRANS_REQUESTS;
|
||||||
StripWhitespace(translation);
|
if (useFilter) Trim(translation);
|
||||||
if (cache) translationCache->try_emplace(sentence, translation);
|
if (cache) translationCache->try_emplace(sentence, translation);
|
||||||
if (cache && translationCache->size() > savedSize + 50) SaveCache();
|
if (cache && translationCache->size() > savedSize + 50) SaveCache();
|
||||||
|
|
||||||
|
11
text.cpp
11
text.cpp
@ -57,7 +57,7 @@ Negatives for data_offset/split_offset refer to registers
|
|||||||
-C for RAX, -14 for RBX, -1C for RCX, -24 for RDX, and so on for RSP, RBP, RSI, RDI, R8-R15
|
-C for RAX, -14 for RBX, -1C for RCX, -24 for RDX, and so on for RSP, RBP, RSI, RDI, R8-R15
|
||||||
* means dereference pointer+deref_offset)";
|
* means dereference pointer+deref_offset)";
|
||||||
const char* SAVE_SETTINGS = u8"Save settings";
|
const char* SAVE_SETTINGS = u8"Save settings";
|
||||||
const char* EXTEN_WINDOW_INSTRUCTIONS = u8R"(Right click the list to add/remove extensions
|
const char* EXTEN_WINDOW_INSTRUCTIONS = u8R"(Right click the list to add or remove extensions
|
||||||
Drag and drop extensions within the list to reorder them
|
Drag and drop extensions within the list to reorder them
|
||||||
(Extensions are used from top to bottom: order DOES matter))";
|
(Extensions are used from top to bottom: order DOES matter))";
|
||||||
const char* ADD_EXTENSION = u8"Add extension";
|
const char* ADD_EXTENSION = u8"Add extension";
|
||||||
@ -97,14 +97,14 @@ const char* MAX_HISTORY_SIZE = u8"Max history size";
|
|||||||
const char* CONFIG_JP_LOCALE = u8"Launch with JP locale";
|
const char* CONFIG_JP_LOCALE = u8"Launch with JP locale";
|
||||||
const wchar_t* CONSOLE = L"Console";
|
const wchar_t* CONSOLE = L"Console";
|
||||||
const wchar_t* CLIPBOARD = L"Clipboard";
|
const wchar_t* CLIPBOARD = L"Clipboard";
|
||||||
const wchar_t* ABOUT = L"Textractor " ARCH L" v" VERSION LR"( made by me: Artikash (email: akashmozumdar@gmail.com)
|
const wchar_t* ABOUT = L"Textractor " ARCH L" v" VERSION LR"( made by Artikash (email: akashmozumdar@gmail.com)
|
||||||
Project homepage: https://github.com/Artikash/Textractor
|
Project homepage: https://github.com/Artikash/Textractor
|
||||||
Tutorial video: https://tinyurl.com/textractor-tutorial
|
Tutorial video: https://tinyurl.com/textractor-tutorial
|
||||||
FAQ: https://github.com/Artikash/Textractor/wiki/FAQ
|
FAQ: https://github.com/Artikash/Textractor/wiki/FAQ
|
||||||
Please contact me with any problems, feature requests, or questions relating to Textractor
|
Please contact Artikash with any problems, feature requests, or questions relating to Textractor
|
||||||
You can do so via the project homepage (issues section) or via email
|
You can do so via the project homepage (issues section) or via email
|
||||||
Source code available under GPLv3 at project homepage
|
Source code available under GPLv3 at project homepage
|
||||||
If you like this project, please tell everyone about it :))";
|
If you like this project, please tell everyone it's time to put down AGTH :))";
|
||||||
const wchar_t* CL_OPTIONS = LR"(usage: Textractor [-p{process id|"process name"}]...
|
const wchar_t* CL_OPTIONS = LR"(usage: Textractor [-p{process id|"process name"}]...
|
||||||
example: Textractor -p4466 -p"My Game.exe" tries to inject processes with id 4466 or with name My Game.exe)";
|
example: Textractor -p4466 -p"My Game.exe" tries to inject processes with id 4466 or with name My Game.exe)";
|
||||||
const wchar_t* UPDATE_AVAILABLE = L"Update available: download it from https://github.com/Artikash/Textractor/releases";
|
const wchar_t* UPDATE_AVAILABLE = L"Update available: download it from https://github.com/Artikash/Textractor/releases";
|
||||||
@ -136,6 +136,7 @@ const char* HIJACK_ERROR = u8"Textractor: Hijack ERROR";
|
|||||||
const char* COULD_NOT_FIND = u8"Textractor: could not find text";
|
const char* COULD_NOT_FIND = u8"Textractor: could not find text";
|
||||||
const char* TRANSLATE_TO = u8"Translate to";
|
const char* TRANSLATE_TO = u8"Translate to";
|
||||||
const char* TRANSLATE_FROM = u8"Translate from";
|
const char* TRANSLATE_FROM = u8"Translate from";
|
||||||
|
const char* FILTER_GARBAGE = u8"Filter garbage characters";
|
||||||
const char* TRANSLATE_SELECTED_THREAD_ONLY = u8"Translate selected text thread only";
|
const char* TRANSLATE_SELECTED_THREAD_ONLY = u8"Translate selected text thread only";
|
||||||
const char* RATE_LIMIT_ALL_THREADS = u8"Rate limit all text threads";
|
const char* RATE_LIMIT_ALL_THREADS = u8"Rate limit all text threads";
|
||||||
const char* RATE_LIMIT_SELECTED_THREAD = u8"Rate limit selected text thread";
|
const char* RATE_LIMIT_SELECTED_THREAD = u8"Rate limit selected text thread";
|
||||||
@ -146,7 +147,7 @@ const wchar_t* TOO_MANY_TRANS_REQUESTS = L"Rate limit exceeded: refuse to make m
|
|||||||
const wchar_t* TRANSLATION_ERROR = L"Error while translating";
|
const wchar_t* TRANSLATION_ERROR = L"Error while translating";
|
||||||
const char* USE_PREV_SENTENCE_CONTEXT = u8"Use previous sentence as context";
|
const char* USE_PREV_SENTENCE_CONTEXT = u8"Use previous sentence as context";
|
||||||
const char* API_KEY = u8"API key";
|
const char* API_KEY = u8"API key";
|
||||||
const char* CHROME_LOCATION = "Google Chrome file location";
|
const char* CHROME_LOCATION = u8"Google Chrome file location";
|
||||||
const char* START_DEVTOOLS = u8"Start DevTools";
|
const char* START_DEVTOOLS = u8"Start DevTools";
|
||||||
const char* STOP_DEVTOOLS = u8"Stop DevTools";
|
const char* STOP_DEVTOOLS = u8"Stop DevTools";
|
||||||
const char* HEADLESS_MODE = u8"Headless mode";
|
const char* HEADLESS_MODE = u8"Headless mode";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user