using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Threading.Tasks; using Dapper; using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; using Microsoft.EntityFrameworkCore; using static LanguageExt.Prelude; namespace ErsatzTV.Infrastructure.Data.Repositories { public class MovieRepository : IMovieRepository { private readonly IDbConnection _dbConnection; private readonly TvContext _dbContext; private readonly IDbContextFactory _dbContextFactory; public MovieRepository( TvContext dbContext, IDbContextFactory dbContextFactory, IDbConnection dbConnection) { _dbContext = dbContext; _dbContextFactory = dbContextFactory; _dbConnection = dbConnection; } public async Task> GetMovie(int movieId) { await using TvContext dbContext = _dbContextFactory.CreateDbContext(); return await dbContext.Movies .Include(m => m.MovieMetadata) .ThenInclude(m => m.Artwork) .OrderBy(m => m.Id) .SingleOrDefaultAsync(m => m.Id == movieId) .Map(Optional); } public async Task> GetOrAdd(LibraryPath libraryPath, string path) { Option maybeExisting = await _dbContext.Movies .Include(i => i.MovieMetadata) .ThenInclude(mm => mm.Artwork) .Include(i => i.LibraryPath) .Include(i => i.MediaVersions) .ThenInclude(mv => mv.MediaFiles) .OrderBy(i => i.MediaVersions.First().MediaFiles.First().Path) .SingleOrDefaultAsync(i => i.MediaVersions.First().MediaFiles.First().Path == path); return await maybeExisting.Match( mediaItem => Right(mediaItem).AsTask(), async () => await AddMovie(libraryPath.Id, path)); } public async Task> GetOrAdd(PlexLibrary library, PlexMovie item) { await using TvContext context = _dbContextFactory.CreateDbContext(); Option maybeExisting = await context.PlexMovies .AsNoTracking() .Include(i => i.MovieMetadata) .Include(i => i.MediaVersions) .ThenInclude(mv => mv.MediaFiles) .OrderBy(i => i.Key) .SingleOrDefaultAsync(i => i.Key == item.Key); return await maybeExisting.Match( plexMovie => Right(plexMovie).AsTask(), async () => await AddPlexMovie(context, library, item)); } public async Task Update(Movie movie) { _dbContext.Movies.Update(movie); return await _dbContext.SaveChangesAsync() > 0; } public Task GetMovieCount() => _dbConnection.QuerySingleAsync(@"SELECT COUNT(DISTINCT MovieId) FROM MovieMetadata"); public Task> GetPagedMovies(int pageNumber, int pageSize) => _dbContext.MovieMetadata.FromSqlRaw( @"SELECT * FROM MovieMetadata WHERE Id IN (SELECT Id FROM MovieMetadata GROUP BY MovieId, MetadataKind HAVING MetadataKind = MAX(MetadataKind)) ORDER BY SortTitle LIMIT {0} OFFSET {1}", pageSize, (pageNumber - 1) * pageSize) .Include(mm => mm.Artwork) .OrderBy(mm => mm.SortTitle) .ToListAsync(); public Task> FindMoviePaths(LibraryPath libraryPath) => _dbConnection.QueryAsync( @"SELECT MF.Path FROM MediaFile MF INNER JOIN MediaVersion MV on MF.MediaVersionId = MV.Id INNER JOIN Movie M on MV.MovieId = M.Id INNER JOIN MediaItem MI on M.Id = MI.Id WHERE MI.LibraryPathId = @LibraryPathId", new { LibraryPathId = libraryPath.Id }); public async Task DeleteByPath(LibraryPath libraryPath, string path) { IEnumerable ids = await _dbConnection.QueryAsync( @"SELECT M.Id FROM Movie M INNER JOIN MediaItem MI on M.Id = MI.Id INNER JOIN MediaVersion MV on M.Id = MV.MovieId INNER JOIN MediaFile MF on MV.Id = MF.MediaVersionId WHERE MI.LibraryPathId = @LibraryPathId AND MF.Path = @Path", new { LibraryPathId = libraryPath.Id, Path = path }); foreach (int movieId in ids) { Movie movie = await _dbContext.Movies.FindAsync(movieId); _dbContext.Movies.Remove(movie); } await _dbContext.SaveChangesAsync(); return Unit.Default; } private async Task> AddMovie(int libraryPathId, string path) { try { var movie = new Movie { LibraryPathId = libraryPathId, MediaVersions = new List { new() { MediaFiles = new List { new() { Path = path } } } } }; await _dbContext.Movies.AddAsync(movie); await _dbContext.SaveChangesAsync(); await _dbContext.Entry(movie).Reference(m => m.LibraryPath).LoadAsync(); return movie; } catch (Exception ex) { return BaseError.New(ex.Message); } } private async Task> AddPlexMovie( TvContext context, PlexLibrary library, PlexMovie item) { try { item.LibraryPathId = library.Paths.Head().Id; await context.PlexMovies.AddAsync(item); await context.SaveChangesAsync(); await context.Entry(item).Reference(i => i.LibraryPath).LoadAsync(); return item; } catch (Exception ex) { return BaseError.New(ex.Message); } } } }