206 lines
8.2 KiB
C#
206 lines
8.2 KiB
C#
using System.ComponentModel;
|
||
using System.Text.RegularExpressions;
|
||
using ACBuildService.Platforms.KuaiShow;
|
||
using Downloader;
|
||
using Newtonsoft.Json;
|
||
using RestSharp;
|
||
|
||
namespace ACBuildService;
|
||
|
||
public class KuaiShou : DownloadPlatforms
|
||
{
|
||
private static readonly Dictionary<string, string> _locationHeaders = new()
|
||
{
|
||
{
|
||
"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"
|
||
},
|
||
{ "Accept-Language", "zh-CN,zh;q=0.9" },
|
||
{ "Cache-Control", "no-cache" },
|
||
{ "Connection", "keep-alive" },
|
||
{ "DNT", "1" },
|
||
{ "Host", "www.kuaishou.com" },
|
||
{ "Pragma", "no-cache" },
|
||
{ "Sec-Fetch-Dest", "document" },
|
||
{ "Sec-Fetch-Mode", "navigate" },
|
||
{ "Sec-Fetch-Site", "none" },
|
||
{ "Sec-Fetch-User", "?1" },
|
||
{ "Upgrade-Insecure-Requests", "1" },
|
||
{
|
||
"UserAgent",
|
||
"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-ch-ua", "\"Chromium\";v=\"128\", \"Not;A=Brand\";v=\"24\", \"Google Chrome\";v=\"128\"" },
|
||
{ "sec-ch-ua-mobile", "?1" },
|
||
{ "sec-ch-ua-platform", "\"Android\"" }
|
||
// {
|
||
// "Cookie",
|
||
// "kpf=PC_WEB; kpn=KUAISHOU_VISION; clientid=3; did=web_6ece7cfdd334f69ac1fe2579040329d0; didv=1725957114469"
|
||
// }
|
||
};
|
||
|
||
private static readonly Dictionary<string, string> _videoInfoHeaders = new()
|
||
{
|
||
{
|
||
"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"
|
||
},
|
||
{ "Accept-Encoding", "gzip, deflate, br, zstd" },
|
||
{ "Accept-Language", "zh-CN,zh;q=0.9" },
|
||
{ "Cache-Control", "no-cache" },
|
||
{ "Connection", "keep-alive" },
|
||
{
|
||
"Cookie",
|
||
"kpf=PC_WEB; clientid=3; did=web_5ee38d442bc5387e413eeeefc42ed4a2; didv=1734437469000; kpn=KUAISHOU_VISION"
|
||
},
|
||
{ "DNT", "1" },
|
||
{ "Host", "m.gifshow.com" },
|
||
{ "Origin", "https://m.gifshow.com" },
|
||
{ "Pragma", "no-cache" },
|
||
// {
|
||
// "Referer",
|
||
// "https://m.gifshow.com/fw/photo/3xvfmfagspjxq9q?cc=share_copylink&kpf=PC_WEB&utm_campaign=pc_share&shareMethod=token&utm_medium=pc_share&kpn=KUAISHOU_VISION&subBiz=SINGLE_ROW_WEB&ztDid=web_126778f97e238efa29915c708f0789b6&shareId=18063407013272&shareToken=X-1KuDdzw7LGTYAM&shareMode=app&efid=0&shareObjectId=3xvfmfagspjxq9q&utm_source=pc_share"
|
||
// },
|
||
{ "Sec-Fetch-Dest", "document" },
|
||
{ "Sec-Fetch-Mode", "navigate" },
|
||
{ "Sec-Fetch-Site", "none" },
|
||
{
|
||
"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"
|
||
},
|
||
{ "content-type", "application/json" },
|
||
{ "sec-ch-ua", "\"Chromium\";v=\"128\", \"Not;A=Brand\";v=\"24\", \"Google Chrome\";v=\"128\"" },
|
||
{ "sec-ch-ua-mobile", "?1" },
|
||
{ "sec-ch-ua-platform", "\"Android\"" }
|
||
};
|
||
|
||
|
||
public override async Task<string> ExtractUrlAsync(string text)
|
||
{
|
||
Log.Info($"开始解析快手链接 {text}");
|
||
return text.Trim();
|
||
}
|
||
|
||
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);
|
||
|
||
return Data;
|
||
}
|
||
|
||
public override async Task<VideoModel> ExtractVideoDataAsync(string url)
|
||
{
|
||
try
|
||
{
|
||
Log.Info($"开始解析链接 {url}");
|
||
|
||
var client = new RestClient();
|
||
var locationRequest = new RestRequest(url);
|
||
|
||
locationRequest.AddHeaders(_locationHeaders);
|
||
var locationResponse = client.Execute(locationRequest);
|
||
|
||
if (locationResponse.ResponseUri is null)
|
||
{
|
||
Log.Error("获取location参数失败");
|
||
throw new InvalidDataException("获取location参数失败");
|
||
}
|
||
|
||
// 发送请求并获取响应
|
||
Log.Info($"开始解析location,reponse {locationResponse.ResponseUri.AbsoluteUri}");
|
||
|
||
var videoRequestUrl =
|
||
locationResponse.ResponseUri.AbsoluteUri.Replace("https://www.kuaishou.com/short-video",
|
||
"https://m.gifshow.com/fw/photo");
|
||
var videoRequest = new RestRequest(videoRequestUrl);
|
||
videoRequest.AddHeaders(_videoInfoHeaders);
|
||
var videoResponse = client.Execute(videoRequest);
|
||
|
||
if (!videoResponse.IsSuccessful) throw new HttpRequestException("request is fail");
|
||
|
||
var content = videoResponse.Content;
|
||
Log.Info($"开始解析响应内容 {content}");
|
||
if (content is null) throw new InvalidDataException("content is null");
|
||
|
||
const string routerDataPattern = @"""photo"":\s*\{(.*?)\},\s*""serialInfo""";
|
||
|
||
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<KuaishouShareVideoData>(videoJson);
|
||
if (videoData is null) throw new InvalidDataException("JSON解析数据为空,请检查分享链接是否正确,如有更多问题请查看日志");
|
||
return new VideoModel
|
||
{
|
||
Platform = ShortVideoPlatformEnum.KuaiShou,
|
||
VideoId = videoData.Manifest.VideoId,
|
||
AuthorName = videoData.UserName,
|
||
AuthorAvatar = videoData.HeadUrl.ToString(),
|
||
UniqueId = videoData.Manifest.VideoId,
|
||
Title = videoData.Caption,
|
||
Cover = videoData.CoverUrls.First().Url.ToString(),
|
||
VideoUrl = videoData.MainMvUrls.First().Url.ToString(),
|
||
Mp3Url = "",
|
||
CreatedTime =
|
||
DateTimeOffset.FromUnixTimeMilliseconds(videoData.Timestamp)
|
||
.ToString("yyyy-MM-dd HH:mm:ss"),
|
||
Desc = videoData.Caption,
|
||
Duration = videoData.Duration.ToString(),
|
||
DiggCount = videoData.LikeCount,
|
||
ViewCount = videoData.ViewCount,
|
||
CollectCount = 0,
|
||
CommentCount = videoData.CommentCount,
|
||
ShareCount = videoData.ShareCount
|
||
};
|
||
}
|
||
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, // Retry up to 5 times on failure
|
||
Timeout = 10000, // 10 seconds timeout for each request
|
||
RequestConfiguration = new RequestConfiguration
|
||
{
|
||
UserAgent = _videoInfoHeaders.GetValueOrDefault("User-Agent")
|
||
}
|
||
};
|
||
|
||
DownloadService downloader = new(downloadConfiguration);
|
||
|
||
downloader.DownloadProgressChanged += onProgressChanged;
|
||
downloader.DownloadFileCompleted += onProgressCompleted;
|
||
|
||
try
|
||
{
|
||
await downloader.DownloadFileTaskAsync(url, savePath + fileName);
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Info($"Download failed: {ex}");
|
||
}
|
||
|
||
return false;
|
||
}
|
||
} |