feat(translate): translate a whole playlist so the next episodes are ready (#16)
Co-authored-by: Tri Vo Khac <git@trivk.dev>
This commit was merged in pull request #16.
This commit is contained in:
1 parent
ad50230829
commit
bcfc2f3f6a
13 files changed
+480
-61
No files matched your search
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
|
||||
@@ -37,6 +38,9 @@ class TranslationService : public QObject
|
||||
Q_PROPERTY(QString glossaryEpisode READ glossaryEpisode NOTIFY glossaryChanged)
|
||||
Q_PROPERTY(QStringList glossaryEpisodes READ glossaryEpisodes NOTIFY glossaryChanged)
|
||||
Q_PROPERTY(QString glossaryError READ glossaryError NOTIFY glossaryErrorChanged)
|
||||
// The video playing. Its translations are shown when ready; others are only saved.
|
||||
// Opening another video stops a single translation but not a playlist one.
|
||||
Q_PROPERTY(QUrl currentVideo READ currentVideo WRITE setCurrentVideo NOTIFY currentVideoChanged)
|
||||
|
||||
public:
|
||||
explicit TranslationService(QObject *parent = nullptr);
|
||||
@@ -54,9 +58,15 @@ public:
|
||||
QString glossaryEpisode() const;
|
||||
QStringList glossaryEpisodes() const;
|
||||
QString glossaryError() const;
|
||||
QUrl currentVideo() const;
|
||||
void setCurrentVideo(const QUrl &url);
|
||||
|
||||
// \a track is an entry of MpvItem::subtitleTracks.
|
||||
Q_INVOKABLE void translate(const QUrl &videoUrl, const QVariantMap &track, const QString &mediaTitle);
|
||||
// Translates every video in \a urls that has no saved translation yet, one after
|
||||
// another, starting at the current video. The current one uses \a currentTrack when
|
||||
// set; the others use the embedded subtitle findTranslationSource() picks.
|
||||
Q_INVOKABLE void translatePlaylist(const QList<QUrl> &urls, const QVariantMap ¤tTrack, const QString ¤tTitle);
|
||||
Q_INVOKABLE void cancel();
|
||||
// Asks the API configured in the settings for its models.
|
||||
Q_INVOKABLE void fetchModels();
|
||||
@@ -83,12 +93,46 @@ Q_SIGNALS:
|
||||
void glossaryChanged();
|
||||
void glossaryErrorChanged();
|
||||
void translated(const QUrl &subtitleUrl, bool fromCache);
|
||||
void currentVideoChanged();
|
||||
// A playlist translation ended without being cancelled. Problems are in errorText.
|
||||
void playlistTranslated(int translatedCount, int alreadyTranslatedCount, int problemCount);
|
||||
|
||||
private:
|
||||
struct Job;
|
||||
struct Batches;
|
||||
// Where translateVideo() left a translation, or why it could not make one.
|
||||
struct Outcome {
|
||||
QString cacheFile;
|
||||
QString suffix;
|
||||
QString configKey;
|
||||
bool fromCache = false;
|
||||
QString error;
|
||||
// The error is about this video's subtitles; other videos may still work.
|
||||
bool skippable = false;
|
||||
|
||||
QCoro::Task<> run(QUrl videoUrl, QVariantMap track);
|
||||
static Outcome ready(const QString &cacheFile, const QString &suffix, const QString &configKey, bool fromCache)
|
||||
{
|
||||
Outcome outcome;
|
||||
outcome.cacheFile = cacheFile;
|
||||
outcome.suffix = suffix;
|
||||
outcome.configKey = configKey;
|
||||
outcome.fromCache = fromCache;
|
||||
return outcome;
|
||||
}
|
||||
static Outcome failed(const QString &error, bool skippable = false)
|
||||
{
|
||||
Outcome outcome;
|
||||
outcome.error = error;
|
||||
outcome.skippable = skippable;
|
||||
return outcome;
|
||||
}
|
||||
};
|
||||
|
||||
QCoro::Task<> run(std::shared_ptr<Job> job, QUrl videoUrl, QVariantMap track);
|
||||
QCoro::Task<> runPlaylist(std::shared_ptr<Job> job, QList<QUrl> urls, QVariantMap currentTrack, QString currentTitle);
|
||||
// Reads the settings and the API key for \a job; nothing (after failing it) when they are not usable.
|
||||
QCoro::Task<std::optional<LlmClient::Config>> startJob(std::shared_ptr<Job> job);
|
||||
QCoro::Task<Outcome> translateVideo(std::shared_ptr<Job> job, LlmClient::Config config, QUrl videoUrl, QVariantMap track);
|
||||
QCoro::Task<> runFetchModels();
|
||||
// Translates \a lines, retrying failures and asking again for lines the model skipped.
|
||||
QCoro::Task<LlmClient::BatchResult> translateLines(std::shared_ptr<Job> job, QStringList lines, QStringList context);
|
||||
@@ -100,8 +144,14 @@ private:
|
||||
// something is missing.
|
||||
QCoro::Task<std::pair<LlmClient::Config, QString>> loadConfig();
|
||||
void fail(const std::shared_ptr<Job> &job, const QString &error);
|
||||
// Saves the translation in \a cacheFile where players find it and announces it.
|
||||
void finish(const std::shared_ptr<Job> &job, const QUrl &videoUrl, const QString &suffix, const QString &configKey, const QString &cacheFile, bool fromCache);
|
||||
// Saves the translation where players find it and, for the current video, shows it.
|
||||
// Returns an error message on failure.
|
||||
QString publish(const std::shared_ptr<Job> &job, const QUrl &videoUrl, const Outcome &outcome);
|
||||
void endJob(const std::shared_ptr<Job> &job);
|
||||
// In a playlist, prefixes the episode number and counts finished episodes in the progress.
|
||||
void setJobStatus(const Job &job, const QString &text);
|
||||
void setJobProgress(const Job &job, double episodeProgress);
|
||||
void setLinesStatus(const Job &job, qsizetype done, qsizetype total);
|
||||
|
||||
void setBusy(bool busy);
|
||||
void setProgress(double progress);
|
||||
@@ -122,4 +172,5 @@ private:
|
||||
GlossaryStore::Glossary m_glossary;
|
||||
QString m_glossaryEpisode;
|
||||
QString m_lastTranslationPath;
|
||||
QUrl m_currentVideo;
|
||||
};
|
||||
Reference in new issue
Block a user