FileDatabase refactor for normalization and consistency

This commit is contained in:
daniel-c-harvey
2025-09-04 16:26:10 -04:00
parent e82366e47f
commit 6fefcbcfb5
24 changed files with 3069 additions and 186 deletions
+34 -1
View File
@@ -1,7 +1,7 @@
namespace DeepDrftContent.FileDatabase.Models;
/// <summary>
/// Base interface for all index types
/// Base interface for all index types - minimal contract
/// </summary>
public interface IIndex
{
@@ -9,7 +9,13 @@ public interface IIndex
/// Gets the key identifier for this index
/// </summary>
string GetKey();
}
/// <summary>
/// Interface for indexes that support entry queries
/// </summary>
public interface IEntryQueryable : IIndex
{
/// <summary>
/// Gets all entry keys in this index
/// </summary>
@@ -25,3 +31,30 @@ public interface IIndex
/// </summary>
bool HasEntry(EntryKey entryKey);
}
/// <summary>
/// Interface for indexes that support directory operations
/// </summary>
public interface IDirectoryIndex : IEntryQueryable
{
/// <summary>
/// Adds an entry to the directory index
/// </summary>
void PutEntry(EntryKey entryKey);
}
/// <summary>
/// Interface for indexes that support vault operations with metadata
/// </summary>
public interface IVaultIndex : IEntryQueryable
{
/// <summary>
/// Gets metadata for a specific entry
/// </summary>
MetaData? GetEntry(EntryKey entryKey);
/// <summary>
/// Adds an entry with metadata to the vault index
/// </summary>
void PutEntry(EntryKey entryKey, MetaData metaData);
}
@@ -65,7 +65,7 @@ public class VaultIndexData : IndexData
/// <summary>
/// Directory index implementation using StructuralSet for entries
/// </summary>
public class DirectoryIndex : IndexData, IIndex
public class DirectoryIndex : IndexData, IDirectoryIndex
{
public StructuralSet<EntryKey> Entries { get; }
@@ -93,7 +93,7 @@ public class DirectoryIndex : IndexData, IIndex
/// <summary>
/// Vault index implementation using StructuralMap for entries with metadata
/// </summary>
public class VaultIndex : IndexData, IIndex
public class VaultIndex : IndexData, IVaultIndex
{
public StructuralMap<EntryKey, MetaData> Entries { get; }
@@ -1,37 +1,22 @@
using DeepDrftContent.FileDatabase.Abstractions;
using DeepDrftContent.FileDatabase.Services;
namespace DeepDrftContent.FileDatabase.Models;
/// <summary>
/// Type mappings for media vault types to their corresponding classes
/// Type mappings for media vault types - simple dictionary-based approach
/// </summary>
public static class MediaVaultTypeMap
{
public static Type GetBinaryType(MediaVaultType vaultType) => vaultType switch
{
MediaVaultType.Media => typeof(MediaBinary),
MediaVaultType.Image => typeof(ImageBinary),
_ => throw new ArgumentException($"Unknown vault type: {vaultType}")
};
private static readonly IMediaTypeRegistry _registry = new SimpleMediaTypeRegistry();
public static Type GetDtoType(MediaVaultType vaultType) => vaultType switch
{
MediaVaultType.Media => typeof(MediaBinaryDto),
MediaVaultType.Image => typeof(ImageBinaryDto),
_ => throw new ArgumentException($"Unknown vault type: {vaultType}")
};
public static Type GetBinaryType(MediaVaultType vaultType) => _registry.GetBinaryType(vaultType);
public static Type GetParamsType(MediaVaultType vaultType) => vaultType switch
{
MediaVaultType.Media => typeof(MediaBinaryParams),
MediaVaultType.Image => typeof(ImageBinaryParams),
_ => throw new ArgumentException($"Unknown vault type: {vaultType}")
};
public static Type GetDtoType(MediaVaultType vaultType) => _registry.GetDtoType(vaultType);
public static Type GetMetaDataType(MediaVaultType vaultType) => vaultType switch
{
MediaVaultType.Media => typeof(MetaData),
MediaVaultType.Image => typeof(ImageMetaData),
_ => throw new ArgumentException($"Unknown vault type: {vaultType}")
};
public static Type GetParamsType(MediaVaultType vaultType) => _registry.GetParamsType(vaultType);
public static Type GetMetaDataType(MediaVaultType vaultType) => _registry.GetMetaDataType(vaultType);
}
/// <summary>
@@ -39,38 +24,52 @@ public static class MediaVaultTypeMap
/// </summary>
public static class MetaDataFactory
{
public static MetaData Create(MediaVaultType type, string entryKey, string extension, double aspectRatio = 1.0)
public static MetaData Create(MediaVaultType type, string entryKey, string extension)
{
return type switch
{
MediaVaultType.Media => new MetaData(entryKey, extension),
MediaVaultType.Image => new ImageMetaData(entryKey, extension, aspectRatio),
MediaVaultType.Image => throw new ArgumentException("Image metadata requires aspect ratio. Use CreateImageMetaData instead."),
MediaVaultType.Audio => throw new ArgumentException("Audio metadata requires duration and bitrate. Use CreateAudioMetaData instead."),
_ => throw new ArgumentException($"Unknown vault type: {type}")
};
}
public static T Create<T>(MediaVaultType type, string entryKey, string extension, double aspectRatio = 1.0)
public static ImageMetaData CreateImageMetaData(string entryKey, string extension, double aspectRatio)
{
return new ImageMetaData(entryKey, extension, aspectRatio);
}
public static AudioMetaData CreateAudioMetaData(string entryKey, string extension, double duration, int bitrate)
{
return new AudioMetaData(entryKey, extension, duration, bitrate);
}
private static readonly IMediaTypeRegistry _metaDataRegistry = new SimpleMediaTypeRegistry();
public static MetaData CreateFromMedia(MediaVaultType type, string entryKey, string extension, object media)
{
return _metaDataRegistry.CreateMetaDataFromMedia(type, entryKey, extension, media);
}
public static T Create<T>(MediaVaultType type, string entryKey, string extension)
where T : MetaData
{
var metaData = Create(type, entryKey, extension, aspectRatio);
var metaData = Create(type, entryKey, extension);
return (T)metaData;
}
}
/// <summary>
/// Factory for creating media parameter objects
/// Factory for creating media parameter objects - simple dictionary-based approach
/// </summary>
public static class MediaParamsFactory
{
private static readonly IMediaTypeRegistry _registry = new SimpleMediaTypeRegistry();
public static object Create(MediaVaultType type, FileBinary fileBinary, MetaData metaData)
{
return type switch
{
MediaVaultType.Media => new MediaBinaryParams(fileBinary.Buffer, fileBinary.Size, metaData.Extension),
MediaVaultType.Image when metaData is ImageMetaData imageMetaData =>
new ImageBinaryParams(fileBinary.Buffer, fileBinary.Size, metaData.Extension, imageMetaData.AspectRatio),
_ => throw new ArgumentException($"Invalid vault type {type} or metadata type mismatch")
};
return _registry.CreateParams(type, fileBinary, metaData);
}
public static T Create<T>(MediaVaultType type, FileBinary fileBinary, MetaData metaData)
@@ -81,20 +80,15 @@ public static class MediaParamsFactory
}
/// <summary>
/// Factory for creating media binary objects from parameters
/// Factory for creating media binary objects - simple dictionary-based approach
/// </summary>
public static class FileBinaryFactory
{
private static readonly IMediaTypeRegistry _registry = new SimpleMediaTypeRegistry();
public static object Create(MediaVaultType vaultType, object parameters)
{
return vaultType switch
{
MediaVaultType.Media when parameters is MediaBinaryParams mediaParams =>
new MediaBinary(mediaParams),
MediaVaultType.Image when parameters is ImageBinaryParams imageParams =>
new ImageBinary(imageParams),
_ => throw new ArgumentException($"Invalid vault type {vaultType} or parameter type mismatch")
};
return _registry.CreateBinary(vaultType, parameters);
}
public static T Create<T>(MediaVaultType vaultType, object parameters) where T : FileBinary
@@ -105,14 +99,7 @@ public static class FileBinaryFactory
public static object From(MediaVaultType type, object mediaBinaryDto)
{
return type switch
{
MediaVaultType.Media when mediaBinaryDto is MediaBinaryDto mediaDto =>
MediaBinary.From(mediaDto),
MediaVaultType.Image when mediaBinaryDto is ImageBinaryDto imageDto =>
ImageBinary.From(imageDto),
_ => throw new ArgumentException($"Invalid type {type} or DTO type mismatch")
};
return _registry.CreateBinaryFromDto(type, mediaBinaryDto);
}
public static T From<T>(MediaVaultType type, object mediaBinaryDto) where T : FileBinary
@@ -123,20 +110,18 @@ public static class FileBinaryFactory
}
/// <summary>
/// Factory for creating DTO objects from media binaries
/// Factory for creating DTO objects from media binaries - simple dictionary-based approach
/// </summary>
public static class FileBinaryDtoFactory
{
private static readonly IMediaTypeRegistry _registry = new SimpleMediaTypeRegistry();
public static object From(MediaVaultType type, object mediaBinary)
{
return type switch
{
MediaVaultType.Media when mediaBinary is MediaBinary media =>
new MediaBinaryDto(media),
MediaVaultType.Image when mediaBinary is ImageBinary image =>
new ImageBinaryDto(image),
_ => throw new ArgumentException($"Invalid type {type} or binary type mismatch")
};
if (mediaBinary is not FileBinary fileBinary)
throw new ArgumentException($"Expected FileBinary but got {mediaBinary.GetType()}");
return _registry.CreateDto(type, fileBinary);
}
public static T From<T>(MediaVaultType type, object mediaBinary)
@@ -140,6 +140,63 @@ public record ImageBinaryDto(string Base64, int Size, string Mime, double Aspect
imageBinary.AspectRatio) { }
}
/// <summary>
/// Parameters for creating an AudioBinary
/// </summary>
/// <param name="Buffer">The binary data</param>
/// <param name="Size">The size of the data in bytes</param>
/// <param name="Extension">The file extension</param>
/// <param name="Duration">The duration of the audio in seconds</param>
/// <param name="Bitrate">The bitrate of the audio in kbps</param>
public record AudioBinaryParams(byte[] Buffer, int Size, string Extension, double Duration, int Bitrate)
: MediaBinaryParams(Buffer, Size, Extension);
/// <summary>
/// Audio binary with duration and bitrate information
/// </summary>
public class AudioBinary : MediaBinary
{
public double Duration { get; }
public int Bitrate { get; }
public AudioBinary(AudioBinaryParams parameters) : base(parameters)
{
Duration = parameters.Duration;
Bitrate = parameters.Bitrate;
}
public static AudioBinary From(AudioBinaryDto dto)
{
var buffer = Convert.FromBase64String(dto.Base64);
var extension = GetExtensionType(dto.Mime);
return new AudioBinary(new AudioBinaryParams(buffer, dto.Size, extension, dto.Duration, dto.Bitrate));
}
private static string GetExtensionType(string mime)
{
return MimeTypeExtensions.GetExtension(mime);
}
}
/// <summary>
/// DTO for AudioBinary serialization
/// </summary>
/// <param name="Base64">Base64 encoded binary data</param>
/// <param name="Size">Size of the original data</param>
/// <param name="Mime">MIME type of the media</param>
/// <param name="Duration">The duration of the audio in seconds</param>
/// <param name="Bitrate">The bitrate of the audio in kbps</param>
public record AudioBinaryDto(string Base64, int Size, string Mime, double Duration, int Bitrate)
: MediaBinaryDto(Base64, Size, Mime)
{
public AudioBinaryDto(AudioBinary audioBinary) : this(
Convert.ToBase64String(audioBinary.Buffer),
audioBinary.Size,
MimeTypeExtensions.GetMimeType(audioBinary.Extension),
audioBinary.Duration,
audioBinary.Bitrate) { }
}
/// <summary>
/// Utility class for MIME type and extension conversions
/// </summary>
@@ -153,7 +210,13 @@ public static class MimeTypeExtensions
{ ".gif", "image/gif" },
{ ".webp", "image/webp" },
{ ".svg", "image/svg+xml" },
{ ".bmp", "image/bmp" }
{ ".bmp", "image/bmp" },
{ ".mp3", "audio/mpeg" },
{ ".wav", "audio/wav" },
{ ".flac", "audio/flac" },
{ ".aac", "audio/aac" },
{ ".ogg", "audio/ogg" },
{ ".m4a", "audio/mp4" }
};
private static readonly Dictionary<string, string> Extensions = new()
@@ -163,7 +226,13 @@ public static class MimeTypeExtensions
{ "image/gif", ".gif" },
{ "image/webp", ".webp" },
{ "image/svg+xml", ".svg" },
{ "image/bmp", ".bmp" }
{ "image/bmp", ".bmp" },
{ "audio/mpeg", ".mp3" },
{ "audio/wav", ".wav" },
{ "audio/flac", ".flac" },
{ "audio/aac", ".aac" },
{ "audio/ogg", ".ogg" },
{ "audio/mp4", ".m4a" }
};
public static string GetMimeType(string extension)
@@ -6,5 +6,6 @@ namespace DeepDrftContent.FileDatabase.Models;
public enum MediaVaultType
{
Media,
Image
Image,
Audio
}
@@ -10,6 +10,7 @@ namespace DeepDrftContent.FileDatabase.Models;
[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type")]
[JsonDerivedType(typeof(MetaData), typeDiscriminator: "media")]
[JsonDerivedType(typeof(ImageMetaData), typeDiscriminator: "image")]
[JsonDerivedType(typeof(AudioMetaData), typeDiscriminator: "audio")]
public record MetaData(string MediaKey, string Extension);
/// <summary>
@@ -20,3 +21,13 @@ public record MetaData(string MediaKey, string Extension);
/// <param name="AspectRatio">The aspect ratio of the image</param>
public record ImageMetaData(string MediaKey, string Extension, double AspectRatio)
: MetaData(MediaKey, Extension);
/// <summary>
/// Extended metadata for audio entries, including duration and bitrate
/// </summary>
/// <param name="MediaKey">The key used to identify the media file</param>
/// <param name="Extension">The file extension of the media</param>
/// <param name="Duration">The duration of the audio in seconds</param>
/// <param name="Bitrate">The bitrate of the audio in kbps</param>
public record AudioMetaData(string MediaKey, string Extension, double Duration, int Bitrate)
: MetaData(MediaKey, Extension);