mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-11-03 19:17:16 -05:00 
			
		
		
		
	Move from newtonsoft.json to system.text.json (#348)
This commit is contained in:
		
						commit
						35e37bbe76
					
				
							
								
								
									
										60
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										60
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							@ -1,60 +0,0 @@
 | 
			
		||||
name: Testing
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - master
 | 
			
		||||
      - next
 | 
			
		||||
  pull_request:
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  tests:
 | 
			
		||||
    name: Back tests
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    container: mcr.microsoft.com/dotnet/sdk:7.0
 | 
			
		||||
    services:
 | 
			
		||||
      postgres:
 | 
			
		||||
        image: postgres
 | 
			
		||||
        env:
 | 
			
		||||
          POSTGRES_PASSWORD: postgres
 | 
			
		||||
        options: >-
 | 
			
		||||
          --health-cmd pg_isready
 | 
			
		||||
          --health-interval 10s
 | 
			
		||||
          --health-timeout 5s
 | 
			
		||||
          --health-retries 5
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
      with:
 | 
			
		||||
        fetch-depth: 0
 | 
			
		||||
 | 
			
		||||
    - name: Build
 | 
			
		||||
      run: |
 | 
			
		||||
        cd back
 | 
			
		||||
        dotnet build '-p:SkipTranscoder=true' -p:CopyLocalLockFileAssemblies=true
 | 
			
		||||
        cp ./out/bin/Kyoo.Abstractions/Debug/net7.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll ./tests/Kyoo.Tests/bin/Debug/net7.0/
 | 
			
		||||
 | 
			
		||||
    - name: Test
 | 
			
		||||
      run: |
 | 
			
		||||
        cd back
 | 
			
		||||
        dotnet test --no-build '-p:CollectCoverage=true;CoverletOutputFormat=opencover' --logger "trx;LogFileName=TestOutputResults.xml"
 | 
			
		||||
      env:
 | 
			
		||||
        POSTGRES_HOST: postgres
 | 
			
		||||
        POSTGRES_USER: postgres
 | 
			
		||||
        POSTGRES_PASSWORD: postgres
 | 
			
		||||
 | 
			
		||||
    - name: Sanitize coverage output
 | 
			
		||||
      if: ${{ always() }}
 | 
			
		||||
      run: sed -i "s'$(pwd)/back'.'" back/tests/Kyoo.Tests/coverage.opencover.xml
 | 
			
		||||
 | 
			
		||||
    - name: Upload tests results
 | 
			
		||||
      if: ${{ always() }}
 | 
			
		||||
      uses: actions/upload-artifact@v4
 | 
			
		||||
      with:
 | 
			
		||||
        name: results.xml
 | 
			
		||||
        path: "**/TestOutputResults.xml"
 | 
			
		||||
 | 
			
		||||
    - name: Upload coverage report
 | 
			
		||||
      if: ${{ always() }}
 | 
			
		||||
      uses: actions/upload-artifact@v4
 | 
			
		||||
      with:
 | 
			
		||||
        name: coverage.xml
 | 
			
		||||
        path: "**/coverage.opencover.xml"
 | 
			
		||||
@ -12,7 +12,6 @@ COPY src/Kyoo.Host/Kyoo.Host.csproj src/Kyoo.Host/Kyoo.Host.csproj
 | 
			
		||||
COPY src/Kyoo.Postgresql/Kyoo.Postgresql.csproj src/Kyoo.Postgresql/Kyoo.Postgresql.csproj
 | 
			
		||||
COPY src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj
 | 
			
		||||
COPY src/Kyoo.Swagger/Kyoo.Swagger.csproj src/Kyoo.Swagger/Kyoo.Swagger.csproj
 | 
			
		||||
COPY tests/Kyoo.Tests/Kyoo.Tests.csproj tests/Kyoo.Tests/Kyoo.Tests.csproj
 | 
			
		||||
RUN dotnet restore -a $TARGETARCH
 | 
			
		||||
 | 
			
		||||
COPY . .
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,6 @@ COPY src/Kyoo.Host/Kyoo.Host.csproj src/Kyoo.Host/Kyoo.Host.csproj
 | 
			
		||||
COPY src/Kyoo.Postgresql/Kyoo.Postgresql.csproj src/Kyoo.Postgresql/Kyoo.Postgresql.csproj
 | 
			
		||||
COPY src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj
 | 
			
		||||
COPY src/Kyoo.Swagger/Kyoo.Swagger.csproj src/Kyoo.Swagger/Kyoo.Swagger.csproj
 | 
			
		||||
COPY tests/Kyoo.Tests/Kyoo.Tests.csproj tests/Kyoo.Tests/Kyoo.Tests.csproj
 | 
			
		||||
RUN dotnet restore
 | 
			
		||||
 | 
			
		||||
WORKDIR /kyoo
 | 
			
		||||
 | 
			
		||||
@ -7,12 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Postgresql", "src\Kyoo
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Authentication", "src\Kyoo.Authentication\Kyoo.Authentication.csproj", "{7A841335-6523-47DB-9717-80AA7BD943FD}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Tests", "tests\Kyoo.Tests\Kyoo.Tests.csproj", "{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Swagger", "src\Kyoo.Swagger\Kyoo.Swagger.csproj", "{7D1A7596-73F6-4D35-842E-A5AD9C620596}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{FEAE1B0E-D797-470F-9030-0EF743575ECC}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Host", "src\Kyoo.Host\Kyoo.Host.csproj", "{0938459E-2E2B-457F-8120-7D8CA93866A6}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Meilisearch", "src\Kyoo.Meilisearch\Kyoo.Meilisearch.csproj", "{F8E6018A-FD51-40EB-99FF-A26BA59F2762}"
 | 
			
		||||
@ -43,10 +39,6 @@ Global
 | 
			
		||||
		{6515380E-1E57-42DA-B6E3-E1C8A848818A}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{6515380E-1E57-42DA-B6E3-E1C8A848818A}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{6515380E-1E57-42DA-B6E3-E1C8A848818A}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
@ -68,7 +60,4 @@ Global
 | 
			
		||||
		{F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(NestedProjects) = preSolution
 | 
			
		||||
		{0C8AA7EA-E723-4532-852F-35AA4E8AFED5} = {FEAE1B0E-D797-470F-9030-0EF743575ECC}
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
EndGlobal
 | 
			
		||||
 | 
			
		||||
@ -68,11 +68,6 @@ namespace Kyoo.Abstractions.Controllers
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		IRepository<Episode> Episodes { get; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The repository that handle people.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		IRepository<People> People { get; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The repository that handle studios.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Autofac;
 | 
			
		||||
using JetBrains.Annotations;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Abstractions.Controllers
 | 
			
		||||
@ -31,7 +30,6 @@ namespace Kyoo.Abstractions.Controllers
 | 
			
		||||
	/// You can inject services in the IPlugin constructor.
 | 
			
		||||
	/// You should only inject well known services like an ILogger, IConfiguration or IWebHostEnvironment.
 | 
			
		||||
	/// </remarks>
 | 
			
		||||
	[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
 | 
			
		||||
	public interface IPlugin
 | 
			
		||||
	{
 | 
			
		||||
		/// <summary>
 | 
			
		||||
 | 
			
		||||
@ -10,11 +10,9 @@
 | 
			
		||||
		<PackageReference Include="Autofac" Version="7.1.0" />
 | 
			
		||||
		<PackageReference Include="Dapper" Version="2.1.24" />
 | 
			
		||||
		<PackageReference Include="EntityFrameworkCore.Projectables" Version="4.1.4-prebeta" />
 | 
			
		||||
		<PackageReference Include="JetBrains.Annotations" Version="2023.2.0" />
 | 
			
		||||
		<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0" />
 | 
			
		||||
		<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
 | 
			
		||||
		<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
 | 
			
		||||
		<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
 | 
			
		||||
		<PackageReference Include="Sprache" Version="2.3.1" />
 | 
			
		||||
		<PackageReference Include="System.ComponentModel.Composition" Version="7.0.0" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
@ -1,28 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Abstractions.Models.Attributes
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// Remove a property from the deserialization pipeline. The user can't input value for this property.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
 | 
			
		||||
	public class DeserializeIgnoreAttribute : Attribute { }
 | 
			
		||||
}
 | 
			
		||||
@ -1,28 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Abstractions.Models.Attributes
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// Remove an property from the serialization pipeline. It will simply be skipped.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
 | 
			
		||||
	public class SerializeIgnoreAttribute : Attribute { }
 | 
			
		||||
}
 | 
			
		||||
@ -19,27 +19,27 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Text.Json;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Newtonsoft.Json.Linq;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Models;
 | 
			
		||||
 | 
			
		||||
public class Patch<T> : Dictionary<string, JToken>
 | 
			
		||||
public class Patch<T> : Dictionary<string, JsonDocument>
 | 
			
		||||
	where T : class, IResource
 | 
			
		||||
{
 | 
			
		||||
	public Guid? Id => this.GetValueOrDefault(nameof(IResource.Id))?.ToObject<Guid>();
 | 
			
		||||
	public Guid? Id => this.GetValueOrDefault(nameof(IResource.Id))?.Deserialize<Guid>();
 | 
			
		||||
 | 
			
		||||
	public string? Slug => this.GetValueOrDefault(nameof(IResource.Slug))?.ToObject<string>();
 | 
			
		||||
	public string? Slug => this.GetValueOrDefault(nameof(IResource.Slug))?.Deserialize<string>();
 | 
			
		||||
 | 
			
		||||
	public T Apply(T current)
 | 
			
		||||
	{
 | 
			
		||||
		foreach ((string property, JToken value) in this)
 | 
			
		||||
		foreach ((string property, JsonDocument value) in this)
 | 
			
		||||
		{
 | 
			
		||||
			PropertyInfo prop = typeof(T).GetProperty(
 | 
			
		||||
				property,
 | 
			
		||||
				BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance
 | 
			
		||||
			)!;
 | 
			
		||||
			prop.SetValue(current, value.ToObject(prop.PropertyType));
 | 
			
		||||
			prop.SetValue(current, value.Deserialize(prop.PropertyType));
 | 
			
		||||
		}
 | 
			
		||||
		return current;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,81 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Abstractions.Models
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// A role a person played for a show. It can be an actor, musician, voice actor, director, writer...
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	/// <remarks>
 | 
			
		||||
	/// This class is not serialized like other classes.
 | 
			
		||||
	/// Based on the <see cref="ForPeople"/> field, it is serialized like
 | 
			
		||||
	/// a show with two extra fields (<see cref="Role"/> and <see cref="Type"/>).
 | 
			
		||||
	/// </remarks>
 | 
			
		||||
	public class PeopleRole : IResource
 | 
			
		||||
	{
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public Guid Id { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public string Slug => ForPeople ? Show!.Slug : People.Slug;
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Should this role be used as a Show substitute (the value is <c>true</c>) or
 | 
			
		||||
		/// as a People substitute (the value is <c>false</c>).
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public bool ForPeople { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The ID of the People playing the role.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public Guid PeopleID { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The people that played this role.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public People People { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The ID of the Show where the People playing in.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public Guid? ShowID { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The show where the People played in.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public Show? Show { get; set; }
 | 
			
		||||
 | 
			
		||||
		public Guid? MovieID { get; set; }
 | 
			
		||||
 | 
			
		||||
		public Movie? Movie { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The type of work the person has done for the show.
 | 
			
		||||
		/// That can be something like "Actor", "Writer", "Music", "Voice Actor"...
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public string Type { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The role the People played.
 | 
			
		||||
		/// This is mostly used to inform witch character was played for actor and voice actors.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public string Role { get; set; }
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -19,10 +19,9 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
using Kyoo.Utils;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Abstractions.Models
 | 
			
		||||
{
 | 
			
		||||
@ -65,13 +64,13 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The list of movies contained in this collection.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public ICollection<Movie>? Movies { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The list of shows contained in this collection.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public ICollection<Show>? Shows { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
 | 
			
		||||
@ -20,9 +20,9 @@ using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using EntityFrameworkCore.Projectables;
 | 
			
		||||
using JetBrains.Annotations;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,6 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
					);
 | 
			
		||||
				return GetSlug(ShowId.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber);
 | 
			
		||||
			}
 | 
			
		||||
			[UsedImplicitly]
 | 
			
		||||
			private set
 | 
			
		||||
			{
 | 
			
		||||
				Match match = Regex.Match(value, @"(?<show>.+)-s(?<season>\d+)e(?<episode>\d+)");
 | 
			
		||||
@ -90,7 +89,7 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The slug of the Show that contain this episode. If this is not set, this episode is ill-formed.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public string? ShowSlug { private get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@ -249,7 +248,7 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
					|| (x.SeasonNumber == SeasonNumber && x.EpisodeNumber > EpisodeNumber)
 | 
			
		||||
				);
 | 
			
		||||
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public ICollection<EpisodeWatchStatus>? Watched { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
 | 
			
		||||
@ -20,8 +20,8 @@ using System;
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Abstractions.Models
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,6 @@
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Abstractions.Models;
 | 
			
		||||
 | 
			
		||||
@ -36,21 +35,18 @@ public class JwtToken(string accessToken, string refreshToken, TimeSpan expireIn
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// The type of this token (always a Bearer).
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	[JsonProperty("token_type")]
 | 
			
		||||
	[JsonPropertyName("token_type")]
 | 
			
		||||
	public string TokenType => "Bearer";
 | 
			
		||||
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// The access token used to authorize requests.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	[JsonProperty("access_token")]
 | 
			
		||||
	[JsonPropertyName("access_token")]
 | 
			
		||||
	public string AccessToken { get; set; } = accessToken;
 | 
			
		||||
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// The refresh token used to retrieve a new access/refresh token when the access token has expired.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	[JsonProperty("refresh_token")]
 | 
			
		||||
	[JsonPropertyName("refresh_token")]
 | 
			
		||||
	public string RefreshToken { get; set; } = refreshToken;
 | 
			
		||||
 | 
			
		||||
@ -58,14 +54,12 @@ public class JwtToken(string accessToken, string refreshToken, TimeSpan expireIn
 | 
			
		||||
	/// When the access token will expire. After this time, the refresh token should be used to retrieve.
 | 
			
		||||
	/// a new token.cs
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	[JsonProperty("expire_in")]
 | 
			
		||||
	[JsonPropertyName("expire_in")]
 | 
			
		||||
	public TimeSpan ExpireIn => ExpireAt.Subtract(DateTime.UtcNow);
 | 
			
		||||
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// The exact date at which the access token will expire.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	[JsonProperty("expire_at")]
 | 
			
		||||
	[JsonPropertyName("expire_at")]
 | 
			
		||||
	public DateTime ExpireAt { get; set; } = DateTime.UtcNow + expireIn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -21,11 +21,11 @@ using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.ComponentModel.DataAnnotations.Schema;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
using EntityFrameworkCore.Projectables;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
using Kyoo.Utils;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Abstractions.Models
 | 
			
		||||
{
 | 
			
		||||
@ -36,7 +36,6 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		: IQuery,
 | 
			
		||||
			IResource,
 | 
			
		||||
			IMetadata,
 | 
			
		||||
			IOnMerge,
 | 
			
		||||
			IThumbnails,
 | 
			
		||||
			IAddedDate,
 | 
			
		||||
			ILibraryItem,
 | 
			
		||||
@ -119,11 +118,11 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public Image? Logo { get; set; }
 | 
			
		||||
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		[Column("air_date")]
 | 
			
		||||
		public DateTime? StartAir => AirDate;
 | 
			
		||||
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		[Column("air_date")]
 | 
			
		||||
		public DateTime? EndAir => AirDate;
 | 
			
		||||
 | 
			
		||||
@ -138,7 +137,7 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The ID of the Studio that made this show.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public Guid? StudioId { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@ -147,15 +146,10 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		[LoadableRelation(nameof(StudioId))]
 | 
			
		||||
		public Studio? Studio { get; set; }
 | 
			
		||||
 | 
			
		||||
		// /// <summary>
 | 
			
		||||
		// /// The list of people that made this show.
 | 
			
		||||
		// /// </summary>
 | 
			
		||||
		// [SerializeIgnore] public ICollection<PeopleRole>? People { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The list of collections that contains this show.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public ICollection<Collection>? Collections { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@ -164,7 +158,7 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		public VideoLinks Links =>
 | 
			
		||||
			new() { Direct = $"/movie/{Slug}/direct", Hls = $"/movie/{Slug}/master.m3u8", };
 | 
			
		||||
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public ICollection<MovieWatchStatus>? Watched { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@ -180,16 +174,6 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		// There is a global query filter to filter by user so we just need to do single.
 | 
			
		||||
		private MovieWatchStatus? _WatchStatus => Watched!.FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public void OnMerge(object merged)
 | 
			
		||||
		{
 | 
			
		||||
			// if (People != null)
 | 
			
		||||
			// {
 | 
			
		||||
			// 	foreach (PeopleRole link in People)
 | 
			
		||||
			// 		link.Movie = this;
 | 
			
		||||
			// }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public Movie() { }
 | 
			
		||||
 | 
			
		||||
		[JsonConstructor]
 | 
			
		||||
 | 
			
		||||
@ -1,80 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.ComponentModel.DataAnnotations.Schema;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
using Kyoo.Utils;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Abstractions.Models
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// An actor, voice actor, writer, animator, somebody who worked on a <see cref="Show"/>.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	[Table("people")]
 | 
			
		||||
	public class People : IQuery, IResource, IMetadata, IThumbnails
 | 
			
		||||
	{
 | 
			
		||||
		public static Sort DefaultSort => new Sort<People>.By(x => x.Name);
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public Guid Id { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		[MaxLength(256)]
 | 
			
		||||
		public string Slug { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The name of this person.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public string Name { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public Image? Poster { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public Image? Thumbnail { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public Image? Logo { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The list of roles this person has played in. See <see cref="PeopleRole"/> for more information.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		public ICollection<PeopleRole>? Roles { get; set; }
 | 
			
		||||
 | 
			
		||||
		public People() { }
 | 
			
		||||
 | 
			
		||||
		[JsonConstructor]
 | 
			
		||||
		public People(string name)
 | 
			
		||||
		{
 | 
			
		||||
			if (name != null)
 | 
			
		||||
			{
 | 
			
		||||
				Slug = Utility.ToSlug(name);
 | 
			
		||||
				Name = name;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -20,9 +20,9 @@ using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.ComponentModel.DataAnnotations.Schema;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using EntityFrameworkCore.Projectables;
 | 
			
		||||
using JetBrains.Annotations;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
 | 
			
		||||
@ -49,8 +49,6 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
					return $"{ShowId}-s{SeasonNumber}";
 | 
			
		||||
				return $"{ShowSlug ?? Show?.Slug}-s{SeasonNumber}";
 | 
			
		||||
			}
 | 
			
		||||
			[UsedImplicitly]
 | 
			
		||||
			[NotNull]
 | 
			
		||||
			private set
 | 
			
		||||
			{
 | 
			
		||||
				Match match = Regex.Match(value, @"(?<show>.+)-s(?<season>\d+)");
 | 
			
		||||
@ -67,7 +65,7 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The slug of the Show that contain this episode. If this is not set, this season is ill-formed.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public string? ShowSlug { private get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@ -124,7 +122,7 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The list of episodes that this season contains.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public ICollection<Episode>? Episodes { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
 | 
			
		||||
@ -21,11 +21,11 @@ using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.ComponentModel.DataAnnotations.Schema;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
using EntityFrameworkCore.Projectables;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
using Kyoo.Utils;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Abstractions.Models
 | 
			
		||||
{
 | 
			
		||||
@ -119,7 +119,7 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public string? Trailer { get; set; }
 | 
			
		||||
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		[Column("start_air")]
 | 
			
		||||
		public DateTime? AirDate => StartAir;
 | 
			
		||||
 | 
			
		||||
@ -129,7 +129,6 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The ID of the Studio that made this show.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		public Guid? StudioId { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@ -138,15 +137,10 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		[LoadableRelation(nameof(StudioId))]
 | 
			
		||||
		public Studio? Studio { get; set; }
 | 
			
		||||
 | 
			
		||||
		// /// <summary>
 | 
			
		||||
		// /// The list of people that made this show.
 | 
			
		||||
		// /// </summary>
 | 
			
		||||
		// [SerializeIgnore] public ICollection<PeopleRole>? People { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The different seasons in this show. If this is a movie, this list is always null or empty.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public ICollection<Season>? Seasons { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@ -154,13 +148,13 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// If this is a movie, there will be a unique episode (with the seasonNumber and episodeNumber set to null).
 | 
			
		||||
		/// Having an episode is necessary to store metadata and tracks.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public ICollection<Episode>? Episodes { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The list of collections that contains this show.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public ICollection<Collection>? Collections { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@ -213,7 +207,7 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
 | 
			
		||||
		private int _EpisodesCount => Episodes!.Count;
 | 
			
		||||
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public ICollection<ShowWatchStatus>? Watched { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@ -232,11 +226,6 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public void OnMerge(object merged)
 | 
			
		||||
		{
 | 
			
		||||
			// if (People != null)
 | 
			
		||||
			// {
 | 
			
		||||
			// 	foreach (PeopleRole link in People)
 | 
			
		||||
			// 		link.Show = this;
 | 
			
		||||
			// }
 | 
			
		||||
			if (Seasons != null)
 | 
			
		||||
			{
 | 
			
		||||
				foreach (Season season in Seasons)
 | 
			
		||||
 | 
			
		||||
@ -19,10 +19,9 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
using Kyoo.Utils;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Abstractions.Models
 | 
			
		||||
{
 | 
			
		||||
@ -48,13 +47,13 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The list of shows that are made by this studio.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public ICollection<Show>? Shows { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The list of movies that are made by this studio.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public ICollection<Movie>? Movies { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
 | 
			
		||||
@ -19,10 +19,9 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
using Kyoo.Utils;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Abstractions.Models
 | 
			
		||||
{
 | 
			
		||||
@ -53,7 +52,7 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The user password (hashed, it can't be read like that). The hashing format is implementation defined.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public string? Password { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Abstractions.Models
 | 
			
		||||
@ -56,25 +57,23 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The ID of the user that started watching this episode.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		public Guid UserId { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The user that started watching this episode.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public User User { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The ID of the movie started.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		public Guid MovieId { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The <see cref="Movie"/> started.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public Movie Movie { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc/>
 | 
			
		||||
@ -113,25 +112,23 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The ID of the user that started watching this episode.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		public Guid UserId { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The user that started watching this episode.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public User User { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The ID of the episode started.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		public Guid? EpisodeId { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The <see cref="Episode"/> started.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public Episode Episode { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc/>
 | 
			
		||||
@ -170,25 +167,23 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The ID of the user that started watching this episode.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		public Guid UserId { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The user that started watching this episode.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public User User { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The ID of the show started.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		public Guid ShowId { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The <see cref="Show"/> started.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public Show Show { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc/>
 | 
			
		||||
@ -212,7 +207,6 @@ namespace Kyoo.Abstractions.Models
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The ID of the episode started.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[SerializeIgnore]
 | 
			
		||||
		public Guid? NextEpisodeId { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,6 @@
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using JetBrains.Annotations;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Abstractions.Models.Utils
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,6 @@
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using JetBrains.Annotations;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Utils
 | 
			
		||||
{
 | 
			
		||||
@ -34,7 +33,6 @@ namespace Kyoo.Utils
 | 
			
		||||
		/// <param name="action">The action to execute is the list is empty</param>
 | 
			
		||||
		/// <typeparam name="T">The type of items inside the list</typeparam>
 | 
			
		||||
		/// <returns>The iterator proxied, there is no dual iterations.</returns>
 | 
			
		||||
		[LinqTunnel]
 | 
			
		||||
		public static IEnumerable<T> IfEmpty<T>(this IEnumerable<T> self, Action action)
 | 
			
		||||
		{
 | 
			
		||||
			static IEnumerable<T> Generator(IEnumerable<T> self, Action action)
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,6 @@ using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using JetBrains.Annotations;
 | 
			
		||||
using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Utils
 | 
			
		||||
@ -44,7 +43,6 @@ namespace Kyoo.Utils
 | 
			
		||||
		/// A dictionary with the missing elements of <paramref name="second"/>
 | 
			
		||||
		/// set to those of <paramref name="first"/>.
 | 
			
		||||
		/// </returns>
 | 
			
		||||
		[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
 | 
			
		||||
		public static IDictionary<T, T2>? CompleteDictionaries<T, T2>(
 | 
			
		||||
			IDictionary<T, T2>? first,
 | 
			
		||||
			IDictionary<T, T2>? second,
 | 
			
		||||
@ -87,11 +85,7 @@ namespace Kyoo.Utils
 | 
			
		||||
		/// </param>
 | 
			
		||||
		/// <typeparam name="T">Fields of T will be completed</typeparam>
 | 
			
		||||
		/// <returns><paramref name="first"/></returns>
 | 
			
		||||
		public static T Complete<T>(
 | 
			
		||||
			T first,
 | 
			
		||||
			T? second,
 | 
			
		||||
			[InstantHandle] Func<PropertyInfo, bool>? where = null
 | 
			
		||||
		)
 | 
			
		||||
		public static T Complete<T>(T first, T? second, Func<PropertyInfo, bool>? where = null)
 | 
			
		||||
		{
 | 
			
		||||
			if (second == null)
 | 
			
		||||
				return first;
 | 
			
		||||
 | 
			
		||||
@ -22,10 +22,8 @@ using System.Globalization;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Runtime.ExceptionServices;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using JetBrains.Annotations;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Utils
 | 
			
		||||
{
 | 
			
		||||
@ -232,7 +230,6 @@ namespace Kyoo.Utils
 | 
			
		||||
		/// </param>
 | 
			
		||||
		/// <exception cref="ArgumentException">No method match the given constraints.</exception>
 | 
			
		||||
		/// <returns>The method handle of the matching method.</returns>
 | 
			
		||||
		[PublicAPI]
 | 
			
		||||
		public static MethodInfo GetMethod(
 | 
			
		||||
			Type type,
 | 
			
		||||
			BindingFlags flag,
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,6 @@
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.12" />
 | 
			
		||||
		<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
 | 
			
		||||
		<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
 | 
			
		||||
 | 
			
		||||
		<ProjectReference Include="../Kyoo.Abstractions/Kyoo.Abstractions.csproj" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
@ -38,7 +38,6 @@ namespace Kyoo.Core.Controllers
 | 
			
		||||
			IRepository<Show> showRepository,
 | 
			
		||||
			IRepository<Season> seasonRepository,
 | 
			
		||||
			IRepository<Episode> episodeRepository,
 | 
			
		||||
			IRepository<People> peopleRepository,
 | 
			
		||||
			IRepository<Studio> studioRepository,
 | 
			
		||||
			IRepository<User> userRepository
 | 
			
		||||
		)
 | 
			
		||||
@ -51,7 +50,6 @@ namespace Kyoo.Core.Controllers
 | 
			
		||||
			Shows = showRepository;
 | 
			
		||||
			Seasons = seasonRepository;
 | 
			
		||||
			Episodes = episodeRepository;
 | 
			
		||||
			People = peopleRepository;
 | 
			
		||||
			Studios = studioRepository;
 | 
			
		||||
			Users = userRepository;
 | 
			
		||||
 | 
			
		||||
@ -64,7 +62,6 @@ namespace Kyoo.Core.Controllers
 | 
			
		||||
				Shows,
 | 
			
		||||
				Seasons,
 | 
			
		||||
				Episodes,
 | 
			
		||||
				People,
 | 
			
		||||
				Studios,
 | 
			
		||||
				Users
 | 
			
		||||
			};
 | 
			
		||||
@ -94,9 +91,6 @@ namespace Kyoo.Core.Controllers
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public IRepository<Episode> Episodes { get; }
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public IRepository<People> People { get; }
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public IRepository<Studio> Studios { get; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -42,29 +42,15 @@ namespace Kyoo.Core.Controllers
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		private readonly IRepository<Studio> _studios;
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// A people repository to handle creation/validation of related people.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		private readonly IRepository<People> _people;
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Create a new <see cref="MovieRepository"/>.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <param name="database">The database handle to use</param>
 | 
			
		||||
		/// <param name="studios">A studio repository</param>
 | 
			
		||||
		/// <param name="people">A people repository</param>
 | 
			
		||||
		/// <param name="thumbs">The thumbnail manager used to store images.</param>
 | 
			
		||||
		public MovieRepository(
 | 
			
		||||
			DatabaseContext database,
 | 
			
		||||
			IRepository<Studio> studios,
 | 
			
		||||
			IRepository<People> people,
 | 
			
		||||
			IThumbnailsManager thumbs
 | 
			
		||||
		)
 | 
			
		||||
			: base(database, thumbs)
 | 
			
		||||
		{
 | 
			
		||||
			_database = database;
 | 
			
		||||
			_studios = studios;
 | 
			
		||||
			_people = people;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
@ -98,17 +84,6 @@ namespace Kyoo.Core.Controllers
 | 
			
		||||
				resource.Studio = await _studios.CreateIfNotExists(resource.Studio);
 | 
			
		||||
				resource.StudioId = resource.Studio.Id;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// if (resource.People != null)
 | 
			
		||||
			// {
 | 
			
		||||
			// 	foreach (PeopleRole role in resource.People)
 | 
			
		||||
			// 	{
 | 
			
		||||
			// 		role.People = _database.LocalEntity<People>(role.People.Slug)
 | 
			
		||||
			// 			?? await _people.CreateIfNotExists(role.People);
 | 
			
		||||
			// 		role.PeopleID = role.People.Id;
 | 
			
		||||
			// 		_database.Entry(role).State = EntityState.Added;
 | 
			
		||||
			// 	}
 | 
			
		||||
			// }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
@ -121,12 +96,6 @@ namespace Kyoo.Core.Controllers
 | 
			
		||||
				await Database.Entry(resource).Reference(x => x.Studio).LoadAsync();
 | 
			
		||||
				resource.Studio = changed.Studio;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// if (changed.People != null)
 | 
			
		||||
			// {
 | 
			
		||||
			// 	await Database.Entry(resource).Collection(x => x.People!).LoadAsync();
 | 
			
		||||
			// 	resource.People = changed.People;
 | 
			
		||||
			// }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
 | 
			
		||||
@ -1,206 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Kyoo.Abstractions.Models.Utils;
 | 
			
		||||
using Kyoo.Postgresql;
 | 
			
		||||
using Kyoo.Utils;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Core.Controllers
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// A local repository to handle people.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	public class PeopleRepository : LocalRepository<People>
 | 
			
		||||
	{
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The database handle
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		private readonly DatabaseContext _database;
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// A lazy loaded show repository to validate requests from shows.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		private readonly Lazy<IRepository<Show>> _shows;
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Create a new <see cref="PeopleRepository"/>
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <param name="database">The database handle</param>
 | 
			
		||||
		/// <param name="shows">A lazy loaded show repository</param>
 | 
			
		||||
		/// <param name="thumbs">The thumbnail manager used to store images.</param>
 | 
			
		||||
		public PeopleRepository(
 | 
			
		||||
			DatabaseContext database,
 | 
			
		||||
			Lazy<IRepository<Show>> shows,
 | 
			
		||||
			IThumbnailsManager thumbs
 | 
			
		||||
		)
 | 
			
		||||
			: base(database, thumbs)
 | 
			
		||||
		{
 | 
			
		||||
			_database = database;
 | 
			
		||||
			_shows = shows;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public override Task<ICollection<People>> Search(
 | 
			
		||||
			string query,
 | 
			
		||||
			Include<People>? include = default
 | 
			
		||||
		)
 | 
			
		||||
		{
 | 
			
		||||
			throw new NotImplementedException();
 | 
			
		||||
			// return await AddIncludes(_database.People, include)
 | 
			
		||||
			// 	.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
 | 
			
		||||
			// 	.Take(20)
 | 
			
		||||
			// 	.ToListAsync();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public override async Task<People> Create(People obj)
 | 
			
		||||
		{
 | 
			
		||||
			await base.Create(obj);
 | 
			
		||||
			_database.Entry(obj).State = EntityState.Added;
 | 
			
		||||
			await _database.SaveChangesAsync(() => Get(obj.Slug));
 | 
			
		||||
			await IRepository<People>.OnResourceCreated(obj);
 | 
			
		||||
			return obj;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		protected override async Task Validate(People resource)
 | 
			
		||||
		{
 | 
			
		||||
			await base.Validate(resource);
 | 
			
		||||
 | 
			
		||||
			if (resource.Roles != null)
 | 
			
		||||
			{
 | 
			
		||||
				foreach (PeopleRole role in resource.Roles)
 | 
			
		||||
				{
 | 
			
		||||
					role.Show =
 | 
			
		||||
						_database.LocalEntity<Show>(role.Show!.Slug)
 | 
			
		||||
						?? await _shows.Value.CreateIfNotExists(role.Show);
 | 
			
		||||
					role.ShowID = role.Show.Id;
 | 
			
		||||
					_database.Entry(role).State = EntityState.Added;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		protected override async Task EditRelations(People resource, People changed)
 | 
			
		||||
		{
 | 
			
		||||
			await Validate(changed);
 | 
			
		||||
 | 
			
		||||
			if (changed.Roles != null)
 | 
			
		||||
			{
 | 
			
		||||
				await Database.Entry(resource).Collection(x => x.Roles!).LoadAsync();
 | 
			
		||||
				resource.Roles = changed.Roles;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public override async Task Delete(People obj)
 | 
			
		||||
		{
 | 
			
		||||
			_database.Entry(obj).State = EntityState.Deleted;
 | 
			
		||||
			obj.Roles.ForEach(x => _database.Entry(x).State = EntityState.Deleted);
 | 
			
		||||
			await _database.SaveChangesAsync();
 | 
			
		||||
			await base.Delete(obj);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// /// <inheritdoc />
 | 
			
		||||
		// public Task<ICollection<PeopleRole>> GetFromShow(int showID,
 | 
			
		||||
		// 	Expression<Func<PeopleRole, bool>>? where = null,
 | 
			
		||||
		// 	Sort<PeopleRole>? sort = default,
 | 
			
		||||
		// 	Pagination? limit = default)
 | 
			
		||||
		// {
 | 
			
		||||
		// ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles
 | 
			
		||||
		// 		.Where(x => x.ShowID == showID)
 | 
			
		||||
		// 		.Include(x => x.People),
 | 
			
		||||
		// 	x => x.People.Name,
 | 
			
		||||
		// 	where,
 | 
			
		||||
		// 	sort,
 | 
			
		||||
		// 	limit);
 | 
			
		||||
		// if (!people.Any() && await _shows.Value.GetOrDefault(showID) == null)
 | 
			
		||||
		// 	throw new ItemNotFoundException();
 | 
			
		||||
		// foreach (PeopleRole role in people)
 | 
			
		||||
		// 	role.ForPeople = true;
 | 
			
		||||
		// return people;
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		// /// <inheritdoc />
 | 
			
		||||
		// public Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
 | 
			
		||||
		// 	Expression<Func<PeopleRole, bool>>? where = null,
 | 
			
		||||
		// 	Sort<PeopleRole>? sort = default,
 | 
			
		||||
		// 	Pagination? limit = default)
 | 
			
		||||
		// {
 | 
			
		||||
		// 	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,
 | 
			
		||||
		// 		sort,
 | 
			
		||||
		// 		limit);
 | 
			
		||||
		// 	if (!people.Any() && await _shows.Value.GetOrDefault(showSlug) == null)
 | 
			
		||||
		// 		throw new ItemNotFoundException();
 | 
			
		||||
		// 	foreach (PeopleRole role in people)
 | 
			
		||||
		// 		role.ForPeople = true;
 | 
			
		||||
		// 	return people;
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		// /// <inheritdoc />
 | 
			
		||||
		// public Task<ICollection<PeopleRole>> GetFromPeople(int id,
 | 
			
		||||
		// 	Expression<Func<PeopleRole, bool>>? where = null,
 | 
			
		||||
		// 	Sort<PeopleRole>? sort = default,
 | 
			
		||||
		// 	Pagination? limit = default)
 | 
			
		||||
		// {
 | 
			
		||||
		// 	ICollection<PeopleRole> roles = await ApplyFilters(_database.PeopleRoles
 | 
			
		||||
		// 			.Where(x => x.PeopleID == id)
 | 
			
		||||
		// 			.Include(x => x.Show),
 | 
			
		||||
		// 		y => _database.PeopleRoles.FirstOrDefaultAsync(x => x.ID == y),
 | 
			
		||||
		// 		x => x.Show.Title,
 | 
			
		||||
		// 		where,
 | 
			
		||||
		// 		sort,
 | 
			
		||||
		// 		limit);
 | 
			
		||||
		// 	if (!roles.Any() && await GetOrDefault(id) == null)
 | 
			
		||||
		// 		throw new ItemNotFoundException();
 | 
			
		||||
		// 	return roles;
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		// /// <inheritdoc />
 | 
			
		||||
		// public Task<ICollection<PeopleRole>> GetFromPeople(string slug,
 | 
			
		||||
		// 	Expression<Func<PeopleRole, bool>>? where = null,
 | 
			
		||||
		// 	Sort<PeopleRole>? sort = default,
 | 
			
		||||
		// 	Pagination? limit = default)
 | 
			
		||||
		// {
 | 
			
		||||
		// 	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);
 | 
			
		||||
		// 	if (!roles.Any() && await GetOrDefault(slug) == null)
 | 
			
		||||
		// 		throw new ItemNotFoundException();
 | 
			
		||||
		// 	return roles;
 | 
			
		||||
		// }
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -43,29 +43,15 @@ namespace Kyoo.Core.Controllers
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		private readonly IRepository<Studio> _studios;
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// A people repository to handle creation/validation of related people.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		private readonly IRepository<People> _people;
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Create a new <see cref="ShowRepository"/>.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <param name="database">The database handle to use</param>
 | 
			
		||||
		/// <param name="studios">A studio repository</param>
 | 
			
		||||
		/// <param name="people">A people repository</param>
 | 
			
		||||
		/// <param name="thumbs">The thumbnail manager used to store images.</param>
 | 
			
		||||
		public ShowRepository(
 | 
			
		||||
			DatabaseContext database,
 | 
			
		||||
			IRepository<Studio> studios,
 | 
			
		||||
			IRepository<People> people,
 | 
			
		||||
			IThumbnailsManager thumbs
 | 
			
		||||
		)
 | 
			
		||||
			: base(database, thumbs)
 | 
			
		||||
		{
 | 
			
		||||
			_database = database;
 | 
			
		||||
			_studios = studios;
 | 
			
		||||
			_people = people;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
@ -99,17 +85,6 @@ namespace Kyoo.Core.Controllers
 | 
			
		||||
				resource.Studio = await _studios.CreateIfNotExists(resource.Studio);
 | 
			
		||||
				resource.StudioId = resource.Studio.Id;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// if (resource.People != null)
 | 
			
		||||
			// {
 | 
			
		||||
			// 	foreach (PeopleRole role in resource.People)
 | 
			
		||||
			// 	{
 | 
			
		||||
			// 		role.People = _database.LocalEntity<People>(role.People.Slug)
 | 
			
		||||
			// 			?? await _people.CreateIfNotExists(role.People);
 | 
			
		||||
			// 		role.PeopleID = role.People.Id;
 | 
			
		||||
			// 		_database.Entry(role).State = EntityState.Added;
 | 
			
		||||
			// 	}
 | 
			
		||||
			// }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
@ -122,12 +97,6 @@ namespace Kyoo.Core.Controllers
 | 
			
		||||
				await Database.Entry(resource).Reference(x => x.Studio).LoadAsync();
 | 
			
		||||
				resource.Studio = changed.Studio;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// if (changed.People != null)
 | 
			
		||||
			// {
 | 
			
		||||
			// 	await Database.Entry(resource).Collection(x => x.People!).LoadAsync();
 | 
			
		||||
			// 	resource.People = changed.People;
 | 
			
		||||
			// }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,8 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text.Json;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
using AspNetCore.Proxy;
 | 
			
		||||
using Autofac;
 | 
			
		||||
using Kyoo.Abstractions;
 | 
			
		||||
@ -30,10 +32,6 @@ using Microsoft.AspNetCore.Builder;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.AspNetCore.Routing;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using Microsoft.Extensions.Options;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using Newtonsoft.Json.Converters;
 | 
			
		||||
using JsonOptions = Kyoo.Core.Api.JsonOptions;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Core
 | 
			
		||||
{
 | 
			
		||||
@ -66,7 +64,6 @@ namespace Kyoo.Core
 | 
			
		||||
			builder.RegisterRepository<ShowRepository>();
 | 
			
		||||
			builder.RegisterRepository<SeasonRepository>();
 | 
			
		||||
			builder.RegisterRepository<EpisodeRepository>();
 | 
			
		||||
			builder.RegisterRepository<PeopleRepository>();
 | 
			
		||||
			builder.RegisterRepository<StudioRepository>();
 | 
			
		||||
			builder.RegisterRepository<UserRepository>().As<IUserRepository>();
 | 
			
		||||
			builder.RegisterRepository<NewsRepository>();
 | 
			
		||||
@ -87,7 +84,6 @@ namespace Kyoo.Core
 | 
			
		||||
		public void Configure(IServiceCollection services)
 | 
			
		||||
		{
 | 
			
		||||
			services.AddHttpContextAccessor();
 | 
			
		||||
			services.AddTransient<IConfigureOptions<MvcNewtonsoftJsonOptions>, JsonOptions>();
 | 
			
		||||
 | 
			
		||||
			services
 | 
			
		||||
				.AddMvcCore(options =>
 | 
			
		||||
@ -97,10 +93,14 @@ namespace Kyoo.Core
 | 
			
		||||
					options.ModelBinderProviders.Insert(0, new IncludeBinder.Provider());
 | 
			
		||||
					options.ModelBinderProviders.Insert(0, new FilterBinder.Provider());
 | 
			
		||||
				})
 | 
			
		||||
				.AddNewtonsoftJson(x =>
 | 
			
		||||
				.AddJsonOptions(x =>
 | 
			
		||||
				{
 | 
			
		||||
					x.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
 | 
			
		||||
					x.SerializerSettings.Converters.Add(new StringEnumConverter());
 | 
			
		||||
					x.JsonSerializerOptions.TypeInfoResolver = new WithKindResolver()
 | 
			
		||||
					{
 | 
			
		||||
						Modifiers = { WithKindResolver.HandleLoadableFields }
 | 
			
		||||
					};
 | 
			
		||||
					x.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
 | 
			
		||||
					x.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
 | 
			
		||||
				})
 | 
			
		||||
				.AddDataAnnotations()
 | 
			
		||||
				.AddControllersAsServices()
 | 
			
		||||
 | 
			
		||||
@ -12,8 +12,6 @@
 | 
			
		||||
		<PackageReference Include="InterpolatedSql.Dapper" Version="2.1.0" />
 | 
			
		||||
		<PackageReference Include="FlexLabs.EntityFrameworkCore.Upsert" Version="7.0.0" />
 | 
			
		||||
		<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
 | 
			
		||||
		<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.12" />
 | 
			
		||||
		<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
 | 
			
		||||
		<PackageReference Include="SkiaSharp" Version="2.88.6" />
 | 
			
		||||
		<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.6" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
@ -1,56 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.Extensions.Options;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Core.Api
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// The custom options of newtonsoft json. This simply add the <see cref="PeopleRoleConverter"/> and set
 | 
			
		||||
	/// the <see cref="JsonSerializerContract"/>. It is on a separate class to use dependency injection.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	public class JsonOptions : IConfigureOptions<MvcNewtonsoftJsonOptions>
 | 
			
		||||
	{
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The http context accessor given to the <see cref="JsonSerializerContract"/>.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		private readonly IHttpContextAccessor _httpContextAccessor;
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Create a new <see cref="JsonOptions"/>.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <param name="httpContextAccessor">
 | 
			
		||||
		/// The http context accessor given to the <see cref="JsonSerializerContract"/>.
 | 
			
		||||
		/// </param>
 | 
			
		||||
		public JsonOptions(IHttpContextAccessor httpContextAccessor)
 | 
			
		||||
		{
 | 
			
		||||
			_httpContextAccessor = httpContextAccessor;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public void Configure(MvcNewtonsoftJsonOptions options)
 | 
			
		||||
		{
 | 
			
		||||
			options.SerializerSettings.ContractResolver = new JsonSerializerContract(
 | 
			
		||||
				_httpContextAccessor
 | 
			
		||||
			);
 | 
			
		||||
			options.SerializerSettings.Converters.Add(new PeopleRoleConverter());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,129 +1,69 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
// // Kyoo - A portable and vast media library solution.
 | 
			
		||||
// // Copyright (c) Kyoo.
 | 
			
		||||
// //
 | 
			
		||||
// // See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
// //
 | 
			
		||||
// // Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// // it under the terms of the GNU General Public License as published by
 | 
			
		||||
// // the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// // any later version.
 | 
			
		||||
// //
 | 
			
		||||
// // Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// // GNU General Public License for more details.
 | 
			
		||||
// //
 | 
			
		||||
// // You should have received a copy of the GNU General Public License
 | 
			
		||||
// // along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
// using System;
 | 
			
		||||
// using System.Collections.Generic;
 | 
			
		||||
// using System.Linq;
 | 
			
		||||
// using System.Net.Http.Formatting;
 | 
			
		||||
// using System.Reflection;
 | 
			
		||||
// using Kyoo.Abstractions.Models;
 | 
			
		||||
// using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
// using Microsoft.AspNetCore.Http;
 | 
			
		||||
// using static System.Text.Json.JsonNamingPolicy;
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
// namespace Kyoo.Core.Api
 | 
			
		||||
// {
 | 
			
		||||
// 	/// <summary>
 | 
			
		||||
// 	/// A custom json serializer that respects <see cref="SerializeIgnoreAttribute"/> and
 | 
			
		||||
// 	/// <see cref="DeserializeIgnoreAttribute"/>. It also handle <see cref="LoadableRelationAttribute"/> via the
 | 
			
		||||
// 	/// <c>fields</c> query parameter and <see cref="IThumbnails"/> items.
 | 
			
		||||
// 	/// </summary>
 | 
			
		||||
// 	public class JsonSerializerContract(IHttpContextAccessor? httpContextAccessor, MediaTypeFormatter formatter)
 | 
			
		||||
// 		: JsonContractResolver(formatter)
 | 
			
		||||
// 	{
 | 
			
		||||
// 		/// <inheritdoc />
 | 
			
		||||
// 		protected override JsonProperty CreateProperty(
 | 
			
		||||
// 			MemberInfo member,
 | 
			
		||||
// 			MemberSerialization memberSerialization
 | 
			
		||||
// 		)
 | 
			
		||||
// 		{
 | 
			
		||||
// 			JsonProperty property = base.CreateProperty(member, memberSerialization);
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using Newtonsoft.Json.Serialization;
 | 
			
		||||
using static System.Text.Json.JsonNamingPolicy;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Core.Api
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// A custom json serializer that respects <see cref="SerializeIgnoreAttribute"/> and
 | 
			
		||||
	/// <see cref="DeserializeIgnoreAttribute"/>. It also handle <see cref="LoadableRelationAttribute"/> via the
 | 
			
		||||
	/// <c>fields</c> query parameter and <see cref="IThumbnails"/> items.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	public class JsonSerializerContract : CamelCasePropertyNamesContractResolver
 | 
			
		||||
	{
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The http context accessor used to retrieve the <c>fields</c> query parameter as well as the type of
 | 
			
		||||
		/// resource currently serializing.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		private readonly IHttpContextAccessor _httpContextAccessor;
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Create a new <see cref="JsonSerializerContract"/>.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <param name="httpContextAccessor">The http context accessor to use.</param>
 | 
			
		||||
		public JsonSerializerContract(IHttpContextAccessor httpContextAccessor)
 | 
			
		||||
		{
 | 
			
		||||
			_httpContextAccessor = httpContextAccessor;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		protected override JsonProperty CreateProperty(
 | 
			
		||||
			MemberInfo member,
 | 
			
		||||
			MemberSerialization memberSerialization
 | 
			
		||||
		)
 | 
			
		||||
		{
 | 
			
		||||
			JsonProperty property = base.CreateProperty(member, memberSerialization);
 | 
			
		||||
 | 
			
		||||
			LoadableRelationAttribute? relation =
 | 
			
		||||
				member.GetCustomAttribute<LoadableRelationAttribute>();
 | 
			
		||||
			if (relation != null)
 | 
			
		||||
			{
 | 
			
		||||
				property.ShouldSerialize = _ =>
 | 
			
		||||
				{
 | 
			
		||||
					if (
 | 
			
		||||
						_httpContextAccessor.HttpContext!.Items["fields"]
 | 
			
		||||
						is not ICollection<string> fields
 | 
			
		||||
					)
 | 
			
		||||
						return false;
 | 
			
		||||
					return fields.Contains(member.Name);
 | 
			
		||||
				};
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (member.GetCustomAttribute<SerializeIgnoreAttribute>() != null)
 | 
			
		||||
				property.ShouldSerialize = _ => false;
 | 
			
		||||
			if (member.GetCustomAttribute<DeserializeIgnoreAttribute>() != null)
 | 
			
		||||
				property.ShouldDeserialize = _ => false;
 | 
			
		||||
			return property;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		protected override IList<JsonProperty> CreateProperties(
 | 
			
		||||
			Type type,
 | 
			
		||||
			MemberSerialization memberSerialization
 | 
			
		||||
		)
 | 
			
		||||
		{
 | 
			
		||||
			IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
 | 
			
		||||
 | 
			
		||||
			if (
 | 
			
		||||
				properties.All(x => x.PropertyName != "kind")
 | 
			
		||||
				&& type.IsAssignableTo(typeof(IResource))
 | 
			
		||||
			)
 | 
			
		||||
			{
 | 
			
		||||
				properties.Add(
 | 
			
		||||
					new JsonProperty()
 | 
			
		||||
					{
 | 
			
		||||
						DeclaringType = type,
 | 
			
		||||
						PropertyName = "kind",
 | 
			
		||||
						UnderlyingName = "kind",
 | 
			
		||||
						PropertyType = typeof(string),
 | 
			
		||||
						ValueProvider = new FixedValueProvider(CamelCase.ConvertName(type.Name)),
 | 
			
		||||
						Readable = true,
 | 
			
		||||
						Writable = false,
 | 
			
		||||
						TypeNameHandling = TypeNameHandling.None,
 | 
			
		||||
					}
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return properties;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public class FixedValueProvider : IValueProvider
 | 
			
		||||
		{
 | 
			
		||||
			private readonly object _value;
 | 
			
		||||
 | 
			
		||||
			public FixedValueProvider(object value)
 | 
			
		||||
			{
 | 
			
		||||
				_value = value;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			public object GetValue(object target) => _value;
 | 
			
		||||
 | 
			
		||||
			public void SetValue(object target, object? value) =>
 | 
			
		||||
				throw new NotImplementedException();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
// 			LoadableRelationAttribute? relation =
 | 
			
		||||
// 				member.GetCustomAttribute<LoadableRelationAttribute>();
 | 
			
		||||
// 			if (relation != null)
 | 
			
		||||
// 			{
 | 
			
		||||
// 				if (httpContextAccessor != null)
 | 
			
		||||
// 				{
 | 
			
		||||
// 					property.ShouldSerialize = _ =>
 | 
			
		||||
// 					{
 | 
			
		||||
// 						if (
 | 
			
		||||
// 							httpContextAccessor.HttpContext!.Items["fields"]
 | 
			
		||||
// 							is not ICollection<string> fields
 | 
			
		||||
// 						)
 | 
			
		||||
// 							return false;
 | 
			
		||||
// 						return fields.Contains(member.Name);
 | 
			
		||||
// 					};
 | 
			
		||||
// 				}
 | 
			
		||||
// 				else
 | 
			
		||||
// 					property.ShouldSerialize = _ => true;
 | 
			
		||||
// 			}
 | 
			
		||||
// 			return property;
 | 
			
		||||
// 		}
 | 
			
		||||
// 	}
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
@ -1,76 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using Newtonsoft.Json.Linq;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Core.Api
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// A custom role's convertor to inline the person or the show depending on the value of
 | 
			
		||||
	/// <see cref="PeopleRole.ForPeople"/>.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	public class PeopleRoleConverter : JsonConverter<PeopleRole>
 | 
			
		||||
	{
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public override void WriteJson(
 | 
			
		||||
			JsonWriter writer,
 | 
			
		||||
			PeopleRole? value,
 | 
			
		||||
			JsonSerializer serializer
 | 
			
		||||
		)
 | 
			
		||||
		{
 | 
			
		||||
			// if (value == null)
 | 
			
		||||
			// {
 | 
			
		||||
			// 	writer.WriteNull();
 | 
			
		||||
			// 	return;
 | 
			
		||||
			// }
 | 
			
		||||
			//
 | 
			
		||||
			// 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;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <inheritdoc />
 | 
			
		||||
		public override PeopleRole ReadJson(
 | 
			
		||||
			JsonReader reader,
 | 
			
		||||
			Type objectType,
 | 
			
		||||
			PeopleRole? existingValue,
 | 
			
		||||
			bool hasExistingValue,
 | 
			
		||||
			JsonSerializer serializer
 | 
			
		||||
		)
 | 
			
		||||
		{
 | 
			
		||||
			throw new NotImplementedException();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,99 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Text.Json;
 | 
			
		||||
using System.Text.Json.Serialization.Metadata;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using static System.Text.Json.JsonNamingPolicy;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Core.Api;
 | 
			
		||||
 | 
			
		||||
public class WithKindResolver : DefaultJsonTypeInfoResolver
 | 
			
		||||
{
 | 
			
		||||
	public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
 | 
			
		||||
	{
 | 
			
		||||
		JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options);
 | 
			
		||||
 | 
			
		||||
		if (jsonTypeInfo.Type.GetCustomAttribute<OneOfAttribute>() != null)
 | 
			
		||||
		{
 | 
			
		||||
			jsonTypeInfo.PolymorphismOptions = new()
 | 
			
		||||
			{
 | 
			
		||||
				TypeDiscriminatorPropertyName = "kind",
 | 
			
		||||
				IgnoreUnrecognizedTypeDiscriminators = true,
 | 
			
		||||
				DerivedTypes = { },
 | 
			
		||||
			};
 | 
			
		||||
			IEnumerable<Type> derived = AppDomain
 | 
			
		||||
				.CurrentDomain.GetAssemblies()
 | 
			
		||||
				.SelectMany(s => s.GetTypes())
 | 
			
		||||
				.Where(p => type.IsAssignableFrom(p) && p.IsClass);
 | 
			
		||||
			foreach (Type der in derived)
 | 
			
		||||
			{
 | 
			
		||||
				jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(
 | 
			
		||||
					new JsonDerivedType(der, CamelCase.ConvertName(der.Name))
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (
 | 
			
		||||
			jsonTypeInfo.Type.IsAssignableTo(typeof(IResource))
 | 
			
		||||
			&& jsonTypeInfo.Properties.All(x => x.Name != "kind")
 | 
			
		||||
		)
 | 
			
		||||
		{
 | 
			
		||||
			jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions
 | 
			
		||||
			{
 | 
			
		||||
				TypeDiscriminatorPropertyName = "kind",
 | 
			
		||||
				IgnoreUnrecognizedTypeDiscriminators = true,
 | 
			
		||||
				DerivedTypes =
 | 
			
		||||
				{
 | 
			
		||||
					new JsonDerivedType(
 | 
			
		||||
						jsonTypeInfo.Type,
 | 
			
		||||
						CamelCase.ConvertName(jsonTypeInfo.Type.Name)
 | 
			
		||||
					),
 | 
			
		||||
				},
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return jsonTypeInfo;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static readonly IHttpContextAccessor _accessor = new HttpContextAccessor();
 | 
			
		||||
 | 
			
		||||
	public static void HandleLoadableFields(JsonTypeInfo info)
 | 
			
		||||
	{
 | 
			
		||||
		foreach (JsonPropertyInfo prop in info.Properties)
 | 
			
		||||
		{
 | 
			
		||||
			object[] attributes =
 | 
			
		||||
				prop.AttributeProvider?.GetCustomAttributes(typeof(LoadableRelationAttribute), true)
 | 
			
		||||
				?? Array.Empty<object>();
 | 
			
		||||
			if (attributes.FirstOrDefault() is not LoadableRelationAttribute relation)
 | 
			
		||||
				continue;
 | 
			
		||||
			prop.ShouldSerialize = (_, _) =>
 | 
			
		||||
			{
 | 
			
		||||
				if (_accessor?.HttpContext?.Items["fields"] is not ICollection<string> fields)
 | 
			
		||||
					return false;
 | 
			
		||||
				return fields.Contains(prop.Name, StringComparer.InvariantCultureIgnoreCase);
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,90 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Kyoo.Abstractions.Models.Attributes;
 | 
			
		||||
using Kyoo.Abstractions.Models.Permissions;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using static Kyoo.Abstractions.Models.Utils.Constants;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Core.Api
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// Information about one or multiple staff member.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	[Route("staff")]
 | 
			
		||||
	[Route("people", Order = AlternativeRoute)]
 | 
			
		||||
	[ApiController]
 | 
			
		||||
	[PartialPermission(nameof(People))]
 | 
			
		||||
	[ApiDefinition("Staff", Group = MetadataGroup)]
 | 
			
		||||
	public class StaffApi : CrudThumbsApi<People>
 | 
			
		||||
	{
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The library manager used to modify or retrieve information in the data store.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		private readonly ILibraryManager _libraryManager;
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Create a new <see cref="StaffApi"/>.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <param name="libraryManager">
 | 
			
		||||
		/// The library manager used to modify or retrieve information about the data store.
 | 
			
		||||
		/// </param>
 | 
			
		||||
		/// <param name="thumbs">The thumbnail manager used to retrieve images paths.</param>
 | 
			
		||||
		public StaffApi(ILibraryManager libraryManager, IThumbnailsManager thumbs)
 | 
			
		||||
			: base(libraryManager.People, thumbs)
 | 
			
		||||
		{
 | 
			
		||||
			_libraryManager = libraryManager;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// /// <summary>
 | 
			
		||||
		// /// Get roles
 | 
			
		||||
		// /// </summary>
 | 
			
		||||
		// /// <remarks>
 | 
			
		||||
		// /// List the roles in witch this person has played, written or worked in a way.
 | 
			
		||||
		// /// </remarks>
 | 
			
		||||
		// /// <param name="identifier">The ID or slug of the person.</param>
 | 
			
		||||
		// /// <param name="sortBy">A key to sort roles by.</param>
 | 
			
		||||
		// /// <param name="where">An optional list of filters.</param>
 | 
			
		||||
		// /// <param name="pagination">The number of roles to return.</param>
 | 
			
		||||
		// /// <returns>A page of roles.</returns>
 | 
			
		||||
		// /// <response code="400">The filters or the sort parameters are invalid.</response>
 | 
			
		||||
		// /// <response code="404">No person with the given ID or slug could be found.</response>
 | 
			
		||||
		// [HttpGet("{identifier:id}/roles")]
 | 
			
		||||
		// [HttpGet("{identifier:id}/role", Order = AlternativeRoute)]
 | 
			
		||||
		// [PartialPermission(Kind.Read)]
 | 
			
		||||
		// [ProducesResponseType(StatusCodes.Status200OK)]
 | 
			
		||||
		// [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
 | 
			
		||||
		// [ProducesResponseType(StatusCodes.Status404NotFound)]
 | 
			
		||||
		// public async Task<ActionResult<Page<PeopleRole>>> GetRoles(Identifier identifier,
 | 
			
		||||
		// 	[FromQuery] Sort<PeopleRole> sortBy,
 | 
			
		||||
		// 	[FromQuery] Dictionary<string, string> where,
 | 
			
		||||
		// 	[FromQuery] Pagination pagination)
 | 
			
		||||
		// {
 | 
			
		||||
		// 	Expression<Func<PeopleRole, bool>>? whereQuery = ApiHelper.ParseWhere<PeopleRole>(where);
 | 
			
		||||
		//
 | 
			
		||||
		// 	ICollection<PeopleRole> resources = await identifier.Match(
 | 
			
		||||
		// 		id => _libraryManager.GetRolesFromPeople(id, whereQuery, sortBy, pagination),
 | 
			
		||||
		// 		slug => _libraryManager.GetRolesFromPeople(slug, whereQuery, sortBy, pagination)
 | 
			
		||||
		// 	);
 | 
			
		||||
		//
 | 
			
		||||
		// 	return Page(resources, pagination.Limit);
 | 
			
		||||
		// }
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -43,40 +43,6 @@ namespace Kyoo.Core.Api
 | 
			
		||||
	public class MovieApi(ILibraryManager libraryManager, IThumbnailsManager thumbs)
 | 
			
		||||
		: TranscoderApi<Movie>(libraryManager.Movies, thumbs)
 | 
			
		||||
	{
 | 
			
		||||
		// /// <summary>
 | 
			
		||||
		// /// Get staff
 | 
			
		||||
		// /// </summary>
 | 
			
		||||
		// /// <remarks>
 | 
			
		||||
		// /// List staff members that made this show.
 | 
			
		||||
		// /// </remarks>
 | 
			
		||||
		// /// <param name="identifier">The ID or slug of the <see cref="Show"/>.</param>
 | 
			
		||||
		// /// <param name="sortBy">A key to sort staff members by.</param>
 | 
			
		||||
		// /// <param name="where">An optional list of filters.</param>
 | 
			
		||||
		// /// <param name="pagination">The number of people to return.</param>
 | 
			
		||||
		// /// <returns>A page of people.</returns>
 | 
			
		||||
		// /// <response code="400">The filters or the sort parameters are invalid.</response>
 | 
			
		||||
		// /// <response code="404">No show with the given ID or slug could be found.</response>
 | 
			
		||||
		// [HttpGet("{identifier:id}/staff")]
 | 
			
		||||
		// [HttpGet("{identifier:id}/people", Order = AlternativeRoute)]
 | 
			
		||||
		// [PartialPermission(Kind.Read)]
 | 
			
		||||
		// [ProducesResponseType(StatusCodes.Status200OK)]
 | 
			
		||||
		// [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
 | 
			
		||||
		// [ProducesResponseType(StatusCodes.Status404NotFound)]
 | 
			
		||||
		// public async Task<ActionResult<Page<PeopleRole>>> GetPeople(Identifier identifier,
 | 
			
		||||
		// 	[FromQuery] string sortBy,
 | 
			
		||||
		// 	[FromQuery] Dictionary<string, string> where,
 | 
			
		||||
		// 	[FromQuery] Pagination pagination)
 | 
			
		||||
		// {
 | 
			
		||||
		// 	Expression<Func<PeopleRole, bool>> whereQuery = ApiHelper.ParseWhere<PeopleRole>(where);
 | 
			
		||||
		// 	Sort<PeopleRole> sort = Sort<PeopleRole>.From(sortBy);
 | 
			
		||||
		//
 | 
			
		||||
		// 	ICollection<PeopleRole> resources = await identifier.Match(
 | 
			
		||||
		// 		id => _libraryManager.GetPeopleFromShow(id, whereQuery, sort, pagination),
 | 
			
		||||
		// 		slug => _libraryManager.GetPeopleFromShow(slug, whereQuery, sort, pagination)
 | 
			
		||||
		// 	);
 | 
			
		||||
		// 	return Page(resources, pagination.Limit);
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Get studio that made the show
 | 
			
		||||
		/// </summary>
 | 
			
		||||
 | 
			
		||||
@ -146,41 +146,6 @@ namespace Kyoo.Core.Api
 | 
			
		||||
			return Page(resources, pagination.Limit);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// /// <summary>
 | 
			
		||||
		// /// Get staff
 | 
			
		||||
		// /// </summary>
 | 
			
		||||
		// /// <remarks>
 | 
			
		||||
		// /// List staff members that made this show.
 | 
			
		||||
		// /// </remarks>
 | 
			
		||||
		// /// <param name="identifier">The ID or slug of the <see cref="Show"/>.</param>
 | 
			
		||||
		// /// <param name="sortBy">A key to sort staff members by.</param>
 | 
			
		||||
		// /// <param name="where">An optional list of filters.</param>
 | 
			
		||||
		// /// <param name="pagination">The number of people to return.</param>
 | 
			
		||||
		// /// <param name="fields">The aditional fields to include in the result.</param>
 | 
			
		||||
		// /// <returns>A page of people.</returns>
 | 
			
		||||
		// /// <response code="400">The filters or the sort parameters are invalid.</response>
 | 
			
		||||
		// /// <response code="404">No show with the given ID or slug could be found.</response>
 | 
			
		||||
		// [HttpGet("{identifier:id}/staff")]
 | 
			
		||||
		// [HttpGet("{identifier:id}/people", Order = AlternativeRoute)]
 | 
			
		||||
		// [PartialPermission(Kind.Read)]
 | 
			
		||||
		// [ProducesResponseType(StatusCodes.Status200OK)]
 | 
			
		||||
		// [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
 | 
			
		||||
		// [ProducesResponseType(StatusCodes.Status404NotFound)]
 | 
			
		||||
		// public async Task<ActionResult<Page<PeopleRole>>> GetPeople(Identifier identifier,
 | 
			
		||||
		// 	[FromQuery] Sort<PeopleRole> sortBy,
 | 
			
		||||
		// 	[FromQuery] Dictionary<string, string> where,
 | 
			
		||||
		// 	[FromQuery] Pagination pagination,
 | 
			
		||||
		// 	[FromQuery] Include<PeopleRole> fields)
 | 
			
		||||
		// {
 | 
			
		||||
		// 	Expression<Func<PeopleRole, bool>>? whereQuery = ApiHelper.ParseWhere<PeopleRole>(where);
 | 
			
		||||
		//
 | 
			
		||||
		// 	ICollection<PeopleRole> resources = await identifier.Match(
 | 
			
		||||
		// 		id => _libraryManager.GetPeopleFromShow(id, whereQuery, sortBy, pagination),
 | 
			
		||||
		// 		slug => _libraryManager.GetPeopleFromShow(slug, whereQuery, sortBy, pagination)
 | 
			
		||||
		// 	);
 | 
			
		||||
		// 	return Page(resources, pagination.Limit);
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Get studio that made the show
 | 
			
		||||
		/// </summary>
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,6 @@
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Kyoo.Host;
 | 
			
		||||
using Microsoft.AspNetCore.Hosting;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Host
 | 
			
		||||
 | 
			
		||||
@ -78,11 +78,6 @@ namespace Kyoo.Postgresql
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public DbSet<Episode> Episodes { get; set; }
 | 
			
		||||
 | 
			
		||||
		// /// <summary>
 | 
			
		||||
		// /// All people of Kyoo. See <see cref="People"/>.
 | 
			
		||||
		// /// </summary>
 | 
			
		||||
		// public DbSet<People> People { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// All studios of Kyoo. See <see cref="Studio"/>.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
@ -93,11 +88,6 @@ namespace Kyoo.Postgresql
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public DbSet<User> Users { get; set; }
 | 
			
		||||
 | 
			
		||||
		// /// <summary>
 | 
			
		||||
		// /// All people's role. See <see cref="PeopleRole"/>.
 | 
			
		||||
		// /// </summary>
 | 
			
		||||
		// public DbSet<PeopleRole> PeopleRoles { get; set; }
 | 
			
		||||
 | 
			
		||||
		public DbSet<MovieWatchStatus> MovieWatchStatus { get; set; }
 | 
			
		||||
 | 
			
		||||
		public DbSet<ShowWatchStatus> ShowWatchStatus { get; set; }
 | 
			
		||||
@ -275,8 +265,6 @@ namespace Kyoo.Postgresql
 | 
			
		||||
				.Ignore(x => x.PreviousEpisode)
 | 
			
		||||
				.Ignore(x => x.NextEpisode);
 | 
			
		||||
 | 
			
		||||
			// modelBuilder.Entity<PeopleRole>()
 | 
			
		||||
			// 	.Ignore(x => x.ForPeople);
 | 
			
		||||
			modelBuilder
 | 
			
		||||
				.Entity<Show>()
 | 
			
		||||
				.HasMany(x => x.Seasons)
 | 
			
		||||
@ -312,14 +300,12 @@ namespace Kyoo.Postgresql
 | 
			
		||||
			_HasMetadata<Show>(modelBuilder);
 | 
			
		||||
			_HasMetadata<Season>(modelBuilder);
 | 
			
		||||
			_HasMetadata<Episode>(modelBuilder);
 | 
			
		||||
			// _HasMetadata<People>(modelBuilder);
 | 
			
		||||
			_HasMetadata<Studio>(modelBuilder);
 | 
			
		||||
 | 
			
		||||
			_HasImages<Collection>(modelBuilder);
 | 
			
		||||
			_HasImages<Movie>(modelBuilder);
 | 
			
		||||
			_HasImages<Show>(modelBuilder);
 | 
			
		||||
			_HasImages<Season>(modelBuilder);
 | 
			
		||||
			// _HasImages<People>(modelBuilder);
 | 
			
		||||
			_HasImages<Episode>(modelBuilder);
 | 
			
		||||
 | 
			
		||||
			_HasAddedDate<Collection>(modelBuilder);
 | 
			
		||||
@ -382,9 +368,6 @@ namespace Kyoo.Postgresql
 | 
			
		||||
			modelBuilder.Entity<Episode>().Ignore(x => x.WatchStatus);
 | 
			
		||||
 | 
			
		||||
			modelBuilder.Entity<Collection>().HasIndex(x => x.Slug).IsUnique();
 | 
			
		||||
			// modelBuilder.Entity<People>()
 | 
			
		||||
			// 	.HasIndex(x => x.Slug)
 | 
			
		||||
			// 	.IsUnique();
 | 
			
		||||
			modelBuilder.Entity<Movie>().HasIndex(x => x.Slug).IsUnique();
 | 
			
		||||
			modelBuilder.Entity<Show>().HasIndex(x => x.Slug).IsUnique();
 | 
			
		||||
			modelBuilder.Entity<Studio>().HasIndex(x => x.Slug).IsUnique();
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System.Data;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using System.Text.Json;
 | 
			
		||||
using Npgsql;
 | 
			
		||||
using NpgsqlTypes;
 | 
			
		||||
using static Dapper.SqlMapper;
 | 
			
		||||
@ -30,13 +30,13 @@ public class JsonTypeHandler<T> : TypeHandler<T>
 | 
			
		||||
	public override T? Parse(object value)
 | 
			
		||||
	{
 | 
			
		||||
		if (value is string str)
 | 
			
		||||
			return JsonConvert.DeserializeObject<T>(str);
 | 
			
		||||
			return JsonSerializer.Deserialize<T>(str);
 | 
			
		||||
		return default;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public override void SetValue(IDbDataParameter parameter, T? value)
 | 
			
		||||
	{
 | 
			
		||||
		parameter.Value = JsonConvert.SerializeObject(value);
 | 
			
		||||
		parameter.Value = JsonSerializer.Serialize(value);
 | 
			
		||||
		((NpgsqlParameter)parameter).NpgsqlDbType = NpgsqlDbType.Jsonb;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,132 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Kyoo.Core;
 | 
			
		||||
using Kyoo.Core.Controllers;
 | 
			
		||||
using Kyoo.Postgresql;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using Moq;
 | 
			
		||||
using Xunit.Abstractions;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests.Database
 | 
			
		||||
{
 | 
			
		||||
	public class RepositoryActivator : IDisposable, IAsyncDisposable
 | 
			
		||||
	{
 | 
			
		||||
		public TestContext Context { get; }
 | 
			
		||||
		public ILibraryManager LibraryManager { get; }
 | 
			
		||||
 | 
			
		||||
		private readonly List<IAsyncDisposable> _databases = new();
 | 
			
		||||
 | 
			
		||||
		private readonly IBaseRepository[] _repositories;
 | 
			
		||||
 | 
			
		||||
		public RepositoryActivator(ITestOutputHelper output, PostgresFixture postgres = null)
 | 
			
		||||
		{
 | 
			
		||||
			Context = new PostgresTestContext(postgres, output);
 | 
			
		||||
 | 
			
		||||
			Mock<IThumbnailsManager> thumbs = new();
 | 
			
		||||
			CollectionRepository collection = new(_NewContext(), thumbs.Object);
 | 
			
		||||
			StudioRepository studio = new(_NewContext(), thumbs.Object);
 | 
			
		||||
			PeopleRepository people =
 | 
			
		||||
				new(
 | 
			
		||||
					_NewContext(),
 | 
			
		||||
					new Lazy<IRepository<Show>>(() => LibraryManager.Shows),
 | 
			
		||||
					thumbs.Object
 | 
			
		||||
				);
 | 
			
		||||
			MovieRepository movies = new(_NewContext(), studio, people, thumbs.Object);
 | 
			
		||||
			ShowRepository show = new(_NewContext(), studio, people, thumbs.Object);
 | 
			
		||||
			SeasonRepository season = new(_NewContext(), thumbs.Object);
 | 
			
		||||
			LibraryItemRepository libraryItem = new(_NewConnection(), new(null));
 | 
			
		||||
			EpisodeRepository episode = new(_NewContext(), show, thumbs.Object);
 | 
			
		||||
			UserRepository user =
 | 
			
		||||
				new(_NewContext(), _NewConnection(), new(null), thumbs.Object, new());
 | 
			
		||||
 | 
			
		||||
			_repositories = new IBaseRepository[]
 | 
			
		||||
			{
 | 
			
		||||
				libraryItem,
 | 
			
		||||
				collection,
 | 
			
		||||
				movies,
 | 
			
		||||
				show,
 | 
			
		||||
				season,
 | 
			
		||||
				episode,
 | 
			
		||||
				people,
 | 
			
		||||
				studio,
 | 
			
		||||
				user
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			ServiceCollection container = new();
 | 
			
		||||
			container.AddScoped((_) => _NewContext());
 | 
			
		||||
			CoreModule.Services = container.BuildServiceProvider();
 | 
			
		||||
 | 
			
		||||
			LibraryManager = new LibraryManager(
 | 
			
		||||
				libraryItem,
 | 
			
		||||
				null,
 | 
			
		||||
				null,
 | 
			
		||||
				collection,
 | 
			
		||||
				movies,
 | 
			
		||||
				show,
 | 
			
		||||
				season,
 | 
			
		||||
				episode,
 | 
			
		||||
				people,
 | 
			
		||||
				studio,
 | 
			
		||||
				user
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public IRepository<T> GetRepository<T>()
 | 
			
		||||
			where T : class, IResource, IQuery
 | 
			
		||||
		{
 | 
			
		||||
			return _repositories.First(x => x.RepositoryType == typeof(T)) as IRepository<T>;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private DatabaseContext _NewContext()
 | 
			
		||||
		{
 | 
			
		||||
			DatabaseContext context = Context.New();
 | 
			
		||||
			_databases.Add(context);
 | 
			
		||||
			return context;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private DbConnection _NewConnection()
 | 
			
		||||
		{
 | 
			
		||||
			DbConnection context = Context.NewConnection();
 | 
			
		||||
			_databases.Add(context);
 | 
			
		||||
			return context;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public void Dispose()
 | 
			
		||||
		{
 | 
			
		||||
			foreach (IDisposable context in _databases)
 | 
			
		||||
				context.Dispose();
 | 
			
		||||
			Context.Dispose();
 | 
			
		||||
			GC.SuppressFinalize(this);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public async ValueTask DisposeAsync()
 | 
			
		||||
		{
 | 
			
		||||
			foreach (IAsyncDisposable context in _databases)
 | 
			
		||||
				await context.DisposeAsync();
 | 
			
		||||
			await Context.DisposeAsync();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,114 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Kyoo.Abstractions.Models.Exceptions;
 | 
			
		||||
using Kyoo.Postgresql;
 | 
			
		||||
using Xunit;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests.Database
 | 
			
		||||
{
 | 
			
		||||
	public abstract class RepositoryTests<T> : IDisposable, IAsyncDisposable
 | 
			
		||||
		where T : class, IResource, IQuery
 | 
			
		||||
	{
 | 
			
		||||
		protected readonly RepositoryActivator Repositories;
 | 
			
		||||
		private readonly IRepository<T> _repository;
 | 
			
		||||
 | 
			
		||||
		protected RepositoryTests(RepositoryActivator repositories)
 | 
			
		||||
		{
 | 
			
		||||
			Repositories = repositories;
 | 
			
		||||
			_repository = Repositories.GetRepository<T>();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public void Dispose()
 | 
			
		||||
		{
 | 
			
		||||
			Repositories.Dispose();
 | 
			
		||||
			GC.SuppressFinalize(this);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public ValueTask DisposeAsync()
 | 
			
		||||
		{
 | 
			
		||||
			return Repositories.DisposeAsync();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task FillTest()
 | 
			
		||||
		{
 | 
			
		||||
			await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
 | 
			
		||||
			Assert.Equal(1, database.Shows.Count());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task GetByIdTest()
 | 
			
		||||
		{
 | 
			
		||||
			T value = await _repository.Get(TestSample.Get<T>().Id);
 | 
			
		||||
			KAssert.DeepEqual(TestSample.Get<T>(), value);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task GetBySlugTest()
 | 
			
		||||
		{
 | 
			
		||||
			T value = await _repository.Get(TestSample.Get<T>().Slug);
 | 
			
		||||
			KAssert.DeepEqual(TestSample.Get<T>(), value);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task GetByFakeSlugTest()
 | 
			
		||||
		{
 | 
			
		||||
			await Assert.ThrowsAsync<ItemNotFoundException>(() => _repository.Get("non-existent"));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task DeleteByIdTest()
 | 
			
		||||
		{
 | 
			
		||||
			await _repository.Delete(TestSample.Get<T>().Id);
 | 
			
		||||
			Assert.Equal(0, await _repository.GetCount());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task DeleteBySlugTest()
 | 
			
		||||
		{
 | 
			
		||||
			await _repository.Delete(TestSample.Get<T>().Slug);
 | 
			
		||||
			Assert.Equal(0, await _repository.GetCount());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task DeleteByValueTest()
 | 
			
		||||
		{
 | 
			
		||||
			await _repository.Delete(TestSample.Get<T>());
 | 
			
		||||
			Assert.Equal(0, await _repository.GetCount());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// [Fact]
 | 
			
		||||
		// public async Task EditNonExistingTest()
 | 
			
		||||
		// {
 | 
			
		||||
		//	 await Assert.ThrowsAsync<ItemNotFoundException>(() => _repository.Edit(new T { Id = 56 }));
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task GetOrDefaultTest()
 | 
			
		||||
		{
 | 
			
		||||
			Assert.Null(await _repository.GetOrDefault("non-existing"));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,158 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Kyoo.Postgresql;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Xunit;
 | 
			
		||||
using Xunit.Abstractions;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests.Database
 | 
			
		||||
{
 | 
			
		||||
	namespace PostgreSQL
 | 
			
		||||
	{
 | 
			
		||||
		[Collection(nameof(Postgresql))]
 | 
			
		||||
		public class CollectionTests : ACollectionTests
 | 
			
		||||
		{
 | 
			
		||||
			public CollectionTests(PostgresFixture postgres, ITestOutputHelper output)
 | 
			
		||||
				: base(new RepositoryActivator(output, postgres)) { }
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public abstract class ACollectionTests : RepositoryTests<Collection>
 | 
			
		||||
	{
 | 
			
		||||
		private readonly IRepository<Collection> _repository;
 | 
			
		||||
 | 
			
		||||
		protected ACollectionTests(RepositoryActivator repositories)
 | 
			
		||||
			: base(repositories)
 | 
			
		||||
		{
 | 
			
		||||
			_repository = Repositories.LibraryManager.Collections;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task CreateWithEmptySlugTest()
 | 
			
		||||
		{
 | 
			
		||||
			Collection collection = TestSample.GetNew<Collection>();
 | 
			
		||||
			collection.Slug = string.Empty;
 | 
			
		||||
			await Assert.ThrowsAsync<ArgumentException>(() => _repository.Create(collection));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task CreateWithNumberSlugTest()
 | 
			
		||||
		{
 | 
			
		||||
			Collection collection = TestSample.GetNew<Collection>();
 | 
			
		||||
			collection.Slug = "2";
 | 
			
		||||
			Collection ret = await _repository.Create(collection);
 | 
			
		||||
			Assert.Equal("2!", ret.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task CreateWithExternalIdTest()
 | 
			
		||||
		{
 | 
			
		||||
			Collection collection = TestSample.GetNew<Collection>();
 | 
			
		||||
			collection.ExternalId = new Dictionary<string, MetadataId>
 | 
			
		||||
			{
 | 
			
		||||
				["1"] = new() { Link = "link", DataId = "id" },
 | 
			
		||||
				["2"] = new() { Link = "new-provider-link", DataId = "new-id" }
 | 
			
		||||
			};
 | 
			
		||||
			await _repository.Create(collection);
 | 
			
		||||
 | 
			
		||||
			Collection retrieved = await _repository.Get(2.AsGuid());
 | 
			
		||||
			Assert.Equal(2, retrieved.ExternalId.Count);
 | 
			
		||||
			KAssert.DeepEqual(collection.ExternalId.First(), retrieved.ExternalId.First());
 | 
			
		||||
			KAssert.DeepEqual(collection.ExternalId.Last(), retrieved.ExternalId.Last());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task EditTest()
 | 
			
		||||
		{
 | 
			
		||||
			Collection value = await _repository.Get(TestSample.Get<Collection>().Slug);
 | 
			
		||||
			value.Name = "New Title";
 | 
			
		||||
			value.Poster = new Image("new-poster");
 | 
			
		||||
			await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
			Collection retrieved = await database.Collections.FirstAsync();
 | 
			
		||||
 | 
			
		||||
			KAssert.DeepEqual(value, retrieved);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task EditMetadataTest()
 | 
			
		||||
		{
 | 
			
		||||
			Collection value = await _repository.Get(TestSample.Get<Collection>().Slug);
 | 
			
		||||
			value.ExternalId = new Dictionary<string, MetadataId>
 | 
			
		||||
			{
 | 
			
		||||
				["test"] = new() { Link = "link", DataId = "id" },
 | 
			
		||||
			};
 | 
			
		||||
			await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
			Collection retrieved = await database.Collections.FirstAsync();
 | 
			
		||||
 | 
			
		||||
			KAssert.DeepEqual(value, retrieved);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task AddMetadataTest()
 | 
			
		||||
		{
 | 
			
		||||
			Collection value = await _repository.Get(TestSample.Get<Collection>().Slug);
 | 
			
		||||
			value.ExternalId = new Dictionary<string, MetadataId>
 | 
			
		||||
			{
 | 
			
		||||
				["toto"] = new() { Link = "link", DataId = "id" },
 | 
			
		||||
			};
 | 
			
		||||
			await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
				Collection retrieved = await database.Collections.FirstAsync();
 | 
			
		||||
 | 
			
		||||
				KAssert.DeepEqual(value, retrieved);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			value.ExternalId.Add("test", new MetadataId { Link = "link", DataId = "id" });
 | 
			
		||||
			await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
				Collection retrieved = await database.Collections.FirstAsync();
 | 
			
		||||
 | 
			
		||||
				KAssert.DeepEqual(value, retrieved);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Theory]
 | 
			
		||||
		[InlineData("test")]
 | 
			
		||||
		[InlineData("super")]
 | 
			
		||||
		[InlineData("title")]
 | 
			
		||||
		[InlineData("TiTlE")]
 | 
			
		||||
		[InlineData("SuPeR")]
 | 
			
		||||
		public async Task SearchTest(string query)
 | 
			
		||||
		{
 | 
			
		||||
			Collection value = new() { Slug = "super-test", Name = "This is a test title", };
 | 
			
		||||
			await _repository.Create(value);
 | 
			
		||||
			ICollection<Collection> ret = await _repository.Search(query);
 | 
			
		||||
			KAssert.DeepEqual(value, ret.First());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,329 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Kyoo.Abstractions.Models.Exceptions;
 | 
			
		||||
using Kyoo.Postgresql;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Xunit;
 | 
			
		||||
using Xunit.Abstractions;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests.Database
 | 
			
		||||
{
 | 
			
		||||
	namespace PostgreSQL
 | 
			
		||||
	{
 | 
			
		||||
		[Collection(nameof(Postgresql))]
 | 
			
		||||
		public class EpisodeTests : AEpisodeTests
 | 
			
		||||
		{
 | 
			
		||||
			public EpisodeTests(PostgresFixture postgres, ITestOutputHelper output)
 | 
			
		||||
				: base(new RepositoryActivator(output, postgres)) { }
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public abstract class AEpisodeTests : RepositoryTests<Episode>
 | 
			
		||||
	{
 | 
			
		||||
		private readonly IRepository<Episode> _repository;
 | 
			
		||||
 | 
			
		||||
		protected AEpisodeTests(RepositoryActivator repositories)
 | 
			
		||||
			: base(repositories)
 | 
			
		||||
		{
 | 
			
		||||
			_repository = repositories.LibraryManager.Episodes;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task SlugEditTest()
 | 
			
		||||
		{
 | 
			
		||||
			Episode episode = await _repository.Get(1.AsGuid());
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e1", episode.Slug);
 | 
			
		||||
			await Repositories.LibraryManager.Shows.Patch(
 | 
			
		||||
				episode.ShowId,
 | 
			
		||||
				(x) =>
 | 
			
		||||
				{
 | 
			
		||||
					x.Slug = "new-slug";
 | 
			
		||||
					return x;
 | 
			
		||||
				}
 | 
			
		||||
			);
 | 
			
		||||
			episode = await _repository.Get(1.AsGuid());
 | 
			
		||||
			Assert.Equal("new-slug-s1e1", episode.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task SeasonNumberEditTest()
 | 
			
		||||
		{
 | 
			
		||||
			Episode episode = await _repository.Get(1.AsGuid());
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e1", episode.Slug);
 | 
			
		||||
			episode = await _repository.Patch(
 | 
			
		||||
				1.AsGuid(),
 | 
			
		||||
				(x) =>
 | 
			
		||||
				{
 | 
			
		||||
					x.SeasonNumber = 2;
 | 
			
		||||
					return x;
 | 
			
		||||
				}
 | 
			
		||||
			);
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-s2e1", episode.Slug);
 | 
			
		||||
			episode = await _repository.Get(1.AsGuid());
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-s2e1", episode.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task EpisodeNumberEditTest()
 | 
			
		||||
		{
 | 
			
		||||
			Episode episode = await _repository.Get(1.AsGuid());
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e1", episode.Slug);
 | 
			
		||||
			episode = await Repositories.LibraryManager.Episodes.Patch(
 | 
			
		||||
				episode.Id,
 | 
			
		||||
				(x) =>
 | 
			
		||||
				{
 | 
			
		||||
					x.EpisodeNumber = 2;
 | 
			
		||||
					return x;
 | 
			
		||||
				}
 | 
			
		||||
			);
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
 | 
			
		||||
			episode = await _repository.Get(1.AsGuid());
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task EpisodeCreationSlugTest()
 | 
			
		||||
		{
 | 
			
		||||
			Episode model = TestSample.Get<Episode>();
 | 
			
		||||
			model.Id = 0.AsGuid();
 | 
			
		||||
			model.ShowId = TestSample.Get<Show>().Id;
 | 
			
		||||
			model.SeasonNumber = 2;
 | 
			
		||||
			model.EpisodeNumber = 4;
 | 
			
		||||
			Episode episode = await _repository.Create(model);
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-s2e4", episode.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void AbsoluteSlugTest()
 | 
			
		||||
		{
 | 
			
		||||
			Assert.Equal(
 | 
			
		||||
				$"{TestSample.Get<Show>().Slug}-{TestSample.GetAbsoluteEpisode().AbsoluteNumber}",
 | 
			
		||||
				TestSample.GetAbsoluteEpisode().Slug
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task EpisodeCreationAbsoluteSlugTest()
 | 
			
		||||
		{
 | 
			
		||||
			Episode episode = await _repository.Create(TestSample.GetAbsoluteEpisode());
 | 
			
		||||
			Assert.Equal(
 | 
			
		||||
				$"{TestSample.Get<Show>().Slug}-{TestSample.GetAbsoluteEpisode().AbsoluteNumber}",
 | 
			
		||||
				episode.Slug
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task SlugEditAbsoluteTest()
 | 
			
		||||
		{
 | 
			
		||||
			Episode episode = await _repository.Create(TestSample.GetAbsoluteEpisode());
 | 
			
		||||
			await Repositories.LibraryManager.Shows.Patch(
 | 
			
		||||
				episode.ShowId,
 | 
			
		||||
				(x) =>
 | 
			
		||||
				{
 | 
			
		||||
					x.Slug = "new-slug";
 | 
			
		||||
					return x;
 | 
			
		||||
				}
 | 
			
		||||
			);
 | 
			
		||||
			episode = await _repository.Get(2.AsGuid());
 | 
			
		||||
			Assert.Equal($"new-slug-3", episode.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task AbsoluteNumberEditTest()
 | 
			
		||||
		{
 | 
			
		||||
			await _repository.Create(TestSample.GetAbsoluteEpisode());
 | 
			
		||||
			Episode episode = await _repository.Patch(
 | 
			
		||||
				2.AsGuid(),
 | 
			
		||||
				(x) =>
 | 
			
		||||
				{
 | 
			
		||||
					x.AbsoluteNumber = 56;
 | 
			
		||||
					return x;
 | 
			
		||||
				}
 | 
			
		||||
			);
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-56", episode.Slug);
 | 
			
		||||
			episode = await _repository.Get(2.AsGuid());
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-56", episode.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task AbsoluteToNormalEditTest()
 | 
			
		||||
		{
 | 
			
		||||
			await _repository.Create(TestSample.GetAbsoluteEpisode());
 | 
			
		||||
			Episode episode = await _repository.Patch(
 | 
			
		||||
				2.AsGuid(),
 | 
			
		||||
				(x) =>
 | 
			
		||||
				{
 | 
			
		||||
					x.SeasonNumber = 1;
 | 
			
		||||
					x.EpisodeNumber = 2;
 | 
			
		||||
					return x;
 | 
			
		||||
				}
 | 
			
		||||
			);
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
 | 
			
		||||
			episode = await _repository.Get(2.AsGuid());
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task NormalToAbsoluteEditTest()
 | 
			
		||||
		{
 | 
			
		||||
			Episode episode = await _repository.Get(1.AsGuid());
 | 
			
		||||
			episode.SeasonNumber = null;
 | 
			
		||||
			episode.AbsoluteNumber = 12;
 | 
			
		||||
			episode = await _repository.Edit(episode);
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-12", episode.Slug);
 | 
			
		||||
			episode = await _repository.Get(1.AsGuid());
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-12", episode.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task CreateWithExternalIdTest()
 | 
			
		||||
		{
 | 
			
		||||
			Episode value = TestSample.GetNew<Episode>();
 | 
			
		||||
			value.ExternalId = new Dictionary<string, MetadataId>
 | 
			
		||||
			{
 | 
			
		||||
				["2"] = new() { Link = "link", DataId = "id" },
 | 
			
		||||
				["3"] = new() { Link = "new-provider-link", DataId = "new-id" }
 | 
			
		||||
			};
 | 
			
		||||
			await _repository.Create(value);
 | 
			
		||||
 | 
			
		||||
			Episode retrieved = await _repository.Get(2.AsGuid());
 | 
			
		||||
			Assert.Equal(2, retrieved.ExternalId.Count);
 | 
			
		||||
			KAssert.DeepEqual(value.ExternalId.First(), retrieved.ExternalId.First());
 | 
			
		||||
			KAssert.DeepEqual(value.ExternalId.Last(), retrieved.ExternalId.Last());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task EditTest()
 | 
			
		||||
		{
 | 
			
		||||
			Episode value = await _repository.Get(TestSample.Get<Episode>().Slug);
 | 
			
		||||
			value.Name = "New Title";
 | 
			
		||||
			value.Poster = new Image("poster");
 | 
			
		||||
			await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
			Episode retrieved = await database.Episodes.FirstAsync();
 | 
			
		||||
 | 
			
		||||
			KAssert.DeepEqual(value, retrieved);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task EditMetadataTest()
 | 
			
		||||
		{
 | 
			
		||||
			Episode value = await _repository.Get(TestSample.Get<Episode>().Slug);
 | 
			
		||||
			value.ExternalId = new Dictionary<string, MetadataId>
 | 
			
		||||
			{
 | 
			
		||||
				["1"] = new() { Link = "link", DataId = "id" },
 | 
			
		||||
			};
 | 
			
		||||
			await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
			Episode retrieved = await database.Episodes.FirstAsync();
 | 
			
		||||
 | 
			
		||||
			KAssert.DeepEqual(value, retrieved);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task AddMetadataTest()
 | 
			
		||||
		{
 | 
			
		||||
			Episode value = await _repository.Get(TestSample.Get<Episode>().Slug);
 | 
			
		||||
			value.ExternalId = new Dictionary<string, MetadataId>
 | 
			
		||||
			{
 | 
			
		||||
				["toto"] = new() { Link = "link", DataId = "id" },
 | 
			
		||||
			};
 | 
			
		||||
			await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
				Episode retrieved = await database.Episodes.FirstAsync();
 | 
			
		||||
 | 
			
		||||
				KAssert.DeepEqual(value, retrieved);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			value.ExternalId.Add("test", new MetadataId { Link = "link", DataId = "id" });
 | 
			
		||||
			await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
				Episode retrieved = await database.Episodes.FirstAsync();
 | 
			
		||||
 | 
			
		||||
				KAssert.DeepEqual(value, retrieved);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Theory]
 | 
			
		||||
		[InlineData("test")]
 | 
			
		||||
		[InlineData("super")]
 | 
			
		||||
		[InlineData("title")]
 | 
			
		||||
		[InlineData("TiTlE")]
 | 
			
		||||
		[InlineData("SuPeR")]
 | 
			
		||||
		public async Task SearchTest(string query)
 | 
			
		||||
		{
 | 
			
		||||
			Episode value = TestSample.Get<Episode>();
 | 
			
		||||
			value.Id = 0.AsGuid();
 | 
			
		||||
			value.Name = "This is a test super title";
 | 
			
		||||
			value.EpisodeNumber = 56;
 | 
			
		||||
			await _repository.Create(value);
 | 
			
		||||
			ICollection<Episode> ret = await _repository.Search(query);
 | 
			
		||||
			KAssert.DeepEqual(value, ret.First());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task CreateTest()
 | 
			
		||||
		{
 | 
			
		||||
			await Assert.ThrowsAsync<DuplicatedItemException>(
 | 
			
		||||
				() => _repository.Create(TestSample.Get<Episode>())
 | 
			
		||||
			);
 | 
			
		||||
			await _repository.Delete(TestSample.Get<Episode>());
 | 
			
		||||
 | 
			
		||||
			Episode expected = TestSample.Get<Episode>();
 | 
			
		||||
			expected.Id = 0.AsGuid();
 | 
			
		||||
			expected.ShowId = (
 | 
			
		||||
				await Repositories.LibraryManager.Shows.Create(TestSample.Get<Show>())
 | 
			
		||||
			).Id;
 | 
			
		||||
			expected.SeasonId = (
 | 
			
		||||
				await Repositories.LibraryManager.Seasons.Create(TestSample.Get<Season>())
 | 
			
		||||
			).Id;
 | 
			
		||||
			await _repository.Create(expected);
 | 
			
		||||
			KAssert.DeepEqual(expected, await _repository.Get(expected.Slug));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task CreateIfNotExistTest()
 | 
			
		||||
		{
 | 
			
		||||
			Episode expected = TestSample.Get<Episode>();
 | 
			
		||||
			KAssert.DeepEqual(
 | 
			
		||||
				expected,
 | 
			
		||||
				await _repository.CreateIfNotExists(TestSample.Get<Episode>())
 | 
			
		||||
			);
 | 
			
		||||
			await _repository.Delete(TestSample.Get<Episode>());
 | 
			
		||||
			expected.ShowId = (
 | 
			
		||||
				await Repositories.LibraryManager.Shows.Create(TestSample.Get<Show>())
 | 
			
		||||
			).Id;
 | 
			
		||||
			expected.SeasonId = (
 | 
			
		||||
				await Repositories.LibraryManager.Seasons.Create(TestSample.Get<Season>())
 | 
			
		||||
			).Id;
 | 
			
		||||
			KAssert.DeepEqual(expected, await _repository.CreateIfNotExists(expected));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,34 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System.Diagnostics.CodeAnalysis;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Xunit;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests.Database
 | 
			
		||||
{
 | 
			
		||||
	public class GlobalTests
 | 
			
		||||
	{
 | 
			
		||||
		[Fact]
 | 
			
		||||
		[SuppressMessage("ReSharper", "EqualExpressionComparison")]
 | 
			
		||||
		public void SampleTest()
 | 
			
		||||
		{
 | 
			
		||||
			Assert.False(ReferenceEquals(TestSample.Get<Show>(), TestSample.Get<Show>()));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,183 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Kyoo.Postgresql;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Xunit;
 | 
			
		||||
using Xunit.Abstractions;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests.Database
 | 
			
		||||
{
 | 
			
		||||
	namespace PostgreSQL
 | 
			
		||||
	{
 | 
			
		||||
		[Collection(nameof(Postgresql))]
 | 
			
		||||
		public class SeasonTests : ASeasonTests
 | 
			
		||||
		{
 | 
			
		||||
			public SeasonTests(PostgresFixture postgres, ITestOutputHelper output)
 | 
			
		||||
				: base(new RepositoryActivator(output, postgres)) { }
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public abstract class ASeasonTests : RepositoryTests<Season>
 | 
			
		||||
	{
 | 
			
		||||
		private readonly IRepository<Season> _repository;
 | 
			
		||||
 | 
			
		||||
		protected ASeasonTests(RepositoryActivator repositories)
 | 
			
		||||
			: base(repositories)
 | 
			
		||||
		{
 | 
			
		||||
			_repository = Repositories.LibraryManager.Seasons;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task SlugEditTest()
 | 
			
		||||
		{
 | 
			
		||||
			Season season = await _repository.Get(1.AsGuid());
 | 
			
		||||
			Assert.Equal("anohana-s1", season.Slug);
 | 
			
		||||
			await Repositories.LibraryManager.Shows.Patch(
 | 
			
		||||
				season.ShowId,
 | 
			
		||||
				(x) =>
 | 
			
		||||
				{
 | 
			
		||||
					x.Slug = "new-slug";
 | 
			
		||||
					return x;
 | 
			
		||||
				}
 | 
			
		||||
			);
 | 
			
		||||
			season = await _repository.Get(1.AsGuid());
 | 
			
		||||
			Assert.Equal("new-slug-s1", season.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task SeasonNumberEditTest()
 | 
			
		||||
		{
 | 
			
		||||
			Season season = await _repository.Get(1.AsGuid());
 | 
			
		||||
			Assert.Equal("anohana-s1", season.Slug);
 | 
			
		||||
			await _repository.Patch(
 | 
			
		||||
				season.Id,
 | 
			
		||||
				(x) =>
 | 
			
		||||
				{
 | 
			
		||||
					x.SeasonNumber = 2;
 | 
			
		||||
					return x;
 | 
			
		||||
				}
 | 
			
		||||
			);
 | 
			
		||||
			season = await _repository.Get(1.AsGuid());
 | 
			
		||||
			Assert.Equal("anohana-s2", season.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task SeasonCreationSlugTest()
 | 
			
		||||
		{
 | 
			
		||||
			Season season = await _repository.Create(
 | 
			
		||||
				new Season { ShowId = TestSample.Get<Show>().Id, SeasonNumber = 2 }
 | 
			
		||||
			);
 | 
			
		||||
			Assert.Equal($"{TestSample.Get<Show>().Slug}-s2", season.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task CreateWithExternalIdTest()
 | 
			
		||||
		{
 | 
			
		||||
			Season season = TestSample.GetNew<Season>();
 | 
			
		||||
			season.ExternalId = new Dictionary<string, MetadataId>
 | 
			
		||||
			{
 | 
			
		||||
				["2"] = new() { Link = "link", DataId = "id" },
 | 
			
		||||
				["1"] = new() { Link = "new-provider-link", DataId = "new-id" }
 | 
			
		||||
			};
 | 
			
		||||
			await _repository.Create(season);
 | 
			
		||||
 | 
			
		||||
			Season retrieved = await _repository.Get(2.AsGuid());
 | 
			
		||||
			Assert.Equal(2, retrieved.ExternalId.Count);
 | 
			
		||||
			KAssert.DeepEqual(season.ExternalId.First(), retrieved.ExternalId.First());
 | 
			
		||||
			KAssert.DeepEqual(season.ExternalId.Last(), retrieved.ExternalId.Last());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task EditTest()
 | 
			
		||||
		{
 | 
			
		||||
			Season value = await _repository.Get(TestSample.Get<Season>().Slug);
 | 
			
		||||
			value.Name = "New Title";
 | 
			
		||||
			value.Poster = new Image("test");
 | 
			
		||||
			await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
			Season retrieved = await database.Seasons.FirstAsync();
 | 
			
		||||
 | 
			
		||||
			KAssert.DeepEqual(value, retrieved);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task EditMetadataTest()
 | 
			
		||||
		{
 | 
			
		||||
			Season value = await _repository.Get(TestSample.Get<Season>().Slug);
 | 
			
		||||
			value.ExternalId = new Dictionary<string, MetadataId>
 | 
			
		||||
			{
 | 
			
		||||
				["toto"] = new() { Link = "link", DataId = "id" },
 | 
			
		||||
			};
 | 
			
		||||
			await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
			Season retrieved = await database.Seasons.FirstAsync();
 | 
			
		||||
 | 
			
		||||
			KAssert.DeepEqual(value, retrieved);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task AddMetadataTest()
 | 
			
		||||
		{
 | 
			
		||||
			Season value = await _repository.Get(TestSample.Get<Season>().Slug);
 | 
			
		||||
			value.ExternalId = new Dictionary<string, MetadataId>
 | 
			
		||||
			{
 | 
			
		||||
				["1"] = new() { Link = "link", DataId = "id" },
 | 
			
		||||
			};
 | 
			
		||||
			await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
				Season retrieved = await database.Seasons.FirstAsync();
 | 
			
		||||
 | 
			
		||||
				KAssert.DeepEqual(value, retrieved);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			value.ExternalId.Add("toto", new MetadataId { Link = "link", DataId = "id" });
 | 
			
		||||
			await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
				Season retrieved = await database.Seasons.FirstAsync();
 | 
			
		||||
 | 
			
		||||
				KAssert.DeepEqual(value, retrieved);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Theory]
 | 
			
		||||
		[InlineData("test")]
 | 
			
		||||
		[InlineData("super")]
 | 
			
		||||
		[InlineData("title")]
 | 
			
		||||
		[InlineData("TiTlE")]
 | 
			
		||||
		[InlineData("SuPeR")]
 | 
			
		||||
		public async Task SearchTest(string query)
 | 
			
		||||
		{
 | 
			
		||||
			Season value = new() { Name = "This is a test super title", ShowId = 1.AsGuid() };
 | 
			
		||||
			await _repository.Create(value);
 | 
			
		||||
			ICollection<Season> ret = await _repository.Search(query);
 | 
			
		||||
			KAssert.DeepEqual(value, ret.First());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,297 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Kyoo.Postgresql;
 | 
			
		||||
using Kyoo.Utils;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Xunit;
 | 
			
		||||
using Xunit.Abstractions;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests.Database
 | 
			
		||||
{
 | 
			
		||||
	namespace PostgreSQL
 | 
			
		||||
	{
 | 
			
		||||
		[Collection(nameof(Postgresql))]
 | 
			
		||||
		public class ShowTests : AShowTests
 | 
			
		||||
		{
 | 
			
		||||
			public ShowTests(PostgresFixture postgres, ITestOutputHelper output)
 | 
			
		||||
				: base(new RepositoryActivator(output, postgres)) { }
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public abstract class AShowTests : RepositoryTests<Show>
 | 
			
		||||
	{
 | 
			
		||||
		private readonly IRepository<Show> _repository;
 | 
			
		||||
 | 
			
		||||
		protected AShowTests(RepositoryActivator repositories)
 | 
			
		||||
			: base(repositories)
 | 
			
		||||
		{
 | 
			
		||||
			_repository = Repositories.LibraryManager.Shows;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task EditTest()
 | 
			
		||||
		{
 | 
			
		||||
			Show value = await _repository.Get(TestSample.Get<Show>().Slug);
 | 
			
		||||
			value.Name = "New Title";
 | 
			
		||||
			Show edited = await _repository.Edit(value);
 | 
			
		||||
			KAssert.DeepEqual(value, edited);
 | 
			
		||||
 | 
			
		||||
			await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
			Show show = await database.Shows.FirstAsync();
 | 
			
		||||
 | 
			
		||||
			KAssert.DeepEqual(show, value);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task EditGenreTest()
 | 
			
		||||
		{
 | 
			
		||||
			Show value = await _repository.Get(TestSample.Get<Show>().Slug);
 | 
			
		||||
			value.Genres = new List<Genre> { Genre.Action };
 | 
			
		||||
			Show edited = await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			Assert.Equal(value.Slug, edited.Slug);
 | 
			
		||||
			Assert.Equal(value.Genres, edited.Genres);
 | 
			
		||||
 | 
			
		||||
			await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
			Show show = await database.Shows.FirstAsync();
 | 
			
		||||
 | 
			
		||||
			Assert.Equal(value.Slug, show.Slug);
 | 
			
		||||
			Assert.Equal(value.Genres, show.Genres);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task AddGenreTest()
 | 
			
		||||
		{
 | 
			
		||||
			Show value = await _repository.Get(TestSample.Get<Show>().Slug);
 | 
			
		||||
			value.Genres.Add(Genre.Drama);
 | 
			
		||||
			Show edited = await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			Assert.Equal(value.Slug, edited.Slug);
 | 
			
		||||
			Assert.Equal(value.Genres, edited.Genres);
 | 
			
		||||
 | 
			
		||||
			await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
			Show show = await database.Shows.FirstAsync();
 | 
			
		||||
 | 
			
		||||
			Assert.Equal(value.Slug, show.Slug);
 | 
			
		||||
			Assert.Equal(value.Genres, show.Genres);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task EditStudioTest()
 | 
			
		||||
		{
 | 
			
		||||
			Show value = await _repository.Get(TestSample.Get<Show>().Slug);
 | 
			
		||||
			value.Studio = new Studio("studio");
 | 
			
		||||
			Show edited = await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			Assert.Equal(value.Slug, edited.Slug);
 | 
			
		||||
			Assert.Equal("studio", edited.Studio!.Slug);
 | 
			
		||||
 | 
			
		||||
			await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
			Show show = await database.Shows.Include(x => x.Studio).FirstAsync();
 | 
			
		||||
 | 
			
		||||
			Assert.Equal(value.Slug, show.Slug);
 | 
			
		||||
			Assert.Equal("studio", show.Studio!.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task EditAliasesTest()
 | 
			
		||||
		{
 | 
			
		||||
			Show value = await _repository.Get(TestSample.Get<Show>().Slug);
 | 
			
		||||
			value.Aliases = new List<string>() { "NiceNewAlias", "SecondAlias" };
 | 
			
		||||
			Show edited = await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			Assert.Equal(value.Slug, edited.Slug);
 | 
			
		||||
			Assert.Equal(value.Aliases, edited.Aliases);
 | 
			
		||||
 | 
			
		||||
			await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
			Show show = await database.Shows.FirstAsync();
 | 
			
		||||
 | 
			
		||||
			Assert.Equal(value.Slug, show.Slug);
 | 
			
		||||
			Assert.Equal(value.Aliases, show.Aliases);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// [Fact]
 | 
			
		||||
		// public async Task EditPeopleTest()
 | 
			
		||||
		// {
 | 
			
		||||
		// 	Show value = await _repository.Get(TestSample.Get<Show>().Slug);
 | 
			
		||||
		// 	value.People = new[]
 | 
			
		||||
		// 	{
 | 
			
		||||
		// 		new PeopleRole
 | 
			
		||||
		// 		{
 | 
			
		||||
		// 			Show = value,
 | 
			
		||||
		// 			People = TestSample.Get<People>(),
 | 
			
		||||
		// 			ForPeople = false,
 | 
			
		||||
		// 			Type = "Actor",
 | 
			
		||||
		// 			Role = "NiceCharacter"
 | 
			
		||||
		// 		}
 | 
			
		||||
		// 	};
 | 
			
		||||
		// 	Show edited = await _repository.Edit(value);
 | 
			
		||||
		//
 | 
			
		||||
		// 	Assert.Equal(value.Slug, edited.Slug);
 | 
			
		||||
		// 	Assert.Equal(edited.People!.First().ShowID, value.Id);
 | 
			
		||||
		// 	Assert.Equal(
 | 
			
		||||
		// 		value.People.Select(x => new { x.Role, x.Slug, x.People.Name }),
 | 
			
		||||
		// 		edited.People.Select(x => new { x.Role, x.Slug, x.People.Name }));
 | 
			
		||||
		//
 | 
			
		||||
		// 	await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
		// 	Show show = await database.Shows
 | 
			
		||||
		// 		.Include(x => x.People)
 | 
			
		||||
		// 		.ThenInclude(x => x.People)
 | 
			
		||||
		// 		.FirstAsync();
 | 
			
		||||
		//
 | 
			
		||||
		// 	Assert.Equal(value.Slug, show.Slug);
 | 
			
		||||
		// 	Assert.Equal(
 | 
			
		||||
		// 		value.People.Select(x => new { x.Role, x.Slug, x.People.Name }),
 | 
			
		||||
		// 		show.People!.Select(x => new { x.Role, x.Slug, x.People.Name }));
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task EditExternalIDsTest()
 | 
			
		||||
		{
 | 
			
		||||
			Show value = await _repository.Get(TestSample.Get<Show>().Slug);
 | 
			
		||||
			value.ExternalId = new Dictionary<string, MetadataId>()
 | 
			
		||||
			{
 | 
			
		||||
				["test"] = new() { DataId = "1234" }
 | 
			
		||||
			};
 | 
			
		||||
			Show edited = await _repository.Edit(value);
 | 
			
		||||
 | 
			
		||||
			Assert.Equal(value.Slug, edited.Slug);
 | 
			
		||||
			KAssert.DeepEqual(value.ExternalId, edited.ExternalId);
 | 
			
		||||
 | 
			
		||||
			await using DatabaseContext database = Repositories.Context.New();
 | 
			
		||||
			Show show = await database.Shows.FirstAsync();
 | 
			
		||||
 | 
			
		||||
			Assert.Equal(value.Slug, show.Slug);
 | 
			
		||||
			KAssert.DeepEqual(value.ExternalId, show.ExternalId);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task CreateWithRelationsTest()
 | 
			
		||||
		{
 | 
			
		||||
			Show expected = TestSample.Get<Show>();
 | 
			
		||||
			expected.Id = 0.AsGuid();
 | 
			
		||||
			expected.Slug = "created-relation-test";
 | 
			
		||||
			expected.ExternalId = new Dictionary<string, MetadataId>
 | 
			
		||||
			{
 | 
			
		||||
				["test"] = new() { DataId = "ID" }
 | 
			
		||||
			};
 | 
			
		||||
			expected.Genres = new List<Genre>() { Genre.Action };
 | 
			
		||||
			// expected.People = new[]
 | 
			
		||||
			// {
 | 
			
		||||
			// 	new PeopleRole
 | 
			
		||||
			// 	{
 | 
			
		||||
			// 		People = TestSample.Get<People>(),
 | 
			
		||||
			// 		Show = expected,
 | 
			
		||||
			// 		ForPeople = false,
 | 
			
		||||
			// 		Role = "actor",
 | 
			
		||||
			// 		Type = "actor"
 | 
			
		||||
			// 	}
 | 
			
		||||
			// };
 | 
			
		||||
			expected.Studio = new Studio("studio");
 | 
			
		||||
			Show created = await _repository.Create(expected);
 | 
			
		||||
			KAssert.DeepEqual(expected, created);
 | 
			
		||||
 | 
			
		||||
			await using DatabaseContext context = Repositories.Context.New();
 | 
			
		||||
			Show retrieved = await context
 | 
			
		||||
				.Shows
 | 
			
		||||
				// .Include(x => x.People)
 | 
			
		||||
				// .ThenInclude(x => x.People)
 | 
			
		||||
				.Include(x => x.Studio)
 | 
			
		||||
				.FirstAsync(x => x.Id == created.Id);
 | 
			
		||||
			// retrieved.People.ForEach(x =>
 | 
			
		||||
			// {
 | 
			
		||||
			// 	x.Show = null;
 | 
			
		||||
			// 	x.People.Roles = null;
 | 
			
		||||
			// 	x.People.Poster = null;
 | 
			
		||||
			// 	x.People.Thumbnail = null;
 | 
			
		||||
			// 	x.People.Logo = null;
 | 
			
		||||
			// });
 | 
			
		||||
			retrieved.Studio!.Shows = null;
 | 
			
		||||
			// expected.People.ForEach(x =>
 | 
			
		||||
			// {
 | 
			
		||||
			// 	x.Show = null;
 | 
			
		||||
			// 	x.People.Roles = null;
 | 
			
		||||
			// 	x.People.Poster = null;
 | 
			
		||||
			// 	x.People.Thumbnail = null;
 | 
			
		||||
			// 	x.People.Logo = null;
 | 
			
		||||
			// });
 | 
			
		||||
 | 
			
		||||
			KAssert.DeepEqual(retrieved, expected);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task CreateWithExternalID()
 | 
			
		||||
		{
 | 
			
		||||
			Show expected = TestSample.Get<Show>();
 | 
			
		||||
			expected.Id = 0.AsGuid();
 | 
			
		||||
			expected.Slug = "created-relation-test";
 | 
			
		||||
			expected.ExternalId = new Dictionary<string, MetadataId>
 | 
			
		||||
			{
 | 
			
		||||
				["test"] = new() { DataId = "ID" }
 | 
			
		||||
			};
 | 
			
		||||
			Show created = await _repository.Create(expected);
 | 
			
		||||
			KAssert.DeepEqual(expected, created);
 | 
			
		||||
			await using DatabaseContext context = Repositories.Context.New();
 | 
			
		||||
			Show retrieved = await context.Shows.FirstAsync(x => x.Id == created.Id);
 | 
			
		||||
			KAssert.DeepEqual(expected, retrieved);
 | 
			
		||||
			Assert.Single(retrieved.ExternalId);
 | 
			
		||||
			Assert.Equal("ID", retrieved.ExternalId["test"].DataId);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task SlugDuplicationTest()
 | 
			
		||||
		{
 | 
			
		||||
			Show test = TestSample.Get<Show>();
 | 
			
		||||
			test.Id = 0.AsGuid();
 | 
			
		||||
			test.Slug = "300";
 | 
			
		||||
			Show created = await _repository.Create(test);
 | 
			
		||||
			Assert.Equal("300!", created.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Theory]
 | 
			
		||||
		[InlineData("test")]
 | 
			
		||||
		[InlineData("super")]
 | 
			
		||||
		[InlineData("title")]
 | 
			
		||||
		[InlineData("TiTlE")]
 | 
			
		||||
		[InlineData("SuPeR")]
 | 
			
		||||
		public async Task SearchTest(string query)
 | 
			
		||||
		{
 | 
			
		||||
			Show value = new() { Slug = "super-test", Name = "This is a test title?" };
 | 
			
		||||
			await _repository.Create(value);
 | 
			
		||||
			ICollection<Show> ret = await _repository.Search(query);
 | 
			
		||||
			KAssert.DeepEqual(value, ret.First());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public async Task DeleteShowWithEpisodeAndSeason()
 | 
			
		||||
		{
 | 
			
		||||
			Show show = TestSample.Get<Show>();
 | 
			
		||||
			Assert.Equal(1, await _repository.GetCount());
 | 
			
		||||
			await _repository.Delete(show);
 | 
			
		||||
			Assert.Equal(0, await Repositories.LibraryManager.Shows.GetCount());
 | 
			
		||||
			Assert.Equal(0, await Repositories.LibraryManager.Seasons.GetCount());
 | 
			
		||||
			Assert.Equal(0, await Repositories.LibraryManager.Episodes.GetCount());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,48 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System.Diagnostics.CodeAnalysis;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Xunit;
 | 
			
		||||
using Xunit.Abstractions;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests.Database
 | 
			
		||||
{
 | 
			
		||||
	namespace PostgreSQL
 | 
			
		||||
	{
 | 
			
		||||
		[Collection(nameof(Postgresql))]
 | 
			
		||||
		public class StudioTests : AStudioTests
 | 
			
		||||
		{
 | 
			
		||||
			public StudioTests(PostgresFixture postgres, ITestOutputHelper output)
 | 
			
		||||
				: base(new RepositoryActivator(output, postgres)) { }
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public abstract class AStudioTests : RepositoryTests<Studio>
 | 
			
		||||
	{
 | 
			
		||||
		[SuppressMessage("ReSharper", "NotAccessedField.Local")]
 | 
			
		||||
		private readonly IRepository<Studio> _repository;
 | 
			
		||||
 | 
			
		||||
		protected AStudioTests(RepositoryActivator repositories)
 | 
			
		||||
			: base(repositories)
 | 
			
		||||
		{
 | 
			
		||||
			_repository = Repositories.LibraryManager.Studios;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,48 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System.Diagnostics.CodeAnalysis;
 | 
			
		||||
using Kyoo.Abstractions.Controllers;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Xunit;
 | 
			
		||||
using Xunit.Abstractions;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests.Database
 | 
			
		||||
{
 | 
			
		||||
	namespace PostgreSQL
 | 
			
		||||
	{
 | 
			
		||||
		[Collection(nameof(Postgresql))]
 | 
			
		||||
		public class UserTests : AUserTests
 | 
			
		||||
		{
 | 
			
		||||
			public UserTests(PostgresFixture postgres, ITestOutputHelper output)
 | 
			
		||||
				: base(new RepositoryActivator(output, postgres)) { }
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public abstract class AUserTests : RepositoryTests<User>
 | 
			
		||||
	{
 | 
			
		||||
		[SuppressMessage("ReSharper", "NotAccessedField.Local")]
 | 
			
		||||
		private readonly IRepository<User> _repository;
 | 
			
		||||
 | 
			
		||||
		protected AUserTests(RepositoryActivator repositories)
 | 
			
		||||
			: base(repositories)
 | 
			
		||||
		{
 | 
			
		||||
			_repository = Repositories.LibraryManager.Users;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,174 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Kyoo.Postgresql;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Npgsql;
 | 
			
		||||
using Xunit;
 | 
			
		||||
using Xunit.Abstractions;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests
 | 
			
		||||
{
 | 
			
		||||
	[CollectionDefinition(nameof(Postgresql))]
 | 
			
		||||
	public class PostgresCollection : ICollectionFixture<PostgresFixture> { }
 | 
			
		||||
 | 
			
		||||
	public sealed class PostgresFixture : IDisposable
 | 
			
		||||
	{
 | 
			
		||||
		private readonly DbContextOptions<DatabaseContext> _options;
 | 
			
		||||
 | 
			
		||||
		public string Template { get; }
 | 
			
		||||
 | 
			
		||||
		public string Connection => PostgresTestContext.GetConnectionString(Template);
 | 
			
		||||
 | 
			
		||||
		public PostgresFixture()
 | 
			
		||||
		{
 | 
			
		||||
			string id = Guid.NewGuid().ToString().Replace('-', '_');
 | 
			
		||||
			Template = $"kyoo_template_{id}";
 | 
			
		||||
 | 
			
		||||
			_options = new DbContextOptionsBuilder<DatabaseContext>().UseNpgsql(Connection).Options;
 | 
			
		||||
 | 
			
		||||
			using PostgresContext context = new(_options, null);
 | 
			
		||||
			context.Database.Migrate();
 | 
			
		||||
 | 
			
		||||
			using NpgsqlConnection conn = (NpgsqlConnection)context.Database.GetDbConnection();
 | 
			
		||||
			conn.Open();
 | 
			
		||||
			conn.ReloadTypes();
 | 
			
		||||
 | 
			
		||||
			TestSample.FillDatabase(context);
 | 
			
		||||
			conn.Close();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public void Dispose()
 | 
			
		||||
		{
 | 
			
		||||
			using PostgresContext context = new(_options, null);
 | 
			
		||||
			context.Database.EnsureDeleted();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public sealed class PostgresTestContext : TestContext
 | 
			
		||||
	{
 | 
			
		||||
		private readonly string _database;
 | 
			
		||||
		private readonly DbContextOptions<DatabaseContext> _context;
 | 
			
		||||
 | 
			
		||||
		public PostgresTestContext(PostgresFixture template, ITestOutputHelper output)
 | 
			
		||||
		{
 | 
			
		||||
			string id = Guid.NewGuid().ToString().Replace('-', '_');
 | 
			
		||||
			_database = $"kyoo_test_{id}";
 | 
			
		||||
 | 
			
		||||
			using (NpgsqlConnection connection = new(template.Connection))
 | 
			
		||||
			{
 | 
			
		||||
				connection.Open();
 | 
			
		||||
				using NpgsqlCommand cmd =
 | 
			
		||||
					new(
 | 
			
		||||
						$"CREATE DATABASE {_database} WITH TEMPLATE {template.Template}",
 | 
			
		||||
						connection
 | 
			
		||||
					);
 | 
			
		||||
				cmd.ExecuteNonQuery();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			_context = new DbContextOptionsBuilder<DatabaseContext>()
 | 
			
		||||
				.UseNpgsql(GetConnectionString(_database))
 | 
			
		||||
				.UseLoggerFactory(
 | 
			
		||||
					LoggerFactory.Create(x =>
 | 
			
		||||
					{
 | 
			
		||||
						x.ClearProviders();
 | 
			
		||||
						x.AddXunit(output);
 | 
			
		||||
					})
 | 
			
		||||
				)
 | 
			
		||||
				.EnableSensitiveDataLogging()
 | 
			
		||||
				.EnableDetailedErrors()
 | 
			
		||||
				.Options;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static string GetConnectionString(string database)
 | 
			
		||||
		{
 | 
			
		||||
			string server = Environment.GetEnvironmentVariable("POSTGRES_HOST") ?? "127.0.0.1";
 | 
			
		||||
			string port = Environment.GetEnvironmentVariable("POSTGRES_PORT") ?? "5432";
 | 
			
		||||
			string username = Environment.GetEnvironmentVariable("POSTGRES_USER") ?? "KyooUser";
 | 
			
		||||
			string password =
 | 
			
		||||
				Environment.GetEnvironmentVariable("POSTGRES_PASSWORD") ?? "KyooPassword";
 | 
			
		||||
			return $"Server={server};Port={port};Database={database};User ID={username};Password={password};Include Error Detail=true";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public override void Dispose()
 | 
			
		||||
		{
 | 
			
		||||
			using DatabaseContext db = New();
 | 
			
		||||
			db.Database.EnsureDeleted();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public override async ValueTask DisposeAsync()
 | 
			
		||||
		{
 | 
			
		||||
			await using DatabaseContext db = New();
 | 
			
		||||
			await db.Database.EnsureDeletedAsync();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public override DatabaseContext New()
 | 
			
		||||
		{
 | 
			
		||||
			return new PostgresContext(_context, null);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public override DbConnection NewConnection()
 | 
			
		||||
		{
 | 
			
		||||
			return new NpgsqlConnection(GetConnectionString(_database));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// Class responsible to fill and create in memory databases for unit tests.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	public abstract class TestContext : IDisposable, IAsyncDisposable
 | 
			
		||||
	{
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Add an arbitrary data to the test context.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public void Add<T>(T obj)
 | 
			
		||||
			where T : class
 | 
			
		||||
		{
 | 
			
		||||
			using DatabaseContext context = New();
 | 
			
		||||
			context.Set<T>().Add(obj);
 | 
			
		||||
			context.SaveChanges();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Add an arbitrary data to the test context.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public async Task AddAsync<T>(T obj)
 | 
			
		||||
			where T : class
 | 
			
		||||
		{
 | 
			
		||||
			await using DatabaseContext context = New();
 | 
			
		||||
			await context.Set<T>().AddAsync(obj);
 | 
			
		||||
			await context.SaveChangesAsync();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Get a new database context connected to a in memory Sqlite database.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <returns>A valid DatabaseContext</returns>
 | 
			
		||||
		public abstract DatabaseContext New();
 | 
			
		||||
 | 
			
		||||
		public abstract DbConnection NewConnection();
 | 
			
		||||
 | 
			
		||||
		public abstract void Dispose();
 | 
			
		||||
 | 
			
		||||
		public abstract ValueTask DisposeAsync();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,290 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Kyoo.Postgresql;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests
 | 
			
		||||
{
 | 
			
		||||
	public static class TestSample
 | 
			
		||||
	{
 | 
			
		||||
		private static readonly Dictionary<Type, Func<object>> NewSamples =
 | 
			
		||||
			new()
 | 
			
		||||
			{
 | 
			
		||||
				{
 | 
			
		||||
					typeof(Collection),
 | 
			
		||||
					() =>
 | 
			
		||||
						new Collection
 | 
			
		||||
						{
 | 
			
		||||
							Id = 2.AsGuid(),
 | 
			
		||||
							Slug = "new-collection",
 | 
			
		||||
							Name = "New Collection",
 | 
			
		||||
							Overview = "A collection created by new sample",
 | 
			
		||||
							Thumbnail = new Image("thumbnail")
 | 
			
		||||
						}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					typeof(Show),
 | 
			
		||||
					() =>
 | 
			
		||||
						new Show
 | 
			
		||||
						{
 | 
			
		||||
							Id = 2.AsGuid(),
 | 
			
		||||
							Slug = "new-show",
 | 
			
		||||
							Name = "New Show",
 | 
			
		||||
							Overview = "overview",
 | 
			
		||||
							Status = Status.Planned,
 | 
			
		||||
							StartAir = new DateTime(2011, 1, 1).ToUniversalTime(),
 | 
			
		||||
							EndAir = new DateTime(2011, 1, 1).ToUniversalTime(),
 | 
			
		||||
							Poster = new Image("Poster"),
 | 
			
		||||
							Logo = new Image("Logo"),
 | 
			
		||||
							Thumbnail = new Image("Thumbnail"),
 | 
			
		||||
							Studio = null
 | 
			
		||||
						}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					typeof(Season),
 | 
			
		||||
					() =>
 | 
			
		||||
						new Season
 | 
			
		||||
						{
 | 
			
		||||
							Id = 2.AsGuid(),
 | 
			
		||||
							ShowId = 1.AsGuid(),
 | 
			
		||||
							ShowSlug = Get<Show>().Slug,
 | 
			
		||||
							Name = "New season",
 | 
			
		||||
							Overview = "New overview",
 | 
			
		||||
							EndDate = new DateTime(2000, 10, 10).ToUniversalTime(),
 | 
			
		||||
							SeasonNumber = 2,
 | 
			
		||||
							StartDate = new DateTime(2010, 10, 10).ToUniversalTime(),
 | 
			
		||||
							Logo = new Image("logo")
 | 
			
		||||
						}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					typeof(Episode),
 | 
			
		||||
					() =>
 | 
			
		||||
						new Episode
 | 
			
		||||
						{
 | 
			
		||||
							Id = 2.AsGuid(),
 | 
			
		||||
							ShowId = 1.AsGuid(),
 | 
			
		||||
							ShowSlug = Get<Show>().Slug,
 | 
			
		||||
							SeasonId = 1.AsGuid(),
 | 
			
		||||
							SeasonNumber = Get<Season>().SeasonNumber,
 | 
			
		||||
							EpisodeNumber = 3,
 | 
			
		||||
							AbsoluteNumber = 4,
 | 
			
		||||
							Path = "/episode-path",
 | 
			
		||||
							Name = "New Episode Title",
 | 
			
		||||
							ReleaseDate = new DateTime(2000, 10, 10).ToUniversalTime(),
 | 
			
		||||
							Overview = "new episode overview",
 | 
			
		||||
							Logo = new Image("new episode logo")
 | 
			
		||||
						}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					typeof(People),
 | 
			
		||||
					() =>
 | 
			
		||||
						new People
 | 
			
		||||
						{
 | 
			
		||||
							Id = 2.AsGuid(),
 | 
			
		||||
							Slug = "new-person-name",
 | 
			
		||||
							Name = "New person name",
 | 
			
		||||
							Logo = new Image("Old Logo"),
 | 
			
		||||
							Poster = new Image("Old poster")
 | 
			
		||||
						}
 | 
			
		||||
				}
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
		private static readonly Dictionary<Type, Func<object>> Samples =
 | 
			
		||||
			new()
 | 
			
		||||
			{
 | 
			
		||||
				{
 | 
			
		||||
					typeof(Collection),
 | 
			
		||||
					() =>
 | 
			
		||||
						new Collection
 | 
			
		||||
						{
 | 
			
		||||
							Id = 1.AsGuid(),
 | 
			
		||||
							Slug = "collection",
 | 
			
		||||
							Name = "Collection",
 | 
			
		||||
							Overview = "A nice collection for tests",
 | 
			
		||||
							Poster = new Image("Poster")
 | 
			
		||||
						}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					typeof(Show),
 | 
			
		||||
					() =>
 | 
			
		||||
						new Show
 | 
			
		||||
						{
 | 
			
		||||
							Id = 1.AsGuid(),
 | 
			
		||||
							Slug = "anohana",
 | 
			
		||||
							Name = "Anohana: The Flower We Saw That Day",
 | 
			
		||||
							Aliases = new List<string>
 | 
			
		||||
							{
 | 
			
		||||
								"Ano Hi Mita Hana no Namae o Bokutachi wa Mada Shiranai.",
 | 
			
		||||
								"AnoHana",
 | 
			
		||||
								"We Still Don't Know the Name of the Flower We Saw That Day."
 | 
			
		||||
							},
 | 
			
		||||
							Overview =
 | 
			
		||||
								"When Yadomi Jinta was a child, he was a central piece in a group of close friends. "
 | 
			
		||||
								+ "In time, however, these childhood friends drifted apart, and when they became high "
 | 
			
		||||
								+ "school students, they had long ceased to think of each other as friends.",
 | 
			
		||||
							Status = Status.Finished,
 | 
			
		||||
							StudioId = 1.AsGuid(),
 | 
			
		||||
							StartAir = new DateTime(2011, 1, 1).ToUniversalTime(),
 | 
			
		||||
							EndAir = new DateTime(2011, 1, 1).ToUniversalTime(),
 | 
			
		||||
							Poster = new Image("Poster"),
 | 
			
		||||
							Logo = new Image("Logo"),
 | 
			
		||||
							Thumbnail = new Image("Thumbnail"),
 | 
			
		||||
							Studio = null
 | 
			
		||||
						}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					typeof(Season),
 | 
			
		||||
					() =>
 | 
			
		||||
						new Season
 | 
			
		||||
						{
 | 
			
		||||
							Id = 1.AsGuid(),
 | 
			
		||||
							ShowSlug = "anohana",
 | 
			
		||||
							ShowId = 1.AsGuid(),
 | 
			
		||||
							SeasonNumber = 1,
 | 
			
		||||
							Name = "Season 1",
 | 
			
		||||
							Overview = "The first season",
 | 
			
		||||
							StartDate = new DateTime(2020, 06, 05).ToUniversalTime(),
 | 
			
		||||
							EndDate = new DateTime(2020, 07, 05).ToUniversalTime(),
 | 
			
		||||
							Poster = new Image("Poster"),
 | 
			
		||||
							Logo = new Image("Logo"),
 | 
			
		||||
							Thumbnail = new Image("Thumbnail")
 | 
			
		||||
						}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					typeof(Episode),
 | 
			
		||||
					() =>
 | 
			
		||||
						new Episode
 | 
			
		||||
						{
 | 
			
		||||
							Id = 1.AsGuid(),
 | 
			
		||||
							ShowSlug = "anohana",
 | 
			
		||||
							ShowId = 1.AsGuid(),
 | 
			
		||||
							SeasonId = 1.AsGuid(),
 | 
			
		||||
							SeasonNumber = 1,
 | 
			
		||||
							EpisodeNumber = 1,
 | 
			
		||||
							AbsoluteNumber = 1,
 | 
			
		||||
							Path = "/home/kyoo/anohana-s1e1",
 | 
			
		||||
							Poster = new Image("Poster"),
 | 
			
		||||
							Logo = new Image("Logo"),
 | 
			
		||||
							Thumbnail = new Image("Thumbnail"),
 | 
			
		||||
							Name = "Episode 1",
 | 
			
		||||
							Overview = "Summary of the first episode",
 | 
			
		||||
							ReleaseDate = new DateTime(2020, 06, 05).ToUniversalTime()
 | 
			
		||||
						}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					typeof(People),
 | 
			
		||||
					() =>
 | 
			
		||||
						new People
 | 
			
		||||
						{
 | 
			
		||||
							Id = 1.AsGuid(),
 | 
			
		||||
							Slug = "the-actor",
 | 
			
		||||
							Name = "The Actor",
 | 
			
		||||
							Poster = new Image("Poster"),
 | 
			
		||||
							Logo = new Image("Logo"),
 | 
			
		||||
							Thumbnail = new Image("Thumbnail")
 | 
			
		||||
						}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					typeof(Studio),
 | 
			
		||||
					() =>
 | 
			
		||||
						new Studio
 | 
			
		||||
						{
 | 
			
		||||
							Id = 1.AsGuid(),
 | 
			
		||||
							Slug = "hyper-studio",
 | 
			
		||||
							Name = "Hyper studio",
 | 
			
		||||
						}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					typeof(User),
 | 
			
		||||
					() =>
 | 
			
		||||
						new User
 | 
			
		||||
						{
 | 
			
		||||
							Id = 1.AsGuid(),
 | 
			
		||||
							Slug = "user",
 | 
			
		||||
							Username = "User",
 | 
			
		||||
							Email = "user@im-a-user.com",
 | 
			
		||||
							Password = "MD5-encoded",
 | 
			
		||||
							Permissions = new[] { "overall.read" }
 | 
			
		||||
						}
 | 
			
		||||
				}
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
		public static T Get<T>()
 | 
			
		||||
		{
 | 
			
		||||
			return (T)Samples[typeof(T)]();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static T GetNew<T>()
 | 
			
		||||
		{
 | 
			
		||||
			return (T)NewSamples[typeof(T)]();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static void FillDatabase(DatabaseContext context)
 | 
			
		||||
		{
 | 
			
		||||
			Collection collection = Get<Collection>();
 | 
			
		||||
			context.Collections.Add(collection);
 | 
			
		||||
 | 
			
		||||
			Show show = Get<Show>();
 | 
			
		||||
			context.Shows.Add(show);
 | 
			
		||||
 | 
			
		||||
			Season season = Get<Season>();
 | 
			
		||||
			season.Show = show;
 | 
			
		||||
			context.Seasons.Add(season);
 | 
			
		||||
 | 
			
		||||
			Episode episode = Get<Episode>();
 | 
			
		||||
			episode.Show = show;
 | 
			
		||||
			episode.Season = season;
 | 
			
		||||
			context.Episodes.Add(episode);
 | 
			
		||||
 | 
			
		||||
			Studio studio = Get<Studio>();
 | 
			
		||||
			studio.Shows = new List<Show> { show };
 | 
			
		||||
			context.Studios.Add(studio);
 | 
			
		||||
 | 
			
		||||
			People people = Get<People>();
 | 
			
		||||
			// context.People.Add(people);
 | 
			
		||||
 | 
			
		||||
			User user = Get<User>();
 | 
			
		||||
			context.Users.Add(user);
 | 
			
		||||
 | 
			
		||||
			context.SaveChanges();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static Episode GetAbsoluteEpisode()
 | 
			
		||||
		{
 | 
			
		||||
			return new()
 | 
			
		||||
			{
 | 
			
		||||
				Id = 2.AsGuid(),
 | 
			
		||||
				ShowSlug = "anohana",
 | 
			
		||||
				ShowId = 1.AsGuid(),
 | 
			
		||||
				SeasonNumber = null,
 | 
			
		||||
				EpisodeNumber = null,
 | 
			
		||||
				AbsoluteNumber = 3,
 | 
			
		||||
				Path = "/home/kyoo/anohana-3",
 | 
			
		||||
				Poster = new Image("Poster"),
 | 
			
		||||
				Logo = new Image("Logo"),
 | 
			
		||||
				Thumbnail = new Image("Thumbnail"),
 | 
			
		||||
				Name = "Episode 3",
 | 
			
		||||
				Overview = "Summary of the third absolute episode",
 | 
			
		||||
				ReleaseDate = new DateTime(2020, 06, 05).ToUniversalTime()
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,85 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Security.Cryptography;
 | 
			
		||||
using FluentAssertions;
 | 
			
		||||
using JetBrains.Annotations;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Xunit.Sdk;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// Custom assertions used by Kyoo's tests.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	public static class KAssert
 | 
			
		||||
	{
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Check if every property of the item is equal to the other's object.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <param name="expected">The value to check against</param>
 | 
			
		||||
		/// <param name="value">The value to check</param>
 | 
			
		||||
		/// <typeparam name="T">The type to check</typeparam>
 | 
			
		||||
		[AssertionMethod]
 | 
			
		||||
		public static void DeepEqual<T>(T expected, T value)
 | 
			
		||||
		{
 | 
			
		||||
			if (expected is IAddedDate ea && value is IAddedDate va)
 | 
			
		||||
			{
 | 
			
		||||
				ea.AddedDate = DateTime.UnixEpoch;
 | 
			
		||||
				va.AddedDate = DateTime.UnixEpoch;
 | 
			
		||||
			}
 | 
			
		||||
			value.Should().BeEquivalentTo(expected);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Explicitly fail a test.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		[AssertionMethod]
 | 
			
		||||
		public static void Fail()
 | 
			
		||||
		{
 | 
			
		||||
			throw new XunitException("Explicit fail");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Explicitly fail a test.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <param name="message">The message that will be seen in the test report</param>
 | 
			
		||||
		[AssertionMethod]
 | 
			
		||||
		public static void Fail(string message)
 | 
			
		||||
		{
 | 
			
		||||
			throw new XunitException(message);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static Guid AsGuid(this string src)
 | 
			
		||||
		{
 | 
			
		||||
			// Use MD5 since (1) it's faster then SHA and (2) it's already 16 bytes which matches the Guid
 | 
			
		||||
			return string.IsNullOrWhiteSpace(src)
 | 
			
		||||
				? Guid.Empty
 | 
			
		||||
				: new Guid(MD5.Create().ComputeHash(System.Text.Encoding.UTF8.GetBytes(src)));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static Guid AsGuid(this int src)
 | 
			
		||||
		{
 | 
			
		||||
			// Use MD5 since (1) it's faster then SHA and (2) it's already 16 bytes which matches the Guid
 | 
			
		||||
			return src == 0
 | 
			
		||||
				? Guid.Empty
 | 
			
		||||
				: new Guid(MD5.Create().ComputeHash(BitConverter.GetBytes(src)));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,35 +0,0 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<TargetFramework>net7.0</TargetFramework>
 | 
			
		||||
		<LangVersion>default</LangVersion>
 | 
			
		||||
 | 
			
		||||
		<IsPackable>false</IsPackable>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<PackageReference Include="coverlet.msbuild" Version="6.0.0">
 | 
			
		||||
			<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
			
		||||
			<PrivateAssets>all</PrivateAssets>
 | 
			
		||||
		</PackageReference>
 | 
			
		||||
		<PackageReference Include="Divergic.Logging.Xunit" Version="4.2.0" />
 | 
			
		||||
		<PackageReference Include="FluentAssertions" Version="6.12.0" />
 | 
			
		||||
		<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
 | 
			
		||||
		<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
 | 
			
		||||
		<PackageReference Include="Moq" Version="4.20.69" />
 | 
			
		||||
		<PackageReference Include="TvDbSharper" Version="4.0.10" />
 | 
			
		||||
		<PackageReference Include="xunit" Version="2.5.1" />
 | 
			
		||||
		<PackageReference Include="xunit.runner.visualstudio" Version="2.5.1">
 | 
			
		||||
			<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
			
		||||
			<PrivateAssets>all</PrivateAssets>
 | 
			
		||||
		</PackageReference>
 | 
			
		||||
		<PackageReference Include="coverlet.collector" Version="6.0.0">
 | 
			
		||||
			<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
			
		||||
			<PrivateAssets>all</PrivateAssets>
 | 
			
		||||
		</PackageReference>
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<ProjectReference Include="../../src/Kyoo.Abstractions/Kyoo.Abstractions.csproj" />
 | 
			
		||||
		<ProjectReference Include="../../src/Kyoo.Host/Kyoo.Host.csproj" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
</Project>
 | 
			
		||||
@ -1,41 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using Kyoo.Utils;
 | 
			
		||||
using Xunit;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests.Utility
 | 
			
		||||
{
 | 
			
		||||
	public class EnumerableTests
 | 
			
		||||
	{
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void IfEmptyTest()
 | 
			
		||||
		{
 | 
			
		||||
			int[] list = { 1, 2, 3, 4 };
 | 
			
		||||
			list = list.IfEmpty(() => KAssert.Fail("Empty action should not be triggered."))
 | 
			
		||||
				.ToArray();
 | 
			
		||||
			list = Array.Empty<int>();
 | 
			
		||||
			Assert.Throws<ArgumentException>(
 | 
			
		||||
				() => list.IfEmpty(() => throw new ArgumentException()).ToList()
 | 
			
		||||
			);
 | 
			
		||||
			Assert.Empty(list.IfEmpty(() => { }));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,145 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using JetBrains.Annotations;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Kyoo.Utils;
 | 
			
		||||
using Xunit;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests.Utility
 | 
			
		||||
{
 | 
			
		||||
	public class MergerTests
 | 
			
		||||
	{
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void CompleteTest()
 | 
			
		||||
		{
 | 
			
		||||
			Studio genre = new() { Name = "merged" };
 | 
			
		||||
			Studio genre2 = new() { Name = "test", Id = 5.AsGuid(), };
 | 
			
		||||
			Studio ret = Merger.Complete(genre, genre2);
 | 
			
		||||
			Assert.True(ReferenceEquals(genre, ret));
 | 
			
		||||
			Assert.Equal(5.AsGuid(), ret.Id);
 | 
			
		||||
			Assert.Equal("test", genre.Name);
 | 
			
		||||
			Assert.Null(genre.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void CompleteDictionaryTest()
 | 
			
		||||
		{
 | 
			
		||||
			Collection collection = new() { Name = "merged", };
 | 
			
		||||
			Collection collection2 = new() { Id = 5.AsGuid(), Name = "test", };
 | 
			
		||||
			Collection ret = Merger.Complete(collection, collection2);
 | 
			
		||||
			Assert.True(ReferenceEquals(collection, ret));
 | 
			
		||||
			Assert.Equal(5.AsGuid(), ret.Id);
 | 
			
		||||
			Assert.Equal("test", ret.Name);
 | 
			
		||||
			Assert.Null(ret.Slug);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void CompleteDictionaryOutParam()
 | 
			
		||||
		{
 | 
			
		||||
			Dictionary<string, string> first = new() { ["logo"] = "logo", ["poster"] = "poster" };
 | 
			
		||||
			Dictionary<string, string> second =
 | 
			
		||||
				new() { ["poster"] = "new-poster", ["thumbnail"] = "thumbnails" };
 | 
			
		||||
			IDictionary<string, string> ret = Merger.CompleteDictionaries(
 | 
			
		||||
				first,
 | 
			
		||||
				second,
 | 
			
		||||
				out bool changed
 | 
			
		||||
			);
 | 
			
		||||
			Assert.True(changed);
 | 
			
		||||
			Assert.Equal(3, ret.Count);
 | 
			
		||||
			Assert.Equal("new-poster", ret["poster"]);
 | 
			
		||||
			Assert.Equal("thumbnails", ret["thumbnail"]);
 | 
			
		||||
			Assert.Equal("logo", ret["logo"]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void CompleteDictionaryEqualTest()
 | 
			
		||||
		{
 | 
			
		||||
			Dictionary<string, string> first = new() { ["poster"] = "poster" };
 | 
			
		||||
			Dictionary<string, string> second = new() { ["poster"] = "new-poster", };
 | 
			
		||||
			IDictionary<string, string> ret = Merger.CompleteDictionaries(
 | 
			
		||||
				first,
 | 
			
		||||
				second,
 | 
			
		||||
				out bool changed
 | 
			
		||||
			);
 | 
			
		||||
			Assert.True(changed);
 | 
			
		||||
			Assert.Single(ret);
 | 
			
		||||
			Assert.Equal("new-poster", ret["poster"]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private class TestMergeSetter
 | 
			
		||||
		{
 | 
			
		||||
			public Dictionary<int, int> Backing;
 | 
			
		||||
 | 
			
		||||
			[UsedImplicitly]
 | 
			
		||||
			public Dictionary<int, int> Dictionary
 | 
			
		||||
			{
 | 
			
		||||
				get => Backing;
 | 
			
		||||
				set
 | 
			
		||||
				{
 | 
			
		||||
					Backing = value;
 | 
			
		||||
					KAssert.Fail();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void CompleteDictionaryNoChangeNoSetTest()
 | 
			
		||||
		{
 | 
			
		||||
			TestMergeSetter first = new() { Backing = new Dictionary<int, int> { [2] = 3 } };
 | 
			
		||||
			TestMergeSetter second = new() { Backing = new Dictionary<int, int>() };
 | 
			
		||||
			Merger.Complete(first, second);
 | 
			
		||||
			// This should no call the setter of first so the test should pass.
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void CompleteDictionaryNullValue()
 | 
			
		||||
		{
 | 
			
		||||
			Dictionary<string, string> first = new() { ["logo"] = "logo", ["poster"] = null };
 | 
			
		||||
			Dictionary<string, string> second =
 | 
			
		||||
				new() { ["poster"] = "new-poster", ["thumbnail"] = "thumbnails" };
 | 
			
		||||
			IDictionary<string, string> ret = Merger.CompleteDictionaries(
 | 
			
		||||
				first,
 | 
			
		||||
				second,
 | 
			
		||||
				out bool changed
 | 
			
		||||
			);
 | 
			
		||||
			Assert.True(changed);
 | 
			
		||||
			Assert.Equal(3, ret.Count);
 | 
			
		||||
			Assert.Equal("new-poster", ret["poster"]);
 | 
			
		||||
			Assert.Equal("thumbnails", ret["thumbnail"]);
 | 
			
		||||
			Assert.Equal("logo", ret["logo"]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void CompleteDictionaryNullValueNoChange()
 | 
			
		||||
		{
 | 
			
		||||
			Dictionary<string, string> first = new() { ["logo"] = "logo", ["poster"] = null };
 | 
			
		||||
			Dictionary<string, string> second = new() { ["poster"] = null, };
 | 
			
		||||
			IDictionary<string, string> ret = Merger.CompleteDictionaries(
 | 
			
		||||
				first,
 | 
			
		||||
				second,
 | 
			
		||||
				out bool changed
 | 
			
		||||
			);
 | 
			
		||||
			Assert.False(changed);
 | 
			
		||||
			Assert.Equal(2, ret.Count);
 | 
			
		||||
			Assert.Null(ret["poster"]);
 | 
			
		||||
			Assert.Equal("logo", ret["logo"]);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,96 +0,0 @@
 | 
			
		||||
// Kyoo - A portable and vast media library solution.
 | 
			
		||||
// Copyright (c) Kyoo.
 | 
			
		||||
//
 | 
			
		||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// any later version.
 | 
			
		||||
//
 | 
			
		||||
// Kyoo is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using Kyoo.Abstractions.Models;
 | 
			
		||||
using Xunit;
 | 
			
		||||
using KUtility = Kyoo.Utils.Utility;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.Tests.Utility
 | 
			
		||||
{
 | 
			
		||||
	public class UtilityTests
 | 
			
		||||
	{
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void IsPropertyExpression_Tests()
 | 
			
		||||
		{
 | 
			
		||||
			Expression<Func<Show, Guid>> member = x => x.Id;
 | 
			
		||||
			Expression<Func<Show, object>> memberCast = x => x.Id;
 | 
			
		||||
 | 
			
		||||
			Assert.True(KUtility.IsPropertyExpression(member));
 | 
			
		||||
			Assert.True(KUtility.IsPropertyExpression(memberCast));
 | 
			
		||||
 | 
			
		||||
			Expression<Func<Show, object>> call = x => x.ToString();
 | 
			
		||||
			Assert.False(KUtility.IsPropertyExpression(call));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void GetPropertyName_Test()
 | 
			
		||||
		{
 | 
			
		||||
			Expression<Func<Show, Guid>> member = x => x.Id;
 | 
			
		||||
			Expression<Func<Show, object>> memberCast = x => x.Id;
 | 
			
		||||
 | 
			
		||||
			Assert.Equal("Id", KUtility.GetPropertyName(member));
 | 
			
		||||
			Assert.Equal("Id", KUtility.GetPropertyName(memberCast));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void GetMethodTest()
 | 
			
		||||
		{
 | 
			
		||||
			MethodInfo method = KUtility.GetMethod(
 | 
			
		||||
				typeof(UtilityTests),
 | 
			
		||||
				BindingFlags.Instance | BindingFlags.Public,
 | 
			
		||||
				nameof(GetMethodTest),
 | 
			
		||||
				Array.Empty<Type>(),
 | 
			
		||||
				Array.Empty<object>()
 | 
			
		||||
			);
 | 
			
		||||
			Assert.Equal(MethodBase.GetCurrentMethod(), method);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void GetMethodInvalidGenericsTest()
 | 
			
		||||
		{
 | 
			
		||||
			Assert.Throws<ArgumentException>(
 | 
			
		||||
				() =>
 | 
			
		||||
					KUtility.GetMethod(
 | 
			
		||||
						typeof(UtilityTests),
 | 
			
		||||
						BindingFlags.Instance | BindingFlags.Public,
 | 
			
		||||
						nameof(GetMethodTest),
 | 
			
		||||
						new[] { typeof(KUtility) },
 | 
			
		||||
						Array.Empty<object>()
 | 
			
		||||
					)
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void GetMethodInvalidParamsTest()
 | 
			
		||||
		{
 | 
			
		||||
			Assert.Throws<ArgumentException>(
 | 
			
		||||
				() =>
 | 
			
		||||
					KUtility.GetMethod(
 | 
			
		||||
						typeof(UtilityTests),
 | 
			
		||||
						BindingFlags.Instance | BindingFlags.Public,
 | 
			
		||||
						nameof(GetMethodTest),
 | 
			
		||||
						Array.Empty<Type>(),
 | 
			
		||||
						new object[] { this }
 | 
			
		||||
					)
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -210,37 +210,40 @@ App.getInitialProps = async (ctx: AppContext) => {
 | 
			
		||||
 | 
			
		||||
	if (typeof window !== "undefined") return { pageProps: superjson.serialize(appProps.pageProps) };
 | 
			
		||||
 | 
			
		||||
	const getUrl = Component.getFetchUrls;
 | 
			
		||||
	const getLayoutUrl =
 | 
			
		||||
		Component.getLayout && "Layout" in Component.getLayout
 | 
			
		||||
			? Component.getLayout.Layout.getFetchUrls
 | 
			
		||||
			: Component.getLayout?.getFetchUrls;
 | 
			
		||||
	const urls: QueryIdentifier[] = [
 | 
			
		||||
		...(getUrl ? getUrl(ctx.router.query as any, items) : []),
 | 
			
		||||
		...(getLayoutUrl ? getLayoutUrl(ctx.router.query as any, items) : []),
 | 
			
		||||
		// always include server info for guest permissions.
 | 
			
		||||
		{ path: ["info"], parser: ServerInfoP },
 | 
			
		||||
	];
 | 
			
		||||
	try {
 | 
			
		||||
		const getUrl = Component.getFetchUrls;
 | 
			
		||||
		const getLayoutUrl =
 | 
			
		||||
			Component.getLayout && "Layout" in Component.getLayout
 | 
			
		||||
				? Component.getLayout.Layout.getFetchUrls
 | 
			
		||||
				: Component.getLayout?.getFetchUrls;
 | 
			
		||||
		const urls: QueryIdentifier[] = [
 | 
			
		||||
			...(getUrl ? getUrl(ctx.router.query as any, items) : []),
 | 
			
		||||
			...(getLayoutUrl ? getLayoutUrl(ctx.router.query as any, items) : []),
 | 
			
		||||
			// always include server info for guest permissions.
 | 
			
		||||
			{ path: ["info"], parser: ServerInfoP },
 | 
			
		||||
		];
 | 
			
		||||
 | 
			
		||||
	setSsrApiUrl();
 | 
			
		||||
		setSsrApiUrl();
 | 
			
		||||
 | 
			
		||||
	const account = readCookie(ctx.ctx.req?.headers.cookie, "account", AccountP);
 | 
			
		||||
	if (account) urls.push({ path: ["auth", "me"], parser: UserP });
 | 
			
		||||
	const [authToken, token, error] = await getTokenWJ(account);
 | 
			
		||||
	if (error) appProps.pageProps.ssrError = error;
 | 
			
		||||
	else {
 | 
			
		||||
		const client = (await fetchQuery(urls, authToken))!;
 | 
			
		||||
		appProps.pageProps.queryState = dehydrate(client);
 | 
			
		||||
		if (account) {
 | 
			
		||||
			appProps.pageProps.token = token;
 | 
			
		||||
			appProps.pageProps.account = {
 | 
			
		||||
				...client.getQueryData(["auth", "me"]),
 | 
			
		||||
				...account,
 | 
			
		||||
			};
 | 
			
		||||
		const account = readCookie(ctx.ctx.req?.headers.cookie, "account", AccountP);
 | 
			
		||||
		if (account) urls.push({ path: ["auth", "me"], parser: UserP });
 | 
			
		||||
		const [authToken, token, error] = await getTokenWJ(account);
 | 
			
		||||
		if (error) appProps.pageProps.ssrError = error;
 | 
			
		||||
		else {
 | 
			
		||||
			const client = (await fetchQuery(urls, authToken))!;
 | 
			
		||||
			appProps.pageProps.queryState = dehydrate(client);
 | 
			
		||||
			if (account) {
 | 
			
		||||
				appProps.pageProps.token = token;
 | 
			
		||||
				appProps.pageProps.account = {
 | 
			
		||||
					...client.getQueryData(["auth", "me"]),
 | 
			
		||||
					...account,
 | 
			
		||||
				};
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		appProps.pageProps.theme = readCookie(ctx.ctx.req?.headers.cookie, "theme") ?? "auto";
 | 
			
		||||
	} catch (e) {
 | 
			
		||||
		console.error("SSR error, disabling it.");
 | 
			
		||||
	}
 | 
			
		||||
	appProps.pageProps.theme = readCookie(ctx.ctx.req?.headers.cookie, "theme") ?? "auto";
 | 
			
		||||
 | 
			
		||||
	return { pageProps: superjson.serialize(appProps.pageProps) };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -62,8 +62,7 @@ export const readCookie = <T extends ZodTypeAny>(
 | 
			
		||||
		}
 | 
			
		||||
		if (c.indexOf(name) == 0) {
 | 
			
		||||
			const str = c.substring(name.length, c.length);
 | 
			
		||||
			const ret = JSON.parse(str);
 | 
			
		||||
			return parser ? parser.parse(ret) : ret;
 | 
			
		||||
			return parser ? parser.parse(JSON.parse(str)) : str;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return null;
 | 
			
		||||
 | 
			
		||||
@ -68,7 +68,7 @@
 | 
			
		||||
		"more": "Plus",
 | 
			
		||||
		"expand": "Développer",
 | 
			
		||||
		"collapse": "Replier",
 | 
			
		||||
		"edit": "Changer",
 | 
			
		||||
		"edit": "Modifier",
 | 
			
		||||
		"or": "OU",
 | 
			
		||||
		"loading": "Chargement en cours"
 | 
			
		||||
	},
 | 
			
		||||
@ -125,7 +125,7 @@
 | 
			
		||||
			},
 | 
			
		||||
			"password": {
 | 
			
		||||
				"label": "Mot de passe",
 | 
			
		||||
				"description": "Changer de mot de passe",
 | 
			
		||||
				"description": "Modifier votre mot de passe",
 | 
			
		||||
				"oldPassword": "Ancien mot de passe",
 | 
			
		||||
				"newPassword": "Nouveau mot de passe"
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user