refactor dbcontext lifetime (#258)
* refactor create playout handler * refactor get all playouts handler * refactor delete playout handler * remove dead code * ignore unnamed artists for collections * more repository cleanup * more schedule items refactoring * more playout refactoring * refactor playout builder * refactor ffmpeg profiles * more ffmpeg profile refactoring * rework resolutions * refactor media collections * refactor config elements * update changelog * more cleanup
This commit is contained in:
@@ -22,14 +22,6 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
_dbConnection = dbConnection;
|
||||
}
|
||||
|
||||
public async Task<Channel> Add(Channel channel)
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
await dbContext.Channels.AddAsync(channel);
|
||||
await dbContext.SaveChangesAsync();
|
||||
return channel;
|
||||
}
|
||||
|
||||
public async Task<Option<Channel>> Get(int id)
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
@@ -124,11 +116,6 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
await dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public Task<int> CountPlayouts(int channelId) =>
|
||||
_dbConnection.QuerySingleAsync<int>(
|
||||
@"SELECT COUNT(*) FROM Playout WHERE ChannelId = @ChannelId",
|
||||
new { ChannelId = channelId });
|
||||
|
||||
public async Task<Unit> RemoveWatermark(Channel channel)
|
||||
{
|
||||
if (channel.Watermark != null)
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Infrastructure.Extensions;
|
||||
using LanguageExt;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using static LanguageExt.Prelude;
|
||||
@@ -11,36 +12,56 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
{
|
||||
public class ConfigElementRepository : IConfigElementRepository
|
||||
{
|
||||
private readonly TvContext _dbContext;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
|
||||
public ConfigElementRepository(TvContext dbContext) => _dbContext = dbContext;
|
||||
public ConfigElementRepository(IDbContextFactory<TvContext> dbContextFactory) =>
|
||||
_dbContextFactory = dbContextFactory;
|
||||
|
||||
public async Task<ConfigElement> Add(ConfigElement configElement)
|
||||
public async Task<Unit> Upsert<T>(ConfigElementKey configElementKey, T value)
|
||||
{
|
||||
await _dbContext.ConfigElements.AddAsync(configElement);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
return configElement;
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
Option<ConfigElement> maybeElement = await dbContext.ConfigElements
|
||||
.SelectOneAsync(c => c.Key, c => c.Key == configElementKey.Key);
|
||||
|
||||
await maybeElement.Match(
|
||||
async element =>
|
||||
{
|
||||
element.Value = value.ToString();
|
||||
await dbContext.SaveChangesAsync();
|
||||
},
|
||||
async () =>
|
||||
{
|
||||
var configElement = new ConfigElement
|
||||
{
|
||||
Key = configElementKey.Key,
|
||||
Value = value.ToString()
|
||||
};
|
||||
|
||||
await dbContext.ConfigElements.AddAsync(configElement);
|
||||
await dbContext.SaveChangesAsync();
|
||||
});
|
||||
|
||||
return Unit.Default;
|
||||
}
|
||||
|
||||
public Task<Option<ConfigElement>> Get(ConfigElementKey key) =>
|
||||
_dbContext.ConfigElements
|
||||
public async Task<Option<ConfigElement>> Get(ConfigElementKey key)
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
return await dbContext.ConfigElements
|
||||
.OrderBy(ce => ce.Key)
|
||||
.SingleOrDefaultAsync(ce => ce.Key == key.Key)
|
||||
.Map(Optional);
|
||||
}
|
||||
|
||||
public Task<Option<T>> GetValue<T>(ConfigElementKey key) =>
|
||||
Get(key).MapT(ce => (T) Convert.ChangeType(ce.Value, typeof(T)));
|
||||
|
||||
public Task Update(ConfigElement configElement)
|
||||
public async Task Delete(ConfigElement configElement)
|
||||
{
|
||||
_dbContext.ConfigElements.Update(configElement);
|
||||
return _dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public Task Delete(ConfigElement configElement)
|
||||
{
|
||||
_dbContext.ConfigElements.Remove(configElement);
|
||||
return _dbContext.SaveChangesAsync();
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
dbContext.ConfigElements.Remove(configElement);
|
||||
await dbContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
|
||||
@@ -11,45 +8,10 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
{
|
||||
public class FFmpegProfileRepository : IFFmpegProfileRepository
|
||||
{
|
||||
private readonly TvContext _dbContext;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
|
||||
public FFmpegProfileRepository(IDbContextFactory<TvContext> dbContextFactory, TvContext dbContext)
|
||||
{
|
||||
public FFmpegProfileRepository(IDbContextFactory<TvContext> dbContextFactory) =>
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_dbContext = dbContext;
|
||||
}
|
||||
|
||||
public async Task<FFmpegProfile> Add(FFmpegProfile ffmpegProfile)
|
||||
{
|
||||
await _dbContext.FFmpegProfiles.AddAsync(ffmpegProfile);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
return ffmpegProfile;
|
||||
}
|
||||
|
||||
public async Task<Option<FFmpegProfile>> Get(int id) =>
|
||||
await _dbContext.FFmpegProfiles
|
||||
.Include(p => p.Resolution)
|
||||
.OrderBy(p => p.Id)
|
||||
.SingleOrDefaultAsync(p => p.Id == id);
|
||||
|
||||
public Task<List<FFmpegProfile>> GetAll() =>
|
||||
_dbContext.FFmpegProfiles
|
||||
.Include(p => p.Resolution)
|
||||
.ToListAsync();
|
||||
|
||||
public Task Update(FFmpegProfile ffmpegProfile)
|
||||
{
|
||||
_dbContext.FFmpegProfiles.Update(ffmpegProfile);
|
||||
return _dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task Delete(int ffmpegProfileId)
|
||||
{
|
||||
FFmpegProfile ffmpegProfile = await _dbContext.FFmpegProfiles.FindAsync(ffmpegProfileId);
|
||||
_dbContext.FFmpegProfiles.Remove(ffmpegProfile);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<FFmpegProfile> Copy(int ffmpegProfileId, string name)
|
||||
{
|
||||
|
||||
@@ -14,203 +14,40 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
public class MediaCollectionRepository : IMediaCollectionRepository
|
||||
{
|
||||
private readonly IDbConnection _dbConnection;
|
||||
private readonly TvContext _dbContext;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
|
||||
public MediaCollectionRepository(
|
||||
TvContext dbContext,
|
||||
IDbContextFactory<TvContext> dbContextFactory,
|
||||
IDbConnection dbConnection)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_dbConnection = dbConnection;
|
||||
}
|
||||
|
||||
public async Task<Collection> Add(Collection collection)
|
||||
{
|
||||
await _dbContext.Collections.AddAsync(collection);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
return collection;
|
||||
}
|
||||
|
||||
public async Task<bool> AddMediaItem(int collectionId, int mediaItemId)
|
||||
{
|
||||
var modified = false;
|
||||
|
||||
Option<Collection> maybeCollection = await _dbContext.Collections
|
||||
.Include(c => c.MediaItems)
|
||||
.OrderBy(c => c.Id)
|
||||
.SingleOrDefaultAsync(c => c.Id == collectionId)
|
||||
.Map(Optional);
|
||||
|
||||
await maybeCollection.IfSomeAsync(
|
||||
async collection =>
|
||||
{
|
||||
if (collection.MediaItems.All(i => i.Id != mediaItemId))
|
||||
{
|
||||
Option<MediaItem> maybeMediaItem = await _dbContext.MediaItems
|
||||
.OrderBy(i => i.Id)
|
||||
.SingleOrDefaultAsync(i => i.Id == mediaItemId)
|
||||
.Map(Optional);
|
||||
|
||||
await maybeMediaItem.IfSomeAsync(
|
||||
async mediaItem =>
|
||||
{
|
||||
collection.MediaItems.Add(mediaItem);
|
||||
modified = await _dbContext.SaveChangesAsync() > 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
public async Task<bool> AddMediaItems(int collectionId, List<int> mediaItemIds)
|
||||
{
|
||||
var modified = false;
|
||||
|
||||
Option<Collection> maybeCollection = await _dbContext.Collections
|
||||
.Include(c => c.MediaItems)
|
||||
.OrderBy(c => c.Id)
|
||||
.SingleOrDefaultAsync(c => c.Id == collectionId)
|
||||
.Map(Optional);
|
||||
|
||||
await maybeCollection.IfSomeAsync(
|
||||
async collection =>
|
||||
{
|
||||
var toAdd = mediaItemIds.Filter(i => collection.MediaItems.All(i2 => i2.Id != i)).ToList();
|
||||
if (toAdd.Any())
|
||||
{
|
||||
List<MediaItem> items = await _dbContext.MediaItems
|
||||
.Filter(mi => toAdd.Contains(mi.Id))
|
||||
.ToListAsync();
|
||||
|
||||
collection.MediaItems.AddRange(items);
|
||||
modified = await _dbContext.SaveChangesAsync() > 0;
|
||||
}
|
||||
});
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
public Task<Option<Collection>> Get(int id) =>
|
||||
_dbContext.Collections
|
||||
.Include(c => c.CollectionItems)
|
||||
.OrderBy(c => c.Id)
|
||||
.SingleOrDefaultAsync(c => c.Id == id)
|
||||
.Map(Optional);
|
||||
|
||||
public Task<Option<Collection>> GetCollectionWithItems(int id) =>
|
||||
_dbContext.Collections
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => i.LibraryPath)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Movie).MovieMetadata)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as MusicVideo).MusicVideoMetadata)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Show).ShowMetadata)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Season).Show)
|
||||
.ThenInclude(s => s.ShowMetadata)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Episode).EpisodeMetadata)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Episode).Season)
|
||||
.ThenInclude(s => s.Show)
|
||||
.ThenInclude(s => s.ShowMetadata)
|
||||
.OrderBy(c => c.Id)
|
||||
.SingleOrDefaultAsync(c => c.Id == id)
|
||||
.Map(Optional);
|
||||
|
||||
public Task<Option<Collection>> GetCollectionWithItemsUntracked(int id) =>
|
||||
_dbContext.Collections
|
||||
.AsNoTracking()
|
||||
.Include(c => c.CollectionItems)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => i.LibraryPath)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Movie).MovieMetadata)
|
||||
.ThenInclude(mm => mm.Artwork)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Artist).ArtistMetadata)
|
||||
.ThenInclude(mvm => mvm.Artwork)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as MusicVideo).MusicVideoMetadata)
|
||||
.ThenInclude(mvm => mvm.Artwork)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as MusicVideo).Artist)
|
||||
.ThenInclude(a => a.ArtistMetadata)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Show).ShowMetadata)
|
||||
.ThenInclude(sm => sm.Artwork)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Season).SeasonMetadata)
|
||||
.ThenInclude(sm => sm.Artwork)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Season).Show)
|
||||
.ThenInclude(s => s.ShowMetadata)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Episode).EpisodeMetadata)
|
||||
.ThenInclude(em => em.Artwork)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Episode).EpisodeMetadata)
|
||||
.ThenInclude(em => em.Directors)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Episode).EpisodeMetadata)
|
||||
.ThenInclude(em => em.Writers)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Episode).Season)
|
||||
.ThenInclude(s => s.Show)
|
||||
.ThenInclude(s => s.ShowMetadata)
|
||||
.Include(c => c.MediaItems)
|
||||
.ThenInclude(i => (i as Episode).Season)
|
||||
.ThenInclude(s => s.SeasonMetadata)
|
||||
.OrderBy(c => c.Id)
|
||||
.SingleOrDefaultAsync(c => c.Id == id)
|
||||
.Map(Optional);
|
||||
|
||||
public Task<Option<Collection>> GetCollectionWithCollectionItemsUntracked(int id) =>
|
||||
_dbContext.Collections
|
||||
.Include(c => c.CollectionItems)
|
||||
.OrderBy(c => c.Id)
|
||||
.SingleOrDefaultAsync(c => c.Id == id)
|
||||
.Map(Optional);
|
||||
|
||||
public Task<List<Collection>> GetAll() =>
|
||||
_dbContext.Collections.ToListAsync();
|
||||
|
||||
public Task<int> CountAllCollections() =>
|
||||
_dbConnection.QuerySingleAsync<int>(@"SELECT COUNT (*) FROM Collection");
|
||||
|
||||
public async Task<List<Collection>> GetPagedCollections(int pageNumber, int pageSize)
|
||||
public async Task<Option<Collection>> GetCollectionWithCollectionItemsUntracked(int id)
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
return await dbContext.Collections.FromSqlRaw(
|
||||
@"SELECT * FROM Collection
|
||||
ORDER BY Name
|
||||
LIMIT {0} OFFSET {1}",
|
||||
pageSize,
|
||||
pageNumber * pageSize)
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
return await dbContext.Collections
|
||||
.Include(c => c.CollectionItems)
|
||||
.OrderBy(c => c.Id)
|
||||
.SingleOrDefaultAsync(c => c.Id == id)
|
||||
.Map(Optional);
|
||||
}
|
||||
|
||||
public Task<Option<List<MediaItem>>> GetItems(int id) =>
|
||||
Get(id).MapT(GetItemsForCollection).Bind(x => x.Sequence());
|
||||
|
||||
public Task<bool> Update(Collection collection)
|
||||
public async Task<List<MediaItem>> GetItems(int collectionId)
|
||||
{
|
||||
_dbContext.Collections.Update(collection);
|
||||
return _dbContext.SaveChangesAsync().Map(result => result > 0);
|
||||
}
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
public async Task Delete(int collectionId)
|
||||
{
|
||||
Collection mediaCollection = await _dbContext.Collections.FindAsync(collectionId);
|
||||
_dbContext.Collections.Remove(mediaCollection);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
var result = new List<MediaItem>();
|
||||
|
||||
result.AddRange(await GetMovieItems(dbContext, collectionId));
|
||||
result.AddRange(await GetShowItems(dbContext, collectionId));
|
||||
result.AddRange(await GetSeasonItems(dbContext, collectionId));
|
||||
result.AddRange(await GetEpisodeItems(dbContext, collectionId));
|
||||
result.AddRange(await GetArtistItems(dbContext, collectionId));
|
||||
result.AddRange(await GetMusicVideoItems(dbContext, collectionId));
|
||||
|
||||
return result.Distinct().ToList();
|
||||
}
|
||||
|
||||
public Task<List<int>> PlayoutIdsUsingCollection(int collectionId) =>
|
||||
@@ -228,45 +65,31 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
@"SELECT IFNULL(MIN(UseCustomPlaybackOrder), 0) FROM Collection WHERE Id = @CollectionId",
|
||||
new { CollectionId = collectionId });
|
||||
|
||||
private async Task<List<MediaItem>> GetItemsForCollection(Collection collection)
|
||||
{
|
||||
var result = new List<MediaItem>();
|
||||
|
||||
result.AddRange(await GetMovieItems(collection));
|
||||
result.AddRange(await GetShowItems(collection));
|
||||
result.AddRange(await GetSeasonItems(collection));
|
||||
result.AddRange(await GetEpisodeItems(collection));
|
||||
result.AddRange(await GetArtistItems(collection));
|
||||
result.AddRange(await GetMusicVideoItems(collection));
|
||||
|
||||
return result.Distinct().ToList();
|
||||
}
|
||||
|
||||
private async Task<List<Movie>> GetMovieItems(Collection collection)
|
||||
private async Task<List<Movie>> GetMovieItems(TvContext dbContext, int collectionId)
|
||||
{
|
||||
IEnumerable<int> ids = await _dbConnection.QueryAsync<int>(
|
||||
@"SELECT m.Id FROM CollectionItem ci
|
||||
INNER JOIN Movie m ON m.Id = ci.MediaItemId
|
||||
WHERE ci.CollectionId = @CollectionId",
|
||||
new { CollectionId = collection.Id });
|
||||
new { CollectionId = collectionId });
|
||||
|
||||
return await _dbContext.Movies
|
||||
return await dbContext.Movies
|
||||
.Include(m => m.MovieMetadata)
|
||||
.Include(m => m.MediaVersions)
|
||||
.Filter(m => ids.Contains(m.Id))
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
private async Task<List<MusicVideo>> GetArtistItems(Collection collection)
|
||||
private async Task<List<MusicVideo>> GetArtistItems(TvContext dbContext, int collectionId)
|
||||
{
|
||||
IEnumerable<int> ids = await _dbConnection.QueryAsync<int>(
|
||||
@"SELECT MusicVideo.Id FROM CollectionItem ci
|
||||
INNER JOIN Artist on Artist.Id = ci.MediaItemId
|
||||
INNER JOIN MusicVideo on Artist.Id = MusicVideo.ArtistId
|
||||
WHERE ci.CollectionId = @CollectionId",
|
||||
new { CollectionId = collection.Id });
|
||||
new { CollectionId = collectionId });
|
||||
|
||||
return await _dbContext.MusicVideos
|
||||
return await dbContext.MusicVideos
|
||||
.Include(m => m.Artist)
|
||||
.ThenInclude(a => a.ArtistMetadata)
|
||||
.Include(m => m.MusicVideoMetadata)
|
||||
@@ -276,15 +99,15 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
}
|
||||
|
||||
|
||||
private async Task<List<MusicVideo>> GetMusicVideoItems(Collection collection)
|
||||
private async Task<List<MusicVideo>> GetMusicVideoItems(TvContext dbContext, int collectionId)
|
||||
{
|
||||
IEnumerable<int> ids = await _dbConnection.QueryAsync<int>(
|
||||
@"SELECT m.Id FROM CollectionItem ci
|
||||
INNER JOIN MusicVideo m ON m.Id = ci.MediaItemId
|
||||
WHERE ci.CollectionId = @CollectionId",
|
||||
new { CollectionId = collection.Id });
|
||||
new { CollectionId = collectionId });
|
||||
|
||||
return await _dbContext.MusicVideos
|
||||
return await dbContext.MusicVideos
|
||||
.Include(m => m.Artist)
|
||||
.ThenInclude(a => a.ArtistMetadata)
|
||||
.Include(m => m.MusicVideoMetadata)
|
||||
@@ -293,7 +116,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
private async Task<List<Episode>> GetShowItems(Collection collection)
|
||||
private async Task<List<Episode>> GetShowItems(TvContext dbContext, int collectionId)
|
||||
{
|
||||
IEnumerable<int> ids = await _dbConnection.QueryAsync<int>(
|
||||
@"SELECT Episode.Id FROM CollectionItem ci
|
||||
@@ -301,9 +124,9 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
INNER JOIN Season ON Season.ShowId = Show.Id
|
||||
INNER JOIN Episode ON Episode.SeasonId = Season.Id
|
||||
WHERE ci.CollectionId = @CollectionId",
|
||||
new { CollectionId = collection.Id });
|
||||
new { CollectionId = collectionId });
|
||||
|
||||
return await _dbContext.Episodes
|
||||
return await dbContext.Episodes
|
||||
.Include(e => e.EpisodeMetadata)
|
||||
.Include(e => e.MediaVersions)
|
||||
.Include(e => e.Season)
|
||||
@@ -313,16 +136,16 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
private async Task<List<Episode>> GetSeasonItems(Collection collection)
|
||||
private async Task<List<Episode>> GetSeasonItems(TvContext dbContext, int collectionId)
|
||||
{
|
||||
IEnumerable<int> ids = await _dbConnection.QueryAsync<int>(
|
||||
@"SELECT Episode.Id FROM CollectionItem ci
|
||||
INNER JOIN Season ON Season.Id = ci.MediaItemId
|
||||
INNER JOIN Episode ON Episode.SeasonId = Season.Id
|
||||
WHERE ci.CollectionId = @CollectionId",
|
||||
new { CollectionId = collection.Id });
|
||||
new { CollectionId = collectionId });
|
||||
|
||||
return await _dbContext.Episodes
|
||||
return await dbContext.Episodes
|
||||
.Include(e => e.EpisodeMetadata)
|
||||
.Include(e => e.MediaVersions)
|
||||
.Include(e => e.Season)
|
||||
@@ -332,15 +155,15 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
private async Task<List<Episode>> GetEpisodeItems(Collection collection)
|
||||
private async Task<List<Episode>> GetEpisodeItems(TvContext dbContext, int collectionId)
|
||||
{
|
||||
IEnumerable<int> ids = await _dbConnection.QueryAsync<int>(
|
||||
@"SELECT Episode.Id FROM CollectionItem ci
|
||||
INNER JOIN Episode ON Episode.Id = ci.MediaItemId
|
||||
WHERE ci.CollectionId = @CollectionId",
|
||||
new { CollectionId = collection.Id });
|
||||
new { CollectionId = collectionId });
|
||||
|
||||
return await _dbContext.Episodes
|
||||
return await dbContext.Episodes
|
||||
.Include(e => e.EpisodeMetadata)
|
||||
.Include(e => e.MediaVersions)
|
||||
.Include(e => e.Season)
|
||||
|
||||
@@ -3,47 +3,16 @@ using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
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 MediaItemRepository : IMediaItemRepository
|
||||
{
|
||||
private readonly IDbConnection _dbConnection;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
|
||||
public MediaItemRepository(IDbContextFactory<TvContext> dbContextFactory, IDbConnection dbConnection)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_dbConnection = dbConnection;
|
||||
}
|
||||
|
||||
public async Task<Option<MediaItem>> Get(int id)
|
||||
{
|
||||
await using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
return await context.MediaItems
|
||||
.Include(i => i.LibraryPath)
|
||||
.OrderBy(i => i.Id)
|
||||
.SingleOrDefaultAsync(i => i.Id == id)
|
||||
.Map(Optional);
|
||||
}
|
||||
|
||||
public async Task<List<MediaItem>> GetAll()
|
||||
{
|
||||
await using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
return await context.MediaItems.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> Update(MediaItem mediaItem)
|
||||
{
|
||||
await using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
context.MediaItems.Update(mediaItem);
|
||||
return await context.SaveChangesAsync() > 0;
|
||||
}
|
||||
public MediaItemRepository(IDbConnection dbConnection) => _dbConnection = dbConnection;
|
||||
|
||||
public Task<List<string>> GetAllLanguageCodes() =>
|
||||
_dbConnection.QueryAsync<string>(
|
||||
|
||||
@@ -24,14 +24,6 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
_dbConnection = dbConnection;
|
||||
}
|
||||
|
||||
public async Task<LocalMediaSource> Add(LocalMediaSource localMediaSource)
|
||||
{
|
||||
await using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
await context.LocalMediaSources.AddAsync(localMediaSource);
|
||||
await context.SaveChangesAsync();
|
||||
return localMediaSource;
|
||||
}
|
||||
|
||||
public async Task<PlexMediaSource> Add(PlexMediaSource plexMediaSource)
|
||||
{
|
||||
await using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
@@ -40,18 +32,6 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
return plexMediaSource;
|
||||
}
|
||||
|
||||
public async Task<List<MediaSource>> GetAll()
|
||||
{
|
||||
await using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
List<MediaSource> all = await context.MediaSources.ToListAsync();
|
||||
foreach (PlexMediaSource plex in all.OfType<PlexMediaSource>())
|
||||
{
|
||||
await context.Entry(plex).Collection(p => p.Connections).LoadAsync();
|
||||
}
|
||||
|
||||
return all;
|
||||
}
|
||||
|
||||
public Task<List<PlexMediaSource>> GetAllPlex()
|
||||
{
|
||||
using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
@@ -87,15 +67,6 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
.Map(Optional);
|
||||
}
|
||||
|
||||
public Task<Option<MediaSource>> Get(int id)
|
||||
{
|
||||
using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
return context.MediaSources
|
||||
.OrderBy(s => s.Id) // https://github.com/dotnet/efcore/issues/22579
|
||||
.SingleOrDefaultAsync(s => s.Id == id)
|
||||
.Map(Optional);
|
||||
}
|
||||
|
||||
public Task<Option<PlexMediaSource>> GetPlex(int id)
|
||||
{
|
||||
using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
@@ -140,20 +111,6 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public Task<int> CountMediaItems(int id)
|
||||
{
|
||||
using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
return context.MediaItems
|
||||
.CountAsync(i => i.LibraryPath.Library.MediaSourceId == id);
|
||||
}
|
||||
|
||||
public async Task Update(LocalMediaSource localMediaSource)
|
||||
{
|
||||
await using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
context.LocalMediaSources.Update(localMediaSource);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task Update(
|
||||
PlexMediaSource plexMediaSource,
|
||||
List<PlexConnection> sortedConnections,
|
||||
@@ -328,21 +285,6 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
return Unit.Default;
|
||||
}
|
||||
|
||||
public async Task Update(PlexLibrary plexMediaSourceLibrary)
|
||||
{
|
||||
await using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
context.PlexLibraries.Update(plexMediaSourceLibrary);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task Delete(int mediaSourceId)
|
||||
{
|
||||
await using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
MediaSource mediaSource = await context.MediaSources.FindAsync(mediaSourceId);
|
||||
context.MediaSources.Remove(mediaSource);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<List<int>> DeleteAllPlex()
|
||||
{
|
||||
await using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
|
||||
@@ -139,25 +139,6 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
async () => await AddPlexMovie(context, library, item));
|
||||
}
|
||||
|
||||
public Task<int> GetMovieCount() =>
|
||||
_dbConnection.QuerySingleAsync<int>(@"SELECT COUNT(DISTINCT MovieId) FROM MovieMetadata");
|
||||
|
||||
public async Task<List<MovieMetadata>> GetPagedMovies(int pageNumber, int pageSize)
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
return await 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)
|
||||
.AsNoTracking()
|
||||
.Include(mm => mm.Artwork)
|
||||
.OrderBy(mm => mm.SortTitle)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<MovieMetadata>> GetMoviesForCards(List<int> ids)
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
@@ -131,23 +131,6 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Option<MusicVideo>> GetMusicVideo(int musicVideoId)
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
return await dbContext.MusicVideos
|
||||
.Include(m => m.MusicVideoMetadata)
|
||||
.ThenInclude(m => m.Artwork)
|
||||
.Include(m => m.MusicVideoMetadata)
|
||||
.ThenInclude(m => m.Genres)
|
||||
.Include(m => m.MusicVideoMetadata)
|
||||
.ThenInclude(m => m.Tags)
|
||||
.Include(m => m.MusicVideoMetadata)
|
||||
.ThenInclude(m => m.Studios)
|
||||
.OrderBy(m => m.Id)
|
||||
.SingleOrDefaultAsync(m => m.Id == musicVideoId)
|
||||
.Map(Optional);
|
||||
}
|
||||
|
||||
public Task<IEnumerable<string>> FindOrphanPaths(LibraryPath libraryPath) =>
|
||||
_dbConnection.QueryAsync<string>(
|
||||
@"SELECT MF.Path
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
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 PlayoutRepository : IPlayoutRepository
|
||||
{
|
||||
private readonly TvContext _dbContext;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
|
||||
public PlayoutRepository(TvContext dbContext, IDbContextFactory<TvContext> dbContextFactory)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
_dbContextFactory = dbContextFactory;
|
||||
}
|
||||
|
||||
public async Task<Playout> Add(Playout playout)
|
||||
{
|
||||
await _dbContext.Playouts.AddAsync(playout);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
return playout;
|
||||
}
|
||||
|
||||
public Task<Option<Playout>> Get(int id) =>
|
||||
_dbContext.Playouts
|
||||
.OrderBy(p => p.Id)
|
||||
.SingleOrDefaultAsync(p => p.Id == id)
|
||||
.Map(Optional);
|
||||
|
||||
public async Task<Option<Playout>> GetFull(int id) =>
|
||||
await _dbContext.Playouts
|
||||
.Include(p => p.Channel)
|
||||
.Include(p => p.Items)
|
||||
.Include(p => p.ProgramScheduleAnchors)
|
||||
.ThenInclude(a => a.MediaItem)
|
||||
.Include(p => p.ProgramSchedule)
|
||||
.ThenInclude(ps => ps.Items)
|
||||
.ThenInclude(psi => psi.Collection)
|
||||
.Include(p => p.ProgramSchedule)
|
||||
.ThenInclude(ps => ps.Items)
|
||||
.ThenInclude(psi => psi.MediaItem)
|
||||
.OrderBy(p => p.Id) // https://github.com/dotnet/efcore/issues/22579#issuecomment-694772289
|
||||
.SingleOrDefaultAsync(p => p.Id == id);
|
||||
|
||||
public Task<Option<PlayoutItem>> GetPlayoutItem(int channelId, DateTimeOffset now) =>
|
||||
_dbContext.PlayoutItems
|
||||
.Where(pi => pi.Playout.ChannelId == channelId)
|
||||
.Where(pi => pi.Start <= now.UtcDateTime && pi.Finish > now.UtcDateTime)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as Episode).MediaVersions)
|
||||
.ThenInclude(mv => mv.MediaFiles)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as Episode).MediaVersions)
|
||||
.ThenInclude(mv => mv.Streams)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as Movie).MediaVersions)
|
||||
.ThenInclude(mv => mv.MediaFiles)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as Movie).MediaVersions)
|
||||
.ThenInclude(mv => mv.Streams)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as MusicVideo).MediaVersions)
|
||||
.ThenInclude(mv => mv.MediaFiles)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as MusicVideo).MediaVersions)
|
||||
.ThenInclude(mv => mv.Streams)
|
||||
.AsNoTracking()
|
||||
.OrderBy(pi => pi.Start)
|
||||
.SingleOrDefaultAsync()
|
||||
.Map(Optional);
|
||||
|
||||
public Task<Option<DateTimeOffset>> GetNextItemStart(int channelId, DateTimeOffset now) =>
|
||||
_dbContext.PlayoutItems
|
||||
.Where(pi => pi.Playout.ChannelId == channelId)
|
||||
.Where(pi => pi.Start > now.UtcDateTime)
|
||||
.OrderBy(pi => pi.Start)
|
||||
.FirstOrDefaultAsync()
|
||||
.Map(Optional)
|
||||
.MapT(pi => pi.StartOffset);
|
||||
|
||||
public async Task<List<PlayoutItem>> GetPlayoutItems(int playoutId)
|
||||
{
|
||||
await using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
return await context.PlayoutItems
|
||||
.AsNoTracking()
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as Movie).MovieMetadata)
|
||||
.ThenInclude(mm => mm.Artwork)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as Movie).MediaVersions)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as MusicVideo).MusicVideoMetadata)
|
||||
.ThenInclude(mm => mm.Artwork)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as MusicVideo).MediaVersions)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as MusicVideo).Artist)
|
||||
.ThenInclude(mm => mm.ArtistMetadata)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as Episode).EpisodeMetadata)
|
||||
.ThenInclude(em => em.Artwork)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as Episode).MediaVersions)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as Episode).Season)
|
||||
.ThenInclude(s => s.SeasonMetadata)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(mi => (mi as Episode).Season.Show)
|
||||
.ThenInclude(s => s.ShowMetadata)
|
||||
.Filter(i => i.PlayoutId == playoutId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<Playout>> GetAll()
|
||||
{
|
||||
await using TvContext context = _dbContextFactory.CreateDbContext();
|
||||
return await context.Playouts
|
||||
.AsNoTracking()
|
||||
.Include(p => p.Channel)
|
||||
.Include(p => p.ProgramSchedule)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public Task Update(Playout playout)
|
||||
{
|
||||
_dbContext.Playouts.Update(playout);
|
||||
return _dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task Delete(int playoutId)
|
||||
{
|
||||
Playout playout = await _dbContext.Playouts.FindAsync(playoutId);
|
||||
_dbContext.Playouts.Remove(playout);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
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 ProgramScheduleRepository : IProgramScheduleRepository
|
||||
{
|
||||
private readonly TvContext _dbContext;
|
||||
|
||||
public ProgramScheduleRepository(TvContext dbContext) => _dbContext = dbContext;
|
||||
|
||||
public async Task<ProgramSchedule> Add(ProgramSchedule programSchedule)
|
||||
{
|
||||
await _dbContext.ProgramSchedules.AddAsync(programSchedule);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
return programSchedule;
|
||||
}
|
||||
|
||||
public Task<Option<ProgramSchedule>> Get(int id) =>
|
||||
_dbContext.ProgramSchedules
|
||||
.OrderBy(s => s.Id)
|
||||
.SingleOrDefaultAsync(s => s.Id == id)
|
||||
.Map(Optional);
|
||||
|
||||
public async Task<Option<ProgramSchedule>> GetWithPlayouts(int id) =>
|
||||
await _dbContext.ProgramSchedules
|
||||
.Include(ps => ps.Items)
|
||||
.Include(ps => ps.Playouts)
|
||||
.OrderBy(ps => ps.Id)
|
||||
.SingleOrDefaultAsync(ps => ps.Id == id);
|
||||
|
||||
public Task<List<ProgramSchedule>> GetAll() =>
|
||||
_dbContext.ProgramSchedules.ToListAsync();
|
||||
|
||||
public async Task Update(ProgramSchedule programSchedule)
|
||||
{
|
||||
_dbContext.ProgramSchedules.Update(programSchedule);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
await _dbContext.Entry(programSchedule).Collection(s => s.Items).Query().Include(i => i.Collection)
|
||||
.LoadAsync();
|
||||
await _dbContext.Entry(programSchedule).Collection(s => s.Playouts).LoadAsync();
|
||||
}
|
||||
|
||||
public async Task Delete(int programScheduleId)
|
||||
{
|
||||
ProgramSchedule programSchedule = await _dbContext.ProgramSchedules.FindAsync(programScheduleId);
|
||||
_dbContext.ProgramSchedules.Remove(programSchedule);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<Option<List<ProgramScheduleItem>>> GetItems(int programScheduleId)
|
||||
{
|
||||
Option<ProgramSchedule> maybeSchedule = await Get(programScheduleId);
|
||||
return await maybeSchedule.Map(
|
||||
async programSchedule =>
|
||||
{
|
||||
await _dbContext.Entry(programSchedule).Collection(s => s.Items).LoadAsync();
|
||||
await _dbContext.Entry(programSchedule).Collection(s => s.Items).Query()
|
||||
.Include(i => i.Collection)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(i => (i as Movie).MovieMetadata)
|
||||
.ThenInclude(mm => mm.Artwork)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(i => (i as Season).SeasonMetadata)
|
||||
.ThenInclude(sm => sm.Artwork)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(i => (i as Season).Show)
|
||||
.ThenInclude(s => s.ShowMetadata)
|
||||
.ThenInclude(sm => sm.Artwork)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(i => (i as Show).ShowMetadata)
|
||||
.ThenInclude(sm => sm.Artwork)
|
||||
.Include(i => i.MediaItem)
|
||||
.ThenInclude(i => (i as Artist).ArtistMetadata)
|
||||
.ThenInclude(am => am.Artwork)
|
||||
.LoadAsync();
|
||||
return programSchedule.Items;
|
||||
}).Sequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
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 ResolutionRepository : IResolutionRepository
|
||||
{
|
||||
private readonly TvContext _dbContext;
|
||||
|
||||
public ResolutionRepository(TvContext dbContext) => _dbContext = dbContext;
|
||||
|
||||
public Task<Option<Resolution>> Get(int id) =>
|
||||
_dbContext.Resolutions
|
||||
.OrderBy(r => r.Id)
|
||||
.SingleOrDefaultAsync(r => r.Id == id)
|
||||
.Map(Optional);
|
||||
|
||||
public Task<List<Resolution>> GetAll() =>
|
||||
_dbContext.Resolutions.ToListAsync();
|
||||
}
|
||||
}
|
||||
@@ -162,19 +162,6 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Option<Episode>> GetEpisode(int episodeId)
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
return await dbContext.Episodes
|
||||
.AsNoTracking()
|
||||
.Include(e => e.Season)
|
||||
.Include(e => e.EpisodeMetadata)
|
||||
.ThenInclude(em => em.Artwork)
|
||||
.OrderBy(s => s.Id)
|
||||
.SingleOrDefaultAsync(s => s.Id == episodeId)
|
||||
.Map(Optional);
|
||||
}
|
||||
|
||||
public async Task<int> GetEpisodeCount(int seasonId)
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
@@ -58,6 +58,7 @@ namespace ErsatzTV.Infrastructure.Data
|
||||
public DbSet<Collection> Collections { get; set; }
|
||||
public DbSet<CollectionItem> CollectionItems { get; set; }
|
||||
public DbSet<ProgramSchedule> ProgramSchedules { get; set; }
|
||||
public DbSet<ProgramScheduleItem> ProgramScheduleItems { get; set; }
|
||||
public DbSet<Playout> Playouts { get; set; }
|
||||
public DbSet<PlayoutItem> PlayoutItems { get; set; }
|
||||
public DbSet<PlayoutProgramScheduleAnchor> PlayoutProgramScheduleItemAnchors { get; set; }
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using LanguageExt;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Extensions
|
||||
{
|
||||
public static class ConfigElementQueryableExtensions
|
||||
{
|
||||
public static Task<Option<T>> GetValue<T>(
|
||||
this IQueryable<ConfigElement> configElements,
|
||||
ConfigElementKey key) =>
|
||||
configElements
|
||||
.SelectOneAsync(ce => ce.Key, ce => ce.Key == key.Key)
|
||||
.MapT(ce => (T) Convert.ChangeType(ce.Value, typeof(T)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using LanguageExt;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using static LanguageExt.Prelude;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Extensions
|
||||
{
|
||||
public static class PlayoutItemQueryableExtensions
|
||||
{
|
||||
public static Task<Option<PlayoutItem>> ForChannelAndTime(
|
||||
this IQueryable<PlayoutItem> dbSet,
|
||||
int channelId,
|
||||
DateTimeOffset time) =>
|
||||
dbSet.Filter(pi => pi.Playout.ChannelId == channelId)
|
||||
.Filter(pi => pi.Start <= time.UtcDateTime && pi.Finish > time.UtcDateTime)
|
||||
.OrderBy(pi => pi.Start)
|
||||
.FirstOrDefaultAsync()
|
||||
.Map(Optional);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using LanguageExt;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Extensions
|
||||
{
|
||||
public static class QueryableExtensions
|
||||
{
|
||||
public static async Task<Option<T>> SelectOneAsync<T, TKey>(
|
||||
this IQueryable<T> enumerable,
|
||||
Expression<Func<T, TKey>> keySelector,
|
||||
Expression<Func<T, bool>> predicate) where T : class =>
|
||||
await enumerable.OrderBy(keySelector).FirstOrDefaultAsync(predicate);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user