diff --git a/Kyoo.Common/Models/Resources/Episode.cs b/Kyoo.Common/Models/Resources/Episode.cs
index 3a8e3b60..692e951c 100644
--- a/Kyoo.Common/Models/Resources/Episode.cs
+++ b/Kyoo.Common/Models/Resources/Episode.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Text.RegularExpressions;
using JetBrains.Annotations;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
@@ -14,9 +15,36 @@ namespace Kyoo.Models
{
///
public int ID { get; set; }
-
+
///
- public string Slug => GetSlug(ShowSlug, SeasonNumber, EpisodeNumber, AbsoluteNumber);
+ public string Slug
+ {
+ get => GetSlug(ShowSlug, SeasonNumber, EpisodeNumber, AbsoluteNumber);
+ set
+ {
+ Match match = Regex.Match(value, @"(?.*)-s(?\d*)e(?\d*)");
+
+ if (match.Success)
+ {
+ ShowSlug = match.Groups["show"].Value;
+ SeasonNumber = int.Parse(match.Groups["season"].Value);
+ EpisodeNumber = int.Parse(match.Groups["episode"].Value);
+ }
+ else
+ {
+ match = Regex.Match(value, @"(?.*)-(?\d*)");
+ if (match.Success)
+ {
+ ShowSlug = match.Groups["Show"].Value;
+ AbsoluteNumber = int.Parse(match.Groups["absolute"].Value);
+ }
+ else
+ ShowSlug = value;
+ SeasonNumber = -1;
+ EpisodeNumber = -1;
+ }
+ }
+ }
///
/// The slug of the Show that contain this episode. If this is not set, this episode is ill-formed.
diff --git a/Kyoo.Common/Models/Resources/Season.cs b/Kyoo.Common/Models/Resources/Season.cs
index 3c8f21a9..35bb6ffe 100644
--- a/Kyoo.Common/Models/Resources/Season.cs
+++ b/Kyoo.Common/Models/Resources/Season.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Text.RegularExpressions;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
@@ -12,9 +13,21 @@ namespace Kyoo.Models
{
///
public int ID { get; set; }
-
+
///
- public string Slug => $"{ShowSlug}-s{SeasonNumber}";
+ public string Slug
+ {
+ get => $"{ShowSlug}-s{SeasonNumber}";
+ set
+ {
+ Match match = Regex.Match(value, @"(?.*)-s(?\d*)");
+
+ if (!match.Success)
+ throw new ArgumentException("Invalid season slug. Format: {showSlug}-s{seasonNumber}");
+ ShowSlug = match.Groups["show"].Value;
+ SeasonNumber = int.Parse(match.Groups["season"].Value);
+ }
+ }
///
/// The slug of the Show that contain this episode. If this is not set, this season is ill-formed.
diff --git a/Kyoo.Common/Models/Resources/Track.cs b/Kyoo.Common/Models/Resources/Track.cs
index 290ecd3f..17736e07 100644
--- a/Kyoo.Common/Models/Resources/Track.cs
+++ b/Kyoo.Common/Models/Resources/Track.cs
@@ -1,5 +1,7 @@
-using System.Globalization;
+using System;
+using System.Globalization;
using System.Linq;
+using System.Text.RegularExpressions;
using Kyoo.Models.Attributes;
namespace Kyoo.Models
@@ -44,10 +46,36 @@ namespace Kyoo.Models
"subrip" => ".srt",
{} x => $".{x}"
};
- return $"{Episode.Slug}.{type}{Language}{index}{(IsForced ? "-forced" : "")}{codec}";
+ return $"{EpisodeSlug}.{type}{Language}{index}{(IsForced ? "-forced" : "")}{codec}";
+ }
+ set
+ {
+ Match match = Regex.Match(value, @"(?.*)-s(?\d+)e(?\d+)"
+ + @"(\.(?\w*))?\.(?.{0,3})(?-forced)?(\..\w)?");
+
+ if (!match.Success)
+ {
+ match = Regex.Match(value, @"(?.*)\.(?.{0,3})(?-forced)?(\..\w)?");
+ if (!match.Success)
+ throw new ArgumentException("Invalid track slug. " +
+ "Format: {episodeSlug}.{language}[-forced][.{extension}]");
+ }
+
+ EpisodeSlug = Episode.GetSlug(match.Groups["show"].Value,
+ match.Groups["season"].Success ? int.Parse(match.Groups["season"].Value) : -1,
+ match.Groups["episode"].Success ? int.Parse(match.Groups["episode"].Value) : -1);
+ Language = match.Groups["language"].Value;
+ IsForced = match.Groups["forced"].Success;
+ if (match.Groups["type"].Success)
+ Type = Enum.Parse(match.Groups["type"].Value, true);
}
}
+ ///
+ /// The slug of the episode that contain this track. If this is not set, this track is ill-formed.
+ ///
+ [SerializeIgnore] public string EpisodeSlug { private get; set; }
+
///
/// The title of the stream.
///
diff --git a/Kyoo.CommonAPI/DatabaseContext.cs b/Kyoo.CommonAPI/DatabaseContext.cs
index b106a2fb..f3d7f867 100644
--- a/Kyoo.CommonAPI/DatabaseContext.cs
+++ b/Kyoo.CommonAPI/DatabaseContext.cs
@@ -260,7 +260,7 @@ namespace Kyoo
modelBuilder.Entity().Property(x => x.Slug).IsRequired();
modelBuilder.Entity().Property(x => x.Slug).IsRequired();
modelBuilder.Entity().Property(x => x.Slug).IsRequired();
-
+
modelBuilder.Entity()
.HasIndex(x => x.Slug)
.IsUnique();
diff --git a/Kyoo.CommonAPI/Kyoo.CommonAPI.csproj b/Kyoo.CommonAPI/Kyoo.CommonAPI.csproj
index 3181be71..f6add6b4 100644
--- a/Kyoo.CommonAPI/Kyoo.CommonAPI.csproj
+++ b/Kyoo.CommonAPI/Kyoo.CommonAPI.csproj
@@ -12,8 +12,8 @@
-
-
+
+
diff --git a/Kyoo.Postgresql/Kyoo.Postgresql.csproj b/Kyoo.Postgresql/Kyoo.Postgresql.csproj
index 05b767ab..1add3667 100644
--- a/Kyoo.Postgresql/Kyoo.Postgresql.csproj
+++ b/Kyoo.Postgresql/Kyoo.Postgresql.csproj
@@ -18,7 +18,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Kyoo.SqLite/Kyoo.SqLite.csproj b/Kyoo.SqLite/Kyoo.SqLite.csproj
index 0d5ab21c..ba87fa11 100644
--- a/Kyoo.SqLite/Kyoo.SqLite.csproj
+++ b/Kyoo.SqLite/Kyoo.SqLite.csproj
@@ -19,11 +19,11 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/Kyoo.SqLite/SqLiteContext.cs b/Kyoo.SqLite/SqLiteContext.cs
index 1a2a392a..81b1626e 100644
--- a/Kyoo.SqLite/SqLiteContext.cs
+++ b/Kyoo.SqLite/SqLiteContext.cs
@@ -82,10 +82,6 @@ namespace Kyoo.SqLite
/// The database's model builder.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
- // modelBuilder.HasPostgresEnum();
- // modelBuilder.HasPostgresEnum();
- // modelBuilder.HasPostgresEnum();
-
ValueConverter arrayConvertor = new(
x => string.Join(";", x),
x => x.Split(';', StringSplitOptions.None));
@@ -112,7 +108,6 @@ namespace Kyoo.SqLite
modelBuilder.Entity()
.Property(x => x.ExtraData)
.HasConversion(jsonConvertor);
-
base.OnModelCreating(modelBuilder);
}
@@ -127,7 +122,7 @@ namespace Kyoo.SqLite
public override Expression> Like(Expression> query, string format)
{
MethodInfo iLike = MethodOfUtils.MethodOf(EF.Functions.Like);
- MethodCallExpression call = Expression.Call(iLike, query.Body, Expression.Constant(format));
+ MethodCallExpression call = Expression.Call(iLike, Expression.Constant(EF.Functions), query.Body, Expression.Constant(format));
return Expression.Lambda>(call, query.Parameters);
}
diff --git a/Kyoo.Tests/Library/RepositoryActivator.cs b/Kyoo.Tests/Library/RepositoryActivator.cs
index 7352a781..df0be918 100644
--- a/Kyoo.Tests/Library/RepositoryActivator.cs
+++ b/Kyoo.Tests/Library/RepositoryActivator.cs
@@ -25,13 +25,13 @@ namespace Kyoo.Tests
PeopleRepository people = new(_database, provider,
new Lazy(() => LibraryManager.ShowRepository));
ShowRepository show = new(_database, studio, people, genre, provider);
- SeasonRepository season = new(_database, provider, show);
+ SeasonRepository season = new(_database, provider);
LibraryItemRepository libraryItem = new(_database,
new Lazy(() => LibraryManager.LibraryRepository),
new Lazy(() => LibraryManager.ShowRepository),
new Lazy(() => LibraryManager.CollectionRepository));
TrackRepository track = new(_database);
- EpisodeRepository episode = new(_database, provider, show, track);
+ EpisodeRepository episode = new(_database, provider, track);
LibraryManager = new LibraryManager(new IBaseRepository[] {
provider,
diff --git a/Kyoo.Tests/Library/SpecificTests/SeasonTests.cs b/Kyoo.Tests/Library/SpecificTests/SeasonTests.cs
new file mode 100644
index 00000000..a61436e9
--- /dev/null
+++ b/Kyoo.Tests/Library/SpecificTests/SeasonTests.cs
@@ -0,0 +1,15 @@
+using Kyoo.Controllers;
+using Kyoo.Models;
+
+namespace Kyoo.Tests.SpecificTests
+{
+ public class SeasonTests : RepositoryTests
+ {
+ private readonly ISeasonRepository _repository;
+
+ public SeasonTests()
+ {
+ _repository = Repositories.LibraryManager.SeasonRepository;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Kyoo.Tests/Library/SpecificTests/ShowTests.cs b/Kyoo.Tests/Library/SpecificTests/ShowTests.cs
index 183780aa..9c0b5138 100644
--- a/Kyoo.Tests/Library/SpecificTests/ShowTests.cs
+++ b/Kyoo.Tests/Library/SpecificTests/ShowTests.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Controllers;
@@ -233,5 +234,23 @@ namespace Kyoo.Tests.SpecificTests
Show reference = TestSample.Get();
Assert.Equal(reference.Slug, await _repository.GetSlug(reference.ID));
}
+
+ [Theory]
+ [InlineData("test")]
+ [InlineData("super")]
+ [InlineData("title")]
+ [InlineData("TiTlE")]
+ [InlineData("SuPeR")]
+ public async Task SearchTest(string query)
+ {
+ Show value = new()
+ {
+ Slug = "super-test",
+ Title = "This is a test title²"
+ };
+ await _repository.Create(value);
+ ICollection ret = await _repository.Search(query);
+ KAssert.DeepEqual(value, ret.First());
+ }
}
}
\ No newline at end of file
diff --git a/Kyoo.Tests/Library/TestSample.cs b/Kyoo.Tests/Library/TestSample.cs
index 7abf1ed1..6f122c89 100644
--- a/Kyoo.Tests/Library/TestSample.cs
+++ b/Kyoo.Tests/Library/TestSample.cs
@@ -45,6 +45,7 @@ namespace Kyoo.Tests
SeasonNumber = 1,
Title = "Season 1",
Overview = "The first season",
+ Show = Get(),
StartDate = new DateTime(2020, 06, 05),
EndDate = new DateTime(2020, 07, 05),
Poster = "poster"
diff --git a/Kyoo/Controllers/Repositories/EpisodeRepository.cs b/Kyoo/Controllers/Repositories/EpisodeRepository.cs
index e6765c13..5a01bd44 100644
--- a/Kyoo/Controllers/Repositories/EpisodeRepository.cs
+++ b/Kyoo/Controllers/Repositories/EpisodeRepository.cs
@@ -2,7 +2,6 @@ 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;
@@ -16,7 +15,7 @@ namespace Kyoo.Controllers
public class EpisodeRepository : LocalRepository, IEpisodeRepository
{
///
- /// The databse handle
+ /// The database handle
///
private readonly DatabaseContext _database;
///
@@ -24,10 +23,6 @@ namespace Kyoo.Controllers
///
private readonly IProviderRepository _providers;
///
- /// A show repository to get show's slug from their ID and keep the slug in each episode.
- ///
- private readonly IShowRepository _shows;
- ///
/// A track repository to handle creation and deletion of tracks related to the current episode.
///
private readonly ITrackRepository _tracks;
@@ -41,66 +36,31 @@ namespace Kyoo.Controllers
///
/// The database handle to use.
/// A provider repository
- /// A show repository
/// A track repository
public EpisodeRepository(DatabaseContext database,
IProviderRepository providers,
- IShowRepository shows,
ITrackRepository tracks)
: base(database)
{
_database = database;
_providers = providers;
- _shows = shows;
_tracks = tracks;
}
-
///
- public override async Task GetOrDefault(int id)
+ public Task GetOrDefault(int showID, int seasonNumber, int episodeNumber)
{
- Episode ret = await base.GetOrDefault(id);
- if (ret != null)
- ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
- return ret;
+ return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
+ && x.SeasonNumber == seasonNumber
+ && x.EpisodeNumber == episodeNumber);
}
///
- public override async Task GetOrDefault(string slug)
+ public Task GetOrDefault(string showSlug, int seasonNumber, int episodeNumber)
{
- Match match = Regex.Match(slug, @"(?.*)-s(?\d*)e(?\d*)");
-
- if (match.Success)
- {
- return await GetOrDefault(match.Groups["show"].Value,
- int.Parse(match.Groups["season"].Value),
- int.Parse(match.Groups["episode"].Value));
- }
-
- Episode episode = await _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == slug);
- if (episode != null)
- episode.ShowSlug = slug;
- return episode;
- }
-
- ///
- public override async Task GetOrDefault(Expression> where)
- {
- Episode ret = await base.GetOrDefault(where);
- if (ret != null)
- ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
- return ret;
- }
-
- ///
- public async Task GetOrDefault(string showSlug, int seasonNumber, int episodeNumber)
- {
- Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
- && x.SeasonNumber == seasonNumber
- && x.EpisodeNumber == episodeNumber);
- if (ret != null)
- ret.ShowSlug = showSlug;
- return ret;
+ return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
+ && x.SeasonNumber == seasonNumber
+ && x.EpisodeNumber == episodeNumber);
}
///
@@ -122,61 +82,30 @@ namespace Kyoo.Controllers
}
///
- public async Task GetOrDefault(int showID, int seasonNumber, int episodeNumber)
+ public Task GetAbsolute(int showID, int absoluteNumber)
{
- Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
- && x.SeasonNumber == seasonNumber
- && x.EpisodeNumber == episodeNumber);
- if (ret != null)
- ret.ShowSlug = await _shows.GetSlug(showID);
- return ret;
+ return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
+ && x.AbsoluteNumber == absoluteNumber);
}
///
- public async Task GetAbsolute(int showID, int absoluteNumber)
+ public Task GetAbsolute(string showSlug, int absoluteNumber)
{
- Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
- && x.AbsoluteNumber == absoluteNumber);
- if (ret != null)
- ret.ShowSlug = await _shows.GetSlug(showID);
- return ret;
- }
-
- ///
- public async Task GetAbsolute(string showSlug, int absoluteNumber)
- {
- Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
- && x.AbsoluteNumber == absoluteNumber);
- if (ret != null)
- ret.ShowSlug = showSlug;
- return ret;
+ return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
+ && x.AbsoluteNumber == absoluteNumber);
}
///
public override async Task> Search(string query)
{
- List episodes = await _database.Episodes
+ return await _database.Episodes
.Where(x => x.EpisodeNumber != -1)
.Where(_database.Like(x => x.Title, $"%{query}%"))
.OrderBy(DefaultSort)
.Take(20)
.ToListAsync();
- foreach (Episode episode in episodes)
- episode.ShowSlug = await _shows.GetSlug(episode.ShowID);
- return episodes;
}
- ///
- public override async Task> GetAll(Expression> where = null,
- Sort sort = default,
- Pagination limit = default)
- {
- ICollection episodes = await base.GetAll(where, sort, limit);
- foreach (Episode episode in episodes)
- episode.ShowSlug = await _shows.GetSlug(episode.ShowID);
- return episodes;
- }
-
///
public override async Task Create(Episode obj)
{
diff --git a/Kyoo/Controllers/Repositories/SeasonRepository.cs b/Kyoo/Controllers/Repositories/SeasonRepository.cs
index ad3d5064..d4529606 100644
--- a/Kyoo/Controllers/Repositories/SeasonRepository.cs
+++ b/Kyoo/Controllers/Repositories/SeasonRepository.cs
@@ -2,7 +2,6 @@ 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;
@@ -23,66 +22,30 @@ namespace Kyoo.Controllers
/// A provider repository to handle externalID creation and deletion
///
private readonly IProviderRepository _providers;
- ///
- /// A show repository to get show's slug from their ID and keep the slug in each episode.
- ///
- private readonly IShowRepository _shows;
///
protected override Expression> DefaultSort => x => x.SeasonNumber;
///
- /// Create a new using the provided handle, a provider & a show repository and
- /// a service provider to lazilly request an episode repository.
+ /// Create a new .
///
/// The database handle that will be used
/// A provider repository
- /// A show repository
public SeasonRepository(DatabaseContext database,
- IProviderRepository providers,
- IShowRepository shows)
+ IProviderRepository providers)
: base(database)
{
_database = database;
_providers = providers;
- _shows = shows;
- }
-
-
- ///
- public override async Task Get(int id)
- {
- Season ret = await base.Get(id);
- ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
- return ret;
}
- ///
- public override async Task Get(Expression> where)
- {
- Season ret = await base.Get(where);
- ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
- return ret;
- }
-
- ///
- public override Task Get(string slug)
- {
- Match match = Regex.Match(slug, @"(?.*)-s(?\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 async Task Get(int showID, int seasonNumber)
{
Season ret = await GetOrDefault(showID, seasonNumber);
if (ret == null)
throw new ItemNotFoundException($"No season {seasonNumber} found for the show {showID}");
- ret.ShowSlug = await _shows.GetSlug(showID);
return ret;
}
@@ -92,7 +55,6 @@ namespace Kyoo.Controllers
Season ret = await GetOrDefault(showSlug, seasonNumber);
if (ret == null)
throw new ItemNotFoundException($"No season {seasonNumber} found for the show {showSlug}");
- ret.ShowSlug = showSlug;
return ret;
}
@@ -113,27 +75,13 @@ namespace Kyoo.Controllers
///
public override async Task> Search(string query)
{
- List seasons = await _database.Seasons
+ return await _database.Seasons
.Where(_database.Like(x => x.Title, $"%{query}%"))
.OrderBy(DefaultSort)
.Take(20)
.ToListAsync();
- foreach (Season season in seasons)
- season.ShowSlug = await _shows.GetSlug(season.ShowID);
- return seasons;
}
-
- ///
- public override async Task> GetAll(Expression> where = null,
- Sort sort = default,
- Pagination limit = default)
- {
- ICollection seasons = await base.GetAll(where, sort, limit);
- foreach (Season season in seasons)
- season.ShowSlug = await _shows.GetSlug(season.ShowID);
- return seasons;
- }
-
+
///
public override async Task Create(Season obj)
{
diff --git a/Kyoo/Controllers/Repositories/TrackRepository.cs b/Kyoo/Controllers/Repositories/TrackRepository.cs
index 4b27a5e4..b33f48a4 100644
--- a/Kyoo/Controllers/Repositories/TrackRepository.cs
+++ b/Kyoo/Controllers/Repositories/TrackRepository.cs
@@ -16,7 +16,7 @@ namespace Kyoo.Controllers
public class TrackRepository : LocalRepository