支持调用`shaka-packager`解密
This commit is contained in:
parent
495d8508f5
commit
d6bbde3ca4
|
@ -29,13 +29,12 @@ namespace N_m3u8DL_RE.Common.Entity
|
||||||
StartRange == segment.StartRange &&
|
StartRange == segment.StartRange &&
|
||||||
StopRange == segment.StopRange &&
|
StopRange == segment.StopRange &&
|
||||||
ExpectLength == segment.ExpectLength &&
|
ExpectLength == segment.ExpectLength &&
|
||||||
EqualityComparer<EncryptInfo>.Default.Equals(EncryptInfo, segment.EncryptInfo) &&
|
|
||||||
Url == segment.Url;
|
Url == segment.Url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return HashCode.Combine(Index, Duration, Title, StartRange, StopRange, ExpectLength, EncryptInfo, Url);
|
return HashCode.Combine(Index, Duration, Title, StartRange, StopRange, ExpectLength, Url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,6 +123,15 @@ namespace N_m3u8DL_RE.Common.Resource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Full path to the tool used for MP4 decryption, like C:\Tools\mp4decrypt.exe 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string cmd_decryptionBinaryPath {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("cmd_decryptionBinaryPath", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Delete temporary files when done 的本地化字符串。
|
/// 查找类似 Delete temporary files when done 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -152,7 +161,7 @@ namespace N_m3u8DL_RE.Common.Resource {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Pass decryption key(s) to mp4decrypt. format:
|
/// 查找类似 Pass decryption key(s) to mp4decrypt/shaka-packager. format:
|
||||||
///--key KID1:KEY1 --key KID2:KEY2 的本地化字符串。
|
///--key KID1:KEY1 --key KID2:KEY2 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string cmd_keys {
|
public static string cmd_keys {
|
||||||
|
@ -287,6 +296,15 @@ namespace N_m3u8DL_RE.Common.Resource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Use shaka-packager instead of mp4decrypt 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string cmd_useShakaPackager {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("cmd_useShakaPackager", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Write meta json after parsed 的本地化字符串。
|
/// 查找类似 Write meta json after parsed 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -231,7 +231,7 @@
|
||||||
<value></value>
|
<value></value>
|
||||||
</data>
|
</data>
|
||||||
<data name="cmd_keys" xml:space="preserve">
|
<data name="cmd_keys" xml:space="preserve">
|
||||||
<value>Pass decryption key(s) to mp4decrypt. format:
|
<value>Pass decryption key(s) to mp4decrypt/shaka-packager. format:
|
||||||
--key KID1:KEY1 --key KID2:KEY2</value>
|
--key KID1:KEY1 --key KID2:KEY2</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="cmd_urlProcessorArgs" xml:space="preserve">
|
<data name="cmd_urlProcessorArgs" xml:space="preserve">
|
||||||
|
@ -240,4 +240,10 @@
|
||||||
<data name="cmd_MP4RealTimeDecryption" xml:space="preserve">
|
<data name="cmd_MP4RealTimeDecryption" xml:space="preserve">
|
||||||
<value>Decrypt MP4 segments in real time</value>
|
<value>Decrypt MP4 segments in real time</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="cmd_useShakaPackager" xml:space="preserve">
|
||||||
|
<value>Use shaka-packager instead of mp4decrypt</value>
|
||||||
|
</data>
|
||||||
|
<data name="cmd_decryptionBinaryPath" xml:space="preserve">
|
||||||
|
<value>Full path to the tool used for MP4 decryption, like C:\Tools\mp4decrypt.exe</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -251,7 +251,7 @@
|
||||||
<value>设置保存文件命名模板, 支持使用变量</value>
|
<value>设置保存文件命名模板, 支持使用变量</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="cmd_keys" xml:space="preserve">
|
<data name="cmd_keys" xml:space="preserve">
|
||||||
<value>设置解密密钥, 程序调用mp4decrpyt进行解密. 格式:
|
<value>设置解密密钥, 程序调用mp4decrpyt/shaka-packager进行解密. 格式:
|
||||||
--key KID1:KEY1 --key KID2:KEY2</value>
|
--key KID1:KEY1 --key KID2:KEY2</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="cmd_urlProcessorArgs" xml:space="preserve">
|
<data name="cmd_urlProcessorArgs" xml:space="preserve">
|
||||||
|
@ -260,4 +260,10 @@
|
||||||
<data name="cmd_MP4RealTimeDecryption" xml:space="preserve">
|
<data name="cmd_MP4RealTimeDecryption" xml:space="preserve">
|
||||||
<value>实时解密MP4分片</value>
|
<value>实时解密MP4分片</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="cmd_useShakaPackager" xml:space="preserve">
|
||||||
|
<value>使用shaka-packager替代mp4decrypt</value>
|
||||||
|
</data>
|
||||||
|
<data name="cmd_decryptionBinaryPath" xml:space="preserve">
|
||||||
|
<value>MP4解密所用工具的全路径, 例如 C:\Tools\mp4decrypt.exe</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -228,7 +228,7 @@
|
||||||
<value>將輸入Url的Params添加至分片, 對某些網站很有用, 例如 kakao.com</value>
|
<value>將輸入Url的Params添加至分片, 對某些網站很有用, 例如 kakao.com</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="cmd_keys" xml:space="preserve">
|
<data name="cmd_keys" xml:space="preserve">
|
||||||
<value>設置解密密鑰, 程序調用mp4decrpyt進行解密. 格式:
|
<value>設置解密密鑰, 程序調用mp4decrpyt/shaka-packager進行解密. 格式:
|
||||||
--key KID1:KEY1 --key KID2:KEY2</value>
|
--key KID1:KEY1 --key KID2:KEY2</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="cmd_urlProcessorArgs" xml:space="preserve">
|
<data name="cmd_urlProcessorArgs" xml:space="preserve">
|
||||||
|
@ -237,4 +237,10 @@
|
||||||
<data name="cmd_MP4RealTimeDecryption" xml:space="preserve">
|
<data name="cmd_MP4RealTimeDecryption" xml:space="preserve">
|
||||||
<value>實時解密MP4分片</value>
|
<value>實時解密MP4分片</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="cmd_useShakaPackager" xml:space="preserve">
|
||||||
|
<value>使用shaka-packager替代mp4decrypt</value>
|
||||||
|
</data>
|
||||||
|
<data name="cmd_decryptionBinaryPath" xml:space="preserve">
|
||||||
|
<value>MP4解密所用工具的全路徑, 例如 C:\Tools\mp4decrypt.exe</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -46,5 +46,19 @@ namespace N_m3u8DL_RE.Common.Util
|
||||||
str = (ts.Hours.ToString("00") == "00" ? "" : ts.Hours.ToString("00") + "h") + ts.Minutes.ToString("00") + "m" + ts.Seconds.ToString("00") + "s";
|
str = (ts.Hours.ToString("00") == "00" ? "" : ts.Hours.ToString("00") + "h") + ts.Minutes.ToString("00") + "m" + ts.Seconds.ToString("00") + "s";
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 寻找可执行程序
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string? FindExecutable(string name)
|
||||||
|
{
|
||||||
|
var fileExt = OperatingSystem.IsWindows() ? ".exe" : "";
|
||||||
|
var searchPath = new[] { Environment.CurrentDirectory, Environment.ProcessPath };
|
||||||
|
var envPath = Environment.GetEnvironmentVariable("PATH")?.Split(Path.PathSeparator) ??
|
||||||
|
Array.Empty<string>();
|
||||||
|
return searchPath.Concat(envPath).Select(p => Path.Combine(p, name + fileExt)).FirstOrDefault(File.Exists);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,8 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
private readonly static Option<bool> WriteMetaJson = new(new string[] { "--write-meta-json" }, description: ResString.cmd_writeMetaJson, getDefaultValue: () => true);
|
private readonly static Option<bool> WriteMetaJson = new(new string[] { "--write-meta-json" }, description: ResString.cmd_writeMetaJson, getDefaultValue: () => true);
|
||||||
private readonly static Option<bool> AppendUrlParams = new(new string[] { "--append-url-params" }, description: ResString.cmd_appendUrlParams, getDefaultValue: () => false);
|
private readonly static Option<bool> AppendUrlParams = new(new string[] { "--append-url-params" }, description: ResString.cmd_appendUrlParams, getDefaultValue: () => false);
|
||||||
private readonly static Option<bool> MP4RealTimeDecryption = new (new string[] { "--mp4-real-time-decryption" }, description: ResString.cmd_MP4RealTimeDecryption, getDefaultValue: () => false);
|
private readonly static Option<bool> MP4RealTimeDecryption = new (new string[] { "--mp4-real-time-decryption" }, description: ResString.cmd_MP4RealTimeDecryption, getDefaultValue: () => false);
|
||||||
|
private readonly static Option<bool> UseShakaPackager = new (new string[] { "--use-shaka-packager" }, description: ResString.cmd_useShakaPackager, getDefaultValue: () => false);
|
||||||
|
private readonly static Option<string?> DecryptionBinaryPath = new(new string[] { "--decryption-binary-path" }, description: ResString.cmd_decryptionBinaryPath);
|
||||||
|
|
||||||
class MyOptionBinder : BinderBase<MyOption>
|
class MyOptionBinder : BinderBase<MyOption>
|
||||||
{
|
{
|
||||||
|
@ -63,6 +65,8 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
Keys = bindingContext.ParseResult.GetValueForOption(Keys),
|
Keys = bindingContext.ParseResult.GetValueForOption(Keys),
|
||||||
UrlProcessorArgs = bindingContext.ParseResult.GetValueForOption(UrlProcessorArgs),
|
UrlProcessorArgs = bindingContext.ParseResult.GetValueForOption(UrlProcessorArgs),
|
||||||
MP4RealTimeDecryption = bindingContext.ParseResult.GetValueForOption(MP4RealTimeDecryption),
|
MP4RealTimeDecryption = bindingContext.ParseResult.GetValueForOption(MP4RealTimeDecryption),
|
||||||
|
UseShakaPackager = bindingContext.ParseResult.GetValueForOption(UseShakaPackager),
|
||||||
|
DecryptionBinaryPath = bindingContext.ParseResult.GetValueForOption(DecryptionBinaryPath),
|
||||||
};
|
};
|
||||||
|
|
||||||
//在这里设置语言
|
//在这里设置语言
|
||||||
|
@ -87,11 +91,11 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
|
|
||||||
public static async Task<int> InvokeArgs(string[] args, Func<MyOption, Task> action)
|
public static async Task<int> InvokeArgs(string[] args, Func<MyOption, Task> action)
|
||||||
{
|
{
|
||||||
var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220723")
|
var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220724")
|
||||||
{
|
{
|
||||||
Input, TmpDir, SaveDir, SaveName, ThreadCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
|
Input, TmpDir, SaveDir, SaveName, ThreadCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
|
||||||
BinaryMerge, DelAfterDone, WriteMetaJson, AppendUrlParams, Keys, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
|
BinaryMerge, DelAfterDone, WriteMetaJson, AppendUrlParams, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
|
||||||
LogLevel, UILanguage, UrlProcessorArgs, MP4RealTimeDecryption
|
LogLevel, UILanguage, UrlProcessorArgs, Keys, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption
|
||||||
};
|
};
|
||||||
rootCommand.TreatUnmatchedTokensAsErrors = true;
|
rootCommand.TreatUnmatchedTokensAsErrors = true;
|
||||||
rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder());
|
rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder());
|
||||||
|
|
|
@ -74,6 +74,10 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool MP4RealTimeDecryption { get; set; }
|
public bool MP4RealTimeDecryption { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// See: <see cref="CommandInvoker.UseShakaPackager"/>.
|
||||||
|
/// </summary>
|
||||||
|
public bool UseShakaPackager { get; set; }
|
||||||
|
/// <summary>
|
||||||
/// See: <see cref="CommandInvoker.SubtitleFormat"/>.
|
/// See: <see cref="CommandInvoker.SubtitleFormat"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SubtitleFormat SubtitleFormat { get; set; }
|
public SubtitleFormat SubtitleFormat { get; set; }
|
||||||
|
@ -97,5 +101,9 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
/// See: <see cref="CommandInvoker.UILanguage"/>.
|
/// See: <see cref="CommandInvoker.UILanguage"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? UILanguage { get; set; }
|
public string? UILanguage { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// See: <see cref="CommandInvoker.DecryptionBinaryPath"/>.
|
||||||
|
/// </summary>
|
||||||
|
public string? DecryptionBinaryPath { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -28,6 +28,8 @@ namespace N_m3u8DL_RE.Config
|
||||||
SavePattern = option.SavePattern;
|
SavePattern = option.SavePattern;
|
||||||
Keys = option.Keys;
|
Keys = option.Keys;
|
||||||
MP4RealTimeDecryption = option.MP4RealTimeDecryption;
|
MP4RealTimeDecryption = option.MP4RealTimeDecryption;
|
||||||
|
UseShakaPackager = option.UseShakaPackager;
|
||||||
|
DecryptionBinaryPath = option.DecryptionBinaryPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -79,6 +81,14 @@ namespace N_m3u8DL_RE.Config
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool MP4RealTimeDecryption { get; set; } = true;
|
public bool MP4RealTimeDecryption { get; set; } = true;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// 使用shaka-packager替代mp4decrypt
|
||||||
|
/// </summary>
|
||||||
|
public bool UseShakaPackager { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// MP4解密所用工具的全路径
|
||||||
|
/// </summary>
|
||||||
|
public string? DecryptionBinaryPath { get; set; }
|
||||||
|
/// <summary>
|
||||||
/// 字幕格式
|
/// 字幕格式
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SubtitleFormat SubtitleFormat { get; set; } = SubtitleFormat.VTT;
|
public SubtitleFormat SubtitleFormat { get; set; } = SubtitleFormat.VTT;
|
||||||
|
|
|
@ -52,13 +52,10 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
var output = Path.Combine(saveDir, saveName + $".{streamSpec.Extension ?? "ts"}");
|
var output = Path.Combine(saveDir, saveName + $".{streamSpec.Extension ?? "ts"}");
|
||||||
|
|
||||||
//mp4decrypt
|
//mp4decrypt
|
||||||
var APP_DIR = Path.GetDirectoryName(Environment.ProcessPath)!;
|
var mp4decrypt = DownloaderConfig.DecryptionBinaryPath!;
|
||||||
var fileName = "mp4decrypt";
|
|
||||||
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
|
|
||||||
fileName += ".exe";
|
|
||||||
var mp4decrypt = Path.Combine(APP_DIR, fileName);
|
|
||||||
if (!File.Exists(mp4decrypt)) mp4decrypt = fileName;
|
|
||||||
var mp4InitFile = "";
|
var mp4InitFile = "";
|
||||||
|
var mp4InitFileDec = "";
|
||||||
|
var currentKID = "";
|
||||||
|
|
||||||
Logger.Debug($"dirName: {dirName}; tmpDir: {tmpDir}; saveDir: {saveDir}; saveName: {saveName}; output: {output}");
|
Logger.Debug($"dirName: {dirName}; tmpDir: {tmpDir}; saveDir: {saveDir}; saveName: {saveName}; output: {output}");
|
||||||
|
|
||||||
|
@ -106,15 +103,18 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
if (info.Scheme != null) Logger.WarnMarkUp($"[grey]Type: {info.Scheme}[/]");
|
if (info.Scheme != null) Logger.WarnMarkUp($"[grey]Type: {info.Scheme}[/]");
|
||||||
if (info.PSSH != null) Logger.WarnMarkUp($"[grey]PSSH(WV): {info.PSSH}[/]");
|
if (info.PSSH != null) Logger.WarnMarkUp($"[grey]PSSH(WV): {info.PSSH}[/]");
|
||||||
if (info.KID != null) Logger.WarnMarkUp($"[grey]KID: {info.KID}[/]");
|
if (info.KID != null) Logger.WarnMarkUp($"[grey]KID: {info.KID}[/]");
|
||||||
|
currentKID = info.KID;
|
||||||
//实时解密
|
//实时解密
|
||||||
if (DownloaderConfig.MP4RealTimeDecryption && streamSpec.Playlist.MediaInit.EncryptInfo.Method != Common.Enum.EncryptMethod.NONE)
|
if (DownloaderConfig.MP4RealTimeDecryption && streamSpec.Playlist.MediaInit.EncryptInfo.Method != Common.Enum.EncryptMethod.NONE)
|
||||||
{
|
{
|
||||||
var enc = result.ActualFilePath;
|
var enc = result.ActualFilePath;
|
||||||
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
|
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
|
||||||
var dResult = await MP4DecryptUtil.DecryptAsync(mp4decrypt, DownloaderConfig.Keys, enc, dec);
|
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.UseShakaPackager, mp4decrypt, DownloaderConfig.Keys, enc, dec, currentKID);
|
||||||
if (dResult)
|
if (dResult)
|
||||||
{
|
{
|
||||||
result.ActualFilePath = dec;
|
//实时解密不需要init文件用于合并
|
||||||
|
FileDic!.Remove(streamSpec.Playlist.MediaInit, out _);
|
||||||
|
mp4InitFileDec = dec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
{
|
{
|
||||||
var enc = result.ActualFilePath;
|
var enc = result.ActualFilePath;
|
||||||
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
|
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
|
||||||
var dResult = await MP4DecryptUtil.DecryptAsync(mp4decrypt, DownloaderConfig.Keys, enc, dec, mp4InitFile);
|
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.UseShakaPackager, mp4decrypt, DownloaderConfig.Keys, enc, dec, currentKID, mp4InitFile);
|
||||||
if (dResult)
|
if (dResult)
|
||||||
{
|
{
|
||||||
File.Delete(enc);
|
File.Delete(enc);
|
||||||
|
@ -147,9 +147,11 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (DownloaderConfig.MP4RealTimeDecryption && mp4InitFile != "")
|
if (DownloaderConfig.MP4RealTimeDecryption && mp4InitFile != "")
|
||||||
{
|
{
|
||||||
File.Delete(mp4InitFile);
|
File.Delete(mp4InitFile);
|
||||||
|
if (mp4InitFileDec != "")
|
||||||
|
File.Delete(mp4InitFileDec);
|
||||||
}
|
}
|
||||||
|
|
||||||
//校验分片数量
|
//校验分片数量
|
||||||
|
@ -345,7 +347,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
}
|
}
|
||||||
|
|
||||||
//删除临时文件夹
|
//删除临时文件夹
|
||||||
if (DownloaderConfig.DelAfterDone)
|
if (!DownloaderConfig.SkipMerge && DownloaderConfig.DelAfterDone)
|
||||||
{
|
{
|
||||||
var files = FileDic.Values.Select(v => v!.ActualFilePath);
|
var files = FileDic.Values.Select(v => v!.ActualFilePath);
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
|
@ -359,14 +361,14 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
}
|
}
|
||||||
|
|
||||||
//调用mp4decrypt解密
|
//调用mp4decrypt解密
|
||||||
if (!DownloaderConfig.MP4RealTimeDecryption && DownloaderConfig.Keys != null && DownloaderConfig.Keys.Length > 0)
|
if (File.Exists(output) && !DownloaderConfig.MP4RealTimeDecryption && DownloaderConfig.Keys != null && DownloaderConfig.Keys.Length > 0)
|
||||||
{
|
{
|
||||||
if (totalCount > 1 && streamSpec.Playlist!.MediaParts.First().MediaSegments.First().EncryptInfo.Method != Common.Enum.EncryptMethod.NONE)
|
if (totalCount > 1 && streamSpec.Playlist!.MediaParts.First().MediaSegments.First().EncryptInfo.Method != Common.Enum.EncryptMethod.NONE)
|
||||||
{
|
{
|
||||||
var enc = output;
|
var enc = output;
|
||||||
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
|
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
|
||||||
Logger.InfoMarkUp($"[grey]Decrypting...[/]");
|
Logger.InfoMarkUp($"[grey]Decrypting...[/]");
|
||||||
var result = await MP4DecryptUtil.DecryptAsync(mp4decrypt, DownloaderConfig.Keys, enc, dec);
|
var result = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.UseShakaPackager, mp4decrypt, DownloaderConfig.Keys, enc, dec, currentKID);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
File.Delete(enc);
|
File.Delete(enc);
|
||||||
|
|
|
@ -38,6 +38,33 @@ namespace N_m3u8DL_RE
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
//预先检查
|
||||||
|
if (option.Keys != null && option.Keys.Length > 0)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(option.DecryptionBinaryPath))
|
||||||
|
{
|
||||||
|
if (option.UseShakaPackager)
|
||||||
|
{
|
||||||
|
var file = GlobalUtil.FindExecutable("shaka-packager");
|
||||||
|
var file2 = GlobalUtil.FindExecutable("packager-linux-x64");
|
||||||
|
var file3 = GlobalUtil.FindExecutable("packager-osx-x64");
|
||||||
|
var file4 = GlobalUtil.FindExecutable("packager-win-x64");
|
||||||
|
if (file == null && file2 == null && file3 == null && file4 == null) throw new FileNotFoundException("shaka-packager not found!");
|
||||||
|
option.DecryptionBinaryPath = file ?? file2 ?? file3 ?? file4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var file = GlobalUtil.FindExecutable("mp4decrypt");
|
||||||
|
if (file == null) throw new FileNotFoundException("mp4decrypt not found!");
|
||||||
|
option.DecryptionBinaryPath = file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!File.Exists(option.DecryptionBinaryPath))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException(option.DecryptionBinaryPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var parserConfig = new ParserConfig()
|
var parserConfig = new ParserConfig()
|
||||||
{
|
{
|
||||||
AppendUrlParams = option.AppendUrlParams,
|
AppendUrlParams = option.AppendUrlParams,
|
||||||
|
|
|
@ -1,41 +1,73 @@
|
||||||
using N_m3u8DL_RE.Config;
|
using System.Diagnostics;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace N_m3u8DL_RE.Util
|
namespace N_m3u8DL_RE.Util
|
||||||
{
|
{
|
||||||
internal class MP4DecryptUtil
|
internal class MP4DecryptUtil
|
||||||
{
|
{
|
||||||
public static async Task<bool> DecryptAsync(string bin, string[]? keys, string source, string dest, string init = "")
|
public static async Task<bool> DecryptAsync(bool shakaPackager, string bin, string[]? keys, string source, string dest, string? kid, string init = "")
|
||||||
{
|
{
|
||||||
if (keys == null || keys.Length == 0) return false;
|
if (keys == null || keys.Length == 0) return false;
|
||||||
|
|
||||||
var cmd = string.Join(" ", keys.Select(k => $"--key {k}"));
|
var keyPair = keys.First();
|
||||||
if (init != "")
|
if (!string.IsNullOrEmpty(kid))
|
||||||
{
|
{
|
||||||
cmd += $" --fragments-info \"{init}\" ";
|
var test = keys.Where(k => k.StartsWith(kid));
|
||||||
|
if (test.Any()) keyPair = test.First();
|
||||||
}
|
}
|
||||||
cmd += $" \"{source}\" \"{dest}\"";
|
|
||||||
|
|
||||||
await Process.Start(new ProcessStartInfo()
|
if (keyPair == null) return false;
|
||||||
|
|
||||||
|
//shakaPackager 无法单独解密init文件
|
||||||
|
if (source.EndsWith("_init.mp4") && shakaPackager) return true;
|
||||||
|
|
||||||
|
var cmd = "";
|
||||||
|
|
||||||
|
var tmpFile = "";
|
||||||
|
if (shakaPackager)
|
||||||
{
|
{
|
||||||
FileName = bin,
|
var enc = source;
|
||||||
Arguments = cmd,
|
//shakaPackager 手动构造文件
|
||||||
RedirectStandardOutput = true,
|
if (init != "")
|
||||||
RedirectStandardError = true,
|
{
|
||||||
UseShellExecute = false
|
tmpFile = Path.ChangeExtension(source, ".itmp");
|
||||||
})!.WaitForExitAsync();
|
DownloadUtil.CombineMultipleFilesIntoSingleFile(new string[] { init, source }, tmpFile);
|
||||||
|
enc = tmpFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = $"--enable_raw_key_decryption input=\"{enc}\",stream=0,output=\"{dest}\" " +
|
||||||
|
$"--keys key_id={keyPair.Split(':')[0]}:key={keyPair.Split(':')[1]}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmd = string.Join(" ", keys.Select(k => $"--key {k}"));
|
||||||
|
if (init != "")
|
||||||
|
{
|
||||||
|
cmd += $" --fragments-info \"{init}\" ";
|
||||||
|
}
|
||||||
|
cmd += $" \"{source}\" \"{dest}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
await RunCommandAsync(bin, cmd);
|
||||||
|
|
||||||
if (File.Exists(dest) && new FileInfo(dest).Length > 0)
|
if (File.Exists(dest) && new FileInfo(dest).Length > 0)
|
||||||
{
|
{
|
||||||
|
if (tmpFile != "" && File.Exists(tmpFile)) File.Delete(tmpFile);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task RunCommandAsync(string name, string arg)
|
||||||
|
{
|
||||||
|
await Process.Start(new ProcessStartInfo()
|
||||||
|
{
|
||||||
|
FileName = name,
|
||||||
|
Arguments = arg,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
UseShellExecute = false
|
||||||
|
})!.WaitForExitAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue