支持TTML+PNG图形字幕

This commit is contained in:
nilaoda 2022-11-14 16:30:58 +08:00
parent 438b2dea7b
commit b0c25c7f25
6 changed files with 82 additions and 12 deletions

View File

@ -103,6 +103,7 @@ namespace N_m3u8DL_RE.Common.Resource
public static string writeJson { get => GetText("writeJson"); } public static string writeJson { get => GetText("writeJson"); }
public static string noStreamsToDownload { get => GetText("noStreamsToDownload"); } public static string noStreamsToDownload { get => GetText("noStreamsToDownload"); }
public static string newVersionFound { get => GetText("newVersionFound"); } public static string newVersionFound { get => GetText("newVersionFound"); }
public static string processImageSub { get => GetText("processImageSub"); }
private static string GetText(string key) private static string GetText(string key)
{ {

View File

@ -10,6 +10,12 @@ namespace N_m3u8DL_RE.Common.Resource
{ {
public static Dictionary<string, TextContainer> LANG_DIC = new() public static Dictionary<string, TextContainer> LANG_DIC = new()
{ {
["processImageSub"] = new TextContainer
(
zhCN: "正在处理图形字幕",
zhTW: "正在處理圖形字幕",
enUS: "Processing Image Sub"
),
["newVersionFound"] = new TextContainer ["newVersionFound"] = new TextContainer
( (
zhCN: "检测到新版本,请尽快升级!", zhCN: "检测到新版本,请尽快升级!",

View File

@ -34,6 +34,8 @@ namespace Mp4SubtitleParser
private static partial Regex LabelFixRegex(); private static partial Regex LabelFixRegex();
[GeneratedRegex("\\<tt[\\s\\S]*?\\<\\/tt\\>")] [GeneratedRegex("\\<tt[\\s\\S]*?\\<\\/tt\\>")]
private static partial Regex MultiElementsFixRegex(); private static partial Regex MultiElementsFixRegex();
[GeneratedRegex("\\<smpte:image.*xml:id=\\\"(.*?)\\\".*\\>([\\s\\S]*?)<\\/smpte:image>")]
private static partial Regex ImageRegex();
public static bool CheckInit(byte[] data) public static bool CheckInit(byte[] data)
{ {
@ -229,18 +231,33 @@ namespace Mp4SubtitleParser
if (_div == null) if (_div == null)
continue; continue;
//PNG Subs
var imageDic = new Dictionary<string, string>(); //id, Base64
if (ImageRegex().IsMatch(xmlDoc.InnerXml))
{
foreach (Match img in ImageRegex().Matches(xmlDoc.InnerXml))
{
imageDic.Add(img.Groups[1].Value, img.Groups[2].Value);
}
}
//Parse <p> label //Parse <p> label
foreach (XmlElement _p in _div!.SelectNodes("ns:p", nsMgr)!) foreach (XmlElement _p in _div!.SelectNodes("ns:p", nsMgr)!)
{ {
var _begin = _p.GetAttribute("begin"); var _begin = _p.GetAttribute("begin");
var _end = _p.GetAttribute("end"); var _end = _p.GetAttribute("end");
var _region = _p.GetAttribute("region"); var _region = _p.GetAttribute("region");
var _bgImg = _p.GetAttribute("smpte:backgroundImage");
var sub = new SubEntity var sub = new SubEntity
{ {
Begin = _begin, Begin = _begin,
End = _end, End = _end,
Region = _region Region = _region
}; };
if (string.IsNullOrEmpty(_bgImg))
{
var _spans = _p.ChildNodes; var _spans = _p.ChildNodes;
//Collect <span> //Collect <span>
foreach (XmlNode _node in _spans) foreach (XmlNode _node in _spans)
@ -261,6 +278,19 @@ namespace Mp4SubtitleParser
sub.ContentStrings.Add(_span.OuterXml); sub.ContentStrings.Add(_span.OuterXml);
} }
} }
}
else
{
var id = _bgImg.Replace("#", "");
if (imageDic.ContainsKey(id))
{
var _span = new XmlDocument().CreateElement("span");
_span.InnerText = $"Base64::{imageDic[id]}";
sub.Contents.Add(_span);
sub.ContentStrings.Add(_span.OuterXml);
}
}
//Check if one <p> has been splitted //Check if one <p> has been splitted
var index = finalSubs.FindLastIndex(s => s.End == _begin && s.Region == _region && s.ContentStrings.SequenceEqual(sub.ContentStrings)); var index = finalSubs.FindLastIndex(s => s.End == _begin && s.Region == _region && s.ContentStrings.SequenceEqual(sub.ContentStrings));
//Skip empty lines //Skip empty lines

View File

@ -18,7 +18,7 @@ namespace N_m3u8DL_RE.CommandLine
{ {
internal partial class CommandInvoker internal partial class CommandInvoker
{ {
public const string VERSION_INFO = "N_m3u8DL-RE (Beta version) 20221113"; public const string VERSION_INFO = "N_m3u8DL-RE (Beta version) 20221114";
[GeneratedRegex("((best|worst)\\d*|all)")] [GeneratedRegex("((best|worst)\\d*|all)")]
private static partial Regex ForStrRegex(); private static partial Regex ForStrRegex();

View File

@ -410,6 +410,22 @@ namespace N_m3u8DL_RE.DownloadManager
//写出字幕 //写出字幕
var firstKey = FileDic.Keys.First(); var firstKey = FileDic.Keys.First();
var files = FileDic.Values.Select(v => v!.ActualFilePath).OrderBy(s => s).ToArray(); var files = FileDic.Values.Select(v => v!.ActualFilePath).OrderBy(s => s).ToArray();
//处理图形字幕
if (finalVtt.Cues.All(v => v.Payload.StartsWith("Base64::")))
{
Logger.WarnMarkUp(ResString.processImageSub);
var _pad = "0".PadLeft(finalVtt.Cues.Count().ToString().Length, '0');
var _i = 0;
foreach (var img in finalVtt.Cues)
{
var base64 = img.Payload[8..];
var name = _i++.ToString(_pad) + ".png";
await File.WriteAllBytesAsync(Path.Combine(tmpDir, name), Convert.FromBase64String(base64));
img.Payload = name;
}
}
foreach (var item in files) File.Delete(item); foreach (var item in files) File.Delete(item);
FileDic.Clear(); FileDic.Clear();
var index = 0; var index = 0;
@ -444,6 +460,22 @@ namespace N_m3u8DL_RE.DownloadManager
//写出字幕 //写出字幕
var firstKey = FileDic.Keys.First(); var firstKey = FileDic.Keys.First();
var files = FileDic.Values.Select(v => v!.ActualFilePath).OrderBy(s => s).ToArray(); var files = FileDic.Values.Select(v => v!.ActualFilePath).OrderBy(s => s).ToArray();
//处理图形字幕
if (finalVtt.Cues.All(v => v.Payload.StartsWith("Base64::")))
{
Logger.WarnMarkUp(ResString.processImageSub);
var _pad = "0".PadLeft(finalVtt.Cues.Count().ToString().Length, '0');
var _i = 0;
foreach (var img in finalVtt.Cues)
{
var base64 = img.Payload[8..];
var name = _i++.ToString(_pad) + ".png";
await File.WriteAllBytesAsync(Path.Combine(tmpDir, name), Convert.FromBase64String(base64));
img.Payload = name;
}
}
foreach (var item in files) File.Delete(item); foreach (var item in files) File.Delete(item);
FileDic.Clear(); FileDic.Clear();
var index = 0; var index = 0;

View File

@ -193,6 +193,7 @@ namespace N_m3u8DL_RE
//url = "https://a38avoddashs3ww-a.akamaihd.net/ondemand/iad_2/8e91/f2f2/ec5a/430f-bd7a-0779f4a0189d/685cda75-609c-41c1-86bb-688f4cdb5521_corrected.mpd"; //url = "https://a38avoddashs3ww-a.akamaihd.net/ondemand/iad_2/8e91/f2f2/ec5a/430f-bd7a-0779f4a0189d/685cda75-609c-41c1-86bb-688f4cdb5521_corrected.mpd";
//url = "http://ht.grelighting.cn/m3u8/OEtYNVNjMFF5N1g2VzNkZ2lwbWEvd1ZtTGJ0dlZXOEk=.m3u8"; //特殊的图片伪装 //url = "http://ht.grelighting.cn/m3u8/OEtYNVNjMFF5N1g2VzNkZ2lwbWEvd1ZtTGJ0dlZXOEk=.m3u8"; //特殊的图片伪装
//url = "https://ali6.a.yximgs.com/udata/music/music_295e59bdb3084def8158873ad6f5c8250.jpg"; //PNG图片伪装 //url = "https://ali6.a.yximgs.com/udata/music/music_295e59bdb3084def8158873ad6f5c8250.jpg"; //PNG图片伪装
//url = "https://vod.cds.nowonline.com.br/Content/dsc/VOD/movie/df/83/4dd69316-2022-45c0-a4f7-c35cad87df83/manifest.mpd"; //TTML+PNG
//url = ""; //url = "";
if (!string.IsNullOrEmpty(option.Input)) if (!string.IsNullOrEmpty(option.Input))