Match and mux in dropped external subtitles
Drag in subtitle files alongside .mkv files and they're matched to the right video by episode code (S04E06, zero-padding and separator insensitive, plus 4x06 as a fallback) and muxed in as extra subtitle tracks. Any subtitle format mkvmerge accepts is recognized: SRT, (S)SA, VobSub, WebVTT, PGS/SUP, USF, SAMI, TTML/DFXP, STL, Kate. Matching works regardless of drop order — subtitles dropped before their video are held pending until a matching video shows up, and vice versa. Language, forced, and SDH flags are guessed from common filename tokens (e.g. "eng", "forced", "sdh") to set the muxed track's language/name/flags; unmatched subtitles are reported in the status bar.
This commit is contained in:
+43
-4
@@ -258,12 +258,13 @@ public class MkvService
|
||||
IReadOnlyList<int> videoKeepIds,
|
||||
IReadOnlyList<int> audioKeepIds,
|
||||
IReadOnlyList<int> subtitleKeepIds,
|
||||
IReadOnlyList<ExternalSubtitleTrack>? externalSubtitles = null,
|
||||
IProgress<ProcessingProgress>? progress = null,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
return StripTracksInternalAsync(
|
||||
filePath, Path.GetFileName(filePath), outputFilePath,
|
||||
videoKeepIds, audioKeepIds, subtitleKeepIds,
|
||||
videoKeepIds, audioKeepIds, subtitleKeepIds, externalSubtitles,
|
||||
progress, ct);
|
||||
}
|
||||
|
||||
@@ -281,6 +282,7 @@ public class MkvService
|
||||
null, // null = keep all video
|
||||
scan.AudioKeepIds,
|
||||
scan.SubtitleKeepIds,
|
||||
null,
|
||||
progress, ct);
|
||||
}
|
||||
|
||||
@@ -386,6 +388,7 @@ public class MkvService
|
||||
IReadOnlyList<int>? videoKeepIds,
|
||||
IReadOnlyList<int>? audioKeepIds,
|
||||
IReadOnlyList<int>? subtitleKeepIds,
|
||||
IReadOnlyList<ExternalSubtitleTrack>? externalSubtitles,
|
||||
IProgress<ProcessingProgress>? progress,
|
||||
CancellationToken ct)
|
||||
{
|
||||
@@ -422,16 +425,52 @@ public class MkvService
|
||||
AddTrackTypeArgs(psi, "--audio-tracks", "--no-audio", audioKeepIds);
|
||||
AddTrackTypeArgs(psi, "--subtitle-tracks", "--no-subtitles", subtitleKeepIds);
|
||||
|
||||
// The one subtitle track the user chose to keep is always flagged
|
||||
// as the default subtitle track in the output.
|
||||
// Only one subtitle track in the whole output should ever be
|
||||
// flagged default. If any external subtitle is marked default,
|
||||
// it takes priority over the kept internal subtitle track.
|
||||
bool hasExternalDefault = externalSubtitles != null && externalSubtitles.Any(s => s.IsDefault);
|
||||
|
||||
if (subtitleKeepIds != null && subtitleKeepIds.Count == 1)
|
||||
{
|
||||
psi.ArgumentList.Add("--default-track-flag");
|
||||
psi.ArgumentList.Add($"{subtitleKeepIds[0]}:yes");
|
||||
psi.ArgumentList.Add(hasExternalDefault ? $"{subtitleKeepIds[0]}:no" : $"{subtitleKeepIds[0]}:yes");
|
||||
}
|
||||
|
||||
psi.ArgumentList.Add(filePath);
|
||||
|
||||
// Append each matched external subtitle file as its own input,
|
||||
// with per-file options (language/name/flags) scoped to it.
|
||||
if (externalSubtitles != null)
|
||||
{
|
||||
foreach (var sub in externalSubtitles)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(sub.Language) && !string.Equals(sub.Language, "und", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
psi.ArgumentList.Add("--language");
|
||||
psi.ArgumentList.Add($"0:{sub.Language}");
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(sub.TrackName))
|
||||
{
|
||||
psi.ArgumentList.Add("--track-name");
|
||||
psi.ArgumentList.Add($"0:{sub.TrackName}");
|
||||
}
|
||||
if (sub.Forced)
|
||||
{
|
||||
psi.ArgumentList.Add("--forced-display-flag");
|
||||
psi.ArgumentList.Add("0:yes");
|
||||
}
|
||||
if (sub.HearingImpaired)
|
||||
{
|
||||
psi.ArgumentList.Add("--hearing-impaired-flag");
|
||||
psi.ArgumentList.Add("0:yes");
|
||||
}
|
||||
psi.ArgumentList.Add("--default-track-flag");
|
||||
psi.ArgumentList.Add(sub.IsDefault ? "0:yes" : "0:no");
|
||||
|
||||
psi.ArgumentList.Add(sub.FilePath);
|
||||
}
|
||||
}
|
||||
|
||||
using var process = new Process { StartInfo = psi };
|
||||
process.Start();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user