feat(translate): translate a whole playlist
This commit is contained in:
1 parent
ad50230829
commit
d15cab5532
13 files changed
+480
-61
No files matched your search
@@ -0,0 +1,97 @@
|
||||
// SPDX-FileCopyrightText: 2026 Tri Vo Khac
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "subtitleextractor.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
#include <QStandardPaths>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTest>
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
class SubtitleExtractorTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
// Makes \a name in \a dir with a German audio track plus \a subtitleLanguages as
|
||||
// SRT subtitle tracks, in that order.
|
||||
static QString makeSample(const QTemporaryDir &dir, const QString &name, const QStringList &subtitleLanguages)
|
||||
{
|
||||
const QString ffmpeg = QStandardPaths::findExecutable(u"ffmpeg"_s);
|
||||
const QString srt = dir.filePath(u"line.srt"_s);
|
||||
QFile file(srt);
|
||||
if (!file.open(QIODevice::WriteOnly) || file.write("1\n00:00:00,000 --> 00:00:01,000\nHello\n") < 0) {
|
||||
return {};
|
||||
}
|
||||
file.close();
|
||||
|
||||
QStringList args{u"-v"_s, u"error"_s, u"-f"_s, u"lavfi"_s, u"-i"_s, u"sine=duration=1"_s};
|
||||
for (qsizetype i = 0; i < subtitleLanguages.size(); ++i) {
|
||||
args << u"-i"_s << srt;
|
||||
}
|
||||
args << u"-map"_s << u"0:a"_s << u"-metadata:s:a:0"_s << u"language=ger"_s;
|
||||
for (qsizetype i = 0; i < subtitleLanguages.size(); ++i) {
|
||||
args << u"-map"_s << u"%1:s"_s.arg(i + 1) << u"-metadata:s:s:%1"_s.arg(i) << u"language=%1"_s.arg(subtitleLanguages.at(i));
|
||||
}
|
||||
const QString path = dir.filePath(name);
|
||||
args << u"-c:s"_s << u"srt"_s << path;
|
||||
QProcess process;
|
||||
process.start(ffmpeg, args);
|
||||
if (!process.waitForFinished() || process.exitCode() != 0) {
|
||||
qWarning() << process.readAllStandardError();
|
||||
return {};
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase()
|
||||
{
|
||||
if (QStandardPaths::findExecutable(u"ffmpeg"_s).isEmpty()) {
|
||||
QSKIP("ffmpeg is needed to make sample files");
|
||||
}
|
||||
}
|
||||
|
||||
void findTranslationSourcePrefersEnglish()
|
||||
{
|
||||
QTemporaryDir dir;
|
||||
const QString path = makeSample(dir, u"episode.mkv"_s, {u"fre"_s, u"eng"_s});
|
||||
QVERIFY(!path.isEmpty());
|
||||
const Subtitles::SourceResult result = Subtitles::findTranslationSource(path);
|
||||
QVERIFY2(result.error.isEmpty(), qPrintable(result.error));
|
||||
// Stream 0 is the audio, 1 French, 2 English.
|
||||
QCOMPARE(result.streamIndex, 2);
|
||||
|
||||
std::atomic_bool cancelled = false;
|
||||
const Subtitles::ExtractResult cues = Subtitles::extractEmbedded(path, result.streamIndex, cancelled);
|
||||
QCOMPARE(cues.cues.size(), 1);
|
||||
QCOMPARE(cues.cues.first().text, u"Hello"_s);
|
||||
}
|
||||
|
||||
void findTranslationSourceFallsBackToTheAudioLanguage()
|
||||
{
|
||||
QTemporaryDir dir;
|
||||
const QString path = makeSample(dir, u"episode.mkv"_s, {u"fre"_s, u"ger"_s});
|
||||
QVERIFY(!path.isEmpty());
|
||||
QCOMPARE(Subtitles::findTranslationSource(path).streamIndex, 2);
|
||||
}
|
||||
|
||||
void findTranslationSourceWithoutSubtitles()
|
||||
{
|
||||
QTemporaryDir dir;
|
||||
const QString path = makeSample(dir, u"episode.mkv"_s, {});
|
||||
QVERIFY(!path.isEmpty());
|
||||
const Subtitles::SourceResult result = Subtitles::findTranslationSource(path);
|
||||
QVERIFY(result.error.isEmpty());
|
||||
QCOMPARE(result.streamIndex, -1);
|
||||
|
||||
QVERIFY(!Subtitles::findTranslationSource(dir.filePath(u"missing.mkv"_s)).error.isEmpty());
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(SubtitleExtractorTest)
|
||||
|
||||
#include "subtitleextractortest.moc"
|
||||
Reference in new issue
Block a user