You've already forked N_m3u8DL-CLI
mirror of
https://github.com/nilaoda/N_m3u8DL-CLI
synced 2025-09-10 12:40:52 +02:00
Compare commits
64 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cf958e833b | ||
![]() |
fb09add0cd | ||
![]() |
5a3c5baefd | ||
![]() |
839afd8e61 | ||
![]() |
338c7a25d0 | ||
![]() |
f57ce8c2da | ||
![]() |
a5009e1683 | ||
![]() |
66933da9de | ||
![]() |
136389e248 | ||
![]() |
7971104bd4 | ||
![]() |
cc4941554d | ||
![]() |
c50371080a | ||
![]() |
154c5f84a1 | ||
![]() |
1556ee42df | ||
![]() |
121ff9f226 | ||
![]() |
e5b84c2f04 | ||
![]() |
7e562a06bb | ||
![]() |
1eae19c3ea | ||
![]() |
ba33049b99 | ||
![]() |
9773087bf9 | ||
![]() |
b60d39ad0d | ||
![]() |
e89705a370 | ||
![]() |
4aea16d672 | ||
![]() |
afc69eae1a | ||
![]() |
2446a25b35 | ||
![]() |
d37524180d | ||
![]() |
b02a29fbb7 | ||
![]() |
e87d02230d | ||
![]() |
63609a0970 | ||
![]() |
1fe798ab19 | ||
![]() |
21b9977501 | ||
![]() |
fbe37be499 | ||
![]() |
6416dd642d | ||
![]() |
967f0582a6 | ||
![]() |
a5aeffc6ec | ||
![]() |
745cbd92ad | ||
![]() |
4a4412a262 | ||
![]() |
084989b3ce | ||
![]() |
640208d95c | ||
![]() |
ad148ba4c7 | ||
![]() |
7c961dbbbf | ||
![]() |
83df7b4301 | ||
![]() |
860282a463 | ||
![]() |
70582029dd | ||
![]() |
1f3002ef66 | ||
![]() |
07c758dd00 | ||
![]() |
38f2f5589f | ||
![]() |
c7b9414613 | ||
![]() |
84953fa16a | ||
![]() |
4cf88711d6 | ||
![]() |
b59940f719 | ||
![]() |
6a6a338868 | ||
![]() |
0d74380e1c | ||
![]() |
5a398bdb79 | ||
![]() |
c2953a3d43 | ||
![]() |
6f2ebfba2d | ||
![]() |
3c1b893919 | ||
![]() |
8b6f411af9 | ||
![]() |
6cfd74f3a0 | ||
![]() |
044068016b | ||
![]() |
6d21ef0a10 | ||
![]() |
6d1834ca9f | ||
![]() |
3e215359b4 | ||
![]() |
3d9610d3bb |
63
.gitattributes
vendored
Normal file
63
.gitattributes
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
###############################################################################
|
||||||
|
# Set default behavior to automatically normalize line endings.
|
||||||
|
###############################################################################
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set default behavior for command prompt diff.
|
||||||
|
#
|
||||||
|
# This is need for earlier builds of msysgit that does not have it on by
|
||||||
|
# default for csharp files.
|
||||||
|
# Note: This is only used by command line
|
||||||
|
###############################################################################
|
||||||
|
#*.cs diff=csharp
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set the merge driver for project and solution files
|
||||||
|
#
|
||||||
|
# Merging from the command prompt will add diff markers to the files if there
|
||||||
|
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||||
|
# the diff markers are never inserted). Diff markers may cause the following
|
||||||
|
# file extensions to fail to load in VS. An alternative would be to treat
|
||||||
|
# these files as binary and thus will always conflict and require user
|
||||||
|
# intervention with every merge. To do so, just uncomment the entries below
|
||||||
|
###############################################################################
|
||||||
|
#*.sln merge=binary
|
||||||
|
#*.csproj merge=binary
|
||||||
|
#*.vbproj merge=binary
|
||||||
|
#*.vcxproj merge=binary
|
||||||
|
#*.vcproj merge=binary
|
||||||
|
#*.dbproj merge=binary
|
||||||
|
#*.fsproj merge=binary
|
||||||
|
#*.lsproj merge=binary
|
||||||
|
#*.wixproj merge=binary
|
||||||
|
#*.modelproj merge=binary
|
||||||
|
#*.sqlproj merge=binary
|
||||||
|
#*.wwaproj merge=binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# behavior for image files
|
||||||
|
#
|
||||||
|
# image files are treated as binary by default.
|
||||||
|
###############################################################################
|
||||||
|
#*.jpg binary
|
||||||
|
#*.png binary
|
||||||
|
#*.gif binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# diff behavior for common document formats
|
||||||
|
#
|
||||||
|
# Convert binary document formats to text before diffing them. This feature
|
||||||
|
# is only available from the command line. Turn it on by uncommenting the
|
||||||
|
# entries below.
|
||||||
|
###############################################################################
|
||||||
|
#*.doc diff=astextplain
|
||||||
|
#*.DOC diff=astextplain
|
||||||
|
#*.docx diff=astextplain
|
||||||
|
#*.DOCX diff=astextplain
|
||||||
|
#*.dot diff=astextplain
|
||||||
|
#*.DOT diff=astextplain
|
||||||
|
#*.pdf diff=astextplain
|
||||||
|
#*.PDF diff=astextplain
|
||||||
|
#*.rtf diff=astextplain
|
||||||
|
#*.RTF diff=astextplain
|
261
.gitignore
vendored
Normal file
261
.gitignore
vendored
Normal file
File diff suppressed because it is too large
Load Diff
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 nilaoda
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
31
N_m3u8DL-CLI.sln
Normal file
31
N_m3u8DL-CLI.sln
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 15
|
||||||
|
VisualStudioVersion = 15.0.27703.2000
|
||||||
|
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
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{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
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {82B9270D-B7B2-4591-BF8A-5B4EBCD0EA8A}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
6
N_m3u8DL-CLI/App.config
Normal file
6
N_m3u8DL-CLI/App.config
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup>
|
||||||
|
</configuration>
|
77
N_m3u8DL-CLI/CommandLineArgument.cs
Normal file
77
N_m3u8DL-CLI/CommandLineArgument.cs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace N_m3u8DL_CLI
|
||||||
|
{
|
||||||
|
public class CommandLineArgument
|
||||||
|
{
|
||||||
|
List<CommandLineArgument> _arguments;
|
||||||
|
|
||||||
|
int _index;
|
||||||
|
|
||||||
|
string _argumentText;
|
||||||
|
|
||||||
|
public CommandLineArgument Next
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_index < _arguments.Count - 1)
|
||||||
|
{
|
||||||
|
return _arguments[_index + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public CommandLineArgument Previous
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_index > 0)
|
||||||
|
{
|
||||||
|
return _arguments[_index - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal CommandLineArgument(List<CommandLineArgument> args, int index, string argument)
|
||||||
|
{
|
||||||
|
_arguments = args;
|
||||||
|
_index = index;
|
||||||
|
_argumentText = argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandLineArgument Take()
|
||||||
|
{
|
||||||
|
return Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<CommandLineArgument> Take(int count)
|
||||||
|
{
|
||||||
|
var list = new List<CommandLineArgument>();
|
||||||
|
var parent = this;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var next = parent.Next;
|
||||||
|
if (next == null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
list.Add(next);
|
||||||
|
|
||||||
|
parent = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator string(CommandLineArgument argument)
|
||||||
|
{
|
||||||
|
return argument._argumentText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return _argumentText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
N_m3u8DL-CLI/CommandLineArgumentParser.cs
Normal file
39
N_m3u8DL-CLI/CommandLineArgumentParser.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace N_m3u8DL_CLI
|
||||||
|
{
|
||||||
|
public class CommandLineArgumentParser
|
||||||
|
{
|
||||||
|
|
||||||
|
List<CommandLineArgument> _arguments;
|
||||||
|
public static CommandLineArgumentParser Parse(string[] args)
|
||||||
|
{
|
||||||
|
return new CommandLineArgumentParser(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandLineArgumentParser(string[] args)
|
||||||
|
{
|
||||||
|
_arguments = new List<CommandLineArgument>();
|
||||||
|
|
||||||
|
for (int i = 0; i < args.Length; i++)
|
||||||
|
{
|
||||||
|
_arguments.Add(new CommandLineArgument(_arguments, i, args[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandLineArgument Get(string argumentName)
|
||||||
|
{
|
||||||
|
return _arguments.FirstOrDefault(p => p == argumentName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Has(string argumentName)
|
||||||
|
{
|
||||||
|
return _arguments.Count(p => p == argumentName) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
82
N_m3u8DL-CLI/Decrypter.cs
Normal file
82
N_m3u8DL-CLI/Decrypter.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace N_m3u8DL_CLI
|
||||||
|
{
|
||||||
|
class Decrypter
|
||||||
|
{
|
||||||
|
public static byte[] AES128Decrypt(string filePath, byte[] keyByte, byte[] ivByte)
|
||||||
|
{
|
||||||
|
FileStream fs = new FileStream(filePath, FileMode.Open);
|
||||||
|
//获取文件大小
|
||||||
|
long size = fs.Length;
|
||||||
|
byte[] inBuff = new byte[size];
|
||||||
|
fs.Read(inBuff, 0, inBuff.Length);
|
||||||
|
fs.Close();
|
||||||
|
|
||||||
|
Aes dcpt = Aes.Create("AES");
|
||||||
|
dcpt.BlockSize = 128;
|
||||||
|
dcpt.KeySize = 128;
|
||||||
|
dcpt.Key = keyByte;
|
||||||
|
dcpt.IV = ivByte;
|
||||||
|
dcpt.Mode = CipherMode.CBC;
|
||||||
|
dcpt.Padding = PaddingMode.PKCS7;
|
||||||
|
|
||||||
|
ICryptoTransform cTransform = dcpt.CreateDecryptor();
|
||||||
|
Byte[] resultArray = cTransform.TransformFinalBlock(inBuff, 0, inBuff.Length);
|
||||||
|
return resultArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] AES128Decrypt(byte[] encryptedBuff, byte[] keyByte, byte[] ivByte)
|
||||||
|
{
|
||||||
|
byte[] inBuff = encryptedBuff;
|
||||||
|
|
||||||
|
Aes dcpt = Aes.Create("AES");
|
||||||
|
dcpt.BlockSize = 128;
|
||||||
|
dcpt.KeySize = 128;
|
||||||
|
dcpt.Key = keyByte;
|
||||||
|
dcpt.IV = ivByte;
|
||||||
|
dcpt.Mode = CipherMode.CBC;
|
||||||
|
dcpt.Padding = PaddingMode.PKCS7;
|
||||||
|
|
||||||
|
ICryptoTransform cTransform = dcpt.CreateDecryptor();
|
||||||
|
Byte[] resultArray = cTransform.TransformFinalBlock(inBuff, 0, inBuff.Length);
|
||||||
|
return resultArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] HexStringToBytes(string hexStr)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(hexStr))
|
||||||
|
{
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hexStr.StartsWith("0x") || hexStr.StartsWith("0X"))
|
||||||
|
{
|
||||||
|
hexStr = hexStr.Remove(0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = hexStr.Length;
|
||||||
|
|
||||||
|
if (count % 2 == 1)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid length of bytes:" + count);
|
||||||
|
}
|
||||||
|
|
||||||
|
int byteCount = count / 2;
|
||||||
|
byte[] result = new byte[byteCount];
|
||||||
|
for (int ii = 0; ii < byteCount; ++ii)
|
||||||
|
{
|
||||||
|
var tempBytes = Byte.Parse(hexStr.Substring(2 * ii, 2), System.Globalization.NumberStyles.HexNumber);
|
||||||
|
result[ii] = tempBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
668
N_m3u8DL-CLI/DownloadManager.cs
Normal file
668
N_m3u8DL-CLI/DownloadManager.cs
Normal file
File diff suppressed because it is too large
Load Diff
231
N_m3u8DL-CLI/Downloader.cs
Normal file
231
N_m3u8DL-CLI/Downloader.cs
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace N_m3u8DL_CLI
|
||||||
|
{
|
||||||
|
class Downloader
|
||||||
|
{
|
||||||
|
private int timeOut = 0;
|
||||||
|
private int retry = 5;
|
||||||
|
private int count = 0;
|
||||||
|
private int segIndex = 0;
|
||||||
|
private double segDur = 0;
|
||||||
|
private string fileUrl = string.Empty;
|
||||||
|
private string savePath = string.Empty;
|
||||||
|
private string headers = string.Empty;
|
||||||
|
private string method = string.Empty;
|
||||||
|
private string key = string.Empty;
|
||||||
|
private string iv = string.Empty;
|
||||||
|
private string liveFile = string.Empty;
|
||||||
|
private long expectByte = -1;
|
||||||
|
private long startByte = 0;
|
||||||
|
private bool isLive = false;
|
||||||
|
private bool isDone = false;
|
||||||
|
private bool firstSeg = true;
|
||||||
|
private FileStream liveStream = null;
|
||||||
|
|
||||||
|
public string FileUrl { get => fileUrl; set => fileUrl = value; }
|
||||||
|
public string SavePath { get => savePath; set => savePath = value; }
|
||||||
|
public string Headers { get => headers; set => headers = value; }
|
||||||
|
public string Method { get => method; set => method = value; }
|
||||||
|
public string Key { get => key; set => key = value; }
|
||||||
|
public string Iv { get => iv; set => iv = value; }
|
||||||
|
public bool IsLive { get => isLive; set => isLive = value; }
|
||||||
|
public int Retry { get => retry; set => retry = value; }
|
||||||
|
public bool IsDone { get => isDone; set => isDone = value; }
|
||||||
|
public int SegIndex { get => segIndex; set => segIndex = value; }
|
||||||
|
public int TimeOut { get => timeOut; set => timeOut = value; }
|
||||||
|
public FileStream LiveStream { get => liveStream; set => liveStream = value; }
|
||||||
|
public string LiveFile { get => liveFile; set => liveFile = value; }
|
||||||
|
public long ExpectByte { get => expectByte; set => expectByte = value; }
|
||||||
|
public long StartByte { get => startByte; set => startByte = value; }
|
||||||
|
public double SegDur { get => segDur; set => segDur = value; }
|
||||||
|
|
||||||
|
//重写WebClinet
|
||||||
|
//private class WebClient : System.Net.WebClient
|
||||||
|
//{
|
||||||
|
// protected override WebRequest GetWebRequest(Uri uri)
|
||||||
|
// {
|
||||||
|
// WebRequest lWebRequest = base.GetWebRequest(uri);
|
||||||
|
// lWebRequest.Timeout = TimeOut;
|
||||||
|
// ((HttpWebRequest)lWebRequest).ReadWriteTimeout = TimeOut;
|
||||||
|
// return lWebRequest;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//WebClient client = new WebClient();
|
||||||
|
|
||||||
|
|
||||||
|
public void Down()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//直播下载
|
||||||
|
if (IsLive)
|
||||||
|
{
|
||||||
|
IsDone = false; //设置为未完成下载
|
||||||
|
|
||||||
|
if (Method == "NONE")
|
||||||
|
{
|
||||||
|
LOGGER.PrintLine("<" + SegIndex + " Downloading>");
|
||||||
|
byte[] segBuff = Global.HttpDownloadFileToBytes(fileUrl, Headers, TimeOut);
|
||||||
|
//byte[] segBuff = Global.WebClientDownloadToBytes(fileUrl, Headers);
|
||||||
|
Global.AppendBytesToFileStreamAndDoNotClose(LiveStream, segBuff);
|
||||||
|
LOGGER.PrintLine("<" + SegIndex + " Complete>\r\n");
|
||||||
|
IsDone = true;
|
||||||
|
}
|
||||||
|
else if (Method == "AES-128")
|
||||||
|
{
|
||||||
|
LOGGER.PrintLine("<" + SegIndex + " Downloading>");
|
||||||
|
byte[] encryptedBuff = Global.HttpDownloadFileToBytes(fileUrl, Headers, TimeOut);
|
||||||
|
//byte[] encryptedBuff = Global.WebClientDownloadToBytes(fileUrl, Headers);
|
||||||
|
byte[] decryptBuff = Decrypter.AES128Decrypt(
|
||||||
|
encryptedBuff,
|
||||||
|
Convert.FromBase64String(Key),
|
||||||
|
Decrypter.HexStringToBytes(Iv)
|
||||||
|
);
|
||||||
|
Global.AppendBytesToFileStreamAndDoNotClose(LiveStream, decryptBuff);
|
||||||
|
LOGGER.PrintLine("<" + SegIndex + " Complete>\r\n");
|
||||||
|
IsDone = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOGGER.PrintLine("不支持这种加密方式!", LOGGER.Error);
|
||||||
|
IsDone = true;
|
||||||
|
}
|
||||||
|
if (firstSeg && Global.FileSize(LiveFile) != 0)
|
||||||
|
{
|
||||||
|
LOGGER.STOPLOG = false; //记录日志
|
||||||
|
foreach (string ss in (string[])Global.GetVideoInfo(LiveFile).ToArray(typeof(string)))
|
||||||
|
{
|
||||||
|
LOGGER.WriteLine(ss.Trim());
|
||||||
|
}
|
||||||
|
firstSeg = false;
|
||||||
|
LOGGER.STOPLOG = true; //停止记录日志
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//点播下载
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(Path.GetDirectoryName(SavePath)))
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(SavePath)); //新建文件夹
|
||||||
|
//是否存在文件,存在则不下载
|
||||||
|
if (File.Exists(Path.GetDirectoryName(savePath) + "\\" + Path.GetFileNameWithoutExtension(savePath) + ".ts"))
|
||||||
|
{
|
||||||
|
Global.BYTEDOWN++; //防止被速度监控程序杀死
|
||||||
|
//Console.WriteLine("Exists " + Path.GetFileNameWithoutExtension(savePath) + ".ts");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Console.WriteLine("开始下载 " + fileUrl);
|
||||||
|
//本地文件
|
||||||
|
if (fileUrl.StartsWith("file:"))
|
||||||
|
{
|
||||||
|
Uri t = new Uri(fileUrl);
|
||||||
|
fileUrl = t.LocalPath;
|
||||||
|
if (File.Exists(fileUrl))
|
||||||
|
{
|
||||||
|
if (ExpectByte == -1) //没有RANGE
|
||||||
|
{
|
||||||
|
FileInfo fi = new FileInfo(fileUrl);
|
||||||
|
fi.CopyTo(savePath);
|
||||||
|
Global.BYTEDOWN += fi.Length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FileStream stream = new FileInfo(fileUrl).OpenRead();
|
||||||
|
//seek文件
|
||||||
|
stream.Seek(StartByte, SeekOrigin.Begin);
|
||||||
|
Byte[] buffer = new Byte[ExpectByte];
|
||||||
|
//从流中读取字节块并将该数据写入给定缓冲区buffer中
|
||||||
|
stream.Read(buffer, 0, Convert.ToInt32(buffer.Length));
|
||||||
|
stream.Close();
|
||||||
|
//写出文件
|
||||||
|
MemoryStream m = new MemoryStream(buffer);
|
||||||
|
FileStream fs = new FileStream(savePath, FileMode.OpenOrCreate);
|
||||||
|
m.WriteTo(fs);
|
||||||
|
m.Close();
|
||||||
|
fs.Close();
|
||||||
|
m = null;
|
||||||
|
fs = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//下载
|
||||||
|
Global.HttpDownloadFile(fileUrl, savePath, TimeOut, Headers, StartByte, ExpectByte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (File.Exists(savePath) && Global.ShouldStop == false)
|
||||||
|
{
|
||||||
|
FileInfo fi = new FileInfo(savePath);
|
||||||
|
if (Method == "NONE")
|
||||||
|
{
|
||||||
|
fi.MoveTo(Path.GetDirectoryName(savePath) + "\\" + Path.GetFileNameWithoutExtension(savePath) + ".ts");
|
||||||
|
DownloadManager.DownloadedSize += fi.Length;
|
||||||
|
//Console.WriteLine(Path.GetFileNameWithoutExtension(savePath) + " Completed.");
|
||||||
|
}
|
||||||
|
else if (File.Exists(fi.FullName)
|
||||||
|
&& Method == "AES-128")
|
||||||
|
{
|
||||||
|
//解密
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] decryptBuff = Decrypter.AES128Decrypt(
|
||||||
|
fi.FullName,
|
||||||
|
Convert.FromBase64String(Key),
|
||||||
|
Decrypter.HexStringToBytes(Iv)
|
||||||
|
);
|
||||||
|
FileStream fs = new FileStream(Path.GetDirectoryName(savePath) + "\\" + Path.GetFileNameWithoutExtension(savePath) + ".ts", FileMode.Create);
|
||||||
|
fs.Write(decryptBuff, 0, decryptBuff.Length);
|
||||||
|
fs.Close();
|
||||||
|
DownloadManager.DownloadedSize += fi.Length;
|
||||||
|
fi.Delete();
|
||||||
|
//Console.WriteLine(Path.GetFileNameWithoutExtension(savePath) + " Completed & Decrypted.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LOGGER.WriteLineError(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(File.Exists(fi.FullName)
|
||||||
|
&& Method != "AES-128")
|
||||||
|
{
|
||||||
|
LOGGER.WriteLineError($"Do not support this METHOD: {Method}");
|
||||||
|
LOGGER.PrintLine("不支持这种加密方式!", LOGGER.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOGGER.WriteLineError("Something was wrong!");
|
||||||
|
LOGGER.PrintLine("遇到了某些错误!", LOGGER.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LOGGER.WriteLineError(ex.Message);
|
||||||
|
if (ex.Message.Contains("404"))
|
||||||
|
{
|
||||||
|
IsDone = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (IsLive && count++ < Retry)
|
||||||
|
{
|
||||||
|
Thread.Sleep(5000);
|
||||||
|
Down();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
169
N_m3u8DL-CLI/FFmpeg.cs
Normal file
169
N_m3u8DL-CLI/FFmpeg.cs
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace N_m3u8DL_CLI
|
||||||
|
{
|
||||||
|
class FFmpeg
|
||||||
|
{
|
||||||
|
private static string outPutPath = string.Empty;
|
||||||
|
private static string reportFile = string.Empty;
|
||||||
|
private static bool useAACFilter = false; //是否启用滤镜
|
||||||
|
private static bool writeDate = true; //是否写入录制日期
|
||||||
|
public static string OutPutPath { get => outPutPath; set => outPutPath = value; }
|
||||||
|
public static string ReportFile { get => reportFile; set => reportFile = value; }
|
||||||
|
public static bool UseAACFilter { get => useAACFilter; set => useAACFilter = value; }
|
||||||
|
public static bool WriteDate { get => writeDate; set => writeDate = value; }
|
||||||
|
|
||||||
|
public static void Merge(string[] files, string muxFormat, bool fastStart,
|
||||||
|
string poster = "", string audioName = "", string title = "",
|
||||||
|
string copyright = "", string comment = "", string encodingTool = "")
|
||||||
|
{
|
||||||
|
string command = "-loglevel warning -i concat:\"";
|
||||||
|
string data = string.Empty;
|
||||||
|
string ddpAudio = string.Empty;
|
||||||
|
string addPoster = "-map 1 -c:v:1 copy -disposition:v:1 attached_pic";
|
||||||
|
ddpAudio = (File.Exists($"{Path.GetFileNameWithoutExtension(OutPutPath + ".mp4")}.txt") ? File.ReadAllText($"{Path.GetFileNameWithoutExtension(OutPutPath + ".mp4")}.txt") : "") ;
|
||||||
|
if (!string.IsNullOrEmpty(ddpAudio)) UseAACFilter = false;
|
||||||
|
|
||||||
|
|
||||||
|
foreach (string t in files)
|
||||||
|
{
|
||||||
|
command += Path.GetFileName(t) + "|";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (muxFormat.ToUpper())
|
||||||
|
{
|
||||||
|
case ("MP4"):
|
||||||
|
command += "\" " + (string.IsNullOrEmpty(poster) ? "" : "-i \"" + poster + "\"");
|
||||||
|
command += " " + (string.IsNullOrEmpty(ddpAudio) ? "" : "-i \"" + ddpAudio + "\"");
|
||||||
|
command +=
|
||||||
|
$" -map 0:v? {(string.IsNullOrEmpty(ddpAudio) ? "-map 0:a?" : $"-map {(string.IsNullOrEmpty(poster) ? "1" : "2")}:a -map 0:a?")} -map 0:s? " + (string.IsNullOrEmpty(poster) ? "" : addPoster)
|
||||||
|
+ (writeDate ? " -metadata date=\"" + DateTime.Now.ToString("o") + "\"" : "") +
|
||||||
|
" -metadata encoding_tool=\"" + encodingTool + "\" -metadata title=\"" + title +
|
||||||
|
"\" -metadata copyright=\"" + copyright + "\" -metadata comment=\"" + comment +
|
||||||
|
$"\" -metadata:s:a:{(string.IsNullOrEmpty(ddpAudio) ? "0" : "1")} handler_name=\"" + audioName + $"\" -metadata:s:a:{(string.IsNullOrEmpty(ddpAudio) ? "0" : "1")} handler=\"" + audioName + "\" ";
|
||||||
|
command += (string.IsNullOrEmpty(ddpAudio) ? "" : " -metadata:s:a:0 handler_name=\"DD+\" -metadata:s:a:0 handler=\"DD+\" ");
|
||||||
|
if (fastStart)
|
||||||
|
command += "-movflags +faststart";
|
||||||
|
command += " -c copy -y " + (UseAACFilter ? "-bsf:a aac_adtstoasc" : "") + " \"" + OutPutPath + ".mp4\"";
|
||||||
|
break;
|
||||||
|
case ("MKV"):
|
||||||
|
command += "\" -map 0 -c copy -y " + (UseAACFilter ? "-bsf:a aac_adtstoasc" : "") + " \"" + OutPutPath + ".mkv\"";
|
||||||
|
break;
|
||||||
|
case ("FLV"):
|
||||||
|
command += "\" -map 0 -c copy -y " + (UseAACFilter ? "-bsf:a aac_adtstoasc" : "") + " \"" + OutPutPath + ".flv\"";
|
||||||
|
break;
|
||||||
|
case ("TS"):
|
||||||
|
command += "\" -map 0 -c copy -y -f mpegts -bsf:v h264_mp4toannexb \"" + OutPutPath + ".ts\"";
|
||||||
|
break;
|
||||||
|
case ("VTT"):
|
||||||
|
command += "\" -map 0 -y \"" + OutPutPath + ".srt\""; //Convert To Srt
|
||||||
|
break;
|
||||||
|
case ("EAC3"):
|
||||||
|
command += "\" -map 0:a -c copy -y \"" + OutPutPath + ".eac3\"";
|
||||||
|
break;
|
||||||
|
case ("AAC"):
|
||||||
|
command += "\" -map 0:a -c copy -y \"" + OutPutPath + ".m4a\"";
|
||||||
|
break;
|
||||||
|
case ("AC3"):
|
||||||
|
command += "\" -map 0:a -c copy -y \"" + OutPutPath + ".ac3\"";
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Run("ffmpeg", command, Path.GetDirectoryName(files[0]));
|
||||||
|
LOGGER.WriteLine("Result in [ffreport.log]");
|
||||||
|
//Console.WriteLine(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ConvertToMPEGTS(string file)
|
||||||
|
{
|
||||||
|
if (Global.VIDEO_TYPE == "H264")
|
||||||
|
{
|
||||||
|
Run("ffmpeg",
|
||||||
|
"-loglevel quiet -i \"" + file + "\" -map 0 -c copy -f mpegts -bsf:v h264_mp4toannexb \""
|
||||||
|
+ Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts\"",
|
||||||
|
Path.GetDirectoryName(file));
|
||||||
|
if (File.Exists(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts"))
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
File.Move(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts", file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Global.VIDEO_TYPE == "H265")
|
||||||
|
{
|
||||||
|
Run("ffmpeg",
|
||||||
|
"-loglevel quiet -i \"" + file + "\" -map 0 -c copy -f mpegts -bsf:v hevc_mp4toannexb \""
|
||||||
|
+ Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts\"",
|
||||||
|
Path.GetDirectoryName(file));
|
||||||
|
if (File.Exists(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts"))
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
File.Move(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts", file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOGGER.WriteLineError("Unkown Video Type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Run(string path, string args, string workDir)
|
||||||
|
{
|
||||||
|
string nowDir = Directory.GetCurrentDirectory(); //当前工作路径
|
||||||
|
Directory.SetCurrentDirectory(workDir);
|
||||||
|
Process p = new Process();//建立外部调用线程
|
||||||
|
p.StartInfo.FileName = path;//要调用外部程序的绝对路径
|
||||||
|
Environment.SetEnvironmentVariable("FFREPORT", "file=" + ReportFile + ":level=32"); //兼容XP系统
|
||||||
|
//p.StartInfo.Environment.Add("FFREPORT", "file=" + ReportFile + ":level=32");
|
||||||
|
p.StartInfo.Arguments = args;//参数(这里就是FFMPEG的参数了)
|
||||||
|
p.StartInfo.UseShellExecute = false;//不使用操作系统外壳程序启动线程(一定为FALSE,详细的请看MSDN)
|
||||||
|
p.StartInfo.RedirectStandardError = true;//把外部程序错误输出写到StandardError流中(这个一定要注意,FFMPEG的所有输出信息,都为错误输出流,用StandardOutput是捕获不到任何消息的...这是我耗费了2个多月得出来的经验...mencoder就是用standardOutput来捕获的)
|
||||||
|
p.StartInfo.CreateNoWindow = false;//不创建进程窗口
|
||||||
|
p.ErrorDataReceived += new DataReceivedEventHandler(Output);//外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
|
||||||
|
p.StartInfo.StandardErrorEncoding = Encoding.UTF8;
|
||||||
|
p.Start();//启动线程
|
||||||
|
p.BeginErrorReadLine();//开始异步读取
|
||||||
|
p.WaitForExit();//阻塞等待进程结束
|
||||||
|
p.Close();//关闭进程
|
||||||
|
p.Dispose();//释放资源
|
||||||
|
Environment.SetEnvironmentVariable("FFREPORT", null); //兼容XP系统
|
||||||
|
Directory.SetCurrentDirectory(nowDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Output(object sendProcess, DataReceivedEventArgs output)
|
||||||
|
{
|
||||||
|
if (!String.IsNullOrEmpty(output.Data))
|
||||||
|
{
|
||||||
|
LOGGER.PrintLine(output.Data, LOGGER.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CheckMPEGTS(string file)
|
||||||
|
{
|
||||||
|
//放行杜比视界或纯音频文件
|
||||||
|
if (Global.VIDEO_TYPE == "DV" || Global.AUDIO_TYPE != "")
|
||||||
|
return true;
|
||||||
|
//如果是多分片,也认为不是MPEGTS
|
||||||
|
if (DownloadManager.PartsCount > 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read))
|
||||||
|
{
|
||||||
|
byte[] firstByte = new byte[1];
|
||||||
|
fs.Read(firstByte, 0, 1);
|
||||||
|
//第一字节的16进制字符串
|
||||||
|
string _1_byte_str = Convert.ToString(firstByte[0], 16);
|
||||||
|
//syncword不为47就不处理
|
||||||
|
if (_1_byte_str != "47")
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
982
N_m3u8DL-CLI/Global.cs
Normal file
982
N_m3u8DL-CLI/Global.cs
Normal file
File diff suppressed because it is too large
Load Diff
149
N_m3u8DL-CLI/HLSLiveDownloader.cs
Normal file
149
N_m3u8DL-CLI/HLSLiveDownloader.cs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Timers;
|
||||||
|
|
||||||
|
namespace N_m3u8DL_CLI
|
||||||
|
{
|
||||||
|
class HLSLiveDownloader
|
||||||
|
{
|
||||||
|
private string liveFile = string.Empty;
|
||||||
|
private string jsonFile = string.Empty;
|
||||||
|
private string headers = string.Empty;
|
||||||
|
private string downDir = string.Empty;
|
||||||
|
private FileStream liveStream = null;
|
||||||
|
private int targetduration = 10;
|
||||||
|
private bool isFirstJson = true;
|
||||||
|
|
||||||
|
|
||||||
|
public string Headers { get => headers; set => headers = value; }
|
||||||
|
public string DownDir { get => downDir; set => downDir = value; }
|
||||||
|
public FileStream LiveStream { get => liveStream; set => liveStream = value; }
|
||||||
|
public string LiveFile { get => liveFile; set => liveFile = value; }
|
||||||
|
|
||||||
|
ArrayList toDownList = new ArrayList(); //所有待下载的列表
|
||||||
|
System.Timers.Timer timer = new System.Timers.Timer();
|
||||||
|
Downloader sd = new Downloader(); //只有一个实例
|
||||||
|
|
||||||
|
public void TimerStart()
|
||||||
|
{
|
||||||
|
timer.Enabled = true;
|
||||||
|
timer.Interval = (targetduration - 2) * 1000; //执行间隔时间,单位为毫秒
|
||||||
|
timer.Start();
|
||||||
|
timer.Elapsed += new ElapsedEventHandler(UpdateList);
|
||||||
|
UpdateList(timer, new EventArgs()); //立即执行一次
|
||||||
|
Record();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TimerStop()
|
||||||
|
{
|
||||||
|
timer.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
//更新列表
|
||||||
|
private void UpdateList(object source, EventArgs e)
|
||||||
|
{
|
||||||
|
jsonFile = Path.Combine(DownDir, "meta.json");
|
||||||
|
if (!File.Exists(jsonFile))
|
||||||
|
{
|
||||||
|
TimerStop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
string jsonContent = File.ReadAllText(jsonFile);
|
||||||
|
JObject initJson = JObject.Parse(jsonContent);
|
||||||
|
string m3u8Url = initJson["m3u8"].Value<string>();
|
||||||
|
targetduration = initJson["m3u8Info"]["targetDuration"].Value<int>();
|
||||||
|
JArray lastSegments = JArray.Parse(initJson["m3u8Info"]["segments"][0].ToString().Trim()); //上次的分段,用于比对新分段
|
||||||
|
ArrayList tempList = new ArrayList(); //所有待下载的列表
|
||||||
|
tempList.Clear();
|
||||||
|
foreach (JObject seg in lastSegments)
|
||||||
|
{
|
||||||
|
tempList.Add(seg.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isFirstJson)
|
||||||
|
{
|
||||||
|
toDownList = tempList;
|
||||||
|
isFirstJson = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Parser parser = new Parser();
|
||||||
|
parser.DownDir = Path.GetDirectoryName(jsonFile);
|
||||||
|
parser.M3u8Url = m3u8Url;
|
||||||
|
parser.Parse(); //产生新的json文件
|
||||||
|
|
||||||
|
jsonContent = File.ReadAllText(jsonFile);
|
||||||
|
initJson = JObject.Parse(jsonContent);
|
||||||
|
JArray segments = JArray.Parse(initJson["m3u8Info"]["segments"][0].ToString()); //大分组
|
||||||
|
foreach (JObject seg in segments)
|
||||||
|
{
|
||||||
|
if (!tempList.Contains(seg.ToString()))
|
||||||
|
{
|
||||||
|
toDownList.Add(seg.ToString()); //加入真正的待下载队列
|
||||||
|
//Console.WriteLine(seg.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//public void TryDownload()
|
||||||
|
//{
|
||||||
|
// Thread t = new Thread(Download);
|
||||||
|
// while (toDownList.Count != 0)
|
||||||
|
// {
|
||||||
|
// t = new Thread(Download);
|
||||||
|
// t.Start();
|
||||||
|
// t.Join();
|
||||||
|
// while (sd.IsDone != true) ; //忙等待
|
||||||
|
// if (toDownList.Count > 0)
|
||||||
|
// toDownList.RemoveAt(0); //下完删除一项
|
||||||
|
// }
|
||||||
|
// Console.WriteLine("Waiting...");
|
||||||
|
//}
|
||||||
|
|
||||||
|
private void Record()
|
||||||
|
{
|
||||||
|
ArrayList temp = toDownList;
|
||||||
|
while(temp.Count != 0)
|
||||||
|
{
|
||||||
|
JObject info = JObject.Parse(temp[0].ToString());
|
||||||
|
int index = info["index"].Value<int>();
|
||||||
|
sd.FileUrl = info["segUri"].Value<string>();
|
||||||
|
sd.Method = info["method"].Value<string>();
|
||||||
|
if (sd.Method != "NONE")
|
||||||
|
{
|
||||||
|
sd.Key = info["key"].Value<string>();
|
||||||
|
sd.Iv = info["iv"].Value<string>();
|
||||||
|
}
|
||||||
|
sd.TimeOut = 60000;
|
||||||
|
sd.SegIndex = index;
|
||||||
|
sd.Headers = Headers;
|
||||||
|
sd.IsLive = true; //标记为直播
|
||||||
|
sd.LiveFile = LiveFile;
|
||||||
|
sd.LiveStream = LiveStream;
|
||||||
|
sd.Down(); //开始下载
|
||||||
|
while (sd.IsDone != true) ; //忙等待
|
||||||
|
if (toDownList.Count > 0)
|
||||||
|
toDownList.RemoveAt(0); //下完删除一项
|
||||||
|
}
|
||||||
|
LOGGER.PrintLine("Waiting...");
|
||||||
|
//不断查找是否有新分段,有的话立即开始下载
|
||||||
|
while (isNewSeg() != true)
|
||||||
|
isNewSeg();
|
||||||
|
Record();
|
||||||
|
}
|
||||||
|
|
||||||
|
//检测是否有新分片
|
||||||
|
private bool isNewSeg()
|
||||||
|
{
|
||||||
|
if (toDownList.Count > 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
N_m3u8DL-CLI/HLSTags.cs
Normal file
38
N_m3u8DL-CLI/HLSTags.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace N_m3u8DL_CLI
|
||||||
|
{
|
||||||
|
class HLSTags
|
||||||
|
{
|
||||||
|
public static string ext_m3u = "#EXTM3U";
|
||||||
|
public static string ext_x_targetduration = "#EXT-X-TARGETDURATION";
|
||||||
|
public static string ext_x_media_sequence = "#EXT-X-MEDIA-SEQUENCE";
|
||||||
|
public static string ext_x_discontinuity_sequence = "#EXT-X-DISCONTINUITY-SEQUENCE";
|
||||||
|
public static string ext_x_program_date_time = "#EXT-X-PROGRAM-DATE-TIME";
|
||||||
|
public static string ext_x_media = "#EXT-X-MEDIA";
|
||||||
|
public static string ext_x_playlist_type = "#EXT-X-PLAYLIST-TYPE";
|
||||||
|
public static string ext_x_key = "#EXT-X-KEY";
|
||||||
|
public static string ext_x_stream_inf = "#EXT-X-STREAM-INF";
|
||||||
|
public static string ext_x_version = "#EXT-X-VERSION";
|
||||||
|
public static string ext_x_allow_cache = "#EXT-X-ALLOW-CACHE";
|
||||||
|
public static string ext_x_endlist = "#EXT-X-ENDLIST";
|
||||||
|
public static string extinf = "#EXTINF";
|
||||||
|
public static string ext_i_frames_only = "#EXT-X-I-FRAMES-ONLY";
|
||||||
|
public static string ext_x_byterange = "#EXT-X-BYTERANGE";
|
||||||
|
public static string ext_x_i_frame_stream_inf = "#EXT-X-I-FRAME-STREAM-INF";
|
||||||
|
public static string ext_x_discontinuity = "#EXT-X-DISCONTINUITY";
|
||||||
|
public static string ext_x_cue_out_start = "#EXT-X-CUE-OUT";
|
||||||
|
public static string ext_x_cue_out = "#EXT-X-CUE-OUT-CONT";
|
||||||
|
public static string ext_is_independent_segments = "#EXT-X-INDEPENDENT-SEGMENTS";
|
||||||
|
public static string ext_x_scte35 = "#EXT-OATCLS-SCTE35";
|
||||||
|
public static string ext_x_cue_start = "#EXT-X-CUE-OUT";
|
||||||
|
public static string ext_x_cue_end = "#EXT-X-CUE-IN";
|
||||||
|
public static string ext_x_cue_span = "#EXT-X-CUE-SPAN";
|
||||||
|
public static string ext_x_map = "#EXT-X-MAP";
|
||||||
|
public static string ext_x_start = "#EXT-X-START";
|
||||||
|
}
|
||||||
|
}
|
158
N_m3u8DL-CLI/LOGGER.cs
Normal file
158
N_m3u8DL-CLI/LOGGER.cs
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace N_m3u8DL_CLI
|
||||||
|
{
|
||||||
|
class LOGGER
|
||||||
|
{
|
||||||
|
public static int CursorIndex = 5;
|
||||||
|
public static int FFmpegCorsorIndex = 5;
|
||||||
|
public const int Default = 1;
|
||||||
|
public const int Error = 2;
|
||||||
|
public const int Warning = 3;
|
||||||
|
|
||||||
|
public static string LOGFILE;
|
||||||
|
public static bool STOPLOG = false;
|
||||||
|
public static string FindLog(string dir)
|
||||||
|
{
|
||||||
|
DirectoryInfo d = new DirectoryInfo(dir);
|
||||||
|
foreach (FileInfo fi in d.GetFiles())
|
||||||
|
{
|
||||||
|
if (fi.Extension.ToUpper() == ".LOG")
|
||||||
|
{
|
||||||
|
return fi.FullName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InitLog()
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(Path.GetDirectoryName(LOGFILE)))//若文件夹不存在则新建文件夹
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(LOGFILE)); //新建文件夹
|
||||||
|
if (File.Exists(LOGFILE))//若文件存在则删除
|
||||||
|
File.Delete(LOGFILE);
|
||||||
|
string file = LOGFILE;
|
||||||
|
string now = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
||||||
|
string init = "LOG " + DateTime.Now.ToString("yyyy/MM/dd") + "\r\n"
|
||||||
|
+ "Save Path: " + Path.GetDirectoryName(LOGFILE) + "\r\n"
|
||||||
|
+ "Task Start: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + "\r\n"
|
||||||
|
+ "Task CommandLine: " + Environment.CommandLine;
|
||||||
|
|
||||||
|
if (File.Exists(Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), "N_m3u8DL-CLI.args.txt")))
|
||||||
|
{
|
||||||
|
init += "\r\nAdditional Args: " + File.ReadAllText(Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), "N_m3u8DL-CLI.args.txt")); //解析命令行
|
||||||
|
}
|
||||||
|
|
||||||
|
init += "\r\n\r\n";
|
||||||
|
File.WriteAllText(file, init, Encoding.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
//读写锁机制,当资源被占用,其他线程等待
|
||||||
|
static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
|
||||||
|
|
||||||
|
public static void PrintLine(string text, int printLevel = 1, int cursorIndex = 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (cursorIndex == 0)
|
||||||
|
Console.SetCursorPosition(0, CursorIndex++);
|
||||||
|
else
|
||||||
|
Console.SetCursorPosition(0, cursorIndex);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
switch (printLevel)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
Console.WriteLine(" ".PadRight(12) + " " + text);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
|
||||||
|
Console.WriteLine(text);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.WriteLine(text);
|
||||||
|
Console.ResetColor();
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
|
||||||
|
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||||
|
Console.WriteLine(text);
|
||||||
|
Console.ResetColor();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteLine(string text)
|
||||||
|
{
|
||||||
|
if (STOPLOG)
|
||||||
|
return;
|
||||||
|
if (!File.Exists(LOGFILE))
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string file = LOGFILE;
|
||||||
|
//进入写入
|
||||||
|
LogWriteLock.EnterWriteLock();
|
||||||
|
using (StreamWriter sw = File.AppendText(file))
|
||||||
|
{
|
||||||
|
sw.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " / (NORMAL) " + text, Encoding.UTF8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
//释放占用
|
||||||
|
LogWriteLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteLineError(string text)
|
||||||
|
{
|
||||||
|
if (!File.Exists(LOGFILE))
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string file = LOGFILE;
|
||||||
|
//进入写入
|
||||||
|
LogWriteLock.EnterWriteLock();
|
||||||
|
using (StreamWriter sw = File.AppendText(file))
|
||||||
|
{
|
||||||
|
sw.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " / (ERROR) " + text, Encoding.UTF8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
//释放占用
|
||||||
|
LogWriteLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Show(string text)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.WriteLine(DateTime.Now.ToString("o") + " " + text);
|
||||||
|
while (Console.ForegroundColor == ConsoleColor.Red)
|
||||||
|
Console.ResetColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
N_m3u8DL-CLI/N_m3u8DL-CLI.csproj
Normal file
95
N_m3u8DL-CLI/N_m3u8DL-CLI.csproj
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{4FB61439-B738-46AC-B3AF-2BF72150D057}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<RootNamespace>N_m3u8DL_CLI</RootNamespace>
|
||||||
|
<AssemblyName>N_m3u8DL-CLI</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ApplicationIcon>logo_3Iv_icon.ico</ApplicationIcon>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.JScript" />
|
||||||
|
<Reference Include="Newtonsoft.Json" />
|
||||||
|
<Reference Include="PresentationFramework" />
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Collections" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.IO" />
|
||||||
|
<Reference Include="System.Web" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="CommandLineArgument.cs" />
|
||||||
|
<Compile Include="CommandLineArgumentParser.cs" />
|
||||||
|
<Compile Include="Decrypter.cs" />
|
||||||
|
<Compile Include="FFmpeg.cs" />
|
||||||
|
<Compile Include="Global.cs" />
|
||||||
|
<Compile Include="HLSLiveDownloader.cs" />
|
||||||
|
<Compile Include="HLSTags.cs" />
|
||||||
|
<Compile Include="LOGGER.cs" />
|
||||||
|
<Compile Include="DownloadManager.cs" />
|
||||||
|
<Compile Include="Parser.cs" />
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Downloader.cs" />
|
||||||
|
<Compile Include="Watcher.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="bin\Debug\Newtonsoft.Json.dll" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<COMReference Include="Scripting">
|
||||||
|
<Guid>{420B2830-E718-11CF-893D-00A0C9054228}</Guid>
|
||||||
|
<VersionMajor>1</VersionMajor>
|
||||||
|
<VersionMinor>0</VersionMinor>
|
||||||
|
<Lcid>0</Lcid>
|
||||||
|
<WrapperTool>tlbimp</WrapperTool>
|
||||||
|
<Isolated>False</Isolated>
|
||||||
|
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||||
|
</COMReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="logo_3Iv_icon.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
748
N_m3u8DL-CLI/Parser.cs
Normal file
748
N_m3u8DL-CLI/Parser.cs
Normal file
File diff suppressed because it is too large
Load Diff
653
N_m3u8DL-CLI/Program.cs
Normal file
653
N_m3u8DL-CLI/Program.cs
Normal file
File diff suppressed because it is too large
Load Diff
36
N_m3u8DL-CLI/Properties/AssemblyInfo.cs
Normal file
36
N_m3u8DL-CLI/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// 有关程序集的一般信息由以下
|
||||||
|
// 控制。更改这些特性值可修改
|
||||||
|
// 与程序集关联的信息。
|
||||||
|
[assembly: AssemblyTitle("N_m3u8DL-CLI")]
|
||||||
|
[assembly: AssemblyDescription("一款命令行m3u8下载器")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("nilaoda")]
|
||||||
|
[assembly: AssemblyProduct("N_m3u8DL-CLI")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2019")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// 将 ComVisible 设置为 false 会使此程序集中的类型
|
||||||
|
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
|
||||||
|
//请将此类型的 ComVisible 特性设置为 true。
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
|
||||||
|
[assembly: Guid("4fb61439-b738-46ac-b3af-2bf72150d057")]
|
||||||
|
|
||||||
|
// 程序集的版本信息由下列四个值组成:
|
||||||
|
//
|
||||||
|
// 主版本
|
||||||
|
// 次版本
|
||||||
|
// 生成号
|
||||||
|
// 修订号
|
||||||
|
//
|
||||||
|
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
|
||||||
|
// 方法是按如下所示使用“*”: :
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
104
N_m3u8DL-CLI/Watcher.cs
Normal file
104
N_m3u8DL-CLI/Watcher.cs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Timers;
|
||||||
|
|
||||||
|
namespace N_m3u8DL_CLI
|
||||||
|
{
|
||||||
|
class Watcher
|
||||||
|
{
|
||||||
|
private string dir = string.Empty;
|
||||||
|
private int total = 0;
|
||||||
|
private static double totalDuration = 0; //总时长
|
||||||
|
private int now = 0;
|
||||||
|
private int partsCount = 0;
|
||||||
|
FileSystemWatcher watcher = new FileSystemWatcher();
|
||||||
|
|
||||||
|
public int Total { get => total; set => total = value; }
|
||||||
|
public int Now { get => now; set => now = value; }
|
||||||
|
public int PartsCount { get => partsCount; set => partsCount = value; }
|
||||||
|
public static double TotalDuration { get => totalDuration; set => totalDuration = value; }
|
||||||
|
|
||||||
|
public Watcher(string Dir)
|
||||||
|
{
|
||||||
|
this.dir = Dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WatcherStrat()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < PartsCount; i++)
|
||||||
|
{
|
||||||
|
Now += Global.GetFileCount(dir + "\\Part_" + i.ToString(DownloadManager.partsPadZero), ".ts");
|
||||||
|
}
|
||||||
|
watcher.Path = dir;
|
||||||
|
watcher.Filter = "*.ts";
|
||||||
|
watcher.IncludeSubdirectories = true; //包括子目录
|
||||||
|
watcher.EnableRaisingEvents = true; //开启提交事件
|
||||||
|
watcher.Created += new FileSystemEventHandler(OnCreated);
|
||||||
|
watcher.Renamed += new RenamedEventHandler(OnCreated);
|
||||||
|
watcher.Deleted += new FileSystemEventHandler(OnDeleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WatcherStop()
|
||||||
|
{
|
||||||
|
watcher.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCreated(object source, FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
if (Path.GetFileNameWithoutExtension(e.FullPath).StartsWith("Part"))
|
||||||
|
return;
|
||||||
|
Now++;
|
||||||
|
if (Now > Total)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Console.Title = Now + " / " + Total;
|
||||||
|
string downloadedSize = Global.FormatFileSize(DownloadManager.DownloadedSize);
|
||||||
|
string estimatedSize = Global.FormatFileSize(DownloadManager.DownloadedSize * total / now);
|
||||||
|
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
|
||||||
|
Console.SetCursorPosition(0, 2);
|
||||||
|
Console.Write(("Progress: " + Now + " of " + Total
|
||||||
|
+ $" ({percent}/{downloadedSize}/{estimatedSize}/{Global.FormatTime(Convert.ToInt32(TotalDuration))})").PadRight(62));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRenamed(object source, RenamedEventArgs e)
|
||||||
|
{
|
||||||
|
if (Path.GetFileNameWithoutExtension(e.FullPath).StartsWith("Part"))
|
||||||
|
return;
|
||||||
|
Now++;
|
||||||
|
if (Now > Total)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Console.Title = Now + " / " + Total;
|
||||||
|
string downloadedSize = Global.FormatFileSize(DownloadManager.DownloadedSize);
|
||||||
|
string estimatedSize = Global.FormatFileSize(DownloadManager.DownloadedSize * total / now);
|
||||||
|
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
|
||||||
|
Console.SetCursorPosition(0, 2);
|
||||||
|
Console.Write(("Progress: " + Now + " of " + Total
|
||||||
|
+ $" ({percent}/{downloadedSize}/{estimatedSize}/{Global.FormatTime(Convert.ToInt32(TotalDuration))})").PadRight(62));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDeleted(object source, FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
if (Path.GetFileNameWithoutExtension(e.FullPath).StartsWith("Part"))
|
||||||
|
return;
|
||||||
|
Now--;
|
||||||
|
if (Now > Total)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Console.Title = Now + " / " + Total;
|
||||||
|
string downloadedSize = Global.FormatFileSize(DownloadManager.DownloadedSize);
|
||||||
|
string estimatedSize = Global.FormatFileSize(DownloadManager.DownloadedSize * total / now);
|
||||||
|
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
|
||||||
|
Console.SetCursorPosition(0, 2);
|
||||||
|
Console.Write(("Progress: " + Now + " of " + Total
|
||||||
|
+ $" ({percent}/{downloadedSize}/{estimatedSize}/{Global.FormatTime(Convert.ToInt32(TotalDuration))})").PadRight(62));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
N_m3u8DL-CLI/logo_3Iv_icon.ico
Normal file
BIN
N_m3u8DL-CLI/logo_3Iv_icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
676
docs/Advanced.html
Normal file
676
docs/Advanced.html
Normal file
File diff suppressed because it is too large
Load Diff
552
docs/GetM3u8.html
Normal file
552
docs/GetM3u8.html
Normal file
File diff suppressed because one or more lines are too long
505
docs/Introductory.html
Normal file
505
docs/Introductory.html
Normal file
File diff suppressed because it is too large
Load Diff
514
docs/LinetvParser.html
Normal file
514
docs/LinetvParser.html
Normal file
File diff suppressed because it is too large
Load Diff
508
docs/M3U8URL2File.html
Normal file
508
docs/M3U8URL2File.html
Normal file
File diff suppressed because it is too large
Load Diff
520
docs/SimpleGUI.html
Normal file
520
docs/SimpleGUI.html
Normal file
File diff suppressed because it is too large
Load Diff
515
docs/VikiParser.html
Normal file
515
docs/VikiParser.html
Normal file
File diff suppressed because it is too large
Load Diff
515
docs/ViuParser.html
Normal file
515
docs/ViuParser.html
Normal file
File diff suppressed because it is too large
Load Diff
520
docs/aliyunEdu.html
Normal file
520
docs/aliyunEdu.html
Normal file
File diff suppressed because it is too large
Load Diff
BIN
docs/gitbook/fonts/fontawesome/FontAwesome.otf
Normal file
BIN
docs/gitbook/fonts/fontawesome/FontAwesome.otf
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user