1、去重

2、分享
3、视频播放
4、批量删除,重新下载
5、自定义标题(仅博主视频)
6、授权页面去掉博主添加,新增关注列表
7、图文视频合成优化
8、其他优化
This commit is contained in:
jianzhichu
2025-11-30 00:42:25 +08:00
parent 63b51d211e
commit e6e9ff3cc3
94 changed files with 8155 additions and 1544 deletions
+22 -25
View File
@@ -6,6 +6,8 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Quartz.Util;
using System.Xml.Linq;
using static Dm.net.buffer.ByteArrayBuffer;
namespace dy.net.Controllers
@@ -32,7 +34,7 @@ namespace dy.net.Controllers
/// <summary>
/// 分页查询
/// </summary>
/// <returns>分页结果(视频列表和总数)</returns>
/// <returns>分页结果</returns>
[HttpPost("paged")]
public async Task<IActionResult> GetPagedAsync(
PageRequestDto dto)
@@ -51,16 +53,26 @@ namespace dy.net.Controllers
}
);
}
/// <summary>
/// 查询所有用户Cookie
/// </summary>
/// <returns></returns>
[HttpGet("list")]
public async Task<IActionResult> GetAllList()
{
var list = await dyCookieService.GetAllOpendAsync();
var result = list.Select(x => new { key= x.MyUserId, name= x.UserName });
return Ok(new { code = 0, data = result });
}
/// <summary>
/// 新增用户Cookie
/// </summary>
[HttpPost("add")]
public async Task<IActionResult> AddAsync([FromBody] DouyinUserCookie dyUserCookies)
public async Task<IActionResult> AddAsync([FromBody] DouyinCookie dyUserCookies)
{
if(dyUserCookies.UpSecUserIdsJson!=null)
{
dyUserCookies.UpSecUserIds = JsonConvert.SerializeObject( dyUserCookies.UpSecUserIdsJson);
}
var result = await dyCookieService.Add(dyUserCookies);
if (result)
{
@@ -74,14 +86,9 @@ namespace dy.net.Controllers
/// 更新用户Cookie
/// </summary>
[HttpPost("update")]
public async Task<IActionResult> UpdateAsync([FromBody] DouyinUserCookie dyUserCookies)
public async Task<IActionResult> UpdateAsync([FromBody] DouyinCookie dyUserCookies)
{
if (dyUserCookies.UpSecUserIdsJson != null )
{
dyUserCookies.UpSecUserIds = JsonConvert.SerializeObject(dyUserCookies.UpSecUserIdsJson);
}
if (dyUserCookies.Id == "0")
{
dyUserCookies.Id=IdGener.GetLong().ToString();
@@ -144,26 +151,16 @@ namespace dy.net.Controllers
public async Task<IActionResult> ExecuteJobNow()
{
var config = commonService.GetConfig();
await quartzJobService.StartJob(config.Cron);
if (config!=null)
await quartzJobService.InitOrReStartAllJobs(config.Cron.ToString());
return Ok(new { code = 0 , error = "" });
}
private async Task ReStartJob() {
var config= commonService.GetConfig();
//var cookies = await dyCookieService.GetAllCookies();
//重置同步状态
//foreach (var cookie in cookies)
//{
// cookie.CollHasSyncd = 0;
// cookie.FavHasSyncd = 0;
// cookie.UperSyncd = 0;
// await dyCookieService.UpdateAsync(cookie);
//}
if (config!=null)
quartzJobService.StartJob(config.Cron);
quartzJobService.InitOrReStartAllJobs(config.Cron.ToString());
//避免前端等待
}
}
+149
View File
@@ -0,0 +1,149 @@
using dy.net.dto;
using dy.net.service;
using dy.net.utils;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace dy.net.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class FollowController : ControllerBase
{
private readonly DouyinFollowService _douyinFollowService;
private readonly DouyinQuartzJobService _douyinQuartzJobService;
public FollowController(DouyinFollowService douyinFollowService, DouyinQuartzJobService douyinQuartzJobService)
{
this._douyinFollowService = douyinFollowService;
_douyinQuartzJobService = douyinQuartzJobService;
}
/// <summary>
/// 分页查询
/// </summary>
/// <returns>分页结果</returns>
[HttpPost("paged")]
public async Task<IActionResult> GetPagedAsync(
FollowRequestDto dto)
{
if (string.IsNullOrWhiteSpace(dto.MySelfId))
{
return Ok(new
{
code = 0,
data = new { }
});
}
var (list, totalCount) = await _douyinFollowService.GetPagedAsync(dto);
return Ok(new
{
code = 0,
data = new
{
data = list,
total = totalCount,
pageIndex = dto.PageIndex,
pageSize = dto.PageSize
}
}
);
}
/// <summary>
/// 重新同步-单次
/// </summary>
/// <returns></returns>
[HttpGet("sync")]
public async Task<IActionResult> SyncFollowList()
{
//后台异步
_douyinQuartzJobService.StartFollowJobOnceAsync();
await Task.Delay(1000);
return Ok(new { code = 0 });
}
/// <summary>
/// 修改关注同步状态
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("openOrCloseSync")]
public async Task<IActionResult> OpenOrCloseSync(FollowUpdateDto dto)
{
if (dto.OpenSync)
{
if (!string.IsNullOrWhiteSpace(dto.SavePath))
{
if (!DouyinFileNameHelper.IsValidWithoutSpecialChars(dto.SavePath))
{
return Ok(new
{
code = -1,
msg = "请输入有效文件夹名称(字母数字中文简体)"
});
}
if (dto.SavePath.Length > 10)
{
return Ok(new
{
code = -1,
msg = "请输入有效文件夹名称(最长10)"
});
}
}
}
var result= await _douyinFollowService.OpenOrCloseSync(dto);
return Ok(new
{
code = result ? 0 : -1,
data = result
});
}
/// <summary>
/// 修改关注全量同步状态
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("openOrCloseFullSync")]
public async Task<IActionResult> OpenOrCloseFullSync(FollowUpdateDto dto)
{
if (dto.FullSync)
{
if (!string.IsNullOrWhiteSpace(dto.SavePath))
{
if (!DouyinFileNameHelper.IsValidWithoutSpecialChars(dto.SavePath))
{
return Ok(new
{
code = -1,
msg = "请输入有效文件夹名称(字母数字中文简体)"
});
}
if (dto.SavePath.Length > 10)
{
return Ok(new
{
code = -1,
msg = "请输入有效文件夹名称(最长10)"
});
}
}
}
var result = await _douyinFollowService.OpenOrCloseFullSync(dto);
return Ok(new
{
code = result ? 0 : -1,
data = result
});
}
}
}
File diff suppressed because one or more lines are too long
+105 -37
View File
@@ -1,4 +1,5 @@
using dy.net.dto;
using dy.net.model;
using dy.net.service;
using dy.net.utils;
using Microsoft.AspNetCore.Authorization;
@@ -14,10 +15,12 @@ namespace dy.net.Controllers
public class VideoController : ControllerBase
{
private readonly DouyinVideoService dyCollectVideoService;
private readonly DouyinQuartzJobService douyinQuartzJobService;
public VideoController(DouyinVideoService dyCollectVideoService)
public VideoController(DouyinVideoService dyCollectVideoService,DouyinQuartzJobService douyinQuartzJobService)
{
this.dyCollectVideoService = dyCollectVideoService;
this.douyinQuartzJobService = douyinQuartzJobService;
}
/// <summary>
/// 分页查询收藏视频
@@ -26,7 +29,7 @@ namespace dy.net.Controllers
[HttpPost("paged")]
public async Task<IActionResult> GetPagedAsync(DouyinVideoPageRequestDto dto)
{
var (list, totalCount) = await dyCollectVideoService.GetPagedAsync(dto.PageIndex, dto.PageSize, dto.Tag, dto.Author,dto.ViedoType,dto.Dates);
var (list, totalCount) = await dyCollectVideoService.GetPagedAsync(dto);
return Ok(new
{
code = 0,
@@ -67,48 +70,87 @@ namespace dy.net.Controllers
{
var viedo = await dyCollectVideoService.GetById(vid);
if(viedo == null)
if (viedo == null)
{
return NotFound($"视频不存在:{vid}");
}
return PlayViedo(viedo);
}
catch (Exception ex)
{
return StatusCode(500, $"视频加载失败:{ex.Message}");
}
}
private IActionResult PlayViedo(DouyinVideo viedo)
{
// 1. 拼接完整物理路径(配置路径 + 文件名)
string videoFullPath = viedo.VideoSavePath;
// 2. 验证文件是否存在
if (!System.IO.File.Exists(videoFullPath))
{
return NotFound($"视频文件不存在:{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);
}
}
/// <summary>
/// 播放视频
/// </summary>
/// <param name="vid"></param>
/// <param name="k"></param>
/// <returns></returns>
[HttpGet("/share/{vid}/{k}")]
[AllowAnonymous]
public async Task<IActionResult> Share([FromRoute] string vid, [FromRoute] string k)
{
try
{
var viedo = await dyCollectVideoService.GetById(vid);
if (viedo == null)
{
return NotFound($"视频不存在:{vid}");
}
// 1. 拼接完整物理路径(配置路径 + 文件名)
string videoFullPath = viedo.VideoSavePath;
// 2. 验证文件是否存在
if (!System.IO.File.Exists(videoFullPath))
var expectedKey = (viedo.FileHash + viedo.AuthorId).Md5();
if (expectedKey != k)
{
return NotFound($"视频文件不存在:{videoFullPath}");
return NotFound($"视频地址无效");
}
// 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);
}
return PlayViedo(viedo);
}
catch (Exception ex)
{
@@ -133,6 +175,32 @@ namespace dy.net.Controllers
};
}
/// <summary>
/// 重新下载
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("redown")]
public async Task<IActionResult> ReDownload(ReDownViedoDto dto)
{
if (dto == null)
{
return Ok(new { code = -1, data = false });
}
else
{
var result = await dyCollectVideoService.ReDownloadViedoAsync(dto);
if (result)
{
//douyinQuartzJobService.StartReDownJobOnceAsync();...无法实现。。逆向失败
return Ok(new { code = 0, data = true });
}
else
{
return Ok(new { code = -1, data = false });
}
}
}
}
}