Finishing the repositories's rework

This commit is contained in:
Zoe Roux 2020-07-19 00:15:34 +02:00
parent 2ad4c89806
commit 81f555ca7e
11 changed files with 144 additions and 708 deletions

View File

@ -93,9 +93,10 @@ namespace Kyoo.Models
{
if (Type != StreamType.Subtitle)
return null;
string slug = $"/subtitle/{Episode.Slug}.{Language ?? ID.ToString()}";
if (IsForced)
slug += "-forced";
string slug = string.IsNullOrEmpty(Language)
? ID.ToString()
: $"{Episode.Slug}.{Language}{(IsForced ? "-forced" : "")}";
switch (Codec)
{
case "ass":

View File

@ -22,7 +22,7 @@ namespace Kyoo.Controllers
public override async Task<ICollection<Collection>> Search(string query)
{
return await _database.Collections
.Where(x => EF.Functions.Like(x.Name, $"%{query}%"))
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
.Take(20)
.ToListAsync();
}

View File

@ -38,7 +38,7 @@ namespace Kyoo.Controllers
public override Task<Episode> Get(string slug)
{
Match match = Regex.Match(slug, @"(<show>.*)-s(<season>\d*)-e(<episode>\d*)");
Match match = Regex.Match(slug, @"(?<show>.*)-s(?<season>\d*)-e(?<episode>\d*)");
if (!match.Success)
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == slug);
@ -57,7 +57,7 @@ namespace Kyoo.Controllers
public override async Task<ICollection<Episode>> Search(string query)
{
return await _database.Episodes
.Where(x => EF.Functions.Like(x.Title, $"%{query}%"))
.Where(x => EF.Functions.ILike(x.Title, $"%{query}%"))
.Take(20)
.ToListAsync();
}

View File

@ -21,10 +21,10 @@ namespace Kyoo.Controllers
}
public async Task<ICollection<Genre>> Search(string query)
public override async Task<ICollection<Genre>> Search(string query)
{
return await _database.Genres
.Where(genre => EF.Functions.Like(genre.Name, $"%{query}%"))
.Where(genre => EF.Functions.ILike(genre.Name, $"%{query}%"))
.Take(20)
.ToListAsync();
}
@ -57,7 +57,7 @@ namespace Kyoo.Controllers
return Task.CompletedTask;
}
public async Task Delete(Genre obj)
public override async Task Delete(Genre obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));

View File

@ -9,54 +9,41 @@ using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers
{
public class LibraryRepository : ILibraryRepository
public class LibraryRepository : LocalRepository<Library>, ILibraryRepository
{
private readonly DatabaseContext _database;
private readonly IProviderRepository _providers;
protected override Expression<Func<Library, object>> DefaultSort => x => x.ID;
public LibraryRepository(DatabaseContext database, IProviderRepository providers)
public LibraryRepository(DatabaseContext database, IProviderRepository providers) : base(database)
{
_database = database;
_providers = providers;
}
public void Dispose()
public override void Dispose()
{
_database.Dispose();
_providers.Dispose();
}
public ValueTask DisposeAsync()
public override async ValueTask DisposeAsync()
{
return _database.DisposeAsync();
await _database.DisposeAsync();
await _providers.DisposeAsync();
}
public Task<Library> Get(int id)
{
return _database.Libraries.FirstOrDefaultAsync(x => x.ID == id);
}
public Task<Library> Get(string slug)
{
return _database.Libraries.FirstOrDefaultAsync(x => x.Slug == slug);
}
public async Task<ICollection<Library>> Search(string query)
public override async Task<ICollection<Library>> Search(string query)
{
return await _database.Libraries
.Where(x => EF.Functions.Like(x.Name, $"%{query}%"))
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
.Take(20)
.ToListAsync();
}
public async Task<ICollection<Library>> GetAll(Expression<Func<Library, bool>> where = null,
Sort<Library> sort = default,
Pagination limit = default)
{
return await _database.Libraries.ToListAsync();
}
public async Task<Library> Create(Library obj)
public override async Task<Library> Create(Library obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
@ -74,73 +61,22 @@ namespace Kyoo.Controllers
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (Helper.IsDuplicateException(ex))
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated library (slug {obj.Slug} already exists).");
throw;
}
return obj;
}
public async Task<Library> CreateIfNotExists(Library obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
Library old = await Get(obj.Slug);
if (old != null)
return old;
try
{
return await Create(obj);
}
catch (DuplicatedItemException)
{
old = await Get(obj.Slug);
if (old == null)
throw new SystemException("Unknown database state.");
return old;
}
}
public async Task<Library> Edit(Library edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
Library old = await Get(edited.Name);
if (old == null)
throw new ItemNotFound($"No library found with the name {edited.Name}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await Validate(old);
await _database.SaveChangesAsync();
return old;
}
private async Task Validate(Library obj)
protected override async Task Validate(Library obj)
{
if (obj.ProviderLinks != null)
foreach (ProviderLink link in obj.ProviderLinks)
link.Provider = await _providers.CreateIfNotExists(link.Provider);
}
public async Task Delete(int id)
{
Library obj = await Get(id);
await Delete(obj);
}
public async Task Delete(string slug)
{
Library obj = await Get(slug);
await Delete(obj);
}
public async Task Delete(Library obj)
public override async Task Delete(Library obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
@ -154,23 +90,5 @@ namespace Kyoo.Controllers
_database.Entry(entry).State = EntityState.Deleted;
await _database.SaveChangesAsync();
}
public async Task DeleteRange(IEnumerable<Library> objs)
{
foreach (Library obj in objs)
await Delete(obj);
}
public async Task DeleteRange(IEnumerable<int> ids)
{
foreach (int id in ids)
await Delete(id);
}
public async Task DeleteRange(IEnumerable<string> slugs)
{
foreach (string slug in slugs)
await Delete(slug);
}
}
}

View File

@ -9,53 +9,40 @@ using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers
{
public class PeopleRepository : IPeopleRepository
public class PeopleRepository : LocalRepository<People>, IPeopleRepository
{
private readonly DatabaseContext _database;
private readonly IProviderRepository _providers;
protected override Expression<Func<People, object>> DefaultSort => x => x.Name;
public PeopleRepository(DatabaseContext database, IProviderRepository providers)
public PeopleRepository(DatabaseContext database, IProviderRepository providers) : base(database)
{
_database = database;
_providers = providers;
}
public void Dispose()
public override void Dispose()
{
_database.Dispose();
_providers.Dispose();
}
public ValueTask DisposeAsync()
public override async ValueTask DisposeAsync()
{
return _database.DisposeAsync();
await _database.DisposeAsync();
await _providers.DisposeAsync();
}
public Task<People> Get(int id)
{
return _database.Peoples.FirstOrDefaultAsync(x => x.ID == id);
}
public Task<People> Get(string slug)
{
return _database.Peoples.FirstOrDefaultAsync(x => x.Slug == slug);
}
public async Task<ICollection<People>> Search(string query)
public override async Task<ICollection<People>> Search(string query)
{
return await _database.Peoples
.Where(people => EF.Functions.Like(people.Name, $"%{query}%"))
.Where(people => EF.Functions.ILike(people.Name, $"%{query}%"))
.Take(20)
.ToListAsync();
}
public async Task<ICollection<People>> GetAll(Expression<Func<People, bool>> where = null,
Sort<People> sort = default,
Pagination limit = default)
{
return await _database.Peoples.ToListAsync();
}
public async Task<People> Create(People obj)
public override async Task<People> Create(People obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
@ -73,7 +60,7 @@ namespace Kyoo.Controllers
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (Helper.IsDuplicateException(ex))
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated people (slug {obj.Slug} already exists).");
throw;
}
@ -81,65 +68,14 @@ namespace Kyoo.Controllers
return obj;
}
public async Task<People> CreateIfNotExists(People obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
People old = await Get(obj.Slug);
if (old != null)
return old;
try
{
return await Create(obj);
}
catch (DuplicatedItemException)
{
old = await Get(obj.Slug);
if (old == null)
throw new SystemException("Unknown database state.");
return old;
}
}
public async Task<People> Edit(People edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
People old = await Get(edited.Slug);
if (old == null)
throw new ItemNotFound($"No people found with the slug {edited.Slug}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await Validate(old);
await _database.SaveChangesAsync();
return old;
}
private async Task Validate(People obj)
protected override async Task Validate(People obj)
{
if (obj.ExternalIDs != null)
foreach (MetadataID link in obj.ExternalIDs)
link.Provider = await _providers.CreateIfNotExists(link.Provider);
}
public async Task Delete(int id)
{
People obj = await Get(id);
await Delete(obj);
}
public async Task Delete(string slug)
{
People obj = await Get(slug);
await Delete(obj);
}
public async Task Delete(People obj)
public override async Task Delete(People obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
@ -153,23 +89,5 @@ namespace Kyoo.Controllers
_database.Entry(link).State = EntityState.Deleted;
await _database.SaveChangesAsync();
}
public async Task DeleteRange(IEnumerable<People> objs)
{
foreach (People obj in objs)
await Delete(obj);
}
public async Task DeleteRange(IEnumerable<int> ids)
{
foreach (int id in ids)
await Delete(id);
}
public async Task DeleteRange(IEnumerable<string> slugs)
{
foreach (string slug in slugs)
await Delete(slug);
}
}
}

View File

@ -9,52 +9,26 @@ using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers
{
public class ProviderRepository : IProviderRepository
public class ProviderRepository : LocalRepository<ProviderID>, IProviderRepository
{
private readonly DatabaseContext _database;
protected override Expression<Func<ProviderID, object>> DefaultSort => x => x.Slug;
public ProviderRepository(DatabaseContext database)
public ProviderRepository(DatabaseContext database) : base(database)
{
_database = database;
}
public void Dispose()
{
_database.Dispose();
}
public ValueTask DisposeAsync()
{
return _database.DisposeAsync();
}
public async Task<ProviderID> Get(int id)
{
return await _database.Providers.FirstOrDefaultAsync(x => x.ID == id);
}
public async Task<ProviderID> Get(string slug)
{
return await _database.Providers.FirstOrDefaultAsync(x => x.Name == slug);
}
public async Task<ICollection<ProviderID>> Search(string query)
public override async Task<ICollection<ProviderID>> Search(string query)
{
return await _database.Providers
.Where(x => EF.Functions.Like(x.Name, $"%{query}%"))
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
.Take(20)
.ToListAsync();
}
public async Task<ICollection<ProviderID>> GetAll(Expression<Func<ProviderID, bool>> where = null,
Sort<ProviderID> sort = default,
Pagination limit = default)
{
return await _database.Providers.ToListAsync();
}
public async Task<ProviderID> Create(ProviderID obj)
public override async Task<ProviderID> Create(ProviderID obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
@ -68,7 +42,7 @@ namespace Kyoo.Controllers
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (Helper.IsDuplicateException(ex))
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated provider (name {obj.Name} already exists).");
throw;
}
@ -76,57 +50,12 @@ namespace Kyoo.Controllers
return obj;
}
public async Task<ProviderID> CreateIfNotExists(ProviderID obj)
protected override Task Validate(ProviderID ressource)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
ProviderID old = await Get(obj.Name);
if (old != null)
return old;
try
{
return await Create(obj);
}
catch (DuplicatedItemException)
{
old = await Get(obj.Name);
if (old == null)
throw new SystemException("Unknown database state.");
return old;
}
return Task.CompletedTask;
}
public async Task<ProviderID> Edit(ProviderID edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
ProviderID old = await Get(edited.Name);
if (old == null)
throw new ItemNotFound($"No provider found with the name {edited.Name}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await _database.SaveChangesAsync();
return old;
}
public async Task Delete(int id)
{
ProviderID obj = await Get(id);
await Delete(obj);
}
public async Task Delete(string slug)
{
ProviderID obj = await Get(slug);
await Delete(obj);
}
public async Task Delete(ProviderID obj)
public override async Task Delete(ProviderID obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
@ -135,23 +64,5 @@ namespace Kyoo.Controllers
// TODO handle ExternalID deletion when they refer to this providerID.
await _database.SaveChangesAsync();
}
public async Task DeleteRange(IEnumerable<ProviderID> objs)
{
foreach (ProviderID obj in objs)
await Delete(obj);
}
public async Task DeleteRange(IEnumerable<int> ids)
{
foreach (int id in ids)
await Delete(id);
}
public async Task DeleteRange(IEnumerable<string> slugs)
{
foreach (string slug in slugs)
await Delete(slug);
}
}
}

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
@ -9,44 +10,44 @@ using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers
{
public class SeasonRepository : ISeasonRepository
public class SeasonRepository : LocalRepository<Season>, ISeasonRepository
{
private readonly DatabaseContext _database;
private readonly IProviderRepository _providers;
private readonly IEpisodeRepository _episodes;
protected override Expression<Func<Season, object>> DefaultSort => x => x.SeasonNumber;
public SeasonRepository(DatabaseContext database, IProviderRepository providers, IEpisodeRepository episodes)
: base(database)
{
_database = database;
_providers = providers;
_episodes = episodes;
}
public void Dispose()
public override void Dispose()
{
_database.Dispose();
_providers.Dispose();
_episodes.Dispose();
}
public ValueTask DisposeAsync()
public override async ValueTask DisposeAsync()
{
return _database.DisposeAsync();
}
public Task<Season> Get(int id)
{
return _database.Seasons.FirstOrDefaultAsync(x => x.ID == id);
await _database.DisposeAsync();
await _providers.DisposeAsync();
await _episodes.DisposeAsync();
}
public Task<Season> Get(string slug)
public override Task<Season> Get(string slug)
{
int index = slug.IndexOf("-s", StringComparison.Ordinal);
if (index == -1)
throw new InvalidOperationException("Invalid season slug. Format: {showSlug}-s{seasonNumber}");
string showSlug = slug.Substring(0, index);
if (!int.TryParse(slug.Substring(index + 2), out int seasonNumber))
throw new InvalidOperationException("Invalid season slug. Format: {showSlug}-s{seasonNumber}");
return Get(showSlug, seasonNumber);
Match match = Regex.Match(slug, @"(?<show>.*)-s(?<season>\d*)");
if (!match.Success)
throw new ArgumentException("Invalid season slug. Format: {showSlug}-s{seasonNumber}");
return Get(match.Groups["show"].Value, int.Parse(match.Groups["season"].Value));
}
public Task<Season> Get(string showSlug, int seasonNumber)
@ -55,22 +56,15 @@ namespace Kyoo.Controllers
&& x.SeasonNumber == seasonNumber);
}
public async Task<ICollection<Season>> Search(string query)
public override async Task<ICollection<Season>> Search(string query)
{
return await _database.Seasons
.Where(x => EF.Functions.Like(x.Title, $"%{query}%"))
.Where(x => EF.Functions.ILike(x.Title, $"%{query}%"))
.Take(20)
.ToListAsync();
}
public async Task<ICollection<Season>> GetAll(Expression<Func<Season, bool>> where = null,
Sort<Season> sort = default,
Pagination limit = default)
{
return await _database.Seasons.ToListAsync();
}
public async Task<Season> Create(Season obj)
public override async Task<Season> Create(Season obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
@ -88,55 +82,15 @@ namespace Kyoo.Controllers
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (Helper.IsDuplicateException(ex))
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated season (slug {obj.Slug} already exists).");
throw;
}
return obj;
}
public async Task<Season> CreateIfNotExists(Season obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
Season old = await Get(obj.Slug);
if (old != null)
return old;
try
{
return await Create(obj);
}
catch (DuplicatedItemException)
{
old = await Get(obj.Slug);
if (old == null)
throw new SystemException("Unknown database state.");
return old;
}
}
public async Task<Season> Edit(Season edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
Season old = await Get(edited.Slug);
if (old == null)
throw new ItemNotFound($"No season found with the slug {edited.Slug}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await Validate(old);
await _database.SaveChangesAsync();
return old;
}
private async Task Validate(Season obj)
protected override async Task Validate(Season obj)
{
if (obj.ShowID <= 0)
throw new InvalidOperationException($"Can't store a season not related to any show (showID: {obj.ShowID}).");
@ -158,25 +112,13 @@ namespace Kyoo.Controllers
return await _database.Seasons.Where(x => x.Show.Slug == showSlug).ToListAsync();
}
public async Task Delete(int id)
{
Season obj = await Get(id);
await Delete(obj);
}
public async Task Delete(string slug)
{
Season obj = await Get(slug);
await Delete(obj);
}
public async Task Delete(string showSlug, int seasonNumber)
{
Season obj = await Get(showSlug, seasonNumber);
await Delete(obj);
}
public async Task Delete(Season obj)
public override async Task Delete(Season obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
@ -192,23 +134,5 @@ namespace Kyoo.Controllers
if (obj.Episodes != null)
await _episodes.DeleteRange(obj.Episodes);
}
public async Task DeleteRange(IEnumerable<Season> objs)
{
foreach (Season obj in objs)
await Delete(obj);
}
public async Task DeleteRange(IEnumerable<int> ids)
{
foreach (int id in ids)
await Delete(id);
}
public async Task DeleteRange(IEnumerable<string> slugs)
{
foreach (string slug in slugs)
await Delete(slug);
}
}
}

View File

@ -3,14 +3,13 @@ using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Kyoo.CommonApi;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers
{
public class ShowRepository : IShowRepository
public class ShowRepository : LocalRepository<Show>, IShowRepository
{
private readonly DatabaseContext _database;
private readonly IStudioRepository _studios;
@ -19,6 +18,7 @@ namespace Kyoo.Controllers
private readonly IProviderRepository _providers;
private readonly ISeasonRepository _seasons;
private readonly IEpisodeRepository _episodes;
protected override Expression<Func<Show, object>> DefaultSort => x => x.Title;
public ShowRepository(DatabaseContext database,
IStudioRepository studios,
@ -27,6 +27,7 @@ namespace Kyoo.Controllers
IProviderRepository providers,
ISeasonRepository seasons,
IEpisodeRepository episodes)
: base(database)
{
_database = database;
_studios = studios;
@ -36,70 +37,39 @@ namespace Kyoo.Controllers
_seasons = seasons;
_episodes = episodes;
}
public void Dispose()
public override void Dispose()
{
_database.Dispose();
_studios.Dispose();
_people.Dispose();
_genres.Dispose();
_providers.Dispose();
_seasons.Dispose();
_episodes.Dispose();
}
public async ValueTask DisposeAsync()
public override async ValueTask DisposeAsync()
{
await Task.WhenAll(_database.DisposeAsync().AsTask(), _studios.DisposeAsync().AsTask());
}
public Task<Show> Get(int id)
{
return _database.Shows.FirstOrDefaultAsync(x => x.ID == id);
}
public Task<Show> Get(string slug)
{
return _database.Shows.FirstOrDefaultAsync(x => x.Slug == slug);
await _database.DisposeAsync();
await _studios.DisposeAsync();
await _people.DisposeAsync();
await _genres.DisposeAsync();
await _providers.DisposeAsync();
await _seasons.DisposeAsync();
await _episodes.DisposeAsync();
}
public Task<Show> GetByPath(string path)
{
return _database.Shows.FirstOrDefaultAsync(x => x.Path == path);
}
public async Task<ICollection<Show>> Search(string query)
public override async Task<ICollection<Show>> Search(string query)
{
return await _database.Shows
.FromSqlInterpolated($@"SELECT * FROM Shows WHERE 'Shows.Title' LIKE {$"%{query}%"}
OR 'Shows.Aliases' LIKE {$"%{query}%"}")
.Where(x => EF.Functions.ILike(x.Title, $"%{query}%")
/*|| EF.Functions.ILike(x.Aliases, $"%{query}%")*/)
.Take(20)
.ToListAsync();
}
public async Task<ICollection<Show>> GetAll(Expression<Func<Show, bool>> where = null,
Sort<Show> sort = default,
Pagination limit = default)
{
IQueryable<Show> query = _database.Shows;
if (where != null)
query = query.Where(where);
Expression<Func<Show, object>> sortKey = sort.Key ?? (x => x.Title);
query = sort.Descendant ? query.OrderByDescending(sortKey) : query.OrderBy(sortKey);
if (limit.AfterID != 0)
{
Show after = await Get(limit.AfterID);
object afterObj = sortKey.Compile()(after);
query = query.Where(Expression.Lambda<Func<Show, bool>>(
ApiHelper.StringCompatibleExpression(Expression.GreaterThan, sortKey.Body, Expression.Constant(afterObj)),
(ParameterExpression)((MemberExpression)sortKey.Body).Expression
));
}
if (limit.Count > 0)
query = query.Take(limit.Count);
return await query.ToListAsync();
}
public async Task<Show> Create(Show obj)
public override async Task<Show> Create(Show obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
@ -123,7 +93,7 @@ namespace Kyoo.Controllers
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (Helper.IsDuplicateException(ex))
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated show (slug {obj.Slug} already exists).");
throw;
}
@ -131,46 +101,7 @@ namespace Kyoo.Controllers
return obj;
}
public async Task<Show> CreateIfNotExists(Show obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
Show old = await Get(obj.Slug);
if (old != null)
return old;
try
{
return await Create(obj);
}
catch (DuplicatedItemException)
{
old = await Get(obj.Slug);
if (old == null)
throw new SystemException("Unknown database state.");
return old;
}
}
public async Task<Show> Edit(Show edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
Show old = await Get(edited.Slug);
if (old == null)
throw new ItemNotFound($"No show found with the slug {edited.Slug}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await Validate(old);
await _database.SaveChangesAsync();
return old;
}
private async Task Validate(Show obj)
protected override async Task Validate(Show obj)
{
if (obj.Studio != null)
obj.Studio = await _studios.CreateIfNotExists(obj.Studio);
@ -210,20 +141,8 @@ namespace Kyoo.Controllers
await _database.SaveChangesAsync();
}
public async Task Delete(int id)
{
Show obj = await Get(id);
await Delete(obj);
}
public async Task Delete(string slug)
{
Show obj = await Get(slug);
await Delete(obj);
}
public async Task Delete(Show obj)
public override async Task Delete(Show obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
@ -258,23 +177,5 @@ namespace Kyoo.Controllers
if (obj.Episodes != null)
await _episodes.DeleteRange(obj.Episodes);
}
public async Task DeleteRange(IEnumerable<Show> objs)
{
foreach (Show obj in objs)
await Delete(obj);
}
public async Task DeleteRange(IEnumerable<int> ids)
{
foreach (int id in ids)
await Delete(id);
}
public async Task DeleteRange(IEnumerable<string> slugs)
{
foreach (string slug in slugs)
await Delete(slug);
}
}
}

View File

@ -9,52 +9,26 @@ using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers
{
public class StudioRepository : IStudioRepository
public class StudioRepository : LocalRepository<Studio>, IStudioRepository
{
private readonly DatabaseContext _database;
protected override Expression<Func<Studio, object>> DefaultSort => x => x.Name;
public StudioRepository(DatabaseContext database)
public StudioRepository(DatabaseContext database) : base(database)
{
_database = database;
}
public void Dispose()
{
_database.Dispose();
}
public ValueTask DisposeAsync()
{
return _database.DisposeAsync();
}
public async Task<Studio> Get(int id)
{
return await _database.Studios.FirstOrDefaultAsync(x => x.ID == id);
}
public async Task<Studio> Get(string slug)
{
return await _database.Studios.FirstOrDefaultAsync(x => x.Slug == slug);
}
public async Task<ICollection<Studio>> Search(string query)
public override async Task<ICollection<Studio>> Search(string query)
{
return await _database.Studios
.Where(x => EF.Functions.Like(x.Name, $"%{query}%"))
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
.Take(20)
.ToListAsync();
}
public async Task<ICollection<Studio>> GetAll(Expression<Func<Studio, bool>> where = null,
Sort<Studio> sort = default,
Pagination limit = default)
{
return await _database.Studios.ToListAsync();
}
public async Task<Studio> Create(Studio obj)
public override async Task<Studio> Create(Studio obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
@ -68,64 +42,19 @@ namespace Kyoo.Controllers
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (Helper.IsDuplicateException(ex))
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated studio (slug {obj.Slug} already exists).");
throw;
}
return obj;
}
public async Task<Studio> CreateIfNotExists(Studio obj)
protected override Task Validate(Studio ressource)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
Studio old = await Get(obj.Slug);
if (old != null)
return old;
try
{
return await Create(obj);
}
catch (DuplicatedItemException)
{
old = await Get(obj.Slug);
if (old == null)
throw new SystemException("Unknown database state.");
return old;
}
}
public async Task<Studio> Edit(Studio edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
Studio old = await Get(edited.Name);
if (old == null)
throw new ItemNotFound($"No studio found with the name {edited.Name}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await _database.SaveChangesAsync();
return old;
}
public async Task Delete(int id)
{
Studio obj = await Get(id);
await Delete(obj);
}
public async Task Delete(string slug)
{
Studio obj = await Get(slug);
await Delete(obj);
return Task.CompletedTask;
}
public async Task Delete(Studio obj)
public override async Task Delete(Studio obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
@ -137,23 +66,5 @@ namespace Kyoo.Controllers
show.StudioID = null;
await _database.SaveChangesAsync();
}
public async Task DeleteRange(IEnumerable<Studio> objs)
{
foreach (Studio obj in objs)
await Delete(obj);
}
public async Task DeleteRange(IEnumerable<int> ids)
{
foreach (int id in ids)
await Delete(id);
}
public async Task DeleteRange(IEnumerable<string> slugs)
{
foreach (string slug in slugs)
await Delete(slug);
}
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
@ -8,34 +9,39 @@ using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers
{
public class TrackRepository : ITrackRepository
public class TrackRepository : LocalRepository<Track>, ITrackRepository
{
private readonly DatabaseContext _database;
protected override Expression<Func<Track, object>> DefaultSort => x => x.ID;
public TrackRepository(DatabaseContext database)
public TrackRepository(DatabaseContext database) : base(database)
{
_database = database;
}
public void Dispose()
{
_database.Dispose();
}
public ValueTask DisposeAsync()
public override Task<Track> Get(string slug)
{
return _database.DisposeAsync();
}
public async Task<Track> Get(int id)
{
return await _database.Tracks.FirstOrDefaultAsync(x => x.ID == id);
}
public Task<Track> Get(string slug)
{
throw new InvalidOperationException("Tracks do not support the get by slug method.");
Match match = Regex.Match(slug,
@"(?<show>.*)-s(?<season>\d*)-e(?<episode>\d*).(?<language>.{0,3})(?<forced>-forced)?(\..*)?");
if (!match.Success)
{
if (int.TryParse(slug, out int id))
return Get(id);
throw new ArgumentException("Invalid track slug. Format: {episodeSlug}.{language}[-forced][.{extension}]");
}
string showSlug = match.Groups["show"].Value;
int seasonNumber = int.Parse(match.Groups["season"].Value);
int episodeNumber = int.Parse(match.Groups["episode"].Value);
string language = match.Groups["language"].Value;
bool forced = match.Groups["forced"].Success;
return _database.Tracks.FirstOrDefaultAsync(x => x.Episode.Show.Slug == showSlug
&& x.Episode.SeasonNumber == seasonNumber
&& x.Episode.EpisodeNumber == episodeNumber
&& x.Language == language
&& x.IsForced == forced);
}
public Task<Track> Get(int episodeID, string languageTag, bool isForced)
@ -45,19 +51,12 @@ namespace Kyoo.Controllers
&& x.IsForced == isForced);
}
public Task<ICollection<Track>> Search(string query)
public override Task<ICollection<Track>> Search(string query)
{
throw new InvalidOperationException("Tracks do not support the search method.");
}
public async Task<ICollection<Track>> GetAll(Expression<Func<Track, bool>> where = null,
Sort<Track> sort = default,
Pagination limit = default)
{
return await _database.Tracks.ToListAsync();
}
public async Task<Track> Create(Track obj)
public override async Task<Track> Create(Track obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
@ -74,48 +73,19 @@ namespace Kyoo.Controllers
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (Helper.IsDuplicateException(ex))
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated track (slug {obj.Slug} already exists).");
throw;
}
return obj;
}
public Task<Track> CreateIfNotExists(Track obj)
protected override Task Validate(Track ressource)
{
return Create(obj);
}
public async Task<Track> Edit(Track edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
Track old = await Get(edited.ID);
if (old == null)
throw new ItemNotFound($"No track found with the ID {edited.ID}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await _database.SaveChangesAsync();
return old;
}
public async Task Delete(int id)
{
Track obj = await Get(id);
await Delete(obj);
}
public async Task Delete(string slug)
{
Track obj = await Get(slug);
await Delete(obj);
return Task.CompletedTask;
}
public async Task Delete(Track obj)
public override async Task Delete(Track obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
@ -123,23 +93,5 @@ namespace Kyoo.Controllers
_database.Entry(obj).State = EntityState.Deleted;
await _database.SaveChangesAsync();
}
public async Task DeleteRange(IEnumerable<Track> objs)
{
foreach (Track obj in objs)
await Delete(obj);
}
public async Task DeleteRange(IEnumerable<int> ids)
{
foreach (int id in ids)
await Delete(id);
}
public async Task DeleteRange(IEnumerable<string> slugs)
{
foreach (string slug in slugs)
await Delete(slug);
}
}
}