docs: 为缩略图、最近文件相关新增类型补全中文 XML 文档注释
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
2c20f9bb54
commit
ff19f4759f
@ -25,6 +25,7 @@ namespace FileShare_EFCore.Database
|
||||
/// <summary>文件库文件记录数据</summary>
|
||||
public DbSet<ManagedFileRecord> ManagedFileRecords => Set<ManagedFileRecord>();
|
||||
|
||||
/// <summary>文件缩略图映射数据</summary>
|
||||
public DbSet<ManagedThumbnailMap> ManagedThumbnailMaps => Set<ManagedThumbnailMap>();
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -90,6 +90,7 @@ namespace FileShare_EFCore.Models
|
||||
/// <summary>所属根目录。</summary>
|
||||
public ManagedLibraryRoot? LibraryRoot { get; set; }
|
||||
|
||||
/// <summary>缩略图映射记录。</summary>
|
||||
public ManagedThumbnailMap? Thumbnail { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,6 +63,7 @@ namespace FileShare_EFCore.Models
|
||||
/// <summary>文件记录。</summary>
|
||||
public List<ManagedFileRecord> Files { get; set; } = new();
|
||||
|
||||
/// <summary>缩略图记录。</summary>
|
||||
public List<ManagedThumbnailMap> Thumbnails { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,34 +4,45 @@ using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace FileShare_EFCore.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件缩略图映射记录,存储视频缩略图的文件路径与内容类型。
|
||||
/// </summary>
|
||||
[Comment("文件缩略图映射记录")]
|
||||
[Table("managed-thumbnail-map")]
|
||||
public class ManagedThumbnailMap
|
||||
{
|
||||
/// <summary>主键 ID。</summary>
|
||||
[Key]
|
||||
[Column("id")]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>所属根目录 ID。</summary>
|
||||
[Column("library-root-id")]
|
||||
public int LibraryRootId { get; set; }
|
||||
|
||||
/// <summary>缩略图相对于缩略图存储根目录的路径。</summary>
|
||||
[Column("relative-path")]
|
||||
[MaxLength(1024)]
|
||||
public string RelativePath { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>缩略图的 MIME 类型。</summary>
|
||||
[Column("content-type")]
|
||||
[MaxLength(100)]
|
||||
public string ContentType { get; set; } = "image/jpeg";
|
||||
|
||||
/// <summary>创建时间 UTC。</summary>
|
||||
[Column("created-at")]
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>更新时间 UTC。</summary>
|
||||
[Column("updated-at")]
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>所属根目录。</summary>
|
||||
public ManagedLibraryRoot? LibraryRoot { get; set; }
|
||||
|
||||
/// <summary>引用了此缩略图的文件记录。</summary>
|
||||
public List<ManagedFileRecord> Files { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
||||
@ -692,6 +692,11 @@ namespace FileShare_PC.Views
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试处理本地缩略图请求,匹配 /api/thumbnails/{id} 路径并返回缩略图文件流。
|
||||
/// </summary>
|
||||
/// <param name="context">HTTP 监听器上下文。</param>
|
||||
/// <returns>如果请求路径匹配缩略图端点则返回 true,否则返回 false。</returns>
|
||||
private async Task<bool> TryHandleLocalThumbnailAsync(HttpListenerContext context)
|
||||
{
|
||||
var request = context.Request;
|
||||
|
||||
@ -85,8 +85,18 @@ namespace FileShare_Services.Services.FileLibrary
|
||||
/// <returns>API 响应。</returns>
|
||||
Task<IApiResponse> BrowseDirectoryAsync(BrowseDirectoryRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// 获取最近添加或最近播放的文件列表。
|
||||
/// </summary>
|
||||
/// <param name="request">包含类型和数量的请求。</param>
|
||||
/// <returns>API 响应。</returns>
|
||||
Task<IApiResponse> GetRecentFilesAsync(RecentFilesRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// 标记文件为已播放,更新最近播放时间。
|
||||
/// </summary>
|
||||
/// <param name="request">包含文件 ID 的请求。</param>
|
||||
/// <returns>API 响应。</returns>
|
||||
Task<IApiResponse> MarkFilePlayedAsync(MarkFilePlayedRequest request);
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,8 +98,20 @@ namespace FileShare_Services.Services.FileLibrary
|
||||
/// <returns>目录浏览响应,包含子目录和文件列表。</returns>
|
||||
Task<BrowseDirectoryResponse> BrowseDirectoryAsync(BrowseDirectoryRequest request, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 获取最近添加或最近播放的文件列表。
|
||||
/// </summary>
|
||||
/// <param name="type">筛选类型,"added" 按创建时间排序,"played" 按最近播放时间排序。</param>
|
||||
/// <param name="count">返回数量,范围 1-48,默认 12。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>文件记录 DTO 列表。</returns>
|
||||
Task<List<FileRecordDto>> GetRecentFilesAsync(string type, int count = 12, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 将指定文件的最近播放时间更新为当前 UTC 时间。
|
||||
/// </summary>
|
||||
/// <param name="id">文件记录 ID。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
Task MarkFilePlayedAsync(int id, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,39 @@
|
||||
namespace FileShare_Services.Services.FileLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// 视频缩略图生成服务接口,负责从视频文件中截取缩略图并获取时长信息。
|
||||
/// </summary>
|
||||
public interface IVideoThumbnailService
|
||||
{
|
||||
/// <summary>
|
||||
/// 为指定视频文件生成缩略图,如果已存在则直接返回。
|
||||
/// </summary>
|
||||
/// <param name="libraryRootId">文件库根目录 ID。</param>
|
||||
/// <param name="videoPath">视频文件的绝对路径。</param>
|
||||
/// <param name="ct">取消令牌。</param>
|
||||
/// <returns>生成的缩略图信息,失败时返回 null。</returns>
|
||||
Task<GeneratedThumbnail?> GenerateThumbnailAsync(int libraryRootId, string videoPath, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// 将相对路径转换为缩略图存储目录下的绝对路径,并校验路径安全性。
|
||||
/// </summary>
|
||||
/// <param name="relativePath">相对路径。</param>
|
||||
/// <returns>绝对路径。</returns>
|
||||
/// <exception cref="InvalidOperationException">当路径越权访问缩略图存储根目录之外的位置时抛出。</exception>
|
||||
string GetAbsolutePath(string relativePath);
|
||||
|
||||
/// <summary>
|
||||
/// 获取视频文件的总时长。
|
||||
/// </summary>
|
||||
/// <param name="videoPath">视频文件的绝对路径。</param>
|
||||
/// <returns>视频时长(秒),获取失败时返回 null。</returns>
|
||||
double? GetVideoDuration(string videoPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缩略图生成结果记录。
|
||||
/// </summary>
|
||||
/// <param name="RelativePath">缩略图相对于存储根目录的路径。</param>
|
||||
/// <param name="ContentType">缩略图的 MIME 类型。</param>
|
||||
public sealed record GeneratedThumbnail(string RelativePath, string ContentType);
|
||||
}
|
||||
|
||||
@ -1,9 +1,17 @@
|
||||
namespace FileShare_Services.Services.FileLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// 缩略图存储与服务配置选项。
|
||||
/// </summary>
|
||||
public sealed class ThumbnailStorageOptions
|
||||
{
|
||||
/// <summary>缩略图文件存储根目录路径。</summary>
|
||||
public string RootPath { get; set; } = Path.Combine(AppContext.BaseDirectory, "thumbnails");
|
||||
|
||||
/// <summary>ffmpeg 可执行文件路径。</summary>
|
||||
public string FfmpegPath { get; set; } = Path.Combine("tools", "ffmpeg", "bin", "ffmpeg.exe");
|
||||
|
||||
/// <summary>ffprobe 可执行文件路径。</summary>
|
||||
public string FfprobePath { get; set; } = Path.Combine("tools", "ffmpeg", "bin", "ffprobe.exe");
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,13 +4,26 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace FileShare_Services.Services.FileLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// 缩略图流服务接口,根据缩略图 ID 返回文件流响应。
|
||||
/// </summary>
|
||||
public interface IThumbnailStreamService
|
||||
{
|
||||
/// <summary>
|
||||
/// 根据缩略图记录 ID 获取缩略图文件流响应。
|
||||
/// </summary>
|
||||
/// <param name="id">缩略图记录 ID。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>文件流响应,不存在或文件丢失时返回 null。</returns>
|
||||
Task<FileStreamResponse?> GetThumbnailAsync(int id, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缩略图流服务实现,从数据库查找缩略图映射并返回对应的物理文件流。
|
||||
/// </summary>
|
||||
public sealed class ThumbnailStreamService(AppDataContext db, IVideoThumbnailService thumbnails) : IThumbnailStreamService
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<FileStreamResponse?> GetThumbnailAsync(int id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var thumbnail = await db.ManagedThumbnailMaps
|
||||
|
||||
@ -4,12 +4,22 @@ using System.Text;
|
||||
|
||||
namespace FileShare_Services.Services.FileLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// 视频缩略图服务实现,使用 ffmpeg 截取视频帧作为缩略图,使用 ffprobe 提取视频时长。
|
||||
/// </summary>
|
||||
public sealed class VideoThumbnailService : IVideoThumbnailService
|
||||
{
|
||||
/// <summary>缩略图文件存储目录的绝对路径。</summary>
|
||||
private readonly string _thumbnailDir;
|
||||
/// <summary>ffmpeg 可执行文件路径。</summary>
|
||||
private readonly string _ffmpegPath;
|
||||
/// <summary>ffprobe 可执行文件路径。</summary>
|
||||
private readonly string _ffprobePath;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化视频缩略图服务,解析并验证配置路径,创建缩略图存储目录。
|
||||
/// </summary>
|
||||
/// <param name="options">缩略图存储配置选项。</param>
|
||||
public VideoThumbnailService(ThumbnailStorageOptions options)
|
||||
{
|
||||
_thumbnailDir = Path.IsPathRooted(options.RootPath)
|
||||
@ -20,6 +30,7 @@ namespace FileShare_Services.Services.FileLibrary
|
||||
Directory.CreateDirectory(_thumbnailDir);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<GeneratedThumbnail?> GenerateThumbnailAsync(int libraryRootId, string videoPath, CancellationToken ct = default)
|
||||
{
|
||||
var hash = ComputeHash(videoPath);
|
||||
@ -69,6 +80,7 @@ namespace FileShare_Services.Services.FileLibrary
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetAbsolutePath(string relativePath)
|
||||
{
|
||||
var root = Path.GetFullPath(_thumbnailDir);
|
||||
@ -82,6 +94,11 @@ namespace FileShare_Services.Services.FileLibrary
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析可执行文件路径,支持绝对路径和相对于应用程序基目录的相对路径。
|
||||
/// </summary>
|
||||
/// <param name="executablePath">配置中的原始可执行文件路径。</param>
|
||||
/// <returns>可用的绝对路径。</returns>
|
||||
private static string ResolveExecutablePath(string executablePath)
|
||||
{
|
||||
if (Path.IsPathRooted(executablePath))
|
||||
@ -95,6 +112,7 @@ namespace FileShare_Services.Services.FileLibrary
|
||||
: executablePath;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double? GetVideoDuration(string videoPath)
|
||||
{
|
||||
try
|
||||
@ -130,6 +148,11 @@ namespace FileShare_Services.Services.FileLibrary
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算视频文件路径的 MD5 哈希值,用于生成唯一的缩略图文件名。
|
||||
/// </summary>
|
||||
/// <param name="videoPath">视频文件的绝对路径。</param>
|
||||
/// <returns>小写的十六进制 MD5 哈希字符串。</returns>
|
||||
public string ComputeHash(string videoPath) =>
|
||||
Convert.ToHexString(MD5.HashData(Encoding.UTF8.GetBytes(videoPath))).ToLowerInvariant();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user