diff options
| author | stefan <stefan@hegedues.at> | 2018-09-12 19:26:21 +0200 |
|---|---|---|
| committer | stefan <stefan@hegedues.at> | 2018-09-12 19:26:21 +0200 |
| commit | 48facb797ed912e4ea6b04b17d1ff190ac2daac4 (patch) | |
| tree | 8dae77a31670a888d733484cb17dd4077d5444e8 /Emby.Server.Implementations/Services | |
| parent | c32d8656382a0eacb301692e0084377fc433ae9b (diff) | |
Update to 3.5.2 and .net core 2.1
Diffstat (limited to 'Emby.Server.Implementations/Services')
8 files changed, 125 insertions, 386 deletions
diff --git a/Emby.Server.Implementations/Services/RequestHelper.cs b/Emby.Server.Implementations/Services/RequestHelper.cs index 7538d3102..711ba8bbc 100644 --- a/Emby.Server.Implementations/Services/RequestHelper.cs +++ b/Emby.Server.Implementations/Services/RequestHelper.cs @@ -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)) { diff --git a/Emby.Server.Implementations/Services/ResponseHelper.cs b/Emby.Server.Implementations/Services/ResponseHelper.cs index 22e1bc4aa..16de1a083 100644 --- a/Emby.Server.Implementations/Services/ResponseHelper.cs +++ b/Emby.Server.Implementations/Services/ResponseHelper.cs @@ -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) diff --git a/Emby.Server.Implementations/Services/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs index 3fd6d88f8..3726c9f6b 100644 --- a/Emby.Server.Implementations/Services/ServiceController.cs +++ b/Emby.Server.Implementations/Services/ServiceController.cs @@ -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; diff --git a/Emby.Server.Implementations/Services/ServiceExec.cs b/Emby.Server.Implementations/Services/ServiceExec.cs index 5709d3e0a..79b57438c 100644 --- a/Emby.Server.Implementations/Services/ServiceExec.cs +++ b/Emby.Server.Implementations/Services/ServiceExec.cs @@ -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>(); diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs index d500595ce..e76857a8d 100644 --- a/Emby.Server.Implementations/Services/ServiceHandler.cs +++ b/Emby.Server.Implementations/Services/ServiceHandler.cs @@ -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) diff --git a/Emby.Server.Implementations/Services/ServicePath.cs b/Emby.Server.Implementations/Services/ServicePath.cs index 0ca36df19..282269e7b 100644 --- a/Emby.Server.Implementations/Services/ServicePath.cs +++ b/Emby.Server.Implementations/Services/ServicePath.cs @@ -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) + { + } + } } }
\ No newline at end of file diff --git a/Emby.Server.Implementations/Services/SwaggerService.cs b/Emby.Server.Implementations/Services/SwaggerService.cs deleted file mode 100644 index fc2bdbd55..000000000 --- a/Emby.Server.Implementations/Services/SwaggerService.cs +++ /dev/null @@ -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; - } - } -} diff --git a/Emby.Server.Implementations/Services/UrlExtensions.cs b/Emby.Server.Implementations/Services/UrlExtensions.cs index c7346789a..ba9889c41 100644 --- a/Emby.Server.Implementations/Services/UrlExtensions.cs +++ b/Emby.Server.Implementations/Services/UrlExtensions.cs @@ -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); |
