mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-03 06:18:28 +01:00
Update to 3.5.2 and .net core 2.1
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Emby.Server.Implementations.HttpServer;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Emby.Server.Implementations.Services
|
||||
{
|
||||
public class RequestHelper
|
||||
{
|
||||
public static Func<Type, Stream, object> GetRequestReader(HttpListenerHost host, string contentType)
|
||||
public static Func<Type, Stream, Task<object>> GetRequestReader(HttpListenerHost host, string contentType)
|
||||
{
|
||||
switch (GetContentTypeWithoutEncoding(contentType))
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Emby.Server.Implementations.Services
|
||||
{
|
||||
public static class ResponseHelper
|
||||
{
|
||||
public static async Task WriteToResponse(IResponse response, IRequest request, object result, CancellationToken cancellationToken)
|
||||
public static Task WriteToResponse(IResponse response, IRequest request, object result, CancellationToken cancellationToken)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
@@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.Services
|
||||
}
|
||||
|
||||
response.SetContentLength(0);
|
||||
return;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var httpResult = result as IHttpResult;
|
||||
@@ -46,18 +46,6 @@ namespace Emby.Server.Implementations.Services
|
||||
// httpResult.ContentType = defaultContentType;
|
||||
//}
|
||||
//response.ContentType = httpResult.ContentType;
|
||||
|
||||
if (httpResult.Cookies != null)
|
||||
{
|
||||
var httpRes = response as IHttpResponse;
|
||||
if (httpRes != null)
|
||||
{
|
||||
foreach (var cookie in httpResult.Cookies)
|
||||
{
|
||||
httpRes.SetCookie(cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var responseOptions = result as IHasHeaders;
|
||||
@@ -90,32 +78,26 @@ namespace Emby.Server.Implementations.Services
|
||||
var asyncStreamWriter = result as IAsyncStreamWriter;
|
||||
if (asyncStreamWriter != null)
|
||||
{
|
||||
await asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken).ConfigureAwait(false);
|
||||
return;
|
||||
return asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken);
|
||||
}
|
||||
|
||||
var streamWriter = result as IStreamWriter;
|
||||
if (streamWriter != null)
|
||||
{
|
||||
streamWriter.WriteTo(response.OutputStream);
|
||||
return;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var fileWriter = result as FileWriter;
|
||||
if (fileWriter != null)
|
||||
{
|
||||
await fileWriter.WriteToAsync(response, cancellationToken).ConfigureAwait(false);
|
||||
return;
|
||||
return fileWriter.WriteToAsync(response, cancellationToken);
|
||||
}
|
||||
|
||||
var stream = result as Stream;
|
||||
if (stream != null)
|
||||
{
|
||||
using (stream)
|
||||
{
|
||||
await stream.CopyToAsync(response.OutputStream).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
return CopyStream(stream, response.OutputStream);
|
||||
}
|
||||
|
||||
var bytes = result as byte[];
|
||||
@@ -126,9 +108,9 @@ namespace Emby.Server.Implementations.Services
|
||||
|
||||
if (bytes.Length > 0)
|
||||
{
|
||||
await response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false);
|
||||
return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
|
||||
}
|
||||
return;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var responseText = result as string;
|
||||
@@ -138,12 +120,20 @@ namespace Emby.Server.Implementations.Services
|
||||
response.SetContentLength(bytes.Length);
|
||||
if (bytes.Length > 0)
|
||||
{
|
||||
await response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false);
|
||||
return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
|
||||
}
|
||||
return;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
await WriteObject(request, result, response).ConfigureAwait(false);
|
||||
return WriteObject(request, result, response);
|
||||
}
|
||||
|
||||
private static async Task CopyStream(Stream src, Stream dest)
|
||||
{
|
||||
using (src)
|
||||
{
|
||||
await src.CopyToAsync(dest).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task WriteObject(IRequest request, object result, IResponse response)
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.Services
|
||||
// mi.ReturnType
|
||||
// : Type.GetType(requestType.FullName + "Response");
|
||||
|
||||
RegisterRestPaths(appHost, requestType);
|
||||
RegisterRestPaths(appHost, requestType, serviceType);
|
||||
|
||||
appHost.AddServiceInfo(serviceType, requestType);
|
||||
}
|
||||
@@ -68,14 +68,14 @@ namespace Emby.Server.Implementations.Services
|
||||
return null;
|
||||
}
|
||||
|
||||
public readonly Dictionary<string, List<RestPath>> RestPathMap = new Dictionary<string, List<RestPath>>(StringComparer.OrdinalIgnoreCase);
|
||||
public readonly RestPath.RestPathMap RestPathMap = new RestPath.RestPathMap();
|
||||
|
||||
public void RegisterRestPaths(HttpListenerHost appHost, Type requestType)
|
||||
public void RegisterRestPaths(HttpListenerHost appHost, Type requestType, Type serviceType)
|
||||
{
|
||||
var attrs = appHost.GetRouteAttributes(requestType);
|
||||
foreach (RouteAttribute attr in attrs)
|
||||
{
|
||||
var restPath = new RestPath(appHost.CreateInstance, appHost.GetParseFn, requestType, attr.Path, attr.Verbs, attr.IsHidden, attr.Summary, attr.Description);
|
||||
var restPath = new RestPath(appHost.CreateInstance, appHost.GetParseFn, requestType, serviceType, attr.Path, attr.Verbs, attr.IsHidden, attr.Summary, attr.Description);
|
||||
|
||||
RegisterRestPath(restPath);
|
||||
}
|
||||
@@ -114,19 +114,20 @@ namespace Emby.Server.Implementations.Services
|
||||
}
|
||||
|
||||
var bestScore = -1;
|
||||
RestPath bestMatch = null;
|
||||
foreach (var restPath in firstMatches)
|
||||
{
|
||||
var score = restPath.MatchScore(httpMethod, matchUsingPathParts, logger);
|
||||
if (score > bestScore) bestScore = score;
|
||||
if (score > bestScore)
|
||||
{
|
||||
bestScore = score;
|
||||
bestMatch = restPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestScore > 0)
|
||||
if (bestScore > 0 && bestMatch != null)
|
||||
{
|
||||
foreach (var restPath in firstMatches)
|
||||
{
|
||||
if (bestScore == restPath.MatchScore(httpMethod, matchUsingPathParts, logger))
|
||||
return restPath;
|
||||
}
|
||||
return bestMatch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,19 +137,21 @@ namespace Emby.Server.Implementations.Services
|
||||
if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches)) continue;
|
||||
|
||||
var bestScore = -1;
|
||||
RestPath bestMatch = null;
|
||||
foreach (var restPath in firstMatches)
|
||||
{
|
||||
var score = restPath.MatchScore(httpMethod, matchUsingPathParts, logger);
|
||||
if (score > bestScore) bestScore = score;
|
||||
}
|
||||
if (bestScore > 0)
|
||||
{
|
||||
foreach (var restPath in firstMatches)
|
||||
if (score > bestScore)
|
||||
{
|
||||
if (bestScore == restPath.MatchScore(httpMethod, matchUsingPathParts, logger))
|
||||
return restPath;
|
||||
bestScore = score;
|
||||
bestMatch = restPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestScore > 0 && bestMatch != null)
|
||||
{
|
||||
return bestMatch;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Emby.Server.Implementations.Services
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<object> Execute(Type serviceType, IRequest request, object instance, object requestDto, string requestName)
|
||||
public static Task<object> Execute(Type serviceType, IRequest request, object instance, object requestDto, string requestName)
|
||||
{
|
||||
var actionName = request.Verb ?? "POST";
|
||||
|
||||
@@ -82,7 +82,10 @@ namespace Emby.Server.Implementations.Services
|
||||
foreach (var requestFilter in actionContext.RequestFilters)
|
||||
{
|
||||
requestFilter.RequestFilter(request, request.Response, requestDto);
|
||||
if (request.Response.IsClosed) return null;
|
||||
if (request.Response.IsClosed)
|
||||
{
|
||||
Task.FromResult<object>(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,17 +94,56 @@ namespace Emby.Server.Implementations.Services
|
||||
var taskResponse = response as Task;
|
||||
if (taskResponse != null)
|
||||
{
|
||||
await taskResponse.ConfigureAwait(false);
|
||||
response = ServiceHandler.GetTaskResult(taskResponse);
|
||||
return GetTaskResult(taskResponse);
|
||||
}
|
||||
|
||||
return response;
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
|
||||
var expectedMethodName = actionName.Substring(0, 1) + actionName.Substring(1).ToLower();
|
||||
throw new NotImplementedException(string.Format("Could not find method named {1}({0}) or Any({0}) on Service {2}", requestDto.GetType().GetMethodName(), expectedMethodName, serviceType.GetMethodName()));
|
||||
}
|
||||
|
||||
private static async Task<object> GetTaskResult(Task task)
|
||||
{
|
||||
try
|
||||
{
|
||||
var taskObject = task as Task<object>;
|
||||
if (taskObject != null)
|
||||
{
|
||||
return await taskObject.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await task.ConfigureAwait(false);
|
||||
|
||||
var type = task.GetType().GetTypeInfo();
|
||||
if (!type.IsGenericType)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var resultProperty = type.GetDeclaredProperty("Result");
|
||||
if (resultProperty == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = resultProperty.GetValue(task);
|
||||
|
||||
// hack alert
|
||||
if (result.GetType().Name.IndexOf("voidtaskresult", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (TypeAccessException)
|
||||
{
|
||||
return null; //return null for void Task's
|
||||
}
|
||||
}
|
||||
|
||||
public static List<ServiceMethod> Reset(Type serviceType)
|
||||
{
|
||||
var actions = new List<ServiceMethod>();
|
||||
|
||||
@@ -11,55 +11,7 @@ namespace Emby.Server.Implementations.Services
|
||||
{
|
||||
public class ServiceHandler
|
||||
{
|
||||
public async Task<object> HandleResponseAsync(object response)
|
||||
{
|
||||
var taskResponse = response as Task;
|
||||
|
||||
if (taskResponse == null)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
||||
await taskResponse.ConfigureAwait(false);
|
||||
|
||||
var taskResult = GetTaskResult(taskResponse);
|
||||
|
||||
var subTask = taskResult as Task;
|
||||
if (subTask != null)
|
||||
{
|
||||
taskResult = GetTaskResult(subTask);
|
||||
}
|
||||
|
||||
return taskResult;
|
||||
}
|
||||
|
||||
internal static object GetTaskResult(Task task)
|
||||
{
|
||||
try
|
||||
{
|
||||
var taskObject = task as Task<object>;
|
||||
if (taskObject != null)
|
||||
{
|
||||
return taskObject.Result;
|
||||
}
|
||||
|
||||
task.Wait();
|
||||
|
||||
var type = task.GetType().GetTypeInfo();
|
||||
if (!type.IsGenericType)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return type.GetDeclaredProperty("Result").GetValue(task);
|
||||
}
|
||||
catch (TypeAccessException)
|
||||
{
|
||||
return null; //return null for void Task's
|
||||
}
|
||||
}
|
||||
|
||||
protected static object CreateContentTypeRequest(HttpListenerHost host, IRequest httpReq, Type requestType, string contentType)
|
||||
protected static Task<object> CreateContentTypeRequest(HttpListenerHost host, IRequest httpReq, Type requestType, string contentType)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(contentType) && httpReq.ContentLength > 0)
|
||||
{
|
||||
@@ -69,7 +21,7 @@ namespace Emby.Server.Implementations.Services
|
||||
return deserializer(requestType, httpReq.InputStream);
|
||||
}
|
||||
}
|
||||
return host.CreateInstance(requestType);
|
||||
return Task.FromResult(host.CreateInstance(requestType));
|
||||
}
|
||||
|
||||
public static RestPath FindMatchingRestPath(string httpMethod, string pathInfo, ILogger logger, out string contentType)
|
||||
@@ -137,14 +89,11 @@ namespace Emby.Server.Implementations.Services
|
||||
if (ResponseContentType != null)
|
||||
httpReq.ResponseContentType = ResponseContentType;
|
||||
|
||||
var request = httpReq.Dto = CreateRequest(appHost, httpReq, restPath, logger);
|
||||
var request = httpReq.Dto = await CreateRequest(appHost, httpReq, restPath, logger).ConfigureAwait(false);
|
||||
|
||||
appHost.ApplyRequestFilters(httpReq, httpRes, request);
|
||||
|
||||
var rawResponse = await appHost.ServiceController.Execute(appHost, request, httpReq).ConfigureAwait(false);
|
||||
|
||||
//var response = await HandleResponseAsync(rawResponse).ConfigureAwait(false);
|
||||
var response = rawResponse;
|
||||
var response = await appHost.ServiceController.Execute(appHost, request, httpReq).ConfigureAwait(false);
|
||||
|
||||
// Apply response filters
|
||||
foreach (var responseFilter in appHost.ResponseFilters)
|
||||
@@ -155,38 +104,37 @@ namespace Emby.Server.Implementations.Services
|
||||
await ResponseHelper.WriteToResponse(httpRes, httpReq, response, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger)
|
||||
public static async Task<object> CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger)
|
||||
{
|
||||
var requestType = restPath.RequestType;
|
||||
|
||||
if (RequireqRequestStream(requestType))
|
||||
{
|
||||
// Used by IRequiresRequestStream
|
||||
var request = ServiceHandler.CreateRequest(httpReq, restPath, GetRequestParams(httpReq), host.CreateInstance(requestType));
|
||||
var requestParams = await GetRequestParams(httpReq).ConfigureAwait(false);
|
||||
var request = ServiceHandler.CreateRequest(httpReq, restPath, requestParams, host.CreateInstance(requestType));
|
||||
|
||||
var rawReq = (IRequiresRequestStream)request;
|
||||
rawReq.RequestStream = httpReq.InputStream;
|
||||
return rawReq;
|
||||
}
|
||||
else
|
||||
{
|
||||
var requestParams = await GetFlattenedRequestParams(httpReq).ConfigureAwait(false);
|
||||
|
||||
var requestParams = GetFlattenedRequestParams(httpReq);
|
||||
return CreateRequest(host, httpReq, restPath, requestParams);
|
||||
var requestDto = await CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType).ConfigureAwait(false);
|
||||
|
||||
return CreateRequest(httpReq, restPath, requestParams, requestDto);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool RequireqRequestStream(Type requestType)
|
||||
public static bool RequireqRequestStream(Type requestType)
|
||||
{
|
||||
var requiresRequestStreamTypeInfo = typeof(IRequiresRequestStream).GetTypeInfo();
|
||||
|
||||
return requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo());
|
||||
}
|
||||
|
||||
public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams)
|
||||
{
|
||||
var requestDto = CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType);
|
||||
|
||||
return CreateRequest(httpReq, restPath, requestParams, requestDto);
|
||||
}
|
||||
|
||||
public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto)
|
||||
{
|
||||
string contentType;
|
||||
@@ -200,7 +148,7 @@ namespace Emby.Server.Implementations.Services
|
||||
/// <summary>
|
||||
/// Duplicate Params are given a unique key by appending a #1 suffix
|
||||
/// </summary>
|
||||
private static Dictionary<string, string> GetRequestParams(IRequest request)
|
||||
private static async Task<Dictionary<string, string>> GetRequestParams(IRequest request)
|
||||
{
|
||||
var map = new Dictionary<string, string>();
|
||||
|
||||
@@ -224,7 +172,7 @@ namespace Emby.Server.Implementations.Services
|
||||
|
||||
if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
|
||||
{
|
||||
var formData = request.FormData;
|
||||
var formData = await request.GetFormData().ConfigureAwait(false);
|
||||
if (formData != null)
|
||||
{
|
||||
foreach (var name in formData.Keys)
|
||||
@@ -258,7 +206,7 @@ namespace Emby.Server.Implementations.Services
|
||||
/// <summary>
|
||||
/// Duplicate params have their values joined together in a comma-delimited string
|
||||
/// </summary>
|
||||
private static Dictionary<string, string> GetFlattenedRequestParams(IRequest request)
|
||||
private static async Task<Dictionary<string, string>> GetFlattenedRequestParams(IRequest request)
|
||||
{
|
||||
var map = new Dictionary<string, string>();
|
||||
|
||||
@@ -270,7 +218,7 @@ namespace Emby.Server.Implementations.Services
|
||||
|
||||
if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
|
||||
{
|
||||
var formData = request.FormData;
|
||||
var formData = await request.GetFormData().ConfigureAwait(false);
|
||||
if (formData != null)
|
||||
{
|
||||
foreach (var name in formData.Keys)
|
||||
|
||||
@@ -48,6 +48,8 @@ namespace Emby.Server.Implementations.Services
|
||||
|
||||
public Type RequestType { get; private set; }
|
||||
|
||||
public Type ServiceType { get; private set; }
|
||||
|
||||
public string Path { get { return this.restPath; } }
|
||||
|
||||
public string Summary { get; private set; }
|
||||
@@ -56,6 +58,11 @@ namespace Emby.Server.Implementations.Services
|
||||
|
||||
public int Priority { get; set; } //passed back to RouteAttribute
|
||||
|
||||
public IEnumerable<string> PathVariables
|
||||
{
|
||||
get { return this.variablesNames.Where(e => !string.IsNullOrWhiteSpace(e)); }
|
||||
}
|
||||
|
||||
public static string[] GetPathPartsForMatching(string pathInfo)
|
||||
{
|
||||
return pathInfo.ToLower().Split(new[] { PathSeperatorChar }, StringSplitOptions.RemoveEmptyEntries);
|
||||
@@ -93,9 +100,10 @@ namespace Emby.Server.Implementations.Services
|
||||
return list;
|
||||
}
|
||||
|
||||
public RestPath(Func<Type, object> createInstanceFn, Func<Type, Func<string, object>> getParseFn, Type requestType, string path, string verbs, bool isHidden = false, string summary = null, string description = null)
|
||||
public RestPath(Func<Type, object> createInstanceFn, Func<Type, Func<string, object>> getParseFn, Type requestType, Type serviceType, string path, string verbs, bool isHidden = false, string summary = null, string description = null)
|
||||
{
|
||||
this.RequestType = requestType;
|
||||
this.ServiceType = serviceType;
|
||||
this.Summary = summary;
|
||||
this.IsHidden = isHidden;
|
||||
this.Description = description;
|
||||
@@ -558,5 +566,12 @@ namespace Emby.Server.Implementations.Services
|
||||
|
||||
return this.typeDeserializer.PopulateFromMap(fromInstance, requestKeyValuesMap);
|
||||
}
|
||||
|
||||
public class RestPathMap : SortedDictionary<string, List<RestPath>>
|
||||
{
|
||||
public RestPathMap() : base(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,260 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Services;
|
||||
|
||||
namespace Emby.Server.Implementations.Services
|
||||
{
|
||||
[Route("/swagger", "GET", Summary = "Gets the swagger specifications")]
|
||||
[Route("/swagger.json", "GET", Summary = "Gets the swagger specifications")]
|
||||
public class GetSwaggerSpec : IReturn<SwaggerSpec>
|
||||
{
|
||||
}
|
||||
|
||||
public class SwaggerSpec
|
||||
{
|
||||
public string swagger { get; set; }
|
||||
public string[] schemes { get; set; }
|
||||
public SwaggerInfo info { get; set; }
|
||||
public string host { get; set; }
|
||||
public string basePath { get; set; }
|
||||
public SwaggerTag[] tags { get; set; }
|
||||
public IDictionary<string, Dictionary<string, SwaggerMethod>> paths { get; set; }
|
||||
public Dictionary<string, SwaggerDefinition> definitions { get; set; }
|
||||
public SwaggerComponents components { get; set; }
|
||||
}
|
||||
|
||||
public class SwaggerComponents
|
||||
{
|
||||
public Dictionary<string, SwaggerSecurityScheme> securitySchemes { get; set; }
|
||||
}
|
||||
|
||||
public class SwaggerSecurityScheme
|
||||
{
|
||||
public string name { get; set; }
|
||||
public string type { get; set; }
|
||||
public string @in { get; set; }
|
||||
}
|
||||
|
||||
public class SwaggerInfo
|
||||
{
|
||||
public string description { get; set; }
|
||||
public string version { get; set; }
|
||||
public string title { get; set; }
|
||||
public string termsOfService { get; set; }
|
||||
|
||||
public SwaggerConcactInfo contact { get; set; }
|
||||
}
|
||||
|
||||
public class SwaggerConcactInfo
|
||||
{
|
||||
public string email { get; set; }
|
||||
public string name { get; set; }
|
||||
public string url { get; set; }
|
||||
}
|
||||
|
||||
public class SwaggerTag
|
||||
{
|
||||
public string description { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
||||
public class SwaggerMethod
|
||||
{
|
||||
public string summary { get; set; }
|
||||
public string description { get; set; }
|
||||
public string[] tags { get; set; }
|
||||
public string operationId { get; set; }
|
||||
public string[] consumes { get; set; }
|
||||
public string[] produces { get; set; }
|
||||
public SwaggerParam[] parameters { get; set; }
|
||||
public Dictionary<string, SwaggerResponse> responses { get; set; }
|
||||
public Dictionary<string, string[]>[] security { get; set; }
|
||||
}
|
||||
|
||||
public class SwaggerParam
|
||||
{
|
||||
public string @in { get; set; }
|
||||
public string name { get; set; }
|
||||
public string description { get; set; }
|
||||
public bool required { get; set; }
|
||||
public string type { get; set; }
|
||||
public string collectionFormat { get; set; }
|
||||
}
|
||||
|
||||
public class SwaggerResponse
|
||||
{
|
||||
public string description { get; set; }
|
||||
|
||||
// ex. "$ref":"#/definitions/Pet"
|
||||
public Dictionary<string, string> schema { get; set; }
|
||||
}
|
||||
|
||||
public class SwaggerDefinition
|
||||
{
|
||||
public string type { get; set; }
|
||||
public Dictionary<string, SwaggerProperty> properties { get; set; }
|
||||
}
|
||||
|
||||
public class SwaggerProperty
|
||||
{
|
||||
public string type { get; set; }
|
||||
public string format { get; set; }
|
||||
public string description { get; set; }
|
||||
public string[] @enum { get; set; }
|
||||
public string @default { get; set; }
|
||||
}
|
||||
|
||||
public class SwaggerService : IService, IRequiresRequest
|
||||
{
|
||||
private SwaggerSpec _spec;
|
||||
|
||||
public IRequest Request { get; set; }
|
||||
|
||||
public object Get(GetSwaggerSpec request)
|
||||
{
|
||||
return _spec ?? (_spec = GetSpec());
|
||||
}
|
||||
|
||||
private SwaggerSpec GetSpec()
|
||||
{
|
||||
string host = null;
|
||||
Uri uri;
|
||||
if (Uri.TryCreate(Request.RawUrl, UriKind.Absolute, out uri))
|
||||
{
|
||||
host = uri.Host;
|
||||
}
|
||||
|
||||
var securitySchemes = new Dictionary<string, SwaggerSecurityScheme>();
|
||||
|
||||
securitySchemes["api_key"] = new SwaggerSecurityScheme
|
||||
{
|
||||
name = "api_key",
|
||||
type = "apiKey",
|
||||
@in = "query"
|
||||
};
|
||||
|
||||
var spec = new SwaggerSpec
|
||||
{
|
||||
schemes = new[] { "http" },
|
||||
tags = GetTags(),
|
||||
swagger = "2.0",
|
||||
info = new SwaggerInfo
|
||||
{
|
||||
title = "Emby Server API",
|
||||
version = "1.0.0",
|
||||
description = "Explore the Emby Server API",
|
||||
contact = new SwaggerConcactInfo
|
||||
{
|
||||
name = "Emby Developer Community",
|
||||
url = "https://emby.media/community/index.php?/forum/47-developer-api"
|
||||
},
|
||||
termsOfService = "https://emby.media/terms"
|
||||
},
|
||||
paths = GetPaths(),
|
||||
definitions = GetDefinitions(),
|
||||
basePath = "/emby",
|
||||
host = host,
|
||||
|
||||
components = new SwaggerComponents
|
||||
{
|
||||
securitySchemes = securitySchemes
|
||||
}
|
||||
};
|
||||
|
||||
return spec;
|
||||
}
|
||||
|
||||
|
||||
private SwaggerTag[] GetTags()
|
||||
{
|
||||
return new SwaggerTag[] { };
|
||||
}
|
||||
|
||||
private Dictionary<string, SwaggerDefinition> GetDefinitions()
|
||||
{
|
||||
return new Dictionary<string, SwaggerDefinition>();
|
||||
}
|
||||
|
||||
private IDictionary<string, Dictionary<string, SwaggerMethod>> GetPaths()
|
||||
{
|
||||
var paths = new SortedDictionary<string, Dictionary<string, SwaggerMethod>>();
|
||||
|
||||
var all = ServiceController.Instance.RestPathMap.OrderBy(i => i.Key, StringComparer.OrdinalIgnoreCase).ToList();
|
||||
|
||||
foreach (var current in all)
|
||||
{
|
||||
foreach (var info in current.Value)
|
||||
{
|
||||
if (info.IsHidden)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (info.Path.StartsWith("/mediabrowser", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (info.Path.StartsWith("/emby", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
paths[info.Path] = GetPathInfo(info);
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
private Dictionary<string, SwaggerMethod> GetPathInfo(RestPath info)
|
||||
{
|
||||
var result = new Dictionary<string, SwaggerMethod>();
|
||||
|
||||
foreach (var verb in info.Verbs)
|
||||
{
|
||||
var responses = new Dictionary<string, SwaggerResponse>
|
||||
{
|
||||
};
|
||||
|
||||
responses["200"] = new SwaggerResponse
|
||||
{
|
||||
description = "OK"
|
||||
};
|
||||
|
||||
var security = new List<Dictionary<string, string[]>>();
|
||||
|
||||
var apiKeySecurity = new Dictionary<string, string[]>();
|
||||
apiKeySecurity["api_key"] = new string[] { };
|
||||
|
||||
security.Add(apiKeySecurity);
|
||||
|
||||
result[verb.ToLower()] = new SwaggerMethod
|
||||
{
|
||||
summary = info.Summary,
|
||||
description = info.Description,
|
||||
produces = new[]
|
||||
{
|
||||
"application/json"
|
||||
},
|
||||
consumes = new[]
|
||||
{
|
||||
"application/json"
|
||||
},
|
||||
operationId = info.RequestType.Name,
|
||||
tags = new string[] { },
|
||||
|
||||
parameters = new SwaggerParam[] { },
|
||||
|
||||
responses = responses,
|
||||
|
||||
security = security.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.Services
|
||||
return type.IsGenericParameter ? "'" + typeName : typeName;
|
||||
}
|
||||
|
||||
public static string LeftPart(string strVal, string needle)
|
||||
private static string LeftPart(string strVal, string needle)
|
||||
{
|
||||
if (strVal == null) return null;
|
||||
var pos = strVal.IndexOf(needle, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
Reference in New Issue
Block a user