用C#轻松搞定m3u8视频下载与合并
嘿,程序员们!今天咱们来聊聊如何用C#写个小程序,轻松下载和合并m3u8视频文件。没错,就是那种分段的流媒体视频。准备好了吗?让我们开始吧!
准备工作
在动手之前,先确保你有以下工具:
- Visual Studio(或者你喜欢的任何C# IDE)
- FFmpeg(这个工具可别少了,确保它在你指定的路径下)
代码实现
下面的代码将帮助你完成这个任务。它的逻辑很简单:从数据库读取视频信息,下载视频分段,然后用FFmpeg合并成一个完整的mp4文件。
// 将以下代码粘贴到你的C#项目中
using System.Diagnostics;
namespace ConsoleApp1
{
public class Demo
{
string RootPath = $"D:/Test/Demo";
public async Task Main()
{
await InitFun();
async Task InitFun()
{
// 从数据库读取数据
List<dynamic> items = new List<dynamic>
{
new
{
FlagName = "A",
Name="计时器 m3u8 ",
Url="http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8"
}
};
using (HttpClient client = new HttpClient())
{
foreach (var item in items)
{
try
{
string flagName = item.FlagName; // 顶层目录
string name = item.Name; // 子目录
string m3u8Url = item.Url;
// 创建文件夹结构
var date = DateTime.Now;
string rootPath = $"{RootPath}/{date.Year}/{date.Month}/{flagName}/";
// 创建顶层目录
if (!Directory.Exists(rootPath))
{
Directory.CreateDirectory(rootPath);
}
// 创建子目录
string subFolderPath = Path.Combine(rootPath, name);
string outputDirectory = Path.Combine(subFolderPath, "ts"); // "ts" 文件夹
string outputFilePath = Path.Combine(subFolderPath, $"{name}.mp4");
if (!Directory.Exists(outputDirectory))
{
Directory.CreateDirectory(outputDirectory);
}
// 检查 URL 是否为 MP4 文件
if (m3u8Url.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase))
{
// 直接下载 MP4 文件
Console.WriteLine($"Downloading MP4 directly from {m3u8Url}...");
byte[] mp4Data = await client.GetByteArrayAsync(m3u8Url);
await File.WriteAllBytesAsync(outputFilePath, mp4Data);
Console.WriteLine($"Downloaded MP4 file to {outputFilePath}.");
}
else
{
string m3u8Content = await client.GetStringAsync(m3u8Url);
string[] lines = m3u8Content.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
var downloadTasks = new List<Task>();
// 下载每个分段视频
foreach (var line in lines)
{
if (line.EndsWith(".ts"))
{
string segmentUrl = line.StartsWith("http") ? line : new Uri(new Uri(m3u8Url), line).ToString();
string segmentFilePath = Path.Combine(outputDirectory, Path.GetFileName(segmentUrl));
// 启动下载任务
downloadTasks.Add(Task.Run(async () =>
{
try
{
Console.WriteLine($"Downloading {segmentUrl}...");
byte[] segmentData = await client.GetByteArrayAsync(segmentUrl);
await File.WriteAllBytesAsync(segmentFilePath, segmentData);
}
catch
{
}
}));
}
}
// 等待所有下载任务完成
await Task.WhenAll(downloadTasks);
//合并
await MergeVideosWithFFmpeg(outputDirectory, outputFilePath);
}
}
catch
{
Console.WriteLine($"Downloading Fail");
}
finally
{
}
}
}
}
return;
}
private async Task MergeVideosWithFFmpeg(string outputDirectory, string outputFilePath)
{
try
{
string ffmpegPath = @"D:\Bin\ffmpeg-master-latest-win64-gpl\bin\ffmpeg.exe"; // 确保FFmpeg在此路径下
string concatFilePath = Path.Combine(outputDirectory, "concat.txt");
// 创建合并文件
using (StreamWriter writer = new StreamWriter(concatFilePath))
{
foreach (var tsFile in Directory.GetFiles(outputDirectory, "*.ts"))
{
writer.WriteLine($"file '{tsFile}'");
}
}
// 执行FFmpeg合并命令
var ffmpegArgs = $"-f concat -safe 0 -i \"{concatFilePath}\" -c copy \"{outputFilePath}\"";
var processStartInfo = new System.Diagnostics.ProcessStartInfo
{
FileName = ffmpegPath,
Arguments = ffmpegArgs,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (var process = new Process { StartInfo = processStartInfo })
{
try
{
process.Start();
// 异步读取输出和错误信息
var outputTask = process.StandardOutput.ReadToEndAsync();
var errorTask = process.StandardError.ReadToEndAsync();
// 等待进程退出,并设置超时
if (process.WaitForExit(60000 * 2)) // 设置超时为 60*2 秒
{
// 输出读取结果
string output = await outputTask;
string error = await errorTask;
if (process.ExitCode == 0)
{
Console.WriteLine($"Merge completed for {outputFilePath}.");
}
else
{
Console.WriteLine($"FFmpeg exited with code {process.ExitCode}. Error: {error}");
}
}
else
{
Console.WriteLine("FFmpeg process timed out.");
}
process.Kill(); // 杀死进程
}
finally
{
// 删除文件夹
if (Directory.Exists(outputDirectory))
{
Directory.Delete(outputDirectory, true);
Console.WriteLine($"Deleted directory {outputDirectory}.");
}
}
}
}
catch
{
}
}
}
}
// 代码内容请参见上方给出的代码
步骤解析
- 创建C#控制台项目:打开Visual Studio,创建一个新的控制台应用程序。
- 粘贴代码:把上面的代码粘贴到
Program.cs
文件里。 - 配置路径:根据你的实际情况修改
RootPath
和ffmpegPath
变量的值。 - 编译运行:编译并运行程序,看看效果!
注意事项
- 确保引用了必要的命名空间,比如
System.Net.Http
、System.IO
和System.Diagnostics
。 - FFmpeg的路径一定要正确,确保工具安装无误。
– FFmpeg教程(超级详细版)
运行结果
总结
通过这个小程序,你不仅能下载m3u8视频,还能把它们合并成一个完整的mp4文件。是不是很简单?
用C#获取“掌上高考”学校信息
嘿,大家好!今天我们来聊聊如何用C#编写一个程序,从“掌上高考”平台获取学校信息。这个程序不仅能获取学校的基本信息,还能提取特色专业、校园设施、就业政策等信息。准备好了吗?让我们开始吧!
准备工作
在开始之前,请确保你已经安装了以下工具:
- Visual Studio(或者你喜欢的任何C# IDE)
- Newtonsoft.Json库(用于处理JSON数据)
你可以在NuGet包管理器中安装Newtonsoft.Json:
Install-Package Newtonsoft.Json
代码实现
下面是程序的主要代码。它会从指定的API获取学校信息,并将其输出到控制台。
// 将以下代码粘贴到你的C#项目中
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
namespace ConsoleApp1
{
public class Main4
{
private readonly static string Root = "https://static-data.gaokao.cn";
public async Task Main()
{
string[] strings = { "31" }; // 这里可以替换成需要查询的学校编码
foreach (string item in strings)
{
await GetSchoolInfo(item);
}
}
static async Task GetSchoolInfo(string schoolCode)
{
try
{
using (HttpClient client = new HttpClient())
{
string url = $"{Root}/www/2.0/school/{schoolCode}/info.json";
HttpResponseMessage response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync();
JObject data = JObject.Parse(Regex.Unescape(json));
if (data != null)
{
var schoolInfo = JsonConvert.DeserializeObject<dynamic>(data["data"].ToString());
Console.WriteLine("高校名称: " + schoolInfo?.name);
Console.WriteLine("高校地址: " + schoolInfo?.address);
Console.WriteLine("校园面积: " + schoolInfo?.area + "亩");
Console.WriteLine("招办电话: " + schoolInfo?.phone);
Console.WriteLine("招生官网: " + schoolInfo?.site);
Console.WriteLine("校园官网: " + schoolInfo?.school_site);
var tag = $"{schoolInfo?.level_name},{schoolInfo?.school_nature_name},{schoolInfo?.dual_class_name},{(schoolInfo?.f985 == "1" ? "985工程" : "")},{(schoolInfo?.f211 == "1" ? "211工程" : "")}";
Console.WriteLine("学校等级特色标签: " + tag);
var specialName = await GetNationFeature(schoolCode);
Console.WriteLine("优势专业(特色专业): " + specialName);
var dormitoryInfo = await GetDormitory(schoolCode);
Console.WriteLine("住宿: " + dormitoryInfo.dormitory);
Console.WriteLine("食堂: " + dormitoryInfo.canteen);
Console.WriteLine("校园风光: " + dormitoryInfo.schoolImg);
var reportInfo = await GetReportName(schoolCode);
Console.WriteLine("就业报告: " + string.Join(',', reportInfo));
var gradeLine = await GetGradeLine(schoolCode);
Console.WriteLine("上一年 内蒙古本科一批分数: " + string.Join(',', gradeLine));
Console.WriteLine("========================\n");
}
}
else
{
Console.WriteLine("获取数据失败,状态码: " + response.StatusCode);
}
}
}
catch (Exception ex)
{
Console.WriteLine("发生异常: " + ex.Message);
}
}
// 其他方法(GetNationFeature, GetDormitory, GetReportName, GetGradeLine)请按需粘贴
}
}
其他方法
在上面的代码中,我们定义了一些辅助方法来获取特色专业、校园设施、就业政策和分数线。这些方法的实现逻辑与 GetSchoolInfo
类似,都是通过HTTP请求获取JSON数据并进行解析。
注意事项
- 请确保你在项目中引用了
System.Net.Http
和Newtonsoft.Json
命名空间。 - 根据需要替换学校编码(如
"31"
)以获取不同学校的信息。
总结
通过这个小程序,你可以轻松获取高校的详细信息,包括地址、面积、特色专业等。这对于想要了解学校的同学们来说,简直是一大利器!
明白了,感谢你的澄清!在这个上下文中,channel
是指通道代码,通常用于标识特定的商品或服务。接下来我会更新教程中的相关内容,以确保读者能够理解这一点。
使用C#抓取布宫票务小程序售票信息
大家好!今天我们将深入探讨如何使用C#编写一个程序,从布宫票务系统抓取可预订的票务信息。这个程序能够获取指定商品的售票信息,并检查明天是否可以预订票。准备好了吗?让我们开始吧!
准备工作
在开始之前,请确保你已经安装了以下工具:
- Visual Studio(或你喜欢的任何C# IDE)
- Newtonsoft.Json库(用于处理JSON数据)
你可以在NuGet包管理器中安装Newtonsoft.Json:
Install-Package Newtonsoft.Json
代码实现
下面是程序的主要代码。它会向指定API发送请求,获取票务信息,并将结果输出到控制台。
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public class Main3
{
static string token = "********"; // 请使用Fiddler抓取你的token
static string channel = "2"; // 通道代码,替换为你需要查询的通道代码
public async Task Main()
{
await GetSalesList(channel, token);
}
static async Task GetSalesList(string commodity_id, string token)
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("token", token);
var _postData = new
{
commodity_id
};
var postData = JsonConvert.SerializeObject(_postData);
var _data = new StringContent(postData, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync($"https://yjtxcx.potalapalace.cn/addons/shopro/sales/make_date", _data);
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
string responseContent = await response.Content.ReadAsStringAsync();
Console.WriteLine(Regex.Unescape(responseContent));
// 解析 JSON 数据
var jsonDataObject = JsonConvert.DeserializeObject<RootObject>(responseContent);
CheckForAvailableTickets(jsonDataObject);
}
}
}
static void CheckForAvailableTickets(RootObject jsonDataObject)
{
// 获取当前日期和明天的日期
DateTime currentDate = DateTime.Now.Date;
DateTime tomorrowDate = currentDate.AddDays(1);
// 查找明天是否可预订
foreach (var item in jsonDataObject.data)
{
DateTime date = DateTime.Parse(item.date);
// 检查明天是否可以订票
if (date == tomorrowDate && item.type == 1)
{
Console.WriteLine($"明天可预订的日期是:{date}");
// 在这里添加消息推送逻辑,例如发送通知
break;
}
if (date >= currentDate && item.type == 1)
{
string status = item.info == "售罄" ? "售罄" : "可预订";
Console.WriteLine($"日期:{date:yyyy-MM-dd},状态:{status}");
// 在这里添加消息推送逻辑,例如发送通知
}
}
}
public class Datum
{
public string date { get; set; }
public int type { get; set; }
public string info { get; set; }
}
public class RootObject
{
public int code { get; set; }
public string msg { get; set; }
public string time { get; set; }
public List<Datum> data { get; set; }
}
}
}
代码解析
-
Token 和 Channel:在代码中,
token
是需要根据实际情况进行设置的,使用Fiddler抓取你的token。channel
是通道代码,代表你要查询的特定商品或服务。 -
HttpClient:我们使用
HttpClient
类来发送POST请求,并将结果解析为JSON格式。 -
数据解析:通过
JsonConvert.DeserializeObject
方法将返回的JSON数据转换为C#对象,并检查可预订的票务信息。 -
日期检查:程序会检查明天的票是否可预订,并输出相关信息。
注意事项
- 请确保你在项目中引用了
System.Net.Http
和Newtonsoft.Json
命名空间。 - 使用Fiddler抓取token时,请确保你已正确配置代理,并能够捕获HTTP请求。
-Fiddler 抓取 https 设置详解
总结
通过这个小程序,你可以轻松获取布宫的票务信息,并检查明天是否可预订。这对于想要参观布宫的朋友们来说,绝对是个好帮手!
如果你在实现过程中遇到问题,或者对token的抓取有疑问,随时欢迎在下方留言讨论。希望这篇教程对你有所帮助!