feat: translate subtitles with an OpenAI-compatible LLM

This commit is contained in:
tri committed 2026-10-27 22:43:03 +07:00
1 parent 48cac86d60
commit 4665bf632a
21 files changed
+1877 -5

No files matched your search

+121
View File
@@ -0,0 +1,121 @@
#include "apikeystore.h"
#include <QCoroSignal>
#include <QPointer>
#include <qt6keychain/keychain.h>
#include <memory>
using namespace Qt::Literals::StringLiterals;
namespace
{
const QString Service = u"dev.trivk.qtpotato"_s;
const QString Key = u"translation-api-key"_s;
struct JobDeleter {
void operator()(QKeychain::Job *job) const
{
job->deleteLater();
}
};
template<typename T>
std::unique_ptr<T, JobDeleter> makeJob()
{
auto job = std::unique_ptr<T, JobDeleter>(new T(Service));
job->setAutoDelete(false);
job->setKey(Key);
return job;
}
}
ApiKeyStore::ApiKeyStore(QObject *parent)
: QObject(parent)
{
refresh();
}
bool ApiKeyStore::hasKey() const
{
return m_hasKey;
}
QString ApiKeyStore::error() const
{
return m_error;
}
void ApiKeyStore::setKey(const QString &key)
{
store(key.trimmed());
}
QCoro::Task<ApiKeyStore::ReadResult> ApiKeyStore::readKey()
{
auto job = makeJob<QKeychain::ReadPasswordJob>();
job->start();
co_await qCoro(job.get(), &QKeychain::Job::finished);
if (job->error() == QKeychain::EntryNotFound) {
co_return ReadResult{};
}
if (job->error() != QKeychain::NoError) {
co_return ReadResult{{}, job->errorString()};
}
co_return ReadResult{job->textData(), {}};
}
QCoro::Task<> ApiKeyStore::refresh()
{
QPointer<ApiKeyStore> self(this);
const ReadResult result = co_await readKey();
if (!self) {
co_return;
}
setHasKey(!result.key.isEmpty());
setError(result.error);
}
QCoro::Task<> ApiKeyStore::store(QString key)
{
QPointer<ApiKeyStore> self(this);
std::unique_ptr<QKeychain::Job, JobDeleter> job;
if (key.isEmpty()) {
job = makeJob<QKeychain::DeletePasswordJob>();
} else {
auto write = makeJob<QKeychain::WritePasswordJob>();
write->setTextData(key);
job.reset(write.release());
}
job->start();
co_await qCoro(job.get(), &QKeychain::Job::finished);
if (!self) {
co_return;
}
if (job->error() != QKeychain::NoError && job->error() != QKeychain::EntryNotFound) {
setError(job->errorString());
co_return;
}
setError({});
setHasKey(!key.isEmpty());
}
void ApiKeyStore::setHasKey(bool hasKey)
{
if (hasKey != m_hasKey) {
m_hasKey = hasKey;
Q_EMIT hasKeyChanged();
}
}
void ApiKeyStore::setError(const QString &error)
{
if (error != m_error) {
m_error = error;
Q_EMIT errorChanged();
}
}