diff --git a/back/.config/dotnet-tools.json b/back/.config/dotnet-tools.json index 7076f7c1..4a8f05ad 100644 --- a/back/.config/dotnet-tools.json +++ b/back/.config/dotnet-tools.json @@ -9,7 +9,7 @@ ] }, "csharpier": { - "version": "0.26.4", + "version": "0.27.2", "commands": [ "dotnet-csharpier" ] diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs index da363959..771faaf7 100644 --- a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs +++ b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs @@ -181,37 +181,35 @@ namespace Kyoo.Abstractions.Models [LoadableRelation( // language=PostgreSQL Sql = """ - select - pe.* -- Episode as pe - from - episodes as "pe" - where - pe.show_id = "this".show_id - and (pe.absolute_number < "this".absolute_number - or pe.season_number < "this".season_number - or (pe.season_number = "this".season_number - and e.episode_number < "this".episode_number)) - order by - pe.absolute_number desc nulls last, - pe.season_number desc, - pe.episode_number desc - limit 1 - """ + select + pe.* -- Episode as pe + from + episodes as "pe" + where + pe.show_id = "this".show_id + and (pe.absolute_number < "this".absolute_number + or pe.season_number < "this".season_number + or (pe.season_number = "this".season_number + and e.episode_number < "this".episode_number)) + order by + pe.absolute_number desc nulls last, + pe.season_number desc, + pe.episode_number desc + limit 1 + """ )] public Episode? PreviousEpisode { get; set; } private Episode? _PreviousEpisode => Show! - .Episodes! - .OrderBy(x => x.AbsoluteNumber == null) + .Episodes!.OrderBy(x => x.AbsoluteNumber == null) .ThenByDescending(x => x.AbsoluteNumber) .ThenByDescending(x => x.SeasonNumber) .ThenByDescending(x => x.EpisodeNumber) - .FirstOrDefault( - x => - x.AbsoluteNumber < AbsoluteNumber - || x.SeasonNumber < SeasonNumber - || (x.SeasonNumber == SeasonNumber && x.EpisodeNumber < EpisodeNumber) + .FirstOrDefault(x => + x.AbsoluteNumber < AbsoluteNumber + || x.SeasonNumber < SeasonNumber + || (x.SeasonNumber == SeasonNumber && x.EpisodeNumber < EpisodeNumber) ); /// @@ -221,36 +219,34 @@ namespace Kyoo.Abstractions.Models [LoadableRelation( // language=PostgreSQL Sql = """ - select - ne.* -- Episode as ne - from - episodes as "ne" - where - ne.show_id = "this".show_id - and (ne.absolute_number > "this".absolute_number - or ne.season_number > "this".season_number - or (ne.season_number = "this".season_number - and e.episode_number > "this".episode_number)) - order by - ne.absolute_number, - ne.season_number, - ne.episode_number - limit 1 - """ + select + ne.* -- Episode as ne + from + episodes as "ne" + where + ne.show_id = "this".show_id + and (ne.absolute_number > "this".absolute_number + or ne.season_number > "this".season_number + or (ne.season_number = "this".season_number + and e.episode_number > "this".episode_number)) + order by + ne.absolute_number, + ne.season_number, + ne.episode_number + limit 1 + """ )] public Episode? NextEpisode { get; set; } private Episode? _NextEpisode => Show! - .Episodes! - .OrderBy(x => x.AbsoluteNumber) + .Episodes!.OrderBy(x => x.AbsoluteNumber) .ThenBy(x => x.SeasonNumber) .ThenBy(x => x.EpisodeNumber) - .FirstOrDefault( - x => - x.AbsoluteNumber > AbsoluteNumber - || x.SeasonNumber > SeasonNumber - || (x.SeasonNumber == SeasonNumber && x.EpisodeNumber > EpisodeNumber) + .FirstOrDefault(x => + x.AbsoluteNumber > AbsoluteNumber + || x.SeasonNumber > SeasonNumber + || (x.SeasonNumber == SeasonNumber && x.EpisodeNumber > EpisodeNumber) ); [SerializeIgnore] diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Season.cs b/back/src/Kyoo.Abstractions/Models/Resources/Season.cs index c7d4c459..df0ae10d 100644 --- a/back/src/Kyoo.Abstractions/Models/Resources/Season.cs +++ b/back/src/Kyoo.Abstractions/Models/Resources/Season.cs @@ -135,14 +135,14 @@ namespace Kyoo.Abstractions.Models [LoadableRelation( // language=PostgreSQL Projected = """ - ( - select - count(*)::int - from - episodes as e - where - e.season_id = id) as episodes_count - """ + ( + select + count(*)::int + from + episodes as e + where + e.season_id = id) as episodes_count + """ )] public int EpisodesCount { get; set; } diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Show.cs b/back/src/Kyoo.Abstractions/Models/Resources/Show.cs index 31a82c20..6da2fe34 100644 --- a/back/src/Kyoo.Abstractions/Models/Resources/Show.cs +++ b/back/src/Kyoo.Abstractions/Models/Resources/Show.cs @@ -170,17 +170,17 @@ namespace Kyoo.Abstractions.Models [LoadableRelation( // language=PostgreSQL Sql = """ - select - fe.* -- Episode as fe - from ( select - e.*, - row_number() over (partition by e.show_id order by e.absolute_number, e.season_number, e.episode_number) as number - from - episodes as e) as "fe" - where - fe.number <= 1 - """, + fe.* -- Episode as fe + from ( + select + e.*, + row_number() over (partition by e.show_id order by e.absolute_number, e.season_number, e.episode_number) as number + from + episodes as e) as "fe" + where + fe.number <= 1 + """, On = "show_id = \"this\".id" )] public Episode? FirstEpisode { get; set; } @@ -200,14 +200,14 @@ namespace Kyoo.Abstractions.Models [LoadableRelation( // language=PostgreSQL Projected = """ - ( - select - count(*)::int - from - episodes as e - where - e.show_id = "this".id) as episodes_count - """ + ( + select + count(*)::int + from + episodes as e + where + e.show_id = "this".id) as episodes_count + """ )] public int EpisodesCount { get; set; } diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Filter.cs b/back/src/Kyoo.Abstractions/Models/Utils/Filter.cs index 85af8072..2c937334 100644 --- a/back/src/Kyoo.Abstractions/Models/Utils/Filter.cs +++ b/back/src/Kyoo.Abstractions/Models/Utils/Filter.cs @@ -205,8 +205,7 @@ public abstract record Filter : Filter if (type.IsEnum) { return Parse - .LetterOrDigit - .Many() + .LetterOrDigit.Many() .Text() .Then(x => { @@ -259,14 +258,11 @@ public abstract record Filter : Filter } PropertyInfo? propInfo = types - .Select( - x => - x.GetProperty( - prop, - BindingFlags.IgnoreCase - | BindingFlags.Public - | BindingFlags.Instance - ) + .Select(x => + x.GetProperty( + prop, + BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance + ) ) .FirstOrDefault(); if (propInfo == null) diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Include.cs b/back/src/Kyoo.Abstractions/Models/Utils/Include.cs index 8801a869..78cf65c1 100644 --- a/back/src/Kyoo.Abstractions/Models/Utils/Include.cs +++ b/back/src/Kyoo.Abstractions/Models/Utils/Include.cs @@ -62,17 +62,14 @@ public class Include : Include .SelectMany(key => { var relations = types - .Select( - x => - x.GetProperty( - key, - BindingFlags.IgnoreCase - | BindingFlags.Public - | BindingFlags.Instance - )! + .Select(x => + x.GetProperty( + key, + BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance + )! ) - .Select( - prop => (prop, attr: prop?.GetCustomAttribute()!) + .Select(prop => + (prop, attr: prop?.GetCustomAttribute()!) ) .Where(x => x.prop != null && x.attr != null) .ToList(); diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Sort.cs b/back/src/Kyoo.Abstractions/Models/Utils/Sort.cs index 26b4a858..951c0f98 100644 --- a/back/src/Kyoo.Abstractions/Models/Utils/Sort.cs +++ b/back/src/Kyoo.Abstractions/Models/Utils/Sort.cs @@ -120,12 +120,11 @@ namespace Kyoo.Abstractions.Controllers Type[] types = typeof(T).GetCustomAttribute()?.Types ?? new[] { typeof(T) }; PropertyInfo? property = types - .Select( - x => - x.GetProperty( - key, - BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance - ) + .Select(x => + x.GetProperty( + key, + BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance + ) ) .FirstOrDefault(x => x != null); if (property == null) diff --git a/back/src/Kyoo.Abstractions/Utility/Merger.cs b/back/src/Kyoo.Abstractions/Utility/Merger.cs index 36ecab06..d440e759 100644 --- a/back/src/Kyoo.Abstractions/Utility/Merger.cs +++ b/back/src/Kyoo.Abstractions/Utility/Merger.cs @@ -60,8 +60,8 @@ namespace Kyoo.Utils hasChanged = false; if (second == null) return first; - hasChanged = second.Any( - x => !first.ContainsKey(x.Key) || x.Value?.Equals(first[x.Key]) == false + hasChanged = second.Any(x => + !first.ContainsKey(x.Key) || x.Value?.Equals(first[x.Key]) == false ); foreach ((T key, T2 value) in first) second.TryAdd(key, value); @@ -98,10 +98,9 @@ namespace Kyoo.Utils Type type = typeof(T); IEnumerable properties = type.GetProperties() - .Where( - x => - x is { CanRead: true, CanWrite: true } - && Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null + .Where(x => + x is { CanRead: true, CanWrite: true } + && Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null ); if (where != null) diff --git a/back/src/Kyoo.Abstractions/Utility/Utility.cs b/back/src/Kyoo.Abstractions/Utility/Utility.cs index 0e4fa9a4..86e7f42f 100644 --- a/back/src/Kyoo.Abstractions/Utility/Utility.cs +++ b/back/src/Kyoo.Abstractions/Utility/Utility.cs @@ -206,8 +206,8 @@ namespace Kyoo.Utils : type.GetInheritanceTree(); return types .Prepend(type) - .FirstOrDefault( - x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType + .FirstOrDefault(x => + x.IsGenericType && x.GetGenericTypeDefinition() == genericType ); } diff --git a/back/src/Kyoo.Authentication/Controllers/PermissionValidator.cs b/back/src/Kyoo.Authentication/Controllers/PermissionValidator.cs index 850a2f47..5defc9c2 100644 --- a/back/src/Kyoo.Authentication/Controllers/PermissionValidator.cs +++ b/back/src/Kyoo.Authentication/Controllers/PermissionValidator.cs @@ -229,11 +229,10 @@ namespace Kyoo.Authentication private AuthenticateResult _ApiKeyCheck(ActionContext context) { if ( - !context - .HttpContext - .Request - .Headers - .TryGetValue("X-API-Key", out StringValues apiKey) + !context.HttpContext.Request.Headers.TryGetValue( + "X-API-Key", + out StringValues apiKey + ) ) return AuthenticateResult.NoResult(); if (!_options.ApiKeys.Contains(apiKey!)) @@ -262,9 +261,9 @@ namespace Kyoo.Authentication private async Task _JwtCheck(ActionContext context) { - AuthenticateResult ret = await context - .HttpContext - .AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme); + AuthenticateResult ret = await context.HttpContext.AuthenticateAsync( + JwtBearerDefaults.AuthenticationScheme + ); // Change the failure message to make the API nice to use. if (ret.Failure != null) return AuthenticateResult.Fail( diff --git a/back/src/Kyoo.Authentication/Models/Options/PermissionOption.cs b/back/src/Kyoo.Authentication/Models/Options/PermissionOption.cs index dbc2819d..82d3054f 100644 --- a/back/src/Kyoo.Authentication/Models/Options/PermissionOption.cs +++ b/back/src/Kyoo.Authentication/Models/Options/PermissionOption.cs @@ -40,10 +40,8 @@ namespace Kyoo.Authentication.Models get { return Enum.GetNames() - .SelectMany( - group => - Enum.GetNames() - .Select(kind => $"{group}.{kind}".ToLowerInvariant()) + .SelectMany(group => + Enum.GetNames().Select(kind => $"{group}.{kind}".ToLowerInvariant()) ) .ToArray(); } diff --git a/back/src/Kyoo.Core/Controllers/Repositories/DapperHelper.cs b/back/src/Kyoo.Core/Controllers/Repositories/DapperHelper.cs index 653b1d37..c692ee24 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/DapperHelper.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/DapperHelper.cs @@ -65,9 +65,8 @@ public static class DapperHelper .Where(x => !x.Key.StartsWith('_')) // If first char is lower, assume manual sql instead of reflection. .Where(x => char.IsLower(key.First()) || x.Value.GetProperty(key) != null) - .Select( - x => - $"{x.Key}.{x.Value.GetProperty(key)?.GetCustomAttribute()?.Name ?? key.ToSnakeCase()}" + .Select(x => + $"{x.Key}.{x.Value.GetProperty(key)?.GetCustomAttribute()?.Name ?? key.ToSnakeCase()}" ) .ToArray(); if (keys.Length == 1) @@ -149,8 +148,7 @@ public static class DapperHelper T Map(T item, IEnumerable relations) { IEnumerable metadatas = include - .Metadatas - .Where(x => x is not Include.ProjectedRelation) + .Metadatas.Where(x => x is not Include.ProjectedRelation) .Select(x => x.Name); foreach ((string name, object? value) in metadatas.Zip(relations)) { @@ -179,26 +177,24 @@ public static class DapperHelper '\n', config .Skip(1) - .Select( - x => - $"when {x.Key}.id is not null then '{x.Value.Name.ToLowerInvariant()}'" + .Select(x => + $"when {x.Key}.id is not null then '{x.Value.Name.ToLowerInvariant()}'" ) ); return $""" - case - {cases:raw} - else '{config.First().Value.Name.ToLowerInvariant():raw}' - end {op} - """; + case + {cases:raw} + else '{config.First().Value.Name.ToLowerInvariant():raw}' + end {op} + """; } IEnumerable properties = config .Where(x => !x.Key.StartsWith('_')) // If first char is lower, assume manual sql instead of reflection. .Where(x => char.IsLower(key.First()) || x.Value.GetProperty(key) != null) - .Select( - x => - $"{x.Key}.{x.Value.GetProperty(key)?.GetCustomAttribute()?.Name ?? key.ToSnakeCase()}" + .Select(x => + $"{x.Key}.{x.Value.GetProperty(key)?.GetCustomAttribute()?.Name ?? key.ToSnakeCase()}" ); FormattableString ret = $"{properties.First():raw} {op}"; @@ -245,8 +241,7 @@ public static class DapperHelper public static string ExpendProjections(Type type, string? prefix, Include include) { IEnumerable projections = include - .Metadatas - .Select(x => (x as Include.ProjectedRelation)!) + .Metadatas.Select(x => (x as Include.ProjectedRelation)!) .Where(x => x != null) .Where(x => type.GetProperty(x.Name) != null) .Select(x => x.Sql.Replace("\"this\".", prefix)); @@ -336,8 +331,8 @@ public static class DapperHelper { string posterProj = string.Join( ", ", - new[] { "poster", "thumbnail", "logo" }.Select( - x => $"{prefix}{x}_source as source, {prefix}{x}_blurhash as blurhash" + new[] { "poster", "thumbnail", "logo" }.Select(x => + $"{prefix}{x}_source as source, {prefix}{x}_blurhash as blurhash" ) ); projection = string.IsNullOrEmpty(projection) diff --git a/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs index 22b38c58..24ad01ac 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs @@ -47,12 +47,10 @@ namespace Kyoo.Core.Controllers IRepository.OnEdited += async (show) => { await using AsyncServiceScope scope = CoreModule.Services.CreateAsyncScope(); - DatabaseContext database = scope - .ServiceProvider - .GetRequiredService(); + DatabaseContext database = + scope.ServiceProvider.GetRequiredService(); List episodes = await database - .Episodes - .AsTracking() + .Episodes.AsTracking() .Where(x => x.ShowId == show.Id) .ToListAsync(); foreach (Episode ep in episodes) @@ -96,19 +94,14 @@ namespace Kyoo.Core.Controllers protected override Task GetDuplicated(Episode item) { if (item is { SeasonNumber: not null, EpisodeNumber: not null }) - return _database - .Episodes - .FirstOrDefaultAsync( - x => - x.ShowId == item.ShowId - && x.SeasonNumber == item.SeasonNumber - && x.EpisodeNumber == item.EpisodeNumber - ); - return _database - .Episodes - .FirstOrDefaultAsync( - x => x.ShowId == item.ShowId && x.AbsoluteNumber == item.AbsoluteNumber + return _database.Episodes.FirstOrDefaultAsync(x => + x.ShowId == item.ShowId + && x.SeasonNumber == item.SeasonNumber + && x.EpisodeNumber == item.EpisodeNumber ); + return _database.Episodes.FirstOrDefaultAsync(x => + x.ShowId == item.ShowId && x.AbsoluteNumber == item.AbsoluteNumber + ); } /// @@ -140,11 +133,9 @@ namespace Kyoo.Core.Controllers } if (resource.SeasonId == null && resource.SeasonNumber != null) { - resource.Season = await _database - .Seasons - .FirstOrDefaultAsync( - x => x.ShowId == resource.ShowId && x.SeasonNumber == resource.SeasonNumber - ); + resource.Season = await _database.Seasons.FirstOrDefaultAsync(x => + x.ShowId == resource.ShowId && x.SeasonNumber == resource.SeasonNumber + ); } } @@ -152,8 +143,7 @@ namespace Kyoo.Core.Controllers public override async Task Delete(Episode obj) { int epCount = await _database - .Episodes - .Where(x => x.ShowId == obj.ShowId) + .Episodes.Where(x => x.ShowId == obj.ShowId) .Take(2) .CountAsync(); _database.Entry(obj).State = EntityState.Deleted; diff --git a/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs index 0164660a..9c070a5b 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs @@ -35,29 +35,29 @@ namespace Kyoo.Core.Controllers // language=PostgreSQL protected override FormattableString Sql => $""" - select - s.*, -- Show as s - m.*, - c.* - /* includes */ - from - shows as s - full outer join ( select - * -- Movie + s.*, -- Show as s + m.*, + c.* + /* includes */ from - movies) as m on false - full outer join( + shows as s + full outer join ( select - c.* -- Collection as c + * -- Movie from - collections as c - left join link_collection_show as ls on ls.collection_id = c.id - left join link_collection_movie as lm on lm.collection_id = c.id - group by c.id - having count(*) > 1 - ) as c on false - """; + movies) as m on false + full outer join( + select + c.* -- Collection as c + from + collections as c + left join link_collection_show as ls on ls.collection_id = c.id + left join link_collection_movie as lm on lm.collection_id = c.id + group by c.id + having count(*) > 1 + ) as c on false + """; protected override Dictionary Config => new() diff --git a/back/src/Kyoo.Core/Controllers/Repositories/NewsRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/NewsRepository.cs index c642f061..6b32491f 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/NewsRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/NewsRepository.cs @@ -32,19 +32,19 @@ namespace Kyoo.Core.Controllers // language=PostgreSQL protected override FormattableString Sql => $""" - select - e.*, -- Episode as e - m.* - /* includes */ - from - episodes as e - full outer join ( select - * -- Movie + e.*, -- Episode as e + m.* + /* includes */ from - movies - ) as m on false - """; + episodes as e + full outer join ( + select + * -- Movie + from + movies + ) as m on false + """; protected override Dictionary Config => new() { { "e", typeof(Episode) }, { "m", typeof(Movie) }, }; diff --git a/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs index 78de4095..2d60d42a 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs @@ -47,12 +47,10 @@ namespace Kyoo.Core.Controllers IRepository.OnEdited += async (show) => { await using AsyncServiceScope scope = CoreModule.Services.CreateAsyncScope(); - DatabaseContext database = scope - .ServiceProvider - .GetRequiredService(); + DatabaseContext database = + scope.ServiceProvider.GetRequiredService(); List seasons = await database - .Seasons - .AsTracking() + .Seasons.AsTracking() .Where(x => x.ShowId == show.Id) .ToListAsync(); foreach (Season season in seasons) @@ -77,11 +75,9 @@ namespace Kyoo.Core.Controllers protected override Task GetDuplicated(Season item) { - return _database - .Seasons - .FirstOrDefaultAsync( - x => x.ShowId == item.ShowId && x.SeasonNumber == item.SeasonNumber - ); + return _database.Seasons.FirstOrDefaultAsync(x => + x.ShowId == item.ShowId && x.SeasonNumber == item.SeasonNumber + ); } /// diff --git a/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs index 2bd896b2..e5a12cfc 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs @@ -66,11 +66,10 @@ public class WatchStatusRepository : IWatchStatusRepository { await using AsyncServiceScope scope = CoreModule.Services.CreateAsyncScope(); DatabaseContext db = scope.ServiceProvider.GetRequiredService(); - WatchStatusRepository repo = scope - .ServiceProvider - .GetRequiredService(); - List users = await db.ShowWatchStatus - .IgnoreQueryFilters() + WatchStatusRepository repo = + scope.ServiceProvider.GetRequiredService(); + List users = await db + .ShowWatchStatus.IgnoreQueryFilters() .Where(x => x.ShowId == ep.ShowId && x.Status == WatchStatus.Completed) .Select(x => x.UserId) .ToListAsync(); @@ -95,41 +94,41 @@ public class WatchStatusRepository : IWatchStatusRepository // language=PostgreSQL protected FormattableString Sql => $""" - select - s.*, - swe.*, -- Episode as swe - m.* - /* includes */ - from ( select - s.*, -- Show as s - sw.*, - sw.added_date as order, - sw.status as watch_status - from - shows as s - inner join show_watch_status as sw on sw.show_id = s.id - and sw.user_id = [current_user]) as s - full outer join ( - select - m.*, -- Movie as m - mw.*, - mw.added_date as order, - mw.status as watch_status - from - movies as m - inner join movie_watch_status as mw on mw.movie_id = m.id - and mw.user_id = [current_user]) as m on false - left join episodes as swe on swe.id = s.next_episode_id - /* includesJoin */ - where - (coalesce(s.watch_status, m.watch_status) = 'watching'::watch_status - or coalesce(s.watch_status, m.watch_status) = 'planned'::watch_status) - /* where */ - order by - coalesce(s.order, m.order) desc, - coalesce(s.id, m.id) asc - """; + s.*, + swe.*, -- Episode as swe + m.* + /* includes */ + from ( + select + s.*, -- Show as s + sw.*, + sw.added_date as order, + sw.status as watch_status + from + shows as s + inner join show_watch_status as sw on sw.show_id = s.id + and sw.user_id = [current_user]) as s + full outer join ( + select + m.*, -- Movie as m + mw.*, + mw.added_date as order, + mw.status as watch_status + from + movies as m + inner join movie_watch_status as mw on mw.movie_id = m.id + and mw.user_id = [current_user]) as m on false + left join episodes as swe on swe.id = s.next_episode_id + /* includesJoin */ + where + (coalesce(s.watch_status, m.watch_status) = 'watching'::watch_status + or coalesce(s.watch_status, m.watch_status) = 'planned'::watch_status) + /* where */ + order by + coalesce(s.order, m.order) desc, + coalesce(s.id, m.id) asc + """; protected Dictionary Config => new() @@ -189,8 +188,7 @@ public class WatchStatusRepository : IWatchStatusRepository { if (include != null) include.Metadatas = include - .Metadatas - .Where(x => x.Name != nameof(Show.WatchStatus)) + .Metadatas.Where(x => x.Name != nameof(Show.WatchStatus)) .ToList(); // We can't use the generic after id hanler since the sort depends on a relation. @@ -226,9 +224,9 @@ public class WatchStatusRepository : IWatchStatusRepository /// public Task GetMovieStatus(Guid movieId, Guid userId) { - return _database - .MovieWatchStatus - .FirstOrDefaultAsync(x => x.MovieId == movieId && x.UserId == userId); + return _database.MovieWatchStatus.FirstOrDefaultAsync(x => + x.MovieId == movieId && x.UserId == userId + ); } /// @@ -277,8 +275,7 @@ public class WatchStatusRepository : IWatchStatusRepository PlayedDate = status == WatchStatus.Completed ? DateTime.UtcNow : null, }; await _database - .MovieWatchStatus - .Upsert(ret) + .MovieWatchStatus.Upsert(ret) .UpdateIf(x => status != Watching || x.Status != Completed) .RunAsync(); return ret; @@ -288,17 +285,16 @@ public class WatchStatusRepository : IWatchStatusRepository public async Task DeleteMovieStatus(Guid movieId, Guid userId) { await _database - .MovieWatchStatus - .Where(x => x.MovieId == movieId && x.UserId == userId) + .MovieWatchStatus.Where(x => x.MovieId == movieId && x.UserId == userId) .ExecuteDeleteAsync(); } /// public Task GetShowStatus(Guid showId, Guid userId) { - return _database - .ShowWatchStatus - .FirstOrDefaultAsync(x => x.ShowId == showId && x.UserId == userId); + return _database.ShowWatchStatus.FirstOrDefaultAsync(x => + x.ShowId == showId && x.UserId == userId + ); } /// @@ -315,12 +311,9 @@ public class WatchStatusRepository : IWatchStatusRepository int unseenEpisodeCount = status != WatchStatus.Completed ? await _database - .Episodes - .Where(x => x.ShowId == showId) - .Where( - x => - x.Watched!.First(x => x.UserId == userId)!.Status - != WatchStatus.Completed + .Episodes.Where(x => x.ShowId == showId) + .Where(x => + x.Watched!.First(x => x.UserId == userId)!.Status != WatchStatus.Completed ) .CountAsync() : 0; @@ -332,57 +325,47 @@ public class WatchStatusRepository : IWatchStatusRepository if (status == WatchStatus.Watching) { var cursor = await _database - .Episodes - .IgnoreQueryFilters() + .Episodes.IgnoreQueryFilters() .Where(x => x.ShowId == showId) .OrderByDescending(x => x.AbsoluteNumber) .ThenByDescending(x => x.SeasonNumber) .ThenByDescending(x => x.EpisodeNumber) - .Select( - x => - new - { - x.Id, - x.AbsoluteNumber, - x.SeasonNumber, - x.EpisodeNumber, - Status = x.Watched!.First(x => x.UserId == userId) - } - ) - .FirstOrDefaultAsync( - x => - x.Status.Status == WatchStatus.Completed - || x.Status.Status == WatchStatus.Watching + .Select(x => new + { + x.Id, + x.AbsoluteNumber, + x.SeasonNumber, + x.EpisodeNumber, + Status = x.Watched!.First(x => x.UserId == userId) + }) + .FirstOrDefaultAsync(x => + x.Status.Status == WatchStatus.Completed + || x.Status.Status == WatchStatus.Watching ); cursorWatchStatus = cursor?.Status; nextEpisodeId = cursor?.Status.Status == WatchStatus.Watching ? cursor.Id : await _database - .Episodes - .IgnoreQueryFilters() + .Episodes.IgnoreQueryFilters() .Where(x => x.ShowId == showId) .OrderBy(x => x.AbsoluteNumber) .ThenBy(x => x.SeasonNumber) .ThenBy(x => x.EpisodeNumber) - .Where( - x => - cursor == null - || x.AbsoluteNumber > cursor.AbsoluteNumber - || x.SeasonNumber > cursor.SeasonNumber - || ( - x.SeasonNumber == cursor.SeasonNumber - && x.EpisodeNumber > cursor.EpisodeNumber - ) - ) - .Select( - x => - new - { - x.Id, - Status = x.Watched!.FirstOrDefault(x => x.UserId == userId) - } + .Where(x => + cursor == null + || x.AbsoluteNumber > cursor.AbsoluteNumber + || x.SeasonNumber > cursor.SeasonNumber + || ( + x.SeasonNumber == cursor.SeasonNumber + && x.EpisodeNumber > cursor.EpisodeNumber + ) ) + .Select(x => new + { + x.Id, + Status = x.Watched!.FirstOrDefault(x => x.UserId == userId) + }) .Where(x => x.Status == null || x.Status.Status != WatchStatus.Completed) // The as Guid? is here to add the nullability status of the queryable. // Without this, FirstOrDefault returns new Guid() when no result is found (which is 16 0s and invalid in sql). @@ -392,24 +375,19 @@ public class WatchStatusRepository : IWatchStatusRepository else if (status == WatchStatus.Completed) { List episodes = await _database - .Episodes - .Where(x => x.ShowId == showId) + .Episodes.Where(x => x.ShowId == showId) .Select(x => x.Id) .ToListAsync(); await _database - .EpisodeWatchStatus - .UpsertRange( - episodes.Select( - episodeId => - new EpisodeWatchStatus - { - UserId = userId, - EpisodeId = episodeId, - Status = WatchStatus.Completed, - AddedDate = DateTime.UtcNow, - PlayedDate = DateTime.UtcNow - } - ) + .EpisodeWatchStatus.UpsertRange( + episodes.Select(episodeId => new EpisodeWatchStatus + { + UserId = userId, + EpisodeId = episodeId, + Status = WatchStatus.Completed, + AddedDate = DateTime.UtcNow, + PlayedDate = DateTime.UtcNow + }) ) .UpdateIf(x => x.Status == Watching || x.Status == Planned) .RunAsync(); @@ -435,8 +413,7 @@ public class WatchStatusRepository : IWatchStatusRepository PlayedDate = status == WatchStatus.Completed ? DateTime.UtcNow : null, }; await _database - .ShowWatchStatus - .Upsert(ret) + .ShowWatchStatus.Upsert(ret) .UpdateIf(x => status != Watching || x.Status != Completed || newEpisode) .RunAsync(); return ret; @@ -446,22 +423,20 @@ public class WatchStatusRepository : IWatchStatusRepository public async Task DeleteShowStatus(Guid showId, Guid userId) { await _database - .ShowWatchStatus - .IgnoreAutoIncludes() + .ShowWatchStatus.IgnoreAutoIncludes() .Where(x => x.ShowId == showId && x.UserId == userId) .ExecuteDeleteAsync(); await _database - .EpisodeWatchStatus - .Where(x => x.Episode.ShowId == showId && x.UserId == userId) + .EpisodeWatchStatus.Where(x => x.Episode.ShowId == showId && x.UserId == userId) .ExecuteDeleteAsync(); } /// public Task GetEpisodeStatus(Guid episodeId, Guid userId) { - return _database - .EpisodeWatchStatus - .FirstOrDefaultAsync(x => x.EpisodeId == episodeId && x.UserId == userId); + return _database.EpisodeWatchStatus.FirstOrDefaultAsync(x => + x.EpisodeId == episodeId && x.UserId == userId + ); } /// @@ -510,8 +485,7 @@ public class WatchStatusRepository : IWatchStatusRepository PlayedDate = status == WatchStatus.Completed ? DateTime.UtcNow : null, }; await _database - .EpisodeWatchStatus - .Upsert(ret) + .EpisodeWatchStatus.Upsert(ret) .UpdateIf(x => status != Watching || x.Status != Completed) .RunAsync(); await SetShowStatus(episode.ShowId, userId, WatchStatus.Watching); @@ -522,8 +496,7 @@ public class WatchStatusRepository : IWatchStatusRepository public async Task DeleteEpisodeStatus(Guid episodeId, Guid userId) { await _database - .EpisodeWatchStatus - .Where(x => x.EpisodeId == episodeId && x.UserId == userId) + .EpisodeWatchStatus.Where(x => x.EpisodeId == episodeId && x.UserId == userId) .ExecuteDeleteAsync(); } } diff --git a/back/src/Kyoo.Core/Controllers/ThumbnailsManager.cs b/back/src/Kyoo.Core/Controllers/ThumbnailsManager.cs index 04db1a1f..7355cb39 100644 --- a/back/src/Kyoo.Core/Controllers/ThumbnailsManager.cs +++ b/back/src/Kyoo.Core/Controllers/ThumbnailsManager.cs @@ -212,14 +212,13 @@ namespace Kyoo.Core.Controllers { IEnumerable images = new[] { "poster", "thumbnail", "logo" } .SelectMany(x => _GetBaseImagePath(item, x)) - .SelectMany( - x => - new[] - { - ImageQuality.High.ToString().ToLowerInvariant(), - ImageQuality.Medium.ToString().ToLowerInvariant(), - ImageQuality.Low.ToString().ToLowerInvariant(), - }.Select(quality => $"{x}.{quality}.webp") + .SelectMany(x => + new[] + { + ImageQuality.High.ToString().ToLowerInvariant(), + ImageQuality.Medium.ToString().ToLowerInvariant(), + ImageQuality.Low.ToString().ToLowerInvariant(), + }.Select(quality => $"{x}.{quality}.webp") ); foreach (string image in images) diff --git a/back/src/Kyoo.Core/CoreModule.cs b/back/src/Kyoo.Core/CoreModule.cs index 5d60f7f0..121fe7b1 100644 --- a/back/src/Kyoo.Core/CoreModule.cs +++ b/back/src/Kyoo.Core/CoreModule.cs @@ -105,8 +105,8 @@ namespace Kyoo.Core options.SuppressMapClientErrors = true; options.InvalidModelStateResponseFactory = ctx => { - string[] errors = ctx.ModelState - .SelectMany(x => x.Value!.Errors) + string[] errors = ctx + .ModelState.SelectMany(x => x.Value!.Errors) .Select(x => x.ErrorMessage) .ToArray(); return new BadRequestObjectResult(new RequestError(errors)); diff --git a/back/src/Kyoo.Core/Views/Helper/BaseApi.cs b/back/src/Kyoo.Core/Views/Helper/BaseApi.cs index 25dad197..8db3378d 100644 --- a/back/src/Kyoo.Core/Views/Helper/BaseApi.cs +++ b/back/src/Kyoo.Core/Views/Helper/BaseApi.cs @@ -45,13 +45,11 @@ namespace Kyoo.Core.Api protected Page Page(ICollection resources, int limit) where TResult : IResource { - Dictionary query = Request - .Query - .ToDictionary( - x => x.Key, - x => x.Value.ToString(), - StringComparer.InvariantCultureIgnoreCase - ); + Dictionary query = Request.Query.ToDictionary( + x => x.Key, + x => x.Value.ToString(), + StringComparer.InvariantCultureIgnoreCase + ); // If the query was sorted randomly, add the seed to the url to get reproducible links (next,prev,first...) if (query.ContainsKey("sortBy")) @@ -66,13 +64,11 @@ namespace Kyoo.Core.Api protected SearchPage SearchPage(SearchPage.SearchResult result) where TResult : IResource { - Dictionary query = Request - .Query - .ToDictionary( - x => x.Key, - x => x.Value.ToString(), - StringComparer.InvariantCultureIgnoreCase - ); + Dictionary query = Request.Query.ToDictionary( + x => x.Key, + x => x.Value.ToString(), + StringComparer.InvariantCultureIgnoreCase + ); string self = Request.Path + query.ToQueryString(); string? previous = null; diff --git a/back/src/Kyoo.Core/Views/Helper/FilterBinder.cs b/back/src/Kyoo.Core/Views/Helper/FilterBinder.cs index e4eac372..3be01fa9 100644 --- a/back/src/Kyoo.Core/Views/Helper/FilterBinder.cs +++ b/back/src/Kyoo.Core/Views/Helper/FilterBinder.cs @@ -28,14 +28,13 @@ public class FilterBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { - ValueProviderResult fields = bindingContext - .ValueProvider - .GetValue(bindingContext.FieldName); + ValueProviderResult fields = bindingContext.ValueProvider.GetValue( + bindingContext.FieldName + ); try { object? filter = bindingContext - .ModelType - .GetMethod(nameof(Filter.From))! + .ModelType.GetMethod(nameof(Filter.From))! .Invoke(null, new object?[] { fields.FirstValue }); bindingContext.Result = ModelBindingResult.Success(filter); return Task.CompletedTask; diff --git a/back/src/Kyoo.Core/Views/Helper/IncludeBinder.cs b/back/src/Kyoo.Core/Views/Helper/IncludeBinder.cs index c3a9aff8..3ddab91b 100644 --- a/back/src/Kyoo.Core/Views/Helper/IncludeBinder.cs +++ b/back/src/Kyoo.Core/Views/Helper/IncludeBinder.cs @@ -31,14 +31,13 @@ public class IncludeBinder : IModelBinder public Task BindModelAsync(ModelBindingContext bindingContext) { - ValueProviderResult fields = bindingContext - .ValueProvider - .GetValue(bindingContext.FieldName); + ValueProviderResult fields = bindingContext.ValueProvider.GetValue( + bindingContext.FieldName + ); try { object include = bindingContext - .ModelType - .GetMethod(nameof(Include.From))! + .ModelType.GetMethod(nameof(Include.From))! .Invoke(null, new object?[] { fields.FirstValue })!; bindingContext.Result = ModelBindingResult.Success(include); bindingContext.HttpContext.Items["fields"] = ((dynamic)include).Fields; diff --git a/back/src/Kyoo.Core/Views/Helper/SortBinder.cs b/back/src/Kyoo.Core/Views/Helper/SortBinder.cs index b46f26d5..8bafab6d 100644 --- a/back/src/Kyoo.Core/Views/Helper/SortBinder.cs +++ b/back/src/Kyoo.Core/Views/Helper/SortBinder.cs @@ -32,9 +32,9 @@ public class SortBinder : IModelBinder public Task BindModelAsync(ModelBindingContext bindingContext) { - ValueProviderResult sortBy = bindingContext - .ValueProvider - .GetValue(bindingContext.FieldName); + ValueProviderResult sortBy = bindingContext.ValueProvider.GetValue( + bindingContext.FieldName + ); uint seed = BitConverter.ToUInt32( BitConverter.GetBytes(_rng.Next(int.MinValue, int.MaxValue)), 0 @@ -42,8 +42,7 @@ public class SortBinder : IModelBinder try { object sort = bindingContext - .ModelType - .GetMethod(nameof(Sort.From))! + .ModelType.GetMethod(nameof(Sort.From))! .Invoke(null, new object?[] { sortBy.FirstValue, seed })!; bindingContext.Result = ModelBindingResult.Success(sort); bindingContext.HttpContext.Items["seed"] = seed; diff --git a/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs b/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs index 53f11d56..ebb0f48e 100644 --- a/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs +++ b/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs @@ -85,17 +85,12 @@ namespace Kyoo.Core.Api [FromQuery] Include fields ) { - ICollection resources = await _libraryManager - .Shows - .GetAll( - Filter.And( - filter, - identifier.Matcher(x => x.StudioId, x => x.Studio!.Slug) - ), - sortBy, - fields, - pagination - ); + ICollection resources = await _libraryManager.Shows.GetAll( + Filter.And(filter, identifier.Matcher(x => x.StudioId, x => x.Studio!.Slug)), + sortBy, + fields, + pagination + ); if ( !resources.Any() diff --git a/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs b/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs index be494cea..c9ef63cd 100644 --- a/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs @@ -200,17 +200,12 @@ namespace Kyoo.Core.Api [FromQuery] Include? fields ) { - ICollection resources = await _libraryManager - .Shows - .GetAll( - Filter.And( - filter, - identifier.IsContainedIn(x => x.Collections) - ), - sortBy == new Sort.Default() ? new Sort.By(x => x.AirDate) : sortBy, - fields, - pagination - ); + ICollection resources = await _libraryManager.Shows.GetAll( + Filter.And(filter, identifier.IsContainedIn(x => x.Collections)), + sortBy == new Sort.Default() ? new Sort.By(x => x.AirDate) : sortBy, + fields, + pagination + ); if ( !resources.Any() @@ -249,19 +244,12 @@ namespace Kyoo.Core.Api [FromQuery] Include? fields ) { - ICollection resources = await _libraryManager - .Movies - .GetAll( - Filter.And( - filter, - identifier.IsContainedIn(x => x.Collections) - ), - sortBy == new Sort.Default() - ? new Sort.By(x => x.AirDate) - : sortBy, - fields, - pagination - ); + ICollection resources = await _libraryManager.Movies.GetAll( + Filter.And(filter, identifier.IsContainedIn(x => x.Collections)), + sortBy == new Sort.Default() ? new Sort.By(x => x.AirDate) : sortBy, + fields, + pagination + ); if ( !resources.Any() diff --git a/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs b/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs index 7a89df3c..5d2a3c86 100644 --- a/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs @@ -77,9 +77,10 @@ namespace Kyoo.Core.Api [FromQuery] Include fields ) { - return await _libraryManager - .Shows - .Get(identifier.IsContainedIn(x => x.Episodes!), fields); + return await _libraryManager.Shows.Get( + identifier.IsContainedIn(x => x.Episodes!), + fields + ); } /// @@ -103,9 +104,10 @@ namespace Kyoo.Core.Api [FromQuery] Include fields ) { - Season? ret = await _libraryManager - .Seasons - .GetOrDefault(identifier.IsContainedIn(x => x.Episodes!), fields); + Season? ret = await _libraryManager.Seasons.GetOrDefault( + identifier.IsContainedIn(x => x.Episodes!), + fields + ); if (ret != null) return ret; Episode? episode = await identifier.Match( @@ -170,9 +172,13 @@ namespace Kyoo.Core.Api id => Task.FromResult(id), async slug => (await _libraryManager.Episodes.Get(slug)).Id ); - return await _libraryManager - .WatchStatus - .SetEpisodeStatus(id, User.GetIdOrThrow(), status, watchedTime, percent); + return await _libraryManager.WatchStatus.SetEpisodeStatus( + id, + User.GetIdOrThrow(), + status, + watchedTime, + percent + ); } /// diff --git a/back/src/Kyoo.Core/Views/Resources/MovieApi.cs b/back/src/Kyoo.Core/Views/Resources/MovieApi.cs index 627a7b93..b5b2ffcb 100644 --- a/back/src/Kyoo.Core/Views/Resources/MovieApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/MovieApi.cs @@ -113,9 +113,10 @@ namespace Kyoo.Core.Api [FromQuery] Include fields ) { - return await _libraryManager - .Studios - .Get(identifier.IsContainedIn(x => x.Movies!), fields); + return await _libraryManager.Studios.Get( + identifier.IsContainedIn(x => x.Movies!), + fields + ); } /// @@ -146,14 +147,12 @@ namespace Kyoo.Core.Api [FromQuery] Include fields ) { - ICollection resources = await _libraryManager - .Collections - .GetAll( - Filter.And(filter, identifier.IsContainedIn(x => x.Movies)), - sortBy, - fields, - pagination - ); + ICollection resources = await _libraryManager.Collections.GetAll( + Filter.And(filter, identifier.IsContainedIn(x => x.Movies)), + sortBy, + fields, + pagination + ); if ( !resources.Any() @@ -219,9 +218,13 @@ namespace Kyoo.Core.Api id => Task.FromResult(id), async slug => (await _libraryManager.Movies.Get(slug)).Id ); - return await _libraryManager - .WatchStatus - .SetMovieStatus(id, User.GetIdOrThrow(), status, watchedTime, percent); + return await _libraryManager.WatchStatus.SetMovieStatus( + id, + User.GetIdOrThrow(), + status, + watchedTime, + percent + ); } /// diff --git a/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs b/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs index 51421035..bcfb517b 100644 --- a/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs @@ -86,17 +86,15 @@ namespace Kyoo.Core.Api [FromQuery] Include fields ) { - ICollection resources = await _libraryManager - .Episodes - .GetAll( - Filter.And( - filter, - identifier.Matcher(x => x.SeasonId, x => x.Season!.Slug) - ), - sortBy, - fields, - pagination - ); + ICollection resources = await _libraryManager.Episodes.GetAll( + Filter.And( + filter, + identifier.Matcher(x => x.SeasonId, x => x.Season!.Slug) + ), + sortBy, + fields, + pagination + ); if ( !resources.Any() @@ -125,9 +123,10 @@ namespace Kyoo.Core.Api [FromQuery] Include fields ) { - Show? ret = await _libraryManager - .Shows - .GetOrDefault(identifier.IsContainedIn(x => x.Seasons!), fields); + Show? ret = await _libraryManager.Shows.GetOrDefault( + identifier.IsContainedIn(x => x.Seasons!), + fields + ); if (ret == null) return NotFound(); return ret; diff --git a/back/src/Kyoo.Core/Views/Resources/ShowApi.cs b/back/src/Kyoo.Core/Views/Resources/ShowApi.cs index dfa4ee19..f0dc5122 100644 --- a/back/src/Kyoo.Core/Views/Resources/ShowApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/ShowApi.cs @@ -88,17 +88,12 @@ namespace Kyoo.Core.Api [FromQuery] Include fields ) { - ICollection resources = await _libraryManager - .Seasons - .GetAll( - Filter.And( - filter, - identifier.Matcher(x => x.ShowId, x => x.Show!.Slug) - ), - sortBy, - fields, - pagination - ); + ICollection resources = await _libraryManager.Seasons.GetAll( + Filter.And(filter, identifier.Matcher(x => x.ShowId, x => x.Show!.Slug)), + sortBy, + fields, + pagination + ); if ( !resources.Any() @@ -136,17 +131,12 @@ namespace Kyoo.Core.Api [FromQuery] Include fields ) { - ICollection resources = await _libraryManager - .Episodes - .GetAll( - Filter.And( - filter, - identifier.Matcher(x => x.ShowId, x => x.Show!.Slug) - ), - sortBy, - fields, - pagination - ); + ICollection resources = await _libraryManager.Episodes.GetAll( + Filter.And(filter, identifier.Matcher(x => x.ShowId, x => x.Show!.Slug)), + sortBy, + fields, + pagination + ); if ( !resources.Any() @@ -210,9 +200,10 @@ namespace Kyoo.Core.Api [FromQuery] Include fields ) { - return await _libraryManager - .Studios - .Get(identifier.IsContainedIn(x => x.Shows!), fields); + return await _libraryManager.Studios.Get( + identifier.IsContainedIn(x => x.Shows!), + fields + ); } /// @@ -243,14 +234,12 @@ namespace Kyoo.Core.Api [FromQuery] Include fields ) { - ICollection resources = await _libraryManager - .Collections - .GetAll( - Filter.And(filter, identifier.IsContainedIn(x => x.Shows!)), - sortBy, - fields, - pagination - ); + ICollection resources = await _libraryManager.Collections.GetAll( + Filter.And(filter, identifier.IsContainedIn(x => x.Shows!)), + sortBy, + fields, + pagination + ); if ( !resources.Any() diff --git a/back/src/Kyoo.Core/Views/Watch/ProxyApi.cs b/back/src/Kyoo.Core/Views/Watch/ProxyApi.cs index a8d37d41..c603a543 100644 --- a/back/src/Kyoo.Core/Views/Watch/ProxyApi.cs +++ b/back/src/Kyoo.Core/Views/Watch/ProxyApi.cs @@ -35,14 +35,13 @@ namespace Kyoo.Core.Api public class ProxyApi : Controller { private readonly HttpProxyOptions _proxyOptions = HttpProxyOptionsBuilder - .Instance - .WithHandleFailure( + .Instance.WithHandleFailure( async (context, exception) => { context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; - await context - .Response - .WriteAsJsonAsync(new RequestError("Service unavailable")); + await context.Response.WriteAsJsonAsync( + new RequestError("Service unavailable") + ); } ) .Build(); diff --git a/back/src/Kyoo.Host/Application.cs b/back/src/Kyoo.Host/Application.cs index 4a5059d3..5633d844 100644 --- a/back/src/Kyoo.Host/Application.cs +++ b/back/src/Kyoo.Host/Application.cs @@ -150,25 +150,19 @@ namespace Kyoo.Host .ConfigureAppConfiguration(x => _SetupConfig(x, args)) .UseSerilog((host, services, builder) => _ConfigureLogging(builder)) .ConfigureServices(x => x.AddRouting()) - .ConfigureWebHost( - x => - x.UseKestrel(options => - { - options.AddServerHeader = false; - }) - .UseIIS() - .UseIISIntegration() - .UseUrls( - Environment.GetEnvironmentVariable("KYOO_BIND_URL") - ?? "http://*:5000" - ) - .UseStartup( - host => - PluginsStartup.FromWebHost( - host, - new LoggerFactory().AddSerilog() - ) - ) + .ConfigureWebHost(x => + x.UseKestrel(options => + { + options.AddServerHeader = false; + }) + .UseIIS() + .UseIISIntegration() + .UseUrls( + Environment.GetEnvironmentVariable("KYOO_BIND_URL") ?? "http://*:5000" + ) + .UseStartup(host => + PluginsStartup.FromWebHost(host, new LoggerFactory().AddSerilog()) + ) ); } @@ -196,20 +190,13 @@ namespace Kyoo.Host "[{@t:HH:mm:ss} {@l:u3} {Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1), 25} " + "({@i:D10})] {@m}{#if not EndsWith(@m, '\n')}\n{#end}{@x}"; builder - .MinimumLevel - .Warning() - .MinimumLevel - .Override("Kyoo", LogEventLevel.Verbose) - .MinimumLevel - .Override("Microsoft.Hosting.Lifetime", LogEventLevel.Verbose) - .MinimumLevel - .Override("Microsoft.EntityFrameworkCore", LogEventLevel.Fatal) - .WriteTo - .Console(new ExpressionTemplate(template, theme: TemplateTheme.Code)) - .Enrich - .WithThreadId() - .Enrich - .FromLogContext(); + .MinimumLevel.Warning() + .MinimumLevel.Override("Kyoo", LogEventLevel.Verbose) + .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Verbose) + .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Fatal) + .WriteTo.Console(new ExpressionTemplate(template, theme: TemplateTheme.Code)) + .Enrich.WithThreadId() + .Enrich.FromLogContext(); } } } diff --git a/back/src/Kyoo.Host/PluginsStartup.cs b/back/src/Kyoo.Host/PluginsStartup.cs index 6fcd4393..a4abf396 100644 --- a/back/src/Kyoo.Host/PluginsStartup.cs +++ b/back/src/Kyoo.Host/PluginsStartup.cs @@ -127,8 +127,8 @@ namespace Kyoo.Host .SelectMany(x => x.ConfigureSteps) .OrderByDescending(x => x.Priority); - using ILifetimeScope scope = container.BeginLifetimeScope( - x => x.RegisterInstance(app).SingleInstance().ExternallyOwned() + using ILifetimeScope scope = container.BeginLifetimeScope(x => + x.RegisterInstance(app).SingleInstance().ExternallyOwned() ); IServiceProvider provider = scope.Resolve(); foreach (IStartupAction step in steps) diff --git a/back/src/Kyoo.Meilisearch/SearchManager.cs b/back/src/Kyoo.Meilisearch/SearchManager.cs index f2ab5d1b..07b5ed11 100644 --- a/back/src/Kyoo.Meilisearch/SearchManager.cs +++ b/back/src/Kyoo.Meilisearch/SearchManager.cs @@ -39,8 +39,10 @@ public class SearchManager : ISearchManager Sort.By @sortBy => MeilisearchModule .IndexSettings[index] - .SortableAttributes - .Contains(sortBy.Key, StringComparer.InvariantCultureIgnoreCase) + .SortableAttributes.Contains( + sortBy.Key, + StringComparer.InvariantCultureIgnoreCase + ) ? new[] { $"{CamelCase.ConvertName(sortBy.Key)}:{(sortBy.Desendant ? "desc" : "asc")}" diff --git a/back/src/Kyoo.Postgresql/DatabaseContext.cs b/back/src/Kyoo.Postgresql/DatabaseContext.cs index 281ccc2e..6f6428ca 100644 --- a/back/src/Kyoo.Postgresql/DatabaseContext.cs +++ b/back/src/Kyoo.Postgresql/DatabaseContext.cs @@ -369,16 +369,13 @@ namespace Kyoo.Postgresql modelBuilder.Entity().HasIndex(x => x.Slug).IsUnique(); modelBuilder .Entity() - .HasIndex( - x => - new - { - ShowID = x.ShowId, - x.SeasonNumber, - x.EpisodeNumber, - x.AbsoluteNumber - } - ) + .HasIndex(x => new + { + ShowID = x.ShowId, + x.SeasonNumber, + x.EpisodeNumber, + x.AbsoluteNumber + }) .IsUnique(); modelBuilder.Entity().HasIndex(x => x.Slug).IsUnique(); modelBuilder.Entity().HasIndex(x => x.Slug).IsUnique(); diff --git a/back/src/Kyoo.Postgresql/Migrations/20231128171554_Initial.cs b/back/src/Kyoo.Postgresql/Migrations/20231128171554_Initial.cs index 20b62adc..72469330 100644 --- a/back/src/Kyoo.Postgresql/Migrations/20231128171554_Initial.cs +++ b/back/src/Kyoo.Postgresql/Migrations/20231128171554_Initial.cs @@ -41,42 +41,41 @@ namespace Kyoo.Postgresql.Migrations migrationBuilder.CreateTable( name: "collections", - columns: table => - new - { - id = table.Column(type: "uuid", nullable: false), - slug = table.Column( - type: "character varying(256)", - maxLength: 256, - nullable: false - ), - name = table.Column(type: "text", nullable: false), - overview = table.Column(type: "text", nullable: true), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - poster_source = table.Column(type: "text", nullable: true), - poster_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - thumbnail_source = table.Column(type: "text", nullable: true), - thumbnail_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - logo_source = table.Column(type: "text", nullable: true), - logo_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - external_id = table.Column(type: "json", nullable: false) - }, + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + slug = table.Column( + type: "character varying(256)", + maxLength: 256, + nullable: false + ), + name = table.Column(type: "text", nullable: false), + overview = table.Column(type: "text", nullable: true), + added_date = table.Column( + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now() at time zone 'utc'" + ), + poster_source = table.Column(type: "text", nullable: true), + poster_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + thumbnail_source = table.Column(type: "text", nullable: true), + thumbnail_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + logo_source = table.Column(type: "text", nullable: true), + logo_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + external_id = table.Column(type: "json", nullable: false) + }, constraints: table => { table.PrimaryKey("pk_collections", x => x.id); @@ -85,18 +84,17 @@ namespace Kyoo.Postgresql.Migrations migrationBuilder.CreateTable( name: "studios", - columns: table => - new - { - id = table.Column(type: "uuid", nullable: false), - slug = table.Column( - type: "character varying(256)", - maxLength: 256, - nullable: false - ), - name = table.Column(type: "text", nullable: false), - external_id = table.Column(type: "json", nullable: false) - }, + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + slug = table.Column( + type: "character varying(256)", + maxLength: 256, + nullable: false + ), + name = table.Column(type: "text", nullable: false), + external_id = table.Column(type: "json", nullable: false) + }, constraints: table => { table.PrimaryKey("pk_studios", x => x.id); @@ -105,31 +103,30 @@ namespace Kyoo.Postgresql.Migrations migrationBuilder.CreateTable( name: "users", - columns: table => - new - { - id = table.Column(type: "uuid", nullable: false), - slug = table.Column( - type: "character varying(256)", - maxLength: 256, - nullable: false - ), - username = table.Column(type: "text", nullable: false), - email = table.Column(type: "text", nullable: false), - password = table.Column(type: "text", nullable: false), - permissions = table.Column(type: "text[]", nullable: false), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - logo_source = table.Column(type: "text", nullable: true), - logo_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ) - }, + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + slug = table.Column( + type: "character varying(256)", + maxLength: 256, + nullable: false + ), + username = table.Column(type: "text", nullable: false), + email = table.Column(type: "text", nullable: false), + password = table.Column(type: "text", nullable: false), + permissions = table.Column(type: "text[]", nullable: false), + added_date = table.Column( + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now() at time zone 'utc'" + ), + logo_source = table.Column(type: "text", nullable: true), + logo_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ) + }, constraints: table => { table.PrimaryKey("pk_users", x => x.id); @@ -138,56 +135,55 @@ namespace Kyoo.Postgresql.Migrations migrationBuilder.CreateTable( name: "movies", - columns: table => - new - { - id = table.Column(type: "uuid", nullable: false), - slug = table.Column( - type: "character varying(256)", - maxLength: 256, - nullable: false - ), - name = table.Column(type: "text", nullable: false), - tagline = table.Column(type: "text", nullable: true), - aliases = table.Column(type: "text[]", nullable: false), - path = table.Column(type: "text", nullable: false), - overview = table.Column(type: "text", nullable: true), - tags = table.Column(type: "text[]", nullable: false), - genres = table.Column(type: "genre[]", nullable: false), - status = table.Column(type: "status", nullable: false), - rating = table.Column(type: "integer", nullable: false), - runtime = table.Column(type: "integer", nullable: false), - air_date = table.Column( - type: "timestamp with time zone", - nullable: true - ), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - poster_source = table.Column(type: "text", nullable: true), - poster_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - thumbnail_source = table.Column(type: "text", nullable: true), - thumbnail_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - logo_source = table.Column(type: "text", nullable: true), - logo_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - trailer = table.Column(type: "text", nullable: true), - external_id = table.Column(type: "json", nullable: false), - studio_id = table.Column(type: "uuid", nullable: true) - }, + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + slug = table.Column( + type: "character varying(256)", + maxLength: 256, + nullable: false + ), + name = table.Column(type: "text", nullable: false), + tagline = table.Column(type: "text", nullable: true), + aliases = table.Column(type: "text[]", nullable: false), + path = table.Column(type: "text", nullable: false), + overview = table.Column(type: "text", nullable: true), + tags = table.Column(type: "text[]", nullable: false), + genres = table.Column(type: "genre[]", nullable: false), + status = table.Column(type: "status", nullable: false), + rating = table.Column(type: "integer", nullable: false), + runtime = table.Column(type: "integer", nullable: false), + air_date = table.Column( + type: "timestamp with time zone", + nullable: true + ), + added_date = table.Column( + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now() at time zone 'utc'" + ), + poster_source = table.Column(type: "text", nullable: true), + poster_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + thumbnail_source = table.Column(type: "text", nullable: true), + thumbnail_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + logo_source = table.Column(type: "text", nullable: true), + logo_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + trailer = table.Column(type: "text", nullable: true), + external_id = table.Column(type: "json", nullable: false), + studio_id = table.Column(type: "uuid", nullable: true) + }, constraints: table => { table.PrimaryKey("pk_movies", x => x.id); @@ -203,58 +199,57 @@ namespace Kyoo.Postgresql.Migrations migrationBuilder.CreateTable( name: "shows", - columns: table => - new - { - id = table.Column(type: "uuid", nullable: false), - slug = table.Column( - type: "character varying(256)", - maxLength: 256, - nullable: false - ), - name = table.Column(type: "text", nullable: false), - tagline = table.Column(type: "text", nullable: true), - aliases = table.Column>(type: "text[]", nullable: false), - overview = table.Column(type: "text", nullable: true), - tags = table.Column>(type: "text[]", nullable: false), - genres = table.Column>(type: "genre[]", nullable: false), - status = table.Column(type: "status", nullable: false), - rating = table.Column(type: "integer", nullable: false), - start_air = table.Column( - type: "timestamp with time zone", - nullable: true - ), - end_air = table.Column( - type: "timestamp with time zone", - nullable: true - ), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - poster_source = table.Column(type: "text", nullable: true), - poster_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - thumbnail_source = table.Column(type: "text", nullable: true), - thumbnail_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - logo_source = table.Column(type: "text", nullable: true), - logo_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - trailer = table.Column(type: "text", nullable: true), - external_id = table.Column(type: "json", nullable: false), - studio_id = table.Column(type: "uuid", nullable: true) - }, + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + slug = table.Column( + type: "character varying(256)", + maxLength: 256, + nullable: false + ), + name = table.Column(type: "text", nullable: false), + tagline = table.Column(type: "text", nullable: true), + aliases = table.Column>(type: "text[]", nullable: false), + overview = table.Column(type: "text", nullable: true), + tags = table.Column>(type: "text[]", nullable: false), + genres = table.Column>(type: "genre[]", nullable: false), + status = table.Column(type: "status", nullable: false), + rating = table.Column(type: "integer", nullable: false), + start_air = table.Column( + type: "timestamp with time zone", + nullable: true + ), + end_air = table.Column( + type: "timestamp with time zone", + nullable: true + ), + added_date = table.Column( + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now() at time zone 'utc'" + ), + poster_source = table.Column(type: "text", nullable: true), + poster_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + thumbnail_source = table.Column(type: "text", nullable: true), + thumbnail_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + logo_source = table.Column(type: "text", nullable: true), + logo_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + trailer = table.Column(type: "text", nullable: true), + external_id = table.Column(type: "json", nullable: false), + studio_id = table.Column(type: "uuid", nullable: true) + }, constraints: table => { table.PrimaryKey("pk_shows", x => x.id); @@ -270,12 +265,11 @@ namespace Kyoo.Postgresql.Migrations migrationBuilder.CreateTable( name: "link_collection_movie", - columns: table => - new - { - collection_id = table.Column(type: "uuid", nullable: false), - movie_id = table.Column(type: "uuid", nullable: false) - }, + columns: table => new + { + collection_id = table.Column(type: "uuid", nullable: false), + movie_id = table.Column(type: "uuid", nullable: false) + }, constraints: table => { table.PrimaryKey( @@ -301,12 +295,11 @@ namespace Kyoo.Postgresql.Migrations migrationBuilder.CreateTable( name: "link_collection_show", - columns: table => - new - { - collection_id = table.Column(type: "uuid", nullable: false), - show_id = table.Column(type: "uuid", nullable: false) - }, + columns: table => new + { + collection_id = table.Column(type: "uuid", nullable: false), + show_id = table.Column(type: "uuid", nullable: false) + }, constraints: table => { table.PrimaryKey( @@ -332,52 +325,51 @@ namespace Kyoo.Postgresql.Migrations migrationBuilder.CreateTable( name: "seasons", - columns: table => - new - { - id = table.Column(type: "uuid", nullable: false), - slug = table.Column( - type: "character varying(256)", - maxLength: 256, - nullable: false - ), - show_id = table.Column(type: "uuid", nullable: false), - season_number = table.Column(type: "integer", nullable: false), - name = table.Column(type: "text", nullable: true), - overview = table.Column(type: "text", nullable: true), - start_date = table.Column( - type: "timestamp with time zone", - nullable: true - ), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - end_date = table.Column( - type: "timestamp with time zone", - nullable: true - ), - poster_source = table.Column(type: "text", nullable: true), - poster_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - thumbnail_source = table.Column(type: "text", nullable: true), - thumbnail_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - logo_source = table.Column(type: "text", nullable: true), - logo_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - external_id = table.Column(type: "json", nullable: false) - }, + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + slug = table.Column( + type: "character varying(256)", + maxLength: 256, + nullable: false + ), + show_id = table.Column(type: "uuid", nullable: false), + season_number = table.Column(type: "integer", nullable: false), + name = table.Column(type: "text", nullable: true), + overview = table.Column(type: "text", nullable: true), + start_date = table.Column( + type: "timestamp with time zone", + nullable: true + ), + added_date = table.Column( + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now() at time zone 'utc'" + ), + end_date = table.Column( + type: "timestamp with time zone", + nullable: true + ), + poster_source = table.Column(type: "text", nullable: true), + poster_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + thumbnail_source = table.Column(type: "text", nullable: true), + thumbnail_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + logo_source = table.Column(type: "text", nullable: true), + logo_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + external_id = table.Column(type: "json", nullable: false) + }, constraints: table => { table.PrimaryKey("pk_seasons", x => x.id); @@ -393,53 +385,52 @@ namespace Kyoo.Postgresql.Migrations migrationBuilder.CreateTable( name: "episodes", - columns: table => - new - { - id = table.Column(type: "uuid", nullable: false), - slug = table.Column( - type: "character varying(256)", - maxLength: 256, - nullable: false - ), - show_id = table.Column(type: "uuid", nullable: false), - season_id = table.Column(type: "uuid", nullable: true), - season_number = table.Column(type: "integer", nullable: true), - episode_number = table.Column(type: "integer", nullable: true), - absolute_number = table.Column(type: "integer", nullable: true), - path = table.Column(type: "text", nullable: false), - name = table.Column(type: "text", nullable: true), - overview = table.Column(type: "text", nullable: true), - runtime = table.Column(type: "integer", nullable: false), - release_date = table.Column( - type: "timestamp with time zone", - nullable: true - ), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - poster_source = table.Column(type: "text", nullable: true), - poster_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - thumbnail_source = table.Column(type: "text", nullable: true), - thumbnail_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - logo_source = table.Column(type: "text", nullable: true), - logo_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - external_id = table.Column(type: "json", nullable: false) - }, + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + slug = table.Column( + type: "character varying(256)", + maxLength: 256, + nullable: false + ), + show_id = table.Column(type: "uuid", nullable: false), + season_id = table.Column(type: "uuid", nullable: true), + season_number = table.Column(type: "integer", nullable: true), + episode_number = table.Column(type: "integer", nullable: true), + absolute_number = table.Column(type: "integer", nullable: true), + path = table.Column(type: "text", nullable: false), + name = table.Column(type: "text", nullable: true), + overview = table.Column(type: "text", nullable: true), + runtime = table.Column(type: "integer", nullable: false), + release_date = table.Column( + type: "timestamp with time zone", + nullable: true + ), + added_date = table.Column( + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now() at time zone 'utc'" + ), + poster_source = table.Column(type: "text", nullable: true), + poster_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + thumbnail_source = table.Column(type: "text", nullable: true), + thumbnail_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + logo_source = table.Column(type: "text", nullable: true), + logo_blurhash = table.Column( + type: "character varying(32)", + maxLength: 32, + nullable: true + ), + external_id = table.Column(type: "json", nullable: false) + }, constraints: table => { table.PrimaryKey("pk_episodes", x => x.id); diff --git a/back/src/Kyoo.Postgresql/Migrations/20231204000849_Watchlist.cs b/back/src/Kyoo.Postgresql/Migrations/20231204000849_Watchlist.cs index ec932d59..85630e77 100644 --- a/back/src/Kyoo.Postgresql/Migrations/20231204000849_Watchlist.cs +++ b/back/src/Kyoo.Postgresql/Migrations/20231204000849_Watchlist.cs @@ -46,24 +46,23 @@ namespace Kyoo.Postgresql.Migrations migrationBuilder.CreateTable( name: "episode_watch_status", - columns: table => - new - { - user_id = table.Column(type: "uuid", nullable: false), - episode_id = table.Column(type: "uuid", nullable: false), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - played_date = table.Column( - type: "timestamp with time zone", - nullable: true - ), - status = table.Column(type: "watch_status", nullable: false), - watched_time = table.Column(type: "integer", nullable: true), - watched_percent = table.Column(type: "integer", nullable: true) - }, + columns: table => new + { + user_id = table.Column(type: "uuid", nullable: false), + episode_id = table.Column(type: "uuid", nullable: false), + added_date = table.Column( + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now() at time zone 'utc'" + ), + played_date = table.Column( + type: "timestamp with time zone", + nullable: true + ), + status = table.Column(type: "watch_status", nullable: false), + watched_time = table.Column(type: "integer", nullable: true), + watched_percent = table.Column(type: "integer", nullable: true) + }, constraints: table => { table.PrimaryKey( @@ -89,24 +88,23 @@ namespace Kyoo.Postgresql.Migrations migrationBuilder.CreateTable( name: "movie_watch_status", - columns: table => - new - { - user_id = table.Column(type: "uuid", nullable: false), - movie_id = table.Column(type: "uuid", nullable: false), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - played_date = table.Column( - type: "timestamp with time zone", - nullable: true - ), - status = table.Column(type: "watch_status", nullable: false), - watched_time = table.Column(type: "integer", nullable: true), - watched_percent = table.Column(type: "integer", nullable: true) - }, + columns: table => new + { + user_id = table.Column(type: "uuid", nullable: false), + movie_id = table.Column(type: "uuid", nullable: false), + added_date = table.Column( + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now() at time zone 'utc'" + ), + played_date = table.Column( + type: "timestamp with time zone", + nullable: true + ), + status = table.Column(type: "watch_status", nullable: false), + watched_time = table.Column(type: "integer", nullable: true), + watched_percent = table.Column(type: "integer", nullable: true) + }, constraints: table => { table.PrimaryKey("pk_movie_watch_status", x => new { x.user_id, x.movie_id }); @@ -129,26 +127,25 @@ namespace Kyoo.Postgresql.Migrations migrationBuilder.CreateTable( name: "show_watch_status", - columns: table => - new - { - user_id = table.Column(type: "uuid", nullable: false), - show_id = table.Column(type: "uuid", nullable: false), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - played_date = table.Column( - type: "timestamp with time zone", - nullable: true - ), - status = table.Column(type: "watch_status", nullable: false), - unseen_episodes_count = table.Column(type: "integer", nullable: false), - next_episode_id = table.Column(type: "uuid", nullable: true), - watched_time = table.Column(type: "integer", nullable: true), - watched_percent = table.Column(type: "integer", nullable: true) - }, + columns: table => new + { + user_id = table.Column(type: "uuid", nullable: false), + show_id = table.Column(type: "uuid", nullable: false), + added_date = table.Column( + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now() at time zone 'utc'" + ), + played_date = table.Column( + type: "timestamp with time zone", + nullable: true + ), + status = table.Column(type: "watch_status", nullable: false), + unseen_episodes_count = table.Column(type: "integer", nullable: false), + next_episode_id = table.Column(type: "uuid", nullable: true), + watched_time = table.Column(type: "integer", nullable: true), + watched_percent = table.Column(type: "integer", nullable: true) + }, constraints: table => { table.PrimaryKey("pk_show_watch_status", x => new { x.user_id, x.show_id }); diff --git a/back/src/Kyoo.Postgresql/PostgresContext.cs b/back/src/Kyoo.Postgresql/PostgresContext.cs index 895c7ff5..4a9e12f7 100644 --- a/back/src/Kyoo.Postgresql/PostgresContext.cs +++ b/back/src/Kyoo.Postgresql/PostgresContext.cs @@ -99,17 +99,14 @@ namespace Kyoo.Postgresql modelBuilder .HasDbFunction(typeof(DatabaseContext).GetMethod(nameof(MD5))!) - .HasTranslation( - args => - new SqlFunctionExpression( - "md5", - args, - nullable: true, - argumentsPropagateNullability: new[] { false }, - type: args[0].Type, - typeMapping: args[0].TypeMapping - ) - ); + .HasTranslation(args => new SqlFunctionExpression( + "md5", + args, + nullable: true, + argumentsPropagateNullability: new[] { false }, + type: args[0].Type, + typeMapping: args[0].TypeMapping + )); base.OnModelCreating(modelBuilder); } diff --git a/back/src/Kyoo.Swagger/ApiSorter.cs b/back/src/Kyoo.Swagger/ApiSorter.cs index c924c61f..0a02e43a 100644 --- a/back/src/Kyoo.Swagger/ApiSorter.cs +++ b/back/src/Kyoo.Swagger/ApiSorter.cs @@ -39,8 +39,7 @@ namespace Kyoo.Swagger { // We can't reorder items by assigning the sorted value to the Paths variable since it has no setter. List> sorted = postProcess - .Paths - .OrderBy(x => x.Key) + .Paths.OrderBy(x => x.Key) .ToList(); postProcess.Paths.Clear(); foreach ((string key, OpenApiPathItem value) in sorted) diff --git a/back/src/Kyoo.Swagger/ApiTagsFilter.cs b/back/src/Kyoo.Swagger/ApiTagsFilter.cs index 84c470c1..e272557e 100644 --- a/back/src/Kyoo.Swagger/ApiTagsFilter.cs +++ b/back/src/Kyoo.Swagger/ApiTagsFilter.cs @@ -21,10 +21,10 @@ using System.Linq; using System.Reflection; using Kyoo.Abstractions.Models.Attributes; using Kyoo.Swagger.Models; +using Namotion.Reflection; using NSwag; using NSwag.Generation.AspNetCore; using NSwag.Generation.Processors.Contexts; -using Namotion.Reflection; namespace Kyoo.Swagger { @@ -42,30 +42,25 @@ namespace Kyoo.Swagger /// This always return true since it should not remove operations. public static bool OperationFilter(OperationProcessorContext context) { - ApiDefinitionAttribute def = context - .ControllerType - .GetCustomAttribute(); + ApiDefinitionAttribute def = + context.ControllerType.GetCustomAttribute(); string name = def?.Name ?? context.ControllerType.Name; - ApiDefinitionAttribute methodOverride = context - .MethodInfo - .GetCustomAttribute(); + ApiDefinitionAttribute methodOverride = + context.MethodInfo.GetCustomAttribute(); if (methodOverride != null) name = methodOverride.Name; context.OperationDescription.Operation.Tags.Add(name); if (context.Document.Tags.All(x => x.Name != name)) { - context - .Document - .Tags - .Add( - new OpenApiTag - { - Name = name, - Description = context.ControllerType.GetXmlDocsSummary() - } - ); + context.Document.Tags.Add( + new OpenApiTag + { + Name = name, + Description = context.ControllerType.GetXmlDocsSummary() + } + ); } if (def?.Group == null) @@ -106,8 +101,7 @@ namespace Kyoo.Swagger { List tagGroups = (List)postProcess.ExtensionData["x-tagGroups"]; List tagsWithoutGroup = postProcess - .Tags - .Select(x => x.Name) + .Tags.Select(x => x.Name) .Where(x => tagGroups.SelectMany(y => y.Tags).All(y => y != x)) .ToList(); if (tagsWithoutGroup.Any()) diff --git a/back/src/Kyoo.Swagger/GenericResponseProvider.cs b/back/src/Kyoo.Swagger/GenericResponseProvider.cs index 6490da24..b9435a4c 100644 --- a/back/src/Kyoo.Swagger/GenericResponseProvider.cs +++ b/back/src/Kyoo.Swagger/GenericResponseProvider.cs @@ -49,8 +49,7 @@ namespace Kyoo.Swagger foreach (ActionModel action in context.Result.Controllers.SelectMany(x => x.Actions)) { IEnumerable responses = action - .Filters - .OfType() + .Filters.OfType() .Where(x => x.Type == typeof(ActionResult<>)); foreach (ProducesResponseTypeAttribute response in responses) { diff --git a/back/src/Kyoo.Swagger/Models/TagGroups.cs b/back/src/Kyoo.Swagger/Models/TagGroups.cs index 5ff7a0de..d04df266 100644 --- a/back/src/Kyoo.Swagger/Models/TagGroups.cs +++ b/back/src/Kyoo.Swagger/Models/TagGroups.cs @@ -17,8 +17,8 @@ // along with Kyoo. If not, see . using System.Collections.Generic; -using NSwag; using Newtonsoft.Json; +using NSwag; namespace Kyoo.Swagger.Models { diff --git a/back/src/Kyoo.Swagger/OperationPermissionProcessor.cs b/back/src/Kyoo.Swagger/OperationPermissionProcessor.cs index d5ff8c13..cb1a8cfc 100644 --- a/back/src/Kyoo.Swagger/OperationPermissionProcessor.cs +++ b/back/src/Kyoo.Swagger/OperationPermissionProcessor.cs @@ -39,8 +39,7 @@ namespace Kyoo.Swagger context.OperationDescription.Operation.Security ??= new List(); OpenApiSecurityRequirement perms = context - .MethodInfo - .GetCustomAttributes() + .MethodInfo.GetCustomAttributes() .Aggregate( new OpenApiSecurityRequirement(), (agg, _) => @@ -51,8 +50,7 @@ namespace Kyoo.Swagger ); perms = context - .MethodInfo - .GetCustomAttributes() + .MethodInfo.GetCustomAttributes() .Aggregate( perms, (agg, cur) => @@ -64,14 +62,12 @@ namespace Kyoo.Swagger } ); - PartialPermissionAttribute controller = context - .ControllerType - .GetCustomAttribute(); + PartialPermissionAttribute controller = + context.ControllerType.GetCustomAttribute(); if (controller != null) { perms = context - .MethodInfo - .GetCustomAttributes() + .MethodInfo.GetCustomAttributes() .Aggregate( perms, (agg, cur) => diff --git a/back/src/Kyoo.Swagger/SwaggerModule.cs b/back/src/Kyoo.Swagger/SwaggerModule.cs index b11c8d6c..cc4033fc 100644 --- a/back/src/Kyoo.Swagger/SwaggerModule.cs +++ b/back/src/Kyoo.Swagger/SwaggerModule.cs @@ -82,20 +82,16 @@ namespace Kyoo.Swagger != AlternativeRoute; return true; }); - document - .SchemaGenerator - .Settings - .TypeMappers - .Add( - new PrimitiveTypeMapper( - typeof(Identifier), - x => - { - x.IsNullableRaw = false; - x.Type = JsonObjectType.String | JsonObjectType.Integer; - } - ) - ); + document.SchemaGenerator.Settings.TypeMappers.Add( + new PrimitiveTypeMapper( + typeof(Identifier), + x => + { + x.IsNullableRaw = false; + x.Type = JsonObjectType.String | JsonObjectType.Integer; + } + ) + ); document.AddSecurity( nameof(Kyoo), diff --git a/back/tests/Kyoo.Tests/Database/SpecificTests/EpisodeTests.cs b/back/tests/Kyoo.Tests/Database/SpecificTests/EpisodeTests.cs index c7f453f0..b25efbd7 100644 --- a/back/tests/Kyoo.Tests/Database/SpecificTests/EpisodeTests.cs +++ b/back/tests/Kyoo.Tests/Database/SpecificTests/EpisodeTests.cs @@ -54,17 +54,14 @@ namespace Kyoo.Tests.Database { Episode episode = await _repository.Get(1.AsGuid()); Assert.Equal($"{TestSample.Get().Slug}-s1e1", episode.Slug); - await Repositories - .LibraryManager - .Shows - .Patch( - episode.ShowId, - (x) => - { - x.Slug = "new-slug"; - return x; - } - ); + await Repositories.LibraryManager.Shows.Patch( + episode.ShowId, + (x) => + { + x.Slug = "new-slug"; + return x; + } + ); episode = await _repository.Get(1.AsGuid()); Assert.Equal("new-slug-s1e1", episode.Slug); } @@ -92,17 +89,14 @@ namespace Kyoo.Tests.Database { Episode episode = await _repository.Get(1.AsGuid()); Assert.Equal($"{TestSample.Get().Slug}-s1e1", episode.Slug); - episode = await Repositories - .LibraryManager - .Episodes - .Patch( - episode.Id, - (x) => - { - x.EpisodeNumber = 2; - return x; - } - ); + episode = await Repositories.LibraryManager.Episodes.Patch( + episode.Id, + (x) => + { + x.EpisodeNumber = 2; + return x; + } + ); Assert.Equal($"{TestSample.Get().Slug}-s1e2", episode.Slug); episode = await _repository.Get(1.AsGuid()); Assert.Equal($"{TestSample.Get().Slug}-s1e2", episode.Slug); @@ -143,17 +137,14 @@ namespace Kyoo.Tests.Database public async Task SlugEditAbsoluteTest() { Episode episode = await _repository.Create(TestSample.GetAbsoluteEpisode()); - await Repositories - .LibraryManager - .Shows - .Patch( - episode.ShowId, - (x) => - { - x.Slug = "new-slug"; - return x; - } - ); + await Repositories.LibraryManager.Shows.Patch( + episode.ShowId, + (x) => + { + x.Slug = "new-slug"; + return x; + } + ); episode = await _repository.Get(2.AsGuid()); Assert.Equal($"new-slug-3", episode.Slug); } diff --git a/back/tests/Kyoo.Tests/Database/SpecificTests/SeasonTests.cs b/back/tests/Kyoo.Tests/Database/SpecificTests/SeasonTests.cs index e1cb8749..68d672cf 100644 --- a/back/tests/Kyoo.Tests/Database/SpecificTests/SeasonTests.cs +++ b/back/tests/Kyoo.Tests/Database/SpecificTests/SeasonTests.cs @@ -53,17 +53,14 @@ namespace Kyoo.Tests.Database { Season season = await _repository.Get(1.AsGuid()); Assert.Equal("anohana-s1", season.Slug); - await Repositories - .LibraryManager - .Shows - .Patch( - season.ShowId, - (x) => - { - x.Slug = "new-slug"; - return x; - } - ); + await Repositories.LibraryManager.Shows.Patch( + season.ShowId, + (x) => + { + x.Slug = "new-slug"; + return x; + } + ); season = await _repository.Get(1.AsGuid()); Assert.Equal("new-slug-s1", season.Slug); } diff --git a/shell.nix b/shell.nix index 8b55b502..f9358ebd 100644 --- a/shell.nix +++ b/shell.nix @@ -10,6 +10,7 @@ combinePackages [ sdk_7_0 aspnetcore_7_0 + runtime_6_0 ]; in pkgs.mkShell {