优化语言代码识别转换; 支持自动混流为mp4

This commit is contained in:
nilaoda 2022-08-22 22:56:58 +08:00
parent 4859c288ed
commit ba9fd870e2
8 changed files with 82 additions and 16 deletions

View File

@ -48,6 +48,7 @@ namespace N_m3u8DL_RE.Common.Resource
public static string cmd_useShakaPackager { get => GetText("cmd_useShakaPackager"); }
public static string cmd_useMkvmerge { get => GetText("cmd_useMkvmerge"); }
public static string cmd_muxAfterDone { get => GetText("cmd_muxAfterDone"); }
public static string cmd_muxToMp4 { get => GetText("cmd_muxToMp4"); }
public static string cmd_writeMetaJson { get => GetText("cmd_writeMetaJson"); }
public static string fetch { get => GetText("fetch"); }
public static string ffmpegMerge { get => GetText("ffmpegMerge"); }

View File

@ -240,9 +240,15 @@ namespace N_m3u8DL_RE.Common.Resource
),
["cmd_muxAfterDone"] = new TextContainer
(
zhCN: "所有工作完成时尝试使用ffmpeg混流分离的音视频(mkv)",
zhTW: "所有工作完成時嘗試使用ffmpeg混流分離的影音(mkv)",
enUS: "When all works is done, try to use ffmpeg to mux the separated audio(s) and video.(mkv)"
zhCN: "所有工作完成时尝试使用ffmpeg混流分离的音视频(默认容器: mkv)",
zhTW: "所有工作完成時嘗試使用ffmpeg混流分離的影音(默認容器: mkv)",
enUS: "When all works is done, try to use ffmpeg to mux the separated streams.(Default: mkv)"
),
["cmd_muxToMp4"] = new TextContainer
(
zhCN: "混流时使用mp4容器而非mkv",
zhTW: "混流時使用mp4容器而非mkv",
enUS: "Use mp4 container instead of mkv when muxing"
),
["cmd_writeMetaJson"] = new TextContainer
(

View File

@ -37,6 +37,7 @@ namespace N_m3u8DL_RE.CommandLine
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<bool> MuxAfterDone = new (new string[] { "--mux-after-done" }, description: ResString.cmd_muxAfterDone, getDefaultValue: () => false);
private readonly static Option<bool> MuxToMp4 = new (new string[] { "--mux-to-mp4" }, description: ResString.cmd_muxToMp4, getDefaultValue: () => false);
private readonly static Option<bool> UseMkvmerge = new(new string[] { "--use-mkvmerge" }, description: ResString.cmd_useMkvmerge, getDefaultValue: () => false);
private readonly static Option<string?> DecryptionBinaryPath = new(new string[] { "--decryption-binary-path" }, description: ResString.cmd_decryptionBinaryPath);
private readonly static Option<string?> FFmpegBinaryPath = new(new string[] { "--ffmpeg-binary-path" }, description: ResString.cmd_ffmpegBinaryPath);
@ -80,6 +81,7 @@ namespace N_m3u8DL_RE.CommandLine
MuxAfterDone = bindingContext.ParseResult.GetValueForOption(MuxAfterDone),
UseMkvmerge = bindingContext.ParseResult.GetValueForOption(UseMkvmerge),
BaseUrl = bindingContext.ParseResult.GetValueForOption(BaseUrl),
MuxToMp4 = bindingContext.ParseResult.GetValueForOption(MuxToMp4),
};
@ -101,9 +103,9 @@ namespace N_m3u8DL_RE.CommandLine
var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220822")
{
Input, TmpDir, SaveDir, SaveName, BaseUrl, ThreadCount, DownloadRetryCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
BinaryMerge, DelAfterDone, WriteMetaJson, MuxAfterDone, AppendUrlParams, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
BinaryMerge, DelAfterDone, WriteMetaJson, MuxAfterDone, MuxToMp4, UseMkvmerge, AppendUrlParams, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
FFmpegBinaryPath, MkvmergeBinaryPath,
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, UseMkvmerge, MP4RealTimeDecryption
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption
};
rootCommand.TreatUnmatchedTokensAsErrors = true;
rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder());

View File

@ -94,6 +94,10 @@ namespace N_m3u8DL_RE.CommandLine
/// </summary>
public bool MuxAfterDone { get; set; }
/// <summary>
/// See: <see cref="CommandInvoker.MuxToMp4"/>.
/// </summary>
public bool MuxToMp4 { get; set; }
/// <summary>
/// See: <see cref="CommandInvoker.UseMkvmerge"/>.
/// </summary>
public bool UseMkvmerge { get; set; }

View File

@ -36,6 +36,7 @@ namespace N_m3u8DL_RE.Config
MuxAfterDone = option.MuxAfterDone;
UseMkvmerge = option.UseMkvmerge;
MkvmergeBinaryPath = option.MkvmergeBinaryPath;
MuxToMp4 = option.MuxToMp4;
}
/// <summary>
@ -99,6 +100,10 @@ namespace N_m3u8DL_RE.Config
/// </summary>
public bool MuxAfterDone { get; set; }
/// <summary>
/// 自动混流音视频容器使用mp4
/// </summary>
public bool MuxToMp4 { get; set; }
/// <summary>
/// 使用mkvmerge混流
/// </summary>
public bool UseMkvmerge { get; set; }

View File

@ -280,7 +280,7 @@ namespace N_m3u8DL_RE.DownloadManager
//检测目标文件是否存在
while (File.Exists(output))
{
Logger.WarnMarkUp($"{output} => {output = Path.ChangeExtension(output, $"copy" + Path.GetExtension(output))}");
Logger.WarnMarkUp($"{Path.GetFileName(output)} => {Path.GetFileName(output = Path.ChangeExtension(output, $"copy" + Path.GetExtension(output)))}");
}
if (DownloaderConfig.MP4RealTimeDecryption && mp4InitFile != "")
@ -492,7 +492,7 @@ namespace N_m3u8DL_RE.DownloadManager
//检测目标文件是否存在
while (File.Exists(ffOut))
{
Logger.WarnMarkUp($"{ffOut} => {ffOut = Path.ChangeExtension(ffOut, $"copy" + Path.GetExtension(ffOut))}");
Logger.WarnMarkUp($"{Path.GetFileName(ffOut)} => {Path.GetFileName(ffOut = Path.ChangeExtension(ffOut, $"copy" + Path.GetExtension(ffOut)))}");
}
mergeSuccess = MergeUtil.MergeByFFmpeg(DownloaderConfig.FFmpegBinaryPath!, files, Path.ChangeExtension(ffOut, null), ext, useAACFilter);
if (mergeSuccess) output = ffOut;
@ -582,10 +582,10 @@ namespace N_m3u8DL_RE.DownloadManager
var saveDir = DownloaderConfig.SaveDir ?? Environment.CurrentDirectory;
var outName = $"{DownloaderConfig.SaveName ?? NowDateTime.ToString("yyyy-MM-dd_HH-mm-ss")}";
var outPath = Path.Combine(saveDir, outName);
Logger.WarnMarkUp($"Muxing to [grey]{outName.EscapeMarkup()}.mkv[/]");
Logger.WarnMarkUp($"Muxing to [grey]{outName.EscapeMarkup()}.{(DownloaderConfig.MuxToMp4 ? "mp4" : "mkv")}[/]");
var result = false;
if (DownloaderConfig.UseMkvmerge) result = MergeUtil.MuxInputsByMkvmerge(DownloaderConfig.MkvmergeBinaryPath!, OutputFiles.ToArray(), outPath);
else result = MergeUtil.MuxInputsByFFmpeg(DownloaderConfig.FFmpegBinaryPath!, OutputFiles.ToArray(), outPath);
else result = MergeUtil.MuxInputsByFFmpeg(DownloaderConfig.FFmpegBinaryPath!, OutputFiles.ToArray(), outPath, DownloaderConfig.MuxToMp4);
//完成后删除各轨道文件
if (result)
{

View File

@ -13,6 +13,7 @@ using N_m3u8DL_RE.Config;
using N_m3u8DL_RE.Util;
using N_m3u8DL_RE.DownloadManager;
using N_m3u8DL_RE.CommandLine;
using N_m3u8DL_RE.Entity;
namespace N_m3u8DL_RE
{
@ -20,6 +21,7 @@ namespace N_m3u8DL_RE
{
static async Task Main(string[] args)
{
Console.CursorVisible = true;
string loc = "en-US";
string currLoc = Thread.CurrentThread.CurrentUICulture.Name;
if (currLoc == "zh-CN" || currLoc == "zh-SG") loc = "zh-CN";
@ -47,6 +49,12 @@ namespace N_m3u8DL_RE
try
{
//检查互斥的选项
if (option.UseMkvmerge && option.MuxToMp4)
{
throw new ArgumentException("Can't use mkvmerge to make mp4!");
}
//预先检查ffmpeg
if (option.FFmpegBinaryPath == null)
option.FFmpegBinaryPath = GlobalUtil.FindExecutable("ffmpeg");

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.CommandLine;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -142,8 +143,9 @@ namespace N_m3u8DL_RE.Util
return false;
}
public static bool MuxInputsByFFmpeg(string binary, OutputFile[] files, string outputPath)
public static bool MuxInputsByFFmpeg(string binary, OutputFile[] files, string outputPath, bool mp4)
{
var ext = mp4 ? "mp4" : "mkv";
string dateString = DateTime.Now.ToString("o");
StringBuilder command = new StringBuilder("-loglevel warning -y ");
@ -159,22 +161,35 @@ namespace N_m3u8DL_RE.Util
command.Append($" -map {i} ");
}
if (mp4)
command.Append($" -c:a copy -c:v copy -c:s mov_text "); //mp4不支持vtt/srt字幕必须转换格式
else
command.Append($" -c copy ");
//CLEAN
command.Append(" -map_metadata -1 ");
//LANG and NAME
for (int i = 0; i < files.Length; i++)
{
command.Append($" -metadata:s:{i} language={files[i].LangCode ?? "und"} ");
//转换语言代码
ConvertLangCodeAndDisplayName(files[i]);
command.Append($" -metadata:s:{i} language=\"{files[i].LangCode ?? "und"}\" ");
if (!string.IsNullOrEmpty(files[i].Description))
command.Append($" -metadata:s:{i} title={files[i].Description} ");
{
if(mp4)
command.Append($" -metadata:s:{i} handler_name=\"{files[i].Description}\" -metadata:s:{i} handler=\"{files[i].Description}\" ");
else
command.Append($" -metadata:s:{i} title=\"{files[i].Description}\" ");
}
}
command.Append($" -metadata date=\"{dateString}\" -ignore_unknown -copy_unknown -c copy \"{outputPath}.mkv\"");
command.Append($" -metadata date=\"{dateString}\" -ignore_unknown -copy_unknown ");
command.Append($" \"{outputPath}.{ext}\"");
InvokeFFmpeg(binary, command.ToString(), Environment.CurrentDirectory);
if (File.Exists($"{outputPath}.mkv") && new FileInfo($"{outputPath}.mkv").Length > 1024)
if (File.Exists($"{outputPath}.{ext}") && new FileInfo($"{outputPath}.{ext}").Length > 1024)
return true;
return false;
@ -189,7 +204,9 @@ namespace N_m3u8DL_RE.Util
//LANG and NAME
for (int i = 0; i < files.Length; i++)
{
command.Append($" --language 0:{files[i].LangCode ?? "und"} ");
//转换语言代码
ConvertLangCodeAndDisplayName(files[i]);
command.Append($" --language 0:\"{files[i].LangCode ?? "und"}\" ");
if (!string.IsNullOrEmpty(files[i].Description))
command.Append($" --track-name 0:\"{files[i].Description}\" ");
command.Append($" \"{files[i].FilePath}\" ");
@ -202,5 +219,28 @@ namespace N_m3u8DL_RE.Util
return false;
}
/// <summary>
/// 转换 ISO 639-1 => ISO 639-2
/// 且当Description为空时将DisplayName写入
/// </summary>
/// <param name="outputFile"></param>
private static void ConvertLangCodeAndDisplayName(OutputFile outputFile)
{
if (string.IsNullOrEmpty(outputFile.LangCode)) return;
CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
foreach (var c in cultures)
{
if (outputFile.LangCode.Split('-')[0] == c.TwoLetterISOLanguageName)
{
outputFile.LangCode = c.ThreeLetterISOLanguageName;
if (string.IsNullOrEmpty(outputFile.Description))
{
outputFile.Description = c.DisplayName;
}
break;
}
}
}
}
}