You've already forked N_m3u8DL-CLI
							
							
				mirror of
				https://github.com/nilaoda/N_m3u8DL-CLI
				synced 2025-10-31 18:00:51 +01:00 
			
		
		
		
	Compare commits
	
		
			86 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 | ||
|   | 377c575c72 | ||
|   | c02d737125 | ||
|   | c1fb395ee0 | ||
|   | 3920ae6178 | ||
|   | f96ea497b7 | ||
|   | 0013ec4bb5 | ||
|   | 604c25a2be | ||
|   | a83afd8908 | ||
|   | 206342c4cf | ||
|   | 296739910e | ||
|   | 5a62342145 | ||
|   | 64df11fefb | ||
|   | 5d82bc9135 | ||
|   | 2a61b61205 | ||
|   | 15568f917c | ||
|   | b196990972 | ||
|   | d4a94d185a | ||
|   | d865c05d9a | ||
|   | 0a8e5e40b8 | ||
|   | 27c88a5d18 | ||
|   | 92b4b4472c | ||
|   | 9aa44ee634 | 
							
								
								
									
										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 | 
							
								
								
									
										80
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,12 +1,47 @@ | |||||||
| # N_m3u8DL-CLI |  | ||||||
| 一个m3u8下载器 |  | ||||||
| ## 基本用法 |  | ||||||
| 双击exe输入m3u8链接或拖入m3u8文件按下回车键。 |  | ||||||
|  |  | ||||||
| ## 高级用法 |  | ||||||
| 使用命令行参数 |  | ||||||
| ``` | ``` | ||||||
| N_m3u8DL-CLI.exe <URL> [OPTIONS]   |  | ||||||
|  | ███╗   ██╗        ███╗   ███╗██████╗ ██╗   ██╗ █████╗ ██████╗ ██╗       ██████╗██╗     ██╗ | ||||||
|  | ████╗  ██║        ████╗ ████║╚════██╗██║   ██║██╔══██╗██╔══██╗██║      ██╔════╝██║     ██║ | ||||||
|  | ██╔██╗ ██║        ██╔████╔██║ █████╔╝██║   ██║╚█████╔╝██║  ██║██║█████╗██║     ██║     ██║ | ||||||
|  | ██║╚██╗██║        ██║╚██╔╝██║ ╚═══██╗██║   ██║██╔══██╗██║  ██║██║╚════╝██║     ██║     ██║ | ||||||
|  | ██║ ╚████║███████╗██║ ╚═╝ ██║██████╔╝╚██████╔╝╚█████╔╝██████╔╝███████╗ ╚██████╗███████╗██║ | ||||||
|  | ╚═╝  ╚═══╝╚══════╝╚═╝     ╚═╝╚═════╝  ╚═════╝  ╚════╝ ╚═════╝ ╚══════╝  ╚═════╝╚══════╝╚═╝ | ||||||
|  |                                                                                            | ||||||
|  | ``` | ||||||
|  | # 关于开源 | ||||||
|  | 本项目已与2019年10月9日开源,代码风格和管理真的很差劲,各取所需就好。 | ||||||
|  |  | ||||||
|  | # 关于跨平台 | ||||||
|  | 本项目已通过`.NET Core`实现跨平台,支持Linux、Windows,请移步:https://github.com/nilaoda/N_m3u8DL-CLI_Core | ||||||
|  |  | ||||||
|  | # N_m3u8DL-CLI | ||||||
|  | 一个**简单易用的**m3u8下载器,下载地址:https://github.com/nilaoda/N_m3u8DL-CLI/releases   | ||||||
|  |  | ||||||
|  | 支持下载m3u8链接或文件为`mp4`或`ts`格式,并提供丰富的命令行选项。 | ||||||
|  |   * 支持`AES-128`加密自动解密 | ||||||
|  |   * 支持多线程下载 | ||||||
|  |   * 支持下载限速 | ||||||
|  |   * 支持断点续传 | ||||||
|  |   * 支持`Master List` | ||||||
|  |   * 支持直播流录制(`BETA`) | ||||||
|  |   * 支持腾讯、爱奇艺、优酷的`杜比视界m3u8`下载 | ||||||
|  |   * 支持自定义`HTTP Headers` | ||||||
|  |   * 支持自动合并 (二进制合并或使用ffmpeg合并) | ||||||
|  |   * 支持选择下载`m3u8`中的指定时间段/分片内容 | ||||||
|  |   * 支持下载路径为网络驱动器的情况 | ||||||
|  |   * 支持下载外挂字幕轨道、音频轨道 | ||||||
|  |   * 支持仅合并为音频 | ||||||
|  |   * 自动使用系统代理(可禁止) | ||||||
|  |   * 针对国内视频网站`m3u8`进行了优化 | ||||||
|  |   * 提供SimpleG简易的`GUI`生成常用参数 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |    | ||||||
|  |  | ||||||
|  | # 命令行选项 | ||||||
|  | ``` | ||||||
|  | N_m3u8DL-CLI.exe <URL|JSON|FILE> [OPTIONS]   | ||||||
|  |  | ||||||
|     --workDir    Directory      设定程序工作目录 |     --workDir    Directory      设定程序工作目录 | ||||||
|     --saveName   Filename       设定存储文件名(不包括后缀) |     --saveName   Filename       设定存储文件名(不包括后缀) | ||||||
| @@ -15,20 +50,23 @@ N_m3u8DL-CLI.exe <URL> [OPTIONS] | |||||||
|     --maxThreads Thread         设定程序的最大线程数(默认为32) |     --maxThreads Thread         设定程序的最大线程数(默认为32) | ||||||
|     --minThreads Thread         设定程序的最小线程数(默认为16) |     --minThreads Thread         设定程序的最小线程数(默认为16) | ||||||
|     --retryCount Count          设定程序的重试次数(默认为15) |     --retryCount Count          设定程序的重试次数(默认为15) | ||||||
|  |     --timeOut    Sec            设定程序网络请求的超时时间(单位为秒,默认为10秒) | ||||||
|     --muxSetJson File           使用外部json文件定义混流选项 |     --muxSetJson File           使用外部json文件定义混流选项 | ||||||
|  |     --useKeyFile File           使用外部16字节文件定义AES-128解密KEY | ||||||
|  |     --useKeyBase64 Base64String 使用Base64字符串定义AES-128解密KEY | ||||||
|  |     --downloadRange Range       仅下载视频的一部分分片或长度 | ||||||
|  |     --stopSpeed  Number         当速度低于此值时,重试(单位为KB/s) | ||||||
|  |     --maxSpeed   Number         设置下载速度上限(单位为KB/s) | ||||||
|     --enableDelAfterDone        开启下载后删除临时文件夹的功能 |     --enableDelAfterDone        开启下载后删除临时文件夹的功能 | ||||||
|     --enableMuxFastStart        开启混流mp4的FastStart特性 |     --enableMuxFastStart        开启混流mp4的FastStart特性 | ||||||
|  |     --enableBinaryMerge         开启二进制合并分片 | ||||||
|  |     --enableParseOnly           开启仅解析模式(程序只进行到meta.json) | ||||||
|  |     --enableAudioOnly           合并时仅封装音频轨道 | ||||||
|  |     --disableDateInfo           关闭混流中的日期写入 | ||||||
|  |     --noMerge                   禁用自动合并 | ||||||
|  |     --noProxy                   不自动使用系统代理 | ||||||
|  |     --disableIntegrityCheck     不检测分片数量是否完整 | ||||||
|  | ``` | ||||||
|  |  | ||||||
| ``` | # 用户文档 | ||||||
| 附录:一个典型的混流Json文件应该长这个样子: | https://nilaoda.github.io/N_m3u8DL-CLI/ | ||||||
| ``` |  | ||||||
|     { |  | ||||||
|         "muxFormat": "mp4",  |  | ||||||
|         "fastStart": "True",  |  | ||||||
|         "poster": "文件路径,\需写为\\",  |  | ||||||
|         "audioName": "",  |  | ||||||
|         "title": "",  |  | ||||||
|         "copyright": "",  |  | ||||||
|         "comment": "" |  | ||||||
|     } |  | ||||||
| ``` |  | ||||||
|   | |||||||
							
								
								
									
										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