using dy.net.dto;
using dy.net.model;
using dy.net.service;
using dy.net.utils;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace dy.net.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class VideoController : ControllerBase
{
private readonly DouyinVideoService douyinVideoService;
private readonly DouyinCommonService douyinCommonService;
public VideoController(DouyinVideoService dyCollectVideoService, DouyinCommonService douyinCommonService)
{
this.douyinVideoService = dyCollectVideoService;
this.douyinCommonService = douyinCommonService;
}
///
/// 分页查询收藏视频
///
///
[Authorize]
[HttpPost("paged")]
public async Task GetPagedAsync(DouyinVideoPageRequestDto dto)
{
var (list, totalCount) = await douyinVideoService.GetPagedAsync(dto);
return ApiResult.Success(new
{
data = list,
total = totalCount,
pageIndex = dto.PageIndex,
pageSize = dto.PageSize
});
}
///
/// 查询统计数据
///
///
[Authorize]
[HttpGet("statics")]
public async Task GetStaticsAsync()
{
var data = await douyinVideoService.GetStatics();
return ApiResult.Success(data);
}
///
/// 播放视频
///
///
///
[AllowAnonymous]
[HttpGet("play/{vid}")]
public async Task StreamVideo([FromRoute] string vid)
{
try
{
var viedo = await douyinVideoService.GetById(vid);
if (viedo == null)
{
return ApiResult.Fail($"视频不存在:{vid}");
}
return PlayViedo(viedo);
}
catch (Exception ex)
{
return ApiResult.Fail($"视频加载失败:{ex.Message}");
}
}
private IActionResult PlayViedo(DouyinVideo viedo)
{
// 1. 拼接完整物理路径(配置路径 + 文件名)
string videoFullPath = viedo.VideoSavePath;
// 2. 验证文件是否存在
if (!System.IO.File.Exists(videoFullPath))
{
return ApiResult.Fail($"视频文件不存在:{videoFullPath}");
}
// 3. 获取文件信息(大小、类型)
var fileInfo = new FileInfo(videoFullPath);
long fileSize = fileInfo.Length;
string contentType = GetContentType(videoFullPath); // 自动识别视频 MIME 类型
// 4. 处理分片请求(前端视频标签自动发起,支持断点续传)
if (Request.Headers.ContainsKey("Range") && long.TryParse(Request.Headers.Range.ToString().Split('=')[1].Split('-')[0], out long start))
{
// 分片起始位置(前端请求的起始字节)
long end = Math.Min(start + 1024 * 1024 * 2, fileSize - 1); // 每片 2MB(可调整)
long chunkSize = end - start + 1;
// 5. 设置分片响应头
Response.StatusCode = StatusCodes.Status206PartialContent;
Response.Headers.Add("Content-Range", $"bytes {start}-{end}/{fileSize}");
Response.Headers.Add("Accept-Ranges", "bytes");
Response.Headers.Add("Content-Length", chunkSize.ToString());
// 6. 读取分片并返回流
var stream = new FileStream(videoFullPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);
stream.Seek(start, SeekOrigin.Begin);
return new FileStreamResult(stream, contentType);
}
else
{
// 完整文件请求(兼容旧浏览器)
return PhysicalFile(videoFullPath, contentType, enableRangeProcessing: true);
}
}
///
/// 播放视频
///
///
///
///
[HttpGet("/share/{vid}/{k}")]
[AllowAnonymous]
public async Task Share([FromRoute] string vid, [FromRoute] string k)
{
try
{
var viedo = await douyinVideoService.GetById(vid);
if (viedo == null)
{
return ApiResult.Fail($"视频不存在:{vid}");
}
var expectedKey = (viedo.FileHash + viedo.AuthorId).Md5();
if (expectedKey != k)
{
return ApiResult.Fail($"视频地址无效");
}
return PlayViedo(viedo);
}
catch (Exception ex)
{
return ApiResult.Fail($"视频加载失败:{ex.Message}");
}
}
///
/// 辅助方法:根据文件名获取 MIME 类型(确保前端正确识别视频格式)
///
private string GetContentType(string filename)
{
string extension = Path.GetExtension(filename).ToLowerInvariant();
return extension switch
{
".mp4" => "video/mp4",
".webm" => "video/webm",
".ogg" => "video/ogg",
".mov" => "video/quicktime",
".avi" => "video/x-msvideo",
_ => "application/octet-stream" // 默认二进制流
};
}
///
/// 重新下载
///
///
///
[HttpPost("redown")]
public async Task ReDownload(ReDownViedoDto dto)
{
if (dto == null)
{
return ApiResult.Fail("参数错误");
}
else
{
var result = await douyinVideoService.ReDownloadViedoAsync(dto);
if (result)
{
return ApiResult.Success(true);
}
else
{
return ApiResult.Fail("错误");
}
}
}
///
/// 删除视频-不再下载
///
///
///
[HttpGet("vdelete/{vid}")]
public async Task DeleteVideo([FromRoute] string vid)
{
if (string.IsNullOrWhiteSpace(vid))
{
return ApiResult.Fail("参数错误");
}
else
{
var video = await douyinVideoService.GetById(vid);
if (video == null)
{
return ApiResult.Fail("请求失败");
}
else
{
var result = await douyinVideoService.ReDownloadViedoAsync(new ReDownViedoDto { Ids = new List { vid } });
if (result)
{
//加入删除逻辑
await douyinCommonService.AddDeleteVideo(new DouyinVideoDelete
{
ViedoId = video.AwemeId,
VideoTitle = video.VideoTitle,
VideoSavePath = video.VideoSavePath
});
Serilog.Log.Debug($"前面的日志,你错了,这条视频是永久删除..哈哈--{video.VideoTitle}");
return ApiResult.Success();
}
else
{
return ApiResult.Fail();
}
}
}
}
///
/// 查询已删除视频列表
///
///
[HttpGet("vdelete/get")]
public async Task GetDeleteVideo()
{
return ApiResult.Success(await douyinCommonService.GetDouyinDeleteVideos());
}
///
/// 查询最新N条数据
///
///
///
[HttpGet("top{top}")]
public async Task GetLastSyncTop([FromRoute]int top = 5)
{
return ApiResult.Success(await douyinVideoService.GetLastSyncTop(top));
}
}
}