You've already forked N_m3u8DL-CLI
mirror of
https://github.com/nilaoda/N_m3u8DL-CLI
synced 2025-09-08 06:00:50 +02:00
Compare commits
72 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ba40641a21 | ||
![]() |
5d76418780 | ||
![]() |
fd48b766b5 | ||
![]() |
0f25cc0ae8 | ||
![]() |
1c6bd688e3 | ||
![]() |
480857cc3b | ||
![]() |
52af9a44a8 | ||
![]() |
84d137b504 | ||
![]() |
a4f1064c81 | ||
![]() |
eac08b12f8 | ||
![]() |
c81c911888 | ||
![]() |
361e901e5b | ||
![]() |
3567f4c4cc | ||
![]() |
2f7b0f7304 | ||
![]() |
000d0db4ae | ||
![]() |
d551ad52c1 | ||
![]() |
c064c83c21 | ||
![]() |
f6ed0f9e4d | ||
![]() |
f65fee94ca | ||
![]() |
ce64a92b0d | ||
![]() |
42790ce540 | ||
![]() |
83d8ca1c8c | ||
![]() |
4b5a64eb98 | ||
![]() |
2b5af09c3b | ||
![]() |
6368adc2ab | ||
![]() |
fcd7840091 | ||
![]() |
d77cb62dff | ||
![]() |
34394c6a2b | ||
![]() |
091cba8555 | ||
![]() |
b02b6b7168 | ||
![]() |
b7408b0599 | ||
![]() |
b83cb35170 | ||
![]() |
ed0a7b71a7 | ||
![]() |
50eae19bf3 | ||
![]() |
cba8f3ea52 | ||
![]() |
7fd93e1232 | ||
![]() |
74a7e3c3ec | ||
![]() |
849d712e11 | ||
![]() |
7544f3a02c | ||
![]() |
0120736c53 | ||
![]() |
e4bde4926c | ||
![]() |
d42cd6a60d | ||
![]() |
175f13adc9 | ||
![]() |
72f1c043b1 | ||
![]() |
a2e2070f5d | ||
![]() |
6c96deb366 | ||
![]() |
2bd900ee5d | ||
![]() |
1261810510 | ||
![]() |
26823dbd7e | ||
![]() |
698699d9fc | ||
![]() |
ebed7fa1e3 | ||
![]() |
73a8348155 | ||
![]() |
5ace0b3a4f | ||
![]() |
5abe889da0 | ||
![]() |
f1070fd1b4 | ||
![]() |
4af82cc7f9 | ||
![]() |
809380b7ab | ||
![]() |
ab57420507 | ||
![]() |
80230f12fe | ||
![]() |
a6c7c0fd8c | ||
![]() |
bd6df6b58c | ||
![]() |
e0a9071d62 | ||
![]() |
5c9bcf72d2 | ||
![]() |
7cf2c12d0c | ||
![]() |
1b35fe2d2c | ||
![]() |
ed3aae1cb9 | ||
![]() |
01c2ecbeb5 | ||
![]() |
9993ec8177 | ||
![]() |
464300c860 | ||
![]() |
45fa58a46f | ||
![]() |
f93ddc7107 | ||
![]() |
05f450fa6d |
@@ -1,12 +1,10 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27703.2000
|
||||
# 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
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUI-MainWindow", "GUI-MainWindow\GUI-MainWindow.csproj", "{FE91DB43-1F1F-4119-B808-A7F4795BB4D0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -17,10 +15,6 @@ Global
|
||||
{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
|
||||
{FE91DB43-1F1F-4119-B808-A7F4795BB4D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FE91DB43-1F1F-4119-B808-A7F4795BB4D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FE91DB43-1F1F-4119-B808-A7F4795BB4D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FE91DB43-1F1F-4119-B808-A7F4795BB4D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
318
N_m3u8DL-CLI/Decode51CtoKey.cs
Normal file
318
N_m3u8DL-CLI/Decode51CtoKey.cs
Normal file
File diff suppressed because one or more lines are too long
203
N_m3u8DL-CLI/DecodeImooc.cs
Normal file
203
N_m3u8DL-CLI/DecodeImooc.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using NiL.JS.BaseLibrary;
|
||||
using NiL.JS.Core;
|
||||
using NiL.JS.Extensions;
|
||||
using System;
|
||||
using Array = System.Array;
|
||||
|
||||
namespace N_m3u8DL_CLI
|
||||
{
|
||||
/*
|
||||
* js代码来自:https://www.imooc.com/static/moco/player/3.0.6.3/mocoplayer.js?v=202006122046
|
||||
*
|
||||
*/
|
||||
class DecodeImooc
|
||||
{
|
||||
private static string JS = @"
|
||||
function n(t, e) {
|
||||
function r(t, e) {
|
||||
var r = '';
|
||||
if ('object' == typeof t)
|
||||
for (var n = 0; n < t.length; n++)
|
||||
r += String.fromCharCode(t[n]);
|
||||
t = r || t;
|
||||
for (var i, o, a = new Uint8Array(t.length), s = e.length, n = 0; n < t.length; n++)
|
||||
o = n % s,
|
||||
i = t[n],
|
||||
i = i.toString().charCodeAt(0),
|
||||
a[n] = i ^ e.charCodeAt(o);
|
||||
return a
|
||||
}
|
||||
function n(t) {
|
||||
var e = '';
|
||||
if ('object' == typeof t)
|
||||
for (var r = 0; r < t.length; r++)
|
||||
e += String.fromCharCode(t[r]);
|
||||
t = e || t;
|
||||
var n = new Uint8Array(t.length);
|
||||
for (r = 0; r < t.length; r++)
|
||||
n[r] = t[r].toString().charCodeAt(0);
|
||||
var i, o, r = 0;
|
||||
for (r = 0; r < n.length; r++)
|
||||
0 != (i = n[r] % 3) && r + i < n.length && (o = n[r + 1],
|
||||
n[r + 1] = n[r + i],
|
||||
n[r + i] = o,
|
||||
r = r + i + 1);
|
||||
return n
|
||||
}
|
||||
function i(t) {
|
||||
var e = '';
|
||||
if ('object' == typeof t)
|
||||
for (var r = 0; r < t.length; r++)
|
||||
e += String.fromCharCode(t[r]);
|
||||
t = e || t;
|
||||
var n = new Uint8Array(t.length);
|
||||
for (r = 0; r < t.length; r++)
|
||||
n[r] = t[r].toString().charCodeAt(0);
|
||||
var r = 0
|
||||
, i = 0
|
||||
, o = 0
|
||||
, a = 0;
|
||||
for (r = 0; r < n.length; r++)
|
||||
o = n[r] % 2,
|
||||
o && r++,
|
||||
a++;
|
||||
var s = new Uint8Array(a);
|
||||
for (r = 0; r < n.length; r++)
|
||||
o = n[r] % 2,
|
||||
s[i++] = o ? n[r++] : n[r];
|
||||
return s
|
||||
}
|
||||
function o(t, e) {
|
||||
var r = 0
|
||||
, n = 0
|
||||
, i = 0
|
||||
, o = 0
|
||||
, a = '';
|
||||
if ('object' == typeof t)
|
||||
for (var r = 0; r < t.length; r++)
|
||||
a += String.fromCharCode(t[r]);
|
||||
t = a || t;
|
||||
var s = new Uint8Array(t.length);
|
||||
for (r = 0; r < t.length; r++)
|
||||
s[r] = t[r].toString().charCodeAt(0);
|
||||
for (r = 0; r < t.length; r++)
|
||||
if (0 != (o = s[r] % 5) && 1 != o && r + o < s.length && (i = s[r + 1],
|
||||
n = r + 2,
|
||||
s[r + 1] = s[r + o],
|
||||
s[o + r] = i,
|
||||
(r = r + o + 1) - 2 > n))
|
||||
for (; n < r - 2; n++)
|
||||
s[n] = s[n] ^ e.charCodeAt(n % e.length);
|
||||
for (r = 0; r < t.length; r++)
|
||||
s[r] = s[r] ^ e.charCodeAt(r % e.length);
|
||||
return s
|
||||
}
|
||||
for (var a = {
|
||||
data: {
|
||||
info: t
|
||||
}
|
||||
}, s = {
|
||||
q: r,
|
||||
h: n,
|
||||
m: i,
|
||||
k: o
|
||||
}, l = a.data.info, u = l.substring(l.length - 4).split(''), c = 0; c < u.length; c++)
|
||||
u[c] = u[c].toString().charCodeAt(0) % 4;
|
||||
u.reverse();
|
||||
for (var d = [], c = 0; c < u.length; c++)
|
||||
d.push(l.substring(u[c] + 1, u[c] + 2)),
|
||||
l = l.substring(0, u[c] + 1) + l.substring(u[c] + 2);
|
||||
a.data.encrypt_table = d,
|
||||
a.data.key_table = [];
|
||||
for (var c in a.data.encrypt_table)
|
||||
'q' != a.data.encrypt_table[c] && 'k' != a.data.encrypt_table[c] || (a.data.key_table.push(l.substring(l.length - 12)),
|
||||
l = l.substring(0, l.length - 12));
|
||||
a.data.key_table.reverse(),
|
||||
a.data.info = l;
|
||||
var f = new Array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1);
|
||||
a.data.info = function(t) {
|
||||
var e, r, n, i, o, a, s;
|
||||
for (a = t.length,
|
||||
o = 0,
|
||||
s = ''; o < a; ) {
|
||||
do {
|
||||
e = f[255 & t.charCodeAt(o++)]
|
||||
} while (o < a && -1 == e);if (-1 == e)
|
||||
break;
|
||||
do {
|
||||
r = f[255 & t.charCodeAt(o++)]
|
||||
} while (o < a && -1 == r);if (-1 == r)
|
||||
break;
|
||||
s += String.fromCharCode(e << 2 | (48 & r) >> 4);
|
||||
do {
|
||||
if (61 == (n = 255 & t.charCodeAt(o++)))
|
||||
return s;
|
||||
n = f[n]
|
||||
} while (o < a && -1 == n);if (-1 == n)
|
||||
break;
|
||||
s += String.fromCharCode((15 & r) << 4 | (60 & n) >> 2);
|
||||
do {
|
||||
if (61 == (i = 255 & t.charCodeAt(o++)))
|
||||
return s;
|
||||
i = f[i]
|
||||
} while (o < a && -1 == i);if (-1 == i)
|
||||
break;
|
||||
s += String.fromCharCode((3 & n) << 6 | i)
|
||||
}
|
||||
return s
|
||||
}(a.data.info);
|
||||
for (var c in a.data.encrypt_table) {
|
||||
var h = a.data.encrypt_table[c];
|
||||
if ('q' == h || 'k' == h) {
|
||||
var p = a.data.key_table.pop();
|
||||
a.data.info = s[a.data.encrypt_table[c]](a.data.info, p)
|
||||
} else
|
||||
a.data.info = s[a.data.encrypt_table[c]](a.data.info)
|
||||
}
|
||||
if (e)
|
||||
return a.data.info;
|
||||
var g = '';
|
||||
for (c = 0; c < a.data.info.length; c++)
|
||||
g += String.fromCharCode(a.data.info[c]);
|
||||
return g
|
||||
}
|
||||
function Uint8ArrayToString(fileData){
|
||||
var dataString = '';
|
||||
for (var i = 0; i < fileData.length; i++) {
|
||||
dataString += Number(fileData[i]) + ',';
|
||||
}
|
||||
return dataString;
|
||||
}
|
||||
function decodeKey(resp){
|
||||
var string = eval('('+resp+')');
|
||||
//return btoa(String.fromCharCode.apply(null, new Uint8Array(n(string.data.info, 1))));
|
||||
return Uint8ArrayToString(new Uint8Array(n(string.data.info, 1)));
|
||||
}
|
||||
function decodeM3u8(resp){
|
||||
var string = eval('('+resp+')');
|
||||
return n(string.data.info);
|
||||
}
|
||||
";
|
||||
|
||||
|
||||
public static string DecodeM3u8(string resp)
|
||||
{
|
||||
var context = new Context();
|
||||
context.Eval(JS);
|
||||
var concatFunction = context.GetVariable("decodeM3u8").As<Function>();
|
||||
string m3u8 = concatFunction.Call(new Arguments { resp }).ToString();
|
||||
return m3u8;
|
||||
}
|
||||
|
||||
public static string DecodeKey(string resp)
|
||||
{
|
||||
var context = new Context();
|
||||
context.Eval(JS);
|
||||
var concatFunction = context.GetVariable("decodeKey").As<Function>();
|
||||
string key = concatFunction.Call(new Arguments { resp }).ToString();
|
||||
byte[] v = Array.ConvertAll(key.Trim(',').Split(','), s => (byte)int.Parse(s));
|
||||
string realKey = Convert.ToBase64String(v);
|
||||
return realKey;
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,7 +10,7 @@ namespace N_m3u8DL_CLI
|
||||
{
|
||||
class Decrypter
|
||||
{
|
||||
public static byte[] AES128Decrypt(string filePath, byte[] keyByte, byte[] ivByte)
|
||||
public static byte[] AES128Decrypt(string filePath, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC)
|
||||
{
|
||||
FileStream fs = new FileStream(filePath, FileMode.Open);
|
||||
//获取文件大小
|
||||
@@ -19,12 +19,12 @@ namespace N_m3u8DL_CLI
|
||||
fs.Read(inBuff, 0, inBuff.Length);
|
||||
fs.Close();
|
||||
|
||||
Aes dcpt = Aes.Create("AES");
|
||||
Aes dcpt = Aes.Create();
|
||||
dcpt.BlockSize = 128;
|
||||
dcpt.KeySize = 128;
|
||||
dcpt.Key = keyByte;
|
||||
dcpt.IV = ivByte;
|
||||
dcpt.Mode = CipherMode.CBC;
|
||||
dcpt.Mode = mode;
|
||||
dcpt.Padding = PaddingMode.PKCS7;
|
||||
|
||||
ICryptoTransform cTransform = dcpt.CreateDecryptor();
|
||||
@@ -32,16 +32,16 @@ namespace N_m3u8DL_CLI
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
public static byte[] AES128Decrypt(byte[] encryptedBuff, byte[] keyByte, byte[] ivByte)
|
||||
public static byte[] AES128Decrypt(byte[] encryptedBuff, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC)
|
||||
{
|
||||
byte[] inBuff = encryptedBuff;
|
||||
|
||||
Aes dcpt = Aes.Create("AES");
|
||||
Aes dcpt = Aes.Create();
|
||||
dcpt.BlockSize = 128;
|
||||
dcpt.KeySize = 128;
|
||||
dcpt.Key = keyByte;
|
||||
dcpt.IV = ivByte;
|
||||
dcpt.Mode = CipherMode.CBC;
|
||||
dcpt.Mode = mode;
|
||||
dcpt.Padding = PaddingMode.PKCS7;
|
||||
|
||||
ICryptoTransform cTransform = dcpt.CreateDecryptor();
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -72,43 +72,55 @@ namespace N_m3u8DL_CLI
|
||||
{
|
||||
IsDone = false; //设置为未完成下载
|
||||
|
||||
if (Method == "NONE")
|
||||
if (Method == "NONE" || method.Contains("NOTSUPPORTED"))
|
||||
{
|
||||
LOGGER.PrintLine("<" + SegIndex + " Downloading>");
|
||||
LOGGER.WriteLine("<" + 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");
|
||||
LOGGER.WriteLine("<" + SegIndex + " Complete>");
|
||||
IsDone = true;
|
||||
}
|
||||
else if (Method == "AES-128")
|
||||
{
|
||||
LOGGER.PrintLine("<" + SegIndex + " Downloading>");
|
||||
LOGGER.WriteLine("<" + SegIndex + " Downloading>");
|
||||
byte[] encryptedBuff = Global.HttpDownloadFileToBytes(fileUrl, Headers, TimeOut);
|
||||
//byte[] encryptedBuff = Global.WebClientDownloadToBytes(fileUrl, Headers);
|
||||
byte[] decryptBuff = Decrypter.AES128Decrypt(
|
||||
byte[] decryptBuff = null;
|
||||
decryptBuff = Decrypter.AES128Decrypt(
|
||||
encryptedBuff,
|
||||
Convert.FromBase64String(Key),
|
||||
Decrypter.HexStringToBytes(Iv)
|
||||
);
|
||||
Global.AppendBytesToFileStreamAndDoNotClose(LiveStream, decryptBuff);
|
||||
LOGGER.PrintLine("<" + SegIndex + " Complete>\r\n");
|
||||
LOGGER.WriteLine("<" + SegIndex + " Complete>");
|
||||
IsDone = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.PrintLine("不支持这种加密方式!", LOGGER.Error);
|
||||
//LOGGER.PrintLine("不支持这种加密方式!", LOGGER.Error);
|
||||
IsDone = true;
|
||||
}
|
||||
if (firstSeg && Global.FileSize(LiveFile) != 0)
|
||||
{
|
||||
LOGGER.STOPLOG = false; //记录日志
|
||||
//LOGGER.STOPLOG = false; //记录日志
|
||||
foreach (string ss in (string[])Global.GetVideoInfo(LiveFile).ToArray(typeof(string)))
|
||||
{
|
||||
LOGGER.WriteLine(ss.Trim());
|
||||
}
|
||||
firstSeg = false;
|
||||
LOGGER.STOPLOG = true; //停止记录日志
|
||||
//LOGGER.STOPLOG = true; //停止记录日志
|
||||
}
|
||||
HLSLiveDownloader.REC_DUR += SegDur;
|
||||
if (HLSLiveDownloader.REC_DUR_LIMIT != -1 && HLSLiveDownloader.REC_DUR >= HLSLiveDownloader.REC_DUR_LIMIT)
|
||||
{
|
||||
LOGGER.PrintLine(strings.recordLimitReached, LOGGER.Warning);
|
||||
LOGGER.WriteLine(strings.recordLimitReached);
|
||||
Environment.Exit(0); //正常退出
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -167,7 +179,7 @@ namespace N_m3u8DL_CLI
|
||||
if (File.Exists(savePath) && Global.ShouldStop == false)
|
||||
{
|
||||
FileInfo fi = new FileInfo(savePath);
|
||||
if (Method == "NONE")
|
||||
if (Method == "NONE" || method.Contains("NOTSUPPORTED"))
|
||||
{
|
||||
fi.MoveTo(Path.GetDirectoryName(savePath) + "\\" + Path.GetFileNameWithoutExtension(savePath) + ".ts");
|
||||
DownloadManager.DownloadedSize += fi.Length;
|
||||
@@ -179,11 +191,24 @@ namespace N_m3u8DL_CLI
|
||||
//解密
|
||||
try
|
||||
{
|
||||
byte[] decryptBuff = Decrypter.AES128Decrypt(
|
||||
fi.FullName,
|
||||
Convert.FromBase64String(Key),
|
||||
Decrypter.HexStringToBytes(Iv)
|
||||
);
|
||||
byte[] decryptBuff = null;
|
||||
if(fileUrl.Contains(".51cto.com/")) //使用AES-128-ECB模式解密
|
||||
{
|
||||
decryptBuff = Decrypter.AES128Decrypt(
|
||||
fi.FullName,
|
||||
Convert.FromBase64String(Key),
|
||||
Decrypter.HexStringToBytes(Iv),
|
||||
System.Security.Cryptography.CipherMode.ECB
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
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();
|
||||
@@ -193,20 +218,16 @@ namespace N_m3u8DL_CLI
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LOGGER.PrintLine(ex.Message, LOGGER.Error);
|
||||
LOGGER.WriteLineError(ex.Message);
|
||||
Thread.Sleep(3000);
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
}
|
||||
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);
|
||||
LOGGER.WriteLineError(strings.SomethingWasWrong);
|
||||
LOGGER.PrintLine(strings.SomethingWasWrong, LOGGER.Error);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
@@ -215,14 +236,14 @@ namespace N_m3u8DL_CLI
|
||||
catch (Exception ex)
|
||||
{
|
||||
LOGGER.WriteLineError(ex.Message);
|
||||
if (ex.Message.Contains("404"))
|
||||
if (ex.Message.Contains("404") || ex.Message.Contains("400"))//(400) 错误的请求,片段过期会提示400错误
|
||||
{
|
||||
IsDone = true;
|
||||
return;
|
||||
}
|
||||
else if (IsLive && count++ < Retry)
|
||||
{
|
||||
Thread.Sleep(5000);
|
||||
Thread.Sleep(2000);//直播一般3-6秒一个片段
|
||||
Down();
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,13 @@ namespace N_m3u8DL_CLI
|
||||
string poster = "", string audioName = "", string title = "",
|
||||
string copyright = "", string comment = "", string encodingTool = "")
|
||||
{
|
||||
//同名文件已存在的共存策略
|
||||
if (File.Exists($"{OutPutPath}.{muxFormat.ToLower()}"))
|
||||
{
|
||||
OutPutPath = Path.Combine(Path.GetDirectoryName(OutPutPath),
|
||||
Path.GetFileName(OutPutPath) + "_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
|
||||
}
|
||||
|
||||
string command = "-loglevel warning -i concat:\"";
|
||||
string data = string.Empty;
|
||||
string ddpAudio = string.Empty;
|
||||
@@ -77,7 +84,7 @@ namespace N_m3u8DL_CLI
|
||||
}
|
||||
|
||||
Run("ffmpeg", command, Path.GetDirectoryName(files[0]));
|
||||
LOGGER.WriteLine("Result in [ffreport.log]");
|
||||
LOGGER.WriteLine(strings.ffmpegDone);
|
||||
//Console.WriteLine(command);
|
||||
}
|
||||
|
||||
@@ -86,7 +93,7 @@ namespace N_m3u8DL_CLI
|
||||
if (Global.VIDEO_TYPE == "H264")
|
||||
{
|
||||
Run("ffmpeg",
|
||||
"-loglevel quiet -i \"" + file + "\" -map 0 -c copy -f mpegts -bsf:v h264_mp4toannexb \""
|
||||
"-loglevel quiet -i \"" + file + "\" -map 0 -c copy -copy_unknown -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"))
|
||||
@@ -98,7 +105,7 @@ namespace N_m3u8DL_CLI
|
||||
else if (Global.VIDEO_TYPE == "H265")
|
||||
{
|
||||
Run("ffmpeg",
|
||||
"-loglevel quiet -i \"" + file + "\" -map 0 -c copy -f mpegts -bsf:v hevc_mp4toannexb \""
|
||||
"-loglevel quiet -i \"" + file + "\" -map 0 -c copy -copy_unknown -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"))
|
||||
|
@@ -30,11 +30,12 @@ namespace N_m3u8DL_CLI
|
||||
|
||||
|
||||
/*===============================================================================*/
|
||||
static string nowVer = "2.4.5";
|
||||
static string nowDate = "20191218";
|
||||
static string nowVer = "2.7.1";
|
||||
static string nowDate = "20200719";
|
||||
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}...");
|
||||
@@ -52,21 +53,21 @@ namespace N_m3u8DL_CLI
|
||||
string latestVer = redirctUrl.Replace("https://github.com/nilaoda/N_m3u8DL-CLI/releases/tag/", "");
|
||||
if (nowVer != latestVer && !latestVer.StartsWith("https"))
|
||||
{
|
||||
Console.Title = $"检测到更新,版本:{latestVer}! 正在尝试自动下载新版";
|
||||
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";
|
||||
if (File.Exists(Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), $"N_m3u8DL-CLI_v{latestVer}.exe")))
|
||||
{
|
||||
Console.Title = $"检测到更新,版本:{latestVer}! 新版下载成功,请您自行替换";
|
||||
Console.Title = string.Format(strings.newerVerisonDownloaded, latestVer);
|
||||
return;
|
||||
}
|
||||
HttpDownloadFile(url, Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), $"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 = $"检测到更新,版本:{latestVer}! 新版下载成功,请您自行替换";
|
||||
Console.Title = string.Format(strings.newerVerisonDownloaded, latestVer);
|
||||
else
|
||||
Console.Title = $"检测到更新,版本:{latestVer}! 新版下载失败,请您自行下载";
|
||||
Console.Title = string.Format(strings.newerVerisonDownloadFailed, latestVer);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -100,87 +101,93 @@ namespace N_m3u8DL_CLI
|
||||
public static string GetWebSource(String url, string headers = "", int TimeOut = 60000)
|
||||
{
|
||||
string htmlCode = string.Empty;
|
||||
try
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
HttpWebRequest webRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
|
||||
webRequest.Method = "GET";
|
||||
if (NoProxy) webRequest.Proxy = null;
|
||||
webRequest.UserAgent = "Mozilla/4.0";
|
||||
webRequest.Headers.Add("Accept-Encoding", "gzip, deflate");
|
||||
webRequest.Timeout = TimeOut; //设置超时
|
||||
webRequest.KeepAlive = false;
|
||||
webRequest.AllowAutoRedirect = true; //自动跳转
|
||||
if (url.Contains("pcvideo") && url.Contains(".titan.mgtv.com"))
|
||||
try
|
||||
{
|
||||
webRequest.UserAgent = "";
|
||||
if (!url.Contains("/internettv/"))
|
||||
webRequest.Referer = "https://player.mgtv.com/mgtv_v6_player/PlayerCore.swf";
|
||||
webRequest.Headers.Add("Cookie", "MQGUID");
|
||||
}
|
||||
//添加headers
|
||||
if (headers != "")
|
||||
{
|
||||
foreach (string att in headers.Split('|'))
|
||||
HttpWebRequest webRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
|
||||
webRequest.Method = "GET";
|
||||
if (NoProxy) webRequest.Proxy = null;
|
||||
webRequest.UserAgent = "Mozilla/4.0";
|
||||
webRequest.Headers.Add("Accept-Encoding", "gzip, deflate");
|
||||
webRequest.Timeout = TimeOut; //设置超时
|
||||
webRequest.KeepAlive = false;
|
||||
webRequest.AllowAutoRedirect = true; //自动跳转
|
||||
if (url.Contains("pcvideo") && url.Contains(".titan.mgtv.com"))
|
||||
{
|
||||
try
|
||||
webRequest.UserAgent = "";
|
||||
if (!url.Contains("/internettv/"))
|
||||
webRequest.Referer = "https://player.mgtv.com/mgtv_v6_player/PlayerCore.swf";
|
||||
webRequest.Headers.Add("Cookie", "MQGUID");
|
||||
}
|
||||
//添加headers
|
||||
if (headers != "")
|
||||
{
|
||||
foreach (string att in headers.Split('|'))
|
||||
{
|
||||
if (att.Split(':')[0].ToLower() == "referer")
|
||||
webRequest.Referer = att.Substring(att.IndexOf(":") + 1);
|
||||
else if (att.Split(':')[0].ToLower() == "user-agent")
|
||||
webRequest.UserAgent = att.Substring(att.IndexOf(":") + 1);
|
||||
else if (att.Split(':')[0].ToLower() == "range")
|
||||
webRequest.AddRange(Convert.ToInt32(att.Substring(att.IndexOf(":") + 1).Split('-')[0], Convert.ToInt32(att.Substring(att.IndexOf(":") + 1).Split('-')[1])));
|
||||
else if (att.Split(':')[0].ToLower() == "accept")
|
||||
webRequest.Accept = att.Substring(att.IndexOf(":") + 1);
|
||||
else
|
||||
webRequest.Headers.Add(att);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.WriteLineError(e.Message);
|
||||
try
|
||||
{
|
||||
if (att.Split(':')[0].ToLower() == "referer")
|
||||
webRequest.Referer = att.Substring(att.IndexOf(":") + 1);
|
||||
else if (att.Split(':')[0].ToLower() == "user-agent")
|
||||
webRequest.UserAgent = att.Substring(att.IndexOf(":") + 1);
|
||||
else if (att.Split(':')[0].ToLower() == "range")
|
||||
webRequest.AddRange(Convert.ToInt32(att.Substring(att.IndexOf(":") + 1).Split('-')[0], Convert.ToInt32(att.Substring(att.IndexOf(":") + 1).Split('-')[1])));
|
||||
else if (att.Split(':')[0].ToLower() == "accept")
|
||||
webRequest.Accept = att.Substring(att.IndexOf(":") + 1);
|
||||
else
|
||||
webRequest.Headers.Add(att);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.WriteLineError(e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
|
||||
if (webResponse.ContentEncoding != null
|
||||
&& webResponse.ContentEncoding.ToLower() == "gzip") //如果使用了GZip则先解压
|
||||
{
|
||||
using (Stream streamReceive = webResponse.GetResponseStream())
|
||||
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
|
||||
if (webResponse.ContentEncoding != null
|
||||
&& webResponse.ContentEncoding.ToLower() == "gzip") //如果使用了GZip则先解压
|
||||
{
|
||||
using (var zipStream =
|
||||
new System.IO.Compression.GZipStream(streamReceive, System.IO.Compression.CompressionMode.Decompress))
|
||||
using (Stream streamReceive = webResponse.GetResponseStream())
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(zipStream, Encoding.UTF8))
|
||||
using (var zipStream =
|
||||
new System.IO.Compression.GZipStream(streamReceive, System.IO.Compression.CompressionMode.Decompress))
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(zipStream, Encoding.UTF8))
|
||||
{
|
||||
htmlCode = sr.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Stream streamReceive = webResponse.GetResponseStream())
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(streamReceive, Encoding.UTF8))
|
||||
{
|
||||
htmlCode = sr.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Stream streamReceive = webResponse.GetResponseStream())
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(streamReceive, Encoding.UTF8))
|
||||
{
|
||||
htmlCode = sr.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (webResponse != null)
|
||||
{
|
||||
webResponse.Close();
|
||||
if (webResponse != null)
|
||||
{
|
||||
webResponse.Close();
|
||||
}
|
||||
if (webRequest != null)
|
||||
{
|
||||
webRequest.Abort();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (webRequest != null)
|
||||
catch (Exception e) //捕获所有异常
|
||||
{
|
||||
webRequest.Abort();
|
||||
LOGGER.WriteLineError(e.Message);
|
||||
Thread.Sleep(1000); //1秒后重试
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (Exception e) //捕获所有异常
|
||||
{
|
||||
LOGGER.WriteLineError(e.Message);
|
||||
}
|
||||
|
||||
return htmlCode;
|
||||
}
|
||||
@@ -290,6 +297,12 @@ namespace N_m3u8DL_CLI
|
||||
/// <param name="outputFilePath"></param>
|
||||
public static void CombineMultipleFilesIntoSingleFile(string[] files, string outputFilePath)
|
||||
{
|
||||
//同名文件已存在的共存策略
|
||||
if (File.Exists(outputFilePath))
|
||||
{
|
||||
outputFilePath = Path.Combine(Path.GetDirectoryName(outputFilePath),
|
||||
Path.GetFileNameWithoutExtension(outputFilePath) + "_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + Path.GetExtension(outputFilePath));
|
||||
}
|
||||
if (files.Length == 1)
|
||||
{
|
||||
FileInfo fi = new FileInfo(files[0]);
|
||||
@@ -653,8 +666,8 @@ namespace N_m3u8DL_CLI
|
||||
//调用ffmpeg获取视频信息
|
||||
public static ArrayList GetVideoInfo(string file)
|
||||
{
|
||||
LOGGER.WriteLine("Reading Video Info");
|
||||
LOGGER.PrintLine("读取文件信息...", LOGGER.Warning);
|
||||
LOGGER.WriteLine(strings.readingFileInfo);
|
||||
LOGGER.PrintLine(strings.readingFileInfo, LOGGER.Warning);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
ArrayList info = new ArrayList();
|
||||
string cmd = "-hide_banner -i \"" + file + "\"";
|
||||
@@ -672,6 +685,7 @@ namespace N_m3u8DL_CLI
|
||||
p.StartInfo.RedirectStandardOutput = true; //由调用程序获取输出信息
|
||||
p.StartInfo.RedirectStandardError = true; //重定向标准错误输出
|
||||
p.StartInfo.CreateNoWindow = true; //不显示程序窗口
|
||||
p.StartInfo.StandardErrorEncoding = Encoding.UTF8;
|
||||
p.Start();//启动程序
|
||||
p.StandardInput.AutoFlush = true;
|
||||
//获取cmd窗口的输出信息
|
||||
|
@@ -12,15 +12,17 @@ namespace N_m3u8DL_CLI
|
||||
{
|
||||
class HLSLiveDownloader
|
||||
{
|
||||
public static int REC_DUR_LIMIT = -1; //默认不限制录制时长
|
||||
public static double REC_DUR = 0; //已录制时长
|
||||
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 double targetduration = 10;
|
||||
private bool isFirstJson = true;
|
||||
|
||||
|
||||
public double TotalDuration { get; set; }
|
||||
public string Headers { get => headers; set => headers = value; }
|
||||
public string DownDir { get => downDir; set => downDir = value; }
|
||||
public FileStream LiveStream { get => liveStream; set => liveStream = value; }
|
||||
@@ -33,7 +35,7 @@ namespace N_m3u8DL_CLI
|
||||
public void TimerStart()
|
||||
{
|
||||
timer.Enabled = true;
|
||||
timer.Interval = (targetduration - 2) * 1000; //执行间隔时间,单位为毫秒
|
||||
//timer.Interval = (targetduration - 2) * 1000; //执行间隔时间,单位为毫秒
|
||||
timer.Start();
|
||||
timer.Elapsed += new ElapsedEventHandler(UpdateList);
|
||||
UpdateList(timer, new EventArgs()); //立即执行一次
|
||||
@@ -57,7 +59,9 @@ namespace N_m3u8DL_CLI
|
||||
string jsonContent = File.ReadAllText(jsonFile);
|
||||
JObject initJson = JObject.Parse(jsonContent);
|
||||
string m3u8Url = initJson["m3u8"].Value<string>();
|
||||
targetduration = initJson["m3u8Info"]["targetDuration"].Value<int>();
|
||||
targetduration = initJson["m3u8Info"]["targetDuration"].Value<double>();
|
||||
TotalDuration = initJson["m3u8Info"]["totalDuration"].Value<double>();
|
||||
timer.Interval = (TotalDuration - targetduration) * 1000;//设置定时器运行间隔
|
||||
JArray lastSegments = JArray.Parse(initJson["m3u8Info"]["segments"][0].ToString().Trim()); //上次的分段,用于比对新分段
|
||||
ArrayList tempList = new ArrayList(); //所有待下载的列表
|
||||
tempList.Clear();
|
||||
@@ -76,6 +80,7 @@ namespace N_m3u8DL_CLI
|
||||
Parser parser = new Parser();
|
||||
parser.DownDir = Path.GetDirectoryName(jsonFile);
|
||||
parser.M3u8Url = m3u8Url;
|
||||
parser.LiveStream = true;
|
||||
parser.Parse(); //产生新的json文件
|
||||
|
||||
jsonContent = File.ReadAllText(jsonFile);
|
||||
@@ -89,6 +94,8 @@ namespace N_m3u8DL_CLI
|
||||
//Console.WriteLine(seg.ToString());
|
||||
}
|
||||
}
|
||||
if (toDownList.Count > 0)
|
||||
Record();
|
||||
}
|
||||
|
||||
//public void TryDownload()
|
||||
@@ -108,10 +115,9 @@ namespace N_m3u8DL_CLI
|
||||
|
||||
private void Record()
|
||||
{
|
||||
ArrayList temp = toDownList;
|
||||
while(temp.Count != 0)
|
||||
while (toDownList.Count > 0 && (sd.FileUrl != "" ? sd.IsDone : true))
|
||||
{
|
||||
JObject info = JObject.Parse(temp[0].ToString());
|
||||
JObject info = JObject.Parse(toDownList[0].ToString());
|
||||
int index = info["index"].Value<int>();
|
||||
sd.FileUrl = info["segUri"].Value<string>();
|
||||
sd.Method = info["method"].Value<string>();
|
||||
@@ -120,22 +126,20 @@ namespace N_m3u8DL_CLI
|
||||
sd.Key = info["key"].Value<string>();
|
||||
sd.Iv = info["iv"].Value<string>();
|
||||
}
|
||||
sd.TimeOut = 60000;
|
||||
sd.TimeOut = (int)timer.Interval - 1000;//超时时间不超过下次执行时间
|
||||
sd.SegIndex = index;
|
||||
sd.Headers = Headers;
|
||||
sd.SegDur = info["duration"].Value<double>();
|
||||
sd.IsLive = true; //标记为直播
|
||||
sd.LiveFile = LiveFile;
|
||||
sd.LiveStream = LiveStream;
|
||||
sd.Down(); //开始下载
|
||||
while (sd.IsDone != true) ; //忙等待
|
||||
while (sd.IsDone != true) { Thread.Sleep(1); }; //忙等待 Thread.Sleep(1) 可防止cpu 100% 防止电脑风扇狂转
|
||||
if (toDownList.Count > 0)
|
||||
toDownList.RemoveAt(0); //下完删除一项
|
||||
}
|
||||
LOGGER.PrintLine("Waiting...");
|
||||
//不断查找是否有新分段,有的话立即开始下载
|
||||
while (isNewSeg() != true)
|
||||
isNewSeg();
|
||||
Record();
|
||||
LOGGER.PrintLine("Waiting...", LOGGER.Warning);
|
||||
LOGGER.WriteLine("Waiting...");
|
||||
}
|
||||
|
||||
//检测是否有新分片
|
||||
|
@@ -61,6 +61,11 @@ namespace N_m3u8DL_CLI
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CursorIndex > 1000)
|
||||
{
|
||||
Console.Clear();
|
||||
CursorIndex = 0;
|
||||
}
|
||||
if (cursorIndex == 0)
|
||||
Console.SetCursorPosition(0, CursorIndex++);
|
||||
else
|
||||
|
@@ -39,7 +39,13 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.JScript" />
|
||||
<Reference Include="Newtonsoft.Json" />
|
||||
<Reference Include="netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\packages\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NiL.JS, Version=2.5.1428.0, Culture=neutral, PublicKeyToken=fa941a7c2a4de689, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NiL.JS.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Collections" />
|
||||
@@ -56,6 +62,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="CommandLineArgument.cs" />
|
||||
<Compile Include="CommandLineArgumentParser.cs" />
|
||||
<Compile Include="Decode51CtoKey.cs" />
|
||||
<Compile Include="Decrypter.cs" />
|
||||
<Compile Include="FFmpeg.cs" />
|
||||
<Compile Include="Global.cs" />
|
||||
@@ -73,6 +80,7 @@
|
||||
<None Include="App.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="bin\Debug\Newtonsoft.Json.dll" />
|
||||
@@ -91,5 +99,17 @@
|
||||
<ItemGroup>
|
||||
<Content Include="logo_3Iv_icon.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="strings.en-US.resx" />
|
||||
<EmbeddedResource Include="strings.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>strings.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="strings.zh-TW.resx" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("nilaoda")]
|
||||
[assembly: AssemblyProduct("N_m3u8DL-CLI")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2019")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2020")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -33,4 +33,4 @@ using System.Runtime.InteropServices;
|
||||
// 方法是按如下所示使用“*”: :
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("2.7.0.0")]
|
||||
|
4
N_m3u8DL-CLI/packages.config
Normal file
4
N_m3u8DL-CLI/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NiL.JS" version="2.5.1428" targetFramework="net46" />
|
||||
</packages>
|
541
N_m3u8DL-CLI/strings.Designer.cs
generated
Normal file
541
N_m3u8DL-CLI/strings.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
0
N_m3u8DL-CLI/strings.en-US.Designer.cs
generated
Normal file
0
N_m3u8DL-CLI/strings.en-US.Designer.cs
generated
Normal file
300
N_m3u8DL-CLI/strings.en-US.resx
Normal file
300
N_m3u8DL-CLI/strings.en-US.resx
Normal file
File diff suppressed because it is too large
Load Diff
301
N_m3u8DL-CLI/strings.resx
Normal file
301
N_m3u8DL-CLI/strings.resx
Normal file
File diff suppressed because it is too large
Load Diff
0
N_m3u8DL-CLI/strings.zh-TW.Designer.cs
generated
Normal file
0
N_m3u8DL-CLI/strings.zh-TW.Designer.cs
generated
Normal file
301
N_m3u8DL-CLI/strings.zh-TW.resx
Normal file
301
N_m3u8DL-CLI/strings.zh-TW.resx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -11,10 +11,9 @@
|
||||
This is a m3u8 downloader.
|
||||
## Summary
|
||||
Supports:
|
||||
* Auto deceypt for `AES-128`
|
||||
* Auto deceypt for `AES-128-CBC`
|
||||
* `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`
|
||||
|
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 it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user