mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-11-01 11:07:14 -04:00
commit
adb6bdb3f2
27
Kyoo.Common/Controllers/IFileManager.cs
Normal file
27
Kyoo.Common/Controllers/IFileManager.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public interface IFileManager
|
||||
{
|
||||
public IActionResult FileResult([CanBeNull] string path, bool rangeSupport = false);
|
||||
|
||||
public StreamReader GetReader([NotNull] string path);
|
||||
|
||||
public Task<ICollection<string>> ListFiles([NotNull] string path);
|
||||
|
||||
public Task<bool> Exists([NotNull] string path);
|
||||
// TODO find a way to handle Transmux/Transcode with this system.
|
||||
|
||||
public string GetExtraDirectory(Show show);
|
||||
|
||||
public string GetExtraDirectory(Season season);
|
||||
|
||||
public string GetExtraDirectory(Episode episode);
|
||||
}
|
||||
}
|
||||
@ -35,6 +35,7 @@ namespace Kyoo.Controllers
|
||||
Task<Track> GetTrack(int id);
|
||||
Task<Studio> GetStudio(int id);
|
||||
Task<People> GetPeople(int id);
|
||||
Task<ProviderID> GetProvider(int id);
|
||||
|
||||
// Get by slug
|
||||
Task<Library> GetLibrary(string slug);
|
||||
@ -49,6 +50,7 @@ namespace Kyoo.Controllers
|
||||
Task<Genre> GetGenre(string slug);
|
||||
Task<Studio> GetStudio(string slug);
|
||||
Task<People> GetPeople(string slug);
|
||||
Task<ProviderID> GetProvider(string slug);
|
||||
|
||||
// Get by predicate
|
||||
Task<Library> GetLibrary(Expression<Func<Library, bool>> where);
|
||||
@ -60,7 +62,20 @@ namespace Kyoo.Controllers
|
||||
Task<Genre> GetGenre(Expression<Func<Genre, bool>> where);
|
||||
Task<Studio> GetStudio(Expression<Func<Studio, bool>> where);
|
||||
Task<People> GetPerson(Expression<Func<People, bool>> where);
|
||||
|
||||
|
||||
Task<T> Load<T, T2>([NotNull] T obj, Expression<Func<T, T2>> member)
|
||||
where T : class, IResource
|
||||
where T2 : class, IResource, new();
|
||||
|
||||
Task<T> Load<T, T2>([NotNull] T obj, Expression<Func<T, ICollection<T2>>> member)
|
||||
where T : class, IResource
|
||||
where T2 : class, new();
|
||||
|
||||
Task<T> Load<T>([NotNull] T obj, string memberName)
|
||||
where T : class, IResource;
|
||||
|
||||
Task Load([NotNull] IResource obj, string memberName);
|
||||
|
||||
// Library Items relations
|
||||
Task<ICollection<LibraryItem>> GetItemsFromLibrary(int id,
|
||||
Expression<Func<LibraryItem, bool>> where = null,
|
||||
@ -104,25 +119,25 @@ namespace Kyoo.Controllers
|
||||
) => GetPeopleFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
// Show Role relations
|
||||
Task<ICollection<ShowRole>> GetRolesFromPeople(int showID,
|
||||
Expression<Func<ShowRole, bool>> where = null,
|
||||
Sort<ShowRole> sort = default,
|
||||
Task<ICollection<PeopleRole>> GetRolesFromPeople(int showID,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
Task<ICollection<ShowRole>> GetRolesFromPeople(int showID,
|
||||
[Optional] Expression<Func<ShowRole, bool>> where,
|
||||
Expression<Func<ShowRole, object>> sort,
|
||||
Task<ICollection<PeopleRole>> GetRolesFromPeople(int showID,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetRolesFromPeople(showID, where, new Sort<ShowRole>(sort), limit);
|
||||
) => GetRolesFromPeople(showID, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
Task<ICollection<ShowRole>> GetRolesFromPeople(string showSlug,
|
||||
Expression<Func<ShowRole, bool>> where = null,
|
||||
Sort<ShowRole> sort = default,
|
||||
Task<ICollection<PeopleRole>> GetRolesFromPeople(string showSlug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
Task<ICollection<ShowRole>> GetRolesFromPeople(string showSlug,
|
||||
[Optional] Expression<Func<ShowRole, bool>> where,
|
||||
Expression<Func<ShowRole, object>> sort,
|
||||
Task<ICollection<PeopleRole>> GetRolesFromPeople(string showSlug,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetRolesFromPeople(showSlug, where, new Sort<ShowRole>(sort), limit);
|
||||
) => GetRolesFromPeople(showSlug, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
// Helpers
|
||||
Task AddShowLink(int showID, int? libraryID, int? collectionID);
|
||||
@ -236,10 +251,9 @@ namespace Kyoo.Controllers
|
||||
Task<Genre> EditGenre(Genre genre, bool resetOld);
|
||||
Task<Studio> EditStudio(Studio studio, bool resetOld);
|
||||
Task<People> EditPeople(People people, bool resetOld);
|
||||
|
||||
|
||||
// Delete values
|
||||
Task DelteLibrary(Library library);
|
||||
Task DeleteLibrary(Library library);
|
||||
Task DeleteCollection(Collection collection);
|
||||
Task DeleteShow(Show show);
|
||||
Task DeleteSeason(Season season);
|
||||
@ -250,7 +264,7 @@ namespace Kyoo.Controllers
|
||||
Task DeletePeople(People people);
|
||||
|
||||
//Delete by slug
|
||||
Task DelteLibrary(string slug);
|
||||
Task DeleteLibrary(string slug);
|
||||
Task DeleteCollection(string slug);
|
||||
Task DeleteShow(string slug);
|
||||
Task DeleteSeason(string slug);
|
||||
@ -261,7 +275,7 @@ namespace Kyoo.Controllers
|
||||
Task DeletePeople(string slug);
|
||||
|
||||
//Delete by id
|
||||
Task DelteLibrary(int id);
|
||||
Task DeleteLibrary(int id);
|
||||
Task DeleteCollection(int id);
|
||||
Task DeleteShow(int id);
|
||||
Task DeleteSeason(int id);
|
||||
|
||||
@ -11,8 +11,8 @@ namespace Kyoo.Controllers
|
||||
Task<Collection> GetCollectionFromName(string name);
|
||||
|
||||
Task<Show> GetShowByID(Show show);
|
||||
Task<IEnumerable<Show>> SearchShows(string showName, bool isMovie);
|
||||
Task<IEnumerable<PeopleRole>> GetPeople(Show show);
|
||||
Task<ICollection<Show>> SearchShows(string showName, bool isMovie);
|
||||
Task<ICollection<PeopleRole>> GetPeople(Show show);
|
||||
|
||||
Task<Season> GetSeason(Show show, int seasonNumber);
|
||||
|
||||
|
||||
@ -12,6 +12,6 @@ namespace Kyoo.Controllers
|
||||
Task<IEnumerable<Show>> SearchShows(string showName, bool isMovie, Library library);
|
||||
Task<Season> GetSeason(Show show, int seasonNumber, Library library);
|
||||
Task<Episode> GetEpisode(Show show, string episodePath, int seasonNumber, int episodeNumber, int absoluteNumber, Library library);
|
||||
Task<IEnumerable<PeopleRole>> GetPeople(Show show, Library library);
|
||||
Task<ICollection<PeopleRole>> GetPeople(Show show, Library library);
|
||||
}
|
||||
}
|
||||
@ -20,7 +20,7 @@ namespace Kyoo.Controllers
|
||||
AfterID = afterID;
|
||||
}
|
||||
|
||||
public static implicit operator Pagination(int limit) => new Pagination(limit);
|
||||
public static implicit operator Pagination(int limit) => new(limit);
|
||||
}
|
||||
|
||||
public struct Sort<T>
|
||||
@ -30,15 +30,11 @@ namespace Kyoo.Controllers
|
||||
|
||||
public Sort(Expression<Func<T, object>> key, bool descendant = false)
|
||||
{
|
||||
Key = ExpressionRewrite.Rewrite<Func<T, object>>(key);
|
||||
Key = key;
|
||||
Descendant = descendant;
|
||||
|
||||
if (Key == null ||
|
||||
Key.Body is MemberExpression ||
|
||||
Key.Body.NodeType == ExpressionType.Convert && ((UnaryExpression)Key.Body).Operand is MemberExpression)
|
||||
return;
|
||||
|
||||
throw new ArgumentException("The given sort key is not valid.");
|
||||
if (!Utility.IsPropertyExpression(Key))
|
||||
throw new ArgumentException("The given sort key is not valid.");
|
||||
}
|
||||
|
||||
public Sort(string sortBy)
|
||||
@ -58,8 +54,7 @@ namespace Kyoo.Controllers
|
||||
Key = property.Type.IsValueType
|
||||
? Expression.Lambda<Func<T, object>>(Expression.Convert(property, typeof(object)), param)
|
||||
: Expression.Lambda<Func<T, object>>(property, param);
|
||||
Key = ExpressionRewrite.Rewrite<Func<T, object>>(Key);
|
||||
|
||||
|
||||
Descendant = order switch
|
||||
{
|
||||
"desc" => true,
|
||||
@ -68,11 +63,6 @@ namespace Kyoo.Controllers
|
||||
_ => throw new ArgumentException($"The sort order, if set, should be :asc or :desc but it was :{order}.")
|
||||
};
|
||||
}
|
||||
|
||||
public Sort<TValue> To<TValue>()
|
||||
{
|
||||
return new Sort<TValue>(Key.Convert<Func<TValue, object>>(), Descendant);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IRepository<T> : IDisposable, IAsyncDisposable where T : class, IResource
|
||||
@ -108,11 +98,14 @@ namespace Kyoo.Controllers
|
||||
Task DeleteRange(IEnumerable<int> ids);
|
||||
Task DeleteRange(params string[] slugs) => DeleteRange(slugs.AsEnumerable());
|
||||
Task DeleteRange(IEnumerable<string> slugs);
|
||||
Task DeleteRange([NotNull] Expression<Func<T, bool>> where);
|
||||
}
|
||||
|
||||
public interface IShowRepository : IRepository<Show>
|
||||
{
|
||||
Task AddShowLink(int showID, int? libraryID, int? collectionID);
|
||||
|
||||
Task<string> GetSlug(int showID);
|
||||
}
|
||||
|
||||
public interface ISeasonRepository : IRepository<Season>
|
||||
@ -190,26 +183,36 @@ namespace Kyoo.Controllers
|
||||
Pagination limit = default
|
||||
) => GetFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
Task<ICollection<ShowRole>> GetFromPeople(int showID,
|
||||
Expression<Func<ShowRole, bool>> where = null,
|
||||
Sort<ShowRole> sort = default,
|
||||
Task<ICollection<PeopleRole>> GetFromPeople(int showID,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
Task<ICollection<ShowRole>> GetFromPeople(int showID,
|
||||
[Optional] Expression<Func<ShowRole, bool>> where,
|
||||
Expression<Func<ShowRole, object>> sort,
|
||||
Task<ICollection<PeopleRole>> GetFromPeople(int showID,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetFromPeople(showID, where, new Sort<ShowRole>(sort), limit);
|
||||
) => GetFromPeople(showID, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
Task<ICollection<ShowRole>> GetFromPeople(string showSlug,
|
||||
Expression<Func<ShowRole, bool>> where = null,
|
||||
Sort<ShowRole> sort = default,
|
||||
Task<ICollection<PeopleRole>> GetFromPeople(string showSlug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
Task<ICollection<ShowRole>> GetFromPeople(string showSlug,
|
||||
[Optional] Expression<Func<ShowRole, bool>> where,
|
||||
Expression<Func<ShowRole, object>> sort,
|
||||
Task<ICollection<PeopleRole>> GetFromPeople(string showSlug,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetFromPeople(showSlug, where, new Sort<ShowRole>(sort), limit);
|
||||
) => GetFromPeople(showSlug, where, new Sort<PeopleRole>(sort), limit);
|
||||
}
|
||||
|
||||
public interface IProviderRepository : IRepository<ProviderID>
|
||||
{
|
||||
Task<ICollection<MetadataID>> GetMetadataID(Expression<Func<MetadataID, bool>> where = null,
|
||||
Sort<MetadataID> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
Task<ICollection<MetadataID>> GetMetadataID([Optional] Expression<Func<MetadataID, bool>> where,
|
||||
Expression<Func<MetadataID, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetMetadataID(where, new Sort<MetadataID>(sort), limit);
|
||||
}
|
||||
|
||||
public interface IProviderRepository : IRepository<ProviderID> {}
|
||||
}
|
||||
|
||||
@ -1,14 +1,24 @@
|
||||
using Kyoo.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public interface IThumbnailsManager
|
||||
{
|
||||
Task<Show> Validate(Show show, bool alwaysDownload = false);
|
||||
Task<Season> Validate(Season season, bool alwaysDownload = false);
|
||||
Task<Episode> Validate(Episode episode, bool alwaysDownload = false);
|
||||
Task<IEnumerable<PeopleRole>> Validate(IEnumerable<PeopleRole> actors, bool alwaysDownload = false);
|
||||
Task Validate(Show show, bool alwaysDownload = false);
|
||||
Task Validate(Season season, bool alwaysDownload = false);
|
||||
Task Validate(Episode episode, bool alwaysDownload = false);
|
||||
Task Validate(People actors, bool alwaysDownload = false);
|
||||
Task Validate(ProviderID actors, bool alwaysDownload = false);
|
||||
|
||||
Task<string> GetShowPoster([NotNull] Show show);
|
||||
Task<string> GetShowLogo([NotNull] Show show);
|
||||
Task<string> GetShowBackdrop([NotNull] Show show);
|
||||
Task<string> GetSeasonPoster([NotNull] Season season);
|
||||
Task<string> GetEpisodeThumb([NotNull] Episode episode);
|
||||
Task<string> GetPeoplePoster([NotNull] People people);
|
||||
Task<string> GetProviderLogo([NotNull] ProviderID provider);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Watch;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public interface ITranscoder
|
||||
{
|
||||
Task<Track[]> ExtractInfos(string path);
|
||||
Task<Track[]> ExtractInfos(Episode episode, bool reextract);
|
||||
Task<string> Transmux(Episode episode);
|
||||
Task<string> Transcode(Episode episode);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Models;
|
||||
@ -19,7 +20,7 @@ namespace Kyoo.Controllers
|
||||
public IStudioRepository StudioRepository { get; }
|
||||
public IPeopleRepository PeopleRepository { get; }
|
||||
public IProviderRepository ProviderRepository { get; }
|
||||
|
||||
|
||||
public LibraryManager(ILibraryRepository libraryRepository,
|
||||
ILibraryItemRepository libraryItemRepository,
|
||||
ICollectionRepository collectionRepository,
|
||||
@ -130,6 +131,11 @@ namespace Kyoo.Controllers
|
||||
return PeopleRepository.Get(id);
|
||||
}
|
||||
|
||||
public Task<ProviderID> GetProvider(int id)
|
||||
{
|
||||
return ProviderRepository.Get(id);
|
||||
}
|
||||
|
||||
public Task<Library> GetLibrary(string slug)
|
||||
{
|
||||
return LibraryRepository.Get(slug);
|
||||
@ -189,6 +195,11 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
return PeopleRepository.Get(slug);
|
||||
}
|
||||
|
||||
public Task<ProviderID> GetProvider(string slug)
|
||||
{
|
||||
return ProviderRepository.Get(slug);
|
||||
}
|
||||
|
||||
public Task<Library> GetLibrary(Expression<Func<Library, bool>> where)
|
||||
{
|
||||
@ -235,6 +246,195 @@ namespace Kyoo.Controllers
|
||||
return PeopleRepository.Get(where);
|
||||
}
|
||||
|
||||
public Task<T> Load<T, T2>(T obj, Expression<Func<T, T2>> member)
|
||||
where T : class, IResource
|
||||
where T2 : class, IResource, new()
|
||||
{
|
||||
if (member == null)
|
||||
throw new ArgumentNullException(nameof(member));
|
||||
return Load(obj, Utility.GetPropertyName(member));
|
||||
}
|
||||
|
||||
public Task<T> Load<T, T2>(T obj, Expression<Func<T, ICollection<T2>>> member)
|
||||
where T : class, IResource
|
||||
where T2 : class, new()
|
||||
{
|
||||
if (member == null)
|
||||
throw new ArgumentNullException(nameof(member));
|
||||
return Load(obj, Utility.GetPropertyName(member));
|
||||
}
|
||||
|
||||
public async Task<T> Load<T>(T obj, string member)
|
||||
where T : class, IResource
|
||||
{
|
||||
await Load(obj as IResource, member);
|
||||
return obj;
|
||||
}
|
||||
|
||||
private async Task SetRelation<T1, T2>(T1 obj,
|
||||
Task<ICollection<T2>> loader,
|
||||
Action<T1, ICollection<T2>> setter,
|
||||
Action<T2, T1> inverse)
|
||||
{
|
||||
ICollection<T2> loaded = await loader;
|
||||
setter(obj, loaded);
|
||||
foreach (T2 item in loaded)
|
||||
inverse(item, obj);
|
||||
}
|
||||
|
||||
public Task Load(IResource obj, string member)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
return (obj, member) 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),
|
||||
|
||||
(Library l, nameof(Library.Collections)) => CollectionRepository
|
||||
.GetAll(x => x.Libraries.Any(y => y.ID == obj.ID))
|
||||
.Then(x => l.Collections = x),
|
||||
|
||||
|
||||
(Collection c, nameof(Library.Shows)) => ShowRepository
|
||||
.GetAll(x => x.Collections.Any(y => y.ID == obj.ID))
|
||||
.Then(x => c.Shows = x),
|
||||
|
||||
(Collection c, nameof(Collection.Libraries)) => LibraryRepository
|
||||
.GetAll(x => x.Collections.Any(y => y.ID == obj.ID))
|
||||
.Then(x => c.Libraries = x),
|
||||
|
||||
|
||||
(Show s, nameof(Show.ExternalIDs)) => SetRelation(s,
|
||||
ProviderRepository.GetMetadataID(x => x.ShowID == obj.ID),
|
||||
(x, y) => x.ExternalIDs = y,
|
||||
(x, y) => { x.Show = y; x.ShowID = y.ID; }),
|
||||
|
||||
(Show s, nameof(Show.Genres)) => GenreRepository
|
||||
.GetAll(x => x.Shows.Any(y => y.ID == obj.ID))
|
||||
.Then(x => s.Genres = x),
|
||||
|
||||
(Show s, nameof(Show.People)) => PeopleRepository
|
||||
.GetFromShow(obj.ID)
|
||||
.Then(x => s.People = x),
|
||||
|
||||
(Show s, nameof(Show.Seasons)) => SetRelation(s,
|
||||
SeasonRepository.GetAll(x => x.Show.ID == obj.ID),
|
||||
(x, y) => x.Seasons = y,
|
||||
(x, y) => { x.Show = y; x.ShowID = y.ID; }),
|
||||
|
||||
(Show s, nameof(Show.Episodes)) => SetRelation(s,
|
||||
EpisodeRepository.GetAll(x => x.Show.ID == obj.ID),
|
||||
(x, y) => x.Episodes = y,
|
||||
(x, y) => { x.Show = y; x.ShowID = y.ID; }),
|
||||
|
||||
(Show s, nameof(Show.Libraries)) => LibraryRepository
|
||||
.GetAll(x => x.Shows.Any(y => y.ID == obj.ID))
|
||||
.Then(x => s.Libraries = x),
|
||||
|
||||
(Show s, nameof(Show.Collections)) => CollectionRepository
|
||||
.GetAll(x => x.Shows.Any(y => y.ID == obj.ID))
|
||||
.Then(x => s.Collections = x),
|
||||
|
||||
(Show s, nameof(Show.Studio)) => StudioRepository
|
||||
.Get(x => x.Shows.Any(y => y.ID == obj.ID))
|
||||
.Then(x =>
|
||||
{
|
||||
s.Studio = x;
|
||||
s.StudioID = x?.ID ?? 0;
|
||||
}),
|
||||
|
||||
|
||||
(Season s, nameof(Season.ExternalIDs)) => SetRelation(s,
|
||||
ProviderRepository.GetMetadataID(x => x.SeasonID == obj.ID),
|
||||
(x, y) => x.ExternalIDs = y,
|
||||
(x, y) => { x.Season = y; x.SeasonID = y.ID; }),
|
||||
|
||||
(Season s, nameof(Season.Episodes)) => SetRelation(s,
|
||||
EpisodeRepository.GetAll(x => x.Season.ID == obj.ID),
|
||||
(x, y) => x.Episodes = y,
|
||||
(x, y) => { x.Season = y; x.SeasonID = y.ID; }),
|
||||
|
||||
(Season s, nameof(Season.Show)) => ShowRepository
|
||||
.Get(x => x.Seasons.Any(y => y.ID == obj.ID))
|
||||
.Then(x =>
|
||||
{
|
||||
s.Show = x;
|
||||
s.ShowID = x?.ID ?? 0;
|
||||
}),
|
||||
|
||||
|
||||
(Episode e, nameof(Episode.ExternalIDs)) => SetRelation(e,
|
||||
ProviderRepository.GetMetadataID(x => x.EpisodeID == obj.ID),
|
||||
(x, y) => x.ExternalIDs = y,
|
||||
(x, y) => { x.Episode = y; x.EpisodeID = y.ID; }),
|
||||
|
||||
(Episode e, nameof(Episode.Tracks)) => SetRelation(e,
|
||||
TrackRepository.GetAll(x => x.Episode.ID == obj.ID),
|
||||
(x, y) => x.Tracks = y,
|
||||
(x, y) => { x.Episode = y; x.EpisodeID = y.ID; }),
|
||||
|
||||
(Episode e, nameof(Episode.Show)) => ShowRepository
|
||||
.Get(x => x.Episodes.Any(y => y.ID == obj.ID))
|
||||
.Then(x =>
|
||||
{
|
||||
e.Show = x;
|
||||
e.ShowID = x?.ID ?? 0;
|
||||
}),
|
||||
|
||||
(Episode e, nameof(Episode.Season)) => SeasonRepository
|
||||
.Get(x => x.Episodes.Any(y => y.ID == e.ID))
|
||||
.Then(x =>
|
||||
{
|
||||
e.Season = x;
|
||||
e.SeasonID = x?.ID ?? 0;
|
||||
}),
|
||||
|
||||
|
||||
(Track t, nameof(Track.Episode)) => EpisodeRepository
|
||||
.Get(x => x.Tracks.Any(y => y.ID == obj.ID))
|
||||
.Then(x =>
|
||||
{
|
||||
t.Episode = x;
|
||||
t.EpisodeID = x?.ID ?? 0;
|
||||
}),
|
||||
|
||||
|
||||
(Genre g, nameof(Genre.Shows)) => ShowRepository
|
||||
.GetAll(x => x.Genres.Any(y => y.ID == obj.ID))
|
||||
.Then(x => g.Shows = x),
|
||||
|
||||
|
||||
(Studio s, nameof(Studio.Shows)) => ShowRepository
|
||||
.GetAll(x => x.Studio.ID == obj.ID)
|
||||
.Then(x => s.Shows = x),
|
||||
|
||||
|
||||
(People p, nameof(People.ExternalIDs)) => SetRelation(p,
|
||||
ProviderRepository.GetMetadataID(x => x.PeopleID == obj.ID),
|
||||
(x, y) => x.ExternalIDs = y,
|
||||
(x, y) => { x.People = y; x.PeopleID = y.ID; }),
|
||||
|
||||
(People p, nameof(People.Roles)) => PeopleRepository
|
||||
.GetFromPeople(obj.ID)
|
||||
.Then(x => p.Roles = x),
|
||||
|
||||
|
||||
(ProviderID p, nameof(ProviderID.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 {member} of {obj.Slug}.")
|
||||
};
|
||||
}
|
||||
|
||||
public Task<ICollection<Library>> GetLibraries(Expression<Func<Library, bool>> where = null,
|
||||
Sort<Library> sort = default,
|
||||
Pagination page = default)
|
||||
@ -337,17 +537,17 @@ namespace Kyoo.Controllers
|
||||
return PeopleRepository.GetFromShow(showSlug, where, sort, limit);
|
||||
}
|
||||
|
||||
public Task<ICollection<ShowRole>> GetRolesFromPeople(int id,
|
||||
Expression<Func<ShowRole, bool>> where = null,
|
||||
Sort<ShowRole> sort = default,
|
||||
public Task<ICollection<PeopleRole>> GetRolesFromPeople(int id,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
return PeopleRepository.GetFromPeople(id, where, sort, limit);
|
||||
}
|
||||
|
||||
public Task<ICollection<ShowRole>> GetRolesFromPeople(string slug,
|
||||
Expression<Func<ShowRole, bool>> where = null,
|
||||
Sort<ShowRole> sort = default,
|
||||
public Task<ICollection<PeopleRole>> GetRolesFromPeople(string slug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
return PeopleRepository.GetFromPeople(slug, where, sort, limit);
|
||||
@ -540,7 +740,7 @@ namespace Kyoo.Controllers
|
||||
return PeopleRepository.Edit(people, resetOld);
|
||||
}
|
||||
|
||||
public Task DelteLibrary(Library library)
|
||||
public Task DeleteLibrary(Library library)
|
||||
{
|
||||
return LibraryRepository.Delete(library);
|
||||
}
|
||||
@ -585,7 +785,7 @@ namespace Kyoo.Controllers
|
||||
return PeopleRepository.Delete(people);
|
||||
}
|
||||
|
||||
public Task DelteLibrary(string library)
|
||||
public Task DeleteLibrary(string library)
|
||||
{
|
||||
return LibraryRepository.Delete(library);
|
||||
}
|
||||
@ -630,7 +830,7 @@ namespace Kyoo.Controllers
|
||||
return PeopleRepository.Delete(people);
|
||||
}
|
||||
|
||||
public Task DelteLibrary(int library)
|
||||
public Task DeleteLibrary(int library)
|
||||
{
|
||||
return LibraryRepository.Delete(library);
|
||||
}
|
||||
|
||||
@ -1,120 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Kyoo
|
||||
{
|
||||
public class ExpressionRewriteAttribute : Attribute
|
||||
{
|
||||
public string Link { get; }
|
||||
public string Inner { get; }
|
||||
|
||||
public ExpressionRewriteAttribute(string link, string inner = null)
|
||||
{
|
||||
Link = link;
|
||||
Inner = inner;
|
||||
}
|
||||
}
|
||||
|
||||
public class ExpressionRewrite : ExpressionVisitor
|
||||
{
|
||||
private string _inner;
|
||||
private readonly List<(string inner, ParameterExpression param, ParameterExpression newParam)> _innerRewrites;
|
||||
|
||||
private ExpressionRewrite()
|
||||
{
|
||||
_innerRewrites = new List<(string, ParameterExpression, ParameterExpression)>();
|
||||
}
|
||||
|
||||
public static Expression Rewrite(Expression expression)
|
||||
{
|
||||
return new ExpressionRewrite().Visit(expression);
|
||||
}
|
||||
|
||||
public static Expression<T> Rewrite<T>(Expression expression) where T : Delegate
|
||||
{
|
||||
return (Expression<T>)new ExpressionRewrite().Visit(expression);
|
||||
}
|
||||
|
||||
protected override Expression VisitMember(MemberExpression node)
|
||||
{
|
||||
(string inner, _, ParameterExpression p) = _innerRewrites.FirstOrDefault(x => x.param == node.Expression);
|
||||
if (inner != null)
|
||||
{
|
||||
Expression param = p;
|
||||
foreach (string accessor in inner.Split('.'))
|
||||
param = Expression.Property(param, accessor);
|
||||
node = Expression.Property(param, node.Member.Name);
|
||||
}
|
||||
|
||||
// Can't use node.Member directly because we want to support attribute override
|
||||
MemberInfo member = node.Expression.Type.GetProperty(node.Member.Name) ?? node.Member;
|
||||
ExpressionRewriteAttribute attr = member!.GetCustomAttribute<ExpressionRewriteAttribute>();
|
||||
if (attr == null)
|
||||
return base.VisitMember(node);
|
||||
|
||||
Expression property = node.Expression;
|
||||
foreach (string child in attr.Link.Split('.'))
|
||||
property = Expression.Property(property, child);
|
||||
|
||||
if (property is MemberExpression expr)
|
||||
Visit(expr.Expression);
|
||||
_inner = attr.Inner;
|
||||
return property;
|
||||
}
|
||||
|
||||
protected override Expression VisitLambda<T>(Expression<T> node)
|
||||
{
|
||||
(_, ParameterExpression oldParam, ParameterExpression param) = _innerRewrites
|
||||
.FirstOrDefault(x => node.Parameters.Any(y => y == x.param));
|
||||
if (param == null)
|
||||
return base.VisitLambda(node);
|
||||
|
||||
ParameterExpression[] newParams = node.Parameters.Where(x => x != oldParam).Append(param).ToArray();
|
||||
return Expression.Lambda(Visit(node.Body)!, newParams);
|
||||
}
|
||||
|
||||
protected override Expression VisitMethodCall(MethodCallExpression node)
|
||||
{
|
||||
int count = node.Arguments.Count;
|
||||
if (node.Object != null)
|
||||
count++;
|
||||
if (count != 2)
|
||||
return base.VisitMethodCall(node);
|
||||
|
||||
Expression instance = node.Object ?? node.Arguments.First();
|
||||
Expression argument = node.Object != null
|
||||
? node.Arguments.First()
|
||||
: node.Arguments[1];
|
||||
|
||||
Type oldType = instance.Type;
|
||||
instance = Visit(instance);
|
||||
if (instance!.Type == oldType)
|
||||
return base.VisitMethodCall(node);
|
||||
|
||||
if (_inner != null && argument is LambdaExpression lambda)
|
||||
{
|
||||
// TODO this type handler will usually work with IEnumerable & others but won't work with everything.
|
||||
Type type = oldType.GetGenericArguments().First();
|
||||
ParameterExpression oldParam = lambda.Parameters.FirstOrDefault(x => x.Type == type);
|
||||
if (oldParam != null)
|
||||
{
|
||||
Type newType = instance.Type.GetGenericArguments().First();
|
||||
ParameterExpression newParam = Expression.Parameter(newType, oldParam.Name);
|
||||
_innerRewrites.Add((_inner, oldParam, newParam));
|
||||
}
|
||||
}
|
||||
argument = Visit(argument);
|
||||
|
||||
// TODO this method handler may not work for some methods (ex: method taking a Fun<> method won't have good generic arguments)
|
||||
MethodInfo method = node.Method.IsGenericMethod
|
||||
? node.Method.GetGenericMethodDefinition().MakeGenericMethod(instance.Type.GetGenericArguments())
|
||||
: node.Method;
|
||||
return node.Object != null
|
||||
? Expression.Call(instance, method!, argument)
|
||||
: Expression.Call(null, method!, instance, argument!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,14 +12,17 @@
|
||||
<Company>SDG</Company>
|
||||
<PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression>
|
||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
||||
<PackageVersion>1.0.22</PackageVersion>
|
||||
<PackageVersion>1.0.23</PackageVersion>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<LangVersion>default</LangVersion>
|
||||
|
||||
<DefineConstants>ENABLE_INTERNAL_LINKS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.0-beta-20204-02" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Kyoo.Models.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property, Inherited = false)]
|
||||
public class EditableRelation : Attribute { }
|
||||
}
|
||||
@ -8,7 +8,4 @@ namespace Kyoo.Models.Attributes
|
||||
{
|
||||
void OnMerge(object merged);
|
||||
}
|
||||
|
||||
public class JsonReadOnly : Attribute { }
|
||||
public class JsonIgnore : JsonReadOnly { }
|
||||
}
|
||||
20
Kyoo.Common/Models/Attributes/RelationAttributes.cs
Normal file
20
Kyoo.Common/Models/Attributes/RelationAttributes.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace Kyoo.Models.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property, Inherited = false)]
|
||||
public class EditableRelationAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class LoadableRelationAttribute : Attribute
|
||||
{
|
||||
public string RelationID { get; }
|
||||
|
||||
public LoadableRelationAttribute() {}
|
||||
|
||||
public LoadableRelationAttribute(string relationID)
|
||||
{
|
||||
RelationID = relationID;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Kyoo.Common/Models/Attributes/SerializeAttribute.cs
Normal file
21
Kyoo.Common/Models/Attributes/SerializeAttribute.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace Kyoo.Models.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||
public class SerializeIgnoreAttribute : Attribute {}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||
public class DeserializeIgnoreAttribute : Attribute {}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||
public class SerializeAsAttribute : Attribute
|
||||
{
|
||||
public string Format { get; }
|
||||
|
||||
public SerializeAsAttribute(string format)
|
||||
{
|
||||
Format = format;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
@ -20,7 +21,8 @@ namespace Kyoo.Models
|
||||
public string TrailerUrl { get; set; }
|
||||
public int? StartYear { get; set; }
|
||||
public int? EndYear { get; set; }
|
||||
public string Poster { get; set; }
|
||||
[SerializeAs("{HOST}/api/{_type}/{Slug}/poster")] public string Poster { get; set; }
|
||||
private string _type => Type == ItemType.Collection ? "collection" : "show";
|
||||
public ItemType Type { get; set; }
|
||||
|
||||
public LibraryItem() {}
|
||||
|
||||
84
Kyoo.Common/Models/Link.cs
Normal file
84
Kyoo.Common/Models/Link.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class Link
|
||||
{
|
||||
public int FirstID { get; set; }
|
||||
public int SecondID { get; set; }
|
||||
|
||||
public Link() {}
|
||||
|
||||
public Link(int firstID, int secondID)
|
||||
{
|
||||
FirstID = firstID;
|
||||
SecondID = secondID;
|
||||
}
|
||||
|
||||
public Link(IResource first, IResource second)
|
||||
{
|
||||
FirstID = first.ID;
|
||||
SecondID = second.ID;
|
||||
}
|
||||
|
||||
public static Link Create(IResource first, IResource second)
|
||||
{
|
||||
return new(first, second);
|
||||
}
|
||||
|
||||
public static Link<T, T2> Create<T, T2>(T first, T2 second)
|
||||
where T : class, IResource
|
||||
where T2 : class, IResource
|
||||
{
|
||||
return new(first, second);
|
||||
}
|
||||
|
||||
public static Link<T, T2> UCreate<T, T2>(T first, T2 second)
|
||||
where T : class, IResource
|
||||
where T2 : class, IResource
|
||||
{
|
||||
return new(first, second, true);
|
||||
}
|
||||
|
||||
public static Expression<Func<Link, object>> PrimaryKey
|
||||
{
|
||||
get
|
||||
{
|
||||
return x => new {First = x.FirstID, Second = x.SecondID};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Link<T1, T2> : Link
|
||||
where T1 : class, IResource
|
||||
where T2 : class, IResource
|
||||
{
|
||||
public virtual T1 First { get; set; }
|
||||
public virtual T2 Second { get; set; }
|
||||
|
||||
|
||||
public Link() {}
|
||||
|
||||
public Link(T1 first, T2 second, bool privateItems = false)
|
||||
: base(first, second)
|
||||
{
|
||||
if (privateItems)
|
||||
return;
|
||||
First = first;
|
||||
Second = second;
|
||||
}
|
||||
|
||||
public Link(int firstID, int secondID)
|
||||
: base(firstID, secondID)
|
||||
{ }
|
||||
|
||||
public new static Expression<Func<Link<T1, T2>, object>> PrimaryKey
|
||||
{
|
||||
get
|
||||
{
|
||||
return x => new {First = x.FirstID, Second = x.SecondID};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,25 +1,24 @@
|
||||
using System;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class MetadataID
|
||||
{
|
||||
[JsonIgnore] public int ID { get; set; }
|
||||
[JsonIgnore] public int ProviderID { get; set; }
|
||||
[SerializeIgnore] public int ID { get; set; }
|
||||
[SerializeIgnore] public int ProviderID { get; set; }
|
||||
public virtual ProviderID Provider {get; set; }
|
||||
|
||||
[JsonIgnore] public int? ShowID { get; set; }
|
||||
[JsonIgnore] public virtual Show Show { get; set; }
|
||||
[SerializeIgnore] public int? ShowID { get; set; }
|
||||
[SerializeIgnore] public virtual Show Show { get; set; }
|
||||
|
||||
[JsonIgnore] public int? EpisodeID { get; set; }
|
||||
[JsonIgnore] public virtual Episode Episode { get; set; }
|
||||
[SerializeIgnore] public int? EpisodeID { get; set; }
|
||||
[SerializeIgnore] public virtual Episode Episode { get; set; }
|
||||
|
||||
[JsonIgnore] public int? SeasonID { get; set; }
|
||||
[JsonIgnore] public virtual Season Season { get; set; }
|
||||
[SerializeIgnore] public int? SeasonID { get; set; }
|
||||
[SerializeIgnore] public virtual Season Season { get; set; }
|
||||
|
||||
[JsonIgnore] public int? PeopleID { get; set; }
|
||||
[JsonIgnore] public virtual People People { get; set; }
|
||||
[SerializeIgnore] public int? PeopleID { get; set; }
|
||||
[SerializeIgnore] public virtual People People { get; set; }
|
||||
|
||||
public string DataID { get; set; }
|
||||
public string Link { get; set; }
|
||||
|
||||
@ -1,46 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class PeopleRole : IResource
|
||||
{
|
||||
[JsonIgnore] public int ID { get; set; }
|
||||
[JsonIgnore] public int PeopleID { get; set; }
|
||||
[JsonIgnore] public virtual People People { get; set; }
|
||||
|
||||
[ExpressionRewrite(nameof(People) + "." + nameof(Models.People.Slug))]
|
||||
public string Slug
|
||||
{
|
||||
get => People.Slug;
|
||||
set => People.Slug = value;
|
||||
}
|
||||
|
||||
[ExpressionRewrite(nameof(People) + "."+ nameof(Models.People.Name))]
|
||||
public string Name
|
||||
{
|
||||
get => People.Name;
|
||||
set => People.Name = value;
|
||||
}
|
||||
|
||||
[ExpressionRewrite(nameof(People) + "."+ nameof(Models.People.Poster))]
|
||||
public string Poster
|
||||
{
|
||||
get => People.Poster;
|
||||
set => People.Poster = value;
|
||||
}
|
||||
|
||||
[ExpressionRewrite(nameof(People) + "."+ nameof(Models.People.ExternalIDs))]
|
||||
public IEnumerable<MetadataID> ExternalIDs
|
||||
{
|
||||
get => People.ExternalIDs;
|
||||
set => People.ExternalIDs = value;
|
||||
}
|
||||
|
||||
[JsonIgnore] public int ShowID { get; set; }
|
||||
[JsonIgnore] public virtual Show Show { get; set; }
|
||||
[SerializeIgnore] public int ID { get; set; }
|
||||
[SerializeIgnore] public string Slug => ForPeople ? Show.Slug : People.Slug;
|
||||
[SerializeIgnore] public bool ForPeople;
|
||||
[SerializeIgnore] public int PeopleID { get; set; }
|
||||
[SerializeIgnore] public virtual People People { get; set; }
|
||||
[SerializeIgnore] public int ShowID { get; set; }
|
||||
[SerializeIgnore] public virtual Show Show { get; set; }
|
||||
public string Role { get; set; }
|
||||
public string Type { get; set; }
|
||||
|
||||
@ -66,67 +37,4 @@ namespace Kyoo.Models
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
public class ShowRole : IResource
|
||||
{
|
||||
public int ID { get; set; }
|
||||
public string Role { get; set; }
|
||||
public string Type { get; set; }
|
||||
|
||||
public string Slug { get; set; }
|
||||
public string Title { get; set; }
|
||||
public IEnumerable<string> Aliases { get; set; }
|
||||
[JsonIgnore] public string Path { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public Status? Status { get; set; }
|
||||
public string TrailerUrl { get; set; }
|
||||
public int? StartYear { get; set; }
|
||||
public int? EndYear { get; set; }
|
||||
public string Poster { get; set; }
|
||||
public string Logo { get; set; }
|
||||
public string Backdrop { get; set; }
|
||||
public bool IsMovie { get; set; }
|
||||
|
||||
public ShowRole() {}
|
||||
|
||||
public ShowRole(PeopleRole x)
|
||||
{
|
||||
ID = x.ID;
|
||||
Role = x.Role;
|
||||
Type = x.Type;
|
||||
Slug = x.Show.Slug;
|
||||
Title = x.Show.Title;
|
||||
Aliases = x.Show.Aliases;
|
||||
Path = x.Show.Path;
|
||||
Overview = x.Show.Overview;
|
||||
Status = x.Show.Status;
|
||||
TrailerUrl = x.Show.TrailerUrl;
|
||||
StartYear = x.Show.StartYear;
|
||||
EndYear = x.Show.EndYear;
|
||||
Poster = x.Show.Poster;
|
||||
Logo = x.Show.Logo;
|
||||
Backdrop = x.Show.Backdrop;
|
||||
IsMovie = x.Show.IsMovie;
|
||||
}
|
||||
|
||||
public static Expression<Func<PeopleRole, ShowRole>> FromPeopleRole => x => new ShowRole
|
||||
{
|
||||
ID = x.ID,
|
||||
Role = x.Role,
|
||||
Type = x.Type,
|
||||
Slug = x.Show.Slug,
|
||||
Title = x.Show.Title,
|
||||
Aliases = x.Show.Aliases,
|
||||
Path = x.Show.Path,
|
||||
Overview = x.Show.Overview,
|
||||
Status = x.Show.Status,
|
||||
TrailerUrl = x.Show.TrailerUrl,
|
||||
StartYear = x.Show.StartYear,
|
||||
EndYear = x.Show.EndYear,
|
||||
Poster = x.Show.Poster,
|
||||
Logo = x.Show.Logo,
|
||||
Backdrop = x.Show.Backdrop,
|
||||
IsMovie = x.Show.IsMovie
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,6 @@ namespace Kyoo.Models
|
||||
public interface IPlugin
|
||||
{
|
||||
public string Name { get; }
|
||||
public IEnumerable<ITask> Tasks { get; }
|
||||
public ICollection<ITask> Tasks { get; }
|
||||
}
|
||||
}
|
||||
@ -8,11 +8,16 @@ namespace Kyoo.Models
|
||||
public int ID { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Poster { get; set; }
|
||||
[SerializeAs("{HOST}/api/collection/{Slug}/poster")] public string Poster { get; set; }
|
||||
public string Overview { get; set; }
|
||||
[JsonIgnore] public virtual IEnumerable<Show> Shows { get; set; }
|
||||
[JsonIgnore] public virtual IEnumerable<Library> Libraries { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Show> Shows { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Library> Libraries { get; set; }
|
||||
|
||||
#if ENABLE_INTERNAL_LINKS
|
||||
[SerializeIgnore] public virtual ICollection<Link<Collection, Show>> ShowLinks { get; set; }
|
||||
[SerializeIgnore] public virtual ICollection<Link<Library, Collection>> LibraryLinks { get; set; }
|
||||
#endif
|
||||
|
||||
public Collection() { }
|
||||
|
||||
public Collection(string slug, string name, string overview, string poster)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
@ -7,38 +8,29 @@ namespace Kyoo.Models
|
||||
public class Episode : IResource, IOnMerge
|
||||
{
|
||||
public int ID { get; set; }
|
||||
public int ShowID { get; set; }
|
||||
[JsonIgnore] public virtual Show Show { get; set; }
|
||||
public int? SeasonID { get; set; }
|
||||
[JsonIgnore] public virtual Season Season { get; set; }
|
||||
public string Slug => GetSlug(ShowSlug, SeasonNumber, EpisodeNumber);
|
||||
[SerializeIgnore] public string ShowSlug { private get; set; }
|
||||
[SerializeIgnore] public int ShowID { get; set; }
|
||||
[LoadableRelation(nameof(ShowID))] public virtual Show Show { get; set; }
|
||||
[SerializeIgnore] public int? SeasonID { get; set; }
|
||||
[LoadableRelation(nameof(SeasonID))] public virtual Season Season { get; set; }
|
||||
|
||||
public int SeasonNumber { get; set; } = -1;
|
||||
public int EpisodeNumber { get; set; } = -1;
|
||||
public int AbsoluteNumber { get; set; } = -1;
|
||||
[JsonIgnore] public string Path { get; set; }
|
||||
[SerializeIgnore] public string Path { get; set; }
|
||||
|
||||
[SerializeAs("{HOST}/api/episodes/{Slug}/thumb")] public string Thumb { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
|
||||
public int Runtime { get; set; } //This runtime variable should be in minutes
|
||||
|
||||
[JsonIgnore] public string Poster { get; set; }
|
||||
[EditableRelation] public virtual IEnumerable<MetadataID> ExternalIDs { get; set; }
|
||||
|
||||
[JsonIgnore] public virtual IEnumerable<Track> Tracks { get; set; }
|
||||
|
||||
public string ShowTitle => Show.Title;
|
||||
public string Slug => GetSlug(Show.Slug, SeasonNumber, EpisodeNumber);
|
||||
public string Thumb
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Show != null)
|
||||
return "thumb/" + Slug;
|
||||
return Poster;
|
||||
}
|
||||
}
|
||||
[EditableRelation] [LoadableRelation] public virtual ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
|
||||
[EditableRelation] [LoadableRelation] public virtual ICollection<Track> Tracks { get; set; }
|
||||
|
||||
|
||||
public Episode() { }
|
||||
|
||||
@ -49,7 +41,7 @@ namespace Kyoo.Models
|
||||
string overview,
|
||||
DateTime? releaseDate,
|
||||
int runtime,
|
||||
string poster,
|
||||
string thumb,
|
||||
IEnumerable<MetadataID> externalIDs)
|
||||
{
|
||||
SeasonNumber = seasonNumber;
|
||||
@ -59,8 +51,8 @@ namespace Kyoo.Models
|
||||
Overview = overview;
|
||||
ReleaseDate = releaseDate;
|
||||
Runtime = runtime;
|
||||
Poster = poster;
|
||||
ExternalIDs = externalIDs;
|
||||
Thumb = thumb;
|
||||
ExternalIDs = externalIDs?.ToArray();
|
||||
}
|
||||
|
||||
public Episode(int showID,
|
||||
@ -75,23 +67,17 @@ namespace Kyoo.Models
|
||||
int runtime,
|
||||
string poster,
|
||||
IEnumerable<MetadataID> externalIDs)
|
||||
: this(seasonNumber, episodeNumber, absoluteNumber, title, overview, releaseDate, runtime, poster, externalIDs)
|
||||
{
|
||||
ShowID = showID;
|
||||
SeasonID = seasonID;
|
||||
SeasonNumber = seasonNumber;
|
||||
EpisodeNumber = episodeNumber;
|
||||
AbsoluteNumber = absoluteNumber;
|
||||
Path = path;
|
||||
Title = title;
|
||||
Overview = overview;
|
||||
ReleaseDate = releaseDate;
|
||||
Runtime = runtime;
|
||||
Poster = poster;
|
||||
ExternalIDs = externalIDs;
|
||||
}
|
||||
|
||||
public static string GetSlug(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
if (showSlug == null)
|
||||
throw new ArgumentException("Show's slug is null. Can't find episode's slug.");
|
||||
if (seasonNumber == -1)
|
||||
return showSlug;
|
||||
return $"{showSlug}-s{seasonNumber}e{episodeNumber}";
|
||||
|
||||
@ -5,12 +5,17 @@ namespace Kyoo.Models
|
||||
{
|
||||
public class Genre : IResource
|
||||
{
|
||||
[JsonIgnore] public int ID { get; set; }
|
||||
public int ID { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonIgnore] public virtual IEnumerable<Show> Shows { get; set; }
|
||||
|
||||
[LoadableRelation] public virtual ICollection<Show> Shows { get; set; }
|
||||
|
||||
#if ENABLE_INTERNAL_LINKS
|
||||
[SerializeIgnore] public virtual ICollection<Link<Show, Genre>> ShowLinks { get; set; }
|
||||
#endif
|
||||
|
||||
|
||||
public Genre() {}
|
||||
|
||||
public Genre(string name)
|
||||
|
||||
@ -9,16 +9,6 @@ namespace Kyoo.Models
|
||||
public string Slug { get; }
|
||||
}
|
||||
|
||||
public interface IResourceLink<out T, out T2>
|
||||
where T : IResource
|
||||
where T2 : IResource
|
||||
{
|
||||
public T Parent { get; }
|
||||
public int ParentID { get; }
|
||||
public T2 Child { get; }
|
||||
public int ChildID { get; }
|
||||
}
|
||||
|
||||
public class ResourceComparer<T> : IEqualityComparer<T> where T : IResource
|
||||
{
|
||||
public bool Equals(T x, T y)
|
||||
@ -37,27 +27,4 @@ namespace Kyoo.Models
|
||||
return HashCode.Combine(obj.ID, obj.Slug);
|
||||
}
|
||||
}
|
||||
|
||||
public class LinkComparer<T, T1, T2> : IEqualityComparer<T>
|
||||
where T : IResourceLink<T1, T2>
|
||||
where T1 : IResource
|
||||
where T2 : IResource
|
||||
{
|
||||
public bool Equals(T x, T y)
|
||||
{
|
||||
if (ReferenceEquals(x, y))
|
||||
return true;
|
||||
if (ReferenceEquals(x, null))
|
||||
return false;
|
||||
if (ReferenceEquals(y, null))
|
||||
return false;
|
||||
return Utility.LinkEquals(x.Parent, x.ParentID, y.Parent, y.ParentID)
|
||||
&& Utility.LinkEquals(x.Child, x.ChildID, y.Child, y.ChildID);
|
||||
}
|
||||
|
||||
public int GetHashCode(T obj)
|
||||
{
|
||||
return HashCode.Combine(obj.Parent, obj.ParentID, obj.Child, obj.ChildID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,19 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class Library : IResource
|
||||
{
|
||||
[JsonIgnore] public int ID { get; set; }
|
||||
public int ID { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Name { get; set; }
|
||||
public IEnumerable<string> Paths { get; set; }
|
||||
public string[] Paths { get; set; }
|
||||
|
||||
[EditableRelation] public virtual IEnumerable<ProviderID> Providers { get; set; }
|
||||
[EditableRelation] [LoadableRelation] public virtual ICollection<ProviderID> Providers { get; set; }
|
||||
|
||||
[JsonIgnore] public virtual IEnumerable<Show> Shows { get; set; }
|
||||
[JsonIgnore] public virtual IEnumerable<Collection> Collections { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Show> Shows { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Collection> Collections { get; set; }
|
||||
|
||||
#if ENABLE_INTERNAL_LINKS
|
||||
[SerializeIgnore] public virtual ICollection<Link<Library, ProviderID>> ProviderLinks { get; set; }
|
||||
[SerializeIgnore] public virtual ICollection<Link<Library, Show>> ShowLinks { get; set; }
|
||||
[SerializeIgnore] public virtual ICollection<Link<Library, Collection>> CollectionLinks { get; set; }
|
||||
#endif
|
||||
|
||||
public Library() { }
|
||||
|
||||
@ -21,8 +28,8 @@ namespace Kyoo.Models
|
||||
{
|
||||
Slug = slug;
|
||||
Name = name;
|
||||
Paths = paths;
|
||||
Providers = providers;
|
||||
Paths = paths?.ToArray();
|
||||
Providers = providers?.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
@ -8,10 +9,10 @@ namespace Kyoo.Models
|
||||
public int ID { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Poster { get; set; }
|
||||
[EditableRelation] public virtual IEnumerable<MetadataID> ExternalIDs { get; set; }
|
||||
[SerializeAs("{HOST}/api/people/{Slug}/poster")] public string Poster { get; set; }
|
||||
[EditableRelation] [LoadableRelation] public virtual ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
|
||||
[EditableRelation] [JsonReadOnly] public virtual IEnumerable<PeopleRole> Roles { get; set; }
|
||||
[EditableRelation] [LoadableRelation] public virtual ICollection<PeopleRole> Roles { get; set; }
|
||||
|
||||
public People() {}
|
||||
|
||||
@ -20,7 +21,7 @@ namespace Kyoo.Models
|
||||
Slug = slug;
|
||||
Name = name;
|
||||
Poster = poster;
|
||||
ExternalIDs = externalIDs;
|
||||
ExternalIDs = externalIDs?.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +1,22 @@
|
||||
using System.Collections.Generic;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class ProviderID : IResource
|
||||
{
|
||||
[JsonIgnore] public int ID { get; set; }
|
||||
public int ID { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Logo { get; set; }
|
||||
|
||||
[SerializeAs("{HOST}/api/providers/{Slug}/logo")] public string Logo { get; set; }
|
||||
|
||||
[LoadableRelation] public virtual ICollection<Library> Libraries { get; set; }
|
||||
|
||||
#if ENABLE_INTERNAL_LINKS
|
||||
[SerializeIgnore] public virtual ICollection<Link<Library, ProviderID>> LibraryLinks { get; set; }
|
||||
[SerializeIgnore] public virtual ICollection<MetadataID> MetadataLinks { get; set; }
|
||||
#endif
|
||||
|
||||
public ProviderID() { }
|
||||
|
||||
public ProviderID(string name, string logo)
|
||||
|
||||
@ -1,25 +1,27 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class Season : IResource
|
||||
{
|
||||
[JsonIgnore] public int ID { get; set; }
|
||||
[JsonIgnore] public int ShowID { get; set; }
|
||||
public int ID { get; set; }
|
||||
public string Slug => $"{ShowSlug}-s{SeasonNumber}";
|
||||
[SerializeIgnore] public int ShowID { get; set; }
|
||||
[SerializeIgnore] public string ShowSlug { private get; set; }
|
||||
[LoadableRelation(nameof(ShowID))] public virtual Show Show { get; set; }
|
||||
|
||||
public int SeasonNumber { get; set; } = -1;
|
||||
|
||||
public string Slug => $"{Show.Slug}-s{SeasonNumber}";
|
||||
public string Title { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public int? Year { get; set; }
|
||||
|
||||
[JsonIgnore] public string Poster { get; set; }
|
||||
[EditableRelation] public virtual IEnumerable<MetadataID> ExternalIDs { get; set; }
|
||||
[SerializeAs("{HOST}/api/seasons/{Slug}/thumb")] public string Poster { get; set; }
|
||||
[EditableRelation] [LoadableRelation] public virtual ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
|
||||
[JsonIgnore] public virtual Show Show { get; set; }
|
||||
[JsonIgnore] public virtual IEnumerable<Episode> Episodes { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Episode> Episodes { get; set; }
|
||||
|
||||
public Season() { }
|
||||
|
||||
@ -37,7 +39,7 @@ namespace Kyoo.Models
|
||||
Overview = overview;
|
||||
Year = year;
|
||||
Poster = poster;
|
||||
ExternalIDs = externalIDs;
|
||||
ExternalIDs = externalIDs?.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,8 +9,8 @@ namespace Kyoo.Models
|
||||
public int ID { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Title { get; set; }
|
||||
[EditableRelation] public IEnumerable<string> Aliases { get; set; }
|
||||
[JsonIgnore] public string Path { get; set; }
|
||||
[EditableRelation] public string[] Aliases { get; set; }
|
||||
[SerializeIgnore] public string Path { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public Status? Status { get; set; }
|
||||
public string TrailerUrl { get; set; }
|
||||
@ -18,23 +18,30 @@ namespace Kyoo.Models
|
||||
public int? StartYear { get; set; }
|
||||
public int? EndYear { get; set; }
|
||||
|
||||
public string Poster { get; set; }
|
||||
public string Logo { get; set; }
|
||||
public string Backdrop { get; set; }
|
||||
[SerializeAs("{HOST}/api/shows/{Slug}/poster")] public string Poster { get; set; }
|
||||
[SerializeAs("{HOST}/api/shows/{Slug}/logo")] public string Logo { get; set; }
|
||||
[SerializeAs("{HOST}/api/shows/{Slug}/backdrop")] public string Backdrop { get; set; }
|
||||
|
||||
public bool IsMovie { get; set; }
|
||||
|
||||
[EditableRelation] public virtual IEnumerable<MetadataID> ExternalIDs { get; set; }
|
||||
[EditableRelation] [LoadableRelation] public virtual ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
|
||||
|
||||
[JsonIgnore] public int? StudioID { get; set; }
|
||||
[EditableRelation] [JsonReadOnly] public virtual Studio Studio { get; set; }
|
||||
[EditableRelation] [JsonReadOnly] public virtual IEnumerable<Genre> Genres { get; set; }
|
||||
[EditableRelation] [JsonReadOnly] public virtual IEnumerable<PeopleRole> People { get; set; }
|
||||
[JsonIgnore] public virtual IEnumerable<Season> Seasons { get; set; }
|
||||
[JsonIgnore] public virtual IEnumerable<Episode> Episodes { get; set; }
|
||||
[JsonIgnore] public virtual IEnumerable<Library> Libraries { get; set; }
|
||||
[JsonIgnore] public virtual IEnumerable<Collection> Collections { get; set; }
|
||||
[SerializeIgnore] public int? StudioID { get; set; }
|
||||
[LoadableRelation(nameof(StudioID))] [EditableRelation] public virtual Studio Studio { get; set; }
|
||||
[LoadableRelation] [EditableRelation] public virtual ICollection<Genre> Genres { get; set; }
|
||||
[LoadableRelation] [EditableRelation] public virtual ICollection<PeopleRole> People { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Season> Seasons { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Episode> Episodes { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Library> Libraries { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Collection> Collections { get; set; }
|
||||
|
||||
#if ENABLE_INTERNAL_LINKS
|
||||
[SerializeIgnore] public virtual ICollection<Link<Library, Show>> LibraryLinks { get; set; }
|
||||
[SerializeIgnore] public virtual ICollection<Link<Collection, Show>> CollectionLinks { get; set; }
|
||||
[SerializeIgnore] public virtual ICollection<Link<Show, Genre>> GenreLinks { get; set; }
|
||||
#endif
|
||||
|
||||
|
||||
public Show() { }
|
||||
|
||||
@ -51,15 +58,15 @@ namespace Kyoo.Models
|
||||
{
|
||||
Slug = slug;
|
||||
Title = title;
|
||||
Aliases = aliases;
|
||||
Aliases = aliases?.ToArray();
|
||||
Path = path;
|
||||
Overview = overview;
|
||||
TrailerUrl = trailerUrl;
|
||||
Genres = genres;
|
||||
Genres = genres?.ToArray();
|
||||
Status = status;
|
||||
StartYear = startYear;
|
||||
EndYear = endYear;
|
||||
ExternalIDs = externalIDs;
|
||||
ExternalIDs = externalIDs?.ToArray();
|
||||
}
|
||||
|
||||
public Show(string slug,
|
||||
@ -78,7 +85,7 @@ namespace Kyoo.Models
|
||||
{
|
||||
Slug = slug;
|
||||
Title = title;
|
||||
Aliases = aliases;
|
||||
Aliases = aliases?.ToArray();
|
||||
Path = path;
|
||||
Overview = overview;
|
||||
TrailerUrl = trailerUrl;
|
||||
@ -88,7 +95,7 @@ namespace Kyoo.Models
|
||||
Poster = poster;
|
||||
Logo = logo;
|
||||
Backdrop = backdrop;
|
||||
ExternalIDs = externalIDs;
|
||||
ExternalIDs = externalIDs?.ToArray();
|
||||
}
|
||||
|
||||
public string GetID(string provider)
|
||||
|
||||
@ -5,11 +5,11 @@ namespace Kyoo.Models
|
||||
{
|
||||
public class Studio : IResource
|
||||
{
|
||||
[JsonIgnore] public int ID { get; set; }
|
||||
public int ID { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonIgnore] public virtual IEnumerable<Show> Shows { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Show> Shows { get; set; }
|
||||
|
||||
public Studio() { }
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ namespace Kyoo.Models
|
||||
Video = 1,
|
||||
Audio = 2,
|
||||
Subtitle = 3,
|
||||
Font = 4
|
||||
Attachment = 4
|
||||
}
|
||||
|
||||
namespace Watch
|
||||
@ -25,8 +25,8 @@ namespace Kyoo.Models
|
||||
public string Codec { get; set; }
|
||||
[MarshalAs(UnmanagedType.I1)] public bool isDefault;
|
||||
[MarshalAs(UnmanagedType.I1)] public bool isForced;
|
||||
[JsonIgnore] public string Path { get; set; }
|
||||
[JsonIgnore] public StreamType Type { get; set; }
|
||||
[SerializeIgnore] public string Path { get; set; }
|
||||
[SerializeIgnore] public StreamType Type { get; set; }
|
||||
|
||||
public Stream() {}
|
||||
|
||||
@ -56,8 +56,9 @@ namespace Kyoo.Models
|
||||
|
||||
public class Track : Stream, IResource
|
||||
{
|
||||
[JsonIgnore] public int ID { get; set; }
|
||||
[JsonIgnore] public int EpisodeID { get; set; }
|
||||
public int ID { get; set; }
|
||||
[SerializeIgnore] public int EpisodeID { get; set; }
|
||||
public int TrackIndex { get; set; }
|
||||
public bool IsDefault
|
||||
{
|
||||
get => isDefault;
|
||||
@ -76,7 +77,7 @@ namespace Kyoo.Models
|
||||
string language = GetLanguage(Language);
|
||||
|
||||
if (language == null)
|
||||
return $"Unknown Language (id: {ID.ToString()})";
|
||||
return $"Unknown (index: {TrackIndex})";
|
||||
CultureInfo info = CultureInfo.GetCultures(CultureTypes.NeutralCultures)
|
||||
.FirstOrDefault(x => x.ThreeLetterISOLanguageName == language);
|
||||
string name = info?.EnglishName ?? language;
|
||||
@ -94,31 +95,37 @@ namespace Kyoo.Models
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Type != StreamType.Subtitle)
|
||||
return null;
|
||||
|
||||
string slug = string.IsNullOrEmpty(Language)
|
||||
? ID.ToString()
|
||||
: $"{Episode.Slug}.{Language}{(IsForced ? "-forced" : "")}";
|
||||
switch (Codec)
|
||||
string type = Type switch
|
||||
{
|
||||
case "ass":
|
||||
slug += ".ass";
|
||||
break;
|
||||
case "subrip":
|
||||
slug += ".srt";
|
||||
break;
|
||||
}
|
||||
return slug;
|
||||
StreamType.Subtitle => "",
|
||||
StreamType.Video => "video.",
|
||||
StreamType.Audio => "audio.",
|
||||
StreamType.Attachment => "font.",
|
||||
_ => ""
|
||||
};
|
||||
string index = TrackIndex != 0 ? $"-{TrackIndex}" : string.Empty;
|
||||
string codec = Codec switch
|
||||
{
|
||||
"subrip" => ".srt",
|
||||
{} x => $".{x}"
|
||||
};
|
||||
return $"{Episode.Slug}.{type}{Language}{index}{(IsForced ? "-forced" : "")}{codec}";
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore] public bool IsExternal { get; set; }
|
||||
[JsonIgnore] public virtual Episode Episode { get; set; }
|
||||
public bool IsExternal { get; set; }
|
||||
[LoadableRelation(nameof(EpisodeID))] public virtual Episode Episode { get; set; }
|
||||
|
||||
public Track() { }
|
||||
|
||||
public Track(StreamType type, string title, string language, bool isDefault, bool isForced, string codec, bool isExternal, string path)
|
||||
public Track(StreamType type,
|
||||
string title,
|
||||
string language,
|
||||
bool isDefault,
|
||||
bool isForced,
|
||||
string codec,
|
||||
bool isExternal,
|
||||
string path)
|
||||
: base(title, language, codec, isDefault, isForced, path, type)
|
||||
{
|
||||
IsExternal = isExternal;
|
||||
@ -136,6 +143,7 @@ namespace Kyoo.Models
|
||||
return mkvLanguage switch
|
||||
{
|
||||
"fre" => "fra",
|
||||
null => "und",
|
||||
_ => mkvLanguage
|
||||
};
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models.Attributes;
|
||||
using Kyoo.Models.Watch;
|
||||
using PathIO = System.IO.Path;
|
||||
|
||||
namespace Kyoo.Models
|
||||
@ -26,31 +25,34 @@ namespace Kyoo.Models
|
||||
|
||||
public class WatchItem
|
||||
{
|
||||
[JsonIgnore] public readonly int EpisodeID = -1;
|
||||
public int EpisodeID { get; set; }
|
||||
|
||||
public string ShowTitle;
|
||||
public string ShowSlug;
|
||||
public int SeasonNumber;
|
||||
public int EpisodeNumber;
|
||||
public string Title;
|
||||
public string Slug;
|
||||
public DateTime? ReleaseDate;
|
||||
[JsonIgnore] public string Path;
|
||||
public Episode PreviousEpisode;
|
||||
public Episode NextEpisode;
|
||||
public bool IsMovie;
|
||||
public string ShowTitle { get; set; }
|
||||
public string ShowSlug { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public int EpisodeNumber { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
[SerializeIgnore] public string Path { get; set; }
|
||||
public Episode PreviousEpisode { get; set; }
|
||||
public Episode NextEpisode { get; set; }
|
||||
public bool IsMovie { get; set; }
|
||||
|
||||
public string Container;
|
||||
public Track Video;
|
||||
public IEnumerable<Track> Audios;
|
||||
public IEnumerable<Track> Subtitles;
|
||||
public IEnumerable<Chapter> Chapters;
|
||||
[SerializeAs("{HOST}/api/show/{ShowSlug}/poster")] public string Poster { get; set; }
|
||||
[SerializeAs("{HOST}/api/show/{ShowSlug}/logo")] public string Logo { get; set; }
|
||||
[SerializeAs("{HOST}/api/show/{ShowSlug}/backdrop")] public string Backdrop { get; set; }
|
||||
|
||||
public string Container { get; set; }
|
||||
public Track Video { get; set; }
|
||||
public ICollection<Track> Audios { get; set; }
|
||||
public ICollection<Track> Subtitles { get; set; }
|
||||
public ICollection<Chapter> Chapters { get; set; }
|
||||
|
||||
public WatchItem() { }
|
||||
|
||||
private WatchItem(int episodeID,
|
||||
string showTitle,
|
||||
string showSlug,
|
||||
Show show,
|
||||
int seasonNumber,
|
||||
int episodeNumber,
|
||||
string title,
|
||||
@ -58,30 +60,34 @@ namespace Kyoo.Models
|
||||
string path)
|
||||
{
|
||||
EpisodeID = episodeID;
|
||||
ShowTitle = showTitle;
|
||||
ShowSlug = showSlug;
|
||||
ShowTitle = show.Title;
|
||||
ShowSlug = show.Slug;
|
||||
SeasonNumber = seasonNumber;
|
||||
EpisodeNumber = episodeNumber;
|
||||
Title = title;
|
||||
ReleaseDate = releaseDate;
|
||||
Path = path;
|
||||
IsMovie = show.IsMovie;
|
||||
|
||||
Poster = show.Poster;
|
||||
Logo = show.Logo;
|
||||
Backdrop = show.Backdrop;
|
||||
|
||||
Container = Path.Substring(Path.LastIndexOf('.') + 1);
|
||||
Slug = Episode.GetSlug(ShowSlug, seasonNumber, episodeNumber);
|
||||
}
|
||||
|
||||
private WatchItem(int episodeID,
|
||||
string showTitle,
|
||||
string showSlug,
|
||||
Show show,
|
||||
int seasonNumber,
|
||||
int episodeNumber,
|
||||
string title,
|
||||
DateTime? releaseDate,
|
||||
string path,
|
||||
Track video,
|
||||
IEnumerable<Track> audios,
|
||||
IEnumerable<Track> subtitles)
|
||||
: this(episodeID, showTitle, showSlug, seasonNumber, episodeNumber, title, releaseDate, path)
|
||||
ICollection<Track> audios,
|
||||
ICollection<Track> subtitles)
|
||||
: this(episodeID, show, seasonNumber, episodeNumber, title, releaseDate, path)
|
||||
{
|
||||
Video = video;
|
||||
Audios = audios;
|
||||
@ -90,11 +96,13 @@ namespace Kyoo.Models
|
||||
|
||||
public static async Task<WatchItem> FromEpisode(Episode ep, ILibraryManager library)
|
||||
{
|
||||
Show show = await library.GetShow(ep.ShowID); // TODO load only the title, the slug & the IsMovie with the library manager.
|
||||
Episode previous = null;
|
||||
Episode next = null;
|
||||
|
||||
if (!show.IsMovie)
|
||||
await library.Load(ep, x => x.Show);
|
||||
await library.Load(ep, x => x.Tracks);
|
||||
|
||||
if (!ep.Show.IsMovie)
|
||||
{
|
||||
if (ep.EpisodeNumber > 1)
|
||||
previous = await library.GetEpisode(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber - 1);
|
||||
@ -110,27 +118,25 @@ namespace Kyoo.Models
|
||||
else
|
||||
next = await library.GetEpisode(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber + 1);
|
||||
}
|
||||
|
||||
|
||||
return new WatchItem(ep.ID,
|
||||
show.Title,
|
||||
show.Slug,
|
||||
ep.Show,
|
||||
ep.SeasonNumber,
|
||||
ep.EpisodeNumber,
|
||||
ep.Title,
|
||||
ep.ReleaseDate,
|
||||
ep.Path,
|
||||
await library.GetTrack(x => x.EpisodeID == ep.ID && x.Type == StreamType.Video),
|
||||
await library.GetTracks(x => x.EpisodeID == ep.ID && x.Type == StreamType.Audio),
|
||||
await library.GetTracks(x => x.EpisodeID == ep.ID && x.Type == StreamType.Subtitle))
|
||||
ep.Tracks.FirstOrDefault(x => x.Type == StreamType.Video),
|
||||
ep.Tracks.Where(x => x.Type == StreamType.Audio).ToArray(),
|
||||
ep.Tracks.Where(x => x.Type == StreamType.Subtitle).ToArray())
|
||||
{
|
||||
IsMovie = show.IsMovie,
|
||||
PreviousEpisode = previous,
|
||||
NextEpisode = next,
|
||||
Chapters = await GetChapters(ep.Path)
|
||||
};
|
||||
}
|
||||
|
||||
private static async Task<IEnumerable<Chapter>> GetChapters(string episodePath)
|
||||
private static async Task<ICollection<Chapter>> GetChapters(string episodePath)
|
||||
{
|
||||
string path = PathIO.Combine(
|
||||
PathIO.GetDirectoryName(episodePath)!,
|
||||
@ -138,7 +144,7 @@ namespace Kyoo.Models
|
||||
PathIO.GetFileNameWithoutExtension(episodePath) + ".txt"
|
||||
);
|
||||
if (!File.Exists(path))
|
||||
return new Chapter[0];
|
||||
return Array.Empty<Chapter>();
|
||||
try
|
||||
{
|
||||
return (await File.ReadAllLinesAsync(path))
|
||||
@ -152,7 +158,7 @@ namespace Kyoo.Models
|
||||
catch
|
||||
{
|
||||
await Console.Error.WriteLineAsync($"Invalid chapter file at {path}");
|
||||
return new Chapter[0];
|
||||
return Array.Empty<Chapter>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,37 @@ namespace Kyoo
|
||||
{
|
||||
public static class Utility
|
||||
{
|
||||
public static bool IsPropertyExpression(LambdaExpression ex)
|
||||
{
|
||||
return ex == null ||
|
||||
ex.Body is MemberExpression ||
|
||||
ex.Body.NodeType == ExpressionType.Convert && ((UnaryExpression)ex.Body).Operand is MemberExpression;
|
||||
}
|
||||
|
||||
public static string GetPropertyName(LambdaExpression ex)
|
||||
{
|
||||
if (!IsPropertyExpression(ex))
|
||||
throw new ArgumentException($"{ex} is not a property expression.");
|
||||
MemberExpression member = ex.Body.NodeType == ExpressionType.Convert
|
||||
? ((UnaryExpression)ex.Body).Operand as MemberExpression
|
||||
: ex.Body as MemberExpression;
|
||||
return member!.Member.Name;
|
||||
}
|
||||
|
||||
public static object GetValue([NotNull] this MemberInfo member, [NotNull] object obj)
|
||||
{
|
||||
if (member == null)
|
||||
throw new ArgumentNullException(nameof(member));
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
return member switch
|
||||
{
|
||||
PropertyInfo property => property.GetValue(obj),
|
||||
FieldInfo field => field.GetValue(obj),
|
||||
_ => throw new ArgumentException($"Can't get value of a non property/field (member: {member}).")
|
||||
};
|
||||
}
|
||||
|
||||
public static string ToSlug(string str)
|
||||
{
|
||||
if (str == null)
|
||||
@ -25,7 +56,7 @@ namespace Kyoo
|
||||
str = str.ToLowerInvariant();
|
||||
|
||||
string normalizedString = str.Normalize(NormalizationForm.FormD);
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
StringBuilder stringBuilder = new();
|
||||
foreach (char c in normalizedString)
|
||||
{
|
||||
UnicodeCategory unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
|
||||
@ -93,7 +124,7 @@ namespace Kyoo
|
||||
return first;
|
||||
}
|
||||
|
||||
public static T Complete<T>(T first, T second)
|
||||
public static T Complete<T>(T first, T second, Func<PropertyInfo, bool> where = null)
|
||||
{
|
||||
if (first == null)
|
||||
throw new ArgumentNullException(nameof(first));
|
||||
@ -104,6 +135,9 @@ namespace Kyoo
|
||||
IEnumerable<PropertyInfo> properties = type.GetProperties()
|
||||
.Where(x => x.CanRead && x.CanWrite
|
||||
&& Attribute.GetCustomAttribute(x, typeof(NotMergableAttribute)) == null);
|
||||
|
||||
if (where != null)
|
||||
properties = properties.Where(where);
|
||||
|
||||
foreach (PropertyInfo property in properties)
|
||||
{
|
||||
@ -112,7 +146,7 @@ namespace Kyoo
|
||||
? Activator.CreateInstance(property.PropertyType)
|
||||
: null;
|
||||
|
||||
if (value?.Equals(defaultValue) == false)
|
||||
if (value?.Equals(defaultValue) == false && value != property.GetValue(first))
|
||||
property.SetValue(first, value);
|
||||
}
|
||||
|
||||
@ -221,6 +255,154 @@ namespace Kyoo
|
||||
: type.GetInheritanceTree();
|
||||
return types.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
|
||||
}
|
||||
|
||||
public static IEnumerable<T2> Map<T, T2>([CanBeNull] this IEnumerable<T> self,
|
||||
[NotNull] Func<T, int, T2> mapper)
|
||||
{
|
||||
if (self == null)
|
||||
yield break;
|
||||
if (mapper == null)
|
||||
throw new ArgumentNullException(nameof(mapper));
|
||||
|
||||
using IEnumerator<T> enumerator = self.GetEnumerator();
|
||||
int index = 0;
|
||||
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
yield return mapper(enumerator.Current, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public static async IAsyncEnumerable<T2> MapAsync<T, T2>([CanBeNull] this IEnumerable<T> self,
|
||||
[NotNull] Func<T, int, Task<T2>> mapper)
|
||||
{
|
||||
if (self == null)
|
||||
yield break;
|
||||
if (mapper == null)
|
||||
throw new ArgumentNullException(nameof(mapper));
|
||||
|
||||
using IEnumerator<T> enumerator = self.GetEnumerator();
|
||||
int index = 0;
|
||||
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
yield return await mapper(enumerator.Current, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public static async IAsyncEnumerable<T2> SelectAsync<T, T2>([CanBeNull] this IEnumerable<T> self,
|
||||
[NotNull] Func<T, Task<T2>> mapper)
|
||||
{
|
||||
if (self == null)
|
||||
yield break;
|
||||
if (mapper == null)
|
||||
throw new ArgumentNullException(nameof(mapper));
|
||||
|
||||
using IEnumerator<T> enumerator = self.GetEnumerator();
|
||||
|
||||
while (enumerator.MoveNext())
|
||||
yield return await mapper(enumerator.Current);
|
||||
}
|
||||
|
||||
public static async Task<List<T>> ToListAsync<T>([NotNull] this IAsyncEnumerable<T> self)
|
||||
{
|
||||
if (self == null)
|
||||
throw new ArgumentNullException(nameof(self));
|
||||
|
||||
List<T> ret = new();
|
||||
|
||||
await foreach(T i in self)
|
||||
ret.Add(i);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> IfEmpty<T>(this IEnumerable<T> self, Action action)
|
||||
{
|
||||
using IEnumerator<T> enumerator = self.GetEnumerator();
|
||||
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
action();
|
||||
yield break;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
yield return enumerator.Current;
|
||||
}
|
||||
while (enumerator.MoveNext());
|
||||
}
|
||||
|
||||
public static void ForEach<T>([CanBeNull] this IEnumerable<T> self, Action<T> action)
|
||||
{
|
||||
if (self == null)
|
||||
return;
|
||||
foreach (T i in self)
|
||||
action(i);
|
||||
}
|
||||
|
||||
public static void ForEach([CanBeNull] this IEnumerable self, Action<object> action)
|
||||
{
|
||||
if (self == null)
|
||||
return;
|
||||
foreach (object i in self)
|
||||
action(i);
|
||||
}
|
||||
|
||||
public static async Task ForEachAsync<T>([CanBeNull] this IEnumerable<T> self, Func<T, Task> action)
|
||||
{
|
||||
if (self == null)
|
||||
return;
|
||||
foreach (T i in self)
|
||||
await action(i);
|
||||
}
|
||||
|
||||
public static async Task ForEachAsync<T>([CanBeNull] this IAsyncEnumerable<T> self, Action<T> action)
|
||||
{
|
||||
if (self == null)
|
||||
return;
|
||||
await foreach (T i in self)
|
||||
action(i);
|
||||
}
|
||||
|
||||
public static async Task ForEachAsync([CanBeNull] this IEnumerable self, Func<object, Task> action)
|
||||
{
|
||||
if (self == null)
|
||||
return;
|
||||
foreach (object i in self)
|
||||
await action(i);
|
||||
}
|
||||
|
||||
private static MethodInfo GetMethod(Type type, BindingFlags flag, string name, Type[] generics, object[] args)
|
||||
{
|
||||
MethodInfo[] methods = type.GetMethods(flag | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
.Where(x => x.Name == name)
|
||||
.Where(x => x.GetGenericArguments().Length == generics.Length)
|
||||
.Where(x => x.GetParameters().Length == args.Length)
|
||||
.IfEmpty(() => throw new NullReferenceException($"A method named {name} with " +
|
||||
$"{args.Length} arguments and {generics.Length} generic " +
|
||||
$"types could not be found on {type.Name}."))
|
||||
.Where(x =>
|
||||
{
|
||||
int i = 0;
|
||||
return x.GetGenericArguments().All(y => y.IsAssignableFrom(generics[i++]));
|
||||
})
|
||||
.IfEmpty(() => throw new NullReferenceException($"No method {name} match the generics specified."))
|
||||
.Where(x =>
|
||||
{
|
||||
int i = 0;
|
||||
return x.GetParameters().All(y => y.ParameterType == args[i++].GetType());
|
||||
})
|
||||
.IfEmpty(() => throw new NullReferenceException($"No method {name} match the parameters's types."))
|
||||
.Take(2)
|
||||
.ToArray();
|
||||
|
||||
if (methods.Length == 1)
|
||||
return methods[0];
|
||||
throw new NullReferenceException($"Multiple methods named {name} match the generics and parameters constraints.");
|
||||
}
|
||||
|
||||
public static T RunGenericMethod<T>(
|
||||
[NotNull] Type owner,
|
||||
@ -245,29 +427,33 @@ namespace Kyoo
|
||||
throw new ArgumentNullException(nameof(types));
|
||||
if (types.Length < 1)
|
||||
throw new ArgumentException($"The {nameof(types)} array is empty. At least one type is needed.");
|
||||
MethodInfo method = owner.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
.SingleOrDefault(x => x.Name == methodName && x.GetParameters().Length == args.Length);
|
||||
if (method == null)
|
||||
throw new NullReferenceException($"A method named {methodName} with {args.Length} arguments could not be found on {owner.FullName}");
|
||||
MethodInfo method = GetMethod(owner, BindingFlags.Static, methodName, types, args);
|
||||
return (T)method.MakeGenericMethod(types).Invoke(null, args?.ToArray());
|
||||
}
|
||||
|
||||
|
||||
public static T RunGenericMethod<T>(
|
||||
[NotNull] object instance,
|
||||
[NotNull] string methodName,
|
||||
[NotNull] Type type,
|
||||
params object[] args)
|
||||
{
|
||||
return RunGenericMethod<T>(instance, methodName, new[] {type}, args);
|
||||
}
|
||||
|
||||
public static T RunGenericMethod<T>(
|
||||
[NotNull] object instance,
|
||||
[NotNull] string methodName,
|
||||
[NotNull] Type type,
|
||||
[NotNull] Type[] types,
|
||||
params object[] args)
|
||||
{
|
||||
if (instance == null)
|
||||
throw new ArgumentNullException(nameof(instance));
|
||||
if (methodName == null)
|
||||
throw new ArgumentNullException(nameof(methodName));
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
MethodInfo method = instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
if (method == null)
|
||||
throw new NullReferenceException($"A method named {methodName} could not be found on {instance.GetType().FullName}");
|
||||
return (T)method.MakeGenericMethod(type).Invoke(instance, args?.ToArray());
|
||||
if (types == null || types.Length == 0)
|
||||
throw new ArgumentNullException(nameof(types));
|
||||
MethodInfo method = GetMethod(instance.GetType(), BindingFlags.Instance, methodName, types, args);
|
||||
return (T)method.MakeGenericMethod(types).Invoke(instance, args?.ToArray());
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
@ -368,6 +554,22 @@ namespace Kyoo
|
||||
return (T)((dynamic)x).Result;
|
||||
}, TaskContinuationOptions.ExecuteSynchronously);
|
||||
}
|
||||
|
||||
public static Expression<Func<T, bool>> ResourceEquals<T>(IResource obj)
|
||||
where T : IResource
|
||||
{
|
||||
if (obj.ID > 0)
|
||||
return x => x.ID == obj.ID || x.Slug == obj.Slug;
|
||||
return x => x.Slug == obj.Slug;
|
||||
}
|
||||
|
||||
public static Func<T, bool> ResourceEqualsFunc<T>(IResource obj)
|
||||
where T : IResource
|
||||
{
|
||||
if (obj.ID > 0)
|
||||
return x => x.ID == obj.ID || x.Slug == obj.Slug;
|
||||
return x => x.Slug == obj.Slug;
|
||||
}
|
||||
|
||||
public static bool ResourceEquals([CanBeNull] object first, [CanBeNull] object second)
|
||||
{
|
||||
@ -382,13 +584,9 @@ namespace Kyoo
|
||||
Type type = GetEnumerableType(eno);
|
||||
if (typeof(IResource).IsAssignableFrom(type))
|
||||
return ResourceEquals(eno.Cast<IResource>(), ens.Cast<IResource>());
|
||||
Type genericDefinition = GetGenericDefinition(type, typeof(IResourceLink<,>));
|
||||
if (genericDefinition == null)
|
||||
return RunGenericMethod<bool>(typeof(Enumerable), "SequenceEqual", type, first, second);
|
||||
Type[] types = genericDefinition.GetGenericArguments().Prepend(type).ToArray();
|
||||
return RunGenericMethod<bool>(typeof(Utility), "LinkEquals", types, eno, ens);
|
||||
return RunGenericMethod<bool>(typeof(Enumerable), "SequenceEqual", type, first, second);
|
||||
}
|
||||
|
||||
|
||||
public static bool ResourceEquals<T>([CanBeNull] T first, [CanBeNull] T second)
|
||||
where T : IResource
|
||||
{
|
||||
@ -422,69 +620,5 @@ namespace Kyoo
|
||||
return true;
|
||||
return firstID == secondID;
|
||||
}
|
||||
|
||||
public static bool LinkEquals<T, T1, T2>([CanBeNull] IEnumerable<T> first, [CanBeNull] IEnumerable<T> second)
|
||||
where T : IResourceLink<T1, T2>
|
||||
where T1 : IResource
|
||||
where T2 : IResource
|
||||
{
|
||||
if (ReferenceEquals(first, second))
|
||||
return true;
|
||||
if (first == null || second == null)
|
||||
return false;
|
||||
return first.SequenceEqual(second, new LinkComparer<T, T1, T2>());
|
||||
}
|
||||
|
||||
public static Expression<T> Convert<T>([CanBeNull] this Expression expr)
|
||||
where T : Delegate
|
||||
{
|
||||
Expression<T> e = expr switch
|
||||
{
|
||||
null => null,
|
||||
LambdaExpression lambda => new ExpressionConverter<T>(lambda).VisitAndConvert(),
|
||||
_ => throw new ArgumentException("Can't convert a non lambda.")
|
||||
};
|
||||
|
||||
return ExpressionRewrite.Rewrite<T>(e);
|
||||
}
|
||||
|
||||
private class ExpressionConverter<TTo> : ExpressionVisitor
|
||||
where TTo : Delegate
|
||||
{
|
||||
private readonly LambdaExpression _expression;
|
||||
private readonly ParameterExpression[] _newParams;
|
||||
|
||||
internal ExpressionConverter(LambdaExpression expression)
|
||||
{
|
||||
_expression = expression;
|
||||
|
||||
Type[] paramTypes = typeof(TTo).GetGenericArguments()[..^1];
|
||||
if (paramTypes.Length != _expression.Parameters.Count)
|
||||
throw new ArgumentException("Parameter count from internal and external lambda are not matched.");
|
||||
|
||||
_newParams = new ParameterExpression[paramTypes.Length];
|
||||
for (int i = 0; i < paramTypes.Length; i++)
|
||||
{
|
||||
if (_expression.Parameters[i].Type == paramTypes[i])
|
||||
_newParams[i] = _expression.Parameters[i];
|
||||
else
|
||||
_newParams[i] = Expression.Parameter(paramTypes[i], _expression.Parameters[i].Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal Expression<TTo> VisitAndConvert()
|
||||
{
|
||||
Type returnType = _expression.Type.GetGenericArguments().Last();
|
||||
Expression body = _expression.ReturnType == returnType
|
||||
? Visit(_expression.Body)
|
||||
: Expression.Convert(Visit(_expression.Body)!, returnType);
|
||||
return Expression.Lambda<TTo>(body!, _newParams);
|
||||
}
|
||||
|
||||
protected override Expression VisitParameter(ParameterExpression node)
|
||||
{
|
||||
return _newParams.FirstOrDefault(x => x.Name == node.Name) ?? node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -26,12 +26,7 @@ namespace Kyoo.CommonApi
|
||||
Expression<Func<T, bool>> defaultWhere = null)
|
||||
{
|
||||
if (where == null || where.Count == 0)
|
||||
{
|
||||
if (defaultWhere == null)
|
||||
return null;
|
||||
Expression body = ExpressionRewrite.Rewrite(defaultWhere.Body);
|
||||
return Expression.Lambda<Func<T, bool>>(body, defaultWhere.Parameters.First());
|
||||
}
|
||||
return defaultWhere;
|
||||
|
||||
ParameterExpression param = defaultWhere?.Parameters.First() ?? Expression.Parameter(typeof(T));
|
||||
Expression expression = defaultWhere?.Body;
|
||||
@ -97,8 +92,7 @@ namespace Kyoo.CommonApi
|
||||
expression = condition;
|
||||
}
|
||||
|
||||
expression = ExpressionRewrite.Rewrite(expression);
|
||||
return Expression.Lambda<Func<T, bool>>(expression, param);
|
||||
return Expression.Lambda<Func<T, bool>>(expression!, param);
|
||||
}
|
||||
|
||||
private static Expression ResourceEqual(Expression parameter, string value, bool notEqual = false)
|
||||
|
||||
@ -12,6 +12,7 @@ using Microsoft.Extensions.Configuration;
|
||||
namespace Kyoo.CommonApi
|
||||
{
|
||||
[ApiController]
|
||||
[ResourceView]
|
||||
public class CrudApi<T> : ControllerBase where T : class, IResource
|
||||
{
|
||||
private readonly IRepository<T> _repository;
|
||||
@ -22,10 +23,10 @@ namespace Kyoo.CommonApi
|
||||
_repository = repository;
|
||||
BaseURL = configuration.GetValue<string>("public_url").TrimEnd('/');
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpGet("{id:int}")]
|
||||
[Authorize(Policy = "Read")]
|
||||
[JsonDetailed]
|
||||
public virtual async Task<ActionResult<T>> Get(int id)
|
||||
{
|
||||
T resource = await _repository.Get(id);
|
||||
@ -37,7 +38,6 @@ namespace Kyoo.CommonApi
|
||||
|
||||
[HttpGet("{slug}")]
|
||||
[Authorize(Policy = "Read")]
|
||||
[JsonDetailed]
|
||||
public virtual async Task<ActionResult<T>> Get(string slug)
|
||||
{
|
||||
T resource = await _repository.Get(slug);
|
||||
@ -68,10 +68,6 @@ namespace Kyoo.CommonApi
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 20)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<T> resources = await _repository.GetAll(ApiHelper.ParseWhere<T>(where),
|
||||
@ -89,7 +85,7 @@ namespace Kyoo.CommonApi
|
||||
protected Page<TResult> Page<TResult>(ICollection<TResult> resources, int limit)
|
||||
where TResult : IResource
|
||||
{
|
||||
return new Page<TResult>(resources,
|
||||
return new(resources,
|
||||
BaseURL + Request.Path,
|
||||
Request.Query.ToDictionary(x => x.Key, x => x.Value.ToString(), StringComparer.InvariantCultureIgnoreCase),
|
||||
limit);
|
||||
@ -186,5 +182,20 @@ namespace Kyoo.CommonApi
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Write")]
|
||||
public virtual async Task<IActionResult> Delete(Dictionary<string, string> where)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _repository.DeleteRange(ApiHelper.ParseWhere<T>(where));
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,103 +1,138 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public class JsonPropertyIgnorer : CamelCasePropertyNamesContractResolver
|
||||
{
|
||||
private int _depth = -1;
|
||||
private string _host;
|
||||
|
||||
public JsonPropertyIgnorer(string host)
|
||||
{
|
||||
_host = host;
|
||||
}
|
||||
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
JsonProperty property = base.CreateProperty(member, memberSerialization);
|
||||
|
||||
property.ShouldSerialize = i => member.GetCustomAttribute<JsonReadOnly>(true) == null;
|
||||
property.ShouldDeserialize = i => member.GetCustomAttribute<JsonIgnore>(true) == null;
|
||||
|
||||
LoadableRelationAttribute relation = member?.GetCustomAttribute<LoadableRelationAttribute>();
|
||||
if (relation != null)
|
||||
{
|
||||
if (relation.RelationID == null)
|
||||
property.ShouldSerialize = x => _depth == 0 && member.GetValue(x) != null;
|
||||
else
|
||||
property.ShouldSerialize = x =>
|
||||
{
|
||||
if (_depth != 0)
|
||||
return false;
|
||||
if (member.GetValue(x) != null)
|
||||
return true;
|
||||
return x.GetType().GetProperty(relation.RelationID)?.GetValue(x) != null;
|
||||
};
|
||||
}
|
||||
|
||||
if (member?.GetCustomAttribute<SerializeIgnoreAttribute>() != null)
|
||||
property.ShouldSerialize = _ => false;
|
||||
if (member?.GetCustomAttribute<DeserializeIgnoreAttribute>() != null)
|
||||
property.ShouldDeserialize = _ => false;
|
||||
|
||||
SerializeAsAttribute serializeAs = member?.GetCustomAttribute<SerializeAsAttribute>();
|
||||
if (serializeAs != null)
|
||||
property.ValueProvider = new SerializeAsProvider(serializeAs.Format, _host);
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
public class JsonPropertySelector : JsonPropertyIgnorer
|
||||
{
|
||||
private readonly Dictionary<Type, HashSet<string>> _ignored;
|
||||
private readonly Dictionary<Type, HashSet<string>> _forceSerialize;
|
||||
|
||||
public JsonPropertySelector()
|
||||
protected override JsonContract CreateContract(Type objectType)
|
||||
{
|
||||
_ignored = new Dictionary<Type, HashSet<string>>();
|
||||
_forceSerialize = new Dictionary<Type, HashSet<string>>();
|
||||
JsonContract contract = base.CreateContract(objectType);
|
||||
if (Utility.GetGenericDefinition(objectType, typeof(Page<>)) == null
|
||||
&& !objectType.IsAssignableTo(typeof(IEnumerable)))
|
||||
{
|
||||
contract.OnSerializingCallbacks.Add((_, _) => _depth++);
|
||||
contract.OnSerializedCallbacks.Add((_, _) => _depth--);
|
||||
}
|
||||
|
||||
return contract;
|
||||
}
|
||||
}
|
||||
|
||||
public class PeopleRoleConverter : JsonConverter<PeopleRole>
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, PeopleRole value, JsonSerializer serializer)
|
||||
{
|
||||
ICollection<PeopleRole> oldPeople = value.Show?.People;
|
||||
ICollection<PeopleRole> oldRoles = value.People?.Roles;
|
||||
if (value.Show != null)
|
||||
value.Show.People = null;
|
||||
if (value.People != null)
|
||||
value.People.Roles = null;
|
||||
|
||||
JObject obj = JObject.FromObject(value.ForPeople ? value.People : value.Show, serializer);
|
||||
obj.Add("role", value.Role);
|
||||
obj.Add("type", value.Type);
|
||||
obj.WriteTo(writer);
|
||||
|
||||
if (value.Show != null)
|
||||
value.Show.People = oldPeople;
|
||||
if (value.People != null)
|
||||
value.People.Roles = oldRoles;
|
||||
}
|
||||
|
||||
public override PeopleRole ReadJson(JsonReader reader,
|
||||
Type objectType,
|
||||
PeopleRole existingValue,
|
||||
bool hasExistingValue,
|
||||
JsonSerializer serializer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class SerializeAsProvider : IValueProvider
|
||||
{
|
||||
private string _format;
|
||||
private string _host;
|
||||
|
||||
public SerializeAsProvider(string format, string host)
|
||||
{
|
||||
_format = format;
|
||||
_host = host.TrimEnd('/');
|
||||
}
|
||||
|
||||
public JsonPropertySelector(Dictionary<Type, HashSet<string>> ignored,
|
||||
Dictionary<Type, HashSet<string>> forceSerialize = null)
|
||||
public object GetValue(object target)
|
||||
{
|
||||
_ignored = ignored ?? new Dictionary<Type, HashSet<string>>();
|
||||
_forceSerialize = forceSerialize ?? new Dictionary<Type, HashSet<string>>();
|
||||
}
|
||||
|
||||
private bool IsIgnored(Type type, string propertyName)
|
||||
{
|
||||
while (type != null)
|
||||
return Regex.Replace(_format, @"(?<!{){(\w+)}", x =>
|
||||
{
|
||||
if (_ignored.ContainsKey(type) && _ignored[type].Contains(propertyName))
|
||||
return true;
|
||||
type = type.BaseType;
|
||||
}
|
||||
string value = x.Groups[1].Value;
|
||||
|
||||
return false;
|
||||
if (value == "HOST")
|
||||
return _host;
|
||||
|
||||
PropertyInfo properties = target.GetType()
|
||||
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
.FirstOrDefault(y => y.Name == value);
|
||||
if (properties == null)
|
||||
return null;
|
||||
if (properties.GetValue(target) is string ret)
|
||||
return ret;
|
||||
throw new ArgumentException($"Invalid serializer replacement {value}");
|
||||
});
|
||||
}
|
||||
|
||||
private bool IsSerializationForced(Type type, string propertyName)
|
||||
|
||||
public void SetValue(object target, object value)
|
||||
{
|
||||
while (type != null)
|
||||
{
|
||||
if (_forceSerialize.ContainsKey(type) && _forceSerialize[type].Contains(propertyName))
|
||||
return true;
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
JsonProperty property = base.CreateProperty(member, memberSerialization);
|
||||
|
||||
if (IsSerializationForced(property.DeclaringType, property.PropertyName))
|
||||
property.ShouldSerialize = i => true;
|
||||
else if (IsIgnored(property.DeclaringType, property.PropertyName))
|
||||
property.ShouldSerialize = i => false;
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
public class JsonDetailed : ActionFilterAttribute
|
||||
{
|
||||
public override void OnActionExecuted(ActionExecutedContext context)
|
||||
{
|
||||
if (context.Result is ObjectResult result)
|
||||
{
|
||||
result.Formatters.Add(new NewtonsoftJsonOutputFormatter(
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new JsonPropertySelector(null, new Dictionary<Type, HashSet<string>>
|
||||
{
|
||||
{typeof(Show), new HashSet<string> {"genres", "studio"}},
|
||||
{typeof(Episode), new HashSet<string> {"tracks"}},
|
||||
{typeof(PeopleRole), new HashSet<string> {"show"}}
|
||||
})
|
||||
},
|
||||
context.HttpContext.RequestServices.GetRequiredService<ArrayPool<char>>(),
|
||||
new MvcOptions()));
|
||||
}
|
||||
// Values are ignored and should not be editable, except if the internal value is set.
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,10 +12,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.3" />
|
||||
<PackageReference Include="Npgsql" Version="4.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Npgsql" Version="5.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -1,22 +1,18 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.CommonApi;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Attributes;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public abstract class LocalRepository<T>
|
||||
public abstract class LocalRepository<T> : IRepository<T>
|
||||
where T : class, IResource
|
||||
{
|
||||
protected readonly DbContext Database;
|
||||
@ -32,6 +28,7 @@ namespace Kyoo.Controllers
|
||||
public virtual void Dispose()
|
||||
{
|
||||
Database.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public virtual ValueTask DisposeAsync()
|
||||
@ -43,6 +40,11 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
return Database.Set<T>().FirstOrDefaultAsync(x => x.ID == id);
|
||||
}
|
||||
|
||||
public virtual Task<T> GetWithTracking(int id)
|
||||
{
|
||||
return Database.Set<T>().AsTracking().FirstOrDefaultAsync(x => x.ID == id);
|
||||
}
|
||||
|
||||
public virtual Task<T> Get(string slug)
|
||||
{
|
||||
@ -54,6 +56,8 @@ namespace Kyoo.Controllers
|
||||
return Database.Set<T>().FirstOrDefaultAsync(predicate);
|
||||
}
|
||||
|
||||
public abstract Task<ICollection<T>> Search(string query);
|
||||
|
||||
public virtual Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null,
|
||||
Sort<T> sort = default,
|
||||
Pagination limit = default)
|
||||
@ -112,7 +116,7 @@ namespace Kyoo.Controllers
|
||||
return query.CountAsync();
|
||||
}
|
||||
|
||||
public virtual async Task<T> Create([NotNull] T obj)
|
||||
public virtual async Task<T> Create(T obj)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
@ -147,61 +151,38 @@ namespace Kyoo.Controllers
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task<T> Edit(T edited, bool resetOld)
|
||||
{
|
||||
if (edited == null)
|
||||
throw new ArgumentNullException(nameof(edited));
|
||||
|
||||
bool lazyLoading = Database.ChangeTracker.LazyLoadingEnabled;
|
||||
Database.ChangeTracker.LazyLoadingEnabled = false;
|
||||
try
|
||||
{
|
||||
T old = await Get(edited.ID);
|
||||
|
||||
T old = await GetWithTracking(edited.ID);
|
||||
if (old == null)
|
||||
throw new ItemNotFound($"No resource found with the ID {edited.ID}.");
|
||||
|
||||
foreach (NavigationEntry navigation in Database.Entry(old).Navigations)
|
||||
{
|
||||
if (navigation.Metadata.PropertyInfo.GetCustomAttribute<EditableRelation>() != null)
|
||||
{
|
||||
if (resetOld)
|
||||
{
|
||||
await navigation.LoadAsync();
|
||||
continue;
|
||||
}
|
||||
IClrPropertyGetter getter = navigation.Metadata.GetGetter();
|
||||
|
||||
if (getter.HasDefaultValue(edited))
|
||||
continue;
|
||||
await navigation.LoadAsync();
|
||||
// TODO this may be usless for lists since the API does not return IDs but the
|
||||
// TODO LinkEquality does not check slugs (their are lazy loaded and only the ID is available)
|
||||
if (Utility.ResourceEquals(getter.GetClrValue(edited), getter.GetClrValue(old)))
|
||||
navigation.Metadata.PropertyInfo.SetValue(edited, default);
|
||||
}
|
||||
else
|
||||
navigation.Metadata.PropertyInfo.SetValue(edited, default);
|
||||
}
|
||||
|
||||
|
||||
if (resetOld)
|
||||
Utility.Nullify(old);
|
||||
Utility.Complete(old, edited);
|
||||
await Validate(old);
|
||||
Utility.Complete(old, edited, x => x.GetCustomAttribute<LoadableRelationAttribute>() == null);
|
||||
await EditRelations(old, edited, resetOld);
|
||||
await Database.SaveChangesAsync();
|
||||
return old;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Database.ChangeTracker.LazyLoadingEnabled = true;
|
||||
Database.ChangeTracker.LazyLoadingEnabled = lazyLoading;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool ShouldValidate<T2>(T2 value)
|
||||
|
||||
protected virtual Task EditRelations(T resource, T changed, bool resetOld)
|
||||
{
|
||||
return value != null && Database.Entry(value).State == EntityState.Detached;
|
||||
return Validate(resource);
|
||||
}
|
||||
|
||||
|
||||
protected virtual Task Validate(T resource)
|
||||
{
|
||||
if (string.IsNullOrEmpty(resource.Slug))
|
||||
@ -221,21 +202,9 @@ namespace Kyoo.Controllers
|
||||
throw new ArgumentException("Resources slug can't be number only.");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (PropertyInfo property in typeof(T).GetProperties()
|
||||
.Where(x => typeof(IEnumerable).IsAssignableFrom(x.PropertyType)
|
||||
&& !typeof(string).IsAssignableFrom(x.PropertyType)
|
||||
&& x.GetCustomAttribute<EditableRelation>() != null))
|
||||
{
|
||||
object value = property.GetValue(resource);
|
||||
if (value == null || value is ICollection || Utility.IsOfGenericType(value, typeof(ICollection<>)))
|
||||
continue;
|
||||
value = Utility.RunGenericMethod<object>(typeof(Enumerable), "ToList", Utility.GetEnumerableType((IEnumerable)value), value);
|
||||
property.SetValue(resource, value);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task Delete(int id)
|
||||
{
|
||||
T resource = await Get(id);
|
||||
@ -249,7 +218,7 @@ namespace Kyoo.Controllers
|
||||
}
|
||||
|
||||
public abstract Task Delete(T obj);
|
||||
|
||||
|
||||
public virtual async Task DeleteRange(IEnumerable<T> objs)
|
||||
{
|
||||
foreach (T obj in objs)
|
||||
@ -267,113 +236,11 @@ namespace Kyoo.Controllers
|
||||
foreach (string slug in slugs)
|
||||
await Delete(slug);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class LocalRepository<T, TInternal> : LocalRepository<TInternal>, IRepository<T>
|
||||
where T : class, IResource
|
||||
where TInternal : class, T, new()
|
||||
{
|
||||
protected LocalRepository(DbContext database) : base(database) { }
|
||||
|
||||
public new Task<T> Get(int id)
|
||||
{
|
||||
return base.Get(id).Cast<T>();
|
||||
}
|
||||
|
||||
public new Task<T> Get(string slug)
|
||||
public async Task DeleteRange(Expression<Func<T, bool>> where)
|
||||
{
|
||||
return base.Get(slug).Cast<T>();
|
||||
}
|
||||
|
||||
public Task<T> Get(Expression<Func<T, bool>> predicate)
|
||||
{
|
||||
return Get(predicate.Convert<Func<TInternal, bool>>()).Cast<T>();
|
||||
}
|
||||
|
||||
public abstract Task<ICollection<T>> Search(string query);
|
||||
|
||||
public virtual Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null,
|
||||
Sort<T> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
return ApplyFilters(Database.Set<TInternal>(), where, sort, limit);
|
||||
}
|
||||
|
||||
protected virtual async Task<ICollection<T>> ApplyFilters(IQueryable<TInternal> query,
|
||||
Expression<Func<T, bool>> where = null,
|
||||
Sort<T> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
ICollection<TInternal> items = await ApplyFilters(query,
|
||||
base.Get,
|
||||
DefaultSort,
|
||||
where.Convert<Func<TInternal, bool>>(),
|
||||
sort.To<TInternal>(),
|
||||
limit);
|
||||
|
||||
return items.ToList<T>();
|
||||
}
|
||||
|
||||
public virtual Task<int> GetCount(Expression<Func<T, bool>> where = null)
|
||||
{
|
||||
IQueryable<TInternal> query = Database.Set<TInternal>();
|
||||
if (where != null)
|
||||
query = query.Where(where.Convert<Func<TInternal, bool>>());
|
||||
return query.CountAsync();
|
||||
}
|
||||
|
||||
Task<T> IRepository<T>.Create(T item)
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
TInternal obj = item as TInternal ?? new TInternal();
|
||||
if (!(item is TInternal))
|
||||
Utility.Assign(obj, item);
|
||||
|
||||
return Create(obj).Cast<T>()
|
||||
.Then(x => item.ID = x.ID);
|
||||
}
|
||||
|
||||
Task<T> IRepository<T>.CreateIfNotExists(T item, bool silentFail)
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
TInternal obj = item as TInternal ?? new TInternal();
|
||||
if (!(item is TInternal))
|
||||
Utility.Assign(obj, item);
|
||||
|
||||
return CreateIfNotExists(obj, silentFail).Cast<T>()
|
||||
.Then(x => item.ID = x.ID);
|
||||
}
|
||||
|
||||
public Task<T> Edit(T edited, bool resetOld)
|
||||
{
|
||||
if (edited == null)
|
||||
throw new ArgumentNullException(nameof(edited));
|
||||
if (edited is TInternal intern)
|
||||
return Edit(intern, resetOld).Cast<T>();
|
||||
TInternal obj = new TInternal();
|
||||
Utility.Assign(obj, edited);
|
||||
return base.Edit(obj, resetOld).Cast<T>();
|
||||
}
|
||||
|
||||
public abstract override Task Delete([NotNull] TInternal obj);
|
||||
|
||||
Task IRepository<T>.Delete(T obj)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
if (obj is TInternal intern)
|
||||
return Delete(intern);
|
||||
TInternal item = new TInternal();
|
||||
Utility.Assign(item, obj);
|
||||
return Delete(item);
|
||||
}
|
||||
|
||||
public virtual async Task DeleteRange(IEnumerable<T> objs)
|
||||
{
|
||||
foreach (T obj in objs)
|
||||
await ((IRepository<T>)this).Delete(obj);
|
||||
ICollection<T> resources = await GetAll(where);
|
||||
await DeleteRange(resources);
|
||||
}
|
||||
}
|
||||
}
|
||||
100
Kyoo.CommonAPI/ResourceViewAttribute.cs
Normal file
100
Kyoo.CommonAPI/ResourceViewAttribute.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Kyoo.CommonApi
|
||||
{
|
||||
public class ResourceViewAttribute : ActionFilterAttribute
|
||||
{
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
if (context.ActionArguments.TryGetValue("where", out object dic) && dic is Dictionary<string, string> where)
|
||||
{
|
||||
where.Remove("fields");
|
||||
foreach ((string key, _) in context.ActionArguments)
|
||||
where.Remove(key);
|
||||
}
|
||||
|
||||
List<string> fields = context.HttpContext.Request.Query["fields"]
|
||||
.SelectMany(x => x.Split(','))
|
||||
.ToList();
|
||||
if (fields.Contains("internal"))
|
||||
{
|
||||
fields.Remove("internal");
|
||||
context.HttpContext.Items["internal"] = true;
|
||||
// TODO disable SerializeAs attributes when this is true.
|
||||
}
|
||||
if (context.ActionDescriptor is ControllerActionDescriptor descriptor)
|
||||
{
|
||||
Type type = descriptor.MethodInfo.ReturnType;
|
||||
type = Utility.GetGenericDefinition(type, typeof(Task<>))?.GetGenericArguments()[0] ?? type;
|
||||
type = Utility.GetGenericDefinition(type, typeof(ActionResult<>))?.GetGenericArguments()[0] ?? type;
|
||||
type = Utility.GetGenericDefinition(type, typeof(Page<>))?.GetGenericArguments()[0] ?? type;
|
||||
|
||||
PropertyInfo[] properties = type.GetProperties()
|
||||
.Where(x => x.GetCustomAttribute<LoadableRelationAttribute>() != null)
|
||||
.ToArray();
|
||||
fields = fields.Select(x =>
|
||||
{
|
||||
string property = properties
|
||||
.FirstOrDefault(y => string.Equals(x, y.Name, StringComparison.InvariantCultureIgnoreCase))
|
||||
?.Name;
|
||||
if (property != null)
|
||||
return property;
|
||||
context.Result = new BadRequestObjectResult(new
|
||||
{
|
||||
Error = $"{x} does not exist on {type.Name}."
|
||||
});
|
||||
return null;
|
||||
})
|
||||
.ToList();
|
||||
if (context.Result != null)
|
||||
return;
|
||||
}
|
||||
context.HttpContext.Items["fields"] = fields;
|
||||
base.OnActionExecuting(context);
|
||||
}
|
||||
|
||||
public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||
{
|
||||
if (context.Result is ObjectResult result)
|
||||
await LoadResultRelations(context, result);
|
||||
await base.OnResultExecutionAsync(context, next);
|
||||
}
|
||||
|
||||
private static async Task LoadResultRelations(ActionContext context, ObjectResult result)
|
||||
{
|
||||
if (result.DeclaredType == null)
|
||||
return;
|
||||
|
||||
await using ILibraryManager library = context.HttpContext.RequestServices.GetService<ILibraryManager>();
|
||||
ICollection<string> fields = (ICollection<string>)context.HttpContext.Items["fields"];
|
||||
Type pageType = Utility.GetGenericDefinition(result.DeclaredType, typeof(Page<>));
|
||||
|
||||
|
||||
if (pageType != null)
|
||||
{
|
||||
foreach (IResource resource in ((dynamic)result.Value).Items)
|
||||
{
|
||||
foreach (string field in fields!)
|
||||
await library!.Load(resource, field);
|
||||
}
|
||||
}
|
||||
else if (result.DeclaredType.IsAssignableTo(typeof(IResource)))
|
||||
{
|
||||
foreach (string field in fields!)
|
||||
await library!.Load((IResource)result.Value, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
86
Kyoo/Controllers/FileManager.cs
Normal file
86
Kyoo/Controllers/FileManager.cs
Normal file
@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public class FileManager : IFileManager
|
||||
{
|
||||
private FileExtensionContentTypeProvider _provider;
|
||||
|
||||
private string _GetContentType(string path)
|
||||
{
|
||||
if (_provider == null)
|
||||
{
|
||||
_provider = new FileExtensionContentTypeProvider();
|
||||
_provider.Mappings[".mkv"] = "video/x-matroska";
|
||||
_provider.Mappings[".ass"] = "text/x-ssa";
|
||||
_provider.Mappings[".srt"] = "application/x-subrip";
|
||||
}
|
||||
|
||||
if (_provider.TryGetContentType(path, out string contentType))
|
||||
return contentType;
|
||||
throw new NotImplementedException($"Can't get the content type of the file at: {path}");
|
||||
}
|
||||
|
||||
// TODO add a way to force content type
|
||||
public IActionResult FileResult(string path, bool range)
|
||||
{
|
||||
if (path == null)
|
||||
return new NotFoundResult();
|
||||
if (!File.Exists(path))
|
||||
return new NotFoundResult();
|
||||
return new PhysicalFileResult(Path.GetFullPath(path), _GetContentType(path))
|
||||
{
|
||||
EnableRangeProcessing = range
|
||||
};
|
||||
}
|
||||
|
||||
public StreamReader GetReader(string path)
|
||||
{
|
||||
if (path == null)
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
return new StreamReader(path);
|
||||
}
|
||||
|
||||
public Task<ICollection<string>> ListFiles(string path)
|
||||
{
|
||||
if (path == null)
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
return Task.FromResult<ICollection<string>>(Directory.GetFiles(path));
|
||||
}
|
||||
|
||||
public Task<bool> Exists(string path)
|
||||
{
|
||||
return Task.FromResult(File.Exists(path));
|
||||
}
|
||||
|
||||
public string GetExtraDirectory(Show show)
|
||||
{
|
||||
string path = Path.Combine(show.Path, "Extra");
|
||||
Directory.CreateDirectory(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
public string GetExtraDirectory(Season season)
|
||||
{
|
||||
if (season.Show == null)
|
||||
throw new NotImplementedException("Can't get season's extra directory when season.Show == null.");
|
||||
// TODO use a season.Path here.
|
||||
string path = Path.Combine(season.Show.Path, "Extra");
|
||||
Directory.CreateDirectory(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
public string GetExtraDirectory(Episode episode)
|
||||
{
|
||||
string path = Path.Combine(Path.GetDirectoryName(episode.Path)!, "Extra");
|
||||
Directory.CreateDirectory(path);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,7 @@ namespace Kyoo.Controllers
|
||||
private async Task<T> GetMetadata<T>(Func<IMetadataProvider, Task<T>> providerCall, Library library, string what)
|
||||
where T : new()
|
||||
{
|
||||
T ret = new T();
|
||||
T ret = new();
|
||||
|
||||
IEnumerable<IMetadataProvider> providers = library?.Providers
|
||||
.Select(x => _providers.FirstOrDefault(y => y.Provider.Slug == x.Slug))
|
||||
@ -40,11 +40,11 @@ namespace Kyoo.Controllers
|
||||
}
|
||||
|
||||
private async Task<List<T>> GetMetadata<T>(
|
||||
Func<IMetadataProvider, Task<IEnumerable<T>>> providerCall,
|
||||
Func<IMetadataProvider, Task<ICollection<T>>> providerCall,
|
||||
Library library,
|
||||
string what)
|
||||
{
|
||||
List<T> ret = new List<T>();
|
||||
List<T> ret = new();
|
||||
|
||||
IEnumerable<IMetadataProvider> providers = library?.Providers
|
||||
.Select(x => _providers.FirstOrDefault(y => y.Provider.Slug == x.Slug))
|
||||
@ -121,6 +121,7 @@ namespace Kyoo.Controllers
|
||||
$"the season {seasonNumber} of {show.Title}");
|
||||
season.Show = show;
|
||||
season.ShowID = show.ID;
|
||||
season.ShowSlug = show.Slug;
|
||||
season.SeasonNumber = season.SeasonNumber == -1 ? seasonNumber : season.SeasonNumber;
|
||||
season.Title ??= $"Season {season.SeasonNumber}";
|
||||
return season;
|
||||
@ -139,6 +140,7 @@ namespace Kyoo.Controllers
|
||||
"an episode");
|
||||
episode.Show = show;
|
||||
episode.ShowID = show.ID;
|
||||
episode.ShowSlug = show.Slug;
|
||||
episode.Path = episodePath;
|
||||
episode.SeasonNumber = episode.SeasonNumber != -1 ? episode.SeasonNumber : seasonNumber;
|
||||
episode.EpisodeNumber = episode.EpisodeNumber != -1 ? episode.EpisodeNumber : episodeNumber;
|
||||
@ -146,7 +148,7 @@ namespace Kyoo.Controllers
|
||||
return episode;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<PeopleRole>> GetPeople(Show show, Library library)
|
||||
public async Task<ICollection<PeopleRole>> GetPeople(Show show, Library library)
|
||||
{
|
||||
List<PeopleRole> people = await GetMetadata(
|
||||
provider => provider.GetPeople(show),
|
||||
|
||||
@ -8,11 +8,11 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public class CollectionRepository : LocalRepository<Collection, CollectionDE>, ICollectionRepository
|
||||
public class CollectionRepository : LocalRepository<Collection>, ICollectionRepository
|
||||
{
|
||||
private bool _disposed;
|
||||
private readonly DatabaseContext _database;
|
||||
protected override Expression<Func<CollectionDE, object>> DefaultSort => x => x.Name;
|
||||
protected override Expression<Func<Collection, object>> DefaultSort => x => x.Name;
|
||||
|
||||
public CollectionRepository(DatabaseContext database) : base(database)
|
||||
{
|
||||
@ -25,6 +25,7 @@ namespace Kyoo.Controllers
|
||||
return;
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
@ -39,11 +40,12 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
return await _database.Collections
|
||||
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
|
||||
.OrderBy(DefaultSort)
|
||||
.Take(20)
|
||||
.ToListAsync<Collection>();
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public override async Task<CollectionDE> Create(CollectionDE obj)
|
||||
public override async Task<Collection> Create(Collection obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
@ -51,18 +53,12 @@ namespace Kyoo.Controllers
|
||||
return obj;
|
||||
}
|
||||
|
||||
public override async Task Delete(CollectionDE obj)
|
||||
public override async Task Delete(Collection obj)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
_database.Entry(obj).State = EntityState.Deleted;
|
||||
if (obj.Links != null)
|
||||
foreach (CollectionLink link in obj.Links)
|
||||
_database.Entry(link).State = EntityState.Deleted;
|
||||
if (obj.LibraryLinks != null)
|
||||
foreach (LibraryLink link in obj.LibraryLinks)
|
||||
_database.Entry(link).State = EntityState.Deleted;
|
||||
await _database.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@ using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
@ -15,13 +14,21 @@ namespace Kyoo.Controllers
|
||||
private bool _disposed;
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly IProviderRepository _providers;
|
||||
private readonly IShowRepository _shows;
|
||||
private readonly ITrackRepository _tracks;
|
||||
protected override Expression<Func<Episode, object>> DefaultSort => x => x.EpisodeNumber;
|
||||
|
||||
|
||||
public EpisodeRepository(DatabaseContext database, IProviderRepository providers) : base(database)
|
||||
public EpisodeRepository(DatabaseContext database,
|
||||
IProviderRepository providers,
|
||||
IShowRepository shows,
|
||||
ITrackRepository tracks)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
_providers = providers;
|
||||
_shows = shows;
|
||||
_tracks = tracks;
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +39,8 @@ namespace Kyoo.Controllers
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
_providers.Dispose();
|
||||
_shows.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
@ -41,11 +50,20 @@ namespace Kyoo.Controllers
|
||||
_disposed = true;
|
||||
await _database.DisposeAsync();
|
||||
await _providers.DisposeAsync();
|
||||
await _shows.DisposeAsync();
|
||||
}
|
||||
|
||||
public override async Task<Episode> Get(int id)
|
||||
{
|
||||
Episode ret = await base.Get(id);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override Task<Episode> Get(string slug)
|
||||
{
|
||||
Match match = Regex.Match(slug, @"(?<show>.*)-s(?<season>\d*)-e(?<episode>\d*)");
|
||||
Match match = Regex.Match(slug, @"(?<show>.*)-s(?<season>\d*)e(?<episode>\d*)");
|
||||
|
||||
if (!match.Success)
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == slug);
|
||||
@ -53,76 +71,138 @@ namespace Kyoo.Controllers
|
||||
int.Parse(match.Groups["season"].Value),
|
||||
int.Parse(match.Groups["episode"].Value));
|
||||
}
|
||||
|
||||
public Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber)
|
||||
|
||||
public override async Task<Episode> Get(Expression<Func<Episode, bool>> predicate)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.SeasonNumber == seasonNumber
|
||||
&& x.EpisodeNumber == episodeNumber);
|
||||
Episode ret = await base.Get(predicate);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Task<Episode> Get(int showID, int seasonNumber, int episodeNumber)
|
||||
public async Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.SeasonNumber == seasonNumber
|
||||
&& x.EpisodeNumber == episodeNumber);
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.SeasonNumber == seasonNumber
|
||||
&& x.EpisodeNumber == episodeNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = showSlug;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Task<Episode> Get(int seasonID, int episodeNumber)
|
||||
public async Task<Episode> Get(int showID, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.SeasonID == seasonID
|
||||
&& x.EpisodeNumber == episodeNumber);
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.SeasonNumber == seasonNumber
|
||||
&& x.EpisodeNumber == episodeNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(showID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Task<Episode> GetAbsolute(int showID, int absoluteNumber)
|
||||
public async Task<Episode> Get(int seasonID, int episodeNumber)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.AbsoluteNumber == absoluteNumber);
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.SeasonID == seasonID
|
||||
&& x.EpisodeNumber == episodeNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Task<Episode> GetAbsolute(string showSlug, int absoluteNumber)
|
||||
public async Task<Episode> GetAbsolute(int showID, int absoluteNumber)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.AbsoluteNumber == absoluteNumber);
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.AbsoluteNumber == absoluteNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(showID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public async Task<ICollection<Episode>> Search(string query)
|
||||
public async Task<Episode> GetAbsolute(string showSlug, int absoluteNumber)
|
||||
{
|
||||
return await _database.Episodes
|
||||
.Where(x => EF.Functions.ILike(x.Title, $"%{query}%"))
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.AbsoluteNumber == absoluteNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = showSlug;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override async Task<ICollection<Episode>> Search(string query)
|
||||
{
|
||||
List<Episode> episodes = await _database.Episodes
|
||||
.Where(x => EF.Functions.ILike(x.Title, $"%{query}%") && x.EpisodeNumber != -1)
|
||||
.OrderBy(DefaultSort)
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
foreach (Episode episode in episodes)
|
||||
episode.ShowSlug = await _shows.GetSlug(episode.ShowID);
|
||||
return episodes;
|
||||
}
|
||||
|
||||
public override async Task<ICollection<Episode>> GetAll(Expression<Func<Episode, bool>> where = null,
|
||||
Sort<Episode> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
ICollection<Episode> episodes = await base.GetAll(where, sort, limit);
|
||||
foreach (Episode episode in episodes)
|
||||
episode.ShowSlug = await _shows.GetSlug(episode.ShowID);
|
||||
return episodes;
|
||||
}
|
||||
|
||||
public override async Task<Episode> Create(Episode obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
if (obj.ExternalIDs != null)
|
||||
foreach (MetadataID entry in obj.ExternalIDs)
|
||||
_database.Entry(entry).State = EntityState.Added;
|
||||
|
||||
if (obj.Tracks != null)
|
||||
foreach (Track entry in obj.Tracks)
|
||||
_database.Entry(entry).State = EntityState.Added;
|
||||
|
||||
obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Added);
|
||||
await _database.SaveChangesAsync($"Trying to insert a duplicated episode (slug {obj.Slug} already exists).");
|
||||
return obj;
|
||||
return await ValidateTracks(obj);
|
||||
}
|
||||
|
||||
protected override async Task Validate(Episode resource)
|
||||
protected override async Task EditRelations(Episode resource, Episode changed, bool resetOld)
|
||||
{
|
||||
if (resource.ShowID <= 0)
|
||||
throw new InvalidOperationException($"Can't store an episode not related to any show (showID: {resource.ShowID}).");
|
||||
|
||||
await base.Validate(resource);
|
||||
|
||||
if (resource.ExternalIDs != null)
|
||||
|
||||
if (changed.Tracks != null || resetOld)
|
||||
{
|
||||
foreach (MetadataID link in resource.ExternalIDs)
|
||||
if (ShouldValidate(link))
|
||||
link.Provider = await _providers.CreateIfNotExists(link.Provider, true);
|
||||
await _tracks.DeleteRange(x => x.EpisodeID == resource.ID);
|
||||
resource.Tracks = changed.Tracks;
|
||||
await ValidateTracks(resource);
|
||||
}
|
||||
|
||||
if (changed.ExternalIDs != null || resetOld)
|
||||
{
|
||||
await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync();
|
||||
resource.ExternalIDs = changed.ExternalIDs;
|
||||
}
|
||||
|
||||
await Validate(resource);
|
||||
}
|
||||
|
||||
private async Task<Episode> ValidateTracks(Episode resource)
|
||||
{
|
||||
resource.Tracks = await resource.Tracks.MapAsync((x, i) =>
|
||||
{
|
||||
x.Episode = resource;
|
||||
x.TrackIndex = resource.Tracks.Take(i).Count(y => x.Language == y.Language
|
||||
&& x.IsForced == y.IsForced
|
||||
&& x.Codec == y.Codec
|
||||
&& x.Type == y.Type);
|
||||
return _tracks.Create(x);
|
||||
}).ToListAsync();
|
||||
return resource;
|
||||
}
|
||||
|
||||
protected override async Task Validate(Episode resource)
|
||||
{
|
||||
await base.Validate(resource);
|
||||
resource.ExternalIDs = await resource.ExternalIDs.SelectAsync(async x =>
|
||||
{
|
||||
x.Provider = await _providers.CreateIfNotExists(x.Provider, true);
|
||||
x.ProviderID = x.Provider.ID;
|
||||
_database.Entry(x.Provider).State = EntityState.Detached;
|
||||
return x;
|
||||
}).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task Delete(string showSlug, int seasonNumber, int episodeNumber)
|
||||
@ -137,10 +217,8 @@ namespace Kyoo.Controllers
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
_database.Entry(obj).State = EntityState.Deleted;
|
||||
if (obj.ExternalIDs != null)
|
||||
foreach (MetadataID entry in obj.ExternalIDs)
|
||||
_database.Entry(entry).State = EntityState.Deleted;
|
||||
// Since Tracks & Episodes are on the same database and handled by dotnet-ef, we can't use the repository to delete them.
|
||||
await obj.Tracks.ForEachAsync(x => _tracks.Delete(x));
|
||||
obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Deleted);
|
||||
await _database.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,11 +8,11 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public class GenreRepository : LocalRepository<Genre, GenreDE>, IGenreRepository
|
||||
public class GenreRepository : LocalRepository<Genre>, IGenreRepository
|
||||
{
|
||||
private bool _disposed;
|
||||
private readonly DatabaseContext _database;
|
||||
protected override Expression<Func<GenreDE, object>> DefaultSort => x => x.Slug;
|
||||
protected override Expression<Func<Genre, object>> DefaultSort => x => x.Slug;
|
||||
|
||||
|
||||
public GenreRepository(DatabaseContext database) : base(database)
|
||||
@ -26,6 +26,7 @@ namespace Kyoo.Controllers
|
||||
return;
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
@ -40,11 +41,12 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
return await _database.Genres
|
||||
.Where(genre => EF.Functions.ILike(genre.Name, $"%{query}%"))
|
||||
.OrderBy(DefaultSort)
|
||||
.Take(20)
|
||||
.ToListAsync<Genre>();
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public override async Task<GenreDE> Create(GenreDE obj)
|
||||
public override async Task<Genre> Create(Genre obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
@ -52,15 +54,12 @@ namespace Kyoo.Controllers
|
||||
return obj;
|
||||
}
|
||||
|
||||
public override async Task Delete(GenreDE obj)
|
||||
public override async Task Delete(Genre obj)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
_database.Entry(obj).State = EntityState.Deleted;
|
||||
if (obj.Links != null)
|
||||
foreach (GenreLink link in obj.Links)
|
||||
_database.Entry(link).State = EntityState.Deleted;
|
||||
await _database.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ namespace Kyoo.Controllers
|
||||
_shows.Value.Dispose();
|
||||
if (_collections.IsValueCreated)
|
||||
_collections.Value.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
@ -71,7 +72,7 @@ namespace Kyoo.Controllers
|
||||
|
||||
private IQueryable<LibraryItem> ItemsQuery
|
||||
=> _database.Shows
|
||||
.Where(x => !_database.CollectionLinks.Any(y => y.ChildID == x.ID))
|
||||
.Where(x => !x.Collections.Any())
|
||||
.Select(LibraryItem.FromShow)
|
||||
.Concat(_database.Collections
|
||||
.Select(LibraryItem.FromCollection));
|
||||
@ -91,10 +92,11 @@ namespace Kyoo.Controllers
|
||||
return query.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<ICollection<LibraryItem>> Search(string query)
|
||||
public override async Task<ICollection<LibraryItem>> Search(string query)
|
||||
{
|
||||
return await ItemsQuery
|
||||
.Where(x => EF.Functions.ILike(x.Title, $"%{query}%"))
|
||||
.OrderBy(DefaultSort)
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
}
|
||||
@ -108,22 +110,19 @@ namespace Kyoo.Controllers
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
public override Task<LibraryItem> Edit(LibraryItem obj, bool reset) => throw new InvalidOperationException();
|
||||
protected override Task Validate(LibraryItem resource) => throw new InvalidOperationException();
|
||||
public override Task Delete(int id) => throw new InvalidOperationException();
|
||||
public override Task Delete(string slug) => throw new InvalidOperationException();
|
||||
public override Task Delete(LibraryItem obj) => throw new InvalidOperationException();
|
||||
|
||||
private IQueryable<LibraryItem> LibraryRelatedQuery(Expression<Func<LibraryLink, bool>> selector)
|
||||
=> _database.LibraryLinks
|
||||
private IQueryable<LibraryItem> LibraryRelatedQuery(Expression<Func<Library, bool>> selector)
|
||||
=> _database.Libraries
|
||||
.Where(selector)
|
||||
.Select(x => x.Show)
|
||||
.Where(x => x != null)
|
||||
.Where(x => !_database.CollectionLinks.Any(y => y.ChildID == x.ID))
|
||||
.SelectMany(x => x.Shows)
|
||||
.Where(x => !x.Collections.Any())
|
||||
.Select(LibraryItem.FromShow)
|
||||
.Concat(_database.LibraryLinks
|
||||
.Concat(_database.Libraries
|
||||
.Where(selector)
|
||||
.Select(x => x.Collection)
|
||||
.Where(x => x != null)
|
||||
.SelectMany(x => x.Collections)
|
||||
.Select(LibraryItem.FromCollection));
|
||||
|
||||
public async Task<ICollection<LibraryItem>> GetFromLibrary(int id,
|
||||
@ -131,7 +130,7 @@ namespace Kyoo.Controllers
|
||||
Sort<LibraryItem> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
ICollection<LibraryItem> items = await ApplyFilters(LibraryRelatedQuery(x => x.LibraryID == id),
|
||||
ICollection<LibraryItem> items = await ApplyFilters(LibraryRelatedQuery(x => x.ID == id),
|
||||
where,
|
||||
sort,
|
||||
limit);
|
||||
@ -145,7 +144,7 @@ namespace Kyoo.Controllers
|
||||
Sort<LibraryItem> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
ICollection<LibraryItem> items = await ApplyFilters(LibraryRelatedQuery(x => x.Library.Slug == slug),
|
||||
ICollection<LibraryItem> items = await ApplyFilters(LibraryRelatedQuery(x => x.Slug == slug),
|
||||
where,
|
||||
sort,
|
||||
limit);
|
||||
|
||||
@ -4,18 +4,16 @@ using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public class LibraryRepository : LocalRepository<Library, LibraryDE>, ILibraryRepository
|
||||
public class LibraryRepository : LocalRepository<Library>, ILibraryRepository
|
||||
{
|
||||
private bool _disposed;
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly IProviderRepository _providers;
|
||||
protected override Expression<Func<LibraryDE, object>> DefaultSort => x => x.ID;
|
||||
protected override Expression<Func<Library, object>> DefaultSort => x => x.ID;
|
||||
|
||||
|
||||
public LibraryRepository(DatabaseContext database, IProviderRepository providers)
|
||||
@ -32,6 +30,7 @@ namespace Kyoo.Controllers
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
_providers.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
@ -47,23 +46,30 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
return await _database.Libraries
|
||||
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
|
||||
.OrderBy(DefaultSort)
|
||||
.Take(20)
|
||||
.ToListAsync<Library>();
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public override async Task<LibraryDE> Create(LibraryDE obj)
|
||||
public override async Task<Library> Create(Library obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
if (obj.ProviderLinks != null)
|
||||
foreach (ProviderLink entry in obj.ProviderLinks)
|
||||
_database.Entry(entry).State = EntityState.Added;
|
||||
|
||||
obj.ProviderLinks = obj.Providers?.Select(x => Link.Create(obj, x)).ToArray();
|
||||
obj.ProviderLinks.ForEach(x => _database.Entry(x).State = EntityState.Added);
|
||||
await _database.SaveChangesAsync($"Trying to insert a duplicated library (slug {obj.Slug} already exists).");
|
||||
return obj;
|
||||
}
|
||||
|
||||
protected override async Task Validate(LibraryDE resource)
|
||||
protected override async Task Validate(Library resource)
|
||||
{
|
||||
await base.Validate(resource);
|
||||
resource.Providers = await resource.Providers
|
||||
.SelectAsync(x => _providers.CreateIfNotExists(x, true))
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
protected override async Task EditRelations(Library resource, Library changed, bool resetOld)
|
||||
{
|
||||
if (string.IsNullOrEmpty(resource.Slug))
|
||||
throw new ArgumentException("The library's slug must be set and not empty");
|
||||
@ -71,27 +77,21 @@ namespace Kyoo.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.");
|
||||
|
||||
await base.Validate(resource);
|
||||
|
||||
if (resource.ProviderLinks != null)
|
||||
foreach (ProviderLink link in resource.ProviderLinks)
|
||||
if (ShouldValidate(link))
|
||||
link.Child = await _providers.CreateIfNotExists(link.Child, true);
|
||||
|
||||
if (changed.Providers != null || resetOld)
|
||||
{
|
||||
await Validate(changed);
|
||||
await Database.Entry(resource).Collection(x => x.Providers).LoadAsync();
|
||||
resource.Providers = changed.Providers;
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task Delete(LibraryDE obj)
|
||||
public override async Task Delete(Library obj)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
_database.Entry(obj).State = EntityState.Deleted;
|
||||
if (obj.ProviderLinks != null)
|
||||
foreach (ProviderLink entry in obj.ProviderLinks)
|
||||
_database.Entry(entry).State = EntityState.Deleted;
|
||||
if (obj.Links != null)
|
||||
foreach (LibraryLink entry in obj.Links)
|
||||
_database.Entry(entry).State = EntityState.Deleted;
|
||||
await _database.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ namespace Kyoo.Controllers
|
||||
_providers.Dispose();
|
||||
if (_shows.IsValueCreated)
|
||||
_shows.Value.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
@ -49,10 +50,11 @@ namespace Kyoo.Controllers
|
||||
await _shows.Value.DisposeAsync();
|
||||
}
|
||||
|
||||
public async Task<ICollection<People>> Search(string query)
|
||||
public override async Task<ICollection<People>> Search(string query)
|
||||
{
|
||||
return await _database.People
|
||||
.Where(people => EF.Functions.ILike(people.Name, $"%{query}%"))
|
||||
.OrderBy(DefaultSort)
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
}
|
||||
@ -61,10 +63,7 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
if (obj.ExternalIDs != null)
|
||||
foreach (MetadataID entry in obj.ExternalIDs)
|
||||
_database.Entry(entry).State = EntityState.Added;
|
||||
|
||||
obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Added);
|
||||
await _database.SaveChangesAsync($"Trying to insert a duplicated people (slug {obj.Slug} already exists).");
|
||||
return obj;
|
||||
}
|
||||
@ -72,25 +71,45 @@ namespace Kyoo.Controllers
|
||||
protected override async Task Validate(People resource)
|
||||
{
|
||||
await base.Validate(resource);
|
||||
|
||||
if (resource.ExternalIDs != null)
|
||||
foreach (MetadataID link in resource.ExternalIDs)
|
||||
if (ShouldValidate(link))
|
||||
link.Provider = await _providers.CreateIfNotExists(link.Provider, true);
|
||||
await resource.ExternalIDs.ForEachAsync(async id =>
|
||||
{
|
||||
id.Provider = await _providers.CreateIfNotExists(id.Provider, true);
|
||||
id.ProviderID = id.Provider.ID;
|
||||
_database.Entry(id.Provider).State = EntityState.Detached;
|
||||
});
|
||||
await resource.Roles.ForEachAsync(async role =>
|
||||
{
|
||||
role.Show = await _shows.Value.CreateIfNotExists(role.Show, true);
|
||||
role.ShowID = role.Show.ID;
|
||||
_database.Entry(role.Show).State = EntityState.Detached;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
protected override async Task EditRelations(People resource, People changed, bool resetOld)
|
||||
{
|
||||
if (changed.Roles != null || resetOld)
|
||||
{
|
||||
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;
|
||||
|
||||
}
|
||||
await base.EditRelations(resource, changed, resetOld);
|
||||
}
|
||||
|
||||
public override async Task Delete(People obj)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
_database.Entry(obj).State = EntityState.Deleted;
|
||||
if (obj.ExternalIDs != null)
|
||||
foreach (MetadataID entry in obj.ExternalIDs)
|
||||
_database.Entry(entry).State = EntityState.Deleted;
|
||||
if (obj.Roles != null)
|
||||
foreach (PeopleRole link in obj.Roles)
|
||||
_database.Entry(link).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();
|
||||
}
|
||||
|
||||
@ -99,7 +118,9 @@ namespace Kyoo.Controllers
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles.Where(x => x.ShowID == showID),
|
||||
ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles
|
||||
.Where(x => x.ShowID == showID)
|
||||
.Include(x => x.People),
|
||||
id => _database.PeopleRoles.FirstOrDefaultAsync(x => x.ID == id),
|
||||
x => x.People.Name,
|
||||
where,
|
||||
@ -107,6 +128,8 @@ namespace Kyoo.Controllers
|
||||
limit);
|
||||
if (!people.Any() && await _shows.Value.Get(showID) == null)
|
||||
throw new ItemNotFound();
|
||||
foreach (PeopleRole role in people)
|
||||
role.ForPeople = true;
|
||||
return people;
|
||||
}
|
||||
|
||||
@ -115,7 +138,10 @@ namespace Kyoo.Controllers
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles.Where(x => x.Show.Slug == showSlug),
|
||||
ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles
|
||||
.Where(x => x.Show.Slug == showSlug)
|
||||
.Include(x => x.People)
|
||||
.Include(x => x.Show),
|
||||
id => _database.PeopleRoles.FirstOrDefaultAsync(x => x.ID == id),
|
||||
x => x.People.Name,
|
||||
where,
|
||||
@ -123,18 +149,21 @@ namespace Kyoo.Controllers
|
||||
limit);
|
||||
if (!people.Any() && await _shows.Value.Get(showSlug) == null)
|
||||
throw new ItemNotFound();
|
||||
foreach (PeopleRole role in people)
|
||||
role.ForPeople = true;
|
||||
return people;
|
||||
}
|
||||
|
||||
public async Task<ICollection<ShowRole>> GetFromPeople(int peopleID,
|
||||
Expression<Func<ShowRole, bool>> where = null,
|
||||
Sort<ShowRole> sort = default,
|
||||
public async Task<ICollection<PeopleRole>> GetFromPeople(int peopleID,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
ICollection<ShowRole> roles = await ApplyFilters(_database.PeopleRoles.Where(x => x.PeopleID == peopleID)
|
||||
.Select(ShowRole.FromPeopleRole),
|
||||
async id => new ShowRole(await _database.PeopleRoles.FirstOrDefaultAsync(x => x.ID == id)),
|
||||
x => x.Title,
|
||||
ICollection<PeopleRole> roles = await ApplyFilters(_database.PeopleRoles
|
||||
.Where(x => x.PeopleID == peopleID)
|
||||
.Include(x => x.Show),
|
||||
id => _database.PeopleRoles.FirstOrDefaultAsync(x => x.ID == id),
|
||||
x => x.Show.Title,
|
||||
where,
|
||||
sort,
|
||||
limit);
|
||||
@ -143,15 +172,16 @@ namespace Kyoo.Controllers
|
||||
return roles;
|
||||
}
|
||||
|
||||
public async Task<ICollection<ShowRole>> GetFromPeople(string slug,
|
||||
Expression<Func<ShowRole, bool>> where = null,
|
||||
Sort<ShowRole> sort = default,
|
||||
public async Task<ICollection<PeopleRole>> GetFromPeople(string slug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
ICollection<ShowRole> roles = await ApplyFilters(_database.PeopleRoles.Where(x => x.People.Slug == slug)
|
||||
.Select(ShowRole.FromPeopleRole),
|
||||
async id => new ShowRole(await _database.PeopleRoles.FirstOrDefaultAsync(x => x.ID == id)),
|
||||
x => x.Title,
|
||||
ICollection<PeopleRole> roles = await ApplyFilters(_database.PeopleRoles
|
||||
.Where(x => x.People.Slug == slug)
|
||||
.Include(x => x.Show),
|
||||
id => _database.PeopleRoles.FirstOrDefaultAsync(x => x.ID == id),
|
||||
x => x.Show.Title,
|
||||
where,
|
||||
sort,
|
||||
limit);
|
||||
|
||||
@ -19,10 +19,11 @@ namespace Kyoo.Controllers
|
||||
_database = database;
|
||||
}
|
||||
|
||||
public async Task<ICollection<ProviderID>> Search(string query)
|
||||
public override async Task<ICollection<ProviderID>> Search(string query)
|
||||
{
|
||||
return await _database.Providers
|
||||
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
|
||||
.OrderBy(DefaultSort)
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
}
|
||||
@ -31,7 +32,6 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
|
||||
await _database.SaveChangesAsync($"Trying to insert a duplicated provider (slug {obj.Slug} already exists).");
|
||||
return obj;
|
||||
}
|
||||
@ -42,8 +42,20 @@ namespace Kyoo.Controllers
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
_database.Entry(obj).State = EntityState.Deleted;
|
||||
// TODO handle ExternalID deletion when they refer to this providerID.
|
||||
obj.MetadataLinks.ForEach(x => _database.Entry(x).State = EntityState.Deleted);
|
||||
await _database.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public Task<ICollection<MetadataID>> GetMetadataID(Expression<Func<MetadataID, bool>> where = null,
|
||||
Sort<MetadataID> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
return ApplyFilters(_database.MetadataIds.Include(y => y.Provider),
|
||||
x => _database.MetadataIds.FirstOrDefaultAsync(y => y.ID == x),
|
||||
x => x.ID,
|
||||
where,
|
||||
sort,
|
||||
limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,6 @@ using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@ -16,17 +15,20 @@ namespace Kyoo.Controllers
|
||||
private bool _disposed;
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly IProviderRepository _providers;
|
||||
private readonly IShowRepository _shows;
|
||||
private readonly Lazy<IEpisodeRepository> _episodes;
|
||||
protected override Expression<Func<Season, object>> DefaultSort => x => x.SeasonNumber;
|
||||
|
||||
|
||||
public SeasonRepository(DatabaseContext database,
|
||||
IProviderRepository providers,
|
||||
IShowRepository shows,
|
||||
IServiceProvider services)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
_providers = providers;
|
||||
_shows = shows;
|
||||
_episodes = new Lazy<IEpisodeRepository>(services.GetRequiredService<IEpisodeRepository>);
|
||||
}
|
||||
|
||||
@ -38,8 +40,10 @@ namespace Kyoo.Controllers
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
_providers.Dispose();
|
||||
_shows.Dispose();
|
||||
if (_episodes.IsValueCreated)
|
||||
_episodes.Value.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
@ -49,10 +53,27 @@ namespace Kyoo.Controllers
|
||||
_disposed = true;
|
||||
await _database.DisposeAsync();
|
||||
await _providers.DisposeAsync();
|
||||
await _shows.DisposeAsync();
|
||||
if (_episodes.IsValueCreated)
|
||||
await _episodes.Value.DisposeAsync();
|
||||
}
|
||||
|
||||
public override async Task<Season> Get(int id)
|
||||
{
|
||||
Season ret = await base.Get(id);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override async Task<Season> Get(Expression<Func<Season, bool>> predicate)
|
||||
{
|
||||
Season ret = await base.Get(predicate);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override Task<Season> Get(string slug)
|
||||
{
|
||||
Match match = Regex.Match(slug, @"(?<show>.*)-s(?<season>\d*)");
|
||||
@ -62,34 +83,51 @@ namespace Kyoo.Controllers
|
||||
return Get(match.Groups["show"].Value, int.Parse(match.Groups["season"].Value));
|
||||
}
|
||||
|
||||
public Task<Season> Get(int showID, int seasonNumber)
|
||||
public async Task<Season> Get(int showID, int seasonNumber)
|
||||
{
|
||||
return _database.Seasons.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.SeasonNumber == seasonNumber);
|
||||
Season ret = await _database.Seasons.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.SeasonNumber == seasonNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(showID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Task<Season> Get(string showSlug, int seasonNumber)
|
||||
public async Task<Season> Get(string showSlug, int seasonNumber)
|
||||
{
|
||||
return _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
Season ret = await _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.SeasonNumber == seasonNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = showSlug;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public async Task<ICollection<Season>> Search(string query)
|
||||
public override async Task<ICollection<Season>> Search(string query)
|
||||
{
|
||||
return await _database.Seasons
|
||||
List<Season> seasons = await _database.Seasons
|
||||
.Where(x => EF.Functions.ILike(x.Title, $"%{query}%"))
|
||||
.OrderBy(DefaultSort)
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
foreach (Season season in seasons)
|
||||
season.ShowSlug = await _shows.GetSlug(season.ShowID);
|
||||
return seasons;
|
||||
}
|
||||
|
||||
public override async Task<ICollection<Season>> GetAll(Expression<Func<Season, bool>> where = null,
|
||||
Sort<Season> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
ICollection<Season> seasons = await base.GetAll(where, sort, limit);
|
||||
foreach (Season season in seasons)
|
||||
season.ShowSlug = await _shows.GetSlug(season.ShowID);
|
||||
return seasons;
|
||||
}
|
||||
|
||||
public override async Task<Season> Create(Season obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
if (obj.ExternalIDs != null)
|
||||
foreach (MetadataID entry in obj.ExternalIDs)
|
||||
_database.Entry(entry).State = EntityState.Added;
|
||||
|
||||
obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Added);
|
||||
await _database.SaveChangesAsync($"Trying to insert a duplicated season (slug {obj.Slug} already exists).");
|
||||
return obj;
|
||||
}
|
||||
@ -100,15 +138,24 @@ namespace Kyoo.Controllers
|
||||
throw new InvalidOperationException($"Can't store a season not related to any show (showID: {resource.ShowID}).");
|
||||
|
||||
await base.Validate(resource);
|
||||
|
||||
if (resource.ExternalIDs != null)
|
||||
await resource.ExternalIDs.ForEachAsync(async id =>
|
||||
{
|
||||
foreach (MetadataID link in resource.ExternalIDs)
|
||||
if (ShouldValidate(link))
|
||||
link.Provider = await _providers.CreateIfNotExists(link.Provider, true);
|
||||
}
|
||||
id.Provider = await _providers.CreateIfNotExists(id.Provider, true);
|
||||
id.ProviderID = id.Provider.ID;
|
||||
_database.Entry(id.Provider).State = EntityState.Detached;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
protected override async Task EditRelations(Season resource, Season 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);
|
||||
}
|
||||
|
||||
public async Task Delete(string showSlug, int seasonNumber)
|
||||
{
|
||||
Season obj = await Get(showSlug, seasonNumber);
|
||||
@ -121,11 +168,7 @@ namespace Kyoo.Controllers
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
_database.Entry(obj).State = EntityState.Deleted;
|
||||
|
||||
if (obj.ExternalIDs != null)
|
||||
foreach (MetadataID entry in obj.ExternalIDs)
|
||||
_database.Entry(entry).State = EntityState.Deleted;
|
||||
|
||||
obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Deleted);
|
||||
await _database.SaveChangesAsync();
|
||||
|
||||
if (obj.Episodes != null)
|
||||
|
||||
@ -9,7 +9,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public class ShowRepository : LocalRepository<Show, ShowDE>, IShowRepository
|
||||
public class ShowRepository : LocalRepository<Show>, IShowRepository
|
||||
{
|
||||
private bool _disposed;
|
||||
private readonly DatabaseContext _database;
|
||||
@ -19,7 +19,7 @@ namespace Kyoo.Controllers
|
||||
private readonly IProviderRepository _providers;
|
||||
private readonly Lazy<ISeasonRepository> _seasons;
|
||||
private readonly Lazy<IEpisodeRepository> _episodes;
|
||||
protected override Expression<Func<ShowDE, object>> DefaultSort => x => x.Title;
|
||||
protected override Expression<Func<Show, object>> DefaultSort => x => x.Title;
|
||||
|
||||
public ShowRepository(DatabaseContext database,
|
||||
IStudioRepository studios,
|
||||
@ -52,6 +52,7 @@ namespace Kyoo.Controllers
|
||||
_seasons.Value.Dispose();
|
||||
if (_episodes.IsValueCreated)
|
||||
_episodes.Value.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
@ -77,89 +78,110 @@ namespace Kyoo.Controllers
|
||||
.Where(x => EF.Functions.ILike(x.Title, query)
|
||||
|| EF.Functions.ILike(x.Slug, query)
|
||||
/*|| x.Aliases.Any(y => EF.Functions.ILike(y, query))*/) // NOT TRANSLATABLE.
|
||||
.OrderBy(DefaultSort)
|
||||
.Take(20)
|
||||
.ToListAsync<Show>();
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public override async Task<ShowDE> Create(ShowDE obj)
|
||||
public override async Task<Show> Create(Show obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
|
||||
if (obj.GenreLinks != null)
|
||||
{
|
||||
foreach (GenreLink entry in obj.GenreLinks)
|
||||
{
|
||||
if (!(entry.Child is GenreDE))
|
||||
entry.Child = new GenreDE(entry.Child);
|
||||
_database.Entry(entry).State = EntityState.Added;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.People != null)
|
||||
foreach (PeopleRole entry in obj.People)
|
||||
_database.Entry(entry).State = EntityState.Added;
|
||||
if (obj.ExternalIDs != null)
|
||||
foreach (MetadataID entry in obj.ExternalIDs)
|
||||
_database.Entry(entry).State = EntityState.Added;
|
||||
|
||||
obj.GenreLinks.ForEach(x => _database.Entry(x).State = EntityState.Added);
|
||||
obj.People.ForEach(x => _database.Entry(x).State = EntityState.Added);
|
||||
obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Added);
|
||||
await _database.SaveChangesAsync($"Trying to insert a duplicated show (slug {obj.Slug} already exists).");
|
||||
return obj;
|
||||
}
|
||||
|
||||
protected override async Task Validate(ShowDE resource)
|
||||
protected override async Task Validate(Show resource)
|
||||
{
|
||||
await base.Validate(resource);
|
||||
|
||||
if (ShouldValidate(resource.Studio))
|
||||
if (resource.Studio != null)
|
||||
resource.Studio = await _studios.CreateIfNotExists(resource.Studio, true);
|
||||
|
||||
if (resource.GenreLinks != null)
|
||||
foreach (GenreLink link in resource.GenreLinks)
|
||||
if (ShouldValidate(link))
|
||||
link.Child = await _genres.CreateIfNotExists(link.Child, true);
|
||||
|
||||
if (resource.People != null)
|
||||
foreach (PeopleRole link in resource.People)
|
||||
if (ShouldValidate(link))
|
||||
link.People = await _people.CreateIfNotExists(link.People, true);
|
||||
|
||||
if (resource.ExternalIDs != null)
|
||||
foreach (MetadataID link in resource.ExternalIDs)
|
||||
if (ShouldValidate(link))
|
||||
link.Provider = await _providers.CreateIfNotExists(link.Provider, true);
|
||||
resource.Genres = await resource.Genres
|
||||
.SelectAsync(x => _genres.CreateIfNotExists(x, true))
|
||||
.ToListAsync();
|
||||
resource.GenreLinks = resource.Genres?
|
||||
.Select(x => Link.UCreate(resource, x))
|
||||
.ToList();
|
||||
await resource.ExternalIDs.ForEachAsync(async id =>
|
||||
{
|
||||
id.Provider = await _providers.CreateIfNotExists(id.Provider, true);
|
||||
id.ProviderID = id.Provider.ID;
|
||||
_database.Entry(id.Provider).State = EntityState.Detached;
|
||||
});
|
||||
await resource.People.ForEachAsync(async role =>
|
||||
{
|
||||
role.People = await _people.CreateIfNotExists(role.People, true);
|
||||
role.PeopleID = role.People.ID;
|
||||
_database.Entry(role.People).State = EntityState.Detached;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
protected override async Task EditRelations(Show resource, Show changed, bool resetOld)
|
||||
{
|
||||
await Validate(changed);
|
||||
|
||||
if (changed.Aliases != null || resetOld)
|
||||
resource.Aliases = changed.Aliases;
|
||||
|
||||
if (changed.Genres != null || resetOld)
|
||||
{
|
||||
await Database.Entry(resource).Collection(x => x.GenreLinks).LoadAsync();
|
||||
resource.GenreLinks = changed.Genres?.Select(x => Link.UCreate(resource, x)).ToList();
|
||||
}
|
||||
|
||||
if (changed.People != null || resetOld)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AddShowLink(int showID, int? libraryID, int? collectionID)
|
||||
{
|
||||
if (collectionID != null)
|
||||
{
|
||||
await _database.CollectionLinks.AddAsync(new CollectionLink {ParentID = collectionID.Value, ChildID = showID});
|
||||
await _database.Links<Collection, Show>()
|
||||
.AddAsync(new Link<Collection, Show>(collectionID.Value, showID));
|
||||
await _database.SaveIfNoDuplicates();
|
||||
|
||||
if (libraryID != null)
|
||||
{
|
||||
await _database.Links<Library, Collection>()
|
||||
.AddAsync(new Link<Library, Collection>(libraryID.Value, collectionID.Value));
|
||||
await _database.SaveIfNoDuplicates();
|
||||
}
|
||||
}
|
||||
if (libraryID != null)
|
||||
{
|
||||
await _database.LibraryLinks.AddAsync(new LibraryLink {LibraryID = libraryID.Value, ShowID = showID});
|
||||
await _database.SaveIfNoDuplicates();
|
||||
}
|
||||
|
||||
if (libraryID != null && collectionID != null)
|
||||
{
|
||||
await _database.LibraryLinks.AddAsync(new LibraryLink {LibraryID = libraryID.Value, CollectionID = collectionID.Value});
|
||||
await _database.Links<Library, Show>()
|
||||
.AddAsync(new Link<Library, Show>(libraryID.Value, showID));
|
||||
await _database.SaveIfNoDuplicates();
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task Delete(ShowDE obj)
|
||||
public Task<string> GetSlug(int showID)
|
||||
{
|
||||
return _database.Shows.Where(x => x.ID == showID)
|
||||
.Select(x => x.Slug)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public override async Task Delete(Show obj)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
_database.Entry(obj).State = EntityState.Deleted;
|
||||
|
||||
if (obj.GenreLinks != null)
|
||||
foreach (GenreLink entry in obj.GenreLinks)
|
||||
_database.Entry(entry).State = EntityState.Deleted;
|
||||
|
||||
if (obj.People != null)
|
||||
foreach (PeopleRole entry in obj.People)
|
||||
@ -168,14 +190,6 @@ namespace Kyoo.Controllers
|
||||
if (obj.ExternalIDs != null)
|
||||
foreach (MetadataID entry in obj.ExternalIDs)
|
||||
_database.Entry(entry).State = EntityState.Deleted;
|
||||
|
||||
if (obj.CollectionLinks != null)
|
||||
foreach (CollectionLink entry in obj.CollectionLinks)
|
||||
_database.Entry(entry).State = EntityState.Deleted;
|
||||
|
||||
if (obj.LibraryLinks != null)
|
||||
foreach (LibraryLink entry in obj.LibraryLinks)
|
||||
_database.Entry(entry).State = EntityState.Deleted;
|
||||
|
||||
await _database.SaveChangesAsync();
|
||||
|
||||
|
||||
@ -19,10 +19,11 @@ namespace Kyoo.Controllers
|
||||
_database = database;
|
||||
}
|
||||
|
||||
public async Task<ICollection<Studio>> Search(string query)
|
||||
public override async Task<ICollection<Studio>> Search(string query)
|
||||
{
|
||||
return await _database.Studios
|
||||
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
|
||||
.OrderBy(DefaultSort)
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
}
|
||||
@ -41,10 +42,6 @@ namespace Kyoo.Controllers
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
_database.Entry(obj).State = EntityState.Deleted;
|
||||
|
||||
// Using Dotnet-EF change discovery service to remove references to this studio on shows.
|
||||
foreach (Show show in obj.Shows)
|
||||
show.StudioID = null;
|
||||
await _database.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
@ -12,7 +13,7 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
private bool _disposed;
|
||||
private readonly DatabaseContext _database;
|
||||
protected override Expression<Func<Track, object>> DefaultSort => x => x.ID;
|
||||
protected override Expression<Func<Track, object>> DefaultSort => x => x.TrackIndex;
|
||||
|
||||
|
||||
public TrackRepository(DatabaseContext database) : base(database)
|
||||
@ -36,10 +37,15 @@ namespace Kyoo.Controllers
|
||||
await _database.DisposeAsync();
|
||||
}
|
||||
|
||||
public Task<Track> Get(string slug, StreamType type = StreamType.Unknown)
|
||||
public override Task<Track> Get(string slug)
|
||||
{
|
||||
return Get(slug, StreamType.Unknown);
|
||||
}
|
||||
|
||||
public Task<Track> Get(string slug, StreamType type)
|
||||
{
|
||||
Match match = Regex.Match(slug,
|
||||
@"(?<show>.*)-s(?<season>\d+)e(?<episode>\d+)\.(?<language>.{0,3})(?<forced>-forced)?(\..*)?");
|
||||
@"(?<show>.*)-s(?<season>\d+)e(?<episode>\d+)(\.(?<type>\w*))?\.(?<language>.{0,3})(?<forced>-forced)?(\..*)?");
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
@ -56,6 +62,8 @@ namespace Kyoo.Controllers
|
||||
int episodeNumber = match.Groups["episode"].Success ? int.Parse(match.Groups["episode"].Value) : -1;
|
||||
string language = match.Groups["language"].Value;
|
||||
bool forced = match.Groups["forced"].Success;
|
||||
if (match.Groups["type"].Success)
|
||||
type = Enum.Parse<StreamType>(match.Groups["type"].Value, true);
|
||||
|
||||
if (type == StreamType.Unknown)
|
||||
{
|
||||
@ -73,7 +81,7 @@ namespace Kyoo.Controllers
|
||||
&& x.IsForced == forced);
|
||||
}
|
||||
|
||||
public Task<ICollection<Track>> Search(string query)
|
||||
public override Task<ICollection<Track>> Search(string query)
|
||||
{
|
||||
throw new InvalidOperationException("Tracks do not support the search method.");
|
||||
}
|
||||
@ -82,22 +90,23 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
if (obj.EpisodeID <= 0)
|
||||
{
|
||||
obj.EpisodeID = obj.Episode?.ID ?? -1;
|
||||
obj.EpisodeID = obj.Episode?.ID ?? 0;
|
||||
if (obj.EpisodeID <= 0)
|
||||
throw new InvalidOperationException($"Can't store a track not related to any episode (episodeID: {obj.EpisodeID}).");
|
||||
}
|
||||
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
await _database.SaveChangesAsync($"Trying to insert a duplicated track (slug {obj.Slug} already exists).");
|
||||
await _database.SaveOrRetry(obj, (x, i) =>
|
||||
{
|
||||
if (i > 10)
|
||||
throw new DuplicatedItemException($"More than 10 same tracks exists {x.Slug}. Aborting...");
|
||||
x.TrackIndex++;
|
||||
return x;
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
protected override Task Validate(Track resource)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
public override async Task Delete(Track obj)
|
||||
{
|
||||
if (obj == null)
|
||||
|
||||
@ -110,7 +110,7 @@ namespace Kyoo.Controllers
|
||||
_tasks.AddRange(CoreTaskHolder.Tasks.Select(x => (x, DateTime.Now + GetTaskDelay(x.Slug))));
|
||||
|
||||
IEnumerable<ITask> prerunTasks = _tasks.Select(x => x.task)
|
||||
.Where(x => x.RunOnStartup && x.Priority == Int32.MaxValue);
|
||||
.Where(x => x.RunOnStartup && x.Priority == int.MaxValue);
|
||||
|
||||
foreach (ITask task in prerunTasks)
|
||||
task.Run(_serviceProvider, _taskToken.Token);
|
||||
|
||||
@ -1,20 +1,28 @@
|
||||
using Kyoo.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public class ThumbnailsManager : IThumbnailsManager
|
||||
{
|
||||
private readonly IConfiguration _config;
|
||||
private readonly IFileManager _files;
|
||||
private readonly string _peoplePath;
|
||||
private readonly string _providerPath;
|
||||
|
||||
public ThumbnailsManager(IConfiguration configuration)
|
||||
public ThumbnailsManager(IConfiguration configuration, IFileManager files)
|
||||
{
|
||||
_config = configuration;
|
||||
_files = files;
|
||||
_peoplePath = Path.GetFullPath(configuration.GetValue<string>("peoplePath"));
|
||||
_providerPath = Path.GetFullPath(configuration.GetValue<string>("providerPath"));
|
||||
Directory.CreateDirectory(_peoplePath);
|
||||
Directory.CreateDirectory(_providerPath);
|
||||
}
|
||||
|
||||
private static async Task DownloadImage(string url, string localPath, string what)
|
||||
@ -26,83 +34,133 @@ namespace Kyoo.Controllers
|
||||
}
|
||||
catch (WebException exception)
|
||||
{
|
||||
await Console.Error.WriteLineAsync($"{what} could not be downloaded.\n\tError: {exception.Message}.");
|
||||
await Console.Error.WriteLineAsync($"{what} could not be downloaded. Error: {exception.Message}.");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Show> Validate(Show show, bool alwaysDownload)
|
||||
public async Task Validate(Show show, bool alwaysDownload)
|
||||
{
|
||||
if (show?.Path == null)
|
||||
return default;
|
||||
|
||||
if (show.Poster != null)
|
||||
{
|
||||
string posterPath = Path.Combine(show.Path, "poster.jpg");
|
||||
string posterPath = await GetShowPoster(show);
|
||||
if (alwaysDownload || !File.Exists(posterPath))
|
||||
await DownloadImage(show.Poster, posterPath, $"The poster of {show.Title}");
|
||||
}
|
||||
if (show.Logo != null)
|
||||
{
|
||||
string logoPath = Path.Combine(show.Path, "logo.png");
|
||||
string logoPath = await GetShowLogo(show);
|
||||
if (alwaysDownload || !File.Exists(logoPath))
|
||||
await DownloadImage(show.Logo, logoPath, $"The logo of {show.Title}");
|
||||
}
|
||||
if (show.Backdrop != null)
|
||||
{
|
||||
string backdropPath = Path.Combine(show.Path, "backdrop.jpg");
|
||||
string backdropPath = await GetShowBackdrop(show);
|
||||
if (alwaysDownload || !File.Exists(backdropPath))
|
||||
await DownloadImage(show.Backdrop, backdropPath, $"The backdrop of {show.Title}");
|
||||
}
|
||||
|
||||
return show;
|
||||
|
||||
foreach (PeopleRole role in show.People)
|
||||
await Validate(role.People, alwaysDownload);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<PeopleRole>> Validate(IEnumerable<PeopleRole> people, bool alwaysDownload)
|
||||
public async Task Validate([NotNull] People people, bool alwaysDownload)
|
||||
{
|
||||
if (people == null)
|
||||
return null;
|
||||
|
||||
throw new ArgumentNullException(nameof(people));
|
||||
string root = _config.GetValue<string>("peoplePath");
|
||||
string localPath = Path.Combine(root, people.Slug + ".jpg");
|
||||
|
||||
Directory.CreateDirectory(root);
|
||||
|
||||
foreach (PeopleRole peop in people)
|
||||
{
|
||||
string localPath = Path.Combine(root, peop.People.Slug + ".jpg");
|
||||
if (peop.People.Poster == null)
|
||||
continue;
|
||||
if (alwaysDownload || !File.Exists(localPath))
|
||||
await DownloadImage(peop.People.Poster, localPath, $"The profile picture of {peop.People.Name}");
|
||||
}
|
||||
|
||||
return people;
|
||||
if (alwaysDownload || !File.Exists(localPath))
|
||||
await DownloadImage(people.Poster, localPath, $"The profile picture of {people.Name}");
|
||||
}
|
||||
|
||||
public async Task<Season> Validate(Season season, bool alwaysDownload)
|
||||
public async Task Validate(Season season, bool alwaysDownload)
|
||||
{
|
||||
if (season?.Show?.Path == null)
|
||||
return default;
|
||||
if (season?.Show?.Path == null || season.Poster == null)
|
||||
return;
|
||||
|
||||
if (season.Poster != null)
|
||||
{
|
||||
string localPath = Path.Combine(season.Show.Path, $"season-{season.SeasonNumber}.jpg");
|
||||
if (alwaysDownload || !File.Exists(localPath))
|
||||
await DownloadImage(season.Poster, localPath, $"The poster of {season.Show.Title}'s season {season.SeasonNumber}");
|
||||
}
|
||||
return season;
|
||||
string localPath = await GetSeasonPoster(season);
|
||||
if (alwaysDownload || !File.Exists(localPath))
|
||||
await DownloadImage(season.Poster, localPath, $"The poster of {season.Show.Title}'s season {season.SeasonNumber}");
|
||||
}
|
||||
|
||||
public async Task<Episode> Validate(Episode episode, bool alwaysDownload)
|
||||
public async Task Validate(Episode episode, bool alwaysDownload)
|
||||
{
|
||||
if (episode?.Path == null)
|
||||
return default;
|
||||
if (episode?.Path == null || episode.Thumb == null)
|
||||
return;
|
||||
|
||||
if (episode.Poster != null)
|
||||
{
|
||||
string localPath = Path.ChangeExtension(episode.Path, "jpg");
|
||||
if (alwaysDownload || !File.Exists(localPath))
|
||||
await DownloadImage(episode.Poster, localPath, $"The thumbnail of {episode.Show.Title}");
|
||||
}
|
||||
return episode;
|
||||
string localPath = await GetEpisodeThumb(episode);
|
||||
if (alwaysDownload || !File.Exists(localPath))
|
||||
await DownloadImage(episode.Thumb, localPath, $"The thumbnail of {episode.Slug}");
|
||||
}
|
||||
|
||||
public async Task Validate(ProviderID provider, bool alwaysDownload)
|
||||
{
|
||||
if (provider.Logo == null)
|
||||
return;
|
||||
|
||||
string root = _config.GetValue<string>("providerPath");
|
||||
string localPath = Path.Combine(root, provider.Slug + ".jpg");
|
||||
|
||||
Directory.CreateDirectory(root);
|
||||
if (alwaysDownload || !File.Exists(localPath))
|
||||
await DownloadImage(provider.Logo, localPath, $"The logo of {provider.Slug}");
|
||||
}
|
||||
|
||||
public Task<string> GetShowBackdrop(Show show)
|
||||
{
|
||||
if (show?.Path == null)
|
||||
throw new ArgumentNullException(nameof(show));
|
||||
return Task.FromResult(Path.Combine(_files.GetExtraDirectory(show), "backdrop.jpg"));
|
||||
}
|
||||
|
||||
public Task<string> GetShowLogo(Show show)
|
||||
{
|
||||
if (show?.Path == null)
|
||||
throw new ArgumentNullException(nameof(show));
|
||||
return Task.FromResult(Path.Combine(_files.GetExtraDirectory(show), "logo.png"));
|
||||
}
|
||||
|
||||
public Task<string> GetShowPoster(Show show)
|
||||
{
|
||||
if (show?.Path == null)
|
||||
throw new ArgumentNullException(nameof(show));
|
||||
return Task.FromResult(Path.Combine(_files.GetExtraDirectory(show), "poster.jpg"));
|
||||
}
|
||||
|
||||
public Task<string> GetSeasonPoster(Season season)
|
||||
{
|
||||
if (season == null)
|
||||
throw new ArgumentNullException(nameof(season));
|
||||
return Task.FromResult(Path.Combine(_files.GetExtraDirectory(season), $"season-{season.SeasonNumber}.jpg"));
|
||||
}
|
||||
|
||||
public Task<string> GetEpisodeThumb(Episode episode)
|
||||
{
|
||||
string dir = Path.Combine(_files.GetExtraDirectory(episode), "Thumbnails");
|
||||
Directory.CreateDirectory(dir);
|
||||
return Task.FromResult(Path.Combine(dir, $"{Path.GetFileNameWithoutExtension(episode.Path)}.jpg"));
|
||||
}
|
||||
|
||||
public Task<string> GetPeoplePoster(People people)
|
||||
{
|
||||
if (people == null)
|
||||
throw new ArgumentNullException(nameof(people));
|
||||
string thumbPath = Path.GetFullPath(Path.Combine(_peoplePath, $"{people.Slug}.jpg"));
|
||||
if (!thumbPath.StartsWith(_peoplePath))
|
||||
return Task.FromResult<string>(null);
|
||||
return Task.FromResult(thumbPath);
|
||||
}
|
||||
|
||||
public Task<string> GetProviderLogo(ProviderID provider)
|
||||
{
|
||||
if (provider == null)
|
||||
throw new ArgumentNullException(nameof(provider));
|
||||
string thumbPath = Path.GetFullPath(Path.Combine(_providerPath, $"{provider.Slug}.jpg"));
|
||||
if (!thumbPath.StartsWith(_providerPath))
|
||||
return Task.FromResult<string>(null);
|
||||
return Task.FromResult(thumbPath.StartsWith(_providerPath) ? thumbPath : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
135
Kyoo/Controllers/Transcoder.cs
Normal file
135
Kyoo/Controllers/Transcoder.cs
Normal file
@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Stream = Kyoo.Models.Watch.Stream;
|
||||
|
||||
// We use threads so tasks are not always awaited.
|
||||
#pragma warning disable 4014
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public class BadTranscoderException : Exception {}
|
||||
|
||||
public class Transcoder : ITranscoder
|
||||
{
|
||||
private static class TranscoderAPI
|
||||
{
|
||||
private const string TranscoderPath = "libtranscoder.so";
|
||||
|
||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int init();
|
||||
|
||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int transmux(string path, string outpath, out float playableDuration);
|
||||
|
||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int transcode(string path, string outpath, out float playableDuration);
|
||||
|
||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern IntPtr extract_infos(string path,
|
||||
string outpath,
|
||||
out int length,
|
||||
out int trackCount,
|
||||
bool reextracct);
|
||||
|
||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void free(IntPtr ptr);
|
||||
|
||||
|
||||
public static Track[] ExtractInfos(string path, string outPath, bool reextract)
|
||||
{
|
||||
int size = Marshal.SizeOf<Stream>();
|
||||
IntPtr ptr = extract_infos(path, outPath, out int arrayLength, out int trackCount, reextract);
|
||||
IntPtr streamsPtr = ptr;
|
||||
Track[] tracks;
|
||||
|
||||
if (trackCount > 0 && ptr != IntPtr.Zero)
|
||||
{
|
||||
tracks = new Track[trackCount];
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < arrayLength; i++)
|
||||
{
|
||||
Stream stream = Marshal.PtrToStructure<Stream>(streamsPtr);
|
||||
if (stream!.Type != StreamType.Unknown)
|
||||
{
|
||||
tracks[j] = new Track(stream);
|
||||
j++;
|
||||
}
|
||||
streamsPtr += size;
|
||||
}
|
||||
}
|
||||
else
|
||||
tracks = Array.Empty<Track>();
|
||||
|
||||
if (ptr != IntPtr.Zero)
|
||||
free(ptr); // free_streams is not necesarry since the Marshal free the unmanaged pointers.
|
||||
return tracks;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IFileManager _files;
|
||||
private readonly string _transmuxPath;
|
||||
private readonly string _transcodePath;
|
||||
|
||||
public Transcoder(IConfiguration config, IFileManager files)
|
||||
{
|
||||
_files = files;
|
||||
_transmuxPath = Path.GetFullPath(config.GetValue<string>("transmuxTempPath"));
|
||||
_transcodePath = Path.GetFullPath(config.GetValue<string>("transcodeTempPath"));
|
||||
|
||||
if (TranscoderAPI.init() != Marshal.SizeOf<Stream>())
|
||||
throw new BadTranscoderException();
|
||||
}
|
||||
|
||||
public Task<Track[]> ExtractInfos(Episode episode, bool reextract)
|
||||
{
|
||||
string dir = _files.GetExtraDirectory(episode);
|
||||
if (dir == null)
|
||||
throw new ArgumentException("Invalid path.");
|
||||
return Task.Factory.StartNew(
|
||||
() => TranscoderAPI.ExtractInfos(episode.Path, dir, reextract),
|
||||
TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
public async Task<string> Transmux(Episode episode)
|
||||
{
|
||||
if (!File.Exists(episode.Path))
|
||||
throw new ArgumentException("Path does not exists. Can't transcode.");
|
||||
|
||||
string folder = Path.Combine(_transmuxPath, episode.Slug);
|
||||
string manifest = Path.Combine(folder, episode.Slug + ".m3u8");
|
||||
float playableDuration = 0;
|
||||
bool transmuxFailed = false;
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
if (File.Exists(manifest))
|
||||
return manifest;
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
await Console.Error.WriteLineAsync($"Access to the path {manifest} is denied. Please change your transmux path in the config.");
|
||||
return null;
|
||||
}
|
||||
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
string cleanManifest = manifest.Replace('\\', '/');
|
||||
transmuxFailed = TranscoderAPI.transmux(episode.Path, cleanManifest, out playableDuration) != 0;
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
while (playableDuration < 10 || !File.Exists(manifest) && !transmuxFailed)
|
||||
await Task.Delay(10);
|
||||
return transmuxFailed ? null : manifest;
|
||||
}
|
||||
|
||||
public Task<string> Transcode(Episode episode)
|
||||
{
|
||||
return Task.FromResult<string>(null); // Not implemented yet.
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,70 +0,0 @@
|
||||
using System;
|
||||
using Kyoo.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Controllers.TranscoderLink;
|
||||
#pragma warning disable 4014
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public class BadTranscoderException : Exception {}
|
||||
|
||||
public class Transcoder : ITranscoder
|
||||
{
|
||||
private readonly string _transmuxPath;
|
||||
private readonly string _transcodePath;
|
||||
|
||||
public Transcoder(IConfiguration config)
|
||||
{
|
||||
_transmuxPath = Path.GetFullPath(config.GetValue<string>("transmuxTempPath"));
|
||||
_transcodePath = Path.GetFullPath(config.GetValue<string>("transcodeTempPath"));
|
||||
|
||||
if (TranscoderAPI.init() != Marshal.SizeOf<Models.Watch.Stream>())
|
||||
throw new BadTranscoderException();
|
||||
}
|
||||
|
||||
public Task<Track[]> ExtractInfos(string path)
|
||||
{
|
||||
string dir = Path.GetDirectoryName(path);
|
||||
if (dir == null)
|
||||
throw new ArgumentException("Invalid path.");
|
||||
|
||||
return Task.Factory.StartNew(() => TranscoderAPI.ExtractInfos(path, dir), TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
public async Task<string> Transmux(Episode episode)
|
||||
{
|
||||
string folder = Path.Combine(_transmuxPath, episode.Slug);
|
||||
string manifest = Path.Combine(folder, episode.Slug + ".m3u8");
|
||||
float playableDuration = 0;
|
||||
bool transmuxFailed = false;
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
if (File.Exists(manifest))
|
||||
return manifest;
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
await Console.Error.WriteLineAsync($"Access to the path {manifest} is denied. Please change your transmux path in the config.");
|
||||
return null;
|
||||
}
|
||||
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
transmuxFailed = TranscoderAPI.transmux(episode.Path, manifest.Replace('\\', '/'), out playableDuration) != 0;
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
while (playableDuration < 10 || !File.Exists(manifest) && !transmuxFailed)
|
||||
await Task.Delay(10);
|
||||
return transmuxFailed ? null : manifest;
|
||||
}
|
||||
|
||||
public Task<string> Transcode(Episode episode)
|
||||
{
|
||||
return Task.FromResult<string>(null); // Not implemented yet.
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Watch;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace Kyoo.Controllers.TranscoderLink
|
||||
{
|
||||
public static class TranscoderAPI
|
||||
{
|
||||
private const string TranscoderPath = "libtranscoder.so";
|
||||
|
||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int init();
|
||||
|
||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int transmux(string path, string out_path, out float playableDuration);
|
||||
|
||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int transcode(string path, string out_path, out float playableDuration);
|
||||
|
||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern IntPtr extract_infos(string path, string outpath, out int length, out int track_count);
|
||||
|
||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void free(IntPtr stream_ptr);
|
||||
|
||||
|
||||
public static Track[] ExtractInfos(string path, string outPath)
|
||||
{
|
||||
int size = Marshal.SizeOf<Stream>();
|
||||
IntPtr ptr = extract_infos(path, outPath, out int arrayLength, out int trackCount);
|
||||
IntPtr streamsPtr = ptr;
|
||||
Track[] tracks;
|
||||
|
||||
if (trackCount > 0 && ptr != IntPtr.Zero)
|
||||
{
|
||||
tracks = new Track[trackCount];
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < arrayLength; i++)
|
||||
{
|
||||
Stream stream = Marshal.PtrToStructure<Stream>(streamsPtr);
|
||||
if (stream!.Type != StreamType.Unknown)
|
||||
{
|
||||
tracks[j] = new Track(stream);
|
||||
j++;
|
||||
}
|
||||
streamsPtr += size;
|
||||
}
|
||||
}
|
||||
else
|
||||
tracks = Array.Empty<Track>();
|
||||
|
||||
if (ptr != IntPtr.Zero)
|
||||
free(ptr); // free_streams is not necesarry since the Marshal free the unmanaged pointers.
|
||||
return tracks;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -21,32 +21,32 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IdentityServer4" Version="3.1.2" />
|
||||
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="3.1.2" />
|
||||
<PackageReference Include="IdentityServer4.EntityFramework" Version="3.1.2" />
|
||||
<PackageReference Include="IdentityServer4.EntityFramework.Storage" Version="3.1.2" />
|
||||
<PackageReference Include="IdentityServer4.Storage" Version="3.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.3" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.3" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.6.7" />
|
||||
<ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj" />
|
||||
<ProjectReference Include="../Kyoo.CommonAPI/Kyoo.CommonAPI.csproj" />
|
||||
<PackageReference Include="IdentityServer4" Version="4.1.1" />
|
||||
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="4.1.1" />
|
||||
<PackageReference Include="IdentityServer4.EntityFramework" Version="4.1.1" />
|
||||
<PackageReference Include="IdentityServer4.EntityFramework.Storage" Version="4.1.1" />
|
||||
<PackageReference Include="IdentityServer4.Storage" Version="4.1.1" />
|
||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.3" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.2" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.9" />
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.3">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="5.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="3.1.12" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="5.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="5.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="5.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<ProjectReference Include="../Kyoo.CommonAPI/Kyoo.CommonAPI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -90,7 +90,7 @@
|
||||
</Target>
|
||||
|
||||
<Target Name="Symlink views to output - Linux" AfterTargets="Build" Condition="$(Configuration) == 'Debug' And $(OS) == 'Unix'">
|
||||
<Exec WorkingDirectory="$(OutputPath)" Command="ln -fs ../../../Views"/>
|
||||
<Exec WorkingDirectory="$(OutputPath)" Command="ln -fs ../../../Views" />
|
||||
</Target>
|
||||
|
||||
<Target Name="Compile the transcoder" BeforeTargets="BeforeBuild">
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -12,39 +12,43 @@ namespace Kyoo
|
||||
{
|
||||
public class DatabaseContext : DbContext
|
||||
{
|
||||
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) { }
|
||||
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options)
|
||||
{
|
||||
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
ChangeTracker.LazyLoadingEnabled = false;
|
||||
}
|
||||
|
||||
public DbSet<LibraryDE> Libraries { get; set; }
|
||||
public DbSet<CollectionDE> Collections { get; set; }
|
||||
public DbSet<ShowDE> Shows { get; set; }
|
||||
public DbSet<Library> Libraries { get; set; }
|
||||
public DbSet<Collection> Collections { get; set; }
|
||||
public DbSet<Show> Shows { get; set; }
|
||||
public DbSet<Season> Seasons { get; set; }
|
||||
public DbSet<Episode> Episodes { get; set; }
|
||||
public DbSet<Track> Tracks { get; set; }
|
||||
public DbSet<GenreDE> Genres { get; set; }
|
||||
public DbSet<Genre> Genres { get; set; }
|
||||
public DbSet<People> People { get; set; }
|
||||
public DbSet<Studio> Studios { get; set; }
|
||||
public DbSet<ProviderID> Providers { get; set; }
|
||||
public DbSet<MetadataID> MetadataIds { get; set; }
|
||||
|
||||
public DbSet<PeopleRole> PeopleRoles { get; set; }
|
||||
|
||||
public DbSet<Link<T1, T2>> Links<T1, T2>()
|
||||
where T1 : class, IResource
|
||||
where T2 : class, IResource
|
||||
{
|
||||
return Set<Link<T1, T2>>();
|
||||
}
|
||||
|
||||
|
||||
public DbSet<LibraryLink> LibraryLinks { get; set; }
|
||||
public DbSet<CollectionLink> CollectionLinks { get; set; }
|
||||
public DbSet<GenreLink> GenreLinks { get; set; }
|
||||
public DbSet<ProviderLink> ProviderLinks { get; set; }
|
||||
|
||||
public DatabaseContext()
|
||||
{
|
||||
NpgsqlConnection.GlobalTypeMapper.MapEnum<Status>();
|
||||
NpgsqlConnection.GlobalTypeMapper.MapEnum<ItemType>();
|
||||
NpgsqlConnection.GlobalTypeMapper.MapEnum<StreamType>();
|
||||
|
||||
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
ChangeTracker.LazyLoadingEnabled = false;
|
||||
}
|
||||
|
||||
private readonly ValueComparer<IEnumerable<string>> _stringArrayComparer =
|
||||
new ValueComparer<IEnumerable<string>>(
|
||||
(l1, l2) => l1.SequenceEqual(l2),
|
||||
arr => arr.Aggregate(0, (i, s) => s.GetHashCode()));
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@ -54,20 +58,13 @@ namespace Kyoo
|
||||
modelBuilder.HasPostgresEnum<ItemType>();
|
||||
modelBuilder.HasPostgresEnum<StreamType>();
|
||||
|
||||
modelBuilder.Ignore<Library>();
|
||||
modelBuilder.Ignore<Collection>();
|
||||
modelBuilder.Ignore<Show>();
|
||||
modelBuilder.Ignore<Genre>();
|
||||
|
||||
modelBuilder.Entity<LibraryDE>()
|
||||
modelBuilder.Entity<Library>()
|
||||
.Property(x => x.Paths)
|
||||
.HasColumnType("text[]")
|
||||
.Metadata.SetValueComparer(_stringArrayComparer);
|
||||
.HasColumnType("text[]");
|
||||
|
||||
modelBuilder.Entity<ShowDE>()
|
||||
modelBuilder.Entity<Show>()
|
||||
.Property(x => x.Aliases)
|
||||
.HasColumnType("text[]")
|
||||
.Metadata.SetValueComparer(_stringArrayComparer);
|
||||
.HasColumnType("text[]");
|
||||
|
||||
modelBuilder.Entity<Track>()
|
||||
.Property(t => t.IsDefault)
|
||||
@ -77,91 +74,69 @@ namespace Kyoo
|
||||
.Property(t => t.IsForced)
|
||||
.ValueGeneratedNever();
|
||||
|
||||
|
||||
modelBuilder.Entity<GenreLink>()
|
||||
.HasKey(x => new {ShowID = x.ParentID, GenreID = x.ChildID});
|
||||
|
||||
modelBuilder.Entity<CollectionLink>()
|
||||
.HasKey(x => new {CollectionID = x.ParentID, ShowID = x.ChildID});
|
||||
modelBuilder.Entity<ProviderID>()
|
||||
.HasMany(x => x.Libraries)
|
||||
.WithMany(x => x.Providers)
|
||||
.UsingEntity<Link<Library, ProviderID>>(
|
||||
y => y
|
||||
.HasOne(x => x.First)
|
||||
.WithMany(x => x.ProviderLinks),
|
||||
y => y
|
||||
.HasOne(x => x.Second)
|
||||
.WithMany(x => x.LibraryLinks),
|
||||
y => y.HasKey(Link<Library, ProviderID>.PrimaryKey));
|
||||
|
||||
modelBuilder.Entity<ProviderLink>()
|
||||
.HasKey(x => new {LibraryID = x.ParentID, ProviderID = x.ChildID});
|
||||
|
||||
|
||||
modelBuilder.Entity<LibraryDE>()
|
||||
.Ignore(x => x.Shows)
|
||||
.Ignore(x => x.Collections)
|
||||
.Ignore(x => x.Providers);
|
||||
modelBuilder.Entity<Collection>()
|
||||
.HasMany(x => x.Libraries)
|
||||
.WithMany(x => x.Collections)
|
||||
.UsingEntity<Link<Library, Collection>>(
|
||||
y => y
|
||||
.HasOne(x => x.First)
|
||||
.WithMany(x => x.CollectionLinks),
|
||||
y => y
|
||||
.HasOne(x => x.Second)
|
||||
.WithMany(x => x.LibraryLinks),
|
||||
y => y.HasKey(Link<Library, Collection>.PrimaryKey));
|
||||
|
||||
modelBuilder.Entity<CollectionDE>()
|
||||
.Ignore(x => x.Shows)
|
||||
.Ignore(x => x.Libraries);
|
||||
modelBuilder.Entity<Show>()
|
||||
.HasMany(x => x.Libraries)
|
||||
.WithMany(x => x.Shows)
|
||||
.UsingEntity<Link<Library, Show>>(
|
||||
y => y
|
||||
.HasOne(x => x.First)
|
||||
.WithMany(x => x.ShowLinks),
|
||||
y => y
|
||||
.HasOne(x => x.Second)
|
||||
.WithMany(x => x.LibraryLinks),
|
||||
y => y.HasKey(Link<Library, Show>.PrimaryKey));
|
||||
|
||||
modelBuilder.Entity<ShowDE>()
|
||||
.Ignore(x => x.Genres)
|
||||
.Ignore(x => x.Libraries)
|
||||
.Ignore(x => x.Collections);
|
||||
modelBuilder.Entity<Show>()
|
||||
.HasMany(x => x.Collections)
|
||||
.WithMany(x => x.Shows)
|
||||
.UsingEntity<Link<Collection, Show>>(
|
||||
y => y
|
||||
.HasOne(x => x.First)
|
||||
.WithMany(x => x.ShowLinks),
|
||||
y => y
|
||||
.HasOne(x => x.Second)
|
||||
.WithMany(x => x.CollectionLinks),
|
||||
y => y.HasKey(Link<Collection, Show>.PrimaryKey));
|
||||
|
||||
modelBuilder.Entity<PeopleRole>()
|
||||
.Ignore(x => x.Slug)
|
||||
.Ignore(x => x.Name)
|
||||
.Ignore(x => x.Poster)
|
||||
.Ignore(x => x.ExternalIDs);
|
||||
|
||||
modelBuilder.Entity<GenreDE>()
|
||||
.Ignore(x => x.Shows);
|
||||
|
||||
|
||||
modelBuilder.Entity<LibraryLink>()
|
||||
.HasOne(x => x.Library as LibraryDE)
|
||||
.WithMany(x => x.Links)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
modelBuilder.Entity<LibraryLink>()
|
||||
.HasOne(x => x.Show as ShowDE)
|
||||
.WithMany(x => x.LibraryLinks)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
modelBuilder.Entity<LibraryLink>()
|
||||
.HasOne(x => x.Collection as CollectionDE)
|
||||
.WithMany(x => x.LibraryLinks)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<CollectionLink>()
|
||||
.HasOne(x => x.Parent as CollectionDE)
|
||||
.WithMany(x => x.Links)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
modelBuilder.Entity<CollectionLink>()
|
||||
.HasOne(x => x.Child as ShowDE)
|
||||
.WithMany(x => x.CollectionLinks)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<GenreLink>()
|
||||
.HasOne(x => x.Child as GenreDE)
|
||||
.WithMany(x => x.Links)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
modelBuilder.Entity<GenreLink>()
|
||||
.HasOne(x => x.Parent as ShowDE)
|
||||
.WithMany(x => x.GenreLinks)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<ProviderLink>()
|
||||
.HasOne(x => x.Parent as LibraryDE)
|
||||
.WithMany(x => x.ProviderLinks)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<Season>()
|
||||
.HasOne(x => x.Show as ShowDE)
|
||||
.WithMany(x => x.Seasons);
|
||||
modelBuilder.Entity<Episode>()
|
||||
.HasOne(x => x.Show as ShowDE)
|
||||
.WithMany(x => x.Episodes);
|
||||
modelBuilder.Entity<PeopleRole>()
|
||||
.HasOne(x => x.Show as ShowDE)
|
||||
.WithMany(x => x.People);
|
||||
|
||||
modelBuilder.Entity<Genre>()
|
||||
.HasMany(x => x.Shows)
|
||||
.WithMany(x => x.Genres)
|
||||
.UsingEntity<Link<Show, Genre>>(
|
||||
y => y
|
||||
.HasOne(x => x.First)
|
||||
.WithMany(x => x.GenreLinks),
|
||||
y => y
|
||||
.HasOne(x => x.Second)
|
||||
.WithMany(x => x.ShowLinks),
|
||||
y => y.HasKey(Link<Show, Genre>.PrimaryKey));
|
||||
|
||||
|
||||
modelBuilder.Entity<MetadataID>()
|
||||
.HasOne(x => x.Show as ShowDE)
|
||||
.HasOne(x => x.Show)
|
||||
.WithMany(x => x.ExternalIDs)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
modelBuilder.Entity<MetadataID>()
|
||||
@ -176,28 +151,32 @@ namespace Kyoo
|
||||
.HasOne(x => x.People)
|
||||
.WithMany(x => x.ExternalIDs)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
modelBuilder.Entity<MetadataID>()
|
||||
.HasOne(x => x.Provider)
|
||||
.WithMany(x => x.MetadataLinks)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<CollectionDE>().Property(x => x.Slug).IsRequired();
|
||||
modelBuilder.Entity<GenreDE>().Property(x => x.Slug).IsRequired();
|
||||
modelBuilder.Entity<LibraryDE>().Property(x => x.Slug).IsRequired();
|
||||
modelBuilder.Entity<Collection>().Property(x => x.Slug).IsRequired();
|
||||
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<ProviderID>().Property(x => x.Slug).IsRequired();
|
||||
modelBuilder.Entity<ShowDE>().Property(x => x.Slug).IsRequired();
|
||||
modelBuilder.Entity<Show>().Property(x => x.Slug).IsRequired();
|
||||
modelBuilder.Entity<Studio>().Property(x => x.Slug).IsRequired();
|
||||
|
||||
modelBuilder.Entity<CollectionDE>()
|
||||
modelBuilder.Entity<Collection>()
|
||||
.HasIndex(x => x.Slug)
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<GenreDE>()
|
||||
modelBuilder.Entity<Genre>()
|
||||
.HasIndex(x => x.Slug)
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<LibraryDE>()
|
||||
modelBuilder.Entity<Library>()
|
||||
.HasIndex(x => x.Slug)
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<People>()
|
||||
.HasIndex(x => x.Slug)
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<ShowDE>()
|
||||
modelBuilder.Entity<Show>()
|
||||
.HasIndex(x => x.Slug)
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<Studio>()
|
||||
@ -212,14 +191,21 @@ namespace Kyoo
|
||||
modelBuilder.Entity<Episode>()
|
||||
.HasIndex(x => new {x.ShowID, x.SeasonNumber, x.EpisodeNumber, x.AbsoluteNumber})
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<LibraryLink>()
|
||||
.HasIndex(x => new {x.LibraryID, x.ShowID})
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<LibraryLink>()
|
||||
.HasIndex(x => new {x.LibraryID, x.CollectionID})
|
||||
modelBuilder.Entity<Track>()
|
||||
.HasIndex(x => new {x.EpisodeID, x.Type, x.Language, x.TrackIndex, x.IsForced})
|
||||
.IsUnique();
|
||||
}
|
||||
|
||||
public T GetTemporaryObject<T>(T model)
|
||||
where T : class, IResource
|
||||
{
|
||||
T tmp = Set<T>().Local.FirstOrDefault(x => x.ID == model.ID);
|
||||
if (tmp != null)
|
||||
return tmp;
|
||||
Entry(model).State = EntityState.Unchanged;
|
||||
return model;
|
||||
}
|
||||
|
||||
public override int SaveChanges()
|
||||
{
|
||||
try
|
||||
@ -266,7 +252,7 @@ namespace Kyoo
|
||||
}
|
||||
|
||||
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
CancellationToken cancellationToken = new())
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -281,7 +267,7 @@ namespace Kyoo
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
|
||||
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new())
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -297,7 +283,7 @@ namespace Kyoo
|
||||
}
|
||||
|
||||
public async Task<int> SaveChangesAsync(string duplicateMessage,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
CancellationToken cancellationToken = new())
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -312,7 +298,7 @@ namespace Kyoo
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<int> SaveIfNoDuplicates(CancellationToken cancellationToken = new CancellationToken())
|
||||
public async Task<int> SaveIfNoDuplicates(CancellationToken cancellationToken = new())
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -324,13 +310,39 @@ namespace Kyoo
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsDuplicateException(DbUpdateException ex)
|
||||
public Task<T> SaveOrRetry<T>(T obj, Func<T, int, T> onFail, CancellationToken cancellationToken = new())
|
||||
{
|
||||
return ex.InnerException is PostgresException inner
|
||||
&& inner.SqlState == PostgresErrorCodes.UniqueViolation;
|
||||
return SaveOrRetry(obj, onFail, 0, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<T> SaveOrRetry<T>(T obj,
|
||||
Func<T, int, T> onFail,
|
||||
int recurse,
|
||||
CancellationToken cancellationToken = new())
|
||||
{
|
||||
try
|
||||
{
|
||||
await base.SaveChangesAsync(true, cancellationToken);
|
||||
return obj;
|
||||
}
|
||||
catch (DbUpdateException ex) when (IsDuplicateException(ex))
|
||||
{
|
||||
recurse++;
|
||||
return await SaveOrRetry(onFail(obj, recurse), onFail, recurse, cancellationToken);
|
||||
}
|
||||
catch (DbUpdateException)
|
||||
{
|
||||
DiscardChanges();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void DiscardChanges()
|
||||
private static bool IsDuplicateException(Exception ex)
|
||||
{
|
||||
return ex.InnerException is PostgresException {SqlState: PostgresErrorCodes.UniqueViolation};
|
||||
}
|
||||
|
||||
private void DiscardChanges()
|
||||
{
|
||||
foreach (EntityEntry entry in ChangeTracker.Entries().Where(x => x.State != EntityState.Unchanged
|
||||
&& x.State != EntityState.Detached))
|
||||
|
||||
@ -10,16 +10,16 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
{
|
||||
[DbContext(typeof(ConfigurationDbContext))]
|
||||
[Migration("20200526235342_Initial")]
|
||||
[Migration("20210306161631_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
|
||||
.HasAnnotation("ProductVersion", "3.1.3")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63)
|
||||
.HasAnnotation("ProductVersion", "5.0.3")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b =>
|
||||
{
|
||||
@ -28,16 +28,20 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<string>("AllowedAccessTokenSigningAlgorithms")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("character varying(1000)")
|
||||
.HasMaxLength(1000);
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("character varying(1000)");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.HasColumnType("boolean");
|
||||
@ -47,12 +51,15 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<bool>("NonEditable")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("ShowInDiscoveryDocument")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTime?>("Updated")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
@ -76,14 +83,14 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.ToTable("ApiClaims");
|
||||
b.ToTable("ApiResourceClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b =>
|
||||
@ -98,22 +105,22 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.ToTable("ApiProperties");
|
||||
b.ToTable("ApiResourceProperties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@ -123,21 +130,80 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
b.Property<int>("ApiResourceId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Scope")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.ToTable("ApiResourceScopes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int>("ApiResourceId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("character varying(1000)")
|
||||
.HasMaxLength(1000);
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("character varying(1000)");
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4000)
|
||||
.HasColumnType("character varying(4000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.ToTable("ApiResourceSecrets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("character varying(1000)");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<bool>("Emphasize")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<bool>("Required")
|
||||
.HasColumnType("boolean");
|
||||
@ -147,8 +213,6 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
@ -162,56 +226,46 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int>("ApiScopeId")
|
||||
b.Property<int>("ScopeId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiScopeId");
|
||||
b.HasIndex("ScopeId");
|
||||
|
||||
b.ToTable("ApiScopeClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int>("ApiResourceId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("character varying(1000)")
|
||||
.HasMaxLength(1000);
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Type")
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.Property<int>("ScopeId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(4000)")
|
||||
.HasMaxLength(4000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
b.HasIndex("ScopeId");
|
||||
|
||||
b.ToTable("ApiSecrets");
|
||||
b.ToTable("ApiScopeProperties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b =>
|
||||
@ -242,6 +296,10 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
b.Property<bool>("AllowRememberConsent")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("AllowedIdentityTokenSigningAlgorithms")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<bool>("AlwaysIncludeUserClaimsInIdToken")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
@ -255,25 +313,25 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("BackChannelLogoutUri")
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.Property<string>("ClientClaimsPrefix")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("ClientName")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("ClientUri")
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.Property<int?>("ConsentLifetime")
|
||||
.HasColumnType("integer");
|
||||
@ -282,8 +340,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("character varying(1000)")
|
||||
.HasMaxLength(1000);
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("character varying(1000)");
|
||||
|
||||
b.Property<int>("DeviceCodeLifetime")
|
||||
.HasColumnType("integer");
|
||||
@ -298,8 +356,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("FrontChannelLogoutUri")
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.Property<int>("IdentityTokenLifetime")
|
||||
.HasColumnType("integer");
|
||||
@ -311,20 +369,20 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("LogoUri")
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.Property<bool>("NonEditable")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("PairWiseSubjectSalt")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("ProtocolType")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<int>("RefreshTokenExpiration")
|
||||
.HasColumnType("integer");
|
||||
@ -341,6 +399,9 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
b.Property<bool>("RequirePkce")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("RequireRequestObject")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("SlidingRefreshTokenLifetime")
|
||||
.HasColumnType("integer");
|
||||
|
||||
@ -351,8 +412,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("UserCodeType")
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<int?>("UserSsoLifetime")
|
||||
.HasColumnType("integer");
|
||||
@ -377,13 +438,13 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -404,8 +465,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Origin")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(150)")
|
||||
.HasMaxLength(150);
|
||||
.HasMaxLength(150)
|
||||
.HasColumnType("character varying(150)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -426,8 +487,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("GrantType")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -448,8 +509,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Provider")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -470,8 +531,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("PostLogoutRedirectUri")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -492,13 +553,13 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -519,8 +580,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("RedirectUri")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -541,8 +602,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Scope")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -565,21 +626,21 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(4000)")
|
||||
.HasMaxLength(4000);
|
||||
.HasMaxLength(4000)
|
||||
.HasColumnType("character varying(4000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -588,28 +649,6 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
b.ToTable("ClientSecrets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int>("IdentityResourceId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IdentityResourceId");
|
||||
|
||||
b.ToTable("IdentityClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@ -621,12 +660,12 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("character varying(1000)")
|
||||
.HasMaxLength(1000);
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("character varying(1000)");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<bool>("Emphasize")
|
||||
.HasColumnType("boolean");
|
||||
@ -636,8 +675,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<bool>("NonEditable")
|
||||
.HasColumnType("boolean");
|
||||
@ -659,6 +698,28 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
b.ToTable("IdentityResources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int>("IdentityResourceId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IdentityResourceId");
|
||||
|
||||
b.ToTable("IdentityResourceClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@ -671,19 +732,19 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IdentityResourceId");
|
||||
|
||||
b.ToTable("IdentityProperties");
|
||||
b.ToTable("IdentityResourceProperties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
|
||||
@ -693,6 +754,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ApiResource");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b =>
|
||||
@ -702,33 +765,52 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ApiResource");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||
.WithMany("Scopes")
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ApiResource");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "ApiScope")
|
||||
.WithMany("UserClaims")
|
||||
.HasForeignKey("ApiScopeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||
.WithMany("Secrets")
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ApiResource");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "Scope")
|
||||
.WithMany("UserClaims")
|
||||
.HasForeignKey("ScopeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Scope");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "Scope")
|
||||
.WithMany("Properties")
|
||||
.HasForeignKey("ScopeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Scope");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
|
||||
@ -738,6 +820,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
|
||||
@ -747,6 +831,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
|
||||
@ -756,6 +842,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
|
||||
@ -765,6 +853,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
|
||||
@ -774,6 +864,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b =>
|
||||
@ -783,6 +875,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
|
||||
@ -792,6 +886,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
|
||||
@ -801,6 +897,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
|
||||
@ -810,15 +908,19 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource")
|
||||
.WithMany("UserClaims")
|
||||
.HasForeignKey("IdentityResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("IdentityResource");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b =>
|
||||
@ -828,6 +930,54 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("IdentityResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("IdentityResource");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b =>
|
||||
{
|
||||
b.Navigation("Properties");
|
||||
|
||||
b.Navigation("Scopes");
|
||||
|
||||
b.Navigation("Secrets");
|
||||
|
||||
b.Navigation("UserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||
{
|
||||
b.Navigation("Properties");
|
||||
|
||||
b.Navigation("UserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b =>
|
||||
{
|
||||
b.Navigation("AllowedCorsOrigins");
|
||||
|
||||
b.Navigation("AllowedGrantTypes");
|
||||
|
||||
b.Navigation("AllowedScopes");
|
||||
|
||||
b.Navigation("Claims");
|
||||
|
||||
b.Navigation("ClientSecrets");
|
||||
|
||||
b.Navigation("IdentityProviderRestrictions");
|
||||
|
||||
b.Navigation("PostLogoutRedirectUris");
|
||||
|
||||
b.Navigation("Properties");
|
||||
|
||||
b.Navigation("RedirectUris");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b =>
|
||||
{
|
||||
b.Navigation("Properties");
|
||||
|
||||
b.Navigation("UserClaims");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
@ -12,69 +12,92 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
name: "ApiResources",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Enabled = table.Column<bool>(nullable: false),
|
||||
Name = table.Column<string>(maxLength: 200, nullable: false),
|
||||
DisplayName = table.Column<string>(maxLength: 200, nullable: true),
|
||||
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||
Created = table.Column<DateTime>(nullable: false),
|
||||
Updated = table.Column<DateTime>(nullable: true),
|
||||
LastAccessed = table.Column<DateTime>(nullable: true),
|
||||
NonEditable = table.Column<bool>(nullable: false)
|
||||
Enabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
Name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
DisplayName = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
Description = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
||||
AllowedAccessTokenSigningAlgorithms = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||
ShowInDiscoveryDocument = table.Column<bool>(type: "boolean", nullable: false),
|
||||
Created = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
|
||||
Updated = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||
LastAccessed = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||
NonEditable = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiResources", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ApiScopes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Enabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
Name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
DisplayName = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
Description = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
||||
Required = table.Column<bool>(type: "boolean", nullable: false),
|
||||
Emphasize = table.Column<bool>(type: "boolean", nullable: false),
|
||||
ShowInDiscoveryDocument = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiScopes", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Clients",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Enabled = table.Column<bool>(nullable: false),
|
||||
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||
ProtocolType = table.Column<string>(maxLength: 200, nullable: false),
|
||||
RequireClientSecret = table.Column<bool>(nullable: false),
|
||||
ClientName = table.Column<string>(maxLength: 200, nullable: true),
|
||||
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||
ClientUri = table.Column<string>(maxLength: 2000, nullable: true),
|
||||
LogoUri = table.Column<string>(maxLength: 2000, nullable: true),
|
||||
RequireConsent = table.Column<bool>(nullable: false),
|
||||
AllowRememberConsent = table.Column<bool>(nullable: false),
|
||||
AlwaysIncludeUserClaimsInIdToken = table.Column<bool>(nullable: false),
|
||||
RequirePkce = table.Column<bool>(nullable: false),
|
||||
AllowPlainTextPkce = table.Column<bool>(nullable: false),
|
||||
AllowAccessTokensViaBrowser = table.Column<bool>(nullable: false),
|
||||
FrontChannelLogoutUri = table.Column<string>(maxLength: 2000, nullable: true),
|
||||
FrontChannelLogoutSessionRequired = table.Column<bool>(nullable: false),
|
||||
BackChannelLogoutUri = table.Column<string>(maxLength: 2000, nullable: true),
|
||||
BackChannelLogoutSessionRequired = table.Column<bool>(nullable: false),
|
||||
AllowOfflineAccess = table.Column<bool>(nullable: false),
|
||||
IdentityTokenLifetime = table.Column<int>(nullable: false),
|
||||
AccessTokenLifetime = table.Column<int>(nullable: false),
|
||||
AuthorizationCodeLifetime = table.Column<int>(nullable: false),
|
||||
ConsentLifetime = table.Column<int>(nullable: true),
|
||||
AbsoluteRefreshTokenLifetime = table.Column<int>(nullable: false),
|
||||
SlidingRefreshTokenLifetime = table.Column<int>(nullable: false),
|
||||
RefreshTokenUsage = table.Column<int>(nullable: false),
|
||||
UpdateAccessTokenClaimsOnRefresh = table.Column<bool>(nullable: false),
|
||||
RefreshTokenExpiration = table.Column<int>(nullable: false),
|
||||
AccessTokenType = table.Column<int>(nullable: false),
|
||||
EnableLocalLogin = table.Column<bool>(nullable: false),
|
||||
IncludeJwtId = table.Column<bool>(nullable: false),
|
||||
AlwaysSendClientClaims = table.Column<bool>(nullable: false),
|
||||
ClientClaimsPrefix = table.Column<string>(maxLength: 200, nullable: true),
|
||||
PairWiseSubjectSalt = table.Column<string>(maxLength: 200, nullable: true),
|
||||
Created = table.Column<DateTime>(nullable: false),
|
||||
Updated = table.Column<DateTime>(nullable: true),
|
||||
LastAccessed = table.Column<DateTime>(nullable: true),
|
||||
UserSsoLifetime = table.Column<int>(nullable: true),
|
||||
UserCodeType = table.Column<string>(maxLength: 100, nullable: true),
|
||||
DeviceCodeLifetime = table.Column<int>(nullable: false),
|
||||
NonEditable = table.Column<bool>(nullable: false)
|
||||
Enabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
ClientId = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
ProtocolType = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
RequireClientSecret = table.Column<bool>(type: "boolean", nullable: false),
|
||||
ClientName = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
Description = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
||||
ClientUri = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
||||
LogoUri = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
||||
RequireConsent = table.Column<bool>(type: "boolean", nullable: false),
|
||||
AllowRememberConsent = table.Column<bool>(type: "boolean", nullable: false),
|
||||
AlwaysIncludeUserClaimsInIdToken = table.Column<bool>(type: "boolean", nullable: false),
|
||||
RequirePkce = table.Column<bool>(type: "boolean", nullable: false),
|
||||
AllowPlainTextPkce = table.Column<bool>(type: "boolean", nullable: false),
|
||||
RequireRequestObject = table.Column<bool>(type: "boolean", nullable: false),
|
||||
AllowAccessTokensViaBrowser = table.Column<bool>(type: "boolean", nullable: false),
|
||||
FrontChannelLogoutUri = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
||||
FrontChannelLogoutSessionRequired = table.Column<bool>(type: "boolean", nullable: false),
|
||||
BackChannelLogoutUri = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
||||
BackChannelLogoutSessionRequired = table.Column<bool>(type: "boolean", nullable: false),
|
||||
AllowOfflineAccess = table.Column<bool>(type: "boolean", nullable: false),
|
||||
IdentityTokenLifetime = table.Column<int>(type: "integer", nullable: false),
|
||||
AllowedIdentityTokenSigningAlgorithms = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||
AccessTokenLifetime = table.Column<int>(type: "integer", nullable: false),
|
||||
AuthorizationCodeLifetime = table.Column<int>(type: "integer", nullable: false),
|
||||
ConsentLifetime = table.Column<int>(type: "integer", nullable: true),
|
||||
AbsoluteRefreshTokenLifetime = table.Column<int>(type: "integer", nullable: false),
|
||||
SlidingRefreshTokenLifetime = table.Column<int>(type: "integer", nullable: false),
|
||||
RefreshTokenUsage = table.Column<int>(type: "integer", nullable: false),
|
||||
UpdateAccessTokenClaimsOnRefresh = table.Column<bool>(type: "boolean", nullable: false),
|
||||
RefreshTokenExpiration = table.Column<int>(type: "integer", nullable: false),
|
||||
AccessTokenType = table.Column<int>(type: "integer", nullable: false),
|
||||
EnableLocalLogin = table.Column<bool>(type: "boolean", nullable: false),
|
||||
IncludeJwtId = table.Column<bool>(type: "boolean", nullable: false),
|
||||
AlwaysSendClientClaims = table.Column<bool>(type: "boolean", nullable: false),
|
||||
ClientClaimsPrefix = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
PairWiseSubjectSalt = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
Created = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
|
||||
Updated = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||
LastAccessed = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||
UserSsoLifetime = table.Column<int>(type: "integer", nullable: true),
|
||||
UserCodeType = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||
DeviceCodeLifetime = table.Column<int>(type: "integer", nullable: false),
|
||||
NonEditable = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -85,18 +108,18 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
name: "IdentityResources",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Enabled = table.Column<bool>(nullable: false),
|
||||
Name = table.Column<string>(maxLength: 200, nullable: false),
|
||||
DisplayName = table.Column<string>(maxLength: 200, nullable: true),
|
||||
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||
Required = table.Column<bool>(nullable: false),
|
||||
Emphasize = table.Column<bool>(nullable: false),
|
||||
ShowInDiscoveryDocument = table.Column<bool>(nullable: false),
|
||||
Created = table.Column<DateTime>(nullable: false),
|
||||
Updated = table.Column<DateTime>(nullable: true),
|
||||
NonEditable = table.Column<bool>(nullable: false)
|
||||
Enabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
Name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
DisplayName = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
Description = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
||||
Required = table.Column<bool>(type: "boolean", nullable: false),
|
||||
Emphasize = table.Column<bool>(type: "boolean", nullable: false),
|
||||
ShowInDiscoveryDocument = table.Column<bool>(type: "boolean", nullable: false),
|
||||
Created = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
|
||||
Updated = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||
NonEditable = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -104,19 +127,19 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ApiClaims",
|
||||
name: "ApiResourceClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Type = table.Column<string>(maxLength: 200, nullable: false),
|
||||
ApiResourceId = table.Column<int>(nullable: false)
|
||||
ApiResourceId = table.Column<int>(type: "integer", nullable: false),
|
||||
Type = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiClaims", x => x.Id);
|
||||
table.PrimaryKey("PK_ApiResourceClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ApiClaims_ApiResources_ApiResourceId",
|
||||
name: "FK_ApiResourceClaims_ApiResources_ApiResourceId",
|
||||
column: x => x.ApiResourceId,
|
||||
principalTable: "ApiResources",
|
||||
principalColumn: "Id",
|
||||
@ -124,20 +147,20 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ApiProperties",
|
||||
name: "ApiResourceProperties",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Key = table.Column<string>(maxLength: 250, nullable: false),
|
||||
Value = table.Column<string>(maxLength: 2000, nullable: false),
|
||||
ApiResourceId = table.Column<int>(nullable: false)
|
||||
ApiResourceId = table.Column<int>(type: "integer", nullable: false),
|
||||
Key = table.Column<string>(type: "character varying(250)", maxLength: 250, nullable: false),
|
||||
Value = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiProperties", x => x.Id);
|
||||
table.PrimaryKey("PK_ApiResourceProperties", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ApiProperties_ApiResources_ApiResourceId",
|
||||
name: "FK_ApiResourceProperties_ApiResources_ApiResourceId",
|
||||
column: x => x.ApiResourceId,
|
||||
principalTable: "ApiResources",
|
||||
principalColumn: "Id",
|
||||
@ -145,24 +168,19 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ApiScopes",
|
||||
name: "ApiResourceScopes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Name = table.Column<string>(maxLength: 200, nullable: false),
|
||||
DisplayName = table.Column<string>(maxLength: 200, nullable: true),
|
||||
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||
Required = table.Column<bool>(nullable: false),
|
||||
Emphasize = table.Column<bool>(nullable: false),
|
||||
ShowInDiscoveryDocument = table.Column<bool>(nullable: false),
|
||||
ApiResourceId = table.Column<int>(nullable: false)
|
||||
Scope = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
ApiResourceId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiScopes", x => x.Id);
|
||||
table.PrimaryKey("PK_ApiResourceScopes", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ApiScopes_ApiResources_ApiResourceId",
|
||||
name: "FK_ApiResourceScopes_ApiResources_ApiResourceId",
|
||||
column: x => x.ApiResourceId,
|
||||
principalTable: "ApiResources",
|
||||
principalColumn: "Id",
|
||||
@ -170,38 +188,79 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ApiSecrets",
|
||||
name: "ApiResourceSecrets",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||
Value = table.Column<string>(maxLength: 4000, nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: true),
|
||||
Type = table.Column<string>(maxLength: 250, nullable: false),
|
||||
Created = table.Column<DateTime>(nullable: false),
|
||||
ApiResourceId = table.Column<int>(nullable: false)
|
||||
ApiResourceId = table.Column<int>(type: "integer", nullable: false),
|
||||
Description = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
||||
Value = table.Column<string>(type: "character varying(4000)", maxLength: 4000, nullable: false),
|
||||
Expiration = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||
Type = table.Column<string>(type: "character varying(250)", maxLength: 250, nullable: false),
|
||||
Created = table.Column<DateTime>(type: "timestamp without time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiSecrets", x => x.Id);
|
||||
table.PrimaryKey("PK_ApiResourceSecrets", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ApiSecrets_ApiResources_ApiResourceId",
|
||||
name: "FK_ApiResourceSecrets_ApiResources_ApiResourceId",
|
||||
column: x => x.ApiResourceId,
|
||||
principalTable: "ApiResources",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ApiScopeClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
ScopeId = table.Column<int>(type: "integer", nullable: false),
|
||||
Type = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiScopeClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ApiScopeClaims_ApiScopes_ScopeId",
|
||||
column: x => x.ScopeId,
|
||||
principalTable: "ApiScopes",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ApiScopeProperties",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
ScopeId = table.Column<int>(type: "integer", nullable: false),
|
||||
Key = table.Column<string>(type: "character varying(250)", maxLength: 250, nullable: false),
|
||||
Value = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiScopeProperties", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ApiScopeProperties_ApiScopes_ScopeId",
|
||||
column: x => x.ScopeId,
|
||||
principalTable: "ApiScopes",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ClientClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Type = table.Column<string>(maxLength: 250, nullable: false),
|
||||
Value = table.Column<string>(maxLength: 250, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
Type = table.Column<string>(type: "character varying(250)", maxLength: 250, nullable: false),
|
||||
Value = table.Column<string>(type: "character varying(250)", maxLength: 250, nullable: false),
|
||||
ClientId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -218,10 +277,10 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
name: "ClientCorsOrigins",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Origin = table.Column<string>(maxLength: 150, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
Origin = table.Column<string>(type: "character varying(150)", maxLength: 150, nullable: false),
|
||||
ClientId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -238,10 +297,10 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
name: "ClientGrantTypes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
GrantType = table.Column<string>(maxLength: 250, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
GrantType = table.Column<string>(type: "character varying(250)", maxLength: 250, nullable: false),
|
||||
ClientId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -258,10 +317,10 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
name: "ClientIdPRestrictions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Provider = table.Column<string>(maxLength: 200, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
Provider = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
ClientId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -278,10 +337,10 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
name: "ClientPostLogoutRedirectUris",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
PostLogoutRedirectUri = table.Column<string>(maxLength: 2000, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
PostLogoutRedirectUri = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: false),
|
||||
ClientId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -298,11 +357,11 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
name: "ClientProperties",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Key = table.Column<string>(maxLength: 250, nullable: false),
|
||||
Value = table.Column<string>(maxLength: 2000, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
ClientId = table.Column<int>(type: "integer", nullable: false),
|
||||
Key = table.Column<string>(type: "character varying(250)", maxLength: 250, nullable: false),
|
||||
Value = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -319,10 +378,10 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
name: "ClientRedirectUris",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
RedirectUri = table.Column<string>(maxLength: 2000, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
RedirectUri = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: false),
|
||||
ClientId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -339,10 +398,10 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
name: "ClientScopes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Scope = table.Column<string>(maxLength: 200, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
Scope = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
ClientId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -359,14 +418,14 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
name: "ClientSecrets",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Description = table.Column<string>(maxLength: 2000, nullable: true),
|
||||
Value = table.Column<string>(maxLength: 4000, nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: true),
|
||||
Type = table.Column<string>(maxLength: 250, nullable: false),
|
||||
Created = table.Column<DateTime>(nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
ClientId = table.Column<int>(type: "integer", nullable: false),
|
||||
Description = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
||||
Value = table.Column<string>(type: "character varying(4000)", maxLength: 4000, nullable: false),
|
||||
Expiration = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||
Type = table.Column<string>(type: "character varying(250)", maxLength: 250, nullable: false),
|
||||
Created = table.Column<DateTime>(type: "timestamp without time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -380,19 +439,19 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "IdentityClaims",
|
||||
name: "IdentityResourceClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Type = table.Column<string>(maxLength: 200, nullable: false),
|
||||
IdentityResourceId = table.Column<int>(nullable: false)
|
||||
IdentityResourceId = table.Column<int>(type: "integer", nullable: false),
|
||||
Type = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_IdentityClaims", x => x.Id);
|
||||
table.PrimaryKey("PK_IdentityResourceClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_IdentityClaims_IdentityResources_IdentityResourceId",
|
||||
name: "FK_IdentityResourceClaims_IdentityResources_IdentityResourceId",
|
||||
column: x => x.IdentityResourceId,
|
||||
principalTable: "IdentityResources",
|
||||
principalColumn: "Id",
|
||||
@ -400,54 +459,34 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "IdentityProperties",
|
||||
name: "IdentityResourceProperties",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Key = table.Column<string>(maxLength: 250, nullable: false),
|
||||
Value = table.Column<string>(maxLength: 2000, nullable: false),
|
||||
IdentityResourceId = table.Column<int>(nullable: false)
|
||||
IdentityResourceId = table.Column<int>(type: "integer", nullable: false),
|
||||
Key = table.Column<string>(type: "character varying(250)", maxLength: 250, nullable: false),
|
||||
Value = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_IdentityProperties", x => x.Id);
|
||||
table.PrimaryKey("PK_IdentityResourceProperties", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_IdentityProperties_IdentityResources_IdentityResourceId",
|
||||
name: "FK_IdentityResourceProperties_IdentityResources_IdentityResour~",
|
||||
column: x => x.IdentityResourceId,
|
||||
principalTable: "IdentityResources",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ApiScopeClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Type = table.Column<string>(maxLength: 200, nullable: false),
|
||||
ApiScopeId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiScopeClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ApiScopeClaims_ApiScopes_ApiScopeId",
|
||||
column: x => x.ApiScopeId,
|
||||
principalTable: "ApiScopes",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiClaims_ApiResourceId",
|
||||
table: "ApiClaims",
|
||||
name: "IX_ApiResourceClaims_ApiResourceId",
|
||||
table: "ApiResourceClaims",
|
||||
column: "ApiResourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiProperties_ApiResourceId",
|
||||
table: "ApiProperties",
|
||||
name: "IX_ApiResourceProperties_ApiResourceId",
|
||||
table: "ApiResourceProperties",
|
||||
column: "ApiResourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
@ -457,26 +496,31 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiScopeClaims_ApiScopeId",
|
||||
table: "ApiScopeClaims",
|
||||
column: "ApiScopeId");
|
||||
name: "IX_ApiResourceScopes_ApiResourceId",
|
||||
table: "ApiResourceScopes",
|
||||
column: "ApiResourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiScopes_ApiResourceId",
|
||||
table: "ApiScopes",
|
||||
name: "IX_ApiResourceSecrets_ApiResourceId",
|
||||
table: "ApiResourceSecrets",
|
||||
column: "ApiResourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiScopeClaims_ScopeId",
|
||||
table: "ApiScopeClaims",
|
||||
column: "ScopeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiScopeProperties_ScopeId",
|
||||
table: "ApiScopeProperties",
|
||||
column: "ScopeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiScopes_Name",
|
||||
table: "ApiScopes",
|
||||
column: "Name",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiSecrets_ApiResourceId",
|
||||
table: "ApiSecrets",
|
||||
column: "ApiResourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ClientClaims_ClientId",
|
||||
table: "ClientClaims",
|
||||
@ -529,13 +573,13 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_IdentityClaims_IdentityResourceId",
|
||||
table: "IdentityClaims",
|
||||
name: "IX_IdentityResourceClaims_IdentityResourceId",
|
||||
table: "IdentityResourceClaims",
|
||||
column: "IdentityResourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_IdentityProperties_IdentityResourceId",
|
||||
table: "IdentityProperties",
|
||||
name: "IX_IdentityResourceProperties_IdentityResourceId",
|
||||
table: "IdentityResourceProperties",
|
||||
column: "IdentityResourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
@ -548,16 +592,22 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiClaims");
|
||||
name: "ApiResourceClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiProperties");
|
||||
name: "ApiResourceProperties");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiResourceScopes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiResourceSecrets");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiScopeClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiSecrets");
|
||||
name: "ApiScopeProperties");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ClientClaims");
|
||||
@ -587,10 +637,13 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
name: "ClientSecrets");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "IdentityClaims");
|
||||
name: "IdentityResourceClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "IdentityProperties");
|
||||
name: "IdentityResourceProperties");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiResources");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiScopes");
|
||||
@ -600,9 +653,6 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "IdentityResources");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiResources");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -15,9 +15,9 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
|
||||
.HasAnnotation("ProductVersion", "3.1.3")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63)
|
||||
.HasAnnotation("ProductVersion", "5.0.3")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b =>
|
||||
{
|
||||
@ -26,16 +26,20 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<string>("AllowedAccessTokenSigningAlgorithms")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("character varying(1000)")
|
||||
.HasMaxLength(1000);
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("character varying(1000)");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.HasColumnType("boolean");
|
||||
@ -45,12 +49,15 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<bool>("NonEditable")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("ShowInDiscoveryDocument")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTime?>("Updated")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
@ -74,14 +81,14 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.ToTable("ApiClaims");
|
||||
b.ToTable("ApiResourceClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b =>
|
||||
@ -96,22 +103,22 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.ToTable("ApiProperties");
|
||||
b.ToTable("ApiResourceProperties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@ -121,21 +128,80 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
b.Property<int>("ApiResourceId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Scope")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.ToTable("ApiResourceScopes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int>("ApiResourceId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("character varying(1000)")
|
||||
.HasMaxLength(1000);
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("character varying(1000)");
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4000)
|
||||
.HasColumnType("character varying(4000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.ToTable("ApiResourceSecrets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("character varying(1000)");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<bool>("Emphasize")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<bool>("Required")
|
||||
.HasColumnType("boolean");
|
||||
@ -145,8 +211,6 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
@ -160,56 +224,46 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int>("ApiScopeId")
|
||||
b.Property<int>("ScopeId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiScopeId");
|
||||
b.HasIndex("ScopeId");
|
||||
|
||||
b.ToTable("ApiScopeClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int>("ApiResourceId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("character varying(1000)")
|
||||
.HasMaxLength(1000);
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Type")
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.Property<int>("ScopeId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(4000)")
|
||||
.HasMaxLength(4000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
b.HasIndex("ScopeId");
|
||||
|
||||
b.ToTable("ApiSecrets");
|
||||
b.ToTable("ApiScopeProperties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b =>
|
||||
@ -240,6 +294,10 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
b.Property<bool>("AllowRememberConsent")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("AllowedIdentityTokenSigningAlgorithms")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<bool>("AlwaysIncludeUserClaimsInIdToken")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
@ -253,25 +311,25 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("BackChannelLogoutUri")
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.Property<string>("ClientClaimsPrefix")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("ClientName")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("ClientUri")
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.Property<int?>("ConsentLifetime")
|
||||
.HasColumnType("integer");
|
||||
@ -280,8 +338,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("character varying(1000)")
|
||||
.HasMaxLength(1000);
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("character varying(1000)");
|
||||
|
||||
b.Property<int>("DeviceCodeLifetime")
|
||||
.HasColumnType("integer");
|
||||
@ -296,8 +354,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("FrontChannelLogoutUri")
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.Property<int>("IdentityTokenLifetime")
|
||||
.HasColumnType("integer");
|
||||
@ -309,20 +367,20 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("LogoUri")
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.Property<bool>("NonEditable")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("PairWiseSubjectSalt")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("ProtocolType")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<int>("RefreshTokenExpiration")
|
||||
.HasColumnType("integer");
|
||||
@ -339,6 +397,9 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
b.Property<bool>("RequirePkce")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("RequireRequestObject")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("SlidingRefreshTokenLifetime")
|
||||
.HasColumnType("integer");
|
||||
|
||||
@ -349,8 +410,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("UserCodeType")
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<int?>("UserSsoLifetime")
|
||||
.HasColumnType("integer");
|
||||
@ -375,13 +436,13 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -402,8 +463,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Origin")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(150)")
|
||||
.HasMaxLength(150);
|
||||
.HasMaxLength(150)
|
||||
.HasColumnType("character varying(150)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -424,8 +485,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("GrantType")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -446,8 +507,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Provider")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -468,8 +529,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("PostLogoutRedirectUri")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -490,13 +551,13 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -517,8 +578,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("RedirectUri")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -539,8 +600,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Scope")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -563,21 +624,21 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(4000)")
|
||||
.HasMaxLength(4000);
|
||||
.HasMaxLength(4000)
|
||||
.HasColumnType("character varying(4000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -586,28 +647,6 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
b.ToTable("ClientSecrets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int>("IdentityResourceId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IdentityResourceId");
|
||||
|
||||
b.ToTable("IdentityClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@ -619,12 +658,12 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("character varying(1000)")
|
||||
.HasMaxLength(1000);
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("character varying(1000)");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<bool>("Emphasize")
|
||||
.HasColumnType("boolean");
|
||||
@ -634,8 +673,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<bool>("NonEditable")
|
||||
.HasColumnType("boolean");
|
||||
@ -657,6 +696,28 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
b.ToTable("IdentityResources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int>("IdentityResourceId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IdentityResourceId");
|
||||
|
||||
b.ToTable("IdentityResourceClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@ -669,19 +730,19 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(250)")
|
||||
.HasMaxLength(250);
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("character varying(250)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasMaxLength(2000);
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IdentityResourceId");
|
||||
|
||||
b.ToTable("IdentityProperties");
|
||||
b.ToTable("IdentityResourceProperties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
|
||||
@ -691,6 +752,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ApiResource");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b =>
|
||||
@ -700,33 +763,52 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ApiResource");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||
.WithMany("Scopes")
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ApiResource");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "ApiScope")
|
||||
.WithMany("UserClaims")
|
||||
.HasForeignKey("ApiScopeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||
.WithMany("Secrets")
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ApiResource");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "Scope")
|
||||
.WithMany("UserClaims")
|
||||
.HasForeignKey("ScopeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Scope");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "Scope")
|
||||
.WithMany("Properties")
|
||||
.HasForeignKey("ScopeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Scope");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
|
||||
@ -736,6 +818,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
|
||||
@ -745,6 +829,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
|
||||
@ -754,6 +840,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
|
||||
@ -763,6 +851,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
|
||||
@ -772,6 +862,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b =>
|
||||
@ -781,6 +873,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
|
||||
@ -790,6 +884,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
|
||||
@ -799,6 +895,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
|
||||
@ -808,15 +906,19 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource")
|
||||
.WithMany("UserClaims")
|
||||
.HasForeignKey("IdentityResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("IdentityResource");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b =>
|
||||
@ -826,6 +928,54 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityConfiguration
|
||||
.HasForeignKey("IdentityResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("IdentityResource");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b =>
|
||||
{
|
||||
b.Navigation("Properties");
|
||||
|
||||
b.Navigation("Scopes");
|
||||
|
||||
b.Navigation("Secrets");
|
||||
|
||||
b.Navigation("UserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||
{
|
||||
b.Navigation("Properties");
|
||||
|
||||
b.Navigation("UserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b =>
|
||||
{
|
||||
b.Navigation("AllowedCorsOrigins");
|
||||
|
||||
b.Navigation("AllowedGrantTypes");
|
||||
|
||||
b.Navigation("AllowedScopes");
|
||||
|
||||
b.Navigation("Claims");
|
||||
|
||||
b.Navigation("ClientSecrets");
|
||||
|
||||
b.Navigation("IdentityProviderRestrictions");
|
||||
|
||||
b.Navigation("PostLogoutRedirectUris");
|
||||
|
||||
b.Navigation("Properties");
|
||||
|
||||
b.Navigation("RedirectUris");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b =>
|
||||
{
|
||||
b.Navigation("Properties");
|
||||
|
||||
b.Navigation("UserClaims");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
|
||||
@ -7,51 +7,59 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
namespace Kyoo.Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
{
|
||||
[DbContext(typeof(IdentityDatabase))]
|
||||
[Migration("20200526235424_Initial")]
|
||||
[Migration("20210216205030_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
|
||||
.HasAnnotation("ProductVersion", "3.1.3")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63)
|
||||
.HasAnnotation("ProductVersion", "5.0.3")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
|
||||
{
|
||||
b.Property<string>("UserCode")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(50000)")
|
||||
.HasMaxLength(50000);
|
||||
.HasMaxLength(50000)
|
||||
.HasColumnType("character varying(50000)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("DeviceCode")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.IsRequired()
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("SessionId")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("UserCode");
|
||||
|
||||
@ -66,33 +74,44 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<DateTime?>("ConsumedTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(50000)")
|
||||
.HasMaxLength(50000);
|
||||
.HasMaxLength(50000)
|
||||
.HasColumnType("character varying(50000)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("SessionId")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasMaxLength(50);
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
@ -100,6 +119,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
|
||||
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||
|
||||
b.HasIndex("SubjectId", "SessionId", "Type");
|
||||
|
||||
b.ToTable("PersistedGrants");
|
||||
});
|
||||
|
||||
@ -116,8 +137,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasMaxLength(256);
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
@ -129,12 +150,12 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasMaxLength(256);
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasMaxLength(256);
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("OTAC")
|
||||
.HasColumnType("text");
|
||||
@ -158,17 +179,17 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasMaxLength(256);
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex");
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("User");
|
||||
});
|
||||
@ -183,18 +204,18 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasMaxLength(256);
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasMaxLength(256);
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex");
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("UserRoles");
|
||||
});
|
||||
@ -250,12 +271,12 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasMaxLength(128);
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasMaxLength(128);
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("text");
|
||||
@ -292,12 +313,12 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasMaxLength(128);
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasMaxLength(128);
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("text");
|
||||
@ -2,7 +2,7 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
namespace Kyoo.Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
{
|
||||
public partial class Initial : Migration
|
||||
{
|
||||
@ -12,13 +12,15 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
name: "DeviceCodes",
|
||||
columns: table => new
|
||||
{
|
||||
UserCode = table.Column<string>(maxLength: 200, nullable: false),
|
||||
DeviceCode = table.Column<string>(maxLength: 200, nullable: false),
|
||||
SubjectId = table.Column<string>(maxLength: 200, nullable: true),
|
||||
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||
CreationTime = table.Column<DateTime>(nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: false),
|
||||
Data = table.Column<string>(maxLength: 50000, nullable: false)
|
||||
UserCode = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
DeviceCode = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
SubjectId = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
SessionId = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||
ClientId = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
Description = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
CreationTime = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
|
||||
Expiration = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
|
||||
Data = table.Column<string>(type: "character varying(50000)", maxLength: 50000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -29,13 +31,16 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
name: "PersistedGrants",
|
||||
columns: table => new
|
||||
{
|
||||
Key = table.Column<string>(maxLength: 200, nullable: false),
|
||||
Type = table.Column<string>(maxLength: 50, nullable: false),
|
||||
SubjectId = table.Column<string>(maxLength: 200, nullable: true),
|
||||
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||
CreationTime = table.Column<DateTime>(nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: true),
|
||||
Data = table.Column<string>(maxLength: 50000, nullable: false)
|
||||
Key = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
Type = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||
SubjectId = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
SessionId = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||
ClientId = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
Description = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
CreationTime = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
|
||||
Expiration = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||
ConsumedTime = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||
Data = table.Column<string>(type: "character varying(50000)", maxLength: 50000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -46,23 +51,23 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
name: "User",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
UserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
Email = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
|
||||
EmailConfirmed = table.Column<bool>(nullable: false),
|
||||
PasswordHash = table.Column<string>(nullable: true),
|
||||
SecurityStamp = table.Column<string>(nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true),
|
||||
PhoneNumber = table.Column<string>(nullable: true),
|
||||
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
|
||||
TwoFactorEnabled = table.Column<bool>(nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
|
||||
LockoutEnabled = table.Column<bool>(nullable: false),
|
||||
AccessFailedCount = table.Column<int>(nullable: false),
|
||||
OTAC = table.Column<string>(nullable: true),
|
||||
OTACExpires = table.Column<DateTime>(nullable: true)
|
||||
Id = table.Column<string>(type: "text", nullable: false),
|
||||
OTAC = table.Column<string>(type: "text", nullable: true),
|
||||
OTACExpires = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||
UserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
NormalizedEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
EmailConfirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||
PasswordHash = table.Column<string>(type: "text", nullable: true),
|
||||
SecurityStamp = table.Column<string>(type: "text", nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true),
|
||||
PhoneNumber = table.Column<string>(type: "text", nullable: true),
|
||||
PhoneNumberConfirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||
TwoFactorEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
LockoutEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
AccessFailedCount = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -73,10 +78,10 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
name: "UserRoles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true)
|
||||
Id = table.Column<string>(type: "text", nullable: false),
|
||||
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -87,11 +92,11 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
name: "UserClaim",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true)
|
||||
UserId = table.Column<string>(type: "text", nullable: false),
|
||||
ClaimType = table.Column<string>(type: "text", nullable: true),
|
||||
ClaimValue = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -108,10 +113,10 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
name: "UserLogin",
|
||||
columns: table => new
|
||||
{
|
||||
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
|
||||
ProviderKey = table.Column<string>(maxLength: 128, nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(nullable: true),
|
||||
UserId = table.Column<string>(nullable: false)
|
||||
LoginProvider = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||
ProviderKey = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(type: "text", nullable: true),
|
||||
UserId = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -128,10 +133,10 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
name: "UserToken",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
|
||||
Name = table.Column<string>(maxLength: 128, nullable: false),
|
||||
Value = table.Column<string>(nullable: true)
|
||||
UserId = table.Column<string>(type: "text", nullable: false),
|
||||
LoginProvider = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||
Name = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||
Value = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -148,35 +153,35 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
name: "UserRole",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
RoleId = table.Column<string>(nullable: false)
|
||||
UserId = table.Column<string>(type: "text", nullable: false),
|
||||
RoleId = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_UserRole", x => new { x.UserId, x.RoleId });
|
||||
table.ForeignKey(
|
||||
name: "FK_UserRole_UserRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "UserRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_UserRole_User_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "User",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_UserRole_UserRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "UserRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UserRoleClaim",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
RoleId = table.Column<string>(nullable: false),
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true)
|
||||
RoleId = table.Column<string>(type: "text", nullable: false),
|
||||
ClaimType = table.Column<string>(type: "text", nullable: true),
|
||||
ClaimValue = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -210,6 +215,11 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
table: "PersistedGrants",
|
||||
columns: new[] { "SubjectId", "ClientId", "Type" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PersistedGrants_SubjectId_SessionId_Type",
|
||||
table: "PersistedGrants",
|
||||
columns: new[] { "SubjectId", "SessionId", "Type" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "EmailIndex",
|
||||
table: "User",
|
||||
@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
namespace Kyoo.Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
{
|
||||
[DbContext(typeof(IdentityDatabase))]
|
||||
partial class IdentityDatabaseModelSnapshot : ModelSnapshot
|
||||
@ -15,41 +15,49 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
|
||||
.HasAnnotation("ProductVersion", "3.1.3")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63)
|
||||
.HasAnnotation("ProductVersion", "5.0.3")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
|
||||
{
|
||||
b.Property<string>("UserCode")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(50000)")
|
||||
.HasMaxLength(50000);
|
||||
.HasMaxLength(50000)
|
||||
.HasColumnType("character varying(50000)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("DeviceCode")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.IsRequired()
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("SessionId")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("UserCode");
|
||||
|
||||
@ -64,33 +72,44 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<DateTime?>("ConsumedTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(50000)")
|
||||
.HasMaxLength(50000);
|
||||
.HasMaxLength(50000)
|
||||
.HasColumnType("character varying(50000)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("SessionId")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasMaxLength(200);
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasMaxLength(50);
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
@ -98,6 +117,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
|
||||
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||
|
||||
b.HasIndex("SubjectId", "SessionId", "Type");
|
||||
|
||||
b.ToTable("PersistedGrants");
|
||||
});
|
||||
|
||||
@ -114,8 +135,8 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasMaxLength(256);
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
@ -127,12 +148,12 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasMaxLength(256);
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasMaxLength(256);
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("OTAC")
|
||||
.HasColumnType("text");
|
||||
@ -156,17 +177,17 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasMaxLength(256);
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex");
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("User");
|
||||
});
|
||||
@ -181,18 +202,18 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasMaxLength(256);
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasMaxLength(256);
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex");
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("UserRoles");
|
||||
});
|
||||
@ -248,12 +269,12 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasMaxLength(128);
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasMaxLength(128);
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("text");
|
||||
@ -290,12 +311,12 @@ namespace Kyoo.Models.DatabaseMigrations.IdentityDatbase
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasMaxLength(128);
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasMaxLength(128);
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("text");
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Kyoo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
@ -11,21 +10,21 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20210128212212_Initial")]
|
||||
[Migration("20210317220956_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:Enum:item_type", "show,movie,collection")
|
||||
.HasAnnotation("Npgsql:Enum:status", "finished,airing,planned,unknown")
|
||||
.HasAnnotation("Npgsql:Enum:stream_type", "unknown,video,audio,subtitle,font")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
|
||||
.HasAnnotation("ProductVersion", "3.1.3")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
.HasPostgresEnum(null, "item_type", new[] { "show", "movie", "collection" })
|
||||
.HasPostgresEnum(null, "status", new[] { "finished", "airing", "planned", "unknown" })
|
||||
.HasPostgresEnum(null, "stream_type", new[] { "unknown", "video", "audio", "subtitle", "font" })
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63)
|
||||
.HasAnnotation("ProductVersion", "5.0.3")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.CollectionDE", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Collection", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd()
|
||||
@ -51,23 +50,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Collections");
|
||||
|
||||
b.HasDiscriminator();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.CollectionLink", b =>
|
||||
{
|
||||
b.Property<int>("ParentID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("ChildID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("ParentID", "ChildID");
|
||||
|
||||
b.HasIndex("ChildID");
|
||||
|
||||
b.ToTable("CollectionLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Episode", b =>
|
||||
@ -89,9 +71,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.Property<string>("Path")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Poster")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime?>("ReleaseDate")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
@ -107,6 +86,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.Property<int>("ShowID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Thumb")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("text");
|
||||
|
||||
@ -120,7 +102,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.ToTable("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.GenreDE", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Genre", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd()
|
||||
@ -140,26 +122,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Genres");
|
||||
|
||||
b.HasDiscriminator();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.GenreLink", b =>
|
||||
{
|
||||
b.Property<int>("ParentID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("ChildID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("ParentID", "ChildID");
|
||||
|
||||
b.HasIndex("ChildID");
|
||||
|
||||
b.ToTable("GenreLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.LibraryDE", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Library", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd()
|
||||
@ -169,7 +134,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<IEnumerable<string>>("Paths")
|
||||
b.Property<string[]>("Paths")
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
@ -182,39 +147,81 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Libraries");
|
||||
|
||||
b.HasDiscriminator();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.LibraryLink", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Collection, Kyoo.Models.Show>", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int?>("CollectionID")
|
||||
b.Property<int>("FirstID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("LibraryID")
|
||||
b.Property<int>("SecondID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("ShowID")
|
||||
b.HasKey("FirstID", "SecondID");
|
||||
|
||||
b.HasIndex("SecondID");
|
||||
|
||||
b.ToTable("Link<Collection, Show>");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Collection>", b =>
|
||||
{
|
||||
b.Property<int>("FirstID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("ID");
|
||||
b.Property<int>("SecondID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasIndex("CollectionID");
|
||||
b.HasKey("FirstID", "SecondID");
|
||||
|
||||
b.HasIndex("ShowID");
|
||||
b.HasIndex("SecondID");
|
||||
|
||||
b.HasIndex("LibraryID", "CollectionID")
|
||||
.IsUnique();
|
||||
b.ToTable("Link<Library, Collection>");
|
||||
});
|
||||
|
||||
b.HasIndex("LibraryID", "ShowID")
|
||||
.IsUnique();
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.ProviderID>", b =>
|
||||
{
|
||||
b.Property<int>("FirstID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.ToTable("LibraryLinks");
|
||||
b.Property<int>("SecondID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("FirstID", "SecondID");
|
||||
|
||||
b.HasIndex("SecondID");
|
||||
|
||||
b.ToTable("Link<Library, ProviderID>");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Show>", b =>
|
||||
{
|
||||
b.Property<int>("FirstID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("SecondID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("FirstID", "SecondID");
|
||||
|
||||
b.HasIndex("SecondID");
|
||||
|
||||
b.ToTable("Link<Library, Show>");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Show, Kyoo.Models.Genre>", b =>
|
||||
{
|
||||
b.Property<int>("FirstID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("SecondID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("FirstID", "SecondID");
|
||||
|
||||
b.HasIndex("SecondID");
|
||||
|
||||
b.ToTable("Link<Show, Genre>");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.MetadataID", b =>
|
||||
@ -338,21 +345,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.ToTable("Providers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.ProviderLink", b =>
|
||||
{
|
||||
b.Property<int>("ParentID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("ChildID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("ParentID", "ChildID");
|
||||
|
||||
b.HasIndex("ChildID");
|
||||
|
||||
b.ToTable("ProviderLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Season", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
@ -386,14 +378,14 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.ToTable("Seasons");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.ShowDE", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Show", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<IEnumerable<string>>("Aliases")
|
||||
b.Property<string[]>("Aliases")
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.Property<string>("Backdrop")
|
||||
@ -444,8 +436,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.HasIndex("StudioID");
|
||||
|
||||
b.ToTable("Shows");
|
||||
|
||||
b.HasDiscriminator();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Studio", b =>
|
||||
@ -501,76 +491,130 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("TrackIndex")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("EpisodeID");
|
||||
b.HasIndex("EpisodeID", "Type", "Language", "TrackIndex", "IsForced")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Tracks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.CollectionLink", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.ShowDE", "Child")
|
||||
.WithMany("CollectionLinks")
|
||||
.HasForeignKey("ChildID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.CollectionDE", "Parent")
|
||||
.WithMany("Links")
|
||||
.HasForeignKey("ParentID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Episode", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.Season", "Season")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("SeasonID");
|
||||
|
||||
b.HasOne("Kyoo.Models.ShowDE", "Show")
|
||||
b.HasOne("Kyoo.Models.Show", "Show")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ShowID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Season");
|
||||
|
||||
b.Navigation("Show");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.GenreLink", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Collection, Kyoo.Models.Show>", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.GenreDE", "Child")
|
||||
.WithMany("Links")
|
||||
.HasForeignKey("ChildID")
|
||||
b.HasOne("Kyoo.Models.Collection", "First")
|
||||
.WithMany("ShowLinks")
|
||||
.HasForeignKey("FirstID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.ShowDE", "Parent")
|
||||
b.HasOne("Kyoo.Models.Show", "Second")
|
||||
.WithMany("CollectionLinks")
|
||||
.HasForeignKey("SecondID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("First");
|
||||
|
||||
b.Navigation("Second");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Collection>", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.Library", "First")
|
||||
.WithMany("CollectionLinks")
|
||||
.HasForeignKey("FirstID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.Collection", "Second")
|
||||
.WithMany("LibraryLinks")
|
||||
.HasForeignKey("SecondID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("First");
|
||||
|
||||
b.Navigation("Second");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.ProviderID>", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.Library", "First")
|
||||
.WithMany("ProviderLinks")
|
||||
.HasForeignKey("FirstID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.ProviderID", "Second")
|
||||
.WithMany("LibraryLinks")
|
||||
.HasForeignKey("SecondID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("First");
|
||||
|
||||
b.Navigation("Second");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Show>", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.Library", "First")
|
||||
.WithMany("ShowLinks")
|
||||
.HasForeignKey("FirstID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.Show", "Second")
|
||||
.WithMany("LibraryLinks")
|
||||
.HasForeignKey("SecondID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("First");
|
||||
|
||||
b.Navigation("Second");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Show, Kyoo.Models.Genre>", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.Show", "First")
|
||||
.WithMany("GenreLinks")
|
||||
.HasForeignKey("ParentID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.LibraryLink", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.CollectionDE", "Collection")
|
||||
.WithMany("LibraryLinks")
|
||||
.HasForeignKey("CollectionID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Kyoo.Models.LibraryDE", "Library")
|
||||
.WithMany("Links")
|
||||
.HasForeignKey("LibraryID")
|
||||
.HasForeignKey("FirstID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.ShowDE", "Show")
|
||||
.WithMany("LibraryLinks")
|
||||
.HasForeignKey("ShowID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
b.HasOne("Kyoo.Models.Genre", "Second")
|
||||
.WithMany("ShowLinks")
|
||||
.HasForeignKey("SecondID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("First");
|
||||
|
||||
b.Navigation("Second");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.MetadataID", b =>
|
||||
@ -586,7 +630,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Kyoo.Models.ProviderID", "Provider")
|
||||
.WithMany()
|
||||
.WithMany("MetadataLinks")
|
||||
.HasForeignKey("ProviderID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
@ -596,10 +640,20 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.HasForeignKey("SeasonID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Kyoo.Models.ShowDE", "Show")
|
||||
b.HasOne("Kyoo.Models.Show", "Show")
|
||||
.WithMany("ExternalIDs")
|
||||
.HasForeignKey("ShowID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("Episode");
|
||||
|
||||
b.Navigation("People");
|
||||
|
||||
b.Navigation("Provider");
|
||||
|
||||
b.Navigation("Season");
|
||||
|
||||
b.Navigation("Show");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.PeopleRole", b =>
|
||||
@ -610,42 +664,35 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.ShowDE", "Show")
|
||||
b.HasOne("Kyoo.Models.Show", "Show")
|
||||
.WithMany("People")
|
||||
.HasForeignKey("ShowID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.ProviderLink", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.ProviderID", "Child")
|
||||
.WithMany()
|
||||
.HasForeignKey("ChildID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
b.Navigation("People");
|
||||
|
||||
b.HasOne("Kyoo.Models.LibraryDE", "Parent")
|
||||
.WithMany("ProviderLinks")
|
||||
.HasForeignKey("ParentID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
b.Navigation("Show");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Season", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.ShowDE", "Show")
|
||||
b.HasOne("Kyoo.Models.Show", "Show")
|
||||
.WithMany("Seasons")
|
||||
.HasForeignKey("ShowID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Show");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.ShowDE", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Show", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.Studio", "Studio")
|
||||
.WithMany()
|
||||
.WithMany("Shows")
|
||||
.HasForeignKey("StudioID");
|
||||
|
||||
b.Navigation("Studio");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Track", b =>
|
||||
@ -655,6 +702,79 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.HasForeignKey("EpisodeID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Episode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Collection", b =>
|
||||
{
|
||||
b.Navigation("LibraryLinks");
|
||||
|
||||
b.Navigation("ShowLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Episode", b =>
|
||||
{
|
||||
b.Navigation("ExternalIDs");
|
||||
|
||||
b.Navigation("Tracks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Genre", b =>
|
||||
{
|
||||
b.Navigation("ShowLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Library", b =>
|
||||
{
|
||||
b.Navigation("CollectionLinks");
|
||||
|
||||
b.Navigation("ProviderLinks");
|
||||
|
||||
b.Navigation("ShowLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.People", b =>
|
||||
{
|
||||
b.Navigation("ExternalIDs");
|
||||
|
||||
b.Navigation("Roles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.ProviderID", b =>
|
||||
{
|
||||
b.Navigation("LibraryLinks");
|
||||
|
||||
b.Navigation("MetadataLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Season", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
|
||||
b.Navigation("ExternalIDs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Show", b =>
|
||||
{
|
||||
b.Navigation("CollectionLinks");
|
||||
|
||||
b.Navigation("Episodes");
|
||||
|
||||
b.Navigation("ExternalIDs");
|
||||
|
||||
b.Navigation("GenreLinks");
|
||||
|
||||
b.Navigation("LibraryLinks");
|
||||
|
||||
b.Navigation("People");
|
||||
|
||||
b.Navigation("Seasons");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Studio", b =>
|
||||
{
|
||||
b.Navigation("Shows");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
@ -17,12 +17,12 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
name: "Collections",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(nullable: false)
|
||||
ID = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Slug = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(nullable: true),
|
||||
Poster = table.Column<string>(nullable: true),
|
||||
Overview = table.Column<string>(nullable: true)
|
||||
Slug = table.Column<string>(type: "text", nullable: false),
|
||||
Name = table.Column<string>(type: "text", nullable: true),
|
||||
Poster = table.Column<string>(type: "text", nullable: true),
|
||||
Overview = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -33,10 +33,10 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
name: "Genres",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(nullable: false)
|
||||
ID = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Slug = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(nullable: true)
|
||||
Slug = table.Column<string>(type: "text", nullable: false),
|
||||
Name = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -47,10 +47,10 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
name: "Libraries",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(nullable: false)
|
||||
ID = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Slug = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(nullable: true),
|
||||
Slug = table.Column<string>(type: "text", nullable: false),
|
||||
Name = table.Column<string>(type: "text", nullable: true),
|
||||
Paths = table.Column<string[]>(type: "text[]", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
@ -62,11 +62,11 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
name: "People",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(nullable: false)
|
||||
ID = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Slug = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(nullable: true),
|
||||
Poster = table.Column<string>(nullable: true)
|
||||
Slug = table.Column<string>(type: "text", nullable: false),
|
||||
Name = table.Column<string>(type: "text", nullable: true),
|
||||
Poster = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -77,11 +77,11 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
name: "Providers",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(nullable: false)
|
||||
ID = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Slug = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(nullable: true),
|
||||
Logo = table.Column<string>(nullable: true)
|
||||
Slug = table.Column<string>(type: "text", nullable: false),
|
||||
Name = table.Column<string>(type: "text", nullable: true),
|
||||
Logo = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -92,10 +92,10 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
name: "Studios",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(nullable: false)
|
||||
ID = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Slug = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(nullable: true)
|
||||
Slug = table.Column<string>(type: "text", nullable: false),
|
||||
Name = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -103,49 +103,73 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ProviderLinks",
|
||||
name: "Link<Library, Collection>",
|
||||
columns: table => new
|
||||
{
|
||||
ParentID = table.Column<int>(nullable: false),
|
||||
ChildID = table.Column<int>(nullable: false)
|
||||
FirstID = table.Column<int>(type: "integer", nullable: false),
|
||||
SecondID = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ProviderLinks", x => new { x.ParentID, x.ChildID });
|
||||
table.PrimaryKey("PK_Link<Library, Collection>", x => new { x.FirstID, x.SecondID });
|
||||
table.ForeignKey(
|
||||
name: "FK_ProviderLinks_Providers_ChildID",
|
||||
column: x => x.ChildID,
|
||||
principalTable: "Providers",
|
||||
name: "FK_Link<Library, Collection>_Collections_SecondID",
|
||||
column: x => x.SecondID,
|
||||
principalTable: "Collections",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ProviderLinks_Libraries_ParentID",
|
||||
column: x => x.ParentID,
|
||||
name: "FK_Link<Library, Collection>_Libraries_FirstID",
|
||||
column: x => x.FirstID,
|
||||
principalTable: "Libraries",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Link<Library, ProviderID>",
|
||||
columns: table => new
|
||||
{
|
||||
FirstID = table.Column<int>(type: "integer", nullable: false),
|
||||
SecondID = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Link<Library, ProviderID>", x => new { x.FirstID, x.SecondID });
|
||||
table.ForeignKey(
|
||||
name: "FK_Link<Library, ProviderID>_Libraries_FirstID",
|
||||
column: x => x.FirstID,
|
||||
principalTable: "Libraries",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Link<Library, ProviderID>_Providers_SecondID",
|
||||
column: x => x.SecondID,
|
||||
principalTable: "Providers",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Shows",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(nullable: false)
|
||||
ID = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Slug = table.Column<string>(nullable: false),
|
||||
Title = table.Column<string>(nullable: true),
|
||||
Slug = table.Column<string>(type: "text", nullable: false),
|
||||
Title = table.Column<string>(type: "text", nullable: true),
|
||||
Aliases = table.Column<string[]>(type: "text[]", nullable: true),
|
||||
Path = table.Column<string>(nullable: true),
|
||||
Overview = table.Column<string>(nullable: true),
|
||||
Status = table.Column<int>(nullable: true),
|
||||
TrailerUrl = table.Column<string>(nullable: true),
|
||||
StartYear = table.Column<int>(nullable: true),
|
||||
EndYear = table.Column<int>(nullable: true),
|
||||
Poster = table.Column<string>(nullable: true),
|
||||
Logo = table.Column<string>(nullable: true),
|
||||
Backdrop = table.Column<string>(nullable: true),
|
||||
IsMovie = table.Column<bool>(nullable: false),
|
||||
StudioID = table.Column<int>(nullable: true)
|
||||
Path = table.Column<string>(type: "text", nullable: true),
|
||||
Overview = table.Column<string>(type: "text", nullable: true),
|
||||
Status = table.Column<int>(type: "integer", nullable: true),
|
||||
TrailerUrl = table.Column<string>(type: "text", nullable: true),
|
||||
StartYear = table.Column<int>(type: "integer", nullable: true),
|
||||
EndYear = table.Column<int>(type: "integer", nullable: true),
|
||||
Poster = table.Column<string>(type: "text", nullable: true),
|
||||
Logo = table.Column<string>(type: "text", nullable: true),
|
||||
Backdrop = table.Column<string>(type: "text", nullable: true),
|
||||
IsMovie = table.Column<bool>(type: "boolean", nullable: false),
|
||||
StudioID = table.Column<int>(type: "integer", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -159,81 +183,72 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "CollectionLinks",
|
||||
name: "Link<Collection, Show>",
|
||||
columns: table => new
|
||||
{
|
||||
ParentID = table.Column<int>(nullable: false),
|
||||
ChildID = table.Column<int>(nullable: false)
|
||||
FirstID = table.Column<int>(type: "integer", nullable: false),
|
||||
SecondID = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_CollectionLinks", x => new { x.ParentID, x.ChildID });
|
||||
table.PrimaryKey("PK_Link<Collection, Show>", x => new { x.FirstID, x.SecondID });
|
||||
table.ForeignKey(
|
||||
name: "FK_CollectionLinks_Shows_ChildID",
|
||||
column: x => x.ChildID,
|
||||
principalTable: "Shows",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_CollectionLinks_Collections_ParentID",
|
||||
column: x => x.ParentID,
|
||||
name: "FK_Link<Collection, Show>_Collections_FirstID",
|
||||
column: x => x.FirstID,
|
||||
principalTable: "Collections",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "GenreLinks",
|
||||
columns: table => new
|
||||
{
|
||||
ParentID = table.Column<int>(nullable: false),
|
||||
ChildID = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_GenreLinks", x => new { x.ParentID, x.ChildID });
|
||||
table.ForeignKey(
|
||||
name: "FK_GenreLinks_Genres_ChildID",
|
||||
column: x => x.ChildID,
|
||||
principalTable: "Genres",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_GenreLinks_Shows_ParentID",
|
||||
column: x => x.ParentID,
|
||||
name: "FK_Link<Collection, Show>_Shows_SecondID",
|
||||
column: x => x.SecondID,
|
||||
principalTable: "Shows",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "LibraryLinks",
|
||||
name: "Link<Library, Show>",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
LibraryID = table.Column<int>(nullable: false),
|
||||
ShowID = table.Column<int>(nullable: true),
|
||||
CollectionID = table.Column<int>(nullable: true)
|
||||
FirstID = table.Column<int>(type: "integer", nullable: false),
|
||||
SecondID = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_LibraryLinks", x => x.ID);
|
||||
table.PrimaryKey("PK_Link<Library, Show>", x => new { x.FirstID, x.SecondID });
|
||||
table.ForeignKey(
|
||||
name: "FK_LibraryLinks_Collections_CollectionID",
|
||||
column: x => x.CollectionID,
|
||||
principalTable: "Collections",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_LibraryLinks_Libraries_LibraryID",
|
||||
column: x => x.LibraryID,
|
||||
name: "FK_Link<Library, Show>_Libraries_FirstID",
|
||||
column: x => x.FirstID,
|
||||
principalTable: "Libraries",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_LibraryLinks_Shows_ShowID",
|
||||
column: x => x.ShowID,
|
||||
name: "FK_Link<Library, Show>_Shows_SecondID",
|
||||
column: x => x.SecondID,
|
||||
principalTable: "Shows",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Link<Show, Genre>",
|
||||
columns: table => new
|
||||
{
|
||||
FirstID = table.Column<int>(type: "integer", nullable: false),
|
||||
SecondID = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Link<Show, Genre>", x => new { x.FirstID, x.SecondID });
|
||||
table.ForeignKey(
|
||||
name: "FK_Link<Show, Genre>_Genres_SecondID",
|
||||
column: x => x.SecondID,
|
||||
principalTable: "Genres",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Link<Show, Genre>_Shows_FirstID",
|
||||
column: x => x.FirstID,
|
||||
principalTable: "Shows",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
@ -243,12 +258,12 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
name: "PeopleRoles",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(nullable: false)
|
||||
ID = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
PeopleID = table.Column<int>(nullable: false),
|
||||
ShowID = table.Column<int>(nullable: false),
|
||||
Role = table.Column<string>(nullable: true),
|
||||
Type = table.Column<string>(nullable: true)
|
||||
PeopleID = table.Column<int>(type: "integer", nullable: false),
|
||||
ShowID = table.Column<int>(type: "integer", nullable: false),
|
||||
Role = table.Column<string>(type: "text", nullable: true),
|
||||
Type = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -271,14 +286,14 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
name: "Seasons",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(nullable: false)
|
||||
ID = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
ShowID = table.Column<int>(nullable: false),
|
||||
SeasonNumber = table.Column<int>(nullable: false),
|
||||
Title = table.Column<string>(nullable: true),
|
||||
Overview = table.Column<string>(nullable: true),
|
||||
Year = table.Column<int>(nullable: true),
|
||||
Poster = table.Column<string>(nullable: true)
|
||||
ShowID = table.Column<int>(type: "integer", nullable: false),
|
||||
SeasonNumber = table.Column<int>(type: "integer", nullable: false),
|
||||
Title = table.Column<string>(type: "text", nullable: true),
|
||||
Overview = table.Column<string>(type: "text", nullable: true),
|
||||
Year = table.Column<int>(type: "integer", nullable: true),
|
||||
Poster = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -295,19 +310,19 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
name: "Episodes",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(nullable: false)
|
||||
ID = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
ShowID = table.Column<int>(nullable: false),
|
||||
SeasonID = table.Column<int>(nullable: true),
|
||||
SeasonNumber = table.Column<int>(nullable: false),
|
||||
EpisodeNumber = table.Column<int>(nullable: false),
|
||||
AbsoluteNumber = table.Column<int>(nullable: false),
|
||||
Path = table.Column<string>(nullable: true),
|
||||
Title = table.Column<string>(nullable: true),
|
||||
Overview = table.Column<string>(nullable: true),
|
||||
ReleaseDate = table.Column<DateTime>(nullable: true),
|
||||
Runtime = table.Column<int>(nullable: false),
|
||||
Poster = table.Column<string>(nullable: true)
|
||||
ShowID = table.Column<int>(type: "integer", nullable: false),
|
||||
SeasonID = table.Column<int>(type: "integer", nullable: true),
|
||||
SeasonNumber = table.Column<int>(type: "integer", nullable: false),
|
||||
EpisodeNumber = table.Column<int>(type: "integer", nullable: false),
|
||||
AbsoluteNumber = table.Column<int>(type: "integer", nullable: false),
|
||||
Path = table.Column<string>(type: "text", nullable: true),
|
||||
Thumb = table.Column<string>(type: "text", nullable: true),
|
||||
Title = table.Column<string>(type: "text", nullable: true),
|
||||
Overview = table.Column<string>(type: "text", nullable: true),
|
||||
ReleaseDate = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||
Runtime = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -330,15 +345,15 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
name: "MetadataIds",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(nullable: false)
|
||||
ID = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
ProviderID = table.Column<int>(nullable: false),
|
||||
ShowID = table.Column<int>(nullable: true),
|
||||
EpisodeID = table.Column<int>(nullable: true),
|
||||
SeasonID = table.Column<int>(nullable: true),
|
||||
PeopleID = table.Column<int>(nullable: true),
|
||||
DataID = table.Column<string>(nullable: true),
|
||||
Link = table.Column<string>(nullable: true)
|
||||
ProviderID = table.Column<int>(type: "integer", nullable: false),
|
||||
ShowID = table.Column<int>(type: "integer", nullable: true),
|
||||
EpisodeID = table.Column<int>(type: "integer", nullable: true),
|
||||
SeasonID = table.Column<int>(type: "integer", nullable: true),
|
||||
PeopleID = table.Column<int>(type: "integer", nullable: true),
|
||||
DataID = table.Column<string>(type: "text", nullable: true),
|
||||
Link = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -379,17 +394,18 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
name: "Tracks",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(nullable: false)
|
||||
ID = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Title = table.Column<string>(nullable: true),
|
||||
Language = table.Column<string>(nullable: true),
|
||||
Codec = table.Column<string>(nullable: true),
|
||||
Path = table.Column<string>(nullable: true),
|
||||
Type = table.Column<int>(nullable: false),
|
||||
EpisodeID = table.Column<int>(nullable: false),
|
||||
IsDefault = table.Column<bool>(nullable: false),
|
||||
IsForced = table.Column<bool>(nullable: false),
|
||||
IsExternal = table.Column<bool>(nullable: false)
|
||||
EpisodeID = table.Column<int>(type: "integer", nullable: false),
|
||||
TrackIndex = table.Column<int>(type: "integer", nullable: false),
|
||||
IsDefault = table.Column<bool>(type: "boolean", nullable: false),
|
||||
IsForced = table.Column<bool>(type: "boolean", nullable: false),
|
||||
IsExternal = table.Column<bool>(type: "boolean", nullable: false),
|
||||
Title = table.Column<string>(type: "text", nullable: true),
|
||||
Language = table.Column<string>(type: "text", nullable: true),
|
||||
Codec = table.Column<string>(type: "text", nullable: true),
|
||||
Path = table.Column<string>(type: "text", nullable: true),
|
||||
Type = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -402,11 +418,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_CollectionLinks_ChildID",
|
||||
table: "CollectionLinks",
|
||||
column: "ChildID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Collections_Slug",
|
||||
table: "Collections",
|
||||
@ -424,11 +435,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
columns: new[] { "ShowID", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_GenreLinks_ChildID",
|
||||
table: "GenreLinks",
|
||||
column: "ChildID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Genres_Slug",
|
||||
table: "Genres",
|
||||
@ -442,26 +448,29 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_LibraryLinks_CollectionID",
|
||||
table: "LibraryLinks",
|
||||
column: "CollectionID");
|
||||
name: "IX_Link<Collection, Show>_SecondID",
|
||||
table: "Link<Collection, Show>",
|
||||
column: "SecondID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_LibraryLinks_ShowID",
|
||||
table: "LibraryLinks",
|
||||
column: "ShowID");
|
||||
name: "IX_Link<Library, Collection>_SecondID",
|
||||
table: "Link<Library, Collection>",
|
||||
column: "SecondID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_LibraryLinks_LibraryID_CollectionID",
|
||||
table: "LibraryLinks",
|
||||
columns: new[] { "LibraryID", "CollectionID" },
|
||||
unique: true);
|
||||
name: "IX_Link<Library, ProviderID>_SecondID",
|
||||
table: "Link<Library, ProviderID>",
|
||||
column: "SecondID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_LibraryLinks_LibraryID_ShowID",
|
||||
table: "LibraryLinks",
|
||||
columns: new[] { "LibraryID", "ShowID" },
|
||||
unique: true);
|
||||
name: "IX_Link<Library, Show>_SecondID",
|
||||
table: "Link<Library, Show>",
|
||||
column: "SecondID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Link<Show, Genre>_SecondID",
|
||||
table: "Link<Show, Genre>",
|
||||
column: "SecondID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MetadataIds_EpisodeID",
|
||||
@ -504,11 +513,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
table: "PeopleRoles",
|
||||
column: "ShowID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProviderLinks_ChildID",
|
||||
table: "ProviderLinks",
|
||||
column: "ChildID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Providers_Slug",
|
||||
table: "Providers",
|
||||
@ -539,21 +543,28 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Tracks_EpisodeID",
|
||||
name: "IX_Tracks_EpisodeID_Type_Language_TrackIndex_IsForced",
|
||||
table: "Tracks",
|
||||
column: "EpisodeID");
|
||||
columns: new[] { "EpisodeID", "Type", "Language", "TrackIndex", "IsForced" },
|
||||
unique: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "CollectionLinks");
|
||||
name: "Link<Collection, Show>");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "GenreLinks");
|
||||
name: "Link<Library, Collection>");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "LibraryLinks");
|
||||
name: "Link<Library, ProviderID>");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Link<Library, Show>");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Link<Show, Genre>");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "MetadataIds");
|
||||
@ -561,26 +572,23 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
migrationBuilder.DropTable(
|
||||
name: "PeopleRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ProviderLinks");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Tracks");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Genres");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Collections");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "People");
|
||||
name: "Libraries");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Genres");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Providers");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Libraries");
|
||||
name: "People");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Episodes");
|
||||
@ -1,6 +1,5 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Kyoo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
@ -16,14 +15,14 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:Enum:item_type", "show,movie,collection")
|
||||
.HasAnnotation("Npgsql:Enum:status", "finished,airing,planned,unknown")
|
||||
.HasAnnotation("Npgsql:Enum:stream_type", "unknown,video,audio,subtitle,font")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
|
||||
.HasAnnotation("ProductVersion", "3.1.3")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
.HasPostgresEnum(null, "item_type", new[] { "show", "movie", "collection" })
|
||||
.HasPostgresEnum(null, "status", new[] { "finished", "airing", "planned", "unknown" })
|
||||
.HasPostgresEnum(null, "stream_type", new[] { "unknown", "video", "audio", "subtitle", "font" })
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63)
|
||||
.HasAnnotation("ProductVersion", "5.0.3")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.CollectionDE", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Collection", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd()
|
||||
@ -49,23 +48,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Collections");
|
||||
|
||||
b.HasDiscriminator();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.CollectionLink", b =>
|
||||
{
|
||||
b.Property<int>("ParentID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("ChildID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("ParentID", "ChildID");
|
||||
|
||||
b.HasIndex("ChildID");
|
||||
|
||||
b.ToTable("CollectionLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Episode", b =>
|
||||
@ -87,9 +69,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.Property<string>("Path")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Poster")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime?>("ReleaseDate")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
@ -105,6 +84,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.Property<int>("ShowID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Thumb")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("text");
|
||||
|
||||
@ -118,7 +100,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.ToTable("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.GenreDE", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Genre", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd()
|
||||
@ -138,26 +120,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Genres");
|
||||
|
||||
b.HasDiscriminator();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.GenreLink", b =>
|
||||
{
|
||||
b.Property<int>("ParentID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("ChildID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("ParentID", "ChildID");
|
||||
|
||||
b.HasIndex("ChildID");
|
||||
|
||||
b.ToTable("GenreLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.LibraryDE", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Library", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd()
|
||||
@ -167,7 +132,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<IEnumerable<string>>("Paths")
|
||||
b.Property<string[]>("Paths")
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
@ -180,39 +145,81 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Libraries");
|
||||
|
||||
b.HasDiscriminator();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.LibraryLink", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Collection, Kyoo.Models.Show>", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int?>("CollectionID")
|
||||
b.Property<int>("FirstID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("LibraryID")
|
||||
b.Property<int>("SecondID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("ShowID")
|
||||
b.HasKey("FirstID", "SecondID");
|
||||
|
||||
b.HasIndex("SecondID");
|
||||
|
||||
b.ToTable("Link<Collection, Show>");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Collection>", b =>
|
||||
{
|
||||
b.Property<int>("FirstID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("ID");
|
||||
b.Property<int>("SecondID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasIndex("CollectionID");
|
||||
b.HasKey("FirstID", "SecondID");
|
||||
|
||||
b.HasIndex("ShowID");
|
||||
b.HasIndex("SecondID");
|
||||
|
||||
b.HasIndex("LibraryID", "CollectionID")
|
||||
.IsUnique();
|
||||
b.ToTable("Link<Library, Collection>");
|
||||
});
|
||||
|
||||
b.HasIndex("LibraryID", "ShowID")
|
||||
.IsUnique();
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.ProviderID>", b =>
|
||||
{
|
||||
b.Property<int>("FirstID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.ToTable("LibraryLinks");
|
||||
b.Property<int>("SecondID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("FirstID", "SecondID");
|
||||
|
||||
b.HasIndex("SecondID");
|
||||
|
||||
b.ToTable("Link<Library, ProviderID>");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Show>", b =>
|
||||
{
|
||||
b.Property<int>("FirstID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("SecondID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("FirstID", "SecondID");
|
||||
|
||||
b.HasIndex("SecondID");
|
||||
|
||||
b.ToTable("Link<Library, Show>");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Show, Kyoo.Models.Genre>", b =>
|
||||
{
|
||||
b.Property<int>("FirstID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("SecondID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("FirstID", "SecondID");
|
||||
|
||||
b.HasIndex("SecondID");
|
||||
|
||||
b.ToTable("Link<Show, Genre>");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.MetadataID", b =>
|
||||
@ -336,21 +343,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.ToTable("Providers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.ProviderLink", b =>
|
||||
{
|
||||
b.Property<int>("ParentID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("ChildID")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("ParentID", "ChildID");
|
||||
|
||||
b.HasIndex("ChildID");
|
||||
|
||||
b.ToTable("ProviderLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Season", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
@ -384,14 +376,14 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.ToTable("Seasons");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.ShowDE", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Show", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<IEnumerable<string>>("Aliases")
|
||||
b.Property<string[]>("Aliases")
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.Property<string>("Backdrop")
|
||||
@ -442,8 +434,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.HasIndex("StudioID");
|
||||
|
||||
b.ToTable("Shows");
|
||||
|
||||
b.HasDiscriminator();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Studio", b =>
|
||||
@ -499,76 +489,130 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("TrackIndex")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("EpisodeID");
|
||||
b.HasIndex("EpisodeID", "Type", "Language", "TrackIndex", "IsForced")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Tracks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.CollectionLink", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.ShowDE", "Child")
|
||||
.WithMany("CollectionLinks")
|
||||
.HasForeignKey("ChildID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.CollectionDE", "Parent")
|
||||
.WithMany("Links")
|
||||
.HasForeignKey("ParentID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Episode", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.Season", "Season")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("SeasonID");
|
||||
|
||||
b.HasOne("Kyoo.Models.ShowDE", "Show")
|
||||
b.HasOne("Kyoo.Models.Show", "Show")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ShowID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Season");
|
||||
|
||||
b.Navigation("Show");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.GenreLink", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Collection, Kyoo.Models.Show>", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.GenreDE", "Child")
|
||||
.WithMany("Links")
|
||||
.HasForeignKey("ChildID")
|
||||
b.HasOne("Kyoo.Models.Collection", "First")
|
||||
.WithMany("ShowLinks")
|
||||
.HasForeignKey("FirstID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.ShowDE", "Parent")
|
||||
b.HasOne("Kyoo.Models.Show", "Second")
|
||||
.WithMany("CollectionLinks")
|
||||
.HasForeignKey("SecondID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("First");
|
||||
|
||||
b.Navigation("Second");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Collection>", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.Library", "First")
|
||||
.WithMany("CollectionLinks")
|
||||
.HasForeignKey("FirstID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.Collection", "Second")
|
||||
.WithMany("LibraryLinks")
|
||||
.HasForeignKey("SecondID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("First");
|
||||
|
||||
b.Navigation("Second");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.ProviderID>", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.Library", "First")
|
||||
.WithMany("ProviderLinks")
|
||||
.HasForeignKey("FirstID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.ProviderID", "Second")
|
||||
.WithMany("LibraryLinks")
|
||||
.HasForeignKey("SecondID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("First");
|
||||
|
||||
b.Navigation("Second");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Show>", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.Library", "First")
|
||||
.WithMany("ShowLinks")
|
||||
.HasForeignKey("FirstID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.Show", "Second")
|
||||
.WithMany("LibraryLinks")
|
||||
.HasForeignKey("SecondID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("First");
|
||||
|
||||
b.Navigation("Second");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Show, Kyoo.Models.Genre>", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.Show", "First")
|
||||
.WithMany("GenreLinks")
|
||||
.HasForeignKey("ParentID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.LibraryLink", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.CollectionDE", "Collection")
|
||||
.WithMany("LibraryLinks")
|
||||
.HasForeignKey("CollectionID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Kyoo.Models.LibraryDE", "Library")
|
||||
.WithMany("Links")
|
||||
.HasForeignKey("LibraryID")
|
||||
.HasForeignKey("FirstID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.ShowDE", "Show")
|
||||
.WithMany("LibraryLinks")
|
||||
.HasForeignKey("ShowID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
b.HasOne("Kyoo.Models.Genre", "Second")
|
||||
.WithMany("ShowLinks")
|
||||
.HasForeignKey("SecondID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("First");
|
||||
|
||||
b.Navigation("Second");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.MetadataID", b =>
|
||||
@ -584,7 +628,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Kyoo.Models.ProviderID", "Provider")
|
||||
.WithMany()
|
||||
.WithMany("MetadataLinks")
|
||||
.HasForeignKey("ProviderID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
@ -594,10 +638,20 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.HasForeignKey("SeasonID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Kyoo.Models.ShowDE", "Show")
|
||||
b.HasOne("Kyoo.Models.Show", "Show")
|
||||
.WithMany("ExternalIDs")
|
||||
.HasForeignKey("ShowID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("Episode");
|
||||
|
||||
b.Navigation("People");
|
||||
|
||||
b.Navigation("Provider");
|
||||
|
||||
b.Navigation("Season");
|
||||
|
||||
b.Navigation("Show");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.PeopleRole", b =>
|
||||
@ -608,42 +662,35 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.ShowDE", "Show")
|
||||
b.HasOne("Kyoo.Models.Show", "Show")
|
||||
.WithMany("People")
|
||||
.HasForeignKey("ShowID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.ProviderLink", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.ProviderID", "Child")
|
||||
.WithMany()
|
||||
.HasForeignKey("ChildID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
b.Navigation("People");
|
||||
|
||||
b.HasOne("Kyoo.Models.LibraryDE", "Parent")
|
||||
.WithMany("ProviderLinks")
|
||||
.HasForeignKey("ParentID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
b.Navigation("Show");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Season", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.ShowDE", "Show")
|
||||
b.HasOne("Kyoo.Models.Show", "Show")
|
||||
.WithMany("Seasons")
|
||||
.HasForeignKey("ShowID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Show");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.ShowDE", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Show", b =>
|
||||
{
|
||||
b.HasOne("Kyoo.Models.Studio", "Studio")
|
||||
.WithMany()
|
||||
.WithMany("Shows")
|
||||
.HasForeignKey("StudioID");
|
||||
|
||||
b.Navigation("Studio");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Track", b =>
|
||||
@ -653,6 +700,79 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.HasForeignKey("EpisodeID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Episode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Collection", b =>
|
||||
{
|
||||
b.Navigation("LibraryLinks");
|
||||
|
||||
b.Navigation("ShowLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Episode", b =>
|
||||
{
|
||||
b.Navigation("ExternalIDs");
|
||||
|
||||
b.Navigation("Tracks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Genre", b =>
|
||||
{
|
||||
b.Navigation("ShowLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Library", b =>
|
||||
{
|
||||
b.Navigation("CollectionLinks");
|
||||
|
||||
b.Navigation("ProviderLinks");
|
||||
|
||||
b.Navigation("ShowLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.People", b =>
|
||||
{
|
||||
b.Navigation("ExternalIDs");
|
||||
|
||||
b.Navigation("Roles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.ProviderID", b =>
|
||||
{
|
||||
b.Navigation("LibraryLinks");
|
||||
|
||||
b.Navigation("MetadataLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Season", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
|
||||
b.Navigation("ExternalIDs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Show", b =>
|
||||
{
|
||||
b.Navigation("CollectionLinks");
|
||||
|
||||
b.Navigation("Episodes");
|
||||
|
||||
b.Navigation("ExternalIDs");
|
||||
|
||||
b.Navigation("GenreLinks");
|
||||
|
||||
b.Navigation("LibraryLinks");
|
||||
|
||||
b.Navigation("People");
|
||||
|
||||
b.Navigation("Seasons");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.Studio", b =>
|
||||
{
|
||||
b.Navigation("Shows");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using IdentityServer4.Models;
|
||||
|
||||
namespace Kyoo
|
||||
{
|
||||
public class IdentityContext
|
||||
public static class IdentityContext
|
||||
{
|
||||
public static IEnumerable<IdentityResource> GetIdentityResources()
|
||||
{
|
||||
@ -19,7 +20,7 @@ namespace Kyoo
|
||||
{
|
||||
return new List<Client>
|
||||
{
|
||||
new Client
|
||||
new()
|
||||
{
|
||||
ClientId = "kyoo.webapp",
|
||||
|
||||
@ -38,6 +39,38 @@ namespace Kyoo
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<ApiScope> GetScopes()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new ApiScope
|
||||
{
|
||||
Name = "kyoo.read",
|
||||
DisplayName = "Read only access to the API.",
|
||||
},
|
||||
new ApiScope
|
||||
{
|
||||
Name = "kyoo.write",
|
||||
DisplayName = "Read and write access to the public API"
|
||||
},
|
||||
new ApiScope
|
||||
{
|
||||
Name = "kyoo.play",
|
||||
DisplayName = "Allow playback of movies and episodes."
|
||||
},
|
||||
new ApiScope
|
||||
{
|
||||
Name = "kyoo.download",
|
||||
DisplayName = "Allow downloading of episodes and movies from kyoo."
|
||||
},
|
||||
new ApiScope
|
||||
{
|
||||
Name = "kyoo.admin",
|
||||
DisplayName = "Full access to the admin's API and the public API."
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<ApiResource> GetApis()
|
||||
{
|
||||
@ -46,34 +79,7 @@ namespace Kyoo
|
||||
new ApiResource
|
||||
{
|
||||
Name = "Kyoo",
|
||||
Scopes =
|
||||
{
|
||||
new Scope
|
||||
{
|
||||
Name = "kyoo.read",
|
||||
DisplayName = "Read only access to the API.",
|
||||
},
|
||||
new Scope
|
||||
{
|
||||
Name = "kyoo.write",
|
||||
DisplayName = "Read and write access to the public API"
|
||||
},
|
||||
new Scope
|
||||
{
|
||||
Name = "kyoo.play",
|
||||
DisplayName = "Allow playback of movies and episodes."
|
||||
},
|
||||
new Scope
|
||||
{
|
||||
Name = "kyoo.download",
|
||||
DisplayName = "Allow downloading of episodes and movies from kyoo."
|
||||
},
|
||||
new Scope
|
||||
{
|
||||
Name = "kyoo.admin",
|
||||
DisplayName = "Full access to the admin's API and the public API."
|
||||
}
|
||||
}
|
||||
Scopes = GetScopes().Select(x => x.Name).ToArray()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Kyoo
|
||||
{
|
||||
// The configuration's database is named ConfigurationDbContext.
|
||||
public class IdentityDatabase : IdentityDbContext<User>, IPersistedGrantDbContext
|
||||
{
|
||||
private readonly IOptions<OperationalStoreOptions> _operationalStoreOptions;
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class CollectionLink : IResourceLink<Collection, Show>
|
||||
{
|
||||
public int ParentID { get; set; }
|
||||
public virtual Collection Parent { get; set; }
|
||||
public int ChildID { get; set; }
|
||||
public virtual Show Child { get; set; }
|
||||
|
||||
public CollectionLink() { }
|
||||
|
||||
public CollectionLink(Collection parent, Show child)
|
||||
{
|
||||
Parent = parent;
|
||||
ParentID = parent.ID;
|
||||
Child = child;
|
||||
ChildID = child.ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class GenreLink : IResourceLink<Show, Genre>
|
||||
{
|
||||
public int ParentID { get; set; }
|
||||
public virtual Show Parent { get; set; }
|
||||
public int ChildID { get; set; }
|
||||
public virtual Genre Child { get; set; }
|
||||
|
||||
public GenreLink() {}
|
||||
|
||||
public GenreLink(Show parent, Genre child)
|
||||
{
|
||||
Parent = parent;
|
||||
Child = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class LibraryLink
|
||||
{
|
||||
public int ID { get; set; }
|
||||
public int LibraryID { get; set; }
|
||||
public virtual Library Library { get; set; }
|
||||
public int? ShowID { get; set; }
|
||||
public virtual Show Show { get; set; }
|
||||
public int? CollectionID { get; set; }
|
||||
public virtual Collection Collection { get; set; }
|
||||
|
||||
public LibraryLink() { }
|
||||
|
||||
public LibraryLink(Library library, Show show)
|
||||
{
|
||||
Library = library;
|
||||
Show = show;
|
||||
}
|
||||
|
||||
public LibraryLink(Library library, Collection collection)
|
||||
{
|
||||
Library = library;
|
||||
Collection = collection;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class ProviderLink : IResourceLink<Library, ProviderID>
|
||||
{
|
||||
[JsonIgnore] public int ParentID { get; set; }
|
||||
[JsonIgnore] public virtual Library Parent { get; set; }
|
||||
[JsonIgnore] public int ChildID { get; set; }
|
||||
[JsonIgnore] public virtual ProviderID Child { get; set; }
|
||||
|
||||
public ProviderLink() { }
|
||||
|
||||
public ProviderLink(ProviderID child, Library parent)
|
||||
{
|
||||
Child = child;
|
||||
Parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class CollectionDE : Collection
|
||||
{
|
||||
[JsonIgnore] [NotMergable] public virtual ICollection<CollectionLink> Links { get; set; }
|
||||
[ExpressionRewrite(nameof(Links), nameof(CollectionLink.Child))]
|
||||
public override IEnumerable<Show> Shows
|
||||
{
|
||||
get => Links?.Select(x => x.Child);
|
||||
set => Links = value?.Select(x => new CollectionLink(this, x)).ToList();
|
||||
}
|
||||
|
||||
[JsonIgnore] [NotMergable] public virtual ICollection<LibraryLink> LibraryLinks { get; set; }
|
||||
|
||||
[ExpressionRewrite(nameof(LibraryLinks), nameof(GenreLink.Child))]
|
||||
public override IEnumerable<Library> Libraries
|
||||
{
|
||||
get => LibraryLinks?.Select(x => x.Library);
|
||||
set => LibraryLinks = value?.Select(x => new LibraryLink(x, this)).ToList();
|
||||
}
|
||||
|
||||
public CollectionDE() {}
|
||||
|
||||
public CollectionDE(Collection collection)
|
||||
{
|
||||
Utility.Assign(this, collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class GenreDE : Genre
|
||||
{
|
||||
[JsonIgnore] [NotMergable] public virtual ICollection<GenreLink> Links { get; set; }
|
||||
|
||||
[ExpressionRewrite(nameof(Links), nameof(GenreLink.Child))]
|
||||
[JsonIgnore] [NotMergable] public override IEnumerable<Show> Shows
|
||||
{
|
||||
get => Links?.Select(x => x.Parent);
|
||||
set => Links = value?.Select(x => new GenreLink(x, this)).ToList();
|
||||
}
|
||||
|
||||
public GenreDE() {}
|
||||
|
||||
public GenreDE(Genre item)
|
||||
{
|
||||
Utility.Assign(this, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class LibraryDE : Library
|
||||
{
|
||||
[EditableRelation] [JsonIgnore] [NotMergable] public virtual ICollection<ProviderLink> ProviderLinks { get; set; }
|
||||
[ExpressionRewrite(nameof(ProviderLinks), nameof(ProviderLink.Child))]
|
||||
public override IEnumerable<ProviderID> Providers
|
||||
{
|
||||
get => ProviderLinks?.Select(x => x.Child);
|
||||
set => ProviderLinks = value?.Select(x => new ProviderLink(x, this)).ToList();
|
||||
}
|
||||
|
||||
[JsonIgnore] [NotMergable] public virtual ICollection<LibraryLink> Links { get; set; }
|
||||
[ExpressionRewrite(nameof(Links), nameof(LibraryLink.Show))]
|
||||
public override IEnumerable<Show> Shows
|
||||
{
|
||||
get => Links?.Where(x => x.Show != null).Select(x => x.Show);
|
||||
set => Links = Utility.MergeLists(
|
||||
value?.Select(x => new LibraryLink(this, x)),
|
||||
Links?.Where(x => x.Show == null))?.ToList();
|
||||
}
|
||||
[ExpressionRewrite(nameof(Links), nameof(LibraryLink.Collection))]
|
||||
public override IEnumerable<Collection> Collections
|
||||
{
|
||||
get => Links?.Where(x => x.Collection != null).Select(x => x.Collection);
|
||||
set => Links = Utility.MergeLists(
|
||||
value?.Select(x => new LibraryLink(this, x)),
|
||||
Links?.Where(x => x.Collection == null))?.ToList();
|
||||
}
|
||||
|
||||
public LibraryDE() {}
|
||||
|
||||
public LibraryDE(Library item)
|
||||
{
|
||||
Utility.Assign(this, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class ShowDE : Show
|
||||
{
|
||||
[EditableRelation] [JsonReadOnly] [NotMergable] public virtual ICollection<GenreLink> GenreLinks { get; set; }
|
||||
[ExpressionRewrite(nameof(GenreLinks), nameof(GenreLink.Child))]
|
||||
public override IEnumerable<Genre> Genres
|
||||
{
|
||||
get => GenreLinks?.Select(x => x.Child);
|
||||
set => GenreLinks = value?.Select(x => new GenreLink(this, x)).ToList();
|
||||
}
|
||||
|
||||
[JsonReadOnly] [NotMergable] public virtual ICollection<LibraryLink> LibraryLinks { get; set; }
|
||||
[ExpressionRewrite(nameof(LibraryLinks), nameof(LibraryLink.Library))]
|
||||
public override IEnumerable<Library> Libraries
|
||||
{
|
||||
get => LibraryLinks?.Select(x => x.Library);
|
||||
set => LibraryLinks = value?.Select(x => new LibraryLink(x, this)).ToList();
|
||||
}
|
||||
|
||||
[JsonReadOnly] [NotMergable] public virtual ICollection<CollectionLink> CollectionLinks { get; set; }
|
||||
[ExpressionRewrite(nameof(CollectionLinks), nameof(CollectionLink.Parent))]
|
||||
public override IEnumerable<Collection> Collections
|
||||
{
|
||||
get => CollectionLinks?.Select(x => x.Parent);
|
||||
set => CollectionLinks = value?.Select(x => new CollectionLink(x, this)).ToList();
|
||||
}
|
||||
|
||||
public ShowDE() {}
|
||||
|
||||
public ShowDE(Show show)
|
||||
{
|
||||
Utility.Assign(this, show);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using IdentityServer4.Extensions;
|
||||
using IdentityServer4.Services;
|
||||
using Kyoo.Api;
|
||||
using Kyoo.Controllers;
|
||||
@ -36,21 +37,30 @@ namespace Kyoo
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
string publicUrl = _configuration.GetValue<string>("public_url");
|
||||
|
||||
services.AddSpaStaticFiles(configuration =>
|
||||
{
|
||||
configuration.RootPath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "wwwroot");
|
||||
});
|
||||
services.AddResponseCompression(x =>
|
||||
{
|
||||
x.EnableForHttps = true;
|
||||
});
|
||||
|
||||
services.AddControllers()
|
||||
.AddNewtonsoftJson(x => x.SerializerSettings.ContractResolver = new JsonPropertyIgnorer());
|
||||
.AddNewtonsoftJson(x =>
|
||||
{
|
||||
x.SerializerSettings.ContractResolver = new JsonPropertyIgnorer(publicUrl);
|
||||
x.SerializerSettings.Converters.Add(new PeopleRoleConverter());
|
||||
});
|
||||
services.AddHttpClient();
|
||||
|
||||
services.AddDbContext<DatabaseContext>(options =>
|
||||
{
|
||||
options.UseLazyLoadingProxies()
|
||||
.UseNpgsql(_configuration.GetConnectionString("Database"));
|
||||
// .EnableSensitiveDataLogging()
|
||||
// .UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole()));
|
||||
options.UseNpgsql(_configuration.GetConnectionString("Database"));
|
||||
// .EnableSensitiveDataLogging()
|
||||
// .UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole()));
|
||||
}, ServiceLifetime.Transient);
|
||||
|
||||
services.AddDbContext<IdentityDatabase>(options =>
|
||||
@ -59,7 +69,6 @@ namespace Kyoo
|
||||
});
|
||||
|
||||
string assemblyName = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
|
||||
string publicUrl = _configuration.GetValue<string>("public_url");
|
||||
|
||||
services.AddIdentityCore<User>(o =>
|
||||
{
|
||||
@ -72,7 +81,6 @@ namespace Kyoo
|
||||
services.AddIdentityServer(options =>
|
||||
{
|
||||
options.IssuerUri = publicUrl;
|
||||
options.PublicOrigin = publicUrl;
|
||||
options.UserInteraction.LoginUrl = publicUrl + "login";
|
||||
options.UserInteraction.ErrorUrl = publicUrl + "error";
|
||||
options.UserInteraction.LogoutUrl = publicUrl + "logout";
|
||||
@ -92,6 +100,7 @@ namespace Kyoo
|
||||
options.EnableTokenCleanup = true;
|
||||
})
|
||||
.AddInMemoryIdentityResources(IdentityContext.GetIdentityResources())
|
||||
.AddInMemoryApiScopes(IdentityContext.GetScopes())
|
||||
.AddInMemoryApiResources(IdentityContext.GetApis())
|
||||
.AddProfileService<AccountController>()
|
||||
.AddSigninKeys(_configuration);
|
||||
@ -146,8 +155,10 @@ namespace Kyoo
|
||||
services.AddScoped<IStudioRepository, StudioRepository>();
|
||||
services.AddScoped<IGenreRepository, GenreRepository>();
|
||||
services.AddScoped<IProviderRepository, ProviderRepository>();
|
||||
|
||||
services.AddScoped<DbContext, DatabaseContext>();
|
||||
|
||||
services.AddScoped<ILibraryManager, LibraryManager>();
|
||||
services.AddSingleton<IFileManager, FileManager>();
|
||||
services.AddSingleton<ITranscoder, Transcoder>();
|
||||
services.AddSingleton<IThumbnailsManager, ThumbnailsManager>();
|
||||
services.AddSingleton<IProviderManager, ProviderManager>();
|
||||
@ -157,7 +168,7 @@ namespace Kyoo
|
||||
services.AddHostedService(provider => (TaskManager)provider.GetService<ITaskManager>());
|
||||
}
|
||||
|
||||
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
@ -181,11 +192,29 @@ namespace Kyoo
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseCookiePolicy(new CookiePolicyOptions
|
||||
app.Use((ctx, next) =>
|
||||
{
|
||||
ctx.Response.Headers.Remove("X-Powered-By");
|
||||
ctx.Response.Headers.Remove("Server");
|
||||
ctx.Response.Headers.Add("Feature-Policy", "autoplay 'self'; fullscreen");
|
||||
ctx.Response.Headers.Add("Content-Security-Policy", "default-src 'self'; script-src 'self' blob: 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'");
|
||||
ctx.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
|
||||
ctx.Response.Headers.Add("Referrer-Policy", "no-referrer");
|
||||
ctx.Response.Headers.Add("Access-Control-Allow-Origin", "null");
|
||||
ctx.Response.Headers.Add("X-Content-Type-Options", "nosniff");
|
||||
return next();
|
||||
});
|
||||
app.UseResponseCompression();
|
||||
app.UseCookiePolicy(new CookiePolicyOptions
|
||||
{
|
||||
MinimumSameSitePolicy = SameSiteMode.Strict
|
||||
});
|
||||
app.UseAuthentication();
|
||||
app.Use((ctx, next) =>
|
||||
{
|
||||
ctx.SetIdentityServerOrigin(_configuration.GetValue<string>("public_url"));
|
||||
return next();
|
||||
});
|
||||
app.UseIdentityServer();
|
||||
app.UseAuthorization();
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ namespace Kyoo.Tasks
|
||||
new PluginLoader(),
|
||||
new Crawler(),
|
||||
new MetadataProviderLoader(),
|
||||
new ReScan(),
|
||||
// new ReScan(),
|
||||
new ExtractMetadata()
|
||||
};
|
||||
}
|
||||
|
||||
@ -75,9 +75,12 @@ namespace Kyoo.Controllers
|
||||
ICollection<Library> libraries = argument == null
|
||||
? await libraryManager.GetLibraries()
|
||||
: new [] { await libraryManager.GetLibrary(argument)};
|
||||
// TODO replace this grotesque way to load the providers.
|
||||
|
||||
if (argument != null && libraries.First() == null)
|
||||
throw new ArgumentException($"No library found with the name {argument}");
|
||||
|
||||
foreach (Library library in libraries)
|
||||
library.Providers = library.Providers;
|
||||
await libraryManager.Load(library, x => x.Providers);
|
||||
|
||||
foreach (Library library in libraries)
|
||||
await Scan(library, episodes, tracks, cancellationToken);
|
||||
@ -123,6 +126,7 @@ namespace Kyoo.Controllers
|
||||
.GroupBy(Path.GetDirectoryName)
|
||||
.ToList();
|
||||
|
||||
// TODO If the library's path end with a /, the regex is broken.
|
||||
IEnumerable<string> tasks = shows.Select(x => x.First());
|
||||
foreach (string[] showTasks in tasks.BatchBy(_parallelTasks))
|
||||
await Task.WhenAll(showTasks
|
||||
@ -287,7 +291,6 @@ namespace Kyoo.Controllers
|
||||
show.Slug += $"-{show.StartYear}";
|
||||
await libraryManager.RegisterShow(show);
|
||||
}
|
||||
await _thumbnailsManager.Validate(show.People);
|
||||
await _thumbnailsManager.Validate(show);
|
||||
return show;
|
||||
}
|
||||
@ -347,16 +350,17 @@ namespace Kyoo.Controllers
|
||||
Title = show.Title,
|
||||
Path = episodePath,
|
||||
Show = show,
|
||||
ShowID = show.ID
|
||||
ShowID = show.ID,
|
||||
ShowSlug = show.Slug
|
||||
};
|
||||
episode.Tracks = await GetTracks(episode);
|
||||
return episode;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<Track>> GetTracks(Episode episode)
|
||||
private async Task<ICollection<Track>> GetTracks(Episode episode)
|
||||
{
|
||||
episode.Tracks = (await _transcoder.ExtractInfos(episode.Path))
|
||||
.Where(x => x.Type != StreamType.Font)
|
||||
episode.Tracks = (await _transcoder.ExtractInfos(episode, false))
|
||||
.Where(x => x.Type != StreamType.Attachment)
|
||||
.ToArray();
|
||||
return episode.Tracks;
|
||||
}
|
||||
|
||||
@ -28,9 +28,9 @@ namespace Kyoo.Tasks
|
||||
IdentityDatabase identityDatabase = serviceScope.ServiceProvider.GetService<IdentityDatabase>();
|
||||
ConfigurationDbContext identityContext = serviceScope.ServiceProvider.GetService<ConfigurationDbContext>();
|
||||
|
||||
databaseContext.Database.Migrate();
|
||||
identityDatabase.Database.Migrate();
|
||||
identityContext.Database.Migrate();
|
||||
databaseContext!.Database.Migrate();
|
||||
identityDatabase!.Database.Migrate();
|
||||
identityContext!.Database.Migrate();
|
||||
|
||||
if (!identityContext.Clients.Any())
|
||||
{
|
||||
|
||||
@ -72,6 +72,7 @@ namespace Kyoo.Tasks
|
||||
{
|
||||
if (thumbs)
|
||||
await _thumbnails!.Validate(show, true);
|
||||
await _library.Load(show, x => x.Seasons);
|
||||
foreach (Season season in show.Seasons)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
@ -84,6 +85,7 @@ namespace Kyoo.Tasks
|
||||
{
|
||||
if (thumbs)
|
||||
await _thumbnails!.Validate(season, true);
|
||||
await _library.Load(season, x => x.Episodes);
|
||||
foreach (Episode episode in season.Episodes)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
@ -98,10 +100,11 @@ namespace Kyoo.Tasks
|
||||
await _thumbnails!.Validate(episode, true);
|
||||
if (subs)
|
||||
{
|
||||
// TODO this doesn't work.
|
||||
IEnumerable<Track> tracks = (await _transcoder!.ExtractInfos(episode.Path))
|
||||
.Where(x => x.Type != StreamType.Font);
|
||||
episode.Tracks = tracks;
|
||||
await _library.Load(episode, x => x.Tracks);
|
||||
episode.Tracks = (await _transcoder!.ExtractInfos(episode, true))
|
||||
.Where(x => x.Type != StreamType.Attachment)
|
||||
.Concat(episode.Tracks.Where(x => x.IsExternal))
|
||||
.ToList();
|
||||
await _library.EditEpisode(episode, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,13 +21,15 @@ namespace Kyoo.Tasks
|
||||
{
|
||||
using IServiceScope serviceScope = serviceProvider.CreateScope();
|
||||
IProviderRepository providers = serviceScope.ServiceProvider.GetService<IProviderRepository>();
|
||||
IThumbnailsManager thumbnails = serviceScope.ServiceProvider.GetService<IThumbnailsManager>();
|
||||
IPluginManager pluginManager = serviceScope.ServiceProvider.GetService<IPluginManager>();
|
||||
|
||||
foreach (IMetadataProvider provider in pluginManager.GetPlugins<IMetadataProvider>())
|
||||
foreach (IMetadataProvider provider in pluginManager!.GetPlugins<IMetadataProvider>())
|
||||
{
|
||||
if (string.IsNullOrEmpty(provider.Provider.Slug))
|
||||
throw new ArgumentException($"Empty provider slug (name: {provider.Provider.Name}).");
|
||||
await providers.CreateIfNotExists(provider.Provider);
|
||||
await providers!.CreateIfNotExists(provider.Provider);
|
||||
await thumbnails!.Validate(provider.Provider);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,127 +1,127 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Kyoo.Tasks
|
||||
{
|
||||
public class ReScan: ITask
|
||||
{
|
||||
public string Slug => "re-scan";
|
||||
public string Name => "ReScan";
|
||||
public string Description => "Re download metadata of an item using it's external ids.";
|
||||
public string HelpMessage => null;
|
||||
public bool RunOnStartup => false;
|
||||
public int Priority => 0;
|
||||
|
||||
|
||||
private IServiceProvider _serviceProvider;
|
||||
private IThumbnailsManager _thumbnailsManager;
|
||||
private IProviderManager _providerManager;
|
||||
private DatabaseContext _database;
|
||||
|
||||
public async Task Run(IServiceProvider serviceProvider, CancellationToken cancellationToken, string arguments = null)
|
||||
{
|
||||
using IServiceScope serviceScope = serviceProvider.CreateScope();
|
||||
_serviceProvider = serviceProvider;
|
||||
_thumbnailsManager = serviceProvider.GetService<IThumbnailsManager>();
|
||||
_providerManager = serviceProvider.GetService<IProviderManager>();
|
||||
_database = serviceScope.ServiceProvider.GetService<DatabaseContext>();
|
||||
|
||||
if (arguments == null || !arguments.Contains('/'))
|
||||
return;
|
||||
|
||||
string slug = arguments.Substring(arguments.IndexOf('/') + 1);
|
||||
switch (arguments.Substring(0, arguments.IndexOf('/')))
|
||||
{
|
||||
case "show":
|
||||
await ReScanShow(slug);
|
||||
break;
|
||||
case "season":
|
||||
await ReScanSeason(slug);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReScanShow(string slug)
|
||||
{
|
||||
Show old;
|
||||
|
||||
using (IServiceScope serviceScope = _serviceProvider.CreateScope())
|
||||
{
|
||||
ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
||||
old = _database.Shows.FirstOrDefault(x => x.Slug == slug);
|
||||
if (old == null)
|
||||
return;
|
||||
Library library = _database.LibraryLinks.First(x => x.Show == old && x.Library != null).Library;
|
||||
Show edited = await _providerManager.CompleteShow(old, library);
|
||||
edited.ID = old.ID;
|
||||
edited.Slug = old.Slug;
|
||||
edited.Path = old.Path;
|
||||
await libraryManager.EditShow(edited, true);
|
||||
await _thumbnailsManager.Validate(edited, true);
|
||||
}
|
||||
if (old.Seasons != null)
|
||||
await Task.WhenAll(old.Seasons.Select(x => ReScanSeason(old, x)));
|
||||
IEnumerable<Episode> orphans = old.Episodes.Where(x => x.Season == null).ToList();
|
||||
if (orphans.Any())
|
||||
await Task.WhenAll(orphans.Select(x => ReScanEpisode(old, x)));
|
||||
}
|
||||
|
||||
private async Task ReScanSeason(string seasonSlug)
|
||||
{
|
||||
string[] infos = seasonSlug.Split('-');
|
||||
if (infos.Length != 2 || int.TryParse(infos[1], out int seasonNumber))
|
||||
return;
|
||||
string slug = infos[0];
|
||||
Show show = _database.Shows.FirstOrDefault(x => x.Slug == slug);
|
||||
if (show == null)
|
||||
return;
|
||||
Season old = _database.Seasons.FirstOrDefault(x => x.SeasonNumber == seasonNumber && x.Show.ID == show.ID);
|
||||
if (old == null)
|
||||
return;
|
||||
await ReScanSeason(show, old);
|
||||
}
|
||||
|
||||
private async Task ReScanSeason(Show show, Season old)
|
||||
{
|
||||
using (IServiceScope serviceScope = _serviceProvider.CreateScope())
|
||||
{
|
||||
ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
||||
Library library = _database.LibraryLinks.First(x => x.Show == show && x.Library != null).Library;
|
||||
Season edited = await _providerManager.GetSeason(show, old.SeasonNumber, library);
|
||||
edited.ID = old.ID;
|
||||
await libraryManager.EditSeason(edited, true);
|
||||
await _thumbnailsManager.Validate(edited, true);
|
||||
}
|
||||
if (old.Episodes != null)
|
||||
await Task.WhenAll(old.Episodes.Select(x => ReScanEpisode(show, x)));
|
||||
}
|
||||
|
||||
private async Task ReScanEpisode(Show show, Episode old)
|
||||
{
|
||||
using IServiceScope serviceScope = _serviceProvider.CreateScope();
|
||||
ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
||||
|
||||
Library library = _database.LibraryLinks.First(x => x.Show == show && x.Library != null).Library;
|
||||
Episode edited = await _providerManager.GetEpisode(show, old.Path, old.SeasonNumber, old.EpisodeNumber, old.AbsoluteNumber, library);
|
||||
edited.ID = old.ID;
|
||||
await libraryManager.EditEpisode(edited, true);
|
||||
await _thumbnailsManager.Validate(edited, true);
|
||||
}
|
||||
|
||||
public Task<IEnumerable<string>> GetPossibleParameters()
|
||||
{
|
||||
return Task.FromResult<IEnumerable<string>>(null);
|
||||
}
|
||||
|
||||
public int? Progress()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// using System;
|
||||
// using System.Collections.Generic;
|
||||
// using System.Linq;
|
||||
// using System.Threading;
|
||||
// using System.Threading.Tasks;
|
||||
// using Kyoo.Controllers;
|
||||
// using Kyoo.Models;
|
||||
// using Microsoft.Extensions.DependencyInjection;
|
||||
//
|
||||
// namespace Kyoo.Tasks
|
||||
// {
|
||||
// public class ReScan: ITask
|
||||
// {
|
||||
// public string Slug => "re-scan";
|
||||
// public string Name => "ReScan";
|
||||
// public string Description => "Re download metadata of an item using it's external ids.";
|
||||
// public string HelpMessage => null;
|
||||
// public bool RunOnStartup => false;
|
||||
// public int Priority => 0;
|
||||
//
|
||||
//
|
||||
// private IServiceProvider _serviceProvider;
|
||||
// private IThumbnailsManager _thumbnailsManager;
|
||||
// private IProviderManager _providerManager;
|
||||
// private DatabaseContext _database;
|
||||
//
|
||||
// public async Task Run(IServiceProvider serviceProvider, CancellationToken cancellationToken, string arguments = null)
|
||||
// {
|
||||
// using IServiceScope serviceScope = serviceProvider.CreateScope();
|
||||
// _serviceProvider = serviceProvider;
|
||||
// _thumbnailsManager = serviceProvider.GetService<IThumbnailsManager>();
|
||||
// _providerManager = serviceProvider.GetService<IProviderManager>();
|
||||
// _database = serviceScope.ServiceProvider.GetService<DatabaseContext>();
|
||||
//
|
||||
// if (arguments == null || !arguments.Contains('/'))
|
||||
// return;
|
||||
//
|
||||
// string slug = arguments.Substring(arguments.IndexOf('/') + 1);
|
||||
// switch (arguments.Substring(0, arguments.IndexOf('/')))
|
||||
// {
|
||||
// case "show":
|
||||
// await ReScanShow(slug);
|
||||
// break;
|
||||
// case "season":
|
||||
// await ReScanSeason(slug);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private async Task ReScanShow(string slug)
|
||||
// {
|
||||
// Show old;
|
||||
//
|
||||
// using (IServiceScope serviceScope = _serviceProvider.CreateScope())
|
||||
// {
|
||||
// ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
||||
// old = _database.Shows.FirstOrDefault(x => x.Slug == slug);
|
||||
// if (old == null)
|
||||
// return;
|
||||
// Library library = _database.LibraryLinks.First(x => x.Show == old && x.Library != null).Library;
|
||||
// Show edited = await _providerManager.CompleteShow(old, library);
|
||||
// edited.ID = old.ID;
|
||||
// edited.Slug = old.Slug;
|
||||
// edited.Path = old.Path;
|
||||
// await libraryManager.EditShow(edited, true);
|
||||
// await _thumbnailsManager.Validate(edited, true);
|
||||
// }
|
||||
// if (old.Seasons != null)
|
||||
// await Task.WhenAll(old.Seasons.Select(x => ReScanSeason(old, x)));
|
||||
// IEnumerable<Episode> orphans = old.Episodes.Where(x => x.Season == null).ToList();
|
||||
// if (orphans.Any())
|
||||
// await Task.WhenAll(orphans.Select(x => ReScanEpisode(old, x)));
|
||||
// }
|
||||
//
|
||||
// private async Task ReScanSeason(string seasonSlug)
|
||||
// {
|
||||
// string[] infos = seasonSlug.Split('-');
|
||||
// if (infos.Length != 2 || int.TryParse(infos[1], out int seasonNumber))
|
||||
// return;
|
||||
// string slug = infos[0];
|
||||
// Show show = _database.Shows.FirstOrDefault(x => x.Slug == slug);
|
||||
// if (show == null)
|
||||
// return;
|
||||
// Season old = _database.Seasons.FirstOrDefault(x => x.SeasonNumber == seasonNumber && x.Show.ID == show.ID);
|
||||
// if (old == null)
|
||||
// return;
|
||||
// await ReScanSeason(show, old);
|
||||
// }
|
||||
//
|
||||
// private async Task ReScanSeason(Show show, Season old)
|
||||
// {
|
||||
// using (IServiceScope serviceScope = _serviceProvider.CreateScope())
|
||||
// {
|
||||
// ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
||||
// Library library = _database.LibraryLinks.First(x => x.Show == show && x.Library != null).Library;
|
||||
// Season edited = await _providerManager.GetSeason(show, old.SeasonNumber, library);
|
||||
// edited.ID = old.ID;
|
||||
// await libraryManager.EditSeason(edited, true);
|
||||
// await _thumbnailsManager.Validate(edited, true);
|
||||
// }
|
||||
// if (old.Episodes != null)
|
||||
// await Task.WhenAll(old.Episodes.Select(x => ReScanEpisode(show, x)));
|
||||
// }
|
||||
//
|
||||
// private async Task ReScanEpisode(Show show, Episode old)
|
||||
// {
|
||||
// using IServiceScope serviceScope = _serviceProvider.CreateScope();
|
||||
// ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
||||
//
|
||||
// Library library = _database.LibraryLinks.First(x => x.Show == show && x.Library != null).Library;
|
||||
// Episode edited = await _providerManager.GetEpisode(show, old.Path, old.SeasonNumber, old.EpisodeNumber, old.AbsoluteNumber, library);
|
||||
// edited.ID = old.ID;
|
||||
// await libraryManager.EditEpisode(edited, true);
|
||||
// await _thumbnailsManager.Validate(edited, true);
|
||||
// }
|
||||
//
|
||||
// public Task<IEnumerable<string>> GetPossibleParameters()
|
||||
// {
|
||||
// return Task.FromResult<IEnumerable<string>>(null);
|
||||
// }
|
||||
//
|
||||
// public int? Progress()
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@ -33,10 +33,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Show> resources = await _libraryManager.GetShows(
|
||||
@ -63,10 +59,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Show> resources = await _libraryManager.GetShows(
|
||||
@ -93,10 +85,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Library> resources = await _libraryManager.GetLibraries(
|
||||
@ -123,10 +111,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Library> resources = await _libraryManager.GetLibraries(
|
||||
|
||||
@ -2,12 +2,10 @@
|
||||
using Kyoo.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.CommonApi;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
@ -19,11 +17,18 @@ namespace Kyoo.Api
|
||||
public class EpisodeApi : CrudApi<Episode>
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IThumbnailsManager _thumbnails;
|
||||
private readonly IFileManager _files;
|
||||
|
||||
public EpisodeApi(ILibraryManager libraryManager, IConfiguration configuration)
|
||||
public EpisodeApi(ILibraryManager libraryManager,
|
||||
IConfiguration configuration,
|
||||
IFileManager files,
|
||||
IThumbnailsManager thumbnails)
|
||||
: base(libraryManager.EpisodeRepository, configuration)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_files = files;
|
||||
_thumbnails = thumbnails;
|
||||
}
|
||||
|
||||
[HttpGet("{episodeID:int}/show")]
|
||||
@ -77,10 +82,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Track> resources = await _libraryManager.GetTracks(
|
||||
@ -109,10 +110,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Track> resources = await _libraryManager.GetTracks(
|
||||
@ -143,10 +140,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Track> resources = await _libraryManager.GetTracks(ApiHelper.ParseWhere<Track>(where, x => x.Episode.Show.Slug == showSlug
|
||||
@ -165,19 +158,24 @@ namespace Kyoo.Api
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/thumb")]
|
||||
[HttpGet("{id:int}/thumb")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetThumb(string showSlug, int seasonNumber, int episodeNumber)
|
||||
public async Task<IActionResult> GetThumb(int id)
|
||||
{
|
||||
string path = (await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber))?.Path;
|
||||
if (path == null)
|
||||
Episode episode = await _libraryManager.GetEpisode(id);
|
||||
if (episode == null)
|
||||
return NotFound();
|
||||
|
||||
string thumb = Path.ChangeExtension(path, "jpg");
|
||||
|
||||
if (System.IO.File.Exists(thumb))
|
||||
return new PhysicalFileResult(Path.GetFullPath(thumb), "image/jpg");
|
||||
return NotFound();
|
||||
return _files.FileResult(await _thumbnails.GetEpisodeThumb(episode));
|
||||
}
|
||||
|
||||
[HttpGet("{slug}/thumb")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetThumb(string slug)
|
||||
{
|
||||
Episode episode = await _libraryManager.GetEpisode(slug);
|
||||
if (episode == null)
|
||||
return NotFound();
|
||||
return _files.FileResult(await _thumbnails.GetEpisodeThumb(episode));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -34,10 +34,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 20)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Show> resources = await _libraryManager.GetShows(
|
||||
@ -64,10 +60,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 20)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Show> resources = await _libraryManager.GetShows(
|
||||
|
||||
@ -45,10 +45,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 50)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Show> resources = await _libraryManager.GetShows(
|
||||
@ -75,10 +71,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 20)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Show> resources = await _libraryManager.GetShows(
|
||||
@ -105,10 +97,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 50)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Collection> resources = await _libraryManager.GetCollections(
|
||||
@ -135,10 +123,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 20)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Collection> resources = await _libraryManager.GetCollections(
|
||||
@ -165,10 +149,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 50)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<LibraryItem> resources = await _libraryManager.GetItemsFromLibrary(id,
|
||||
@ -195,10 +175,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 50)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<LibraryItem> resources = await _libraryManager.GetItemsFromLibrary(slug,
|
||||
|
||||
@ -15,6 +15,7 @@ namespace Kyoo.Api
|
||||
[Route("api/item")]
|
||||
[Route("api/items")]
|
||||
[ApiController]
|
||||
[ResourceView]
|
||||
public class LibraryItemApi : ControllerBase
|
||||
{
|
||||
private readonly ILibraryItemRepository _libraryItems;
|
||||
@ -34,10 +35,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 50)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<LibraryItem> resources = await _libraryItems.GetAll(
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.CommonApi;
|
||||
using Kyoo.Controllers;
|
||||
@ -17,34 +16,34 @@ namespace Kyoo.Api
|
||||
public class PeopleApi : CrudApi<People>
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly string _peoplePath;
|
||||
private readonly IFileManager _files;
|
||||
private readonly IThumbnailsManager _thumbs;
|
||||
|
||||
public PeopleApi(ILibraryManager libraryManager, IConfiguration configuration)
|
||||
public PeopleApi(ILibraryManager libraryManager,
|
||||
IConfiguration configuration,
|
||||
IFileManager files,
|
||||
IThumbnailsManager thumbs)
|
||||
: base(libraryManager.PeopleRepository, configuration)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_peoplePath = configuration.GetValue<string>("peoplePath");
|
||||
_files = files;
|
||||
_thumbs = thumbs;
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}/role")]
|
||||
[HttpGet("{id:int}/roles")]
|
||||
[Authorize(Policy = "Read")]
|
||||
[JsonDetailed]
|
||||
public async Task<ActionResult<Page<ShowRole>>> GetRoles(int id,
|
||||
public async Task<ActionResult<Page<PeopleRole>>> GetRoles(int id,
|
||||
[FromQuery] string sortBy,
|
||||
[FromQuery] int afterID,
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 20)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<ShowRole> resources = await _libraryManager.GetRolesFromPeople(id,
|
||||
ApiHelper.ParseWhere<ShowRole>(where),
|
||||
new Sort<ShowRole>(sortBy),
|
||||
ICollection<PeopleRole> resources = await _libraryManager.GetRolesFromPeople(id,
|
||||
ApiHelper.ParseWhere<PeopleRole>(where),
|
||||
new Sort<PeopleRole>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
return Page(resources, limit);
|
||||
@ -62,22 +61,17 @@ namespace Kyoo.Api
|
||||
[HttpGet("{slug}/role")]
|
||||
[HttpGet("{slug}/roles")]
|
||||
[Authorize(Policy = "Read")]
|
||||
[JsonDetailed]
|
||||
public async Task<ActionResult<Page<ShowRole>>> GetRoles(string slug,
|
||||
public async Task<ActionResult<Page<PeopleRole>>> GetRoles(string slug,
|
||||
[FromQuery] string sortBy,
|
||||
[FromQuery] int afterID,
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 20)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<ShowRole> resources = await _libraryManager.GetRolesFromPeople(slug,
|
||||
ApiHelper.ParseWhere<ShowRole>(where),
|
||||
new Sort<ShowRole>(sortBy),
|
||||
ICollection<PeopleRole> resources = await _libraryManager.GetRolesFromPeople(slug,
|
||||
ApiHelper.ParseWhere<PeopleRole>(where),
|
||||
new Sort<PeopleRole>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
return Page(resources, limit);
|
||||
@ -92,15 +86,20 @@ namespace Kyoo.Api
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}/poster")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetPeopleIcon(int id)
|
||||
{
|
||||
People people = await _libraryManager.GetPeople(id);
|
||||
return _files.FileResult(await _thumbs.GetPeoplePoster(people));
|
||||
}
|
||||
|
||||
[HttpGet("{slug}/poster")]
|
||||
[Authorize(Policy="Read")]
|
||||
public IActionResult GetPeopleIcon(string slug)
|
||||
public async Task<IActionResult> GetPeopleIcon(string slug)
|
||||
{
|
||||
string thumbPath = Path.Combine(_peoplePath, slug + ".jpg");
|
||||
if (!System.IO.File.Exists(thumbPath))
|
||||
return NotFound();
|
||||
|
||||
return new PhysicalFileResult(Path.GetFullPath(thumbPath), "image/jpg");
|
||||
People people = await _libraryManager.GetPeople(slug);
|
||||
return _files.FileResult(await _thumbs.GetPeoplePoster(people));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.CommonApi;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models;
|
||||
@ -13,12 +13,35 @@ namespace Kyoo.Api
|
||||
[ApiController]
|
||||
public class ProviderAPI : CrudApi<ProviderID>
|
||||
{
|
||||
private readonly IThumbnailsManager _thumbnails;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IFileManager _files;
|
||||
|
||||
public ProviderAPI(ILibraryManager libraryManager, IConfiguration config)
|
||||
public ProviderAPI(ILibraryManager libraryManager,
|
||||
IConfiguration config,
|
||||
IFileManager files,
|
||||
IThumbnailsManager thumbnails)
|
||||
: base(libraryManager.ProviderRepository, config)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_files = files;
|
||||
_thumbnails = thumbnails;
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}/logo")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetLogo(int id)
|
||||
{
|
||||
ProviderID provider = await _libraryManager.GetProvider(id);
|
||||
return _files.FileResult(await _thumbnails.GetProviderLogo(provider));
|
||||
}
|
||||
|
||||
[HttpGet("{slug}/logo")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetLogo(string slug)
|
||||
{
|
||||
ProviderID provider = await _libraryManager.GetProvider(slug);
|
||||
return _files.FileResult(await _thumbnails.GetProviderLogo(provider));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,11 +17,18 @@ namespace Kyoo.Api
|
||||
public class SeasonApi : CrudApi<Season>
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IThumbnailsManager _thumbs;
|
||||
private readonly IFileManager _files;
|
||||
|
||||
public SeasonApi(ILibraryManager libraryManager, IConfiguration configuration)
|
||||
public SeasonApi(ILibraryManager libraryManager,
|
||||
IConfiguration configuration,
|
||||
IThumbnailsManager thumbs,
|
||||
IFileManager files)
|
||||
: base(libraryManager.SeasonRepository, configuration)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_thumbs = thumbs;
|
||||
_files = files;
|
||||
}
|
||||
|
||||
[HttpGet("{seasonID:int}/episode")]
|
||||
@ -33,10 +40,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Episode> resources = await _libraryManager.GetEpisodes(
|
||||
@ -64,10 +67,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Episode> resources = await _libraryManager.GetEpisodes(
|
||||
@ -96,10 +95,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Episode> resources = await _libraryManager.GetEpisodes(
|
||||
@ -137,5 +132,23 @@ namespace Kyoo.Api
|
||||
{
|
||||
return await _libraryManager.GetShow(showID);
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}/thumb")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetThumb(int id)
|
||||
{
|
||||
Season season = await _libraryManager.GetSeason(id);
|
||||
await _libraryManager.Load(season, x => x.Show);
|
||||
return _files.FileResult(await _thumbs.GetSeasonPoster(season));
|
||||
}
|
||||
|
||||
[HttpGet("{slug}/thumb")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetThumb(string slug)
|
||||
{
|
||||
Season season = await _libraryManager.GetSeason(slug);
|
||||
await _libraryManager.Load(season, x => x.Show);
|
||||
return _files.FileResult(await _thumbs.GetSeasonPoster(season));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,6 @@ using Kyoo.CommonApi;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Kyoo.Api
|
||||
@ -20,12 +19,18 @@ namespace Kyoo.Api
|
||||
public class ShowApi : CrudApi<Show>
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private FileExtensionContentTypeProvider _provider;
|
||||
private readonly IFileManager _files;
|
||||
private readonly IThumbnailsManager _thumbs;
|
||||
|
||||
public ShowApi(ILibraryManager libraryManager, IConfiguration configuration)
|
||||
public ShowApi(ILibraryManager libraryManager,
|
||||
IFileManager files,
|
||||
IThumbnailsManager thumbs,
|
||||
IConfiguration configuration)
|
||||
: base(libraryManager.ShowRepository, configuration)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_files = files;
|
||||
_thumbs = thumbs;
|
||||
}
|
||||
|
||||
[HttpGet("{showID:int}/season")]
|
||||
@ -37,10 +42,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 20)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Season> resources = await _libraryManager.GetSeasons(
|
||||
@ -67,10 +68,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 20)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Season> resources = await _libraryManager.GetSeasons(
|
||||
@ -97,10 +94,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 50)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Episode> resources = await _libraryManager.GetEpisodes(
|
||||
@ -127,10 +120,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 50)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Episode> resources = await _libraryManager.GetEpisodes(
|
||||
@ -156,10 +145,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<PeopleRole> resources = await _libraryManager.GetPeopleFromShow(showID,
|
||||
@ -185,10 +170,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<PeopleRole> resources = await _libraryManager.GetPeopleFromShow(slug,
|
||||
@ -215,10 +196,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Genre> resources = await _libraryManager.GetGenres(
|
||||
@ -245,10 +222,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Genre> resources = await _libraryManager.GetGenres(
|
||||
@ -303,10 +276,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Library> resources = await _libraryManager.GetLibraries(
|
||||
@ -333,10 +302,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Library> resources = await _libraryManager.GetLibraries(
|
||||
@ -363,10 +328,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Collection> resources = await _libraryManager.GetCollections(
|
||||
@ -393,10 +354,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 30)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Collection> resources = await _libraryManager.GetCollections(
|
||||
@ -419,13 +376,11 @@ namespace Kyoo.Api
|
||||
[Authorize(Policy = "Read")]
|
||||
public async Task<ActionResult<Dictionary<string, string>>> GetFonts(string slug)
|
||||
{
|
||||
string path = (await _libraryManager.GetShow(slug))?.Path;
|
||||
if (path == null)
|
||||
Show show = await _libraryManager.GetShow(slug);
|
||||
if (show == null)
|
||||
return NotFound();
|
||||
path = Path.Combine(path, "Subtitles", "fonts");
|
||||
if (!Directory.Exists(path))
|
||||
return new Dictionary<string, string>();
|
||||
return Directory.GetFiles(path)
|
||||
string path = Path.Combine(_files.GetExtraDirectory(show), "Attachments");
|
||||
return (await _files.ListFiles(path))
|
||||
.ToDictionary(Path.GetFileNameWithoutExtension,
|
||||
x => $"{BaseURL}/api/shows/{slug}/fonts/{Path.GetFileName(x)}");
|
||||
}
|
||||
@ -433,64 +388,43 @@ namespace Kyoo.Api
|
||||
[HttpGet("{showSlug}/font/{slug}")]
|
||||
[HttpGet("{showSlug}/fonts/{slug}")]
|
||||
[Authorize(Policy = "Read")]
|
||||
public async Task<ActionResult> GetFont(string showSlug, string slug)
|
||||
public async Task<IActionResult> GetFont(string showSlug, string slug)
|
||||
{
|
||||
string path = (await _libraryManager.GetShow(showSlug))?.Path;
|
||||
if (path == null)
|
||||
Show show = await _libraryManager.GetShow(showSlug);
|
||||
if (show == null)
|
||||
return NotFound();
|
||||
string fontPath = Path.Combine(path, "Subtitles", "fonts", slug);
|
||||
if (!System.IO.File.Exists(fontPath))
|
||||
return NotFound();
|
||||
|
||||
if (_provider == null)
|
||||
_provider = new FileExtensionContentTypeProvider();
|
||||
_provider.TryGetContentType(path, out string contentType);
|
||||
return PhysicalFile(fontPath, contentType ?? "application/x-font-ttf");
|
||||
string path = Path.Combine(_files.GetExtraDirectory(show), "Attachments", slug);
|
||||
return _files.FileResult(path);
|
||||
}
|
||||
|
||||
[HttpGet("{slug}/poster")]
|
||||
[Authorize(Policy = "Read")]
|
||||
public async Task<ActionResult> GetPoster(string slug)
|
||||
public async Task<IActionResult> GetPoster(string slug)
|
||||
{
|
||||
string path = (await _libraryManager.GetShow(slug))?.Path;
|
||||
if (path == null)
|
||||
Show show = await _libraryManager.GetShow(slug);
|
||||
if (show == null)
|
||||
return NotFound();
|
||||
|
||||
string poster = Path.Combine(path, "poster.jpg");
|
||||
|
||||
if (System.IO.File.Exists(poster))
|
||||
return new PhysicalFileResult(Path.GetFullPath(poster), "image/jpg");
|
||||
return NotFound();
|
||||
return _files.FileResult(await _thumbs.GetShowPoster(show));
|
||||
}
|
||||
|
||||
[HttpGet("{slug}/logo")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetLogo(string slug)
|
||||
{
|
||||
string path = (await _libraryManager.GetShow(slug))?.Path;
|
||||
if (path == null)
|
||||
Show show = await _libraryManager.GetShow(slug);
|
||||
if (show == null)
|
||||
return NotFound();
|
||||
|
||||
string logo = Path.Combine(path, "logo.png");
|
||||
|
||||
if (System.IO.File.Exists(logo))
|
||||
return new PhysicalFileResult(Path.GetFullPath(logo), "image/png");
|
||||
return NotFound();
|
||||
return _files.FileResult(await _thumbs.GetShowLogo(show));
|
||||
}
|
||||
|
||||
[HttpGet("{slug}/backdrop")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetBackdrop(string slug)
|
||||
{
|
||||
string path = (await _libraryManager.GetShow(slug))?.Path;
|
||||
if (path == null)
|
||||
Show show = await _libraryManager.GetShow(slug);
|
||||
if (show == null)
|
||||
return NotFound();
|
||||
|
||||
string thumb = Path.Combine(path, "backdrop.jpg");
|
||||
|
||||
if (System.IO.File.Exists(thumb))
|
||||
return new PhysicalFileResult(Path.GetFullPath(thumb), "image/jpg");
|
||||
return NotFound();
|
||||
return _files.FileResult(await _thumbs.GetShowBackdrop(show));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@ using System.Threading.Tasks;
|
||||
using Kyoo.CommonApi;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@ -34,10 +33,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 20)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Show> resources = await _libraryManager.GetShows(
|
||||
@ -64,10 +59,6 @@ namespace Kyoo.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] int limit = 20)
|
||||
{
|
||||
where.Remove("sortBy");
|
||||
where.Remove("limit");
|
||||
where.Remove("afterID");
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<Show> resources = await _libraryManager.GetShows(
|
||||
|
||||
@ -14,10 +14,12 @@ namespace Kyoo.Api
|
||||
public class SubtitleApi : ControllerBase
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IFileManager _files;
|
||||
|
||||
public SubtitleApi(ILibraryManager libraryManager)
|
||||
public SubtitleApi(ILibraryManager libraryManager, IFileManager files)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_files = files;
|
||||
}
|
||||
|
||||
|
||||
@ -35,13 +37,12 @@ namespace Kyoo.Api
|
||||
return BadRequest(new {error = ex.Message});
|
||||
}
|
||||
|
||||
if (subtitle == null)
|
||||
if (subtitle == null || subtitle.Type != StreamType.Subtitle)
|
||||
return NotFound();
|
||||
|
||||
if (subtitle.Codec == "subrip" && extension == "vtt")
|
||||
return new ConvertSubripToVtt(subtitle.Path);
|
||||
string mime = subtitle.Codec == "ass" ? "text/x-ssa" : "application/x-subrip";
|
||||
return PhysicalFile(subtitle.Path, mime);
|
||||
return new ConvertSubripToVtt(subtitle.Path, _files);
|
||||
return _files.FileResult(subtitle.Path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,27 +50,29 @@ namespace Kyoo.Api
|
||||
public class ConvertSubripToVtt : IActionResult
|
||||
{
|
||||
private readonly string _path;
|
||||
private readonly IFileManager _files;
|
||||
|
||||
public ConvertSubripToVtt(string subtitlePath)
|
||||
public ConvertSubripToVtt(string subtitlePath, IFileManager files)
|
||||
{
|
||||
_path = subtitlePath;
|
||||
_files = files;
|
||||
}
|
||||
|
||||
public async Task ExecuteResultAsync(ActionContext context)
|
||||
{
|
||||
string line;
|
||||
List<string> lines = new List<string>();
|
||||
List<string> lines = new();
|
||||
|
||||
context.HttpContext.Response.StatusCode = 200;
|
||||
context.HttpContext.Response.Headers.Add("Content-Type", "text/vtt");
|
||||
|
||||
await using (StreamWriter writer = new StreamWriter(context.HttpContext.Response.Body))
|
||||
await using (StreamWriter writer = new(context.HttpContext.Response.Body))
|
||||
{
|
||||
await writer.WriteLineAsync("WEBVTT");
|
||||
await writer.WriteLineAsync("");
|
||||
await writer.WriteLineAsync("");
|
||||
|
||||
using StreamReader reader = new StreamReader(_path);
|
||||
using StreamReader reader = _files.GetReader(_path);
|
||||
string line;
|
||||
while ((line = await reader.ReadLineAsync()) != null)
|
||||
{
|
||||
if (line == "")
|
||||
|
||||
@ -1,94 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Controllers;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Kyoo.Api
|
||||
{
|
||||
public class ThumbnailController : ControllerBase
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly string _peoplePath;
|
||||
|
||||
|
||||
public ThumbnailController(ILibraryManager libraryManager, IConfiguration config)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_peoplePath = config.GetValue<string>("peoplePath");
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("poster/{showSlug}")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetShowThumb(string showSlug)
|
||||
{
|
||||
string path = (await _libraryManager.GetShow(showSlug))?.Path;
|
||||
if (path == null)
|
||||
return NotFound();
|
||||
|
||||
string thumb = Path.Combine(path, "poster.jpg");
|
||||
|
||||
if (System.IO.File.Exists(thumb))
|
||||
return new PhysicalFileResult(Path.GetFullPath(thumb), "image/jpg");
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
[HttpGet("logo/{showSlug}")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetShowLogo(string showSlug)
|
||||
{
|
||||
string path = (await _libraryManager.GetShow(showSlug))?.Path;
|
||||
if (path == null)
|
||||
return NotFound();
|
||||
|
||||
string thumb = Path.Combine(path, "logo.png");
|
||||
|
||||
if (System.IO.File.Exists(thumb))
|
||||
return new PhysicalFileResult(Path.GetFullPath(thumb), "image/jpg");
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
[HttpGet("backdrop/{showSlug}")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetShowBackdrop(string showSlug)
|
||||
{
|
||||
string path = (await _libraryManager.GetShow(showSlug))?.Path;
|
||||
if (path == null)
|
||||
return NotFound();
|
||||
|
||||
string thumb = Path.Combine(path, "backdrop.jpg");
|
||||
|
||||
if (System.IO.File.Exists(thumb))
|
||||
return new PhysicalFileResult(Path.GetFullPath(thumb), "image/jpg");
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
[HttpGet("peopleimg/{peopleSlug}")]
|
||||
[Authorize(Policy="Read")]
|
||||
public IActionResult GetPeopleIcon(string peopleSlug)
|
||||
{
|
||||
string thumbPath = Path.Combine(_peoplePath, peopleSlug + ".jpg");
|
||||
if (!System.IO.File.Exists(thumbPath))
|
||||
return NotFound();
|
||||
|
||||
return new PhysicalFileResult(Path.GetFullPath(thumbPath), "image/jpg");
|
||||
}
|
||||
|
||||
[HttpGet("thumb/{showSlug}-s{seasonNumber}e{episodeNumber}")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetEpisodeThumb(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
string path = (await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber))?.Path;
|
||||
if (path == null)
|
||||
return NotFound();
|
||||
|
||||
string thumb = Path.ChangeExtension(path, "jpg");
|
||||
|
||||
if (System.IO.File.Exists(thumb))
|
||||
return new PhysicalFileResult(Path.GetFullPath(thumb), "image/jpg");
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
56
Kyoo/Views/API/TrackApi.cs
Normal file
56
Kyoo/Views/API/TrackApi.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.CommonApi;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Kyoo.Api
|
||||
{
|
||||
[Route("api/track")]
|
||||
[Route("api/tracks")]
|
||||
[ApiController]
|
||||
public class TrackApi : CrudApi<Track>
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
public TrackApi(ILibraryManager libraryManager, IConfiguration configuration)
|
||||
: base(libraryManager.TrackRepository, configuration)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}/episode")]
|
||||
[Authorize(Policy = "Read")]
|
||||
public async Task<ActionResult<Episode>> GetEpisode(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _libraryManager.GetEpisode(x => x.Tracks.Any(y => y.ID == id));
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{slug}/episode")]
|
||||
[Authorize(Policy = "Read")]
|
||||
public async Task<ActionResult<Episode>> GetEpisode(string slug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO This won't work with the local repository implementation.
|
||||
// TODO Implement something like this (a dotnet-ef's QueryCompilationContext): https://stackoverflow.com/questions/62687811/how-can-i-convert-a-custom-function-to-a-sql-expression-for-entity-framework-cor
|
||||
return await _libraryManager.GetEpisode(x => x.Tracks.Any(y => y.Slug == slug));
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,11 @@
|
||||
using Kyoo.Controllers;
|
||||
using System.IO;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
|
||||
namespace Kyoo.Api
|
||||
{
|
||||
@ -16,14 +15,18 @@ namespace Kyoo.Api
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ITranscoder _transcoder;
|
||||
private readonly IFileManager _files;
|
||||
private readonly string _transmuxPath;
|
||||
private readonly string _transcodePath;
|
||||
private FileExtensionContentTypeProvider _provider;
|
||||
|
||||
public VideoApi(ILibraryManager libraryManager, ITranscoder transcoder, IConfiguration config)
|
||||
public VideoApi(ILibraryManager libraryManager,
|
||||
ITranscoder transcoder,
|
||||
IConfiguration config,
|
||||
IFileManager files)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_transcoder = transcoder;
|
||||
_files = files;
|
||||
_transmuxPath = config.GetValue<string>("transmuxTempPath");
|
||||
_transcodePath = config.GetValue<string>("transcodeTempPath");
|
||||
}
|
||||
@ -37,19 +40,6 @@ namespace Kyoo.Api
|
||||
ctx.HttpContext.Response.Headers.Add("Expires", "0");
|
||||
}
|
||||
|
||||
private string _GetContentType(string path)
|
||||
{
|
||||
if (_provider == null)
|
||||
{
|
||||
_provider = new FileExtensionContentTypeProvider();
|
||||
_provider.Mappings[".mkv"] = "video/x-matroska";
|
||||
}
|
||||
|
||||
if (_provider.TryGetContentType(path, out string contentType))
|
||||
return contentType;
|
||||
return "video/mp4";
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}")]
|
||||
[HttpGet("direct/{showSlug}-s{seasonNumber:int}e{episodeNumber:int}")]
|
||||
@ -60,9 +50,9 @@ namespace Kyoo.Api
|
||||
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
||||
|
||||
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
||||
if (episode != null && System.IO.File.Exists(episode.Path))
|
||||
return PhysicalFile(episode.Path, _GetContentType(episode.Path), true);
|
||||
return NotFound();
|
||||
if (episode == null)
|
||||
return NotFound();
|
||||
return _files.FileResult(episode.Path, true);
|
||||
}
|
||||
|
||||
[HttpGet("{movieSlug}")]
|
||||
@ -72,9 +62,9 @@ namespace Kyoo.Api
|
||||
{
|
||||
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
||||
|
||||
if (episode != null && System.IO.File.Exists(episode.Path))
|
||||
return PhysicalFile(episode.Path, _GetContentType(episode.Path), true);
|
||||
return NotFound();
|
||||
if (episode == null)
|
||||
return NotFound();
|
||||
return _files.FileResult(episode.Path, true);
|
||||
}
|
||||
|
||||
|
||||
@ -86,12 +76,12 @@ namespace Kyoo.Api
|
||||
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
||||
|
||||
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
||||
if (episode == null || !System.IO.File.Exists(episode.Path))
|
||||
if (episode == null)
|
||||
return NotFound();
|
||||
string path = await _transcoder.Transmux(episode);
|
||||
if (path == null)
|
||||
return StatusCode(500);
|
||||
return PhysicalFile(path, "application/x-mpegurl", true);
|
||||
return _files.FileResult(path, true);
|
||||
}
|
||||
|
||||
[HttpGet("transmux/{movieSlug}/master.m3u8")]
|
||||
@ -100,12 +90,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
||||
|
||||
if (episode == null || !System.IO.File.Exists(episode.Path))
|
||||
if (episode == null)
|
||||
return NotFound();
|
||||
string path = await _transcoder.Transmux(episode);
|
||||
if (path == null)
|
||||
return StatusCode(500);
|
||||
return PhysicalFile(path, "application/x-mpegurl", true);
|
||||
return _files.FileResult(path, true);
|
||||
}
|
||||
|
||||
[HttpGet("transcode/{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/master.m3u8")]
|
||||
@ -116,12 +106,12 @@ namespace Kyoo.Api
|
||||
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
||||
|
||||
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
||||
if (episode == null || !System.IO.File.Exists(episode.Path))
|
||||
if (episode == null)
|
||||
return NotFound();
|
||||
string path = await _transcoder.Transcode(episode);
|
||||
if (path == null)
|
||||
return StatusCode(500);
|
||||
return PhysicalFile(path, "application/x-mpegurl", true);
|
||||
return _files.FileResult(path, true);
|
||||
}
|
||||
|
||||
[HttpGet("transcode/{movieSlug}/master.m3u8")]
|
||||
@ -130,12 +120,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
||||
|
||||
if (episode == null || !System.IO.File.Exists(episode.Path))
|
||||
if (episode == null)
|
||||
return NotFound();
|
||||
string path = await _transcoder.Transcode(episode);
|
||||
if (path == null)
|
||||
return StatusCode(500);
|
||||
return PhysicalFile(path, "application/x-mpegurl", true);
|
||||
return _files.FileResult(path, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 57d382f7246287a611892359e444355e745d0795
|
||||
Subproject commit ab52f039021928cab9f6ed8c17a0488ca198ef74
|
||||
@ -18,7 +18,7 @@
|
||||
"ConnectionStrings": {
|
||||
"Database": "Server=127.0.0.1; Port=5432; Database=kyooDB; User Id=kyoo; Password=kyooPassword; Pooling=true; MaxPoolSize=95; Timeout=30;"
|
||||
},
|
||||
"parallelTasks": "40",
|
||||
"parallelTasks": "1",
|
||||
|
||||
"scheduledTasks": {
|
||||
"scan": "24:00:00"
|
||||
@ -29,6 +29,7 @@
|
||||
"transmuxTempPath": "cached/kyoo/transmux",
|
||||
"transcodeTempPath": "cached/kyoo/transcode",
|
||||
"peoplePath": "people",
|
||||
"providerPath": "providers",
|
||||
"profilePicturePath": "users/",
|
||||
"plugins": "plugins/",
|
||||
"defaultPermissions": "read,play,write,admin",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user