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,6 +43,19 @@ public class QueuedFileItem : INotifyPropertyChanged
|
||||
public string FileSizeDisplay { get; }
|
||||
public ObservableCollection<TrackItem> Tracks { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// External subtitle files (dropped separately and matched to this file by
|
||||
/// episode code, e.g. S04E06) that will be muxed in as additional
|
||||
/// subtitle tracks when this file is processed.
|
||||
/// </summary>
|
||||
public ObservableCollection<ExternalSubtitleTrack> ExternalSubtitles { get; } = new();
|
||||
|
||||
public bool HasExternalSubtitles => ExternalSubtitles.Count > 0;
|
||||
|
||||
public string ExternalSubtitlesTooltip => ExternalSubtitles.Count == 0
|
||||
? string.Empty
|
||||
: "External subtitles to mux in:\n" + string.Join("\n", ExternalSubtitles.Select(s => $"- {s.FileName} ({s.LanguageDisplay})"));
|
||||
|
||||
public string Status
|
||||
{
|
||||
get => _status;
|
||||
@@ -71,7 +84,12 @@ public class QueuedFileItem : INotifyPropertyChanged
|
||||
int audioSelected = Tracks.Count(t => t.Type == "audio" && t.Copy);
|
||||
int subtitleSelected = Tracks.Count(t => t.Type == "subtitles" && t.Copy);
|
||||
|
||||
return audioSelected == 1 && subtitleSelected == 1
|
||||
// A subtitle requirement is met either by picking exactly one
|
||||
// internal track, or by having at least one matched external
|
||||
// subtitle file (which can also be combined with one internal track).
|
||||
bool subtitleOk = subtitleSelected == 1 || (subtitleSelected == 0 && ExternalSubtitles.Count > 0);
|
||||
|
||||
return audioSelected == 1 && subtitleOk
|
||||
? FileLoadState.Correct
|
||||
: FileLoadState.Incorrect;
|
||||
}
|
||||
@@ -87,6 +105,7 @@ public class QueuedFileItem : INotifyPropertyChanged
|
||||
FilePath = filePath;
|
||||
FileSizeDisplay = FormatFileSize(filePath);
|
||||
Tracks.CollectionChanged += OnTracksChanged;
|
||||
ExternalSubtitles.CollectionChanged += OnExternalSubtitlesChanged;
|
||||
}
|
||||
|
||||
private static string FormatFileSize(string filePath)
|
||||
@@ -112,6 +131,13 @@ public class QueuedFileItem : INotifyPropertyChanged
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExternalSubtitlesChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
OnPropertyChanged(nameof(HasExternalSubtitles));
|
||||
OnPropertyChanged(nameof(ExternalSubtitlesTooltip));
|
||||
NotifyLoadState();
|
||||
}
|
||||
|
||||
private void OnTracksChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.NewItems != null)
|
||||
|
||||
Reference in New Issue
Block a user