mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Feat rework images, delete providers
This commit is contained in:
parent
e075306363
commit
386c6bf268
@ -85,11 +85,6 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// </summary>
|
||||
IGenreRepository GenreRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle providers.
|
||||
/// </summary>
|
||||
IProviderRepository ProviderRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle users.
|
||||
/// </summary>
|
||||
|
@ -446,25 +446,6 @@ namespace Kyoo.Abstractions.Controllers
|
||||
Pagination limit = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A repository to handle providers.
|
||||
/// </summary>
|
||||
public interface IProviderRepository : IRepository<Provider>
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a list of external ids that match all filters
|
||||
/// </summary>
|
||||
/// <param name="where">A predicate to add arbitrary filter</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">Pagination information (where to start and how many to get)</param>
|
||||
/// <typeparam name="T">The type of metadata to retrieve</typeparam>
|
||||
/// <returns>A filtered list of external ids.</returns>
|
||||
Task<ICollection<MetadataID>> GetMetadataID<T>(Expression<Func<MetadataID, bool>> where = null,
|
||||
Sort<MetadataID> sort = default,
|
||||
Pagination limit = default)
|
||||
where T : class, IMetadata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A repository to handle users.
|
||||
/// </summary>
|
||||
|
@ -35,22 +35,20 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <param name="item">
|
||||
/// The item to cache images.
|
||||
/// </param>
|
||||
/// <param name="alwaysDownload">
|
||||
/// <c>true</c> if images should be downloaded even if they already exists locally, <c>false</c> otherwise.
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type of the item</typeparam>
|
||||
/// <returns><c>true</c> if an image has been downloaded, <c>false</c> otherwise.</returns>
|
||||
Task<bool> DownloadImages<T>(T item, bool alwaysDownload = false)
|
||||
Task DownloadImages<T>(T item)
|
||||
where T : IThumbnails;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the local path of an image of the given item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to retrieve the poster from.</param>
|
||||
/// <param name="imageId">The ID of the image. See <see cref="Images"/> for values.</param>
|
||||
/// <param name="image">The ID of the image.</param>
|
||||
/// <param name="quality">The quality of the image</param>
|
||||
/// <typeparam name="T">The type of the item</typeparam>
|
||||
/// <returns>The path of the image for the given resource or null if it does not exists.</returns>
|
||||
string? GetImagePath<T>(T item, int imageId)
|
||||
string GetImagePath<T>(T item, string image, ImageQuality quality)
|
||||
where T : IThumbnails;
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,13 @@ namespace Kyoo.Abstractions.Models
|
||||
public DateTime? EndAir { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
public Image Poster { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image Logo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of this item (ether a collection, a show or a movie).
|
||||
@ -110,7 +116,9 @@ namespace Kyoo.Abstractions.Models
|
||||
Status = show.Status;
|
||||
StartAir = show.StartAir;
|
||||
EndAir = show.EndAir;
|
||||
Images = show.Images;
|
||||
Poster = show.Poster;
|
||||
Thumbnail = show.Thumbnail;
|
||||
Logo = show.Logo;
|
||||
Type = show.IsMovie ? ItemType.Movie : ItemType.Show;
|
||||
}
|
||||
|
||||
@ -127,7 +135,9 @@ namespace Kyoo.Abstractions.Models
|
||||
Status = Models.Status.Unknown;
|
||||
StartAir = null;
|
||||
EndAir = null;
|
||||
Images = collection.Images;
|
||||
Poster = collection.Poster;
|
||||
Thumbnail = collection.Thumbnail;
|
||||
Logo = collection.Logo;
|
||||
Type = ItemType.Collection;
|
||||
}
|
||||
|
||||
@ -143,7 +153,9 @@ namespace Kyoo.Abstractions.Models
|
||||
Status = x.Status,
|
||||
StartAir = x.StartAir,
|
||||
EndAir = x.EndAir,
|
||||
Images = x.Images,
|
||||
Poster = x.Poster,
|
||||
Thumbnail = x.Thumbnail,
|
||||
Logo = x.Logo,
|
||||
Type = x.IsMovie ? ItemType.Movie : ItemType.Show
|
||||
};
|
||||
|
||||
@ -159,7 +171,9 @@ namespace Kyoo.Abstractions.Models
|
||||
Status = Models.Status.Unknown,
|
||||
StartAir = null,
|
||||
EndAir = null,
|
||||
Images = x.Images,
|
||||
Poster = x.Poster,
|
||||
Thumbnail = x.Thumbnail,
|
||||
Logo = x.Logo,
|
||||
Type = ItemType.Collection
|
||||
};
|
||||
|
||||
|
@ -14,11 +14,6 @@
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
@ -27,29 +22,6 @@ namespace Kyoo.Abstractions.Models
|
||||
/// </summary>
|
||||
public class MetadataID
|
||||
{
|
||||
/// <summary>
|
||||
/// The expression to retrieve the unique ID of a MetadataID. This is an aggregate of the two resources IDs.
|
||||
/// </summary>
|
||||
public static Expression<Func<MetadataID, object>> PrimaryKey
|
||||
{
|
||||
get { return x => new { First = x.ResourceID, Second = x.ProviderID }; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the resource which possess the metadata.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int ResourceID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the provider.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int ProviderID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The provider that can do something with this ID.
|
||||
/// </summary>
|
||||
public Provider Provider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the resource on the external provider.
|
||||
/// </summary>
|
||||
|
@ -39,7 +39,13 @@ namespace Kyoo.Abstractions.Models
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
public Image Poster { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image Logo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The description of this collection.
|
||||
@ -57,6 +63,6 @@ namespace Kyoo.Abstractions.Models
|
||||
[LoadableRelation] public ICollection<Library> Libraries { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[EditableRelation][LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
public Dictionary<string, MetadataID> ExternalId { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -127,9 +127,6 @@ namespace Kyoo.Abstractions.Models
|
||||
/// </summary>
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The title of this episode.
|
||||
/// </summary>
|
||||
@ -146,7 +143,16 @@ namespace Kyoo.Abstractions.Models
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[EditableRelation][LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
public Image Poster { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image Logo { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, MetadataID> ExternalId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the slug of an episode.
|
||||
|
@ -16,11 +16,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
@ -32,67 +28,6 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// The link to metadata providers that this show has. See <see cref="MetadataID"/> for more information.
|
||||
/// </summary>
|
||||
[EditableRelation]
|
||||
[LoadableRelation]
|
||||
public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A static class containing extensions method for every <see cref="IMetadata"/> class.
|
||||
/// This allow one to use metadata more easily.
|
||||
/// </summary>
|
||||
public static class MetadataExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieve the internal provider's ID of an item using it's provider slug.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method will never return anything if the <see cref="IMetadata.ExternalIDs"/> are not loaded.
|
||||
/// </remarks>
|
||||
/// <param name="self">An instance of <see cref="IMetadata"/> to retrieve the ID from.</param>
|
||||
/// <param name="provider">The slug of the provider</param>
|
||||
/// <returns>The <see cref="MetadataID.DataID"/> field of the asked provider.</returns>
|
||||
[CanBeNull]
|
||||
public static string GetID(this IMetadata self, string provider)
|
||||
{
|
||||
return self.ExternalIDs?.FirstOrDefault(x => x.Provider.Slug == provider)?.DataID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the internal provider's ID of an item using it's provider slug.
|
||||
/// If the ID could be found, it is converted to the <typeparamref name="T"/> type and <c>true</c> is returned.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method will never succeed if the <see cref="IMetadata.ExternalIDs"/> are not loaded.
|
||||
/// </remarks>
|
||||
/// <param name="self">An instance of <see cref="IMetadata"/> to retrieve the ID from.</param>
|
||||
/// <param name="provider">The slug of the provider</param>
|
||||
/// <param name="id">
|
||||
/// The <see cref="MetadataID.DataID"/> field of the asked provider parsed
|
||||
/// and converted to the <typeparamref name="T"/> type.
|
||||
/// It is only relevant if this method returns <c>true</c>.
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type to convert the <see cref="MetadataID.DataID"/> to.</typeparam>
|
||||
/// <returns><c>true</c> if this method succeeded, <c>false</c> otherwise.</returns>
|
||||
public static bool TryGetID<T>(this IMetadata self, string provider, out T id)
|
||||
{
|
||||
string dataID = self.ExternalIDs?.FirstOrDefault(x => x.Provider.Slug == provider)?.DataID;
|
||||
if (dataID == null)
|
||||
{
|
||||
id = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
id = (T)Convert.ChangeType(dataID, typeof(T));
|
||||
}
|
||||
catch
|
||||
{
|
||||
id = default;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public Dictionary<string, MetadataID> ExternalId { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
@ -25,53 +24,56 @@ namespace Kyoo.Abstractions.Models
|
||||
/// An interface representing items that contains images (like posters, thumbnails, logo, banners...)
|
||||
/// </summary>
|
||||
public interface IThumbnails
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of images mapped to a certain index.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An arbitrary index should not be used, instead use indexes from <see cref="Models.Images"/>
|
||||
/// </remarks>
|
||||
/// <example>{"0": "example.com/dune/poster"}</example>
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class containing constant values for images. To be used as index of a <see cref="IThumbnails.Images"/>.
|
||||
/// </summary>
|
||||
public static class Images
|
||||
{
|
||||
/// <summary>
|
||||
/// A poster is a 9/16 format image with the cover of the resource.
|
||||
/// </summary>
|
||||
public const int Poster = 0;
|
||||
public Image Poster { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A thumbnail is a 16/9 format image, it could ether be used as a background or as a preview but it usually
|
||||
/// is not an official image.
|
||||
/// </summary>
|
||||
public const int Thumbnail = 1;
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A logo is a small image representing the resource.
|
||||
/// </summary>
|
||||
public const int Logo = 2;
|
||||
public Image Logo { get; set; }
|
||||
}
|
||||
|
||||
public class Image
|
||||
{
|
||||
/// <summary>
|
||||
/// The original image from another server.
|
||||
/// </summary>
|
||||
public string Source { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A video of a few minutes that tease the content.
|
||||
/// A hash to display as placeholder while the image is loading.
|
||||
/// </summary>
|
||||
public const int Trailer = 3;
|
||||
[MaxLength(32)]
|
||||
public string Blurhash { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The quality of an image
|
||||
/// </summary>
|
||||
public enum ImageQuality
|
||||
{
|
||||
/// <summary>
|
||||
/// Small
|
||||
/// </summary>
|
||||
Small,
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the name of an image using it's ID. It is also used by the serializer to retrieve all named images.
|
||||
/// If a plugin adds a new image type, it should add it's value and name here to allow the serializer to add it.
|
||||
/// Medium
|
||||
/// </summary>
|
||||
public static Dictionary<int, string> ImageName { get; } = new()
|
||||
{
|
||||
[Poster] = nameof(Poster),
|
||||
[Thumbnail] = nameof(Thumbnail),
|
||||
[Logo] = nameof(Logo),
|
||||
[Trailer] = nameof(Trailer)
|
||||
};
|
||||
Medium,
|
||||
|
||||
/// <summary>
|
||||
/// Large
|
||||
/// </summary>
|
||||
Large,
|
||||
}
|
||||
}
|
||||
|
@ -42,11 +42,6 @@ namespace Kyoo.Abstractions.Models
|
||||
/// </summary>
|
||||
public string[] Paths { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of <see cref="Provider"/> used for items in this library.
|
||||
/// </summary>
|
||||
[EditableRelation][LoadableRelation] public ICollection<Provider> Providers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of shows in this library.
|
||||
/// </summary>
|
||||
|
@ -38,10 +38,16 @@ namespace Kyoo.Abstractions.Models
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
public Image Poster { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[EditableRelation][LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image Logo { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, MetadataID> ExternalId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of roles this person has played in. See <see cref="PeopleRole"/> for more information.
|
||||
|
@ -1,71 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Utils;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A dead class that will be removed later.
|
||||
/// </summary>
|
||||
// TODO: Delete this class
|
||||
public class Provider : IResource, IThumbnails
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of this provider.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of libraries that uses this provider.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Library> Libraries { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, default, <see cref="Provider"/>
|
||||
/// </summary>
|
||||
public Provider() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Provider"/> and specify it's <see cref="Name"/>.
|
||||
/// The <see cref="Slug"/> is automatically calculated from it's name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of this provider.</param>
|
||||
/// <param name="logo">The logo of this provider.</param>
|
||||
public Provider(string name, string logo)
|
||||
{
|
||||
Slug = Utility.ToSlug(name);
|
||||
Name = name;
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Models.Images.Logo] = logo
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -99,10 +99,16 @@ namespace Kyoo.Abstractions.Models
|
||||
public DateTime? EndDate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
public Image Poster { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[EditableRelation][LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image Logo { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, MetadataID> ExternalId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of episodes that this season contains.
|
||||
|
@ -59,13 +59,6 @@ namespace Kyoo.Abstractions.Models
|
||||
/// </summary>
|
||||
public Status Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An URL to a trailer.
|
||||
/// </summary>
|
||||
/// TODO for now, this is set to a youtube url. It should be cached and converted to a local file.
|
||||
[Obsolete("Use Images instead of this, this is only kept for the API response.")]
|
||||
public string TrailerUrl => Images?.GetValueOrDefault(Models.Images.Trailer);
|
||||
|
||||
/// <summary>
|
||||
/// The date this show started airing. It can be null if this is unknown.
|
||||
/// </summary>
|
||||
@ -78,16 +71,27 @@ namespace Kyoo.Abstractions.Models
|
||||
/// </summary>
|
||||
public DateTime? EndAir { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if this show represent a movie, false otherwise.
|
||||
/// </summary>
|
||||
public bool IsMovie { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[EditableRelation][LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
public Image Poster { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image Logo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A video of a few minutes that tease the content.
|
||||
/// </summary>
|
||||
public string Trailer { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, MetadataID> ExternalId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the Studio that made this show.
|
||||
|
@ -44,7 +44,7 @@ namespace Kyoo.Abstractions.Models
|
||||
[LoadableRelation] public ICollection<Show> Shows { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[EditableRelation][LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
public Dictionary<string, MetadataID> ExternalId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, empty, <see cref="Studio"/>.
|
||||
|
@ -24,7 +24,7 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// A single user of the app.
|
||||
/// </summary>
|
||||
public class User : IResource, IThumbnails
|
||||
public class User : IResource
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
@ -59,8 +59,10 @@ namespace Kyoo.Abstractions.Models
|
||||
[SerializeIgnore]
|
||||
public Dictionary<string, string> ExtraData { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
/// <summary>
|
||||
/// A logo is a small image representing the resource.
|
||||
/// </summary>
|
||||
public Image Logo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of shows the user has finished.
|
||||
|
@ -103,7 +103,13 @@ namespace Kyoo.Abstractions.Models
|
||||
public bool IsMovie { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
public Image Poster { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image Logo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The transcoder's info for this item. This include subtitles, fonts, chapters...
|
||||
@ -145,7 +151,9 @@ namespace Kyoo.Abstractions.Models
|
||||
Title = ep.Title,
|
||||
Overview = ep.Overview,
|
||||
ReleaseDate = ep.ReleaseDate,
|
||||
Images = ep.Show.Images,
|
||||
Poster = ep.Poster,
|
||||
Thumbnail = ep.Thumbnail,
|
||||
Logo = ep.Logo,
|
||||
PreviousEpisode = ep.Show.IsMovie
|
||||
? null
|
||||
: (await library.GetAll<Episode>(
|
||||
|
@ -66,9 +66,6 @@ namespace Kyoo.Core.Controllers
|
||||
/// <inheritdoc />
|
||||
public IGenreRepository GenreRepository { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IProviderRepository ProviderRepository { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IUserRepository UserRepository { get; }
|
||||
|
||||
@ -89,7 +86,6 @@ namespace Kyoo.Core.Controllers
|
||||
PeopleRepository = GetRepository<People>() as IPeopleRepository;
|
||||
StudioRepository = GetRepository<Studio>() as IStudioRepository;
|
||||
GenreRepository = GetRepository<Genre>() as IGenreRepository;
|
||||
ProviderRepository = GetRepository<Provider>() as IProviderRepository;
|
||||
UserRepository = GetRepository<User>() as IUserRepository;
|
||||
}
|
||||
|
||||
@ -259,10 +255,6 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
return (obj, member: memberName) switch
|
||||
{
|
||||
(Library l, nameof(Library.Providers)) => ProviderRepository
|
||||
.GetAll(x => x.Libraries.Any(y => y.ID == obj.ID))
|
||||
.Then(x => l.Providers = x),
|
||||
|
||||
(Library l, nameof(Library.Shows)) => ShowRepository
|
||||
.GetAll(x => x.Libraries.Any(y => y.ID == obj.ID))
|
||||
.Then(x => l.Shows = x),
|
||||
@ -272,11 +264,6 @@ namespace Kyoo.Core.Controllers
|
||||
.Then(x => l.Collections = x),
|
||||
|
||||
|
||||
(Collection c, nameof(Collection.ExternalIDs)) => _SetRelation(c,
|
||||
ProviderRepository.GetMetadataID<Collection>(x => x.ResourceID == obj.ID),
|
||||
(x, y) => x.ExternalIDs = y,
|
||||
(x, y) => { x.ResourceID = y.ID; }),
|
||||
|
||||
(Collection c, nameof(Collection.Shows)) => ShowRepository
|
||||
.GetAll(x => x.Collections.Any(y => y.ID == obj.ID))
|
||||
.Then(x => c.Shows = x),
|
||||
@ -286,11 +273,6 @@ namespace Kyoo.Core.Controllers
|
||||
.Then(x => c.Libraries = x),
|
||||
|
||||
|
||||
(Show s, nameof(Show.ExternalIDs)) => _SetRelation(s,
|
||||
ProviderRepository.GetMetadataID<Show>(x => x.ResourceID == obj.ID),
|
||||
(x, y) => x.ExternalIDs = y,
|
||||
(x, y) => { x.ResourceID = y.ID; }),
|
||||
|
||||
(Show s, nameof(Show.Genres)) => GenreRepository
|
||||
.GetAll(x => x.Shows.Any(y => y.ID == obj.ID))
|
||||
.Then(x => s.Genres = x),
|
||||
@ -326,11 +308,6 @@ namespace Kyoo.Core.Controllers
|
||||
}),
|
||||
|
||||
|
||||
(Season s, nameof(Season.ExternalIDs)) => _SetRelation(s,
|
||||
ProviderRepository.GetMetadataID<Season>(x => x.ResourceID == obj.ID),
|
||||
(x, y) => x.ExternalIDs = y,
|
||||
(x, y) => { x.ResourceID = y.ID; }),
|
||||
|
||||
(Season s, nameof(Season.Episodes)) => _SetRelation(s,
|
||||
EpisodeRepository.GetAll(x => x.Season.ID == obj.ID),
|
||||
(x, y) => x.Episodes = y,
|
||||
@ -345,11 +322,6 @@ namespace Kyoo.Core.Controllers
|
||||
}),
|
||||
|
||||
|
||||
(Episode e, nameof(Episode.ExternalIDs)) => _SetRelation(e,
|
||||
ProviderRepository.GetMetadataID<Episode>(x => x.ResourceID == obj.ID),
|
||||
(x, y) => x.ExternalIDs = y,
|
||||
(x, y) => { x.ResourceID = y.ID; }),
|
||||
|
||||
(Episode e, nameof(Episode.Show)) => ShowRepository
|
||||
.GetOrDefault(x => x.Episodes.Any(y => y.ID == obj.ID))
|
||||
.Then(x =>
|
||||
@ -376,27 +348,10 @@ namespace Kyoo.Core.Controllers
|
||||
.GetAll(x => x.Studio.ID == obj.ID)
|
||||
.Then(x => s.Shows = x),
|
||||
|
||||
(Studio s, nameof(Studio.ExternalIDs)) => _SetRelation(s,
|
||||
ProviderRepository.GetMetadataID<Studio>(x => x.ResourceID == obj.ID),
|
||||
(x, y) => x.ExternalIDs = y,
|
||||
(x, y) => { x.ResourceID = y.ID; }),
|
||||
|
||||
|
||||
(People p, nameof(People.ExternalIDs)) => _SetRelation(p,
|
||||
ProviderRepository.GetMetadataID<People>(x => x.ResourceID == obj.ID),
|
||||
(x, y) => x.ExternalIDs = y,
|
||||
(x, y) => { x.ResourceID = y.ID; }),
|
||||
|
||||
(People p, nameof(People.Roles)) => PeopleRepository
|
||||
.GetFromPeople(obj.ID)
|
||||
.Then(x => p.Roles = x),
|
||||
|
||||
|
||||
(Provider p, nameof(Provider.Libraries)) => LibraryRepository
|
||||
.GetAll(x => x.Providers.Any(y => y.ID == obj.ID))
|
||||
.Then(x => p.Libraries = x),
|
||||
|
||||
|
||||
_ => throw new ArgumentException($"Couldn't find a way to load {memberName} of {obj.Slug}.")
|
||||
};
|
||||
}
|
||||
|
@ -37,11 +37,6 @@ namespace Kyoo.Core.Controllers
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
/// <summary>
|
||||
/// A provider repository to handle externalID creation and deletion
|
||||
/// </summary>
|
||||
private readonly IProviderRepository _providers;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Sort<Collection> DefaultSort => new Sort<Collection>.By(nameof(Collection.Name));
|
||||
|
||||
@ -49,12 +44,10 @@ namespace Kyoo.Core.Controllers
|
||||
/// Create a new <see cref="CollectionRepository"/>.
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle to use</param>
|
||||
/// /// <param name="providers">A provider repository</param>
|
||||
public CollectionRepository(DatabaseContext database, IProviderRepository providers)
|
||||
public CollectionRepository(DatabaseContext database)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
_providers = providers;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -82,33 +75,8 @@ namespace Kyoo.Core.Controllers
|
||||
{
|
||||
await base.Validate(resource);
|
||||
|
||||
if (string.IsNullOrEmpty(resource.Slug))
|
||||
throw new ArgumentException("The collection's slug must be set and not empty");
|
||||
if (string.IsNullOrEmpty(resource.Name))
|
||||
throw new ArgumentException("The collection's name must be set and not empty");
|
||||
|
||||
if (resource.ExternalIDs != null)
|
||||
{
|
||||
foreach (MetadataID id in resource.ExternalIDs)
|
||||
{
|
||||
id.Provider = _database.LocalEntity<Provider>(id.Provider.Slug)
|
||||
?? await _providers.CreateIfNotExists(id.Provider);
|
||||
id.ProviderID = id.Provider.ID;
|
||||
}
|
||||
_database.MetadataIds<Collection>().AttachRange(resource.ExternalIDs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task EditRelations(Collection resource, Collection changed, bool resetOld)
|
||||
{
|
||||
await Validate(changed);
|
||||
|
||||
if (changed.ExternalIDs != null || resetOld)
|
||||
{
|
||||
await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync();
|
||||
resource.ExternalIDs = changed.ExternalIDs;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -39,11 +39,6 @@ namespace Kyoo.Core.Controllers
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
/// <summary>
|
||||
/// A provider repository to handle externalID creation and deletion
|
||||
/// </summary>
|
||||
private readonly IProviderRepository _providers;
|
||||
|
||||
private readonly IShowRepository _shows;
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -59,14 +54,11 @@ namespace Kyoo.Core.Controllers
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle to use.</param>
|
||||
/// <param name="shows">A show repository</param>
|
||||
/// <param name="providers">A provider repository</param>
|
||||
public EpisodeRepository(DatabaseContext database,
|
||||
IShowRepository shows,
|
||||
IProviderRepository providers)
|
||||
IShowRepository shows)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
_providers = providers;
|
||||
_shows = shows;
|
||||
|
||||
// Edit episode slugs when the show's slug changes.
|
||||
@ -160,18 +152,6 @@ namespace Kyoo.Core.Controllers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task EditRelations(Episode resource, Episode changed, bool resetOld)
|
||||
{
|
||||
await Validate(changed);
|
||||
|
||||
if (changed.ExternalIDs != null || resetOld)
|
||||
{
|
||||
await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync();
|
||||
resource.ExternalIDs = changed.ExternalIDs;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task Validate(Episode resource)
|
||||
{
|
||||
@ -185,17 +165,6 @@ namespace Kyoo.Core.Controllers
|
||||
}
|
||||
resource.ShowID = resource.Show.ID;
|
||||
}
|
||||
|
||||
if (resource.ExternalIDs != null)
|
||||
{
|
||||
foreach (MetadataID id in resource.ExternalIDs)
|
||||
{
|
||||
id.Provider = _database.LocalEntity<Provider>(id.Provider.Slug)
|
||||
?? await _providers.CreateIfNotExists(id.Provider);
|
||||
id.ProviderID = id.Provider.ID;
|
||||
}
|
||||
_database.MetadataIds<Episode>().AttachRange(resource.ExternalIDs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -206,7 +175,6 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
int epCount = await _database.Episodes.Where(x => x.ShowID == obj.ShowID).Take(2).CountAsync();
|
||||
_database.Entry(obj).State = EntityState.Deleted;
|
||||
obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Deleted);
|
||||
await _database.SaveChangesAsync();
|
||||
await base.Delete(obj);
|
||||
if (epCount == 1)
|
||||
|
@ -38,11 +38,6 @@ namespace Kyoo.Core.Controllers
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
/// <summary>
|
||||
/// A provider repository to handle externalID creation and deletion
|
||||
/// </summary>
|
||||
private readonly IProviderRepository _providers;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Sort<Library> DefaultSort => new Sort<Library>.By(x => x.ID);
|
||||
|
||||
@ -50,12 +45,10 @@ namespace Kyoo.Core.Controllers
|
||||
/// Create a new <see cref="LibraryRepository"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle</param>
|
||||
/// <param name="providers">The provider repository</param>
|
||||
public LibraryRepository(DatabaseContext database, IProviderRepository providers)
|
||||
public LibraryRepository(DatabaseContext database)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
_providers = providers;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -90,29 +83,6 @@ namespace Kyoo.Core.Controllers
|
||||
throw new ArgumentException("The library's name must be set and not empty");
|
||||
if (resource.Paths == null || !resource.Paths.Any())
|
||||
throw new ArgumentException("The library should have a least one path.");
|
||||
|
||||
if (resource.Providers != null)
|
||||
{
|
||||
resource.Providers = await resource.Providers
|
||||
.SelectAsync(async x =>
|
||||
_database.LocalEntity<Provider>(x.Slug)
|
||||
?? await _providers.CreateIfNotExists(x)
|
||||
)
|
||||
.ToListAsync();
|
||||
_database.AttachRange(resource.Providers);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task EditRelations(Library resource, Library changed, bool resetOld)
|
||||
{
|
||||
await Validate(changed);
|
||||
|
||||
if (changed.Providers != null || resetOld)
|
||||
{
|
||||
await Database.Entry(resource).Collection(x => x.Providers).LoadAsync();
|
||||
resource.Providers = changed.Providers;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -341,6 +341,8 @@ namespace Kyoo.Core.Controllers
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
await Validate(obj);
|
||||
// if (obj is IThumbnails thumbs)
|
||||
// await _thumbnailsManager.DownloadImages(thumbs);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -39,11 +39,6 @@ namespace Kyoo.Core.Controllers
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
/// <summary>
|
||||
/// A provider repository to handle externalID creation and deletion
|
||||
/// </summary>
|
||||
private readonly IProviderRepository _providers;
|
||||
|
||||
/// <summary>
|
||||
/// A lazy loaded show repository to validate requests from shows.
|
||||
/// </summary>
|
||||
@ -56,15 +51,12 @@ namespace Kyoo.Core.Controllers
|
||||
/// Create a new <see cref="PeopleRepository"/>
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle</param>
|
||||
/// <param name="providers">A provider repository</param>
|
||||
/// <param name="shows">A lazy loaded show repository</param>
|
||||
public PeopleRepository(DatabaseContext database,
|
||||
IProviderRepository providers,
|
||||
Lazy<IShowRepository> shows)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
_providers = providers;
|
||||
_shows = shows;
|
||||
}
|
||||
|
||||
@ -94,17 +86,6 @@ namespace Kyoo.Core.Controllers
|
||||
{
|
||||
await base.Validate(resource);
|
||||
|
||||
if (resource.ExternalIDs != null)
|
||||
{
|
||||
foreach (MetadataID id in resource.ExternalIDs)
|
||||
{
|
||||
id.Provider = _database.LocalEntity<Provider>(id.Provider.Slug)
|
||||
?? await _providers.CreateIfNotExists(id.Provider);
|
||||
id.ProviderID = id.Provider.ID;
|
||||
}
|
||||
_database.MetadataIds<People>().AttachRange(resource.ExternalIDs);
|
||||
}
|
||||
|
||||
if (resource.Roles != null)
|
||||
{
|
||||
foreach (PeopleRole role in resource.Roles)
|
||||
@ -127,12 +108,6 @@ namespace Kyoo.Core.Controllers
|
||||
await Database.Entry(resource).Collection(x => x.Roles).LoadAsync();
|
||||
resource.Roles = changed.Roles;
|
||||
}
|
||||
|
||||
if (changed.ExternalIDs != null || resetOld)
|
||||
{
|
||||
await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync();
|
||||
resource.ExternalIDs = changed.ExternalIDs;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -142,7 +117,6 @@ namespace Kyoo.Core.Controllers
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
_database.Entry(obj).State = EntityState.Deleted;
|
||||
obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Deleted);
|
||||
obj.Roles.ForEach(x => _database.Entry(x).State = EntityState.Deleted);
|
||||
await _database.SaveChangesAsync();
|
||||
await base.Delete(obj);
|
||||
|
@ -1,98 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Postgresql;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Core.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A local repository to handle providers.
|
||||
/// </summary>
|
||||
public class ProviderRepository : LocalRepository<Provider>, IProviderRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// The database handle
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="ProviderRepository" />.
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle</param>
|
||||
public ProviderRepository(DatabaseContext database)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Sort<Provider> DefaultSort => new Sort<Provider>.By(x => x.Slug);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<ICollection<Provider>> Search(string query)
|
||||
{
|
||||
return await Sort(
|
||||
_database.Providers
|
||||
.Where(_database.Like<Provider>(x => x.Name, $"%{query}%"))
|
||||
)
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<Provider> Create(Provider obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
await _database.SaveChangesAsync(() => Get(obj.Slug));
|
||||
OnResourceCreated(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task Delete(Provider obj)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
_database.Entry(obj).State = EntityState.Deleted;
|
||||
await _database.SaveChangesAsync();
|
||||
await base.Delete(obj);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ICollection<MetadataID>> GetMetadataID<T>(Expression<Func<MetadataID, bool>> where = null,
|
||||
Sort<MetadataID> sort = default,
|
||||
Pagination limit = default)
|
||||
where T : class, IMetadata
|
||||
{
|
||||
return await _database.MetadataIds<T>()
|
||||
.Include(y => y.Provider)
|
||||
.Where(where)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
}
|
@ -38,11 +38,6 @@ namespace Kyoo.Core.Controllers
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
/// <summary>
|
||||
/// A provider repository to handle externalID creation and deletion
|
||||
/// </summary>
|
||||
private readonly IProviderRepository _providers;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Sort<Season> DefaultSort => new Sort<Season>.By(x => x.SeasonNumber);
|
||||
|
||||
@ -51,14 +46,11 @@ namespace Kyoo.Core.Controllers
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle that will be used</param>
|
||||
/// <param name="shows">A shows repository</param>
|
||||
/// <param name="providers">A provider repository</param>
|
||||
public SeasonRepository(DatabaseContext database,
|
||||
IShowRepository shows,
|
||||
IProviderRepository providers)
|
||||
IShowRepository shows)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
_providers = providers;
|
||||
|
||||
// Edit seasons slugs when the show's slug changes.
|
||||
shows.OnEdited += (show) =>
|
||||
@ -140,29 +132,6 @@ namespace Kyoo.Core.Controllers
|
||||
}
|
||||
resource.ShowID = resource.Show.ID;
|
||||
}
|
||||
|
||||
if (resource.ExternalIDs != null)
|
||||
{
|
||||
foreach (MetadataID id in resource.ExternalIDs)
|
||||
{
|
||||
id.Provider = _database.LocalEntity<Provider>(id.Provider.Slug)
|
||||
?? await _providers.CreateIfNotExists(id.Provider);
|
||||
id.ProviderID = id.Provider.ID;
|
||||
}
|
||||
_database.MetadataIds<Season>().AttachRange(resource.ExternalIDs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task EditRelations(Season resource, Season changed, bool resetOld)
|
||||
{
|
||||
await Validate(changed);
|
||||
|
||||
if (changed.ExternalIDs != null || resetOld)
|
||||
{
|
||||
await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync();
|
||||
resource.ExternalIDs = changed.ExternalIDs;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -52,11 +52,6 @@ namespace Kyoo.Core.Controllers
|
||||
/// </summary>
|
||||
private readonly IGenreRepository _genres;
|
||||
|
||||
/// <summary>
|
||||
/// A provider repository to handle externalID creation and deletion
|
||||
/// </summary>
|
||||
private readonly IProviderRepository _providers;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Sort<Show> DefaultSort => new Sort<Show>.By(x => x.Title);
|
||||
|
||||
@ -67,19 +62,16 @@ namespace Kyoo.Core.Controllers
|
||||
/// <param name="studios">A studio repository</param>
|
||||
/// <param name="people">A people repository</param>
|
||||
/// <param name="genres">A genres repository</param>
|
||||
/// <param name="providers">A provider repository</param>
|
||||
public ShowRepository(DatabaseContext database,
|
||||
IStudioRepository studios,
|
||||
IPeopleRepository people,
|
||||
IGenreRepository genres,
|
||||
IProviderRepository providers)
|
||||
IGenreRepository genres)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
_studios = studios;
|
||||
_people = people;
|
||||
_genres = genres;
|
||||
_providers = providers;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -124,17 +116,6 @@ namespace Kyoo.Core.Controllers
|
||||
_database.AttachRange(resource.Genres);
|
||||
}
|
||||
|
||||
if (resource.ExternalIDs != null)
|
||||
{
|
||||
foreach (MetadataID id in resource.ExternalIDs)
|
||||
{
|
||||
id.Provider = _database.LocalEntity<Provider>(id.Provider.Slug)
|
||||
?? await _providers.CreateIfNotExists(id.Provider);
|
||||
id.ProviderID = id.Provider.ID;
|
||||
}
|
||||
_database.MetadataIds<Show>().AttachRange(resource.ExternalIDs);
|
||||
}
|
||||
|
||||
if (resource.People != null)
|
||||
{
|
||||
foreach (PeopleRole role in resource.People)
|
||||
@ -172,12 +153,6 @@ namespace Kyoo.Core.Controllers
|
||||
await Database.Entry(resource).Collection(x => x.People).LoadAsync();
|
||||
resource.People = changed.People;
|
||||
}
|
||||
|
||||
if (changed.ExternalIDs != null || resetOld)
|
||||
{
|
||||
await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync();
|
||||
resource.ExternalIDs = changed.ExternalIDs;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -38,11 +38,6 @@ namespace Kyoo.Core.Controllers
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
/// <summary>
|
||||
/// A provider repository to handle externalID creation and deletion
|
||||
/// </summary>
|
||||
private readonly IProviderRepository _providers;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Sort<Studio> DefaultSort => new Sort<Studio>.By(x => x.Name);
|
||||
|
||||
@ -50,12 +45,10 @@ namespace Kyoo.Core.Controllers
|
||||
/// Create a new <see cref="StudioRepository"/>.
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle</param>
|
||||
/// <param name="providers">A provider repository</param>
|
||||
public StudioRepository(DatabaseContext database, IProviderRepository providers)
|
||||
public StudioRepository(DatabaseContext database)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
_providers = providers;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -83,30 +76,7 @@ namespace Kyoo.Core.Controllers
|
||||
protected override async Task Validate(Studio resource)
|
||||
{
|
||||
resource.Slug ??= Utility.ToSlug(resource.Name);
|
||||
|
||||
await base.Validate(resource);
|
||||
if (resource.ExternalIDs != null)
|
||||
{
|
||||
foreach (MetadataID id in resource.ExternalIDs)
|
||||
{
|
||||
id.Provider = _database.LocalEntity<Provider>(id.Provider.Slug)
|
||||
?? await _providers.CreateIfNotExists(id.Provider);
|
||||
id.ProviderID = id.Provider.ID;
|
||||
}
|
||||
_database.MetadataIds<Studio>().AttachRange(resource.ExternalIDs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task EditRelations(Studio resource, Studio changed, bool resetOld)
|
||||
{
|
||||
if (changed.ExternalIDs != null || resetOld)
|
||||
{
|
||||
await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync();
|
||||
resource.ExternalIDs = changed.ExternalIDs;
|
||||
}
|
||||
|
||||
await base.EditRelations(resource, changed, resetOld);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -18,13 +18,12 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SkiaSharp;
|
||||
|
||||
#nullable enable
|
||||
|
||||
@ -54,18 +53,18 @@ namespace Kyoo.Core.Controllers
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An helper function to download an image.
|
||||
/// </summary>
|
||||
/// <param name="url">The distant url of the image</param>
|
||||
/// <param name="localPath">The local path of the image</param>
|
||||
/// <param name="what">What is currently downloaded (used for errors)</param>
|
||||
/// <returns><c>true</c> if an image has been downloaded, <c>false</c> otherwise.</returns>
|
||||
private async Task<bool> _DownloadImage(string url, string localPath, string what)
|
||||
private static async Task _WriteTo(SKBitmap bitmap, string path)
|
||||
{
|
||||
if (url == localPath)
|
||||
return false;
|
||||
SKData data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 18);
|
||||
await using Stream reader = data.AsStream();
|
||||
await using Stream file = File.Create(path);
|
||||
await reader.CopyToAsync(file);
|
||||
}
|
||||
|
||||
private async Task _DownloadImage(string? url, string localPath, string what)
|
||||
{
|
||||
if (url == null)
|
||||
return;
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Downloading image {What}", what);
|
||||
@ -73,86 +72,53 @@ namespace Kyoo.Core.Controllers
|
||||
HttpClient client = _clientFactory.CreateClient();
|
||||
HttpResponseMessage response = await client.GetAsync(url);
|
||||
response.EnsureSuccessStatusCode();
|
||||
string mime = response.Content.Headers.ContentType?.MediaType!;
|
||||
await using Stream reader = await response.Content.ReadAsStreamAsync();
|
||||
SKBitmap bitmap = SKBitmap.Decode(reader);
|
||||
|
||||
string extension = new FileExtensionContentTypeProvider()
|
||||
.Mappings.FirstOrDefault(x => x.Value == mime)
|
||||
.Key;
|
||||
await using Stream local = File.Create(localPath + extension);
|
||||
await reader.CopyToAsync(local);
|
||||
return true;
|
||||
bitmap.Resize(new SKSizeI(bitmap.Width, bitmap.Height), SKFilterQuality.High);
|
||||
await _WriteTo(bitmap, $"{localPath}.{ImageQuality.Large.ToString().ToLowerInvariant()}.jpg");
|
||||
|
||||
bitmap.Resize(new SKSizeI(bitmap.Width, bitmap.Height), SKFilterQuality.Medium);
|
||||
await _WriteTo(bitmap, $"{localPath}.{ImageQuality.Medium.ToString().ToLowerInvariant()}.jpg");
|
||||
|
||||
bitmap.Resize(new SKSizeI(bitmap.Width, bitmap.Height), SKFilterQuality.Low);
|
||||
await _WriteTo(bitmap, $"{localPath}.{ImageQuality.Small.ToString().ToLowerInvariant()}.jpg");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "{What} could not be downloaded", what);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> DownloadImages<T>(T item, bool alwaysDownload = false)
|
||||
public async Task DownloadImages<T>(T item)
|
||||
where T : IThumbnails
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
|
||||
if (item.Images == null)
|
||||
return false;
|
||||
|
||||
string name = item is IResource res ? res.Slug : "???";
|
||||
bool ret = false;
|
||||
|
||||
foreach ((int id, string image) in item.Images.Where(x => x.Value != null))
|
||||
{
|
||||
string localPath = _GetPrivateImagePath(item, id);
|
||||
if (alwaysDownload || !Path.Exists(localPath))
|
||||
ret |= await _DownloadImage(image, localPath, $"The image n {id} of {name}");
|
||||
}
|
||||
|
||||
return ret;
|
||||
await _DownloadImage(item.Poster.Source, _GetBaseImagePath(item, "poster"), $"The poster of {name}");
|
||||
await _DownloadImage(item.Thumbnail.Source, _GetBaseImagePath(item, "thumbnail"), $"The poster of {name}");
|
||||
await _DownloadImage(item.Logo.Source, _GetBaseImagePath(item, "logo"), $"The poster of {name}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the local path of an image of the given item <b>without an extension</b>.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to retrieve the poster from.</param>
|
||||
/// <param name="imageId">The ID of the image. See <see cref="Images"/> for values.</param>
|
||||
/// <typeparam name="T">The type of the item</typeparam>
|
||||
/// <returns>The path of the image for the given resource, <b>even if it does not exists</b></returns>
|
||||
private static string _GetPrivateImagePath<T>(T item, int imageId)
|
||||
private static string _GetBaseImagePath<T>(T item, string image)
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
|
||||
string directory = item switch
|
||||
{
|
||||
IResource res => Path.Combine("./metadata", typeof(T).Name.ToLowerInvariant(), res.Slug),
|
||||
_ => Path.Combine("./metadata", typeof(T).Name.ToLowerInvariant())
|
||||
};
|
||||
Directory.CreateDirectory(directory);
|
||||
string imageName = imageId switch
|
||||
{
|
||||
Images.Poster => "poster",
|
||||
Images.Logo => "logo",
|
||||
Images.Thumbnail => "thumbnail",
|
||||
Images.Trailer => "trailer",
|
||||
_ => $"{imageId}"
|
||||
};
|
||||
return Path.Combine(directory, imageName);
|
||||
return Path.Combine(directory, image);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? GetImagePath<T>(T item, int imageId)
|
||||
public string GetImagePath<T>(T item, string image, ImageQuality quality)
|
||||
where T : IThumbnails
|
||||
{
|
||||
string basePath = _GetPrivateImagePath(item, imageId);
|
||||
string directory = Path.GetDirectoryName(basePath)!;
|
||||
string baseFile = Path.GetFileName(basePath);
|
||||
if (!Directory.Exists(directory))
|
||||
return null;
|
||||
return Directory.GetFiles(directory, "*", SearchOption.TopDirectoryOnly)
|
||||
.FirstOrDefault(x => Path.GetFileNameWithoutExtension(x) == baseFile);
|
||||
return $"{_GetBaseImagePath(item, image)}.{quality.ToString().ToLowerInvariant()}.jpg";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,6 @@ namespace Kyoo.Core
|
||||
builder.RegisterRepository<IPeopleRepository, PeopleRepository>();
|
||||
builder.RegisterRepository<IStudioRepository, StudioRepository>();
|
||||
builder.RegisterRepository<IGenreRepository, GenreRepository>();
|
||||
builder.RegisterRepository<IProviderRepository, ProviderRepository>();
|
||||
builder.RegisterRepository<IUserRepository, UserRepository>();
|
||||
}
|
||||
|
||||
|
@ -6,9 +6,11 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AspNetCore.Proxy" Version="4.4.0" />
|
||||
<PackageReference Include="BlurHashSharp.SkiaSharp" Version="1.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.9" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -16,7 +16,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
@ -25,7 +24,6 @@ using Kyoo.Abstractions.Models.Permissions;
|
||||
using Kyoo.Abstractions.Models.Utils;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using static Kyoo.Abstractions.Models.Utils.Constants;
|
||||
|
||||
namespace Kyoo.Core.Api
|
||||
@ -59,37 +57,7 @@ namespace Kyoo.Core.Api
|
||||
_thumbs = thumbs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the content type of a file using it's extension.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file</param>
|
||||
/// <exception cref="NotImplementedException">The extension of the file is not known.</exception>
|
||||
/// <returns>The content type of the file</returns>
|
||||
private static string _GetContentType(string path)
|
||||
{
|
||||
FileExtensionContentTypeProvider provider = new();
|
||||
if (provider.TryGetContentType(path, out string contentType))
|
||||
return contentType;
|
||||
throw new NotImplementedException($"Can't get the content type of the file at: {path}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get image
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Get an image for the specified item.
|
||||
/// List of commonly available images:<br/>
|
||||
/// - Poster: Image 0, also available at /poster<br/>
|
||||
/// - Thumbnail: Image 1, also available at /thumbnail<br/>
|
||||
/// - Logo: Image 3, also available at /logo<br/>
|
||||
/// <br/>
|
||||
/// Other images can be arbitrarily added by plugins so any image number can be specified from this endpoint.
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the resource to get the image for.</param>
|
||||
/// <param name="image">The number of the image to retrieve.</param>
|
||||
/// <returns>The image asked.</returns>
|
||||
/// <response code="404">No item exist with the specific identifier or the image does not exists on kyoo.</response>
|
||||
private async Task<IActionResult> _GetImage(Identifier identifier, int image)
|
||||
private async Task<IActionResult> _GetImage(Identifier identifier, string image, ImageQuality? quality)
|
||||
{
|
||||
T resource = await identifier.Match(
|
||||
id => Repository.GetOrDefault(id),
|
||||
@ -97,10 +65,10 @@ namespace Kyoo.Core.Api
|
||||
);
|
||||
if (resource == null)
|
||||
return NotFound();
|
||||
string path = _thumbs.GetImagePath(resource, image);
|
||||
string path = _thumbs.GetImagePath(resource, image, quality ?? ImageQuality.Large);
|
||||
if (path == null || !System.IO.File.Exists(path))
|
||||
return NotFound();
|
||||
return PhysicalFile(Path.GetFullPath(path), _GetContentType(path), true);
|
||||
return PhysicalFile(Path.GetFullPath(path), "image/jpeg", true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -110,17 +78,18 @@ namespace Kyoo.Core.Api
|
||||
/// Get the poster for the specified item.
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the resource to get the image for.</param>
|
||||
/// <param name="quality">The quality of the image to retrieve.</param>
|
||||
/// <returns>The image asked.</returns>
|
||||
/// <response code="404">
|
||||
/// No item exist with the specific identifier or the image does not exists on kyoo.
|
||||
/// </response>
|
||||
[HttpGet("{identifier:id}/poster", Order = AlternativeRoute)]
|
||||
[HttpGet("{identifier:id}/poster")]
|
||||
[PartialPermission(Kind.Read)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public Task<IActionResult> GetPoster(Identifier identifier)
|
||||
public Task<IActionResult> GetPoster(Identifier identifier, [FromQuery] ImageQuality? quality)
|
||||
{
|
||||
return _GetImage(identifier, Images.Poster);
|
||||
return _GetImage(identifier, "poster", quality);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -130,17 +99,18 @@ namespace Kyoo.Core.Api
|
||||
/// Get the logo for the specified item.
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the resource to get the image for.</param>
|
||||
/// <param name="quality">The quality of the image to retrieve.</param>
|
||||
/// <returns>The image asked.</returns>
|
||||
/// <response code="404">
|
||||
/// No item exist with the specific identifier or the image does not exists on kyoo.
|
||||
/// </response>
|
||||
[HttpGet("{identifier:id}/logo", Order = AlternativeRoute)]
|
||||
[HttpGet("{identifier:id}/logo")]
|
||||
[PartialPermission(Kind.Read)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public Task<IActionResult> GetLogo(Identifier identifier)
|
||||
public Task<IActionResult> GetLogo(Identifier identifier, [FromQuery] ImageQuality? quality)
|
||||
{
|
||||
return _GetImage(identifier, Images.Logo);
|
||||
return _GetImage(identifier, "logo", quality);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -150,15 +120,16 @@ namespace Kyoo.Core.Api
|
||||
/// Get the thumbnail for the specified item.
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the resource to get the image for.</param>
|
||||
/// <param name="quality">The quality of the image to retrieve.</param>
|
||||
/// <returns>The image asked.</returns>
|
||||
/// <response code="404">
|
||||
/// No item exist with the specific identifier or the image does not exists on kyoo.
|
||||
/// </response>
|
||||
[HttpGet("{identifier:id}/thumbnail")]
|
||||
[HttpGet("{identifier:id}/backdrop", Order = AlternativeRoute)]
|
||||
[HttpGet("{identifier:id}/thumbnail", Order = AlternativeRoute)]
|
||||
public Task<IActionResult> GetBackdrop(Identifier identifier)
|
||||
public Task<IActionResult> GetBackdrop(Identifier identifier, [FromQuery] ImageQuality? quality)
|
||||
{
|
||||
return _GetImage(identifier, Images.Thumbnail);
|
||||
return _GetImage(identifier, "thumbnail", quality);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -79,28 +79,28 @@ namespace Kyoo.Core.Api
|
||||
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||
{
|
||||
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
||||
if (!type.IsAssignableTo(typeof(IThumbnails)))
|
||||
if (!type.IsAssignableTo(typeof(Image)))
|
||||
return properties;
|
||||
foreach ((int id, string image) in Images.ImageName)
|
||||
{
|
||||
properties.Add(new JsonProperty
|
||||
{
|
||||
DeclaringType = type,
|
||||
PropertyName = image.ToLower(),
|
||||
UnderlyingName = image,
|
||||
PropertyType = typeof(string),
|
||||
Readable = true,
|
||||
Writable = false,
|
||||
ItemIsReference = false,
|
||||
TypeNameHandling = TypeNameHandling.None,
|
||||
ShouldSerialize = x =>
|
||||
{
|
||||
IThumbnails thumb = (IThumbnails)x;
|
||||
return thumb?.Images?.ContainsKey(id) == true;
|
||||
},
|
||||
ValueProvider = new ThumbnailProvider(id)
|
||||
});
|
||||
}
|
||||
// foreach ((int id, string image) in Images.ImageName)
|
||||
// {
|
||||
// properties.Add(new JsonProperty
|
||||
// {
|
||||
// DeclaringType = type,
|
||||
// PropertyName = image.ToLower(),
|
||||
// UnderlyingName = image,
|
||||
// PropertyType = typeof(string),
|
||||
// Readable = true,
|
||||
// Writable = false,
|
||||
// ItemIsReference = false,
|
||||
// TypeNameHandling = TypeNameHandling.None,
|
||||
// ShouldSerialize = x =>
|
||||
// {
|
||||
// IThumbnails thumb = (IThumbnails)x;
|
||||
// return thumb?.Images?.ContainsKey(id) == true;
|
||||
// },
|
||||
// ValueProvider = new ThumbnailProvider(id)
|
||||
// });
|
||||
// }
|
||||
|
||||
return properties;
|
||||
}
|
||||
@ -128,23 +128,22 @@ namespace Kyoo.Core.Api
|
||||
/// <inheritdoc />
|
||||
public void SetValue(object target, object value)
|
||||
{
|
||||
if (target is not IThumbnails thumb)
|
||||
throw new ArgumentException($"The given object is not an Thumbnail.");
|
||||
thumb.Images[_imageIndex] = value as string;
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object GetValue(object target)
|
||||
{
|
||||
string slug = (target as IResource)?.Slug ?? (target as ICustomTypeDescriptor)?.GetComponentName();
|
||||
if (target is not IThumbnails thumb
|
||||
|| slug == null
|
||||
|| string.IsNullOrEmpty(thumb.Images?.GetValueOrDefault(_imageIndex)))
|
||||
return null;
|
||||
string type = target is ICustomTypeDescriptor descriptor
|
||||
? descriptor.GetClassName()
|
||||
: target.GetType().Name;
|
||||
return $"/{type}/{slug}/{Images.ImageName[_imageIndex]}".ToLowerInvariant();
|
||||
return null;
|
||||
// string slug = (target as IResource)?.Slug ?? (target as ICustomTypeDescriptor)?.GetComponentName();
|
||||
// if (target is not IThumbnails thumb
|
||||
// || slug == null
|
||||
// || string.IsNullOrEmpty(thumb.Images?.GetValueOrDefault(_imageIndex)))
|
||||
// return null;
|
||||
// string type = target is ICustomTypeDescriptor descriptor
|
||||
// ? descriptor.GetClassName()
|
||||
// : target.GetType().Name;
|
||||
// return $"/{type}/{slug}/{Images.ImageName[_imageIndex]}".ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
@ -80,11 +81,6 @@ namespace Kyoo.Postgresql
|
||||
/// </summary>
|
||||
public DbSet<Studio> Studios { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// All providers of Kyoo. See <see cref="Provider"/>.
|
||||
/// </summary>
|
||||
public DbSet<Provider> Providers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of registered users.
|
||||
/// </summary>
|
||||
@ -108,17 +104,6 @@ namespace Kyoo.Postgresql
|
||||
/// </remarks>
|
||||
public DbSet<LibraryItem> LibraryItems { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get all metadataIDs (ExternalIDs) of a given resource. See <see cref="MetadataID"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The metadata of this type will be returned.</typeparam>
|
||||
/// <returns>A queryable of metadata ids for a type.</returns>
|
||||
public DbSet<MetadataID> MetadataIds<T>()
|
||||
where T : class, IMetadata
|
||||
{
|
||||
return Set<MetadataID>(MetadataName<T>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a many to many link between two resources.
|
||||
/// </summary>
|
||||
@ -194,17 +179,33 @@ namespace Kyoo.Postgresql
|
||||
/// </summary>
|
||||
/// <param name="modelBuilder">The database model builder</param>
|
||||
/// <typeparam name="T">The type to add metadata to.</typeparam>
|
||||
private void _HasMetadata<T>(ModelBuilder modelBuilder)
|
||||
private static void _HasMetadata<T>(ModelBuilder modelBuilder)
|
||||
where T : class, IMetadata
|
||||
{
|
||||
modelBuilder.SharedTypeEntity<MetadataID>(MetadataName<T>())
|
||||
.HasKey(MetadataID.PrimaryKey);
|
||||
// TODO: Waiting for https://github.com/dotnet/efcore/issues/29825
|
||||
// modelBuilder.Entity<T>()
|
||||
// .OwnsOne(x => x.ExternalIDs, x =>
|
||||
// {
|
||||
// x.ToJson();
|
||||
// });
|
||||
modelBuilder.Entity<T>()
|
||||
.Property(x => x.ExternalId)
|
||||
.HasConversion(
|
||||
v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),
|
||||
v => JsonSerializer.Deserialize<Dictionary<string, MetadataID>>(v, (JsonSerializerOptions)null)
|
||||
)
|
||||
.HasColumnType("json");
|
||||
}
|
||||
|
||||
modelBuilder.SharedTypeEntity<MetadataID>(MetadataName<T>())
|
||||
.HasOne<T>()
|
||||
.WithMany(x => x.ExternalIDs)
|
||||
.HasForeignKey(x => x.ResourceID)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
private static void _HasImages<T>(ModelBuilder modelBuilder)
|
||||
where T : class, IThumbnails
|
||||
{
|
||||
modelBuilder.Entity<T>()
|
||||
.OwnsOne(x => x.Poster);
|
||||
modelBuilder.Entity<T>()
|
||||
.OwnsOne(x => x.Thumbnail);
|
||||
modelBuilder.Entity<T>()
|
||||
.OwnsOne(x => x.Logo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -269,7 +270,6 @@ namespace Kyoo.Postgresql
|
||||
.WithMany(x => x.Shows)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
_HasManyToMany<Library, Provider>(modelBuilder, x => x.Providers, x => x.Libraries);
|
||||
_HasManyToMany<Library, Collection>(modelBuilder, x => x.Collections, x => x.Libraries);
|
||||
_HasManyToMany<Library, Show>(modelBuilder, x => x.Shows, x => x.Libraries);
|
||||
_HasManyToMany<Collection, Show>(modelBuilder, x => x.Shows, x => x.Collections);
|
||||
@ -287,6 +287,15 @@ namespace Kyoo.Postgresql
|
||||
_HasMetadata<People>(modelBuilder);
|
||||
_HasMetadata<Studio>(modelBuilder);
|
||||
|
||||
_HasImages<LibraryItem>(modelBuilder);
|
||||
_HasImages<Collection>(modelBuilder);
|
||||
_HasImages<Show>(modelBuilder);
|
||||
_HasImages<Season>(modelBuilder);
|
||||
_HasImages<Episode>(modelBuilder);
|
||||
_HasImages<People>(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<User>().OwnsOne(x => x.Logo);
|
||||
|
||||
modelBuilder.Entity<WatchedEpisode>()
|
||||
.HasKey(x => new { User = x.UserID, Episode = x.EpisodeID });
|
||||
|
||||
@ -294,7 +303,6 @@ namespace Kyoo.Postgresql
|
||||
modelBuilder.Entity<Genre>().Property(x => x.Slug).IsRequired();
|
||||
modelBuilder.Entity<Library>().Property(x => x.Slug).IsRequired();
|
||||
modelBuilder.Entity<People>().Property(x => x.Slug).IsRequired();
|
||||
modelBuilder.Entity<Provider>().Property(x => x.Slug).IsRequired();
|
||||
modelBuilder.Entity<Show>().Property(x => x.Slug).IsRequired();
|
||||
modelBuilder.Entity<Season>().Property(x => x.Slug).IsRequired();
|
||||
modelBuilder.Entity<Episode>().Property(x => x.Slug).IsRequired();
|
||||
@ -319,9 +327,6 @@ namespace Kyoo.Postgresql
|
||||
modelBuilder.Entity<Studio>()
|
||||
.HasIndex(x => x.Slug)
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<Provider>()
|
||||
.HasIndex(x => x.Slug)
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<Season>()
|
||||
.HasIndex(x => new { x.ShowID, x.SeasonNumber })
|
||||
.IsUnique();
|
||||
|
1339
back/src/Kyoo.Postgresql/Migrations/20230804143919_AddBlurhash.Designer.cs
generated
Normal file
1339
back/src/Kyoo.Postgresql/Migrations/20230804143919_AddBlurhash.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,772 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Kyoo.Postgresql.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddBlurhash : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
MigrationHelper.DropLibraryItemsView(migrationBuilder);
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "collection_metadata_id");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "episode_metadata_id");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "link_library_provider");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "people_metadata_id");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "season_metadata_id");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "show_metadata_id");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "studio_metadata_id");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "providers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "images",
|
||||
table: "users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "images",
|
||||
table: "shows");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "images",
|
||||
table: "seasons");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "images",
|
||||
table: "people");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "images",
|
||||
table: "episodes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "images",
|
||||
table: "collections");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "logo_blurhash",
|
||||
table: "users",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "logo_source",
|
||||
table: "users",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "external_id",
|
||||
table: "studios",
|
||||
type: "json",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "external_id",
|
||||
table: "shows",
|
||||
type: "json",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "logo_blurhash",
|
||||
table: "shows",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "logo_source",
|
||||
table: "shows",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "poster_blurhash",
|
||||
table: "shows",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "poster_source",
|
||||
table: "shows",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "thumbnail_blurhash",
|
||||
table: "shows",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "thumbnail_source",
|
||||
table: "shows",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "trailer",
|
||||
table: "shows",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "external_id",
|
||||
table: "seasons",
|
||||
type: "json",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "logo_blurhash",
|
||||
table: "seasons",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "logo_source",
|
||||
table: "seasons",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "poster_blurhash",
|
||||
table: "seasons",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "poster_source",
|
||||
table: "seasons",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "thumbnail_blurhash",
|
||||
table: "seasons",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "thumbnail_source",
|
||||
table: "seasons",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "external_id",
|
||||
table: "people",
|
||||
type: "json",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "logo_blurhash",
|
||||
table: "people",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "logo_source",
|
||||
table: "people",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "poster_blurhash",
|
||||
table: "people",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "poster_source",
|
||||
table: "people",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "thumbnail_blurhash",
|
||||
table: "people",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "thumbnail_source",
|
||||
table: "people",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "external_id",
|
||||
table: "episodes",
|
||||
type: "json",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "logo_blurhash",
|
||||
table: "episodes",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "logo_source",
|
||||
table: "episodes",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "poster_blurhash",
|
||||
table: "episodes",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "poster_source",
|
||||
table: "episodes",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "thumbnail_blurhash",
|
||||
table: "episodes",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "thumbnail_source",
|
||||
table: "episodes",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "external_id",
|
||||
table: "collections",
|
||||
type: "json",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "logo_blurhash",
|
||||
table: "collections",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "logo_source",
|
||||
table: "collections",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "poster_blurhash",
|
||||
table: "collections",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "poster_source",
|
||||
table: "collections",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "thumbnail_blurhash",
|
||||
table: "collections",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "thumbnail_source",
|
||||
table: "collections",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
MigrationHelper.CreateLibraryItemsView(migrationBuilder);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
MigrationHelper.DropLibraryItemsView(migrationBuilder);
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "logo_blurhash",
|
||||
table: "users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "logo_source",
|
||||
table: "users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "external_id",
|
||||
table: "studios");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "external_id",
|
||||
table: "shows");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "logo_blurhash",
|
||||
table: "shows");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "logo_source",
|
||||
table: "shows");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "poster_blurhash",
|
||||
table: "shows");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "poster_source",
|
||||
table: "shows");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "thumbnail_blurhash",
|
||||
table: "shows");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "thumbnail_source",
|
||||
table: "shows");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "trailer",
|
||||
table: "shows");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "external_id",
|
||||
table: "seasons");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "logo_blurhash",
|
||||
table: "seasons");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "logo_source",
|
||||
table: "seasons");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "poster_blurhash",
|
||||
table: "seasons");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "poster_source",
|
||||
table: "seasons");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "thumbnail_blurhash",
|
||||
table: "seasons");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "thumbnail_source",
|
||||
table: "seasons");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "external_id",
|
||||
table: "people");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "logo_blurhash",
|
||||
table: "people");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "logo_source",
|
||||
table: "people");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "poster_blurhash",
|
||||
table: "people");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "poster_source",
|
||||
table: "people");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "thumbnail_blurhash",
|
||||
table: "people");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "thumbnail_source",
|
||||
table: "people");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "external_id",
|
||||
table: "episodes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "logo_blurhash",
|
||||
table: "episodes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "logo_source",
|
||||
table: "episodes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "poster_blurhash",
|
||||
table: "episodes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "poster_source",
|
||||
table: "episodes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "thumbnail_blurhash",
|
||||
table: "episodes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "thumbnail_source",
|
||||
table: "episodes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "external_id",
|
||||
table: "collections");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "logo_blurhash",
|
||||
table: "collections");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "logo_source",
|
||||
table: "collections");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "poster_blurhash",
|
||||
table: "collections");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "poster_source",
|
||||
table: "collections");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "thumbnail_blurhash",
|
||||
table: "collections");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "thumbnail_source",
|
||||
table: "collections");
|
||||
|
||||
migrationBuilder.AddColumn<Dictionary<int, string>>(
|
||||
name: "images",
|
||||
table: "users",
|
||||
type: "jsonb",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Dictionary<int, string>>(
|
||||
name: "images",
|
||||
table: "shows",
|
||||
type: "jsonb",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Dictionary<int, string>>(
|
||||
name: "images",
|
||||
table: "seasons",
|
||||
type: "jsonb",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Dictionary<int, string>>(
|
||||
name: "images",
|
||||
table: "people",
|
||||
type: "jsonb",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Dictionary<int, string>>(
|
||||
name: "images",
|
||||
table: "episodes",
|
||||
type: "jsonb",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Dictionary<int, string>>(
|
||||
name: "images",
|
||||
table: "collections",
|
||||
type: "jsonb",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "providers",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
images = table.Column<Dictionary<int, string>>(type: "jsonb", nullable: true),
|
||||
name = table.Column<string>(type: "text", nullable: true),
|
||||
slug = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_providers", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "collection_metadata_id",
|
||||
columns: table => new
|
||||
{
|
||||
resource_id = table.Column<int>(type: "integer", nullable: false),
|
||||
provider_id = table.Column<int>(type: "integer", nullable: false),
|
||||
data_id = table.Column<string>(type: "text", nullable: true),
|
||||
link = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_collection_metadata_id", x => new { x.resource_id, x.provider_id });
|
||||
table.ForeignKey(
|
||||
name: "fk_collection_metadata_id_collections_collection_id",
|
||||
column: x => x.resource_id,
|
||||
principalTable: "collections",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_collection_metadata_id_providers_provider_id",
|
||||
column: x => x.provider_id,
|
||||
principalTable: "providers",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "episode_metadata_id",
|
||||
columns: table => new
|
||||
{
|
||||
resource_id = table.Column<int>(type: "integer", nullable: false),
|
||||
provider_id = table.Column<int>(type: "integer", nullable: false),
|
||||
data_id = table.Column<string>(type: "text", nullable: true),
|
||||
link = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_episode_metadata_id", x => new { x.resource_id, x.provider_id });
|
||||
table.ForeignKey(
|
||||
name: "fk_episode_metadata_id_episodes_episode_id",
|
||||
column: x => x.resource_id,
|
||||
principalTable: "episodes",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_episode_metadata_id_providers_provider_id",
|
||||
column: x => x.provider_id,
|
||||
principalTable: "providers",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "link_library_provider",
|
||||
columns: table => new
|
||||
{
|
||||
library_id = table.Column<int>(type: "integer", nullable: false),
|
||||
provider_id = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_link_library_provider", x => new { x.library_id, x.provider_id });
|
||||
table.ForeignKey(
|
||||
name: "fk_link_library_provider_libraries_library_id",
|
||||
column: x => x.library_id,
|
||||
principalTable: "libraries",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_link_library_provider_providers_provider_id",
|
||||
column: x => x.provider_id,
|
||||
principalTable: "providers",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "people_metadata_id",
|
||||
columns: table => new
|
||||
{
|
||||
resource_id = table.Column<int>(type: "integer", nullable: false),
|
||||
provider_id = table.Column<int>(type: "integer", nullable: false),
|
||||
data_id = table.Column<string>(type: "text", nullable: true),
|
||||
link = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_people_metadata_id", x => new { x.resource_id, x.provider_id });
|
||||
table.ForeignKey(
|
||||
name: "fk_people_metadata_id_people_people_id",
|
||||
column: x => x.resource_id,
|
||||
principalTable: "people",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_people_metadata_id_providers_provider_id",
|
||||
column: x => x.provider_id,
|
||||
principalTable: "providers",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "season_metadata_id",
|
||||
columns: table => new
|
||||
{
|
||||
resource_id = table.Column<int>(type: "integer", nullable: false),
|
||||
provider_id = table.Column<int>(type: "integer", nullable: false),
|
||||
data_id = table.Column<string>(type: "text", nullable: true),
|
||||
link = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_season_metadata_id", x => new { x.resource_id, x.provider_id });
|
||||
table.ForeignKey(
|
||||
name: "fk_season_metadata_id_providers_provider_id",
|
||||
column: x => x.provider_id,
|
||||
principalTable: "providers",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_season_metadata_id_seasons_season_id",
|
||||
column: x => x.resource_id,
|
||||
principalTable: "seasons",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "show_metadata_id",
|
||||
columns: table => new
|
||||
{
|
||||
resource_id = table.Column<int>(type: "integer", nullable: false),
|
||||
provider_id = table.Column<int>(type: "integer", nullable: false),
|
||||
data_id = table.Column<string>(type: "text", nullable: true),
|
||||
link = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_show_metadata_id", x => new { x.resource_id, x.provider_id });
|
||||
table.ForeignKey(
|
||||
name: "fk_show_metadata_id_providers_provider_id",
|
||||
column: x => x.provider_id,
|
||||
principalTable: "providers",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_show_metadata_id_shows_show_id",
|
||||
column: x => x.resource_id,
|
||||
principalTable: "shows",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "studio_metadata_id",
|
||||
columns: table => new
|
||||
{
|
||||
resource_id = table.Column<int>(type: "integer", nullable: false),
|
||||
provider_id = table.Column<int>(type: "integer", nullable: false),
|
||||
data_id = table.Column<string>(type: "text", nullable: true),
|
||||
link = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_studio_metadata_id", x => new { x.resource_id, x.provider_id });
|
||||
table.ForeignKey(
|
||||
name: "fk_studio_metadata_id_providers_provider_id",
|
||||
column: x => x.provider_id,
|
||||
principalTable: "providers",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_studio_metadata_id_studios_studio_id",
|
||||
column: x => x.resource_id,
|
||||
principalTable: "studios",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_collection_metadata_id_provider_id",
|
||||
table: "collection_metadata_id",
|
||||
column: "provider_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_episode_metadata_id_provider_id",
|
||||
table: "episode_metadata_id",
|
||||
column: "provider_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_link_library_provider_provider_id",
|
||||
table: "link_library_provider",
|
||||
column: "provider_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_people_metadata_id_provider_id",
|
||||
table: "people_metadata_id",
|
||||
column: "provider_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_providers_slug",
|
||||
table: "providers",
|
||||
column: "slug",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_season_metadata_id_provider_id",
|
||||
table: "season_metadata_id",
|
||||
column: "provider_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_show_metadata_id_provider_id",
|
||||
table: "show_metadata_id",
|
||||
column: "provider_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_studio_metadata_id_provider_id",
|
||||
table: "studio_metadata_id",
|
||||
column: "provider_id");
|
||||
|
||||
MigrationHelper.CreateLibraryItemsView(migrationBuilder);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -33,11 +33,6 @@ namespace Kyoo.Postgresql
|
||||
/// </summary>
|
||||
public class PostgresContext : DatabaseContext
|
||||
{
|
||||
/// <summary>
|
||||
/// The connection string to use.
|
||||
/// </summary>
|
||||
private readonly string _connection;
|
||||
|
||||
/// <summary>
|
||||
/// Is this instance in debug mode?
|
||||
/// </summary>
|
||||
@ -78,7 +73,6 @@ namespace Kyoo.Postgresql
|
||||
/// <param name="debugMode">Is this instance in debug mode?</param>
|
||||
public PostgresContext(string connection, bool debugMode)
|
||||
{
|
||||
_connection = connection;
|
||||
_debugMode = debugMode;
|
||||
}
|
||||
|
||||
@ -116,31 +110,6 @@ namespace Kyoo.Postgresql
|
||||
.Property(x => x.ExtraData)
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
modelBuilder.Entity<LibraryItem>()
|
||||
.Property(x => x.Images)
|
||||
.HasColumnType("jsonb");
|
||||
modelBuilder.Entity<Collection>()
|
||||
.Property(x => x.Images)
|
||||
.HasColumnType("jsonb");
|
||||
modelBuilder.Entity<Show>()
|
||||
.Property(x => x.Images)
|
||||
.HasColumnType("jsonb");
|
||||
modelBuilder.Entity<Season>()
|
||||
.Property(x => x.Images)
|
||||
.HasColumnType("jsonb");
|
||||
modelBuilder.Entity<Episode>()
|
||||
.Property(x => x.Images)
|
||||
.HasColumnType("jsonb");
|
||||
modelBuilder.Entity<People>()
|
||||
.Property(x => x.Images)
|
||||
.HasColumnType("jsonb");
|
||||
modelBuilder.Entity<Provider>()
|
||||
.Property(x => x.Images)
|
||||
.HasColumnType("jsonb");
|
||||
modelBuilder.Entity<User>()
|
||||
.Property(x => x.Images)
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
@ -87,7 +86,6 @@ namespace Kyoo.Swagger
|
||||
x.IsNullableRaw = false;
|
||||
x.Type = JsonObjectType.String | JsonObjectType.Integer;
|
||||
}));
|
||||
document.SchemaProcessors.Add(new ThumbnailProcessor());
|
||||
|
||||
document.AddSecurity(nameof(Kyoo), new OpenApiSecurityScheme
|
||||
{
|
||||
|
@ -1,49 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using Kyoo.Abstractions.Models;
|
||||
using NJsonSchema;
|
||||
using NJsonSchema.Generation;
|
||||
|
||||
namespace Kyoo.Swagger
|
||||
{
|
||||
/// <summary>
|
||||
/// An operation processor to add computed fields of <see cref="IThumbnails"/>.
|
||||
/// </summary>
|
||||
public class ThumbnailProcessor : ISchemaProcessor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void Process(SchemaProcessorContext context)
|
||||
{
|
||||
if (!context.ContextualType.OriginalType.IsAssignableTo(typeof(IThumbnails)))
|
||||
return;
|
||||
foreach ((int _, string imageP) in Images.ImageName)
|
||||
{
|
||||
string image = imageP.ToLower()[0] + imageP[1..];
|
||||
context.Schema.Properties.Add(image, new JsonSchemaProperty
|
||||
{
|
||||
Type = JsonObjectType.String,
|
||||
IsNullableRaw = true,
|
||||
Description = $"An url to the {image} of this resource. If this resource does not have an image, " +
|
||||
$"the link will be null. If the kyoo's instance is not capable of handling this kind of image " +
|
||||
$"for the specific resource, this field won't be present."
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -37,22 +37,20 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
Context = new PostgresTestContext(postgres, output);
|
||||
|
||||
ProviderRepository provider = new(_NewContext());
|
||||
LibraryRepository library = new(_NewContext(), provider);
|
||||
CollectionRepository collection = new(_NewContext(), provider);
|
||||
LibraryRepository library = new(_NewContext());
|
||||
CollectionRepository collection = new(_NewContext());
|
||||
GenreRepository genre = new(_NewContext());
|
||||
StudioRepository studio = new(_NewContext(), provider);
|
||||
PeopleRepository people = new(_NewContext(), provider,
|
||||
StudioRepository studio = new(_NewContext());
|
||||
PeopleRepository people = new(_NewContext(),
|
||||
new Lazy<IShowRepository>(() => LibraryManager.ShowRepository));
|
||||
ShowRepository show = new(_NewContext(), studio, people, genre, provider);
|
||||
SeasonRepository season = new(_NewContext(), show, provider);
|
||||
ShowRepository show = new(_NewContext(), studio, people, genre);
|
||||
SeasonRepository season = new(_NewContext(), show);
|
||||
LibraryItemRepository libraryItem = new(_NewContext(),
|
||||
new Lazy<ILibraryRepository>(() => LibraryManager.LibraryRepository));
|
||||
EpisodeRepository episode = new(_NewContext(), show, provider);
|
||||
EpisodeRepository episode = new(_NewContext(), show);
|
||||
UserRepository user = new(_NewContext());
|
||||
|
||||
LibraryManager = new LibraryManager(new IBaseRepository[] {
|
||||
provider,
|
||||
library,
|
||||
libraryItem,
|
||||
collection,
|
||||
|
@ -78,7 +78,7 @@ namespace Kyoo.Tests.Database
|
||||
public async Task CreateWithExternalIdTest()
|
||||
{
|
||||
Collection collection = TestSample.GetNew<Collection>();
|
||||
collection.ExternalIDs = new[]
|
||||
collection.ExternalId = new[]
|
||||
{
|
||||
new MetadataID
|
||||
{
|
||||
@ -96,10 +96,10 @@ namespace Kyoo.Tests.Database
|
||||
await _repository.Create(collection);
|
||||
|
||||
Collection retrieved = await _repository.Get(2);
|
||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalIDs);
|
||||
Assert.Equal(2, retrieved.ExternalIDs.Count);
|
||||
KAssert.DeepEqual(collection.ExternalIDs.First(), retrieved.ExternalIDs.First());
|
||||
KAssert.DeepEqual(collection.ExternalIDs.Last(), retrieved.ExternalIDs.Last());
|
||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
|
||||
Assert.Equal(2, retrieved.ExternalId.Count);
|
||||
KAssert.DeepEqual(collection.ExternalId.First(), retrieved.ExternalId.First());
|
||||
KAssert.DeepEqual(collection.ExternalId.Last(), retrieved.ExternalId.Last());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -123,7 +123,7 @@ namespace Kyoo.Tests.Database
|
||||
public async Task EditMetadataTest()
|
||||
{
|
||||
Collection value = await _repository.Get(TestSample.Get<Collection>().Slug);
|
||||
value.ExternalIDs = new[]
|
||||
value.ExternalId = new[]
|
||||
{
|
||||
new MetadataID
|
||||
{
|
||||
@ -136,7 +136,7 @@ namespace Kyoo.Tests.Database
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Collection retrieved = await database.Collections
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
@ -147,7 +147,7 @@ namespace Kyoo.Tests.Database
|
||||
public async Task AddMetadataTest()
|
||||
{
|
||||
Collection value = await _repository.Get(TestSample.Get<Collection>().Slug);
|
||||
value.ExternalIDs = new List<MetadataID>
|
||||
value.ExternalId = new List<MetadataID>
|
||||
{
|
||||
new()
|
||||
{
|
||||
@ -161,14 +161,14 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Collection retrieved = await database.Collections
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
|
||||
value.ExternalIDs.Add(new MetadataID
|
||||
value.ExternalId.Add(new MetadataID
|
||||
{
|
||||
Provider = TestSample.GetNew<Provider>(),
|
||||
Link = "link",
|
||||
@ -179,7 +179,7 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Collection retrieved = await database.Collections
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
|
@ -206,7 +206,7 @@ namespace Kyoo.Tests.Database
|
||||
public async Task CreateWithExternalIdTest()
|
||||
{
|
||||
Episode value = TestSample.GetNew<Episode>();
|
||||
value.ExternalIDs = new[]
|
||||
value.ExternalId = new[]
|
||||
{
|
||||
new MetadataID
|
||||
{
|
||||
@ -224,10 +224,10 @@ namespace Kyoo.Tests.Database
|
||||
await _repository.Create(value);
|
||||
|
||||
Episode retrieved = await _repository.Get(2);
|
||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalIDs);
|
||||
Assert.Equal(2, retrieved.ExternalIDs.Count);
|
||||
KAssert.DeepEqual(value.ExternalIDs.First(), retrieved.ExternalIDs.First());
|
||||
KAssert.DeepEqual(value.ExternalIDs.Last(), retrieved.ExternalIDs.Last());
|
||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
|
||||
Assert.Equal(2, retrieved.ExternalId.Count);
|
||||
KAssert.DeepEqual(value.ExternalId.First(), retrieved.ExternalId.First());
|
||||
KAssert.DeepEqual(value.ExternalId.Last(), retrieved.ExternalId.Last());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -251,7 +251,7 @@ namespace Kyoo.Tests.Database
|
||||
public async Task EditMetadataTest()
|
||||
{
|
||||
Episode value = await _repository.Get(TestSample.Get<Episode>().Slug);
|
||||
value.ExternalIDs = new[]
|
||||
value.ExternalId = new[]
|
||||
{
|
||||
new MetadataID
|
||||
{
|
||||
@ -264,7 +264,7 @@ namespace Kyoo.Tests.Database
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Episode retrieved = await database.Episodes
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
@ -275,7 +275,7 @@ namespace Kyoo.Tests.Database
|
||||
public async Task AddMetadataTest()
|
||||
{
|
||||
Episode value = await _repository.Get(TestSample.Get<Episode>().Slug);
|
||||
value.ExternalIDs = new List<MetadataID>
|
||||
value.ExternalId = new List<MetadataID>
|
||||
{
|
||||
new()
|
||||
{
|
||||
@ -289,14 +289,14 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Episode retrieved = await database.Episodes
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
|
||||
value.ExternalIDs.Add(new MetadataID
|
||||
value.ExternalId.Add(new MetadataID
|
||||
{
|
||||
Provider = TestSample.GetNew<Provider>(),
|
||||
Link = "link",
|
||||
@ -307,7 +307,7 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Episode retrieved = await database.Episodes
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
|
@ -52,7 +52,7 @@ namespace Kyoo.Tests.Database
|
||||
public async Task CreateWithExternalIdTest()
|
||||
{
|
||||
People value = TestSample.GetNew<People>();
|
||||
value.ExternalIDs = new[]
|
||||
value.ExternalId = new[]
|
||||
{
|
||||
new MetadataID
|
||||
{
|
||||
@ -70,10 +70,10 @@ namespace Kyoo.Tests.Database
|
||||
await _repository.Create(value);
|
||||
|
||||
People retrieved = await _repository.Get(2);
|
||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalIDs);
|
||||
Assert.Equal(2, retrieved.ExternalIDs.Count);
|
||||
KAssert.DeepEqual(value.ExternalIDs.First(), retrieved.ExternalIDs.First());
|
||||
KAssert.DeepEqual(value.ExternalIDs.Last(), retrieved.ExternalIDs.Last());
|
||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
|
||||
Assert.Equal(2, retrieved.ExternalId.Count);
|
||||
KAssert.DeepEqual(value.ExternalId.First(), retrieved.ExternalId.First());
|
||||
KAssert.DeepEqual(value.ExternalId.Last(), retrieved.ExternalId.Last());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -97,7 +97,7 @@ namespace Kyoo.Tests.Database
|
||||
public async Task EditMetadataTest()
|
||||
{
|
||||
People value = await _repository.Get(TestSample.Get<People>().Slug);
|
||||
value.ExternalIDs = new[]
|
||||
value.ExternalId = new[]
|
||||
{
|
||||
new MetadataID
|
||||
{
|
||||
@ -110,7 +110,7 @@ namespace Kyoo.Tests.Database
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
People retrieved = await database.People
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
@ -121,7 +121,7 @@ namespace Kyoo.Tests.Database
|
||||
public async Task AddMetadataTest()
|
||||
{
|
||||
People value = await _repository.Get(TestSample.Get<People>().Slug);
|
||||
value.ExternalIDs = new List<MetadataID>
|
||||
value.ExternalId = new List<MetadataID>
|
||||
{
|
||||
new()
|
||||
{
|
||||
@ -135,14 +135,14 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
People retrieved = await database.People
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
|
||||
value.ExternalIDs.Add(new MetadataID
|
||||
value.ExternalId.Add(new MetadataID
|
||||
{
|
||||
Provider = TestSample.GetNew<Provider>(),
|
||||
Link = "link",
|
||||
@ -153,7 +153,7 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
People retrieved = await database.People
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
|
@ -93,7 +93,7 @@ namespace Kyoo.Tests.Database
|
||||
public async Task CreateWithExternalIdTest()
|
||||
{
|
||||
Season season = TestSample.GetNew<Season>();
|
||||
season.ExternalIDs = new[]
|
||||
season.ExternalId = new[]
|
||||
{
|
||||
new MetadataID
|
||||
{
|
||||
@ -111,10 +111,10 @@ namespace Kyoo.Tests.Database
|
||||
await _repository.Create(season);
|
||||
|
||||
Season retrieved = await _repository.Get(2);
|
||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalIDs);
|
||||
Assert.Equal(2, retrieved.ExternalIDs.Count);
|
||||
KAssert.DeepEqual(season.ExternalIDs.First(), retrieved.ExternalIDs.First());
|
||||
KAssert.DeepEqual(season.ExternalIDs.Last(), retrieved.ExternalIDs.Last());
|
||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
|
||||
Assert.Equal(2, retrieved.ExternalId.Count);
|
||||
KAssert.DeepEqual(season.ExternalId.First(), retrieved.ExternalId.First());
|
||||
KAssert.DeepEqual(season.ExternalId.Last(), retrieved.ExternalId.Last());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -138,7 +138,7 @@ namespace Kyoo.Tests.Database
|
||||
public async Task EditMetadataTest()
|
||||
{
|
||||
Season value = await _repository.Get(TestSample.Get<Season>().Slug);
|
||||
value.ExternalIDs = new[]
|
||||
value.ExternalId = new[]
|
||||
{
|
||||
new MetadataID
|
||||
{
|
||||
@ -151,7 +151,7 @@ namespace Kyoo.Tests.Database
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Season retrieved = await database.Seasons
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
@ -162,7 +162,7 @@ namespace Kyoo.Tests.Database
|
||||
public async Task AddMetadataTest()
|
||||
{
|
||||
Season value = await _repository.Get(TestSample.Get<Season>().Slug);
|
||||
value.ExternalIDs = new List<MetadataID>
|
||||
value.ExternalId = new List<MetadataID>
|
||||
{
|
||||
new()
|
||||
{
|
||||
@ -176,14 +176,14 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Season retrieved = await database.Seasons
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
|
||||
value.ExternalIDs.Add(new MetadataID
|
||||
value.ExternalId.Add(new MetadataID
|
||||
{
|
||||
Provider = TestSample.GetNew<Provider>(),
|
||||
Link = "link",
|
||||
@ -194,7 +194,7 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Season retrieved = await database.Seasons
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
|
@ -180,7 +180,7 @@ namespace Kyoo.Tests.Database
|
||||
public async Task EditExternalIDsTest()
|
||||
{
|
||||
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||
value.ExternalIDs = new[]
|
||||
value.ExternalId = new[]
|
||||
{
|
||||
new MetadataID
|
||||
{
|
||||
@ -192,19 +192,19 @@ namespace Kyoo.Tests.Database
|
||||
|
||||
Assert.Equal(value.Slug, edited.Slug);
|
||||
Assert.Equal(
|
||||
value.ExternalIDs.Select(x => new { x.DataID, x.Provider.Slug }),
|
||||
edited.ExternalIDs.Select(x => new { x.DataID, x.Provider.Slug }));
|
||||
value.ExternalId.Select(x => new { x.DataID, x.Provider.Slug }),
|
||||
edited.ExternalId.Select(x => new { x.DataID, x.Provider.Slug }));
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Show show = await database.Shows
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
Assert.Equal(value.Slug, show.Slug);
|
||||
Assert.Equal(
|
||||
value.ExternalIDs.Select(x => new { x.DataID, x.Provider.Slug }),
|
||||
show.ExternalIDs.Select(x => new { x.DataID, x.Provider.Slug }));
|
||||
value.ExternalId.Select(x => new { x.DataID, x.Provider.Slug }),
|
||||
show.ExternalId.Select(x => new { x.DataID, x.Provider.Slug }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -225,7 +225,7 @@ namespace Kyoo.Tests.Database
|
||||
Assert.Equal("reset", edited.Slug);
|
||||
Assert.Equal("Reset", edited.Title);
|
||||
Assert.Null(edited.Aliases);
|
||||
Assert.Null(edited.ExternalIDs);
|
||||
Assert.Null(edited.ExternalId);
|
||||
Assert.Null(edited.People);
|
||||
Assert.Null(edited.Genres);
|
||||
Assert.Null(edited.Studio);
|
||||
@ -237,7 +237,7 @@ namespace Kyoo.Tests.Database
|
||||
Show expected = TestSample.Get<Show>();
|
||||
expected.ID = 0;
|
||||
expected.Slug = "created-relation-test";
|
||||
expected.ExternalIDs = new[]
|
||||
expected.ExternalId = new[]
|
||||
{
|
||||
new MetadataID
|
||||
{
|
||||
@ -269,7 +269,7 @@ namespace Kyoo.Tests.Database
|
||||
|
||||
await using DatabaseContext context = Repositories.Context.New();
|
||||
Show retrieved = await context.Shows
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.Include(x => x.Genres)
|
||||
.Include(x => x.People)
|
||||
@ -300,7 +300,7 @@ namespace Kyoo.Tests.Database
|
||||
Show expected = TestSample.Get<Show>();
|
||||
expected.ID = 0;
|
||||
expected.Slug = "created-relation-test";
|
||||
expected.ExternalIDs = new[]
|
||||
expected.ExternalId = new[]
|
||||
{
|
||||
new MetadataID
|
||||
{
|
||||
@ -312,12 +312,12 @@ namespace Kyoo.Tests.Database
|
||||
KAssert.DeepEqual(expected, created);
|
||||
await using DatabaseContext context = Repositories.Context.New();
|
||||
Show retrieved = await context.Shows
|
||||
.Include(x => x.ExternalIDs)
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync(x => x.ID == created.ID);
|
||||
KAssert.DeepEqual(expected, retrieved);
|
||||
Assert.Single(retrieved.ExternalIDs);
|
||||
Assert.Equal("ID", retrieved.ExternalIDs.First().DataID);
|
||||
Assert.Single(retrieved.ExternalId);
|
||||
Assert.Equal("ID", retrieved.ExternalId.First().DataID);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -350,14 +350,9 @@ namespace Kyoo.Tests
|
||||
people.ID = 0;
|
||||
context.People.Add(people);
|
||||
|
||||
Provider provider = Get<Provider>();
|
||||
provider.ID = 0;
|
||||
context.Providers.Add(provider);
|
||||
|
||||
Library library = Get<Library>();
|
||||
library.ID = 0;
|
||||
library.Collections = new List<Collection> { collection };
|
||||
library.Providers = new List<Provider> { provider };
|
||||
context.Libraries.Add(library);
|
||||
|
||||
User user = Get<User>();
|
||||
|
@ -353,69 +353,54 @@ namespace Kyoo.Tests.Utility
|
||||
{
|
||||
ID = 5,
|
||||
Name = "merged",
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Logo] = "logo",
|
||||
[Images.Poster] = "poster"
|
||||
}
|
||||
|
||||
};
|
||||
Collection collection2 = new()
|
||||
{
|
||||
Name = "test",
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Poster] = "new-poster",
|
||||
[Images.Thumbnail] = "thumbnails"
|
||||
}
|
||||
};
|
||||
Collection ret = Merger.Complete(collection, collection2);
|
||||
Assert.True(ReferenceEquals(collection, ret));
|
||||
Assert.Equal(5, ret.ID);
|
||||
Assert.Equal("test", ret.Name);
|
||||
Assert.Null(ret.Slug);
|
||||
Assert.Equal(3, ret.Images.Count);
|
||||
Assert.Equal("new-poster", ret.Images[Images.Poster]);
|
||||
Assert.Equal("thumbnails", ret.Images[Images.Thumbnail]);
|
||||
Assert.Equal("logo", ret.Images[Images.Logo]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompleteDictionaryOutParam()
|
||||
{
|
||||
Dictionary<int, string> first = new()
|
||||
Dictionary<string, string> first = new()
|
||||
{
|
||||
[Images.Logo] = "logo",
|
||||
[Images.Poster] = "poster"
|
||||
["logo"] = "logo",
|
||||
["poster"] = "poster"
|
||||
};
|
||||
Dictionary<int, string> second = new()
|
||||
Dictionary<string, string> second = new()
|
||||
{
|
||||
[Images.Poster] = "new-poster",
|
||||
[Images.Thumbnail] = "thumbnails"
|
||||
["poster"] = "new-poster",
|
||||
["thumbnail"] = "thumbnails"
|
||||
};
|
||||
IDictionary<int, string> ret = Merger.CompleteDictionaries(first, second, out bool changed);
|
||||
IDictionary<string, string> ret = Merger.CompleteDictionaries(first, second, out bool changed);
|
||||
Assert.True(changed);
|
||||
Assert.Equal(3, ret.Count);
|
||||
Assert.Equal("new-poster", ret[Images.Poster]);
|
||||
Assert.Equal("thumbnails", ret[Images.Thumbnail]);
|
||||
Assert.Equal("logo", ret[Images.Logo]);
|
||||
Assert.Equal("new-poster", ret["poster"]);
|
||||
Assert.Equal("thumbnails", ret["thumbnail"]);
|
||||
Assert.Equal("logo", ret["logo"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompleteDictionaryEqualTest()
|
||||
{
|
||||
Dictionary<int, string> first = new()
|
||||
Dictionary<string, string> first = new()
|
||||
{
|
||||
[Images.Poster] = "poster"
|
||||
["poster"] = "poster"
|
||||
};
|
||||
Dictionary<int, string> second = new()
|
||||
Dictionary<string, string> second = new()
|
||||
{
|
||||
[Images.Poster] = "new-poster",
|
||||
["poster"] = "new-poster",
|
||||
};
|
||||
IDictionary<int, string> ret = Merger.CompleteDictionaries(first, second, out bool changed);
|
||||
IDictionary<string, string> ret = Merger.CompleteDictionaries(first, second, out bool changed);
|
||||
Assert.True(changed);
|
||||
Assert.Single(ret);
|
||||
Assert.Equal("new-poster", ret[Images.Poster]);
|
||||
Assert.Equal("new-poster", ret["poster"]);
|
||||
}
|
||||
|
||||
private class TestMergeSetter
|
||||
@ -473,81 +458,81 @@ namespace Kyoo.Tests.Utility
|
||||
[Fact]
|
||||
public void MergeDictionaryNullValue()
|
||||
{
|
||||
Dictionary<int, string> first = new()
|
||||
Dictionary<string, string> first = new()
|
||||
{
|
||||
[Images.Logo] = "logo",
|
||||
[Images.Poster] = null
|
||||
["logo"] = "logo",
|
||||
["poster"] = null
|
||||
};
|
||||
Dictionary<int, string> second = new()
|
||||
Dictionary<string, string> second = new()
|
||||
{
|
||||
[Images.Poster] = "new-poster",
|
||||
[Images.Thumbnail] = "thumbnails"
|
||||
["poster"] = "new-poster",
|
||||
["thumbnail"] = "thumbnails"
|
||||
};
|
||||
IDictionary<int, string> ret = Merger.MergeDictionaries(first, second, out bool changed);
|
||||
IDictionary<string, string> ret = Merger.MergeDictionaries(first, second, out bool changed);
|
||||
Assert.True(changed);
|
||||
Assert.Equal(3, ret.Count);
|
||||
Assert.Equal("new-poster", ret[Images.Poster]);
|
||||
Assert.Equal("thumbnails", ret[Images.Thumbnail]);
|
||||
Assert.Equal("logo", ret[Images.Logo]);
|
||||
Assert.Equal("new-poster", ret["poster"]);
|
||||
Assert.Equal("thumbnails", ret["thumbnail"]);
|
||||
Assert.Equal("logo", ret["logo"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeDictionaryNullValueNoChange()
|
||||
{
|
||||
Dictionary<int, string> first = new()
|
||||
Dictionary<string, string> first = new()
|
||||
{
|
||||
[Images.Logo] = "logo",
|
||||
[Images.Poster] = null
|
||||
["logo"] = "logo",
|
||||
["poster"] = null
|
||||
};
|
||||
Dictionary<int, string> second = new()
|
||||
Dictionary<string, string> second = new()
|
||||
{
|
||||
[Images.Poster] = null,
|
||||
["poster"] = null,
|
||||
};
|
||||
IDictionary<int, string> ret = Merger.MergeDictionaries(first, second, out bool changed);
|
||||
IDictionary<string, string> ret = Merger.MergeDictionaries(first, second, out bool changed);
|
||||
Assert.False(changed);
|
||||
Assert.Equal(2, ret.Count);
|
||||
Assert.Null(ret[Images.Poster]);
|
||||
Assert.Equal("logo", ret[Images.Logo]);
|
||||
Assert.Null(ret["poster"]);
|
||||
Assert.Equal("logo", ret["logo"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompleteDictionaryNullValue()
|
||||
{
|
||||
Dictionary<int, string> first = new()
|
||||
Dictionary<string, string> first = new()
|
||||
{
|
||||
[Images.Logo] = "logo",
|
||||
[Images.Poster] = null
|
||||
["logo"] = "logo",
|
||||
["poster"] = null
|
||||
};
|
||||
Dictionary<int, string> second = new()
|
||||
Dictionary<string, string> second = new()
|
||||
{
|
||||
[Images.Poster] = "new-poster",
|
||||
[Images.Thumbnail] = "thumbnails"
|
||||
["poster"] = "new-poster",
|
||||
["thumbnail"] = "thumbnails"
|
||||
};
|
||||
IDictionary<int, string> ret = Merger.CompleteDictionaries(first, second, out bool changed);
|
||||
IDictionary<string, string> ret = Merger.CompleteDictionaries(first, second, out bool changed);
|
||||
Assert.True(changed);
|
||||
Assert.Equal(3, ret.Count);
|
||||
Assert.Equal("new-poster", ret[Images.Poster]);
|
||||
Assert.Equal("thumbnails", ret[Images.Thumbnail]);
|
||||
Assert.Equal("logo", ret[Images.Logo]);
|
||||
Assert.Equal("new-poster", ret["poster"]);
|
||||
Assert.Equal("thumbnails", ret["thumbnail"]);
|
||||
Assert.Equal("logo", ret["logo"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompleteDictionaryNullValueNoChange()
|
||||
{
|
||||
Dictionary<int, string> first = new()
|
||||
Dictionary<string, string> first = new()
|
||||
{
|
||||
[Images.Logo] = "logo",
|
||||
[Images.Poster] = null
|
||||
["logo"] = "logo",
|
||||
["poster"] = null
|
||||
};
|
||||
Dictionary<int, string> second = new()
|
||||
Dictionary<string, string> second = new()
|
||||
{
|
||||
[Images.Poster] = null,
|
||||
["poster"] = null,
|
||||
};
|
||||
IDictionary<int, string> ret = Merger.CompleteDictionaries(first, second, out bool changed);
|
||||
IDictionary<string, string> ret = Merger.CompleteDictionaries(first, second, out bool changed);
|
||||
Assert.False(changed);
|
||||
Assert.Equal(2, ret.Count);
|
||||
Assert.Null(ret[Images.Poster]);
|
||||
Assert.Equal("logo", ret[Images.Logo]);
|
||||
Assert.Null(ret["poster"]);
|
||||
Assert.Equal("logo", ret["logo"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ namespace Kyoo.Tests.Utility
|
||||
Assert.True(KUtility.IsPropertyExpression(member));
|
||||
Assert.True(KUtility.IsPropertyExpression(memberCast));
|
||||
|
||||
Expression<Func<Show, object>> call = x => x.GetID("test");
|
||||
Expression<Func<Show, object>> call = x => x.ToString();
|
||||
Assert.False(KUtility.IsPropertyExpression(call));
|
||||
}
|
||||
|
||||
|
@ -102,7 +102,7 @@ const Menu = <AsProps,>({
|
||||
}),
|
||||
])}
|
||||
>
|
||||
<ScrollView {...css([])}>
|
||||
<ScrollView>
|
||||
<IconButton
|
||||
icon={Close}
|
||||
color={theme.colors.black}
|
||||
|
@ -44,7 +44,5 @@ class Episode:
|
||||
"images": {
|
||||
"1": self.thumbnail,
|
||||
},
|
||||
# TODO: The back has bad external id support, we disable it for now
|
||||
"external_ids": None,
|
||||
"show": None,
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user