You've already forked N_m3u8DL-CLI
mirror of
https://github.com/nilaoda/N_m3u8DL-CLI
synced 2025-09-05 23:29:33 +02:00
Compare commits
309 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
985f6e57c3 | ||
![]() |
d9eea2e80f | ||
![]() |
0cc4a87a4c | ||
![]() |
66d0864d72 | ||
![]() |
965ac2b513 | ||
![]() |
a95334ec57 | ||
![]() |
e05a21a034 | ||
![]() |
13cd5d0870 | ||
![]() |
8d9ad7af41 | ||
![]() |
0a11816acf | ||
![]() |
81ba4ff7d3 | ||
![]() |
f5a9ed08a1 | ||
![]() |
6b8bfde19f | ||
![]() |
ff738f2849 | ||
![]() |
3d35c24a2e | ||
![]() |
41d7151e7e | ||
![]() |
94246ef163 | ||
![]() |
9c118631cf | ||
![]() |
2ee9ab234f | ||
![]() |
606f2184df | ||
![]() |
7dd2b6cee2 | ||
![]() |
f744aa990e | ||
![]() |
97c9f444b8 | ||
![]() |
8aac37bbe2 | ||
![]() |
b7e19ec699 | ||
![]() |
7e7c5c6ba9 | ||
![]() |
50722079ba | ||
![]() |
d529ece032 | ||
![]() |
9ed8b359a1 | ||
![]() |
a7591e3978 | ||
![]() |
094a89bc79 | ||
![]() |
9e7b603d3b | ||
![]() |
3801090928 | ||
![]() |
42c1d628d4 | ||
![]() |
ecefecd827 | ||
![]() |
4661818c1f | ||
![]() |
fa61cd136d | ||
![]() |
472fb0cf02 | ||
![]() |
28e39a94ac | ||
![]() |
a42ade64f3 | ||
![]() |
539f773e37 | ||
![]() |
d6f27cf90f | ||
![]() |
8912ee5e4b | ||
![]() |
409437b2c7 | ||
![]() |
350d3b508d | ||
![]() |
1e3f06eaae | ||
![]() |
2d4bc42d55 | ||
![]() |
68e06d3fa7 | ||
![]() |
6c3340e94e | ||
![]() |
051faa9f2b | ||
![]() |
115f9c9424 | ||
![]() |
faf67cd527 | ||
![]() |
38d1a1a2dc | ||
![]() |
12eb68d592 | ||
![]() |
0804e295e5 | ||
![]() |
847c4683cb | ||
![]() |
8c72947860 | ||
![]() |
f0b240a6ee | ||
![]() |
793cf53042 | ||
![]() |
612fc29197 | ||
![]() |
307e2389de | ||
![]() |
1c932abdc3 | ||
![]() |
314f0065c7 | ||
![]() |
59060bb74d | ||
![]() |
cab882c3a3 | ||
![]() |
9955532ce5 | ||
![]() |
7e127be8c2 | ||
![]() |
b46571a57f | ||
![]() |
da5861d907 | ||
![]() |
92bc91a1fb | ||
![]() |
439f50103e | ||
![]() |
8a95e31b2f | ||
![]() |
115b8a156a | ||
![]() |
120bcaebb5 | ||
![]() |
455d56707c | ||
![]() |
048adcf118 | ||
![]() |
fe5aa27b1c | ||
![]() |
039aa489b1 | ||
![]() |
14e80f0b06 | ||
![]() |
2256fff549 | ||
![]() |
84cfd4e138 | ||
![]() |
e70c229135 | ||
![]() |
8b520d0c19 | ||
![]() |
71d69de51a | ||
![]() |
bc89ead00d | ||
![]() |
ae79d6eb3a | ||
![]() |
96bd8af883 | ||
![]() |
89b1e30e0f | ||
![]() |
7a741359ab | ||
![]() |
564b6ad291 | ||
![]() |
a0fc9404f7 | ||
![]() |
5267be1699 | ||
![]() |
20bfda39e7 | ||
![]() |
ba4c0eeda7 | ||
![]() |
5d72e24002 | ||
![]() |
a87c051d23 | ||
![]() |
44e1b68d6b | ||
![]() |
e65dfa52cd | ||
![]() |
965c173899 | ||
![]() |
880af02cc2 | ||
![]() |
2742de43c4 | ||
![]() |
9d8cb57390 | ||
![]() |
9e2a192dab | ||
![]() |
33cf9e2256 | ||
![]() |
2959cbbb5c | ||
![]() |
c2eb8a6adc | ||
![]() |
e1b591b81c | ||
![]() |
334b1939b5 | ||
![]() |
7e916b65fd | ||
![]() |
4ead563fa2 | ||
![]() |
1b387a06e5 | ||
![]() |
6e7b4ac7ea | ||
![]() |
e98c5205d1 | ||
![]() |
d7890dd124 | ||
![]() |
82f2111522 | ||
![]() |
4c3207586f | ||
![]() |
69b411e37c | ||
![]() |
1e8525041f | ||
![]() |
65ae72d4a4 | ||
![]() |
4a4bfae5ab | ||
![]() |
d586dddfcd | ||
![]() |
fca6b3ff6c | ||
![]() |
5d75626a36 | ||
![]() |
a94271c244 | ||
![]() |
c51118dce7 | ||
![]() |
81b2e87bf7 | ||
![]() |
71a9878aaa | ||
![]() |
769fe4e926 | ||
![]() |
1f57ba7c09 | ||
![]() |
71282bda30 | ||
![]() |
41ee8aebdf | ||
![]() |
a4537bc093 | ||
![]() |
b8a60b3917 | ||
![]() |
8091dd290f | ||
![]() |
d48e84e611 | ||
![]() |
9f5423a437 | ||
![]() |
ce7e38770a | ||
![]() |
8fdb2e918e | ||
![]() |
d4b7d240c1 | ||
![]() |
484d2941ed | ||
![]() |
a0f2b66575 | ||
![]() |
65cc0681e2 | ||
![]() |
7d980ec9a2 | ||
![]() |
ec5892c05a | ||
![]() |
9aed50fbf9 | ||
![]() |
d657b455cd | ||
![]() |
cda6575605 | ||
![]() |
0d6377d41b | ||
![]() |
9c76bdcbce | ||
![]() |
83915ff606 | ||
![]() |
9cd4746f33 | ||
![]() |
33a5b917ac | ||
![]() |
f69978bd82 | ||
![]() |
d9fd526886 | ||
![]() |
1cc8ecfaaf | ||
![]() |
2bc2dde2ad | ||
![]() |
c3ddcf9e0e | ||
![]() |
ba5d20dd02 | ||
![]() |
16f705fe66 | ||
![]() |
d141cabc4a | ||
![]() |
adcf884a93 | ||
![]() |
9d903a025f | ||
![]() |
0bd23ab641 | ||
![]() |
a38f27ccd7 | ||
![]() |
3acec5efd3 | ||
![]() |
90874e4bfe | ||
![]() |
b94768e3e8 | ||
![]() |
311f3b882e | ||
![]() |
4b4f537984 | ||
![]() |
8032d50b42 | ||
![]() |
f615764e55 | ||
![]() |
ccaa200ef8 | ||
![]() |
6058d878eb | ||
![]() |
c712c6dee0 | ||
![]() |
bb24bb998f | ||
![]() |
e9d951efa5 | ||
![]() |
6f88a805ef | ||
![]() |
6aa6d63a8d | ||
![]() |
147246caba | ||
![]() |
35c1ee4777 | ||
![]() |
8b32081b85 | ||
![]() |
c00de328d1 | ||
![]() |
d5193c1645 | ||
![]() |
c06fbf5820 | ||
![]() |
e700edba56 | ||
![]() |
aad948da7c | ||
![]() |
14f7b20176 | ||
![]() |
a66a9a4096 | ||
![]() |
c862f23a9c | ||
![]() |
1a722e80de | ||
![]() |
eff43e8ac3 | ||
![]() |
7648f8f8dc | ||
![]() |
1edc1a43fe | ||
![]() |
01e7735018 | ||
![]() |
a0c41d6116 | ||
![]() |
c3c25774de | ||
![]() |
ba40641a21 | ||
![]() |
5d76418780 | ||
![]() |
fd48b766b5 | ||
![]() |
0f25cc0ae8 | ||
![]() |
1c6bd688e3 | ||
![]() |
480857cc3b | ||
![]() |
52af9a44a8 | ||
![]() |
84d137b504 | ||
![]() |
a4f1064c81 | ||
![]() |
eac08b12f8 | ||
![]() |
c81c911888 | ||
![]() |
361e901e5b | ||
![]() |
3567f4c4cc | ||
![]() |
2f7b0f7304 | ||
![]() |
000d0db4ae | ||
![]() |
d551ad52c1 | ||
![]() |
c064c83c21 | ||
![]() |
f6ed0f9e4d | ||
![]() |
f65fee94ca | ||
![]() |
ce64a92b0d | ||
![]() |
42790ce540 | ||
![]() |
83d8ca1c8c | ||
![]() |
4b5a64eb98 | ||
![]() |
2b5af09c3b | ||
![]() |
6368adc2ab | ||
![]() |
fcd7840091 | ||
![]() |
d77cb62dff | ||
![]() |
34394c6a2b | ||
![]() |
091cba8555 | ||
![]() |
b02b6b7168 | ||
![]() |
b7408b0599 | ||
![]() |
b83cb35170 | ||
![]() |
ed0a7b71a7 | ||
![]() |
50eae19bf3 | ||
![]() |
cba8f3ea52 | ||
![]() |
7fd93e1232 | ||
![]() |
74a7e3c3ec | ||
![]() |
849d712e11 | ||
![]() |
7544f3a02c | ||
![]() |
0120736c53 | ||
![]() |
e4bde4926c | ||
![]() |
d42cd6a60d | ||
![]() |
175f13adc9 | ||
![]() |
72f1c043b1 | ||
![]() |
a2e2070f5d | ||
![]() |
6c96deb366 | ||
![]() |
2bd900ee5d | ||
![]() |
1261810510 | ||
![]() |
26823dbd7e | ||
![]() |
698699d9fc | ||
![]() |
ebed7fa1e3 | ||
![]() |
73a8348155 | ||
![]() |
5ace0b3a4f | ||
![]() |
5abe889da0 | ||
![]() |
f1070fd1b4 | ||
![]() |
4af82cc7f9 | ||
![]() |
809380b7ab | ||
![]() |
ab57420507 | ||
![]() |
80230f12fe | ||
![]() |
a6c7c0fd8c | ||
![]() |
bd6df6b58c | ||
![]() |
e0a9071d62 | ||
![]() |
5c9bcf72d2 | ||
![]() |
7cf2c12d0c | ||
![]() |
1b35fe2d2c | ||
![]() |
ed3aae1cb9 | ||
![]() |
01c2ecbeb5 | ||
![]() |
9993ec8177 | ||
![]() |
464300c860 | ||
![]() |
45fa58a46f | ||
![]() |
f93ddc7107 | ||
![]() |
05f450fa6d | ||
![]() |
580c374d7b | ||
![]() |
b58dc5d8e0 | ||
![]() |
f50cf7862a | ||
![]() |
fdad68d483 | ||
![]() |
d7aaa5323b | ||
![]() |
6c2e13b800 | ||
![]() |
086fc57958 | ||
![]() |
bc349b8977 | ||
![]() |
cc4efed3c6 | ||
![]() |
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 |
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
|
13
.github/FUNDING.yml
vendored
Normal file
13
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: ['https://nilaoda.github.io/N_m3u8DL-CLI/source/images/alipay.png','https://www.buymeacoffee.com/nilaoda']
|
34
.github/workflows/build_latest.yml
vendored
Normal file
34
.github/workflows/build_latest.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Build_Latest
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout Code
|
||||
|
||||
- name: Setup MSBuild Path
|
||||
uses: warrenbuckley/Setup-MSBuild@v1
|
||||
env:
|
||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
|
||||
|
||||
- name: Setup NuGet
|
||||
uses: NuGet/setup-nuget@v1.0.2
|
||||
env:
|
||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
|
||||
|
||||
- name: Restore NuGet Packages
|
||||
run: nuget restore N_m3u8DL-CLI.sln
|
||||
|
||||
- name: Build
|
||||
run: msbuild N_m3u8DL-CLI.sln /p:Configuration=Release /p:DebugSymbols=false /p:DebugType=None
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v1.0.0
|
||||
with:
|
||||
name: N_m3u8DL-CLI_latest
|
||||
path: N_m3u8DL-CLI\bin\Release\N_m3u8DL-CLI.exe
|
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.
|
25
N_m3u8DL-CLI.sln
Normal file
25
N_m3u8DL-CLI.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29215.179
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "N_m3u8DL-CLI", "N_m3u8DL-CLI\N_m3u8DL-CLI.csproj", "{4FB61439-B738-46AC-B3AF-2BF72150D057}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {82B9270D-B7B2-4591-BF8A-5B4EBCD0EA8A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
6
N_m3u8DL-CLI/App.config
Normal file
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>
|
661
N_m3u8DL-CLI/CSChaCha20.cs
Normal file
661
N_m3u8DL-CLI/CSChaCha20.cs
Normal file
File diff suppressed because it is too large
Load Diff
318
N_m3u8DL-CLI/Decode51CtoKey.cs
Normal file
318
N_m3u8DL-CLI/Decode51CtoKey.cs
Normal file
File diff suppressed because one or more lines are too long
83
N_m3u8DL-CLI/DecodeCdeledu.cs
Normal file
83
N_m3u8DL-CLI/DecodeCdeledu.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using NiL.JS.BaseLibrary;
|
||||
using NiL.JS.Core;
|
||||
using NiL.JS.Extensions;
|
||||
using System;
|
||||
using Array = System.Array;
|
||||
|
||||
namespace N_m3u8DL_CLI
|
||||
{
|
||||
internal class DecodeCdeledu
|
||||
{
|
||||
private static string JS = @"
|
||||
var _keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
|
||||
var removePaddingChars = function(input) {
|
||||
var lkey = _keyStr.indexOf(input.charAt(input.length - 1));
|
||||
if (lkey == 64) {
|
||||
return input.substring(0, input.length - 1);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
var base64Decode = function(input, arrayBuffer) {
|
||||
input = removePaddingChars(input);
|
||||
input = removePaddingChars(input);
|
||||
var bytes = parseInt((input.length / 4) * 3, 10);
|
||||
var uarray;
|
||||
var chr1, chr2, chr3;
|
||||
var enc1, enc2, enc3, enc4;
|
||||
var i = 0;
|
||||
var j = 0;
|
||||
if (arrayBuffer) {
|
||||
uarray = new Uint8Array(arrayBuffer);
|
||||
} else {
|
||||
uarray = new Uint8Array(bytes);
|
||||
}
|
||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
|
||||
for (i = 0; i < bytes; i += 3) {
|
||||
enc1 = _keyStr.indexOf(input.charAt(j++));
|
||||
enc2 = _keyStr.indexOf(input.charAt(j++));
|
||||
enc3 = _keyStr.indexOf(input.charAt(j++));
|
||||
enc4 = _keyStr.indexOf(input.charAt(j++));
|
||||
chr1 = (enc1 << 2) | (enc2 >> 4);
|
||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
||||
chr3 = ((enc3 & 3) << 6) | enc4;
|
||||
uarray[i] = chr1;
|
||||
if (enc3 != 64)
|
||||
uarray[i + 1] = chr2;
|
||||
if (enc4 != 64)
|
||||
uarray[i + 2] = chr3;
|
||||
}
|
||||
return uarray;
|
||||
}
|
||||
|
||||
var uint8ArrayToString = function(uDataArr) {
|
||||
var arrStr = '';
|
||||
for (var i = 0; i < uDataArr.length; i++) {
|
||||
arrStr += String.fromCharCode(uDataArr[i]);
|
||||
}
|
||||
return arrStr;
|
||||
}
|
||||
|
||||
var decodeKey = function(dataKeyString) {
|
||||
var decodeArr = base64Decode(dataKeyString);
|
||||
var decodeArrString = uint8ArrayToString(decodeArr);
|
||||
return decodeArrString;
|
||||
if (decodeArrString.indexOf('|&|') > 0) {
|
||||
return decodeArrString;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
";
|
||||
//https://video.cdeledu.com/js/lib/cdel.hls.min-1.0.js?v=1.3
|
||||
public static string DecodeKey(string txt)
|
||||
{
|
||||
var context = new Context();
|
||||
context.Eval(JS);
|
||||
var concatFunction = context.GetVariable("decodeKey").As<Function>();
|
||||
string key = concatFunction.Call(new Arguments { txt }).ToString();
|
||||
string realKey = key.Split(new string[] { "|&|" }, StringSplitOptions.None)[1];
|
||||
return realKey;
|
||||
}
|
||||
}
|
||||
}
|
41
N_m3u8DL-CLI/DecodeDdyun.cs
Normal file
41
N_m3u8DL-CLI/DecodeDdyun.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace N_m3u8DL_CLI
|
||||
{
|
||||
class DecodeDdyun
|
||||
{
|
||||
public static string DecryptM3u8(byte[] byteArray)
|
||||
{
|
||||
string tmp = DecodeNfmovies.DecryptM3u8(byteArray);
|
||||
if (tmp.StartsWith("duoduo.key"))
|
||||
{
|
||||
tmp = Regex.Replace(tmp, @"#EXT-X-BYTERANGE:.*\s", "");
|
||||
tmp = tmp.Replace("https:", "jump/https:")
|
||||
.Replace("inews.gtimg.com", "puui.qpic.cn");
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
//https://player.ddyunp.com/jQuery.min.js?v1.5
|
||||
public static string GetVaildM3u8Url(string url)
|
||||
{
|
||||
//url: https://hls.ddyunp.com/ddyun/id/1/key/playlist.m3u8
|
||||
string id = Regex.Match(url, @"\w{20,}").Value;
|
||||
string tm = Global.GetTimeStamp(false);
|
||||
string t = ((long.Parse(tm) / 0x186a0) * 0x64).ToString();
|
||||
string tmp = id + "duoduo" + "1" + t;
|
||||
MD5 md5 = MD5.Create();
|
||||
byte[] bs = Encoding.UTF8.GetBytes(tmp);
|
||||
byte[] hs = md5.ComputeHash(bs);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (byte b in hs)
|
||||
{
|
||||
sb.Append(b.ToString("x2"));
|
||||
}
|
||||
string key = sb.ToString();
|
||||
return Regex.Replace(url, @"1/\w{20,}", "1/" + key);
|
||||
}
|
||||
}
|
||||
}
|
53
N_m3u8DL-CLI/DecodeHuke88Key.cs
Normal file
53
N_m3u8DL-CLI/DecodeHuke88Key.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace N_m3u8DL_CLI
|
||||
{
|
||||
//https://js.huke88.com/assets/revision/js/plugins/tcplayer/tcplayer.v4.1.min.js?v=930
|
||||
//https://js.huke88.com/assets/revision/js/plugins/tcplayer/libs/hls.min.0.13.2m.js?v=930
|
||||
class DecodeHuke88Key
|
||||
{
|
||||
private static string[] GetOverlayInfo(string url)
|
||||
{
|
||||
var enc = new Regex("eyJ\\w{100,}").Match(url).Value;
|
||||
var json = Encoding.UTF8.GetString(Convert.FromBase64String(enc));
|
||||
JObject jObject = JObject.Parse(json);
|
||||
var key = jObject["overlayKey"].ToString();
|
||||
var iv = jObject["overlayIv"].ToString();
|
||||
return new string[] { key, iv };
|
||||
}
|
||||
|
||||
public static string DecodeKey(string url, byte[] data)
|
||||
{
|
||||
var info = GetOverlayInfo(url);
|
||||
var overlayKey = info[0];
|
||||
var overlayIv = info[1];
|
||||
var l = new List<byte>();
|
||||
var c = new List<byte>();
|
||||
for (int h = 0; h < 16; h++)
|
||||
{
|
||||
var f = overlayKey.Substring(2 * h, 2);
|
||||
var g = overlayIv.Substring(2 * h, 2);
|
||||
l.Add(Convert.ToByte(f, 16));
|
||||
c.Add(Convert.ToByte(g, 16));
|
||||
}
|
||||
|
||||
var _lastCipherblock = c.ToArray();
|
||||
|
||||
var t = new byte[data.Length];
|
||||
var r = data;
|
||||
r = Decrypter.AES128Decrypt(data, l.ToArray(), Decrypter.HexStringToBytes("00000000000000000000000000000000"), CipherMode.CBC, PaddingMode.Zeros);
|
||||
|
||||
for (var o = 0; o < 16; o++)
|
||||
t[o] = (byte)(r[o] ^ _lastCipherblock[o]);
|
||||
|
||||
var key = Convert.ToBase64String(t);
|
||||
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
203
N_m3u8DL-CLI/DecodeImooc.cs
Normal file
203
N_m3u8DL-CLI/DecodeImooc.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using NiL.JS.BaseLibrary;
|
||||
using NiL.JS.Core;
|
||||
using NiL.JS.Extensions;
|
||||
using System;
|
||||
using Array = System.Array;
|
||||
|
||||
namespace N_m3u8DL_CLI
|
||||
{
|
||||
/*
|
||||
* js代码来自:https://www.imooc.com/static/moco/player/3.0.6.3/mocoplayer.js?v=202006122046
|
||||
*
|
||||
*/
|
||||
class DecodeImooc
|
||||
{
|
||||
private static string JS = @"
|
||||
function n(t, e) {
|
||||
function r(t, e) {
|
||||
var r = '';
|
||||
if ('object' == typeof t)
|
||||
for (var n = 0; n < t.length; n++)
|
||||
r += String.fromCharCode(t[n]);
|
||||
t = r || t;
|
||||
for (var i, o, a = new Uint8Array(t.length), s = e.length, n = 0; n < t.length; n++)
|
||||
o = n % s,
|
||||
i = t[n],
|
||||
i = i.toString().charCodeAt(0),
|
||||
a[n] = i ^ e.charCodeAt(o);
|
||||
return a
|
||||
}
|
||||
function n(t) {
|
||||
var e = '';
|
||||
if ('object' == typeof t)
|
||||
for (var r = 0; r < t.length; r++)
|
||||
e += String.fromCharCode(t[r]);
|
||||
t = e || t;
|
||||
var n = new Uint8Array(t.length);
|
||||
for (r = 0; r < t.length; r++)
|
||||
n[r] = t[r].toString().charCodeAt(0);
|
||||
var i, o, r = 0;
|
||||
for (r = 0; r < n.length; r++)
|
||||
0 != (i = n[r] % 3) && r + i < n.length && (o = n[r + 1],
|
||||
n[r + 1] = n[r + i],
|
||||
n[r + i] = o,
|
||||
r = r + i + 1);
|
||||
return n
|
||||
}
|
||||
function i(t) {
|
||||
var e = '';
|
||||
if ('object' == typeof t)
|
||||
for (var r = 0; r < t.length; r++)
|
||||
e += String.fromCharCode(t[r]);
|
||||
t = e || t;
|
||||
var n = new Uint8Array(t.length);
|
||||
for (r = 0; r < t.length; r++)
|
||||
n[r] = t[r].toString().charCodeAt(0);
|
||||
var r = 0
|
||||
, i = 0
|
||||
, o = 0
|
||||
, a = 0;
|
||||
for (r = 0; r < n.length; r++)
|
||||
o = n[r] % 2,
|
||||
o && r++,
|
||||
a++;
|
||||
var s = new Uint8Array(a);
|
||||
for (r = 0; r < n.length; r++)
|
||||
o = n[r] % 2,
|
||||
s[i++] = o ? n[r++] : n[r];
|
||||
return s
|
||||
}
|
||||
function o(t, e) {
|
||||
var r = 0
|
||||
, n = 0
|
||||
, i = 0
|
||||
, o = 0
|
||||
, a = '';
|
||||
if ('object' == typeof t)
|
||||
for (var r = 0; r < t.length; r++)
|
||||
a += String.fromCharCode(t[r]);
|
||||
t = a || t;
|
||||
var s = new Uint8Array(t.length);
|
||||
for (r = 0; r < t.length; r++)
|
||||
s[r] = t[r].toString().charCodeAt(0);
|
||||
for (r = 0; r < t.length; r++)
|
||||
if (0 != (o = s[r] % 5) && 1 != o && r + o < s.length && (i = s[r + 1],
|
||||
n = r + 2,
|
||||
s[r + 1] = s[r + o],
|
||||
s[o + r] = i,
|
||||
(r = r + o + 1) - 2 > n))
|
||||
for (; n < r - 2; n++)
|
||||
s[n] = s[n] ^ e.charCodeAt(n % e.length);
|
||||
for (r = 0; r < t.length; r++)
|
||||
s[r] = s[r] ^ e.charCodeAt(r % e.length);
|
||||
return s
|
||||
}
|
||||
for (var a = {
|
||||
data: {
|
||||
info: t
|
||||
}
|
||||
}, s = {
|
||||
q: r,
|
||||
h: n,
|
||||
m: i,
|
||||
k: o
|
||||
}, l = a.data.info, u = l.substring(l.length - 4).split(''), c = 0; c < u.length; c++)
|
||||
u[c] = u[c].toString().charCodeAt(0) % 4;
|
||||
u.reverse();
|
||||
for (var d = [], c = 0; c < u.length; c++)
|
||||
d.push(l.substring(u[c] + 1, u[c] + 2)),
|
||||
l = l.substring(0, u[c] + 1) + l.substring(u[c] + 2);
|
||||
a.data.encrypt_table = d,
|
||||
a.data.key_table = [];
|
||||
for (var c in a.data.encrypt_table)
|
||||
'q' != a.data.encrypt_table[c] && 'k' != a.data.encrypt_table[c] || (a.data.key_table.push(l.substring(l.length - 12)),
|
||||
l = l.substring(0, l.length - 12));
|
||||
a.data.key_table.reverse(),
|
||||
a.data.info = l;
|
||||
var f = new Array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1);
|
||||
a.data.info = function(t) {
|
||||
var e, r, n, i, o, a, s;
|
||||
for (a = t.length,
|
||||
o = 0,
|
||||
s = ''; o < a; ) {
|
||||
do {
|
||||
e = f[255 & t.charCodeAt(o++)]
|
||||
} while (o < a && -1 == e);if (-1 == e)
|
||||
break;
|
||||
do {
|
||||
r = f[255 & t.charCodeAt(o++)]
|
||||
} while (o < a && -1 == r);if (-1 == r)
|
||||
break;
|
||||
s += String.fromCharCode(e << 2 | (48 & r) >> 4);
|
||||
do {
|
||||
if (61 == (n = 255 & t.charCodeAt(o++)))
|
||||
return s;
|
||||
n = f[n]
|
||||
} while (o < a && -1 == n);if (-1 == n)
|
||||
break;
|
||||
s += String.fromCharCode((15 & r) << 4 | (60 & n) >> 2);
|
||||
do {
|
||||
if (61 == (i = 255 & t.charCodeAt(o++)))
|
||||
return s;
|
||||
i = f[i]
|
||||
} while (o < a && -1 == i);if (-1 == i)
|
||||
break;
|
||||
s += String.fromCharCode((3 & n) << 6 | i)
|
||||
}
|
||||
return s
|
||||
}(a.data.info);
|
||||
for (var c in a.data.encrypt_table) {
|
||||
var h = a.data.encrypt_table[c];
|
||||
if ('q' == h || 'k' == h) {
|
||||
var p = a.data.key_table.pop();
|
||||
a.data.info = s[a.data.encrypt_table[c]](a.data.info, p)
|
||||
} else
|
||||
a.data.info = s[a.data.encrypt_table[c]](a.data.info)
|
||||
}
|
||||
if (e)
|
||||
return a.data.info;
|
||||
var g = '';
|
||||
for (c = 0; c < a.data.info.length; c++)
|
||||
g += String.fromCharCode(a.data.info[c]);
|
||||
return g
|
||||
}
|
||||
function Uint8ArrayToString(fileData){
|
||||
var dataString = '';
|
||||
for (var i = 0; i < fileData.length; i++) {
|
||||
dataString += Number(fileData[i]) + ',';
|
||||
}
|
||||
return dataString;
|
||||
}
|
||||
function decodeKey(resp){
|
||||
var string = eval('('+resp+')');
|
||||
//return btoa(String.fromCharCode.apply(null, new Uint8Array(n(string.data.info, 1))));
|
||||
return Uint8ArrayToString(new Uint8Array(n(string.data.info, 1)));
|
||||
}
|
||||
function decodeM3u8(resp){
|
||||
var string = eval('('+resp+')');
|
||||
return n(string.data.info);
|
||||
}
|
||||
";
|
||||
|
||||
|
||||
public static string DecodeM3u8(string resp)
|
||||
{
|
||||
var context = new Context();
|
||||
context.Eval(JS);
|
||||
var concatFunction = context.GetVariable("decodeM3u8").As<Function>();
|
||||
string m3u8 = concatFunction.Call(new Arguments { resp }).ToString();
|
||||
return m3u8;
|
||||
}
|
||||
|
||||
public static string DecodeKey(string resp)
|
||||
{
|
||||
var context = new Context();
|
||||
context.Eval(JS);
|
||||
var concatFunction = context.GetVariable("decodeKey").As<Function>();
|
||||
string key = concatFunction.Call(new Arguments { resp }).ToString();
|
||||
byte[] v = Array.ConvertAll(key.Trim(',').Split(','), s => (byte)int.Parse(s));
|
||||
string realKey = Convert.ToBase64String(v);
|
||||
return realKey;
|
||||
}
|
||||
}
|
||||
}
|
36
N_m3u8DL-CLI/DecodeNfmovies.cs
Normal file
36
N_m3u8DL-CLI/DecodeNfmovies.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace N_m3u8DL_CLI
|
||||
{
|
||||
class DecodeNfmovies
|
||||
{
|
||||
//https://jx.nfmovies.com/hls.min.js
|
||||
public static string DecryptM3u8(byte[] byteArray)
|
||||
{
|
||||
var t = byteArray;
|
||||
var decrypt = "";
|
||||
if (137 == t[0] && 80 == t[1] && 130 == t[354] && 96 == t[353]) t = t.Skip(355).ToArray();
|
||||
else
|
||||
{
|
||||
if (137 != t[0] || 80 != t[1] || 130 != t[394] || 96 != t[393])
|
||||
{
|
||||
for (var i = 0; i < t.Length; i++) decrypt += Convert.ToChar(t[i]);
|
||||
return decrypt;
|
||||
}
|
||||
t = t.Skip(395).ToArray();
|
||||
}
|
||||
using (var zipStream =
|
||||
new System.IO.Compression.GZipStream(new MemoryStream(t), System.IO.Compression.CompressionMode.Decompress))
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(zipStream, Encoding.UTF8))
|
||||
{
|
||||
decrypt = sr.ReadToEnd();
|
||||
}
|
||||
}
|
||||
return decrypt;
|
||||
}
|
||||
}
|
||||
}
|
114
N_m3u8DL-CLI/Decrypter.cs
Normal file
114
N_m3u8DL-CLI/Decrypter.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using CSChaCha20;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace N_m3u8DL_CLI
|
||||
{
|
||||
class Decrypter
|
||||
{
|
||||
public static byte[] AES128Decrypt(string filePath, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
|
||||
{
|
||||
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();
|
||||
dcpt.BlockSize = 128;
|
||||
dcpt.KeySize = 128;
|
||||
dcpt.Key = keyByte;
|
||||
dcpt.IV = ivByte;
|
||||
dcpt.Mode = mode;
|
||||
dcpt.Padding = padding;
|
||||
|
||||
ICryptoTransform cTransform = dcpt.CreateDecryptor();
|
||||
Byte[] resultArray = cTransform.TransformFinalBlock(inBuff, 0, inBuff.Length);
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
public static byte[] AES128Decrypt(byte[] encryptedBuff, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
|
||||
{
|
||||
byte[] inBuff = encryptedBuff;
|
||||
|
||||
Aes dcpt = Aes.Create();
|
||||
dcpt.BlockSize = 128;
|
||||
dcpt.KeySize = 128;
|
||||
dcpt.Key = keyByte;
|
||||
dcpt.IV = ivByte;
|
||||
dcpt.Mode = mode;
|
||||
dcpt.Padding = padding;
|
||||
|
||||
ICryptoTransform cTransform = dcpt.CreateDecryptor();
|
||||
Byte[] resultArray = cTransform.TransformFinalBlock(inBuff, 0, inBuff.Length);
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
public static byte[] CHACHA20Decrypt(byte[] encryptedBuff, byte[] keyBytes, byte[] nonceBytes)
|
||||
{
|
||||
if (keyBytes.Length != 32)
|
||||
throw new Exception("Key must be 32 bytes!");
|
||||
if (nonceBytes.Length != 12 && nonceBytes.Length != 8)
|
||||
throw new Exception("Key must be 12 or 8 bytes!");
|
||||
if (nonceBytes.Length == 8)
|
||||
nonceBytes = (new byte[4] { 0, 0, 0, 0 }).Concat(nonceBytes).ToArray();
|
||||
|
||||
var decStream = new MemoryStream();
|
||||
using (BinaryReader reader = new BinaryReader(new MemoryStream(encryptedBuff)))
|
||||
{
|
||||
using (BinaryWriter writer = new BinaryWriter(decStream))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var buffer = reader.ReadBytes(1024);
|
||||
byte[] dec = new byte[buffer.Length];
|
||||
if (buffer.Length > 0)
|
||||
{
|
||||
ChaCha20 forDecrypting = new ChaCha20(keyBytes, nonceBytes, 0);
|
||||
forDecrypting.DecryptBytes(dec, buffer);
|
||||
writer.Write(dec, 0, dec.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return decStream.ToArray();
|
||||
}
|
||||
|
||||
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
265
N_m3u8DL-CLI/Downloader.cs
Normal file
265
N_m3u8DL-CLI/Downloader.cs
Normal file
File diff suppressed because it is too large
Load Diff
177
N_m3u8DL-CLI/FFmpeg.cs
Normal file
177
N_m3u8DL-CLI/FFmpeg.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
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
|
||||
{
|
||||
public static string FFMPEG_PATH = "ffmpeg";
|
||||
public static string REC_TIME = ""; //录制日期
|
||||
|
||||
public static string OutPutPath { get; set; } = string.Empty;
|
||||
public static string ReportFile { get; set; } = string.Empty;
|
||||
public static bool UseAACFilter { get; set; } = false; //是否启用滤镜
|
||||
public static bool WriteDate { get; set; } = true; //是否写入录制日期
|
||||
|
||||
public static void Merge(string[] files, string muxFormat, bool fastStart,
|
||||
string poster = "", string audioName = "", string title = "",
|
||||
string copyright = "", string comment = "", string encodingTool = "")
|
||||
{
|
||||
string dateString = string.IsNullOrEmpty(REC_TIME) ? DateTime.Now.ToString("o") : REC_TIME;
|
||||
|
||||
//同名文件已存在的共存策略
|
||||
if (File.Exists($"{OutPutPath}.{muxFormat.ToLower()}"))
|
||||
{
|
||||
OutPutPath = Path.Combine(Path.GetDirectoryName(OutPutPath),
|
||||
Path.GetFileName(OutPutPath) + "_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
|
||||
}
|
||||
|
||||
string command = "-loglevel warning -i concat:\"";
|
||||
string data = string.Empty;
|
||||
string ddpAudio = string.Empty;
|
||||
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=\"" + dateString + "\"" : "") +
|
||||
" -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_PATH, command, Path.GetDirectoryName(files[0]));
|
||||
LOGGER.WriteLine(strings.ffmpegDone);
|
||||
//Console.WriteLine(command);
|
||||
}
|
||||
|
||||
public static void ConvertToMPEGTS(string file)
|
||||
{
|
||||
if (Global.VIDEO_TYPE == "H264")
|
||||
{
|
||||
Run(FFMPEG_PATH,
|
||||
"-loglevel quiet -i \"" + file + "\" -map 0 -c copy -copy_unknown -f mpegts -bsf:v h264_mp4toannexb \""
|
||||
+ Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts\"",
|
||||
Path.GetDirectoryName(file));
|
||||
if (File.Exists(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts"))
|
||||
{
|
||||
File.Delete(file);
|
||||
File.Move(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts", file);
|
||||
}
|
||||
}
|
||||
else if (Global.VIDEO_TYPE == "H265")
|
||||
{
|
||||
Run(FFMPEG_PATH,
|
||||
"-loglevel quiet -i \"" + file + "\" -map 0 -c copy -copy_unknown -f mpegts -bsf:v hevc_mp4toannexb \""
|
||||
+ Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts\"",
|
||||
Path.GetDirectoryName(file));
|
||||
if (File.Exists(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts"))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
1284
N_m3u8DL-CLI/Global.cs
Normal file
1284
N_m3u8DL-CLI/Global.cs
Normal file
File diff suppressed because it is too large
Load Diff
156
N_m3u8DL-CLI/HLSLiveDownloader.cs
Normal file
156
N_m3u8DL-CLI/HLSLiveDownloader.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
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
|
||||
{
|
||||
public static int REC_DUR_LIMIT = -1; //默认不限制录制时长
|
||||
public static double REC_DUR = 0; //已录制时长
|
||||
private string liveFile = string.Empty;
|
||||
private string jsonFile = string.Empty;
|
||||
private string headers = string.Empty;
|
||||
private string downDir = string.Empty;
|
||||
private FileStream liveStream = null;
|
||||
private double targetduration = 10;
|
||||
private bool isFirstJson = true;
|
||||
|
||||
public double TotalDuration { get; set; }
|
||||
public string Headers { get => headers; set => headers = value; }
|
||||
public string DownDir { get => downDir; set => downDir = value; }
|
||||
public FileStream LiveStream { get => liveStream; set => liveStream = value; }
|
||||
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<double>();
|
||||
TotalDuration = initJson["m3u8Info"]["totalDuration"].Value<double>();
|
||||
timer.Interval = Math.Abs(TotalDuration - targetduration) * 1000;//设置定时器运行间隔
|
||||
if (timer.Interval <= 1000) timer.Interval = 10000;
|
||||
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.Headers = Headers;
|
||||
parser.DownDir = Path.GetDirectoryName(jsonFile);
|
||||
parser.M3u8Url = m3u8Url;
|
||||
parser.LiveStream = true;
|
||||
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());
|
||||
}
|
||||
}
|
||||
if (toDownList.Count > 0)
|
||||
Record();
|
||||
}
|
||||
|
||||
//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()
|
||||
{
|
||||
while (toDownList.Count > 0 && (sd.FileUrl != "" ? sd.IsDone : true))
|
||||
{
|
||||
JObject info = JObject.Parse(toDownList[0].ToString());
|
||||
int index = info["index"].Value<int>();
|
||||
sd.FileUrl = info["segUri"].Value<string>();
|
||||
sd.Method = info["method"].Value<string>();
|
||||
if (sd.Method != "NONE")
|
||||
{
|
||||
sd.Key = info["key"].Value<string>();
|
||||
sd.Iv = info["iv"].Value<string>();
|
||||
}
|
||||
sd.TimeOut = (int)timer.Interval - 1000;//超时时间不超过下次执行时间
|
||||
if (sd.TimeOut <= 0) sd.TimeOut = (int)timer.Interval;
|
||||
sd.SegIndex = index;
|
||||
sd.Headers = Headers;
|
||||
sd.SegDur = info["duration"].Value<double>();
|
||||
sd.IsLive = true; //标记为直播
|
||||
sd.LiveFile = LiveFile;
|
||||
sd.LiveStream = LiveStream;
|
||||
sd.Down(); //开始下载
|
||||
while (sd.IsDone != true) { Thread.Sleep(1); }; //忙等待 Thread.Sleep(1) 可防止cpu 100% 防止电脑风扇狂转
|
||||
if (toDownList.Count > 0)
|
||||
toDownList.RemoveAt(0); //下完删除一项
|
||||
}
|
||||
LOGGER.PrintLine("Waiting...", LOGGER.Warning);
|
||||
LOGGER.WriteLine("Waiting...");
|
||||
}
|
||||
|
||||
//检测是否有新分片
|
||||
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";
|
||||
}
|
||||
}
|
116
N_m3u8DL-CLI/IqJsonParser.cs
Normal file
116
N_m3u8DL-CLI/IqJsonParser.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace N_m3u8DL_CLI
|
||||
{
|
||||
class IqJsonParser
|
||||
{
|
||||
public static string Parse(string downDir, string json)
|
||||
{
|
||||
JObject jObject = JObject.Parse(json);
|
||||
var aClips = jObject["payload"]["wm_a"]["audio_track1"]["files"].Value<JArray>();
|
||||
var vClips = jObject["payload"]["wm_a"]["video_track1"]["files"].Value<JArray>();
|
||||
|
||||
var codecsList = new List<string>();
|
||||
|
||||
var audioPath = "";
|
||||
var videoPath = "";
|
||||
var audioInitPath = "";
|
||||
var videoInitPath = "";
|
||||
|
||||
if (aClips.Count > 0)
|
||||
{
|
||||
var init = jObject["payload"]["wm_a"]["audio_track1"]["codec_init"].Value<string>();
|
||||
byte[] bytes = Convert.FromBase64String(init);
|
||||
//输出init文件
|
||||
audioInitPath = Path.Combine(downDir, "iqAudioInit.mp4");
|
||||
File.WriteAllBytes(audioInitPath, bytes);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("#EXTM3U");
|
||||
sb.AppendLine("#EXT-X-VERSION:3");
|
||||
sb.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");
|
||||
sb.AppendLine("#CREATED-BY:N_m3u8DL-CLI");
|
||||
sb.AppendLine($"#EXT-CODEC:{jObject["payload"]["wm_a"]["audio_track1"]["codec"].Value<string>()}");
|
||||
sb.AppendLine($"#EXT-KID:{jObject["payload"]["wm_a"]["audio_track1"]["key_id"].Value<string>()}");
|
||||
sb.AppendLine($"#EXT-X-MAP:URI=\"{new Uri(Path.Combine(downDir + "(Audio)", "iqAudioInit.mp4")).ToString()}\"");
|
||||
sb.AppendLine("#EXT-X-KEY:METHOD=PLZ-KEEP-RAW,URI=\"None\"");
|
||||
foreach (var a in aClips)
|
||||
{
|
||||
sb.AppendLine($"#EXTINF:{a["duration_second"].ToString()}");
|
||||
sb.AppendLine(a["file_name"].Value<string>());
|
||||
}
|
||||
sb.AppendLine("#EXT-X-ENDLIST");
|
||||
//输出m3u8文件
|
||||
var _path = Path.Combine(downDir, "iqAudio.m3u8");
|
||||
File.WriteAllText(_path, sb.ToString());
|
||||
audioPath = new Uri(_path).ToString();
|
||||
codecsList.Add(jObject["payload"]["wm_a"]["audio_track1"]["codec"].Value<string>());
|
||||
}
|
||||
|
||||
if (vClips.Count > 0)
|
||||
{
|
||||
var init = jObject["payload"]["wm_a"]["video_track1"]["codec_init"].Value<string>();
|
||||
byte[] bytes = Convert.FromBase64String(init);
|
||||
//输出init文件
|
||||
videoInitPath = Path.Combine(downDir, "iqVideoInit.mp4");
|
||||
File.WriteAllBytes(videoInitPath, bytes);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("#EXTM3U");
|
||||
sb.AppendLine("#EXT-X-VERSION:3");
|
||||
sb.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");
|
||||
sb.AppendLine("#CREATED-BY:N_m3u8DL-CLI");
|
||||
sb.AppendLine($"#EXT-CODEC:{jObject["payload"]["wm_a"]["video_track1"]["codec"].Value<string>()}");
|
||||
sb.AppendLine($"#EXT-KID:{jObject["payload"]["wm_a"]["video_track1"]["key_id"].Value<string>()}");
|
||||
sb.AppendLine($"#EXT-X-MAP:URI=\"{new Uri(videoInitPath).ToString()}\"");
|
||||
sb.AppendLine("#EXT-X-KEY:METHOD=PLZ-KEEP-RAW,URI=\"None\"");
|
||||
foreach (var a in vClips)
|
||||
{
|
||||
var start = a["seekable"]["pos_start"].Value<long>();
|
||||
var size = a["size"].Value<long>();
|
||||
sb.AppendLine($"#EXTINF:{a["duration_second"].ToString()}");
|
||||
sb.AppendLine($"#EXT-X-BYTERANGE:{size}@{start}");
|
||||
sb.AppendLine(a["file_name"].Value<string>());
|
||||
}
|
||||
sb.AppendLine("#EXT-X-ENDLIST");
|
||||
//输出m3u8文件
|
||||
var _path = Path.Combine(downDir, "iqVideo.m3u8");
|
||||
File.WriteAllText(_path, sb.ToString());
|
||||
videoPath = new Uri(_path).ToString();
|
||||
codecsList.Add(jObject["payload"]["wm_a"]["video_track1"]["codec"].Value<string>());
|
||||
}
|
||||
|
||||
var content = "";
|
||||
if ((videoPath == "" && audioPath != "") || Global.VIDEO_TYPE == "IGNORE")
|
||||
{
|
||||
return audioPath;
|
||||
}
|
||||
else if (audioPath == "" && videoPath != "")
|
||||
{
|
||||
return videoPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Directory.Exists(downDir + "(Audio)"))
|
||||
Directory.CreateDirectory(downDir + "(Audio)");
|
||||
var _path = Path.Combine(downDir + "(Audio)", "iqAudio.m3u8");
|
||||
var _pathInit = Path.Combine(downDir + "(Audio)", "iqAudioInit.mp4");
|
||||
File.Copy(new Uri(audioPath).LocalPath, _path, true);
|
||||
File.Copy(new Uri(audioInitPath).LocalPath, _pathInit, true);
|
||||
audioPath = new Uri(_path).ToString();
|
||||
content = $"#EXTM3U\r\n" +
|
||||
$"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"{audioPath}\",GROUP-ID=\"default-audio-group\",NAME=\"stream_0\",AUTOSELECT=YES,CHANNELS=\"0\"\r\n" +
|
||||
$"#EXT-X-STREAM-INF:BANDWIDTH=99999,CODECS=\"{string.Join(",", codecsList)}\",RESOLUTION=0x0,AUDIO=\"default-audio-group\"\r\n" +
|
||||
$"{videoPath}";
|
||||
}
|
||||
|
||||
var _masterPath = Path.Combine(downDir, "master.m3u8");
|
||||
File.WriteAllText(_masterPath, content);
|
||||
return new Uri(_masterPath).ToString();
|
||||
}
|
||||
}
|
||||
}
|
163
N_m3u8DL-CLI/LOGGER.cs
Normal file
163
N_m3u8DL-CLI/LOGGER.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
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 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)); //新建文件夹
|
||||
//若文件存在则加序号
|
||||
int index = 1;
|
||||
var fileName = Path.GetFileNameWithoutExtension(LOGFILE);
|
||||
while (File.Exists(LOGFILE))
|
||||
{
|
||||
LOGFILE = Path.Combine(Path.GetDirectoryName(LOGFILE), $"{fileName}-{index++}.log");
|
||||
}
|
||||
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 windowWith = 63;
|
||||
try
|
||||
{
|
||||
windowWith = Console.WindowWidth;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// empty
|
||||
}
|
||||
switch (printLevel)
|
||||
{
|
||||
case 0:
|
||||
Console.Write("\r" + new string(' ', windowWith - 1) + "\r");
|
||||
Console.WriteLine(" ".PadRight(12) + " " + text);
|
||||
break;
|
||||
case 1:
|
||||
Console.Write("\r" + new string(' ', windowWith - 1) + "\r");
|
||||
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
|
||||
Console.WriteLine(text);
|
||||
break;
|
||||
case 2:
|
||||
Console.Write("\r" + new string(' ', windowWith - 1) + "\r");
|
||||
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine(text);
|
||||
Console.ResetColor();
|
||||
break;
|
||||
case 3:
|
||||
Console.Write("\r" + new string(' ', windowWith - 1) + "\r");
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
874
N_m3u8DL-CLI/MPDParser.cs
Normal file
874
N_m3u8DL-CLI/MPDParser.cs
Normal file
File diff suppressed because it is too large
Load Diff
110
N_m3u8DL-CLI/MyOptions.cs
Normal file
110
N_m3u8DL-CLI/MyOptions.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using CommandLine;
|
||||
using CommandLine.Text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace N_m3u8DL_CLI
|
||||
{
|
||||
internal class MyOptions
|
||||
{
|
||||
[Value(0, Hidden = true, MetaName = "Input Source", HelpText = "Help_input", ResourceType = typeof(strings))]
|
||||
public string Input { get; set; }
|
||||
|
||||
[Option("workDir", HelpText = "Help_workDir", ResourceType = typeof(strings))]
|
||||
public string WorkDir { get; set; }
|
||||
|
||||
[Option("saveName", HelpText = "Help_saveName", ResourceType = typeof(strings))]
|
||||
public string SaveName { get; set; } = "";
|
||||
|
||||
[Option("baseUrl", HelpText = "Help_baseUrl", ResourceType = typeof(strings))]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[Option("headers", HelpText = "Help_headers", ResourceType = typeof(strings))]
|
||||
public string Headers { get; set; } = "";
|
||||
|
||||
[Option("maxThreads", Default = 32U, HelpText = "Help_maxThreads", ResourceType = typeof(strings))]
|
||||
public uint MaxThreads { get; set; }
|
||||
|
||||
[Option("minThreads", Default = 16U, HelpText = "Help_minThreads", ResourceType = typeof(strings))]
|
||||
public uint MinThreads { get; set; }
|
||||
|
||||
[Option("retryCount", Default = 15U, HelpText = "Help_retryCount", ResourceType = typeof(strings))]
|
||||
public uint RetryCount { get; set; }
|
||||
|
||||
[Option("timeOut", Default = 10U, HelpText = "Help_timeOut", ResourceType = typeof(strings))]
|
||||
public uint TimeOut { get; set; }
|
||||
|
||||
[Option("muxSetJson", HelpText = "Help_muxSetJson", ResourceType = typeof(strings))]
|
||||
public string MuxSetJson { get; set; }
|
||||
|
||||
[Option("useKeyFile", HelpText = "Help_useKeyFile", ResourceType = typeof(strings))]
|
||||
public string UseKeyFile { get; set; }
|
||||
|
||||
[Option("useKeyBase64", HelpText = "Help_useKeyBase64", ResourceType = typeof(strings))]
|
||||
public string UseKeyBase64 { get; set; }
|
||||
|
||||
[Option("useKeyIV", HelpText = "Help_useKeyIV", ResourceType = typeof(strings))]
|
||||
public string UseKeyIV { get; set; }
|
||||
|
||||
[Option("downloadRange", HelpText = "Help_downloadRange", ResourceType = typeof(strings))]
|
||||
public string DownloadRange { get; set; }
|
||||
|
||||
[Option("liveRecDur", HelpText = "Help_liveRecDur", ResourceType = typeof(strings))]
|
||||
public string LiveRecDur { get; set; }
|
||||
|
||||
[Option("stopSpeed", HelpText = "Help_stopSpeed", ResourceType = typeof(strings))]
|
||||
public long StopSpeed { get; set; } = 0L;
|
||||
|
||||
[Option("maxSpeed", HelpText = "Help_maxSpeed", ResourceType = typeof(strings))]
|
||||
public long MaxSpeed { get; set; } = 0L;
|
||||
|
||||
[Option("proxyAddress", HelpText = "Help_proxyAddress", ResourceType = typeof(strings))]
|
||||
public string ProxyAddress { get; set; }
|
||||
|
||||
[Option("enableDelAfterDone", HelpText = "Help_enableDelAfterDone", ResourceType = typeof(strings))]
|
||||
public bool EnableDelAfterDone { get; set; }
|
||||
|
||||
[Option("enableMuxFastStart", HelpText = "Help_enableMuxFastStart", ResourceType = typeof(strings))]
|
||||
public bool EnableMuxFastStart { get; set; }
|
||||
|
||||
[Option("enableBinaryMerge", HelpText = "Help_enableBinaryMerge", ResourceType = typeof(strings))]
|
||||
public bool EnableBinaryMerge { get; set; }
|
||||
|
||||
[Option("enableParseOnly", HelpText = "Help_enableParseOnly", ResourceType = typeof(strings))]
|
||||
public bool EnableParseOnly { get; set; }
|
||||
|
||||
[Option("enableAudioOnly", HelpText = "Help_enableAudioOnly", ResourceType = typeof(strings))]
|
||||
public bool EnableAudioOnly { get; set; }
|
||||
|
||||
[Option("disableDateInfo", HelpText = "Help_disableDateInfo", ResourceType = typeof(strings))]
|
||||
public bool DisableDateInfo { get; set; }
|
||||
|
||||
[Option("disableIntegrityCheck", HelpText = "Help_disableIntegrityCheck", ResourceType = typeof(strings))]
|
||||
public bool DisableIntegrityCheck { get; set; }
|
||||
|
||||
[Option("noMerge", HelpText = "Help_noMerge", ResourceType = typeof(strings))]
|
||||
public bool NoMerge { get; set; }
|
||||
|
||||
[Option("noProxy", HelpText = "Help_noProxy", ResourceType = typeof(strings))]
|
||||
public bool NoProxy { get; set; }
|
||||
|
||||
[Option("registerUrlProtocol", HelpText = "Help_registerUrlProtocol", ResourceType = typeof(strings))]
|
||||
public bool RegisterUrlProtocol { get; set; }
|
||||
|
||||
[Option("unregisterUrlProtocol", HelpText = "Help_unregisterUrlProtocol", ResourceType = typeof(strings))]
|
||||
public bool UnregisterUrlProtocol { get; set; }
|
||||
|
||||
[Option("enableChaCha20", HelpText = "enableChaCha20")]
|
||||
public bool EnableChaCha20 { get; set; }
|
||||
|
||||
[Option("chaCha20KeyBase64", HelpText = "ChaCha20KeyBase64")]
|
||||
public string ChaCha20KeyBase64 { get; set; }
|
||||
|
||||
[Option("chaCha20NonceBase64", HelpText = "ChaCha20NonceBase64")]
|
||||
public string ChaCha20NonceBase64 { get; set; }
|
||||
|
||||
}
|
||||
}
|
179
N_m3u8DL-CLI/N_m3u8DL-CLI.csproj
Normal file
179
N_m3u8DL-CLI/N_m3u8DL-CLI.csproj
Normal file
@@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props" Condition="Exists('..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props')" />
|
||||
<Import Project="..\packages\Resource.Embedder.2.1.1\build\Resource.Embedder.props" Condition="Exists('..\packages\Resource.Embedder.2.1.1\build\Resource.Embedder.props')" />
|
||||
<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 />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</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="BrotliSharpLib, Version=0.3.2.0, Culture=neutral, PublicKeyToken=3f4e2a1cd615fcb7, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\BrotliSharpLib.0.3.3\lib\net451\BrotliSharpLib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="CommandLine, Version=2.8.0.0, Culture=neutral, PublicKeyToken=5a870481e358d379, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CommandLineParser.2.8.0\lib\net45\CommandLine.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Costura, Version=4.1.0.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Costura.Fody.4.1.0\lib\net40\Costura.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Build.Framework" />
|
||||
<Reference Include="Microsoft.Build.Utilities.v4.0" />
|
||||
<Reference Include="Microsoft.JScript" />
|
||||
<Reference Include="Microsoft.Win32.TaskScheduler, Version=2.8.7.0, Culture=neutral, PublicKeyToken=c416bc1b32d97233, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\TaskScheduler.2.8.7\lib\net452\Microsoft.Win32.TaskScheduler.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MihaZupan.HttpToSocks5Proxy, Version=1.4.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\HttpToSocks5Proxy.1.4.0\lib\net45\MihaZupan.HttpToSocks5Proxy.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NiL.JS, Version=2.5.1428.0, Culture=neutral, PublicKeyToken=fa941a7c2a4de689, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NiL.JS.2.5.1428\lib\net45\NiL.JS.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Collections" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.IO" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<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" />
|
||||
<Reference Include="UACHelper, Version=1.3.0.4, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\UACHelper.1.3.0.5\lib\net40\UACHelper.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CSChaCha20.cs" />
|
||||
<Compile Include="Decode51CtoKey.cs" />
|
||||
<Compile Include="DecodeCdeledu.cs" />
|
||||
<Compile Include="DecodeDdyun.cs" />
|
||||
<Compile Include="DecodeHuke88Key.cs" />
|
||||
<Compile Include="DecodeImooc.cs" />
|
||||
<Compile Include="Decrypter.cs" />
|
||||
<Compile Include="DecodeNfmovies.cs" />
|
||||
<Compile Include="FFmpeg.cs" />
|
||||
<Compile Include="Global.cs" />
|
||||
<Compile Include="HLSLiveDownloader.cs" />
|
||||
<Compile Include="HLSTags.cs" />
|
||||
<Compile Include="IqJsonParser.cs" />
|
||||
<Compile Include="LOGGER.cs" />
|
||||
<Compile Include="DownloadManager.cs" />
|
||||
<Compile Include="MPDParser.cs" />
|
||||
<Compile Include="MyOptions.cs" />
|
||||
<Compile Include="Parser.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="ProgressReporter.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Downloader.cs" />
|
||||
<Compile Include="strings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>strings.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="strings.en-US.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>strings.en-US.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="strings.zh-TW.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>strings.zh-TW.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Watcher.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
</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>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="strings.en-US.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>strings.en-US.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="strings.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>strings.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="strings.zh-TW.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>strings.zh-TW.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Resource.Embedder.2.1.1\build\Resource.Embedder.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Resource.Embedder.2.1.1\build\Resource.Embedder.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Fody.6.0.0\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.6.0.0\build\Fody.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\Fody.6.0.0\build\Fody.targets" Condition="Exists('..\packages\Fody.6.0.0\build\Fody.targets')" />
|
||||
</Project>
|
1015
N_m3u8DL-CLI/Parser.cs
Normal file
1015
N_m3u8DL-CLI/Parser.cs
Normal file
File diff suppressed because it is too large
Load Diff
525
N_m3u8DL-CLI/Program.cs
Normal file
525
N_m3u8DL-CLI/Program.cs
Normal file
File diff suppressed because it is too large
Load Diff
39
N_m3u8DL-CLI/ProgressReporter.cs
Normal file
39
N_m3u8DL-CLI/ProgressReporter.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
|
||||
{
|
||||
class ProgressReporter
|
||||
{
|
||||
private static string speed = "";
|
||||
private static string progress = "";
|
||||
|
||||
static object lockThis = new object();
|
||||
public static void Report(string progress, string speed)
|
||||
{
|
||||
lock (lockThis)
|
||||
{
|
||||
int windowWith = 63;
|
||||
try
|
||||
{
|
||||
windowWith = Console.WindowWidth;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// empty
|
||||
}
|
||||
if (!string.IsNullOrEmpty(progress)) ProgressReporter.progress = progress;
|
||||
if (!string.IsNullOrEmpty(speed)) ProgressReporter.speed = speed;
|
||||
string now = DateTime.Now.ToString("HH:mm:ss.000");
|
||||
var sub = windowWith - 4 - ProgressReporter.progress.Length - ProgressReporter.speed.Length - now.Length;
|
||||
if (sub <= 0) sub = 0;
|
||||
string print = now + " " + ProgressReporter.progress + " " + ProgressReporter.speed + new string(' ', sub);
|
||||
Console.Write("\r" + print + "\r");
|
||||
//Console.Write(print);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
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 © 2022")]
|
||||
[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("3.0.2.0")]
|
||||
[assembly: AssemblyFileVersion("3.0.2.0")]
|
110
N_m3u8DL-CLI/Watcher.cs
Normal file
110
N_m3u8DL-CLI/Watcher.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
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);
|
||||
int padding = downloadedSize.Length > estimatedSize.Length ? downloadedSize.Length : estimatedSize.Length;
|
||||
DownloadManager.ToDoSize = (DownloadManager.DownloadedSize * total / now) - DownloadManager.DownloadedSize;
|
||||
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
|
||||
var print = "Progress: " + Now + "/" + Total
|
||||
+ $" ({percent}) -- {downloadedSize.PadLeft(padding)}/{estimatedSize.PadRight(padding)}";
|
||||
ProgressReporter.Report(print, "");
|
||||
}
|
||||
|
||||
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);
|
||||
int padding = downloadedSize.Length > estimatedSize.Length ? downloadedSize.Length : estimatedSize.Length;
|
||||
DownloadManager.ToDoSize = (DownloadManager.DownloadedSize * total / now) - DownloadManager.DownloadedSize;
|
||||
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
|
||||
var print = "Progress: " + Now + "/" + Total
|
||||
+ $" ({percent}) -- {downloadedSize.PadLeft(padding)}/{estimatedSize.PadRight(padding)}";
|
||||
ProgressReporter.Report(print, "");
|
||||
}
|
||||
|
||||
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);
|
||||
int padding = downloadedSize.Length > estimatedSize.Length ? downloadedSize.Length : estimatedSize.Length;
|
||||
DownloadManager.ToDoSize = (DownloadManager.DownloadedSize * total / now) - DownloadManager.DownloadedSize;
|
||||
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
|
||||
var print = "Progress: " + Now + "/" + Total
|
||||
+ $" ({percent}) -- {downloadedSize.PadLeft(padding)}/{estimatedSize.PadRight(padding)}";
|
||||
ProgressReporter.Report(print, "");
|
||||
}
|
||||
}
|
||||
}
|
362
N_m3u8DL-CLI/changelog.txt
Normal file
362
N_m3u8DL-CLI/changelog.txt
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user