mirror of
https://github.com/bolucat/Archive.git
synced 2025-09-26 20:21:35 +08:00
225 lines
8.9 KiB
C#
225 lines
8.9 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Data;
|
||
using System.Linq;
|
||
using System.Net.Http;
|
||
using System.Net.Http.Json;
|
||
using System.Text.Json;
|
||
using System.Text.Json.Serialization;
|
||
using System.Text.Json.Serialization.Metadata;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using BBDown.Core;
|
||
using Microsoft.AspNetCore.Builder;
|
||
using Microsoft.AspNetCore.Http;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
namespace BBDown;
|
||
|
||
public class BBDownApiServer
|
||
{
|
||
private WebApplication? app;
|
||
private readonly List<DownloadTask> runningTasks = [];
|
||
private readonly List<DownloadTask> finishedTasks = [];
|
||
|
||
public void SetUpServer()
|
||
{
|
||
if (app is not null) return;
|
||
var builder = WebApplication.CreateSlimBuilder();
|
||
builder.Services.ConfigureHttpJsonOptions((options) =>
|
||
{
|
||
options.SerializerOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine(options.SerializerOptions.TypeInfoResolver, AppJsonSerializerContext.Default);
|
||
});
|
||
builder.Services.AddCors((options) =>
|
||
{
|
||
options.AddPolicy("AllowAnyOrigin",
|
||
policy =>
|
||
{
|
||
policy.AllowAnyOrigin()
|
||
.AllowAnyMethod()
|
||
.AllowAnyHeader();
|
||
});
|
||
});
|
||
app = builder.Build();
|
||
app.UseCors("AllowAnyOrigin");
|
||
var taskStatusApi = app.MapGroup("/get-tasks");
|
||
taskStatusApi.MapGet("/", handler: () => Results.Json(new DownloadTaskCollection(runningTasks, finishedTasks), AppJsonSerializerContext.Default.DownloadTaskCollection));
|
||
taskStatusApi.MapGet("/running", handler: () => Results.Json(runningTasks, AppJsonSerializerContext.Default.ListDownloadTask));
|
||
taskStatusApi.MapGet("/finished", handler: () => Results.Json(finishedTasks, AppJsonSerializerContext.Default.ListDownloadTask));
|
||
taskStatusApi.MapGet("/{id}", (string id) =>
|
||
{
|
||
var task = finishedTasks.FirstOrDefault(a => a.Aid == id);
|
||
var rtask = runningTasks.FirstOrDefault(a => a.Aid == id);
|
||
if (rtask is not null) task = rtask;
|
||
if (task is null)
|
||
{
|
||
return Results.NotFound();
|
||
}
|
||
return Results.Json(task, AppJsonSerializerContext.Default.DownloadTask);
|
||
});
|
||
app.MapPost("/add-task", (MyOptionBindingResult<ServeRequestOptions> bindingResult) =>
|
||
{
|
||
if (!bindingResult.IsValid)
|
||
{
|
||
//var exception = bindingResult.Exception;
|
||
return Results.BadRequest("输入有误");
|
||
}
|
||
var req = bindingResult.Result;
|
||
_ = AddDownloadTaskAsync(req)
|
||
.ContinueWith(async task => {
|
||
// send request to callback webhook
|
||
if (string.IsNullOrEmpty(req.CallBackWebHook))
|
||
{
|
||
return;
|
||
}
|
||
string callback = req.CallBackWebHook;
|
||
var client = new HttpClient();
|
||
var downloadTask = await task;
|
||
string? jsonContent = JsonSerializer.Serialize(downloadTask, AppJsonSerializerContext.Default.DownloadTask);
|
||
try
|
||
{
|
||
await client.PostAsync(callback, new StringContent(jsonContent, System.Text.Encoding.UTF8, "application/json"));
|
||
}
|
||
catch (System.Exception e)
|
||
{
|
||
Logger.LogDebug("回调失败", e.Message);
|
||
}
|
||
});
|
||
return Results.Ok();
|
||
});
|
||
var finishedRemovalApi = app.MapGroup("remove-finished");
|
||
finishedRemovalApi.MapGet("/", () => { finishedTasks.RemoveAll(t => true); return Results.Ok(); });
|
||
finishedRemovalApi.MapGet("/failed", () => { finishedTasks.RemoveAll(t => !t.IsSuccessful); return Results.Ok(); });
|
||
finishedRemovalApi.MapGet("/{id}", (string id) => { finishedTasks.RemoveAll(t => t.Aid == id); return Results.Ok(); });
|
||
}
|
||
|
||
public void Run(string url)
|
||
{
|
||
if (app is null) return;
|
||
bool result = Uri.TryCreate(url, UriKind.Absolute, out Uri? uriResult)
|
||
&& uriResult.Scheme == Uri.UriSchemeHttp;
|
||
if (!result)
|
||
{
|
||
Console.BackgroundColor = ConsoleColor.Red;
|
||
Console.ForegroundColor = ConsoleColor.White;
|
||
Console.WriteLine($"{url}不是合法的http URL,url示例:http://0.0.0.0:5000");
|
||
Console.WriteLine("如果您需要https,请额外配置反向代理");
|
||
Console.ResetColor();
|
||
Console.WriteLine();
|
||
Thread.Sleep(1);
|
||
Environment.Exit(1);
|
||
}
|
||
app.Run(url);
|
||
}
|
||
|
||
private async Task<DownloadTask> AddDownloadTaskAsync(MyOption option)
|
||
{
|
||
var aid = await BBDownUtil.GetAvIdAsync(option.Url);
|
||
DownloadTask? runningTask = runningTasks.FirstOrDefault(task => task.Aid == aid);
|
||
if (runningTask is not null)
|
||
{
|
||
return runningTask;
|
||
};
|
||
var task = new DownloadTask(aid, option.Url, DateTimeOffset.Now.ToUnixTimeSeconds());
|
||
runningTasks.Add(task);
|
||
try
|
||
{
|
||
var (encodingPriority, dfnPriority, firstEncoding, downloadDanmaku, downloadDanmakuFormats, input, savePathFormat, lang, aidOri, delay) = Program.SetUpWork(option);
|
||
var (fetchedAid, vInfo, apiType) = await Program.GetVideoInfoAsync(option, aidOri, input);
|
||
task.Title = vInfo.Title;
|
||
task.Pic = vInfo.Pic;
|
||
task.VideoPubTime = vInfo.PubTime;
|
||
await Program.DownloadPagesAsync(option, vInfo, encodingPriority, dfnPriority, firstEncoding, downloadDanmaku, downloadDanmakuFormats,
|
||
input, savePathFormat, lang, fetchedAid, delay, apiType, task);
|
||
task.IsSuccessful = true;
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Console.BackgroundColor = ConsoleColor.Red;
|
||
Console.ForegroundColor = ConsoleColor.White;
|
||
Console.WriteLine($"{aid}下载失败");
|
||
var msg = Config.DEBUG_LOG ? e.ToString() : e.Message;
|
||
Console.Write($"{msg}{Environment.NewLine}请尝试升级到最新版本后重试!");
|
||
Console.ResetColor();
|
||
Console.WriteLine();
|
||
}
|
||
task.TaskFinishTime = DateTimeOffset.Now.ToUnixTimeSeconds();
|
||
if (task.IsSuccessful)
|
||
{
|
||
task.Progress = 1f;
|
||
task.DownloadSpeed = (double)(task.TotalDownloadedBytes / (task.TaskFinishTime - task.TaskCreateTime));
|
||
}
|
||
runningTasks.Remove(task);
|
||
finishedTasks.Add(task);
|
||
return task;
|
||
}
|
||
}
|
||
|
||
public record DownloadTask(string Aid, string Url, long TaskCreateTime)
|
||
{
|
||
[JsonInclude]
|
||
public string? Title = null;
|
||
[JsonInclude]
|
||
public string? Pic = null;
|
||
[JsonInclude]
|
||
public long? VideoPubTime = null;
|
||
[JsonInclude]
|
||
public long? TaskFinishTime = null;
|
||
[JsonInclude]
|
||
public double Progress = 0f;
|
||
[JsonInclude]
|
||
public double DownloadSpeed = 0f;
|
||
[JsonInclude]
|
||
public double TotalDownloadedBytes = 0f;
|
||
[JsonInclude]
|
||
public bool IsSuccessful = false;
|
||
|
||
[JsonInclude]
|
||
public List<string> SavePaths = new();
|
||
};
|
||
public record DownloadTaskCollection(List<DownloadTask> Running, List<DownloadTask> Finished);
|
||
|
||
record struct MyOptionBindingResult<T>(T? Result, Exception? Exception)
|
||
{
|
||
public bool IsValid => Exception is null;
|
||
|
||
public static async ValueTask<MyOptionBindingResult<T>> BindAsync(HttpContext httpContext)
|
||
{
|
||
try
|
||
{
|
||
JsonTypeInfo? jsonTypeInfo = SourceGenerationContext.Default.GetTypeInfo(typeof(T));
|
||
if (jsonTypeInfo is null)
|
||
{
|
||
return new(default, new InvalidOperationException($"Cannot find TypeInfo for type {typeof(T)}"));
|
||
}
|
||
var item = await httpContext.Request.ReadFromJsonAsync(jsonTypeInfo);
|
||
|
||
if (item is null) return new(default, new NoNullAllowedException());
|
||
|
||
return new((T)item, null);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return new(default, ex);
|
||
}
|
||
}
|
||
}
|
||
|
||
[JsonSerializable(typeof(ProblemDetails))]
|
||
[JsonSerializable(typeof(ValidationProblemDetails))]
|
||
[JsonSerializable(typeof(HttpValidationProblemDetails))]
|
||
[JsonSerializable(typeof(DownloadTask))]
|
||
[JsonSerializable(typeof(List<DownloadTask>))]
|
||
[JsonSerializable(typeof(DownloadTaskCollection))]
|
||
public partial class AppJsonSerializerContext : JsonSerializerContext
|
||
{
|
||
|
||
}
|
||
|
||
[JsonSerializable(typeof(MyOption))]
|
||
[JsonSerializable(typeof(ServeRequestOptions))]
|
||
internal partial class SourceGenerationContext : JsonSerializerContext
|
||
{
|
||
|
||
}
|