1
mirror of https://github.com/nilaoda/N_m3u8DL-CLI synced 2025-09-08 06:00:50 +02:00

Compare commits

...

31 Commits
2.9.7 ... 2.9.8

Author SHA1 Message Date
nilaoda
faf67cd527 2.9.8 2021-11-20 10:14:31 +08:00
nilaoda
38d1a1a2dc 修正init url缺失baseurl问题 2021-11-12 23:28:35 +08:00
nilaoda
12eb68d592 修复日志冲突问题 2021-10-19 11:14:41 +08:00
nilaoda
0804e295e5 修复流选择的显示bug 2021-10-19 10:53:03 +08:00
nilaoda
847c4683cb update wetv js 2021-10-17 00:51:46 +08:00
nilaoda
8c72947860 package exe only 2021-10-17 00:24:34 +08:00
nilaoda
f0b240a6ee 优化AppleTV处理 2021-09-05 19:21:08 +08:00
nilaoda
793cf53042 解决同一个period且同id导致被重复添加分片 2021-09-05 18:17:05 +08:00
nilaoda
612fc29197 修复padding负值问题 2021-09-02 22:41:16 +08:00
nilaoda
307e2389de Update README.md 2021-09-02 10:46:11 +08:00
nilaoda
1c932abdc3 Update AssemblyInfo.cs 2021-08-23 11:38:19 +08:00
nilaoda
314f0065c7 fix node select bug and type show bug #496 2021-08-23 11:35:14 +08:00
nilaoda
59060bb74d Merge pull request #495 from ncnnnnn/patch-2
Update README.md
2021-08-21 22:30:13 +08:00
ncnnnnn
cab882c3a3 Update README.md 2021-08-21 20:35:02 +08:00
nilaoda
9955532ce5 强校验MAP下载成功 2021-08-15 20:18:06 +08:00
nilaoda
7e127be8c2 初步修改显示输出样式 2021-08-15 15:32:01 +08:00
nilaoda
b46571a57f Merge pull request #470 from CW-B-W/master
Fix typos in README_ENG.md
2021-07-18 02:28:52 +08:00
Chiao-Wei Wang
da5861d907 Update README_ENG.md 2021-07-18 01:57:16 +08:00
nilaoda
92bc91a1fb drm-json to m3u8 2021-07-04 00:40:05 +08:00
nilaoda
439f50103e 1.支持iq-drm格式json自动转换 2.优化master选择最高清晰度逻辑 2021-07-04 00:38:17 +08:00
nilaoda
8a95e31b2f update docs 2021-07-03 22:35:00 +08:00
nilaoda
115b8a156a Update packages.config 2021-06-27 23:06:33 +08:00
nilaoda
120bcaebb5 Update N_m3u8DL-CLI.csproj 2021-06-27 23:01:22 +08:00
nilaoda
455d56707c Update N_m3u8DL-CLI.csproj 2021-06-27 22:55:05 +08:00
nilaoda
048adcf118 支持解压brotli 2021-06-27 22:41:12 +08:00
nilaoda
fe5aa27b1c 修正判断png图片时可能出现的数组越界bug 2021-06-27 22:39:16 +08:00
nilaoda
039aa489b1 优化范围选择结尾识别 2021-06-27 22:38:32 +08:00
nilaoda
14e80f0b06 优化输出并增加ETA显示 2021-03-27 14:04:31 +08:00
nilaoda
2256fff549 日志文件初始化时,精确到毫秒防止重复 2021-03-27 14:03:54 +08:00
nilaoda
84cfd4e138 Update README_ENG.md 2021-03-26 09:35:28 +08:00
nilaoda
e70c229135 Update README.md 2021-03-26 09:30:30 +08:00
26 changed files with 384 additions and 161 deletions

View File

@@ -31,4 +31,4 @@ jobs:
uses: actions/upload-artifact@v1.0.0
with:
name: N_m3u8DL-CLI_latest
path: N_m3u8DL-CLI\bin\Release\
path: N_m3u8DL-CLI\bin\Release\N_m3u8DL-CLI.exe

View File

@@ -36,12 +36,14 @@ namespace N_m3u8DL_CLI
public string MuxSetJson { get; set; } = string.Empty;
public int TimeOut { get; set; } = 10000; //超时设置
public static double DownloadedSize { get; set; } = 0; //已下载大小
public static double ToDoSize { get; set; } = 0; //待下载大小
public static bool HasSetDir { get; set; } = false;
public bool NoMerge { get; set; } = false;
public static int CalcTime { get; set; } = 1; //计算速度的间隔
public static int Count { get; set; } = 0;
public static int PartsCount { get; set; } = 0;
public static bool DisableIntegrityCheck { get; set; } = false; //关闭完整性检查
public static bool HasExtMap { get; set; } = false; //是否有MAP
static CancellationTokenSource cts = new CancellationTokenSource();
//计算下载速度
@@ -52,14 +54,24 @@ namespace N_m3u8DL_CLI
timer.AutoReset = true;
timer.Elapsed += delegate
{
Console.SetCursorPosition(0, 1);
Console.Write("Speed: " + Global.FormatFileSize((Global.BYTEDOWN) / CalcTime) + " / s".PadRight(70));
var eta = "";
if (ToDoSize != 0)
{
eta = " @ " + Global.FormatTime(Convert.ToInt32(ToDoSize / (Global.BYTEDOWN / CalcTime)));
}
var print = Global.FormatFileSize((Global.BYTEDOWN) / CalcTime) + "/s" + eta;
ProgressReporter.Report("", "(" + print + ")");
if (Global.HadReadInfo && Global.BYTEDOWN <= Global.STOP_SPEED * 1024 * CalcTime)
{
stopCount++;
Console.SetCursorPosition(0, 1);
Console.Write("Speed: " + Global.FormatFileSize((Global.BYTEDOWN) / CalcTime) + " / s [" + stopCount + "]".PadRight(70));
eta = "";
if (ToDoSize != 0)
{
eta = " @ " + Global.FormatTime(Convert.ToInt32(ToDoSize / (Global.BYTEDOWN / CalcTime)));
}
print = Global.FormatFileSize((Global.BYTEDOWN) / CalcTime) + "/s [" + stopCount + "]" + eta;
ProgressReporter.Report("", "(" + print + ")");
if (stopCount >= 12)
{
@@ -127,8 +139,6 @@ namespace N_m3u8DL_CLI
watcher.PartsCount = PartsCount;
watcher.WatcherStrat();
//开始计算速度
timer.Enabled = true;
cts = new CancellationTokenSource();
//开始调用下载
@@ -136,8 +146,10 @@ namespace N_m3u8DL_CLI
LOGGER.PrintLine(strings.startDownloading, LOGGER.Warning);
//下载MAP文件若有
try
downloadMap:
if (HasExtMap)
{
LOGGER.PrintLine(strings.downloadingMapFile);
Downloader sd = new Downloader();
sd.TimeOut = TimeOut;
sd.FileUrl = initJson["m3u8Info"]["extMAP"].Value<string>();
@@ -155,12 +167,12 @@ namespace N_m3u8DL_CLI
File.Delete(sd.SavePath);
if (File.Exists(DownDir + "\\Part_0\\!MAP.ts"))
File.Delete(DownDir + "\\Part_0\\!MAP.ts");
LOGGER.PrintLine(strings.downloadingMapFile);
sd.Down(); //开始下载
}
catch (Exception e)
{
//LOG.WriteLineError(e.ToString());
if (!File.Exists(DownDir + "\\!MAP.ts")) //检测是否成功下载
{
Thread.Sleep(1000);
goto downloadMap;
}
}
//首先下载第一个分片
@@ -192,6 +204,8 @@ namespace N_m3u8DL_CLI
if (File.Exists(sd.SavePath))
File.Delete(sd.SavePath);
LOGGER.PrintLine(strings.downloadingFirstSegement);
//开始计算速度
timer.Enabled = true;
if (!Global.ShouldStop)
sd.Down(); //开始下载
}
@@ -337,7 +351,6 @@ namespace N_m3u8DL_CLI
else //开始合并
{
LOGGER.PrintLine(strings.downloadComplete + (DisableIntegrityCheck ? "(" + strings.disableIntegrityCheck + ")" : ""));
Console.WriteLine();
if (NoMerge == false)
{
string exePath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
@@ -381,7 +394,6 @@ namespace N_m3u8DL_CLI
{
if (Global.VIDEO_TYPE != "DV") //不是杜比视界
{
LOGGER.FFmpegCorsorIndex = LOGGER.CursorIndex;
//检测是否为MPEG-TS封装不是的话就转换为TS封装
foreach (string s in Global.GetFiles(DownDir + "\\Part_0", ".ts"))
{
@@ -467,7 +479,6 @@ namespace N_m3u8DL_CLI
DownDir = parser.DownDir;
parser.Parse(); //开始解析
Thread.Sleep(1000);
LOGGER.CursorIndex = 5;
Global.HadReadInfo = false;
Global.VIDEO_TYPE = "";
Global.AUDIO_TYPE = "";
@@ -492,16 +503,13 @@ namespace N_m3u8DL_CLI
DownDir = parser.DownDir;
parser.Parse(); //开始解析
Thread.Sleep(1000);
LOGGER.CursorIndex = 5;
Global.HadReadInfo = false;
Global.VIDEO_TYPE = "";
Global.AUDIO_TYPE = "";
DoDownload();
}
LOGGER.PrintLine(strings.taskDone, LOGGER.Warning);
Console.CursorVisible = true;
Environment.Exit(0); //正常退出程序
Console.Clear();
return;
}
@@ -540,7 +548,6 @@ namespace N_m3u8DL_CLI
{
if (Global.VIDEO_TYPE != "DV") //不是爱奇艺杜比视界
{
LOGGER.FFmpegCorsorIndex = LOGGER.CursorIndex;
//检测是否为MPEG-TS封装不是的话就转换为TS封装
foreach (string s in Global.GetFiles(DownDir, ".ts"))
{
@@ -615,7 +622,6 @@ namespace N_m3u8DL_CLI
DownDir = parser.DownDir;
parser.Parse(); //开始解析
Thread.Sleep(1000);
LOGGER.CursorIndex = 5;
Global.HadReadInfo = false;
Global.VIDEO_TYPE = "";
Global.AUDIO_TYPE = "";
@@ -640,17 +646,13 @@ namespace N_m3u8DL_CLI
DownDir = parser.DownDir;
parser.Parse(); //开始解析
Thread.Sleep(1000);
LOGGER.CursorIndex = 5;
Global.HadReadInfo = false;
Global.VIDEO_TYPE = "";
Global.AUDIO_TYPE = "";
DoDownload();
}
LOGGER.PrintLine(strings.taskDone, LOGGER.Warning);
Console.CursorVisible = true;
Environment.Exit(0); //正常退出程序
Console.Clear();
}
else
{

View File

@@ -1,4 +1,5 @@
using Newtonsoft.Json;
using BrotliSharpLib;
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -34,17 +35,11 @@ namespace N_m3u8DL_CLI
/*===============================================================================*/
static Version ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
static string nowVer = $"{ver.Major}.{ver.Minor}.{ver.Build}";
static string nowDate = "20210325";
static string nowDate = "20211120";
public static void WriteInit()
{
Console.Clear();
Console.SetCursorPosition(0, 0);
Console.BackgroundColor = ConsoleColor.Blue; //设置背景色
Console.ForegroundColor = ConsoleColor.White; //设置前景色,即字体颜色
Console.WriteLine($"N_m3u8DL-CLI v{nowVer} {nowDate}...");
Console.ResetColor(); //将控制台的前景色和背景色设为默认值
Console.WriteLine("Speed: waiting");
Console.WriteLine("Progress: waiting");
Console.WriteLine($"N_m3u8DL-CLI version {nowVer} 2018-2021");
Console.WriteLine($" built date: {nowDate}");
Console.WriteLine();
}
@@ -59,8 +54,8 @@ namespace N_m3u8DL_CLI
Console.Title = string.Format(strings.newerVisionDetected, latestVer);
try
{
//尝试下载新版本(去码云)
string url = $"https://gitee.com/nilaoda/N_m3u8DL-CLI/raw/master/N_m3u8DL-CLI_v{latestVer}.exe";
//尝试下载新版本
string url = $"https://mirror.ghproxy.com/https://github.com/nilaoda/N_m3u8DL-CLI/releases/download/{latestVer}/N_m3u8DL-CLI_v{latestVer}.exe";
if (File.Exists(Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), $"N_m3u8DL-CLI_v{latestVer}.exe")))
{
Console.Title = string.Format(strings.newerVerisonDownloaded, latestVer);
@@ -186,6 +181,20 @@ namespace N_m3u8DL_CLI
}
}
}
else if (webResponse.ContentEncoding != null
&& webResponse.ContentEncoding.ToLower() == "br") //如果使用了Brotli则先解压
{
using (Stream streamReceive = webResponse.GetResponseStream())
{
using (var bs = new BrotliStream(streamReceive, CompressionMode.Decompress))
{
using (StreamReader sr = new StreamReader(bs, Encoding.UTF8))
{
htmlCode = sr.ReadToEnd();
}
}
}
}
else
{
using (Stream streamReceive = webResponse.GetResponseStream())
@@ -697,27 +706,27 @@ namespace N_m3u8DL_CLI
{
return;
}
else if (137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3] && 96 == u[118] && 130 == u[119])
else if (u.Length > 120 && 137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3] && 96 == u[118] && 130 == u[119])
{
u = u.Skip(120).ToArray();
}
else if (137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3] && 96 == u[6100] && 130 == u[6101])
else if (u.Length > 6102 && 137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3] && 96 == u[6100] && 130 == u[6101])
{
u = u.Skip(6102).ToArray();
}
else if (137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3] && 96 == u[67] && 130 == u[68])
else if (u.Length > 69 && 137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3] && 96 == u[67] && 130 == u[68])
{
u = u.Skip(69).ToArray();
}
else if (137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3] && 96 == u[769] && 130 == u[770])
else if (u.Length > 771 && 137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3] && 96 == u[769] && 130 == u[770])
{
u = u.Skip(771).ToArray();
}
else if (137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3])
else if (u.Length > 4 && 137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3])
{
//确定是PNG但是需要手动查询结尾标记 0x47 出现两次
int skip = 0;
for (int i = 4; i < u.Length - 188 * 2; i++)
for (int i = 4; i < u.Length - 188 * 2 - 4; i++)
{
if (u[i] == 0x47 && u[i + 188] == 0x47 && u[i + 188 + 188] == 0x47)
{

View File

@@ -0,0 +1,116 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
class IqJsonParser
{
public static string Parse(string downDir, string json)
{
JObject jObject = JObject.Parse(json);
var aClips = jObject["payload"]["wm_a"]["audio_track1"]["files"].Value<JArray>();
var vClips = jObject["payload"]["wm_a"]["video_track1"]["files"].Value<JArray>();
var codecsList = new List<string>();
var audioPath = "";
var videoPath = "";
var audioInitPath = "";
var videoInitPath = "";
if (aClips.Count > 0)
{
var init = jObject["payload"]["wm_a"]["audio_track1"]["codec_init"].Value<string>();
byte[] bytes = Convert.FromBase64String(init);
//输出init文件
audioInitPath = Path.Combine(downDir, "iqAudioInit.mp4");
File.WriteAllBytes(audioInitPath, bytes);
StringBuilder sb = new StringBuilder();
sb.AppendLine("#EXTM3U");
sb.AppendLine("#EXT-X-VERSION:3");
sb.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");
sb.AppendLine("#CREATED-BY:N_m3u8DL-CLI");
sb.AppendLine($"#EXT-CODEC:{jObject["payload"]["wm_a"]["audio_track1"]["codec"].Value<string>()}");
sb.AppendLine($"#EXT-KID:{jObject["payload"]["wm_a"]["audio_track1"]["key_id"].Value<string>()}");
sb.AppendLine($"#EXT-X-MAP:URI=\"{new Uri(Path.Combine(downDir + "(Audio)", "iqAudioInit.mp4")).ToString()}\"");
sb.AppendLine("#EXT-X-KEY:METHOD=PLZ-KEEP-RAW,URI=\"None\"");
foreach (var a in aClips)
{
sb.AppendLine($"#EXTINF:{a["duration_second"].ToString()}");
sb.AppendLine(a["file_name"].Value<string>());
}
sb.AppendLine("#EXT-X-ENDLIST");
//输出m3u8文件
var _path = Path.Combine(downDir, "iqAudio.m3u8");
File.WriteAllText(_path, sb.ToString());
audioPath = new Uri(_path).ToString();
codecsList.Add(jObject["payload"]["wm_a"]["audio_track1"]["codec"].Value<string>());
}
if (vClips.Count > 0)
{
var init = jObject["payload"]["wm_a"]["video_track1"]["codec_init"].Value<string>();
byte[] bytes = Convert.FromBase64String(init);
//输出init文件
videoInitPath = Path.Combine(downDir, "iqVideoInit.mp4");
File.WriteAllBytes(videoInitPath, bytes);
StringBuilder sb = new StringBuilder();
sb.AppendLine("#EXTM3U");
sb.AppendLine("#EXT-X-VERSION:3");
sb.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");
sb.AppendLine("#CREATED-BY:N_m3u8DL-CLI");
sb.AppendLine($"#EXT-CODEC:{jObject["payload"]["wm_a"]["video_track1"]["codec"].Value<string>()}");
sb.AppendLine($"#EXT-KID:{jObject["payload"]["wm_a"]["video_track1"]["key_id"].Value<string>()}");
sb.AppendLine($"#EXT-X-MAP:URI=\"{new Uri(videoInitPath).ToString()}\"");
sb.AppendLine("#EXT-X-KEY:METHOD=PLZ-KEEP-RAW,URI=\"None\"");
foreach (var a in vClips)
{
var start = a["seekable"]["pos_start"].Value<long>();
var size = a["size"].Value<long>();
sb.AppendLine($"#EXTINF:{a["duration_second"].ToString()}");
sb.AppendLine($"#EXT-X-BYTERANGE:{size}@{start}");
sb.AppendLine(a["file_name"].Value<string>());
}
sb.AppendLine("#EXT-X-ENDLIST");
//输出m3u8文件
var _path = Path.Combine(downDir, "iqVideo.m3u8");
File.WriteAllText(_path, sb.ToString());
videoPath = new Uri(_path).ToString();
codecsList.Add(jObject["payload"]["wm_a"]["video_track1"]["codec"].Value<string>());
}
var content = "";
if ((videoPath == "" && audioPath != "") || Global.VIDEO_TYPE == "IGNORE")
{
return audioPath;
}
else if (audioPath == "" && videoPath != "")
{
return videoPath;
}
else
{
if (!Directory.Exists(downDir + "(Audio)"))
Directory.CreateDirectory(downDir + "(Audio)");
var _path = Path.Combine(downDir + "(Audio)", "iqAudio.m3u8");
var _pathInit = Path.Combine(downDir + "(Audio)", "iqAudioInit.mp4");
File.Copy(new Uri(audioPath).LocalPath, _path, true);
File.Copy(new Uri(audioInitPath).LocalPath, _pathInit, true);
audioPath = new Uri(_path).ToString();
content = $"#EXTM3U\r\n" +
$"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"{audioPath}\",GROUP-ID=\"default-audio-group\",NAME=\"stream_0\",AUTOSELECT=YES,CHANNELS=\"0\"\r\n" +
$"#EXT-X-STREAM-INF:BANDWIDTH=99999,CODECS=\"{string.Join(",", codecsList)}\",RESOLUTION=0x0,AUDIO=\"default-audio-group\"\r\n" +
$"{videoPath}";
}
var _masterPath = Path.Combine(downDir, "master.m3u8");
File.WriteAllText(_masterPath, content);
return new Uri(_masterPath).ToString();
}
}
}

View File

@@ -11,8 +11,6 @@ namespace N_m3u8DL_CLI
{
class LOGGER
{
public static int CursorIndex = 5;
public static int FFmpegCorsorIndex = 5;
public const int Default = 1;
public const int Error = 2;
public const int Warning = 3;
@@ -36,8 +34,13 @@ namespace N_m3u8DL_CLI
{
if (!Directory.Exists(Path.GetDirectoryName(LOGFILE)))//若文件夹不存在则新建文件夹
Directory.CreateDirectory(Path.GetDirectoryName(LOGFILE)); //新建文件夹
if (File.Exists(LOGFILE))//若文件存在则删除
File.Delete(LOGFILE);
//若文件存在则加序号
int index = 1;
var fileName = Path.GetFileNameWithoutExtension(LOGFILE);
while (File.Exists(LOGFILE))
{
LOGFILE = Path.Combine(Path.GetDirectoryName(LOGFILE), $"{fileName}-{index++}.log");
}
string file = LOGFILE;
string now = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
string init = "LOG " + DateTime.Now.ToString("yyyy/MM/dd") + "\r\n"
@@ -57,40 +60,28 @@ namespace N_m3u8DL_CLI
//读写锁机制,当资源被占用,其他线程等待
static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
public static void PrintLine(string text, int printLevel = 1, int cursorIndex = 0)
public static void PrintLine(string text, int printLevel = 1)
{
try
{
if (CursorIndex > 1000)
{
Console.Clear();
CursorIndex = 0;
}
if (cursorIndex == 0)
Console.SetCursorPosition(0, CursorIndex++);
else
Console.SetCursorPosition(0, cursorIndex);
}
catch (Exception)
{
;
}
switch (printLevel)
{
case 0:
Console.Write("\r" + new string(' ', Console.WindowWidth - 1) + "\r");
Console.WriteLine(" ".PadRight(12) + " " + text);
break;
case 1:
Console.Write("\r" + new string(' ', Console.WindowWidth - 1) + "\r");
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
Console.WriteLine(text);
break;
case 2:
Console.Write("\r" + new string(' ', Console.WindowWidth - 1) + "\r");
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(text);
Console.ResetColor();
break;
case 3:
Console.Write("\r" + new string(' ', Console.WindowWidth - 1) + "\r");
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine(text);
@@ -118,7 +109,7 @@ namespace N_m3u8DL_CLI
}
catch (Exception)
{
}
finally
{
@@ -143,7 +134,7 @@ namespace N_m3u8DL_CLI
}
catch (Exception)
{
}
finally
{
@@ -156,7 +147,7 @@ namespace N_m3u8DL_CLI
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(DateTime.Now.ToString("o") + " " + text);
while (Console.ForegroundColor == ConsoleColor.Red)
while (Console.ForegroundColor == ConsoleColor.Red)
Console.ResetColor();
}
}

View File

@@ -135,7 +135,16 @@ namespace N_m3u8DL_CLI
XmlDocument mpdDoc = new XmlDocument();
mpdDoc.LoadXml(mpdContent);
XmlNode xn = mpdDoc.LastChild;
XmlNode xn = null;
//Select MPD node
foreach (XmlNode node in mpdDoc.ChildNodes)
{
if (node.NodeType == XmlNodeType.Element && node.Name == "MPD")
{
xn = node;
break;
}
}
var mediaPresentationDuration = ((XmlElement)xn).GetAttribute("mediaPresentationDuration");
var ns = ((XmlElement)xn).GetAttribute("xmlns");
@@ -145,9 +154,11 @@ namespace N_m3u8DL_CLI
TimeSpan ts = XmlConvert.ToTimeSpan(mediaPresentationDuration); //时长
var formatList = new List<Dictionary<string, dynamic>>(); //存放所有音视频清晰度
var periodIndex = 0; //解决同一个period且同id导致被重复添加分片
foreach (XmlElement period in xn.SelectNodes("ns:Period", nsMgr))
{
periodIndex++;
var periodDuration = string.IsNullOrEmpty(period.GetAttribute("duration")) ? XmlConvert.ToTimeSpan(mediaPresentationDuration) : XmlConvert.ToTimeSpan(period.GetAttribute("duration"));
var periodMsInfo = ExtractMultisegmentInfo(period, nsMgr, new Dictionary<string, dynamic>()
{
@@ -224,6 +235,7 @@ namespace N_m3u8DL_CLI
var bandwidth = IntOrNull(GetAttribute("bandwidth"));
var f = new Dictionary<string, dynamic>
{
["PeriodIndex"] = periodIndex,
["ContentType"] = contentType,
["FormatId"] = representationId,
["ManifestUrl"] = mpdUrl,
@@ -448,7 +460,7 @@ namespace N_m3u8DL_CLI
f["FragmentBaseUrl"] = baseUrl;
if (representationMsInfo.ContainsKey("InitializationUrl"))
{
f["InitializationUrl"] = representationMsInfo["InitializationUrl"];
f["InitializationUrl"] = CombineURL(baseUrl, representationMsInfo["InitializationUrl"]);
if (f["InitializationUrl"].StartsWith("$$Range"))
{
f["InitializationUrl"] = CombineURL(baseUrl, f["InitializationUrl"]);
@@ -473,7 +485,8 @@ namespace N_m3u8DL_CLI
{
for (int i = 0; i < formatList.Count; i++)
{
if (formatList[i]["FormatId"] == f["FormatId"] && formatList[i]["Width"] == f["Width"] && formatList[i]["ContentType"] == f["ContentType"])
//参数相同但不在同一个Period才可以
if (formatList[i]["FormatId"] == f["FormatId"] && formatList[i]["Width"] == f["Width"] && formatList[i]["ContentType"] == f["ContentType"] && formatList[i]["PeriodIndex"] != f["PeriodIndex"])
{
formatList[i]["Fragments"].AddRange(f["Fragments"]);
break;
@@ -510,7 +523,7 @@ namespace N_m3u8DL_CLI
{
string Stringify(Dictionary<string, dynamic> f)
{
var type = f["ContentType"] == "aduio" ? "Audio" : "Video";
var type = f["ContentType"] == "audio" ? "Audio" : "Video";
var res = type == "Video" ? $"[{f["Width"]}x{f["Height"]}]" : "";
var id = $"[{f["FormatId"]}] ";
var tbr = $"[{((int)f["Tbr"]).ToString().PadLeft(4)} Kbps] ";
@@ -521,24 +534,23 @@ namespace N_m3u8DL_CLI
return $"{type} => {id}{tbr}{asr}{fps}{lang}{codecs}{res}";
}
var startCursorIndex = LOGGER.CursorIndex;
var startCursorIndex = Console.CursorTop;
var cursorIndex = startCursorIndex;
for (int i = 0; i < formatList.Count; i++)
{
Console.WriteLine("".PadRight(13) + $"[{i.ToString().PadLeft(2)}]. {Stringify(formatList[i])}");
LOGGER.CursorIndex++;
cursorIndex++;
}
Console.CursorVisible = true;
LOGGER.PrintLine("Found Multiple Language Audio Tracks.\r\n" + "".PadRight(13) + "Please Select What You Want(Up to 1 Video and 1 Audio).");
Console.Write("".PadRight(13) + "Enter Numbers Separated By A Space: ");
var input = Console.ReadLine();
LOGGER.CursorIndex += 2;
Console.CursorVisible = false;
for (int i = startCursorIndex; i < LOGGER.CursorIndex; i++)
cursorIndex += 2;
for (int i = startCursorIndex; i < cursorIndex; i++)
{
Console.SetCursorPosition(0, i);
Console.Write("".PadRight(300));
}
LOGGER.CursorIndex = startCursorIndex;
Console.SetCursorPosition(0, startCursorIndex);
if (!string.IsNullOrEmpty(input))
{
bestVideo = new Dictionary<string, dynamic>() { ["Tbr"] = 0 };

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props" Condition="Exists('..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props')" />
<Import Project="..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props" Condition="Exists('..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props')" />
<Import Project="..\packages\Resource.Embedder.2.1.1\build\Resource.Embedder.props" Condition="Exists('..\packages\Resource.Embedder.2.1.1\build\Resource.Embedder.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -41,9 +42,14 @@
<ApplicationIcon>logo_3Iv_icon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="BrotliSharpLib, Version=0.3.2.0, Culture=neutral, PublicKeyToken=3f4e2a1cd615fcb7, processorArchitecture=MSIL">
<HintPath>..\packages\BrotliSharpLib.0.3.3\lib\net451\BrotliSharpLib.dll</HintPath>
</Reference>
<Reference Include="Costura, Version=4.1.0.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
<HintPath>..\packages\Costura.Fody.4.1.0\lib\net40\Costura.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Build.Framework" />
<Reference Include="Microsoft.Build.Utilities.v4.0" />
<Reference Include="Microsoft.JScript" />
<Reference Include="netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
@@ -57,6 +63,9 @@
<Reference Include="System.Collections" />
<Reference Include="System.Core" />
<Reference Include="System.IO" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
@@ -78,11 +87,13 @@
<Compile Include="Global.cs" />
<Compile Include="HLSLiveDownloader.cs" />
<Compile Include="HLSTags.cs" />
<Compile Include="IqJsonParser.cs" />
<Compile Include="LOGGER.cs" />
<Compile Include="DownloadManager.cs" />
<Compile Include="MPDParser.cs" />
<Compile Include="Parser.cs" />
<Compile Include="Program.cs" />
<Compile Include="ProgressReporter.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Downloader.cs" />
<Compile Include="strings.Designer.cs">
@@ -137,12 +148,13 @@
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Fody.6.0.0\build\Fody.targets" Condition="Exists('..\packages\Fody.6.0.0\build\Fody.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Resource.Embedder.2.1.1\build\Resource.Embedder.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Resource.Embedder.2.1.1\build\Resource.Embedder.props'))" />
<Error Condition="!Exists('..\packages\Fody.6.0.0\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.6.0.0\build\Fody.targets'))" />
<Error Condition="!Exists('..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props'))" />
</Target>
<Import Project="..\packages\Fody.6.0.0\build\Fody.targets" Condition="Exists('..\packages\Fody.6.0.0\build\Fody.targets')" />
</Project>

View File

@@ -102,8 +102,13 @@ namespace N_m3u8DL_CLI
//获取m3u8内容
if (!LiveStream)
LOGGER.PrintLine(strings.downloadingM3u8, LOGGER.Warning);
//if (!LiveStream)
// LOGGER.PrintLine(strings.downloadingM3u8, LOGGER.Warning);
if (M3u8Url.Contains(".cntv."))
{
M3u8Url = M3u8Url.Replace("/h5e/", "/");
}
if (M3u8Url.StartsWith("http"))
{
@@ -139,15 +144,10 @@ namespace N_m3u8DL_CLI
m3u8Content = DecodeImooc.DecodeM3u8(m3u8Content);
}
if (M3u8Url.Contains("cntv.qcloudcdn.com"))
{
M3u8Url = M3u8Url.Replace("/h5e/", "/");
}
if (m3u8Content.Contains("</MPD>") && m3u8Content.Contains("<MPD"))
{
LOGGER.PrintLine(strings.startParsingMpd, LOGGER.Warning);
LOGGER.WriteLine(strings.startParsingMpd);
//LOGGER.PrintLine(strings.startParsingMpd, LOGGER.Warning);
//LOGGER.WriteLine(strings.startParsingMpd);
var mpdSavePath = Path.Combine(DownDir, "dash.mpd");
//输出mpd文件
File.WriteAllText(mpdSavePath, m3u8Content);
@@ -158,6 +158,17 @@ namespace N_m3u8DL_CLI
m3u8Content = File.ReadAllText(new Uri(M3u8Url).LocalPath);
}
if (m3u8Content.StartsWith("{\"payload\""))
{
var iqJsonPath = Path.Combine(DownDir, "iq.json");
//输出mpd文件
File.WriteAllText(iqJsonPath, m3u8Content);
//分析json文件
var newUri = IqJsonParser.Parse(DownDir, m3u8Content);
M3u8Url = newUri;
m3u8Content = File.ReadAllText(new Uri(M3u8Url).LocalPath);
}
//输出m3u8文件
File.WriteAllText(m3u8SavePath, m3u8Content);
@@ -182,10 +193,10 @@ namespace N_m3u8DL_CLI
}
//针对AppleTv修正
if (m3u8Content.Contains("#EXT-X-DISCONTINUITY") && m3u8Content.Contains("#EXT-X-MAP") && M3u8Url.Contains(".apple.com/"))
if (m3u8Content.Contains("#EXT-X-DISCONTINUITY") && m3u8Content.Contains("#EXT-X-MAP") && (M3u8Url.Contains(".apple.com/") || Regex.IsMatch(m3u8Content, "#EXT-X-MAP.*\\.apple\\.com/")))
{
//只取加密部分即可
Regex ykmap = new Regex("(#EXT-X-KEY:[\\s\\S]*?)#EXT-X-DISCONTINUITY");
Regex ykmap = new Regex("(#EXT-X-KEY:[\\s\\S]*?)(#EXT-X-DISCONTINUITY|#EXT-X-ENDLIST)");
if (ykmap.IsMatch(m3u8Content))
{
m3u8Content = "#EXTM3U\r\n" + ykmap.Match(m3u8Content).Groups[1].Value + "\r\n#EXT-X-ENDLIST";
@@ -201,11 +212,11 @@ namespace N_m3u8DL_CLI
BaseUrl = GetBaseUrl(M3u8Url, Headers);
}
if (!LiveStream)
{
LOGGER.WriteLine(strings.parsingM3u8);
LOGGER.PrintLine(strings.parsingM3u8);
}
//if (!LiveStream)
//{
// LOGGER.WriteLine(strings.parsingM3u8);
// LOGGER.PrintLine(strings.parsingM3u8);
//}
if (!string.IsNullOrEmpty(KeyBase64))
{
@@ -542,7 +553,7 @@ namespace N_m3u8DL_CLI
}
sb.Append("}");
extLists.Add(sb.ToString().Replace(",}", "}"));
if (Convert.ToInt64(extList[0]) > bestBandwidth)
if (Convert.ToInt64(extList[0]) >= bestBandwidth)
{
bestBandwidth = Convert.ToInt64(extList[0]);
bestUrl = listUrl;
@@ -606,25 +617,24 @@ namespace N_m3u8DL_CLI
//多种音频语言 让用户选择
else
{
var startCursorIndex = LOGGER.CursorIndex;
var startCursorIndex = Console.CursorTop;
var cursorIndex = startCursorIndex;
LOGGER.PrintLine("Found Multiple Language Audio Tracks.", LOGGER.Warning);
for (int i = 0; i < MEDIA_AUDIO_GROUP[bestUrlAudio].Count; i++)
{
Console.WriteLine("".PadRight(13) + $"[{i.ToString().PadLeft(2)}]. {bestUrlAudio} => {MEDIA_AUDIO_GROUP[bestUrlAudio][i]}");
LOGGER.CursorIndex++;
cursorIndex++;
}
Console.CursorVisible = true;
LOGGER.PrintLine("Please Select What You Want.(Up To 1 Track)");
Console.Write("".PadRight(13) + "Enter Number: ");
var input = Console.ReadLine();
LOGGER.CursorIndex += 2;
Console.CursorVisible = false;
for (int i = startCursorIndex; i < LOGGER.CursorIndex; i++)
cursorIndex += 2;
for (int i = startCursorIndex; i < cursorIndex; i++)
{
Console.SetCursorPosition(0, i);
Console.Write("".PadRight(300));
}
LOGGER.CursorIndex = startCursorIndex;
Console.SetCursorPosition(0, startCursorIndex);
audioUrl = MEDIA_AUDIO_GROUP[bestUrlAudio][int.Parse(input)].Uri;
}
}
@@ -637,25 +647,24 @@ namespace N_m3u8DL_CLI
//多种字幕语言 让用户选择
else
{
var startCursorIndex = LOGGER.CursorIndex;
var startCursorIndex = Console.CursorTop;
var cursorIndex = startCursorIndex;
LOGGER.PrintLine("Found Multiple Language Subtitle Tracks.", LOGGER.Warning);
for (int i = 0; i < MEDIA_SUB_GROUP[bestUrlSub].Count; i++)
{
Console.WriteLine("".PadRight(13) + $"[{i.ToString().PadLeft(2)}]. {bestUrlSub} => {MEDIA_SUB_GROUP[bestUrlSub][i]}");
LOGGER.CursorIndex++;
cursorIndex++;
}
Console.CursorVisible = true;
LOGGER.PrintLine("Please Select What You Want.(Up To 1 Track)");
Console.Write("".PadRight(13) + "Enter Number: ");
var input = Console.ReadLine();
LOGGER.CursorIndex += 2;
Console.CursorVisible = false;
for (int i = startCursorIndex; i < LOGGER.CursorIndex; i++)
cursorIndex += 2;
for (int i = startCursorIndex; i < cursorIndex; i++)
{
Console.SetCursorPosition(0, i);
Console.Write("".PadRight(300));
}
LOGGER.CursorIndex = startCursorIndex;
Console.SetCursorPosition(0, startCursorIndex);
subUrl = MEDIA_SUB_GROUP[bestUrlSub][int.Parse(input)].Uri;
}
}
@@ -665,11 +674,16 @@ namespace N_m3u8DL_CLI
jsonM3u8Info.Add("sub", subUrl);
if (extMAP[0] != "")
{
DownloadManager.HasExtMap = true;
if (extMAP[1] == "")
jsonM3u8Info.Add("extMAP", extMAP[0]);
else
jsonM3u8Info.Add("extMAP", extMAP[0] + "|" + extMAP[1]);
}
else
{
DownloadManager.HasExtMap = false;
}
//根据DurRange来生成分片Range
if (DurStart != "" || DurEnd != "")

View File

@@ -28,14 +28,10 @@ namespace N_m3u8DL_CLI.NetCore
case 0:
LOGGER.WriteLine(strings.ExitedCtrlC
+ "\r\n\r\nTask End: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")); //Ctrl+C关闭
Console.CursorVisible = true;
Console.SetCursorPosition(0, LOGGER.CursorIndex);
break;
case 2:
LOGGER.WriteLine(strings.ExitedForce
+ "\r\n\r\nTask End: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")); //按控制台关闭按钮关闭
Console.CursorVisible = true;
Console.SetCursorPosition(0, LOGGER.CursorIndex);
break;
}
return false;
@@ -279,6 +275,7 @@ namespace N_m3u8DL_CLI.NetCore
{
Parser.DurStart = reg2.Match(p).Groups[1].Value;
Parser.DurEnd = reg2.Match(p).Groups[5].Value;
if (Parser.DurEnd == "00:00:00") Parser.DurEnd = "";
Parser.DelAd = false;
}
}
@@ -335,15 +332,14 @@ namespace N_m3u8DL_CLI.NetCore
testurl = args[0];
else
{
Console.CursorVisible = true;
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write("N_m3u8DL-CLI");
Console.ResetColor();
Console.Write(" > ");
args = Global.ParseArguments(Console.ReadLine()).ToArray(); //解析命令行
Console.Clear();
Global.WriteInit();
Console.CursorVisible = false;
goto parseArgs;
}
@@ -376,7 +372,6 @@ namespace N_m3u8DL_CLI.NetCore
//开始解析
Console.CursorVisible = false;
LOGGER.PrintLine($"{strings.fileName}{fileName}");
LOGGER.PrintLine($"{strings.savePath}{Path.GetDirectoryName(Path.Combine(workDir, fileName))}");
@@ -391,10 +386,10 @@ namespace N_m3u8DL_CLI.NetCore
parser.BaseUrl = baseUrl;
parser.Headers = reqHeaders;
string exePath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
LOGGER.LOGFILE = Path.Combine(exePath, "Logs", DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".log");
LOGGER.LOGFILE = Path.Combine(exePath, "Logs", DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss-fff") + ".log");
LOGGER.InitLog();
LOGGER.WriteLine(strings.startParsing + testurl);
LOGGER.PrintLine(strings.startParsing, LOGGER.Warning);
LOGGER.PrintLine(strings.startParsing + " " + testurl, LOGGER.Warning);
if (testurl.EndsWith(".json") && File.Exists(testurl)) //可直接跳过解析
{
if (!Directory.Exists(Path.Combine(workDir, fileName)))//若文件夹不存在则新建文件夹
@@ -428,7 +423,6 @@ namespace N_m3u8DL_CLI.NetCore
DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(workDir, fileName));
directoryInfo.Delete(true);
LOGGER.PrintLine(strings.InvalidUri, LOGGER.Error);
LOGGER.CursorIndex = 5;
inputRetryCount--;
goto input;
}
@@ -486,14 +480,12 @@ namespace N_m3u8DL_CLI.NetCore
HTTPListener.StartListening();*/
LOGGER.WriteLineError(strings.downloadFailed);
LOGGER.PrintLine(strings.downloadFailed, LOGGER.Error);
Console.CursorVisible = true;
Thread.Sleep(3000);
Environment.Exit(-1);
//Console.Write("按任意键继续..."); Console.ReadKey(); return;
}
catch (Exception ex)
{
Console.CursorVisible = true;
LOGGER.PrintLine(ex.Message, LOGGER.Error);
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
class ProgressReporter
{
private static string speed = "";
private static string progress = "";
static object lockThis = new object();
public static void Report(string progress, string speed)
{
lock (lockThis)
{
if (!string.IsNullOrEmpty(progress)) ProgressReporter.progress = progress;
if (!string.IsNullOrEmpty(speed)) ProgressReporter.speed = speed;
string now = DateTime.Now.ToString("HH:mm:ss.000");
var sub = Console.WindowWidth - 4 - ProgressReporter.progress.Length - ProgressReporter.speed.Length - now.Length;
if (sub <= 0) sub = 0;
string print = now + " " + ProgressReporter.progress + " " + ProgressReporter.speed + new string(' ', sub);
Console.Write("\r" + print + "\r");
//Console.Write(print);
}
}
}
}

View File

@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("nilaoda")]
[assembly: AssemblyProduct("N_m3u8DL-CLI")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
// 方法是按如下所示使用“*”: :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("2.7.0.0")]
[assembly: AssemblyVersion("2.9.8.0")]
[assembly: AssemblyFileVersion("2.9.8.0")]

View File

@@ -59,10 +59,12 @@ namespace N_m3u8DL_CLI
//Console.Title = Now + " / " + Total;
string downloadedSize = Global.FormatFileSize(DownloadManager.DownloadedSize);
string estimatedSize = Global.FormatFileSize(DownloadManager.DownloadedSize * total / now);
int padding = downloadedSize.Length > estimatedSize.Length ? downloadedSize.Length : estimatedSize.Length;
DownloadManager.ToDoSize = (DownloadManager.DownloadedSize * total / now) - DownloadManager.DownloadedSize;
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
Console.SetCursorPosition(0, 2);
Console.Write(("Progress: " + Now + " of " + Total
+ $" ({percent}/{downloadedSize}/{estimatedSize}/{Global.FormatTime(Convert.ToInt32(TotalDuration))})").PadRight(62));
var print = "Progress: " + Now + "/" + Total
+ $" ({percent}) -- {downloadedSize.PadLeft(padding)}/{estimatedSize.PadRight(padding)}";
ProgressReporter.Report(print, "");
}
private void OnRenamed(object source, RenamedEventArgs e)
@@ -77,10 +79,12 @@ namespace N_m3u8DL_CLI
//Console.Title = Now + " / " + Total;
string downloadedSize = Global.FormatFileSize(DownloadManager.DownloadedSize);
string estimatedSize = Global.FormatFileSize(DownloadManager.DownloadedSize * total / now);
int padding = downloadedSize.Length > estimatedSize.Length ? downloadedSize.Length : estimatedSize.Length;
DownloadManager.ToDoSize = (DownloadManager.DownloadedSize * total / now) - DownloadManager.DownloadedSize;
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
Console.SetCursorPosition(0, 2);
Console.Write(("Progress: " + Now + " of " + Total
+ $" ({percent}/{downloadedSize}/{estimatedSize}/{Global.FormatTime(Convert.ToInt32(TotalDuration))})").PadRight(62));
var print = "Progress: " + Now + "/" + Total
+ $" ({percent}) -- {downloadedSize.PadLeft(padding)}/{estimatedSize.PadRight(padding)}";
ProgressReporter.Report(print, "");
}
private void OnDeleted(object source, FileSystemEventArgs e)
@@ -95,10 +99,12 @@ namespace N_m3u8DL_CLI
//Console.Title = Now + " / " + Total;
string downloadedSize = Global.FormatFileSize(DownloadManager.DownloadedSize);
string estimatedSize = Global.FormatFileSize(DownloadManager.DownloadedSize * total / now);
int padding = downloadedSize.Length > estimatedSize.Length ? downloadedSize.Length : estimatedSize.Length;
DownloadManager.ToDoSize = (DownloadManager.DownloadedSize * total / now) - DownloadManager.DownloadedSize;
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
Console.SetCursorPosition(0, 2);
Console.Write(("Progress: " + Now + " of " + Total
+ $" ({percent}/{downloadedSize}/{estimatedSize}/{Global.FormatTime(Convert.ToInt32(TotalDuration))})").PadRight(62));
var print = "Progress: " + Now + "/" + Total
+ $" ({percent}) -- {downloadedSize.PadLeft(padding)}/{estimatedSize.PadRight(padding)}";
ProgressReporter.Report(print, "");
}
}
}

View File

@@ -337,4 +337,26 @@
- 适配AppleTv资源
2021年3月25日
- 优化下载监控
- 为下载分片增加了自动重试机制(3次)
- 为下载分片增加了自动重试机制(3次)
2021年3月27日
- 优化显示输出
- 增加ETA显示
2021年6月27日
- 修正判断png图片时可能出现的数组越界bug
- 支持解压brotli测试地址 https://www.baobuzz.com/m3u8/236963.m3u8?sign=811ae52382b7dd1d247f705e1bcaddf4
2021年7月4日
- 优化master选择最高清晰度逻辑大于改为大于等于
- 支持爱奇艺DRM-JSON自动转换为m3u8
2021年8月15日
- 优化显示输出
- 强校验MAP下载成功
2021年9月5日
- 修复MPD节点选择BUG
- 修复速度输出padding负值问题
- 修复同一个period且同id导致被重复添加分片
- 优化AppleTV判断
2021年10月19日
- 修复选择清晰度在输入选项后界面异常问题
- 修复日志冲突问题
2021年11月12日
- 修复init url缺失baseurl问题

View File

@@ -1,7 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Fody" version="6.0.0" targetFramework="net46" developmentDependency="true" />
<package id="BrotliSharpLib" version="0.3.3" targetFramework="net46" />
<package id="Costura.Fody" version="4.1.0" targetFramework="net46" />
<package id="Fody" version="6.0.0" targetFramework="net46" developmentDependency="true" />
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net46" />
<package id="NiL.JS" version="2.5.1428" targetFramework="net46" />
<package id="Resource.Embedder" version="2.1.1" targetFramework="net46" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net46" />
</packages>

View File

@@ -13,9 +13,13 @@
# [ENGLISH VERSION](https://github.com/nilaoda/N_m3u8DL-CLI/blob/master/README_ENG.md)
# 下载使用
* 发行版: https://github.com/nilaoda/N_m3u8DL-CLI/releases
* 自动构建版`(供测试)`: https://github.com/nilaoda/N_m3u8DL-CLI/actions
# 关于开源
本项目已2019年10月9日开源采用MIT许可证各取所需。
本项目已2019年10月9日开源采用MIT许可证各取所需。
# 关于跨平台
搁置了
@@ -81,5 +85,8 @@ N_m3u8DL-CLI.exe <URL|File|JSON> [OPTIONS]
# 用户文档
https://nilaoda.github.io/N_m3u8DL-CLI/
# 聊聊
https://discord.gg/W5tvcRJDPs
# 赞赏
![Wow](https://nilaoda.github.io/N_m3u8DL-CLI/source/images/alipay.png)

View File

@@ -8,10 +8,10 @@
╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚════╝ ╚═════╝ ╚══════╝ ╚═════╝╚══════╝╚═╝
```
This is a m3u8 downloader.
This is an m3u8 downloader.
## Summary
Supports:
* Auto deceypt for `AES-128-CBC`
* Auto decrypt for `AES-128-CBC`
* `Master List`
* Live stream recording(`BETA`)
* Customize HTTP headers
@@ -20,8 +20,8 @@ Supports:
* Network driver on Windows OS
* Alternative audio/video track
* Mux without video track
* Costom HTTP proxy or Use system proxy
* Optimization for Chinese streaming platform
* Custom HTTP proxy or Use system proxy
* Optimization for Chinese streaming platforms
![ScreenShot](https://nilaoda.github.io/N_m3u8DL-CLI/source/images/%E7%9B%B4%E6%8E%A5%E4%BD%BF%E7%94%A8.gif)
@@ -50,7 +50,7 @@ N_m3u8DL-CLI.exe <URL|JSON|FILE> [OPTIONS]
--proxyAddress http://xx Set HTTP Proxy, like http://127.0.0.1:8080
--enableDelAfterDone Enable delete clips after download completed
--enableMuxFastStart Enable fast start for mp4
--enableBinaryMerge Enable use binary merge instead ffmpeg
--enableBinaryMerge Enable use binary merge instead of ffmpeg
--enableParseOnly Enable parse mode
--enableAudioOnly Enable only audio track when mux use ffmpeg
--disableDateInfo Disable write date info when mux use ffmpeg
@@ -61,3 +61,6 @@ N_m3u8DL-CLI.exe <URL|JSON|FILE> [OPTIONS]
## Document
https://nilaoda.github.io/N_m3u8DL-CLI/
## Chit-chat
https://discord.gg/W5tvcRJDPs

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long