You've already forked N_m3u8DL-CLI
mirror of
https://github.com/nilaoda/N_m3u8DL-CLI
synced 2025-09-18 12:00:50 +02:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1edc1a43fe | ||
![]() |
01e7735018 | ||
![]() |
a0c41d6116 | ||
![]() |
c3c25774de | ||
![]() |
ba40641a21 | ||
![]() |
5d76418780 | ||
![]() |
fd48b766b5 |
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace N_m3u8DL_CLI
|
namespace N_m3u8DL_CLI
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* https://www.cnblogs.com/linxuanchen/p/c-sharp-command-line-argument-parser.html
|
||||||
|
*/
|
||||||
public class CommandLineArgument
|
public class CommandLineArgument
|
||||||
{
|
{
|
||||||
List<CommandLineArgument> _arguments;
|
List<CommandLineArgument> _arguments;
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
using NiL.JS.BaseLibrary;
|
using NiL.JS.BaseLibrary;
|
||||||
using NiL.JS.Core;
|
using NiL.JS.Core;
|
||||||
using NiL.JS.Extensions;
|
using NiL.JS.Extensions;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace N_m3u8DL_CLI
|
namespace N_m3u8DL_CLI
|
||||||
{
|
{
|
||||||
@@ -285,6 +287,19 @@ function getKey(text, lid) {
|
|||||||
return btoa(dec(text, lid));
|
return btoa(dec(text, lid));
|
||||||
}";
|
}";
|
||||||
|
|
||||||
|
private static string MD5Encoding(string rawPass)
|
||||||
|
{
|
||||||
|
MD5 md5 = MD5.Create();
|
||||||
|
byte[] bs = Encoding.UTF8.GetBytes(rawPass);
|
||||||
|
byte[] hs = md5.ComputeHash(bs);
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
foreach (byte b in hs)
|
||||||
|
{
|
||||||
|
sb.Append(b.ToString("x2"));
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetDecodeKey(string encodeKey, string lid)
|
public static string GetDecodeKey(string encodeKey, string lid)
|
||||||
{
|
{
|
||||||
var context = new Context();
|
var context = new Context();
|
||||||
@@ -293,5 +308,11 @@ function getKey(text, lid) {
|
|||||||
string key = concatFunction.Call(new Arguments { encodeKey, lid }).ToString();
|
string key = concatFunction.Call(new Arguments { encodeKey, lid }).ToString();
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetSign(string lid)
|
||||||
|
{
|
||||||
|
var data = lid + "eDu_51Cto_siyuanTlw";
|
||||||
|
return MD5Encoding(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
N_m3u8DL-CLI/DecryptNfmovies.cs
Normal file
36
N_m3u8DL-CLI/DecryptNfmovies.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace N_m3u8DL_CLI
|
||||||
|
{
|
||||||
|
class DecryptNfmovies
|
||||||
|
{
|
||||||
|
//https://jx.nfmovies.com/hls.min.js
|
||||||
|
public static string DecryptM3u8(byte[] byteArray)
|
||||||
|
{
|
||||||
|
var t = byteArray;
|
||||||
|
var decrypt = "";
|
||||||
|
if (137 == t[0] && 80 == t[1] && 130 == t[354] && 96 == t[353]) t = t.Skip(355).ToArray();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (137 != t[0] || 80 != t[1] || 130 != t[394] || 96 != t[393])
|
||||||
|
{
|
||||||
|
for (var i = 0; i < t.Length; i++) decrypt += Convert.ToChar(t[i]);
|
||||||
|
return decrypt;
|
||||||
|
}
|
||||||
|
t = t.Skip(395).ToArray();
|
||||||
|
}
|
||||||
|
using (var zipStream =
|
||||||
|
new System.IO.Compression.GZipStream(new MemoryStream(t), System.IO.Compression.CompressionMode.Decompress))
|
||||||
|
{
|
||||||
|
using (StreamReader sr = new StreamReader(zipStream, Encoding.UTF8))
|
||||||
|
{
|
||||||
|
decrypt = sr.ReadToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decrypt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -19,7 +19,7 @@ namespace N_m3u8DL_CLI
|
|||||||
fs.Read(inBuff, 0, inBuff.Length);
|
fs.Read(inBuff, 0, inBuff.Length);
|
||||||
fs.Close();
|
fs.Close();
|
||||||
|
|
||||||
Aes dcpt = Aes.Create("AES");
|
Aes dcpt = Aes.Create();
|
||||||
dcpt.BlockSize = 128;
|
dcpt.BlockSize = 128;
|
||||||
dcpt.KeySize = 128;
|
dcpt.KeySize = 128;
|
||||||
dcpt.Key = keyByte;
|
dcpt.Key = keyByte;
|
||||||
@@ -36,7 +36,7 @@ namespace N_m3u8DL_CLI
|
|||||||
{
|
{
|
||||||
byte[] inBuff = encryptedBuff;
|
byte[] inBuff = encryptedBuff;
|
||||||
|
|
||||||
Aes dcpt = Aes.Create("AES");
|
Aes dcpt = Aes.Create();
|
||||||
dcpt.BlockSize = 128;
|
dcpt.BlockSize = 128;
|
||||||
dcpt.KeySize = 128;
|
dcpt.KeySize = 128;
|
||||||
dcpt.Key = keyByte;
|
dcpt.Key = keyByte;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -118,8 +118,8 @@ namespace N_m3u8DL_CLI
|
|||||||
HLSLiveDownloader.REC_DUR += SegDur;
|
HLSLiveDownloader.REC_DUR += SegDur;
|
||||||
if (HLSLiveDownloader.REC_DUR_LIMIT != -1 && HLSLiveDownloader.REC_DUR >= HLSLiveDownloader.REC_DUR_LIMIT)
|
if (HLSLiveDownloader.REC_DUR_LIMIT != -1 && HLSLiveDownloader.REC_DUR >= HLSLiveDownloader.REC_DUR_LIMIT)
|
||||||
{
|
{
|
||||||
LOGGER.PrintLine("录制已到达限定长度", LOGGER.Warning);
|
LOGGER.PrintLine(strings.recordLimitReached, LOGGER.Warning);
|
||||||
LOGGER.WriteLine("录制已到达限定长度");
|
LOGGER.WriteLine(strings.recordLimitReached);
|
||||||
Environment.Exit(0); //正常退出
|
Environment.Exit(0); //正常退出
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -226,8 +226,8 @@ namespace N_m3u8DL_CLI
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOGGER.WriteLineError("Something was wrong!");
|
LOGGER.WriteLineError(strings.SomethingWasWrong);
|
||||||
LOGGER.PrintLine("遇到了某些错误!", LOGGER.Error);
|
LOGGER.PrintLine(strings.SomethingWasWrong, LOGGER.Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@@ -84,7 +84,7 @@ namespace N_m3u8DL_CLI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Run("ffmpeg", command, Path.GetDirectoryName(files[0]));
|
Run("ffmpeg", command, Path.GetDirectoryName(files[0]));
|
||||||
LOGGER.WriteLine("Result in [ffreport.log]");
|
LOGGER.WriteLine(strings.ffmpegDone);
|
||||||
//Console.WriteLine(command);
|
//Console.WriteLine(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -30,8 +30,8 @@ namespace N_m3u8DL_CLI
|
|||||||
|
|
||||||
|
|
||||||
/*===============================================================================*/
|
/*===============================================================================*/
|
||||||
static string nowVer = "2.6.2";
|
static string nowVer = "2.7.3";
|
||||||
static string nowDate = "20200417";
|
static string nowDate = "20200914";
|
||||||
public static void WriteInit()
|
public static void WriteInit()
|
||||||
{
|
{
|
||||||
Console.Clear();
|
Console.Clear();
|
||||||
@@ -53,21 +53,21 @@ namespace N_m3u8DL_CLI
|
|||||||
string latestVer = redirctUrl.Replace("https://github.com/nilaoda/N_m3u8DL-CLI/releases/tag/", "");
|
string latestVer = redirctUrl.Replace("https://github.com/nilaoda/N_m3u8DL-CLI/releases/tag/", "");
|
||||||
if (nowVer != latestVer && !latestVer.StartsWith("https"))
|
if (nowVer != latestVer && !latestVer.StartsWith("https"))
|
||||||
{
|
{
|
||||||
Console.Title = $"检测到更新,版本:{latestVer}! 正在尝试自动下载新版";
|
Console.Title = string.Format(strings.newerVisionDetected, latestVer);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//尝试下载新版本(去码云)
|
//尝试下载新版本(去码云)
|
||||||
string url = $"https://gitee.com/nilaoda/N_m3u8DL-CLI/raw/master/N_m3u8DL-CLI_v{latestVer}.exe";
|
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")))
|
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;
|
return;
|
||||||
}
|
}
|
||||||
HttpDownloadFile(url, Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), $"N_m3u8DL-CLI_v{latestVer}.exe"));
|
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")))
|
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
|
else
|
||||||
Console.Title = $"检测到更新,版本:{latestVer}! 新版下载失败,请您自行下载";
|
Console.Title = string.Format(strings.newerVerisonDownloadFailed, latestVer);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
@@ -545,6 +545,7 @@ namespace N_m3u8DL_CLI
|
|||||||
|
|
||||||
long totalLen = 0;
|
long totalLen = 0;
|
||||||
long downLen = 0;
|
long downLen = 0;
|
||||||
|
bool pngHeader = false; //PNG HEADER检测
|
||||||
using (var response = (HttpWebResponse)request.GetResponse())
|
using (var response = (HttpWebResponse)request.GetResponse())
|
||||||
{
|
{
|
||||||
using (var responseStream = response.GetResponseStream())
|
using (var responseStream = response.GetResponseStream())
|
||||||
@@ -555,6 +556,10 @@ namespace N_m3u8DL_CLI
|
|||||||
totalLen = response.ContentLength;
|
totalLen = response.ContentLength;
|
||||||
byte[] bArr = new byte[1024];
|
byte[] bArr = new byte[1024];
|
||||||
int size = responseStream.Read(bArr, 0, (int)bArr.Length);
|
int size = responseStream.Read(bArr, 0, (int)bArr.Length);
|
||||||
|
if (!pngHeader && size > 3 && 137 == bArr[0] && 80 == bArr[1] && 78 == bArr[2] && 71 == bArr[3]) ;
|
||||||
|
{
|
||||||
|
pngHeader = true;
|
||||||
|
}
|
||||||
while (size > 0)
|
while (size > 0)
|
||||||
{
|
{
|
||||||
stream.Write(bArr, 0, size);
|
stream.Write(bArr, 0, size);
|
||||||
@@ -579,6 +584,8 @@ namespace N_m3u8DL_CLI
|
|||||||
try { File.Delete(path); } catch (Exception) { }
|
try { File.Delete(path); } catch (Exception) { }
|
||||||
if (totalLen != -1 && downLen != totalLen)
|
if (totalLen != -1 && downLen != totalLen)
|
||||||
try { File.Delete(path); } catch (Exception) { }
|
try { File.Delete(path); } catch (Exception) { }
|
||||||
|
if (pngHeader)
|
||||||
|
TrySkipPngHeader(path);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -587,6 +594,51 @@ namespace N_m3u8DL_CLI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用于处理利用图床上传TS导致前面被插入PNG Header的情况
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath"></param>
|
||||||
|
public static void TrySkipPngHeader(string filePath)
|
||||||
|
{
|
||||||
|
var u = File.ReadAllBytes(filePath);
|
||||||
|
if (0x47 == u[0])
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (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])
|
||||||
|
{
|
||||||
|
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])
|
||||||
|
{
|
||||||
|
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])
|
||||||
|
{
|
||||||
|
u = u.Skip(771).ToArray();
|
||||||
|
}
|
||||||
|
else if (137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3])
|
||||||
|
{
|
||||||
|
//确定是PNG但是需要手动查询结尾标记(0x60 0x82 0x47)
|
||||||
|
int skip = 0;
|
||||||
|
for (int i = 4; i < u.Length - 3; i++)
|
||||||
|
{
|
||||||
|
if (u[i] == 0x60 && u[i + 1] == 0x82 && u[i + 2] == 0x47)
|
||||||
|
{
|
||||||
|
skip = i + 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u = u.Skip(skip).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
File.WriteAllBytes(filePath, u);
|
||||||
|
}
|
||||||
|
|
||||||
//格式化json字符串
|
//格式化json字符串
|
||||||
public static string ConvertJsonString(string str)
|
public static string ConvertJsonString(string str)
|
||||||
{
|
{
|
||||||
@@ -666,8 +718,8 @@ namespace N_m3u8DL_CLI
|
|||||||
//调用ffmpeg获取视频信息
|
//调用ffmpeg获取视频信息
|
||||||
public static ArrayList GetVideoInfo(string file)
|
public static ArrayList GetVideoInfo(string file)
|
||||||
{
|
{
|
||||||
LOGGER.WriteLine("Reading Video Info");
|
LOGGER.WriteLine(strings.readingFileInfo);
|
||||||
LOGGER.PrintLine("读取文件信息...", LOGGER.Warning);
|
LOGGER.PrintLine(strings.readingFileInfo, LOGGER.Warning);
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
ArrayList info = new ArrayList();
|
ArrayList info = new ArrayList();
|
||||||
string cmd = "-hide_banner -i \"" + file + "\"";
|
string cmd = "-hide_banner -i \"" + file + "\"";
|
||||||
@@ -1015,5 +1067,58 @@ namespace N_m3u8DL_CLI
|
|||||||
return wr;
|
return wr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过X-TIMESTAMP-MAP 调整VTT字幕的时间轴
|
||||||
|
*/
|
||||||
|
public static void ReAdjustVtt(string[] vtts)
|
||||||
|
{
|
||||||
|
string MsToTime(int ms)
|
||||||
|
{
|
||||||
|
TimeSpan ts = new TimeSpan(0, 0, 0, 0, ms);
|
||||||
|
string str = "";
|
||||||
|
str = (ts.Hours.ToString("00") + ":") + ts.Minutes.ToString("00") + ":" + ts.Seconds.ToString("00") + "." + ts.Milliseconds.ToString("000");
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TimeToMs(string line)
|
||||||
|
{
|
||||||
|
int hh = Convert.ToInt32(line.Split(':')[0]);
|
||||||
|
int mm = Convert.ToInt32(line.Split(':')[1]);
|
||||||
|
int ss = Convert.ToInt32(line.Split(':')[2].Split('.')[0]);
|
||||||
|
int ms = Convert.ToInt32(line.Split(':')[2].Split('.')[1]);
|
||||||
|
return hh * 60 * 60 * 1000 + mm * 60 * 1000 + ss * 1000 + ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
int addTime = 0;
|
||||||
|
int baseTime = 0;
|
||||||
|
for (int i = 0; i < vtts.Length; i++)
|
||||||
|
{
|
||||||
|
string tmp = File.ReadAllText(vtts[i], Encoding.UTF8);
|
||||||
|
if (!Regex.IsMatch(tmp, "X-TIMESTAMP-MAP.*MPEGTS:(\\d+)"))
|
||||||
|
break;
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
int newTime = Convert.ToInt32(Regex.Match(tmp, "X-TIMESTAMP-MAP.*MPEGTS:(\\d+)").Groups[1].Value);
|
||||||
|
if (newTime == 900000)
|
||||||
|
continue;
|
||||||
|
//计算偏移量
|
||||||
|
//LOGGER.PrintLine((newTime - baseTime).ToString());
|
||||||
|
addTime = addTime + ((newTime - baseTime) / 100);
|
||||||
|
if ((newTime - baseTime) == 6300000)
|
||||||
|
addTime -= 3000;
|
||||||
|
//将新的作为基准时间
|
||||||
|
baseTime = newTime;
|
||||||
|
foreach (Match m in Regex.Matches(tmp, @"(\d{2}:\d{2}:\d{2}\.\d{3}) --> (\d{2}:\d{2}:\d{2}\.\d{3})"))
|
||||||
|
{
|
||||||
|
string start = m.Groups[1].Value;
|
||||||
|
string end = m.Groups[2].Value;
|
||||||
|
tmp = tmp.Replace(m.Value, MsToTime(TimeToMs(start) + addTime) + " --> " + MsToTime(TimeToMs(end) + addTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File.WriteAllText(vtts[i], Regex.Replace(tmp, "X-TIMESTAMP-MAP=.*", ""), Encoding.UTF8);
|
||||||
|
}
|
||||||
|
//Console.ReadLine();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -41,10 +41,10 @@
|
|||||||
<Reference Include="Microsoft.JScript" />
|
<Reference Include="Microsoft.JScript" />
|
||||||
<Reference Include="netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
|
<Reference Include="netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
|
||||||
<Reference Include="Newtonsoft.Json">
|
<Reference Include="Newtonsoft.Json">
|
||||||
<HintPath>..\packages\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\..\Newtonsoft.Json.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="NiL.JS, Version=2.5.1428.0, Culture=neutral, PublicKeyToken=fa941a7c2a4de689, processorArchitecture=MSIL">
|
<Reference Include="NiL.JS, Version=2.5.1428.0, Culture=neutral, PublicKeyToken=fa941a7c2a4de689, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\NiL.JS.dll</HintPath>
|
<HintPath>..\packages\NiL.JS.2.5.1428\lib\net45\NiL.JS.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="PresentationFramework" />
|
<Reference Include="PresentationFramework" />
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
@@ -63,7 +63,9 @@
|
|||||||
<Compile Include="CommandLineArgument.cs" />
|
<Compile Include="CommandLineArgument.cs" />
|
||||||
<Compile Include="CommandLineArgumentParser.cs" />
|
<Compile Include="CommandLineArgumentParser.cs" />
|
||||||
<Compile Include="Decode51CtoKey.cs" />
|
<Compile Include="Decode51CtoKey.cs" />
|
||||||
|
<Compile Include="DecodeImooc.cs" />
|
||||||
<Compile Include="Decrypter.cs" />
|
<Compile Include="Decrypter.cs" />
|
||||||
|
<Compile Include="DecryptNfmovies.cs" />
|
||||||
<Compile Include="FFmpeg.cs" />
|
<Compile Include="FFmpeg.cs" />
|
||||||
<Compile Include="Global.cs" />
|
<Compile Include="Global.cs" />
|
||||||
<Compile Include="HLSLiveDownloader.cs" />
|
<Compile Include="HLSLiveDownloader.cs" />
|
||||||
@@ -74,6 +76,21 @@
|
|||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Downloader.cs" />
|
<Compile Include="Downloader.cs" />
|
||||||
|
<Compile Include="strings.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<DependentUpon>strings.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="strings.en-US.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<DependentUpon>strings.en-US.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="strings.zh-TW.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<DependentUpon>strings.zh-TW.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Watcher.cs" />
|
<Compile Include="Watcher.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -99,5 +116,19 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="logo_3Iv_icon.ico" />
|
<Content Include="logo_3Iv_icon.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="strings.en-US.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>strings.en-US.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="strings.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>strings.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="strings.zh-TW.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>strings.zh-TW.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
@@ -24,6 +24,7 @@ namespace N_m3u8DL_CLI
|
|||||||
private string downName = string.Empty;
|
private string downName = string.Empty;
|
||||||
private string keyFile = string.Empty;
|
private string keyFile = string.Empty;
|
||||||
private string keyBase64 = string.Empty;
|
private string keyBase64 = string.Empty;
|
||||||
|
private string keyIV = string.Empty;
|
||||||
private bool liveStream = false;
|
private bool liveStream = false;
|
||||||
private long bestBandwidth = 0;
|
private long bestBandwidth = 0;
|
||||||
private string bestUrl = string.Empty;
|
private string bestUrl = string.Empty;
|
||||||
@@ -60,6 +61,7 @@ namespace N_m3u8DL_CLI
|
|||||||
public string KeyFile { get => keyFile; set => keyFile = value; }
|
public string KeyFile { get => keyFile; set => keyFile = value; }
|
||||||
public string KeyBase64 { get => keyBase64; set => keyBase64 = value; }
|
public string KeyBase64 { get => keyBase64; set => keyBase64 = value; }
|
||||||
public bool LiveStream { get => liveStream; set => liveStream = value; }
|
public bool LiveStream { get => liveStream; set => liveStream = value; }
|
||||||
|
public string KeyIV { get => keyIV; set => keyIV = value; }
|
||||||
|
|
||||||
public void Parse()
|
public void Parse()
|
||||||
{
|
{
|
||||||
@@ -89,10 +91,15 @@ namespace N_m3u8DL_CLI
|
|||||||
|
|
||||||
//获取m3u8内容
|
//获取m3u8内容
|
||||||
if (!liveStream)
|
if (!liveStream)
|
||||||
LOGGER.PrintLine("获取m3u8内容", LOGGER.Warning);
|
LOGGER.PrintLine(strings.downloadingM3u8, LOGGER.Warning);
|
||||||
|
|
||||||
if (M3u8Url.StartsWith("http"))
|
if (M3u8Url.StartsWith("http"))
|
||||||
m3u8Content = Global.GetWebSource(M3u8Url, headers);
|
{
|
||||||
|
if (M3u8Url.Contains("nfmovies.com/hls"))
|
||||||
|
m3u8Content = DecryptNfmovies.DecryptM3u8(Global.HttpDownloadFileToBytes(M3u8Url, headers));
|
||||||
|
else
|
||||||
|
m3u8Content = Global.GetWebSource(M3u8Url, headers);
|
||||||
|
}
|
||||||
else if (M3u8Url.StartsWith("file:"))
|
else if (M3u8Url.StartsWith("file:"))
|
||||||
{
|
{
|
||||||
Uri t = new Uri(M3u8Url);
|
Uri t = new Uri(M3u8Url);
|
||||||
@@ -116,9 +123,24 @@ namespace N_m3u8DL_CLI
|
|||||||
if (M3u8Url.Contains("tlivecloud-playback-cdn.ysp.cctv.cn") && M3u8Url.Contains("endtime="))
|
if (M3u8Url.Contains("tlivecloud-playback-cdn.ysp.cctv.cn") && M3u8Url.Contains("endtime="))
|
||||||
isEndlist = true;
|
isEndlist = true;
|
||||||
|
|
||||||
|
if (M3u8Url.Contains("imooc.com/"))
|
||||||
|
{
|
||||||
|
m3u8Content = DecodeImooc.DecodeM3u8(m3u8Content);
|
||||||
|
}
|
||||||
|
|
||||||
//输出m3u8文件
|
//输出m3u8文件
|
||||||
File.WriteAllText(m3u8SavePath, m3u8Content);
|
File.WriteAllText(m3u8SavePath, m3u8Content);
|
||||||
|
|
||||||
|
//针对优酷#EXT-X-VERSION:7杜比视界片源修正
|
||||||
|
if (m3u8Content.Contains("#EXT-X-DISCONTINUITY") && m3u8Content.Contains("#EXT-X-MAP") && m3u8Content.Contains("ott.cibntv.net") && m3u8Content.Contains("ccode="))
|
||||||
|
{
|
||||||
|
Regex ykmap = new Regex("#EXT-X-DISCONTINUITY\\s+#EXT-X-MAP:URI=\\\"(.*?)\\\",BYTERANGE=\\\"(.*?)\\\"");
|
||||||
|
foreach (Match m in ykmap.Matches(m3u8Content))
|
||||||
|
{
|
||||||
|
m3u8Content = m3u8Content.Replace(m.Value, $"#EXTINF:0.000000,\n#EXT-X-BYTERANGE:{m.Groups[2].Value}\n{m.Groups[1].Value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//如果BaseUrl为空则截取字符串充当
|
//如果BaseUrl为空则截取字符串充当
|
||||||
if (BaseUrl == "")
|
if (BaseUrl == "")
|
||||||
{
|
{
|
||||||
@@ -130,19 +152,28 @@ namespace N_m3u8DL_CLI
|
|||||||
|
|
||||||
if (!liveStream)
|
if (!liveStream)
|
||||||
{
|
{
|
||||||
LOGGER.WriteLine("Parsing Content");
|
LOGGER.WriteLine(strings.parsingM3u8);
|
||||||
LOGGER.PrintLine("解析m3u8内容");
|
LOGGER.PrintLine(strings.parsingM3u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(keyBase64))
|
if (!string.IsNullOrEmpty(keyBase64))
|
||||||
{
|
{
|
||||||
string line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"base64:{keyBase64}\"";
|
string line = "";
|
||||||
|
if (string.IsNullOrEmpty(keyIV))
|
||||||
|
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"base64:{keyBase64}\"";
|
||||||
|
else
|
||||||
|
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"base64:{keyBase64}\",IV=0x{keyIV.Replace("0x", "")}";
|
||||||
m3u8CurrentKey = ParseKey(line);
|
m3u8CurrentKey = ParseKey(line);
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(keyFile))
|
if (!string.IsNullOrEmpty(keyFile))
|
||||||
{
|
{
|
||||||
|
string line = "";
|
||||||
Uri u = new Uri(keyFile);
|
Uri u = new Uri(keyFile);
|
||||||
string line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"{u.ToString()}\"";
|
if (string.IsNullOrEmpty(keyIV))
|
||||||
|
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"{u.ToString()}\"";
|
||||||
|
else
|
||||||
|
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"{u.ToString()}\",IV=0x{keyIV.Replace("0x", "")}";
|
||||||
|
|
||||||
m3u8CurrentKey = ParseKey(line);
|
m3u8CurrentKey = ParseKey(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +283,7 @@ namespace N_m3u8DL_CLI
|
|||||||
segInfo.Add("key", m3u8CurrentKey[1]);
|
segInfo.Add("key", m3u8CurrentKey[1]);
|
||||||
//没有读取到IV,自己生成
|
//没有读取到IV,自己生成
|
||||||
if (m3u8CurrentKey[2] == "")
|
if (m3u8CurrentKey[2] == "")
|
||||||
segInfo.Add("iv", "0x" + Convert.ToString(segIndex, 2).PadLeft(32, '0'));
|
segInfo.Add("iv", "0x" + Convert.ToString(segIndex, 16).PadLeft(32, '0'));
|
||||||
else
|
else
|
||||||
segInfo.Add("iv", m3u8CurrentKey[2]);
|
segInfo.Add("iv", m3u8CurrentKey[2]);
|
||||||
}
|
}
|
||||||
@@ -428,8 +459,8 @@ namespace N_m3u8DL_CLI
|
|||||||
|
|
||||||
if (isM3u == false)
|
if (isM3u == false)
|
||||||
{
|
{
|
||||||
LOGGER.WriteLineError("NOT Contain #EXTM3U");
|
LOGGER.WriteLineError(strings.invalidM3u8);
|
||||||
LOGGER.PrintLine("无法读取m3u8", LOGGER.Error);
|
LOGGER.PrintLine(strings.invalidM3u8, LOGGER.Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,16 +588,22 @@ namespace N_m3u8DL_CLI
|
|||||||
//输出JSON文件
|
//输出JSON文件
|
||||||
if (!liveStream)
|
if (!liveStream)
|
||||||
{
|
{
|
||||||
LOGGER.WriteLine("Writing Json: [meta.json]");
|
LOGGER.WriteLine(strings.wrtingMeta);
|
||||||
LOGGER.PrintLine("写出meta.json");
|
LOGGER.PrintLine(strings.wrtingMeta);
|
||||||
}
|
}
|
||||||
File.WriteAllText(jsonSavePath, jsonResult.ToString());
|
File.WriteAllText(jsonSavePath, jsonResult.ToString());
|
||||||
//检测是否为master list
|
//检测是否为master list
|
||||||
MasterListCheck();
|
MasterListCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool downloadingM3u8KeyTip = false;
|
||||||
public string[] ParseKey(string line)
|
public string[] ParseKey(string line)
|
||||||
{
|
{
|
||||||
|
if (!downloadingM3u8KeyTip)
|
||||||
|
{
|
||||||
|
LOGGER.PrintLine(strings.downloadingM3u8Key, LOGGER.Warning);
|
||||||
|
downloadingM3u8KeyTip = true;
|
||||||
|
}
|
||||||
string[] tmp = line.Replace(HLSTags.ext_x_key + ":", "").Split(',');
|
string[] tmp = line.Replace(HLSTags.ext_x_key + ":", "").Split(',');
|
||||||
string[] key = new string[] { "NONE", "", "" };
|
string[] key = new string[] { "NONE", "", "" };
|
||||||
string u_l = Global.GetTagAttribute(lastKeyLine.Replace(HLSTags.ext_x_key + ":", ""), "URI");
|
string u_l = Global.GetTagAttribute(lastKeyLine.Replace(HLSTags.ext_x_key + ":", ""), "URI");
|
||||||
@@ -579,7 +616,7 @@ namespace N_m3u8DL_CLI
|
|||||||
{
|
{
|
||||||
if (m != "AES-128")
|
if (m != "AES-128")
|
||||||
{
|
{
|
||||||
LOGGER.PrintLine($"不支持{m}加密方式,将不被处理,且强制开启二进制合并", LOGGER.Error);
|
LOGGER.PrintLine(string.Format(strings.notSupportMethod, m), LOGGER.Error);
|
||||||
DownloadManager.BinaryMerge = true;
|
DownloadManager.BinaryMerge = true;
|
||||||
return new string[] { $"{m}(NOTSUPPORTED)", "", "" };
|
return new string[] { $"{m}(NOTSUPPORTED)", "", "" };
|
||||||
}
|
}
|
||||||
@@ -593,8 +630,7 @@ namespace N_m3u8DL_CLI
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOGGER.PrintLine("获取m3u8 key...", LOGGER.Warning);
|
LOGGER.WriteLine(strings.downloadingM3u8Key + " " + key[1]);
|
||||||
LOGGER.WriteLine("Opening " + key[1]);
|
|
||||||
if (key[1].StartsWith("http"))
|
if (key[1].StartsWith("http"))
|
||||||
{
|
{
|
||||||
string keyUrl = key[1];
|
string keyUrl = key[1];
|
||||||
@@ -621,7 +657,7 @@ namespace N_m3u8DL_CLI
|
|||||||
{
|
{
|
||||||
indexs = "0-1-2-3-4-5-6-7-8-10-11-12-14-15-16-18".Split('-');
|
indexs = "0-1-2-3-4-5-6-7-8-10-11-12-14-15-16-18".Split('-');
|
||||||
}
|
}
|
||||||
else if(algorithmNum == 2)
|
else if (algorithmNum == 2)
|
||||||
{
|
{
|
||||||
var a_CODE = (int)Encoding.ASCII.GetBytes("a")[0];
|
var a_CODE = (int)Encoding.ASCII.GetBytes("a")[0];
|
||||||
|
|
||||||
@@ -666,7 +702,7 @@ namespace N_m3u8DL_CLI
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(encKey.Length == 17)
|
else if (encKey.Length == 17)
|
||||||
{
|
{
|
||||||
indexs = "1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16".Split('-');
|
indexs = "1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16".Split('-');
|
||||||
}
|
}
|
||||||
@@ -682,6 +718,10 @@ namespace N_m3u8DL_CLI
|
|||||||
}
|
}
|
||||||
key[1] = Convert.ToBase64String(Encoding.Default.GetBytes(decKey));
|
key[1] = Convert.ToBase64String(Encoding.Default.GetBytes(decKey));
|
||||||
} //气球云
|
} //气球云
|
||||||
|
else if (key[1].Contains("imooc.com/"))
|
||||||
|
{
|
||||||
|
key[1] = DecodeImooc.DecodeKey(Global.GetWebSource(key[1], Headers));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (keyUrl.Contains("https://keydeliver.linetv.tw/jurassicPark")) //linetv
|
if (keyUrl.Contains("https://keydeliver.linetv.tw/jurassicPark")) //linetv
|
||||||
@@ -699,8 +739,8 @@ namespace N_m3u8DL_CLI
|
|||||||
string keyUrl = CombineURL(BaseUrl, key[1]);
|
string keyUrl = CombineURL(BaseUrl, key[1]);
|
||||||
if (keyUrl.Contains("edu.51cto.com")) //51cto
|
if (keyUrl.Contains("edu.51cto.com")) //51cto
|
||||||
{
|
{
|
||||||
keyUrl = keyUrl + "&sign=" + Global.GetTimeStamp(false);
|
|
||||||
string lessonId = Global.GetQueryString("lesson_id", keyUrl);
|
string lessonId = Global.GetQueryString("lesson_id", keyUrl);
|
||||||
|
keyUrl = keyUrl + "&sign=" + Decode51CtoKey.GetSign(lessonId);
|
||||||
var encodeKey = Encoding.UTF8.GetString(Global.HttpDownloadFileToBytes(keyUrl, Headers));
|
var encodeKey = Encoding.UTF8.GetString(Global.HttpDownloadFileToBytes(keyUrl, Headers));
|
||||||
key[1] = Decode51CtoKey.GetDecodeKey(encodeKey, lessonId);
|
key[1] = Decode51CtoKey.GetDecodeKey(encodeKey, lessonId);
|
||||||
}
|
}
|
||||||
@@ -724,18 +764,18 @@ namespace N_m3u8DL_CLI
|
|||||||
{
|
{
|
||||||
File.Copy(m3u8SavePath, Path.GetDirectoryName(m3u8SavePath) + "\\master.m3u8", true);
|
File.Copy(m3u8SavePath, Path.GetDirectoryName(m3u8SavePath) + "\\master.m3u8", true);
|
||||||
LOGGER.WriteLine("Master List Found");
|
LOGGER.WriteLine("Master List Found");
|
||||||
LOGGER.PrintLine("识别到大师列表", LOGGER.Warning);
|
LOGGER.PrintLine(strings.masterListFound, LOGGER.Warning);
|
||||||
string t = "{" + "\"masterUri\":\"" + M3u8Url + "\","
|
string t = "{" + "\"masterUri\":\"" + M3u8Url + "\","
|
||||||
+ "\"updateTime\":\"" + DateTime.Now.ToString("o") + "\","
|
+ "\"updateTime\":\"" + DateTime.Now.ToString("o") + "\","
|
||||||
+ "\"playLists:\":[" + string.Join(",", extLists.ToArray()) + "]" + "}";
|
+ "\"playLists:\":[" + string.Join(",", extLists.ToArray()) + "]" + "}";
|
||||||
//输出json文件
|
//输出json文件
|
||||||
LOGGER.WriteLine("Writing Master List Json: [playLists.json]");
|
LOGGER.WriteLine(strings.wrtingMasterMeta);
|
||||||
LOGGER.PrintLine("写出playLists.json");
|
LOGGER.PrintLine(strings.wrtingMasterMeta);
|
||||||
File.WriteAllText(Path.GetDirectoryName(jsonSavePath) + "\\playLists.json", Global.ConvertJsonString(t));
|
File.WriteAllText(Path.GetDirectoryName(jsonSavePath) + "\\playLists.json", Global.ConvertJsonString(t));
|
||||||
LOGGER.WriteLine("Select Playlist: " + bestUrl);
|
LOGGER.WriteLine(strings.selectPlaylist + ": " + bestUrl);
|
||||||
LOGGER.PrintLine("已自动选择最高清晰度");
|
LOGGER.PrintLine(strings.selectPlaylist);
|
||||||
LOGGER.WriteLine("Start Re-Parsing");
|
LOGGER.WriteLine(strings.startReParsing);
|
||||||
LOGGER.PrintLine("重新解析m3u8...", LOGGER.Warning);
|
LOGGER.PrintLine(strings.startReParsing, LOGGER.Warning);
|
||||||
//重置Baseurl并重新解析
|
//重置Baseurl并重新解析
|
||||||
M3u8Url = bestUrl;
|
M3u8Url = bestUrl;
|
||||||
BaseUrl = "";
|
BaseUrl = "";
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@@ -262,8 +263,30 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
/// - 增加同名文件合并时共存策略
|
/// - 增加同名文件合并时共存策略
|
||||||
/// 2020年4月17日
|
/// 2020年4月17日
|
||||||
/// - 优化异常捕获
|
/// - 优化异常捕获
|
||||||
/// - 控制台输出设置为UTF-8
|
|
||||||
/// - 细节优化
|
/// - 细节优化
|
||||||
|
/// 2020年4月22日
|
||||||
|
/// - 51cto getsign
|
||||||
|
/// 2020年5月23日
|
||||||
|
/// - 优酷杜比视界下载逻辑优化
|
||||||
|
/// 2020年6月15日
|
||||||
|
/// - 支持IMOCO m3u8/key解密
|
||||||
|
/// 2020年7月18日
|
||||||
|
/// - 从当前路径和exe路径同时寻找ffmpeg
|
||||||
|
/// - 支持多语言本地化(简繁英)
|
||||||
|
/// 2020年8月4日
|
||||||
|
/// - 修复外挂字幕命名问题
|
||||||
|
/// - 修复外挂字幕识别问题
|
||||||
|
/// - 修复外挂轨道的一些逻辑问题
|
||||||
|
/// - 优化多语言识别逻辑
|
||||||
|
/// 2020年8月5日
|
||||||
|
/// - 支持相对时间的vtt合并(还存在问题)
|
||||||
|
/// 2020年8月9日
|
||||||
|
/// - 修复IV错误导致的AES-128解密异常问题
|
||||||
|
/// - 支持自定义IV(--useKeyIV)
|
||||||
|
/// 2020年9月12日
|
||||||
|
/// - 支持nfmovies m3u8解密
|
||||||
|
/// - 支持自动去除PNG Header(https://puui.qpic.cn/newsapp_ls/0/12418116195/0)
|
||||||
|
/// - 修复相对时间的vtt合并的一些错误逻辑(还存在问题)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
///
|
///
|
||||||
|
|
||||||
@@ -278,13 +301,13 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
switch (CtrlType)
|
switch (CtrlType)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
LOGGER.WriteLine("Exited: Ctrl + C"
|
LOGGER.WriteLine(strings.ExitedCtrlC
|
||||||
+ "\r\n\r\nTask End: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")); //Ctrl+C关闭
|
+ "\r\n\r\nTask End: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")); //Ctrl+C关闭
|
||||||
Console.CursorVisible = true;
|
Console.CursorVisible = true;
|
||||||
Console.SetCursorPosition(0, LOGGER.CursorIndex);
|
Console.SetCursorPosition(0, LOGGER.CursorIndex);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
LOGGER.WriteLine("Exited: Force"
|
LOGGER.WriteLine(strings.ExitedForce
|
||||||
+ "\r\n\r\nTask End: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")); //按控制台关闭按钮关闭
|
+ "\r\n\r\nTask End: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")); //按控制台关闭按钮关闭
|
||||||
Console.CursorVisible = true;
|
Console.CursorVisible = true;
|
||||||
Console.SetCursorPosition(0, LOGGER.CursorIndex);
|
Console.SetCursorPosition(0, LOGGER.CursorIndex);
|
||||||
@@ -303,7 +326,23 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
{
|
{
|
||||||
SetConsoleCtrlHandler(cancelHandler, true);
|
SetConsoleCtrlHandler(cancelHandler, true);
|
||||||
ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
|
ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
|
||||||
Console.OutputEncoding = Encoding.GetEncoding("gbk");
|
string loc = "zh-CN";
|
||||||
|
string currLoc = Thread.CurrentThread.CurrentUICulture.Name;
|
||||||
|
if (currLoc == "zh-TW" || currLoc == "zh-HK" || currLoc == "zh-MO")
|
||||||
|
{
|
||||||
|
loc = "zh-TW";
|
||||||
|
}
|
||||||
|
else if (loc == "zh-CN" || loc == "zh-SG")
|
||||||
|
{
|
||||||
|
loc = "zh-CN";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loc = "en-US";
|
||||||
|
}
|
||||||
|
//设置语言
|
||||||
|
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(loc);
|
||||||
|
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(loc);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -313,7 +352,7 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
string fileName = "";
|
string fileName = "";
|
||||||
|
|
||||||
//寻找ffmpeg.exe
|
//寻找ffmpeg.exe
|
||||||
if (!File.Exists("ffmpeg.exe"))
|
if (!File.Exists("ffmpeg.exe") && !File.Exists(Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), "ffmpeg.exe")))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -331,14 +370,14 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
|
|
||||||
Console.BackgroundColor = ConsoleColor.Red; //设置背景色
|
Console.BackgroundColor = ConsoleColor.Red; //设置背景色
|
||||||
Console.ForegroundColor = ConsoleColor.White; //设置前景色,即字体颜色
|
Console.ForegroundColor = ConsoleColor.White; //设置前景色,即字体颜色
|
||||||
Console.WriteLine("在PATH和程序路径下找不到 ffmpeg.exe");
|
Console.WriteLine(strings.ffmpegLost);
|
||||||
Console.ResetColor(); //将控制台的前景色和背景色设为默认值
|
Console.ResetColor(); //将控制台的前景色和背景色设为默认值
|
||||||
Console.WriteLine("请下载ffmpeg.exe并把他放到程序同目录.");
|
Console.WriteLine(strings.ffmpegTip);
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("x86 https://ffmpeg.zeranoe.com/builds/win32/static/");
|
Console.WriteLine("x86 https://ffmpeg.zeranoe.com/builds/win32/static/");
|
||||||
Console.WriteLine("x64 https://ffmpeg.zeranoe.com/builds/win64/static/");
|
Console.WriteLine("x64 https://ffmpeg.zeranoe.com/builds/win64/static/");
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("按任意键退出.");
|
Console.WriteLine(strings.pressAnyKeyExit);
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
Environment.Exit(-1);
|
Environment.Exit(-1);
|
||||||
}
|
}
|
||||||
@@ -363,6 +402,7 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
string reqHeaders = "";
|
string reqHeaders = "";
|
||||||
string keyFile = "";
|
string keyFile = "";
|
||||||
string keyBase64 = "";
|
string keyBase64 = "";
|
||||||
|
string keyIV = "";
|
||||||
string muxSetJson = "MUXSETS.json";
|
string muxSetJson = "MUXSETS.json";
|
||||||
string workDir = CURRENT_PATH + "\\Downloads";
|
string workDir = CURRENT_PATH + "\\Downloads";
|
||||||
bool muxFastStart = false;
|
bool muxFastStart = false;
|
||||||
@@ -386,32 +426,7 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
var arguments = CommandLineArgumentParser.Parse(args);
|
var arguments = CommandLineArgumentParser.Parse(args);
|
||||||
if (args.Length == 1 && args[0] == "--help")
|
if (args.Length == 1 && args[0] == "--help")
|
||||||
{
|
{
|
||||||
Console.WriteLine(@"N_m3u8DL-CLI.exe <URL|File|JSON> [OPTIONS]
|
Console.WriteLine(strings.helpInfo);
|
||||||
|
|
||||||
--workDir Directory 设定程序工作目录
|
|
||||||
--saveName Filename 设定存储文件名(不包括后缀)
|
|
||||||
--baseUrl BaseUrl 设定Baseurl
|
|
||||||
--headers headers 设定请求头,格式 key:value 使用|分割不同的key&value
|
|
||||||
--maxThreads Thread 设定程序的最大线程数(默认为32)
|
|
||||||
--minThreads Thread 设定程序的最小线程数(默认为16)
|
|
||||||
--retryCount Count 设定程序的重试次数(默认为15)
|
|
||||||
--timeOut Sec 设定程序网络请求的超时时间(单位为秒,默认为10秒)
|
|
||||||
--muxSetJson File 使用外部json文件定义混流选项
|
|
||||||
--useKeyFile File 使用外部16字节文件定义AES-128解密KEY
|
|
||||||
--useKeyBase64 Base64String 使用Base64字符串定义AES-128解密KEY
|
|
||||||
--downloadRange Range 仅下载视频的一部分分片或长度
|
|
||||||
--liveRecDur HH:MM:SS 直播录制时,达到此长度自动退出软件
|
|
||||||
--stopSpeed Number 当速度低于此值时,重试(单位为KB/s)
|
|
||||||
--maxSpeed Number 设置下载速度上限(单位为KB/s)
|
|
||||||
--enableDelAfterDone 开启下载后删除临时文件夹的功能
|
|
||||||
--enableMuxFastStart 开启混流mp4的FastStart特性
|
|
||||||
--enableBinaryMerge 开启二进制合并分片
|
|
||||||
--enableParseOnly 开启仅解析模式(程序只进行到meta.json)
|
|
||||||
--enableAudioOnly 合并时仅封装音频轨道
|
|
||||||
--disableDateInfo 关闭混流中的日期写入
|
|
||||||
--noMerge 禁用自动合并
|
|
||||||
--noProxy 不自动使用系统代理
|
|
||||||
--disableIntegrityCheck 不检测分片数量是否完整");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (arguments.Has("--enableDelAfterDone"))
|
if (arguments.Has("--enableDelAfterDone"))
|
||||||
@@ -476,6 +491,10 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
{
|
{
|
||||||
keyBase64 = arguments.Get("--useKeyBase64").Next;
|
keyBase64 = arguments.Get("--useKeyBase64").Next;
|
||||||
}
|
}
|
||||||
|
if (arguments.Has("--useKeyIV"))
|
||||||
|
{
|
||||||
|
keyIV = arguments.Get("--useKeyIV").Next;
|
||||||
|
}
|
||||||
if (arguments.Has("--stopSpeed"))
|
if (arguments.Has("--stopSpeed"))
|
||||||
{
|
{
|
||||||
Global.STOP_SPEED = Convert.ToInt64(arguments.Get("--stopSpeed").Next);
|
Global.STOP_SPEED = Convert.ToInt64(arguments.Get("--stopSpeed").Next);
|
||||||
@@ -627,14 +646,15 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
//开始解析
|
//开始解析
|
||||||
|
|
||||||
Console.CursorVisible = false;
|
Console.CursorVisible = false;
|
||||||
LOGGER.PrintLine($"文件名称:{fileName}");
|
LOGGER.PrintLine($"{strings.fileName}{fileName}");
|
||||||
LOGGER.PrintLine($"存储路径:{Path.GetDirectoryName(Path.Combine(workDir, fileName))}");
|
LOGGER.PrintLine($"{strings.savePath}{Path.GetDirectoryName(Path.Combine(workDir, fileName))}");
|
||||||
|
|
||||||
Parser parser = new Parser();
|
Parser parser = new Parser();
|
||||||
parser.DownName = fileName;
|
parser.DownName = fileName;
|
||||||
parser.DownDir = Path.Combine(workDir, parser.DownName);
|
parser.DownDir = Path.Combine(workDir, parser.DownName);
|
||||||
parser.M3u8Url = testurl;
|
parser.M3u8Url = testurl;
|
||||||
parser.KeyBase64 = keyBase64;
|
parser.KeyBase64 = keyBase64;
|
||||||
|
parser.KeyIV = keyIV;
|
||||||
parser.KeyFile = keyFile;
|
parser.KeyFile = keyFile;
|
||||||
if (baseUrl != "")
|
if (baseUrl != "")
|
||||||
parser.BaseUrl = baseUrl;
|
parser.BaseUrl = baseUrl;
|
||||||
@@ -642,8 +662,8 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
string exePath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
|
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") + ".log");
|
||||||
LOGGER.InitLog();
|
LOGGER.InitLog();
|
||||||
LOGGER.WriteLine("Start Parsing " + testurl);
|
LOGGER.WriteLine(strings.startParsing + testurl);
|
||||||
LOGGER.PrintLine("开始解析地址...", LOGGER.Warning);
|
LOGGER.PrintLine(strings.startParsing, LOGGER.Warning);
|
||||||
if (testurl.EndsWith(".json") && File.Exists(testurl)) //可直接跳过解析
|
if (testurl.EndsWith(".json") && File.Exists(testurl)) //可直接跳过解析
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(Path.Combine(workDir, fileName)))//若文件夹不存在则新建文件夹
|
if (!Directory.Exists(Path.Combine(workDir, fileName)))//若文件夹不存在则新建文件夹
|
||||||
@@ -658,7 +678,7 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
//仅解析模式
|
//仅解析模式
|
||||||
if (parseOnly)
|
if (parseOnly)
|
||||||
{
|
{
|
||||||
LOGGER.PrintLine("解析m3u8成功, 程序退出");
|
LOGGER.PrintLine(strings.parseExit);
|
||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,15 +688,15 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
isVOD = Convert.ToBoolean(initJson["m3u8Info"]["vod"].ToString());
|
isVOD = Convert.ToBoolean(initJson["m3u8Info"]["vod"].ToString());
|
||||||
//传给Watcher总时长
|
//传给Watcher总时长
|
||||||
Watcher.TotalDuration = initJson["m3u8Info"]["totalDuration"].Value<double>();
|
Watcher.TotalDuration = initJson["m3u8Info"]["totalDuration"].Value<double>();
|
||||||
LOGGER.PrintLine($"文件时长:{Global.FormatTime((int)Watcher.TotalDuration)}");
|
LOGGER.PrintLine($"{strings.fileDuration}{Global.FormatTime((int)Watcher.TotalDuration)}");
|
||||||
LOGGER.PrintLine("总分片:" + initJson["m3u8Info"]["originalCount"].Value<int>()
|
LOGGER.PrintLine(strings.segCount + initJson["m3u8Info"]["originalCount"].Value<int>()
|
||||||
+ ", 已选择分片:" + initJson["m3u8Info"]["count"].Value<int>());
|
+ $", {strings.selectedCount}" + initJson["m3u8Info"]["count"].Value<int>());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(workDir, fileName));
|
DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(workDir, fileName));
|
||||||
directoryInfo.Delete(true);
|
directoryInfo.Delete(true);
|
||||||
LOGGER.PrintLine("地址无效", LOGGER.Error);
|
LOGGER.PrintLine(strings.InvalidUri, LOGGER.Error);
|
||||||
LOGGER.CursorIndex = 5;
|
LOGGER.CursorIndex = 5;
|
||||||
inputRetryCount--;
|
inputRetryCount--;
|
||||||
goto input;
|
goto input;
|
||||||
@@ -713,9 +733,8 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
//直播
|
//直播
|
||||||
if (isVOD == false)
|
if (isVOD == false)
|
||||||
{
|
{
|
||||||
LOGGER.WriteLine("Living Stream Found");
|
LOGGER.WriteLine(strings.liveStreamFoundAndRecoding);
|
||||||
LOGGER.WriteLine("Start Recording");
|
LOGGER.PrintLine(strings.liveStreamFoundAndRecoding);
|
||||||
LOGGER.PrintLine("识别为直播流, 开始录制");
|
|
||||||
//LOGGER.STOPLOG = true; //停止记录日志
|
//LOGGER.STOPLOG = true; //停止记录日志
|
||||||
//开辟文件流,且不关闭。(便于播放器不断读取文件)
|
//开辟文件流,且不关闭。(便于播放器不断读取文件)
|
||||||
string LivePath = Path.Combine(Directory.GetParent(parser.DownDir).FullName
|
string LivePath = Path.Combine(Directory.GetParent(parser.DownDir).FullName
|
||||||
@@ -734,8 +753,8 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
//监听测试
|
//监听测试
|
||||||
/*httplitsen:
|
/*httplitsen:
|
||||||
HTTPListener.StartListening();*/
|
HTTPListener.StartListening();*/
|
||||||
LOGGER.WriteLineError("Download Failed");
|
LOGGER.WriteLineError(strings.downloadFailed);
|
||||||
LOGGER.PrintLine("下载失败, 程序退出", LOGGER.Error);
|
LOGGER.PrintLine(strings.downloadFailed, LOGGER.Error);
|
||||||
Console.CursorVisible = true;
|
Console.CursorVisible = true;
|
||||||
Thread.Sleep(3000);
|
Thread.Sleep(3000);
|
||||||
Environment.Exit(-1);
|
Environment.Exit(-1);
|
||||||
|
@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("nilaoda")]
|
[assembly: AssemblyCompany("nilaoda")]
|
||||||
[assembly: AssemblyProduct("N_m3u8DL-CLI")]
|
[assembly: AssemblyProduct("N_m3u8DL-CLI")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2019")]
|
[assembly: AssemblyCopyright("Copyright © 2020")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
@@ -33,4 +33,4 @@ using System.Runtime.InteropServices;
|
|||||||
// 方法是按如下所示使用“*”: :
|
// 方法是按如下所示使用“*”: :
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
[assembly: AssemblyFileVersion("2.7.0.0")]
|
||||||
|
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
301
N_m3u8DL-CLI/strings.en-US.resx
Normal file
301
N_m3u8DL-CLI/strings.en-US.resx
Normal file
File diff suppressed because it is too large
Load Diff
302
N_m3u8DL-CLI/strings.resx
Normal file
302
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
302
N_m3u8DL-CLI/strings.zh-TW.resx
Normal file
302
N_m3u8DL-CLI/strings.zh-TW.resx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -61,6 +61,7 @@ N_m3u8DL-CLI.exe <URL|JSON|FILE> [OPTIONS]
|
|||||||
--muxSetJson File 使用外部json文件定义混流选项
|
--muxSetJson File 使用外部json文件定义混流选项
|
||||||
--useKeyFile File 使用外部16字节文件定义AES-128解密KEY
|
--useKeyFile File 使用外部16字节文件定义AES-128解密KEY
|
||||||
--useKeyBase64 Base64String 使用Base64字符串定义AES-128解密KEY
|
--useKeyBase64 Base64String 使用Base64字符串定义AES-128解密KEY
|
||||||
|
--useKeyIV HEXString 使用HEX字符串定义AES-128解密IV
|
||||||
--downloadRange Range 仅下载视频的一部分分片或长度
|
--downloadRange Range 仅下载视频的一部分分片或长度
|
||||||
--liveRecDur HH:MM:SS 直播录制时,达到此长度自动退出软件
|
--liveRecDur HH:MM:SS 直播录制时,达到此长度自动退出软件
|
||||||
--stopSpeed Number 当速度低于此值时,重试(单位为KB/s)
|
--stopSpeed Number 当速度低于此值时,重试(单位为KB/s)
|
||||||
|
@@ -43,6 +43,7 @@ N_m3u8DL-CLI.exe <URL|JSON|FILE> [OPTIONS]
|
|||||||
--muxSetJson File Set a json file for mux
|
--muxSetJson File Set a json file for mux
|
||||||
--useKeyFile File Use 16 bytes file as KEY for AES-128 decryption
|
--useKeyFile File Use 16 bytes file as KEY for AES-128 decryption
|
||||||
--useKeyBase64 Base64String Use Base64 String as KEY for AES-128 decryption
|
--useKeyBase64 Base64String Use Base64 String as KEY for AES-128 decryption
|
||||||
|
--useKeyIV HEXString Use HEX String as IV for AES-128 decryption
|
||||||
--downloadRange Range Set range for a video
|
--downloadRange Range Set range for a video
|
||||||
--stopSpeed Number Speed below this, retry(KB/s)
|
--stopSpeed Number Speed below this, retry(KB/s)
|
||||||
--maxSpeed Number Set max download speed(KB/s)
|
--maxSpeed Number Set max download speed(KB/s)
|
||||||
|
Reference in New Issue
Block a user