Files
2026-02-08 17:47:11 +08:00

178 lines
6.7 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.ComponentModel;
using System.Text.RegularExpressions;
using ACBuildService.Platforms.DouYin;
using Downloader;
using Newtonsoft.Json;
using RestSharp;
namespace ACBuildService;
public class DouYin : DownloadPlatforms
{
private static readonly Dictionary<string, string> _defaultHeaders = new()
{
{
"User-Agent",
"Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36"
},
{ "sec-fetch-site", "same-origin" },
{ "sec-fetch-mode", "cors" },
{ "sec-fetch-dest", "empty" },
{ "sec-ch-ua-platform", "Windows" },
{ "sec-ch-ua-mobile", "?0" },
{ "sec-ch-ua", "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\"" },
{ "referer", "https://www.douyin.com/?recommend=1" },
{ "priority", "u=1, i" },
{ "pragma", "no-cache" },
{ "cache-control", "no-cache" },
{ "accept-language", "zh-CN,zh;q=0.9,en;q=0.8" },
{
"accept",
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
},
{ "dnt", "1" }
};
public async override Task<string> ExtractUrlAsync(string text)
{
Log.Info($"开始解析抖音链接 {text}");
return Regex.Match(text, @"https?://[^\s]+").Value;
}
public override async Task<VideoModel> ParseShare(string DownloadUrlText)
{
if (!DownloadUrlText.Contains("https://"))
{
Log.Error("请输入正确的分享链接");
return null;
}
var downloadUrl = await ExtractUrlAsync(DownloadUrlText);
VideoModel Data = await ExtractVideoDataAsync(downloadUrl);
Data.ShareId = downloadUrl.Replace("https://v.douyin.com/", "").Replace("/", "");
return Data;
}
/// <summary>
/// 根据链接 https://v.douyin.com/ircqoExo/ 解析出页面中的数据
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public override Task<VideoModel> ExtractVideoDataAsync(string url)
{
try
{
Log.Info($"开始解析链接 {url}");
// 创建RestClient
RestClient client = new(url);
// 创建请求对象
RestRequest request = new();
// 设置 User-Agent 模拟手机浏览器
request.AddHeaders(_defaultHeaders);
// 发送请求并获取响应
var response = client.Execute(request);
if (!response.IsSuccessful) throw new HttpRequestException("request is fail");
var content = response.Content;
Log.Info($"开始解析响应内容 {content}");
if (content is null) throw new InvalidDataException("content is null");
const string routerDataPattern = @"_ROUTER_DATA\s*=\s*(\{.*?\})<";
var matchJson = Regex.Match(content, routerDataPattern);
Log.Info($"开始解析匹配到的json {matchJson}");
if (matchJson.Groups.Count < 2) throw new InvalidDataException("未匹配到合法的数据matchJson.Groups.Count < 2");
var videoJson = matchJson.Groups[1].Value;
Log.Info($"开始解析匹配到的json {videoJson}");
// 反序列化JSON字符串为C#对象
var videoData = JsonConvert.DeserializeObject<DouYinShareRouterData>(videoJson);
if (videoData is null) throw new InvalidDataException("JSON解析数据为空请检查分享链接是否正确如有更多问题请查看日志");
var videoInfoData = videoData.LoaderData.VideoIdPage.VideoInfoRes.ItemList.First();
var video = new VideoModel
{
Platform = ShortVideoPlatformEnum.DouYin,
VideoId = videoInfoData.AwemeId,
AuthorName = videoInfoData.Author.Nickname,
UniqueId = videoInfoData.Author.UniqueId == ""
? videoInfoData.Author.ShortId
: videoInfoData.Author.UniqueId,
AuthorAvatar = videoInfoData.Author.AvatarThumb.UrlList.First().ToString(),
Title = videoInfoData.Author.Signature,
Cover = videoInfoData.Video.Cover.UrlList.Last().ToString(),
Mp3Url = "",
CreatedTime =
DateTimeOffset.FromUnixTimeSeconds(videoInfoData.CreateTime)
.ToString("yyyy-MM-dd HH:mm:ss"),
Desc = videoInfoData.Desc,
Duration = "",
DiggCount = videoInfoData.Statistics.DiggCount,
CollectCount = videoInfoData.Statistics.CollectCount,
CommentCount = videoInfoData.Statistics.CommentCount,
ShareCount = videoInfoData.Statistics.ShareCount
};
switch (videoInfoData.AwemeType)
{
case 2:
video.VideoUrl = videoInfoData.Video.Cover.UrlList.First().ToString();
break;
default:
video.VideoUrl = videoInfoData.Video.PlayAddr.UrlList.First().ToString().Replace("playwm", "play");
break;
}
if (video.VideoUrl.Contains("ratio=720p"))
{
video.VideoUrl = video.VideoUrl.Replace("ratio=720p", "ratio=1080p");
}
return Task.FromResult(video);
}
catch (Exception e)
{
Log.Error(e.Message);
throw;
}
}
public override async Task<bool> DownloadAsync(string url, string savePath, string fileName,
EventHandler<DownloadProgressChangedEventArgs> onProgressChanged,
EventHandler<AsyncCompletedEventArgs> onProgressCompleted)
{
DownloadConfiguration downloadConfiguration = new()
{
ChunkCount = 8, // Download in 8 chunks (increase for larger files)
MaxTryAgainOnFailure = 5,
Timeout = 10000, // 10 seconds timeout for each request
RequestConfiguration = new RequestConfiguration
{
UserAgent = _defaultHeaders.GetValueOrDefault("User-Agent")
}
};
DownloadService downloader = new(downloadConfiguration);
downloader.DownloadProgressChanged += onProgressChanged;
downloader.DownloadFileCompleted += onProgressCompleted;
try
{
await downloader.DownloadFileTaskAsync(url, Path.Combine(savePath, fileName));
return true;
}
catch (Exception ex)
{
Log.Info($"Download failed: {ex}");
}
return false;
}
}