using
Newtonsoft.Json;
using
System;
using
System.IO;
using
System.IO.Pipes;
using
System.Net;
using
System.Security.Cryptography;
using
System.Text;
using
System.Text.RegularExpressions;
using
System.Web;
using
static
System.Net.Mime.MediaTypeNames;
using
static
System.Runtime.InteropServices.JavaScript.JSType;
namespace
xmflv
{
internal
class
Program
{
static
async
Task Main(
string
[] args)
{
Console.WriteLine(
"Hello, World!"
);
Console.Write(
"请输入视频URL(支持腾讯视频,爱奇艺):"
);
string
fromUrl = Console.ReadLine();
if
(
string
.IsNullOrEmpty(fromUrl))
{
Console.WriteLine(
"输入url为空!"
);
Console.ReadLine();
}
fromUrl = EncodeUrl(fromUrl);
long
timeStamp = GenerateTimestamp();
string
plaintext = CalculateMD5Hash($
"{timeStamp}{UrlDecode(fromUrl)}"
);
string
ciphertext = Sign(plaintext);
HttpHelper http =
new
HttpHelper();
HttpItem item =
new
HttpItem()
{
URL =
"接口地址的url"
,
Method =
"POST"
,
Accept =
"application/json, text/javascript, */*; q=0.01"
,
ContentType =
"application/x-www-form-urlencoded; charset=UTF-8"
,
UserAgent =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0"
,
Postdata = $
"wap=1&url={fromUrl}&time={timeStamp}&key={EncodeKey(ciphertext)}"
,
};
item.Header.Add(
"Origin"
,
"地址自己抓一下"
);
HttpResult result = http.GetHtml(item);
if
(result.StatusCode != System.Net.HttpStatusCode.OK)
{
Console.Write(
"请求失败!"
);
}
else
{
string
html = result.Html;
var
resultDynamic = JsonConvert.DeserializeObject<dynamic>(html);
if
(!
string
.IsNullOrEmpty(html) && (
int
)resultDynamic.code == 200)
{
var
extm3u = DecryptAES((
string
)resultDynamic.url, (
string
)resultDynamic.aes_key, (
string
)resultDynamic.aes_iv);
var
AllUrl = ExtractURLs(DecryptAES((
string
)resultDynamic.html, (
string
)resultDynamic.aes_key, (
string
)resultDynamic.aes_iv));
item =
new
HttpItem()
{
URL = RemoveSpecialCharacters(extm3u),
Method =
"GET"
,
UserAgent =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0"
,
Accept =
"*/*"
,
};
item.Header.Add(
"Origin"
,
"地址自己抓一下"
);
result = http.GetHtml(item);
if
(result.StatusCode == System.Net.HttpStatusCode.OK && !
string
.IsNullOrEmpty(result.Html))
{
html = result.Html;
string
[] lines = html.Split(
new
[] {
'\n'
,
'\r'
}, StringSplitOptions.RemoveEmptyEntries);
string
folderPath = AppDomain.CurrentDomain.BaseDirectory + $
"{resultDynamic.name}"
;
int
i = 0;
foreach
(
string
line
in
lines)
{
if
(!line.StartsWith(
"#"
))
{
if
(!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
string
fileName = $
"{i}.ts"
;
string
filePath = Path.Combine(folderPath, fileName);
if
(!File.Exists(filePath))
{
bool
bl =
true
;
int
j = 0;
while
(bl)
{
try
{
using
(HttpClient client =
new
HttpClient())
{
if
(extm3u.Contains(
"IP地址包含就用这个"
))
{
item =
new
HttpItem()
{
URL =
"https://服务器地址自己抓一下/"
+ line,
Method =
"GET"
,
UserAgent =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0"
,
Accept =
"*/*"
,
};
item.Header.Add(
"Origin"
,
"地址自己抓一下"
);
result = http.GetHtml(item);
var
location = result.RedirectUrl;
using
(HttpResponseMessage response =
await
client.GetAsync(location))
{
response.EnsureSuccessStatusCode();
using
(Stream stream =
await
response.Content.ReadAsStreamAsync())
{
using
(FileStream fileStream =
new
FileStream(filePath, FileMode.Create, FileAccess.Write))
{
await
stream.CopyToAsync(fileStream);
bl =
false
;
break
;
}
}
}
}
else
{
string
pattern =
@"/([^/]+)$"
;
string
match = Regex.Replace(extm3u, pattern,
""
);
string
mp4Url = match +
"/"
+ line;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(mp4Url);
request.Method =
"GET"
;
request.Headers.Add(
"sec-ch-ua"
,
"\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Microsoft Edge\";v=\"120\""
);
request.Headers.Add(
"sec-ch-ua-mobile"
,
"?0"
);
request.UserAgent =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0"
;
request.Headers.Add(
"sec-ch-ua-platform"
,
"\"Windows\""
);
request.Accept =
"/"
;
request.Headers.Add(
"Origin"
,
"地址自己抓一下"
);
request.Headers.Add(
"Sec-Fetch-Site"
,
"cross-site"
);
request.Headers.Add(
"Sec-Fetch-Mode"
,
"cors"
);
request.Headers.Add(
"Sec-Fetch-Dest"
,
"empty"
);
request.Headers.Add(
"Accept-Encoding"
,
"gzip, deflate, br"
);
request.Headers.Add(
"Accept-Language"
,
"zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"
);
try
{
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using
(
var
stream = response.GetResponseStream())
{
using
(FileStream fileStream =
new
FileStream(filePath, FileMode.Create, FileAccess.Write))
{
await
stream.CopyToAsync(fileStream);
bl =
false
;
}
response.Close();
break
;
}
}
catch
(WebException ex)
{
Console.WriteLine(
"An error occurred: "
+ ex.Message);
}
}
}
}
catch
(Exception ex)
{
j++;
if
(j > 3)
{
bl =
false
;
Console.WriteLine($
"3次请求地址自己抓一下{line}失败已经跳过!"
);
}
}
}
}
i++;
}
}
CombineMP3(folderPath);
}
else
{
Console.WriteLine(
"请求加密接口失败!"
);
}
}
else
{
Console.WriteLine(
"请求失败!"
+ html);
}
}
Console.ReadLine();
}
static
void
CombineMP3(
string
folder)
{
string
outputFilePath = $
"{folder}.mp4"
;
string
[] mp3Files = Directory.GetFiles(folder,
"*.ts"
);
List<
byte
[]> mp3DataList =
new
List<
byte
[]>();
List<
string
> sortedFiles = mp3Files.OrderBy(f => Path.GetFileNameWithoutExtension(f),
new
NumericFileNameComparer()).ToList();
foreach
(
string
filePath
in
sortedFiles)
{
byte
[] mp3Data = File.ReadAllBytes(filePath);
mp3DataList.Add(mp3Data);
}
byte
[] outputData = mp3DataList.SelectMany(data => data).ToArray();
File.WriteAllBytes(outputFilePath, outputData);
Directory.Delete(folder,
true
);
Console.WriteLine(
"合并完成,输出文件路径:"
+ outputFilePath);
}
public
static
string
RemoveSpecialCharacters(
string
url)
{
string
pattern =
@"[^a-zA-Z0-9.:/?=_-]"
;
return
Regex.Replace(url, pattern,
""
);
}
public
class
NumericFileNameComparer : IComparer<
string
>
{
public
int
Compare(
string
x,
string
y)
{
string
fileNameX = Path.GetFileNameWithoutExtension(x);
string
fileNameY = Path.GetFileNameWithoutExtension(y);
if
(fileNameX ==
null
|| fileNameY ==
null
)
{
return
0;
}
int
numericValueX, numericValueY;
bool
successX =
int
.TryParse(fileNameX,
out
numericValueX);
bool
successY =
int
.TryParse(fileNameY,
out
numericValueY);
if
(successX && successY)
{
return
numericValueX.CompareTo(numericValueY);
}
else
if
(successX)
{
return
-1;
}
else
if
(successY)
{
return
1;
}
else
{
return
string
.Compare(fileNameX, fileNameY, StringComparison.Ordinal);
}
}
}
/// <summary>
/// 获取全集链接
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public
static
List<
string
> ExtractURLs(
string
text)
{
List<
string
> urls =
new
List<
string
>();
Regex regex =
new
Regex(
@"./\?url=(.*?)'"
);
MatchCollection matches = regex.Matches(text);
foreach
(Match match
in
matches)
{
string
url = match.Groups[1].Value;
urls.Add(url);
}
return
urls;
}
/// <summary>
/// 获取请求中的key
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public
static
string
Sign(
string
a)
{
byte
[] md5Bytes;
using
(
var
md5 = MD5.Create())
{
byte
[] inputBytes = Encoding.UTF8.GetBytes(a);
md5Bytes = md5.ComputeHash(inputBytes);
}
string
b = BitConverter.ToString(md5Bytes).Replace(
"-"
,
""
).ToLower();
byte
[] key = Encoding.UTF8.GetBytes(b);
byte
[] iv = Encoding.UTF8.GetBytes(
"3cccf88181408f19"
);
PaddingMode padding = PaddingMode.Zeros;
byte
[] encryptedBytes;
using
(
var
aes =
new
AesCryptoServiceProvider())
{
aes.Key = key;
aes.IV = iv;
aes.Padding = padding;
aes.Mode = CipherMode.CBC;
byte
[] inputBytes = Encoding.UTF8.GetBytes(a);
using
(
var
encryptor = aes.CreateEncryptor())
{
encryptedBytes = encryptor.TransformFinalBlock(inputBytes, 0, inputBytes.Length);
}
}
string
e = Convert.ToBase64String(encryptedBytes);
return
e;
}
/// <summary>
/// 时间戳+url
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
static
string
CalculateMD5Hash(
string
input)
{
using
(MD5 md5 = MD5.Create())
{
byte
[] inputBytes = Encoding.UTF8.GetBytes(input);
byte
[] hashBytes = md5.ComputeHash(inputBytes);
StringBuilder stringBuilder =
new
StringBuilder();
for
(
int
i = 0; i < hashBytes.Length; i++)
{
stringBuilder.Append(hashBytes[i].ToString(
"x2"
));
}
return
stringBuilder.ToString();
}
}
/// <summary>
/// 获取时间戳
/// </summary>
/// <returns></returns>
static
long
GenerateTimestamp()
{
DateTime epoch =
new
DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
TimeSpan timeSpan = DateTime.UtcNow - epoch;
long
timestamp = (
long
)timeSpan.TotalMilliseconds;
return
timestamp;
}
/// <summary>
/// 对网址进行url编码
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
static
string
EncodeUrl(
string
str)
{
string
encodedUrl = Uri.EscapeDataString(str);
encodedUrl = encodedUrl.Replace(
"%3A"
,
"%253A"
).Replace(
"%2F"
,
"%252F"
);
return
encodedUrl;
}
static
string
EncodeKey(
string
str)
{
string
encodedUrl = Uri.EscapeDataString(str);
return
encodedUrl;
}
public
static
string
UrlDecode(
string
encodedUrl)
{
string
decodedUrl = HttpUtility.UrlDecode(encodedUrl);
return
decodedUrl;
}
/// <summary>
/// 解密
/// </summary>
/// <param name="encryptedText"></param>
/// <param name="key"></param>
/// <param name="iv"></param>
/// <returns></returns>
static
string
DecryptAES(
string
encryptedText,
string
key,
string
iv)
{
byte
[] keyBytes = Encoding.UTF8.GetBytes(key);
byte
[] ivBytes = Encoding.UTF8.GetBytes(iv);
byte
[] encryptedBytes = Convert.FromBase64String(encryptedText);
using
(Aes aesAlg = Aes.Create())
{
aesAlg.Key = keyBytes;
aesAlg.IV = ivBytes;
aesAlg.Padding = PaddingMode.Zeros;
aesAlg.Mode = CipherMode.CBC;
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using
(MemoryStream msDecrypt =
new
MemoryStream(encryptedBytes))
{
using
(CryptoStream csDecrypt =
new
CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using
(StreamReader srDecrypt =
new
StreamReader(csDecrypt))
{
return
srDecrypt.ReadToEnd();
}
}
}
}
}
}
}