允许实时调用mp4decrpyt解密

This commit is contained in:
nilaoda 2022-07-23 12:27:26 +08:00
parent 306eff7079
commit 495d8508f5
9 changed files with 124 additions and 23 deletions

View File

@ -170,6 +170,15 @@ namespace N_m3u8DL_RE.Common.Resource {
}
}
/// <summary>
/// 查找类似 Decrypt MP4 segments in real time 的本地化字符串。
/// </summary>
public static string cmd_MP4RealTimeDecryption {
get {
return ResourceManager.GetString("cmd_MP4RealTimeDecryption", resourceCulture);
}
}
/// <summary>
/// 查找类似 Set output directory 的本地化字符串。
/// </summary>

View File

@ -237,4 +237,7 @@
<data name="cmd_urlProcessorArgs" xml:space="preserve">
<value>Give these arguments to the URL Processors.</value>
</data>
<data name="cmd_MP4RealTimeDecryption" xml:space="preserve">
<value>Decrypt MP4 segments in real time</value>
</data>
</root>

View File

@ -257,4 +257,7 @@
<data name="cmd_urlProcessorArgs" xml:space="preserve">
<value>此字符串将直接传递给URL Processor</value>
</data>
<data name="cmd_MP4RealTimeDecryption" xml:space="preserve">
<value>实时解密MP4分片</value>
</data>
</root>

View File

@ -234,4 +234,7 @@
<data name="cmd_urlProcessorArgs" xml:space="preserve">
<value>此字符串將直接傳遞給URL Processor</value>
</data>
<data name="cmd_MP4RealTimeDecryption" xml:space="preserve">
<value>實時解密MP4分片</value>
</data>
</root>

View File

@ -12,8 +12,8 @@ namespace N_m3u8DL_RE.CommandLine
{
private readonly static Argument<string> Input = new(name: "input", description: ResString.cmd_Input);
private readonly static Option<string?> TmpDir = new(new string[] { "--tmp-dir" }, description: ResString.cmd_tmpDir);
private readonly static Option<string?> SaveDir = new(new string[] { "--save-dir", "-o" }, description: ResString.cmd_saveDir);
private readonly static Option<string?> SaveName = new(new string[] { "--save-name", "-O" }, description: ResString.cmd_saveName);
private readonly static Option<string?> SaveDir = new(new string[] { "--save-dir" }, description: ResString.cmd_saveDir);
private readonly static Option<string?> SaveName = new(new string[] { "--save-name" }, description: ResString.cmd_saveName);
private readonly static Option<string?> SavePattern = new(new string[] { "--save-pattern" }, description: ResString.cmd_savePattern, getDefaultValue: () => "<SaveName>_<Id>_<Codecs>_<Language>_<Ext>");
private readonly static Option<string?> UILanguage = new(new string[] { "--ui-language" }, description: ResString.cmd_uiLanguage);
private readonly static Option<string?> UrlProcessorArgs = new(new string[] { "--urlprocessor-args" }, description: ResString.cmd_urlProcessorArgs);
@ -32,6 +32,7 @@ namespace N_m3u8DL_RE.CommandLine
private readonly static Option<bool> CheckSegmentsCount = new(new string[] { "--check-segments-count" }, description: ResString.cmd_checkSegmentsCount, 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> MP4RealTimeDecryption = new (new string[] { "--mp4-real-time-decryption" }, description: ResString.cmd_MP4RealTimeDecryption, getDefaultValue: () => false);
class MyOptionBinder : BinderBase<MyOption>
{
@ -61,6 +62,7 @@ namespace N_m3u8DL_RE.CommandLine
SavePattern = bindingContext.ParseResult.GetValueForOption(SavePattern),
Keys = bindingContext.ParseResult.GetValueForOption(Keys),
UrlProcessorArgs = bindingContext.ParseResult.GetValueForOption(UrlProcessorArgs),
MP4RealTimeDecryption = bindingContext.ParseResult.GetValueForOption(MP4RealTimeDecryption),
};
//在这里设置语言
@ -85,11 +87,11 @@ namespace N_m3u8DL_RE.CommandLine
public static async Task<int> InvokeArgs(string[] args, Func<MyOption, Task> action)
{
var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220721")
var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220723")
{
Input, TmpDir, SaveDir, SaveName, ThreadCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
BinaryMerge, DelAfterDone, WriteMetaJson, AppendUrlParams, Keys, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
LogLevel, UILanguage, UrlProcessorArgs
LogLevel, UILanguage, UrlProcessorArgs, MP4RealTimeDecryption
};
rootCommand.TreatUnmatchedTokensAsErrors = true;
rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder());

View File

@ -70,6 +70,10 @@ namespace N_m3u8DL_RE.CommandLine
/// </summary>
public bool AppendUrlParams { get; set; }
/// <summary>
/// See: <see cref="CommandInvoker.MP4RealTimeDecryption"/>.
/// </summary>
public bool MP4RealTimeDecryption { get; set; }
/// <summary>
/// See: <see cref="CommandInvoker.SubtitleFormat"/>.
/// </summary>
public SubtitleFormat SubtitleFormat { get; set; }

View File

@ -27,6 +27,7 @@ namespace N_m3u8DL_RE.Config
ThreadCount = option.ThreadCount;
SavePattern = option.SavePattern;
Keys = option.Keys;
MP4RealTimeDecryption = option.MP4RealTimeDecryption;
}
/// <summary>
@ -74,6 +75,10 @@ namespace N_m3u8DL_RE.Config
/// </summary>
public bool AutoSubtitleFix { get; set; } = true;
/// <summary>
/// MP4实时解密
/// </summary>
public bool MP4RealTimeDecryption { get; set; } = true;
/// <summary>
/// 字幕格式
/// </summary>
public SubtitleFormat SubtitleFormat { get; set; } = SubtitleFormat.VTT;

View File

@ -51,6 +51,15 @@ namespace N_m3u8DL_RE.DownloadManager
var headers = DownloaderConfig.Headers;
var output = Path.Combine(saveDir, saveName + $".{streamSpec.Extension ?? "ts"}");
//mp4decrypt
var APP_DIR = Path.GetDirectoryName(Environment.ProcessPath)!;
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 = "";
Logger.Debug($"dirName: {dirName}; tmpDir: {tmpDir}; saveDir: {saveDir}; saveName: {saveName}; output: {output}");
//创建文件夹
@ -76,12 +85,20 @@ namespace N_m3u8DL_RE.DownloadManager
var path = Path.Combine(tmpDir, "_init.mp4.tmp");
var result = await Downloader.DownloadSegmentAsync(streamSpec.Playlist.MediaInit, path, headers);
FileDic[streamSpec.Playlist.MediaInit] = result;
if (result == null)
{
throw new Exception("Download init file failed!");
}
mp4InitFile = result.ActualFilePath;
task.Increment(1);
//修改输出后缀
if (streamSpec.MediaType == Common.Enum.MediaType.AUDIO)
output = Path.ChangeExtension(output, ".m4a");
else
output = Path.ChangeExtension(output, ".mp4");
//读取mp4信息
if (result != null && result.Success)
{
var data = File.ReadAllBytes(result.ActualFilePath);
@ -89,6 +106,17 @@ namespace N_m3u8DL_RE.DownloadManager
if (info.Scheme != null) Logger.WarnMarkUp($"[grey]Type: {info.Scheme}[/]");
if (info.PSSH != null) Logger.WarnMarkUp($"[grey]PSSH(WV): {info.PSSH}[/]");
if (info.KID != null) Logger.WarnMarkUp($"[grey]KID: {info.KID}[/]");
//实时解密
if (DownloaderConfig.MP4RealTimeDecryption && streamSpec.Playlist.MediaInit.EncryptInfo.Method != Common.Enum.EncryptMethod.NONE)
{
var enc = result.ActualFilePath;
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
var dResult = await MP4DecryptUtil.DecryptAsync(mp4decrypt, DownloaderConfig.Keys, enc, dec);
if (dResult)
{
result.ActualFilePath = dec;
}
}
}
}
@ -105,8 +133,25 @@ namespace N_m3u8DL_RE.DownloadManager
var result = await Downloader.DownloadSegmentAsync(seg, path, headers);
FileDic[seg] = result;
task.Increment(1);
//实时解密
if (DownloaderConfig.MP4RealTimeDecryption && seg.EncryptInfo.Method != Common.Enum.EncryptMethod.NONE && result != null)
{
var enc = result.ActualFilePath;
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);
if (dResult)
{
File.Delete(enc);
result.ActualFilePath = dec;
}
}
});
if (DownloaderConfig.MP4RealTimeDecryption && mp4InitFile != "")
{
File.Delete(mp4InitFile);
}
//校验分片数量
if (DownloaderConfig.CheckSegmentsCount && FileDic.Values.Any(s => s == null))
{
@ -313,30 +358,16 @@ namespace N_m3u8DL_RE.DownloadManager
}
}
if (DownloaderConfig.Keys != null && DownloaderConfig.Keys.Length > 0)
//调用mp4decrypt解密
if (!DownloaderConfig.MP4RealTimeDecryption && DownloaderConfig.Keys != null && DownloaderConfig.Keys.Length > 0)
{
var APP_DIR = Path.GetDirectoryName(Environment.ProcessPath)!;
var fileName = "mp4decrypt";
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
fileName += ".exe";
var mp4decrypt = Path.Combine(APP_DIR, fileName);
if (!File.Exists(mp4decrypt)) mp4decrypt = fileName;
if (streamSpec.Playlist!.MediaParts.First().MediaSegments.First().EncryptInfo != null
&& 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 dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
var cmd = string.Join(" ", DownloaderConfig.Keys.Select(k => $"--key {k}")) + $" \"{enc}\" \"{dec}\"";
Logger.InfoMarkUp($"[grey]Decrypting...[/]");
await Process.Start(new ProcessStartInfo()
{
FileName = mp4decrypt,
Arguments = cmd,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
})!.WaitForExitAsync();
if (File.Exists(dec) && new FileInfo(dec).Length > 0)
var result = await MP4DecryptUtil.DecryptAsync(mp4decrypt, DownloaderConfig.Keys, enc, dec);
if (result)
{
File.Delete(enc);
output = dec;

View File

@ -0,0 +1,41 @@
using N_m3u8DL_RE.Config;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace N_m3u8DL_RE.Util
{
internal class MP4DecryptUtil
{
public static async Task<bool> DecryptAsync(string bin, string[]? keys, string source, string dest, string init = "")
{
if (keys == null || keys.Length == 0) return false;
var cmd = string.Join(" ", keys.Select(k => $"--key {k}"));
if (init != "")
{
cmd += $" --fragments-info \"{init}\" ";
}
cmd += $" \"{source}\" \"{dest}\"";
await Process.Start(new ProcessStartInfo()
{
FileName = bin,
Arguments = cmd,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
})!.WaitForExitAsync();
if (File.Exists(dest) && new FileInfo(dest).Length > 0)
{
return true;
}
return false;
}
}
}