diff --git a/DeepDrftContent.Services/DeepDrftContent.Services.csproj b/DeepDrftContent.Services/DeepDrftContent.Services.csproj
index e132d96..30e03d1 100644
--- a/DeepDrftContent.Services/DeepDrftContent.Services.csproj
+++ b/DeepDrftContent.Services/DeepDrftContent.Services.csproj
@@ -10,4 +10,8 @@
+
+
+
+
diff --git a/DeepDrftContent.Services/FileDatabase/Models/MediaFactories.cs b/DeepDrftContent.Services/FileDatabase/Models/MediaFactories.cs
index e277f79..80be94a 100644
--- a/DeepDrftContent.Services/FileDatabase/Models/MediaFactories.cs
+++ b/DeepDrftContent.Services/FileDatabase/Models/MediaFactories.cs
@@ -3,12 +3,20 @@ using DeepDrftContent.Services.FileDatabase.Services;
namespace DeepDrftContent.Services.FileDatabase.Models;
+///
+/// Shared media type registry instance — one allocation for all factory classes in this file.
+///
+file static class SharedMediaTypeRegistry
+{
+ internal static readonly IMediaTypeRegistry Instance = new SimpleMediaTypeRegistry();
+}
+
///
/// Type mappings for media vault types - simple dictionary-based approach
///
public static class MediaVaultTypeMap
{
- private static readonly IMediaTypeRegistry _registry = new SimpleMediaTypeRegistry();
+ private static readonly IMediaTypeRegistry _registry = SharedMediaTypeRegistry.Instance;
public static Type GetBinaryType(MediaVaultType vaultType) => _registry.GetBinaryType(vaultType);
@@ -55,7 +63,7 @@ public static class MetaDataFactory
return new AudioMetaData(entryKey, extension, duration, bitrate);
}
- private static readonly IMediaTypeRegistry _metaDataRegistry = new SimpleMediaTypeRegistry();
+ private static readonly IMediaTypeRegistry _metaDataRegistry = SharedMediaTypeRegistry.Instance;
public static MetaData CreateFromMedia(MediaVaultType type, string entryKey, string extension, object media)
{
@@ -75,7 +83,7 @@ public static class MetaDataFactory
///
public static class MediaParamsFactory
{
- private static readonly IMediaTypeRegistry _registry = new SimpleMediaTypeRegistry();
+ private static readonly IMediaTypeRegistry _registry = SharedMediaTypeRegistry.Instance;
public static object Create(MediaVaultType type, FileBinary fileBinary, MetaData metaData)
{
@@ -94,7 +102,7 @@ public static class MediaParamsFactory
///
public static class FileBinaryFactory
{
- private static readonly IMediaTypeRegistry _registry = new SimpleMediaTypeRegistry();
+ private static readonly IMediaTypeRegistry _registry = SharedMediaTypeRegistry.Instance;
public static object Create(MediaVaultType vaultType, object parameters)
{
@@ -124,7 +132,7 @@ public static class FileBinaryFactory
///
public static class FileBinaryDtoFactory
{
- private static readonly IMediaTypeRegistry _registry = new SimpleMediaTypeRegistry();
+ private static readonly IMediaTypeRegistry _registry = SharedMediaTypeRegistry.Instance;
public static object From(MediaVaultType type, object mediaBinary)
{
diff --git a/DeepDrftContent.Services/FileDatabase/Models/MediaModels.cs b/DeepDrftContent.Services/FileDatabase/Models/MediaModels.cs
index 6a096b5..862fac9 100644
--- a/DeepDrftContent.Services/FileDatabase/Models/MediaModels.cs
+++ b/DeepDrftContent.Services/FileDatabase/Models/MediaModels.cs
@@ -68,7 +68,7 @@ public class MediaBinary : FileBinary
return new MediaBinary(new MediaBinaryParams(buffer, dto.Size, extension));
}
- private static string GetExtensionType(string mime)
+ protected static string GetExtensionType(string mime)
{
return MimeTypeExtensions.GetExtension(mime);
}
@@ -116,11 +116,6 @@ public class ImageBinary : MediaBinary
var extension = GetExtensionType(dto.Mime);
return new ImageBinary(new ImageBinaryParams(buffer, dto.Size, extension, dto.AspectRatio));
}
-
- private static string GetExtensionType(string mime)
- {
- return MimeTypeExtensions.GetExtension(mime);
- }
}
///
@@ -171,11 +166,6 @@ public class AudioBinary : MediaBinary
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);
- }
}
///
diff --git a/DeepDrftContent.Services/FileDatabase/Services/FileDatabase.cs b/DeepDrftContent.Services/FileDatabase/Services/FileDatabase.cs
index 975d463..87dd442 100644
--- a/DeepDrftContent.Services/FileDatabase/Services/FileDatabase.cs
+++ b/DeepDrftContent.Services/FileDatabase/Services/FileDatabase.cs
@@ -11,6 +11,7 @@ public class FileDatabase : DirectoryIndexDirectory, IDisposable
{
private readonly StructuralMap _vaults;
private readonly IndexWatcher _indexWatcher;
+ private readonly IndexFactoryService _indexFactory;
private bool _disposed;
///
@@ -23,7 +24,7 @@ public class FileDatabase : DirectoryIndexDirectory, IDisposable
if (rootIndex != null)
{
- var db = new FileDatabase(rootPath, rootIndex);
+ var db = new FileDatabase(rootPath, rootIndex, factoryService);
await db.InitVaultsAsync();
return db;
}
@@ -31,10 +32,11 @@ public class FileDatabase : DirectoryIndexDirectory, IDisposable
return null;
}
- private FileDatabase(string rootPath, IDirectoryIndex index) : base(rootPath, index)
+ private FileDatabase(string rootPath, IDirectoryIndex index, IndexFactoryService indexFactory) : base(rootPath, index)
{
_vaults = new StructuralMap();
_indexWatcher = new IndexWatcher();
+ _indexFactory = indexFactory;
}
///
@@ -58,7 +60,7 @@ public class FileDatabase : DirectoryIndexDirectory, IDisposable
private async Task InitVaultAsync(string vaultId, MediaVaultType vaultType)
{
var path = Path.Combine(RootPath, vaultId);
- var directoryVault = await MediaVaultFactory.From(path, vaultType);
+ var directoryVault = await MediaVaultFactory.From(path, vaultType, _indexFactory);
if (directoryVault != null)
{
@@ -80,9 +82,8 @@ public class FileDatabase : DirectoryIndexDirectory, IDisposable
{
try
{
- var factoryService = new IndexFactoryService();
var vaultPath = Path.Combine(RootPath, vaultId);
- var index = await factoryService.LoadIndexAsync(IndexType.Vault, vaultPath);
+ var index = await _indexFactory.LoadIndexAsync(IndexType.Vault, vaultPath);
if (index is VaultIndex vaultIndex)
{
@@ -115,25 +116,18 @@ public class FileDatabase : DirectoryIndexDirectory, IDisposable
}
///
- /// Creates a new vault
+ /// Creates a new vault. Propagates exceptions to the caller — vault creation failure is not
+ /// silently swallowable because a partially-created vault would leave the index inconsistent.
///
public async Task CreateVaultAsync(string vaultId, MediaVaultType vaultType)
{
- try
- {
- var path = Path.Combine(RootPath, vaultId);
- var directoryVault = await MediaVaultFactory.From(path, vaultType);
+ var path = Path.Combine(RootPath, vaultId);
+ var directoryVault = await MediaVaultFactory.From(path, vaultType, _indexFactory);
- if (directoryVault != null)
- {
- _vaults.Set(vaultId, directoryVault);
- // Now using string-based index
- await AddToIndexAsync(vaultId);
- }
- }
- catch
+ if (directoryVault != null)
{
- throw;
+ _vaults.Set(vaultId, directoryVault);
+ await AddToIndexAsync(vaultId);
}
}
diff --git a/DeepDrftContent.Services/FileDatabase/Services/IndexSystem.cs b/DeepDrftContent.Services/FileDatabase/Services/IndexSystem.cs
index 5802b8f..7f79375 100644
--- a/DeepDrftContent.Services/FileDatabase/Services/IndexSystem.cs
+++ b/DeepDrftContent.Services/FileDatabase/Services/IndexSystem.cs
@@ -1,6 +1,7 @@
using DeepDrftContent.Services.FileDatabase.Abstractions;
using DeepDrftContent.Services.FileDatabase.Models;
using DeepDrftContent.Services.FileDatabase.Utils;
+using Microsoft.Extensions.Logging;
namespace DeepDrftContent.Services.FileDatabase.Services;
@@ -96,12 +97,15 @@ public class VaultIndexDirectory : IndexDirectory
{
private IVaultIndex _vaultIndex;
private readonly SemaphoreSlim _indexLock = new(1, 1);
- private readonly IndexFactoryService _factoryService = new();
+ private readonly IndexFactoryService _factoryService;
+ private readonly ILogger? _logger;
- public VaultIndexDirectory(string rootPath, IVaultIndex index, IIndexDataFactory? indexDataFactory = null)
- : base(rootPath, IndexType.Vault, index, indexDataFactory)
+ public VaultIndexDirectory(string rootPath, IVaultIndex index, IIndexDataFactory? indexDataFactory = null, ILogger? logger = null, IndexFactoryService? factoryService = null)
+ : base(rootPath, IndexType.Vault, index, indexDataFactory ?? factoryService)
{
_vaultIndex = index;
+ _logger = logger;
+ _factoryService = factoryService ?? new IndexFactoryService();
}
protected async Task AddToIndexAsync(string entryId, MetaData metaData)
@@ -131,12 +135,18 @@ public class VaultIndexDirectory : IndexDirectory
{
_vaultIndex = vaultIndex;
Index = vaultIndex;
- Console.WriteLine($"VaultIndexDirectory: Reloaded index for {RootPath}, {vaultIndex.GetEntriesSize()} entries");
+ if (_logger != null)
+ _logger.LogDebug("VaultIndexDirectory: Reloaded index for {RootPath}, {EntryCount} entries", RootPath, vaultIndex.GetEntriesSize());
+ else
+ Console.WriteLine($"VaultIndexDirectory: Reloaded index for {RootPath}, {vaultIndex.GetEntriesSize()} entries");
}
}
catch (Exception ex)
{
- Console.WriteLine($"VaultIndexDirectory: Failed to reload index for {RootPath}: {ex.Message}");
+ if (_logger != null)
+ _logger.LogWarning(ex, "VaultIndexDirectory: Failed to reload index for {RootPath}", RootPath);
+ else
+ Console.WriteLine($"VaultIndexDirectory: Failed to reload index for {RootPath}: {ex.Message}");
}
finally
{
diff --git a/DeepDrftContent.Services/FileDatabase/Services/IndexWatcher.cs b/DeepDrftContent.Services/FileDatabase/Services/IndexWatcher.cs
index 9daad5b..2c6e3ec 100644
--- a/DeepDrftContent.Services/FileDatabase/Services/IndexWatcher.cs
+++ b/DeepDrftContent.Services/FileDatabase/Services/IndexWatcher.cs
@@ -1,4 +1,5 @@
using DeepDrftContent.Services.FileDatabase.Models;
+using Microsoft.Extensions.Logging;
namespace DeepDrftContent.Services.FileDatabase.Services;
@@ -11,8 +12,14 @@ public class IndexWatcher : IDisposable
private readonly Dictionary _watchers = new();
private readonly Dictionary _reloadCallbacks = new();
private readonly object _lock = new();
+ private readonly ILogger? _logger;
private bool _disposed;
+ public IndexWatcher(ILogger? logger = null)
+ {
+ _logger = logger;
+ }
+
///
/// Registers an index file to be watched for changes.
///
@@ -46,11 +53,17 @@ public class IndexWatcher : IDisposable
_watchers[indexPath] = watcher;
_reloadCallbacks[indexPath] = onChanged;
- Console.WriteLine($"IndexWatcher: Watching {indexPath}/index");
+ if (_logger != null)
+ _logger.LogDebug("IndexWatcher: Watching {IndexPath}/index", indexPath);
+ else
+ Console.WriteLine($"IndexWatcher: Watching {indexPath}/index");
}
catch (Exception ex)
{
- Console.WriteLine($"IndexWatcher: Failed to watch {indexPath}: {ex.Message}");
+ if (_logger != null)
+ _logger.LogWarning(ex, "IndexWatcher: Failed to watch {IndexPath}", indexPath);
+ else
+ Console.WriteLine($"IndexWatcher: Failed to watch {indexPath}: {ex.Message}");
}
}
}
@@ -83,7 +96,10 @@ public class IndexWatcher : IDisposable
{
if (_reloadCallbacks.TryGetValue(indexPath, out var callback))
{
- Console.WriteLine($"IndexWatcher: Index changed at {indexPath}, triggering reload");
+ if (_logger != null)
+ _logger.LogDebug("IndexWatcher: Index changed at {IndexPath}, triggering reload", indexPath);
+ else
+ Console.WriteLine($"IndexWatcher: Index changed at {indexPath}, triggering reload");
// Invoke callback on a background thread to avoid blocking the watcher
Task.Run(() =>
@@ -94,7 +110,10 @@ public class IndexWatcher : IDisposable
}
catch (Exception ex)
{
- Console.WriteLine($"IndexWatcher: Reload callback failed: {ex.Message}");
+ if (_logger != null)
+ _logger.LogWarning(ex, "IndexWatcher: Reload callback failed for {IndexPath}", indexPath);
+ else
+ Console.WriteLine($"IndexWatcher: Reload callback failed: {ex.Message}");
}
});
}
diff --git a/DeepDrftContent.Services/FileDatabase/Services/MediaVault.cs b/DeepDrftContent.Services/FileDatabase/Services/MediaVault.cs
index 525d5a5..a10f29a 100644
--- a/DeepDrftContent.Services/FileDatabase/Services/MediaVault.cs
+++ b/DeepDrftContent.Services/FileDatabase/Services/MediaVault.cs
@@ -9,7 +9,8 @@ namespace DeepDrftContent.Services.FileDatabase.Services;
///
public abstract class MediaVault : VaultIndexDirectory
{
- protected MediaVault(string rootPath, VaultIndex index) : base(rootPath, index) { }
+ protected MediaVault(string rootPath, VaultIndex index, IndexFactoryService? factoryService = null)
+ : base(rootPath, index, factoryService: factoryService) { }
///
/// Generates a media key from an entry key by sanitizing special characters
@@ -105,19 +106,20 @@ public abstract class MediaVault : VaultIndexDirectory
///
public class ImageVault : MediaVault
{
- private ImageVault(string rootPath, VaultIndex index) : base(rootPath, index) { }
+ private ImageVault(string rootPath, VaultIndex index, IndexFactoryService? factoryService = null)
+ : base(rootPath, index, factoryService) { }
///
/// Factory method to create an ImageVault instance
///
- public static async Task FromAsync(string rootPath)
+ public static async Task FromAsync(string rootPath, IndexFactoryService? factoryService = null)
{
- var factoryService = new IndexFactoryService();
- var index = await factoryService.LoadOrCreateVaultIndexAsync(rootPath, MediaVaultType.Image);
+ var factory = factoryService ?? new IndexFactoryService();
+ var index = await factory.LoadOrCreateVaultIndexAsync(rootPath, MediaVaultType.Image);
if (index != null)
{
- return new ImageVault(rootPath, (VaultIndex)index);
+ return new ImageVault(rootPath, (VaultIndex)index, factory);
}
return null;
@@ -126,16 +128,17 @@ public class ImageVault : MediaVault
public class AudioVault : MediaVault
{
- private AudioVault(string rootPath, VaultIndex index) : base(rootPath, index) { }
-
- public static async Task FromAsync(string rootPath)
+ private AudioVault(string rootPath, VaultIndex index, IndexFactoryService? factoryService = null)
+ : base(rootPath, index, factoryService) { }
+
+ public static async Task FromAsync(string rootPath, IndexFactoryService? factoryService = null)
{
- var factoryService = new IndexFactoryService();
- var index = await factoryService.LoadOrCreateVaultIndexAsync(rootPath, MediaVaultType.Audio);
+ var factory = factoryService ?? new IndexFactoryService();
+ var index = await factory.LoadOrCreateVaultIndexAsync(rootPath, MediaVaultType.Audio);
if (index != null)
{
- return new AudioVault(rootPath, (VaultIndex)index);
+ return new AudioVault(rootPath, (VaultIndex)index, factory);
}
return null;
diff --git a/DeepDrftContent.Services/FileDatabase/Services/MediaVaultFactory.cs b/DeepDrftContent.Services/FileDatabase/Services/MediaVaultFactory.cs
index 63d53d1..54b0f04 100644
--- a/DeepDrftContent.Services/FileDatabase/Services/MediaVaultFactory.cs
+++ b/DeepDrftContent.Services/FileDatabase/Services/MediaVaultFactory.cs
@@ -1,5 +1,4 @@
-using DeepDrftContent.Services.FileDatabase.Abstractions;
-using DeepDrftContent.Services.FileDatabase.Models;
+using DeepDrftContent.Services.FileDatabase.Models;
namespace DeepDrftContent.Services.FileDatabase.Services;
@@ -8,10 +7,13 @@ namespace DeepDrftContent.Services.FileDatabase.Services;
///
public static class MediaVaultFactory
{
- private static readonly IMediaTypeRegistry _registry = new SimpleMediaTypeRegistry();
-
- public static async Task From(string rootPath, MediaVaultType mediaType)
+ public static async Task From(string rootPath, MediaVaultType mediaType, IndexFactoryService? factoryService = null)
{
- return await _registry.CreateVaultAsync(mediaType, rootPath);
+ return mediaType switch
+ {
+ MediaVaultType.Image => await ImageVault.FromAsync(rootPath, factoryService),
+ MediaVaultType.Audio => await AudioVault.FromAsync(rootPath, factoryService),
+ _ => null
+ };
}
}
\ No newline at end of file
diff --git a/DeepDrftContent.Services/TrackService.cs b/DeepDrftContent.Services/TrackService.cs
index f6d5794..1906031 100644
--- a/DeepDrftContent.Services/TrackService.cs
+++ b/DeepDrftContent.Services/TrackService.cs
@@ -75,9 +75,10 @@ public class TrackService
return trackEntity;
}
- catch (Exception ex)
+ catch (Exception ex) when (ex is not OperationCanceledException)
{
- throw new InvalidOperationException($"Failed to add track: {ex.Message}", ex);
+ Console.WriteLine($"TrackService.AddTrackFromWavAsync failed: {ex.Message}");
+ return null;
}
}
diff --git a/DeepDrftModels/DTOs/TrackDto.cs b/DeepDrftModels/DTOs/TrackDto.cs
index 1993481..f63d8ed 100644
--- a/DeepDrftModels/DTOs/TrackDto.cs
+++ b/DeepDrftModels/DTOs/TrackDto.cs
@@ -4,8 +4,8 @@ public class TrackDto
{
public long Id { get; set; }
public required string EntryKey { get; set; }
- public string TrackName { get; set; }
- public string Artist { get; set; }
+ public required string TrackName { get; set; }
+ public required string Artist { get; set; }
public string? Album { get; set; }
public string? Genre { get; set; }
public DateOnly? ReleaseDate { get; set; }
diff --git a/DeepDrftModels/Models/PagedResult.cs b/DeepDrftModels/Models/PagedResult.cs
index 39a20ec..46ab081 100644
--- a/DeepDrftModels/Models/PagedResult.cs
+++ b/DeepDrftModels/Models/PagedResult.cs
@@ -6,7 +6,7 @@ public class PagedResult
public int TotalCount { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
- public int TotalPages => (int)Math.Ceiling((double)TotalCount / PageSize);
+ public int TotalPages => PageSize > 0 ? (int)Math.Ceiling((double)TotalCount / PageSize) : 0;
public bool HasNextPage => Page < TotalPages;
public bool HasPreviousPage => Page > 1;
@@ -27,7 +27,7 @@ public class PagedResult
public PagedResult(IEnumerable items, int totalCount, int page, int pageSize)
{
- Items = items.ToList() ?? new List();
+ Items = items.ToList();
TotalCount = totalCount;
Page = page;
PageSize = pageSize;