using Dapper; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Repositories; using Microsoft.EntityFrameworkCore; namespace ErsatzTV.Infrastructure.Data.Repositories; public class LibraryRepository : ILibraryRepository { private readonly IDbContextFactory _dbContextFactory; private readonly ILocalFileSystem _localFileSystem; public LibraryRepository(ILocalFileSystem localFileSystem, IDbContextFactory dbContextFactory) { _localFileSystem = localFileSystem; _dbContextFactory = dbContextFactory; } public async Task Add(LibraryPath libraryPath) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); await dbContext.LibraryPaths.AddAsync(libraryPath); await dbContext.SaveChangesAsync(); return libraryPath; } public async Task> GetLibrary(int libraryId) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.Libraries .Include(l => l.Paths) .ThenInclude(p => p.LibraryFolders) .OrderBy(l => l.Id) .SingleOrDefaultAsync(l => l.Id == libraryId) .Map(Optional); } public async Task> GetLocal(int libraryId) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.LocalLibraries .OrderBy(l => l.Id) .SingleOrDefaultAsync(l => l.Id == libraryId) .Map(Optional); } public async Task> GetAll() { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.Libraries .AsNoTracking() .Include(l => l.MediaSource) .Include(l => l.Paths) .ToListAsync(); } public async Task UpdateLastScan(Library library) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.Connection.ExecuteAsync( "UPDATE Library SET LastScan = @LastScan WHERE Id = @Id", new { library.LastScan, library.Id }).ToUnit(); } public async Task UpdateLastScan(LibraryPath libraryPath) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.Connection.ExecuteAsync( "UPDATE LibraryPath SET LastScan = @LastScan WHERE Id = @Id", new { libraryPath.LastScan, libraryPath.Id }).ToUnit(); } public async Task> GetLocalPaths(int libraryId) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.LocalLibraries .Include(l => l.Paths) .OrderBy(l => l.Id) .SingleOrDefaultAsync(l => l.Id == libraryId) .Map(Optional) .Match(l => l.Paths, () => new List()); } public async Task CountMediaItemsByPath(int libraryPathId) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.Connection.QuerySingleAsync( @"SELECT COUNT(*) FROM MediaItem WHERE LibraryPathId = @LibraryPathId", new { LibraryPathId = libraryPathId }); } public async Task SetEtag( LibraryPath libraryPath, Option knownFolder, string path, string etag) => await knownFolder.Match( async folder => { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); await dbContext.Connection.ExecuteAsync( "UPDATE LibraryFolder SET Etag = @Etag WHERE Id = @Id", new { folder.Id, Etag = etag }); }, async () => { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); await dbContext.LibraryFolders.AddAsync( new LibraryFolder { Path = path, Etag = etag, LibraryPathId = libraryPath.Id }); await dbContext.SaveChangesAsync(); }).ToUnit(); public async Task CleanEtagsForLibraryPath(LibraryPath libraryPath) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); IEnumerable folders = await dbContext.Connection.QueryAsync( @"SELECT LF.Path FROM LibraryFolder LF WHERE LF.LibraryPathId = @LibraryPathId", new { LibraryPathId = libraryPath.Id }); foreach (string folder in folders.Where(f => !_localFileSystem.FolderExists(f))) { await dbContext.Connection.ExecuteAsync( @"DELETE FROM LibraryFolder WHERE LibraryPathId = @LibraryPathId AND Path = @Path", new { LibraryPathId = libraryPath.Id, Path = folder }); } return Unit.Default; } }