From b0c25c7f2517cbe96a90c26e64c6d4b0b2a166c8 Mon Sep 17 00:00:00 2001 From: nilaoda Date: Mon, 14 Nov 2022 16:30:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81TTML+PNG=E5=9B=BE=E5=BD=A2?= =?UTF-8?q?=E5=AD=97=E5=B9=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/N_m3u8DL-RE.Common/Resource/ResString.cs | 1 + src/N_m3u8DL-RE.Common/Resource/StaticText.cs | 6 +++ src/N_m3u8DL-RE.Parser/Mp4/MP4TtmlUtil.cs | 52 +++++++++++++++---- src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs | 2 +- .../DownloadManager/SimpleDownloadManager.cs | 32 ++++++++++++ src/N_m3u8DL-RE/Program.cs | 1 + 6 files changed, 82 insertions(+), 12 deletions(-) diff --git a/src/N_m3u8DL-RE.Common/Resource/ResString.cs b/src/N_m3u8DL-RE.Common/Resource/ResString.cs index c243216..84a8631 100644 --- a/src/N_m3u8DL-RE.Common/Resource/ResString.cs +++ b/src/N_m3u8DL-RE.Common/Resource/ResString.cs @@ -103,6 +103,7 @@ namespace N_m3u8DL_RE.Common.Resource public static string writeJson { get => GetText("writeJson"); } public static string noStreamsToDownload { get => GetText("noStreamsToDownload"); } public static string newVersionFound { get => GetText("newVersionFound"); } + public static string processImageSub { get => GetText("processImageSub"); } private static string GetText(string key) { diff --git a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs index f554ac6..3ffd9de 100644 --- a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs +++ b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs @@ -10,6 +10,12 @@ namespace N_m3u8DL_RE.Common.Resource { public static Dictionary LANG_DIC = new() { + ["processImageSub"] = new TextContainer + ( + zhCN: "正在处理图形字幕", + zhTW: "正在處理圖形字幕", + enUS: "Processing Image Sub" + ), ["newVersionFound"] = new TextContainer ( zhCN: "检测到新版本,请尽快升级!", diff --git a/src/N_m3u8DL-RE.Parser/Mp4/MP4TtmlUtil.cs b/src/N_m3u8DL-RE.Parser/Mp4/MP4TtmlUtil.cs index 8edc581..6341824 100644 --- a/src/N_m3u8DL-RE.Parser/Mp4/MP4TtmlUtil.cs +++ b/src/N_m3u8DL-RE.Parser/Mp4/MP4TtmlUtil.cs @@ -34,6 +34,8 @@ namespace Mp4SubtitleParser private static partial Regex LabelFixRegex(); [GeneratedRegex("\\")] private static partial Regex MultiElementsFixRegex(); + [GeneratedRegex("\\([\\s\\S]*?)<\\/smpte:image>")] + private static partial Regex ImageRegex(); public static bool CheckInit(byte[] data) { @@ -229,38 +231,66 @@ namespace Mp4SubtitleParser if (_div == null) continue; + + //PNG Subs + var imageDic = new Dictionary(); //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

label foreach (XmlElement _p in _div!.SelectNodes("ns:p", nsMgr)!) { var _begin = _p.GetAttribute("begin"); var _end = _p.GetAttribute("end"); var _region = _p.GetAttribute("region"); + var _bgImg = _p.GetAttribute("smpte:backgroundImage"); var sub = new SubEntity { Begin = _begin, End = _end, Region = _region }; - var _spans = _p.ChildNodes; - //Collect - foreach (XmlNode _node in _spans) + + if (string.IsNullOrEmpty(_bgImg)) { - if (_node.NodeType == XmlNodeType.Element) + var _spans = _p.ChildNodes; + //Collect + foreach (XmlNode _node in _spans) { - var _span = (XmlElement)_node; - if (string.IsNullOrEmpty(_span.InnerText)) - continue; - sub.Contents.Add(_span); - sub.ContentStrings.Add(_span.OuterXml); + if (_node.NodeType == XmlNodeType.Element) + { + var _span = (XmlElement)_node; + if (string.IsNullOrEmpty(_span.InnerText)) + continue; + sub.Contents.Add(_span); + sub.ContentStrings.Add(_span.OuterXml); + } + else if (_node.NodeType == XmlNodeType.Text) + { + var _span = new XmlDocument().CreateElement("span"); + _span.InnerText = _node.Value!; + sub.Contents.Add(_span); + sub.ContentStrings.Add(_span.OuterXml); + } } - else if (_node.NodeType == XmlNodeType.Text) + } + else + { + var id = _bgImg.Replace("#", ""); + if (imageDic.ContainsKey(id)) { var _span = new XmlDocument().CreateElement("span"); - _span.InnerText = _node.Value!; + _span.InnerText = $"Base64::{imageDic[id]}"; sub.Contents.Add(_span); sub.ContentStrings.Add(_span.OuterXml); } } + //Check if one

has been splitted var index = finalSubs.FindLastIndex(s => s.End == _begin && s.Region == _region && s.ContentStrings.SequenceEqual(sub.ContentStrings)); //Skip empty lines diff --git a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs index 9a8dadb..a269b1f 100644 --- a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs +++ b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs @@ -18,7 +18,7 @@ namespace N_m3u8DL_RE.CommandLine { 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)")] private static partial Regex ForStrRegex(); diff --git a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs index f4d7a99..d092909 100644 --- a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs +++ b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs @@ -410,6 +410,22 @@ namespace N_m3u8DL_RE.DownloadManager //写出字幕 var firstKey = FileDic.Keys.First(); 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); FileDic.Clear(); var index = 0; @@ -444,6 +460,22 @@ namespace N_m3u8DL_RE.DownloadManager //写出字幕 var firstKey = FileDic.Keys.First(); 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); FileDic.Clear(); var index = 0; diff --git a/src/N_m3u8DL-RE/Program.cs b/src/N_m3u8DL-RE/Program.cs index f92aa45..d6a8866 100644 --- a/src/N_m3u8DL-RE/Program.cs +++ b/src/N_m3u8DL-RE/Program.cs @@ -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 = "http://ht.grelighting.cn/m3u8/OEtYNVNjMFF5N1g2VzNkZ2lwbWEvd1ZtTGJ0dlZXOEk=.m3u8"; //特殊的图片伪装 //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 = ""; if (!string.IsNullOrEmpty(option.Input))