using DotBased.Monads; using Manager.App.Models.Library; using Manager.App.Models.Settings; using Manager.App.Models.System; using Manager.Data.Contexts; using Manager.Data.Entities.LibraryContext; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; namespace Manager.App.Services; public class LibraryService : ILibraryService { private readonly ILogger _logger; private readonly LibrarySettings _librarySettings; private readonly IDbContextFactory _dbContextFactory; private readonly DirectoryInfo _libraryDirectory; private const string SubDirMedia = "Media"; private const string SubDirChannels = "Channels"; public LibraryService(ILogger logger, IOptions librarySettings, IDbContextFactory contextFactory) { _logger = logger; _librarySettings = librarySettings.Value; _dbContextFactory = contextFactory; _libraryDirectory = Directory.CreateDirectory(_librarySettings.Path); logger.LogDebug("Working dir for library: {LibraryWorkingDir}", _libraryDirectory.FullName); Directory.CreateDirectory(Path.Combine(_librarySettings.Path, SubDirMedia)); Directory.CreateDirectory(Path.Combine(_librarySettings.Path, SubDirChannels)); } public async Task> GetChannelByIdAsync(string id, CancellationToken cancellationToken = default) { if (string.IsNullOrWhiteSpace(id)) { return ResultError.Fail("Channel id cannot be null or empty!"); } try { await using var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken); var channel = await context.Channels.Include(c => c.ClientAccount).FirstOrDefaultAsync(c => c.Id == id, cancellationToken); if (channel == null) { return ResultError.Fail("Channel not found!"); } return channel; } catch (Exception e) { return HandleException(e); } } public async Task SaveChannelAsync(ChannelEntity channel, CancellationToken cancellationToken = default) { try { await using var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken); if (context.Channels.Any(c => c.Id == channel.Id)) { context.Channels.Update(channel); } else { context.Channels.Add(channel); } var changed = await context.SaveChangesAsync(cancellationToken); return changed <= 0 ? Result.Success() : ResultError.Fail("Failed to save channel!"); } catch (Exception e) { return ResultError.Error(e); } } public async Task> GetLibraryInfoAsync(CancellationToken cancellationToken = default) { try { await using var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken); var libInfo = new LibraryInformation { LibraryPath = _libraryDirectory.FullName, CreatedAtUtc = _libraryDirectory.CreationTimeUtc, LastModifiedUtc = _libraryDirectory.LastWriteTimeUtc, TotalChannels = await context.Channels.CountAsync(cancellationToken: cancellationToken), TotalMedia = await context.Media.CountAsync(cancellationToken: cancellationToken), TotalSizeBytes = GetDirectorySize(_libraryDirectory) }; return libInfo; } catch (Exception e) { return HandleException(e); } } public async Task> GetChannelAccountsAsync(int total = 20, int offset = 0, CancellationToken cancellationToken = default) { try { await using var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken); var orderedAccounts = context.Channels.Include(x => x.ClientAccount).Where(x => x.ClientAccount != null).OrderBy(x => x.Id); return new ListResultReturn(orderedAccounts.Skip(offset).Take(total).ToList(),orderedAccounts.Count()); } catch (Exception e) { return HandleException(e); } } private long GetDirectorySize(DirectoryInfo dir) { try { var size = dir.EnumerateFiles("*", SearchOption.AllDirectories).Select(f => f.Length).Sum(); return size; } catch (Exception e) { _logger.LogError(e, "Error while getting directory size."); throw; } } private ResultError HandleException(Exception exception) { if (exception is OperationCanceledException) { return ResultError.Fail("Library service operation cancelled"); } _logger.LogError(exception, "Failed to get library information"); return ResultError.Fail("Failed to get library information"); } }