using Downloader;
using Microsoft.Extensions.DependencyInjection;
using SqlSugar;
using SqlSugar.IOC;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using VideoAnalysisCore.Job;
using VideoAnalysisCore.Model;
using VideoAnalysisCore.Model.Enum;
using AlibabaCloud.SDK.Vod20170321;
using UserCenter.Model.Enum;
using System.Security.Policy;
using AlibabaCloud.SDK.Vod20170321.Models;
namespace VideoAnalysisCore.Common
{
public static class DownloadFileExpand
{
///
/// 初始化下载器
///
/// 下载速度mb/s
public static void AddDownloadFileExpand(this IServiceCollection services)
{
DownloadFile.DownloadSpeed = AppCommon.Config.TaskSetting.DownloadSpeed;
services.AddTransient();
DownloadFile.Opt = new DownloadConfiguration()
{
// 通常,主机支持的最大值为8000字节,默认值是8000
BufferBlockSize = 10240,
// 要下载的文件部分数量,默认值是1
ChunkCount = 8,
// 下载速度限制为2MB/秒,默认值为零(即无限制)
MaximumBytesPerSecond = 1024 * 1024 * DownloadFile.DownloadSpeed,
// 故障转移时的最大重试次数
MaxTryAgainOnFailover = 5,
// 每50MB后释放内存缓冲区
MaximumMemoryBufferBytes = 1024 * 1024 * 50,
// 是否并行下载文件的各个部分。默认值为false
ParallelDownload = true,
// 并行下载的数量。默认值与分块数量相同
ParallelCount = 4,
// 每个流块读取器的超时时间(毫秒),默认值是1000
Timeout = 1000,
// 如果只想下载大型文件的特定字节范围,则设置为true
RangeDownload = false,
// 大型文件下载范围的起始偏移量
RangeLow = 0,
// 大型文件下载范围的结束偏移量
RangeHigh = 0,
// 下载失败完成时清除包块数据,默认值为false
ClearPackageOnCompletionWithFailure = true,
// 将文件分多个部分下载时的最小分块大小,默认值是512
MinimumSizeOfChunking = 1024,
// 在开始下载之前,按照文件大小预留文件的存储空间,默认值为false
ReserveStorageSpaceBeforeStartingDownload = true,
// 在下载进度改变事件中,通过ReceivedBytes获取按需下载的数据
EnableLiveStreaming = false,
// 配置和自定义请求头
RequestConfiguration =
{
Accept = "*/*",
//CookieContainer = cookies,
Headers = new WebHeaderCollection(), // { 你的自定义头部信息 }
KeepAlive = true, // 默认值为false
ProtocolVersion = HttpVersion.Version11, // 默认值是HTTP 1.1
UseDefaultCredentials = false,
// 你的自定义用户代理或你的应用名称/应用版本。
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
//Proxy = new WebProxy()//使用代理
//{
// Address = new Uri("http://Learn.VideoAnalysis"),
// UseDefaultCredentials = false,
// Credentials = System.Net.CredentialCache.DefaultNetworkCredentials,
// BypassProxyOnLocal = true
//}
}
};
}
}
///
///
///
public class DownloadFile
{
public static DownloadConfiguration Opt { get; set; } = default!;
public static int DownloadSpeed { get; set; } = default!;
private readonly Repository videoTaskDB;
private readonly Repository packageInfoTaskDB;
private readonly Client vodClient;
private readonly RedisManager redisManager;
private readonly IServiceProvider serviceProvider;
readonly string taskVideoName = "task.mp4";
readonly string taskPPTVideoName = "ppt.mp4";
// 注入工作流管理器,用于更新进度
private readonly VideoSliceWorkflowManager _videoSliceWorkflowManager;
private readonly TidySlideWorkflowManager _tidySlideWorkflowManager;
public DownloadFile(Repository videoTaskDB, Client vodClient, Repository nackageInfoTaskDB, RedisManager redisManager, IServiceProvider serviceProvider, VideoSliceWorkflowManager videoSliceWorkflowManager, TidySlideWorkflowManager tidySlideWorkflowManager)
{
this.videoTaskDB = videoTaskDB;
this.vodClient = vodClient;
this.packageInfoTaskDB = nackageInfoTaskDB;
this.redisManager = redisManager;
this.serviceProvider = serviceProvider;
_videoSliceWorkflowManager = videoSliceWorkflowManager;
_tidySlideWorkflowManager = tidySlideWorkflowManager;
}
// 辅助方法:根据工作流名称获取对应的管理器实例
private dynamic GetWorkflowManager(string workflowName)
{
if (workflowName == "TidySlideWorkflow") return _tidySlideWorkflowManager;
return _videoSliceWorkflowManager;
}
// 根据 Content-Type 映射文件后缀
static string GetExtensionFromContentType(HttpResponseMessage res)
{
var contentType = res.Content.Headers.ContentType?.MediaType;
return contentType switch
{
"application/pdf" => ".pdf",
"image/jpeg" => ".jpg",
"image/png" => ".png",
"application/zip" => ".zip",
"text/html" => ".html",
"audio/wav" => ".wav",
"audio/mp4" => ".mp4",
"audio/mp3" => ".mp3",
// 根据需要添加其他 Content-Type 映射
_ => ".mp4", // 默认二进制文件
};
}
///
/// 使用 HttpClient 下载任务的资源文件到本地
///
///
///
public async Task RunTask(string task, string workflowName = "VideoSliceWorkflow")
{
var taskId = long.Parse(task);
//获取资源文件 地址
var taskInfo = await videoTaskDB.CopyNew().AsQueryable()
.Where(s => s.Id == taskId).FirstAsync();
if (taskInfo is null )
throw new Exception($"任务为null/是教研视频/没有视频课程名称");
var fileUrl = taskInfo.MediaUrl;
switch (taskInfo?.VideoType)
{
case AttachmentsInfoType.无:
case AttachmentsInfoType.新课:
case AttachmentsInfoType.复习:
break;
default:
throw new Exception("无效的课程类型");
}
if (string.IsNullOrEmpty(fileUrl))
{
var videoInfo = await vodClient.GetPlayInfoAsync(new GetPlayInfoRequest()
{
VideoId = taskInfo.TagId,
Formats = "mp4",
OutputType = "cdn",
AuthTimeout = 3600 * 24 * 12,
});
if (videoInfo is null || videoInfo.StatusCode != 200 && !videoInfo.Body.PlayInfoList.PlayInfo.Any())
throw new Exception($"{DateTime.Now} 视频订阅=>获取阿里云视频信息失败 VideoCode {taskInfo.TagId} StatusCode {videoInfo?.StatusCode}");
fileUrl = videoInfo.Body.PlayInfoList.PlayInfo.First().PlayURL;
}
if (string.IsNullOrEmpty(fileUrl))
throw new Exception($"任务id[{task}] 资源地址无效 {fileUrl}");
// 尝试从 URL 中获取文件后缀
string fileExtension = Path.GetExtension(new Uri(fileUrl).AbsolutePath);
//否则 获取 从Content-Type 获取文件后缀
if (string.IsNullOrEmpty(fileExtension))
throw new Exception($"未能从资源路径中获取文件后缀");
//创建下载文件缓存路径
if (!Directory.Exists(AppCommon.TaskCachedFile)) Directory.CreateDirectory(AppCommon.TaskCachedFile);
var localPath = task.LocalPath();
var outputPath = Path.Combine(localPath, taskVideoName);
if (!Directory.Exists(localPath)) Directory.CreateDirectory(localPath);
await videoTaskDB.CopyNew()
.AsUpdateable()
.SetColumns(it => it.LocalMediaPath == outputPath)
.Where(it => it.Id == long.Parse(task))
.ExecuteCommandAsync();
//下载PPT视频
if (string.IsNullOrEmpty(taskInfo.PPTVideoUrl)&&
!string.IsNullOrEmpty(taskInfo.PPTVideoCode))
{
taskInfo.PPTVideoUrl = await packageInfoTaskDB.AsQueryable()
.Where(s => s.VideoCode == taskInfo.PPTVideoCode)
.Select(s => s.VideoUrl)
.FirstAsync();
if (!string.IsNullOrEmpty(taskInfo.PPTVideoUrl))
await videoTaskDB.CopyNew().AsUpdateable(taskInfo)
.UpdateColumns(it => new { it.PPTVideoUrl })
.ExecuteCommandAsync();
}
if (!string.IsNullOrEmpty(taskInfo.PPTVideoUrl))
{
await Download(taskInfo.PPTVideoUrl, localPath, taskPPTVideoName,
(s, e) => GetWorkflowManager(workflowName).SetTaskProgress(task, "PPT->" + Math.Round(e.ProgressPercentage, 1))
);
}
try
{
//下载原视频
await Download(fileUrl, localPath, taskVideoName,
(s, e) => GetWorkflowManager(workflowName).SetTaskProgress(task, Math.Round(e.ProgressPercentage, 1))
);
}
catch
{
throw;
}
}
///
/// 下载文件
///
///
///
///
///
public async Task Download(string fileUrl, string localPath, string fileName,Action