默认使用ffmpeg进行mp4解密 (#504)

BREAKING CHANGE
This commit is contained in:
nilaoda 2024-11-23 21:52:47 +08:00
parent e8e92b6337
commit 312325ca18
12 changed files with 102 additions and 50 deletions

View File

@ -77,6 +77,7 @@ public class ResString
public static string cmd_uiLanguage => GetText("cmd_uiLanguage");
public static string cmd_urlProcessorArgs => GetText("cmd_urlProcessorArgs");
public static string cmd_useShakaPackager => GetText("cmd_useShakaPackager");
public static string cmd_useMp4decrypt => GetText("cmd_useMp4decrypt");
public static string cmd_concurrentDownload => GetText("cmd_concurrentDownload");
public static string cmd_useSystemProxy => GetText("cmd_useSystemProxy");
public static string cmd_customProxy => GetText("cmd_customProxy");

View File

@ -270,9 +270,9 @@ internal class StaticText
),
["cmd_keys"] = new TextContainer
(
zhCN: "设置解密密钥, 程序调用mp4decrpyt/shaka-packager进行解密. 格式:\r\n--key KID1:KEY1 --key KID2:KEY2\r\n对于KEY相同的情况可以直接输入 --key KEY",
zhTW: "設置解密密鑰, 程序調用mp4decrpyt/shaka-packager進行解密. 格式:\r\n--key KID1:KEY1 --key KID2:KEY2\r\n對於KEY相同的情況可以直接輸入 --key KEY",
enUS: "Set decryption key(s) to mp4decrypt/shaka-packager. format:\r\n--key KID1:KEY1 --key KID2:KEY2\r\nor use --key KEY if all tracks share the same key."
zhCN: "设置解密密钥, 程序调用mp4decrpyt/shaka-packager/ffmpeg进行解密. 格式:\r\n--key KID1:KEY1 --key KID2:KEY2\r\n对于KEY相同的情况可以直接输入 --key KEY",
zhTW: "設置解密密鑰, 程序調用mp4decrpyt/shaka-packager/ffmpeg進行解密. 格式:\r\n--key KID1:KEY1 --key KID2:KEY2\r\n對於KEY相同的情況可以直接輸入 --key KEY",
enUS: "Set decryption key(s) to mp4decrypt/shaka-packager/ffmpeg. format:\r\n--key KID1:KEY1 --key KID2:KEY2\r\nor use --key KEY if all tracks share the same key."
),
["cmd_keyText"] = new TextContainer
(
@ -468,9 +468,15 @@ internal class StaticText
),
["cmd_useShakaPackager"] = new TextContainer
(
zhCN: "解密时使用shaka-packager替代mp4decrypt",
zhTW: "解密時使用shaka-packager替代mp4decrypt",
enUS: "Use shaka-packager instead of mp4decrypt to decrypt"
zhCN: "解密时使用shaka-packager替代ffmpeg",
zhTW: "解密時使用shaka-packager替代ffmpeg",
enUS: "Use shaka-packager instead of ffmpeg to decrypt"
),
["cmd_useMp4decrypt"] = new TextContainer
(
zhCN: "解密时使用mp4decrypt替代ffmpeg",
zhTW: "解密時使用mp4decrypt替代ffmpeg",
enUS: "Use mp4decrypt instead of ffmpeg to decrypt"
),
["cmd_concurrentDownload"] = new TextContainer
(
@ -744,9 +750,9 @@ internal class StaticText
),
["realTimeDecMessage"] = new TextContainer
(
zhCN: "启用实时解密时建议用shaka-packager而非mp4decrypt",
zhTW: "啟用即時解密時建議用shaka-packager而非mp4decrypt",
enUS: "When enabling real-time decryption, it is recommended to use shaka-packager instead of mp4decrypt"
zhCN: "启用实时解密时建议用shaka-packager而非mp4decrypt/ffmpeg",
zhTW: "啟用即時解密時建議用shaka-packager而非mp4decrypt/ffmpeg",
enUS: "When enabling real-time decryption, it is recommended to use shaka-packager instead of mp4decrypt/ffmpeg"
),
["liveLimitReached"] = new TextContainer
(

View File

@ -368,7 +368,7 @@ internal class HLSExtractor : IExtractor
// #EXT-X-MAP
else if (line.StartsWith(HLSTags.ext_x_map))
{
if (playlist.MediaInit == null)
if (playlist.MediaInit == null || hasAd)
{
playlist.MediaInit = new MediaSegment()
{

View File

@ -54,6 +54,7 @@ internal partial class CommandInvoker
private static readonly Option<bool> AppendUrlParams = new(["--append-url-params"], description: ResString.cmd_appendUrlParams, getDefaultValue: () => false);
private static readonly Option<bool> MP4RealTimeDecryption = new (["--mp4-real-time-decryption"], description: ResString.cmd_MP4RealTimeDecryption, getDefaultValue: () => false);
private static readonly Option<bool> UseShakaPackager = new (["--use-shaka-packager"], description: ResString.cmd_useShakaPackager, getDefaultValue: () => false);
private static readonly Option<bool> UseMp4Decrypt = new (["--use-mp4decrypt"], description: ResString.cmd_useMp4decrypt, getDefaultValue: () => false);
private static readonly Option<bool> ForceAnsiConsole = new(["--force-ansi-console"], description: ResString.cmd_forceAnsiConsole);
private static readonly Option<bool> NoAnsiColor = new(["--no-ansi-color"], description: ResString.cmd_noAnsiColor);
private static readonly Option<string?> DecryptionBinaryPath = new(["--decryption-binary-path"], description: ResString.cmd_decryptionBinaryPath) { ArgumentHelpName = "PATH" };
@ -523,6 +524,7 @@ internal partial class CommandInvoker
UrlProcessorArgs = bindingContext.ParseResult.GetValueForOption(UrlProcessorArgs),
MP4RealTimeDecryption = bindingContext.ParseResult.GetValueForOption(MP4RealTimeDecryption),
UseShakaPackager = bindingContext.ParseResult.GetValueForOption(UseShakaPackager),
UseMp4Decrypt = bindingContext.ParseResult.GetValueForOption(UseMp4Decrypt),
DecryptionBinaryPath = bindingContext.ParseResult.GetValueForOption(DecryptionBinaryPath),
FFmpegBinaryPath = bindingContext.ParseResult.GetValueForOption(FFmpegBinaryPath),
KeyTextFile = bindingContext.ParseResult.GetValueForOption(KeyTextFile),
@ -615,7 +617,7 @@ internal partial class CommandInvoker
Input, TmpDir, SaveDir, SaveName, BaseUrl, ThreadCount, DownloadRetryCount, HttpRequestTimeout, ForceAnsiConsole, NoAnsiColor,AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
BinaryMerge, UseFFmpegConcatDemuxer, DelAfterDone, NoDateInfo, NoLog, WriteMetaJson, AppendUrlParams, ConcurrentDownload, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
FFmpegBinaryPath,
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption,
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, UseMp4Decrypt, MP4RealTimeDecryption,
MaxSpeed,
MuxAfterDone,
CustomHLSMethod, CustomHLSKey, CustomHLSIv, UseSystemProxy, CustomProxy, CustomRange, TaskStartAt,

View File

@ -141,6 +141,10 @@ internal class MyOption
/// </summary>
public bool UseShakaPackager { get; set; }
/// <summary>
/// See: <see cref="CommandInvoker.UseMp4Decrypt"/>.
/// </summary>
public bool UseMp4Decrypt { get; set; }
/// <summary>
/// See: <see cref="CommandInvoker.MuxAfterDone"/>.
/// </summary>
public bool MuxAfterDone { get; set; }
@ -266,4 +270,13 @@ internal class MyOption
/// See: <see cref="CommandInvoker.LiveFixVttByAudio"/>.
/// </summary>
public bool LiveFixVttByAudio { get; set; }
public DecryptEngine GetDecryptEngine()
{
if (UseShakaPackager)
return DecryptEngine.SHAKA_PACKAGE;
if (UseMp4Decrypt)
return DecryptEngine.MP4DECRYPT;
return DecryptEngine.FFMPEG;
}
}

View File

@ -13,6 +13,7 @@ using N_m3u8DL_RE.Util;
using Spectre.Console;
using System.Collections.Concurrent;
using System.Text;
using N_m3u8DL_RE.Enum;
namespace N_m3u8DL_RE.DownloadManager;
@ -39,9 +40,9 @@ internal class SimpleDownloadManager
if (_key != null)
{
if (DownloaderConfig.MyOptions.Keys == null)
DownloaderConfig.MyOptions.Keys = new string[] { _key };
DownloaderConfig.MyOptions.Keys = [_key];
else
DownloaderConfig.MyOptions.Keys = DownloaderConfig.MyOptions.Keys.Concat(new string[] { _key }).ToArray();
DownloaderConfig.MyOptions.Keys = [..DownloaderConfig.MyOptions.Keys, _key];
}
}
@ -109,8 +110,8 @@ internal class SimpleDownloadManager
var saveName = DownloaderConfig.MyOptions.SaveName != null ? $"{DownloaderConfig.MyOptions.SaveName}.{streamSpec.Language}".TrimEnd('.') : dirName;
var headers = DownloaderConfig.Headers;
// mp4decrypt
var mp4decrypt = DownloaderConfig.MyOptions.DecryptionBinaryPath!;
var decryptionBinaryPath = DownloaderConfig.MyOptions.DecryptionBinaryPath!;
var decryptEngine = DownloaderConfig.MyOptions.GetDecryptEngine();
var mp4InitFile = "";
var currentKID = "";
var readInfo = false; // 是否读取过
@ -171,7 +172,7 @@ internal class SimpleDownloadManager
currentKID = mp4Info.KID;
// try shaka packager, which can handle WebM
if (string.IsNullOrEmpty(currentKID) && DownloaderConfig.MyOptions.UseShakaPackager) {
currentKID = MP4DecryptUtil.ReadInitShaka(result.ActualFilePath, mp4decrypt);
currentKID = MP4DecryptUtil.ReadInitShaka(result.ActualFilePath, decryptionBinaryPath);
}
// 从文件读取KEY
await SearchKeyAsync(currentKID);
@ -180,7 +181,7 @@ internal class SimpleDownloadManager
{
var enc = result.ActualFilePath;
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, isMultiDRM: mp4Info.isMultiDRM);
var dResult = await MP4DecryptUtil.DecryptAsync(decryptEngine, decryptionBinaryPath, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, isMultiDRM: mp4Info.isMultiDRM);
if (dResult)
{
FileDic[streamSpec.Playlist.MediaInit]!.ActualFilePath = dec;
@ -229,7 +230,7 @@ internal class SimpleDownloadManager
// 需要重新解密init
var enc = FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath;
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID);
var dResult = await MP4DecryptUtil.DecryptAsync(decryptEngine, decryptionBinaryPath, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID);
if (dResult)
{
FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath = dec;
@ -243,7 +244,7 @@ internal class SimpleDownloadManager
}
// try shaka packager, which can handle WebM
if (string.IsNullOrEmpty(currentKID) && DownloaderConfig.MyOptions.UseShakaPackager) {
currentKID = MP4DecryptUtil.ReadInitShaka(result.ActualFilePath, mp4decrypt);
currentKID = MP4DecryptUtil.ReadInitShaka(result.ActualFilePath, decryptionBinaryPath);
}
// 从文件读取KEY
await SearchKeyAsync(currentKID);
@ -253,7 +254,7 @@ internal class SimpleDownloadManager
var enc = result.ActualFilePath;
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
mp4Info = MP4DecryptUtil.GetMP4Info(enc);
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile, isMultiDRM: mp4Info.isMultiDRM);
var dResult = await MP4DecryptUtil.DecryptAsync(decryptEngine, decryptionBinaryPath, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile, isMultiDRM: mp4Info.isMultiDRM);
if (dResult)
{
File.Delete(enc);
@ -291,7 +292,7 @@ internal class SimpleDownloadManager
var enc = result.ActualFilePath;
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
mp4Info = MP4DecryptUtil.GetMP4Info(enc);
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile, isMultiDRM: mp4Info.isMultiDRM);
var dResult = await MP4DecryptUtil.DecryptAsync(decryptEngine, decryptionBinaryPath, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile, isMultiDRM: mp4Info.isMultiDRM);
if (dResult)
{
File.Delete(enc);
@ -322,8 +323,8 @@ internal class SimpleDownloadManager
if (!string.IsNullOrEmpty(currentKID) && DownloaderConfig.MyOptions.MP4RealTimeDecryption && DownloaderConfig.MyOptions.Keys != null && DownloaderConfig.MyOptions.Keys.Length > 0 && mp4InitFile != "")
{
File.Delete(mp4InitFile);
// shaka实时解密不需要init文件用于合并
if (DownloaderConfig.MyOptions.UseShakaPackager)
// shaka/ffmpeg实时解密不需要init文件用于合并
if (decryptEngine != DecryptEngine.MP4DECRYPT)
{
FileDic!.Remove(streamSpec.Playlist!.MediaInit, out _);
}
@ -590,7 +591,7 @@ internal class SimpleDownloadManager
currentKID = MP4DecryptUtil.GetMP4Info(output).KID;
// try shaka packager, which can handle WebM
if (string.IsNullOrEmpty(currentKID) && DownloaderConfig.MyOptions.UseShakaPackager) {
currentKID = MP4DecryptUtil.ReadInitShaka(output, mp4decrypt);
currentKID = MP4DecryptUtil.ReadInitShaka(output, decryptionBinaryPath);
}
// 从文件读取KEY
await SearchKeyAsync(currentKID);
@ -602,8 +603,8 @@ internal class SimpleDownloadManager
var enc = output;
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
mp4Info = MP4DecryptUtil.GetMP4Info(enc);
Logger.InfoMarkUp($"[grey]Decrypting...[/]");
var result = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, isMultiDRM: mp4Info.isMultiDRM);
Logger.InfoMarkUp($"[grey]Decrypting using {decryptEngine}...[/]");
var result = await MP4DecryptUtil.DecryptAsync(decryptEngine, decryptionBinaryPath, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, isMultiDRM: mp4Info.isMultiDRM);
if (result)
{
File.Delete(enc);
@ -653,8 +654,7 @@ internal class SimpleDownloadManager
}
progress.Columns(progressColumns);
if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !DownloaderConfig.MyOptions.UseShakaPackager
&& DownloaderConfig.MyOptions.Keys != null && DownloaderConfig.MyOptions.Keys.Length > 0)
if (DownloaderConfig.MyOptions is { MP4RealTimeDecryption: true, UseShakaPackager: false, Keys.Length: > 0 })
Logger.WarnMarkUp($"[darkorange3_1]{ResString.realTimeDecMessage}[/]");
await progress.StartAsync(async ctx =>

View File

@ -16,6 +16,7 @@ using System.Collections.Concurrent;
using System.IO.Pipes;
using System.Text;
using System.Threading.Tasks.Dataflow;
using N_m3u8DL_RE.Enum;
namespace N_m3u8DL_RE.DownloadManager;
@ -143,8 +144,7 @@ internal class SimpleLiveRecordManager2
private async Task<bool> RecordStreamAsync(StreamSpec streamSpec, ProgressTask task, SpeedContainer speedContainer, BufferBlock<List<MediaSegment>> source)
{
var baseTimestamp = PublishDateTime == null ? 0L : (long)(PublishDateTime.Value.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds;
// mp4decrypt
var mp4decrypt = DownloaderConfig.MyOptions.DecryptionBinaryPath!;
var decryptionBinaryPath = DownloaderConfig.MyOptions.DecryptionBinaryPath!;
var mp4InitFile = "";
var currentKID = "";
var readInfo = false; // 是否读取过
@ -164,6 +164,7 @@ internal class SimpleLiveRecordManager2
var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
var saveName = DownloaderConfig.MyOptions.SaveName != null ? $"{DownloaderConfig.MyOptions.SaveName}.{streamSpec.Language}".TrimEnd('.') : dirName;
var headers = DownloaderConfig.Headers;
var decryptEngine = DownloaderConfig.MyOptions.GetDecryptEngine();
Logger.Debug($"dirName: {dirName}; tmpDir: {tmpDir}; saveDir: {saveDir}; saveName: {saveName}");
@ -214,7 +215,7 @@ internal class SimpleLiveRecordManager2
{
var enc = result.ActualFilePath;
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID);
var dResult = await MP4DecryptUtil.DecryptAsync(decryptEngine, decryptionBinaryPath, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID);
if (dResult)
{
FileDic[streamSpec.Playlist.MediaInit]!.ActualFilePath = dec;
@ -274,7 +275,7 @@ internal class SimpleLiveRecordManager2
// 需要重新解密init
var enc = FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath;
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID);
var dResult = await MP4DecryptUtil.DecryptAsync(decryptEngine, decryptionBinaryPath, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID);
if (dResult)
{
FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath = dec;
@ -293,7 +294,7 @@ internal class SimpleLiveRecordManager2
{
var enc = result.ActualFilePath;
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile);
var dResult = await MP4DecryptUtil.DecryptAsync(decryptEngine, decryptionBinaryPath, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile);
if (dResult)
{
File.Delete(enc);
@ -336,7 +337,7 @@ internal class SimpleLiveRecordManager2
{
var enc = result.ActualFilePath;
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile);
var dResult = await MP4DecryptUtil.DecryptAsync(decryptEngine, decryptionBinaryPath, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile);
if (dResult)
{
File.Delete(enc);
@ -554,10 +555,10 @@ internal class SimpleLiveRecordManager2
var files = FileDic.Where(f => f.Key != streamSpec.Playlist!.MediaInit).OrderBy(s => s.Key.Index).Select(f => f.Value).Select(v => v!.ActualFilePath).ToArray();
if (initResult != null && mp4InitFile != "")
{
// shaka实时解密不需要init文件用于合并mp4decrpyt需要
if (!DownloaderConfig.MyOptions.UseShakaPackager)
// shaka/ffmpeg实时解密不需要init文件用于合并mp4decrpyt需要
if (decryptEngine != DecryptEngine.MP4DECRYPT)
{
files = new string[] { initResult.ActualFilePath }.Concat(files).ToArray();
files = [initResult.ActualFilePath, ..files];
}
}
foreach (var inputFilePath in files)
@ -839,8 +840,7 @@ internal class SimpleLiveRecordManager2
DownloaderConfig.MyOptions.ConcurrentDownload = true;
DownloaderConfig.MyOptions.MP4RealTimeDecryption = true;
DownloaderConfig.MyOptions.LiveRecordLimit = DownloaderConfig.MyOptions.LiveRecordLimit ?? TimeSpan.MaxValue;
if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !DownloaderConfig.MyOptions.UseShakaPackager
&& DownloaderConfig.MyOptions.Keys != null && DownloaderConfig.MyOptions.Keys.Length > 0)
if (DownloaderConfig.MyOptions is { MP4RealTimeDecryption: true, UseShakaPackager: false, Keys.Length: > 0 })
Logger.WarnMarkUp($"[darkorange3_1]{ResString.realTimeDecMessage}[/]");
var limit = DownloaderConfig.MyOptions.LiveRecordLimit;
if (limit != TimeSpan.MaxValue)

View File

@ -0,0 +1,8 @@
namespace N_m3u8DL_RE.Enum;
internal enum DecryptEngine
{
MP4DECRYPT,
SHAKA_PACKAGE,
FFMPEG,
}

View File

@ -105,11 +105,15 @@ internal class Program
}
// 检查互斥的选项
if (option is { MuxAfterDone: false, MuxImports.Count: > 0 })
{
throw new ArgumentException("MuxAfterDone disabled, MuxImports not allowed!");
}
if (option is { UseShakaPackager: true, UseMp4Decrypt: true })
{
throw new ArgumentException("UseShakaPackager and UseMp4Decrypt cannot be enabled simultaneously!");
}
// LivePipeMux开启时 LiveRealTimeMerge必须开启
if (option is { LivePipeMux: true, LiveRealTimeMerge: false })
@ -154,13 +158,17 @@ internal class Program
option.DecryptionBinaryPath = file ?? file2 ?? file3 ?? file4;
Logger.Extra($"shaka-packager => {option.DecryptionBinaryPath}");
}
else
else if (option.UseMp4Decrypt)
{
var file = GlobalUtil.FindExecutable("mp4decrypt");
if (file == null) throw new FileNotFoundException("mp4decrypt not found!");
option.DecryptionBinaryPath = file;
Logger.Extra($"mp4decrypt => {option.DecryptionBinaryPath}");
}
else
{
option.DecryptionBinaryPath = option.FFmpegBinaryPath;
}
}
else if (!File.Exists(option.DecryptionBinaryPath))
{

View File

@ -24,7 +24,7 @@ internal static class DownloadUtil
else
{
var buffer = new byte[expect];
await inputStream.ReadAsync(buffer);
_ = await inputStream.ReadAsync(buffer);
await outputStream.WriteAsync(buffer, 0, buffer.Length);
speedContainer.Add(buffer.Length);
}

View File

@ -14,6 +14,7 @@ internal static class LanguageCodeUtil
{
private static readonly List<Language> ALL_LANGS = @"
default;und;default;default
af;afr;Afrikaans;Afrikaans
af-ZA;afr;Afrikaans (South Africa);Afrikaans (South Africa)
am;amh;Amharic;Amharic

View File

@ -1,16 +1,16 @@
using Mp4SubtitleParser;
using N_m3u8DL_RE.Common.Log;
using N_m3u8DL_RE.Common.Resource;
using N_m3u8DL_RE.Config;
using System.Diagnostics;
using System.Text.RegularExpressions;
using N_m3u8DL_RE.Enum;
namespace N_m3u8DL_RE.Util;
internal static class MP4DecryptUtil
{
private static readonly string ZeroKid = "00000000000000000000000000000000";
public static async Task<bool> DecryptAsync(bool shakaPackager, string bin, string[]? keys, string source, string dest, string? kid, string init = "", bool isMultiDRM=false)
public static async Task<bool> DecryptAsync(DecryptEngine decryptEngine, string bin, string[]? keys, string source, string dest, string? kid, string init = "", bool isMultiDRM=false)
{
if (keys == null || keys.Length == 0) return false;
@ -45,27 +45,27 @@ internal static class MP4DecryptUtil
if (keyPair == null) return false;
// shakaPackager 无法单独解密init文件
if (source.EndsWith("_init.mp4") && shakaPackager) return false;
// shakaPackager/ffmpeg 无法单独解密init文件
if (source.EndsWith("_init.mp4") && decryptEngine != DecryptEngine.MP4DECRYPT) return false;
string cmd;
var tmpFile = "";
if (shakaPackager)
if (decryptEngine == DecryptEngine.SHAKA_PACKAGE)
{
var enc = source;
// shakaPackager 手动构造文件
if (init != "")
{
tmpFile = Path.ChangeExtension(source, ".itmp");
MergeUtil.CombineMultipleFilesIntoSingleFile(new string[] { init, source }, tmpFile);
MergeUtil.CombineMultipleFilesIntoSingleFile([init, source], tmpFile);
enc = tmpFile;
}
cmd = $"--quiet --enable_raw_key_decryption input=\"{enc}\",stream=0,output=\"{dest}\" " +
$"--keys {(trackId != null ? $"label={trackId}:" : "")}key_id={(trackId != null ? ZeroKid : kid)}:key={keyPair.Split(':')[1]}";
}
else
else if (decryptEngine == DecryptEngine.MP4DECRYPT)
{
if (trackId == null)
{
@ -81,6 +81,19 @@ internal static class MP4DecryptUtil
}
cmd += $" \"{source}\" \"{dest}\"";
}
else
{
var enc = source;
// ffmpeg实时解密 手动构造文件
if (init != "")
{
tmpFile = Path.ChangeExtension(source, ".itmp");
MergeUtil.CombineMultipleFilesIntoSingleFile([init, source], tmpFile);
enc = tmpFile;
}
cmd = $"-loglevel error -nostdin -decryption_key {keyPair.Split(':')[1]} -i \"{enc}\" -c copy \"{dest}\"";
}
await RunCommandAsync(bin, cmd);
@ -153,7 +166,7 @@ internal static class MP4DecryptUtil
{
using var fs = File.OpenRead(output);
var header = new byte[1 * 1024 * 1024]; // 1MB
fs.Read(header);
_ = fs.Read(header);
return GetMP4Info(header);
}