using System.Drawing;
using Dpz.Core.Shard.Service;
using Dpz.Core.Web.Models.Video;

namespace Dpz.Core.Web.Controllers;

public class VideoController(
    IBarrageService barrageService,
    ILogger<VideoController> logger,
    IVideoShardService videoShardService
) : Controller
{
    public async Task<IActionResult> Index()
    {
        this.SetTitle("视频");
        var videos = await videoShardService.GetVideosAsync();
        return View(videos);
    }

    [Route("video/watch/{id}.html")]
    public async Task<IActionResult> Watch(string id)
    {
        var video = await videoShardService.GetVideoAsync(id);
        if (video == null)
        {
            return NotFound();
        }

        this.SetTitle(video.VideoTitle ?? "");
        return View(video);
    }

    [HttpPatch("play/{id}")]
    public async Task<IActionResult> Play(string id)
    {
        await videoShardService.AddPlayAsync(id);
        return NoContent();
    }

    [Route("history/chat/v3/"), HttpGet]
    public async Task<IActionResult> HistoryChat(string id)
    {
        var dbDanmaku = await barrageService.GetGroupBarragesAsync(id);
        var allDanmaku = dbDanmaku.Select(x =>
        {
            var result = int.TryParse(x.Color, out var color);
            return new object[]
            {
                x.Time,
                x.Position,
                result ? color : x.Color ?? "#FFF",
                "阿胖",
                x.Text ?? "",
            };
        });
        return Json(new { code = 0, data = allDanmaku });
    }

    [Route("history/danmaku/{id}"), HttpGet]
    public async Task<IActionResult> HistoryDanmaku(string id)
    {
        var dbDanmaku = await barrageService.GetGroupBarragesAsync(id);
        var allDanmaku = dbDanmaku
            .OrderBy(x => x.Time)
            .Select(x =>
            {
                var result = int.TryParse(x.Color, out var color);
                var colorRgb = Color.FromArgb(color);
                return new
                {
                    color = result ? $"rgb({colorRgb.R},{colorRgb.G},{colorRgb.B})" : x.Color,
                    text = x.Text,
                    time = x.Time,
                    type = x.Position switch
                    {
                        0 => "scroll",
                        1 => "top",
                        2 => "bottom",
                        _ => "",
                    },
                };
            })
            .ToList();
        return Json(allDanmaku);
    }

    [Route("history/danmaku"), HttpPost]
    public async Task<IActionResult> HistoryDanmaku([FromBody] VideoDanmaku2 danmaku)
    {
        if (string.IsNullOrWhiteSpace(danmaku.Text))
        {
            return Json(new ResultInfo(true));
        }
        var color = ColorTranslator.FromHtml("#FF0000");
        var vmBarrage = new VmBarrage
        {
            Color = color.ToArgb().ToString(),
            Group = danmaku.Id,
            Position = danmaku.Type switch
            {
                "scroll" => 0,
                "top" => 1,
                "bottom" => 2,
                _ => 0,
            },
            SendTime = DateTime.Now,
            Size = 0,
            Text = danmaku.Text,
            Time = danmaku.Time,
        };
        await barrageService.AddBarrageAsync(vmBarrage);
        return Json(new ResultInfo(true));
    }

    [Route("history/chat/v3/"), HttpPost]
    public async Task<IActionResult> HistoryChat([FromBody] VideoDanmaku danmaku)
    {
        if (string.IsNullOrWhiteSpace(danmaku.Text))
        {
            return Json(new { code = 0, data = danmaku });
        }
        var vmBarrage = new VmBarrage
        {
            Color = danmaku.Color.ToString(),
            Group = danmaku.Id,
            Position = (int)danmaku.Type,
            SendTime = DateTime.Now,
            Size = 0,
            Text = danmaku.Text,
            Time = danmaku.Time,
        };
        await barrageService.AddBarrageAsync(vmBarrage);
        return Json(new { code = 0, data = danmaku });
    }

    [Route("history/danmaku/v2/{id}"), HttpGet]
    public async Task<IActionResult> HistoryDanmakuV2(string id)
    {
        var dbDanmaku = await barrageService.GetGroupBarragesAsync(id);
        var data = dbDanmaku
            .Select(x =>
            {
                var result = int.TryParse(x.Color, out var color);
                var colorRgb = Color.FromArgb(color);
                return new
                {
                    text = x.Text,
                    time = x.Time,
                    color = result ? $"rgb({colorRgb.R},{colorRgb.G},{colorRgb.B})" : x.Color,
                    border = false,
                    mode = x.Position switch
                    {
                        0 => 0,
                        1 => 1,
                        2 => 1,
                        _ => 0,
                    },
                };
            })
            .ToList();
        return Json(data);
    }

    [Route("send/danmaku/v2"), HttpPost]
    public async Task<IActionResult> SendDanmaku([FromBody] SendDanmaku danmaku)
    {
        if (!ModelState.IsValid)
        {
            return Json(new ResultInfo(false, ModelState));
        }
        Color color;
        try
        {
            color = ColorTranslator.FromHtml(danmaku.Color);
        }
        catch (Exception e)
        {
            color = ColorTranslator.FromHtml("#FFFFFF");
            logger.LogError(e, "color translator fail,in color:{Color}", danmaku.Color);
        }

        var vmBarrage = new VmBarrage
        {
            Color = color.ToArgb().ToString(),
            Group = danmaku.Id,
            Position = danmaku.Mode,
            SendTime = DateTime.Now,
            Size = 0,
            Text = danmaku.Text,
            Time = danmaku.Time,
        };
        await barrageService.AddBarrageAsync(vmBarrage);
        return Json(new ResultInfo(true));
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

这是一段 ASP.NET MVC 控制器代码(VideoController),用于处理视频页面与弹幕(barrage/danmaku)相关的请求。总体上依赖以下服务/组件:

  • IBarrageService:用于获取/添加弹幕数据。
  • IVideoShardService:用于获取视频列表/单个视频、增加播放量等。
  • ILogger:记录日志。
  • 使用 System.Drawing 处理颜色(Color、ColorTranslator)。

下面按方法逐一解释其功能与返回的数据结构,并补充注意事项与可能的问题。

总体

  • 控制器继承自 Controller,使用路由特性为各个动作定义 URL。
  • 多个路由对同一路径分别用 GET/POST(或其他方法)实现读写操作(例如 history/chat/v3/ 既有 GET 也有 POST)。
  • 弹幕在内部以 VmBarrage(包含 Color、Group、Position、Text、Time 等)存储/传递;颜色在数据库里以字符串形式存储,这里有时是颜色的 ARGB 整数字符串(ToArgb().ToString()),有时可能是 HTML 颜色字符串。

具体动作

  1. Index()
  • 路径默认(控制器 Index)。
  • 从 videoShardService 获取视频列表(GetVideosAsync),并把结果传到视图。
  • 同时调用 this.SetTitle("视频")(设置页面标题,可能是某个扩展方法)。
  1. Watch(string id)
  • 路径:video/watch/{id}.html
  • 根据 id 获取单个视频(GetVideoAsync)。若为空返回 404 NotFound。
  • 将视频的标题设置为页面标题,并返回视图(传入 video 模型)。
  1. Play(string id)
  • 路径:PATCH play/{id}
  • 调用 videoShardService.AddPlayAsync(id) 增加播放计数(或记录播放),然后返回 204 NoContent。
  1. HistoryChat (GET) — [Route("history/chat/v3/"), HttpGet]
  • 根据 id 从 barrageService 获取该组弹幕(GetGroupBarragesAsync)。
  • 将每条弹幕投影为 object[] 数组,内容为 [Time, Position, Color, "阿胖", Text]:
    • Color 字段先尝试 int.TryParse(x.Color, out color),若能解析则传色值(int),否则传 x.Color 或 "#FFF"(默认)。
    • 固定用户名为 "阿胖"(写死)。
  • 返回 JSON:{ code = 0, data = allDanmaku }。
  1. HistoryDanmaku (GET) — [Route("history/danmaku/{id}"), HttpGet]
  • 获取该组弹幕,按 x.Time 排序。
  • 将每条弹幕映射为对象 { color, text, time, type }:
    • x.Color 尝试解析为 int(ARGB),用 Color.FromArgb(color) 得到 RGB,然后格式化为 "rgb(R,G,B)";若解析失败则直接使用 x.Color。
    • type 根据 Position 映射为 "scroll"/"top"/"bottom"(0/1/2)。
  • 返回 JSON 列表(List)。
  1. HistoryDanmaku (POST) — [Route("history/danmaku"), HttpPost]
  • 接收 VideoDanmaku2(假定包含 Id、Text、Type、Time 等)。
  • 如果 Text 为空白,直接返回 ResultInfo(true)(看起来表示成功,但不保存弹幕)。
  • 否则构造 VmBarrage:
    • 默认颜色固定为 "#FF0000"(用 ColorTranslator.FromHtml,然后 ToArgb().ToString() 存入 Color 字段)。
    • Group = danmaku.Id,Position 根据字符串 Type 转为 0/1/2,SendTime = DateTime.Now 等。
  • 调用 barrageService.AddBarrageAsync 添加弹幕,返回 ResultInfo(true)。
  1. HistoryChat (POST) — [Route("history/chat/v3/"), HttpPost]
  • 接收 VideoDanmaku(含 Color、Id、Type(可能为枚举或数值)、Text、Time)。
  • 如果 Text 为空白,直接返回 { code = 0, data = danmaku }。
  • 构造 VmBarrage,将 danmaku.Color.ToString() 作为存储的 Color(注意:这里直接存传入的 Color),Position = (int)danmaku.Type,SendTime = Now,等。
  • 添加弹幕后返回 { code = 0, data = danmaku }。
  1. HistoryDanmakuV2 (GET) — [Route("history/danmaku/v2/{id}"), HttpGet]
  • 获取该组弹幕并映射为客户端 v2 格式:
    • 字段:text、time、color(同样尝试解析 ARGB int 并格式化为 "rgb(...)",否则直接用原字符串)、border = false 固定、mode 映射(0/1)。
    • Position 0 -> mode 0,1/2 -> mode 1(将 top/bottom 归为同一 mode)。
  • 返回 JSON 列表。
  1. SendDanmaku (POST) — [Route("send/danmaku/v2"), HttpPost]
  • 接收 SendDanmaku(有 Id、Text、Time、Color、Mode 等),先检查 ModelState,若无效返回错误信息 ResultInfo(false, ModelState)。
  • 尝试用 ColorTranslator.FromHtml(danmaku.Color) 转换颜色字符串,若失败记录日志并默认白色 (#FFFFFF)。
  • 构造 VmBarrage,将颜色保存为 color.ToArgb().ToString(),Group = danmaku.Id,Position = danmaku.Mode 等。
  • 保存弹幕并返回 ResultInfo(true)。

实现细节与注意事项(可能的问题)

  • 颜色处理:
    • 数据库中 Color 字段在不同地方既可能是 HTML 颜色字符串(如 "#FF0000"),也可能是 ARGB 整数字符串(ToArgb().ToString())。代码中有多种解析方式(int.TryParse -> Color.FromArgb,或 ColorTranslator.FromHtml),混合使用可能导致不一致或解析失败。
    • 在某些方法中,int.TryParse 解析失败时直接返回 x.Color(未保证是客户端想要的格式)。在少数地方返回的默认颜色为 "#FFF" 或 "#FFFFFF",不统一。
    • 在服务器端使用 System.Drawing(Color)在跨平台(Linux)环境下可能有兼容性/依赖问题,生产中通常建议使用轻量的颜色处理库或直接操作字符串。
  • 返回结构不完全一致:
    • 不同接口(v1/v2/chat)返回的 JSON 结构格式不同,客户端需要对应适配。
    • 部分 POST 接口对空文本直接返回成功(不保存)——这可能是设计决定,但也可能导致前端误判(应返回 400/错误信息更直观)。
  • 安全性与防刷:
    • 接口允许匿名发送弹幕(代码中没有看到身份校验或频率限制),需要考虑反垃圾、验证、速率限制、内容过滤等。
  • 日志与错误处理:
    • 在 ColorTranslator.FromHtml 捕获异常并记录了错误,比较好。但其他地方(如 Color.FromArgb 传入非法 int)没有额外异常处理,按现状 Color.FromArgb 接受 int 通常不会抛异常,但若传入的 int 语义错误可能导致颜色异常。
  • 路由/方法设计:
    • 同一路径根据 HTTP 方法区分 GET/POST(如 history/chat/v3/)是常见做法,但要注意前端调用时使用正确的 HTTP 动词。
    • Play 使用 PATCH,很贴切用于“局部更新”(增加播放数)。

总结

  • 该控制器主要实现视频列表与观看页面,以及多种弹幕的读取(不同格式/版本)与发送接口,并支持增加播放量功能。
  • 关键流程是:从视频/弹幕服务读取数据,转换为客户端需要的 JSON 或视图模型,或接收前端弹幕请求并持久化为 VmBarrage。
  • 需留意颜色格式统一、跨平台 System.Drawing 问题、输入验证与防刷/内容审核等实际生产考虑。

如需,我可以:

  • 绘制每个接口的示例请求/响应 JSON;
  • 指出并给出改善颜色处理、统一返回格式与验证的具体代码改进建议。需要哪个请告诉我。
评论加载中...