feat(translate): keep translated subtitles next to the video so they load on reopen (#14)
Co-authored-by: Tri Vo Khac <git@trivk.dev>
This commit was merged in pull request #14.
This commit is contained in:
1 parent
b34a01a92f
commit
76b12d6632
13 files changed
+716
-65
No files matched your search
+69
-5
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QLocale>
|
||||
#include <QSaveFile>
|
||||
#include <QStandardPaths>
|
||||
@@ -44,6 +45,38 @@ QList<TrackSelection::Track> selectionTracks(const QVariantList &tracks)
|
||||
return result;
|
||||
}
|
||||
|
||||
QString subtitlePath(const QVariantList &tracks, int id)
|
||||
{
|
||||
for (const QVariant &entry : tracks) {
|
||||
const QVariantMap track = entry.toMap();
|
||||
if (track.value(QStringLiteral("id")).toInt() == id) {
|
||||
return track.value(QStringLiteral("externalFilename")).toString();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
int subtitleIdForPath(const QVariantList &tracks, const QString &path)
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
for (const QVariant &entry : tracks) {
|
||||
const QVariantMap track = entry.toMap();
|
||||
if (track.value(QStringLiteral("externalFilename")).toString() == path) {
|
||||
return track.value(QStringLiteral("id")).toInt();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// "movie.abc123.vi.srt" -> "vi", the way mpv tags subtitles it finds next to a video.
|
||||
QString filenameLanguage(const QString &path)
|
||||
{
|
||||
const QStringList parts = QFileInfo(path).completeBaseName().split(QLatin1Char('.'));
|
||||
return parts.size() > 1 ? parts.constLast() : QString();
|
||||
}
|
||||
|
||||
QString trackLanguage(const QVariantList &tracks, int id)
|
||||
{
|
||||
for (const QVariant &entry : tracks) {
|
||||
@@ -112,7 +145,7 @@ MpvItem::MpvItem(QQuickItem *parent)
|
||||
// Before the first file, so its options apply from the start.
|
||||
applyUserConfig();
|
||||
if (m_pendingUrl.isValid()) {
|
||||
loadFile(std::exchange(m_pendingUrl, {}));
|
||||
loadFile(std::exchange(m_pendingUrl, {}), QString(m_openingTranslatedPath), QString(m_openingTranslatedTitle));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -156,12 +189,18 @@ bool MpvItem::isFileLoaded() const
|
||||
return m_fileLoaded;
|
||||
}
|
||||
|
||||
void MpvItem::loadFile(const QUrl &url)
|
||||
void MpvItem::loadFile(const QUrl &url, const QString &translatedSubtitle, const QString &translatedTitle)
|
||||
{
|
||||
if (!url.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_translatedPaths.clear();
|
||||
m_openingTranslatedPath = translatedSubtitle;
|
||||
m_openingTranslatedTitle = translatedTitle;
|
||||
if (!translatedSubtitle.isEmpty()) {
|
||||
m_translatedPaths.append(translatedSubtitle);
|
||||
}
|
||||
m_currentUrl = url;
|
||||
Q_EMIT currentUrlChanged();
|
||||
|
||||
@@ -355,6 +394,9 @@ void MpvItem::showTranslatedSubtitleFile(const QUrl &url, const QString &title)
|
||||
return;
|
||||
}
|
||||
const QString path = localPathOrUrl(url);
|
||||
if (!m_translatedPaths.contains(path)) {
|
||||
m_translatedPaths.append(path);
|
||||
}
|
||||
for (const QVariant &entry : std::as_const(m_subtitleTracks)) {
|
||||
const QVariantMap track = entry.toMap();
|
||||
if (track.value(QStringLiteral("externalFilename")).toString() == path) {
|
||||
@@ -370,11 +412,22 @@ void MpvItem::showTranslatedSubtitleFile(const QUrl &url, const QString &title)
|
||||
|
||||
int MpvItem::translationSourceId() const
|
||||
{
|
||||
QVariantList sources;
|
||||
for (const QVariant &entry : std::as_const(m_subtitleTracks)) {
|
||||
if (!isTranslatedSubtitle(entry.toMap().value(QStringLiteral("id")).toInt())) {
|
||||
sources.append(entry);
|
||||
}
|
||||
}
|
||||
// English first: it is the usual original, and dubbed releases often default to
|
||||
// subtitles translated from it.
|
||||
return TrackSelection::pickTranslationSource(selectionTracks(m_subtitleTracks),
|
||||
return TrackSelection::pickTranslationSource(selectionTracks(sources),
|
||||
{QStringLiteral("en"), trackLanguage(m_audioTracks, m_audioId)},
|
||||
m_subtitleId);
|
||||
isTranslatedSubtitle(m_subtitleId) ? 0 : m_subtitleId);
|
||||
}
|
||||
|
||||
bool MpvItem::isTranslatedSubtitle(int id) const
|
||||
{
|
||||
return id > 0 && m_translatedPaths.contains(subtitlePath(m_subtitleTracks, id));
|
||||
}
|
||||
|
||||
void MpvItem::selectInitialTracks()
|
||||
@@ -382,6 +435,16 @@ void MpvItem::selectInitialTracks()
|
||||
// The observed track-list may still describe the previous file; read it directly.
|
||||
updateTracks(getProperty(QStringLiteral("track-list")).toList());
|
||||
|
||||
// mpv finds a translation next to the video by itself (sub-auto); one kept in the
|
||||
// cache is added here, before choosing, so it can be chosen.
|
||||
const QString translated = std::exchange(m_openingTranslatedPath, {});
|
||||
int translationId = subtitleIdForPath(m_subtitleTracks, translated);
|
||||
if (!translated.isEmpty() && translationId == 0 && QFileInfo::exists(translated)) {
|
||||
commandBlocking(QStringList{QStringLiteral("sub-add"), translated, QStringLiteral("auto"), m_openingTranslatedTitle, filenameLanguage(translated)});
|
||||
updateTracks(getProperty(QStringLiteral("track-list")).toList());
|
||||
translationId = subtitleIdForPath(m_subtitleTracks, translated);
|
||||
}
|
||||
|
||||
const int audioId = TrackSelection::pickAudio(selectionTracks(m_audioTracks), m_preferredAudioLanguages, m_audioId);
|
||||
if (audioId != m_audioId) {
|
||||
setAudioId(audioId);
|
||||
@@ -393,7 +456,8 @@ void MpvItem::selectInitialTracks()
|
||||
m_preferredSubtitleLanguages,
|
||||
fallbackLanguages,
|
||||
m_subtitleId,
|
||||
m_autoSelectSubtitle);
|
||||
m_autoSelectSubtitle,
|
||||
translationId);
|
||||
if (subtitleId != m_subtitleId) {
|
||||
setSubtitleId(subtitleId);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user