diff --git a/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSContentProcessor.cs b/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSContentProcessor.cs index 260fbca..47535f4 100644 --- a/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSContentProcessor.cs +++ b/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSContentProcessor.cs @@ -22,6 +22,8 @@ namespace N_m3u8DL_RE.Parser.Processor.HLS private static partial Regex OrderFixRegex(); [GeneratedRegex("#EXT-X-MAP.*\\.apple\\.com/")] private static partial Regex ATVRegex(); + [GeneratedRegex("(#EXT-X-KEY:[\\s\\S]*?)(#EXT-X-DISCONTINUITY|#EXT-X-ENDLIST)")] + private static partial Regex ATVRegex2(); public override bool CanProcess(ExtractorType extractorType, string rawText, ParserConfig parserConfig) => extractorType == ExtractorType.HLS; @@ -80,7 +82,7 @@ namespace N_m3u8DL_RE.Parser.Processor.HLS if (m3u8Content.Contains("#EXT-X-DISCONTINUITY") && m3u8Content.Contains("#EXT-X-MAP") && (m3u8Url.Contains(".apple.com/") || ATVRegex().IsMatch(m3u8Content))) { //只取加密部分即可 - Regex ykmap = DNSPRegex(); + Regex ykmap = ATVRegex2(); if (ykmap.IsMatch(m3u8Content)) { m3u8Content = "#EXTM3U\r\n" + ykmap.Match(m3u8Content).Groups[1].Value + "\r\n#EXT-X-ENDLIST"; diff --git a/src/N_m3u8DL-RE/Downloader/SimpleDownloader.cs b/src/N_m3u8DL-RE/Downloader/SimpleDownloader.cs index b8113e9..1fe73a2 100644 --- a/src/N_m3u8DL-RE/Downloader/SimpleDownloader.cs +++ b/src/N_m3u8DL-RE/Downloader/SimpleDownloader.cs @@ -85,12 +85,19 @@ namespace N_m3u8DL_RE.Downloader cancellationTokenSource = new(); var des = Path.ChangeExtension(path, null); - //已下载过跳过 + //已下载跳过 if (File.Exists(des)) { return new DownloadResult() { ActualContentLength = 0, ActualFilePath = des }; } + //已解密跳过 + var dec = Path.Combine(Path.GetDirectoryName(des)!, Path.GetFileNameWithoutExtension(des) + "_dec" + Path.GetExtension(des)); + if (File.Exists(dec)) + { + return new DownloadResult() { ActualContentLength = 0, ActualFilePath = dec }; + } + //另起线程进行监控 using var watcher = Task.Factory.StartNew(async () => { diff --git a/src/N_m3u8DL-RE/Util/MP4DecryptUtil.cs b/src/N_m3u8DL-RE/Util/MP4DecryptUtil.cs index cab5038..e715bda 100644 --- a/src/N_m3u8DL-RE/Util/MP4DecryptUtil.cs +++ b/src/N_m3u8DL-RE/Util/MP4DecryptUtil.cs @@ -7,17 +7,26 @@ namespace N_m3u8DL_RE.Util { internal class MP4DecryptUtil { + private static string ZeroKid = "00000000000000000000000000000000"; public static async Task DecryptAsync(bool shakaPackager, string bin, string[]? keys, string source, string dest, string? kid, string init = "") { if (keys == null || keys.Length == 0) return false; - var keyPair = keys.First(); + string? keyPair = null; + string? trackId = null; if (!string.IsNullOrEmpty(kid)) { var test = keys.Where(k => k.StartsWith(kid)); if (test.Any()) keyPair = test.First(); } + //Apple + if (kid == ZeroKid) + { + keyPair = keys.First(); + trackId = "1"; + } + if (keyPair == null) return false; //shakaPackager 无法单独解密init文件 @@ -37,12 +46,19 @@ namespace N_m3u8DL_RE.Util enc = tmpFile; } - cmd = $"--enable_raw_key_decryption input=\"{enc}\",stream=0,output=\"{dest}\" " + - $"--keys key_id={keyPair.Split(':')[0]}:key={keyPair.Split(':')[1]}"; + cmd = $"--quiet --enable_raw_key_decryption input=\"{enc}\",stream=0,output=\"{dest}\" " + + $"--keys {(trackId != null ? $"label={trackId}:" : "")}key_id={ZeroKid}:key={keyPair.Split(':')[1]}"; } else { - cmd = string.Join(" ", keys.Select(k => $"--key {k}")); + if (trackId == null) + { + cmd = string.Join(" ", keys.Select(k => $"--key {k}")); + } + else + { + cmd = string.Join(" ", keys.Select(k => $"--key {trackId}:{k.Split(':')[1]}")); + } if (init != "") { cmd += $" --fragments-info \"{init}\" "; @@ -69,8 +85,8 @@ namespace N_m3u8DL_RE.Util { FileName = name, Arguments = arg, - RedirectStandardOutput = true, - RedirectStandardError = true, + //RedirectStandardOutput = true, + //RedirectStandardError = true, CreateNoWindow = true, UseShellExecute = false })!.WaitForExitAsync();