WIP: metadata class rework

This commit is contained in:
2026-04-20 13:31:34 +08:00
parent 021f4b1cd1
commit 2d66684b98
5 changed files with 13 additions and 132 deletions
+9 -2
View File
@@ -54,6 +54,9 @@ public class AudioFileMeta
#endregion #endregion
///<summary>声道配置</summary>
public string? ChannelLayout { get; set; }
/// <summary>短ID,便于人工识别或显示的简短标识</summary> /// <summary>短ID,便于人工识别或显示的简短标识</summary>
public string? ShortId { get; set; } public string? ShortId { get; set; }
@@ -114,10 +117,14 @@ public class AudioFileMeta
public string? Manufacturer { get; set; } public string? Manufacturer { get; set; }
/// <summary>来源方,原始创建者或来源机构</summary> /// <summary>来源方,原始创建者或来源机构</summary>
public string? Originator { get; set; } public string? BwfOriginator { get; set; }
/// <summary>来源引用,原始来源的参考编号</summary> /// <summary>来源引用,原始来源的参考编号</summary>
public string? OriginatorRef { get; set; } public string? BwfOriginatorRef { get; set; }
public DateTime? BwfDate { get; set; }
public string? BwfDescription { get; set; }
/// <summary>项目名称,所属制作项目</summary> /// <summary>项目名称,所属制作项目</summary>
public string? ProjectName { get; set; } public string? ProjectName { get; set; }
-1
View File
@@ -32,7 +32,6 @@ public class AudioFileMetaValidator
// 验证数值字段 // 验证数值字段
ValidatePositiveValue(meta.Duration, "Duration", errors); ValidatePositiveValue(meta.Duration, "Duration", errors);
ValidatePositiveValue(meta.TotalSamples, "TotalSamples", errors);
ValidatePositiveValue(meta.BitDepth, "BitDepth", errors); ValidatePositiveValue(meta.BitDepth, "BitDepth", errors);
ValidatePositiveValue(meta.Channels, "Channels", errors); ValidatePositiveValue(meta.Channels, "Channels", errors);
ValidatePositiveValue(meta.SampleRate, "SampleRate", errors); ValidatePositiveValue(meta.SampleRate, "SampleRate", errors);
-125
View File
@@ -1,125 +0,0 @@
using System.Security.Cryptography;
namespace OCES.Resonance.Core;
/// <summary>
/// 音频库管理服务,协调扫描、读取和入库的完整流程
/// </summary>
public class AudioLibraryService
{
readonly AudioFileScanner _scanner = new();
readonly AudioMetadataReader _metadataReader = new();
/// <summary>
/// 扫描目录并将结果添加到数据库
/// </summary>
/// <param name="directoryPath">要扫描的目录路径</param>
/// <param name="databaseName">数据库名称(不含扩展名)</param>
/// <param name="skipExisting">是否跳过已存在的文件</param>
/// <returns>扫描结果统计</returns>
public async Task<ScanResult> ScanAndImportToLibrary(
string directoryPath,
string databaseName = "default",
bool skipExisting = true)
{
var result = new ScanResult
{
TotalFiles = 0,
SuccessCount = 0,
SkipCount = 0,
ErrorCount = 0,
Errors = [],
};
// 初始化数据库
Database.InitializeDatabase();
// 扫描所有音频文件
List<string> audioFiles = this._scanner.ScanDirectory(directoryPath).ToList();
result.TotalFiles = audioFiles.Count;
if (audioFiles.Count == 0)
{
return result;
}
List<AudioFileMeta> metadataList = [];
// 读取每个文件的元数据
foreach (string filePath in audioFiles)
{
try
{
// 计算 MD5 用于查重
string md5 = CalculateMd5ForFile(filePath);
// 读取元数据
AudioFileMeta metadata = this._metadataReader.ReadMetadata(filePath);
metadataList.Add(metadata);
result.SuccessCount++;
}
catch (Exception ex)
{
result.ErrorCount++;
result.Errors.Add($"读取失败 {filePath}: {ex.Message}");
}
}
// 批量入库
if (metadataList.Count <= 0)
return await Task.FromResult(result);
{
try
{
Database.AddEntries(metadataList);
}
catch (Exception ex)
{
result.Errors.Add($"批量入库失败:{ex.Message}");
throw;
}
}
return await Task.FromResult(result);
}
/// <summary>
/// 计算文件 MD5(用于查重,不创建完整 AudioFileMeta 对象)
/// </summary>
private static string CalculateMd5ForFile(string filePath)
{
using MD5 md5 = MD5.Create();
using FileStream stream = File.OpenRead(filePath);
byte[] hash = md5.ComputeHash(stream);
return Convert.ToHexString(hash);
}
}
/// <summary>
/// 扫描结果统计
/// </summary>
public class ScanResult
{
/// <summary>扫描到的文件总数</summary>
public int TotalFiles { get; set; }
/// <summary>成功入库的文件数</summary>
public int SuccessCount { get; set; }
/// <summary>跳过的文件数(已存在)</summary>
public int SkipCount { get; set; }
/// <summary>读取失败的文件数</summary>
public int ErrorCount { get; set; }
/// <summary>错误详情列表</summary>
public List<string> Errors { get; set; } = [];
/// <summary>
/// 获取人类可读的统计报告
/// </summary>
public string GetSummary()
{
return $"扫描完成:共 {TotalFiles} 个文件,成功 {SuccessCount} 个,跳过 {SkipCount} 个,失败 {ErrorCount} 个";
}
}
+2 -2
View File
@@ -62,8 +62,8 @@ public class AudioMetadataReader
// BWF 特定字段 (通过附加数据获取) // BWF 特定字段 (通过附加数据获取)
CodingHistory = GetBwfField(track, "CodingHistory"), CodingHistory = GetBwfField(track, "CodingHistory"),
Originator = GetBwfField(track, "Originator"), BwfOriginator = GetBwfField(track, "BwfOriginator"),
OriginatorRef = GetBwfField(track, "OriginatorRef"), BwfOriginatorRef = GetBwfField(track, "BwfOriginatorRef"),
}; };
return meta; return meta;
+2 -2
View File
@@ -141,7 +141,7 @@ public static class Database
@Bpm, @FrameRate, @Timecode, @Description, @Category, @Subcategory, @Bpm, @FrameRate, @Timecode, @Description, @Category, @Subcategory,
@CatId, @CategoryFull, @Genre, @Style, @Mood, @Keywords, @Rating, @CatId, @CategoryFull, @Genre, @Style, @Mood, @Keywords, @Rating,
@Artist, @Composer, @Designer, @Recordist, @Publisher, @Manufacturer, @Artist, @Composer, @Designer, @Recordist, @Publisher, @Manufacturer,
@Originator, @OriginatorRef, @ProjectName, @Library, @CdTitle, @BwfOriginator, @BwfOriginatorRef, @ProjectName, @Library, @CdTitle,
@TrackTitle, @Episode, @Scene, @Take, @Tape, @CueNumber, @SyncPoint, @TrackTitle, @Episode, @Scene, @Take, @Tape, @CueNumber, @SyncPoint,
@ReleaseDate, @TrackYear, @IsEdited, @IsSplit, @Location, @Group, @ReleaseDate, @TrackYear, @IsEdited, @IsSplit, @Location, @Group,
@Markers, @Comments, @Notes, @Copyright, @CodingHistory, @Microphone, @Markers, @Comments, @Notes, @Copyright, @CodingHistory, @Microphone,
@@ -192,7 +192,7 @@ public static class Database
@Bpm, @FrameRate, @Timecode, @Description, @Category, @Subcategory, @Bpm, @FrameRate, @Timecode, @Description, @Category, @Subcategory,
@CatId, @CategoryFull, @Genre, @Style, @Mood, @Keywords, @Rating, @CatId, @CategoryFull, @Genre, @Style, @Mood, @Keywords, @Rating,
@Artist, @Composer, @Designer, @Recordist, @Publisher, @Manufacturer, @Artist, @Composer, @Designer, @Recordist, @Publisher, @Manufacturer,
@Originator, @OriginatorRef, @ProjectName, @Library, @CdTitle, @BwfOriginator, @BwfOriginatorRef, @ProjectName, @Library, @CdTitle,
@TrackTitle, @Episode, @Scene, @Take, @Tape, @CueNumber, @SyncPoint, @TrackTitle, @Episode, @Scene, @Take, @Tape, @CueNumber, @SyncPoint,
@ReleaseDate, @TrackYear, @IsEdited, @IsSplit, @Location, @Group, @ReleaseDate, @TrackYear, @IsEdited, @IsSplit, @Location, @Group,
@Markers, @Comments, @Notes, @Copyright, @CodingHistory, @Microphone, @Markers, @Comments, @Notes, @Copyright, @CodingHistory, @Microphone,