优化异常处理
This commit is contained in:
parent
3a9784a5f9
commit
3013c7895f
|
@ -8,6 +8,7 @@ using N_m3u8DL_RE.Util;
|
|||
using NiL.JS.Expressions;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Binding;
|
||||
using System.CommandLine.Builder;
|
||||
using System.CommandLine.Parsing;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
|
@ -17,7 +18,7 @@ namespace N_m3u8DL_RE.CommandLine
|
|||
{
|
||||
internal partial class CommandInvoker
|
||||
{
|
||||
public const string VERSION_INFO = "N_m3u8DL-RE (Beta version) 20221029";
|
||||
public const string VERSION_INFO = "N_m3u8DL-RE (Beta version) 20221030";
|
||||
|
||||
[GeneratedRegex("((best|worst)\\d*|all)")]
|
||||
private static partial Regex ForStrRegex();
|
||||
|
@ -450,7 +451,22 @@ namespace N_m3u8DL_RE.CommandLine
|
|||
rootCommand.TreatUnmatchedTokensAsErrors = true;
|
||||
rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder());
|
||||
|
||||
return await rootCommand.InvokeAsync(args);
|
||||
var parser = new CommandLineBuilder(rootCommand)
|
||||
.UseDefaults()
|
||||
.EnablePosixBundling(false)
|
||||
.UseExceptionHandler((ex, context) =>
|
||||
{
|
||||
try { Console.CursorVisible = true; } catch { }
|
||||
string msg = Logger.LogLevel == Common.Log.LogLevel.DEBUG ? ex.ToString() : ex.Message;
|
||||
#if DEBUG
|
||||
msg = ex.ToString();
|
||||
#endif
|
||||
Logger.Error(msg);
|
||||
Thread.Sleep(3000);
|
||||
}, 1)
|
||||
.Build();
|
||||
|
||||
return await parser.InvokeAsync(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,272 +78,260 @@ namespace N_m3u8DL_RE
|
|||
HTTPUtil.HttpClientHandler.UseProxy = true;
|
||||
}
|
||||
|
||||
try
|
||||
//检查互斥的选项
|
||||
|
||||
if (!option.MuxAfterDone && option.MuxImports != null && option.MuxImports.Count > 0)
|
||||
{
|
||||
//检查互斥的选项
|
||||
throw new ArgumentException("MuxAfterDone disabled, MuxImports not allowed!");
|
||||
}
|
||||
|
||||
if (!option.MuxAfterDone && option.MuxImports != null && option.MuxImports.Count > 0)
|
||||
//预先检查ffmpeg
|
||||
if (option.FFmpegBinaryPath == null)
|
||||
option.FFmpegBinaryPath = GlobalUtil.FindExecutable("ffmpeg");
|
||||
|
||||
if (string.IsNullOrEmpty(option.FFmpegBinaryPath) || !File.Exists(option.FFmpegBinaryPath))
|
||||
{
|
||||
throw new FileNotFoundException(ResString.ffmpegNotFound);
|
||||
}
|
||||
|
||||
//预先检查mkvmerge
|
||||
if (option.UseMkvmerge && option.MuxAfterDone)
|
||||
{
|
||||
if (option.MkvmergeBinaryPath == null)
|
||||
option.MkvmergeBinaryPath = GlobalUtil.FindExecutable("mkvmerge");
|
||||
if (string.IsNullOrEmpty(option.MkvmergeBinaryPath) || !File.Exists(option.MkvmergeBinaryPath))
|
||||
{
|
||||
throw new ArgumentException("MuxAfterDone disabled, MuxImports not allowed!");
|
||||
}
|
||||
|
||||
//预先检查ffmpeg
|
||||
if (option.FFmpegBinaryPath == null)
|
||||
option.FFmpegBinaryPath = GlobalUtil.FindExecutable("ffmpeg");
|
||||
|
||||
if (string.IsNullOrEmpty(option.FFmpegBinaryPath) || !File.Exists(option.FFmpegBinaryPath))
|
||||
{
|
||||
throw new FileNotFoundException(ResString.ffmpegNotFound);
|
||||
}
|
||||
|
||||
//预先检查mkvmerge
|
||||
if (option.UseMkvmerge && option.MuxAfterDone)
|
||||
{
|
||||
if (option.MkvmergeBinaryPath == null)
|
||||
option.MkvmergeBinaryPath = GlobalUtil.FindExecutable("mkvmerge");
|
||||
if (string.IsNullOrEmpty(option.MkvmergeBinaryPath) || !File.Exists(option.MkvmergeBinaryPath))
|
||||
{
|
||||
throw new FileNotFoundException("mkvmerge not found");
|
||||
}
|
||||
}
|
||||
|
||||
//预先检查
|
||||
if ((option.Keys != null && option.Keys.Length > 0) || option.KeyTextFile != null)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
//默认的headers
|
||||
var headers = new Dictionary<string, string>()
|
||||
{
|
||||
["user-agent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
|
||||
};
|
||||
//添加或替换用户输入的headers
|
||||
foreach (var item in option.Headers)
|
||||
{
|
||||
headers[item.Key] = item.Value;
|
||||
}
|
||||
|
||||
var parserConfig = new ParserConfig()
|
||||
{
|
||||
AppendUrlParams = option.AppendUrlParams,
|
||||
UrlProcessorArgs = option.UrlProcessorArgs,
|
||||
BaseUrl = option.BaseUrl!,
|
||||
Headers = headers,
|
||||
CustomMethod = option.CustomHLSMethod,
|
||||
CustomeKey = option.CustomHLSKey,
|
||||
CustomeIV = option.CustomHLSIv,
|
||||
};
|
||||
|
||||
//demo1
|
||||
parserConfig.ContentProcessors.Insert(0, new DemoProcessor());
|
||||
//demo2
|
||||
parserConfig.KeyProcessors.Insert(0, new DemoProcessor2());
|
||||
//for www.nowehoryzonty.pl
|
||||
parserConfig.UrlProcessors.Insert(0, new NowehoryzontyUrlProcessor());
|
||||
|
||||
var url = string.Empty;
|
||||
//url = "https://vod-ftc-eu-west-1.media.dssott.com/ps01/disney/29a73209-b706-4f21-8384-acddccb154d2/ctr-all-6cf6fec6-94dc-4f8b-ae67-5ada60ee1e83-42bd5bca-f9a8-4299-83e3-0fb2b4ec0a62.m3u8"; //迪士尼
|
||||
//url = "https://play.itunes.apple.com/WebObjects/MZPlay.woa/hls/subscription/playlist.m3u8?cc=US&svcId=tvs.vds.4105&a=1580273278&isExternal=true&brandId=tvs.sbd.4000&id=337246031&l=en-US&aec=UHD&xtrick=true&webbrowser=true"; //啥都有
|
||||
//url = "https://media.axprod.net/TestVectors/v7-Clear/Manifest_1080p.mpd"; //多音轨多字幕
|
||||
//url = "https://cmafref.akamaized.net/cmaf/live-ull/2006350/akambr/out.mpd"; //直播
|
||||
//url = "http://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8";
|
||||
//url = "https://vod.sdn.wavve.com/hls/S01/S01_E461382925.1/1/5000/chunklist.m3u8";
|
||||
url = "https://bitmovin-a.akamaihd.net/content/art-of-motion_drm/mpds/11331.mpd";
|
||||
//url = "http://tv-live.ynkmit.com/tv/anning.m3u8?txSecret=7528f35fb4b62bd24d55b891899db68f&txTime=632C8680"; //直播
|
||||
//url = "https://rest-as.ott.kaltura.com/api_v3/service/assetFile/action/playManifest/partnerId/147/assetId/1304099/assetType/media/assetFileId/16136929/contextType/PLAYBACK/isAltUrl/False/ks/djJ8MTQ3fMusTFH6PCZpcrfKLQwI-pPm9ex6b6r49wioe32WH2udXeM4reyWIkSDpi7HhvhxBHAHAKiHrcnkmIJQpyAt4MuDBG0ywGQ-jOeqQFcTRQ8BGJGw6g-smSBLwSbo4CCx9M9vWNJX3GkOfhoMAY4yRU-ur3okHiVq1mUJ82XBd_iVqLuzodnc9sJEtcHH0zc5CoPiTq2xor-dq3yDURnZm3isfSN3t9uLIJEW09oE-SJ84DM5GUuFUdbnIV8bdcWUsPicUg-Top1G2D3WcWXq4EvPnwvD8jrC_vsiOpLHf5akAwtdGsJ6__cXUmT7a-QlfjdvaZ5T8UhDLnttHmsxYs2E5c0lh4uOvvJou8dD8iYxUexlPI2j4QUkBRxqOEVLSNV3Y82-5TTRqgnK_uGYXHwk7EAmDws7hbLj2-DJ1heXDcye3OJYdunJgAS-9ma5zmQQNiY_HYh6wj2N1HpCTNAtWWga6R9fC0VgBTZbidW-YwMSGzIvMQfIfWKe15X7Oc_hCs-zGfW9XeRJZrutcWKK_D_HlzpQVBF2vIF3XgaI/a.mpd";
|
||||
//url = "https://dash.akamaized.net/dash264/TestCases/2c/qualcomm/1/MultiResMPEG2.mpd";
|
||||
url = "https://cmaf.lln.latam.hbomaxcdn.com/videos/GYPGKMQjoDkVLBQEAAAAo/1/1b5ad5/1_single_J8sExA_1080hi.mpd";
|
||||
//url = "https://livesim.dashif.org/dash/vod/testpic_2s/multi_subs.mpd"; //ttml + mp4
|
||||
//url = "http://media.axprod.net/TestVectors/v6-Clear/Manifest_1080p.mpd"; //vtt + mp4
|
||||
//url = "https://livesim.dashif.org/dash/vod/testpic_2s/xml_subs.mpd"; //ttml
|
||||
//url = "https://storage.googleapis.com/shaka-demo-assets/angel-one-hls/hls.m3u8"; //HLS vtt
|
||||
//url = "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_adv_example_hevc/master.m3u8"; //高级HLS fMP4+VTT
|
||||
//url = "https://events-delivery.apple.com/0205eyyhwbbqexozkwmgccegwnjyrktg/m3u8/vod_index-dpyfrsVksFWjneFiptbXnAMYBtGYbXeZ.m3u8"; //高级HLS fMP4+VTT
|
||||
//url = "https://apionvod5.seezntv.com/ktmain1/cold/CP/55521/202207/media/MIAM61RPSGL150000100_DRM/MIAM61RPSGL150000100_H.m3u8?sid=0000000F50000040000A700000020000";
|
||||
//url = "https://ewcdn12.nowe.com/session/16-5-72579e3-2103014898783810281/Content/DASH_VOS3/VOD/6908/19585/d2afa5fe-e9c8-40f0-8d18-648aaaf292b6/f677841a-9d8f-2ff5-3517-674ba49ef192/manifest.mpd?token=894db5d69931835f82dd8e393974ef9f_1658146180";
|
||||
//url = "https://ols-ww100-cp.akamaized.net/manifest/master/06ee6f68-ee80-11ea-9bc5-02b68fb543c4/65794a72596d6c30496a6f7a4e6a67324e4441774d444173496e42735958526d62334a74496a6f695a47567a6133527663434973496d526c646d6c6a5a565235634755694f694a335a5749694c434a746232526c62434936496e6470626d527664334d694c434a7663315235634755694f694a6a61484a76625755694c434a7663794936496a45774d6934774c6a41694c434a68634841694f69497a4c6a416966513d3d/dash.mpd?cpatoken=exp=1658223027~acl=/manifest/master/06ee6f68-ee80-11ea-9bc5-02b68fb543c4/*~hmac=644c608aac361f688e9b24b0f345c801d0f2d335819431d1873ff7aeac46d6b2&access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXZpY2VfaWQiOm51bGwsIndhdGNoX3R5cGUiOiJQUkVNSVVNIiwicHJvZ3JhbV9pZCI6ImUwMWRmYjAyLTM1YmItMTFlOS1hNDI3LTA2YTA0MTdjMWQxZSIsImFkX3RhZyI6ZmFsc2UsInBhcmVudF9wcm9ncmFtX2lkIjoiZmJmMDc2MDYtMzNmYi0xMWU5LWE0MjctMDZhMDQxN2MxZDFlIiwiY2xpZW50X2lkIjoiNGQ3MDViZTQtYTQ5ZS0xMWVhLWJiMzctMDI0MmFjMTMwMDAyIiwidmlkZW9fdHlwZSI6InZvZCIsImdyYW50X3R5cGUiOiJwbGF5X3ZpZGVvIiwidXNlcl9pZCI6ImFhNTMxZWQ2LWM2NTMtNDliYS04NGI1LWFkZDRmNGIzNGMyNyIsImN1cnJlbnRfc2Vjb25kIjowLCJyZXBvcnRfaWQiOiJOU1RHIiwic2NvcGUiOlsicHVibGljOi4qIiwibWU6LioiXSwiZXhwIjoxNjU4Mzk1ODI2LCJkZXRlY3Rpb25faWQiOm51bGwsInZpZGVvX2lkIjoiODc0Yjk0ZDItNzZiYi00YzliLTgzODQtNzJlMTA0NWVjOGMxIiwiaXNzIjoiQXNpYXBsYXktT0F1dGgtU2VydmVyIiwiaWF0IjoxNjU4MTM2NjI2LCJ0ZXJyaXRvcnkiOiJUVyJ9.1juciYIyMNzykXKu-nGLR_cYWvPMEAE9ub-ny7RzFnM";
|
||||
//url = "https://a38avoddashs3ww-a.akamaihd.net/ondemand/iad_2/8e91/f2f2/ec5a/430f-bd7a-0779f4a0189d/685cda75-609c-41c1-86bb-688f4cdb5521_corrected.mpd";
|
||||
//url = "https://dcs-vod.mp.lura.live/vod/p/session/manifest.mpd?i=i177610817-nb45239a2-e962-4137-bc70-1790359619e6";
|
||||
//url = "https://theater.kktv.com.tw/98/04000198010001_584b26392f7f7f11fc62299214a55fb7/16113081449d8d5e9960_sub_dash.mpd"; //MPD+VTT
|
||||
//url = "https://vsl.play.kakao.com/vod/rvty90n7btua6u9oebr97i8zl/dash/vhs/cenc/adaptive.mpd?e=1658297362&p=71&h=53766bdde112d59da2b2514e8ab41e81"; //需要补params
|
||||
//url = "https://a38avoddashs3ww-a.akamaihd.net/ondemand/iad_2/8e91/f2f2/ec5a/430f-bd7a-0779f4a0189d/685cda75-609c-41c1-86bb-688f4cdb5521_corrected.mpd";
|
||||
//url = "";
|
||||
|
||||
if (!string.IsNullOrEmpty(option.Input))
|
||||
{
|
||||
url = option.Input;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(url))
|
||||
{
|
||||
url = AnsiConsole.Ask<string>("Input [green]URL[/]: ");
|
||||
}
|
||||
|
||||
//流提取器配置
|
||||
var extractor = new StreamExtractor(parserConfig);
|
||||
extractor.LoadSourceFromUrl(url);
|
||||
|
||||
//解析流信息
|
||||
var streams = await extractor.ExtractStreamsAsync();
|
||||
|
||||
//全部媒体
|
||||
var lists = streams.OrderBy(p => p.MediaType).ThenByDescending(p => p.Bandwidth).ThenByDescending(GetOrder);
|
||||
//基本流
|
||||
var basicStreams = lists.Where(x => x.MediaType == null || x.MediaType == MediaType.VIDEO);
|
||||
//可选音频轨道
|
||||
var audios = lists.Where(x => x.MediaType == MediaType.AUDIO);
|
||||
//可选字幕轨道
|
||||
var subs = lists.Where(x => x.MediaType == MediaType.SUBTITLES);
|
||||
|
||||
if (option.WriteMetaJson)
|
||||
{
|
||||
Logger.Warn(ResString.writeJson);
|
||||
await File.WriteAllTextAsync("meta.json", GlobalUtil.ConvertToJson(lists), Encoding.UTF8);
|
||||
}
|
||||
|
||||
Logger.Info(ResString.streamsInfo, lists.Count(), basicStreams.Count(), audios.Count(), subs.Count());
|
||||
|
||||
foreach (var item in lists)
|
||||
{
|
||||
Logger.InfoMarkUp(item.ToString());
|
||||
}
|
||||
|
||||
var selectedStreams = new List<StreamSpec>();
|
||||
if (option.AutoSelect)
|
||||
{
|
||||
if (basicStreams.Any())
|
||||
selectedStreams.Add(basicStreams.First());
|
||||
var langs = audios.DistinctBy(a => a.Language).Select(a => a.Language);
|
||||
foreach (var lang in langs)
|
||||
{
|
||||
selectedStreams.Add(audios.Where(a => a.Language == lang).OrderByDescending(a => a.Bandwidth).ThenByDescending(GetOrder).First());
|
||||
}
|
||||
selectedStreams.AddRange(subs);
|
||||
}
|
||||
else if (option.SubOnly)
|
||||
{
|
||||
selectedStreams.AddRange(subs);
|
||||
}
|
||||
else if (option.VideoFilter != null || option.AudioFilter != null || option.SubtitleFilter != null)
|
||||
{
|
||||
basicStreams = FilterUtil.DoFilter(basicStreams, option.VideoFilter);
|
||||
audios = FilterUtil.DoFilter(audios, option.AudioFilter);
|
||||
subs = FilterUtil.DoFilter(subs, option.SubtitleFilter);
|
||||
selectedStreams = basicStreams.Concat(audios).Concat(subs).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
//展示交互式选择框
|
||||
selectedStreams = FilterUtil.SelectStreams(lists);
|
||||
}
|
||||
|
||||
if (!selectedStreams.Any())
|
||||
throw new Exception(ResString.noStreamsToDownload);
|
||||
|
||||
//HLS: 选中流中若有没加载出playlist的,加载playlist
|
||||
//DASH: 加载playlist (调用url预处理器)
|
||||
if (selectedStreams.Any(s => s.Playlist == null) || extractor.ExtractorType == ExtractorType.MPEG_DASH)
|
||||
await extractor.FetchPlayListAsync(selectedStreams);
|
||||
|
||||
//直播检测
|
||||
var livingFlag = selectedStreams.Any(s => s.Playlist?.IsLive == true) && !option.LivePerformAsVod;
|
||||
if (livingFlag)
|
||||
{
|
||||
Logger.WarnMarkUp($"[white on darkorange3_1]{ResString.liveFound}[/]");
|
||||
}
|
||||
|
||||
//无法识别的加密方式,自动开启二进制合并
|
||||
if (selectedStreams.Any(s => s.Playlist.MediaParts.Any(p => p.MediaSegments.Any(m => m.EncryptInfo.Method == EncryptMethod.UNKNOWN))))
|
||||
{
|
||||
Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge3}[/]");
|
||||
}
|
||||
|
||||
if (option.WriteMetaJson)
|
||||
{
|
||||
Logger.Warn(ResString.writeJson);
|
||||
await File.WriteAllTextAsync("meta_selected.json", GlobalUtil.ConvertToJson(selectedStreams), Encoding.UTF8);
|
||||
}
|
||||
|
||||
Logger.Info(ResString.selectedStream);
|
||||
foreach (var item in selectedStreams)
|
||||
{
|
||||
Logger.InfoMarkUp(item.ToString());
|
||||
}
|
||||
|
||||
if (option.SkipDownload)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
Console.ReadKey();
|
||||
#endif
|
||||
|
||||
//尝试从URL或文件读取文件名
|
||||
if (string.IsNullOrEmpty(option.SaveName))
|
||||
{
|
||||
option.SaveName = OtherUtil.GetFileNameFromInput(option.Input);
|
||||
}
|
||||
|
||||
Logger.InfoMarkUp(ResString.saveName + $"[deepskyblue1]{option.SaveName.EscapeMarkup()}[/]");
|
||||
|
||||
//下载配置
|
||||
var downloadConfig = new DownloaderConfig()
|
||||
{
|
||||
MyOptions = option,
|
||||
Headers = parserConfig.Headers, //使用命令行解析得到的Headers
|
||||
};
|
||||
|
||||
if (!livingFlag)
|
||||
{
|
||||
//开始下载
|
||||
var sdm = new SimpleDownloadManager(downloadConfig);
|
||||
var result = await sdm.StartDownloadAsync(selectedStreams);
|
||||
if (result)
|
||||
Logger.InfoMarkUp("[white on green]Done[/]");
|
||||
else
|
||||
Logger.ErrorMarkUp("[white on red]Faild[/]");
|
||||
}
|
||||
else
|
||||
{
|
||||
var sldm = new SimpleLiveRecordManager2(downloadConfig, selectedStreams, extractor);
|
||||
var result = await sldm.StartRecordAsync();
|
||||
if (result)
|
||||
Logger.InfoMarkUp("[white on green]Done[/]");
|
||||
else
|
||||
Logger.ErrorMarkUp("[white on red]Faild[/]");
|
||||
throw new FileNotFoundException("mkvmerge not found");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
//预先检查
|
||||
if ((option.Keys != null && option.Keys.Length > 0) || option.KeyTextFile != null)
|
||||
{
|
||||
string msg = Logger.LogLevel == LogLevel.DEBUG ? ex.ToString() : ex.Message;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
//默认的headers
|
||||
var headers = new Dictionary<string, string>()
|
||||
{
|
||||
["user-agent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
|
||||
};
|
||||
//添加或替换用户输入的headers
|
||||
foreach (var item in option.Headers)
|
||||
{
|
||||
headers[item.Key] = item.Value;
|
||||
}
|
||||
|
||||
var parserConfig = new ParserConfig()
|
||||
{
|
||||
AppendUrlParams = option.AppendUrlParams,
|
||||
UrlProcessorArgs = option.UrlProcessorArgs,
|
||||
BaseUrl = option.BaseUrl!,
|
||||
Headers = headers,
|
||||
CustomMethod = option.CustomHLSMethod,
|
||||
CustomeKey = option.CustomHLSKey,
|
||||
CustomeIV = option.CustomHLSIv,
|
||||
};
|
||||
|
||||
//demo1
|
||||
parserConfig.ContentProcessors.Insert(0, new DemoProcessor());
|
||||
//demo2
|
||||
parserConfig.KeyProcessors.Insert(0, new DemoProcessor2());
|
||||
//for www.nowehoryzonty.pl
|
||||
parserConfig.UrlProcessors.Insert(0, new NowehoryzontyUrlProcessor());
|
||||
|
||||
var url = string.Empty;
|
||||
//url = "https://vod-ftc-eu-west-1.media.dssott.com/ps01/disney/29a73209-b706-4f21-8384-acddccb154d2/ctr-all-6cf6fec6-94dc-4f8b-ae67-5ada60ee1e83-42bd5bca-f9a8-4299-83e3-0fb2b4ec0a62.m3u8"; //迪士尼
|
||||
//url = "https://play.itunes.apple.com/WebObjects/MZPlay.woa/hls/subscription/playlist.m3u8?cc=US&svcId=tvs.vds.4105&a=1580273278&isExternal=true&brandId=tvs.sbd.4000&id=337246031&l=en-US&aec=UHD&xtrick=true&webbrowser=true"; //啥都有
|
||||
//url = "https://media.axprod.net/TestVectors/v7-Clear/Manifest_1080p.mpd"; //多音轨多字幕
|
||||
//url = "https://cmafref.akamaized.net/cmaf/live-ull/2006350/akambr/out.mpd"; //直播
|
||||
//url = "http://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8";
|
||||
//url = "https://vod.sdn.wavve.com/hls/S01/S01_E461382925.1/1/5000/chunklist.m3u8";
|
||||
url = "https://bitmovin-a.akamaihd.net/content/art-of-motion_drm/mpds/11331.mpd";
|
||||
//url = "http://tv-live.ynkmit.com/tv/anning.m3u8?txSecret=7528f35fb4b62bd24d55b891899db68f&txTime=632C8680"; //直播
|
||||
//url = "https://rest-as.ott.kaltura.com/api_v3/service/assetFile/action/playManifest/partnerId/147/assetId/1304099/assetType/media/assetFileId/16136929/contextType/PLAYBACK/isAltUrl/False/ks/djJ8MTQ3fMusTFH6PCZpcrfKLQwI-pPm9ex6b6r49wioe32WH2udXeM4reyWIkSDpi7HhvhxBHAHAKiHrcnkmIJQpyAt4MuDBG0ywGQ-jOeqQFcTRQ8BGJGw6g-smSBLwSbo4CCx9M9vWNJX3GkOfhoMAY4yRU-ur3okHiVq1mUJ82XBd_iVqLuzodnc9sJEtcHH0zc5CoPiTq2xor-dq3yDURnZm3isfSN3t9uLIJEW09oE-SJ84DM5GUuFUdbnIV8bdcWUsPicUg-Top1G2D3WcWXq4EvPnwvD8jrC_vsiOpLHf5akAwtdGsJ6__cXUmT7a-QlfjdvaZ5T8UhDLnttHmsxYs2E5c0lh4uOvvJou8dD8iYxUexlPI2j4QUkBRxqOEVLSNV3Y82-5TTRqgnK_uGYXHwk7EAmDws7hbLj2-DJ1heXDcye3OJYdunJgAS-9ma5zmQQNiY_HYh6wj2N1HpCTNAtWWga6R9fC0VgBTZbidW-YwMSGzIvMQfIfWKe15X7Oc_hCs-zGfW9XeRJZrutcWKK_D_HlzpQVBF2vIF3XgaI/a.mpd";
|
||||
//url = "https://dash.akamaized.net/dash264/TestCases/2c/qualcomm/1/MultiResMPEG2.mpd";
|
||||
url = "https://cmaf.lln.latam.hbomaxcdn.com/videos/GYPGKMQjoDkVLBQEAAAAo/1/1b5ad5/1_single_J8sExA_1080hi.mpd";
|
||||
//url = "https://livesim.dashif.org/dash/vod/testpic_2s/multi_subs.mpd"; //ttml + mp4
|
||||
//url = "http://media.axprod.net/TestVectors/v6-Clear/Manifest_1080p.mpd"; //vtt + mp4
|
||||
//url = "https://livesim.dashif.org/dash/vod/testpic_2s/xml_subs.mpd"; //ttml
|
||||
//url = "https://storage.googleapis.com/shaka-demo-assets/angel-one-hls/hls.m3u8"; //HLS vtt
|
||||
//url = "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_adv_example_hevc/master.m3u8"; //高级HLS fMP4+VTT
|
||||
//url = "https://events-delivery.apple.com/0205eyyhwbbqexozkwmgccegwnjyrktg/m3u8/vod_index-dpyfrsVksFWjneFiptbXnAMYBtGYbXeZ.m3u8"; //高级HLS fMP4+VTT
|
||||
//url = "https://apionvod5.seezntv.com/ktmain1/cold/CP/55521/202207/media/MIAM61RPSGL150000100_DRM/MIAM61RPSGL150000100_H.m3u8?sid=0000000F50000040000A700000020000";
|
||||
//url = "https://ewcdn12.nowe.com/session/16-5-72579e3-2103014898783810281/Content/DASH_VOS3/VOD/6908/19585/d2afa5fe-e9c8-40f0-8d18-648aaaf292b6/f677841a-9d8f-2ff5-3517-674ba49ef192/manifest.mpd?token=894db5d69931835f82dd8e393974ef9f_1658146180";
|
||||
//url = "https://ols-ww100-cp.akamaized.net/manifest/master/06ee6f68-ee80-11ea-9bc5-02b68fb543c4/65794a72596d6c30496a6f7a4e6a67324e4441774d444173496e42735958526d62334a74496a6f695a47567a6133527663434973496d526c646d6c6a5a565235634755694f694a335a5749694c434a746232526c62434936496e6470626d527664334d694c434a7663315235634755694f694a6a61484a76625755694c434a7663794936496a45774d6934774c6a41694c434a68634841694f69497a4c6a416966513d3d/dash.mpd?cpatoken=exp=1658223027~acl=/manifest/master/06ee6f68-ee80-11ea-9bc5-02b68fb543c4/*~hmac=644c608aac361f688e9b24b0f345c801d0f2d335819431d1873ff7aeac46d6b2&access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXZpY2VfaWQiOm51bGwsIndhdGNoX3R5cGUiOiJQUkVNSVVNIiwicHJvZ3JhbV9pZCI6ImUwMWRmYjAyLTM1YmItMTFlOS1hNDI3LTA2YTA0MTdjMWQxZSIsImFkX3RhZyI6ZmFsc2UsInBhcmVudF9wcm9ncmFtX2lkIjoiZmJmMDc2MDYtMzNmYi0xMWU5LWE0MjctMDZhMDQxN2MxZDFlIiwiY2xpZW50X2lkIjoiNGQ3MDViZTQtYTQ5ZS0xMWVhLWJiMzctMDI0MmFjMTMwMDAyIiwidmlkZW9fdHlwZSI6InZvZCIsImdyYW50X3R5cGUiOiJwbGF5X3ZpZGVvIiwidXNlcl9pZCI6ImFhNTMxZWQ2LWM2NTMtNDliYS04NGI1LWFkZDRmNGIzNGMyNyIsImN1cnJlbnRfc2Vjb25kIjowLCJyZXBvcnRfaWQiOiJOU1RHIiwic2NvcGUiOlsicHVibGljOi4qIiwibWU6LioiXSwiZXhwIjoxNjU4Mzk1ODI2LCJkZXRlY3Rpb25faWQiOm51bGwsInZpZGVvX2lkIjoiODc0Yjk0ZDItNzZiYi00YzliLTgzODQtNzJlMTA0NWVjOGMxIiwiaXNzIjoiQXNpYXBsYXktT0F1dGgtU2VydmVyIiwiaWF0IjoxNjU4MTM2NjI2LCJ0ZXJyaXRvcnkiOiJUVyJ9.1juciYIyMNzykXKu-nGLR_cYWvPMEAE9ub-ny7RzFnM";
|
||||
//url = "https://a38avoddashs3ww-a.akamaihd.net/ondemand/iad_2/8e91/f2f2/ec5a/430f-bd7a-0779f4a0189d/685cda75-609c-41c1-86bb-688f4cdb5521_corrected.mpd";
|
||||
//url = "https://dcs-vod.mp.lura.live/vod/p/session/manifest.mpd?i=i177610817-nb45239a2-e962-4137-bc70-1790359619e6";
|
||||
//url = "https://theater.kktv.com.tw/98/04000198010001_584b26392f7f7f11fc62299214a55fb7/16113081449d8d5e9960_sub_dash.mpd"; //MPD+VTT
|
||||
//url = "https://vsl.play.kakao.com/vod/rvty90n7btua6u9oebr97i8zl/dash/vhs/cenc/adaptive.mpd?e=1658297362&p=71&h=53766bdde112d59da2b2514e8ab41e81"; //需要补params
|
||||
//url = "https://a38avoddashs3ww-a.akamaihd.net/ondemand/iad_2/8e91/f2f2/ec5a/430f-bd7a-0779f4a0189d/685cda75-609c-41c1-86bb-688f4cdb5521_corrected.mpd";
|
||||
//url = "";
|
||||
|
||||
if (!string.IsNullOrEmpty(option.Input))
|
||||
{
|
||||
url = option.Input;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(url))
|
||||
{
|
||||
url = AnsiConsole.Ask<string>("Input [green]URL[/]: ");
|
||||
}
|
||||
|
||||
//流提取器配置
|
||||
var extractor = new StreamExtractor(parserConfig);
|
||||
extractor.LoadSourceFromUrl(url);
|
||||
|
||||
//解析流信息
|
||||
var streams = await extractor.ExtractStreamsAsync();
|
||||
|
||||
//全部媒体
|
||||
var lists = streams.OrderBy(p => p.MediaType).ThenByDescending(p => p.Bandwidth).ThenByDescending(GetOrder);
|
||||
//基本流
|
||||
var basicStreams = lists.Where(x => x.MediaType == null || x.MediaType == MediaType.VIDEO);
|
||||
//可选音频轨道
|
||||
var audios = lists.Where(x => x.MediaType == MediaType.AUDIO);
|
||||
//可选字幕轨道
|
||||
var subs = lists.Where(x => x.MediaType == MediaType.SUBTITLES);
|
||||
|
||||
if (option.WriteMetaJson)
|
||||
{
|
||||
Logger.Warn(ResString.writeJson);
|
||||
await File.WriteAllTextAsync("meta.json", GlobalUtil.ConvertToJson(lists), Encoding.UTF8);
|
||||
}
|
||||
|
||||
Logger.Info(ResString.streamsInfo, lists.Count(), basicStreams.Count(), audios.Count(), subs.Count());
|
||||
|
||||
foreach (var item in lists)
|
||||
{
|
||||
Logger.InfoMarkUp(item.ToString());
|
||||
}
|
||||
|
||||
var selectedStreams = new List<StreamSpec>();
|
||||
if (option.AutoSelect)
|
||||
{
|
||||
if (basicStreams.Any())
|
||||
selectedStreams.Add(basicStreams.First());
|
||||
var langs = audios.DistinctBy(a => a.Language).Select(a => a.Language);
|
||||
foreach (var lang in langs)
|
||||
{
|
||||
selectedStreams.Add(audios.Where(a => a.Language == lang).OrderByDescending(a => a.Bandwidth).ThenByDescending(GetOrder).First());
|
||||
}
|
||||
selectedStreams.AddRange(subs);
|
||||
}
|
||||
else if (option.SubOnly)
|
||||
{
|
||||
selectedStreams.AddRange(subs);
|
||||
}
|
||||
else if (option.VideoFilter != null || option.AudioFilter != null || option.SubtitleFilter != null)
|
||||
{
|
||||
basicStreams = FilterUtil.DoFilter(basicStreams, option.VideoFilter);
|
||||
audios = FilterUtil.DoFilter(audios, option.AudioFilter);
|
||||
subs = FilterUtil.DoFilter(subs, option.SubtitleFilter);
|
||||
selectedStreams = basicStreams.Concat(audios).Concat(subs).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
//展示交互式选择框
|
||||
selectedStreams = FilterUtil.SelectStreams(lists);
|
||||
}
|
||||
|
||||
if (!selectedStreams.Any())
|
||||
throw new Exception(ResString.noStreamsToDownload);
|
||||
|
||||
//HLS: 选中流中若有没加载出playlist的,加载playlist
|
||||
//DASH: 加载playlist (调用url预处理器)
|
||||
if (selectedStreams.Any(s => s.Playlist == null) || extractor.ExtractorType == ExtractorType.MPEG_DASH)
|
||||
await extractor.FetchPlayListAsync(selectedStreams);
|
||||
|
||||
//直播检测
|
||||
var livingFlag = selectedStreams.Any(s => s.Playlist?.IsLive == true) && !option.LivePerformAsVod;
|
||||
if (livingFlag)
|
||||
{
|
||||
Logger.WarnMarkUp($"[white on darkorange3_1]{ResString.liveFound}[/]");
|
||||
}
|
||||
|
||||
//无法识别的加密方式,自动开启二进制合并
|
||||
if (selectedStreams.Any(s => s.Playlist.MediaParts.Any(p => p.MediaSegments.Any(m => m.EncryptInfo.Method == EncryptMethod.UNKNOWN))))
|
||||
{
|
||||
Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge3}[/]");
|
||||
}
|
||||
|
||||
if (option.WriteMetaJson)
|
||||
{
|
||||
Logger.Warn(ResString.writeJson);
|
||||
await File.WriteAllTextAsync("meta_selected.json", GlobalUtil.ConvertToJson(selectedStreams), Encoding.UTF8);
|
||||
}
|
||||
|
||||
Logger.Info(ResString.selectedStream);
|
||||
foreach (var item in selectedStreams)
|
||||
{
|
||||
Logger.InfoMarkUp(item.ToString());
|
||||
}
|
||||
|
||||
if (option.SkipDownload)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
msg = ex.ToString();
|
||||
Console.ReadKey();
|
||||
#endif
|
||||
Logger.Error(msg);
|
||||
await Task.Delay(3000);
|
||||
|
||||
//尝试从URL或文件读取文件名
|
||||
if (string.IsNullOrEmpty(option.SaveName))
|
||||
{
|
||||
option.SaveName = OtherUtil.GetFileNameFromInput(option.Input);
|
||||
}
|
||||
|
||||
Logger.InfoMarkUp(ResString.saveName + $"[deepskyblue1]{option.SaveName.EscapeMarkup()}[/]");
|
||||
|
||||
//下载配置
|
||||
var downloadConfig = new DownloaderConfig()
|
||||
{
|
||||
MyOptions = option,
|
||||
Headers = parserConfig.Headers, //使用命令行解析得到的Headers
|
||||
};
|
||||
|
||||
if (!livingFlag)
|
||||
{
|
||||
//开始下载
|
||||
var sdm = new SimpleDownloadManager(downloadConfig);
|
||||
var result = await sdm.StartDownloadAsync(selectedStreams);
|
||||
if (result)
|
||||
Logger.InfoMarkUp("[white on green]Done[/]");
|
||||
else
|
||||
Logger.ErrorMarkUp("[white on red]Faild[/]");
|
||||
}
|
||||
else
|
||||
{
|
||||
var sldm = new SimpleLiveRecordManager2(downloadConfig, selectedStreams, extractor);
|
||||
var result = await sldm.StartRecordAsync();
|
||||
if (result)
|
||||
Logger.InfoMarkUp("[white on green]Done[/]");
|
||||
else
|
||||
Logger.ErrorMarkUp("[white on red]Faild[/]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue