用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的抓取有疑问,随时欢迎在下方留言讨论。希望这篇教程对你有所帮助!
C# 实现易班刷网薪
主要是对易班文章发起点赞。
public async void YibanLike()
{
try
{
int offset = 0;
using (HttpClient client = new HttpClient())
{
var YibCookie = "";//替换为自己的cookie
client.DefaultRequestHeaders.Add("Cookie", YibCookie);
//获取token用于对文章的评论进行评论
var token = await getToken(client);
if (string.IsNullOrEmpty(token))
{
await GetArticleList(offset, pushPlus,client,"");
}
else
{
await GetArticleList(offset, pushPlus, client, token);
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
获取Token
static async Task<string> getToken(HttpClient client)
{
HttpResponseMessage response = await client.PostAsync($"https://s.yiban.cn/api/security/getToken",null);
var token = "";
if (response.IsSuccessStatusCode)
{
var _responseBody = await response.Content.ReadAsStringAsync();
var responseBody = JsonConvert.DeserializeObject<dynamic>(_responseBody);
if (responseBody.code == "200")
{
token = responseBody.data.csrfToken;
}
}
return token;
}
private async Task GetArticleList(int offset,PushPlus pushPlus, HttpClient client,string token)
{
if (offset > 5)
{
return;
}
try
{
//这里的参数boardId、orgId暂时需要根据自己的需要进行设置
var postData = $"offset={offset}&count=10&boardId=EqGilbKmdn62Ka3&order=&orgId=2006794";
HttpResponseMessage response = await client.GetAsync($"https://s.yiban.cn/api/forum/getListByBoard?" + postData);
if (response.IsSuccessStatusCode)
{
string responseContent = await response.Content.ReadAsStringAsync();
// 将JSON字符串反序列化为动态对象
dynamic responseBody = JsonConvert.DeserializeObject<dynamic>(responseContent);
Console.WriteLine(responseBody);
if (responseBody.code == "200")
{
var ResultType = responseBody.data.GetType();
if (ResultType.Name == "JArray")
{
return;
}
dynamic List = responseBody.data.list;
foreach (dynamic item in List)
{
Console.WriteLine(item.id);
Random random = new Random();
// 生成一个 5000 到 20000 毫秒之间的随机整数(即 5 到 20 秒)
int sleepTime = random.Next(5000, 20001); // 5000 到 20000 毫秒
Console.WriteLine($"Sleeping for {sleepTime / 1000.0} seconds...");
await Task.Delay(sleepTime); // 异步睡眠
await GetPostDetail(client, Convert.ToString(item.id), token);
}
offset++;
await GetArticleList(offset, pushPlus,client, token);
}
else
{
Console.WriteLine(responseBody.message);
}
}
else
{
pushPlus.content = $"文章信息失败!";
await SendPushPlus(pushPlus);
//Console.WriteLine("文章信息失败!");
}
//}
}
catch (Exception ex)
{
pushPlus.content = $"文章点赞异常,Error: {ex.Message}";
await SendPushPlus(pushPlus);
}
finally
{
}
}
static async Task GetPostDetail(HttpClient client, string articleId,string token)
{
HttpResponseMessage response = await client.GetAsync($"https://s.yiban.cn/api/forum/detail?callback=fetchUser&postId=" + articleId);
if (response.IsSuccessStatusCode)
{
string responseContent = await response.Content.ReadAsStringAsync();
// 将JSON字符串反序列化为动态对象
dynamic responseBody = JsonConvert.DeserializeObject<dynamic>(responseContent);
if (responseBody.code == "200")
{
Console.WriteLine("文章信息获取成功!");
var userId = responseBody.data.user.id;
//获取成功发起点赞
var _postData = new
{
action = "up",
postId = articleId,
userId
};
var postData = JsonConvert.SerializeObject(_postData);
var _data = new StringContent(postData, Encoding.UTF8, "application/json");
HttpResponseMessage responseLike = await client.PostAsync($"https://s.yiban.cn/api/post/thumb", _data);
if (responseLike.IsSuccessStatusCode)
{
responseContent = await responseLike.Content.ReadAsStringAsync();
responseBody = JsonConvert.DeserializeObject<dynamic>(responseContent);
if(responseBody.code == "200")
{
Console.WriteLine("点赞成功");
}
}
//对文章评论发起点赞
await PrimaryComment(client,articleId,token);
}
}
}
/// <summary>
/// 对文章评论发起点赞
/// </summary>
/// <param name="client"></param>
/// <param name="articleId"></param>
/// <returns></returns>
static async Task PrimaryComment(HttpClient client, string articleId, string token)
{
HttpResponseMessage response = await client.GetAsync($"https://s.yiban.cn/api/forum/primaryComment?postId={articleId}&offset=0&count=20");
if (response.IsSuccessStatusCode)
{
var responseContent = await response.Content.ReadAsStringAsync();
var responseBody = JsonConvert.DeserializeObject<dynamic>(responseContent);
if (responseBody.code == "200")
{
Console.WriteLine("评论列表获取成功!");
dynamic List = responseBody.data?.list;
foreach (dynamic item in List)
{
var commentId = item.id;
var userId = item.user.id;
Random random = new Random();
int sleepTime = random.Next(3000, 5000); // 3000 到 20000 毫秒
Console.WriteLine($"Sleeping for {sleepTime / 1000.0} seconds...");
await Task.Delay(sleepTime); // 异步睡眠
var _postData = new
{
action = "up",
postId = articleId,
userId,
commentId
};
var postData = JsonConvert.SerializeObject(_postData);
var _data = new StringContent(postData, Encoding.UTF8, "application/json");
HttpResponseMessage responseLike = await client.PostAsync($"https://s.yiban.cn/api/post/thumbComment", _data);
if (responseLike.IsSuccessStatusCode)
{
responseContent = await responseLike.Content.ReadAsStringAsync();
responseBody = JsonConvert.DeserializeObject<dynamic>(responseContent);
if (responseBody.code == "200")
{
Console.WriteLine("点赞成功");
}
}
if (!string.IsNullOrWhiteSpace(token))
{
//刷评论
}
}
}
}
}