using JetBrains.Annotations;
namespace Dpz.Core.WebApi.Controllers;
/// <summary>
/// 弹幕管理
/// </summary>
[ApiController, Route("api/[controller]"), Authorize(Policy = "System")]
public class DanmakuController(IBarrageService barrageService, ILogger<DanmakuController> logger)
: ControllerBase
{
/// <summary>
/// 获取弹幕
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
[HttpGet]
[ProducesResponseType<List<VmBarrage>>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<IActionResult> GetDanmaku([FromQuery] DanmakuQueryDto query)
{
var list = await barrageService.GetPageAsync(
query.PageIndex,
query.PageSize,
query.Text,
query.Group
);
list.AddPaginationMetadata(Response.Headers);
return Ok(list);
}
/// <summary>
/// 获取弹幕分组
/// </summary>
/// <returns></returns>
[HttpGet("group")]
[ProducesResponseType<List<string>>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<IActionResult> GetGroups()
{
var groups = await barrageService.GetGroupsAsync();
return Ok(groups);
}
/// <summary>
/// 删除弹幕
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<IActionResult> Delete([FromBody] [Required] string[] id)
{
await barrageService.DeleteAsync(id);
return NoContent();
}
/// <summary>
/// 导入A站弹幕
/// </summary>
/// <returns></returns>
[HttpPost("import/acfun")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<IActionResult> ImportAcFun([FromForm] ImportDanmaku import)
{
var extensionName = Path.GetExtension(import.File.FileName);
if (extensionName != ".json")
{
return NoContent();
}
List<AcFunDanmaku> danmakus;
try
{
danmakus =
await JsonSerializer.DeserializeAsync<List<AcFunDanmaku>>(
import.File.OpenReadStream(),
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
) ?? [];
}
catch (Exception e)
{
logger.LogError(e, "import acfun danmaku fail");
return BadRequest(e.Message);
}
if (danmakus.Count == 0)
{
return BadRequest("no danmaku");
}
var source = danmakus
.Select(x => new VmBarrage
{
Group = import.Group,
Color = x.Color.ToString(),
Position = x.Mode == 5 ? 1 : 0,
SendTime = x.GetCreateTime(),
Size = x.Size,
Text = x.Body,
Time = x.Position / (decimal)1e3,
})
.ToArray();
await barrageService.ImportAsync(source);
return NoContent();
}
/// <summary>
/// 导入B站弹幕
/// </summary>
/// <returns></returns>
[HttpPost("import/bilibili")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<IActionResult> ImportBilibili([FromForm] ImportDanmaku import)
{
var extensionName = Path.GetExtension(import.File.FileName);
if (extensionName != ".xml")
{
return NoContent();
}
try
{
var source = await XElement.LoadAsync(
import.File.OpenReadStream(),
LoadOptions.None,
CancellationToken.None
);
var bilibiliDanmakus = (
from x in source.Elements("d")
let p = x.Attribute("p")
where p != null && !string.IsNullOrEmpty(p.Value)
let props = p.Value.Split(",")
where props.Length >= 5
let type = props[1] switch
{
"1" => DanmakuType.Rolling,
"4" => DanmakuType.Bottom,
"5" => DanmakuType.Top,
_ => DanmakuType.Rolling,
}
select new VmBarrage
{
Group = import.Group,
Color = props[3],
Position = (int)type,
SendTime = long.Parse(props[4]).ToDateTime(),
Size = int.Parse(props[2]),
Text = x.Value,
Time = decimal.Parse(props[0]),
}
).ToArray();
await barrageService.ImportAsync(bilibiliDanmakus);
return NoContent();
}
catch (Exception e)
{
logger.LogError(e, "import bilibili danmaku fail");
return BadRequest(e.Message);
}
}
/// <summary>
/// AcFun弹幕
/// </summary>
[UsedImplicitly]
private record AcFunDanmaku(
int Color,
int Mode,
long CreateTime,
string? Body,
int Position,
int Size
)
{
public DateTime GetCreateTime()
{
var time = CreateTime / 1000D;
var utcTime = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero).AddSeconds(time);
return utcTime.LocalDateTime;
}
}
private enum DanmakuType
{
Rolling = 0,
Top = 1,
Bottom = 2,
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
这是一个ASP.NET Core Web API控制器,用于管理弹幕系统。让我详细解释这个代码的功能和结构:
控制器概述
DanmakuController 是一个弹幕管理控制器,具有以下特点:
- 使用主构造函数注入
IBarrageService和ILogger - 需要 "System" 策略授权
- API路由为
api/Danmaku
主要功能
1. 获取弹幕 (GET /api/Danmaku)
[HttpGet]
public async Task<IActionResult> GetDanmaku([FromQuery] DanmakuQueryDto query)
- 支持分页查询弹幕
- 可按文本内容和分组筛选
- 返回弹幕列表并添加分页元数据到响应头
2. 获取弹幕分组 (GET /api/Danmaku/group)
[HttpGet("group")]
public async Task<IActionResult> GetGroups()
- 返回所有可用的弹幕分组列表
3. 删除弹幕 (DELETE /api/Danmaku)
[HttpDelete]
public async Task<IActionResult> Delete([FromBody] [Required] string[] id)
- 批量删除指定ID的弹幕
- 接收ID数组作为请求体
4. 导入A站弹幕 (POST /api/Danmaku/import/acfun)
[HttpPost("import/acfun")]
public async Task<IActionResult> ImportAcFun([FromForm] ImportDanmaku import)
- 导入AcFun平台的弹幕文件(JSON格式)
- 解析AcFun特定的弹幕数据结构
- 转换为通用的
VmBarrage格式
5. 导入B站弹幕 (POST /api/Danmaku/import/bilibili)
[HttpPost("import/bilibili")]
public async Task<IActionResult> ImportBilibili([FromForm] ImportDanmaku import)
- 导入哔哩哔哩平台的弹幕文件(XML格式)
- 解析B站XML弹幕格式
- 支持不同弹幕类型(滚动、顶部、底部)
内部类型定义
AcFunDanmaku Record
private record AcFunDanmaku(int Color, int Mode, long CreateTime, string? Body, int Position, int Size)
- 表示AcFun弹幕的数据结构
- 包含
GetCreateTime()方法将时间戳转换为本地时间
DanmakuType 枚举
private enum DanmakuType { Rolling = 0, Top = 1, Bottom = 2 }
- 定义弹幕显示位置类型
关键特性
- 错误处理: 所有导入操作都包含异常处理和日志记录
- 文件验证: 检查上传文件的扩展名
- 数据转换: 将不同平台的弹幕格式转换为统一的内部格式
- 异步操作: 所有数据库操作都是异步的
- RESTful设计: 遵循REST API设计规范
- 文档注释: 完整的XML文档注释用于API文档生成
这个控制器为弹幕系统提供了完整的CRUD操作以及多平台弹幕导入功能,是一个功能完整的弹幕管理API。
AI 正在分析代码…
评论加载中...