mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-11-03 19:17:16 -05:00 
			
		
		
		
	Updating the search api
This commit is contained in:
		
							parent
							
								
									6a0fb2dd38
								
							
						
					
					
						commit
						2df3562045
					
				@ -293,6 +293,26 @@ namespace Kyoo.Controllers
 | 
			
		||||
			Pagination limit = default
 | 
			
		||||
		) => GetLibrariesFromCollection(slug, where, new Sort<Library>(sort), limit);
 | 
			
		||||
		
 | 
			
		||||
		Task<ICollection<ShowRole>> GetRolesFromPeople(int showID,
 | 
			
		||||
			Expression<Func<ShowRole, bool>> where = null, 
 | 
			
		||||
			Sort<ShowRole> sort = default,
 | 
			
		||||
			Pagination limit = default);
 | 
			
		||||
		Task<ICollection<ShowRole>> GetRolesFromPeople(int showID,
 | 
			
		||||
			[Optional] Expression<Func<ShowRole, bool>> where,
 | 
			
		||||
			Expression<Func<ShowRole, object>> sort,
 | 
			
		||||
			Pagination limit = default
 | 
			
		||||
		) => GetRolesFromPeople(showID, where, new Sort<ShowRole>(sort), limit);
 | 
			
		||||
		
 | 
			
		||||
		Task<ICollection<ShowRole>> GetRolesFromPeople(string showSlug,
 | 
			
		||||
			Expression<Func<ShowRole, bool>> where = null, 
 | 
			
		||||
			Sort<ShowRole> sort = default,
 | 
			
		||||
			Pagination limit = default);
 | 
			
		||||
		Task<ICollection<ShowRole>> GetRolesFromPeople(string showSlug,
 | 
			
		||||
			[Optional] Expression<Func<ShowRole, bool>> where,
 | 
			
		||||
			Expression<Func<ShowRole, object>> sort,
 | 
			
		||||
			Pagination limit = default
 | 
			
		||||
		) => GetRolesFromPeople(showSlug, where, new Sort<ShowRole>(sort), limit);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		// Helpers
 | 
			
		||||
		Task AddShowLink(int showID, int? libraryID, int? collectionID);
 | 
			
		||||
 | 
			
		||||
@ -395,6 +395,26 @@ namespace Kyoo.Controllers
 | 
			
		||||
			Expression<Func<PeopleLink, object>> sort,
 | 
			
		||||
			Pagination limit = default
 | 
			
		||||
		) => GetFromShow(showSlug, where, new Sort<PeopleLink>(sort), limit);
 | 
			
		||||
		
 | 
			
		||||
		Task<ICollection<ShowRole>> GetFromPeople(int showID,
 | 
			
		||||
			Expression<Func<ShowRole, bool>> where = null, 
 | 
			
		||||
			Sort<ShowRole> sort = default,
 | 
			
		||||
			Pagination limit = default);
 | 
			
		||||
		Task<ICollection<ShowRole>> GetFromPeople(int showID,
 | 
			
		||||
			[Optional] Expression<Func<ShowRole, bool>> where,
 | 
			
		||||
			Expression<Func<ShowRole, object>> sort,
 | 
			
		||||
			Pagination limit = default
 | 
			
		||||
		) => GetFromPeople(showID, where, new Sort<ShowRole>(sort), limit);
 | 
			
		||||
		
 | 
			
		||||
		Task<ICollection<ShowRole>> GetFromPeople(string showSlug,
 | 
			
		||||
			Expression<Func<ShowRole, bool>> where = null, 
 | 
			
		||||
			Sort<ShowRole> sort = default,
 | 
			
		||||
			Pagination limit = default);
 | 
			
		||||
		Task<ICollection<ShowRole>> GetFromPeople(string showSlug,
 | 
			
		||||
			[Optional] Expression<Func<ShowRole, bool>> where,
 | 
			
		||||
			Expression<Func<ShowRole, object>> sort,
 | 
			
		||||
			Pagination limit = default
 | 
			
		||||
		) => GetFromPeople(showSlug, where, new Sort<ShowRole>(sort), limit);
 | 
			
		||||
	}
 | 
			
		||||
	public interface IProviderRepository : IRepository<ProviderID> {}
 | 
			
		||||
}
 | 
			
		||||
@ -412,6 +412,22 @@ namespace Kyoo.Controllers
 | 
			
		||||
			return LibraryRepository.GetFromCollection(slug, where, sort, limit);
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		public Task<ICollection<ShowRole>> GetRolesFromPeople(int id, 
 | 
			
		||||
			Expression<Func<ShowRole, bool>> where = null, 
 | 
			
		||||
			Sort<ShowRole> 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, 
 | 
			
		||||
			Pagination limit = default)
 | 
			
		||||
		{
 | 
			
		||||
			return PeopleRepository.GetFromPeople(slug, where, sort, limit);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public Task AddShowLink(int showID, int? libraryID, int? collectionID)
 | 
			
		||||
		{
 | 
			
		||||
			return ShowRepository.AddShowLink(showID, libraryID, collectionID);
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Models
 | 
			
		||||
@ -21,6 +23,12 @@ namespace Kyoo.Models
 | 
			
		||||
			set => People.Name = value;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		public string Poster
 | 
			
		||||
		{
 | 
			
		||||
			get => People.Poster;
 | 
			
		||||
			set => People.Poster = value;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		public IEnumerable<MetadataID> ExternalIDs
 | 
			
		||||
		{
 | 
			
		||||
			get => People.ExternalIDs;
 | 
			
		||||
@ -42,11 +50,79 @@ namespace Kyoo.Models
 | 
			
		||||
			Type = type;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public PeopleLink(string slug, string name, string role, string type, string imgPrimary, IEnumerable<MetadataID> externalIDs)
 | 
			
		||||
		public PeopleLink(string slug, 
 | 
			
		||||
			string name, 
 | 
			
		||||
			string role, 
 | 
			
		||||
			string type,
 | 
			
		||||
			string poster,
 | 
			
		||||
			IEnumerable<MetadataID> externalIDs)
 | 
			
		||||
		{
 | 
			
		||||
			People = new People(slug, name, imgPrimary, externalIDs);
 | 
			
		||||
			People = new People(slug, name, poster, externalIDs);
 | 
			
		||||
			Role = role;
 | 
			
		||||
			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(PeopleLink 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<PeopleLink, 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
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -13,7 +13,7 @@ namespace Kyoo.Models
 | 
			
		||||
		public string Poster { get; set; }
 | 
			
		||||
		public string Overview { get; set; }
 | 
			
		||||
		[NotMergable] [JsonIgnore] public virtual IEnumerable<CollectionLink> Links { get; set; }
 | 
			
		||||
		public virtual IEnumerable<Show> Shows
 | 
			
		||||
		[JsonIgnore] public virtual IEnumerable<Show> Shows
 | 
			
		||||
		{
 | 
			
		||||
			get => Links.Select(x => x.Show);
 | 
			
		||||
			set => Links = value.Select(x => new CollectionLink(this, x));
 | 
			
		||||
 | 
			
		||||
@ -9,18 +9,18 @@ namespace Kyoo.Models
 | 
			
		||||
		public int ID { get; set; }
 | 
			
		||||
		public string Slug { get; set; }
 | 
			
		||||
		public string Name { get; set; }
 | 
			
		||||
		[JsonIgnore] public string ImgPrimary { get; set; }
 | 
			
		||||
		public string Poster { get; set; }
 | 
			
		||||
		public virtual IEnumerable<MetadataID> ExternalIDs { get; set; }
 | 
			
		||||
		
 | 
			
		||||
		[JsonIgnore] public virtual IEnumerable<PeopleLink> Roles { get; set; }
 | 
			
		||||
		
 | 
			
		||||
		public People() {}
 | 
			
		||||
 | 
			
		||||
		public People(string slug, string name, string imgPrimary, IEnumerable<MetadataID> externalIDs)
 | 
			
		||||
		public People(string slug, string name, string poster, IEnumerable<MetadataID> externalIDs)
 | 
			
		||||
		{
 | 
			
		||||
			Slug = slug;
 | 
			
		||||
			Name = name;
 | 
			
		||||
			ImgPrimary = imgPrimary;
 | 
			
		||||
			Poster = poster;
 | 
			
		||||
			ExternalIDs = externalIDs;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -77,8 +77,9 @@ namespace Kyoo.Controllers
 | 
			
		||||
					{
 | 
			
		||||
						ContractResolver = new JsonPropertySelector(null, new Dictionary<Type, HashSet<string>>()
 | 
			
		||||
						{
 | 
			
		||||
							{typeof(Show), new HashSet<string> {"genres", "studio", "people", "seasons"}},
 | 
			
		||||
							{typeof(Episode), new HashSet<string> {"tracks"}}
 | 
			
		||||
							{typeof(Show), new HashSet<string> {"genres", "studio"}},
 | 
			
		||||
							{typeof(Episode), new HashSet<string> {"tracks"}},
 | 
			
		||||
							{typeof(PeopleLink), new HashSet<string> {"show"}}
 | 
			
		||||
						})
 | 
			
		||||
					},
 | 
			
		||||
				context.HttpContext.RequestServices.GetRequiredService<ArrayPool<char>>(), 
 | 
			
		||||
 | 
			
		||||
@ -102,8 +102,8 @@ namespace Kyoo.Controllers
 | 
			
		||||
				};
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ICollection<PeopleLink> people = await ApplyFilters(_database.PeopleLinks.Where(x => x.ShowID == showID),
 | 
			
		||||
				id => _database.PeopleLinks.FirstOrDefaultAsync(x => x.ID == id),
 | 
			
		||||
			ICollection<PeopleLink> people = await ApplyFilters(_database.PeopleRoles.Where(x => x.ShowID == showID),
 | 
			
		||||
				id => _database.PeopleRoles.FirstOrDefaultAsync(x => x.ID == id),
 | 
			
		||||
				x => x.People.Name,
 | 
			
		||||
				where,
 | 
			
		||||
				sort,
 | 
			
		||||
@ -128,8 +128,8 @@ namespace Kyoo.Controllers
 | 
			
		||||
				};
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			ICollection<PeopleLink> people = await ApplyFilters(_database.PeopleLinks.Where(x => x.Show.Slug == showSlug),
 | 
			
		||||
				id => _database.PeopleLinks.FirstOrDefaultAsync(x => x.ID == id),
 | 
			
		||||
			ICollection<PeopleLink> people = await ApplyFilters(_database.PeopleRoles.Where(x => x.Show.Slug == showSlug),
 | 
			
		||||
				id => _database.PeopleRoles.FirstOrDefaultAsync(x => x.ID == id),
 | 
			
		||||
				x => x.People.Name,
 | 
			
		||||
				where,
 | 
			
		||||
				sort,
 | 
			
		||||
@ -138,5 +138,39 @@ namespace Kyoo.Controllers
 | 
			
		||||
				throw new ItemNotFound();
 | 
			
		||||
			return people;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		public async Task<ICollection<ShowRole>> GetFromPeople(int peopleID,
 | 
			
		||||
			Expression<Func<ShowRole, bool>> where = null,
 | 
			
		||||
			Sort<ShowRole> 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,
 | 
			
		||||
				where,
 | 
			
		||||
				sort,
 | 
			
		||||
				limit);
 | 
			
		||||
			if (!roles.Any() && await Get(peopleID) == null)
 | 
			
		||||
				throw new ItemNotFound();
 | 
			
		||||
			return roles;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		public async Task<ICollection<ShowRole>> GetFromPeople(string slug,
 | 
			
		||||
			Expression<Func<ShowRole, bool>> where = null,
 | 
			
		||||
			Sort<ShowRole> 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,
 | 
			
		||||
				where,
 | 
			
		||||
				sort,
 | 
			
		||||
				limit);
 | 
			
		||||
			if (!roles.Any() && await Get(slug) == null)
 | 
			
		||||
				throw new ItemNotFound();
 | 
			
		||||
			return roles;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -78,9 +78,10 @@ namespace Kyoo.Controllers
 | 
			
		||||
 | 
			
		||||
		public override async Task<ICollection<Show>> Search(string query)
 | 
			
		||||
		{
 | 
			
		||||
			query = $"%{query}%";
 | 
			
		||||
			return await _database.Shows
 | 
			
		||||
				.Where(x => EF.Functions.ILike(x.Title, $"%{query}%") 
 | 
			
		||||
				            /*|| EF.Functions.ILike(x.Aliases, $"%{query}%")*/)
 | 
			
		||||
				.Where(x => EF.Functions.ILike(x.Title, query) 
 | 
			
		||||
				            /*|| x.Aliases.Any(y => EF.Functions.ILike(y, query))*/) // NOT TRANSLATABLE.
 | 
			
		||||
				.Take(20)
 | 
			
		||||
				.ToListAsync();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -68,10 +68,10 @@ namespace Kyoo.Controllers
 | 
			
		||||
			foreach (PeopleLink peop in people)
 | 
			
		||||
			{
 | 
			
		||||
				string localPath = Path.Combine(root, peop.People.Slug + ".jpg");
 | 
			
		||||
				if (peop.People.ImgPrimary == null)
 | 
			
		||||
				if (peop.People.Poster == null)
 | 
			
		||||
					continue;
 | 
			
		||||
				if (alwaysDownload || !File.Exists(localPath))
 | 
			
		||||
					await DownloadImage(peop.People.ImgPrimary, localPath, $"The profile picture of {peop.People.Name}");
 | 
			
		||||
					await DownloadImage(peop.People.Poster, localPath, $"The profile picture of {peop.People.Name}");
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			return people;
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ using IdentityServer4.EntityFramework.Interfaces;
 | 
			
		||||
using IdentityServer4.EntityFramework.Options;
 | 
			
		||||
using Kyoo.Models;
 | 
			
		||||
using Kyoo.Models.Exceptions;
 | 
			
		||||
using Kyoo.Models.Watch;
 | 
			
		||||
using Microsoft.AspNetCore.Identity;
 | 
			
		||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
@ -68,36 +69,45 @@ namespace Kyoo
 | 
			
		||||
		public DbSet<ProviderID> Providers { get; set; }
 | 
			
		||||
		public DbSet<MetadataID> MetadataIds { get; set; }
 | 
			
		||||
		
 | 
			
		||||
		public DbSet<LibraryLink> LibraryLinks { get; set; }
 | 
			
		||||
		public DbSet<CollectionLink> CollectionLinks { get; set; }
 | 
			
		||||
		public DbSet<PeopleLink> PeopleLinks { get; set; }
 | 
			
		||||
		public DbSet<PeopleLink> PeopleRoles { get; set; }
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		// This is used because EF doesn't support Many-To-Many relationships so for now we need to override the getter/setters to store this.
 | 
			
		||||
		public DbSet<LibraryLink> LibraryLinks { get; set; }
 | 
			
		||||
		public DbSet<CollectionLink> CollectionLinks { get; set; }
 | 
			
		||||
		public DbSet<GenreLink> GenreLinks { get; set; }
 | 
			
		||||
		public DbSet<ProviderLink> ProviderLinks { get; set; }
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		private readonly ValueConverter<IEnumerable<string>, string> _stringArrayConverter = 
 | 
			
		||||
			new ValueConverter<IEnumerable<string>, string>(
 | 
			
		||||
			arr => string.Join("|", arr),
 | 
			
		||||
			str => str.Split("|", StringSplitOptions.None));
 | 
			
		||||
		public DatabaseContext()
 | 
			
		||||
		{
 | 
			
		||||
			NpgsqlConnection.GlobalTypeMapper.MapEnum<Status>();
 | 
			
		||||
			NpgsqlConnection.GlobalTypeMapper.MapEnum<ItemType>();
 | 
			
		||||
			NpgsqlConnection.GlobalTypeMapper.MapEnum<StreamType>();
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		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)
 | 
			
		||||
		{
 | 
			
		||||
			base.OnModelCreating(modelBuilder);
 | 
			
		||||
 | 
			
		||||
			modelBuilder.Entity<Library>().Property(e => e.Paths)
 | 
			
		||||
				.HasConversion(_stringArrayConverter).Metadata
 | 
			
		||||
				.SetValueComparer(_stringArrayComparer);
 | 
			
		||||
			modelBuilder.Entity<Show>().Property(e => e.Aliases)
 | 
			
		||||
				.HasConversion(_stringArrayConverter).Metadata
 | 
			
		||||
				.SetValueComparer(_stringArrayComparer);
 | 
			
		||||
			modelBuilder.HasPostgresEnum<Status>();
 | 
			
		||||
			modelBuilder.HasPostgresEnum<ItemType>();
 | 
			
		||||
			modelBuilder.HasPostgresEnum<StreamType>();
 | 
			
		||||
 | 
			
		||||
			modelBuilder.Entity<Library>()
 | 
			
		||||
				.Property(x => x.Paths)
 | 
			
		||||
				.HasColumnType("text[]")
 | 
			
		||||
				.Metadata.SetValueComparer(_stringArrayComparer);
 | 
			
		||||
 | 
			
		||||
			modelBuilder.Entity<Show>()
 | 
			
		||||
				.Property(x => x.Aliases)
 | 
			
		||||
				.HasColumnType("text[]")
 | 
			
		||||
				.Metadata.SetValueComparer(_stringArrayComparer);
 | 
			
		||||
			
 | 
			
		||||
			
 | 
			
		||||
			modelBuilder.Entity<Track>()
 | 
			
		||||
				.Property(t => t.IsDefault)
 | 
			
		||||
@ -127,6 +137,7 @@ namespace Kyoo
 | 
			
		||||
			modelBuilder.Entity<PeopleLink>()
 | 
			
		||||
				.Ignore(x => x.Slug)
 | 
			
		||||
				.Ignore(x => x.Name)
 | 
			
		||||
				.Ignore(x => x.Poster)
 | 
			
		||||
				.Ignore(x => x.ExternalIDs);
 | 
			
		||||
 | 
			
		||||
			modelBuilder.Entity<Genre>()
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
// <auto-generated />
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Kyoo;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Infrastructure;
 | 
			
		||||
@ -10,13 +11,16 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
 | 
			
		||||
namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
{
 | 
			
		||||
    [DbContext(typeof(DatabaseContext))]
 | 
			
		||||
    [Migration("20200803040029_Initial")]
 | 
			
		||||
    [Migration("20200804172021_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", "unknow,video,audio,subtitle")
 | 
			
		||||
                .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
 | 
			
		||||
                .HasAnnotation("ProductVersion", "3.1.3")
 | 
			
		||||
                .HasAnnotation("Relational:MaxIdentifierLength", 63);
 | 
			
		||||
@ -167,8 +171,8 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
                    b.Property<string>("Name")
 | 
			
		||||
                        .HasColumnType("text");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Paths")
 | 
			
		||||
                        .HasColumnType("text");
 | 
			
		||||
                    b.Property<IEnumerable<string>>("Paths")
 | 
			
		||||
                        .HasColumnType("text[]");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Slug")
 | 
			
		||||
                        .HasColumnType("text");
 | 
			
		||||
@ -262,10 +266,10 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
                        .HasColumnType("integer")
 | 
			
		||||
                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("ImgPrimary")
 | 
			
		||||
                    b.Property<string>("Name")
 | 
			
		||||
                        .HasColumnType("text");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Name")
 | 
			
		||||
                    b.Property<string>("Poster")
 | 
			
		||||
                        .HasColumnType("text");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Slug")
 | 
			
		||||
@ -304,7 +308,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("ShowID");
 | 
			
		||||
 | 
			
		||||
                    b.ToTable("PeopleLinks");
 | 
			
		||||
                    b.ToTable("PeopleRoles");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("Kyoo.Models.ProviderID", b =>
 | 
			
		||||
@ -393,8 +397,8 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
                        .HasColumnType("integer")
 | 
			
		||||
                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Aliases")
 | 
			
		||||
                        .HasColumnType("text");
 | 
			
		||||
                    b.Property<IEnumerable<string>>("Aliases")
 | 
			
		||||
                        .HasColumnType("text[]");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Backdrop")
 | 
			
		||||
                        .HasColumnType("text");
 | 
			
		||||
@ -8,6 +8,11 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
    {
 | 
			
		||||
        protected override void Up(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.AlterDatabase()
 | 
			
		||||
                .Annotation("Npgsql:Enum:item_type", "show,movie,collection")
 | 
			
		||||
                .Annotation("Npgsql:Enum:status", "finished,airing,planned,unknown")
 | 
			
		||||
                .Annotation("Npgsql:Enum:stream_type", "unknow,video,audio,subtitle");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateTable(
 | 
			
		||||
                name: "Collections",
 | 
			
		||||
                columns: table => new
 | 
			
		||||
@ -46,7 +51,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
                        .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
 | 
			
		||||
                    Slug = table.Column<string>(nullable: true),
 | 
			
		||||
                    Name = table.Column<string>(nullable: true),
 | 
			
		||||
                    Paths = table.Column<string>(nullable: true)
 | 
			
		||||
                    Paths = table.Column<string[]>(type: "text[]", nullable: true)
 | 
			
		||||
                },
 | 
			
		||||
                constraints: table =>
 | 
			
		||||
                {
 | 
			
		||||
@ -61,7 +66,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
                        .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
 | 
			
		||||
                    Slug = table.Column<string>(nullable: true),
 | 
			
		||||
                    Name = table.Column<string>(nullable: true),
 | 
			
		||||
                    ImgPrimary = table.Column<string>(nullable: true)
 | 
			
		||||
                    Poster = table.Column<string>(nullable: true)
 | 
			
		||||
                },
 | 
			
		||||
                constraints: table =>
 | 
			
		||||
                {
 | 
			
		||||
@ -131,7 +136,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
                        .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
 | 
			
		||||
                    Slug = table.Column<string>(nullable: true),
 | 
			
		||||
                    Title = table.Column<string>(nullable: true),
 | 
			
		||||
                    Aliases = table.Column<string>(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),
 | 
			
		||||
@ -239,7 +244,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateTable(
 | 
			
		||||
                name: "PeopleLinks",
 | 
			
		||||
                name: "PeopleRoles",
 | 
			
		||||
                columns: table => new
 | 
			
		||||
                {
 | 
			
		||||
                    ID = table.Column<int>(nullable: false)
 | 
			
		||||
@ -251,15 +256,15 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
                },
 | 
			
		||||
                constraints: table =>
 | 
			
		||||
                {
 | 
			
		||||
                    table.PrimaryKey("PK_PeopleLinks", x => x.ID);
 | 
			
		||||
                    table.PrimaryKey("PK_PeopleRoles", x => x.ID);
 | 
			
		||||
                    table.ForeignKey(
 | 
			
		||||
                        name: "FK_PeopleLinks_People_PeopleID",
 | 
			
		||||
                        name: "FK_PeopleRoles_People_PeopleID",
 | 
			
		||||
                        column: x => x.PeopleID,
 | 
			
		||||
                        principalTable: "People",
 | 
			
		||||
                        principalColumn: "ID",
 | 
			
		||||
                        onDelete: ReferentialAction.Cascade);
 | 
			
		||||
                    table.ForeignKey(
 | 
			
		||||
                        name: "FK_PeopleLinks_Shows_ShowID",
 | 
			
		||||
                        name: "FK_PeopleRoles_Shows_ShowID",
 | 
			
		||||
                        column: x => x.ShowID,
 | 
			
		||||
                        principalTable: "Shows",
 | 
			
		||||
                        principalColumn: "ID",
 | 
			
		||||
@ -500,13 +505,13 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
                unique: true);
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateIndex(
 | 
			
		||||
                name: "IX_PeopleLinks_PeopleID",
 | 
			
		||||
                table: "PeopleLinks",
 | 
			
		||||
                name: "IX_PeopleRoles_PeopleID",
 | 
			
		||||
                table: "PeopleRoles",
 | 
			
		||||
                column: "PeopleID");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateIndex(
 | 
			
		||||
                name: "IX_PeopleLinks_ShowID",
 | 
			
		||||
                table: "PeopleLinks",
 | 
			
		||||
                name: "IX_PeopleRoles_ShowID",
 | 
			
		||||
                table: "PeopleRoles",
 | 
			
		||||
                column: "ShowID");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateIndex(
 | 
			
		||||
@ -569,7 +574,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
                name: "MetadataIds");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropTable(
 | 
			
		||||
                name: "PeopleLinks");
 | 
			
		||||
                name: "PeopleRoles");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropTable(
 | 
			
		||||
                name: "ProviderLinks");
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
// <auto-generated />
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Kyoo;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Infrastructure;
 | 
			
		||||
@ -15,6 +16,9 @@ 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", "unknow,video,audio,subtitle")
 | 
			
		||||
                .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
 | 
			
		||||
                .HasAnnotation("ProductVersion", "3.1.3")
 | 
			
		||||
                .HasAnnotation("Relational:MaxIdentifierLength", 63);
 | 
			
		||||
@ -165,8 +169,8 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
                    b.Property<string>("Name")
 | 
			
		||||
                        .HasColumnType("text");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Paths")
 | 
			
		||||
                        .HasColumnType("text");
 | 
			
		||||
                    b.Property<IEnumerable<string>>("Paths")
 | 
			
		||||
                        .HasColumnType("text[]");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Slug")
 | 
			
		||||
                        .HasColumnType("text");
 | 
			
		||||
@ -260,10 +264,10 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
                        .HasColumnType("integer")
 | 
			
		||||
                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("ImgPrimary")
 | 
			
		||||
                    b.Property<string>("Name")
 | 
			
		||||
                        .HasColumnType("text");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Name")
 | 
			
		||||
                    b.Property<string>("Poster")
 | 
			
		||||
                        .HasColumnType("text");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Slug")
 | 
			
		||||
@ -302,7 +306,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("ShowID");
 | 
			
		||||
 | 
			
		||||
                    b.ToTable("PeopleLinks");
 | 
			
		||||
                    b.ToTable("PeopleRoles");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("Kyoo.Models.ProviderID", b =>
 | 
			
		||||
@ -391,8 +395,8 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
 | 
			
		||||
                        .HasColumnType("integer")
 | 
			
		||||
                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Aliases")
 | 
			
		||||
                        .HasColumnType("text");
 | 
			
		||||
                    b.Property<IEnumerable<string>>("Aliases")
 | 
			
		||||
                        .HasColumnType("text[]");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Backdrop")
 | 
			
		||||
                        .HasColumnType("text");
 | 
			
		||||
 | 
			
		||||
@ -1,36 +0,0 @@
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Kyoo.Controllers;
 | 
			
		||||
using Kyoo.Models;
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Api
 | 
			
		||||
{
 | 
			
		||||
	[Route("api/[controller]")]
 | 
			
		||||
	[ApiController]
 | 
			
		||||
	public class PeopleController : ControllerBase
 | 
			
		||||
	{
 | 
			
		||||
		private readonly ILibraryManager _libraryManager;
 | 
			
		||||
 | 
			
		||||
		public PeopleController(ILibraryManager libraryManager)
 | 
			
		||||
		{
 | 
			
		||||
			_libraryManager = libraryManager;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[HttpGet("{peopleSlug}")]
 | 
			
		||||
		[Authorize(Policy="Read")]
 | 
			
		||||
		public async Task<ActionResult<Collection>> GetPeople(string peopleSlug)
 | 
			
		||||
		{
 | 
			
		||||
			People people = await _libraryManager.GetPeople(peopleSlug);
 | 
			
		||||
 | 
			
		||||
			if (people == null)
 | 
			
		||||
				return NotFound();
 | 
			
		||||
			return new Collection(people.Slug, people.Name, null, null)
 | 
			
		||||
			{
 | 
			
		||||
				Shows = people.Roles.Select(x => x.Show),
 | 
			
		||||
				Poster = "peopleimg/" + people.Slug
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										93
									
								
								Kyoo/Views/API/PeopleApi.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								Kyoo/Views/API/PeopleApi.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,93 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
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/people")]
 | 
			
		||||
	[ApiController]
 | 
			
		||||
	public class PeopleApi : CrudApi<People>
 | 
			
		||||
	{
 | 
			
		||||
		private readonly ILibraryManager _libraryManager;
 | 
			
		||||
 | 
			
		||||
		public PeopleApi(ILibraryManager libraryManager, IConfiguration configuration) 
 | 
			
		||||
			: base(libraryManager.PeopleRepository, configuration)
 | 
			
		||||
		{
 | 
			
		||||
			_libraryManager = libraryManager;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[HttpGet("{id:int}/role")]
 | 
			
		||||
		[HttpGet("{id:int}/roles")]
 | 
			
		||||
		[Authorize(Policy = "Read")]
 | 
			
		||||
		[JsonDetailed]
 | 
			
		||||
		public async Task<ActionResult<Page<ShowRole>>> 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),
 | 
			
		||||
					new Pagination(limit, afterID));
 | 
			
		||||
 | 
			
		||||
				return Page(resources, limit);
 | 
			
		||||
			}
 | 
			
		||||
			catch (ItemNotFound)
 | 
			
		||||
			{
 | 
			
		||||
				return NotFound();
 | 
			
		||||
			}
 | 
			
		||||
			catch (ArgumentException ex)
 | 
			
		||||
			{
 | 
			
		||||
				return BadRequest(new {Error = ex.Message});
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[HttpGet("{slug}/role")]
 | 
			
		||||
		[HttpGet("{slug}/roles")]
 | 
			
		||||
		[Authorize(Policy = "Read")]
 | 
			
		||||
		[JsonDetailed]
 | 
			
		||||
		public async Task<ActionResult<Page<ShowRole>>> 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> ressources = await _libraryManager.GetRolesFromPeople(slug,
 | 
			
		||||
					ApiHelper.ParseWhere<ShowRole>(where),
 | 
			
		||||
					new Sort<ShowRole>(sortBy),
 | 
			
		||||
					new Pagination(limit, afterID));
 | 
			
		||||
 | 
			
		||||
				return Page(ressources, limit);
 | 
			
		||||
			}
 | 
			
		||||
			catch (ItemNotFound)
 | 
			
		||||
			{
 | 
			
		||||
				return NotFound();
 | 
			
		||||
			}
 | 
			
		||||
			catch (ArgumentException ex)
 | 
			
		||||
			{
 | 
			
		||||
				return BadRequest(new {Error = ex.Message});
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -6,13 +6,13 @@ using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Api
 | 
			
		||||
{
 | 
			
		||||
	[Route("api/[controller]")]
 | 
			
		||||
	[Route("api/search")]
 | 
			
		||||
	[ApiController]
 | 
			
		||||
	public class SearchController : ControllerBase
 | 
			
		||||
	public class SearchApi : ControllerBase
 | 
			
		||||
	{
 | 
			
		||||
		private readonly ILibraryManager _libraryManager;
 | 
			
		||||
 | 
			
		||||
		public SearchController(ILibraryManager libraryManager)
 | 
			
		||||
		public SearchApi(ILibraryManager libraryManager)
 | 
			
		||||
		{
 | 
			
		||||
			_libraryManager = libraryManager;
 | 
			
		||||
		}
 | 
			
		||||
@ -21,7 +21,7 @@ namespace Kyoo.Api
 | 
			
		||||
		[Authorize(Policy="Read")]
 | 
			
		||||
		public async Task<ActionResult<SearchResult>> Search(string query)
 | 
			
		||||
		{
 | 
			
		||||
			SearchResult result = new SearchResult
 | 
			
		||||
			return new SearchResult
 | 
			
		||||
			{
 | 
			
		||||
				Query = query,
 | 
			
		||||
				Collections = await _libraryManager.SearchCollections(query),
 | 
			
		||||
@ -31,7 +31,6 @@ namespace Kyoo.Api
 | 
			
		||||
				Genres = await _libraryManager.SearchGenres(query),
 | 
			
		||||
				Studios = await _libraryManager.SearchStudios(query)
 | 
			
		||||
			};
 | 
			
		||||
			return result;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1 +1 @@
 | 
			
		||||
Subproject commit de1b3b069aa4f920bd5248223eda151b1eedf541
 | 
			
		||||
Subproject commit 3dad4b29d6565d08ef0bce1e450b5de65f6fac16
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user