diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IThumbnails.cs b/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IThumbnails.cs index e3454fc1..72a6d455 100644 --- a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IThumbnails.cs +++ b/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IThumbnails.cs @@ -20,6 +20,7 @@ using System; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Globalization; +using Kyoo.Abstractions.Models.Attributes; namespace Kyoo.Abstractions.Models { @@ -59,6 +60,24 @@ namespace Kyoo.Abstractions.Models [MaxLength(32)] public string Blurhash { get; set; } + [SerializeIgnore] + public string Path { private get; set; } + + /// + /// The url to retrieve the low quality image. + /// + public string Low => $"{Path}?quality=low"; + + /// + /// The url to retrieve the medium quality image. + /// + public string Medium => $"{Path}?quality=medium"; + + /// + /// The url to retrieve the high quality image. + /// + public string High => $"{Path}?quality=high"; + public Image(string source, string? blurhash = null) { Source = source; diff --git a/back/src/Kyoo.Core/Controllers/Repositories/CollectionRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/CollectionRepository.cs index f5d19bc3..ca90cec7 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/CollectionRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/CollectionRepository.cs @@ -53,11 +53,12 @@ namespace Kyoo.Core.Controllers /// public override async Task> Search(string query) { - return await Sort( + return (await Sort( _database.Collections .Where(_database.Like(x => x.Name + " " + x.Slug, $"%{query}%")) .Take(20) - ).ToListAsync(); + ).ToListAsync()) + .Select(SetBackingImageSelf).ToList(); } /// diff --git a/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs index 5fba8395..19c5b839 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs @@ -79,7 +79,7 @@ namespace Kyoo.Core.Controllers { return _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == showID && x.SeasonNumber == seasonNumber - && x.EpisodeNumber == episodeNumber); + && x.EpisodeNumber == episodeNumber).Then(SetBackingImage); } /// @@ -87,7 +87,7 @@ namespace Kyoo.Core.Controllers { return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug && x.SeasonNumber == seasonNumber - && x.EpisodeNumber == episodeNumber); + && x.EpisodeNumber == episodeNumber).Then(SetBackingImage); } /// @@ -112,14 +112,14 @@ namespace Kyoo.Core.Controllers public Task GetAbsolute(int showID, int absoluteNumber) { return _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == showID - && x.AbsoluteNumber == absoluteNumber); + && x.AbsoluteNumber == absoluteNumber).Then(SetBackingImage); } /// public Task GetAbsolute(string showSlug, int absoluteNumber) { return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug - && x.AbsoluteNumber == absoluteNumber); + && x.AbsoluteNumber == absoluteNumber).Then(SetBackingImage); } /// @@ -134,7 +134,10 @@ namespace Kyoo.Core.Controllers .Take(20) .ToListAsync(); foreach (Episode ep in ret) + { ep.Show.Episodes = null; + SetBackingImage(ep); + } return ret; } diff --git a/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs index 0a6350f8..87a28f24 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs @@ -24,6 +24,7 @@ using System.Threading.Tasks; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; using Kyoo.Postgresql; +using Kyoo.Utils; using Microsoft.EntityFrameworkCore; namespace Kyoo.Core.Controllers @@ -54,13 +55,19 @@ namespace Kyoo.Core.Controllers /// public override async Task GetOrDefault(int id) { - return await _database.LibraryItems.FirstOrDefaultAsync(x => x.Id == id); + return await _database.LibraryItems.SingleOrDefaultAsync(x => x.Id == id).Then(SetBackingImage); } /// public override async Task GetOrDefault(string slug) { - return await _database.LibraryItems.SingleOrDefaultAsync(x => x.Slug == slug); + return await _database.LibraryItems.SingleOrDefaultAsync(x => x.Slug == slug).Then(SetBackingImage); + } + + /// + public override async Task GetOrDefault(Expression> where, Sort sortBy = default) + { + return await Sort(_database.LibraryItems, sortBy).FirstOrDefaultAsync(where).Then(SetBackingImage); } /// @@ -68,7 +75,8 @@ namespace Kyoo.Core.Controllers Sort sort = default, Pagination limit = default) { - return await ApplyFilters(_database.LibraryItems, where, sort, limit); + return (await ApplyFilters(_database.LibraryItems, where, sort, limit)) + .Select(SetBackingImageSelf).ToList(); } /// @@ -83,12 +91,14 @@ namespace Kyoo.Core.Controllers /// public override async Task> Search(string query) { - return await Sort( + return (await Sort( _database.LibraryItems .Where(_database.Like(x => x.Name, $"%{query}%")) ) .Take(20) - .ToListAsync(); + .ToListAsync()) + .Select(SetBackingImageSelf) + .ToList(); } /// @@ -100,7 +110,7 @@ namespace Kyoo.Core.Controllers => throw new InvalidOperationException(); /// - public override Task Edit(ILibraryItem obj) + public override Task Edit(ILibraryItem edited) => throw new InvalidOperationException(); /// diff --git a/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs index 5c3340d8..a079abc3 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs @@ -30,7 +30,6 @@ using Kyoo.Abstractions.Models.Exceptions; using Kyoo.Core.Api; using Kyoo.Utils; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; namespace Kyoo.Core.Controllers { @@ -227,6 +226,28 @@ namespace Kyoo.Core.Controllers return Expression.Lambda>(filter!, x); } + protected void SetBackingImage(T obj) + { + if (obj is not IThumbnails thumbs) + return; + string type = obj is ILibraryItem item + ? item.Kind.ToString().ToLowerInvariant() + : typeof(T).Name.ToLowerInvariant(); + + if (thumbs.Poster != null) + thumbs.Poster.Path = $"{type}/{obj.Slug}/poster"; + if (thumbs.Thumbnail != null) + thumbs.Thumbnail.Path = $"{type}/{obj.Slug}/thumbnail"; + if (thumbs.Logo != null) + thumbs.Logo.Path = $"{type}/{obj.Slug}/logo"; + } + + protected T SetBackingImageSelf(T obj) + { + SetBackingImage(obj); + return obj; + } + /// /// Get a resource from it's ID and make the instance track it. /// @@ -236,6 +257,7 @@ namespace Kyoo.Core.Controllers protected virtual async Task GetWithTracking(int id) { T ret = await Database.Set().AsTracking().FirstOrDefaultAsync(x => x.Id == id); + SetBackingImage(ret); if (ret == null) throw new ItemNotFoundException($"No {typeof(T).Name} found with the id {id}"); return ret; @@ -271,30 +293,31 @@ namespace Kyoo.Core.Controllers /// public virtual Task GetOrDefault(int id) { - return Database.Set().FirstOrDefaultAsync(x => x.Id == id); + return Database.Set().FirstOrDefaultAsync(x => x.Id == id).Then(SetBackingImage); } /// public virtual Task GetOrDefault(string slug) { - return Database.Set().FirstOrDefaultAsync(x => x.Slug == slug); + return Database.Set().FirstOrDefaultAsync(x => x.Slug == slug).Then(SetBackingImage); } /// public virtual Task GetOrDefault(Expression> where, Sort sortBy = default) { - return Sort(Database.Set(), sortBy).FirstOrDefaultAsync(where); + return Sort(Database.Set(), sortBy).FirstOrDefaultAsync(where).Then(SetBackingImage); } /// public abstract Task> Search(string query); /// - public virtual Task> GetAll(Expression> where = null, + public virtual async Task> GetAll(Expression> where = null, Sort sort = default, Pagination limit = default) { - return ApplyFilters(Database.Set(), where, sort, limit); + return (await ApplyFilters(Database.Set(), where, sort, limit)) + .Select(SetBackingImageSelf).ToList(); } /// @@ -349,6 +372,7 @@ namespace Kyoo.Core.Controllers if (thumbs.Logo != null) Database.Entry(thumbs).Reference(x => x.Logo).TargetEntry.State = EntityState.Added; } + SetBackingImage(obj); return obj; } @@ -400,6 +424,7 @@ namespace Kyoo.Core.Controllers await EditRelations(old, edited); await Database.SaveChangesAsync(); OnEdited?.Invoke(old); + SetBackingImage(old); return old; } finally @@ -423,6 +448,7 @@ namespace Kyoo.Core.Controllers await Database.SaveChangesAsync(); OnEdited?.Invoke(resource); + SetBackingImage(resource); return resource; } finally diff --git a/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs index cc5f89eb..49f315bb 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs @@ -69,12 +69,14 @@ namespace Kyoo.Core.Controllers public override async Task> Search(string query) { query = $"%{query}%"; - return await Sort( + return (await Sort( _database.Movies .Where(_database.Like(x => x.Name + " " + x.Slug, query)) ) .Take(20) - .ToListAsync(); + .ToListAsync()) + .Select(SetBackingImageSelf) + .ToList(); } /// diff --git a/back/src/Kyoo.Core/Controllers/Repositories/PeopleRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/PeopleRepository.cs index a01b2c4f..c7cdcbda 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/PeopleRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/PeopleRepository.cs @@ -63,12 +63,14 @@ namespace Kyoo.Core.Controllers /// public override async Task> Search(string query) { - return await Sort( + return (await Sort( _database.People .Where(_database.Like(x => x.Name, $"%{query}%")) ) .Take(20) - .ToListAsync(); + .ToListAsync()) + .Select(SetBackingImageSelf) + .ToList(); } /// diff --git a/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs index d9d41d2c..e7aed032 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs @@ -24,6 +24,7 @@ using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models.Exceptions; using Kyoo.Postgresql; +using Kyoo.Utils; using Microsoft.EntityFrameworkCore; namespace Kyoo.Core.Controllers @@ -87,25 +88,27 @@ namespace Kyoo.Core.Controllers public Task GetOrDefault(int showID, int seasonNumber) { return _database.Seasons.FirstOrDefaultAsync(x => x.ShowId == showID - && x.SeasonNumber == seasonNumber); + && x.SeasonNumber == seasonNumber).Then(SetBackingImage); } /// public Task GetOrDefault(string showSlug, int seasonNumber) { return _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug - && x.SeasonNumber == seasonNumber); + && x.SeasonNumber == seasonNumber).Then(SetBackingImage); } /// public override async Task> Search(string query) { - return await Sort( + return (await Sort( _database.Seasons .Where(_database.Like(x => x.Name, $"%{query}%")) ) .Take(20) - .ToListAsync(); + .ToListAsync()) + .Select(SetBackingImageSelf) + .ToList(); } /// diff --git a/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs index 0a829e0f..065202ee 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs @@ -70,12 +70,14 @@ namespace Kyoo.Core.Controllers public override async Task> Search(string query) { query = $"%{query}%"; - return await Sort( + return (await Sort( _database.Shows .Where(_database.Like(x => x.Name + " " + x.Slug, query)) ) .Take(20) - .ToListAsync(); + .ToListAsync()) + .Select(SetBackingImageSelf) + .ToList(); } /// diff --git a/back/src/Kyoo.Core/Controllers/Repositories/StudioRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/StudioRepository.cs index 536accba..22201c74 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/StudioRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/StudioRepository.cs @@ -16,7 +16,6 @@ // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -54,12 +53,14 @@ namespace Kyoo.Core.Controllers /// public override async Task> Search(string query) { - return await Sort( + return (await Sort( _database.Studios .Where(_database.Like(x => x.Name, $"%{query}%")) ) .Take(20) - .ToListAsync(); + .ToListAsync()) + .Select(SetBackingImageSelf) + .ToList(); } /// diff --git a/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs index c91c8a49..2fcecb8a 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs @@ -16,7 +16,6 @@ // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -53,12 +52,14 @@ namespace Kyoo.Core.Controllers /// public override async Task> Search(string query) { - return await Sort( + return (await Sort( _database.Users .Where(_database.Like(x => x.Username, $"%{query}%")) ) .Take(20) - .ToListAsync(); + .ToListAsync()) + .Select(SetBackingImageSelf) + .ToList(); } ///