mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-02 13:14:29 -04:00
API: Documenting the items's API AND removing the BaseUrl need on APIs constructor, retriving it on the controller DI service
This commit is contained in:
parent
2287919b60
commit
7e4fab1ee1
@ -89,3 +89,5 @@ resharper_xmldoc_attribute_indent = align_by_first_attribute
|
|||||||
resharper_xmldoc_indent_child_elements = RemoveIndent
|
resharper_xmldoc_indent_child_elements = RemoveIndent
|
||||||
resharper_xmldoc_indent_text = RemoveIndent
|
resharper_xmldoc_indent_text = RemoveIndent
|
||||||
|
|
||||||
|
# Waiting for https://github.com/dotnet/roslyn/issues/44596 to get fixed.
|
||||||
|
# file_header_template = Kyoo - A portable and vast media library solution.\nCopyright (c) Kyoo.\n\nSee AUTHORS.md and LICENSE file in the project root for full license information.\n\nKyoo is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nKyoo is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
@ -32,8 +32,13 @@ namespace Kyoo.Abstractions.Models.Utils
|
|||||||
public const int AlternativeRoute = 1;
|
public const int AlternativeRoute = 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A group name for <see cref="ApiDefinitionAttribute"/>. It should be used for every <see cref="IResource"/>.
|
/// A group name for <see cref="ApiDefinitionAttribute"/>. It should be used for main resources of kyoo.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string ResourcesGroup = "Resources";
|
public const string ResourcesGroup = "Resources";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A group name for <see cref="ApiDefinitionAttribute"/>. It should be used for endpoints useful for playback.
|
||||||
|
/// </summary>
|
||||||
|
public const string WatchGroup = "Watch";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
using System;
|
||||||
using Autofac;
|
using Autofac;
|
||||||
using Autofac.Builder;
|
using Autofac.Builder;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
@ -96,9 +97,10 @@ namespace Kyoo.Abstractions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="configuration">The configuration instance</param>
|
/// <param name="configuration">The configuration instance</param>
|
||||||
/// <returns>The public URl of kyoo (without a slash at the end)</returns>
|
/// <returns>The public URl of kyoo (without a slash at the end)</returns>
|
||||||
public static string GetPublicUrl(this IConfiguration configuration)
|
public static Uri GetPublicUrl(this IConfiguration configuration)
|
||||||
{
|
{
|
||||||
return configuration["basics:publicUrl"]?.TrimEnd('/') ?? "http://localhost:5000";
|
string uri = configuration["basics:publicUrl"]?.TrimEnd('/') ?? "http://localhost:5000";
|
||||||
|
return new Uri(uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ namespace Kyoo.Authentication
|
|||||||
|
|
||||||
DefaultCorsPolicyService cors = new(_logger)
|
DefaultCorsPolicyService cors = new(_logger)
|
||||||
{
|
{
|
||||||
AllowedOrigins = { new Uri(_configuration.GetPublicUrl()).GetLeftPart(UriPartial.Authority) }
|
AllowedOrigins = { _configuration.GetPublicUrl().GetLeftPart(UriPartial.Authority) }
|
||||||
};
|
};
|
||||||
builder.RegisterInstance(cors).As<ICorsPolicyService>().SingleInstance();
|
builder.RegisterInstance(cors).As<ICorsPolicyService>().SingleInstance();
|
||||||
}
|
}
|
||||||
@ -112,7 +112,7 @@ namespace Kyoo.Authentication
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Configure(IServiceCollection services)
|
public void Configure(IServiceCollection services)
|
||||||
{
|
{
|
||||||
string publicUrl = _configuration.GetPublicUrl();
|
Uri publicUrl = _configuration.GetPublicUrl();
|
||||||
|
|
||||||
if (_environment.IsDevelopment())
|
if (_environment.IsDevelopment())
|
||||||
IdentityModelEventSource.ShowPII = true;
|
IdentityModelEventSource.ShowPII = true;
|
||||||
@ -136,7 +136,7 @@ namespace Kyoo.Authentication
|
|||||||
|
|
||||||
services.AddIdentityServer(options =>
|
services.AddIdentityServer(options =>
|
||||||
{
|
{
|
||||||
options.IssuerUri = publicUrl;
|
options.IssuerUri = publicUrl.ToString();
|
||||||
options.UserInteraction.LoginUrl = $"{publicUrl}/login";
|
options.UserInteraction.LoginUrl = $"{publicUrl}/login";
|
||||||
options.UserInteraction.ErrorUrl = $"{publicUrl}/error";
|
options.UserInteraction.ErrorUrl = $"{publicUrl}/error";
|
||||||
options.UserInteraction.LogoutUrl = $"{publicUrl}/logout";
|
options.UserInteraction.LogoutUrl = $"{publicUrl}/logout";
|
||||||
@ -151,7 +151,7 @@ namespace Kyoo.Authentication
|
|||||||
services.AddAuthentication()
|
services.AddAuthentication()
|
||||||
.AddJwtBearer(options =>
|
.AddJwtBearer(options =>
|
||||||
{
|
{
|
||||||
options.Authority = publicUrl;
|
options.Authority = publicUrl.ToString();
|
||||||
options.Audience = "kyoo";
|
options.Audience = "kyoo";
|
||||||
options.RequireHttpsMetadata = false;
|
options.RequireHttpsMetadata = false;
|
||||||
});
|
});
|
||||||
@ -189,7 +189,7 @@ namespace Kyoo.Authentication
|
|||||||
{
|
{
|
||||||
app.Use((ctx, next) =>
|
app.Use((ctx, next) =>
|
||||||
{
|
{
|
||||||
ctx.SetIdentityServerOrigin(_configuration.GetPublicUrl());
|
ctx.SetIdentityServerOrigin(_configuration.GetPublicUrl().ToString());
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
app.UseIdentityServer();
|
app.UseIdentityServer();
|
||||||
|
@ -140,7 +140,7 @@ namespace Kyoo.Core
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Configure(IServiceCollection services)
|
public void Configure(IServiceCollection services)
|
||||||
{
|
{
|
||||||
string publicUrl = _configuration.GetPublicUrl();
|
Uri publicUrl = _configuration.GetPublicUrl();
|
||||||
|
|
||||||
services.AddMvcCore()
|
services.AddMvcCore()
|
||||||
.AddDataAnnotations()
|
.AddDataAnnotations()
|
||||||
|
@ -25,10 +25,8 @@ using Kyoo.Abstractions.Models;
|
|||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Kyoo.Core.Models.Options;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using static Kyoo.Abstractions.Models.Utils.Constants;
|
using static Kyoo.Abstractions.Models.Utils.Constants;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
@ -56,14 +54,10 @@ namespace Kyoo.Core.Api
|
|||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="files">The file manager used to send images.</param>
|
/// <param name="files">The file manager used to send images.</param>
|
||||||
/// <param name="thumbs">The thumbnail manager used to retrieve images paths.</param>
|
/// <param name="thumbs">The thumbnail manager used to retrieve images paths.</param>
|
||||||
/// <param name="options">
|
|
||||||
/// Options used to retrieve the base URL of Kyoo.
|
|
||||||
/// </param>
|
|
||||||
public CollectionApi(ILibraryManager libraryManager,
|
public CollectionApi(ILibraryManager libraryManager,
|
||||||
IFileSystem files,
|
IFileSystem files,
|
||||||
IThumbnailsManager thumbs,
|
IThumbnailsManager thumbs)
|
||||||
IOptions<BasicOptions> options)
|
: base(libraryManager.CollectionRepository, files, thumbs)
|
||||||
: base(libraryManager.CollectionRepository, files, thumbs, options.Value.PublicUrl)
|
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
}
|
}
|
||||||
|
@ -25,10 +25,8 @@ using Kyoo.Abstractions.Models;
|
|||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Kyoo.Core.Models.Options;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using static Kyoo.Abstractions.Models.Utils.Constants;
|
using static Kyoo.Abstractions.Models.Utils.Constants;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
@ -39,6 +37,7 @@ namespace Kyoo.Core.Api
|
|||||||
[Route("api/episodes")]
|
[Route("api/episodes")]
|
||||||
[Route("api/episode", Order = AlternativeRoute)]
|
[Route("api/episode", Order = AlternativeRoute)]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
|
[ResourceView]
|
||||||
[PartialPermission(nameof(EpisodeApi))]
|
[PartialPermission(nameof(EpisodeApi))]
|
||||||
[ApiDefinition("Episodes", Group = ResourcesGroup)]
|
[ApiDefinition("Episodes", Group = ResourcesGroup)]
|
||||||
public class EpisodeApi : CrudThumbsApi<Episode>
|
public class EpisodeApi : CrudThumbsApi<Episode>
|
||||||
@ -56,14 +55,10 @@ namespace Kyoo.Core.Api
|
|||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="files">The file manager used to send images.</param>
|
/// <param name="files">The file manager used to send images.</param>
|
||||||
/// <param name="thumbnails">The thumbnail manager used to retrieve images paths.</param>
|
/// <param name="thumbnails">The thumbnail manager used to retrieve images paths.</param>
|
||||||
/// <param name="options">
|
|
||||||
/// Options used to retrieve the base URL of Kyoo.
|
|
||||||
/// </param>
|
|
||||||
public EpisodeApi(ILibraryManager libraryManager,
|
public EpisodeApi(ILibraryManager libraryManager,
|
||||||
IFileSystem files,
|
IFileSystem files,
|
||||||
IThumbnailsManager thumbnails,
|
IThumbnailsManager thumbnails)
|
||||||
IOptions<BasicOptions> options)
|
: base(libraryManager.EpisodeRepository, files, thumbnails)
|
||||||
: base(libraryManager.EpisodeRepository, files, thumbnails, options.Value.PublicUrl)
|
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,7 @@ using System.Threading.Tasks;
|
|||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Core.Models.Options;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
{
|
{
|
||||||
@ -37,8 +35,8 @@ namespace Kyoo.Core.Api
|
|||||||
{
|
{
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
|
||||||
public GenreApi(ILibraryManager libraryManager, IOptions<BasicOptions> options)
|
public GenreApi(ILibraryManager libraryManager)
|
||||||
: base(libraryManager.GenreRepository, options.Value.PublicUrl)
|
: base(libraryManager.GenreRepository)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,10 @@ using System.Linq;
|
|||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
|
using Kyoo.Core.Models.Options;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
{
|
{
|
||||||
|
60
src/Kyoo.Core/Views/Helper/BaseApi.cs
Normal file
60
src/Kyoo.Core/Views/Helper/BaseApi.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// 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 Kyoo.Abstractions;
|
||||||
|
using Kyoo.Abstractions.Models;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Kyoo.Core.Api
|
||||||
|
{
|
||||||
|
public class BaseApi : ControllerBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Construct and return a page from an api.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resources">The list of resources that should be included in the current page.</param>
|
||||||
|
/// <param name="limit">
|
||||||
|
/// The max number of items that should be present per page. This should be the same as in the request,
|
||||||
|
/// it is used to calculate if this is the last page and so on.
|
||||||
|
/// </param>
|
||||||
|
/// <typeparam name="TResult">The type of items on the page.</typeparam>
|
||||||
|
/// <returns>A Page representing the response.</returns>
|
||||||
|
protected Page<TResult> Page<TResult>(ICollection<TResult> resources, int limit)
|
||||||
|
where TResult : IResource
|
||||||
|
{
|
||||||
|
Uri publicUrl = HttpContext.RequestServices
|
||||||
|
.GetRequiredService<IConfiguration>()
|
||||||
|
.GetPublicUrl();
|
||||||
|
return new Page<TResult>(
|
||||||
|
resources,
|
||||||
|
new Uri(publicUrl, Request.Path),
|
||||||
|
Request.Query.ToDictionary(
|
||||||
|
x => x.Key,
|
||||||
|
x => x.Value.ToString(),
|
||||||
|
StringComparer.InvariantCultureIgnoreCase
|
||||||
|
),
|
||||||
|
limit
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
@ -36,7 +35,7 @@ namespace Kyoo.Core.Api
|
|||||||
/// <typeparam name="T">The type of resource to make CRUD apis for.</typeparam>
|
/// <typeparam name="T">The type of resource to make CRUD apis for.</typeparam>
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[ResourceView]
|
[ResourceView]
|
||||||
public class CrudApi<T> : ControllerBase
|
public class CrudApi<T> : BaseApi
|
||||||
where T : class, IResource
|
where T : class, IResource
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -44,44 +43,15 @@ namespace Kyoo.Core.Api
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected IRepository<T> Repository { get; }
|
protected IRepository<T> Repository { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The base URL of Kyoo. This will be used to create links for images and
|
|
||||||
/// <see cref="Abstractions.Models.Page{T}"/>.
|
|
||||||
/// </summary>
|
|
||||||
protected Uri BaseURL { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="CrudApi{T}"/> using the given repository and base url.
|
/// Create a new <see cref="CrudApi{T}"/> using the given repository and base url.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="repository">
|
/// <param name="repository">
|
||||||
/// The repository to use as a baking store for the type <typeparamref name="T"/>.
|
/// The repository to use as a baking store for the type <typeparamref name="T"/>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="baseURL">
|
public CrudApi(IRepository<T> repository)
|
||||||
/// The base URL of Kyoo to use to create links.
|
|
||||||
/// </param>
|
|
||||||
public CrudApi(IRepository<T> repository, Uri baseURL)
|
|
||||||
{
|
{
|
||||||
Repository = repository;
|
Repository = repository;
|
||||||
BaseURL = baseURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Construct and return a page from an api.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="resources">The list of resources that should be included in the current page.</param>
|
|
||||||
/// <param name="limit">
|
|
||||||
/// The max number of items that should be present per page. This should be the same as in the request,
|
|
||||||
/// it is used to calculate if this is the last page and so on.
|
|
||||||
/// </param>
|
|
||||||
/// <typeparam name="TResult">The type of items on the page.</typeparam>
|
|
||||||
/// <returns>A Page representing the response.</returns>
|
|
||||||
protected Page<TResult> Page<TResult>(ICollection<TResult> resources, int limit)
|
|
||||||
where TResult : IResource
|
|
||||||
{
|
|
||||||
return new Page<TResult>(resources,
|
|
||||||
new Uri(BaseURL, Request.Path),
|
|
||||||
Request.Query.ToDictionary(x => x.Key, x => x.Value.ToString(), StringComparer.InvariantCultureIgnoreCase),
|
|
||||||
limit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
@ -56,14 +55,10 @@ namespace Kyoo.Core.Api
|
|||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="files">The file manager used to send images.</param>
|
/// <param name="files">The file manager used to send images.</param>
|
||||||
/// <param name="thumbs">The thumbnail manager used to retrieve images paths.</param>
|
/// <param name="thumbs">The thumbnail manager used to retrieve images paths.</param>
|
||||||
/// <param name="baseURL">
|
|
||||||
/// The base URL of Kyoo to use to create links.
|
|
||||||
/// </param>
|
|
||||||
public CrudThumbsApi(IRepository<T> repository,
|
public CrudThumbsApi(IRepository<T> repository,
|
||||||
IFileSystem files,
|
IFileSystem files,
|
||||||
IThumbnailsManager thumbs,
|
IThumbnailsManager thumbs)
|
||||||
Uri baseURL)
|
: base(repository)
|
||||||
: base(repository, baseURL)
|
|
||||||
{
|
{
|
||||||
_files = files;
|
_files = files;
|
||||||
_thumbs = thumbs;
|
_thumbs = thumbs;
|
||||||
|
@ -30,9 +30,9 @@ namespace Kyoo.Core.Api
|
|||||||
public class JsonPropertyIgnorer : CamelCasePropertyNamesContractResolver
|
public class JsonPropertyIgnorer : CamelCasePropertyNamesContractResolver
|
||||||
{
|
{
|
||||||
private int _depth = -1;
|
private int _depth = -1;
|
||||||
private string _host;
|
private readonly Uri _host;
|
||||||
|
|
||||||
public JsonPropertyIgnorer(string host)
|
public JsonPropertyIgnorer(Uri host)
|
||||||
{
|
{
|
||||||
_host = host;
|
_host = host;
|
||||||
}
|
}
|
||||||
|
@ -26,13 +26,13 @@ namespace Kyoo.Core.Api
|
|||||||
{
|
{
|
||||||
public class SerializeAsProvider : IValueProvider
|
public class SerializeAsProvider : IValueProvider
|
||||||
{
|
{
|
||||||
private string _format;
|
private readonly string _format;
|
||||||
private string _host;
|
private readonly Uri _host;
|
||||||
|
|
||||||
public SerializeAsProvider(string format, string host)
|
public SerializeAsProvider(string format, Uri host)
|
||||||
{
|
{
|
||||||
_format = format;
|
_format = format;
|
||||||
_host = host.TrimEnd('/');
|
_host = host;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object GetValue(object target)
|
public object GetValue(object target)
|
||||||
@ -43,7 +43,7 @@ namespace Kyoo.Core.Api
|
|||||||
string modifier = x.Groups[3].Value;
|
string modifier = x.Groups[3].Value;
|
||||||
|
|
||||||
if (value == "HOST")
|
if (value == "HOST")
|
||||||
return _host;
|
return _host.ToString().TrimEnd('/');
|
||||||
|
|
||||||
PropertyInfo properties = target.GetType()
|
PropertyInfo properties = target.GetType()
|
||||||
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||||
|
@ -26,10 +26,8 @@ using Kyoo.Abstractions.Models;
|
|||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Kyoo.Core.Models.Options;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using static Kyoo.Abstractions.Models.Utils.Constants;
|
using static Kyoo.Abstractions.Models.Utils.Constants;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
@ -40,6 +38,7 @@ namespace Kyoo.Core.Api
|
|||||||
[Route("api/libraries")]
|
[Route("api/libraries")]
|
||||||
[Route("api/library", Order = AlternativeRoute)]
|
[Route("api/library", Order = AlternativeRoute)]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
|
[ResourceView]
|
||||||
[PartialPermission(nameof(LibraryApi), Group = Group.Admin)]
|
[PartialPermission(nameof(LibraryApi), Group = Group.Admin)]
|
||||||
[ApiDefinition("Library", Group = ResourcesGroup)]
|
[ApiDefinition("Library", Group = ResourcesGroup)]
|
||||||
public class LibraryApi : CrudApi<Library>
|
public class LibraryApi : CrudApi<Library>
|
||||||
@ -55,11 +54,8 @@ namespace Kyoo.Core.Api
|
|||||||
/// <param name="libraryManager">
|
/// <param name="libraryManager">
|
||||||
/// The library manager used to modify or retrieve information in the data store.
|
/// The library manager used to modify or retrieve information in the data store.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="options">
|
public LibraryApi(ILibraryManager libraryManager)
|
||||||
/// Options used to retrieve the base URL of Kyoo.
|
: base(libraryManager.LibraryRepository)
|
||||||
/// </param>
|
|
||||||
public LibraryApi(ILibraryManager libraryManager, IOptions<BasicOptions> options)
|
|
||||||
: base(libraryManager.LibraryRepository, options.Value.PublicUrl)
|
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
}
|
}
|
||||||
@ -159,7 +155,7 @@ namespace Kyoo.Core.Api
|
|||||||
/// List all items of this library.
|
/// List all items of this library.
|
||||||
/// An item can ether represent a collection or a show.
|
/// An item can ether represent a collection or a show.
|
||||||
/// This endpoint allow one to retrieve all collections and shows that are not contained in a collection.
|
/// This endpoint allow one to retrieve all collections and shows that are not contained in a collection.
|
||||||
/// This is what is displayed on the /browse page of the webapp.
|
/// This is what is displayed on the /browse/library page of the webapp.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="identifier">The ID or slug of the <see cref="Library"/>.</param>
|
/// <param name="identifier">The ID or slug of the <see cref="Library"/>.</param>
|
||||||
/// <param name="sortBy">A key to sort items by.</param>
|
/// <param name="sortBy">A key to sort items by.</param>
|
||||||
|
@ -18,59 +18,85 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Core.Models.Options;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
using static Kyoo.Abstractions.Models.Utils.Constants;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
{
|
{
|
||||||
[Route("api/item")]
|
/// <summary>
|
||||||
|
/// Endpoint for items that are not part of a specific library.
|
||||||
|
/// An item can ether represent a collection or a show.
|
||||||
|
/// </summary>
|
||||||
[Route("api/items")]
|
[Route("api/items")]
|
||||||
|
[Route("api/item", Order = AlternativeRoute)]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[ResourceView]
|
[ResourceView]
|
||||||
public class LibraryItemApi : ControllerBase
|
[ApiDefinition("Items", Group = ResourcesGroup)]
|
||||||
|
public class LibraryItemApi : BaseApi
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The library item repository used to modify or retrieve information in the data store.
|
||||||
|
/// </summary>
|
||||||
private readonly ILibraryItemRepository _libraryItems;
|
private readonly ILibraryItemRepository _libraryItems;
|
||||||
private readonly Uri _baseURL;
|
|
||||||
|
|
||||||
public LibraryItemApi(ILibraryItemRepository libraryItems, IOptions<BasicOptions> options)
|
/// <summary>
|
||||||
|
/// Create a new <see cref="LibraryItemApi"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="libraryItems">
|
||||||
|
/// The library item repository used to modify or retrieve information in the data store.
|
||||||
|
/// </param>
|
||||||
|
public LibraryItemApi(ILibraryItemRepository libraryItems)
|
||||||
{
|
{
|
||||||
_libraryItems = libraryItems;
|
_libraryItems = libraryItems;
|
||||||
_baseURL = options.Value.PublicUrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get items
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// List all items of kyoo.
|
||||||
|
/// An item can ether represent a collection or a show.
|
||||||
|
/// This endpoint allow one to retrieve all collections and shows that are not contained in a collection.
|
||||||
|
/// This is what is displayed on the /browse page of the webapp.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="sortBy">A key to sort items by.</param>
|
||||||
|
/// <param name="where">An optional list of filters.</param>
|
||||||
|
/// <param name="limit">The number of items to return.</param>
|
||||||
|
/// <param name="afterID">An optional item's ID to start the query from this specific item.</param>
|
||||||
|
/// <returns>A page of items.</returns>
|
||||||
|
/// <response code="400">The filters or the sort parameters are invalid.</response>
|
||||||
|
/// <response code="404">No library with the given ID or slug could be found.</response>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Permission(nameof(LibraryItemApi), Kind.Read)]
|
[Permission(nameof(LibraryItemApi), Kind.Read)]
|
||||||
public async Task<ActionResult<Page<LibraryItem>>> GetAll([FromQuery] string sortBy,
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[FromQuery] int afterID,
|
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<ActionResult<Page<LibraryItem>>> GetAll(
|
||||||
|
[FromQuery] string sortBy,
|
||||||
[FromQuery] Dictionary<string, string> where,
|
[FromQuery] Dictionary<string, string> where,
|
||||||
[FromQuery] int limit = 50)
|
[FromQuery] int limit = 50,
|
||||||
|
[FromQuery] int? afterID = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ICollection<LibraryItem> resources = await _libraryItems.GetAll(
|
ICollection<LibraryItem> resources = await _libraryItems.GetAll(
|
||||||
ApiHelper.ParseWhere<LibraryItem>(where),
|
ApiHelper.ParseWhere<LibraryItem>(where),
|
||||||
new Sort<LibraryItem>(sortBy),
|
new Sort<LibraryItem>(sortBy),
|
||||||
new Pagination(limit, afterID));
|
new Pagination(limit, afterID)
|
||||||
|
);
|
||||||
|
|
||||||
return new Page<LibraryItem>(resources,
|
return Page(resources, limit);
|
||||||
new Uri(_baseURL, Request.Path),
|
|
||||||
Request.Query.ToDictionary(x => x.Key, x => x.Value.ToString(), StringComparer.InvariantCultureIgnoreCase),
|
|
||||||
limit);
|
|
||||||
}
|
|
||||||
catch (ItemNotFoundException)
|
|
||||||
{
|
|
||||||
return NotFound();
|
|
||||||
}
|
}
|
||||||
catch (ArgumentException ex)
|
catch (ArgumentException ex)
|
||||||
{
|
{
|
||||||
return BadRequest(new { Error = ex.Message });
|
return BadRequest(new RequestError(ex.Message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,7 @@ using Kyoo.Abstractions.Controllers;
|
|||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
using Kyoo.Abstractions.Models.Exceptions;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Core.Models.Options;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
{
|
{
|
||||||
@ -39,10 +37,9 @@ namespace Kyoo.Core.Api
|
|||||||
private readonly IThumbnailsManager _thumbs;
|
private readonly IThumbnailsManager _thumbs;
|
||||||
|
|
||||||
public PeopleApi(ILibraryManager libraryManager,
|
public PeopleApi(ILibraryManager libraryManager,
|
||||||
IOptions<BasicOptions> options,
|
|
||||||
IFileSystem files,
|
IFileSystem files,
|
||||||
IThumbnailsManager thumbs)
|
IThumbnailsManager thumbs)
|
||||||
: base(libraryManager.PeopleRepository, options.Value.PublicUrl)
|
: base(libraryManager.PeopleRepository)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_files = files;
|
_files = files;
|
||||||
|
@ -20,9 +20,7 @@ using System.Threading.Tasks;
|
|||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Core.Models.Options;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
{
|
{
|
||||||
@ -37,10 +35,9 @@ namespace Kyoo.Core.Api
|
|||||||
private readonly IFileSystem _files;
|
private readonly IFileSystem _files;
|
||||||
|
|
||||||
public ProviderApi(ILibraryManager libraryManager,
|
public ProviderApi(ILibraryManager libraryManager,
|
||||||
IOptions<BasicOptions> options,
|
|
||||||
IFileSystem files,
|
IFileSystem files,
|
||||||
IThumbnailsManager thumbnails)
|
IThumbnailsManager thumbnails)
|
||||||
: base(libraryManager.ProviderRepository, options.Value.PublicUrl)
|
: base(libraryManager.ProviderRepository)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_files = files;
|
_files = files;
|
||||||
|
@ -25,10 +25,8 @@ using Kyoo.Abstractions.Models;
|
|||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Kyoo.Core.Models.Options;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using static Kyoo.Abstractions.Models.Utils.Constants;
|
using static Kyoo.Abstractions.Models.Utils.Constants;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
@ -56,14 +54,10 @@ namespace Kyoo.Core.Api
|
|||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="files">The file manager used to send images.</param>
|
/// <param name="files">The file manager used to send images.</param>
|
||||||
/// <param name="thumbs">The thumbnail manager used to retrieve images paths.</param>
|
/// <param name="thumbs">The thumbnail manager used to retrieve images paths.</param>
|
||||||
/// <param name="options">
|
|
||||||
/// Options used to retrieve the base URL of Kyoo.
|
|
||||||
/// </param>
|
|
||||||
public SeasonApi(ILibraryManager libraryManager,
|
public SeasonApi(ILibraryManager libraryManager,
|
||||||
IFileSystem files,
|
IFileSystem files,
|
||||||
IThumbnailsManager thumbs,
|
IThumbnailsManager thumbs)
|
||||||
IOptions<BasicOptions> options)
|
: base(libraryManager.SeasonRepository, files, thumbs)
|
||||||
: base(libraryManager.SeasonRepository, files, thumbs, options.Value.PublicUrl)
|
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,12 @@ namespace Kyoo.Core.Api
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly IFileSystem _files;
|
private readonly IFileSystem _files;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The base URL of Kyoo. This will be used to create links for images and
|
||||||
|
/// <see cref="Abstractions.Models.Page{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Uri _baseURL;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="ShowApi"/>.
|
/// Create a new <see cref="ShowApi"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -72,10 +78,11 @@ namespace Kyoo.Core.Api
|
|||||||
IFileSystem files,
|
IFileSystem files,
|
||||||
IThumbnailsManager thumbs,
|
IThumbnailsManager thumbs,
|
||||||
IOptions<BasicOptions> options)
|
IOptions<BasicOptions> options)
|
||||||
: base(libraryManager.ShowRepository, files, thumbs, options.Value.PublicUrl)
|
: base(libraryManager.ShowRepository, files, thumbs)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_files = files;
|
_files = files;
|
||||||
|
_baseURL = options.Value.PublicUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -392,7 +399,7 @@ namespace Kyoo.Core.Api
|
|||||||
return (await _files.ListFiles(path))
|
return (await _files.ListFiles(path))
|
||||||
.ToDictionary(
|
.ToDictionary(
|
||||||
Path.GetFileNameWithoutExtension,
|
Path.GetFileNameWithoutExtension,
|
||||||
x => $"{BaseURL}api/shows/{identifier}/fonts/{Path.GetFileName(x)}"
|
x => $"{_baseURL}api/shows/{identifier}/fonts/{Path.GetFileName(x)}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,9 +23,7 @@ using System.Threading.Tasks;
|
|||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Core.Models.Options;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
{
|
{
|
||||||
@ -37,8 +35,8 @@ namespace Kyoo.Core.Api
|
|||||||
{
|
{
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
|
||||||
public StudioApi(ILibraryManager libraryManager, IOptions<BasicOptions> options)
|
public StudioApi(ILibraryManager libraryManager)
|
||||||
: base(libraryManager.StudioRepository, options.Value.PublicUrl)
|
: base(libraryManager.StudioRepository)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
}
|
}
|
||||||
|
@ -22,22 +22,22 @@ using Kyoo.Abstractions.Models;
|
|||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Kyoo.Core.Models.Options;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using static Kyoo.Abstractions.Models.Utils.Constants;
|
using static Kyoo.Abstractions.Models.Utils.Constants;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Information about one or multiple <see cref="Track"/>.
|
/// Information about one or multiple <see cref="Track"/>.
|
||||||
|
/// A track contain metadata about a video, an audio or a subtitles.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Route("api/tracks")]
|
[Route("api/tracks")]
|
||||||
[Route("api/track", Order = AlternativeRoute)]
|
[Route("api/track", Order = AlternativeRoute)]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
|
[ResourceView]
|
||||||
[PartialPermission(nameof(Track))]
|
[PartialPermission(nameof(Track))]
|
||||||
[ApiDefinition("Tracks", Group = ResourcesGroup)]
|
[ApiDefinition("Tracks", Group = WatchGroup)]
|
||||||
public class TrackApi : CrudApi<Track>
|
public class TrackApi : CrudApi<Track>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -51,11 +51,8 @@ namespace Kyoo.Core.Api
|
|||||||
/// <param name="libraryManager">
|
/// <param name="libraryManager">
|
||||||
/// The library manager used to modify or retrieve information in the data store.
|
/// The library manager used to modify or retrieve information in the data store.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="options">
|
public TrackApi(ILibraryManager libraryManager)
|
||||||
/// Options used to retrieve the base URL of Kyoo.
|
: base(libraryManager.TrackRepository)
|
||||||
/// </param>
|
|
||||||
public TrackApi(ILibraryManager libraryManager, IOptions<BasicOptions> options)
|
|
||||||
: base(libraryManager.TrackRepository, options.Value.PublicUrl)
|
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user