emby-toolbox/EmbyToolbox/Services/SubtitleCodecRules.cs
Emby Toolbox 6264b487fe Initial commit: Emby Toolbox (conversion scroll fix, bulk Del for tracks).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 21:33:47 +05:00

139 lines
4.8 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using EmbyToolbox.Models;
namespace EmbyToolbox.Services;
/// <summary>Правила совместимости субтитров с контейнером MKV/MP4 и codec copy в FFmpeg.</summary>
public static class SubtitleCodecRules
{
public static string Normalize(string? codec) =>
string.IsNullOrWhiteSpace(codec) ? string.Empty : codec.Trim().ToLowerInvariant();
public static bool IsTeletext(string? codec) => Normalize(codec) == "dvb_teletext";
public static bool IsUnknownSubtitleCodec(string? codec)
{
var n = Normalize(codec);
return n is "" or "?" or "unknown";
}
/// <summary>Кодеки, которые можно mux copy в Matroska (без teletext / mov_text и пр.).</summary>
public static bool IsMkvCopySafe(string? codec)
{
return Normalize(codec) switch
{
"subrip" or "srt" or "ass" or "ssa" => true,
"webvtt" or "wvtt" => true,
"hdmv_pgs_subtitle" or "pgssub" => true,
"dvd_subtitle" or "dvdsub" => true,
_ => false
};
}
public static bool IsMp4TextDirectCopy(string? codec) => Normalize(codec) == "mov_text";
/// <summary>Текстовые дорожки, для MP4 требующие перекодирования в mov_text (не mux copy).</summary>
public static bool Mp4RequiresSubtitleTranscode(string? codec)
{
return Normalize(codec) switch
{
"subrip" or "srt" or "ass" or "ssa" => true,
_ => false
};
}
public static bool TargetsMkv(string? container) =>
!string.IsNullOrWhiteSpace(container) &&
(container.Contains("mkv", StringComparison.OrdinalIgnoreCase) ||
container.Contains("matro", StringComparison.OrdinalIgnoreCase));
public static bool TargetsMp4(string? container) =>
!string.IsNullOrWhiteSpace(container) &&
(container.Contains("mp4", StringComparison.OrdinalIgnoreCase) ||
container.Contains("m4v", StringComparison.OrdinalIgnoreCase) ||
container.Contains("mov", StringComparison.OrdinalIgnoreCase));
/// <summary>Действо по умолчанию для встроенной дорожки субтитров после анализа.</summary>
public static TrackActionKind DefaultEmbeddedSubtitleAction(string? codecName, string? profileContainer)
{
var c = codecName;
if (IsTeletext(c) || IsUnknownSubtitleCodec(c))
{
return TrackActionKind.Remove;
}
if (TargetsMkv(profileContainer))
{
return IsMkvCopySafe(c) ? TrackActionKind.Keep : TrackActionKind.Remove;
}
if (TargetsMp4(profileContainer))
{
if (IsMp4TextDirectCopy(c))
{
return TrackActionKind.Keep;
}
if (Mp4RequiresSubtitleTranscode(c))
{
return TrackActionKind.Convert;
}
return TrackActionKind.Remove;
}
return IsMkvCopySafe(c) ? TrackActionKind.Keep : TrackActionKind.Remove;
}
/// <summary>Встроенную субдорожку с этим решением включают в ffmpeg -map только если истина.</summary>
public static bool ShouldMapEmbeddedSubtitle(
MediaAnalysisResult media,
TrackOverrideEntry t,
string effectiveContainer)
{
if (t.Source != SourceKind.Embedded || t.StreamKind != MediaStreamKind.Subtitle)
{
return true;
}
if (t.Action == TrackActionKind.Remove)
{
return false;
}
var codec = media.AllStreams.FirstOrDefault(s => s.Index == t.StreamIndex)?.CodecName;
if (TargetsMkv(effectiveContainer))
{
if (!IsMkvCopySafe(codec))
{
return false;
}
return t.Action is TrackActionKind.Keep or TrackActionKind.Convert;
}
if (TargetsMp4(effectiveContainer))
{
if (IsMp4TextDirectCopy(codec) && t.Action == TrackActionKind.Keep)
{
return true;
}
if (Mp4RequiresSubtitleTranscode(codec) &&
t.Action is TrackActionKind.Convert or TrackActionKind.Keep)
{
return true;
}
return false;
}
return IsMkvCopySafe(codec) ? t.Action != TrackActionKind.Remove : false;
}
/// <summary>Строковая подпись Codec для дорожки (субтитры отображать как текстовые / teletext).</summary>
public static string EmbeddedSubtitleCodecLabel(string? ffprobeCodec) =>
IsTeletext(ffprobeCodec) ? "dvb_teletext (subtitle)" : ffprobeCodec ?? "?";
}