1
mirror of https://github.com/nilaoda/N_m3u8DL-CLI synced 2025-09-07 02:45:59 +02:00

Compare commits

...

104 Commits
1.3 ... 2.4.8

Author SHA1 Message Date
nilaoda
a6c7c0fd8c ?__gda__行为优化 2020-01-31 21:39:05 +08:00
nilaoda
bd6df6b58c JS增加naver tv 2020-01-31 21:38:27 +08:00
nilaoda
e0a9071d62 修改超链接错误 2020-01-30 19:16:11 +08:00
nilaoda
5c9bcf72d2 更新OnDemandChina获取JS 2020-01-29 23:48:15 +08:00
nilaoda
7cf2c12d0c 增加OnDemandChina获取JS 2020-01-29 23:41:03 +08:00
nilaoda
1b35fe2d2c Update DownloadManager.cs 2020-01-29 22:45:26 +08:00
nilaoda
ed3aae1cb9 v2.4.7 2020-01-29 22:28:07 +08:00
nilaoda
01c2ecbeb5 增加爱奇艺字幕JS 2020-01-28 23:48:35 +08:00
nilaoda
9993ec8177 添加获取爱奇艺高帧率视频JS 2020-01-09 23:01:20 +08:00
nilaoda
464300c860 Update N_m3u8DL-CLI.sln 2020-01-07 21:22:43 +08:00
nilaoda
45fa58a46f Update README.md 2020-01-06 09:44:09 +08:00
nilaoda
f93ddc7107 Update README.md 2019-12-30 16:38:08 +08:00
nilaoda
05f450fa6d 永远信任https 2019-12-28 23:45:31 +08:00
nilaoda
580c374d7b 更新腾讯视频代码 2019-12-28 14:17:43 +08:00
nilaoda
b58dc5d8e0 Update README_ENG.md 2019-12-26 23:29:03 +08:00
nilaoda
f50cf7862a Add link 2019-12-23 17:16:28 +08:00
nilaoda
fdad68d483 Create README_ENG.md 2019-12-23 17:04:46 +08:00
nilaoda
d7aaa5323b 修复腾讯视频HDR10下载问题 2019-12-18 23:04:05 +08:00
nilaoda
6c2e13b800 v2.4.4
修复part大于1时读取json混流文件的严重错误;自动去除优酷的广告分片及前情提要
2019-12-18 19:49:58 +08:00
nilaoda
086fc57958 修复解析bug;增加杜比视界识别场景 2019-12-18 09:15:42 +08:00
nilaoda
bc349b8977 文件名特殊字符处理 2019-12-16 22:16:54 +08:00
nilaoda
cc4efed3c6 完善芒果TV请求头的自动添加 2019-11-30 00:43:00 +08:00
nilaoda
cf958e833b fix doc 2019-11-15 23:55:30 +08:00
nilaoda
fb09add0cd update docs 2019-11-15 10:49:07 +08:00
nilaoda
5a3c5baefd 更新JS 2019-11-13 23:35:04 +08:00
nilaoda
839afd8e61 Update N_m3u8DL-CLI.csproj 2019-11-08 20:38:25 +08:00
nilaoda
338c7a25d0 更新文档 2019-10-24 15:40:28 +08:00
nilaoda
f57ce8c2da Update README.md 2019-10-24 15:32:14 +08:00
nilaoda
a5009e1683 增加不检测完整性及自定义KEY 2019-10-24 14:44:09 +08:00
nilaoda
66933da9de Update 2019-10-19 21:02:50 +08:00
nilaoda
136389e248 去掉了针对优酷链接的默认行为 2019-10-18 00:16:57 +08:00
nilaoda
7971104bd4 更新优酷DRM视频临时解决方法 2019-10-16 14:35:57 +08:00
nilaoda
cc4941554d Create N_m3u8DL-CLI.sln 2019-10-09 22:03:15 +08:00
nilaoda
c50371080a Update README.md 2019-10-09 21:59:22 +08:00
nilaoda
154c5f84a1 开放源代码 2019-10-09 21:52:07 +08:00
nilaoda
1556ee42df Update doc. 2019-10-05 11:28:21 +08:00
nilaoda
121ff9f226 Update README.md 2019-09-22 20:42:30 +08:00
nilaoda
e5b84c2f04 viki parser 2019-09-21 22:15:27 +08:00
nilaoda
7e562a06bb Add VIKI (JS) 2019-09-21 01:01:37 +08:00
nilaoda
1eae19c3ea Merge branch 'master' of https://github.com/nilaoda/N_m3u8DL-CLI 2019-09-18 19:32:46 +08:00
nilaoda
ba33049b99 Update README.md 2019-09-18 19:32:10 +08:00
nilaoda
9773087bf9 Update docs 2019-09-18 19:31:08 +08:00
nilaoda
b60d39ad0d Update README.md 2019-09-16 22:15:46 +08:00
nilaoda
e89705a370 Update README.md 2019-09-10 22:27:26 +08:00
nilaoda
4aea16d672 Update README.md 2019-09-10 13:56:43 +08:00
nilaoda
afc69eae1a Merge pull request #19 from nilaoda/add-license-1
Create LICENSE
2019-09-10 11:59:13 +08:00
nilaoda
2446a25b35 Create LICENSE 2019-09-10 11:58:37 +08:00
nilaoda
d37524180d Update README.md 2019-09-10 11:21:59 +08:00
nilaoda
b02a29fbb7 更新文档 2019-09-09 17:22:59 +08:00
nilaoda
e87d02230d Merge branch 'master' of https://github.com/nilaoda/N_m3u8DL-CLI 2019-09-09 17:20:08 +08:00
nilaoda
63609a0970 增加--enableAudioOnly 2019-09-09 17:19:47 +08:00
nilaoda
1fe798ab19 Update README.md 2019-09-08 22:40:36 +08:00
nilaoda
21b9977501 Update README.md 2019-09-08 22:40:01 +08:00
nilaoda
fbe37be499 更新截图 2019-09-08 22:17:37 +08:00
nilaoda
6416dd642d 更新爱奇艺4K-H264 2019-08-27 01:52:23 +08:00
nilaoda
967f0582a6 修复台湾爱奇艺杜比 2019-08-25 00:02:02 +08:00
nilaoda
a5aeffc6ec 更新js 2019-08-24 23:09:25 +08:00
nilaoda
745cbd92ad 更新文档 2019-08-22 23:26:29 +08:00
nilaoda
4a4412a262 常规更新 2019-08-21 14:55:24 +08:00
nilaoda
084989b3ce update js 2019-08-11 23:38:52 +08:00
nilaoda
640208d95c 增加wetv字幕js 2019-08-11 23:31:33 +08:00
nilaoda
ad148ba4c7 更新芒果TV 2019-08-08 21:47:54 +08:00
nilaoda
7c961dbbbf 修正文档日期 2019-08-08 21:12:34 +08:00
nilaoda
83df7b4301 增加爱奇艺1080P H265 2019-08-08 21:08:41 +08:00
nilaoda
860282a463 支持愛奇藝 2019-08-07 12:06:07 +08:00
nilaoda
70582029dd 修复爱奇艺4K_H264 2019-08-07 11:23:53 +08:00
nilaoda
1f3002ef66 更新爱奇艺4K264、4K265代码 2019-08-07 10:21:56 +08:00
nilaoda
07c758dd00 增加爱奇艺杜比音轨JS 2019-08-07 02:09:59 +08:00
nilaoda
38f2f5589f 更新爱奇艺JS 2019-08-04 23:58:34 +08:00
nilaoda
c7b9414613 更新说明书 2019-07-24 00:45:03 +08:00
nilaoda
84953fa16a 更新爱奇艺JS 2019-07-14 21:43:03 +08:00
nilaoda
4cf88711d6 更新爱奇艺JS 2019-07-13 19:11:54 +08:00
nilaoda
b59940f719 修正错别字 2019-07-10 23:54:30 +08:00
nilaoda
6a6a338868 增加阿里云大学教程 2019-07-10 23:51:03 +08:00
nilaoda
0d74380e1c 更新文档 2019-07-10 22:42:30 +08:00
nilaoda
5a398bdb79 更新文档 2019-07-10 22:40:36 +08:00
nilaoda
c2953a3d43 修复腾讯视频m3u8获取代码 2019-07-10 21:31:41 +08:00
nilaoda
6f2ebfba2d 增加气球云教程 2019-07-10 00:49:39 +08:00
nilaoda
3c1b893919 Update README.md 2019-07-09 01:54:59 +08:00
nilaoda
8b6f411af9 add doc 2019-07-09 01:54:12 +08:00
nilaoda
6cfd74f3a0 add docs 2019-07-09 01:42:37 +08:00
nilaoda
044068016b Update README.md 2019-07-07 22:35:53 +08:00
nilaoda
6d21ef0a10 Update README.md 2019-07-07 21:36:36 +08:00
nilaoda
6d1834ca9f Update README.md 2019-07-07 21:28:37 +08:00
nilaoda
3e215359b4 Javascript 2019-07-07 21:26:57 +08:00
nilaoda
3d9610d3bb 搜狐 2019-07-07 16:41:17 +08:00
nilaoda
377c575c72 Update README.md 2019-06-21 21:41:12 +08:00
nilaoda
c02d737125 Update README.md 2019-06-21 21:26:04 +08:00
nilaoda
c1fb395ee0 爱奇艺代码 2019-06-19 20:53:54 +08:00
nilaoda
3920ae6178 Update README.md 2019-06-19 20:34:42 +08:00
nilaoda
f96ea497b7 Update README.md 2019-06-16 16:24:29 +08:00
nilaoda
0013ec4bb5 Update README.md 2019-06-16 16:05:26 +08:00
nilaoda
604c25a2be Update README.md 2019-06-16 16:05:15 +08:00
nilaoda
a83afd8908 Update README.md 2019-06-16 15:46:33 +08:00
nilaoda
206342c4cf Update README.md 2019-06-14 11:29:26 +08:00
nilaoda
296739910e Update README.md 2019-06-14 11:29:02 +08:00
nilaoda
5a62342145 add mgtv 2019-06-13 17:28:59 +08:00
nilaoda
64df11fefb 更新优酷代码 2019-06-13 17:05:50 +08:00
nilaoda
5d82bc9135 Update README.md 2019-06-12 11:04:41 +08:00
nilaoda
2a61b61205 Update README.md 2019-06-11 12:54:37 +08:00
nilaoda
15568f917c Update README.md 2019-06-11 12:52:05 +08:00
nilaoda
b196990972 Update README.md 2019-06-11 11:23:19 +08:00
nilaoda
d4a94d185a Update README.md 2019-06-08 23:04:43 +08:00
nilaoda
d865c05d9a Update README.md 2019-06-08 01:14:18 +08:00
83 changed files with 13249 additions and 30 deletions

63
.gitattributes vendored Normal file
View File

@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

261
.gitignore vendored Normal file

File diff suppressed because it is too large Load Diff

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 nilaoda
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

25
N_m3u8DL-CLI.sln Normal file
View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29215.179
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "N_m3u8DL-CLI", "N_m3u8DL-CLI\N_m3u8DL-CLI.csproj", "{4FB61439-B738-46AC-B3AF-2BF72150D057}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {82B9270D-B7B2-4591-BF8A-5B4EBCD0EA8A}
EndGlobalSection
EndGlobal

6
N_m3u8DL-CLI/App.config Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup>
</configuration>

View File

@@ -0,0 +1,77 @@
using System.Collections.Generic;
namespace N_m3u8DL_CLI
{
public class CommandLineArgument
{
List<CommandLineArgument> _arguments;
int _index;
string _argumentText;
public CommandLineArgument Next
{
get
{
if (_index < _arguments.Count - 1)
{
return _arguments[_index + 1];
}
return null;
}
}
public CommandLineArgument Previous
{
get
{
if (_index > 0)
{
return _arguments[_index - 1];
}
return null;
}
}
internal CommandLineArgument(List<CommandLineArgument> args, int index, string argument)
{
_arguments = args;
_index = index;
_argumentText = argument;
}
public CommandLineArgument Take()
{
return Next;
}
public IEnumerable<CommandLineArgument> Take(int count)
{
var list = new List<CommandLineArgument>();
var parent = this;
for (int i = 0; i < count; i++)
{
var next = parent.Next;
if (next == null)
break;
list.Add(next);
parent = next;
}
return list;
}
public static implicit operator string(CommandLineArgument argument)
{
return argument._argumentText;
}
public override string ToString()
{
return _argumentText;
}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
public class CommandLineArgumentParser
{
List<CommandLineArgument> _arguments;
public static CommandLineArgumentParser Parse(string[] args)
{
return new CommandLineArgumentParser(args);
}
public CommandLineArgumentParser(string[] args)
{
_arguments = new List<CommandLineArgument>();
for (int i = 0; i < args.Length; i++)
{
_arguments.Add(new CommandLineArgument(_arguments, i, args[i]));
}
}
public CommandLineArgument Get(string argumentName)
{
return _arguments.FirstOrDefault(p => p == argumentName);
}
public bool Has(string argumentName)
{
return _arguments.Count(p => p == argumentName) > 0;
}
}
}

82
N_m3u8DL-CLI/Decrypter.cs Normal file
View File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
class Decrypter
{
public static byte[] AES128Decrypt(string filePath, byte[] keyByte, byte[] ivByte)
{
FileStream fs = new FileStream(filePath, FileMode.Open);
//获取文件大小
long size = fs.Length;
byte[] inBuff = new byte[size];
fs.Read(inBuff, 0, inBuff.Length);
fs.Close();
Aes dcpt = Aes.Create("AES");
dcpt.BlockSize = 128;
dcpt.KeySize = 128;
dcpt.Key = keyByte;
dcpt.IV = ivByte;
dcpt.Mode = CipherMode.CBC;
dcpt.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = dcpt.CreateDecryptor();
Byte[] resultArray = cTransform.TransformFinalBlock(inBuff, 0, inBuff.Length);
return resultArray;
}
public static byte[] AES128Decrypt(byte[] encryptedBuff, byte[] keyByte, byte[] ivByte)
{
byte[] inBuff = encryptedBuff;
Aes dcpt = Aes.Create("AES");
dcpt.BlockSize = 128;
dcpt.KeySize = 128;
dcpt.Key = keyByte;
dcpt.IV = ivByte;
dcpt.Mode = CipherMode.CBC;
dcpt.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = dcpt.CreateDecryptor();
Byte[] resultArray = cTransform.TransformFinalBlock(inBuff, 0, inBuff.Length);
return resultArray;
}
public static byte[] HexStringToBytes(string hexStr)
{
if (string.IsNullOrEmpty(hexStr))
{
return new byte[0];
}
if (hexStr.StartsWith("0x") || hexStr.StartsWith("0X"))
{
hexStr = hexStr.Remove(0, 2);
}
int count = hexStr.Length;
if (count % 2 == 1)
{
throw new ArgumentException("Invalid length of bytes:" + count);
}
int byteCount = count / 2;
byte[] result = new byte[byteCount];
for (int ii = 0; ii < byteCount; ++ii)
{
var tempBytes = Byte.Parse(hexStr.Substring(2 * ii, 2), System.Globalization.NumberStyles.HexNumber);
result[ii] = tempBytes;
}
return result;
}
}
}

File diff suppressed because it is too large Load Diff

231
N_m3u8DL-CLI/Downloader.cs Normal file
View File

@@ -0,0 +1,231 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
class Downloader
{
private int timeOut = 0;
private int retry = 5;
private int count = 0;
private int segIndex = 0;
private double segDur = 0;
private string fileUrl = string.Empty;
private string savePath = string.Empty;
private string headers = string.Empty;
private string method = string.Empty;
private string key = string.Empty;
private string iv = string.Empty;
private string liveFile = string.Empty;
private long expectByte = -1;
private long startByte = 0;
private bool isLive = false;
private bool isDone = false;
private bool firstSeg = true;
private FileStream liveStream = null;
public string FileUrl { get => fileUrl; set => fileUrl = value; }
public string SavePath { get => savePath; set => savePath = value; }
public string Headers { get => headers; set => headers = value; }
public string Method { get => method; set => method = value; }
public string Key { get => key; set => key = value; }
public string Iv { get => iv; set => iv = value; }
public bool IsLive { get => isLive; set => isLive = value; }
public int Retry { get => retry; set => retry = value; }
public bool IsDone { get => isDone; set => isDone = value; }
public int SegIndex { get => segIndex; set => segIndex = value; }
public int TimeOut { get => timeOut; set => timeOut = value; }
public FileStream LiveStream { get => liveStream; set => liveStream = value; }
public string LiveFile { get => liveFile; set => liveFile = value; }
public long ExpectByte { get => expectByte; set => expectByte = value; }
public long StartByte { get => startByte; set => startByte = value; }
public double SegDur { get => segDur; set => segDur = value; }
//重写WebClinet
//private class WebClient : System.Net.WebClient
//{
// protected override WebRequest GetWebRequest(Uri uri)
// {
// WebRequest lWebRequest = base.GetWebRequest(uri);
// lWebRequest.Timeout = TimeOut;
// ((HttpWebRequest)lWebRequest).ReadWriteTimeout = TimeOut;
// return lWebRequest;
// }
//}
//WebClient client = new WebClient();
public void Down()
{
try
{
//直播下载
if (IsLive)
{
IsDone = false; //设置为未完成下载
if (Method == "NONE")
{
LOGGER.PrintLine("<" + SegIndex + " Downloading>");
byte[] segBuff = Global.HttpDownloadFileToBytes(fileUrl, Headers, TimeOut);
//byte[] segBuff = Global.WebClientDownloadToBytes(fileUrl, Headers);
Global.AppendBytesToFileStreamAndDoNotClose(LiveStream, segBuff);
LOGGER.PrintLine("<" + SegIndex + " Complete>\r\n");
IsDone = true;
}
else if (Method == "AES-128")
{
LOGGER.PrintLine("<" + SegIndex + " Downloading>");
byte[] encryptedBuff = Global.HttpDownloadFileToBytes(fileUrl, Headers, TimeOut);
//byte[] encryptedBuff = Global.WebClientDownloadToBytes(fileUrl, Headers);
byte[] decryptBuff = Decrypter.AES128Decrypt(
encryptedBuff,
Convert.FromBase64String(Key),
Decrypter.HexStringToBytes(Iv)
);
Global.AppendBytesToFileStreamAndDoNotClose(LiveStream, decryptBuff);
LOGGER.PrintLine("<" + SegIndex + " Complete>\r\n");
IsDone = true;
}
else
{
LOGGER.PrintLine("不支持这种加密方式!", LOGGER.Error);
IsDone = true;
}
if (firstSeg && Global.FileSize(LiveFile) != 0)
{
LOGGER.STOPLOG = false; //记录日志
foreach (string ss in (string[])Global.GetVideoInfo(LiveFile).ToArray(typeof(string)))
{
LOGGER.WriteLine(ss.Trim());
}
firstSeg = false;
LOGGER.STOPLOG = true; //停止记录日志
}
return;
}
//点播下载
else
{
if (!Directory.Exists(Path.GetDirectoryName(SavePath)))
Directory.CreateDirectory(Path.GetDirectoryName(SavePath)); //新建文件夹
//是否存在文件,存在则不下载
if (File.Exists(Path.GetDirectoryName(savePath) + "\\" + Path.GetFileNameWithoutExtension(savePath) + ".ts"))
{
Global.BYTEDOWN++; //防止被速度监控程序杀死
//Console.WriteLine("Exists " + Path.GetFileNameWithoutExtension(savePath) + ".ts");
return;
}
//Console.WriteLine("开始下载 " + fileUrl);
//本地文件
if (fileUrl.StartsWith("file:"))
{
Uri t = new Uri(fileUrl);
fileUrl = t.LocalPath;
if (File.Exists(fileUrl))
{
if (ExpectByte == -1) //没有RANGE
{
FileInfo fi = new FileInfo(fileUrl);
fi.CopyTo(savePath);
Global.BYTEDOWN += fi.Length;
}
else
{
FileStream stream = new FileInfo(fileUrl).OpenRead();
//seek文件
stream.Seek(StartByte, SeekOrigin.Begin);
Byte[] buffer = new Byte[ExpectByte];
//从流中读取字节块并将该数据写入给定缓冲区buffer中
stream.Read(buffer, 0, Convert.ToInt32(buffer.Length));
stream.Close();
//写出文件
MemoryStream m = new MemoryStream(buffer);
FileStream fs = new FileStream(savePath, FileMode.OpenOrCreate);
m.WriteTo(fs);
m.Close();
fs.Close();
m = null;
fs = null;
}
}
}
else
{
//下载
Global.HttpDownloadFile(fileUrl, savePath, TimeOut, Headers, StartByte, ExpectByte);
}
}
if (File.Exists(savePath) && Global.ShouldStop == false)
{
FileInfo fi = new FileInfo(savePath);
if (Method == "NONE")
{
fi.MoveTo(Path.GetDirectoryName(savePath) + "\\" + Path.GetFileNameWithoutExtension(savePath) + ".ts");
DownloadManager.DownloadedSize += fi.Length;
//Console.WriteLine(Path.GetFileNameWithoutExtension(savePath) + " Completed.");
}
else if (File.Exists(fi.FullName)
&& Method == "AES-128")
{
//解密
try
{
byte[] decryptBuff = Decrypter.AES128Decrypt(
fi.FullName,
Convert.FromBase64String(Key),
Decrypter.HexStringToBytes(Iv)
);
FileStream fs = new FileStream(Path.GetDirectoryName(savePath) + "\\" + Path.GetFileNameWithoutExtension(savePath) + ".ts", FileMode.Create);
fs.Write(decryptBuff, 0, decryptBuff.Length);
fs.Close();
DownloadManager.DownloadedSize += fi.Length;
fi.Delete();
//Console.WriteLine(Path.GetFileNameWithoutExtension(savePath) + " Completed & Decrypted.");
}
catch (Exception ex)
{
LOGGER.WriteLineError(ex.Message);
}
}
else if(File.Exists(fi.FullName)
&& Method != "AES-128")
{
LOGGER.WriteLineError($"Do not support this METHOD: {Method}");
LOGGER.PrintLine("不支持这种加密方式!", LOGGER.Error);
return;
}
else
{
LOGGER.WriteLineError("Something was wrong!");
LOGGER.PrintLine("遇到了某些错误!", LOGGER.Error);
return;
}
return;
}
}
catch (Exception ex)
{
LOGGER.WriteLineError(ex.Message);
if (ex.Message.Contains("404"))
{
IsDone = true;
return;
}
else if (IsLive && count++ < Retry)
{
Thread.Sleep(5000);
Down();
}
}
}
}
}

169
N_m3u8DL-CLI/FFmpeg.cs Normal file
View File

@@ -0,0 +1,169 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
class FFmpeg
{
private static string outPutPath = string.Empty;
private static string reportFile = string.Empty;
private static bool useAACFilter = false; //是否启用滤镜
private static bool writeDate = true; //是否写入录制日期
public static string OutPutPath { get => outPutPath; set => outPutPath = value; }
public static string ReportFile { get => reportFile; set => reportFile = value; }
public static bool UseAACFilter { get => useAACFilter; set => useAACFilter = value; }
public static bool WriteDate { get => writeDate; set => writeDate = value; }
public static void Merge(string[] files, string muxFormat, bool fastStart,
string poster = "", string audioName = "", string title = "",
string copyright = "", string comment = "", string encodingTool = "")
{
string command = "-loglevel warning -i concat:\"";
string data = string.Empty;
string ddpAudio = string.Empty;
string addPoster = "-map 1 -c:v:1 copy -disposition:v:1 attached_pic";
ddpAudio = (File.Exists($"{Path.GetFileNameWithoutExtension(OutPutPath + ".mp4")}.txt") ? File.ReadAllText($"{Path.GetFileNameWithoutExtension(OutPutPath + ".mp4")}.txt") : "") ;
if (!string.IsNullOrEmpty(ddpAudio)) UseAACFilter = false;
foreach (string t in files)
{
command += Path.GetFileName(t) + "|";
}
switch (muxFormat.ToUpper())
{
case ("MP4"):
command += "\" " + (string.IsNullOrEmpty(poster) ? "" : "-i \"" + poster + "\"");
command += " " + (string.IsNullOrEmpty(ddpAudio) ? "" : "-i \"" + ddpAudio + "\"");
command +=
$" -map 0:v? {(string.IsNullOrEmpty(ddpAudio) ? "-map 0:a?" : $"-map {(string.IsNullOrEmpty(poster) ? "1" : "2")}:a -map 0:a?")} -map 0:s? " + (string.IsNullOrEmpty(poster) ? "" : addPoster)
+ (writeDate ? " -metadata date=\"" + DateTime.Now.ToString("o") + "\"" : "") +
" -metadata encoding_tool=\"" + encodingTool + "\" -metadata title=\"" + title +
"\" -metadata copyright=\"" + copyright + "\" -metadata comment=\"" + comment +
$"\" -metadata:s:a:{(string.IsNullOrEmpty(ddpAudio) ? "0" : "1")} handler_name=\"" + audioName + $"\" -metadata:s:a:{(string.IsNullOrEmpty(ddpAudio) ? "0" : "1")} handler=\"" + audioName + "\" ";
command += (string.IsNullOrEmpty(ddpAudio) ? "" : " -metadata:s:a:0 handler_name=\"DD+\" -metadata:s:a:0 handler=\"DD+\" ");
if (fastStart)
command += "-movflags +faststart";
command += " -c copy -y " + (UseAACFilter ? "-bsf:a aac_adtstoasc" : "") + " \"" + OutPutPath + ".mp4\"";
break;
case ("MKV"):
command += "\" -map 0 -c copy -y " + (UseAACFilter ? "-bsf:a aac_adtstoasc" : "") + " \"" + OutPutPath + ".mkv\"";
break;
case ("FLV"):
command += "\" -map 0 -c copy -y " + (UseAACFilter ? "-bsf:a aac_adtstoasc" : "") + " \"" + OutPutPath + ".flv\"";
break;
case ("TS"):
command += "\" -map 0 -c copy -y -f mpegts -bsf:v h264_mp4toannexb \"" + OutPutPath + ".ts\"";
break;
case ("VTT"):
command += "\" -map 0 -y \"" + OutPutPath + ".srt\""; //Convert To Srt
break;
case ("EAC3"):
command += "\" -map 0:a -c copy -y \"" + OutPutPath + ".eac3\"";
break;
case ("AAC"):
command += "\" -map 0:a -c copy -y \"" + OutPutPath + ".m4a\"";
break;
case ("AC3"):
command += "\" -map 0:a -c copy -y \"" + OutPutPath + ".ac3\"";
break;
}
Run("ffmpeg", command, Path.GetDirectoryName(files[0]));
LOGGER.WriteLine("Result in [ffreport.log]");
//Console.WriteLine(command);
}
public static void ConvertToMPEGTS(string file)
{
if (Global.VIDEO_TYPE == "H264")
{
Run("ffmpeg",
"-loglevel quiet -i \"" + file + "\" -map 0 -c copy -f mpegts -bsf:v h264_mp4toannexb \""
+ Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts\"",
Path.GetDirectoryName(file));
if (File.Exists(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts"))
{
File.Delete(file);
File.Move(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts", file);
}
}
else if (Global.VIDEO_TYPE == "H265")
{
Run("ffmpeg",
"-loglevel quiet -i \"" + file + "\" -map 0 -c copy -f mpegts -bsf:v hevc_mp4toannexb \""
+ Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts\"",
Path.GetDirectoryName(file));
if (File.Exists(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts"))
{
File.Delete(file);
File.Move(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts", file);
}
}
else
{
LOGGER.WriteLineError("Unkown Video Type");
}
}
public static void Run(string path, string args, string workDir)
{
string nowDir = Directory.GetCurrentDirectory(); //当前工作路径
Directory.SetCurrentDirectory(workDir);
Process p = new Process();//建立外部调用线程
p.StartInfo.FileName = path;//要调用外部程序的绝对路径
Environment.SetEnvironmentVariable("FFREPORT", "file=" + ReportFile + ":level=32"); //兼容XP系统
//p.StartInfo.Environment.Add("FFREPORT", "file=" + ReportFile + ":level=32");
p.StartInfo.Arguments = args;//参数(这里就是FFMPEG的参数了)
p.StartInfo.UseShellExecute = false;//不使用操作系统外壳程序启动线程(一定为FALSE,详细的请看MSDN)
p.StartInfo.RedirectStandardError = true;//把外部程序错误输出写到StandardError流中(这个一定要注意,FFMPEG的所有输出信息,都为错误输出流,用StandardOutput是捕获不到任何消息的...这是我耗费了2个多月得出来的经验...mencoder就是用standardOutput来捕获的)
p.StartInfo.CreateNoWindow = false;//不创建进程窗口
p.ErrorDataReceived += new DataReceivedEventHandler(Output);//外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
p.StartInfo.StandardErrorEncoding = Encoding.UTF8;
p.Start();//启动线程
p.BeginErrorReadLine();//开始异步读取
p.WaitForExit();//阻塞等待进程结束
p.Close();//关闭进程
p.Dispose();//释放资源
Environment.SetEnvironmentVariable("FFREPORT", null); //兼容XP系统
Directory.SetCurrentDirectory(nowDir);
}
private static void Output(object sendProcess, DataReceivedEventArgs output)
{
if (!String.IsNullOrEmpty(output.Data))
{
LOGGER.PrintLine(output.Data, LOGGER.Warning);
}
}
public static bool CheckMPEGTS(string file)
{
//放行杜比视界或纯音频文件
if (Global.VIDEO_TYPE == "DV" || Global.AUDIO_TYPE != "")
return true;
//如果是多分片也认为不是MPEGTS
if (DownloadManager.PartsCount > 1)
return false;
using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read))
{
byte[] firstByte = new byte[1];
fs.Read(firstByte, 0, 1);
//第一字节的16进制字符串
string _1_byte_str = Convert.ToString(firstByte[0], 16);
//syncword不为47就不处理
if (_1_byte_str != "47")
return false;
}
return true;
}
}
}

1005
N_m3u8DL-CLI/Global.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,149 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Timers;
namespace N_m3u8DL_CLI
{
class HLSLiveDownloader
{
private string liveFile = string.Empty;
private string jsonFile = string.Empty;
private string headers = string.Empty;
private string downDir = string.Empty;
private FileStream liveStream = null;
private int targetduration = 10;
private bool isFirstJson = true;
public string Headers { get => headers; set => headers = value; }
public string DownDir { get => downDir; set => downDir = value; }
public FileStream LiveStream { get => liveStream; set => liveStream = value; }
public string LiveFile { get => liveFile; set => liveFile = value; }
ArrayList toDownList = new ArrayList(); //所有待下载的列表
System.Timers.Timer timer = new System.Timers.Timer();
Downloader sd = new Downloader(); //只有一个实例
public void TimerStart()
{
timer.Enabled = true;
timer.Interval = (targetduration - 2) * 1000; //执行间隔时间,单位为毫秒
timer.Start();
timer.Elapsed += new ElapsedEventHandler(UpdateList);
UpdateList(timer, new EventArgs()); //立即执行一次
Record();
}
public void TimerStop()
{
timer.Stop();
}
//更新列表
private void UpdateList(object source, EventArgs e)
{
jsonFile = Path.Combine(DownDir, "meta.json");
if (!File.Exists(jsonFile))
{
TimerStop();
return;
}
string jsonContent = File.ReadAllText(jsonFile);
JObject initJson = JObject.Parse(jsonContent);
string m3u8Url = initJson["m3u8"].Value<string>();
targetduration = initJson["m3u8Info"]["targetDuration"].Value<int>();
JArray lastSegments = JArray.Parse(initJson["m3u8Info"]["segments"][0].ToString().Trim()); //上次的分段,用于比对新分段
ArrayList tempList = new ArrayList(); //所有待下载的列表
tempList.Clear();
foreach (JObject seg in lastSegments)
{
tempList.Add(seg.ToString());
}
if(isFirstJson)
{
toDownList = tempList;
isFirstJson = false;
return;
}
Parser parser = new Parser();
parser.DownDir = Path.GetDirectoryName(jsonFile);
parser.M3u8Url = m3u8Url;
parser.Parse(); //产生新的json文件
jsonContent = File.ReadAllText(jsonFile);
initJson = JObject.Parse(jsonContent);
JArray segments = JArray.Parse(initJson["m3u8Info"]["segments"][0].ToString()); //大分组
foreach (JObject seg in segments)
{
if (!tempList.Contains(seg.ToString()))
{
toDownList.Add(seg.ToString()); //加入真正的待下载队列
//Console.WriteLine(seg.ToString());
}
}
}
//public void TryDownload()
//{
// Thread t = new Thread(Download);
// while (toDownList.Count != 0)
// {
// t = new Thread(Download);
// t.Start();
// t.Join();
// while (sd.IsDone != true) ; //忙等待
// if (toDownList.Count > 0)
// toDownList.RemoveAt(0); //下完删除一项
// }
// Console.WriteLine("Waiting...");
//}
private void Record()
{
ArrayList temp = toDownList;
while(temp.Count != 0)
{
JObject info = JObject.Parse(temp[0].ToString());
int index = info["index"].Value<int>();
sd.FileUrl = info["segUri"].Value<string>();
sd.Method = info["method"].Value<string>();
if (sd.Method != "NONE")
{
sd.Key = info["key"].Value<string>();
sd.Iv = info["iv"].Value<string>();
}
sd.TimeOut = 60000;
sd.SegIndex = index;
sd.Headers = Headers;
sd.IsLive = true; //标记为直播
sd.LiveFile = LiveFile;
sd.LiveStream = LiveStream;
sd.Down(); //开始下载
while (sd.IsDone != true) ; //忙等待
if (toDownList.Count > 0)
toDownList.RemoveAt(0); //下完删除一项
}
LOGGER.PrintLine("Waiting...");
//不断查找是否有新分段,有的话立即开始下载
while (isNewSeg() != true)
isNewSeg();
Record();
}
//检测是否有新分片
private bool isNewSeg()
{
if (toDownList.Count > 0)
return true;
return false;
}
}
}

38
N_m3u8DL-CLI/HLSTags.cs Normal file
View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
class HLSTags
{
public static string ext_m3u = "#EXTM3U";
public static string ext_x_targetduration = "#EXT-X-TARGETDURATION";
public static string ext_x_media_sequence = "#EXT-X-MEDIA-SEQUENCE";
public static string ext_x_discontinuity_sequence = "#EXT-X-DISCONTINUITY-SEQUENCE";
public static string ext_x_program_date_time = "#EXT-X-PROGRAM-DATE-TIME";
public static string ext_x_media = "#EXT-X-MEDIA";
public static string ext_x_playlist_type = "#EXT-X-PLAYLIST-TYPE";
public static string ext_x_key = "#EXT-X-KEY";
public static string ext_x_stream_inf = "#EXT-X-STREAM-INF";
public static string ext_x_version = "#EXT-X-VERSION";
public static string ext_x_allow_cache = "#EXT-X-ALLOW-CACHE";
public static string ext_x_endlist = "#EXT-X-ENDLIST";
public static string extinf = "#EXTINF";
public static string ext_i_frames_only = "#EXT-X-I-FRAMES-ONLY";
public static string ext_x_byterange = "#EXT-X-BYTERANGE";
public static string ext_x_i_frame_stream_inf = "#EXT-X-I-FRAME-STREAM-INF";
public static string ext_x_discontinuity = "#EXT-X-DISCONTINUITY";
public static string ext_x_cue_out_start = "#EXT-X-CUE-OUT";
public static string ext_x_cue_out = "#EXT-X-CUE-OUT-CONT";
public static string ext_is_independent_segments = "#EXT-X-INDEPENDENT-SEGMENTS";
public static string ext_x_scte35 = "#EXT-OATCLS-SCTE35";
public static string ext_x_cue_start = "#EXT-X-CUE-OUT";
public static string ext_x_cue_end = "#EXT-X-CUE-IN";
public static string ext_x_cue_span = "#EXT-X-CUE-SPAN";
public static string ext_x_map = "#EXT-X-MAP";
public static string ext_x_start = "#EXT-X-START";
}
}

158
N_m3u8DL-CLI/LOGGER.cs Normal file
View File

@@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
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;
public static string LOGFILE;
public static bool STOPLOG = false;
public static string FindLog(string dir)
{
DirectoryInfo d = new DirectoryInfo(dir);
foreach (FileInfo fi in d.GetFiles())
{
if (fi.Extension.ToUpper() == ".LOG")
{
return fi.FullName;
}
}
return "";
}
public static void InitLog()
{
if (!Directory.Exists(Path.GetDirectoryName(LOGFILE)))//若文件夹不存在则新建文件夹
Directory.CreateDirectory(Path.GetDirectoryName(LOGFILE)); //新建文件夹
if (File.Exists(LOGFILE))//若文件存在则删除
File.Delete(LOGFILE);
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"
+ "Save Path: " + Path.GetDirectoryName(LOGFILE) + "\r\n"
+ "Task Start: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + "\r\n"
+ "Task CommandLine: " + Environment.CommandLine;
if (File.Exists(Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), "N_m3u8DL-CLI.args.txt")))
{
init += "\r\nAdditional Args: " + File.ReadAllText(Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), "N_m3u8DL-CLI.args.txt")); //解析命令行
}
init += "\r\n\r\n";
File.WriteAllText(file, init, Encoding.UTF8);
}
//读写锁机制,当资源被占用,其他线程等待
static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
public static void PrintLine(string text, int printLevel = 1, int cursorIndex = 0)
{
try
{
if (cursorIndex == 0)
Console.SetCursorPosition(0, CursorIndex++);
else
Console.SetCursorPosition(0, cursorIndex);
}
catch (Exception)
{
;
}
switch (printLevel)
{
case 0:
Console.WriteLine(" ".PadRight(12) + " " + text);
break;
case 1:
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
Console.WriteLine(text);
break;
case 2:
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(text);
Console.ResetColor();
break;
case 3:
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine(text);
Console.ResetColor();
break;
}
}
public static void WriteLine(string text)
{
if (STOPLOG)
return;
if (!File.Exists(LOGFILE))
return;
try
{
string file = LOGFILE;
//进入写入
LogWriteLock.EnterWriteLock();
using (StreamWriter sw = File.AppendText(file))
{
sw.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " / (NORMAL) " + text, Encoding.UTF8);
}
}
catch (Exception)
{
}
finally
{
//释放占用
LogWriteLock.ExitWriteLock();
}
}
public static void WriteLineError(string text)
{
if (!File.Exists(LOGFILE))
return;
try
{
string file = LOGFILE;
//进入写入
LogWriteLock.EnterWriteLock();
using (StreamWriter sw = File.AppendText(file))
{
sw.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " / (ERROR) " + text, Encoding.UTF8);
}
}
catch (Exception)
{
}
finally
{
//释放占用
LogWriteLock.ExitWriteLock();
}
}
public static void Show(string text)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(DateTime.Now.ToString("o") + " " + text);
while (Console.ForegroundColor == ConsoleColor.Red)
Console.ResetColor();
}
}
}

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{4FB61439-B738-46AC-B3AF-2BF72150D057}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>N_m3u8DL_CLI</RootNamespace>
<AssemblyName>N_m3u8DL-CLI</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>logo_3Iv_icon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.JScript" />
<Reference Include="Newtonsoft.Json" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.Collections" />
<Reference Include="System.Core" />
<Reference Include="System.IO" />
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CommandLineArgument.cs" />
<Compile Include="CommandLineArgumentParser.cs" />
<Compile Include="Decrypter.cs" />
<Compile Include="FFmpeg.cs" />
<Compile Include="Global.cs" />
<Compile Include="HLSLiveDownloader.cs" />
<Compile Include="HLSTags.cs" />
<Compile Include="LOGGER.cs" />
<Compile Include="DownloadManager.cs" />
<Compile Include="Parser.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Downloader.cs" />
<Compile Include="Watcher.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<None Include="bin\Debug\Newtonsoft.Json.dll" />
</ItemGroup>
<ItemGroup>
<COMReference Include="Scripting">
<Guid>{420B2830-E718-11CF-893D-00A0C9054228}</Guid>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<ItemGroup>
<Content Include="logo_3Iv_icon.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

776
N_m3u8DL-CLI/Parser.cs Normal file

File diff suppressed because it is too large Load Diff

680
N_m3u8DL-CLI/Program.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("N_m3u8DL-CLI")]
[assembly: AssemblyDescription("一款命令行m3u8下载器")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("nilaoda")]
[assembly: AssemblyProduct("N_m3u8DL-CLI")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("4fb61439-b738-46ac-b3af-2bf72150d057")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
// 方法是按如下所示使用“*”: :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

104
N_m3u8DL-CLI/Watcher.cs Normal file
View File

@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
namespace N_m3u8DL_CLI
{
class Watcher
{
private string dir = string.Empty;
private int total = 0;
private static double totalDuration = 0; //总时长
private int now = 0;
private int partsCount = 0;
FileSystemWatcher watcher = new FileSystemWatcher();
public int Total { get => total; set => total = value; }
public int Now { get => now; set => now = value; }
public int PartsCount { get => partsCount; set => partsCount = value; }
public static double TotalDuration { get => totalDuration; set => totalDuration = value; }
public Watcher(string Dir)
{
this.dir = Dir;
}
public void WatcherStrat()
{
for (int i = 0; i < PartsCount; i++)
{
Now += Global.GetFileCount(dir + "\\Part_" + i.ToString(DownloadManager.partsPadZero), ".ts");
}
watcher.Path = dir;
watcher.Filter = "*.ts";
watcher.IncludeSubdirectories = true; //包括子目录
watcher.EnableRaisingEvents = true; //开启提交事件
watcher.Created += new FileSystemEventHandler(OnCreated);
watcher.Renamed += new RenamedEventHandler(OnCreated);
watcher.Deleted += new FileSystemEventHandler(OnDeleted);
}
public void WatcherStop()
{
watcher.Dispose();
}
private void OnCreated(object source, FileSystemEventArgs e)
{
if (Path.GetFileNameWithoutExtension(e.FullPath).StartsWith("Part"))
return;
Now++;
if (Now > Total)
{
return;
}
//Console.Title = Now + " / " + Total;
string downloadedSize = Global.FormatFileSize(DownloadManager.DownloadedSize);
string estimatedSize = Global.FormatFileSize(DownloadManager.DownloadedSize * total / now);
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));
}
private void OnRenamed(object source, RenamedEventArgs e)
{
if (Path.GetFileNameWithoutExtension(e.FullPath).StartsWith("Part"))
return;
Now++;
if (Now > Total)
{
return;
}
//Console.Title = Now + " / " + Total;
string downloadedSize = Global.FormatFileSize(DownloadManager.DownloadedSize);
string estimatedSize = Global.FormatFileSize(DownloadManager.DownloadedSize * total / now);
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));
}
private void OnDeleted(object source, FileSystemEventArgs e)
{
if (Path.GetFileNameWithoutExtension(e.FullPath).StartsWith("Part"))
return;
Now--;
if (Now > Total)
{
return;
}
//Console.Title = Now + " / " + Total;
string downloadedSize = Global.FormatFileSize(DownloadManager.DownloadedSize);
string estimatedSize = Global.FormatFileSize(DownloadManager.DownloadedSize * total / now);
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));
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

102
README.md

File diff suppressed because one or more lines are too long

62
README_ENG.md Normal file
View File

@@ -0,0 +1,62 @@
```
███╗ ██╗ ███╗ ███╗██████╗ ██╗ ██╗ █████╗ ██████╗ ██╗ ██████╗██╗ ██╗
████╗ ██║ ████╗ ████║╚════██╗██║ ██║██╔══██╗██╔══██╗██║ ██╔════╝██║ ██║
██╔██╗ ██║ ██╔████╔██║ █████╔╝██║ ██║╚█████╔╝██║ ██║██║█████╗██║ ██║ ██║
██║╚██╗██║ ██║╚██╔╝██║ ╚═══██╗██║ ██║██╔══██╗██║ ██║██║╚════╝██║ ██║ ██║
██║ ╚████║███████╗██║ ╚═╝ ██║██████╔╝╚██████╔╝╚█████╔╝██████╔╝███████╗ ╚██████╗███████╗██║
╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚════╝ ╚═════╝ ╚══════╝ ╚═════╝╚══════╝╚═╝
```
This is a m3u8 downloader.
## Summary
Supports:
* Auto deceypt for `AES-128`
* `Master List`
* Live stream recording(`BETA`)
* `Dolby Vision` from IQiYi, YouKu, Tencent
* Customize HTTP headers
* Auto merge clips(Binary or ffmpeg)
* Select save clip by `time code` or `index`
* Network driver on Windows OS
* Alternative audio/video track
* Mux without video track
* Auto use system proxy
* Optimization for Chinese streaming platform
![ScreenShot](https://nilaoda.github.io/N_m3u8DL-CLI/source/images/%E7%9B%B4%E6%8E%A5%E4%BD%BF%E7%94%A8.gif)
## GUI
* Easy-to-use `GUI`
## Options
```
N_m3u8DL-CLI.exe <URL|JSON|FILE> [OPTIONS]
--workDir Directory Set work dir (Video will be here)
--saveName Filename Set save name(Exclude extention)
--baseUrl BaseUrl Set Baseurl
--headers headers Set HTTP headersformat: key:value user | split all key&value
--maxThreads Thread Set max thread(default: 32)
--minThreads Thread Set min thread(default: 16)
--retryCount Count Set retry times(default: 15)
--timeOut Sec Set timeout for http request(seconddefault: 10)
--muxSetJson File Set a json file for mux
--useKeyFile File Use 16 bytes file as KEY for AES-128 decryption
--useKeyBase64 Base64String Use Base64 String as KEY for AES-128 decryption
--downloadRange Range Set range for a video
--stopSpeed Number Speed below this, retry(KB/s)
--maxSpeed Number Set max download speed(KB/s)
--enableDelAfterDone Enable delete clips after download completed
--enableMuxFastStart Enable fast start for mp4
--enableBinaryMerge Enable use binary merge instead ffmpeg
--enableParseOnly Enable parse mode
--enableAudioOnly Enable only audio track when mux use ffmpeg
--disableDateInfo Disable write date info when mux use ffmpeg
--noMerge Disable auto merge
--noProxy Disable use system proxy
--disableIntegrityCheck Disable integrity check
```
## Document
https://nilaoda.github.io/N_m3u8DL-CLI/

676
docs/Advanced.html Normal file

File diff suppressed because it is too large Load Diff

585
docs/GetM3u8.html Normal file

File diff suppressed because one or more lines are too long

505
docs/Introductory.html Normal file

File diff suppressed because it is too large Load Diff

514
docs/LinetvParser.html Normal file

File diff suppressed because it is too large Load Diff

508
docs/M3U8URL2File.html Normal file

File diff suppressed because it is too large Load Diff

520
docs/SimpleGUI.html Normal file

File diff suppressed because it is too large Load Diff

515
docs/VikiParser.html Normal file

File diff suppressed because it is too large Load Diff

515
docs/ViuParser.html Normal file

File diff suppressed because it is too large Load Diff

520
docs/aliyunEdu.html Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More