static void Main(string[] args)
{
string DRMKey = “d9b6e701edfa8b9d”; //DRMKey
string m3u8Url = ""; //m3u8在线地址
string savePath = "D\\VIDEO\\"; //保存的本地路径
string saveFileName = "VIDEO_FILE_NAME"; //保存的文件(夹)名称,如果为空 则使用默认m3u8文件名
try
{
// 创建本地保存目录
int index = m3u8Url.LastIndexOf("/");
string dirName = string.IsNullOrEmpty(saveFileName) ? m3u8Url.Substring(index + 1) : saveFileName;
string finalSavePath = savePath + dirName + "\\";
if (!Directory.Exists(finalSavePath))
{
Directory.CreateDirectory(finalSavePath);
}
// 读取m3u8文件内容
string m3u8Content = HttpGet(m3u8Url);
//string m3u8Content = File.ReadAllText("D:/test.m3u8");
string aesKey = DRMKey;
// string aesKey = getAESKey(DRMKey);
// string aesKey = getAESKey(DRMKey);
//Console.WriteLine("aesKey:" + aesKey);
Uri uri = new Uri(m3u8Url);
string domain = uri.Scheme + "://" + uri.Authority;
//Console.WriteLine("m3u8域名为:" + domain);
List<string> tsList = Regex.Matches(m3u8Content, @"\n(.*?.ts)").Select(m => m.Value).ToList();
//List<string> ivList = Regex.Matches(m3u8Content, @"IV=(.*?)\n").Select(m => m.Value).ToList();
//if (tsList.Count != ivList.Count || tsList.Count == 0)
//{
// Console.WriteLine("m3u8Content 解析失败");
//}
//else
//{
Console.WriteLine("m3u8Content 解析完成,共有 ts文件");
List<string> aaaaaaaaaa = new List<string>();
for (int i = 0; i < tsList.Count; i++)
{
string ts = tsList[i].Replace("\n", "");
// string iv = ivList[i].Replace("\n", "");
//iv = iv.Replace("IV=0x", "");
//iv = iv.Substring(0, 16); //去除前缀,取IV前16位
int idx = ts.LastIndexOf("/");
string tsFileName = ts.Substring(idx + 1);
try
{
string saveFilepath = finalSavePath +"aa"+ i+".ts";
if (!File.Exists(saveFilepath))
{
Console.WriteLine("开始下载ts: " + domain + ts);
byte[] encByte = HttpGetByte(ts);
if (encByte != null)
{
Console.WriteLine("开始解密, IV -> " + "");
byte[] decByte = null;
try
{
decByte = AESDecrypt(encByte, aesKey, aesKey);
}
catch (Exception e1)
{
error_arr.Add(tsFileName);
Console.WriteLine("解密ts文件异常。" + e1.Message);
}
if (decByte != null)
{
//保存视频文件
File.WriteAllBytes(saveFilepath, decByte);
Console.WriteLine(tsFileName + " 下载完成");
aaaaaaaaaa.Add(saveFilepath);
}
}
else
{
error_arr.Add(tsFileName);
Console.WriteLine("HttpGetByte 结果返回null");
}
}
else
{
Console.WriteLine($"文件 {saveFilepath} 已存在");
}
}
catch (Exception ee)
{
error_arr.Add(tsFileName);
Console.WriteLine("发生异常。" + ee);
}
//}
}
MergeVideo(aaaaaaaaaa, finalSavePath, "all.ts");
// MergeVideoStatr(aaaaaaaaaa, finalSavePath, "all.ts");
}
catch (Exception ex)
{
Console.WriteLine("发生异常。" + ex);
}
Console.WriteLine("所有操作已完成. 保存目录 " + savePath);
if (error_arr.Count > 0)
{
List<string> list = error_arr.Distinct().ToList();
Console.WriteLine($"其中 共有{error_arr.Count}个文件下载失败:");
list.ForEach(x =>
{
Console.WriteLine(x);
});
}
Console.ReadKey();
}
private static string getAESKey(string key)
{
string[] arr = key.Split(",");
string aesKey = "";
for (int i = 0; i < arr.Length; i++)
{
string tmp = int.Parse(arr[i].Trim()).ToString("X"); //10进制转16进制
tmp = HexStringToASCII(tmp);
aesKey += tmp;
}
return aesKey;
}
/// <summary>
/// 十六进制字符串转换为ASCII
/// </summary>
/// <param name="hexstring">一条十六进制字符串</param>
/// <returns>返回一条ASCII码</returns>
public static string HexStringToASCII(string hexstring)
{
byte[] bt = HexStringToBinary(hexstring);
string lin = "";
for (int i = 0; i < bt.Length; i++)
{
lin = lin + bt[i] + " ";
}
string[] ss = lin.Trim().Split(new char[] { ' ' });
char[] c = new char[ss.Length];
int a;
for (int i = 0; i < c.Length; i++)
{
a = Convert.ToInt32(ss[i]);
c[i] = Convert.ToChar(a);
}
string b = new string(c);
return b;
}
/// <summary>
/// 16进制字符串转换为二进制数组
/// </summary>
/// <param name="hexstring">用空格切割字符串</param>
/// <returns>返回一个二进制字符串</returns>
public static byte[] HexStringToBinary(string hexstring)
{
string[] tmpary = hexstring.Trim().Split(' ');
byte[] buff = new byte[tmpary.Length];
for (int i = 0; i < buff.Length; i++)
{
buff[i] = Convert.ToByte(tmpary[i], 16);
}
return buff;
}
/// <summary>
/// AES解密
/// </summary>
/// <param name="cipherText"></param>
/// <param name="Key"></param>
/// <param name="IV"></param>
/// <returns></returns>
public static byte[] AESDecrypt(byte[] cipherText, string Key, string IV)
{
// Check arguments.
//if (cipherText == null || cipherText.Length <= 0)
// throw new ArgumentNullException("cipherText");
//if (Key == null || Key.Length <= 0)
// throw new ArgumentNullException("Key");
//if (IV == null || IV.Length <= 0)
// throw new ArgumentNullException("IV");
Declare the string used to hold
the decrypted text.
byte[] res = null;
// Create an AesManaged object
// with the specified key and IV.
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = Encoding.ASCII.GetBytes(Key);
aesAlg.IV = Encoding.ASCII.GetBytes(Key);
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
byte[] tmp = new byte[cipherText.Length + 32];
int len = csDecrypt.Read(tmp, 0, cipherText.Length + 32);
byte[] ret = new byte[len];
Array.Copy(tmp, 0, ret, 0, len);
res = ret;
}
}
}
return res;
}
public static string HttpGet(string url)
{
try
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.Timeout = 20000;
var response = (HttpWebResponse)request.GetResponse();
using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
catch (Exception ex)
{
Console.Write("HttpGet 异常," + ex.Message);
Console.Write(ex);
return "";
}
}
public static byte[] HttpGetByte(string url)
{
try
{
byte[] arraryByte = null;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.Timeout = 20000;
request.Method = "GET";
using (WebResponse wr = request.GetResponse())
{
int length = (int)wr.ContentLength;
using (StreamReader reader = new StreamReader(wr.GetResponseStream(), Encoding.UTF8))
{
HttpWebResponse response = wr as HttpWebResponse;
Stream stream = response.GetResponseStream();
//读取到内存
MemoryStream stmMemory = new MemoryStream();
byte[] buffer1 = new byte[length];
int i;
//将字节逐个放入到Byte 中
while ((i = stream.Read(buffer1, 0, buffer1.Length)) > 0)
{
stmMemory.Write(buffer1, 0, i);
}
arraryByte = stmMemory.ToArray();
stmMemory.Close();
}
}
return arraryByte;
}
catch (Exception ex)
{
Console.Write("HttpGetByte 异常," + ex.Message);
Console.Write(ex);
return null;
}
}
//cmd 文件合并
public static void MergeVideoStatr(List<string> nameList, string savePath, string fileName)
{
//当次进行生成的文件名 存储合成文件名
List<string> Mergefile = new List<string>();
if (nameList.Count > 10)
{
int i = 0;
foreach (var item in nameList)
{
//第二次之后合并 需要将合并好的视频放到最前面
if (Mergefile.Count == 0 && i == 1) Mergefile.Add(Path.Combine(savePath, fileName));
Mergefile.Add(item);
//每10个文件合并一次
if (Mergefile.Count == 10)
{
if (i == 0) MergeVideo(Mergefile, savePath, fileName);
else
{
MergeVideo(Mergefile, savePath, fileName);
}
//转换状态 下次添加新生成的初次合成的完成版文件
i = 1;
//清除重新添加
Mergefile.Clear();
}
}
//清理剩下的文件
if (Mergefile.Count != 0 && i == 1)
{
MergeVideo(Mergefile, savePath, fileName);
}
}
else
{
if (nameList.Count == 0) throw new Exception("合并视频数不能为0");
MergeVideo(nameList, savePath, fileName);
}
}
/// <summary>
/// 将某一文件夹下的ts文件 合并为一个完整版 BY GJW
/// </summary>
/// <param name="nameList">需要合并的视频地址集合</param>
/// <param name="savePath">保存地址</param>
/// <param name="fileNmae">合成的文件名</param>
public static void MergeVideo(List<string> nameList, string savePath, string fileNmae)
{
if (nameList.Count == 0 || string.IsNullOrEmpty(savePath)) throw new Exception("文件路径不能为空");
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.UseShellExecute = false; //是否使用操作系统shell启动
p.StartInfo.RedirectStandardInput = true;//接受来自调用程序的输入信息
p.StartInfo.RedirectStandardOutput = true;//由调用程序获取输出信息
p.StartInfo.RedirectStandardError = true;//重定向标准错误输出
p.StartInfo.CreateNoWindow = true;//不显示程序窗口
p.Start();//启动程序
//向cmd窗口发送输入信息
//拼接命令
//string cmdstr = string.Format(@"copy /b {0}\*.ts {1}\{2}_完整版.ts",videoPath,savePath,fileNmae);
string cmdstr = string.Format(@"copy /b ""{0}"" ""{1}\{2}""", string.Join(@"""+""", nameList), savePath, fileNmae);
p.StandardInput.WriteLine(cmdstr + "&exit");
p.StandardInput.AutoFlush = true;
//获取cmd窗口的输出信息
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();//等待程序执行完退出进程
p.Close();
}