From 369785c184a8e19250c6e3b16b3609c222399552 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 8 Mar 2019 20:17:17 +0100 Subject: Remove usage of depricated 'WebRequest' Ref: https://docs.microsoft.com/en-us/dotnet/api/system.net.webrequest?view=netframework-4.7.2 --- .../HttpClientManager/HttpClientInfo.cs | 18 - .../HttpClientManager/HttpClientManager.cs | 432 +++++++-------------- 2 files changed, 150 insertions(+), 300 deletions(-) delete mode 100644 Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs deleted file mode 100644 index f747b01b9..000000000 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Net.Http; - -namespace Emby.Server.Implementations.HttpClientManager -{ - /// - /// Class HttpClientInfo - /// - public class HttpClientInfo - { - /// - /// Gets or sets the last timeout. - /// - /// The last timeout. - public DateTime LastTimeout { get; set; } - public HttpClient HttpClient { get; set; } - } -} diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 1bebdd163..581d6bafd 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Net; -using System.Net.Cache; +using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -55,12 +54,13 @@ namespace Emby.Server.Implementations.HttpClientManager { throw new ArgumentNullException(nameof(appPaths)); } + if (loggerFactory == null) { throw new ArgumentNullException(nameof(loggerFactory)); } - _logger = loggerFactory.CreateLogger("HttpClient"); + _logger = loggerFactory.CreateLogger(nameof(HttpClientManager)); _fileSystem = fileSystem; _appPaths = appPaths; _defaultUserAgentFn = defaultUserAgentFn; @@ -74,27 +74,26 @@ namespace Emby.Server.Implementations.HttpClientManager /// DON'T dispose it after use. /// /// The HTTP clients. - private readonly ConcurrentDictionary _httpClients = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _httpClients = new ConcurrentDictionary(); /// /// Gets /// - /// The host. + /// The host. /// if set to true [enable HTTP compression]. /// HttpClient. /// host - private HttpClientInfo GetHttpClient(string host, bool enableHttpCompression) + private HttpClient GetHttpClient(string url, bool enableHttpCompression) { - if (string.IsNullOrEmpty(host)) - { - throw new ArgumentNullException(nameof(host)); - } - - var key = host + enableHttpCompression; + var key = GetHostFromUrl(url) + enableHttpCompression; if (!_httpClients.TryGetValue(key, out var client)) { - client = new HttpClientInfo(); + + client = new HttpClient() + { + BaseAddress = new Uri(url) + }; _httpClients.TryAdd(key, client); } @@ -102,110 +101,87 @@ namespace Emby.Server.Implementations.HttpClientManager return client; } - private WebRequest GetRequest(HttpRequestOptions options, string method) + private HttpRequestMessage GetRequestMessage(HttpRequestOptions options, HttpMethod method) { string url = options.Url; - var uriAddress = new Uri(url); string userInfo = uriAddress.UserInfo; if (!string.IsNullOrWhiteSpace(userInfo)) { - _logger.LogInformation("Found userInfo in url: {0} ... url: {1}", userInfo, url); + _logger.LogWarning("Found userInfo in url: {0} ... url: {1}", userInfo, url); url = url.Replace(userInfo + "@", string.Empty); } - var request = WebRequest.Create(url); + var request = new HttpRequestMessage(method, url); - if (request is HttpWebRequest httpWebRequest) - { - AddRequestHeaders(httpWebRequest, options); + AddRequestHeaders(request, options); - if (options.EnableHttpCompression) + if (options.EnableHttpCompression) + { + if (options.DecompressionMethod.HasValue + && options.DecompressionMethod.Value == CompressionMethod.Gzip) { - httpWebRequest.AutomaticDecompression = DecompressionMethods.Deflate; - if (options.DecompressionMethod.HasValue - && options.DecompressionMethod.Value == CompressionMethod.Gzip) - { - httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip; - } + request.Headers.Add(HeaderNames.AcceptEncoding, new[] { "gzip", "deflate" }); } else { - httpWebRequest.AutomaticDecompression = DecompressionMethods.None; + request.Headers.Add(HeaderNames.AcceptEncoding, "deflate"); } + } - httpWebRequest.KeepAlive = options.EnableKeepAlive; + if (options.EnableKeepAlive) + { + request.Headers.Add(HeaderNames.Connection, "Keep-Alive"); + } - if (!string.IsNullOrEmpty(options.Host)) - { - httpWebRequest.Host = options.Host; - } + if (!string.IsNullOrEmpty(options.Host)) + { + request.Headers.Add(HeaderNames.Host, options.Host); + } - if (!string.IsNullOrEmpty(options.Referer)) - { - httpWebRequest.Referer = options.Referer; - } + if (!string.IsNullOrEmpty(options.Referer)) + { + request.Headers.Add(HeaderNames.Referer, options.Referer); } - request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache); + //request.Headers.Add(HeaderNames.CacheControl, "no-cache"); - request.Method = method; - request.Timeout = options.TimeoutMs; + //request.Headers.Add(HeaderNames., options.TimeoutMs; + /* if (!string.IsNullOrWhiteSpace(userInfo)) { var parts = userInfo.Split(':'); if (parts.Length == 2) { - request.Credentials = GetCredential(url, parts[0], parts[1]); - // TODO: .net core ?? - request.PreAuthenticate = true; + request.Headers.Add(HeaderNames., GetCredential(url, parts[0], parts[1]); } } + */ return request; } - private static CredentialCache GetCredential(string url, string username, string password) - { - //ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3; - var credentialCache = new CredentialCache(); - credentialCache.Add(new Uri(url), "Basic", new NetworkCredential(username, password)); - return credentialCache; - } - - private void AddRequestHeaders(HttpWebRequest request, HttpRequestOptions options) + private void AddRequestHeaders(HttpRequestMessage request, HttpRequestOptions options) { var hasUserAgent = false; foreach (var header in options.RequestHeaders) { - if (string.Equals(header.Key, HeaderNames.Accept, StringComparison.OrdinalIgnoreCase)) - { - request.Accept = header.Value; - } - else if (string.Equals(header.Key, HeaderNames.UserAgent, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(header.Key, HeaderNames.UserAgent, StringComparison.OrdinalIgnoreCase)) { - SetUserAgent(request, header.Value); hasUserAgent = true; } - else - { - request.Headers.Set(header.Key, header.Value); - } + + request.Headers.Add(header.Key, header.Value); } if (!hasUserAgent && options.EnableDefaultUserAgent) { - SetUserAgent(request, _defaultUserAgentFn()); + request.Headers.Add(HeaderNames.UserAgent, _defaultUserAgentFn()); } } - private static void SetUserAgent(HttpWebRequest request, string userAgent) - { - request.UserAgent = userAgent; - } - /// /// Gets the response internal. /// @@ -213,7 +189,7 @@ namespace Emby.Server.Implementations.HttpClientManager /// Task{HttpResponseInfo}. public Task GetResponse(HttpRequestOptions options) { - return SendAsync(options, "GET"); + return SendAsync(options, HttpMethod.Get); } /// @@ -235,7 +211,21 @@ namespace Emby.Server.Implementations.HttpClientManager /// Task{HttpResponseInfo}. /// /// - public async Task SendAsync(HttpRequestOptions options, string httpMethod) + public Task SendAsync(HttpRequestOptions options, string httpMethod) + { + var httpMethod2 = GetHttpMethod(httpMethod); + return SendAsync(options, httpMethod2); + } + + /// + /// send as an asynchronous operation. + /// + /// The options. + /// The HTTP method. + /// Task{HttpResponseInfo}. + /// + /// + public async Task SendAsync(HttpRequestOptions options, HttpMethod httpMethod) { if (options.CacheMode == CacheMode.None) { @@ -263,6 +253,40 @@ namespace Emby.Server.Implementations.HttpClientManager return response; } + private HttpMethod GetHttpMethod(string httpMethod) + { + if (httpMethod.Equals("DELETE", StringComparison.OrdinalIgnoreCase)) + { + return HttpMethod.Delete; + } + else if (httpMethod.Equals("GET", StringComparison.OrdinalIgnoreCase)) + { + return HttpMethod.Get; + } + else if (httpMethod.Equals("HEAD", StringComparison.OrdinalIgnoreCase)) + { + return HttpMethod.Head; + } + else if (httpMethod.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase)) + { + return HttpMethod.Options; + } + else if (httpMethod.Equals("POST", StringComparison.OrdinalIgnoreCase)) + { + return HttpMethod.Post; + } + else if (httpMethod.Equals("PUT", StringComparison.OrdinalIgnoreCase)) + { + return HttpMethod.Put; + } + else if (httpMethod.Equals("TRACE", StringComparison.OrdinalIgnoreCase)) + { + return HttpMethod.Trace; + } + + throw new ArgumentException("Invalid HTTP method", nameof(httpMethod)); + } + private HttpResponseInfo GetCachedResponse(string responseCachePath, TimeSpan cacheLength, string url) { if (File.Exists(responseCachePath) @@ -294,31 +318,23 @@ namespace Emby.Server.Implementations.HttpClientManager } } - private async Task SendAsyncInternal(HttpRequestOptions options, string httpMethod) + private async Task SendAsyncInternal(HttpRequestOptions options, HttpMethod httpMethod) { ValidateParams(options); options.CancellationToken.ThrowIfCancellationRequested(); - var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression); - - if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < TimeoutSeconds) - { - throw new HttpException(string.Format("Cancelling connection to {0} due to a previous timeout.", options.Url)) - { - IsTimedOut = true - }; - } + var client = GetHttpClient(options.Url, options.EnableHttpCompression); - var httpWebRequest = GetRequest(options, httpMethod); + var httpWebRequest = GetRequestMessage(options, httpMethod); if (options.RequestContentBytes != null || !string.IsNullOrEmpty(options.RequestContent) || - string.Equals(httpMethod, "post", StringComparison.OrdinalIgnoreCase)) + httpMethod == HttpMethod.Post) { try { - var bytes = options.RequestContentBytes ?? Encoding.UTF8.GetBytes(options.RequestContent ?? string.Empty); + httpWebRequest.Content = new StringContent(Encoding.UTF8.GetString(options.RequestContentBytes) ?? options.RequestContent ?? string.Empty); var contentType = options.RequestContentType ?? "application/x-www-form-urlencoded"; @@ -327,8 +343,8 @@ namespace Emby.Server.Implementations.HttpClientManager contentType = contentType.TrimEnd(';') + "; charset=\"utf-8\""; } - httpWebRequest.ContentType = contentType; - (await httpWebRequest.GetRequestStreamAsync().ConfigureAwait(false)).Write(bytes, 0, bytes.Length); + httpWebRequest.Headers.Add(HeaderNames.ContentType, contentType); + await client.SendAsync(httpWebRequest).ConfigureAwait(false); } catch (Exception ex) { @@ -341,68 +357,45 @@ namespace Emby.Server.Implementations.HttpClientManager await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); } - if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < TimeoutSeconds) - { - options.ResourcePool?.Release(); - - throw new HttpException($"Connection to {options.Url} timed out") { IsTimedOut = true }; - } - if (options.LogRequest) { - if (options.LogRequestAsDebug) - { - _logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToUpper(CultureInfo.CurrentCulture), options.Url); - } - else - { - _logger.LogInformation("HttpClientManager {0}: {1}", httpMethod.ToUpper(CultureInfo.CurrentCulture), options.Url); - } + _logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToString(), options.Url); } try { options.CancellationToken.ThrowIfCancellationRequested(); - if (!options.BufferContent) + /*if (!options.BufferContent) { - var response = await GetResponseAsync(httpWebRequest, TimeSpan.FromMilliseconds(options.TimeoutMs)).ConfigureAwait(false); - - var httpResponse = (HttpWebResponse)response; + var response = await client.HttpClient.SendAsync(httpWebRequest).ConfigureAwait(false); - EnsureSuccessStatusCode(client, httpResponse, options); + await EnsureSuccessStatusCode(client, response, options).ConfigureAwait(false); options.CancellationToken.ThrowIfCancellationRequested(); - return GetResponseInfo(httpResponse, httpResponse.GetResponseStream(), GetContentLength(httpResponse), httpResponse); - } + return GetResponseInfo(response, await response.Content.ReadAsStreamAsync().ConfigureAwait(false), response.Content.Headers.ContentLength, response); + }*/ - using (var response = await GetResponseAsync(httpWebRequest, TimeSpan.FromMilliseconds(options.TimeoutMs)).ConfigureAwait(false)) + using (var response = await client.SendAsync(httpWebRequest).ConfigureAwait(false)) { - var httpResponse = (HttpWebResponse)response; - - EnsureSuccessStatusCode(client, httpResponse, options); + await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); options.CancellationToken.ThrowIfCancellationRequested(); - using (var stream = httpResponse.GetResponseStream()) + using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { var memoryStream = new MemoryStream(); await stream.CopyToAsync(memoryStream).ConfigureAwait(false); - memoryStream.Position = 0; - return GetResponseInfo(httpResponse, memoryStream, memoryStream.Length, null); + return GetResponseInfo(response, memoryStream, memoryStream.Length, null); } } } catch (OperationCanceledException ex) { - throw GetCancellationException(options, client, options.CancellationToken, ex); - } - catch (Exception ex) - { - throw GetException(ex, options, client); + throw GetCancellationException(options, options.CancellationToken, ex); } finally { @@ -410,69 +403,54 @@ namespace Emby.Server.Implementations.HttpClientManager } } - private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, Stream content, long? contentLength, IDisposable disposable) + private HttpResponseInfo GetResponseInfo(HttpResponseMessage httpResponse, Stream content, long? contentLength, IDisposable disposable) { var responseInfo = new HttpResponseInfo(disposable) { Content = content, StatusCode = httpResponse.StatusCode, - ContentType = httpResponse.ContentType, + ContentType = httpResponse.Content.Headers.ContentType?.MediaType, ContentLength = contentLength, - ResponseUrl = httpResponse.ResponseUri.ToString() + ResponseUrl = httpResponse.Content.Headers.ContentLocation?.ToString() }; if (httpResponse.Headers != null) { - SetHeaders(httpResponse.Headers, responseInfo); + SetHeaders(httpResponse.Content.Headers, responseInfo); } return responseInfo; } - private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, string tempFile, long? contentLength) + private HttpResponseInfo GetResponseInfo(HttpResponseMessage httpResponse, string tempFile, long? contentLength) { var responseInfo = new HttpResponseInfo { TempFilePath = tempFile, StatusCode = httpResponse.StatusCode, - ContentType = httpResponse.ContentType, + ContentType = httpResponse.Content.Headers.ContentType?.MediaType, ContentLength = contentLength }; if (httpResponse.Headers != null) { - SetHeaders(httpResponse.Headers, responseInfo); + SetHeaders(httpResponse.Content.Headers, responseInfo); } return responseInfo; } - private static void SetHeaders(WebHeaderCollection headers, HttpResponseInfo responseInfo) + private static void SetHeaders(HttpContentHeaders headers, HttpResponseInfo responseInfo) { - foreach (var key in headers.AllKeys) + foreach (var key in headers) { - responseInfo.Headers[key] = headers[key]; + responseInfo.Headers[key.Key] = string.Join(", ", key.Value); } } public Task Post(HttpRequestOptions options) { - return SendAsync(options, "POST"); - } - - /// - /// Performs a POST request - /// - /// The options. - /// Params to add to the POST data. - /// stream on success, null on failure - public async Task Post(HttpRequestOptions options, Dictionary postData) - { - options.SetPostData(postData); - - var response = await Post(options).ConfigureAwait(false); - - return response.Content; + return SendAsync(options, HttpMethod.Post); } /// @@ -482,9 +460,10 @@ namespace Emby.Server.Implementations.HttpClientManager /// Task{System.String}. public async Task GetTempFile(HttpRequestOptions options) { - var response = await GetTempFileResponse(options).ConfigureAwait(false); - - return response.TempFilePath; + using (var response = await GetTempFileResponse(options).ConfigureAwait(false)) + { + return response.TempFilePath; + } } public async Task GetTempFileResponse(HttpRequestOptions options) @@ -502,7 +481,7 @@ namespace Emby.Server.Implementations.HttpClientManager options.CancellationToken.ThrowIfCancellationRequested(); - var httpWebRequest = GetRequest(options, "GET"); + var httpWebRequest = GetRequestMessage(options, HttpMethod.Get); if (options.ResourcePool != null) { @@ -513,33 +492,22 @@ namespace Emby.Server.Implementations.HttpClientManager if (options.LogRequest) { - if (options.LogRequestAsDebug) - { - _logger.LogDebug("HttpClientManager.GetTempFileResponse url: {0}", options.Url); - } - else - { - _logger.LogInformation("HttpClientManager.GetTempFileResponse url: {0}", options.Url); - } + _logger.LogDebug("HttpClientManager.GetTempFileResponse url: {0}", options.Url); } - var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression); + var client = GetHttpClient(options.Url, options.EnableHttpCompression); try { options.CancellationToken.ThrowIfCancellationRequested(); - using (var response = await httpWebRequest.GetResponseAsync().ConfigureAwait(false)) + using (var response = (await client.SendAsync(httpWebRequest).ConfigureAwait(false))) { - var httpResponse = (HttpWebResponse)response; - - EnsureSuccessStatusCode(client, httpResponse, options); + await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); options.CancellationToken.ThrowIfCancellationRequested(); - var contentLength = GetContentLength(httpResponse); - - using (var stream = httpResponse.GetResponseStream()) + using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); @@ -547,13 +515,18 @@ namespace Emby.Server.Implementations.HttpClientManager options.Progress.Report(100); - return GetResponseInfo(httpResponse, tempFile, contentLength); + var contentLength = response.Content.Headers.ContentLength; + return GetResponseInfo(response, tempFile, contentLength); } } catch (Exception ex) { - DeleteTempFile(tempFile); - throw GetException(ex, options, client); + if (File.Exists(tempFile)) + { + File.Delete(tempFile); + } + + throw GetException(ex, options); } finally { @@ -561,21 +534,7 @@ namespace Emby.Server.Implementations.HttpClientManager } } - private static long? GetContentLength(HttpWebResponse response) - { - var length = response.ContentLength; - - if (length == 0) - { - return null; - } - - return length; - } - - protected static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - private Exception GetException(Exception ex, HttpRequestOptions options, HttpClientInfo client) + private Exception GetException(Exception ex, HttpRequestOptions options) { if (ex is HttpException) { @@ -599,11 +558,6 @@ namespace Emby.Server.Implementations.HttpClientManager if (response != null) { exception.StatusCode = response.StatusCode; - - if ((int)response.StatusCode == 429) - { - client.LastTimeout = DateTime.UtcNow; - } } } @@ -624,7 +578,7 @@ namespace Emby.Server.Implementations.HttpClientManager if (operationCanceledException != null) { - return GetCancellationException(options, client, options.CancellationToken, operationCanceledException); + return GetCancellationException(options, options.CancellationToken, operationCanceledException); } if (options.LogErrors) @@ -635,18 +589,6 @@ namespace Emby.Server.Implementations.HttpClientManager return ex; } - private void DeleteTempFile(string file) - { - try - { - _fileSystem.DeleteFile(file); - } - catch (IOException) - { - // Might not have been created at all. No need to worry. - } - } - private void ValidateParams(HttpRequestOptions options) { if (string.IsNullOrEmpty(options.Url)) @@ -682,11 +624,10 @@ namespace Emby.Server.Implementations.HttpClientManager /// Throws the cancellation exception. /// /// The options. - /// The client. /// The cancellation token. /// The exception. /// Exception. - private Exception GetCancellationException(HttpRequestOptions options, HttpClientInfo client, CancellationToken cancellationToken, OperationCanceledException exception) + private Exception GetCancellationException(HttpRequestOptions options, CancellationToken cancellationToken, OperationCanceledException exception) { // If the HttpClient's timeout is reached, it will cancel the Task internally if (!cancellationToken.IsCancellationRequested) @@ -698,8 +639,6 @@ namespace Emby.Server.Implementations.HttpClientManager _logger.LogError(msg); } - client.LastTimeout = DateTime.UtcNow; - // Throw an HttpException so that the caller doesn't think it was cancelled by user code return new HttpException(msg, exception) { @@ -710,91 +649,20 @@ namespace Emby.Server.Implementations.HttpClientManager return exception; } - private void EnsureSuccessStatusCode(HttpClientInfo client, HttpWebResponse response, HttpRequestOptions options) + private async Task EnsureSuccessStatusCode(HttpResponseMessage response, HttpRequestOptions options) { - var statusCode = response.StatusCode; - - var isSuccessful = statusCode >= HttpStatusCode.OK && statusCode <= (HttpStatusCode)299; - - if (isSuccessful) + if (response.IsSuccessStatusCode) { return; } - if (options.LogErrorResponseBody) - { - try - { - using (var stream = response.GetResponseStream()) - { - if (stream != null) - { - using (var reader = new StreamReader(stream)) - { - var msg = reader.ReadToEnd(); - - _logger.LogError(msg); - } - } - } - } - catch - { + var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + _logger.LogError(msg); - } - } - - throw new HttpException(response.StatusDescription) + throw new HttpException(response.ReasonPhrase) { StatusCode = response.StatusCode }; } - - private static Task GetResponseAsync(WebRequest request, TimeSpan timeout) - { - var taskCompletion = new TaskCompletionSource(); - - var asyncTask = Task.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null); - - ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, TimeoutCallback, request, timeout, true); - var callback = new TaskCallback { taskCompletion = taskCompletion }; - asyncTask.ContinueWith(callback.OnSuccess, TaskContinuationOptions.NotOnFaulted); - - // Handle errors - asyncTask.ContinueWith(callback.OnError, TaskContinuationOptions.OnlyOnFaulted); - - return taskCompletion.Task; - } - - private static void TimeoutCallback(object state, bool timedOut) - { - if (timedOut && state != null) - { - var request = (WebRequest)state; - request.Abort(); - } - } - - private class TaskCallback - { - public TaskCompletionSource taskCompletion; - - public void OnSuccess(Task task) - { - taskCompletion.TrySetResult(task.Result); - } - - public void OnError(Task task) - { - if (task.Exception == null) - { - taskCompletion.TrySetException(Enumerable.Empty()); - } - else - { - taskCompletion.TrySetException(task.Exception); - } - } - } } } -- cgit v1.2.3 From 7f42dcc60fd3aaf30f2408f6bddeb2b8bf921cdf Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 8 Mar 2019 20:32:14 +0100 Subject: Remove more unused stuff --- Emby.Server.Implementations/ApplicationHost.cs | 11 +++++------ .../HttpClientManager/HttpClientManager.cs | 18 ------------------ .../LiveTv/Listings/SchedulesDirect.cs | 10 +--------- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 2 -- .../LiveTv/TunerHosts/SharedHttpStream.cs | 6 ------ MediaBrowser.Common/Net/HttpRequestOptions.cs | 21 +-------------------- 6 files changed, 7 insertions(+), 61 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index f7d9bad1b..2f396f814 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; @@ -34,7 +35,6 @@ using Emby.Server.Implementations.IO; using Emby.Server.Implementations.Library; using Emby.Server.Implementations.LiveTv; using Emby.Server.Implementations.Localization; -using Emby.Server.Implementations.Middleware; using Emby.Server.Implementations.Net; using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.Reflection; @@ -1479,12 +1479,12 @@ namespace Emby.Server.Implementations LogErrorResponseBody = false, LogErrors = false, LogRequest = false, - TimeoutMs = 10000, BufferContent = false, CancellationToken = cancellationToken })) { - return GetLocalApiUrl(response.ReadToEnd().Trim()); + string res = await response.ReadToEndAsync().ConfigureAwait(false); + return GetLocalApiUrl(res.Trim()); } } catch (Exception ex) @@ -1604,16 +1604,15 @@ namespace Emby.Server.Implementations LogErrorResponseBody = false, LogErrors = logPing, LogRequest = logPing, - TimeoutMs = 5000, BufferContent = false, CancellationToken = cancellationToken - }, "POST").ConfigureAwait(false)) + }, HttpMethod.Post).ConfigureAwait(false)) { using (var reader = new StreamReader(response.Content)) { - var result = reader.ReadToEnd(); + var result = await reader.ReadToEndAsync().ConfigureAwait(false); var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase); _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid); diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 581d6bafd..b82d55d0e 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -352,11 +352,6 @@ namespace Emby.Server.Implementations.HttpClientManager } } - if (options.ResourcePool != null) - { - await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); - } - if (options.LogRequest) { _logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToString(), options.Url); @@ -397,10 +392,6 @@ namespace Emby.Server.Implementations.HttpClientManager { throw GetCancellationException(options, options.CancellationToken, ex); } - finally - { - options.ResourcePool?.Release(); - } } private HttpResponseInfo GetResponseInfo(HttpResponseMessage httpResponse, Stream content, long? contentLength, IDisposable disposable) @@ -483,11 +474,6 @@ namespace Emby.Server.Implementations.HttpClientManager var httpWebRequest = GetRequestMessage(options, HttpMethod.Get); - if (options.ResourcePool != null) - { - await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); - } - options.Progress.Report(0); if (options.LogRequest) @@ -528,10 +514,6 @@ namespace Emby.Server.Implementations.HttpClientManager throw GetException(ex, options); } - finally - { - options.ResourcePool?.Release(); - } } private Exception GetException(Exception ex, HttpRequestOptions options) diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 4137760d0..f3f747718 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -96,8 +96,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings Url = ApiUrl + "/schedules", UserAgent = UserAgent, CancellationToken = cancellationToken, - // The data can be large so give it some extra time - TimeoutMs = 60000, LogErrorResponseBody = true, RequestContent = requestString }; @@ -115,9 +113,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings Url = ApiUrl + "/programs", UserAgent = UserAgent, CancellationToken = cancellationToken, - LogErrorResponseBody = true, - // The data can be large so give it some extra time - TimeoutMs = 60000 + LogErrorResponseBody = true }; httpOptions.RequestHeaders["token"] = token; @@ -483,8 +479,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings CancellationToken = cancellationToken, RequestContent = imageIdString, LogErrorResponseBody = true, - // The data can be large so give it some extra time - TimeoutMs = 60000 }; try @@ -871,8 +865,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings UserAgent = UserAgent, CancellationToken = cancellationToken, LogErrorResponseBody = true, - // The data can be large so give it some extra time - TimeoutMs = 60000 }; httpOptions.RequestHeaders["token"] = token; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 24b100edd..761275f8f 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -138,7 +138,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { Url = string.Format("{0}/discover.json", GetApiUrl(info)), CancellationToken = cancellationToken, - TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(10).TotalMilliseconds), BufferContent = false }, "GET").ConfigureAwait(false)) @@ -191,7 +190,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { Url = string.Format("{0}/tuners.html", GetApiUrl(info)), CancellationToken = cancellationToken, - TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds), BufferContent = false })) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index d74cf3be2..e8b34da0c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -47,13 +47,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts CancellationToken = CancellationToken.None, BufferContent = false, - // Increase a little bit - TimeoutMs = 30000, - EnableHttpCompression = false, - - LogResponse = true, - LogResponseHeaders = true }; foreach (var header in mediaSource.RequiredHttpHeaders) diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs index 874383fed..38e0ff0f5 100644 --- a/MediaBrowser.Common/Net/HttpRequestOptions.cs +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -28,18 +28,13 @@ namespace MediaBrowser.Common.Net get => GetHeaderValue(HeaderNames.Accept); set => RequestHeaders[HeaderNames.Accept] = value; } + /// /// Gets or sets the cancellation token. /// /// The cancellation token. public CancellationToken CancellationToken { get; set; } - /// - /// Gets or sets the resource pool. - /// - /// The resource pool. - public SemaphoreSlim ResourcePool { get; set; } - /// /// Gets or sets the user agent. /// @@ -86,8 +81,6 @@ namespace MediaBrowser.Common.Net public bool LogRequest { get; set; } public bool LogRequestAsDebug { get; set; } public bool LogErrors { get; set; } - public bool LogResponse { get; set; } - public bool LogResponseHeaders { get; set; } public bool LogErrorResponseBody { get; set; } public bool EnableKeepAlive { get; set; } @@ -95,7 +88,6 @@ namespace MediaBrowser.Common.Net public CacheMode CacheMode { get; set; } public TimeSpan CacheLength { get; set; } - public int TimeoutMs { get; set; } public bool EnableDefaultUserAgent { get; set; } public bool AppendCharsetToMimeType { get; set; } @@ -119,17 +111,6 @@ namespace MediaBrowser.Common.Net LogRequest = true; LogErrors = true; CacheMode = CacheMode.None; - - TimeoutMs = 20000; - } - - public void SetPostData(IDictionary values) - { - var strings = values.Keys.Select(key => string.Format("{0}={1}", key, values[key])); - var postContent = string.Join("&", strings.ToArray()); - - RequestContent = postContent; - RequestContentType = "application/x-www-form-urlencoded"; } } -- cgit v1.2.3 From 6566c9136057f7b272b0d35501ed85034034d11e Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 25 Mar 2019 21:27:03 +0100 Subject: Seperate changes from #1023 The unrelated changes from #1023 (and more) --- .../Activity/ActivityLogEntryPoint.cs | 33 ++++++--- Emby.Server.Implementations/ApplicationHost.cs | 5 +- .../Channels/ChannelManager.cs | 9 --- Emby.Server.Implementations/Dto/DtoService.cs | 28 +++----- .../Security/AuthenticationRepository.cs | 4 -- .../Services/StringMapTypeDeserializer.cs | 6 +- .../Session/SessionManager.cs | 78 +++++++--------------- MediaBrowser.Controller/Entities/UserView.cs | 5 +- .../Entities/UserViewBuilder.cs | 12 ++-- 9 files changed, 72 insertions(+), 108 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index 98cd97c31..f32ad7196 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -3,12 +3,10 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; using MediaBrowser.Controller; using MediaBrowser.Controller.Authentication; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -29,31 +27,39 @@ namespace Emby.Server.Implementations.Activity { public class ActivityLogEntryPoint : IServerEntryPoint { + private readonly ILogger _logger; private readonly IInstallationManager _installationManager; private readonly ISessionManager _sessionManager; private readonly ITaskManager _taskManager; private readonly IActivityManager _activityManager; private readonly ILocalizationManager _localization; - private readonly ILibraryManager _libraryManager; private readonly ISubtitleManager _subManager; private readonly IUserManager _userManager; - private readonly IServerConfigurationManager _config; private readonly IServerApplicationHost _appHost; private readonly IDeviceManager _deviceManager; - public ActivityLogEntryPoint(ISessionManager sessionManager, IDeviceManager deviceManager, ITaskManager taskManager, IActivityManager activityManager, ILocalizationManager localization, IInstallationManager installationManager, ILibraryManager libraryManager, ISubtitleManager subManager, IUserManager userManager, IServerConfigurationManager config, IServerApplicationHost appHost) + public ActivityLogEntryPoint( + ILogger logger, + ISessionManager sessionManager, + IDeviceManager deviceManager, + ITaskManager taskManager, + IActivityManager activityManager, + ILocalizationManager localization, + IInstallationManager installationManager, + ISubtitleManager subManager, + IUserManager userManager, + IServerApplicationHost appHost) { + _logger = logger; _sessionManager = sessionManager; + _deviceManager = deviceManager; _taskManager = taskManager; _activityManager = activityManager; _localization = localization; _installationManager = installationManager; - _libraryManager = libraryManager; _subManager = subManager; _userManager = userManager; - _config = config; _appHost = appHost; - _deviceManager = deviceManager; } public Task RunAsync() @@ -124,7 +130,7 @@ namespace Emby.Server.Implementations.Activity if (item == null) { - //_logger.LogWarning("PlaybackStopped reported with null media info."); + _logger.LogWarning("PlaybackStopped reported with null media info."); return; } @@ -155,7 +161,7 @@ namespace Emby.Server.Implementations.Activity if (item == null) { - //_logger.LogWarning("PlaybackStart reported with null media info."); + _logger.LogWarning("PlaybackStart reported with null media info."); return; } @@ -203,6 +209,7 @@ namespace Emby.Server.Implementations.Activity { return NotificationType.AudioPlayback.ToString(); } + if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) { return NotificationType.VideoPlayback.ToString(); @@ -217,6 +224,7 @@ namespace Emby.Server.Implementations.Activity { return NotificationType.AudioPlaybackStopped.ToString(); } + if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) { return NotificationType.VideoPlaybackStopped.ToString(); @@ -415,6 +423,7 @@ namespace Emby.Server.Implementations.Activity { vals.Add(e.Result.ErrorMessage); } + if (!string.IsNullOrEmpty(e.Result.LongErrorMessage)) { vals.Add(e.Result.LongErrorMessage); @@ -424,7 +433,7 @@ namespace Emby.Server.Implementations.Activity { Name = string.Format(_localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name), Type = NotificationType.TaskFailed.ToString(), - Overview = string.Join(Environment.NewLine, vals.ToArray()), + Overview = string.Join(Environment.NewLine, vals), ShortOverview = runningTime, Severity = LogLevel.Error }); @@ -503,6 +512,7 @@ namespace Emby.Server.Implementations.Activity { values.Add(CreateValueString(span.Hours, "hour")); } + // Number of minutes if (span.Minutes >= 1) { @@ -526,6 +536,7 @@ namespace Emby.Server.Implementations.Activity builder.Append(values[i]); } + // Return result return builder.ToString(); } diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 484942946..ff49c74a3 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -831,10 +831,10 @@ namespace Emby.Server.Implementations DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager); serviceCollection.AddSingleton(DtoService); - ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient, ProviderManager); + ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, ProviderManager); serviceCollection.AddSingleton(ChannelManager); - SessionManager = new SessionManager(UserDataManager, LoggerFactory, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager); + SessionManager = new SessionManager(UserDataManager, LoggerFactory, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, this, AuthenticationRepository, DeviceManager, MediaSourceManager); serviceCollection.AddSingleton(SessionManager); serviceCollection.AddSingleton( @@ -1035,7 +1035,6 @@ namespace Emby.Server.Implementations Video.LiveTvManager = LiveTvManager; Folder.UserViewManager = UserViewManager; UserView.TVSeriesManager = TVSeriesManager; - UserView.PlaylistManager = PlaylistManager; UserView.CollectionManager = CollectionManager; BaseItem.MediaSourceManager = MediaSourceManager; CollectionFolder.XmlSerializer = XmlSerializer; diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 7e50650d7..e9961e8bd 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; @@ -20,7 +19,6 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Channels; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Serialization; @@ -40,11 +38,8 @@ namespace Emby.Server.Implementations.Channels private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; private readonly IProviderManager _providerManager; - private readonly ILocalizationManager _localization; - public ChannelManager( IUserManager userManager, IDtoService dtoService, @@ -54,8 +49,6 @@ namespace Emby.Server.Implementations.Channels IFileSystem fileSystem, IUserDataManager userDataManager, IJsonSerializer jsonSerializer, - ILocalizationManager localization, - IHttpClient httpClient, IProviderManager providerManager) { _userManager = userManager; @@ -66,8 +59,6 @@ namespace Emby.Server.Implementations.Channels _fileSystem = fileSystem; _userDataManager = userDataManager; _jsonSerializer = jsonSerializer; - _localization = localization; - _httpClient = httpClient; _providerManager = providerManager; } diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 7b28a22a8..2f1b60be9 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -89,14 +89,11 @@ namespace Emby.Server.Implementations.Dto var channelTuples = new List>(); var index = 0; - var allCollectionFolders = _libraryManager.GetUserRootFolder().Children.OfType().ToList(); - foreach (var item in items) { - var dto = GetBaseItemDtoInternal(item, options, allCollectionFolders, user, owner); + var dto = GetBaseItemDtoInternal(item, options, user, owner); - var tvChannel = item as LiveTvChannel; - if (tvChannel != null) + if (item is LiveTvChannel tvChannel) { channelTuples.Add(new Tuple(dto, tvChannel)); } @@ -105,9 +102,7 @@ namespace Emby.Server.Implementations.Dto programTuples.Add(new Tuple(item, dto)); } - var byName = item as IItemByName; - - if (byName != null) + if (item is IItemByName byName) { if (options.ContainsField(ItemFields.ItemCounts)) { @@ -130,8 +125,7 @@ namespace Emby.Server.Implementations.Dto if (programTuples.Count > 0) { - var task = _livetvManager().AddInfoToProgramDto(programTuples, options.Fields, user); - Task.WaitAll(task); + _livetvManager().AddInfoToProgramDto(programTuples, options.Fields, user).GetAwaiter().GetResult(); } if (channelTuples.Count > 0) @@ -144,8 +138,7 @@ namespace Emby.Server.Implementations.Dto public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null) { - var allCollectionFolders = _libraryManager.GetUserRootFolder().Children.OfType().ToList(); - var dto = GetBaseItemDtoInternal(item, options, allCollectionFolders, user, owner); + var dto = GetBaseItemDtoInternal(item, options, user, owner); var tvChannel = item as LiveTvChannel; if (tvChannel != null) { @@ -188,7 +181,7 @@ namespace Emby.Server.Implementations.Dto }); } - private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, List allCollectionFolders, User user = null, BaseItem owner = null) + private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null) { var dto = new BaseItemDto { @@ -312,6 +305,7 @@ namespace Emby.Server.Implementations.Dto { path = path.TrimStart('.'); } + if (!string.IsNullOrEmpty(path) && containers.Contains(path, StringComparer.OrdinalIgnoreCase)) { fileExtensionContainer = path; @@ -325,8 +319,7 @@ namespace Emby.Server.Implementations.Dto public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List taggedItems, User user = null) { - var allCollectionFolders = _libraryManager.GetUserRootFolder().Children.OfType().ToList(); - var dto = GetBaseItemDtoInternal(item, options, allCollectionFolders, user); + var dto = GetBaseItemDtoInternal(item, options, user); if (taggedItems != null && options.ContainsField(ItemFields.ItemCounts)) { @@ -1051,14 +1044,15 @@ namespace Emby.Server.Implementations.Dto } else { - mediaStreams = dto.MediaSources.Where(i => string.Equals(i.Id, item.Id.ToString("N"), StringComparison.OrdinalIgnoreCase)) + string id = item.Id.ToString("N"); + mediaStreams = dto.MediaSources.Where(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)) .SelectMany(i => i.MediaStreams) .ToArray(); } } else { - mediaStreams = _mediaSourceManager().GetStaticMediaSources(item, true).First().MediaStreams.ToArray(); + mediaStreams = _mediaSourceManager().GetStaticMediaSources(item, true)[0].MediaStreams.ToArray(); } dto.MediaStreams = mediaStreams; diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs index c81a93767..29b8dfd3d 100644 --- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs +++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs @@ -15,13 +15,9 @@ namespace Emby.Server.Implementations.Security { public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository { - private readonly IServerConfigurationManager _config; - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - public AuthenticationRepository(ILoggerFactory loggerFactory, IServerConfigurationManager config) : base(loggerFactory.CreateLogger(nameof(AuthenticationRepository))) { - _config = config; DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "authentication.db"); } diff --git a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs index f835aa1b5..7d42b2b51 100644 --- a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs +++ b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs @@ -48,7 +48,7 @@ namespace Emby.Server.Implementations.Services foreach (var propertyInfo in RestPath.GetSerializableProperties(type)) { - var propertySetFn = TypeAccessor.GetSetPropertyMethod(type, propertyInfo); + var propertySetFn = TypeAccessor.GetSetPropertyMethod(propertyInfo); var propertyType = propertyInfo.PropertyType; var propertyParseStringFn = GetParseFn(propertyType); var propertySerializer = new PropertySerializerEntry(propertySetFn, propertyParseStringFn, propertyType); @@ -110,9 +110,9 @@ namespace Emby.Server.Implementations.Services } } - internal class TypeAccessor + internal static class TypeAccessor { - public static Action GetSetPropertyMethod(Type type, PropertyInfo propertyInfo) + public static Action GetSetPropertyMethod(PropertyInfo propertyInfo) { if (!propertyInfo.CanWrite || propertyInfo.GetIndexParameters().Length > 0) { diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 985748caf..dc23551db 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Devices; @@ -25,7 +24,6 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; using MediaBrowser.Model.Library; using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; @@ -53,8 +51,6 @@ namespace Emby.Server.Implementations.Session private readonly IImageProcessor _imageProcessor; private readonly IMediaSourceManager _mediaSourceManager; - private readonly IHttpClient _httpClient; - private readonly IJsonSerializer _jsonSerializer; private readonly IServerApplicationHost _appHost; private readonly IAuthenticationRepository _authRepo; @@ -96,9 +92,7 @@ namespace Emby.Server.Implementations.Session IMusicManager musicManager, IDtoService dtoService, IImageProcessor imageProcessor, - IJsonSerializer jsonSerializer, IServerApplicationHost appHost, - IHttpClient httpClient, IAuthenticationRepository authRepo, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager) @@ -110,9 +104,7 @@ namespace Emby.Server.Implementations.Session _musicManager = musicManager; _dtoService = dtoService; _imageProcessor = imageProcessor; - _jsonSerializer = jsonSerializer; _appHost = appHost; - _httpClient = httpClient; _authRepo = authRepo; _deviceManager = deviceManager; _mediaSourceManager = mediaSourceManager; @@ -347,8 +339,7 @@ namespace Emby.Server.Implementations.Session var runtimeTicks = libraryItem.RunTimeTicks; MediaSourceInfo mediaSource = null; - var hasMediaSources = libraryItem as IHasMediaSources; - if (hasMediaSources != null) + if (libraryItem is IHasMediaSources hasMediaSources) { mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); @@ -1841,64 +1832,49 @@ namespace Emby.Server.Implementations.Session var data = dataFn(); - var tasks = sessions.Select(session => Task.Run(async () => + IEnumerable GetTasks() { - try - { - await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) + foreach (var session in sessions) { - _logger.LogError("Error sending message", ex); + yield return SendMessageToSession(session, name, data, cancellationToken); } + } - }, cancellationToken)).ToArray(); - - return Task.WhenAll(tasks); + return Task.WhenAll(GetTasks()); } public Task SendMessageToUserSessions(List userIds, string name, T data, CancellationToken cancellationToken) { CheckDisposed(); - var sessions = Sessions.Where(i => userIds.Any(i.ContainsUser)).ToList(); + var sessions = Sessions.Where(i => userIds.Any(i.ContainsUser)); - var tasks = sessions.Select(session => Task.Run(async () => + IEnumerable GetTasks() { - try - { - await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) + foreach (var session in sessions) { - _logger.LogError("Error sending message", ex); + yield return SendMessageToSession(session, name, data, cancellationToken); } + } - }, cancellationToken)).ToArray(); - - return Task.WhenAll(tasks); + return Task.WhenAll(GetTasks()); } public Task SendMessageToUserDeviceSessions(string deviceId, string name, T data, CancellationToken cancellationToken) { CheckDisposed(); - var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)).ToList(); + var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)); - var tasks = sessions.Select(session => Task.Run(async () => + IEnumerable GetTasks() { - try - { - await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) + foreach (var session in sessions) { - _logger.LogError("Error sending message", ex); + yield return SendMessageToSession(session, name, data, cancellationToken); } + } - }, cancellationToken)).ToArray(); - - return Task.WhenAll(tasks); + return Task.WhenAll(GetTasks()); } public Task SendMessageToUserDeviceAndAdminSessions(string deviceId, string name, T data, CancellationToken cancellationToken) @@ -1906,23 +1882,17 @@ namespace Emby.Server.Implementations.Session CheckDisposed(); var sessions = Sessions - .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i)) - .ToList(); + .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i)); - var tasks = sessions.Select(session => Task.Run(async () => + IEnumerable GetTasks() { - try - { - await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) + foreach (var session in sessions) { - _logger.LogError("Error sending message", ex); + yield return SendMessageToSession(session, name, data, cancellationToken); } + } - }, cancellationToken)).ToArray(); - - return Task.WhenAll(tasks); + return Task.WhenAll(GetTasks()); } private bool IsAdminSession(SessionInfo s) diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 3e2191376..4a6d32dce 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.TV; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Serialization; @@ -17,7 +16,6 @@ namespace MediaBrowser.Controller.Entities public Guid? UserId { get; set; } public static ITVSeriesManager TVSeriesManager; - public static IPlaylistManager PlaylistManager; [IgnoreDataMember] public string CollectionType => ViewType; @@ -38,6 +36,7 @@ namespace MediaBrowser.Controller.Entities { list.Add(Id); } + return list; } @@ -65,7 +64,7 @@ namespace MediaBrowser.Controller.Entities parent = LibraryManager.GetItemById(ParentId) as Folder ?? parent; } - return new UserViewBuilder(UserViewManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, ConfigurationManager, PlaylistManager) + return new UserViewBuilder(UserViewManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, ConfigurationManager) .GetUserItems(parent, this, CollectionType, query); } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 683218a9e..e483c8f34 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -5,7 +5,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.TV; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; @@ -21,9 +20,14 @@ namespace MediaBrowser.Controller.Entities private readonly IUserDataManager _userDataManager; private readonly ITVSeriesManager _tvSeriesManager; private readonly IServerConfigurationManager _config; - private readonly IPlaylistManager _playlistManager; - public UserViewBuilder(IUserViewManager userViewManager, ILibraryManager libraryManager, ILogger logger, IUserDataManager userDataManager, ITVSeriesManager tvSeriesManager, IServerConfigurationManager config, IPlaylistManager playlistManager) + public UserViewBuilder( + IUserViewManager userViewManager, + ILibraryManager libraryManager, + ILogger logger, + IUserDataManager userDataManager, + ITVSeriesManager tvSeriesManager, + IServerConfigurationManager config) { _userViewManager = userViewManager; _libraryManager = libraryManager; @@ -31,7 +35,6 @@ namespace MediaBrowser.Controller.Entities _userDataManager = userDataManager; _tvSeriesManager = tvSeriesManager; _config = config; - _playlistManager = playlistManager; } public QueryResult GetUserItems(Folder queryParent, Folder displayParent, string viewType, InternalItemsQuery query) @@ -110,6 +113,7 @@ namespace MediaBrowser.Controller.Entities { return GetResult(GetMediaFolders(user).OfType().SelectMany(i => i.GetChildren(user, true)), queryParent, query); } + return queryParent.GetItems(query); } } -- cgit v1.2.3 From 2696ac5eacfb4702d629bc06a8b42b868c316116 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 24 Feb 2019 03:16:19 +0100 Subject: Lower the amount of running tasks --- .../HttpServer/HttpListenerHost.cs | 15 +++------ .../Session/SessionWebSocketListener.cs | 6 ++-- .../SocketSharp/RequestMono.cs | 16 ++------- .../SocketSharp/WebSocketSharpRequest.cs | 39 +++++++++++----------- Jellyfin.Server/Program.cs | 6 ++-- MediaBrowser.Api/Library/LibraryService.cs | 19 +++++------ .../Net/BasePeriodicWebSocketListener.cs | 4 +-- MediaBrowser.Controller/Net/IWebSocketListener.cs | 2 +- RSSDP/HttpParserBase.cs | 6 ++-- RSSDP/SsdpCommunicationsServer.cs | 21 +++++------- 10 files changed, 53 insertions(+), 81 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index e8d47cad5..9214f2afe 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -11,7 +11,6 @@ using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.Net; using Emby.Server.Implementations.Services; -using Emby.Server.Implementations.SocketSharp; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; @@ -823,19 +822,15 @@ namespace Emby.Server.Implementations.HttpServer Logger.LogDebug("Websocket message received: {0}", result.MessageType); - var tasks = _webSocketListeners.Select(i => Task.Run(async () => + IEnumerable GetTasks() { - try - { - await i.ProcessMessage(result).ConfigureAwait(false); - } - catch (Exception ex) + foreach (var x in _webSocketListeners) { - Logger.LogError(ex, "{0} failed processing WebSocket message {1}", i.GetType().Name, result.MessageType ?? string.Empty); + yield return x.ProcessMessageAsync(result); } - })); + } - return Task.WhenAll(tasks); + return Task.WhenAll(GetTasks()); } public void Dispose() diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs index a551433ed..63ec75762 100644 --- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs @@ -89,10 +89,8 @@ namespace Emby.Server.Implementations.Session /// /// The message. /// Task. - public Task ProcessMessage(WebSocketMessageInfo message) - { - return Task.CompletedTask; - } + public Task ProcessMessageAsync(WebSocketMessageInfo message) + => Task.CompletedTask; private void EnsureController(SessionInfo session, IWebSocketConnection connection) { diff --git a/Emby.Server.Implementations/SocketSharp/RequestMono.cs b/Emby.Server.Implementations/SocketSharp/RequestMono.cs index 373f6d758..ec637186f 100644 --- a/Emby.Server.Implementations/SocketSharp/RequestMono.cs +++ b/Emby.Server.Implementations/SocketSharp/RequestMono.cs @@ -86,8 +86,7 @@ namespace Emby.Server.Implementations.SocketSharp else { // We use a substream, as in 2.x we will support large uploads streamed to disk, - var sub = new HttpPostedFile(e.Filename, e.ContentType, input, e.Start, e.Length); - files[e.Name] = sub; + files[e.Name] = new HttpPostedFile(e.Filename, e.ContentType, input, e.Start, e.Length); } } } @@ -374,7 +373,7 @@ namespace Emby.Server.Implementations.SocketSharp var elem = new Element(); ReadOnlySpan header; - while ((header = ReadHeaders().AsSpan()) != null) + while ((header = ReadLine().AsSpan()).Length != 0) { if (header.StartsWith("Content-Disposition:".AsSpan(), StringComparison.OrdinalIgnoreCase)) { @@ -513,17 +512,6 @@ namespace Emby.Server.Implementations.SocketSharp return false; } - private string ReadHeaders() - { - string s = ReadLine(); - if (s.Length == 0) - { - return null; - } - - return s; - } - private static bool CompareBytes(byte[] orig, byte[] other) { for (int i = orig.Length - 1; i >= 0; i--) diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs index e0a0ee286..6fdc6a3c8 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Net; +using System.Linq; using System.Text; -using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Logging; @@ -474,27 +474,28 @@ namespace Emby.Server.Implementations.SocketSharp { get { - if (httpFiles == null) + if (httpFiles != null) { - if (files == null) - { - return httpFiles = Array.Empty(); - } + return httpFiles; + } - httpFiles = new IHttpFile[files.Count]; - var i = 0; - foreach (var pair in files) + if (files == null) + { + return httpFiles = Array.Empty(); + } + + var values = files.Values; + httpFiles = new IHttpFile[values.Count]; + for (int i = 0; i < values.Count; i++) + { + var reqFile = values.ElementAt(i); + httpFiles[i] = new HttpFile { - var reqFile = pair.Value; - httpFiles[i] = new HttpFile - { - ContentType = reqFile.ContentType, - ContentLength = reqFile.ContentLength, - FileName = reqFile.FileName, - InputStream = reqFile.InputStream, - }; - i++; - } + ContentType = reqFile.ContentType, + ContentLength = reqFile.ContentLength, + FileName = reqFile.FileName, + InputStream = reqFile.InputStream, + }; } return httpFiles; diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 82a76c637..fbeb7a2e8 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -37,7 +37,7 @@ namespace Jellyfin.Server private static bool _restartOnShutdown; private static IConfiguration appConfig; - public static async Task Main(string[] args) + public static Task Main(string[] args) { // For backwards compatibility. // Modify any input arguments now which start with single-hyphen to POSIX standard @@ -51,8 +51,8 @@ namespace Jellyfin.Server } // Parse the command line arguments and either start the app or exit indicating error - await Parser.Default.ParseArguments(args) - .MapResult(StartApp, _ => Task.CompletedTask).ConfigureAwait(false); + return Parser.Default.ParseArguments(args) + .MapResult(StartApp, _ => Task.CompletedTask); } public static void Shutdown() diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 8eefbdf2c..8a5a793df 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -988,19 +988,16 @@ namespace MediaBrowser.Api.Library /// Posts the specified request. /// /// The request. - public void Post(RefreshLibrary request) + public async Task Post(RefreshLibrary request) { - Task.Run(() => + try { - try - { - _libraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None); - } - catch (Exception ex) - { - Logger.LogError(ex, "Error refreshing library"); - } - }); + await _libraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.LogError(ex, "Error refreshing library"); + } } /// diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index 844412546..ee5c1a165 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.Controller.Net /// /// The message. /// Task. - public Task ProcessMessage(WebSocketMessageInfo message) + public Task ProcessMessageAsync(WebSocketMessageInfo message) { if (message == null) { @@ -74,7 +74,7 @@ namespace MediaBrowser.Controller.Net Stop(message); } - return Task.FromResult(true); + return Task.CompletedTask; } protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); diff --git a/MediaBrowser.Controller/Net/IWebSocketListener.cs b/MediaBrowser.Controller/Net/IWebSocketListener.cs index e38f0e259..0f472a2bc 100644 --- a/MediaBrowser.Controller/Net/IWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/IWebSocketListener.cs @@ -12,6 +12,6 @@ namespace MediaBrowser.Controller.Net /// /// The message. /// Task. - Task ProcessMessage(WebSocketMessageInfo message); + Task ProcessMessageAsync(WebSocketMessageInfo message); } } diff --git a/RSSDP/HttpParserBase.cs b/RSSDP/HttpParserBase.cs index 18712470d..76d816e7b 100644 --- a/RSSDP/HttpParserBase.cs +++ b/RSSDP/HttpParserBase.cs @@ -23,8 +23,6 @@ namespace Rssdp.Infrastructure #region Public Methods - private static byte[] EmptyByteArray = new byte[]{}; - /// /// Parses the provided into either a or object. /// @@ -46,7 +44,7 @@ namespace Rssdp.Infrastructure if (data.Length == 0) throw new ArgumentException("data cannot be an empty string.", nameof(data)); if (!LineTerminators.Any(data.Contains)) throw new ArgumentException("data is not a valid request, it does not contain any CRLF/LF terminators.", nameof(data)); - using (var retVal = new ByteArrayContent(EmptyByteArray)) + using (var retVal = new ByteArrayContent(Array.Empty())) { var lines = data.Split(LineTerminators, StringSplitOptions.None); @@ -209,4 +207,4 @@ namespace Rssdp.Infrastructure #endregion } -} \ No newline at end of file +} diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index d9a4b6ac0..5d2afc37a 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -355,7 +355,7 @@ namespace Rssdp.Infrastructure { var socket = _SocketFactory.CreateUdpMulticastSocket(SsdpConstants.MulticastLocalAdminAddress, _MulticastTtl, SsdpConstants.MulticastPort); - ListenToSocket(socket); + _ = ListenToSocketInternal(socket); return socket; } @@ -389,19 +389,12 @@ namespace Rssdp.Infrastructure foreach (var socket in sockets) { - ListenToSocket(socket); + _ = ListenToSocketInternal(socket); } return sockets; } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "t", Justification = "Capturing task to local variable removes compiler warning, task is not otherwise required.")] - private void ListenToSocket(ISocket socket) - { - // Tasks are captured to local variables even if we don't use them just to avoid compiler warnings. - var t = Task.Run(() => ListenToSocketInternal(socket)); - } - private async Task ListenToSocketInternal(ISocket socket) { var cancelled = false; @@ -448,10 +441,10 @@ namespace Rssdp.Infrastructure private void ProcessMessage(string data, IpEndPointInfo endPoint, IpAddressInfo receivedOnLocalIpAddress) { - //Responses start with the HTTP version, prefixed with HTTP/ while - //requests start with a method which can vary and might be one we haven't - //seen/don't know. We'll check if this message is a request or a response - //by checking for the HTTP/ prefix on the start of the message. + // Responses start with the HTTP version, prefixed with HTTP/ while + // requests start with a method which can vary and might be one we haven't + // seen/don't know. We'll check if this message is a request or a response + // by checking for the HTTP/ prefix on the start of the message. if (data.StartsWith("HTTP/", StringComparison.OrdinalIgnoreCase)) { HttpResponseMessage responseMessage = null; @@ -465,7 +458,9 @@ namespace Rssdp.Infrastructure } if (responseMessage != null) + { OnResponseReceived(responseMessage, endPoint, receivedOnLocalIpAddress); + } } else { -- cgit v1.2.3 From 8ed5d154b746c086ccb6b1163772ea4d05067abd Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 27 Mar 2019 16:07:08 +0100 Subject: Remove duplicate code --- .../Session/SessionManager.cs | 62 ++++++++-------------- 1 file changed, 23 insertions(+), 39 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index dc23551db..b7974dd7e 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1037,6 +1037,25 @@ namespace Emby.Server.Implementations.Session } } + private static Task SendMessageToSessions(IEnumerable sessions, string name, T data, CancellationToken cancellationToken) + { + IEnumerable GetTasks() + { + foreach (var session in sessions) + { + var controllers = session.SessionControllers; + var messageId = Guid.NewGuid().ToString("N"); + + foreach (var controller in controllers) + { + yield return controller.SendMessage(name, messageId, data, controllers, cancellationToken); + } + } + } + + return Task.WhenAll(GetTasks()); + } + public async Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken) { CheckDisposed(); @@ -1832,15 +1851,7 @@ namespace Emby.Server.Implementations.Session var data = dataFn(); - IEnumerable GetTasks() - { - foreach (var session in sessions) - { - yield return SendMessageToSession(session, name, data, cancellationToken); - } - } - - return Task.WhenAll(GetTasks()); + return SendMessageToSessions(sessions, name, data, cancellationToken); } public Task SendMessageToUserSessions(List userIds, string name, T data, CancellationToken cancellationToken) @@ -1848,16 +1859,7 @@ namespace Emby.Server.Implementations.Session CheckDisposed(); var sessions = Sessions.Where(i => userIds.Any(i.ContainsUser)); - - IEnumerable GetTasks() - { - foreach (var session in sessions) - { - yield return SendMessageToSession(session, name, data, cancellationToken); - } - } - - return Task.WhenAll(GetTasks()); + return SendMessageToSessions(sessions, name, data, cancellationToken); } public Task SendMessageToUserDeviceSessions(string deviceId, string name, T data, CancellationToken cancellationToken) @@ -1865,16 +1867,7 @@ namespace Emby.Server.Implementations.Session CheckDisposed(); var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)); - - IEnumerable GetTasks() - { - foreach (var session in sessions) - { - yield return SendMessageToSession(session, name, data, cancellationToken); - } - } - - return Task.WhenAll(GetTasks()); + return SendMessageToSessions(sessions, name, data, cancellationToken); } public Task SendMessageToUserDeviceAndAdminSessions(string deviceId, string name, T data, CancellationToken cancellationToken) @@ -1883,16 +1876,7 @@ namespace Emby.Server.Implementations.Session var sessions = Sessions .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i)); - - IEnumerable GetTasks() - { - foreach (var session in sessions) - { - yield return SendMessageToSession(session, name, data, cancellationToken); - } - } - - return Task.WhenAll(GetTasks()); + return SendMessageToSessions(sessions, name, data, cancellationToken); } private bool IsAdminSession(SessionInfo s) -- cgit v1.2.3 From 6c0e2e249ddbab24c67b655b701e473d9343c2ef Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 27 Mar 2019 16:13:36 +0100 Subject: Even more duplicate code removed --- .../Session/SessionManager.cs | 56 +++------------------- 1 file changed, 6 insertions(+), 50 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index b7974dd7e..6b9573b1d 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1242,12 +1242,13 @@ namespace Emby.Server.Implementations.Session return SendMessageToSession(session, "Playstate", command, cancellationToken); } - private void AssertCanControl(SessionInfo session, SessionInfo controllingSession) + private static void AssertCanControl(SessionInfo session, SessionInfo controllingSession) { if (session == null) { throw new ArgumentNullException(nameof(session)); } + if (controllingSession == null) { throw new ArgumentNullException(nameof(controllingSession)); @@ -1259,26 +1260,11 @@ namespace Emby.Server.Implementations.Session /// /// The cancellation token. /// Task. - public async Task SendRestartRequiredNotification(CancellationToken cancellationToken) + public Task SendRestartRequiredNotification(CancellationToken cancellationToken) { CheckDisposed(); - var sessions = Sessions.ToList(); - - var tasks = sessions.Select(session => Task.Run(async () => - { - try - { - await SendMessageToSession(session, "RestartRequired", string.Empty, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError("Error in SendRestartRequiredNotification.", ex); - } - - }, cancellationToken)).ToArray(); - - await Task.WhenAll(tasks).ConfigureAwait(false); + return SendMessageToSessions(Sessions, "RestartRequired", string.Empty, cancellationToken); } /// @@ -1290,22 +1276,7 @@ namespace Emby.Server.Implementations.Session { CheckDisposed(); - var sessions = Sessions.ToList(); - - var tasks = sessions.Select(session => Task.Run(async () => - { - try - { - await SendMessageToSession(session, "ServerShuttingDown", string.Empty, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError("Error in SendServerShutdownNotification.", ex); - } - - }, cancellationToken)).ToArray(); - - return Task.WhenAll(tasks); + return SendMessageToSessions(Sessions, "ServerShuttingDown", string.Empty, cancellationToken); } /// @@ -1319,22 +1290,7 @@ namespace Emby.Server.Implementations.Session _logger.LogDebug("Beginning SendServerRestartNotification"); - var sessions = Sessions.ToList(); - - var tasks = sessions.Select(session => Task.Run(async () => - { - try - { - await SendMessageToSession(session, "ServerRestarting", string.Empty, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError("Error in SendServerRestartNotification.", ex); - } - - }, cancellationToken)).ToArray(); - - return Task.WhenAll(tasks); + return SendMessageToSessions(Sessions, "ServerRestarting", string.Empty, cancellationToken); } /// -- cgit v1.2.3 From b69b19ddce63651306bde494adfa656fd658b230 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 27 Mar 2019 16:28:52 +0100 Subject: Move messageId out of outer loop --- Emby.Server.Implementations/Session/SessionManager.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 6b9573b1d..53ed5fc22 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1041,11 +1041,10 @@ namespace Emby.Server.Implementations.Session { IEnumerable GetTasks() { + var messageId = Guid.NewGuid().ToString("N"); foreach (var session in sessions) { var controllers = session.SessionControllers; - var messageId = Guid.NewGuid().ToString("N"); - foreach (var controller in controllers) { yield return controller.SendMessage(name, messageId, data, controllers, cancellationToken); -- cgit v1.2.3 From 0794a3edf48eb232030560c4a03587cd77f38b65 Mon Sep 17 00:00:00 2001 From: bugfixin Date: Thu, 18 Apr 2019 21:47:19 +0000 Subject: Adjust detection of 'sample' in filenames to use regex boundaries --- .../Library/CoreResolutionIgnoreRule.cs | 11 +++-------- .../Library/Resolvers/Movies/MovieResolver.cs | 13 +++---------- 2 files changed, 6 insertions(+), 18 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index c644d13ea..c9d4e4342 100644 --- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Resolvers; @@ -148,15 +149,9 @@ namespace Emby.Server.Implementations.Library } // Ignore samples - var sampleFilename = " " + filename.Replace(".", " ", StringComparison.OrdinalIgnoreCase) - .Replace("-", " ", StringComparison.OrdinalIgnoreCase) - .Replace("_", " ", StringComparison.OrdinalIgnoreCase) - .Replace("!", " ", StringComparison.OrdinalIgnoreCase); + Match m = Regex.Match(filename,"\bsample\b",RegexOptions.IgnoreCase); - if (sampleFilename.IndexOf(" sample ", StringComparison.OrdinalIgnoreCase) != -1) - { - return true; - } + return m.Success; } return false; diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 848563679..47c3e71d7 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using Emby.Naming.Video; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; @@ -167,17 +168,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies private static bool IsIgnored(string filename) { // Ignore samples - var sampleFilename = " " + filename.Replace(".", " ", StringComparison.OrdinalIgnoreCase) - .Replace("-", " ", StringComparison.OrdinalIgnoreCase) - .Replace("_", " ", StringComparison.OrdinalIgnoreCase) - .Replace("!", " ", StringComparison.OrdinalIgnoreCase); + Match m = Regex.Match(filename,@"\bsample\b",RegexOptions.IgnoreCase); - if (sampleFilename.IndexOf(" sample ", StringComparison.OrdinalIgnoreCase) != -1) - { - return true; - } - - return false; + return m.Success; } private bool ContainsFile(List result, FileSystemMetadata file) -- cgit v1.2.3 From da842d5a730ea8db2fab8e4c71a501191d54e46c Mon Sep 17 00:00:00 2001 From: bugfixin Date: Fri, 19 Apr 2019 18:35:28 +0000 Subject: Fix incorrect escaping in regex pattern --- Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index c9d4e4342..a70077163 100644 --- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -149,7 +149,7 @@ namespace Emby.Server.Implementations.Library } // Ignore samples - Match m = Regex.Match(filename,"\bsample\b",RegexOptions.IgnoreCase); + Match m = Regex.Match(filename,@"\bsample\b",RegexOptions.IgnoreCase); return m.Success; } -- cgit v1.2.3 From 4a9b349c0463b46a3208b949f75d951f092aa735 Mon Sep 17 00:00:00 2001 From: dkanada Date: Thu, 16 May 2019 16:45:56 -0700 Subject: only return useful drives --- Emby.Server.Implementations/IO/ManagedFileSystem.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 0dea5041a..f0bea0455 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -667,13 +667,13 @@ namespace Emby.Server.Implementations.IO public virtual List GetDrives() { - // Only include drives in the ready state or this method could end up being very slow, waiting for drives to timeout - return DriveInfo.GetDrives().Where(d => d.IsReady).Select(d => new FileSystemMetadata + // check for ready state to avoid waiting for drives to timeout + // some drives on linux have no actual size or are used for other purposes + return DriveInfo.GetDrives().Where(d => d.IsReady && d.TotalSize != 0 && d.DriveType != DriveType.Ram).Select(d => new FileSystemMetadata { Name = d.Name, FullName = d.RootDirectory.FullName, IsDirectory = true - }).ToList(); } -- cgit v1.2.3 From 69ee49bee607d716a857a1525f503575ebf6db7f Mon Sep 17 00:00:00 2001 From: DrPandemic Date: Sat, 25 May 2019 13:46:55 -0400 Subject: Format correctly the PIN when updating it --- .../Library/DefaultAuthenticationProvider.cs | 28 ++++++++++++++++++++++ Emby.Server.Implementations/Library/UserManager.cs | 27 ++++----------------- .../Authentication/IAuthenticationProvider.cs | 3 +++ 3 files changed, 36 insertions(+), 22 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs index 0527464ff..fe09b07ff 100644 --- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs +++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs @@ -165,6 +165,34 @@ namespace Emby.Server.Implementations.Library return user.Password; } + public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash) + { + ConvertPasswordFormat(user); + + if (newPassword != null) + { + newPasswordHash = string.Format("$SHA1${0}", GetHashedString(user, newPassword)); + } + + if (string.IsNullOrWhiteSpace(newPasswordHash)) + { + throw new ArgumentNullException(nameof(newPasswordHash)); + } + + user.EasyPassword = newPasswordHash; + } + + public string GetEasyPasswordHash(User user) + { + // This should be removed in the future. This was added to let user login after + // Jellyfin 10.3.3 failed to save a well formatted PIN. + ConvertPasswordFormat(user); + + return string.IsNullOrEmpty(user.EasyPassword) + ? null + : (new PasswordHash(user.EasyPassword)).Hash; + } + public string GetHashedStringChangeAuth(string newPassword, PasswordHash passwordHash) { passwordHash.HashBytes = Encoding.UTF8.GetBytes(newPassword); diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index b396ee51a..a0b8d4ba4 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -471,7 +471,7 @@ namespace Emby.Server.Implementations.Library if (password == null) { // legacy - success = string.Equals(_defaultAuthenticationProvider.GetPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); + success = string.Equals(GetAuthenticationProvider(user).GetPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); } else { @@ -497,11 +497,11 @@ namespace Emby.Server.Implementations.Library if (password == null) { // legacy - success = string.Equals(GetLocalPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); + success = string.Equals(GetAuthenticationProvider(user).GetEasyPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); } else { - success = string.Equals(GetLocalPasswordHash(user), _defaultAuthenticationProvider.GetHashedString(user, password), StringComparison.OrdinalIgnoreCase); + success = string.Equals(GetAuthenticationProvider(user).GetEasyPasswordHash(user), _defaultAuthenticationProvider.GetHashedString(user, password), StringComparison.OrdinalIgnoreCase); } } } @@ -546,13 +546,6 @@ namespace Emby.Server.Implementations.Library } } - private string GetLocalPasswordHash(User user) - { - return string.IsNullOrEmpty(user.EasyPassword) - ? null - : (new PasswordHash(user.EasyPassword)).Hash; - } - /// /// Loads the users from the repository /// @@ -596,7 +589,7 @@ namespace Emby.Server.Implementations.Library } bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user).Result; - bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetLocalPasswordHash(user)); + bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(user)); bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ? hasConfiguredEasyPassword : @@ -884,17 +877,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(user)); } - if (newPassword != null) - { - newPasswordHash = _defaultAuthenticationProvider.GetHashedString(user, newPassword); - } - - if (string.IsNullOrWhiteSpace(newPasswordHash)) - { - throw new ArgumentNullException(nameof(newPasswordHash)); - } - - user.EasyPassword = newPasswordHash; + GetAuthenticationProvider(user).ChangeEasyPassword(user, newPassword, newPasswordHash); UpdateUser(user); diff --git a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs index b9f282bd2..2cf531eed 100644 --- a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs +++ b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs @@ -11,6 +11,9 @@ namespace MediaBrowser.Controller.Authentication Task Authenticate(string username, string password); Task HasPassword(User user); Task ChangePassword(User user, string newPassword); + void ChangeEasyPassword(User user, string newPassword, string newPasswordHash); + string GetPasswordHash(User user); + string GetEasyPasswordHash(User user); } public interface IRequiresResolvedUser -- cgit v1.2.3 From a6f9ceedd82fe34a8d1f088d91f7c217ee070aad Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 10 May 2019 20:37:42 +0200 Subject: Fix more warnings --- Emby.Naming/Audio/AlbumParser.cs | 26 ++++---- Emby.Naming/Audio/MultiPartResult.cs | 2 + Emby.Naming/AudioBook/AudioBookFileInfo.cs | 29 +++++++-- Emby.Naming/AudioBook/AudioBookFilePathParser.cs | 13 ++-- .../AudioBook/AudioBookFilePathParserResult.cs | 2 + Emby.Naming/AudioBook/AudioBookInfo.cs | 21 ++++-- Emby.Naming/AudioBook/AudioBookListResolver.cs | 2 +- Emby.Naming/AudioBook/AudioBookResolver.cs | 11 ++-- Emby.Naming/Common/EpisodeExpression.cs | 17 ++++- Emby.Naming/Common/MediaType.cs | 2 + Emby.Naming/Common/NamingOptions.cs | 75 +++++++++++----------- Emby.Naming/Emby.Naming.csproj | 18 +++++- Emby.Naming/Extensions/StringExtensions.cs | 1 + Emby.Naming/StringExtensions.cs | 30 --------- Emby.Naming/Subtitles/SubtitleInfo.cs | 3 + Emby.Naming/TV/EpisodeInfo.cs | 11 ++++ Emby.Naming/TV/EpisodePathParser.cs | 52 +++++++-------- Emby.Naming/TV/EpisodePathParserResult.cs | 7 ++ Emby.Naming/TV/EpisodeResolver.cs | 12 +++- Emby.Naming/TV/SeasonPathParser.cs | 42 ++++++------ Emby.Naming/TV/SeasonPathParserResult.cs | 2 + Emby.Naming/Video/CleanDateTimeParser.cs | 20 +++--- Emby.Naming/Video/ExtraResolver.cs | 2 - Emby.Naming/Video/FileStack.cs | 4 +- Emby.Naming/Video/Format3DParser.cs | 10 ++- Emby.Naming/Video/Format3DResult.cs | 12 ++-- Emby.Naming/Video/StackResolver.cs | 19 ++++-- Emby.Naming/Video/StubResolver.cs | 32 +++++---- Emby.Naming/Video/StubResult.cs | 1 + Emby.Naming/Video/StubTypeRule.cs | 1 + Emby.Naming/Video/VideoFileInfo.cs | 12 +++- Emby.Naming/Video/VideoInfo.cs | 4 ++ Emby.Naming/Video/VideoListResolver.cs | 40 ++++++------ Emby.Naming/Video/VideoResolver.cs | 12 ++-- .../Emby.Server.Implementations.csproj | 4 +- .../Library/LibraryManager.cs | 2 +- .../Library/Resolvers/TV/SeasonResolver.cs | 2 +- .../Library/Resolvers/TV/SeriesResolver.cs | 4 +- Jellyfin.Server/Jellyfin.Server.csproj | 6 +- Jellyfin.Server/Program.cs | 6 +- jellyfin.ruleset | 5 ++ 41 files changed, 339 insertions(+), 237 deletions(-) delete mode 100644 Emby.Naming/StringExtensions.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/Audio/AlbumParser.cs b/Emby.Naming/Audio/AlbumParser.cs index 7d029a9f4..e8d765552 100644 --- a/Emby.Naming/Audio/AlbumParser.cs +++ b/Emby.Naming/Audio/AlbumParser.cs @@ -33,27 +33,29 @@ namespace Emby.Naming.Audio // Normalize // Remove whitespace - filename = filename.Replace("-", " "); - filename = filename.Replace(".", " "); - filename = filename.Replace("(", " "); - filename = filename.Replace(")", " "); + filename = filename.Replace('-', ' '); + filename = filename.Replace('.', ' '); + filename = filename.Replace('(', ' '); + filename = filename.Replace(')', ' '); filename = Regex.Replace(filename, @"\s+", " "); filename = filename.TrimStart(); foreach (var prefix in _options.AlbumStackingPrefixes) { - if (filename.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) == 0) + if (filename.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) != 0) { - var tmp = filename.Substring(prefix.Length); + continue; + } + + var tmp = filename.Substring(prefix.Length); - tmp = tmp.Trim().Split(' ').FirstOrDefault() ?? string.Empty; + tmp = tmp.Trim().Split(' ').FirstOrDefault() ?? string.Empty; - if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val)) - { - result.IsMultiPart = true; - break; - } + if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _)) + { + result.IsMultiPart = true; + break; } } diff --git a/Emby.Naming/Audio/MultiPartResult.cs b/Emby.Naming/Audio/MultiPartResult.cs index b1fa6e563..00e4a9eb2 100644 --- a/Emby.Naming/Audio/MultiPartResult.cs +++ b/Emby.Naming/Audio/MultiPartResult.cs @@ -7,11 +7,13 @@ namespace Emby.Naming.Audio /// /// The name. public string Name { get; set; } + /// /// Gets or sets the part. /// /// The part. public string Part { get; set; } + /// /// Gets or sets a value indicating whether this instance is multi part. /// diff --git a/Emby.Naming/AudioBook/AudioBookFileInfo.cs b/Emby.Naming/AudioBook/AudioBookFileInfo.cs index de66a5402..326ea05ef 100644 --- a/Emby.Naming/AudioBook/AudioBookFileInfo.cs +++ b/Emby.Naming/AudioBook/AudioBookFileInfo.cs @@ -12,35 +12,56 @@ namespace Emby.Naming.AudioBook /// /// The path. public string Path { get; set; } + /// /// Gets or sets the container. /// /// The container. public string Container { get; set; } + /// /// Gets or sets the part number. /// /// The part number. public int? PartNumber { get; set; } + /// /// Gets or sets the chapter number. /// /// The chapter number. public int? ChapterNumber { get; set; } + /// /// Gets or sets the type. /// /// The type. public bool IsDirectory { get; set; } + /// public int CompareTo(AudioBookFileInfo other) { - if (ReferenceEquals(this, other)) return 0; - if (ReferenceEquals(null, other)) return 1; + if (ReferenceEquals(this, other)) + { + return 0; + } + + if (ReferenceEquals(null, other)) + { + return 1; + } + var chapterNumberComparison = Nullable.Compare(ChapterNumber, other.ChapterNumber); - if (chapterNumberComparison != 0) return chapterNumberComparison; + if (chapterNumberComparison != 0) + { + return chapterNumberComparison; + } + var partNumberComparison = Nullable.Compare(PartNumber, other.PartNumber); - if (partNumberComparison != 0) return partNumberComparison; + if (partNumberComparison != 0) + { + return partNumberComparison; + } + return string.Compare(Path, other.Path, StringComparison.Ordinal); } } diff --git a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs index 590979794..ea7f06c8c 100644 --- a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs +++ b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs @@ -1,3 +1,4 @@ +using System; using System.Globalization; using System.IO; using System.Text.RegularExpressions; @@ -14,14 +15,13 @@ namespace Emby.Naming.AudioBook _options = options; } - public AudioBookFilePathParserResult Parse(string path, bool IsDirectory) + public AudioBookFilePathParserResult Parse(string path) { - var result = Parse(path); - return !result.Success ? new AudioBookFilePathParserResult() : result; - } + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } - private AudioBookFilePathParserResult Parse(string path) - { var result = new AudioBookFilePathParserResult(); var fileName = Path.GetFileNameWithoutExtension(path); foreach (var expression in _options.AudioBookPartsExpressions) @@ -40,6 +40,7 @@ namespace Emby.Naming.AudioBook } } } + if (!result.PartNumber.HasValue) { var value = match.Groups["part"]; diff --git a/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs b/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs index 3a8e3c31f..f845e8243 100644 --- a/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs +++ b/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs @@ -3,7 +3,9 @@ namespace Emby.Naming.AudioBook public class AudioBookFilePathParserResult { public int? PartNumber { get; set; } + public int? ChapterNumber { get; set; } + public bool Success { get; set; } } } diff --git a/Emby.Naming/AudioBook/AudioBookInfo.cs b/Emby.Naming/AudioBook/AudioBookInfo.cs index f6e1d5be4..600d3f05d 100644 --- a/Emby.Naming/AudioBook/AudioBookInfo.cs +++ b/Emby.Naming/AudioBook/AudioBookInfo.cs @@ -7,33 +7,40 @@ namespace Emby.Naming.AudioBook /// public class AudioBookInfo { + public AudioBookInfo() + { + Files = new List(); + Extras = new List(); + AlternateVersions = new List(); + } + /// /// Gets or sets the name. /// /// The name. public string Name { get; set; } + + /// + /// Gets or sets the year. + /// public int? Year { get; set; } + /// /// Gets or sets the files. /// /// The files. public List Files { get; set; } + /// /// Gets or sets the extras. /// /// The extras. public List Extras { get; set; } + /// /// Gets or sets the alternate versions. /// /// The alternate versions. public List AlternateVersions { get; set; } - - public AudioBookInfo() - { - Files = new List(); - Extras = new List(); - AlternateVersions = new List(); - } } } diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs index 4e3ad7cac..414ef1183 100644 --- a/Emby.Naming/AudioBook/AudioBookListResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs @@ -15,7 +15,7 @@ namespace Emby.Naming.AudioBook _options = options; } - public IEnumerable Resolve(List files) + public IEnumerable Resolve(IEnumerable files) { var audioBookResolver = new AudioBookResolver(_options); diff --git a/Emby.Naming/AudioBook/AudioBookResolver.cs b/Emby.Naming/AudioBook/AudioBookResolver.cs index 67ab62e80..4a2b516d0 100644 --- a/Emby.Naming/AudioBook/AudioBookResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookResolver.cs @@ -24,19 +24,21 @@ namespace Emby.Naming.AudioBook return Resolve(path, true); } - public AudioBookFileInfo Resolve(string path, bool IsDirectory = false) + public AudioBookFileInfo Resolve(string path, bool isDirectory = false) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } - if (IsDirectory) // TODO + // TODO + if (isDirectory) { return null; } var extension = Path.GetExtension(path); + // Check supported extensions if (!_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { @@ -45,8 +47,7 @@ namespace Emby.Naming.AudioBook var container = extension.TrimStart('.'); - var parsingResult = new AudioBookFilePathParser(_options) - .Parse(path, IsDirectory); + var parsingResult = new AudioBookFilePathParser(_options).Parse(path); return new AudioBookFileInfo { @@ -54,7 +55,7 @@ namespace Emby.Naming.AudioBook Container = container, PartNumber = parsingResult.PartNumber, ChapterNumber = parsingResult.ChapterNumber, - IsDirectory = IsDirectory + IsDirectory = isDirectory }; } } diff --git a/Emby.Naming/Common/EpisodeExpression.cs b/Emby.Naming/Common/EpisodeExpression.cs index fd85bf76a..136d8189d 100644 --- a/Emby.Naming/Common/EpisodeExpression.cs +++ b/Emby.Naming/Common/EpisodeExpression.cs @@ -6,17 +6,28 @@ namespace Emby.Naming.Common public class EpisodeExpression { private string _expression; - public string Expression { get => _expression; - set { _expression = value; _regex = null; } } + private Regex _regex; + + public string Expression + { + get => _expression; + set + { + _expression = value; + _regex = null; + } + } public bool IsByDate { get; set; } + public bool IsOptimistic { get; set; } + public bool IsNamed { get; set; } + public bool SupportsAbsoluteEpisodeNumbers { get; set; } public string[] DateTimeFormats { get; set; } - private Regex _regex; public Regex Regex => _regex ?? (_regex = new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled)); public EpisodeExpression(string expression, bool byDate) diff --git a/Emby.Naming/Common/MediaType.cs b/Emby.Naming/Common/MediaType.cs index 49cc9ee39..a7b08bf79 100644 --- a/Emby.Naming/Common/MediaType.cs +++ b/Emby.Naming/Common/MediaType.cs @@ -6,10 +6,12 @@ namespace Emby.Naming.Common /// The audio /// Audio = 0, + /// /// The photo /// Photo = 1, + /// /// The video /// diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index 2ef0208ba..88a9b46e6 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -8,19 +8,25 @@ namespace Emby.Naming.Common public class NamingOptions { public string[] AudioFileExtensions { get; set; } + public string[] AlbumStackingPrefixes { get; set; } public string[] SubtitleFileExtensions { get; set; } + public char[] SubtitleFlagDelimiters { get; set; } public string[] SubtitleForcedFlags { get; set; } + public string[] SubtitleDefaultFlags { get; set; } public EpisodeExpression[] EpisodeExpressions { get; set; } + public string[] EpisodeWithoutSeasonExpressions { get; set; } + public string[] EpisodeMultiPartExpressions { get; set; } public string[] VideoFileExtensions { get; set; } + public string[] StubFileExtensions { get; set; } public string[] AudioBookPartsExpressions { get; set; } @@ -28,12 +34,14 @@ namespace Emby.Naming.Common public StubTypeRule[] StubTypes { get; set; } public char[] VideoFlagDelimiters { get; set; } + public Format3DRule[] Format3DRules { get; set; } public string[] VideoFileStackingExpressions { get; set; } + public string[] CleanDateTimes { get; set; } - public string[] CleanStrings { get; set; } + public string[] CleanStrings { get; set; } public EpisodeExpression[] MultipleEpisodeExpressions { get; set; } @@ -41,7 +49,7 @@ namespace Emby.Naming.Common public NamingOptions() { - VideoFileExtensions = new string[] + VideoFileExtensions = new[] { ".m4v", ".3gp", @@ -106,53 +114,53 @@ namespace Emby.Naming.Common { new StubTypeRule { - StubType = "dvd", - Token = "dvd" + StubType = "dvd", + Token = "dvd" }, new StubTypeRule { - StubType = "hddvd", - Token = "hddvd" + StubType = "hddvd", + Token = "hddvd" }, new StubTypeRule { - StubType = "bluray", - Token = "bluray" + StubType = "bluray", + Token = "bluray" }, new StubTypeRule { - StubType = "bluray", - Token = "brrip" + StubType = "bluray", + Token = "brrip" }, new StubTypeRule { - StubType = "bluray", - Token = "bd25" + StubType = "bluray", + Token = "bd25" }, new StubTypeRule { - StubType = "bluray", - Token = "bd50" + StubType = "bluray", + Token = "bd50" }, new StubTypeRule { - StubType = "vhs", - Token = "vhs" + StubType = "vhs", + Token = "vhs" }, new StubTypeRule { - StubType = "tv", - Token = "HDTV" + StubType = "tv", + Token = "HDTV" }, new StubTypeRule { - StubType = "tv", - Token = "PDTV" + StubType = "tv", + Token = "PDTV" }, new StubTypeRule { - StubType = "tv", - Token = "DSR" + StubType = "tv", + Token = "DSR" } }; @@ -286,7 +294,7 @@ namespace Emby.Naming.Common new EpisodeExpression(@"[\._ -]()[Ee][Pp]_?([0-9]+)([^\\/]*)$"), new EpisodeExpression("([0-9]{4})[\\.-]([0-9]{2})[\\.-]([0-9]{2})", true) { - DateTimeFormats = new [] + DateTimeFormats = new[] { "yyyy.MM.dd", "yyyy-MM-dd", @@ -295,7 +303,7 @@ namespace Emby.Naming.Common }, new EpisodeExpression("([0-9]{2})[\\.-]([0-9]{2})[\\.-]([0-9]{4})", true) { - DateTimeFormats = new [] + DateTimeFormats = new[] { "dd.MM.yyyy", "dd-MM-yyyy", @@ -348,9 +356,7 @@ namespace Emby.Naming.Common }, // "1-12 episode title" - new EpisodeExpression(@"([0-9]+)-([0-9]+)") - { - }, + new EpisodeExpression(@"([0-9]+)-([0-9]+)"), // "01 - blah.avi", "01-blah.avi" new EpisodeExpression(@".*(\\|\/)(?\d{1,3})(-(?\d{2,3}))*\s?-\s?[^\\\/]*$") @@ -427,7 +433,7 @@ namespace Emby.Naming.Common Token = "_trailer", MediaType = MediaType.Video }, - new ExtraRule + new ExtraRule { ExtraType = "trailer", RuleType = ExtraRuleType.Suffix, @@ -462,7 +468,7 @@ namespace Emby.Naming.Common Token = "_sample", MediaType = MediaType.Video }, - new ExtraRule + new ExtraRule { ExtraType = "sample", RuleType = ExtraRuleType.Suffix, @@ -476,7 +482,6 @@ namespace Emby.Naming.Common Token = "theme", MediaType = MediaType.Audio }, - new ExtraRule { ExtraType = "scene", @@ -526,8 +531,8 @@ namespace Emby.Naming.Common Token = "-short", MediaType = MediaType.Video } - }; + Format3DRules = new[] { // Kodi rules: @@ -648,12 +653,10 @@ namespace Emby.Naming.Common @".*(\\|\/)(?((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?\d{1,4})[xX](?\d{1,3}))(-[xX]?[eE]?(?\d{1,3}))+[^\\\/]*$", @".*(\\|\/)(?[^\\\/]*)[sS](?\d{1,4})[xX\.]?[eE](?\d{1,3})((-| - )?[xXeE](?\d{1,3}))+[^\\\/]*$", @".*(\\|\/)(?[^\\\/]*)[sS](?\d{1,4})[xX\.]?[eE](?\d{1,3})(-[xX]?[eE]?(?\d{1,3}))+[^\\\/]*$" - }.Select(i => new EpisodeExpression(i) - { - IsNamed = true - - }).ToArray(); + { + IsNamed = true + }).ToArray(); VideoFileExtensions = extensions .Distinct(StringComparer.OrdinalIgnoreCase) diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index c448ec0ce..699d89325 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -18,6 +18,22 @@ Jellyfin.Naming https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt https://github.com/jellyfin/jellyfin + true + + + + true + + + + + + + + + + + ../jellyfin.ruleset diff --git a/Emby.Naming/Extensions/StringExtensions.cs b/Emby.Naming/Extensions/StringExtensions.cs index 26c09aeb4..5512127a8 100644 --- a/Emby.Naming/Extensions/StringExtensions.cs +++ b/Emby.Naming/Extensions/StringExtensions.cs @@ -5,6 +5,7 @@ namespace Emby.Naming.Extensions { public static class StringExtensions { + // TODO: @bond remove this when moving to netstandard2.1 public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison) { var sb = new StringBuilder(); diff --git a/Emby.Naming/StringExtensions.cs b/Emby.Naming/StringExtensions.cs deleted file mode 100644 index 7c61922af..000000000 --- a/Emby.Naming/StringExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Text; - -namespace Emby.Naming -{ - internal static class StringExtensions - { - public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison) - { - var sb = new StringBuilder(); - - var previousIndex = 0; - var index = str.IndexOf(oldValue, comparison); - - while (index != -1) - { - sb.Append(str.Substring(previousIndex, index - previousIndex)); - sb.Append(newValue); - index += oldValue.Length; - - previousIndex = index; - index = str.IndexOf(oldValue, index, comparison); - } - - sb.Append(str.Substring(previousIndex)); - - return sb.ToString(); - } - } -} diff --git a/Emby.Naming/Subtitles/SubtitleInfo.cs b/Emby.Naming/Subtitles/SubtitleInfo.cs index e4709dfbb..96fce04d7 100644 --- a/Emby.Naming/Subtitles/SubtitleInfo.cs +++ b/Emby.Naming/Subtitles/SubtitleInfo.cs @@ -7,16 +7,19 @@ namespace Emby.Naming.Subtitles /// /// The path. public string Path { get; set; } + /// /// Gets or sets the language. /// /// The language. public string Language { get; set; } + /// /// Gets or sets a value indicating whether this instance is default. /// /// true if this instance is default; otherwise, false. public bool IsDefault { get; set; } + /// /// Gets or sets a value indicating whether this instance is forced. /// diff --git a/Emby.Naming/TV/EpisodeInfo.cs b/Emby.Naming/TV/EpisodeInfo.cs index c8aca7a6f..de79b8bba 100644 --- a/Emby.Naming/TV/EpisodeInfo.cs +++ b/Emby.Naming/TV/EpisodeInfo.cs @@ -7,31 +7,37 @@ namespace Emby.Naming.TV /// /// The path. public string Path { get; set; } + /// /// Gets or sets the container. /// /// The container. public string Container { get; set; } + /// /// Gets or sets the name of the series. /// /// The name of the series. public string SeriesName { get; set; } + /// /// Gets or sets the format3 d. /// /// The format3 d. public string Format3D { get; set; } + /// /// Gets or sets a value indicating whether [is3 d]. /// /// true if [is3 d]; otherwise, false. public bool Is3D { get; set; } + /// /// Gets or sets a value indicating whether this instance is stub. /// /// true if this instance is stub; otherwise, false. public bool IsStub { get; set; } + /// /// Gets or sets the type of the stub. /// @@ -39,12 +45,17 @@ namespace Emby.Naming.TV public string StubType { get; set; } public int? SeasonNumber { get; set; } + public int? EpisodeNumber { get; set; } + public int? EndingEpsiodeNumber { get; set; } public int? Year { get; set; } + public int? Month { get; set; } + public int? Day { get; set; } + public bool IsByDate { get; set; } } } diff --git a/Emby.Naming/TV/EpisodePathParser.cs b/Emby.Naming/TV/EpisodePathParser.cs index a8f81a3b8..812bc970e 100644 --- a/Emby.Naming/TV/EpisodePathParser.cs +++ b/Emby.Naming/TV/EpisodePathParser.cs @@ -15,12 +15,12 @@ namespace Emby.Naming.TV _options = options; } - public EpisodePathParserResult Parse(string path, bool IsDirectory, bool? isNamed = null, bool? isOptimistic = null, bool? supportsAbsoluteNumbers = null, bool fillExtendedInfo = true) + public EpisodePathParserResult Parse(string path, bool isDirectory, bool? isNamed = null, bool? isOptimistic = null, bool? supportsAbsoluteNumbers = null, bool fillExtendedInfo = true) { // Added to be able to use regex patterns which require a file extension. // There were no failed tests without this block, but to be safe, we can keep it until // the regex which require file extensions are modified so that they don't need them. - if (IsDirectory) + if (isDirectory) { path += ".mp4"; } @@ -29,28 +29,20 @@ namespace Emby.Naming.TV foreach (var expression in _options.EpisodeExpressions) { - if (supportsAbsoluteNumbers.HasValue) + if (supportsAbsoluteNumbers.HasValue + && expression.SupportsAbsoluteEpisodeNumbers != supportsAbsoluteNumbers.Value) { - if (expression.SupportsAbsoluteEpisodeNumbers != supportsAbsoluteNumbers.Value) - { - continue; - } + continue; } - if (isNamed.HasValue) + if (isNamed.HasValue && expression.IsNamed != isNamed.Value) { - if (expression.IsNamed != isNamed.Value) - { - continue; - } + continue; } - if (isOptimistic.HasValue) + if (isOptimistic.HasValue && expression.IsOptimistic != isOptimistic.Value) { - if (expression.IsOptimistic != isOptimistic.Value) - { - continue; - } + continue; } var currentResult = Parse(path, expression); @@ -97,7 +89,8 @@ namespace Emby.Naming.TV DateTime date; if (expression.DateTimeFormats.Length > 0) { - if (DateTime.TryParseExact(match.Groups[0].Value, + if (DateTime.TryParseExact( + match.Groups[0].Value, expression.DateTimeFormats, CultureInfo.InvariantCulture, DateTimeStyles.None, @@ -109,17 +102,15 @@ namespace Emby.Naming.TV result.Success = true; } } - else + else if (DateTime.TryParse(match.Groups[0].Value, out date)) { - if (DateTime.TryParse(match.Groups[0].Value, out date)) - { - result.Year = date.Year; - result.Month = date.Month; - result.Day = date.Day; - result.Success = true; - } + result.Year = date.Year; + result.Month = date.Month; + result.Day = date.Day; + result.Success = true; } + // TODO: Only consider success if date successfully parsed? result.Success = true; } @@ -142,7 +133,8 @@ namespace Emby.Naming.TV // or a 'p' or 'i' as what you would get with a pixel resolution specification. // It avoids erroneous parsing of something like "series-s09e14-1080p.mkv" as a multi-episode from E14 to E108 int nextIndex = endingNumberGroup.Index + endingNumberGroup.Length; - if (nextIndex >= name.Length || "0123456789iIpP".IndexOf(name[nextIndex]) == -1) + if (nextIndex >= name.Length + || "0123456789iIpP".IndexOf(name[nextIndex]) == -1) { if (int.TryParse(endingNumberGroup.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) { @@ -160,6 +152,7 @@ namespace Emby.Naming.TV { result.SeasonNumber = num; } + if (int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) { result.EpisodeNumber = num; @@ -171,8 +164,11 @@ namespace Emby.Naming.TV // Invalidate match when the season is 200 through 1927 or above 2500 // because it is an error unless the TV show is intentionally using false season numbers. // It avoids erroneous parsing of something like "Series Special (1920x1080).mkv" as being season 1920 episode 1080. - if (result.SeasonNumber >= 200 && result.SeasonNumber < 1928 || result.SeasonNumber > 2500) + if ((result.SeasonNumber >= 200 && result.SeasonNumber < 1928) + || result.SeasonNumber > 2500) + { result.Success = false; + } result.IsByDate = expression.IsByDate; } diff --git a/Emby.Naming/TV/EpisodePathParserResult.cs b/Emby.Naming/TV/EpisodePathParserResult.cs index e1a48bfbc..996edfc50 100644 --- a/Emby.Naming/TV/EpisodePathParserResult.cs +++ b/Emby.Naming/TV/EpisodePathParserResult.cs @@ -3,14 +3,21 @@ namespace Emby.Naming.TV public class EpisodePathParserResult { public int? SeasonNumber { get; set; } + public int? EpisodeNumber { get; set; } + public int? EndingEpsiodeNumber { get; set; } + public string SeriesName { get; set; } + public bool Success { get; set; } public bool IsByDate { get; set; } + public int? Year { get; set; } + public int? Month { get; set; } + public int? Day { get; set; } } } diff --git a/Emby.Naming/TV/EpisodeResolver.cs b/Emby.Naming/TV/EpisodeResolver.cs index fccf9bdec..2d7bcb638 100644 --- a/Emby.Naming/TV/EpisodeResolver.cs +++ b/Emby.Naming/TV/EpisodeResolver.cs @@ -15,7 +15,13 @@ namespace Emby.Naming.TV _options = options; } - public EpisodeInfo Resolve(string path, bool IsDirectory, bool? isNamed = null, bool? isOptimistic = null, bool? supportsAbsoluteNumbers = null, bool fillExtendedInfo = true) + public EpisodeInfo Resolve( + string path, + bool isDirectory, + bool? isNamed = null, + bool? isOptimistic = null, + bool? supportsAbsoluteNumbers = null, + bool fillExtendedInfo = true) { if (string.IsNullOrEmpty(path)) { @@ -26,7 +32,7 @@ namespace Emby.Naming.TV string container = null; string stubType = null; - if (!IsDirectory) + if (!isDirectory) { var extension = Path.GetExtension(path); // Check supported extensions @@ -52,7 +58,7 @@ namespace Emby.Naming.TV var format3DResult = new Format3DParser(_options).Parse(flags); var parsingResult = new EpisodePathParser(_options) - .Parse(path, IsDirectory, isNamed, isOptimistic, supportsAbsoluteNumbers, fillExtendedInfo); + .Parse(path, isDirectory, isNamed, isOptimistic, supportsAbsoluteNumbers, fillExtendedInfo); return new EpisodeInfo { diff --git a/Emby.Naming/TV/SeasonPathParser.cs b/Emby.Naming/TV/SeasonPathParser.cs index f1dcc50b8..e81b2bb34 100644 --- a/Emby.Naming/TV/SeasonPathParser.cs +++ b/Emby.Naming/TV/SeasonPathParser.cs @@ -3,30 +3,24 @@ using System.Globalization; using System.IO; using System.Linq; using Emby.Naming.Common; +using Emby.Naming.Extensions; namespace Emby.Naming.TV { public class SeasonPathParser { - private readonly NamingOptions _options; - - public SeasonPathParser(NamingOptions options) - { - _options = options; - } - public SeasonPathParserResult Parse(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders) { var result = new SeasonPathParserResult(); var seasonNumberInfo = GetSeasonNumberFromPath(path, supportSpecialAliases, supportNumericSeasonFolders); - result.SeasonNumber = seasonNumberInfo.Item1; + result.SeasonNumber = seasonNumberInfo.seasonNumber; if (result.SeasonNumber.HasValue) { result.Success = true; - result.IsSeasonFolder = seasonNumberInfo.Item2; + result.IsSeasonFolder = seasonNumberInfo.isSeasonFolder; } return result; @@ -35,7 +29,7 @@ namespace Emby.Naming.TV /// /// A season folder must contain one of these somewhere in the name /// - private static readonly string[] SeasonFolderNames = + private static readonly string[] _seasonFolderNames = { "season", "sæson", @@ -54,19 +48,23 @@ namespace Emby.Naming.TV /// if set to true [support special aliases]. /// if set to true [support numeric season folders]. /// System.Nullable{System.Int32}. - private Tuple GetSeasonNumberFromPath(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders) + private static (int? seasonNumber, bool isSeasonFolder) GetSeasonNumberFromPath( + string path, + bool supportSpecialAliases, + bool supportNumericSeasonFolders) { - var filename = Path.GetFileName(path); + var filename = Path.GetFileName(path) ?? string.Empty; if (supportSpecialAliases) { if (string.Equals(filename, "specials", StringComparison.OrdinalIgnoreCase)) { - return new Tuple(0, true); + return (0, true); } + if (string.Equals(filename, "extras", StringComparison.OrdinalIgnoreCase)) { - return new Tuple(0, true); + return (0, true); } } @@ -74,7 +72,7 @@ namespace Emby.Naming.TV { if (int.TryParse(filename, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val)) { - return new Tuple(val, true); + return (val, true); } } @@ -84,12 +82,12 @@ namespace Emby.Naming.TV if (int.TryParse(testFilename, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val)) { - return new Tuple(val, true); + return (val, true); } } // Look for one of the season folder names - foreach (var name in SeasonFolderNames) + foreach (var name in _seasonFolderNames) { var index = filename.IndexOf(name, StringComparison.OrdinalIgnoreCase); @@ -107,10 +105,10 @@ namespace Emby.Naming.TV var parts = filename.Split(new[] { '.', '_', ' ', '-' }, StringSplitOptions.RemoveEmptyEntries); var resultNumber = parts.Select(GetSeasonNumberFromPart).FirstOrDefault(i => i.HasValue); - return new Tuple(resultNumber, true); + return (resultNumber, true); } - private int? GetSeasonNumberFromPart(string part) + private static int? GetSeasonNumberFromPart(string part) { if (part.Length < 2 || !part.StartsWith("s", StringComparison.OrdinalIgnoreCase)) { @@ -132,7 +130,7 @@ namespace Emby.Naming.TV /// /// The path. /// System.Nullable{System.Int32}. - private Tuple GetSeasonNumberFromPathSubstring(string path) + private static (int? seasonNumber, bool isSeasonFolder) GetSeasonNumberFromPathSubstring(string path) { var numericStart = -1; var length = 0; @@ -174,10 +172,10 @@ namespace Emby.Naming.TV if (numericStart == -1) { - return new Tuple(null, isSeasonFolder); + return (null, isSeasonFolder); } - return new Tuple(int.Parse(path.Substring(numericStart, length), CultureInfo.InvariantCulture), isSeasonFolder); + return (int.Parse(path.Substring(numericStart, length), CultureInfo.InvariantCulture), isSeasonFolder); } } } diff --git a/Emby.Naming/TV/SeasonPathParserResult.cs b/Emby.Naming/TV/SeasonPathParserResult.cs index eab27a4a5..548dbd5d2 100644 --- a/Emby.Naming/TV/SeasonPathParserResult.cs +++ b/Emby.Naming/TV/SeasonPathParserResult.cs @@ -7,11 +7,13 @@ namespace Emby.Naming.TV /// /// The season number. public int? SeasonNumber { get; set; } + /// /// Gets or sets a value indicating whether this is success. /// /// true if success; otherwise, false. public bool Success { get; set; } + public bool IsSeasonFolder { get; set; } } } diff --git a/Emby.Naming/Video/CleanDateTimeParser.cs b/Emby.Naming/Video/CleanDateTimeParser.cs index 74807ef53..25fa09c48 100644 --- a/Emby.Naming/Video/CleanDateTimeParser.cs +++ b/Emby.Naming/Video/CleanDateTimeParser.cs @@ -27,8 +27,8 @@ namespace Emby.Naming.Video { var extension = Path.GetExtension(name) ?? string.Empty; // Check supported extensions - if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase) && - !_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) + if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase) + && !_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { // Dummy up a file extension because the expressions will fail without one // This is tricky because we can't just check Path.GetExtension for empty @@ -38,7 +38,6 @@ namespace Emby.Naming.Video } catch (ArgumentException) { - } var result = _options.CleanDateTimeRegexes.Select(i => Clean(name, i)) @@ -69,14 +68,15 @@ namespace Emby.Naming.Video var match = expression.Match(name); - if (match.Success && match.Groups.Count == 4) + if (match.Success + && match.Groups.Count == 4 + && match.Groups[1].Success + && match.Groups[2].Success + && int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year)) { - if (match.Groups[1].Success && match.Groups[2].Success && int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year)) - { - name = match.Groups[1].Value; - result.Year = year; - result.HasChanged = true; - } + name = match.Groups[1].Value; + result.Year = year; + result.HasChanged = true; } result.Name = name; diff --git a/Emby.Naming/Video/ExtraResolver.cs b/Emby.Naming/Video/ExtraResolver.cs index 3459b689a..9f70494d0 100644 --- a/Emby.Naming/Video/ExtraResolver.cs +++ b/Emby.Naming/Video/ExtraResolver.cs @@ -56,7 +56,6 @@ namespace Emby.Naming.Video result.Rule = rule; } } - else if (rule.RuleType == ExtraRuleType.Suffix) { var filename = Path.GetFileNameWithoutExtension(path); @@ -67,7 +66,6 @@ namespace Emby.Naming.Video result.Rule = rule; } } - else if (rule.RuleType == ExtraRuleType.Regex) { var filename = Path.GetFileName(path); diff --git a/Emby.Naming/Video/FileStack.cs b/Emby.Naming/Video/FileStack.cs index 2df1e9aed..584bdf2d2 100644 --- a/Emby.Naming/Video/FileStack.cs +++ b/Emby.Naming/Video/FileStack.cs @@ -15,9 +15,9 @@ namespace Emby.Naming.Video Files = new List(); } - public bool ContainsFile(string file, bool IsDirectory) + public bool ContainsFile(string file, bool isDirectory) { - if (IsDirectoryStack == IsDirectory) + if (IsDirectoryStack == isDirectory) { return Files.Contains(file, StringComparer.OrdinalIgnoreCase); } diff --git a/Emby.Naming/Video/Format3DParser.cs b/Emby.Naming/Video/Format3DParser.cs index e6f830c58..333a48641 100644 --- a/Emby.Naming/Video/Format3DParser.cs +++ b/Emby.Naming/Video/Format3DParser.cs @@ -15,10 +15,12 @@ namespace Emby.Naming.Video public Format3DResult Parse(string path) { - var delimeters = _options.VideoFlagDelimiters.ToList(); - delimeters.Add(' '); + int oldLen = _options.VideoFlagDelimiters.Length; + var delimeters = new char[oldLen + 1]; + _options.VideoFlagDelimiters.CopyTo(delimeters, 0); + delimeters[oldLen] = ' '; - return Parse(new FlagParser(_options).GetFlags(path, delimeters.ToArray())); + return Parse(new FlagParser(_options).GetFlags(path, delimeters)); } internal Format3DResult Parse(string[] videoFlags) @@ -66,8 +68,10 @@ namespace Emby.Naming.Video format = flag; result.Tokens.Add(rule.Token); } + break; } + foundPrefix = string.Equals(flag, rule.PreceedingToken, StringComparison.OrdinalIgnoreCase); } diff --git a/Emby.Naming/Video/Format3DResult.cs b/Emby.Naming/Video/Format3DResult.cs index e12494079..40fc31e08 100644 --- a/Emby.Naming/Video/Format3DResult.cs +++ b/Emby.Naming/Video/Format3DResult.cs @@ -4,25 +4,27 @@ namespace Emby.Naming.Video { public class Format3DResult { + public Format3DResult() + { + Tokens = new List(); + } + /// /// Gets or sets a value indicating whether [is3 d]. /// /// true if [is3 d]; otherwise, false. public bool Is3D { get; set; } + /// /// Gets or sets the format3 d. /// /// The format3 d. public string Format3D { get; set; } + /// /// Gets or sets the tokens. /// /// The tokens. public List Tokens { get; set; } - - public Format3DResult() - { - Tokens = new List(); - } } } diff --git a/Emby.Naming/Video/StackResolver.cs b/Emby.Naming/Video/StackResolver.cs index 4893002c1..b8ba42da4 100644 --- a/Emby.Naming/Video/StackResolver.cs +++ b/Emby.Naming/Video/StackResolver.cs @@ -40,17 +40,24 @@ namespace Emby.Naming.Video var result = new StackResult(); foreach (var directory in files.GroupBy(file => file.IsDirectory ? file.FullName : Path.GetDirectoryName(file.FullName))) { - var stack = new FileStack(); - stack.Name = Path.GetFileName(directory.Key); - stack.IsDirectoryStack = false; + var stack = new FileStack() + { + Name = Path.GetFileName(directory.Key), + IsDirectoryStack = false + }; foreach (var file in directory) { if (file.IsDirectory) + { continue; + } + stack.Files.Add(file.FullName); } + result.Stacks.Add(stack); } + return result; } @@ -114,16 +121,16 @@ namespace Emby.Naming.Video { if (!string.Equals(volume1, volume2, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(ignore1, ignore2, StringComparison.OrdinalIgnoreCase) && - string.Equals(extension1, extension2, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(ignore1, ignore2, StringComparison.OrdinalIgnoreCase) + && string.Equals(extension1, extension2, StringComparison.OrdinalIgnoreCase)) { if (stack.Files.Count == 0) { stack.Name = title1 + ignore1; stack.IsDirectoryStack = file1.IsDirectory; - //stack.Name = title1 + ignore1 + extension1; stack.Files.Add(file1.FullName); } + stack.Files.Add(file2.FullName); } else diff --git a/Emby.Naming/Video/StubResolver.cs b/Emby.Naming/Video/StubResolver.cs index f86bcbdf0..b78244cb3 100644 --- a/Emby.Naming/Video/StubResolver.cs +++ b/Emby.Naming/Video/StubResolver.cs @@ -9,24 +9,32 @@ namespace Emby.Naming.Video { public static StubResult ResolveFile(string path, NamingOptions options) { - var result = new StubResult(); - var extension = Path.GetExtension(path) ?? string.Empty; + if (path == null) + { + return default(StubResult); + } + + var extension = Path.GetExtension(path); - if (options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) + if (!options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { - result.IsStub = true; + return default(StubResult); + } - path = Path.GetFileNameWithoutExtension(path); + var result = new StubResult() + { + IsStub = true + }; - var token = (Path.GetExtension(path) ?? string.Empty).TrimStart('.'); + path = Path.GetFileNameWithoutExtension(path); + var token = Path.GetExtension(path).TrimStart('.'); - foreach (var rule in options.StubTypes) + foreach (var rule in options.StubTypes) + { + if (string.Equals(rule.Token, token, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(rule.Token, token, StringComparison.OrdinalIgnoreCase)) - { - result.StubType = rule.StubType; - break; - } + result.StubType = rule.StubType; + break; } } diff --git a/Emby.Naming/Video/StubResult.cs b/Emby.Naming/Video/StubResult.cs index 7f9509ca5..7a62e7b98 100644 --- a/Emby.Naming/Video/StubResult.cs +++ b/Emby.Naming/Video/StubResult.cs @@ -7,6 +7,7 @@ namespace Emby.Naming.Video /// /// true if this instance is stub; otherwise, false. public bool IsStub { get; set; } + /// /// Gets or sets the type of the stub. /// diff --git a/Emby.Naming/Video/StubTypeRule.cs b/Emby.Naming/Video/StubTypeRule.cs index b46050085..d76532150 100644 --- a/Emby.Naming/Video/StubTypeRule.cs +++ b/Emby.Naming/Video/StubTypeRule.cs @@ -7,6 +7,7 @@ namespace Emby.Naming.Video /// /// The token. public string Token { get; set; } + /// /// Gets or sets the type of the stub. /// diff --git a/Emby.Naming/Video/VideoFileInfo.cs b/Emby.Naming/Video/VideoFileInfo.cs index 6a29ada7e..78f688ca8 100644 --- a/Emby.Naming/Video/VideoFileInfo.cs +++ b/Emby.Naming/Video/VideoFileInfo.cs @@ -1,4 +1,3 @@ - namespace Emby.Naming.Video { /// @@ -11,56 +10,67 @@ namespace Emby.Naming.Video /// /// The path. public string Path { get; set; } + /// /// Gets or sets the container. /// /// The container. public string Container { get; set; } + /// /// Gets or sets the name. /// /// The name. public string Name { get; set; } + /// /// Gets or sets the year. /// /// The year. public int? Year { get; set; } + /// /// Gets or sets the type of the extra, e.g. trailer, theme song, behing the scenes, etc. /// /// The type of the extra. public string ExtraType { get; set; } + /// /// Gets or sets the extra rule. /// /// The extra rule. public ExtraRule ExtraRule { get; set; } + /// /// Gets or sets the format3 d. /// /// The format3 d. public string Format3D { get; set; } + /// /// Gets or sets a value indicating whether [is3 d]. /// /// true if [is3 d]; otherwise, false. public bool Is3D { get; set; } + /// /// Gets or sets a value indicating whether this instance is stub. /// /// true if this instance is stub; otherwise, false. public bool IsStub { get; set; } + /// /// Gets or sets the type of the stub. /// /// The type of the stub. public string StubType { get; set; } + /// /// Gets or sets the type. /// /// The type. public bool IsDirectory { get; set; } + /// /// Gets the file name without extension. /// diff --git a/Emby.Naming/Video/VideoInfo.cs b/Emby.Naming/Video/VideoInfo.cs index d96d0e757..2e456bda2 100644 --- a/Emby.Naming/Video/VideoInfo.cs +++ b/Emby.Naming/Video/VideoInfo.cs @@ -12,21 +12,25 @@ namespace Emby.Naming.Video /// /// The name. public string Name { get; set; } + /// /// Gets or sets the year. /// /// The year. public int? Year { get; set; } + /// /// Gets or sets the files. /// /// The files. public List Files { get; set; } + /// /// Gets or sets the extras. /// /// The extras. public List Extras { get; set; } + /// /// Gets or sets the alternate versions. /// diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index afedc30ef..5fa0041e0 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -53,7 +53,7 @@ namespace Emby.Naming.Video Name = stack.Name }; - info.Year = info.Files.First().Year; + info.Year = info.Files[0].Year; var extraBaseNames = new List { @@ -87,7 +87,7 @@ namespace Emby.Naming.Video Name = media.Name }; - info.Year = info.Files.First().Year; + info.Year = info.Files[0].Year; var extras = GetExtras(remainingFiles, new List { media.FileNameWithoutExtension }); @@ -115,7 +115,7 @@ namespace Emby.Naming.Video if (!string.IsNullOrEmpty(parentPath)) { - var folderName = Path.GetFileName(Path.GetDirectoryName(videoPath)); + var folderName = Path.GetFileName(parentPath); if (!string.IsNullOrEmpty(folderName)) { var extras = GetExtras(remainingFiles, new List { folderName }); @@ -163,9 +163,7 @@ namespace Emby.Naming.Video Year = i.Year })); - var orderedList = list.OrderBy(i => i.Name); - - return orderedList; + return list.OrderBy(i => i.Name); } private IEnumerable GetVideosGroupedByVersion(List videos) @@ -179,23 +177,21 @@ namespace Emby.Naming.Video var folderName = Path.GetFileName(Path.GetDirectoryName(videos[0].Files[0].Path)); - if (!string.IsNullOrEmpty(folderName) && folderName.Length > 1) + if (!string.IsNullOrEmpty(folderName) + && folderName.Length > 1 + && videos.All(i => i.Files.Count == 1 + && IsEligibleForMultiVersion(folderName, i.Files[0].Path)) + && HaveSameYear(videos)) { - if (videos.All(i => i.Files.Count == 1 && IsEligibleForMultiVersion(folderName, i.Files[0].Path))) - { - if (HaveSameYear(videos)) - { - var ordered = videos.OrderBy(i => i.Name).ToList(); + var ordered = videos.OrderBy(i => i.Name).ToList(); - list.Add(ordered[0]); + list.Add(ordered[0]); - list[0].AlternateVersions = ordered.Skip(1).Select(i => i.Files[0]).ToList(); - list[0].Name = folderName; - list[0].Extras.AddRange(ordered.Skip(1).SelectMany(i => i.Extras)); + list[0].AlternateVersions = ordered.Skip(1).Select(i => i.Files[0]).ToList(); + list[0].Name = folderName; + list[0].Extras.AddRange(ordered.Skip(1).SelectMany(i => i.Extras)); - return list; - } - } + return list; } return videos; @@ -213,9 +209,9 @@ namespace Emby.Naming.Video if (testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase)) { testFilename = testFilename.Substring(folderName.Length).Trim(); - return string.IsNullOrEmpty(testFilename) || - testFilename.StartsWith("-") || - string.IsNullOrWhiteSpace(Regex.Replace(testFilename, @"\[([^]]*)\]", string.Empty)) ; + return string.IsNullOrEmpty(testFilename) + || testFilename[0] == '-' + || string.IsNullOrWhiteSpace(Regex.Replace(testFilename, @"\[([^]]*)\]", string.Empty)); } return false; diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index a67315651..02a25c4b5 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -38,10 +38,11 @@ namespace Emby.Naming.Video /// Resolves the specified path. /// /// The path. - /// if set to true [is folder]. + /// if set to true [is folder]. + /// Whether or not the name should be parsed for info /// VideoFileInfo. /// path - public VideoFileInfo Resolve(string path, bool IsDirectory, bool parseName = true) + public VideoFileInfo Resolve(string path, bool isDirectory, bool parseName = true) { if (string.IsNullOrEmpty(path)) { @@ -52,9 +53,10 @@ namespace Emby.Naming.Video string container = null; string stubType = null; - if (!IsDirectory) + if (!isDirectory) { var extension = Path.GetExtension(path); + // Check supported extensions if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { @@ -79,7 +81,7 @@ namespace Emby.Naming.Video var extraResult = new ExtraResolver(_options).GetExtraInfo(path); - var name = IsDirectory + var name = isDirectory ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); @@ -108,7 +110,7 @@ namespace Emby.Naming.Video Is3D = format3DResult.Is3D, Format3D = format3DResult.Format3D, ExtraType = extraResult.ExtraType, - IsDirectory = IsDirectory, + IsDirectory = isDirectory, ExtraRule = extraResult.Rule }; } diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 2c7962452..d4e17c42a 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -52,8 +52,8 @@ - - + + diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 1673e3777..4b5063ada 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2368,7 +2368,7 @@ namespace Emby.Server.Implementations.Library public int? GetSeasonNumberFromPath(string path) { - return new SeasonPathParser(GetNamingOptions()).Parse(path, true, true).SeasonNumber; + return new SeasonPathParser().Parse(path, true, true).SeasonNumber; } public bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh) diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs index ce1386e91..3b9e48d97 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs @@ -52,7 +52,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV var path = args.Path; - var seasonParserResult = new SeasonPathParser(namingOptions).Parse(path, true, true); + var seasonParserResult = new SeasonPathParser().Parse(path, true, true); var season = new Season { diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 5c95534ec..1f873d7c6 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -194,9 +194,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// true if [is season folder] [the specified path]; otherwise, false. private static bool IsSeasonFolder(string path, bool isTvContentType, ILibraryManager libraryManager) { - var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions(); - - var seasonNumber = new SeasonPathParser(namingOptions).Parse(path, isTvContentType, isTvContentType).SeasonNumber; + var seasonNumber = new SeasonPathParser().Parse(path, isTvContentType, isTvContentType).SeasonNumber; return seasonNumber.HasValue; } diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 9346a2d25..81f145abf 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -12,7 +12,7 @@ latest - SA1600;SA1601;CS1591 + SA1600;SA1601;SA1629;CS1591 true @@ -26,8 +26,8 @@ - - + + diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index fab584bef..d8ca12117 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -122,8 +122,12 @@ namespace Jellyfin.Server // The default connection limit is 10 for ASP.NET hosted applications and 2 for all others. ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit); +// CA5359: Do Not Disable Certificate Validation +#pragma warning disable CA5359 + // Allow all https requests ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); +#pragma warning restore CA5359 var fileSystem = new ManagedFileSystem(_loggerFactory, appPaths); @@ -368,7 +372,7 @@ namespace Jellyfin.Server } catch (Exception ex) { - _logger.LogInformation(ex, "Skia not available. Will fallback to NullIMageEncoder. {0}"); + _logger.LogInformation(ex, "Skia not available. Will fallback to NullIMageEncoder."); } return new NullImageEncoder(); diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 262121a32..0a60c8c7a 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -14,12 +14,17 @@ + + + + + -- cgit v1.2.3 From 430483c7a1206e1e4fa6402f67c291931c55c4f0 Mon Sep 17 00:00:00 2001 From: dracocephalum Date: Thu, 30 May 2019 06:09:49 +0000 Subject: Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant/ --- Emby.Server.Implementations/Localization/Core/zh-TW.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index effff5566..293fc28a8 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -89,5 +89,8 @@ "UserStoppedPlayingItemWithValues": "{0} 已停止在 {2} 播放 {1}", "ValueHasBeenAddedToLibrary": "{0} 已新增至您的媒體庫", "ValueSpecialEpisodeName": "特典 - {0}", - "VersionNumber": "版本 {0}" + "VersionNumber": "版本 {0}", + "HeaderRecordingGroups": "錄製組", + "Inherit": "繼承", + "SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕" } -- cgit v1.2.3 From 3431a85adf9a5f9fbcd2ce4551af384c050051e1 Mon Sep 17 00:00:00 2001 From: exveria1015 Date: Thu, 30 May 2019 22:28:51 +0000 Subject: Translated using Weblate (Japanese) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ja/ --- .../Localization/Core/ja.json | 97 +++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json index 0967ef424..53a43a125 100644 --- a/Emby.Server.Implementations/Localization/Core/ja.json +++ b/Emby.Server.Implementations/Localization/Core/ja.json @@ -1 +1,96 @@ -{} +{ + "Albums": "アルバム", + "AppDeviceValues": "アプリ: {0}, デバイス: {1}", + "Application": "アプリケーション", + "Artists": "アーティスト", + "AuthenticationSucceededWithUserName": "{0} 認証に成功しました", + "Books": "ブック", + "CameraImageUploadedFrom": "新しいカメライメージが {0}からアップロードされました", + "Channels": "チャンネル", + "ChapterNameValue": "チャプター {0}", + "Collections": "コレクション", + "DeviceOfflineWithName": "{0} が切断されました", + "DeviceOnlineWithName": "{0} が接続されました", + "FailedLoginAttemptWithUserName": "ログインを試行しましたが {0}によって失敗しました", + "Favorites": "お気に入り", + "Folders": "フォルダ", + "Genres": "ジャンル", + "HeaderAlbumArtists": "アルバムアーティスト", + "HeaderCameraUploads": "カメラアップロード", + "HeaderContinueWatching": "視聴中", + "HeaderFavoriteAlbums": "お気に入りのアルバム", + "HeaderFavoriteArtists": "お気に入りのアーティスト", + "HeaderFavoriteEpisodes": "お気に入りのエピソード", + "HeaderFavoriteShows": "お気に入りの番組", + "HeaderFavoriteSongs": "お気に入りの曲", + "HeaderLiveTV": "ライブ テレビ", + "HeaderNextUp": "次", + "HeaderRecordingGroups": "レコーディンググループ", + "HomeVideos": "ホームビデオ", + "Inherit": "継承", + "ItemAddedWithName": "{0} をライブラリに追加しました", + "ItemRemovedWithName": "{0} をライブラリから削除しました", + "LabelIpAddressValue": "IPアドレス: {0}", + "LabelRunningTimeValue": "稼働時間: {0}", + "Latest": "最新", + "MessageApplicationUpdated": "Jellyfin Server が更新されました", + "MessageApplicationUpdatedTo": "Jellyfin Server が {0}に更新されました", + "MessageNamedServerConfigurationUpdatedWithValue": "サーバー設定項目の {0} が更新されました", + "MessageServerConfigurationUpdated": "サーバー設定が更新されました", + "MixedContent": "ミックスコンテンツ", + "Movies": "ムービー", + "Music": "ミュージック", + "MusicVideos": "ミュージックビデオ", + "NameInstallFailed": "{0}のインストールに失敗しました", + "NameSeasonNumber": "シーズン {0}", + "NameSeasonUnknown": "不明なシーズン", + "NewVersionIsAvailable": "新しいバージョンの Jellyfin Server がダウンロード可能です。", + "NotificationOptionApplicationUpdateAvailable": "アプリケーションの更新があります", + "NotificationOptionApplicationUpdateInstalled": "アプリケーションは最新です", + "NotificationOptionAudioPlayback": "オーディオの再生を開始", + "NotificationOptionAudioPlaybackStopped": "オーディオの再生をストップしました", + "NotificationOptionCameraImageUploaded": "カメライメージがアップロードされました", + "NotificationOptionInstallationFailed": "インストール失敗", + "NotificationOptionNewLibraryContent": "新しいコンテンツを追加しました", + "NotificationOptionPluginError": "プラグインに障害が発生しました", + "NotificationOptionPluginInstalled": "プラグインがインストールされました", + "NotificationOptionPluginUninstalled": "プラグインがアンインストールされました", + "NotificationOptionPluginUpdateInstalled": "プラグインのアップデートをインストールしました", + "NotificationOptionServerRestartRequired": "サーバーを再起動してください", + "NotificationOptionTaskFailed": "スケジュールされていたタスクの失敗", + "NotificationOptionUserLockedOut": "ユーザーはロックされています", + "NotificationOptionVideoPlayback": "ビデオの再生を開始しました", + "NotificationOptionVideoPlaybackStopped": "ビデオを停止しました", + "Photos": "フォト", + "Playlists": "プレイリスト", + "Plugin": "プラグイン", + "PluginInstalledWithName": "{0} がインストールされました", + "PluginUninstalledWithName": "{0} がアンインストールされました", + "PluginUpdatedWithName": "{0} が更新されました", + "ProviderValue": "プロバイダ: {0}", + "ScheduledTaskFailedWithName": "{0} が失敗しました", + "ScheduledTaskStartedWithName": "{0} が開始されました", + "ServerNameNeedsToBeRestarted": "{0} を再起動してください", + "Shows": "番組", + "Songs": "曲", + "StartupEmbyServerIsLoading": "Jellyfin Server は現在読み込み中です。しばらくしてからもう一度お試しください。", + "SubtitleDownloadFailureFromForItem": "{0} から {1}の字幕のダウンロードに失敗しました", + "SubtitlesDownloadedForItem": "{0} の字幕がダウンロードされました", + "Sync": "同期", + "System": "システム", + "TvShows": "テレビ番組", + "User": "ユーザー", + "UserCreatedWithName": "ユーザー {0} が作成されました", + "UserDeletedWithName": "User {0} を削除しました", + "UserDownloadingItemWithValues": "{0} が {1} をダウンロードしています", + "UserLockedOutWithName": "ユーザー {0} はロックされています", + "UserOfflineFromDevice": "{0} は {1} から切断しました", + "UserOnlineFromDevice": "{0} は {1} からオンラインになりました", + "UserPasswordChangedWithName": "ユーザー {0} のパスワードは変更されました", + "UserPolicyUpdatedWithName": "ユーザーポリシーが{0}に更新されました", + "UserStartedPlayingItemWithValues": "{0} は {2}で{1} を再生しています", + "UserStoppedPlayingItemWithValues": "{0} は{2}で{1} の再生が終わりました", + "ValueHasBeenAddedToLibrary": "{0}はあなたのメディアライブラリに追加されました", + "ValueSpecialEpisodeName": "スペシャル - {0}", + "VersionNumber": "バージョン {0}" +} -- cgit v1.2.3 From a2d9420139639ff339daf77c6658bf2f22502110 Mon Sep 17 00:00:00 2001 From: Hyunsu Nam Date: Fri, 31 May 2019 01:10:13 +0000 Subject: Translated using Weblate (Korean) Currently translated at 97.8% (92 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ko/ --- .../Localization/Core/ko.json | 156 ++++++++++----------- 1 file changed, 78 insertions(+), 78 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index 21808fd18..5a7ba8ba7 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -1,88 +1,88 @@ { - "Albums": "Albums", - "AppDeviceValues": "App: {0}, Device: {1}", - "Application": "Application", - "Artists": "Artists", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", - "Books": "Books", - "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", - "Channels": "Channels", - "ChapterNameValue": "Chapter {0}", - "Collections": "Collections", - "DeviceOfflineWithName": "{0} has disconnected", - "DeviceOnlineWithName": "{0} is connected", - "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", - "Favorites": "Favorites", - "Folders": "Folders", - "Genres": "Genres", + "Albums": "앨범", + "AppDeviceValues": "앱: {0}, 디바이스: {1}", + "Application": "애플리케이션", + "Artists": "아티스트", + "AuthenticationSucceededWithUserName": "{0} 인증에 성공했습니다.", + "Books": "책", + "CameraImageUploadedFrom": "새로운 카메라 이미지가 {0}에서 업로드되었습니다.", + "Channels": "채널", + "ChapterNameValue": "챕터 {0}", + "Collections": "컬렉션", + "DeviceOfflineWithName": "{0}가 접속이 끊어졌습니다.", + "DeviceOnlineWithName": "{0}가 접속되었습니다.", + "FailedLoginAttemptWithUserName": "{0}에서 로그인이 실패했습니다.", + "Favorites": "즐겨찾기", + "Folders": "폴더", + "Genres": "장르", "HeaderAlbumArtists": "앨범 아티스트", - "HeaderCameraUploads": "Camera Uploads", + "HeaderCameraUploads": "카메라 업로드", "HeaderContinueWatching": "계속 시청하기", - "HeaderFavoriteAlbums": "Favorite Albums", - "HeaderFavoriteArtists": "Favorite Artists", + "HeaderFavoriteAlbums": "좋아하는 앨범", + "HeaderFavoriteArtists": "좋아하는 아티스트", "HeaderFavoriteEpisodes": "Favorite Episodes", "HeaderFavoriteShows": "즐겨찾는 쇼", - "HeaderFavoriteSongs": "Favorite Songs", - "HeaderLiveTV": "Live TV", - "HeaderNextUp": "Next Up", - "HeaderRecordingGroups": "Recording Groups", - "HomeVideos": "Home videos", - "Inherit": "Inherit", - "ItemAddedWithName": "{0} was added to the library", - "ItemRemovedWithName": "{0} was removed from the library", - "LabelIpAddressValue": "Ip address: {0}", - "LabelRunningTimeValue": "Running time: {0}", - "Latest": "Latest", - "MessageApplicationUpdated": "Jellyfin Server has been updated", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", - "MessageServerConfigurationUpdated": "Server configuration has been updated", - "MixedContent": "Mixed content", - "Movies": "Movies", - "Music": "Music", - "MusicVideos": "Music videos", - "NameInstallFailed": "{0} installation failed", - "NameSeasonNumber": "Season {0}", - "NameSeasonUnknown": "Season Unknown", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", - "NotificationOptionApplicationUpdateAvailable": "Application update available", - "NotificationOptionApplicationUpdateInstalled": "Application update installed", - "NotificationOptionAudioPlayback": "Audio playback started", - "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", - "NotificationOptionCameraImageUploaded": "Camera image uploaded", - "NotificationOptionInstallationFailed": "Installation failure", - "NotificationOptionNewLibraryContent": "New content added", - "NotificationOptionPluginError": "Plugin failure", - "NotificationOptionPluginInstalled": "Plugin installed", - "NotificationOptionPluginUninstalled": "Plugin uninstalled", - "NotificationOptionPluginUpdateInstalled": "Plugin update installed", - "NotificationOptionServerRestartRequired": "Server restart required", - "NotificationOptionTaskFailed": "Scheduled task failure", - "NotificationOptionUserLockedOut": "User locked out", - "NotificationOptionVideoPlayback": "Video playback started", - "NotificationOptionVideoPlaybackStopped": "Video playback stopped", - "Photos": "Photos", - "Playlists": "Playlists", - "Plugin": "Plugin", - "PluginInstalledWithName": "{0} was installed", - "PluginUninstalledWithName": "{0} was uninstalled", - "PluginUpdatedWithName": "{0} was updated", - "ProviderValue": "Provider: {0}", - "ScheduledTaskFailedWithName": "{0} failed", - "ScheduledTaskStartedWithName": "{0} started", - "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", - "Shows": "Shows", - "Songs": "Songs", + "HeaderFavoriteSongs": "좋아하는 노래", + "HeaderLiveTV": "TV 방송", + "HeaderNextUp": "다음으로", + "HeaderRecordingGroups": "녹화 그룹", + "HomeVideos": "홈 비디오", + "Inherit": "상속", + "ItemAddedWithName": "{0} 라이브러리에 추가됨", + "ItemRemovedWithName": "{0} 라이브러리에서 제거됨", + "LabelIpAddressValue": "IP 주소: {0}", + "LabelRunningTimeValue": "상영 시간: {0}", + "Latest": "최근", + "MessageApplicationUpdated": "Jellyfin 서버 업데이트됨", + "MessageApplicationUpdatedTo": "Jellyfin 서버가 {0}로 업데이트됨", + "MessageNamedServerConfigurationUpdatedWithValue": "서버 환경 설정 {0} 섹션 업데이트 됨", + "MessageServerConfigurationUpdated": "서버 환경 설정 업데이드됨", + "MixedContent": "혼합 콘텐츠", + "Movies": "영화", + "Music": "음악", + "MusicVideos": "뮤직 비디오", + "NameInstallFailed": "{0} 설치 실패.", + "NameSeasonNumber": "시즌 {0}", + "NameSeasonUnknown": "알 수 없는 시즌", + "NewVersionIsAvailable": "새 버전의 Jellyfin 서버를 사용할 수 있습니다.", + "NotificationOptionApplicationUpdateAvailable": "애플리케이션 업데이트 사용 가능", + "NotificationOptionApplicationUpdateInstalled": "애플리케이션 업데이트가 설치됨", + "NotificationOptionAudioPlayback": "오디오 재생을 시작함", + "NotificationOptionAudioPlaybackStopped": "오디오 재생이 중지됨", + "NotificationOptionCameraImageUploaded": "카메라 이미지가 업로드됨", + "NotificationOptionInstallationFailed": "설치 실패", + "NotificationOptionNewLibraryContent": "새 콘텐트가 추가됨", + "NotificationOptionPluginError": "플러그인 실패", + "NotificationOptionPluginInstalled": "플러그인이 설치됨", + "NotificationOptionPluginUninstalled": "플러그인이 설치 제거됨", + "NotificationOptionPluginUpdateInstalled": "플러그인 업데이트가 설치됨", + "NotificationOptionServerRestartRequired": "서버를 다시 시작하십시오", + "NotificationOptionTaskFailed": "예약 작업 실패", + "NotificationOptionUserLockedOut": "사용자가 잠겼습니다", + "NotificationOptionVideoPlayback": "비디오 재생을 시작함", + "NotificationOptionVideoPlaybackStopped": "비디오 재생이 중지됨", + "Photos": "사진", + "Playlists": "재생목록", + "Plugin": "플러그인", + "PluginInstalledWithName": "{0} 설치됨", + "PluginUninstalledWithName": "{0} 설치 제거됨", + "PluginUpdatedWithName": "{0} 업데이트됨", + "ProviderValue": "제공자: {0}", + "ScheduledTaskFailedWithName": "{0} 실패", + "ScheduledTaskStartedWithName": "{0} 시작", + "ServerNameNeedsToBeRestarted": "{0} 를 재시작하십시오", + "Shows": "프로그램", + "Songs": "노래", "StartupEmbyServerIsLoading": "Jellyfin 서버를 불러오고 있습니다. 잠시후 다시시도 해주세요.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", - "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", - "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", - "Sync": "Sync", - "System": "System", - "TvShows": "TV Shows", - "User": "User", - "UserCreatedWithName": "User {0} has been created", - "UserDeletedWithName": "User {0} has been deleted", + "SubtitleDownloadFailureFromForItem": "{0}에서 {1} 자막 다운로드에 실패했습니다", + "SubtitlesDownloadedForItem": "{0} 자막을 다운로드했습니다", + "Sync": "동기화", + "System": "시스템", + "TvShows": "TV 쇼", + "User": "사용자", + "UserCreatedWithName": "사용자 {0} 생성됨", + "UserDeletedWithName": "사용자 {0} 삭제됨", "UserDownloadingItemWithValues": "{0} is downloading {1}", "UserLockedOutWithName": "User {0} has been locked out", "UserOfflineFromDevice": "{0} has disconnected from {1}", -- cgit v1.2.3 From 4adaeee0547bf67ebaebb200dcdd7b34af964051 Mon Sep 17 00:00:00 2001 From: Julio García Date: Sat, 1 Jun 2019 11:29:04 +0000 Subject: Translated using Weblate (Spanish) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es/ --- Emby.Server.Implementations/Localization/Core/es.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json index 1850b8f25..f03184d5b 100644 --- a/Emby.Server.Implementations/Localization/Core/es.json +++ b/Emby.Server.Implementations/Localization/Core/es.json @@ -21,7 +21,7 @@ "HeaderFavoriteAlbums": "Álbumes favoritos", "HeaderFavoriteArtists": "Artistas favoritos", "HeaderFavoriteEpisodes": "Episodios favoritos", - "HeaderFavoriteShows": "Programas favoritos", + "HeaderFavoriteShows": "Series favoritas", "HeaderFavoriteSongs": "Canciones favoritas", "HeaderLiveTV": "TV en directo", "HeaderNextUp": "Siguiendo", -- cgit v1.2.3 From 6b6776042c0c456c128734056e28765f1717ffc7 Mon Sep 17 00:00:00 2001 From: Juvenal Yescas Date: Tue, 28 May 2019 14:56:25 +0000 Subject: Translated using Weblate (Spanish (Mexico)) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_MX/ --- Emby.Server.Implementations/Localization/Core/es-MX.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json index 2285f2808..003632968 100644 --- a/Emby.Server.Implementations/Localization/Core/es-MX.json +++ b/Emby.Server.Implementations/Localization/Core/es-MX.json @@ -18,11 +18,11 @@ "HeaderAlbumArtists": "Artistas del Álbum", "HeaderCameraUploads": "Subidos desde Camara", "HeaderContinueWatching": "Continuar Viendo", - "HeaderFavoriteAlbums": "Álbumes Favoritos", - "HeaderFavoriteArtists": "Artistas Favoritos", - "HeaderFavoriteEpisodes": "Episodios Preferidos", - "HeaderFavoriteShows": "Programas Preferidos", - "HeaderFavoriteSongs": "Canciones Favoritas", + "HeaderFavoriteAlbums": "Álbumes favoritos", + "HeaderFavoriteArtists": "Artistas favoritos", + "HeaderFavoriteEpisodes": "Episodios favoritos", + "HeaderFavoriteShows": "Programas favoritos", + "HeaderFavoriteSongs": "Canciones favoritas", "HeaderLiveTV": "TV en Vivo", "HeaderNextUp": "A Continuación", "HeaderRecordingGroups": "Grupos de Grabaciones", -- cgit v1.2.3 From b768ad978efdb653022dc490055688d321f2bf34 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 2 Jun 2019 21:49:12 -0700 Subject: split the new command to more than one line --- Emby.Server.Implementations/IO/ManagedFileSystem.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index f0bea0455..7c2ea50e2 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -647,7 +647,6 @@ namespace Emby.Server.Implementations.IO public virtual bool IsPathFile(string path) { // Cannot use Path.IsPathRooted because it returns false under mono when using windows-based paths, e.g. C:\\ - if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) != -1 && !path.StartsWith("file://", StringComparison.OrdinalIgnoreCase)) { @@ -655,8 +654,6 @@ namespace Emby.Server.Implementations.IO } return true; - - //return Path.IsPathRooted(path); } public virtual void DeleteFile(string path) @@ -669,7 +666,8 @@ namespace Emby.Server.Implementations.IO { // check for ready state to avoid waiting for drives to timeout // some drives on linux have no actual size or are used for other purposes - return DriveInfo.GetDrives().Where(d => d.IsReady && d.TotalSize != 0 && d.DriveType != DriveType.Ram).Select(d => new FileSystemMetadata + return DriveInfo.GetDrives().Where(d => d.IsReady && d.TotalSize != 0 && d.DriveType != DriveType.Ram) + .Select(d => new FileSystemMetadata { Name = d.Name, FullName = d.RootDirectory.FullName, -- cgit v1.2.3 From d78a55adb4f66b8a82449216a11657da1388ab12 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sat, 8 Jun 2019 22:54:31 -0400 Subject: Implement InvalidAuthProvider Implements the InvalidAuthProvider, which acts as a fallback if a configured authentication provider, e.g. LDAP, is unavailable due to a load failure or removal. Until the user or the authentication plugin is corrected, this will cause users with the missing provider to be locked out, while throwing errors in the logs about the issue. Fixes #1445 part 2 --- .../Library/InvalidAuthProvider.cs | 46 ++++++++++++++++++++++ Emby.Server.Implementations/Library/UserManager.cs | 20 +++++++--- 2 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 Emby.Server.Implementations/Library/InvalidAuthProvider.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs new file mode 100644 index 000000000..ee2569562 --- /dev/null +++ b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Controller.Authentication; +using MediaBrowser.Controller.Entities; + +namespace Emby.Server.Implementations.Library +{ + public class InvalidAuthProvider : IAuthenticationProvider + { + public string Name => "InvalidorMissingAuthenticationProvider"; + + public bool IsEnabled => true; + + public Task Authenticate(string username, string password) + { + throw new Exception("User Account cannot login with this provider. The Normal provider for this user cannot be found"); + } + + public Task HasPassword(User user) + { + return Task.FromResult(true); + } + + public Task ChangePassword(User user, string newPassword) + { + return Task.FromResult(true); + } + + public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash) + { + // Nothing here + } + + public string GetPasswordHash(User user) + { + return ""; + } + + public string GetEasyPasswordHash(User user) + { + return ""; + } + } +} diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index a0b8d4ba4..ca43f7aaa 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -79,6 +79,8 @@ namespace Emby.Server.Implementations.Library private IAuthenticationProvider[] _authenticationProviders; private DefaultAuthenticationProvider _defaultAuthenticationProvider; + private InvalidAuthProvider _invalidAuthProvider; + private IPasswordResetProvider[] _passwordResetProviders; private DefaultPasswordResetProvider _defaultPasswordResetProvider; @@ -141,6 +143,8 @@ namespace Emby.Server.Implementations.Library _defaultAuthenticationProvider = _authenticationProviders.OfType().First(); + _invalidAuthProvider = _authenticationProviders.OfType().First(); + _passwordResetProviders = passwordResetProviders.ToArray(); _defaultPasswordResetProvider = passwordResetProviders.OfType().First(); @@ -307,11 +311,14 @@ namespace Emby.Server.Implementations.Library user = Users .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase)); - var hasNewUserPolicy = authenticationProvider as IHasNewUserPolicy; - if (hasNewUserPolicy != null) + if (authenticationProvider.GetType() != typeof(InvalidAuthProvider)) { - var policy = hasNewUserPolicy.GetNewUserPolicy(); - UpdateUserPolicy(user, policy, true); + var hasNewUserPolicy = authenticationProvider as IHasNewUserPolicy; + if (hasNewUserPolicy != null) + { + var policy = hasNewUserPolicy.GetNewUserPolicy(); + UpdateUserPolicy(user, policy, true); + } } } } @@ -400,7 +407,10 @@ namespace Emby.Server.Implementations.Library if (providers.Length == 0) { - providers = new IAuthenticationProvider[] { _defaultAuthenticationProvider }; + // this function used to assign any user without an auth provider to the default. + // we're going to have it use a new function now. + _logger.LogWarning($"The user {user.Name} was found but no Authentication Provider with ID: {user.Policy.AuthenticationProviderId} was found. Assigning user to InvalidAuthProvider temporarily"); + providers = new IAuthenticationProvider[] { _invalidAuthProvider }; } return providers; -- cgit v1.2.3 From 74ef3898798033a7cad987c4a869e7e72f57b229 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 9 Jun 2019 11:07:35 -0400 Subject: Add nicer log message and comment --- Emby.Server.Implementations/Library/UserManager.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index ca43f7aaa..83584acf3 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -407,9 +407,8 @@ namespace Emby.Server.Implementations.Library if (providers.Length == 0) { - // this function used to assign any user without an auth provider to the default. - // we're going to have it use a new function now. - _logger.LogWarning($"The user {user.Name} was found but no Authentication Provider with ID: {user.Policy.AuthenticationProviderId} was found. Assigning user to InvalidAuthProvider temporarily"); + // Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found + _logger.LogWarning("User {0} was found with invalid/missing Authentication Provider {1}. Assigning user to InvalidAuthProvider until this is corrected", user.Name, user.Policy.AuthenticationProviderId); providers = new IAuthenticationProvider[] { _invalidAuthProvider }; } -- cgit v1.2.3 From b70083f3b370055b2942e450291ce42345732cb7 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 9 Jun 2019 13:41:14 -0400 Subject: Apply suggestions from code review Co-Authored-By: Claus Vium Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Library/InvalidAuthProvider.cs | 8 ++++---- Emby.Server.Implementations/Library/UserManager.cs | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs index ee2569562..133864708 100644 --- a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs +++ b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs @@ -9,7 +9,7 @@ namespace Emby.Server.Implementations.Library { public class InvalidAuthProvider : IAuthenticationProvider { - public string Name => "InvalidorMissingAuthenticationProvider"; + public string Name => "InvalidOrMissingAuthenticationProvider"; public bool IsEnabled => true; @@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.Library public Task ChangePassword(User user, string newPassword) { - return Task.FromResult(true); + return Task.CompletedTask; } public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash) @@ -35,12 +35,12 @@ namespace Emby.Server.Implementations.Library public string GetPasswordHash(User user) { - return ""; + return string.Empty; } public string GetEasyPasswordHash(User user) { - return ""; + return string.Empty; } } } diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 83584acf3..04abfc315 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -313,8 +313,7 @@ namespace Emby.Server.Implementations.Library if (authenticationProvider.GetType() != typeof(InvalidAuthProvider)) { - var hasNewUserPolicy = authenticationProvider as IHasNewUserPolicy; - if (hasNewUserPolicy != null) + if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy) { var policy = hasNewUserPolicy.GetNewUserPolicy(); UpdateUserPolicy(user, policy, true); @@ -408,7 +407,7 @@ namespace Emby.Server.Implementations.Library if (providers.Length == 0) { // Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found - _logger.LogWarning("User {0} was found with invalid/missing Authentication Provider {1}. Assigning user to InvalidAuthProvider until this is corrected", user.Name, user.Policy.AuthenticationProviderId); + _logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user.Name, user.Policy.AuthenticationProviderId); providers = new IAuthenticationProvider[] { _invalidAuthProvider }; } -- cgit v1.2.3 From 20e2cb2d8693e3f53749bcde5b5fd6367bd15007 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 9 Jun 2019 13:45:51 -0400 Subject: Use SecurityException for auth failure --- Emby.Server.Implementations/Library/InvalidAuthProvider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs index 133864708..25d233137 100644 --- a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs +++ b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading.Tasks; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Net; namespace Emby.Server.Implementations.Library { @@ -15,7 +16,7 @@ namespace Emby.Server.Implementations.Library public Task Authenticate(string username, string password) { - throw new Exception("User Account cannot login with this provider. The Normal provider for this user cannot be found"); + throw new SecurityException("User Account cannot login with this provider. The Normal provider for this user cannot be found"); } public Task HasPassword(User user) -- cgit v1.2.3 From c230d49d7c37d4fbe77676b835c3afd6c8cb56e7 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 9 Jun 2019 13:46:53 -0400 Subject: Don't set a default reset provider --- Emby.Server.Implementations/Library/UserManager.cs | 5 ----- 1 file changed, 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 04abfc315..4233ea8f4 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -425,11 +425,6 @@ namespace Emby.Server.Implementations.Library providers = providers.Where(i => string.Equals(passwordResetProviderId, GetPasswordResetProviderId(i), StringComparison.OrdinalIgnoreCase)).ToArray(); } - if (providers.Length == 0) - { - providers = new IPasswordResetProvider[] { _defaultPasswordResetProvider }; - } - return providers; } -- cgit v1.2.3 From 4b8f735cb89901bd1004d590f4f2820c23e2493c Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 9 Jun 2019 13:57:49 -0400 Subject: Remove superfluous conditional This wasn't needed to prevent updating the policy on-disk from my tests and can be removed as suggested by @Bond-009 --- Emby.Server.Implementations/Library/UserManager.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 4233ea8f4..16becbd52 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -311,13 +311,10 @@ namespace Emby.Server.Implementations.Library user = Users .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase)); - if (authenticationProvider.GetType() != typeof(InvalidAuthProvider)) + if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy) { - if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy) - { - var policy = hasNewUserPolicy.GetNewUserPolicy(); - UpdateUserPolicy(user, policy, true); - } + var policy = hasNewUserPolicy.GetNewUserPolicy(); + UpdateUserPolicy(user, policy, true); } } } -- cgit v1.2.3 From 2946ae10092cddadade4c84cfa000129bf117e03 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 9 Jun 2019 15:27:38 -0400 Subject: Revert "Don't set a default reset provider" This reverts commit c230d49d7c37d4fbe77676b835c3afd6c8cb56e7. This reenables an edge case where an admin might want to reset, with the default auth provider, the password of an externally-provided user so they could "unlock" the account while it was failing. There might be minor security implications to this, but the malicious actor would need FS access to do it (as they would with any password resets) so it's probably best to keep it as-is. Removing this in the first place was due to a misunderstanding anyways so no harm. --- Emby.Server.Implementations/Library/UserManager.cs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 16becbd52..ff375e590 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -422,6 +422,11 @@ namespace Emby.Server.Implementations.Library providers = providers.Where(i => string.Equals(passwordResetProviderId, GetPasswordResetProviderId(i), StringComparison.OrdinalIgnoreCase)).ToArray(); } + if (providers.Length == 0) + { + providers = new IPasswordResetProvider[] { _defaultPasswordResetProvider }; + } + return providers; } -- cgit v1.2.3 From aa3022754527759d307138f606136561b4151d2a Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 9 Jun 2019 23:51:52 +0200 Subject: Improve main code flow Improved the way how some parts of the code depend on eachother Fixed some style issues --- .../IO/ManagedFileSystem.cs | 6 +- Jellyfin.Drawing.Skia/SkiaEncoder.cs | 72 ++++++++++------------ Jellyfin.Drawing.Skia/StripCollageBuilder.cs | 48 +++++++-------- Jellyfin.Server/Program.cs | 56 ++++++++++------- 4 files changed, 92 insertions(+), 90 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 7c2ea50e2..8517abed6 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -20,16 +20,14 @@ namespace Emby.Server.Implementations.IO protected ILogger Logger; private readonly List _shortcutHandlers = new List(); - private readonly string _tempPath; - private readonly bool _isEnvironmentCaseInsensitive; public ManagedFileSystem( - ILoggerFactory loggerFactory, + ILogger logger, IApplicationPaths applicationPaths) { - Logger = loggerFactory.CreateLogger("FileSystem"); + Logger = logger; _tempPath = applicationPaths.TempDirectory; _isEnvironmentCaseInsensitive = OperatingSystem.Id == OperatingSystemId.Windows; diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index 5060476ba..f6c120801 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -2,14 +2,11 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; -using System.Linq; -using System.Reflection; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Extensions; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; using SkiaSharp; @@ -18,24 +15,28 @@ namespace Jellyfin.Drawing.Skia public class SkiaEncoder : IImageEncoder { private readonly ILogger _logger; - private static IApplicationPaths _appPaths; - private readonly IFileSystem _fileSystem; - private static ILocalizationManager _localizationManager; + private readonly IApplicationPaths _appPaths; + private readonly ILocalizationManager _localizationManager; + + private static readonly HashSet _transparentImageTypes + = new HashSet(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" }; public SkiaEncoder( - ILoggerFactory loggerFactory, + ILogger logger, IApplicationPaths appPaths, - IFileSystem fileSystem, ILocalizationManager localizationManager) { - _logger = loggerFactory.CreateLogger("ImageEncoder"); + _logger = logger; _appPaths = appPaths; - _fileSystem = fileSystem; _localizationManager = localizationManager; - - LogVersion(); } + public string Name => "Skia"; + + public bool SupportsImageCollageCreation => true; + + public bool SupportsImageEncoding => true; + public IReadOnlyCollection SupportedInputFormats => new HashSet(StringComparer.OrdinalIgnoreCase) { @@ -66,17 +67,15 @@ namespace Jellyfin.Drawing.Skia public IReadOnlyCollection SupportedOutputFormats => new HashSet() { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png }; - private void LogVersion() + /// + /// Test to determine if the native lib is available + /// + public static void TestSkia() { // test an operation that requires the native library SKPMColor.PreMultiply(SKColors.Black); - - _logger.LogInformation("SkiaSharp version: " + GetVersion()); } - public static Version GetVersion() - => typeof(SKBitmap).GetTypeInfo().Assembly.GetName().Version; - private static bool IsTransparent(SKColor color) => (color.Red == 255 && color.Green == 255 && color.Blue == 255) || color.Alpha == 0; @@ -106,6 +105,7 @@ namespace Jellyfin.Drawing.Skia return false; } } + return true; } @@ -118,6 +118,7 @@ namespace Jellyfin.Drawing.Skia return false; } } + return true; } @@ -197,7 +198,7 @@ namespace Jellyfin.Drawing.Skia private static bool HasDiacritics(string text) => !string.Equals(text, text.RemoveDiacritics(), StringComparison.Ordinal); - private static bool RequiresSpecialCharacterHack(string path) + private bool RequiresSpecialCharacterHack(string path) { if (_localizationManager.HasUnicodeCategory(path, UnicodeCategory.OtherLetter)) { @@ -212,7 +213,7 @@ namespace Jellyfin.Drawing.Skia return false; } - private static string NormalizePath(string path, IFileSystem fileSystem) + private string NormalizePath(string path) { if (!RequiresSpecialCharacterHack(path)) { @@ -255,21 +256,18 @@ namespace Jellyfin.Drawing.Skia } } - private static readonly HashSet TransparentImageTypes - = new HashSet(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" }; - - internal static SKBitmap Decode(string path, bool forceCleanBitmap, IFileSystem fileSystem, ImageOrientation? orientation, out SKEncodedOrigin origin) + internal SKBitmap Decode(string path, bool forceCleanBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin) { if (!File.Exists(path)) { throw new FileNotFoundException("File not found", path); } - var requiresTransparencyHack = TransparentImageTypes.Contains(Path.GetExtension(path)); + var requiresTransparencyHack = _transparentImageTypes.Contains(Path.GetExtension(path)); if (requiresTransparencyHack || forceCleanBitmap) { - using (var stream = new SKFileStream(NormalizePath(path, fileSystem))) + using (var stream = new SKFileStream(NormalizePath(path))) using (var codec = SKCodec.Create(stream)) { if (codec == null) @@ -290,11 +288,11 @@ namespace Jellyfin.Drawing.Skia } } - var resultBitmap = SKBitmap.Decode(NormalizePath(path, fileSystem)); + var resultBitmap = SKBitmap.Decode(NormalizePath(path)); if (resultBitmap == null) { - return Decode(path, true, fileSystem, orientation, out origin); + return Decode(path, true, orientation, out origin); } // If we have to resize these they often end up distorted @@ -302,7 +300,7 @@ namespace Jellyfin.Drawing.Skia { using (resultBitmap) { - return Decode(path, true, fileSystem, orientation, out origin); + return Decode(path, true, orientation, out origin); } } @@ -314,13 +312,13 @@ namespace Jellyfin.Drawing.Skia { if (cropWhitespace) { - using (var bitmap = Decode(path, forceAnalyzeBitmap, _fileSystem, orientation, out origin)) + using (var bitmap = Decode(path, forceAnalyzeBitmap, orientation, out origin)) { return CropWhiteSpace(bitmap); } } - return Decode(path, forceAnalyzeBitmap, _fileSystem, orientation, out origin); + return Decode(path, forceAnalyzeBitmap, orientation, out origin); } private SKBitmap GetBitmap(string path, bool cropWhitespace, bool autoOrient, ImageOrientation? orientation) @@ -607,16 +605,16 @@ namespace Jellyfin.Drawing.Skia if (ratio >= 1.4) { - new StripCollageBuilder(_appPaths, _fileSystem).BuildThumbCollage(options.InputPaths, options.OutputPath, options.Width, options.Height); + new StripCollageBuilder(this).BuildThumbCollage(options.InputPaths, options.OutputPath, options.Width, options.Height); } else if (ratio >= .9) { - new StripCollageBuilder(_appPaths, _fileSystem).BuildSquareCollage(options.InputPaths, options.OutputPath, options.Width, options.Height); + new StripCollageBuilder(this).BuildSquareCollage(options.InputPaths, options.OutputPath, options.Width, options.Height); } else { // TODO: Create Poster collage capability - new StripCollageBuilder(_appPaths, _fileSystem).BuildSquareCollage(options.InputPaths, options.OutputPath, options.Width, options.Height); + new StripCollageBuilder(this).BuildSquareCollage(options.InputPaths, options.OutputPath, options.Width, options.Height); } } @@ -645,11 +643,5 @@ namespace Jellyfin.Drawing.Skia _logger.LogError(ex, "Error drawing indicator overlay"); } } - - public string Name => "Skia"; - - public bool SupportsImageCollageCreation => true; - - public bool SupportsImageEncoding => true; } } diff --git a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs index 7d404ce64..1f2a6e81a 100644 --- a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs +++ b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs @@ -1,21 +1,17 @@ using System; using System.Collections.Generic; using System.IO; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.IO; using SkiaSharp; namespace Jellyfin.Drawing.Skia { public class StripCollageBuilder { - private readonly IApplicationPaths _appPaths; - private readonly IFileSystem _fileSystem; + private readonly SkiaEncoder _skiaEncoder; - public StripCollageBuilder(IApplicationPaths appPaths, IFileSystem fileSystem) + public StripCollageBuilder(SkiaEncoder skiaEncoder) { - _appPaths = appPaths; - _fileSystem = fileSystem; + _skiaEncoder = skiaEncoder; } public static SKEncodedImageFormat GetEncodedFormat(string outputPath) @@ -25,19 +21,28 @@ namespace Jellyfin.Drawing.Skia throw new ArgumentNullException(nameof(outputPath)); } - var ext = Path.GetExtension(outputPath).ToLowerInvariant(); + var ext = Path.GetExtension(outputPath); - if (ext == ".jpg" || ext == ".jpeg") + if (string.Equals(ext, ".jpg", StringComparison.OrdinalIgnoreCase) + || string.Equals(ext, ".jpeg", StringComparison.OrdinalIgnoreCase)) + { return SKEncodedImageFormat.Jpeg; + } - if (ext == ".webp") + if (string.Equals(ext, ".webp", StringComparison.OrdinalIgnoreCase)) + { return SKEncodedImageFormat.Webp; + } - if (ext == ".gif") + if (string.Equals(ext, ".gif", StringComparison.OrdinalIgnoreCase)) + { return SKEncodedImageFormat.Gif; + } - if (ext == ".bmp") + if (string.Equals(ext, ".bmp", StringComparison.OrdinalIgnoreCase)) + { return SKEncodedImageFormat.Bmp; + } // default to png return SKEncodedImageFormat.Png; @@ -47,25 +52,19 @@ namespace Jellyfin.Drawing.Skia { using (var bitmap = BuildSquareCollageBitmap(paths, width, height)) using (var outputStream = new SKFileWStream(outputPath)) + using (var pixmap = new SKPixmap(new SKImageInfo(width, height), bitmap.GetPixels())) { - using (var pixmap = new SKPixmap(new SKImageInfo(width, height), bitmap.GetPixels())) - { - pixmap.Encode(outputStream, GetEncodedFormat(outputPath), 90); - } + pixmap.Encode(outputStream, GetEncodedFormat(outputPath), 90); } } public void BuildThumbCollage(string[] paths, string outputPath, int width, int height) { using (var bitmap = BuildThumbCollageBitmap(paths, width, height)) + using (var outputStream = new SKFileWStream(outputPath)) + using (var pixmap = new SKPixmap(new SKImageInfo(width, height), bitmap.GetPixels())) { - using (var outputStream = new SKFileWStream(outputPath)) - { - using (var pixmap = new SKPixmap(new SKImageInfo(width, height), bitmap.GetPixels())) - { - pixmap.Encode(outputStream, GetEncodedFormat(outputPath), 90); - } - } + pixmap.Encode(outputStream, GetEncodedFormat(outputPath), 90); } } @@ -127,7 +126,7 @@ namespace Jellyfin.Drawing.Skia currentIndex = 0; } - bitmap = SkiaEncoder.Decode(paths[currentIndex], false, _fileSystem, null, out var origin); + bitmap = _skiaEncoder.Decode(paths[currentIndex], false, null, out var origin); imagesTested[currentIndex] = 0; @@ -156,7 +155,6 @@ namespace Jellyfin.Drawing.Skia { for (var y = 0; y < 2; y++) { - using (var currentBitmap = GetNextValidImage(paths, imageIndex, out int newIndex)) { imageIndex = newIndex; diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 049dd761b..91752a16d 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -34,7 +34,6 @@ namespace Jellyfin.Server private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory(); private static ILogger _logger; private static bool _restartOnShutdown; - private static IConfiguration appConfig; public static Task Main(string[] args) { @@ -76,7 +75,7 @@ namespace Jellyfin.Server // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath); - appConfig = await CreateConfiguration(appPaths).ConfigureAwait(false); + IConfiguration appConfig = await CreateConfiguration(appPaths).ConfigureAwait(false); CreateLogger(appConfig, appPaths); @@ -116,8 +115,6 @@ namespace Jellyfin.Server ApplicationHost.LogEnvironmentInfo(_logger, appPaths); - SQLitePCL.Batteries_V2.Init(); - // Increase the max http request limit // The default connection limit is 10 for ASP.NET hosted applications and 2 for all others. ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit); @@ -129,20 +126,20 @@ namespace Jellyfin.Server ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); #pragma warning restore CA5359 - var fileSystem = new ManagedFileSystem(_loggerFactory, appPaths); + SQLitePCL.Batteries_V2.Init(); using (var appHost = new CoreAppHost( appPaths, _loggerFactory, options, - fileSystem, + new ManagedFileSystem(_loggerFactory.CreateLogger(), appPaths), new NullImageEncoder(), new NetworkManager(_loggerFactory), appConfig)) { await appHost.InitAsync(new ServiceCollection()).ConfigureAwait(false); - appHost.ImageProcessor.ImageEncoder = GetImageEncoder(fileSystem, appPaths, appHost.LocalizationManager); + appHost.ImageProcessor.ImageEncoder = GetImageEncoder(appPaths, appHost.LocalizationManager); await appHost.RunStartupTasksAsync().ConfigureAwait(false); @@ -165,7 +162,7 @@ namespace Jellyfin.Server /// /// Create the data, config and log paths from the variety of inputs(command line args, - /// environment variables) or decide on what default to use. For Windows it's %AppPath% + /// environment variables) or decide on what default to use. For Windows it's %AppPath% /// for everything else the XDG approach is followed: /// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html /// @@ -187,7 +184,9 @@ namespace Jellyfin.Server if (string.IsNullOrEmpty(dataDir)) { // LocalApplicationData follows the XDG spec on unix machines - dataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "jellyfin"); + dataDir = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "jellyfin"); } } @@ -206,20 +205,26 @@ namespace Jellyfin.Server if (string.IsNullOrEmpty(configDir)) { - if (options.DataDir != null || Directory.Exists(Path.Combine(dataDir, "config")) || RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (options.DataDir != null + || Directory.Exists(Path.Combine(dataDir, "config")) + || RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // Hang config folder off already set dataDir configDir = Path.Combine(dataDir, "config"); } else { - // $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration files should be stored. + // $XDG_CONFIG_HOME defines the base directory relative to which + // user specific configuration files should be stored. configDir = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); - // If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME /.config should be used. + // If $XDG_CONFIG_HOME is either not set or empty, + // a default equal to $HOME /.config should be used. if (string.IsNullOrEmpty(configDir)) { - configDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config"); + configDir = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), + ".config"); } configDir = Path.Combine(configDir, "jellyfin"); @@ -247,13 +252,17 @@ namespace Jellyfin.Server } else { - // $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data files should be stored. + // $XDG_CACHE_HOME defines the base directory relative to which + // user specific non-essential data files should be stored. cacheDir = Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); - // If $XDG_CACHE_HOME is either not set or empty, a default equal to $HOME/.cache should be used. + // If $XDG_CACHE_HOME is either not set or empty, + // a default equal to $HOME/.cache should be used. if (string.IsNullOrEmpty(cacheDir)) { - cacheDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".cache"); + cacheDir = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), + ".cache"); } cacheDir = Path.Combine(cacheDir, "jellyfin"); @@ -362,17 +371,22 @@ namespace Jellyfin.Server } private static IImageEncoder GetImageEncoder( - IFileSystem fileSystem, IApplicationPaths appPaths, ILocalizationManager localizationManager) { try { - return new SkiaEncoder(_loggerFactory, appPaths, fileSystem, localizationManager); + // Test if the native lib is available + SkiaEncoder.TestSkia(); + + return new SkiaEncoder( + _loggerFactory.CreateLogger(), + appPaths, + localizationManager); } catch (Exception ex) { - _logger.LogInformation(ex, "Skia not available. Will fallback to NullIMageEncoder."); + _logger.LogWarning(ex, "Skia not available. Will fallback to NullIMageEncoder."); } return new NullImageEncoder(); @@ -386,7 +400,7 @@ namespace Jellyfin.Server if (string.IsNullOrWhiteSpace(module)) { - module = Environment.GetCommandLineArgs().First(); + module = Environment.GetCommandLineArgs()[0]; } string commandLineArgsString; @@ -398,7 +412,7 @@ namespace Jellyfin.Server else { commandLineArgsString = string.Join( - " ", + ' ', Environment.GetCommandLineArgs().Skip(1).Select(NormalizeCommandLineArgument)); } -- cgit v1.2.3 From 253e72f66720c2f590c443f8347e2187f4f36db0 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 10 Jun 2019 00:53:16 +0200 Subject: Simplify file serving code --- Emby.Server.Implementations/ApplicationHost.cs | 7 +- Emby.Server.Implementations/ResourceFileManager.cs | 30 +---- Jellyfin.Server/CoreAppHost.cs | 7 +- MediaBrowser.Controller/IResourceFileManager.cs | 11 +- MediaBrowser.WebDashboard/Api/DashboardService.cs | 13 +- MediaBrowser.WebDashboard/Api/PackageCreator.cs | 148 ++++++++------------- 6 files changed, 67 insertions(+), 149 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 53bc85b28..a4e54af2c 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -231,11 +231,6 @@ namespace Emby.Server.Implementations /// The server configuration manager. public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager; - protected virtual IResourceFileManager CreateResourceFileManager() - { - return new ResourceFileManager(HttpResultFactory, LoggerFactory, FileSystemManager); - } - /// /// Gets or sets the user manager. /// @@ -886,7 +881,7 @@ namespace Emby.Server.Implementations SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(LibraryManager, LoggerFactory, ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, ProcessFactory); serviceCollection.AddSingleton(SubtitleEncoder); - serviceCollection.AddSingleton(CreateResourceFileManager()); + serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager)); displayPreferencesRepo.Initialize(); diff --git a/Emby.Server.Implementations/ResourceFileManager.cs b/Emby.Server.Implementations/ResourceFileManager.cs index 890d848f4..6eda2b503 100644 --- a/Emby.Server.Implementations/ResourceFileManager.cs +++ b/Emby.Server.Implementations/ResourceFileManager.cs @@ -1,10 +1,8 @@ using System; using System.IO; -using System.Threading.Tasks; using MediaBrowser.Controller; using MediaBrowser.Controller.Net; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations @@ -13,34 +11,14 @@ namespace Emby.Server.Implementations { private readonly IFileSystem _fileSystem; private readonly ILogger _logger; - private readonly IHttpResultFactory _resultFactory; - public ResourceFileManager( - IHttpResultFactory resultFactory, - ILoggerFactory loggerFactory, - IFileSystem fileSystem) + public ResourceFileManager(ILogger logger, IFileSystem fileSystem) { - _resultFactory = resultFactory; - _logger = loggerFactory.CreateLogger("ResourceManager"); + _logger = logger; _fileSystem = fileSystem; } - public Stream GetResourceFileStream(string basePath, string virtualPath) - { - return _fileSystem.GetFileStream(GetResourcePath(basePath, virtualPath), FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true); - } - - public Task GetStaticFileResult(IRequest request, string basePath, string virtualPath, string contentType, TimeSpan? cacheDuration) - { - return _resultFactory.GetStaticFileResult(request, GetResourcePath(basePath, virtualPath)); - } - - public string ReadAllText(string basePath, string virtualPath) - { - return File.ReadAllText(GetResourcePath(basePath, virtualPath)); - } - - private string GetResourcePath(string basePath, string virtualPath) + public string GetResourcePath(string basePath, string virtualPath) { var fullPath = Path.Combine(basePath, virtualPath.Replace('/', Path.DirectorySeparatorChar)); @@ -50,7 +28,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - _logger.LogError(ex, "Error in Path.GetFullPath"); + _logger.LogError(ex, "Error retrieving full path"); } // Don't allow file system access outside of the source folder diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 8e6ed7a7e..b9b0cc382 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; using System.Reflection; using Emby.Server.Implementations; -using Emby.Server.Implementations.HttpServer; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Drawing; using MediaBrowser.Model.IO; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -15,8 +16,8 @@ namespace Jellyfin.Server ILoggerFactory loggerFactory, StartupOptions options, IFileSystem fileSystem, - MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, - MediaBrowser.Common.Net.INetworkManager networkManager, + IImageEncoder imageEncoder, + INetworkManager networkManager, IConfiguration configuration) : base( applicationPaths, diff --git a/MediaBrowser.Controller/IResourceFileManager.cs b/MediaBrowser.Controller/IResourceFileManager.cs index f70ea6a17..69a51cec8 100644 --- a/MediaBrowser.Controller/IResourceFileManager.cs +++ b/MediaBrowser.Controller/IResourceFileManager.cs @@ -1,16 +1,7 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using MediaBrowser.Model.Services; - namespace MediaBrowser.Controller { public interface IResourceFileManager { - Task GetStaticFileResult(IRequest request, string basePath, string virtualPath, string contentType, TimeSpan? cacheDuration); - - Stream GetResourceFileStream(string basePath, string virtualPath); - - string ReadAllText(string basePath, string virtualPath); + string GetResourcePath(string basePath, string virtualPath); } } diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 58ab2d27b..d2ffd5efc 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -114,8 +114,6 @@ namespace MediaBrowser.WebDashboard.Api private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IFileSystem _fileSystem; - private readonly ILocalizationManager _localization; - private readonly IJsonSerializer _jsonSerializer; private IResourceFileManager _resourceFileManager; /// @@ -126,16 +124,12 @@ namespace MediaBrowser.WebDashboard.Api IResourceFileManager resourceFileManager, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem, - ILocalizationManager localization, - IJsonSerializer jsonSerializer, ILogger logger, IHttpResultFactory resultFactory) { _appHost = appHost; _serverConfigurationManager = serverConfigurationManager; _fileSystem = fileSystem; - _localization = localization; - _jsonSerializer = jsonSerializer; _logger = logger; _resultFactory = resultFactory; _resourceFileManager = resourceFileManager; @@ -205,6 +199,7 @@ namespace MediaBrowser.WebDashboard.Api { return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.js"), () => Task.FromResult(stream)); } + if (isTemplate) { return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => Task.FromResult(stream)); @@ -316,7 +311,7 @@ namespace MediaBrowser.WebDashboard.Api // Bounce them to the startup wizard if it hasn't been completed yet if (!_serverConfigurationManager.Configuration.IsStartupWizardCompleted && Request.RawUrl.IndexOf("wizard", StringComparison.OrdinalIgnoreCase) == -1 && - GetPackageCreator(basePath).IsCoreHtml(path)) + PackageCreator.IsCoreHtml(path)) { // But don't redirect if an html import is being requested. if (path.IndexOf("bower_components", StringComparison.OrdinalIgnoreCase) == -1) @@ -355,7 +350,7 @@ namespace MediaBrowser.WebDashboard.Api return await _resultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(basePath, path, localizationCulture)).ConfigureAwait(false); } - return await _resourceFileManager.GetStaticFileResult(Request, basePath, path, contentType, cacheDuration); + return await _resultFactory.GetStaticFileResult(Request, _resourceFileManager.GetResourcePath(basePath, path)); } private string GetLocalizationCulture() @@ -374,7 +369,7 @@ namespace MediaBrowser.WebDashboard.Api private PackageCreator GetPackageCreator(string basePath) { - return new PackageCreator(basePath, _fileSystem, _logger, _serverConfigurationManager, _resourceFileManager); + return new PackageCreator(basePath, _resourceFileManager); } public async Task Get(GetDashboardPackage request) diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 2d0e0e188..133bf61e8 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -1,139 +1,108 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; using System.Threading.Tasks; using MediaBrowser.Controller; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Model.IO; -using Microsoft.Extensions.Logging; namespace MediaBrowser.WebDashboard.Api { public class PackageCreator { - private readonly IFileSystem _fileSystem; - private readonly ILogger _logger; - private readonly IServerConfigurationManager _config; private readonly string _basePath; - private IResourceFileManager _resourceFileManager; + private readonly IResourceFileManager _resourceFileManager; - public PackageCreator(string basePath, IFileSystem fileSystem, ILogger logger, IServerConfigurationManager config, IResourceFileManager resourceFileManager) + public PackageCreator(string basePath, IResourceFileManager resourceFileManager) { - _fileSystem = fileSystem; - _logger = logger; - _config = config; _basePath = basePath; _resourceFileManager = resourceFileManager; } - public async Task GetResource(string virtualPath, + public async Task GetResource( + string virtualPath, string mode, string localizationCulture, string appVersion) { - var resourceStream = GetRawResourceStream(virtualPath); + var resourcePath = _resourceFileManager.GetResourcePath(_basePath, virtualPath); + Stream resourceStream = File.OpenRead(resourcePath); - if (resourceStream != null) + if (resourceStream != null && IsCoreHtml(virtualPath)) { - if (IsFormat(virtualPath, "html")) - { - if (IsCoreHtml(virtualPath)) - { - resourceStream = await ModifyHtml(virtualPath, resourceStream, mode, appVersion, localizationCulture).ConfigureAwait(false); - } - } + resourceStream = await ModifyHtml(virtualPath, resourceStream, mode, appVersion, localizationCulture).ConfigureAwait(false); } return resourceStream; } - /// - /// Determines whether the specified path is HTML. - /// - /// The path. - /// The format. - /// true if the specified path is HTML; otherwise, false. - private static bool IsFormat(string path, string format) - { - return Path.GetExtension(path).EndsWith(format, StringComparison.OrdinalIgnoreCase); - } - - public bool IsCoreHtml(string path) + public static bool IsCoreHtml(string path) { if (path.IndexOf(".template.html", StringComparison.OrdinalIgnoreCase) != -1) { return false; } - return IsFormat(path, "html"); + return string.Equals(Path.GetExtension(path), ".html", StringComparison.OrdinalIgnoreCase); } /// /// Modifies the HTML by adding common meta tags, css and js. /// /// Task{Stream}. - public async Task ModifyHtml(string path, Stream sourceStream, string mode, string appVersion, string localizationCulture) + public async Task ModifyHtml( + string path, + Stream sourceStream, + string mode, + string appVersion, + string localizationCulture) { var isMainIndexPage = string.Equals(path, "index.html", StringComparison.OrdinalIgnoreCase); - using (sourceStream) + string html; + using (var reader = new StreamReader(sourceStream, Encoding.UTF8)) { - string html; - - using (var memoryStream = new MemoryStream()) - { - await sourceStream.CopyToAsync(memoryStream).ConfigureAwait(false); - - var originalBytes = memoryStream.ToArray(); + html = await reader.ReadToEndAsync().ConfigureAwait(false); + } - html = Encoding.UTF8.GetString(originalBytes, 0, originalBytes.Length); + if (isMainIndexPage && !string.IsNullOrWhiteSpace(localizationCulture)) + { + var lang = localizationCulture.Split('-')[0]; - if (isMainIndexPage) - { - if (!string.IsNullOrWhiteSpace(localizationCulture)) - { - var lang = localizationCulture.Split('-').FirstOrDefault(); + html = html.Replace("", "" + GetMetaTags(mode)); + } - if (isMainIndexPage) - { - html = html.Replace("", "" + GetMetaTags(mode)); - } + // Disable embedded scripts from plugins. We'll run them later once resources have loaded + if (html.IndexOf("", "-->"); + } - // Disable embedded scripts from plugins. We'll run them later once resources have loaded - if (html.IndexOf("", "-->"); - } + if (isMainIndexPage) + { + html = html.Replace("", GetCommonJavascript(mode, appVersion) + ""); + } - if (isMainIndexPage) - { - html = html.Replace("", GetCommonJavascript(mode, appVersion) + ""); - } + var bytes = Encoding.UTF8.GetBytes(html); - var bytes = Encoding.UTF8.GetBytes(html); + return new MemoryStream(bytes); - return new MemoryStream(bytes); - } } /// /// Gets the meta tags. /// /// System.String. - private string GetMetaTags(string mode) + private static string GetMetaTags(string mode) { var sb = new StringBuilder(); - if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase) || - string.Equals(mode, "android", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase) + || string.Equals(mode, "android", StringComparison.OrdinalIgnoreCase)) { sb.Append(""); } @@ -147,7 +116,7 @@ namespace MediaBrowser.WebDashboard.Api /// The mode. /// The version. /// System.String. - private string GetCommonJavascript(string mode, string version) + private static string GetCommonJavascript(string mode, string version) { var builder = new StringBuilder(); @@ -156,7 +125,6 @@ namespace MediaBrowser.WebDashboard.Api { builder.AppendFormat("window.appMode='{0}';", mode); } - else { builder.AppendFormat("window.dashboardVersion='{0}';", version); @@ -164,31 +132,21 @@ namespace MediaBrowser.WebDashboard.Api builder.Append(""); - var versionString = string.IsNullOrWhiteSpace(mode) ? "?v=" + version : string.Empty; - - var files = new List(); - - files.Add("scripts/apploader.js" + versionString); - if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase)) { - files.Insert(0, "cordova.js"); + builder.Append(""); } - var tags = files.Select(s => string.Format("", s)).ToArray(); + builder.Append(""); return builder.ToString(); } - - /// - /// Gets the raw resource stream. - /// - private Stream GetRawResourceStream(string virtualPath) - { - return _resourceFileManager.GetResourceFileStream(_basePath, virtualPath); - } - } } -- cgit v1.2.3 From b136f1408481661df7666652e75625e877873299 Mon Sep 17 00:00:00 2001 From: ferferga Date: Mon, 10 Jun 2019 11:31:38 +0200 Subject: Vacuum databases at startup --- Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index fba81306b..821c4b448 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -203,6 +203,7 @@ namespace Emby.Server.Implementations.Data { var queries = new List { + "VACUUM", "PRAGMA journal_mode=WAL", "PRAGMA page_size=4096", "PRAGMA synchronous=Normal" -- cgit v1.2.3 From 6ebac0e5002ee79ac4acc5ac2114384436ea0f9a Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 14 Jun 2019 08:20:52 +0200 Subject: Update Emby.Server.Implementations/ApplicationHost.cs Co-Authored-By: Jean-Samuel Aubry-Guzzi --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 4d5fc585d..4fc2d0d9c 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1643,7 +1643,7 @@ namespace Emby.Server.Implementations { Url = apiUrl, LogErrorResponseBody = false, - LogErrors = logPing, + LogErrors = LogPing, LogRequest = logPing, BufferContent = false, -- cgit v1.2.3 From af099a9b53616fdc38392c86236d00413cdc14c5 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 14 Jun 2019 08:21:06 +0200 Subject: Update Emby.Server.Implementations/ApplicationHost.cs Co-Authored-By: Jean-Samuel Aubry-Guzzi --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 4fc2d0d9c..f35b322a2 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1644,7 +1644,7 @@ namespace Emby.Server.Implementations Url = apiUrl, LogErrorResponseBody = false, LogErrors = LogPing, - LogRequest = logPing, + LogRequest = LogPing, BufferContent = false, CancellationToken = cancellationToken -- cgit v1.2.3 From 65a0ca2f32e2eba640fbfead46ffb5bfd02e2c88 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 14 Jun 2019 18:38:14 +0200 Subject: Improvements to InstallationManager --- .../Activity/ActivityLogEntryPoint.cs | 2 +- .../Emby.Server.Implementations.csproj | 5 + .../ScheduledTasks/Tasks/PluginUpdateTask.cs | 21 +-- .../Updates/InstallationManager.cs | 177 +++++++++------------ MediaBrowser.Api/PackageService.cs | 10 +- .../Updates/IInstallationManager.cs | 16 +- MediaBrowser.Model/Updates/PackageVersionInfo.cs | 22 +-- MediaBrowser.Providers/Omdb/OmdbProvider.cs | 3 +- 8 files changed, 110 insertions(+), 146 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index 0530a251c..cd3af9064 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -346,7 +346,7 @@ namespace Emby.Server.Implementations.Activity }); } - private void OnPluginUpdated(object sender, GenericEventArgs> e) + private void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, PackageVersionInfo)> e) { CreateLogEntry(new ActivityLogEntry { diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index d4e17c42a..6c50698b2 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -46,6 +46,11 @@ false + + + latest + + true diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs index c6431c311..bde7d5c81 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs @@ -1,4 +1,3 @@ -using MediaBrowser.Common; using MediaBrowser.Common.Updates; using MediaBrowser.Model.Net; using System; @@ -25,13 +24,10 @@ namespace Emby.Server.Implementations.ScheduledTasks private readonly IInstallationManager _installationManager; - private readonly IApplicationHost _appHost; - - public PluginUpdateTask(ILogger logger, IInstallationManager installationManager, IApplicationHost appHost) + public PluginUpdateTask(ILogger logger, IInstallationManager installationManager) { _logger = logger; _installationManager = installationManager; - _appHost = appHost; } /// @@ -40,14 +36,11 @@ namespace Emby.Server.Implementations.ScheduledTasks /// IEnumerable{BaseTaskTrigger}. public IEnumerable GetDefaultTriggers() { - return new[] { - - // At startup - new TaskTriggerInfo {Type = TaskTriggerInfo.TriggerStartup}, - - // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks} - }; + // At startup + yield return new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerStartup }; + + // Every so often + yield return new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks }; } /// @@ -72,7 +65,7 @@ namespace Emby.Server.Implementations.ScheduledTasks try { - await _installationManager.InstallPackage(package, true, new SimpleProgress(), cancellationToken).ConfigureAwait(false); + await _installationManager.InstallPackage(package, new SimpleProgress(), cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 6833c20c3..b3d76124b 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; @@ -33,7 +34,7 @@ namespace Emby.Server.Implementations.Updates /// /// The current installations /// - public List> CurrentInstallations { get; set; } + private List<(InstallationInfo info, CancellationTokenSource token)> _currentInstallations { get; set; } /// /// The completed installations @@ -47,49 +48,15 @@ namespace Emby.Server.Implementations.Updates /// public event EventHandler> PluginUninstalled; - /// - /// Called when [plugin uninstalled]. - /// - /// The plugin. - private void OnPluginUninstalled(IPlugin plugin) - { - PluginUninstalled?.Invoke(this, new GenericEventArgs { Argument = plugin }); - } - /// /// Occurs when [plugin updated]. /// - public event EventHandler>> PluginUpdated; - /// - /// Called when [plugin updated]. - /// - /// The plugin. - /// The new version. - private void OnPluginUpdated(IPlugin plugin, PackageVersionInfo newVersion) - { - _logger.LogInformation("Plugin updated: {0} {1} {2}", newVersion.name, newVersion.versionStr ?? string.Empty, newVersion.classification); - - PluginUpdated?.Invoke(this, new GenericEventArgs> { Argument = new Tuple(plugin, newVersion) }); - - _applicationHost.NotifyPendingRestart(); - } + public event EventHandler> PluginUpdated; /// /// Occurs when [plugin updated]. /// public event EventHandler> PluginInstalled; - /// - /// Called when [plugin installed]. - /// - /// The package. - private void OnPluginInstalled(PackageVersionInfo package) - { - _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification); - - PluginInstalled?.Invoke(this, new GenericEventArgs { Argument = package }); - - _applicationHost.NotifyPendingRestart(); - } /// /// The _logger @@ -111,7 +78,7 @@ namespace Emby.Server.Implementations.Updates private readonly IZipClient _zipClient; public InstallationManager( - ILoggerFactory loggerFactory, + ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, @@ -120,15 +87,15 @@ namespace Emby.Server.Implementations.Updates IFileSystem fileSystem, IZipClient zipClient) { - if (loggerFactory == null) + if (logger == null) { - throw new ArgumentNullException(nameof(loggerFactory)); + throw new ArgumentNullException(nameof(logger)); } - CurrentInstallations = new List>(); + _currentInstallations = new List<(InstallationInfo, CancellationTokenSource)>(); _completedInstallationsInternal = new ConcurrentBag(); - _logger = loggerFactory.CreateLogger(nameof(InstallationManager)); + _logger = logger; _applicationHost = appHost; _appPaths = appPaths; _httpClient = httpClient; @@ -138,21 +105,12 @@ namespace Emby.Server.Implementations.Updates _zipClient = zipClient; } - private static Version GetPackageVersion(PackageVersionInfo version) - { - return new Version(ValueOrDefault(version.versionStr, "0.0.0.1")); - } - - private static string ValueOrDefault(string str, string def) - { - return string.IsNullOrEmpty(str) ? def : str; - } - /// /// Gets all available packages. /// /// Task{List{PackageInfo}}. - public async Task> GetAvailablePackages(CancellationToken cancellationToken, + public async Task> GetAvailablePackages( + CancellationToken cancellationToken, bool withRegistration = true, string packageType = null, Version applicationVersion = null) @@ -172,22 +130,14 @@ namespace Emby.Server.Implementations.Updates { Url = "https://repo.jellyfin.org/releases/plugin/manifest.json", CancellationToken = cancellationToken, - Progress = new SimpleProgress(), CacheLength = GetCacheLength() - }, "GET").ConfigureAwait(false)) + }, HttpMethod.Get).ConfigureAwait(false)) + using (var stream = response.Content) { - using (var stream = response.Content) - { - return FilterPackages(await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false)); - } + return FilterPackages(await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false)); } } - private PackageVersionClass GetSystemUpdateLevel() - { - return _applicationHost.SystemUpdateLevel; - } - private static TimeSpan GetCacheLength() { return TimeSpan.FromMinutes(3); @@ -211,7 +161,7 @@ namespace Emby.Server.Implementations.Updates } package.versions = versions - .OrderByDescending(GetPackageVersion) + .OrderByDescending(x => x.Version) .ToArray(); if (package.versions.Length == 0) @@ -294,7 +244,7 @@ namespace Emby.Server.Implementations.Updates return null; } - return package.versions.FirstOrDefault(v => GetPackageVersion(v).Equals(version) && v.classification == classification); + return package.versions.FirstOrDefault(v => v.Version == version && v.classification == classification); } /// @@ -331,7 +281,7 @@ namespace Emby.Server.Implementations.Updates } return package.versions - .OrderByDescending(GetPackageVersion) + .OrderByDescending(x => x.Version) .FirstOrDefault(v => v.classification <= classification && IsPackageVersionUpToDate(v, currentServerVersion)); } @@ -346,14 +296,14 @@ namespace Emby.Server.Implementations.Updates { var catalog = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false); - var systemUpdateLevel = GetSystemUpdateLevel(); + var systemUpdateLevel = _applicationHost.SystemUpdateLevel; // Figure out what needs to be installed return _applicationHost.Plugins.Select(p => { var latestPluginInfo = GetLatestCompatibleVersion(catalog, p.Name, p.Id.ToString(), applicationVersion, systemUpdateLevel); - return latestPluginInfo != null && GetPackageVersion(latestPluginInfo) > p.Version ? latestPluginInfo : null; + return latestPluginInfo != null && latestPluginInfo.Version > p.Version ? latestPluginInfo : null; }).Where(i => i != null) .Where(p => !string.IsNullOrEmpty(p.sourceUrl) && !CompletedInstallations.Any(i => string.Equals(i.AssemblyGuid, p.guid, StringComparison.OrdinalIgnoreCase))); @@ -368,7 +318,7 @@ namespace Emby.Server.Implementations.Updates /// The cancellation token. /// Task. /// package - public async Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress progress, CancellationToken cancellationToken) + public async Task InstallPackage(PackageVersionInfo package, IProgress progress, CancellationToken cancellationToken) { if (package == null) { @@ -391,12 +341,12 @@ namespace Emby.Server.Implementations.Updates var innerCancellationTokenSource = new CancellationTokenSource(); - var tuple = new Tuple(installationInfo, innerCancellationTokenSource); + var tuple = (installationInfo, innerCancellationTokenSource); // Add it to the in-progress list - lock (CurrentInstallations) + lock (_currentInstallations) { - CurrentInstallations.Add(tuple); + _currentInstallations.Add(tuple); } var innerProgress = new ActionableProgress(); @@ -421,11 +371,11 @@ namespace Emby.Server.Implementations.Updates try { - await InstallPackageInternal(package, isPlugin, innerProgress, linkedToken).ConfigureAwait(false); + await InstallPackageInternal(package, innerProgress, linkedToken).ConfigureAwait(false); - lock (CurrentInstallations) + lock (_currentInstallations) { - CurrentInstallations.Remove(tuple); + _currentInstallations.Remove(tuple); } _completedInstallationsInternal.Add(installationInfo); @@ -434,9 +384,9 @@ namespace Emby.Server.Implementations.Updates } catch (OperationCanceledException) { - lock (CurrentInstallations) + lock (_currentInstallations) { - CurrentInstallations.Remove(tuple); + _currentInstallations.Remove(tuple); } _logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.versionStr); @@ -449,9 +399,9 @@ namespace Emby.Server.Implementations.Updates { _logger.LogError(ex, "Package installation failed"); - lock (CurrentInstallations) + lock (_currentInstallations) { - CurrentInstallations.Remove(tuple); + _currentInstallations.Remove(tuple); } PackageInstallationFailed?.Invoke(this, new InstallationFailedEventArgs @@ -477,16 +427,12 @@ namespace Emby.Server.Implementations.Updates /// The progress. /// The cancellation token. /// Task. - private async Task InstallPackageInternal(PackageVersionInfo package, bool isPlugin, IProgress progress, CancellationToken cancellationToken) + private async Task InstallPackageInternal(PackageVersionInfo package, IProgress progress, CancellationToken cancellationToken) { - IPlugin plugin = null; - - if (isPlugin) - { - // Set last update time if we were installed before - plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase)) + // Set last update time if we were installed before + IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase)) ?? _applicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase)); - } + string targetPath = plugin == null ? null : plugin.AssemblyFilePath; @@ -494,17 +440,20 @@ namespace Emby.Server.Implementations.Updates await PerformPackageInstallation(progress, targetPath, package, cancellationToken).ConfigureAwait(false); // Do plugin-specific processing - if (isPlugin) + if (plugin == null) { - if (plugin == null) - { - OnPluginInstalled(package); - } - else - { - OnPluginUpdated(plugin, package); - } + _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification); + + PluginInstalled?.Invoke(this, new GenericEventArgs(package)); } + else + { + _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification); + + PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, PackageVersionInfo)>((plugin, package))); + } + + _applicationHost.NotifyPendingRestart(); } private async Task PerformPackageInstallation(IProgress progress, string target, PackageVersionInfo package, CancellationToken cancellationToken) @@ -622,11 +571,34 @@ namespace Emby.Server.Implementations.Updates _config.SaveConfiguration(); } - OnPluginUninstalled(plugin); + PluginUninstalled?.Invoke(this, new GenericEventArgs { Argument = plugin }); _applicationHost.NotifyPendingRestart(); } + /// + public bool CancelInstallation(Guid id) + { + lock (_currentInstallations) + { + var install = _currentInstallations.Find(x => x.Item1.Id == id); + if (install == default((InstallationInfo, CancellationTokenSource))) + { + return false; + } + + install.Item2.Cancel(); + _currentInstallations.Remove(install); + return true; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + /// /// Releases unmanaged and - optionally - managed resources. /// @@ -635,21 +607,16 @@ namespace Emby.Server.Implementations.Updates { if (dispose) { - lock (CurrentInstallations) + lock (_currentInstallations) { - foreach (var tuple in CurrentInstallations) + foreach (var tuple in _currentInstallations) { tuple.Item2.Dispose(); } - CurrentInstallations.Clear(); + _currentInstallations.Clear(); } } } - - public void Dispose() - { - Dispose(true); - } } } diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs index fbb876dea..cf1e08d53 100644 --- a/MediaBrowser.Api/PackageService.cs +++ b/MediaBrowser.Api/PackageService.cs @@ -197,7 +197,7 @@ namespace MediaBrowser.Api throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name)); } - await _installationManager.InstallPackage(package, true, new SimpleProgress(), CancellationToken.None); + await _installationManager.InstallPackage(package, new SimpleProgress(), CancellationToken.None); } /// @@ -206,13 +206,7 @@ namespace MediaBrowser.Api /// The request. public void Delete(CancelPackageInstallation request) { - var info = _installationManager.CurrentInstallations.FirstOrDefault(i => i.Item1.Id.Equals(request.Id)); - - if (info != null) - { - info.Item2.Cancel(); - } + _installationManager.CancelInstallation(new Guid(request.Id)); } } - } diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs index a263be35f..3472a5692 100644 --- a/MediaBrowser.Common/Updates/IInstallationManager.cs +++ b/MediaBrowser.Common/Updates/IInstallationManager.cs @@ -15,11 +15,6 @@ namespace MediaBrowser.Common.Updates event EventHandler PackageInstallationFailed; event EventHandler PackageInstallationCancelled; - /// - /// The current installations - /// - List> CurrentInstallations { get; set; } - /// /// The completed installations /// @@ -33,7 +28,7 @@ namespace MediaBrowser.Common.Updates /// /// Occurs when [plugin updated]. /// - event EventHandler>> PluginUpdated; + event EventHandler> PluginUpdated; /// /// Occurs when [plugin updated]. @@ -107,7 +102,7 @@ namespace MediaBrowser.Common.Updates /// The cancellation token. /// Task. /// package - Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress progress, CancellationToken cancellationToken); + Task InstallPackage(PackageVersionInfo package, IProgress progress, CancellationToken cancellationToken); /// /// Uninstalls a plugin @@ -115,5 +110,12 @@ namespace MediaBrowser.Common.Updates /// The plugin. /// void UninstallPlugin(IPlugin plugin); + + /// + /// Cancels the installation + /// + /// The id of the package that is being installed + /// Returns true if the install was cancelled + bool CancelInstallation(Guid id); } } diff --git a/MediaBrowser.Model/Updates/PackageVersionInfo.cs b/MediaBrowser.Model/Updates/PackageVersionInfo.cs index be531770d..7ef07c0df 100644 --- a/MediaBrowser.Model/Updates/PackageVersionInfo.cs +++ b/MediaBrowser.Model/Updates/PackageVersionInfo.cs @@ -30,23 +30,25 @@ namespace MediaBrowser.Model.Updates /// The _version /// private Version _version; + /// /// Gets or sets the version. /// Had to make this an interpreted property since Protobuf can't handle Version /// /// The version. [IgnoreDataMember] - public Version version => _version ?? (_version = new Version(ValueOrDefault(versionStr, "0.0.0.1"))); - - /// - /// Values the or default. - /// - /// The STR. - /// The def. - /// System.String. - private static string ValueOrDefault(string str, string def) + public Version Version { - return string.IsNullOrEmpty(str) ? def : str; + get + { + if (_version == null) + { + var ver = versionStr; + _version = new Version(string.IsNullOrEmpty(ver) ? "0.0.0.1" : ver); + } + + return _version; + } } /// diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index 19dce34d6..f8b876580 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -347,7 +348,7 @@ namespace MediaBrowser.Providers.Omdb CancellationToken = cancellationToken, BufferContent = true, EnableDefaultUserAgent = true - }, "GET"); + }, HttpMethod.Get); } internal string GetDataFilePath(string imdbId) -- cgit v1.2.3 From 555459525515edf9f506465cefce0c6a6aca326d Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 14 Jun 2019 18:49:57 +0200 Subject: Update deps --- Emby.Naming/Emby.Naming.csproj | 2 +- .../Emby.Server.Implementations.csproj | 7 +++---- Jellyfin.Server/Jellyfin.Server.csproj | 14 +++++++------- .../MediaBrowser.MediaEncoding.csproj | 2 +- MediaBrowser.Providers/MediaBrowser.Providers.csproj | 2 +- jellyfin.ruleset | 4 ++++ 6 files changed, 17 insertions(+), 14 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 6e05eb795..9e2a4950f 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -23,7 +23,7 @@ - + diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index d4e17c42a..49015a07e 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -31,10 +31,9 @@ - - + + - @@ -52,7 +51,7 @@ - + diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 81f145abf..641b3f182 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -26,7 +26,7 @@ - + @@ -36,17 +36,17 @@ - - + + - - + + - - + + diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index e4757543e..c0f92ac4a 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -18,7 +18,7 @@ - + diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index cfbb85ea6..5941ed436 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -14,7 +14,7 @@ - + diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 0a60c8c7a..1249a60c0 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -25,10 +25,14 @@ + + + + -- cgit v1.2.3 From 067200be83ff220c0aae501d138b0ab284370b60 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 8 Mar 2019 20:17:17 +0100 Subject: Remove usage of depricated 'WebRequest' Ref: https://docs.microsoft.com/en-us/dotnet/api/system.net.webrequest?view=netframework-4.7.2 --- .../HttpClientManager/HttpClientInfo.cs | 18 - .../HttpClientManager/HttpClientManager.cs | 432 +++++++-------------- MediaBrowser.Common/Net/HttpRequestOptions.cs | 1 - MediaBrowser.Common/Net/IHttpClient.cs | 11 + 4 files changed, 161 insertions(+), 301 deletions(-) delete mode 100644 Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs deleted file mode 100644 index f747b01b9..000000000 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Net.Http; - -namespace Emby.Server.Implementations.HttpClientManager -{ - /// - /// Class HttpClientInfo - /// - public class HttpClientInfo - { - /// - /// Gets or sets the last timeout. - /// - /// The last timeout. - public DateTime LastTimeout { get; set; } - public HttpClient HttpClient { get; set; } - } -} diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 1bebdd163..581d6bafd 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Net; -using System.Net.Cache; +using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -55,12 +54,13 @@ namespace Emby.Server.Implementations.HttpClientManager { throw new ArgumentNullException(nameof(appPaths)); } + if (loggerFactory == null) { throw new ArgumentNullException(nameof(loggerFactory)); } - _logger = loggerFactory.CreateLogger("HttpClient"); + _logger = loggerFactory.CreateLogger(nameof(HttpClientManager)); _fileSystem = fileSystem; _appPaths = appPaths; _defaultUserAgentFn = defaultUserAgentFn; @@ -74,27 +74,26 @@ namespace Emby.Server.Implementations.HttpClientManager /// DON'T dispose it after use. /// /// The HTTP clients. - private readonly ConcurrentDictionary _httpClients = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _httpClients = new ConcurrentDictionary(); /// /// Gets /// - /// The host. + /// The host. /// if set to true [enable HTTP compression]. /// HttpClient. /// host - private HttpClientInfo GetHttpClient(string host, bool enableHttpCompression) + private HttpClient GetHttpClient(string url, bool enableHttpCompression) { - if (string.IsNullOrEmpty(host)) - { - throw new ArgumentNullException(nameof(host)); - } - - var key = host + enableHttpCompression; + var key = GetHostFromUrl(url) + enableHttpCompression; if (!_httpClients.TryGetValue(key, out var client)) { - client = new HttpClientInfo(); + + client = new HttpClient() + { + BaseAddress = new Uri(url) + }; _httpClients.TryAdd(key, client); } @@ -102,110 +101,87 @@ namespace Emby.Server.Implementations.HttpClientManager return client; } - private WebRequest GetRequest(HttpRequestOptions options, string method) + private HttpRequestMessage GetRequestMessage(HttpRequestOptions options, HttpMethod method) { string url = options.Url; - var uriAddress = new Uri(url); string userInfo = uriAddress.UserInfo; if (!string.IsNullOrWhiteSpace(userInfo)) { - _logger.LogInformation("Found userInfo in url: {0} ... url: {1}", userInfo, url); + _logger.LogWarning("Found userInfo in url: {0} ... url: {1}", userInfo, url); url = url.Replace(userInfo + "@", string.Empty); } - var request = WebRequest.Create(url); + var request = new HttpRequestMessage(method, url); - if (request is HttpWebRequest httpWebRequest) - { - AddRequestHeaders(httpWebRequest, options); + AddRequestHeaders(request, options); - if (options.EnableHttpCompression) + if (options.EnableHttpCompression) + { + if (options.DecompressionMethod.HasValue + && options.DecompressionMethod.Value == CompressionMethod.Gzip) { - httpWebRequest.AutomaticDecompression = DecompressionMethods.Deflate; - if (options.DecompressionMethod.HasValue - && options.DecompressionMethod.Value == CompressionMethod.Gzip) - { - httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip; - } + request.Headers.Add(HeaderNames.AcceptEncoding, new[] { "gzip", "deflate" }); } else { - httpWebRequest.AutomaticDecompression = DecompressionMethods.None; + request.Headers.Add(HeaderNames.AcceptEncoding, "deflate"); } + } - httpWebRequest.KeepAlive = options.EnableKeepAlive; + if (options.EnableKeepAlive) + { + request.Headers.Add(HeaderNames.Connection, "Keep-Alive"); + } - if (!string.IsNullOrEmpty(options.Host)) - { - httpWebRequest.Host = options.Host; - } + if (!string.IsNullOrEmpty(options.Host)) + { + request.Headers.Add(HeaderNames.Host, options.Host); + } - if (!string.IsNullOrEmpty(options.Referer)) - { - httpWebRequest.Referer = options.Referer; - } + if (!string.IsNullOrEmpty(options.Referer)) + { + request.Headers.Add(HeaderNames.Referer, options.Referer); } - request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache); + //request.Headers.Add(HeaderNames.CacheControl, "no-cache"); - request.Method = method; - request.Timeout = options.TimeoutMs; + //request.Headers.Add(HeaderNames., options.TimeoutMs; + /* if (!string.IsNullOrWhiteSpace(userInfo)) { var parts = userInfo.Split(':'); if (parts.Length == 2) { - request.Credentials = GetCredential(url, parts[0], parts[1]); - // TODO: .net core ?? - request.PreAuthenticate = true; + request.Headers.Add(HeaderNames., GetCredential(url, parts[0], parts[1]); } } + */ return request; } - private static CredentialCache GetCredential(string url, string username, string password) - { - //ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3; - var credentialCache = new CredentialCache(); - credentialCache.Add(new Uri(url), "Basic", new NetworkCredential(username, password)); - return credentialCache; - } - - private void AddRequestHeaders(HttpWebRequest request, HttpRequestOptions options) + private void AddRequestHeaders(HttpRequestMessage request, HttpRequestOptions options) { var hasUserAgent = false; foreach (var header in options.RequestHeaders) { - if (string.Equals(header.Key, HeaderNames.Accept, StringComparison.OrdinalIgnoreCase)) - { - request.Accept = header.Value; - } - else if (string.Equals(header.Key, HeaderNames.UserAgent, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(header.Key, HeaderNames.UserAgent, StringComparison.OrdinalIgnoreCase)) { - SetUserAgent(request, header.Value); hasUserAgent = true; } - else - { - request.Headers.Set(header.Key, header.Value); - } + + request.Headers.Add(header.Key, header.Value); } if (!hasUserAgent && options.EnableDefaultUserAgent) { - SetUserAgent(request, _defaultUserAgentFn()); + request.Headers.Add(HeaderNames.UserAgent, _defaultUserAgentFn()); } } - private static void SetUserAgent(HttpWebRequest request, string userAgent) - { - request.UserAgent = userAgent; - } - /// /// Gets the response internal. /// @@ -213,7 +189,7 @@ namespace Emby.Server.Implementations.HttpClientManager /// Task{HttpResponseInfo}. public Task GetResponse(HttpRequestOptions options) { - return SendAsync(options, "GET"); + return SendAsync(options, HttpMethod.Get); } /// @@ -235,7 +211,21 @@ namespace Emby.Server.Implementations.HttpClientManager /// Task{HttpResponseInfo}. /// /// - public async Task SendAsync(HttpRequestOptions options, string httpMethod) + public Task SendAsync(HttpRequestOptions options, string httpMethod) + { + var httpMethod2 = GetHttpMethod(httpMethod); + return SendAsync(options, httpMethod2); + } + + /// + /// send as an asynchronous operation. + /// + /// The options. + /// The HTTP method. + /// Task{HttpResponseInfo}. + /// + /// + public async Task SendAsync(HttpRequestOptions options, HttpMethod httpMethod) { if (options.CacheMode == CacheMode.None) { @@ -263,6 +253,40 @@ namespace Emby.Server.Implementations.HttpClientManager return response; } + private HttpMethod GetHttpMethod(string httpMethod) + { + if (httpMethod.Equals("DELETE", StringComparison.OrdinalIgnoreCase)) + { + return HttpMethod.Delete; + } + else if (httpMethod.Equals("GET", StringComparison.OrdinalIgnoreCase)) + { + return HttpMethod.Get; + } + else if (httpMethod.Equals("HEAD", StringComparison.OrdinalIgnoreCase)) + { + return HttpMethod.Head; + } + else if (httpMethod.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase)) + { + return HttpMethod.Options; + } + else if (httpMethod.Equals("POST", StringComparison.OrdinalIgnoreCase)) + { + return HttpMethod.Post; + } + else if (httpMethod.Equals("PUT", StringComparison.OrdinalIgnoreCase)) + { + return HttpMethod.Put; + } + else if (httpMethod.Equals("TRACE", StringComparison.OrdinalIgnoreCase)) + { + return HttpMethod.Trace; + } + + throw new ArgumentException("Invalid HTTP method", nameof(httpMethod)); + } + private HttpResponseInfo GetCachedResponse(string responseCachePath, TimeSpan cacheLength, string url) { if (File.Exists(responseCachePath) @@ -294,31 +318,23 @@ namespace Emby.Server.Implementations.HttpClientManager } } - private async Task SendAsyncInternal(HttpRequestOptions options, string httpMethod) + private async Task SendAsyncInternal(HttpRequestOptions options, HttpMethod httpMethod) { ValidateParams(options); options.CancellationToken.ThrowIfCancellationRequested(); - var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression); - - if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < TimeoutSeconds) - { - throw new HttpException(string.Format("Cancelling connection to {0} due to a previous timeout.", options.Url)) - { - IsTimedOut = true - }; - } + var client = GetHttpClient(options.Url, options.EnableHttpCompression); - var httpWebRequest = GetRequest(options, httpMethod); + var httpWebRequest = GetRequestMessage(options, httpMethod); if (options.RequestContentBytes != null || !string.IsNullOrEmpty(options.RequestContent) || - string.Equals(httpMethod, "post", StringComparison.OrdinalIgnoreCase)) + httpMethod == HttpMethod.Post) { try { - var bytes = options.RequestContentBytes ?? Encoding.UTF8.GetBytes(options.RequestContent ?? string.Empty); + httpWebRequest.Content = new StringContent(Encoding.UTF8.GetString(options.RequestContentBytes) ?? options.RequestContent ?? string.Empty); var contentType = options.RequestContentType ?? "application/x-www-form-urlencoded"; @@ -327,8 +343,8 @@ namespace Emby.Server.Implementations.HttpClientManager contentType = contentType.TrimEnd(';') + "; charset=\"utf-8\""; } - httpWebRequest.ContentType = contentType; - (await httpWebRequest.GetRequestStreamAsync().ConfigureAwait(false)).Write(bytes, 0, bytes.Length); + httpWebRequest.Headers.Add(HeaderNames.ContentType, contentType); + await client.SendAsync(httpWebRequest).ConfigureAwait(false); } catch (Exception ex) { @@ -341,68 +357,45 @@ namespace Emby.Server.Implementations.HttpClientManager await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); } - if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < TimeoutSeconds) - { - options.ResourcePool?.Release(); - - throw new HttpException($"Connection to {options.Url} timed out") { IsTimedOut = true }; - } - if (options.LogRequest) { - if (options.LogRequestAsDebug) - { - _logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToUpper(CultureInfo.CurrentCulture), options.Url); - } - else - { - _logger.LogInformation("HttpClientManager {0}: {1}", httpMethod.ToUpper(CultureInfo.CurrentCulture), options.Url); - } + _logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToString(), options.Url); } try { options.CancellationToken.ThrowIfCancellationRequested(); - if (!options.BufferContent) + /*if (!options.BufferContent) { - var response = await GetResponseAsync(httpWebRequest, TimeSpan.FromMilliseconds(options.TimeoutMs)).ConfigureAwait(false); - - var httpResponse = (HttpWebResponse)response; + var response = await client.HttpClient.SendAsync(httpWebRequest).ConfigureAwait(false); - EnsureSuccessStatusCode(client, httpResponse, options); + await EnsureSuccessStatusCode(client, response, options).ConfigureAwait(false); options.CancellationToken.ThrowIfCancellationRequested(); - return GetResponseInfo(httpResponse, httpResponse.GetResponseStream(), GetContentLength(httpResponse), httpResponse); - } + return GetResponseInfo(response, await response.Content.ReadAsStreamAsync().ConfigureAwait(false), response.Content.Headers.ContentLength, response); + }*/ - using (var response = await GetResponseAsync(httpWebRequest, TimeSpan.FromMilliseconds(options.TimeoutMs)).ConfigureAwait(false)) + using (var response = await client.SendAsync(httpWebRequest).ConfigureAwait(false)) { - var httpResponse = (HttpWebResponse)response; - - EnsureSuccessStatusCode(client, httpResponse, options); + await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); options.CancellationToken.ThrowIfCancellationRequested(); - using (var stream = httpResponse.GetResponseStream()) + using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { var memoryStream = new MemoryStream(); await stream.CopyToAsync(memoryStream).ConfigureAwait(false); - memoryStream.Position = 0; - return GetResponseInfo(httpResponse, memoryStream, memoryStream.Length, null); + return GetResponseInfo(response, memoryStream, memoryStream.Length, null); } } } catch (OperationCanceledException ex) { - throw GetCancellationException(options, client, options.CancellationToken, ex); - } - catch (Exception ex) - { - throw GetException(ex, options, client); + throw GetCancellationException(options, options.CancellationToken, ex); } finally { @@ -410,69 +403,54 @@ namespace Emby.Server.Implementations.HttpClientManager } } - private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, Stream content, long? contentLength, IDisposable disposable) + private HttpResponseInfo GetResponseInfo(HttpResponseMessage httpResponse, Stream content, long? contentLength, IDisposable disposable) { var responseInfo = new HttpResponseInfo(disposable) { Content = content, StatusCode = httpResponse.StatusCode, - ContentType = httpResponse.ContentType, + ContentType = httpResponse.Content.Headers.ContentType?.MediaType, ContentLength = contentLength, - ResponseUrl = httpResponse.ResponseUri.ToString() + ResponseUrl = httpResponse.Content.Headers.ContentLocation?.ToString() }; if (httpResponse.Headers != null) { - SetHeaders(httpResponse.Headers, responseInfo); + SetHeaders(httpResponse.Content.Headers, responseInfo); } return responseInfo; } - private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, string tempFile, long? contentLength) + private HttpResponseInfo GetResponseInfo(HttpResponseMessage httpResponse, string tempFile, long? contentLength) { var responseInfo = new HttpResponseInfo { TempFilePath = tempFile, StatusCode = httpResponse.StatusCode, - ContentType = httpResponse.ContentType, + ContentType = httpResponse.Content.Headers.ContentType?.MediaType, ContentLength = contentLength }; if (httpResponse.Headers != null) { - SetHeaders(httpResponse.Headers, responseInfo); + SetHeaders(httpResponse.Content.Headers, responseInfo); } return responseInfo; } - private static void SetHeaders(WebHeaderCollection headers, HttpResponseInfo responseInfo) + private static void SetHeaders(HttpContentHeaders headers, HttpResponseInfo responseInfo) { - foreach (var key in headers.AllKeys) + foreach (var key in headers) { - responseInfo.Headers[key] = headers[key]; + responseInfo.Headers[key.Key] = string.Join(", ", key.Value); } } public Task Post(HttpRequestOptions options) { - return SendAsync(options, "POST"); - } - - /// - /// Performs a POST request - /// - /// The options. - /// Params to add to the POST data. - /// stream on success, null on failure - public async Task Post(HttpRequestOptions options, Dictionary postData) - { - options.SetPostData(postData); - - var response = await Post(options).ConfigureAwait(false); - - return response.Content; + return SendAsync(options, HttpMethod.Post); } /// @@ -482,9 +460,10 @@ namespace Emby.Server.Implementations.HttpClientManager /// Task{System.String}. public async Task GetTempFile(HttpRequestOptions options) { - var response = await GetTempFileResponse(options).ConfigureAwait(false); - - return response.TempFilePath; + using (var response = await GetTempFileResponse(options).ConfigureAwait(false)) + { + return response.TempFilePath; + } } public async Task GetTempFileResponse(HttpRequestOptions options) @@ -502,7 +481,7 @@ namespace Emby.Server.Implementations.HttpClientManager options.CancellationToken.ThrowIfCancellationRequested(); - var httpWebRequest = GetRequest(options, "GET"); + var httpWebRequest = GetRequestMessage(options, HttpMethod.Get); if (options.ResourcePool != null) { @@ -513,33 +492,22 @@ namespace Emby.Server.Implementations.HttpClientManager if (options.LogRequest) { - if (options.LogRequestAsDebug) - { - _logger.LogDebug("HttpClientManager.GetTempFileResponse url: {0}", options.Url); - } - else - { - _logger.LogInformation("HttpClientManager.GetTempFileResponse url: {0}", options.Url); - } + _logger.LogDebug("HttpClientManager.GetTempFileResponse url: {0}", options.Url); } - var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression); + var client = GetHttpClient(options.Url, options.EnableHttpCompression); try { options.CancellationToken.ThrowIfCancellationRequested(); - using (var response = await httpWebRequest.GetResponseAsync().ConfigureAwait(false)) + using (var response = (await client.SendAsync(httpWebRequest).ConfigureAwait(false))) { - var httpResponse = (HttpWebResponse)response; - - EnsureSuccessStatusCode(client, httpResponse, options); + await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); options.CancellationToken.ThrowIfCancellationRequested(); - var contentLength = GetContentLength(httpResponse); - - using (var stream = httpResponse.GetResponseStream()) + using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); @@ -547,13 +515,18 @@ namespace Emby.Server.Implementations.HttpClientManager options.Progress.Report(100); - return GetResponseInfo(httpResponse, tempFile, contentLength); + var contentLength = response.Content.Headers.ContentLength; + return GetResponseInfo(response, tempFile, contentLength); } } catch (Exception ex) { - DeleteTempFile(tempFile); - throw GetException(ex, options, client); + if (File.Exists(tempFile)) + { + File.Delete(tempFile); + } + + throw GetException(ex, options); } finally { @@ -561,21 +534,7 @@ namespace Emby.Server.Implementations.HttpClientManager } } - private static long? GetContentLength(HttpWebResponse response) - { - var length = response.ContentLength; - - if (length == 0) - { - return null; - } - - return length; - } - - protected static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - private Exception GetException(Exception ex, HttpRequestOptions options, HttpClientInfo client) + private Exception GetException(Exception ex, HttpRequestOptions options) { if (ex is HttpException) { @@ -599,11 +558,6 @@ namespace Emby.Server.Implementations.HttpClientManager if (response != null) { exception.StatusCode = response.StatusCode; - - if ((int)response.StatusCode == 429) - { - client.LastTimeout = DateTime.UtcNow; - } } } @@ -624,7 +578,7 @@ namespace Emby.Server.Implementations.HttpClientManager if (operationCanceledException != null) { - return GetCancellationException(options, client, options.CancellationToken, operationCanceledException); + return GetCancellationException(options, options.CancellationToken, operationCanceledException); } if (options.LogErrors) @@ -635,18 +589,6 @@ namespace Emby.Server.Implementations.HttpClientManager return ex; } - private void DeleteTempFile(string file) - { - try - { - _fileSystem.DeleteFile(file); - } - catch (IOException) - { - // Might not have been created at all. No need to worry. - } - } - private void ValidateParams(HttpRequestOptions options) { if (string.IsNullOrEmpty(options.Url)) @@ -682,11 +624,10 @@ namespace Emby.Server.Implementations.HttpClientManager /// Throws the cancellation exception. /// /// The options. - /// The client. /// The cancellation token. /// The exception. /// Exception. - private Exception GetCancellationException(HttpRequestOptions options, HttpClientInfo client, CancellationToken cancellationToken, OperationCanceledException exception) + private Exception GetCancellationException(HttpRequestOptions options, CancellationToken cancellationToken, OperationCanceledException exception) { // If the HttpClient's timeout is reached, it will cancel the Task internally if (!cancellationToken.IsCancellationRequested) @@ -698,8 +639,6 @@ namespace Emby.Server.Implementations.HttpClientManager _logger.LogError(msg); } - client.LastTimeout = DateTime.UtcNow; - // Throw an HttpException so that the caller doesn't think it was cancelled by user code return new HttpException(msg, exception) { @@ -710,91 +649,20 @@ namespace Emby.Server.Implementations.HttpClientManager return exception; } - private void EnsureSuccessStatusCode(HttpClientInfo client, HttpWebResponse response, HttpRequestOptions options) + private async Task EnsureSuccessStatusCode(HttpResponseMessage response, HttpRequestOptions options) { - var statusCode = response.StatusCode; - - var isSuccessful = statusCode >= HttpStatusCode.OK && statusCode <= (HttpStatusCode)299; - - if (isSuccessful) + if (response.IsSuccessStatusCode) { return; } - if (options.LogErrorResponseBody) - { - try - { - using (var stream = response.GetResponseStream()) - { - if (stream != null) - { - using (var reader = new StreamReader(stream)) - { - var msg = reader.ReadToEnd(); - - _logger.LogError(msg); - } - } - } - } - catch - { + var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + _logger.LogError(msg); - } - } - - throw new HttpException(response.StatusDescription) + throw new HttpException(response.ReasonPhrase) { StatusCode = response.StatusCode }; } - - private static Task GetResponseAsync(WebRequest request, TimeSpan timeout) - { - var taskCompletion = new TaskCompletionSource(); - - var asyncTask = Task.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null); - - ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, TimeoutCallback, request, timeout, true); - var callback = new TaskCallback { taskCompletion = taskCompletion }; - asyncTask.ContinueWith(callback.OnSuccess, TaskContinuationOptions.NotOnFaulted); - - // Handle errors - asyncTask.ContinueWith(callback.OnError, TaskContinuationOptions.OnlyOnFaulted); - - return taskCompletion.Task; - } - - private static void TimeoutCallback(object state, bool timedOut) - { - if (timedOut && state != null) - { - var request = (WebRequest)state; - request.Abort(); - } - } - - private class TaskCallback - { - public TaskCompletionSource taskCompletion; - - public void OnSuccess(Task task) - { - taskCompletion.TrySetResult(task.Result); - } - - public void OnError(Task task) - { - if (task.Exception == null) - { - taskCompletion.TrySetException(Enumerable.Empty()); - } - else - { - taskCompletion.TrySetException(task.Exception); - } - } - } } } diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs index bea178517..874383fed 100644 --- a/MediaBrowser.Common/Net/HttpRequestOptions.cs +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -99,7 +99,6 @@ namespace MediaBrowser.Common.Net public bool EnableDefaultUserAgent { get; set; } public bool AppendCharsetToMimeType { get; set; } - public string DownloadFilePath { get; set; } private string GetHeaderValue(string name) { diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs index 5aaf7e0be..d332ab200 100644 --- a/MediaBrowser.Common/Net/IHttpClient.cs +++ b/MediaBrowser.Common/Net/IHttpClient.cs @@ -1,5 +1,6 @@ using System.IO; using System.Threading.Tasks; +using System.Net.Http; namespace MediaBrowser.Common.Net { @@ -23,6 +24,8 @@ namespace MediaBrowser.Common.Net Task Get(HttpRequestOptions options); /// + /// Warning: Depricated function, + /// use 'Task SendAsync(HttpRequestOptions options, HttpMethod httpMethod);' instead /// Sends the asynchronous. /// /// The options. @@ -30,6 +33,14 @@ namespace MediaBrowser.Common.Net /// Task{HttpResponseInfo}. Task SendAsync(HttpRequestOptions options, string httpMethod); + /// + /// Sends the asynchronous. + /// + /// The options. + /// The HTTP method. + /// Task{HttpResponseInfo}. + Task SendAsync(HttpRequestOptions options, HttpMethod httpMethod); + /// /// Posts the specified options. /// -- cgit v1.2.3 From 0a5550b13d3a32ae7158dafb38eca6ed94e17d60 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 8 Mar 2019 20:32:14 +0100 Subject: Remove more unused stuff --- Emby.Server.Implementations/ApplicationHost.cs | 11 ++++++----- .../HttpClientManager/HttpClientManager.cs | 18 ------------------ .../LiveTv/Listings/SchedulesDirect.cs | 10 +--------- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 2 -- .../LiveTv/TunerHosts/SharedHttpStream.cs | 6 ------ MediaBrowser.Common/Net/HttpRequestOptions.cs | 21 +-------------------- 6 files changed, 8 insertions(+), 60 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 5d5a63a63..02d661d15 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; @@ -1560,12 +1561,12 @@ namespace Emby.Server.Implementations LogErrorResponseBody = false, LogErrors = false, LogRequest = false, - TimeoutMs = 10000, BufferContent = false, CancellationToken = cancellationToken }).ConfigureAwait(false)) { - return GetWanApiUrl(response.ReadToEnd().Trim()); + string res = await response.ReadToEndAsync().ConfigureAwait(false); + return GetWanApiUrl(res.Trim()); } } catch (Exception ex) @@ -1717,15 +1718,15 @@ namespace Emby.Server.Implementations LogErrorResponseBody = false, LogErrors = LogPing, LogRequest = LogPing, - TimeoutMs = 5000, BufferContent = false, CancellationToken = cancellationToken - }, "POST").ConfigureAwait(false)) + }, HttpMethod.Post).ConfigureAwait(false)) + { using (var reader = new StreamReader(response.Content)) { - var result = reader.ReadToEnd(); + var result = await reader.ReadToEndAsync().ConfigureAwait(false); var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase); _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid); diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 581d6bafd..b82d55d0e 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -352,11 +352,6 @@ namespace Emby.Server.Implementations.HttpClientManager } } - if (options.ResourcePool != null) - { - await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); - } - if (options.LogRequest) { _logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToString(), options.Url); @@ -397,10 +392,6 @@ namespace Emby.Server.Implementations.HttpClientManager { throw GetCancellationException(options, options.CancellationToken, ex); } - finally - { - options.ResourcePool?.Release(); - } } private HttpResponseInfo GetResponseInfo(HttpResponseMessage httpResponse, Stream content, long? contentLength, IDisposable disposable) @@ -483,11 +474,6 @@ namespace Emby.Server.Implementations.HttpClientManager var httpWebRequest = GetRequestMessage(options, HttpMethod.Get); - if (options.ResourcePool != null) - { - await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); - } - options.Progress.Report(0); if (options.LogRequest) @@ -528,10 +514,6 @@ namespace Emby.Server.Implementations.HttpClientManager throw GetException(ex, options); } - finally - { - options.ResourcePool?.Release(); - } } private Exception GetException(Exception ex, HttpRequestOptions options) diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 4137760d0..f3f747718 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -96,8 +96,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings Url = ApiUrl + "/schedules", UserAgent = UserAgent, CancellationToken = cancellationToken, - // The data can be large so give it some extra time - TimeoutMs = 60000, LogErrorResponseBody = true, RequestContent = requestString }; @@ -115,9 +113,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings Url = ApiUrl + "/programs", UserAgent = UserAgent, CancellationToken = cancellationToken, - LogErrorResponseBody = true, - // The data can be large so give it some extra time - TimeoutMs = 60000 + LogErrorResponseBody = true }; httpOptions.RequestHeaders["token"] = token; @@ -483,8 +479,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings CancellationToken = cancellationToken, RequestContent = imageIdString, LogErrorResponseBody = true, - // The data can be large so give it some extra time - TimeoutMs = 60000 }; try @@ -871,8 +865,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings UserAgent = UserAgent, CancellationToken = cancellationToken, LogErrorResponseBody = true, - // The data can be large so give it some extra time - TimeoutMs = 60000 }; httpOptions.RequestHeaders["token"] = token; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 24b100edd..761275f8f 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -138,7 +138,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { Url = string.Format("{0}/discover.json", GetApiUrl(info)), CancellationToken = cancellationToken, - TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(10).TotalMilliseconds), BufferContent = false }, "GET").ConfigureAwait(false)) @@ -191,7 +190,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { Url = string.Format("{0}/tuners.html", GetApiUrl(info)), CancellationToken = cancellationToken, - TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds), BufferContent = false })) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index d74cf3be2..e8b34da0c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -47,13 +47,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts CancellationToken = CancellationToken.None, BufferContent = false, - // Increase a little bit - TimeoutMs = 30000, - EnableHttpCompression = false, - - LogResponse = true, - LogResponseHeaders = true }; foreach (var header in mediaSource.RequiredHttpHeaders) diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs index 874383fed..38e0ff0f5 100644 --- a/MediaBrowser.Common/Net/HttpRequestOptions.cs +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -28,18 +28,13 @@ namespace MediaBrowser.Common.Net get => GetHeaderValue(HeaderNames.Accept); set => RequestHeaders[HeaderNames.Accept] = value; } + /// /// Gets or sets the cancellation token. /// /// The cancellation token. public CancellationToken CancellationToken { get; set; } - /// - /// Gets or sets the resource pool. - /// - /// The resource pool. - public SemaphoreSlim ResourcePool { get; set; } - /// /// Gets or sets the user agent. /// @@ -86,8 +81,6 @@ namespace MediaBrowser.Common.Net public bool LogRequest { get; set; } public bool LogRequestAsDebug { get; set; } public bool LogErrors { get; set; } - public bool LogResponse { get; set; } - public bool LogResponseHeaders { get; set; } public bool LogErrorResponseBody { get; set; } public bool EnableKeepAlive { get; set; } @@ -95,7 +88,6 @@ namespace MediaBrowser.Common.Net public CacheMode CacheMode { get; set; } public TimeSpan CacheLength { get; set; } - public int TimeoutMs { get; set; } public bool EnableDefaultUserAgent { get; set; } public bool AppendCharsetToMimeType { get; set; } @@ -119,17 +111,6 @@ namespace MediaBrowser.Common.Net LogRequest = true; LogErrors = true; CacheMode = CacheMode.None; - - TimeoutMs = 20000; - } - - public void SetPostData(IDictionary values) - { - var strings = values.Keys.Select(key => string.Format("{0}={1}", key, values[key])); - var postContent = string.Join("&", strings.ToArray()); - - RequestContent = postContent; - RequestContentType = "application/x-www-form-urlencoded"; } } -- cgit v1.2.3 From cec22ad10daf7abef2f27f846e4022d5a35faccf Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 20 Feb 2019 14:26:49 +0100 Subject: Simplify db code --- .../Activity/ActivityRepository.cs | 5 +- .../Data/BaseSqliteRepository.cs | 239 ++----- .../Data/ManagedConnection.cs | 79 --- .../Data/SqliteDisplayPreferencesRepository.cs | 70 +- .../Data/SqliteExtensions.cs | 2 +- .../Data/SqliteItemRepository.cs | 733 ++++++++++----------- .../Data/SqliteUserDataRepository.cs | 76 +-- .../Data/SqliteUserRepository.cs | 96 ++- .../Security/AuthenticationRepository.cs | 216 +++--- 9 files changed, 589 insertions(+), 927 deletions(-) delete mode 100644 Emby.Server.Implementations/Data/ManagedConnection.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index aeed8b6f1..495d96977 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -57,7 +57,7 @@ namespace Emby.Server.Implementations.Activity } } - private void TryMigrate(ManagedConnection connection) + private void TryMigrate(SQLiteDatabaseConnection connection) { try { @@ -85,7 +85,6 @@ namespace Emby.Server.Implementations.Activity throw new ArgumentNullException(nameof(entry)); } - using (WriteLock.Write()) using (var connection = CreateConnection()) { connection.RunInTransaction(db => @@ -124,7 +123,6 @@ namespace Emby.Server.Implementations.Activity throw new ArgumentNullException(nameof(entry)); } - using (WriteLock.Write()) using (var connection = CreateConnection()) { connection.RunInTransaction(db => @@ -159,7 +157,6 @@ namespace Emby.Server.Implementations.Activity public QueryResult GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit) { - using (WriteLock.Read()) using (var connection = CreateConnection(true)) { var commandText = BaseActivitySelectText; diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index fba81306b..29edddb3a 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using SQLitePCL; using SQLitePCL.pretty; @@ -12,15 +13,12 @@ namespace Emby.Server.Implementations.Data public abstract class BaseSqliteRepository : IDisposable { protected string DbFilePath { get; set; } - protected ReaderWriterLockSlim WriteLock; protected ILogger Logger { get; private set; } protected BaseSqliteRepository(ILogger logger) { Logger = logger; - - WriteLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); } protected TransactionMode TransactionMode => TransactionMode.Deferred; @@ -31,130 +29,96 @@ namespace Emby.Server.Implementations.Data static BaseSqliteRepository() { - SQLite3.EnableSharedCache = false; - - int rc = raw.sqlite3_config(raw.SQLITE_CONFIG_MEMSTATUS, 0); - //CheckOk(rc); - - rc = raw.sqlite3_config(raw.SQLITE_CONFIG_MULTITHREAD, 1); - //rc = raw.sqlite3_config(raw.SQLITE_CONFIG_SINGLETHREAD, 1); - //rc = raw.sqlite3_config(raw.SQLITE_CONFIG_SERIALIZED, 1); - //CheckOk(rc); - - rc = raw.sqlite3_enable_shared_cache(1); - ThreadSafeMode = raw.sqlite3_threadsafe(); } private static bool _versionLogged; private string _defaultWal; - protected ManagedConnection _connection; - - protected virtual bool EnableSingleConnection => true; - protected ManagedConnection CreateConnection(bool isReadOnly = false) + protected SQLiteDatabaseConnection CreateConnection(bool isReadOnly = false) { - if (_connection != null) + if (!_versionLogged) { - return _connection; + _versionLogged = true; + Logger.LogInformation("Sqlite version: " + SQLite3.Version); + Logger.LogInformation("Sqlite compiler options: " + string.Join(",", SQLite3.CompilerOptions)); } - lock (WriteLock) + ConnectionFlags connectionFlags; + + if (isReadOnly) { - if (!_versionLogged) - { - _versionLogged = true; - Logger.LogInformation("Sqlite version: " + SQLite3.Version); - Logger.LogInformation("Sqlite compiler options: " + string.Join(",", SQLite3.CompilerOptions.ToArray())); - } + //Logger.LogInformation("Opening read connection"); + //connectionFlags = ConnectionFlags.Create; + connectionFlags = ConnectionFlags.ReadOnly; + } + else + { + //Logger.LogInformation("Opening write connection"); + connectionFlags = ConnectionFlags.Create; + connectionFlags |= ConnectionFlags.ReadWrite; + } + + connectionFlags |= ConnectionFlags.SharedCached; + connectionFlags |= ConnectionFlags.FullMutex; - ConnectionFlags connectionFlags; + var db = SQLite3.Open(DbFilePath, connectionFlags, null); - if (isReadOnly) + try + { + if (string.IsNullOrWhiteSpace(_defaultWal)) { - //Logger.LogInformation("Opening read connection"); - //connectionFlags = ConnectionFlags.ReadOnly; - connectionFlags = ConnectionFlags.Create; - connectionFlags |= ConnectionFlags.ReadWrite; + _defaultWal = db.Query("PRAGMA journal_mode").SelectScalarString().First(); + + Logger.LogInformation("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal); } - else + + var queries = new List + { + //"PRAGMA cache size=-10000" + //"PRAGMA read_uncommitted = true", + //"PRAGMA synchronous=Normal" + }; + + if (CacheSize.HasValue) { - //Logger.LogInformation("Opening write connection"); - connectionFlags = ConnectionFlags.Create; - connectionFlags |= ConnectionFlags.ReadWrite; + queries.Add("PRAGMA cache_size=" + CacheSize.Value.ToString(CultureInfo.InvariantCulture)); } - if (EnableSingleConnection) + if (EnableTempStoreMemory) { - connectionFlags |= ConnectionFlags.PrivateCache; + queries.Add("PRAGMA temp_store = memory"); } else { - connectionFlags |= ConnectionFlags.SharedCached; + queries.Add("PRAGMA temp_store = file"); } - connectionFlags |= ConnectionFlags.NoMutex; - - var db = SQLite3.Open(DbFilePath, connectionFlags, null); - - try + foreach (var query in queries) { - if (string.IsNullOrWhiteSpace(_defaultWal)) - { - _defaultWal = db.Query("PRAGMA journal_mode").SelectScalarString().First(); - - Logger.LogInformation("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal); - } - - var queries = new List - { - //"PRAGMA cache size=-10000" - //"PRAGMA read_uncommitted = true", - "PRAGMA synchronous=Normal" - }; - - if (CacheSize.HasValue) - { - queries.Add("PRAGMA cache_size=" + CacheSize.Value.ToString(CultureInfo.InvariantCulture)); - } - - if (EnableTempStoreMemory) - { - queries.Add("PRAGMA temp_store = memory"); - } - else - { - queries.Add("PRAGMA temp_store = file"); - } - - foreach (var query in queries) - { - db.Execute(query); - } + db.Execute(query); } - catch + } + catch + { + using (db) { - using (db) - { - } - - throw; } - _connection = new ManagedConnection(db, false); - - return _connection; + throw; } + + return db; } - public IStatement PrepareStatement(ManagedConnection connection, string sql) + public IStatement PrepareStatement(SQLiteDatabaseConnection connection, string sql) { return connection.PrepareStatement(sql); } - public IStatement PrepareStatementSafe(ManagedConnection connection, string sql) + public IStatement PrepareStatementSafe(SQLiteDatabaseConnection connection, string sql) { return connection.PrepareStatement(sql); } @@ -179,7 +143,7 @@ namespace Emby.Server.Implementations.Data return sql.Select(connection.PrepareStatement).ToList(); } - protected bool TableExists(ManagedConnection connection, string name) + protected bool TableExists(SQLiteDatabaseConnection connection, string name) { return connection.RunInTransaction(db => { @@ -199,7 +163,7 @@ namespace Emby.Server.Implementations.Data }, ReadTransactionMode); } - protected void RunDefaultInitialization(ManagedConnection db) + protected void RunDefaultInitialization(SQLiteDatabaseConnection db) { var queries = new List { @@ -243,7 +207,7 @@ namespace Emby.Server.Implementations.Data public void Dispose() { - _disposed = true; + Dispose(true); } @@ -255,42 +219,13 @@ namespace Emby.Server.Implementations.Data /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool dispose) { - if (dispose) - { - DisposeConnection(); - } - } - - private void DisposeConnection() - { - try - { - lock (_disposeLock) - { - using (WriteLock.Write()) - { - if (_connection != null) - { - using (_connection) - { - _connection.Close(); - } - _connection = null; - } - - CloseConnection(); - } - } - } - catch (Exception ex) + if (_disposed) { - Logger.LogError(ex, "Error disposing database"); + return; } - } - protected virtual void CloseConnection() - { + _disposed = true; } protected List GetColumnNames(IDatabaseConnection connection, string table) @@ -320,60 +255,4 @@ namespace Emby.Server.Implementations.Data connection.Execute("alter table " + table + " add column " + columnName + " " + type + " NULL"); } } - - public static class ReaderWriterLockSlimExtensions - { - private sealed class ReadLockToken : IDisposable - { - private ReaderWriterLockSlim _sync; - public ReadLockToken(ReaderWriterLockSlim sync) - { - _sync = sync; - sync.EnterReadLock(); - } - public void Dispose() - { - if (_sync != null) - { - _sync.ExitReadLock(); - _sync = null; - } - } - } - private sealed class WriteLockToken : IDisposable - { - private ReaderWriterLockSlim _sync; - public WriteLockToken(ReaderWriterLockSlim sync) - { - _sync = sync; - sync.EnterWriteLock(); - } - public void Dispose() - { - if (_sync != null) - { - _sync.ExitWriteLock(); - _sync = null; - } - } - } - - public static IDisposable Read(this ReaderWriterLockSlim obj) - { - //if (BaseSqliteRepository.ThreadSafeMode > 0) - //{ - // return new DummyToken(); - //} - return new WriteLockToken(obj); - } - - public static IDisposable Write(this ReaderWriterLockSlim obj) - { - //if (BaseSqliteRepository.ThreadSafeMode > 0) - //{ - // return new DummyToken(); - //} - return new WriteLockToken(obj); - } - } } diff --git a/Emby.Server.Implementations/Data/ManagedConnection.cs b/Emby.Server.Implementations/Data/ManagedConnection.cs deleted file mode 100644 index b8f1e581a..000000000 --- a/Emby.Server.Implementations/Data/ManagedConnection.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Collections.Generic; -using SQLitePCL.pretty; - -namespace Emby.Server.Implementations.Data -{ - public class ManagedConnection : IDisposable - { - private SQLiteDatabaseConnection db; - private readonly bool _closeOnDispose; - - public ManagedConnection(SQLiteDatabaseConnection db, bool closeOnDispose) - { - this.db = db; - _closeOnDispose = closeOnDispose; - } - - public IStatement PrepareStatement(string sql) - { - return db.PrepareStatement(sql); - } - - public IEnumerable PrepareAll(string sql) - { - return db.PrepareAll(sql); - } - - public void ExecuteAll(string sql) - { - db.ExecuteAll(sql); - } - - public void Execute(string sql, params object[] values) - { - db.Execute(sql, values); - } - - public void RunQueries(string[] sql) - { - db.RunQueries(sql); - } - - public void RunInTransaction(Action action, TransactionMode mode) - { - db.RunInTransaction(action, mode); - } - - public T RunInTransaction(Func action, TransactionMode mode) - { - return db.RunInTransaction(action, mode); - } - - public IEnumerable> Query(string sql) - { - return db.Query(sql); - } - - public IEnumerable> Query(string sql, params object[] values) - { - return db.Query(sql, values); - } - - public void Close() - { - using (db) - { - - } - } - - public void Dispose() - { - if (_closeOnDispose) - { - Close(); - } - } - } -} diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs index 47552806d..86a7c1551 100644 --- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs @@ -98,15 +98,12 @@ namespace Emby.Server.Implementations.Data cancellationToken.ThrowIfCancellationRequested(); - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => - { - SaveDisplayPreferences(displayPreferences, userId, client, db); - }, TransactionMode); - } + SaveDisplayPreferences(displayPreferences, userId, client, db); + }, TransactionMode); } } @@ -142,18 +139,15 @@ namespace Emby.Server.Implementations.Data cancellationToken.ThrowIfCancellationRequested(); - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => + foreach (var displayPreference in displayPreferences) { - foreach (var displayPreference in displayPreferences) - { - SaveDisplayPreferences(displayPreference, userId, displayPreference.Client, db); - } - }, TransactionMode); - } + SaveDisplayPreferences(displayPreference, userId, displayPreference.Client, db); + } + }, TransactionMode); } } @@ -174,27 +168,24 @@ namespace Emby.Server.Implementations.Data var guidId = displayPreferencesId.GetMD5(); - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where id = @id and userId=@userId and client=@client")) { - using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where id = @id and userId=@userId and client=@client")) - { - statement.TryBind("@id", guidId.ToGuidBlob()); - statement.TryBind("@userId", userId.ToGuidBlob()); - statement.TryBind("@client", client); - - foreach (var row in statement.ExecuteQuery()) - { - return Get(row); - } - } + statement.TryBind("@id", guidId.ToGuidBlob()); + statement.TryBind("@userId", userId.ToGuidBlob()); + statement.TryBind("@client", client); - return new DisplayPreferences + foreach (var row in statement.ExecuteQuery()) { - Id = guidId.ToString("N") - }; + return Get(row); + } } + + return new DisplayPreferences + { + Id = guidId.ToString("N") + }; } } @@ -208,18 +199,15 @@ namespace Emby.Server.Implementations.Data { var list = new List(); - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId")) { - using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId")) - { - statement.TryBind("@userId", userId.ToGuidBlob()); + statement.TryBind("@userId", userId.ToGuidBlob()); - foreach (var row in statement.ExecuteQuery()) - { - list.Add(Get(row)); - } + foreach (var row in statement.ExecuteQuery()) + { + list.Add(Get(row)); } } } diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs index a486cb1a0..c0f27b25a 100644 --- a/Emby.Server.Implementations/Data/SqliteExtensions.cs +++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs @@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.Data } } - public static void Attach(ManagedConnection db, string path, string alias) + public static void Attach(SQLiteDatabaseConnection db, string path, string alias) { var commandText = string.Format("attach @path as {0};", alias); diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 8841a9a50..baa5b713a 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -319,7 +319,7 @@ namespace Emby.Server.Implementations.Data connection.RunQueries(postQueries); } - userDataRepo.Initialize(WriteLock, _connection, userManager); + userDataRepo.Initialize(userManager); } private static readonly string[] _retriveItemColumns = @@ -551,21 +551,18 @@ namespace Emby.Server.Implementations.Data CheckDisposed(); - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => + using (var saveImagesStatement = PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id")) { - using (var saveImagesStatement = PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id")) - { - saveImagesStatement.TryBind("@Id", item.Id.ToGuidBlob()); - saveImagesStatement.TryBind("@Images", SerializeImages(item)); + saveImagesStatement.TryBind("@Id", item.Id.ToGuidBlob()); + saveImagesStatement.TryBind("@Images", SerializeImages(item)); - saveImagesStatement.MoveNext(); - } - }, TransactionMode); - } + saveImagesStatement.MoveNext(); + } + }, TransactionMode); } } @@ -605,16 +602,12 @@ namespace Emby.Server.Implementations.Data tuples.Add((item, ancestorIds, topParent, userdataKey, inheritedTags)); } - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => - { - SaveItemsInTranscation(db, tuples); - - }, TransactionMode); - } + SaveItemsInTranscation(db, tuples); + }, TransactionMode); } } @@ -1193,23 +1186,20 @@ namespace Emby.Server.Implementations.Data CheckDisposed(); - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + using (var statement = PrepareStatementSafe(connection, "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where guid = @guid")) { - using (var statement = PrepareStatementSafe(connection, "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where guid = @guid")) - { - statement.TryBind("@guid", id); + statement.TryBind("@guid", id); - foreach (var row in statement.ExecuteQuery()) - { - return GetItem(row, new InternalItemsQuery()); - } + foreach (var row in statement.ExecuteQuery()) + { + return GetItem(row, new InternalItemsQuery()); } - - return null; } } + + return null; } private bool TypeRequiresDeserialization(Type type) @@ -1909,24 +1899,21 @@ namespace Emby.Server.Implementations.Data { CheckDisposed(); - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + var list = new List(); + + using (var statement = PrepareStatementSafe(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc")) { - var list = new List(); + statement.TryBind("@ItemId", item.Id); - using (var statement = PrepareStatementSafe(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc")) + foreach (var row in statement.ExecuteQuery()) { - statement.TryBind("@ItemId", item.Id); - - foreach (var row in statement.ExecuteQuery()) - { - list.Add(GetChapter(row, item)); - } + list.Add(GetChapter(row, item)); } - - return list; } + + return list; } } @@ -1941,22 +1928,20 @@ namespace Emby.Server.Implementations.Data { CheckDisposed(); - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + using (var statement = PrepareStatementSafe(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex")) { - using (var statement = PrepareStatementSafe(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex")) - { - statement.TryBind("@ItemId", item.Id); - statement.TryBind("@ChapterIndex", index); + statement.TryBind("@ItemId", item.Id); + statement.TryBind("@ChapterIndex", index); - foreach (var row in statement.ExecuteQuery()) - { - return GetChapter(row, item); - } + foreach (var row in statement.ExecuteQuery()) + { + return GetChapter(row, item); } } } + return null; } @@ -2012,21 +1997,18 @@ namespace Emby.Server.Implementations.Data throw new ArgumentNullException(nameof(chapters)); } - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => - { - var idBlob = id.ToGuidBlob(); + var idBlob = id.ToGuidBlob(); // First delete chapters db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob); - InsertChapters(idBlob, chapters, db); + InsertChapters(idBlob, chapters, db); - }, TransactionMode); - } + }, TransactionMode); } } @@ -2551,29 +2533,25 @@ namespace Emby.Server.Implementations.Data commandText += " where " + string.Join(" AND ", whereClauses); } - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + using (var statement = PrepareStatementSafe(connection, commandText)) { - using (var statement = PrepareStatementSafe(connection, commandText)) + if (EnableJoinUserData(query)) { - if (EnableJoinUserData(query)) - { - statement.TryBind("@UserId", query.User.InternalId); - } + statement.TryBind("@UserId", query.User.InternalId); + } - BindSimilarParams(query, statement); - BindSearchParams(query, statement); + BindSimilarParams(query, statement); + BindSearchParams(query, statement); - // Running this again will bind the params - GetWhereClauses(query, statement); + // Running this again will bind the params + GetWhereClauses(query, statement); - var count = statement.ExecuteQuery().SelectScalarInt().First(); - LogQueryTime("GetCount", commandText, now); - return count; - } + var count = statement.ExecuteQuery().SelectScalarInt().First(); + LogQueryTime("GetCount", commandText, now); + return count; } - } } @@ -2624,67 +2602,64 @@ namespace Emby.Server.Implementations.Data } } - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) - { - var list = new List(); + var list = new List(); - using (var statement = PrepareStatementSafe(connection, commandText)) + using (var statement = PrepareStatementSafe(connection, commandText)) + { + if (EnableJoinUserData(query)) { - if (EnableJoinUserData(query)) - { - statement.TryBind("@UserId", query.User.InternalId); - } + statement.TryBind("@UserId", query.User.InternalId); + } - BindSimilarParams(query, statement); - BindSearchParams(query, statement); + BindSimilarParams(query, statement); + BindSearchParams(query, statement); - // Running this again will bind the params - GetWhereClauses(query, statement); + // Running this again will bind the params + GetWhereClauses(query, statement); - var hasEpisodeAttributes = HasEpisodeAttributes(query); - var hasServiceName = HasServiceName(query); - var hasProgramAttributes = HasProgramAttributes(query); - var hasStartDate = HasStartDate(query); - var hasTrailerTypes = HasTrailerTypes(query); - var hasArtistFields = HasArtistFields(query); - var hasSeriesFields = HasSeriesFields(query); + var hasEpisodeAttributes = HasEpisodeAttributes(query); + var hasServiceName = HasServiceName(query); + var hasProgramAttributes = HasProgramAttributes(query); + var hasStartDate = HasStartDate(query); + var hasTrailerTypes = HasTrailerTypes(query); + var hasArtistFields = HasArtistFields(query); + var hasSeriesFields = HasSeriesFields(query); - foreach (var row in statement.ExecuteQuery()) + foreach (var row in statement.ExecuteQuery()) + { + var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields); + if (item != null) { - var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields); - if (item != null) - { - list.Add(item); - } + list.Add(item); } } + } - // Hack for right now since we currently don't support filtering out these duplicates within a query - if (query.EnableGroupByMetadataKey) + // Hack for right now since we currently don't support filtering out these duplicates within a query + if (query.EnableGroupByMetadataKey) + { + var limit = query.Limit ?? int.MaxValue; + limit -= 4; + var newList = new List(); + + foreach (var item in list) { - var limit = query.Limit ?? int.MaxValue; - limit -= 4; - var newList = new List(); + AddItem(newList, item); - foreach (var item in list) + if (newList.Count >= limit) { - AddItem(newList, item); - - if (newList.Count >= limit) - { - break; - } + break; } - - list = newList; } - LogQueryTime("GetItemList", commandText, now); - - return list; + list = newList; } + + LogQueryTime("GetItemList", commandText, now); + + return list; } } @@ -2845,75 +2820,72 @@ namespace Emby.Server.Implementations.Data statementTexts.Add(commandText); } - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + return connection.RunInTransaction(db => { - return connection.RunInTransaction(db => - { - var result = new QueryResult(); - var statements = PrepareAllSafe(db, statementTexts); + var result = new QueryResult(); + var statements = PrepareAllSafe(db, statementTexts); - if (!isReturningZeroItems) + if (!isReturningZeroItems) + { + using (var statement = statements[0]) { - using (var statement = statements[0]) + if (EnableJoinUserData(query)) { - if (EnableJoinUserData(query)) - { - statement.TryBind("@UserId", query.User.InternalId); - } + statement.TryBind("@UserId", query.User.InternalId); + } - BindSimilarParams(query, statement); - BindSearchParams(query, statement); + BindSimilarParams(query, statement); + BindSearchParams(query, statement); // Running this again will bind the params GetWhereClauses(query, statement); - var hasEpisodeAttributes = HasEpisodeAttributes(query); - var hasServiceName = HasServiceName(query); - var hasProgramAttributes = HasProgramAttributes(query); - var hasStartDate = HasStartDate(query); - var hasTrailerTypes = HasTrailerTypes(query); - var hasArtistFields = HasArtistFields(query); - var hasSeriesFields = HasSeriesFields(query); + var hasEpisodeAttributes = HasEpisodeAttributes(query); + var hasServiceName = HasServiceName(query); + var hasProgramAttributes = HasProgramAttributes(query); + var hasStartDate = HasStartDate(query); + var hasTrailerTypes = HasTrailerTypes(query); + var hasArtistFields = HasArtistFields(query); + var hasSeriesFields = HasSeriesFields(query); - foreach (var row in statement.ExecuteQuery()) + foreach (var row in statement.ExecuteQuery()) + { + var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields); + if (item != null) { - var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields); - if (item != null) - { - list.Add(item); - } + list.Add(item); } } } + } - if (query.EnableTotalRecordCount) + if (query.EnableTotalRecordCount) + { + using (var statement = statements[statements.Count - 1]) { - using (var statement = statements[statements.Count - 1]) + if (EnableJoinUserData(query)) { - if (EnableJoinUserData(query)) - { - statement.TryBind("@UserId", query.User.InternalId); - } + statement.TryBind("@UserId", query.User.InternalId); + } - BindSimilarParams(query, statement); - BindSearchParams(query, statement); + BindSimilarParams(query, statement); + BindSearchParams(query, statement); // Running this again will bind the params GetWhereClauses(query, statement); - result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); - } + result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); } + } - LogQueryTime("GetItems", commandText, now); + LogQueryTime("GetItems", commandText, now); - result.Items = list.ToArray(); - return result; + result.Items = list.ToArray(); + return result; - }, ReadTransactionMode); - } + }, ReadTransactionMode); } } @@ -3080,35 +3052,32 @@ namespace Emby.Server.Implementations.Data } } - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) - { - var list = new List(); + var list = new List(); - using (var statement = PrepareStatementSafe(connection, commandText)) + using (var statement = PrepareStatementSafe(connection, commandText)) + { + if (EnableJoinUserData(query)) { - if (EnableJoinUserData(query)) - { - statement.TryBind("@UserId", query.User.InternalId); - } + statement.TryBind("@UserId", query.User.InternalId); + } - BindSimilarParams(query, statement); - BindSearchParams(query, statement); + BindSimilarParams(query, statement); + BindSearchParams(query, statement); - // Running this again will bind the params - GetWhereClauses(query, statement); + // Running this again will bind the params + GetWhereClauses(query, statement); - foreach (var row in statement.ExecuteQuery()) - { - list.Add(row[0].ReadGuidFromBlob()); - } + foreach (var row in statement.ExecuteQuery()) + { + list.Add(row[0].ReadGuidFromBlob()); } + } - LogQueryTime("GetItemList", commandText, now); + LogQueryTime("GetItemList", commandText, now); - return list; - } + return list; } } @@ -3149,39 +3118,36 @@ namespace Emby.Server.Implementations.Data } } - using (WriteLock.Read()) + var list = new List>(); + using (var connection = CreateConnection(true)) { - var list = new List>(); - using (var connection = CreateConnection(true)) + using (var statement = PrepareStatementSafe(connection, commandText)) { - using (var statement = PrepareStatementSafe(connection, commandText)) + if (EnableJoinUserData(query)) { - if (EnableJoinUserData(query)) - { - statement.TryBind("@UserId", query.User.InternalId); - } + statement.TryBind("@UserId", query.User.InternalId); + } - // Running this again will bind the params - GetWhereClauses(query, statement); + // Running this again will bind the params + GetWhereClauses(query, statement); - foreach (var row in statement.ExecuteQuery()) - { - var id = row.GetGuid(0); - string path = null; + foreach (var row in statement.ExecuteQuery()) + { + var id = row.GetGuid(0); + string path = null; - if (!row.IsDBNull(1)) - { - path = row.GetString(1); - } - list.Add(new Tuple(id, path)); + if (!row.IsDBNull(1)) + { + path = row.GetString(1); } + list.Add(new Tuple(id, path)); } } + } - LogQueryTime("GetItemIdsWithPath", commandText, now); + LogQueryTime("GetItemIdsWithPath", commandText, now); - return list; - } + return list; } public QueryResult GetItemIds(InternalItemsQuery query) @@ -3265,64 +3231,61 @@ namespace Emby.Server.Implementations.Data statementTexts.Add(commandText); } - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + return connection.RunInTransaction(db => { - return connection.RunInTransaction(db => - { - var result = new QueryResult(); + var result = new QueryResult(); - var statements = PrepareAllSafe(db, statementTexts); + var statements = PrepareAllSafe(db, statementTexts); - if (!isReturningZeroItems) + if (!isReturningZeroItems) + { + using (var statement = statements[0]) { - using (var statement = statements[0]) + if (EnableJoinUserData(query)) { - if (EnableJoinUserData(query)) - { - statement.TryBind("@UserId", query.User.InternalId); - } + statement.TryBind("@UserId", query.User.InternalId); + } - BindSimilarParams(query, statement); - BindSearchParams(query, statement); + BindSimilarParams(query, statement); + BindSearchParams(query, statement); // Running this again will bind the params GetWhereClauses(query, statement); - foreach (var row in statement.ExecuteQuery()) - { - list.Add(row[0].ReadGuidFromBlob()); - } + foreach (var row in statement.ExecuteQuery()) + { + list.Add(row[0].ReadGuidFromBlob()); } } + } - if (query.EnableTotalRecordCount) + if (query.EnableTotalRecordCount) + { + using (var statement = statements[statements.Count - 1]) { - using (var statement = statements[statements.Count - 1]) + if (EnableJoinUserData(query)) { - if (EnableJoinUserData(query)) - { - statement.TryBind("@UserId", query.User.InternalId); - } + statement.TryBind("@UserId", query.User.InternalId); + } - BindSimilarParams(query, statement); - BindSearchParams(query, statement); + BindSimilarParams(query, statement); + BindSearchParams(query, statement); // Running this again will bind the params GetWhereClauses(query, statement); - result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); - } + result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); } + } - LogQueryTime("GetItemIds", commandText, now); + LogQueryTime("GetItemIds", commandText, now); - result.Items = list.ToArray(); - return result; + result.Items = list.ToArray(); + return result; - }, ReadTransactionMode); - } + }, ReadTransactionMode); } } @@ -4899,14 +4862,12 @@ namespace Emby.Server.Implementations.Data private void UpdateInheritedTags(CancellationToken cancellationToken) { - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => + connection.ExecuteAll(string.Join(";", new string[] { - connection.ExecuteAll(string.Join(";", new string[] - { "delete from itemvalues where type = 6", "insert into itemvalues (ItemId, Type, Value, CleanValue) select ItemId, 6, Value, CleanValue from ItemValues where Type=4", @@ -4916,10 +4877,9 @@ FROM AncestorIds LEFT JOIN ItemValues ON (AncestorIds.AncestorId = ItemValues.ItemId) where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type = 4 " - })); + })); - }, TransactionMode); - } + }, TransactionMode); } } @@ -4965,13 +4925,11 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type CheckDisposed(); - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => - { - var idBlob = id.ToGuidBlob(); + var idBlob = id.ToGuidBlob(); // Delete people ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", idBlob); @@ -4990,8 +4948,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type // Delete the item ExecuteWithSingleParam(db, "delete from TypedBaseItems where guid=@Id", idBlob); - }, TransactionMode); - } + }, TransactionMode); } } @@ -5025,23 +4982,20 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type commandText += " order by ListOrder"; - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + var list = new List(); + using (var statement = PrepareStatementSafe(connection, commandText)) { - var list = new List(); - using (var statement = PrepareStatementSafe(connection, commandText)) - { - // Run this again to bind the params - GetPeopleWhereClauses(query, statement); + // Run this again to bind the params + GetPeopleWhereClauses(query, statement); - foreach (var row in statement.ExecuteQuery()) - { - list.Add(row.GetString(0)); - } + foreach (var row in statement.ExecuteQuery()) + { + list.Add(row.GetString(0)); } - return list; } + return list; } } @@ -5065,25 +5019,22 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type commandText += " order by ListOrder"; - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + var list = new List(); + + using (var statement = PrepareStatementSafe(connection, commandText)) { - var list = new List(); + // Run this again to bind the params + GetPeopleWhereClauses(query, statement); - using (var statement = PrepareStatementSafe(connection, commandText)) + foreach (var row in statement.ExecuteQuery()) { - // Run this again to bind the params - GetPeopleWhereClauses(query, statement); - - foreach (var row in statement.ExecuteQuery()) - { - list.Add(GetPerson(row)); - } + list.Add(GetPerson(row)); } - - return list; } + + return list; } } @@ -5294,27 +5245,24 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type commandText += " Group By CleanValue"; - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) - { - var list = new List(); + var list = new List(); - using (var statement = PrepareStatementSafe(connection, commandText)) + using (var statement = PrepareStatementSafe(connection, commandText)) + { + foreach (var row in statement.ExecuteQuery()) { - foreach (var row in statement.ExecuteQuery()) + if (!row.IsDBNull(0)) { - if (!row.IsDBNull(0)) - { - list.Add(row.GetString(0)); - } + list.Add(row.GetString(0)); } } + } - LogQueryTime("GetItemValueNames", commandText, now); + LogQueryTime("GetItemValueNames", commandText, now); - return list; - } + return list; } } @@ -5483,100 +5431,97 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type statementTexts.Add(countText); } - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + return connection.RunInTransaction(db => { - return connection.RunInTransaction(db => - { - var list = new List<(BaseItem, ItemCounts)>(); - var result = new QueryResult<(BaseItem, ItemCounts)>(); + var list = new List<(BaseItem, ItemCounts)>(); + var result = new QueryResult<(BaseItem, ItemCounts)>(); - var statements = PrepareAllSafe(db, statementTexts); + var statements = PrepareAllSafe(db, statementTexts); - if (!isReturningZeroItems) + if (!isReturningZeroItems) + { + using (var statement = statements[0]) { - using (var statement = statements[0]) + statement.TryBind("@SelectType", returnType); + if (EnableJoinUserData(query)) { - statement.TryBind("@SelectType", returnType); - if (EnableJoinUserData(query)) - { - statement.TryBind("@UserId", query.User.InternalId); - } + statement.TryBind("@UserId", query.User.InternalId); + } - if (typeSubQuery != null) - { - GetWhereClauses(typeSubQuery, null); - } - BindSimilarParams(query, statement); - BindSearchParams(query, statement); - GetWhereClauses(innerQuery, statement); - GetWhereClauses(outerQuery, statement); - - var hasEpisodeAttributes = HasEpisodeAttributes(query); - var hasProgramAttributes = HasProgramAttributes(query); - var hasServiceName = HasServiceName(query); - var hasStartDate = HasStartDate(query); - var hasTrailerTypes = HasTrailerTypes(query); - var hasArtistFields = HasArtistFields(query); - var hasSeriesFields = HasSeriesFields(query); - - foreach (var row in statement.ExecuteQuery()) + if (typeSubQuery != null) + { + GetWhereClauses(typeSubQuery, null); + } + BindSimilarParams(query, statement); + BindSearchParams(query, statement); + GetWhereClauses(innerQuery, statement); + GetWhereClauses(outerQuery, statement); + + var hasEpisodeAttributes = HasEpisodeAttributes(query); + var hasProgramAttributes = HasProgramAttributes(query); + var hasServiceName = HasServiceName(query); + var hasStartDate = HasStartDate(query); + var hasTrailerTypes = HasTrailerTypes(query); + var hasArtistFields = HasArtistFields(query); + var hasSeriesFields = HasSeriesFields(query); + + foreach (var row in statement.ExecuteQuery()) + { + var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields); + if (item != null) { - var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields); - if (item != null) - { - var countStartColumn = columns.Count - 1; + var countStartColumn = columns.Count - 1; - list.Add((item, GetItemCounts(row, countStartColumn, typesToCount))); - } + list.Add((item, GetItemCounts(row, countStartColumn, typesToCount))); } - - LogQueryTime("GetItemValues", commandText, now); } + + LogQueryTime("GetItemValues", commandText, now); } + } - if (query.EnableTotalRecordCount) - { - commandText = "select " - + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" })) - + GetFromText() - + GetJoinUserDataText(query) - + whereText; + if (query.EnableTotalRecordCount) + { + commandText = "select " + + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" })) + + GetFromText() + + GetJoinUserDataText(query) + + whereText; - using (var statement = statements[statements.Count - 1]) + using (var statement = statements[statements.Count - 1]) + { + statement.TryBind("@SelectType", returnType); + if (EnableJoinUserData(query)) { - statement.TryBind("@SelectType", returnType); - if (EnableJoinUserData(query)) - { - statement.TryBind("@UserId", query.User.InternalId); - } + statement.TryBind("@UserId", query.User.InternalId); + } - if (typeSubQuery != null) - { - GetWhereClauses(typeSubQuery, null); - } - BindSimilarParams(query, statement); - BindSearchParams(query, statement); - GetWhereClauses(innerQuery, statement); - GetWhereClauses(outerQuery, statement); + if (typeSubQuery != null) + { + GetWhereClauses(typeSubQuery, null); + } + BindSimilarParams(query, statement); + BindSearchParams(query, statement); + GetWhereClauses(innerQuery, statement); + GetWhereClauses(outerQuery, statement); - result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); + result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); - LogQueryTime("GetItemValues", commandText, now); - } + LogQueryTime("GetItemValues", commandText, now); } + } - if (result.TotalRecordCount == 0) - { - result.TotalRecordCount = list.Count; - } - result.Items = list.ToArray(); + if (result.TotalRecordCount == 0) + { + result.TotalRecordCount = list.Count; + } + result.Items = list.ToArray(); - return result; + return result; - }, ReadTransactionMode); - } + }, ReadTransactionMode); } } @@ -5753,22 +5698,18 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type CheckDisposed(); - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => - { - var itemIdBlob = itemId.ToGuidBlob(); + var itemIdBlob = itemId.ToGuidBlob(); // First delete chapters db.Execute("delete from People where ItemId=@ItemId", itemIdBlob); - InsertPeople(itemIdBlob, people, db); - - }, TransactionMode); + InsertPeople(itemIdBlob, people, db); - } + }, TransactionMode); } } @@ -5874,34 +5815,31 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type cmdText += " order by StreamIndex ASC"; - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + var list = new List(); + + using (var statement = PrepareStatementSafe(connection, cmdText)) { - var list = new List(); + statement.TryBind("@ItemId", query.ItemId.ToGuidBlob()); - using (var statement = PrepareStatementSafe(connection, cmdText)) + if (query.Type.HasValue) { - statement.TryBind("@ItemId", query.ItemId.ToGuidBlob()); - - if (query.Type.HasValue) - { - statement.TryBind("@StreamType", query.Type.Value.ToString()); - } - - if (query.Index.HasValue) - { - statement.TryBind("@StreamIndex", query.Index.Value); - } + statement.TryBind("@StreamType", query.Type.Value.ToString()); + } - foreach (var row in statement.ExecuteQuery()) - { - list.Add(GetMediaStream(row)); - } + if (query.Index.HasValue) + { + statement.TryBind("@StreamIndex", query.Index.Value); } - return list; + foreach (var row in statement.ExecuteQuery()) + { + list.Add(GetMediaStream(row)); + } } + + return list; } } @@ -5921,21 +5859,18 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type cancellationToken.ThrowIfCancellationRequested(); - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => - { - var itemIdBlob = id.ToGuidBlob(); + var itemIdBlob = id.ToGuidBlob(); // First delete chapters db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob); - InsertMediaStreams(itemIdBlob, streams, db); + InsertMediaStreams(itemIdBlob, streams, db); - }, TransactionMode); - } + }, TransactionMode); } } diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index 4109b7ad1..f544dfac4 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -7,7 +7,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; using SQLitePCL.pretty; @@ -33,13 +32,8 @@ namespace Emby.Server.Implementations.Data /// Opens the connection to the database /// /// Task. - public void Initialize(ReaderWriterLockSlim writeLock, ManagedConnection managedConnection, IUserManager userManager) + public void Initialize(IUserManager userManager) { - _connection = managedConnection; - - WriteLock.Dispose(); - WriteLock = writeLock; - using (var connection = CreateConnection()) { var userDatasTableExists = TableExists(connection, "UserDatas"); @@ -178,15 +172,12 @@ namespace Emby.Server.Implementations.Data { cancellationToken.ThrowIfCancellationRequested(); - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => - { - SaveUserData(db, internalUserId, key, userData); - }, TransactionMode); - } + SaveUserData(db, internalUserId, key, userData); + }, TransactionMode); } } @@ -249,18 +240,15 @@ namespace Emby.Server.Implementations.Data { cancellationToken.ThrowIfCancellationRequested(); - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => + foreach (var userItemData in userDataList) { - foreach (var userItemData in userDataList) - { - SaveUserData(db, internalUserId, userItemData.Key, userItemData); - } - }, TransactionMode); - } + SaveUserData(db, internalUserId, userItemData.Key, userItemData); + } + }, TransactionMode); } } @@ -281,28 +269,26 @@ namespace Emby.Server.Implementations.Data { throw new ArgumentNullException(nameof(internalUserId)); } + if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException(nameof(key)); } - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where key =@Key and userId=@UserId")) { - using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where key =@Key and userId=@UserId")) - { - statement.TryBind("@UserId", internalUserId); - statement.TryBind("@Key", key); + statement.TryBind("@UserId", internalUserId); + statement.TryBind("@Key", key); - foreach (var row in statement.ExecuteQuery()) - { - return ReadRow(row); - } + foreach (var row in statement.ExecuteQuery()) + { + return ReadRow(row); } - - return null; } + + return null; } } @@ -335,18 +321,15 @@ namespace Emby.Server.Implementations.Data var list = new List(); - using (WriteLock.Read()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where userId=@UserId")) { - using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where userId=@UserId")) - { - statement.TryBind("@UserId", internalUserId); + statement.TryBind("@UserId", internalUserId); - foreach (var row in statement.ExecuteQuery()) - { - list.Add(ReadRow(row)); - } + foreach (var row in statement.ExecuteQuery()) + { + list.Add(ReadRow(row)); } } } @@ -397,10 +380,5 @@ namespace Emby.Server.Implementations.Data { // handled by library database } - - protected override void CloseConnection() - { - // handled by library database - } } } diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs index 5957b2903..785452ad3 100644 --- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs @@ -60,7 +60,7 @@ namespace Emby.Server.Implementations.Data } } - private void TryMigrateToLocalUsersTable(ManagedConnection connection) + private void TryMigrateToLocalUsersTable(SQLiteDatabaseConnection connection) { try { @@ -119,31 +119,28 @@ namespace Emby.Server.Implementations.Data var serialized = _jsonSerializer.SerializeToBytes(user); - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => + using (var statement = db.PrepareStatement("insert into LocalUsersv2 (guid, data) values (@guid, @data)")) { - using (var statement = db.PrepareStatement("insert into LocalUsersv2 (guid, data) values (@guid, @data)")) - { - statement.TryBind("@guid", user.Id.ToGuidBlob()); - statement.TryBind("@data", serialized); + statement.TryBind("@guid", user.Id.ToGuidBlob()); + statement.TryBind("@data", serialized); - statement.MoveNext(); - } + statement.MoveNext(); + } - var createdUser = GetUser(user.Id, false); + var createdUser = GetUser(user.Id, connection); - if (createdUser == null) - { - throw new ApplicationException("created user should never be null"); - } + if (createdUser == null) + { + throw new ApplicationException("created user should never be null"); + } - user.InternalId = createdUser.InternalId; + user.InternalId = createdUser.InternalId; - }, TransactionMode); - } + }, TransactionMode); } } @@ -156,39 +153,30 @@ namespace Emby.Server.Implementations.Data var serialized = _jsonSerializer.SerializeToBytes(user); - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => + using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId")) { - using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId")) - { - statement.TryBind("@InternalId", user.InternalId); - statement.TryBind("@data", serialized); - statement.MoveNext(); - } + statement.TryBind("@InternalId", user.InternalId); + statement.TryBind("@data", serialized); + statement.MoveNext(); + } - }, TransactionMode); - } + }, TransactionMode); } } - private User GetUser(Guid guid, bool openLock) + private User GetUser(Guid guid, SQLiteDatabaseConnection connection) { - using (openLock ? WriteLock.Read() : null) + using (var statement = connection.PrepareStatement("select id,guid,data from LocalUsersv2 where guid=@guid")) { - using (var connection = CreateConnection(true)) - { - using (var statement = connection.PrepareStatement("select id,guid,data from LocalUsersv2 where guid=@guid")) - { - statement.TryBind("@guid", guid); + statement.TryBind("@guid", guid); - foreach (var row in statement.ExecuteQuery()) - { - return GetUser(row); - } - } + foreach (var row in statement.ExecuteQuery()) + { + return GetUser(row); } } @@ -218,14 +206,11 @@ namespace Emby.Server.Implementations.Data { var list = new List(); - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + foreach (var row in connection.Query("select id,guid,data from LocalUsersv2")) { - foreach (var row in connection.Query("select id,guid,data from LocalUsersv2")) - { - list.Add(GetUser(row)); - } + list.Add(GetUser(row)); } } @@ -245,19 +230,16 @@ namespace Emby.Server.Implementations.Data throw new ArgumentNullException(nameof(user)); } - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => + using (var statement = db.PrepareStatement("delete from LocalUsersv2 where Id=@id")) { - using (var statement = db.PrepareStatement("delete from LocalUsersv2 where Id=@id")) - { - statement.TryBind("@id", user.InternalId); - statement.MoveNext(); - } - }, TransactionMode); - } + statement.TryBind("@id", user.InternalId); + statement.MoveNext(); + } + }, TransactionMode); } } } diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs index 29b8dfd3d..abc23239e 100644 --- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs +++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs @@ -48,7 +48,7 @@ namespace Emby.Server.Implementations.Security } } - private void TryMigrate(ManagedConnection connection, bool tableNewlyCreated) + private void TryMigrate(SQLiteDatabaseConnection connection, bool tableNewlyCreated) { try { @@ -87,31 +87,28 @@ namespace Emby.Server.Implementations.Security throw new ArgumentNullException(nameof(info)); } - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => + using (var statement = db.PrepareStatement("insert into Tokens (AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, UserName, IsActive, DateCreated, DateLastActivity) values (@AccessToken, @DeviceId, @AppName, @AppVersion, @DeviceName, @UserId, @UserName, @IsActive, @DateCreated, @DateLastActivity)")) { - using (var statement = db.PrepareStatement("insert into Tokens (AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, UserName, IsActive, DateCreated, DateLastActivity) values (@AccessToken, @DeviceId, @AppName, @AppVersion, @DeviceName, @UserId, @UserName, @IsActive, @DateCreated, @DateLastActivity)")) - { - statement.TryBind("@AccessToken", info.AccessToken); - - statement.TryBind("@DeviceId", info.DeviceId); - statement.TryBind("@AppName", info.AppName); - statement.TryBind("@AppVersion", info.AppVersion); - statement.TryBind("@DeviceName", info.DeviceName); - statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N"))); - statement.TryBind("@UserName", info.UserName); - statement.TryBind("@IsActive", true); - statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue()); - statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue()); - - statement.MoveNext(); - } - - }, TransactionMode); - } + statement.TryBind("@AccessToken", info.AccessToken); + + statement.TryBind("@DeviceId", info.DeviceId); + statement.TryBind("@AppName", info.AppName); + statement.TryBind("@AppVersion", info.AppVersion); + statement.TryBind("@DeviceName", info.DeviceName); + statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N"))); + statement.TryBind("@UserName", info.UserName); + statement.TryBind("@IsActive", true); + statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue()); + statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue()); + + statement.MoveNext(); + } + + }, TransactionMode); } } @@ -122,31 +119,28 @@ namespace Emby.Server.Implementations.Security throw new ArgumentNullException(nameof(info)); } - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => + using (var statement = db.PrepareStatement("Update Tokens set AccessToken=@AccessToken, DeviceId=@DeviceId, AppName=@AppName, AppVersion=@AppVersion, DeviceName=@DeviceName, UserId=@UserId, UserName=@UserName, DateCreated=@DateCreated, DateLastActivity=@DateLastActivity where Id=@Id")) { - using (var statement = db.PrepareStatement("Update Tokens set AccessToken=@AccessToken, DeviceId=@DeviceId, AppName=@AppName, AppVersion=@AppVersion, DeviceName=@DeviceName, UserId=@UserId, UserName=@UserName, DateCreated=@DateCreated, DateLastActivity=@DateLastActivity where Id=@Id")) - { - statement.TryBind("@Id", info.Id); - - statement.TryBind("@AccessToken", info.AccessToken); - - statement.TryBind("@DeviceId", info.DeviceId); - statement.TryBind("@AppName", info.AppName); - statement.TryBind("@AppVersion", info.AppVersion); - statement.TryBind("@DeviceName", info.DeviceName); - statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N"))); - statement.TryBind("@UserName", info.UserName); - statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue()); - statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue()); - - statement.MoveNext(); - } - }, TransactionMode); - } + statement.TryBind("@Id", info.Id); + + statement.TryBind("@AccessToken", info.AccessToken); + + statement.TryBind("@DeviceId", info.DeviceId); + statement.TryBind("@AppName", info.AppName); + statement.TryBind("@AppVersion", info.AppVersion); + statement.TryBind("@DeviceName", info.DeviceName); + statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N"))); + statement.TryBind("@UserName", info.UserName); + statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue()); + statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue()); + + statement.MoveNext(); + } + }, TransactionMode); } } @@ -157,20 +151,17 @@ namespace Emby.Server.Implementations.Security throw new ArgumentNullException(nameof(info)); } - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => + using (var statement = db.PrepareStatement("Delete from Tokens where Id=@Id")) { - using (var statement = db.PrepareStatement("Delete from Tokens where Id=@Id")) - { - statement.TryBind("@Id", info.Id); + statement.TryBind("@Id", info.Id); - statement.MoveNext(); - } - }, TransactionMode); - } + statement.MoveNext(); + } + }, TransactionMode); } } @@ -257,45 +248,42 @@ namespace Emby.Server.Implementations.Security var list = new List(); - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + return connection.RunInTransaction(db => { - return connection.RunInTransaction(db => - { - var result = new QueryResult(); + var result = new QueryResult(); - var statementTexts = new List(); - statementTexts.Add(commandText); - statementTexts.Add("select count (Id) from Tokens" + whereTextWithoutPaging); + var statementTexts = new List(); + statementTexts.Add(commandText); + statementTexts.Add("select count (Id) from Tokens" + whereTextWithoutPaging); - var statements = PrepareAllSafe(db, statementTexts) - .ToList(); + var statements = PrepareAllSafe(db, statementTexts) + .ToList(); - using (var statement = statements[0]) - { - BindAuthenticationQueryParams(query, statement); + using (var statement = statements[0]) + { + BindAuthenticationQueryParams(query, statement); - foreach (var row in statement.ExecuteQuery()) - { - list.Add(Get(row)); - } + foreach (var row in statement.ExecuteQuery()) + { + list.Add(Get(row)); + } - using (var totalCountStatement = statements[1]) - { - BindAuthenticationQueryParams(query, totalCountStatement); + using (var totalCountStatement = statements[1]) + { + BindAuthenticationQueryParams(query, totalCountStatement); - result.TotalRecordCount = totalCountStatement.ExecuteQuery() - .SelectScalarInt() - .First(); - } + result.TotalRecordCount = totalCountStatement.ExecuteQuery() + .SelectScalarInt() + .First(); } + } - result.Items = list.ToArray(); - return result; + result.Items = list.ToArray(); + return result; - }, ReadTransactionMode); - } + }, ReadTransactionMode); } } @@ -358,31 +346,28 @@ namespace Emby.Server.Implementations.Security public DeviceOptions GetDeviceOptions(string deviceId) { - using (WriteLock.Read()) + using (var connection = CreateConnection(true)) { - using (var connection = CreateConnection(true)) + return connection.RunInTransaction(db => { - return connection.RunInTransaction(db => + using (var statement = PrepareStatementSafe(db, "select CustomName from Devices where Id=@DeviceId")) { - using (var statement = PrepareStatementSafe(db, "select CustomName from Devices where Id=@DeviceId")) - { - statement.TryBind("@DeviceId", deviceId); + statement.TryBind("@DeviceId", deviceId); - var result = new DeviceOptions(); + var result = new DeviceOptions(); - foreach (var row in statement.ExecuteQuery()) + foreach (var row in statement.ExecuteQuery()) + { + if (row[0].SQLiteType != SQLiteType.Null) { - if (row[0].SQLiteType != SQLiteType.Null) - { - result.CustomName = row[0].ToString(); - } + result.CustomName = row[0].ToString(); } - - return result; } - }, ReadTransactionMode); - } + return result; + } + + }, ReadTransactionMode); } } @@ -393,30 +378,27 @@ namespace Emby.Server.Implementations.Security throw new ArgumentNullException(nameof(options)); } - using (WriteLock.Write()) + using (var connection = CreateConnection()) { - using (var connection = CreateConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => + using (var statement = db.PrepareStatement("replace into devices (Id, CustomName, Capabilities) VALUES (@Id, @CustomName, (Select Capabilities from Devices where Id=@Id))")) { - using (var statement = db.PrepareStatement("replace into devices (Id, CustomName, Capabilities) VALUES (@Id, @CustomName, (Select Capabilities from Devices where Id=@Id))")) - { - statement.TryBind("@Id", deviceId); - - if (string.IsNullOrWhiteSpace(options.CustomName)) - { - statement.TryBindNull("@CustomName"); - } - else - { - statement.TryBind("@CustomName", options.CustomName); - } + statement.TryBind("@Id", deviceId); - statement.MoveNext(); + if (string.IsNullOrWhiteSpace(options.CustomName)) + { + statement.TryBindNull("@CustomName"); + } + else + { + statement.TryBind("@CustomName", options.CustomName); } - }, TransactionMode); - } + statement.MoveNext(); + } + + }, TransactionMode); } } } -- cgit v1.2.3 From c30ba14c1f9701638bbb47e81d3e7027cb778135 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Tue, 26 Feb 2019 18:30:13 +0100 Subject: Use a connection pool instead of creating new connections --- .../Activity/ActivityRepository.cs | 11 +- .../Data/BaseSqliteRepository.cs | 114 ++++++++++----------- .../Data/ManagedConnection.cs | 87 ++++++++++++++++ .../Data/SqliteDisplayPreferencesRepository.cs | 11 +- .../Data/SqliteItemRepository.cs | 47 +++++---- .../Data/SqliteUserDataRepository.cs | 11 +- .../Data/SqliteUserRepository.cs | 15 +-- .../Security/AuthenticationRepository.cs | 17 +-- 8 files changed, 199 insertions(+), 114 deletions(-) create mode 100644 Emby.Server.Implementations/Data/ManagedConnection.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index 495d96977..a38cb38d7 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -43,7 +43,8 @@ namespace Emby.Server.Implementations.Activity private void InitializeInternal() { - using (var connection = CreateConnection()) + CreateConnections().GetAwaiter().GetResult(); + using (var connection = GetConnection()) { RunDefaultInitialization(connection); @@ -57,7 +58,7 @@ namespace Emby.Server.Implementations.Activity } } - private void TryMigrate(SQLiteDatabaseConnection connection) + private void TryMigrate(ManagedConnection connection) { try { @@ -85,7 +86,7 @@ namespace Emby.Server.Implementations.Activity throw new ArgumentNullException(nameof(entry)); } - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -123,7 +124,7 @@ namespace Emby.Server.Implementations.Activity throw new ArgumentNullException(nameof(entry)); } - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -157,7 +158,7 @@ namespace Emby.Server.Implementations.Activity public QueryResult GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit) { - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { var commandText = BaseActivitySelectText; var whereClauses = new List(); diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 29edddb3a..6a19ea373 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -27,98 +28,93 @@ namespace Emby.Server.Implementations.Data internal static int ThreadSafeMode { get; set; } + protected virtual ConnectionFlags DefaultConnectionFlags => ConnectionFlags.SharedCached | ConnectionFlags.FullMutex; + + private readonly SemaphoreSlim WriteLock = new SemaphoreSlim(1, 1); + + private SQLiteDatabaseConnection WriteConnection; + + private readonly BlockingCollection ReadConnectionPool = new BlockingCollection(); + static BaseSqliteRepository() { ThreadSafeMode = raw.sqlite3_threadsafe(); } - private static bool _versionLogged; - private string _defaultWal; - protected SQLiteDatabaseConnection CreateConnection(bool isReadOnly = false) + protected async Task CreateConnections() { - if (!_versionLogged) - { - _versionLogged = true; - Logger.LogInformation("Sqlite version: " + SQLite3.Version); - Logger.LogInformation("Sqlite compiler options: " + string.Join(",", SQLite3.CompilerOptions)); - } - - ConnectionFlags connectionFlags; - - if (isReadOnly) - { - //Logger.LogInformation("Opening read connection"); - //connectionFlags = ConnectionFlags.Create; - connectionFlags = ConnectionFlags.ReadOnly; - } - else - { - //Logger.LogInformation("Opening write connection"); - connectionFlags = ConnectionFlags.Create; - connectionFlags |= ConnectionFlags.ReadWrite; - } - - connectionFlags |= ConnectionFlags.SharedCached; - connectionFlags |= ConnectionFlags.FullMutex; - - var db = SQLite3.Open(DbFilePath, connectionFlags, null); + await WriteLock.WaitAsync().ConfigureAwait(false); try { - if (string.IsNullOrWhiteSpace(_defaultWal)) + if (WriteConnection == null) { - _defaultWal = db.Query("PRAGMA journal_mode").SelectScalarString().First(); - - Logger.LogInformation("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal); + WriteConnection = SQLite3.Open( + DbFilePath, + DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite, + null); } - var queries = new List + if (string.IsNullOrWhiteSpace(_defaultWal)) { - //"PRAGMA cache size=-10000" - //"PRAGMA read_uncommitted = true", - //"PRAGMA synchronous=Normal" - }; + _defaultWal = WriteConnection.Query("PRAGMA journal_mode").SelectScalarString().First(); - if (CacheSize.HasValue) - { - queries.Add("PRAGMA cache_size=" + CacheSize.Value.ToString(CultureInfo.InvariantCulture)); + Logger.LogInformation("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal); } if (EnableTempStoreMemory) { - queries.Add("PRAGMA temp_store = memory"); + WriteConnection.Execute("PRAGMA temp_store = memory"); } else { - queries.Add("PRAGMA temp_store = file"); - } - - foreach (var query in queries) - { - db.Execute(query); + WriteConnection.Execute("PRAGMA temp_store = file"); } } catch { - using (db) - { - - } throw; } + finally + { + WriteLock.Release(); + } - return db; + // Add one reading connection for each thread + int threads = System.Environment.ProcessorCount; + for (int i = 0; i <= threads; i++) + { + ReadConnectionPool.Add(SQLite3.Open(DbFilePath, DefaultConnectionFlags | ConnectionFlags.ReadOnly, null)); + } } - public IStatement PrepareStatement(SQLiteDatabaseConnection connection, string sql) + protected ManagedConnection GetConnection(bool isReadOnly = false) + { + if (isReadOnly) + { + return new ManagedConnection(ReadConnectionPool.Take(), ReadConnectionPool); + } + else + { + if (WriteConnection == null) + { + throw new InvalidOperationException("Can't access the write connection at this time."); + } + + WriteLock.Wait(); + return new ManagedConnection(WriteConnection, WriteLock); + } + } + + public IStatement PrepareStatement(ManagedConnection connection, string sql) { return connection.PrepareStatement(sql); } - public IStatement PrepareStatementSafe(SQLiteDatabaseConnection connection, string sql) + public IStatement PrepareStatementSafe(ManagedConnection connection, string sql) { return connection.PrepareStatement(sql); } @@ -143,7 +139,7 @@ namespace Emby.Server.Implementations.Data return sql.Select(connection.PrepareStatement).ToList(); } - protected bool TableExists(SQLiteDatabaseConnection connection, string name) + protected bool TableExists(ManagedConnection connection, string name) { return connection.RunInTransaction(db => { @@ -163,7 +159,7 @@ namespace Emby.Server.Implementations.Data }, ReadTransactionMode); } - protected void RunDefaultInitialization(SQLiteDatabaseConnection db) + protected void RunDefaultInitialization(ManagedConnection db) { var queries = new List { @@ -192,9 +188,7 @@ namespace Emby.Server.Implementations.Data Logger.LogInformation("PRAGMA synchronous=" + db.Query("PRAGMA synchronous").SelectScalarString().First()); } - protected virtual bool EnableTempStoreMemory => false; - - protected virtual int? CacheSize => null; + protected virtual bool EnableTempStoreMemory => true; private bool _disposed; protected void CheckDisposed() diff --git a/Emby.Server.Implementations/Data/ManagedConnection.cs b/Emby.Server.Implementations/Data/ManagedConnection.cs new file mode 100644 index 000000000..71b934a9b --- /dev/null +++ b/Emby.Server.Implementations/Data/ManagedConnection.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using SQLitePCL.pretty; + +namespace Emby.Server.Implementations.Data +{ + public class ManagedConnection : IDisposable + { + private SQLiteDatabaseConnection _db; + private SemaphoreSlim _writeLock; + private BlockingCollection _readConPool; + private bool _disposed = false; + + public ManagedConnection(SQLiteDatabaseConnection db, SemaphoreSlim writeLock) + { + _db = db; + _writeLock = writeLock; + } + + public ManagedConnection(SQLiteDatabaseConnection db, BlockingCollection queue) + { + _db = db; + _readConPool = queue; + } + + public IStatement PrepareStatement(string sql) + { + return _db.PrepareStatement(sql); + } + + public IEnumerable PrepareAll(string sql) + { + return _db.PrepareAll(sql); + } + + public void ExecuteAll(string sql) + { + _db.ExecuteAll(sql); + } + + public void Execute(string sql, params object[] values) + { + _db.Execute(sql, values); + } + + public void RunQueries(string[] sql) + { + _db.RunQueries(sql); + } + + public void RunInTransaction(Action action, TransactionMode mode) + { + _db.RunInTransaction(action, mode); + } + + public T RunInTransaction(Func action, TransactionMode mode) + { + return _db.RunInTransaction(action, mode); + } + + public IEnumerable> Query(string sql) + { + return _db.Query(sql); + } + + public IEnumerable> Query(string sql, params object[] values) + { + return _db.Query(sql, values); + } + + public void Dispose() + { + if (_disposed) + { + return; + } + + _writeLock?.Release(); + _readConPool?.Add(_db); + + _db = null; // Don't dispose it + _disposed = true; + } + } +} diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs index 86a7c1551..d620f3962 100644 --- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs @@ -61,7 +61,8 @@ namespace Emby.Server.Implementations.Data /// Task. private void InitializeInternal() { - using (var connection = CreateConnection()) + CreateConnections().GetAwaiter().GetResult(); + using (var connection = GetConnection()) { RunDefaultInitialization(connection); @@ -98,7 +99,7 @@ namespace Emby.Server.Implementations.Data cancellationToken.ThrowIfCancellationRequested(); - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -139,7 +140,7 @@ namespace Emby.Server.Implementations.Data cancellationToken.ThrowIfCancellationRequested(); - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -168,7 +169,7 @@ namespace Emby.Server.Implementations.Data var guidId = displayPreferencesId.GetMD5(); - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where id = @id and userId=@userId and client=@client")) { @@ -199,7 +200,7 @@ namespace Emby.Server.Implementations.Data { var list = new List(); - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId")) { diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index baa5b713a..e96b6ce3a 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -92,8 +92,6 @@ namespace Emby.Server.Implementations.Data private const string ChaptersTableName = "Chapters2"; - protected override int? CacheSize => 20000; - protected override bool EnableTempStoreMemory => true; /// @@ -101,7 +99,8 @@ namespace Emby.Server.Implementations.Data /// public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager) { - using (var connection = CreateConnection()) + CreateConnections().GetAwaiter().GetResult(); + using (var connection = GetConnection()) { RunDefaultInitialization(connection); @@ -551,7 +550,7 @@ namespace Emby.Server.Implementations.Data CheckDisposed(); - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -602,7 +601,7 @@ namespace Emby.Server.Implementations.Data tuples.Add((item, ancestorIds, topParent, userdataKey, inheritedTags)); } - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -1186,7 +1185,7 @@ namespace Emby.Server.Implementations.Data CheckDisposed(); - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { using (var statement = PrepareStatementSafe(connection, "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where guid = @guid")) { @@ -1899,7 +1898,7 @@ namespace Emby.Server.Implementations.Data { CheckDisposed(); - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { var list = new List(); @@ -1928,7 +1927,7 @@ namespace Emby.Server.Implementations.Data { CheckDisposed(); - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { using (var statement = PrepareStatementSafe(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex")) { @@ -1997,7 +1996,7 @@ namespace Emby.Server.Implementations.Data throw new ArgumentNullException(nameof(chapters)); } - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -2533,7 +2532,7 @@ namespace Emby.Server.Implementations.Data commandText += " where " + string.Join(" AND ", whereClauses); } - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { using (var statement = PrepareStatementSafe(connection, commandText)) { @@ -2602,7 +2601,7 @@ namespace Emby.Server.Implementations.Data } } - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { var list = new List(); @@ -2820,7 +2819,7 @@ namespace Emby.Server.Implementations.Data statementTexts.Add(commandText); } - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { return connection.RunInTransaction(db => { @@ -3052,7 +3051,7 @@ namespace Emby.Server.Implementations.Data } } - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { var list = new List(); @@ -3119,7 +3118,7 @@ namespace Emby.Server.Implementations.Data } var list = new List>(); - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { using (var statement = PrepareStatementSafe(connection, commandText)) { @@ -3231,7 +3230,7 @@ namespace Emby.Server.Implementations.Data statementTexts.Add(commandText); } - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { return connection.RunInTransaction(db => { @@ -4862,7 +4861,7 @@ namespace Emby.Server.Implementations.Data private void UpdateInheritedTags(CancellationToken cancellationToken) { - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -4925,7 +4924,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type CheckDisposed(); - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -4982,7 +4981,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type commandText += " order by ListOrder"; - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { var list = new List(); using (var statement = PrepareStatementSafe(connection, commandText)) @@ -5019,7 +5018,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type commandText += " order by ListOrder"; - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { var list = new List(); @@ -5245,7 +5244,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type commandText += " Group By CleanValue"; - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { var list = new List(); @@ -5431,7 +5430,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type statementTexts.Add(countText); } - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { return connection.RunInTransaction(db => { @@ -5698,7 +5697,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type CheckDisposed(); - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -5815,7 +5814,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type cmdText += " order by StreamIndex ASC"; - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { var list = new List(); @@ -5859,7 +5858,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type cancellationToken.ThrowIfCancellationRequested(); - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index f544dfac4..9dc31d597 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -34,7 +34,8 @@ namespace Emby.Server.Implementations.Data /// Task. public void Initialize(IUserManager userManager) { - using (var connection = CreateConnection()) + CreateConnections().GetAwaiter().GetResult(); + using (var connection = GetConnection()) { var userDatasTableExists = TableExists(connection, "UserDatas"); var userDataTableExists = TableExists(connection, "userdata"); @@ -172,7 +173,7 @@ namespace Emby.Server.Implementations.Data { cancellationToken.ThrowIfCancellationRequested(); - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -240,7 +241,7 @@ namespace Emby.Server.Implementations.Data { cancellationToken.ThrowIfCancellationRequested(); - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -275,7 +276,7 @@ namespace Emby.Server.Implementations.Data throw new ArgumentNullException(nameof(key)); } - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where key =@Key and userId=@UserId")) { @@ -321,7 +322,7 @@ namespace Emby.Server.Implementations.Data var list = new List(); - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where userId=@UserId")) { diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs index 785452ad3..ef8ae60b3 100644 --- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs @@ -40,7 +40,8 @@ namespace Emby.Server.Implementations.Data /// Task. public void Initialize() { - using (var connection = CreateConnection()) + CreateConnections().GetAwaiter().GetResult(); + using (var connection = GetConnection()) { RunDefaultInitialization(connection); @@ -60,7 +61,7 @@ namespace Emby.Server.Implementations.Data } } - private void TryMigrateToLocalUsersTable(SQLiteDatabaseConnection connection) + private void TryMigrateToLocalUsersTable(ManagedConnection connection) { try { @@ -119,7 +120,7 @@ namespace Emby.Server.Implementations.Data var serialized = _jsonSerializer.SerializeToBytes(user); - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -153,7 +154,7 @@ namespace Emby.Server.Implementations.Data var serialized = _jsonSerializer.SerializeToBytes(user); - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -168,7 +169,7 @@ namespace Emby.Server.Implementations.Data } } - private User GetUser(Guid guid, SQLiteDatabaseConnection connection) + private User GetUser(Guid guid, ManagedConnection connection) { using (var statement = connection.PrepareStatement("select id,guid,data from LocalUsersv2 where guid=@guid")) { @@ -206,7 +207,7 @@ namespace Emby.Server.Implementations.Data { var list = new List(); - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { foreach (var row in connection.Query("select id,guid,data from LocalUsersv2")) { @@ -230,7 +231,7 @@ namespace Emby.Server.Implementations.Data throw new ArgumentNullException(nameof(user)); } - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs index abc23239e..dfcd6af0d 100644 --- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs +++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs @@ -23,7 +23,8 @@ namespace Emby.Server.Implementations.Security public void Initialize() { - using (var connection = CreateConnection()) + CreateConnections().GetAwaiter().GetResult(); + using (var connection = GetConnection()) { RunDefaultInitialization(connection); @@ -48,7 +49,7 @@ namespace Emby.Server.Implementations.Security } } - private void TryMigrate(SQLiteDatabaseConnection connection, bool tableNewlyCreated) + private void TryMigrate(ManagedConnection connection, bool tableNewlyCreated) { try { @@ -87,7 +88,7 @@ namespace Emby.Server.Implementations.Security throw new ArgumentNullException(nameof(info)); } - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -119,7 +120,7 @@ namespace Emby.Server.Implementations.Security throw new ArgumentNullException(nameof(info)); } - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -151,7 +152,7 @@ namespace Emby.Server.Implementations.Security throw new ArgumentNullException(nameof(info)); } - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { @@ -248,7 +249,7 @@ namespace Emby.Server.Implementations.Security var list = new List(); - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { return connection.RunInTransaction(db => { @@ -346,7 +347,7 @@ namespace Emby.Server.Implementations.Security public DeviceOptions GetDeviceOptions(string deviceId) { - using (var connection = CreateConnection(true)) + using (var connection = GetConnection(true)) { return connection.RunInTransaction(db => { @@ -378,7 +379,7 @@ namespace Emby.Server.Implementations.Security throw new ArgumentNullException(nameof(options)); } - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { -- cgit v1.2.3 From e5248cfaa25a4aaa00e9cc8ba7d23a2b3c690b6e Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Tue, 26 Feb 2019 18:58:33 +0100 Subject: Properly dispose --- .../Data/BaseSqliteRepository.cs | 53 ++++++++++++---------- 1 file changed, 30 insertions(+), 23 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 6a19ea373..33bbbbfe4 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -28,7 +27,7 @@ namespace Emby.Server.Implementations.Data internal static int ThreadSafeMode { get; set; } - protected virtual ConnectionFlags DefaultConnectionFlags => ConnectionFlags.SharedCached | ConnectionFlags.FullMutex; + protected virtual ConnectionFlags DefaultConnectionFlags => ConnectionFlags.SharedCached | ConnectionFlags.NoMutex; private readonly SemaphoreSlim WriteLock = new SemaphoreSlim(1, 1); @@ -39,6 +38,7 @@ namespace Emby.Server.Implementations.Data static BaseSqliteRepository() { ThreadSafeMode = raw.sqlite3_threadsafe(); + raw.sqlite3_enable_shared_cache(1); } private string _defaultWal; @@ -55,6 +55,7 @@ namespace Emby.Server.Implementations.Data DbFilePath, DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite, null); + SQLiteDatabaseConnectionBuilder.InMemory. } if (string.IsNullOrWhiteSpace(_defaultWal)) @@ -110,34 +111,22 @@ namespace Emby.Server.Implementations.Data } public IStatement PrepareStatement(ManagedConnection connection, string sql) - { - return connection.PrepareStatement(sql); - } + => connection.PrepareStatement(sql); public IStatement PrepareStatementSafe(ManagedConnection connection, string sql) - { - return connection.PrepareStatement(sql); - } + => connection.PrepareStatement(sql); public IStatement PrepareStatement(IDatabaseConnection connection, string sql) - { - return connection.PrepareStatement(sql); - } + => connection.PrepareStatement(sql); public IStatement PrepareStatementSafe(IDatabaseConnection connection, string sql) - { - return connection.PrepareStatement(sql); - } + => connection.PrepareStatement(sql); - public List PrepareAll(IDatabaseConnection connection, IEnumerable sql) - { - return PrepareAllSafe(connection, sql); - } + public IEnumerable PrepareAll(IDatabaseConnection connection, IEnumerable sql) + => PrepareAllSafe(connection, sql); - public List PrepareAllSafe(IDatabaseConnection connection, IEnumerable sql) - { - return sql.Select(connection.PrepareStatement).ToList(); - } + public IEnumerable PrepareAllSafe(IDatabaseConnection connection, IEnumerable sql) + => sql.Select(connection.PrepareStatement); protected bool TableExists(ManagedConnection connection, string name) { @@ -201,7 +190,6 @@ namespace Emby.Server.Implementations.Data public void Dispose() { - Dispose(true); } @@ -218,6 +206,25 @@ namespace Emby.Server.Implementations.Data return; } + if (dispose) + { + WriteLock.Wait(); + try + { + WriteConnection.Dispose(); + } + finally + { + WriteLock.Release(); + } + + foreach (var i in ReadConnectionPool) + { + i.Dispose(); + } + + ReadConnectionPool.Dispose(); + } _disposed = true; } -- cgit v1.2.3 From 30842656a7fb1d23eef245dcfb57287ebdb960a0 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Tue, 26 Feb 2019 19:01:18 +0100 Subject: Properly dispose --- Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 1 - Emby.Server.Implementations/Data/SqliteItemRepository.cs | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 33bbbbfe4..db63f68d0 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -55,7 +55,6 @@ namespace Emby.Server.Implementations.Data DbFilePath, DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite, null); - SQLiteDatabaseConnectionBuilder.InMemory. } if (string.IsNullOrWhiteSpace(_defaultWal)) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index e96b6ce3a..a2f490c4a 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -616,7 +616,7 @@ namespace Emby.Server.Implementations.Data { GetSaveItemCommandText(), "delete from AncestorIds where ItemId=@ItemId" - }); + }).ToList(); using (var saveItemStatement = statements[0]) using (var deleteAncestorsStatement = statements[1]) @@ -2824,7 +2824,7 @@ namespace Emby.Server.Implementations.Data return connection.RunInTransaction(db => { var result = new QueryResult(); - var statements = PrepareAllSafe(db, statementTexts); + var statements = PrepareAllSafe(db, statementTexts).ToList(); if (!isReturningZeroItems) { @@ -3236,7 +3236,7 @@ namespace Emby.Server.Implementations.Data { var result = new QueryResult(); - var statements = PrepareAllSafe(db, statementTexts); + var statements = PrepareAllSafe(db, statementTexts).ToList(); if (!isReturningZeroItems) { @@ -5437,7 +5437,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type var list = new List<(BaseItem, ItemCounts)>(); var result = new QueryResult<(BaseItem, ItemCounts)>(); - var statements = PrepareAllSafe(db, statementTexts); + var statements = PrepareAllSafe(db, statementTexts).ToList(); if (!isReturningZeroItems) { -- cgit v1.2.3 From 27c29bbb4c75d5fc5c9111c5552c37a1f137dd58 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 11 Mar 2019 22:33:27 +0100 Subject: Back to a single connection --- .../Activity/ActivityRepository.cs | 1 - .../Data/BaseSqliteRepository.cs | 80 ++++++---------------- .../Data/ManagedConnection.cs | 13 +--- .../Data/SqliteDisplayPreferencesRepository.cs | 1 - .../Data/SqliteItemRepository.cs | 1 - .../Data/SqliteUserDataRepository.cs | 1 - .../Data/SqliteUserRepository.cs | 4 +- .../Security/AuthenticationRepository.cs | 1 - .../ScheduledTasksWebSocketListener.cs | 1 - .../Session/SessionInfoWebSocketListener.cs | 1 - 10 files changed, 23 insertions(+), 81 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index a38cb38d7..63931e134 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -43,7 +43,6 @@ namespace Emby.Server.Implementations.Activity private void InitializeInternal() { - CreateConnections().GetAwaiter().GetResult(); using (var connection = GetConnection()) { RunDefaultInitialization(connection); diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index db63f68d0..33a0b7ddf 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; using SQLitePCL; using SQLitePCL.pretty; @@ -33,8 +31,6 @@ namespace Emby.Server.Implementations.Data private SQLiteDatabaseConnection WriteConnection; - private readonly BlockingCollection ReadConnectionPool = new BlockingCollection(); - static BaseSqliteRepository() { ThreadSafeMode = raw.sqlite3_threadsafe(); @@ -43,70 +39,37 @@ namespace Emby.Server.Implementations.Data private string _defaultWal; - protected async Task CreateConnections() + protected ManagedConnection GetConnection(bool isReadOnly = false) { - await WriteLock.WaitAsync().ConfigureAwait(false); - - try + WriteLock.Wait(); + if (WriteConnection != null) { - if (WriteConnection == null) - { - WriteConnection = SQLite3.Open( - DbFilePath, - DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite, - null); - } + return new ManagedConnection(WriteConnection, WriteLock); + } - if (string.IsNullOrWhiteSpace(_defaultWal)) - { - _defaultWal = WriteConnection.Query("PRAGMA journal_mode").SelectScalarString().First(); + WriteConnection = SQLite3.Open( + DbFilePath, + DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite, + null); - Logger.LogInformation("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal); - } - if (EnableTempStoreMemory) - { - WriteConnection.Execute("PRAGMA temp_store = memory"); - } - else - { - WriteConnection.Execute("PRAGMA temp_store = file"); - } - } - catch + if (string.IsNullOrWhiteSpace(_defaultWal)) { + _defaultWal = WriteConnection.Query("PRAGMA journal_mode").SelectScalarString().First(); - throw; - } - finally - { - WriteLock.Release(); + Logger.LogInformation("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal); } - // Add one reading connection for each thread - int threads = System.Environment.ProcessorCount; - for (int i = 0; i <= threads; i++) - { - ReadConnectionPool.Add(SQLite3.Open(DbFilePath, DefaultConnectionFlags | ConnectionFlags.ReadOnly, null)); - } - } - - protected ManagedConnection GetConnection(bool isReadOnly = false) - { - if (isReadOnly) + if (EnableTempStoreMemory) { - return new ManagedConnection(ReadConnectionPool.Take(), ReadConnectionPool); + WriteConnection.Execute("PRAGMA temp_store = memory"); } else { - if (WriteConnection == null) - { - throw new InvalidOperationException("Can't access the write connection at this time."); - } - - WriteLock.Wait(); - return new ManagedConnection(WriteConnection, WriteLock); + WriteConnection.Execute("PRAGMA temp_store = file"); } + + return new ManagedConnection(WriteConnection, WriteLock); } public IStatement PrepareStatement(ManagedConnection connection, string sql) @@ -217,14 +180,11 @@ namespace Emby.Server.Implementations.Data WriteLock.Release(); } - foreach (var i in ReadConnectionPool) - { - i.Dispose(); - } - - ReadConnectionPool.Dispose(); + WriteLock.Dispose(); } + WriteConnection = null; + _disposed = true; } diff --git a/Emby.Server.Implementations/Data/ManagedConnection.cs b/Emby.Server.Implementations/Data/ManagedConnection.cs index 71b934a9b..4c3424410 100644 --- a/Emby.Server.Implementations/Data/ManagedConnection.cs +++ b/Emby.Server.Implementations/Data/ManagedConnection.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using SQLitePCL.pretty; @@ -9,8 +8,7 @@ namespace Emby.Server.Implementations.Data public class ManagedConnection : IDisposable { private SQLiteDatabaseConnection _db; - private SemaphoreSlim _writeLock; - private BlockingCollection _readConPool; + private readonly SemaphoreSlim _writeLock; private bool _disposed = false; public ManagedConnection(SQLiteDatabaseConnection db, SemaphoreSlim writeLock) @@ -19,12 +17,6 @@ namespace Emby.Server.Implementations.Data _writeLock = writeLock; } - public ManagedConnection(SQLiteDatabaseConnection db, BlockingCollection queue) - { - _db = db; - _readConPool = queue; - } - public IStatement PrepareStatement(string sql) { return _db.PrepareStatement(sql); @@ -77,8 +69,7 @@ namespace Emby.Server.Implementations.Data return; } - _writeLock?.Release(); - _readConPool?.Add(_db); + _writeLock.Release(); _db = null; // Don't dispose it _disposed = true; diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs index d620f3962..7f8df7626 100644 --- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs @@ -61,7 +61,6 @@ namespace Emby.Server.Implementations.Data /// Task. private void InitializeInternal() { - CreateConnections().GetAwaiter().GetResult(); using (var connection = GetConnection()) { RunDefaultInitialization(connection); diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index a2f490c4a..9e96d7745 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -99,7 +99,6 @@ namespace Emby.Server.Implementations.Data /// public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager) { - CreateConnections().GetAwaiter().GetResult(); using (var connection = GetConnection()) { RunDefaultInitialization(connection); diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index 9dc31d597..355755014 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -34,7 +34,6 @@ namespace Emby.Server.Implementations.Data /// Task. public void Initialize(IUserManager userManager) { - CreateConnections().GetAwaiter().GetResult(); using (var connection = GetConnection()) { var userDatasTableExists = TableExists(connection, "UserDatas"); diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs index ef8ae60b3..e79b3d601 100644 --- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs @@ -40,7 +40,6 @@ namespace Emby.Server.Implementations.Data /// Task. public void Initialize() { - CreateConnections().GetAwaiter().GetResult(); using (var connection = GetConnection()) { RunDefaultInitialization(connection); @@ -90,8 +89,7 @@ namespace Emby.Server.Implementations.Data user.Password = null; var serialized = _jsonSerializer.SerializeToBytes(user); - using (WriteLock.Write()) - using (var connection = CreateConnection()) + using (var connection = GetConnection()) { connection.RunInTransaction(db => { diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs index dfcd6af0d..efe56c081 100644 --- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs +++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs @@ -23,7 +23,6 @@ namespace Emby.Server.Implementations.Security public void Initialize() { - CreateConnections().GetAwaiter().GetResult(); using (var connection = GetConnection()) { RunDefaultInitialization(connection); diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs index d24a18743..d9530ffb7 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Events; diff --git a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs index b79e9f84b..f1a6622fb 100644 --- a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs +++ b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; -- cgit v1.2.3 From b6954f3bfd68c87923348444a5923406cf672f9b Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 11 Mar 2019 23:07:38 +0100 Subject: More --- .../Activity/ActivityRepository.cs | 8 ++--- .../Data/BaseSqliteRepository.cs | 42 +++++++++------------- .../Data/SqliteDisplayPreferencesRepository.cs | 6 ++-- Jellyfin.Server/Program.cs | 7 +++- 4 files changed, 30 insertions(+), 33 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index 63931e134..cac1a9feb 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -15,14 +15,14 @@ namespace Emby.Server.Implementations.Activity { public class ActivityRepository : BaseSqliteRepository, IActivityRepository { - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - protected IFileSystem FileSystem { get; private set; } + private static readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private readonly IFileSystem _fileSystem; public ActivityRepository(ILoggerFactory loggerFactory, IServerApplicationPaths appPaths, IFileSystem fileSystem) : base(loggerFactory.CreateLogger(nameof(ActivityRepository))) { DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db"); - FileSystem = fileSystem; + _fileSystem = fileSystem; } public void Initialize() @@ -35,7 +35,7 @@ namespace Emby.Server.Implementations.Activity { Logger.LogError(ex, "Error loading database file. Will reset and retry."); - FileSystem.DeleteFile(DbFilePath); + _fileSystem.DeleteFile(DbFilePath); InitializeInternal(); } diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 33a0b7ddf..f3bd07bb0 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -12,7 +12,7 @@ namespace Emby.Server.Implementations.Data { protected string DbFilePath { get; set; } - protected ILogger Logger { get; private set; } + protected ILogger Logger { get; } protected BaseSqliteRepository(ILogger logger) { @@ -23,31 +23,23 @@ namespace Emby.Server.Implementations.Data protected TransactionMode ReadTransactionMode => TransactionMode.Deferred; - internal static int ThreadSafeMode { get; set; } - protected virtual ConnectionFlags DefaultConnectionFlags => ConnectionFlags.SharedCached | ConnectionFlags.NoMutex; - private readonly SemaphoreSlim WriteLock = new SemaphoreSlim(1, 1); - - private SQLiteDatabaseConnection WriteConnection; + private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); - static BaseSqliteRepository() - { - ThreadSafeMode = raw.sqlite3_threadsafe(); - raw.sqlite3_enable_shared_cache(1); - } + private SQLiteDatabaseConnection _writeConnection; private string _defaultWal; - protected ManagedConnection GetConnection(bool isReadOnly = false) + protected ManagedConnection GetConnection(bool _ = false) { - WriteLock.Wait(); - if (WriteConnection != null) + _writeLock.Wait(); + if (_writeConnection != null) { - return new ManagedConnection(WriteConnection, WriteLock); + return new ManagedConnection(_writeConnection, _writeLock); } - WriteConnection = SQLite3.Open( + _writeConnection = SQLite3.Open( DbFilePath, DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite, null); @@ -55,21 +47,21 @@ namespace Emby.Server.Implementations.Data if (string.IsNullOrWhiteSpace(_defaultWal)) { - _defaultWal = WriteConnection.Query("PRAGMA journal_mode").SelectScalarString().First(); + _defaultWal = _writeConnection.Query("PRAGMA journal_mode").SelectScalarString().First(); Logger.LogInformation("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal); } if (EnableTempStoreMemory) { - WriteConnection.Execute("PRAGMA temp_store = memory"); + _writeConnection.Execute("PRAGMA temp_store = memory"); } else { - WriteConnection.Execute("PRAGMA temp_store = file"); + _writeConnection.Execute("PRAGMA temp_store = file"); } - return new ManagedConnection(WriteConnection, WriteLock); + return new ManagedConnection(_writeConnection, _writeLock); } public IStatement PrepareStatement(ManagedConnection connection, string sql) @@ -170,20 +162,20 @@ namespace Emby.Server.Implementations.Data if (dispose) { - WriteLock.Wait(); + _writeLock.Wait(); try { - WriteConnection.Dispose(); + _writeConnection.Dispose(); } finally { - WriteLock.Release(); + _writeLock.Release(); } - WriteLock.Dispose(); + _writeLock.Dispose(); } - WriteConnection = null; + _writeConnection = null; _disposed = true; } diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs index 7f8df7626..1d44b0b29 100644 --- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs @@ -18,13 +18,13 @@ namespace Emby.Server.Implementations.Data /// public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository { - protected IFileSystem FileSystem { get; private set; } + private readonly IFileSystem _fileSystem; public SqliteDisplayPreferencesRepository(ILoggerFactory loggerFactory, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IFileSystem fileSystem) : base(loggerFactory.CreateLogger(nameof(SqliteDisplayPreferencesRepository))) { _jsonSerializer = jsonSerializer; - FileSystem = fileSystem; + _fileSystem = fileSystem; DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db"); } @@ -49,7 +49,7 @@ namespace Emby.Server.Implementations.Data { Logger.LogError(ex, "Error loading database file. Will reset and retry."); - FileSystem.DeleteFile(DbFilePath); + _fileSystem.DeleteFile(DbFilePath); InitializeInternal(); } diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 91752a16d..11c09db98 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -24,6 +24,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Serilog; using Serilog.AspNetCore; +using SQLitePCL; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Jellyfin.Server @@ -126,7 +127,11 @@ namespace Jellyfin.Server ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); #pragma warning restore CA5359 - SQLitePCL.Batteries_V2.Init(); + Batteries_V2.Init(); + if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK) + { + Console.WriteLine("WARN: Failed to enable shared cache for SQLite"); + } using (var appHost = new CoreAppHost( appPaths, -- cgit v1.2.3 From e88ebd748d98cf9bd2a3978d36254f1644ce751a Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Tue, 2 Apr 2019 22:15:18 +0200 Subject: Final fixes --- .../Data/BaseSqliteRepository.cs | 44 +++++++++------------- .../Data/SqliteItemRepository.cs | 40 ++++++++++---------- .../Data/SqliteUserDataRepository.cs | 7 +++- .../Security/AuthenticationRepository.cs | 6 +-- Jellyfin.Server/Program.cs | 4 -- 5 files changed, 47 insertions(+), 54 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index f3bd07bb0..63cef80b0 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using Microsoft.Extensions.Logging; -using SQLitePCL; using SQLitePCL.pretty; namespace Emby.Server.Implementations.Data @@ -23,23 +22,23 @@ namespace Emby.Server.Implementations.Data protected TransactionMode ReadTransactionMode => TransactionMode.Deferred; - protected virtual ConnectionFlags DefaultConnectionFlags => ConnectionFlags.SharedCached | ConnectionFlags.NoMutex; + protected virtual ConnectionFlags DefaultConnectionFlags => ConnectionFlags.NoMutex; - private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); + protected SemaphoreSlim WriteLock = new SemaphoreSlim(1, 1); - private SQLiteDatabaseConnection _writeConnection; + protected SQLiteDatabaseConnection WriteConnection; private string _defaultWal; protected ManagedConnection GetConnection(bool _ = false) { - _writeLock.Wait(); - if (_writeConnection != null) + WriteLock.Wait(); + if (WriteConnection != null) { - return new ManagedConnection(_writeConnection, _writeLock); + return new ManagedConnection(WriteConnection, WriteLock); } - _writeConnection = SQLite3.Open( + WriteConnection = SQLite3.Open( DbFilePath, DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite, null); @@ -47,38 +46,29 @@ namespace Emby.Server.Implementations.Data if (string.IsNullOrWhiteSpace(_defaultWal)) { - _defaultWal = _writeConnection.Query("PRAGMA journal_mode").SelectScalarString().First(); + _defaultWal = WriteConnection.Query("PRAGMA journal_mode").SelectScalarString().First(); Logger.LogInformation("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal); } if (EnableTempStoreMemory) { - _writeConnection.Execute("PRAGMA temp_store = memory"); + WriteConnection.Execute("PRAGMA temp_store = memory"); } else { - _writeConnection.Execute("PRAGMA temp_store = file"); + WriteConnection.Execute("PRAGMA temp_store = file"); } - return new ManagedConnection(_writeConnection, _writeLock); + return new ManagedConnection(WriteConnection, WriteLock); } public IStatement PrepareStatement(ManagedConnection connection, string sql) => connection.PrepareStatement(sql); - public IStatement PrepareStatementSafe(ManagedConnection connection, string sql) - => connection.PrepareStatement(sql); - public IStatement PrepareStatement(IDatabaseConnection connection, string sql) => connection.PrepareStatement(sql); - public IStatement PrepareStatementSafe(IDatabaseConnection connection, string sql) - => connection.PrepareStatement(sql); - - public IEnumerable PrepareAll(IDatabaseConnection connection, IEnumerable sql) - => PrepareAllSafe(connection, sql); - public IEnumerable PrepareAllSafe(IDatabaseConnection connection, IEnumerable sql) => sql.Select(connection.PrepareStatement); @@ -145,6 +135,7 @@ namespace Emby.Server.Implementations.Data public void Dispose() { Dispose(true); + GC.SuppressFinalize(this); } private readonly object _disposeLock = new object(); @@ -162,20 +153,21 @@ namespace Emby.Server.Implementations.Data if (dispose) { - _writeLock.Wait(); + WriteLock.Wait(); try { - _writeConnection.Dispose(); + WriteConnection.Dispose(); } finally { - _writeLock.Release(); + WriteLock.Release(); } - _writeLock.Dispose(); + WriteLock.Dispose(); } - _writeConnection = null; + WriteConnection = null; + WriteLock = null; _disposed = true; } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 9e96d7745..462d91e41 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -317,7 +317,7 @@ namespace Emby.Server.Implementations.Data connection.RunQueries(postQueries); } - userDataRepo.Initialize(userManager); + userDataRepo.Initialize(userManager, WriteLock, WriteConnection); } private static readonly string[] _retriveItemColumns = @@ -551,16 +551,16 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction((Action)(db => { - using (var saveImagesStatement = PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id")) + using (var saveImagesStatement = base.PrepareStatement((IDatabaseConnection)db, (string)"Update TypedBaseItems set Images=@Images where guid=@Id")) { saveImagesStatement.TryBind("@Id", item.Id.ToGuidBlob()); saveImagesStatement.TryBind("@Images", SerializeImages(item)); saveImagesStatement.MoveNext(); } - }, TransactionMode); + }), TransactionMode); } } @@ -1186,7 +1186,7 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection(true)) { - using (var statement = PrepareStatementSafe(connection, "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where guid = @guid")) + using (var statement = PrepareStatement(connection, "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where guid = @guid")) { statement.TryBind("@guid", id); @@ -1901,7 +1901,7 @@ namespace Emby.Server.Implementations.Data { var list = new List(); - using (var statement = PrepareStatementSafe(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc")) + using (var statement = PrepareStatement(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc")) { statement.TryBind("@ItemId", item.Id); @@ -1928,7 +1928,7 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection(true)) { - using (var statement = PrepareStatementSafe(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex")) + using (var statement = PrepareStatement(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex")) { statement.TryBind("@ItemId", item.Id); statement.TryBind("@ChapterIndex", index); @@ -2028,7 +2028,7 @@ namespace Emby.Server.Implementations.Data } insertText.Length -= 1; // Remove last , - using (var statement = PrepareStatementSafe(db, insertText.ToString())) + using (var statement = PrepareStatement(db, insertText.ToString())) { statement.TryBind("@ItemId", idBlob); @@ -2533,7 +2533,7 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection(true)) { - using (var statement = PrepareStatementSafe(connection, commandText)) + using (var statement = PrepareStatement(connection, commandText)) { if (EnableJoinUserData(query)) { @@ -2604,7 +2604,7 @@ namespace Emby.Server.Implementations.Data { var list = new List(); - using (var statement = PrepareStatementSafe(connection, commandText)) + using (var statement = PrepareStatement(connection, commandText)) { if (EnableJoinUserData(query)) { @@ -3054,7 +3054,7 @@ namespace Emby.Server.Implementations.Data { var list = new List(); - using (var statement = PrepareStatementSafe(connection, commandText)) + using (var statement = PrepareStatement(connection, commandText)) { if (EnableJoinUserData(query)) { @@ -3119,7 +3119,7 @@ namespace Emby.Server.Implementations.Data var list = new List>(); using (var connection = GetConnection(true)) { - using (var statement = PrepareStatementSafe(connection, commandText)) + using (var statement = PrepareStatement(connection, commandText)) { if (EnableJoinUserData(query)) { @@ -4983,7 +4983,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type using (var connection = GetConnection(true)) { var list = new List(); - using (var statement = PrepareStatementSafe(connection, commandText)) + using (var statement = PrepareStatement(connection, commandText)) { // Run this again to bind the params GetPeopleWhereClauses(query, statement); @@ -5021,7 +5021,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type { var list = new List(); - using (var statement = PrepareStatementSafe(connection, commandText)) + using (var statement = PrepareStatement(connection, commandText)) { // Run this again to bind the params GetPeopleWhereClauses(query, statement); @@ -5146,7 +5146,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type insertText.AppendFormat("(@ItemId, @AncestorId{0}, @AncestorIdText{0})", i.ToString(CultureInfo.InvariantCulture)); } - using (var statement = PrepareStatementSafe(db, insertText.ToString())) + using (var statement = PrepareStatement(db, insertText.ToString())) { statement.TryBind("@ItemId", itemIdBlob); @@ -5247,7 +5247,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type { var list = new List(); - using (var statement = PrepareStatementSafe(connection, commandText)) + using (var statement = PrepareStatement(connection, commandText)) { foreach (var row in statement.ExecuteQuery()) { @@ -5651,7 +5651,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type isSubsequentRow = true; } - using (var statement = PrepareStatementSafe(db, insertText.ToString())) + using (var statement = PrepareStatement(db, insertText.ToString())) { statement.TryBind("@ItemId", idBlob); @@ -5735,7 +5735,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type isSubsequentRow = true; } - using (var statement = PrepareStatementSafe(db, insertText.ToString())) + using (var statement = PrepareStatement(db, insertText.ToString())) { statement.TryBind("@ItemId", idBlob); @@ -5817,7 +5817,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type { var list = new List(); - using (var statement = PrepareStatementSafe(connection, cmdText)) + using (var statement = PrepareStatement(connection, cmdText)) { statement.TryBind("@ItemId", query.ItemId.ToGuidBlob()); @@ -5902,7 +5902,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type insertText.Append(")"); } - using (var statement = PrepareStatementSafe(db, insertText.ToString())) + using (var statement = PrepareStatement(db, insertText.ToString())) { statement.TryBind("@ItemId", idBlob); diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index 355755014..6ac398937 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -32,8 +32,13 @@ namespace Emby.Server.Implementations.Data /// Opens the connection to the database /// /// Task. - public void Initialize(IUserManager userManager) + public void Initialize(IUserManager userManager, SemaphoreSlim dbLock, SQLiteDatabaseConnection dbConnection) { + WriteLock.Dispose(); + WriteLock = dbLock; + WriteConnection?.Dispose(); + WriteConnection = dbConnection; + using (var connection = GetConnection()) { var userDatasTableExists = TableExists(connection, "UserDatas"); diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs index efe56c081..29afb9f64 100644 --- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs +++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs @@ -348,9 +348,9 @@ namespace Emby.Server.Implementations.Security { using (var connection = GetConnection(true)) { - return connection.RunInTransaction(db => + return connection.RunInTransaction((Func)(db => { - using (var statement = PrepareStatementSafe(db, "select CustomName from Devices where Id=@DeviceId")) + using (var statement = base.PrepareStatement((IDatabaseConnection)db, (string)"select CustomName from Devices where Id=@DeviceId")) { statement.TryBind("@DeviceId", deviceId); @@ -367,7 +367,7 @@ namespace Emby.Server.Implementations.Security return result; } - }, ReadTransactionMode); + }), ReadTransactionMode); } } diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 11c09db98..9454b28d6 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -128,10 +128,6 @@ namespace Jellyfin.Server #pragma warning restore CA5359 Batteries_V2.Init(); - if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK) - { - Console.WriteLine("WARN: Failed to enable shared cache for SQLite"); - } using (var appHost = new CoreAppHost( appPaths, -- cgit v1.2.3 From 02b864e41b80c07941d262d9d3723c0e7967d557 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Tue, 2 Apr 2019 22:18:13 +0200 Subject: Last line? --- Emby.Server.Implementations/Activity/ActivityRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index cac1a9feb..e45add355 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -15,7 +15,7 @@ namespace Emby.Server.Implementations.Activity { public class ActivityRepository : BaseSqliteRepository, IActivityRepository { - private static readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US")); private readonly IFileSystem _fileSystem; public ActivityRepository(ILoggerFactory loggerFactory, IServerApplicationPaths appPaths, IFileSystem fileSystem) -- cgit v1.2.3 From d00ad28efd10e2bb312c9a08055f83df26065494 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 3 Apr 2019 13:46:07 +0200 Subject: Address comments --- .../Activity/ActivityRepository.cs | 2 +- .../Data/BaseSqliteRepository.cs | 2 +- .../Data/SqliteItemRepository.cs | 24 ++++++++++++++-------- .../Data/SqliteUserDataRepository.cs | 5 ----- .../Security/AuthenticationRepository.cs | 8 ++++---- 5 files changed, 21 insertions(+), 20 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index e45add355..f8a1b32af 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -215,7 +215,7 @@ namespace Emby.Server.Implementations.Activity var list = new List(); var result = new QueryResult(); - var statements = PrepareAllSafe(db, statementTexts).ToList(); + var statements = PrepareAll(db, statementTexts).ToList(); using (var statement = statements[0]) { diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 63cef80b0..c5af156bb 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -69,7 +69,7 @@ namespace Emby.Server.Implementations.Data public IStatement PrepareStatement(IDatabaseConnection connection, string sql) => connection.PrepareStatement(sql); - public IEnumerable PrepareAllSafe(IDatabaseConnection connection, IEnumerable sql) + public IEnumerable PrepareAll(IDatabaseConnection connection, IEnumerable sql) => sql.Select(connection.PrepareStatement); protected bool TableExists(ManagedConnection connection, string name) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 462d91e41..5dc104347 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -518,10 +518,11 @@ namespace Emby.Server.Implementations.Data { saveItemCommandCommandText += ","; } + saveItemCommandCommandText += "@" + saveColumns[i]; } - saveItemCommandCommandText += ")"; - return saveItemCommandCommandText; + + return saveItemCommandCommandText + ")"; } /// @@ -551,16 +552,16 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection()) { - connection.RunInTransaction((Action)(db => + connection.RunInTransaction(db => { - using (var saveImagesStatement = base.PrepareStatement((IDatabaseConnection)db, (string)"Update TypedBaseItems set Images=@Images where guid=@Id")) + using (var saveImagesStatement = base.PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id")) { saveImagesStatement.TryBind("@Id", item.Id.ToGuidBlob()); saveImagesStatement.TryBind("@Images", SerializeImages(item)); saveImagesStatement.MoveNext(); } - }), TransactionMode); + }, TransactionMode); } } @@ -611,7 +612,7 @@ namespace Emby.Server.Implementations.Data private void SaveItemsInTranscation(IDatabaseConnection db, IEnumerable<(BaseItem, List, BaseItem, string, List)> tuples) { - var statements = PrepareAllSafe(db, new string[] + var statements = PrepareAll(db, new string[] { GetSaveItemCommandText(), "delete from AncestorIds where ItemId=@ItemId" @@ -990,6 +991,7 @@ namespace Emby.Server.Implementations.Data { albumArtists = string.Join("|", hasAlbumArtists.AlbumArtists); } + saveItemStatement.TryBind("@AlbumArtists", albumArtists); saveItemStatement.TryBind("@ExternalId", item.ExternalId); @@ -1026,6 +1028,7 @@ namespace Emby.Server.Implementations.Data { continue; } + str.Append($"{i.Key}={i.Value}|"); } @@ -1033,6 +1036,7 @@ namespace Emby.Server.Implementations.Data { return null; } + str.Length -= 1; // Remove last | return str.ToString(); } @@ -1070,6 +1074,7 @@ namespace Emby.Server.Implementations.Data { return null; } + StringBuilder str = new StringBuilder(); foreach (var i in images) { @@ -1079,6 +1084,7 @@ namespace Emby.Server.Implementations.Data } str.Append(ToValueString(i) + "|"); } + str.Length -= 1; // Remove last | return str.ToString(); } @@ -2823,7 +2829,7 @@ namespace Emby.Server.Implementations.Data return connection.RunInTransaction(db => { var result = new QueryResult(); - var statements = PrepareAllSafe(db, statementTexts).ToList(); + var statements = PrepareAll(db, statementTexts).ToList(); if (!isReturningZeroItems) { @@ -3235,7 +3241,7 @@ namespace Emby.Server.Implementations.Data { var result = new QueryResult(); - var statements = PrepareAllSafe(db, statementTexts).ToList(); + var statements = PrepareAll(db, statementTexts).ToList(); if (!isReturningZeroItems) { @@ -5436,7 +5442,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type var list = new List<(BaseItem, ItemCounts)>(); var result = new QueryResult<(BaseItem, ItemCounts)>(); - var statements = PrepareAllSafe(db, statementTexts).ToList(); + var statements = PrepareAll(db, statementTexts).ToList(); if (!isReturningZeroItems) { diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index 6ac398937..0580203c5 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -380,10 +380,5 @@ namespace Emby.Server.Implementations.Data return userData; } - - protected override void Dispose(bool dispose) - { - // handled by library database - } } } diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs index 29afb9f64..c8ecd7e6e 100644 --- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs +++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs @@ -258,7 +258,7 @@ namespace Emby.Server.Implementations.Security statementTexts.Add(commandText); statementTexts.Add("select count (Id) from Tokens" + whereTextWithoutPaging); - var statements = PrepareAllSafe(db, statementTexts) + var statements = PrepareAll(db, statementTexts) .ToList(); using (var statement = statements[0]) @@ -348,9 +348,9 @@ namespace Emby.Server.Implementations.Security { using (var connection = GetConnection(true)) { - return connection.RunInTransaction((Func)(db => + return connection.RunInTransaction(db => { - using (var statement = base.PrepareStatement((IDatabaseConnection)db, (string)"select CustomName from Devices where Id=@DeviceId")) + using (var statement = base.PrepareStatement(db, "select CustomName from Devices where Id=@DeviceId")) { statement.TryBind("@DeviceId", deviceId); @@ -367,7 +367,7 @@ namespace Emby.Server.Implementations.Security return result; } - }), ReadTransactionMode); + }, ReadTransactionMode); } } -- cgit v1.2.3 From edfd2d0cd959020cdcd2196320850d28ae10094d Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 3 Apr 2019 14:22:17 +0200 Subject: Fix startup --- Emby.Server.Implementations/ApplicationHost.cs | 11 +++++---- .../Data/SqliteUserRepository.cs | 27 ++++++++++------------ 2 files changed, 18 insertions(+), 20 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 62cc6ec47..3aa2dbf9a 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -754,10 +754,6 @@ namespace Emby.Server.Implementations UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, () => UserManager); serviceCollection.AddSingleton(UserDataManager); - UserRepository = GetUserRepository(); - // This is only needed for disposal purposes. If removing this, make sure to have the manager handle disposing it - serviceCollection.AddSingleton(UserRepository); - var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LoggerFactory, JsonSerializer, ApplicationPaths, FileSystemManager); serviceCollection.AddSingleton(displayPreferencesRepo); @@ -767,6 +763,8 @@ namespace Emby.Server.Implementations AuthenticationRepository = GetAuthenticationRepository(); serviceCollection.AddSingleton(AuthenticationRepository); + UserRepository = GetUserRepository(); + UserManager = new UserManager(LoggerFactory, ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager); serviceCollection.AddSingleton(UserManager); @@ -807,7 +805,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(TVSeriesManager); DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager); - serviceCollection.AddSingleton(DeviceManager); MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder); @@ -1893,8 +1890,12 @@ namespace Emby.Server.Implementations Logger.LogError(ex, "Error disposing {Type}", part.GetType().Name); } } + + UserRepository.Dispose(); } + UserRepository = null; + _disposed = true; } } diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs index e79b3d601..a0c6d2903 100644 --- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs @@ -56,7 +56,7 @@ namespace Emby.Server.Implementations.Data TryMigrateToLocalUsersTable(connection); } - RemoveEmptyPasswordHashes(); + RemoveEmptyPasswordHashes(connection); } } @@ -75,10 +75,12 @@ namespace Emby.Server.Implementations.Data } } - private void RemoveEmptyPasswordHashes() + private void RemoveEmptyPasswordHashes(ManagedConnection connection) { - foreach (var user in RetrieveAllUsers()) + foreach (var row in connection.Query("select id,guid,data from LocalUsersv2")) { + var user = GetUser(row); + // If the user password is the sha1 hash of the empty string, remove it if (!string.Equals(user.Password, "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal) && !string.Equals(user.Password, "$SHA1$DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal)) @@ -89,21 +91,16 @@ namespace Emby.Server.Implementations.Data user.Password = null; var serialized = _jsonSerializer.SerializeToBytes(user); - using (var connection = GetConnection()) + connection.RunInTransaction(db => { - connection.RunInTransaction(db => + using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId")) { - using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId")) - { - statement.TryBind("@InternalId", user.InternalId); - statement.TryBind("@data", serialized); - statement.MoveNext(); - } - - }, TransactionMode); - } + statement.TryBind("@InternalId", user.InternalId); + statement.TryBind("@data", serialized); + statement.MoveNext(); + } + }, TransactionMode); } - } /// -- cgit v1.2.3 From 7898af4cebe58bc11d120552594098041fff56fb Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 3 Apr 2019 17:34:54 +0200 Subject: Reworked PRAGMA statements use --- .../Activity/ActivityRepository.cs | 2 - .../Data/BaseSqliteRepository.cs | 90 +++++++++++----------- .../Data/SqliteDisplayPreferencesRepository.cs | 2 - .../Data/SqliteItemRepository.cs | 4 +- .../Data/SqliteUserDataRepository.cs | 2 - .../Data/SqliteUserRepository.cs | 2 - .../Security/AuthenticationRepository.cs | 2 - 7 files changed, 45 insertions(+), 59 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index f8a1b32af..de46ab965 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -45,8 +45,6 @@ namespace Emby.Server.Implementations.Activity { using (var connection = GetConnection()) { - RunDefaultInitialization(connection); - connection.RunQueries(new[] { "create table if not exists ActivityLog (Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)", diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index c5af156bb..4da6665c2 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -9,27 +9,37 @@ namespace Emby.Server.Implementations.Data { public abstract class BaseSqliteRepository : IDisposable { - protected string DbFilePath { get; set; } - - protected ILogger Logger { get; } + private bool _disposed = false; protected BaseSqliteRepository(ILogger logger) { Logger = logger; } + protected string DbFilePath { get; set; } + + protected ILogger Logger { get; } + + protected virtual ConnectionFlags DefaultConnectionFlags => ConnectionFlags.NoMutex; + protected TransactionMode TransactionMode => TransactionMode.Deferred; protected TransactionMode ReadTransactionMode => TransactionMode.Deferred; - protected virtual ConnectionFlags DefaultConnectionFlags => ConnectionFlags.NoMutex; + protected virtual int? CacheSize => null; + + protected virtual string JournalMode => "WAL"; + + protected virtual int? PageSize => null; + + protected virtual TempStoreMode TempStore => TempStoreMode.Default; + + protected virtual SynchronousMode? Synchronous => null; protected SemaphoreSlim WriteLock = new SemaphoreSlim(1, 1); protected SQLiteDatabaseConnection WriteConnection; - private string _defaultWal; - protected ManagedConnection GetConnection(bool _ = false) { WriteLock.Wait(); @@ -43,23 +53,28 @@ namespace Emby.Server.Implementations.Data DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite, null); - - if (string.IsNullOrWhiteSpace(_defaultWal)) + if (CacheSize.HasValue) { - _defaultWal = WriteConnection.Query("PRAGMA journal_mode").SelectScalarString().First(); + WriteConnection.Execute("PRAGMA cache_size=" + (int)CacheSize.Value); + } - Logger.LogInformation("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal); + if (!string.IsNullOrWhiteSpace(JournalMode)) + { + WriteConnection.Execute("PRAGMA journal_mode=" + JournalMode); } - if (EnableTempStoreMemory) + if (Synchronous.HasValue) { - WriteConnection.Execute("PRAGMA temp_store = memory"); + WriteConnection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value); } - else + + if (PageSize.HasValue) { - WriteConnection.Execute("PRAGMA temp_store = file"); + WriteConnection.Execute("PRAGMA page_size=" + (int)PageSize.Value); } + WriteConnection.Execute("PRAGMA temp_store=" + (int)TempStore); + return new ManagedConnection(WriteConnection, WriteLock); } @@ -92,38 +107,6 @@ namespace Emby.Server.Implementations.Data }, ReadTransactionMode); } - protected void RunDefaultInitialization(ManagedConnection db) - { - var queries = new List - { - "PRAGMA journal_mode=WAL", - "PRAGMA page_size=4096", - "PRAGMA synchronous=Normal" - }; - - if (EnableTempStoreMemory) - { - queries.AddRange(new List - { - "pragma default_temp_store = memory", - "pragma temp_store = memory" - }); - } - else - { - queries.AddRange(new List - { - "pragma temp_store = file" - }); - } - - db.ExecuteAll(string.Join(";", queries)); - Logger.LogInformation("PRAGMA synchronous=" + db.Query("PRAGMA synchronous").SelectScalarString().First()); - } - - protected virtual bool EnableTempStoreMemory => true; - - private bool _disposed; protected void CheckDisposed() { if (_disposed) @@ -199,4 +182,19 @@ namespace Emby.Server.Implementations.Data connection.Execute("alter table " + table + " add column " + columnName + " " + type + " NULL"); } } + + public enum SynchronousMode + { + Off = 0, + Normal = 1, + Full = 2, + Extra = 3 + } + + public enum TempStoreMode + { + Default = 0, + File = 1, + Memory = 2 + } } diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs index 1d44b0b29..01ef9851d 100644 --- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs @@ -63,8 +63,6 @@ namespace Emby.Server.Implementations.Data { using (var connection = GetConnection()) { - RunDefaultInitialization(connection); - string[] queries = { "create table if not exists userdisplaypreferences (id GUID NOT NULL, userId GUID NOT NULL, client text NOT NULL, data BLOB NOT NULL)", diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 5dc104347..8a56e16bb 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -92,7 +92,7 @@ namespace Emby.Server.Implementations.Data private const string ChaptersTableName = "Chapters2"; - protected override bool EnableTempStoreMemory => true; + protected override TempStoreMode TempStore => TempStoreMode.Memory; /// /// Opens the connection to the database @@ -101,8 +101,6 @@ namespace Emby.Server.Implementations.Data { using (var connection = GetConnection()) { - RunDefaultInitialization(connection); - const string createMediaStreamsTableCommand = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))"; diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index 0580203c5..4035bb99d 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -128,8 +128,6 @@ namespace Emby.Server.Implementations.Data return list; } - protected override bool EnableTempStoreMemory => true; - /// /// Saves the user data. /// diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs index a0c6d2903..cd364e7f4 100644 --- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs @@ -42,8 +42,6 @@ namespace Emby.Server.Implementations.Data { using (var connection = GetConnection()) { - RunDefaultInitialization(connection); - var localUsersTableExists = TableExists(connection, "LocalUsersv2"); connection.RunQueries(new[] { diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs index c8ecd7e6e..545e11bf9 100644 --- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs +++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs @@ -25,8 +25,6 @@ namespace Emby.Server.Implementations.Security { using (var connection = GetConnection()) { - RunDefaultInitialization(connection); - var tableNewlyCreated = !TableExists(connection, "Tokens"); string[] queries = { -- cgit v1.2.3 From db2765aae5556fd4e05e1c310027bdbd699327d2 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 3 Apr 2019 18:02:43 +0200 Subject: Last bit of cleanup --- .../Data/BaseSqliteRepository.cs | 56 +++++++++++----------- 1 file changed, 27 insertions(+), 29 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 4da6665c2..7938d6b7e 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -107,6 +107,33 @@ namespace Emby.Server.Implementations.Data }, ReadTransactionMode); } + protected List GetColumnNames(IDatabaseConnection connection, string table) + { + var list = new List(); + + foreach (var row in connection.Query("PRAGMA table_info(" + table + ")")) + { + if (row[1].SQLiteType != SQLiteType.Null) + { + var name = row[1].ToString(); + + list.Add(name); + } + } + + return list; + } + + protected void AddColumn(IDatabaseConnection connection, string table, string columnName, string type, List existingColumnNames) + { + if (existingColumnNames.Contains(columnName, StringComparer.OrdinalIgnoreCase)) + { + return; + } + + connection.Execute("alter table " + table + " add column " + columnName + " " + type + " NULL"); + } + protected void CheckDisposed() { if (_disposed) @@ -121,8 +148,6 @@ namespace Emby.Server.Implementations.Data GC.SuppressFinalize(this); } - private readonly object _disposeLock = new object(); - /// /// Releases unmanaged and - optionally - managed resources. /// @@ -154,33 +179,6 @@ namespace Emby.Server.Implementations.Data _disposed = true; } - - protected List GetColumnNames(IDatabaseConnection connection, string table) - { - var list = new List(); - - foreach (var row in connection.Query("PRAGMA table_info(" + table + ")")) - { - if (row[1].SQLiteType != SQLiteType.Null) - { - var name = row[1].ToString(); - - list.Add(name); - } - } - - return list; - } - - protected void AddColumn(IDatabaseConnection connection, string table, string columnName, string type, List existingColumnNames) - { - if (existingColumnNames.Contains(columnName, StringComparer.OrdinalIgnoreCase)) - { - return; - } - - connection.Execute("alter table " + table + " add column " + columnName + " " + type + " NULL"); - } } public enum SynchronousMode -- cgit v1.2.3 From d961278b3dcab57910b260115cd45d9831e6443e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 3 Apr 2019 18:15:04 +0200 Subject: Reduce amount of raw sql --- .../Data/SqliteUserRepository.cs | 23 ++++++------ Emby.Server.Implementations/Library/UserManager.cs | 42 +++++++++++----------- 2 files changed, 34 insertions(+), 31 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs index cd364e7f4..de2354eef 100644 --- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs @@ -75,10 +75,8 @@ namespace Emby.Server.Implementations.Data private void RemoveEmptyPasswordHashes(ManagedConnection connection) { - foreach (var row in connection.Query("select id,guid,data from LocalUsersv2")) + foreach (var user in RetrieveAllUsers(connection)) { - var user = GetUser(row); - // If the user password is the sha1 hash of the empty string, remove it if (!string.Equals(user.Password, "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal) && !string.Equals(user.Password, "$SHA1$DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal)) @@ -198,17 +196,22 @@ namespace Emby.Server.Implementations.Data /// IEnumerable{User}. public List RetrieveAllUsers() { - var list = new List(); - using (var connection = GetConnection(true)) { - foreach (var row in connection.Query("select id,guid,data from LocalUsersv2")) - { - list.Add(GetUser(row)); - } + return new List(RetrieveAllUsers(connection)); } + } - return list; + /// + /// Retrieve all users from the database + /// + /// IEnumerable{User}. + private IEnumerable RetrieveAllUsers(ManagedConnection connection) + { + foreach (var row in connection.Query("select id,guid,data from LocalUsersv2")) + { + yield return GetUser(row); + } } /// diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index ff375e590..1701ced42 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -222,9 +222,8 @@ namespace Emby.Server.Implementations.Library public void Initialize() { - _users = LoadUsers(); - - var users = Users.ToList(); + var users = LoadUsers(); + _users = users.ToArray(); // If there are no local users with admin rights, make them all admins if (!users.Any(i => i.Policy.IsAdministrator)) @@ -555,35 +554,36 @@ namespace Emby.Server.Implementations.Library /// Loads the users from the repository /// /// IEnumerable{User}. - private User[] LoadUsers() + private List LoadUsers() { var users = UserRepository.RetrieveAllUsers(); // There always has to be at least one user. - if (users.Count == 0) + if (users.Count != 0) { - var defaultName = Environment.UserName; - if (string.IsNullOrWhiteSpace(defaultName)) - { - defaultName = "MyJellyfinUser"; - } - var name = MakeValidUsername(defaultName); + return users; + } - var user = InstantiateNewUser(name); + var defaultName = Environment.UserName; + if (string.IsNullOrWhiteSpace(defaultName)) + { + defaultName = "MyJellyfinUser"; + } - user.DateLastSaved = DateTime.UtcNow; + var name = MakeValidUsername(defaultName); - UserRepository.CreateUser(user); + var user = InstantiateNewUser(name); + + user.DateLastSaved = DateTime.UtcNow; - users.Add(user); + UserRepository.CreateUser(user); - user.Policy.IsAdministrator = true; - user.Policy.EnableContentDeletion = true; - user.Policy.EnableRemoteControlOfOtherUsers = true; - UpdateUserPolicy(user, user.Policy, false); - } + user.Policy.IsAdministrator = true; + user.Policy.EnableContentDeletion = true; + user.Policy.EnableRemoteControlOfOtherUsers = true; + UpdateUserPolicy(user, user.Policy, false); - return users.ToArray(); + return new List { user }; } public UserDto GetUserDto(User user, string remoteEndPoint = null) -- cgit v1.2.3 From ecb8d8991b1ea4e1e26a69c7c9e0a217927d27d4 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 28 Jun 2019 12:22:33 +0200 Subject: Fix whitespace --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- Emby.Server.Implementations/Updates/InstallationManager.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 6c50698b2..48c915ede 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -48,7 +48,7 @@ - latest + latest diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index b3d76124b..9bc85633d 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -433,7 +433,6 @@ namespace Emby.Server.Implementations.Updates IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase)) ?? _applicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase)); - string targetPath = plugin == null ? null : plugin.AssemblyFilePath; // Do the install -- cgit v1.2.3 From 45c13141f9ac3da07f4d15c1fb90442d3eb855e2 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 1 Jul 2019 17:59:01 +0200 Subject: Address comments --- .../Data/BaseSqliteRepository.cs | 15 +++++---- .../Data/SqliteItemRepository.cs | 38 +++++++++++----------- 2 files changed, 27 insertions(+), 26 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 7938d6b7e..6e061c154 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -36,9 +36,9 @@ namespace Emby.Server.Implementations.Data protected virtual SynchronousMode? Synchronous => null; - protected SemaphoreSlim WriteLock = new SemaphoreSlim(1, 1); + protected SemaphoreSlim WriteLock { get; set; } = new SemaphoreSlim(1, 1); - protected SQLiteDatabaseConnection WriteConnection; + protected SQLiteDatabaseConnection WriteConnection { get; set; } protected ManagedConnection GetConnection(bool _ = false) { @@ -55,7 +55,7 @@ namespace Emby.Server.Implementations.Data if (CacheSize.HasValue) { - WriteConnection.Execute("PRAGMA cache_size=" + (int)CacheSize.Value); + WriteConnection.Execute("PRAGMA cache_size=" + CacheSize.Value); } if (!string.IsNullOrWhiteSpace(JournalMode)) @@ -70,7 +70,7 @@ namespace Emby.Server.Implementations.Data if (PageSize.HasValue) { - WriteConnection.Execute("PRAGMA page_size=" + (int)PageSize.Value); + WriteConnection.Execute("PRAGMA page_size=" + PageSize.Value); } WriteConnection.Execute("PRAGMA temp_store=" + (int)TempStore); @@ -109,7 +109,7 @@ namespace Emby.Server.Implementations.Data protected List GetColumnNames(IDatabaseConnection connection, string table) { - var list = new List(); + var columnNames = new List(); foreach (var row in connection.Query("PRAGMA table_info(" + table + ")")) { @@ -117,11 +117,11 @@ namespace Emby.Server.Implementations.Data { var name = row[1].ToString(); - list.Add(name); + columnNames.Add(name); } } - return list; + return columnNames; } protected void AddColumn(IDatabaseConnection connection, string table, string columnName, string type, List existingColumnNames) @@ -142,6 +142,7 @@ namespace Emby.Server.Implementations.Data } } + /// public void Dispose() { Dispose(true); diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 8a56e16bb..1cefcec7c 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -36,13 +36,9 @@ namespace Emby.Server.Implementations.Data /// public class SqliteItemRepository : BaseSqliteRepository, IItemRepository { - private readonly TypeMapper _typeMapper; + private const string ChaptersTableName = "Chapters2"; - /// - /// Gets the name of the repository - /// - /// The name. - public string Name => "SQLite"; + private readonly TypeMapper _typeMapper; /// /// Gets the json serializer. @@ -54,12 +50,9 @@ namespace Emby.Server.Implementations.Data /// The _app paths /// private readonly IServerConfigurationManager _config; - private IServerApplicationHost _appHost; - + private readonly IServerApplicationHost _appHost; private readonly ILocalizationManager _localization; - public IImageProcessor ImageProcessor { get; set; } - /// /// Initializes a new instance of the class. /// @@ -90,10 +83,17 @@ namespace Emby.Server.Implementations.Data DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db"); } - private const string ChaptersTableName = "Chapters2"; + /// + public string Name => "SQLite"; + + /// + protected override int? CacheSize => 20000; + /// protected override TempStoreMode TempStore => TempStoreMode.Memory; + public IImageProcessor ImageProcessor { get; set; } + /// /// Opens the connection to the database /// @@ -1903,7 +1903,7 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection(true)) { - var list = new List(); + var chapters = new List(); using (var statement = PrepareStatement(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc")) { @@ -1911,11 +1911,11 @@ namespace Emby.Server.Implementations.Data foreach (var row in statement.ExecuteQuery()) { - list.Add(GetChapter(row, item)); + chapters.Add(GetChapter(row, item)); } } - return list; + return chapters; } } @@ -2606,7 +2606,7 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection(true)) { - var list = new List(); + var items = new List(); using (var statement = PrepareStatement(connection, commandText)) { @@ -2634,7 +2634,7 @@ namespace Emby.Server.Implementations.Data var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields); if (item != null) { - list.Add(item); + items.Add(item); } } } @@ -2646,7 +2646,7 @@ namespace Emby.Server.Implementations.Data limit -= 4; var newList = new List(); - foreach (var item in list) + foreach (var item in items) { AddItem(newList, item); @@ -2656,12 +2656,12 @@ namespace Emby.Server.Implementations.Data } } - list = newList; + items = newList; } LogQueryTime("GetItemList", commandText, now); - return list; + return items; } } -- cgit v1.2.3 From 29ae7b9aeb46ac297ba5c3253d73cd4cd325c042 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 1 Jul 2019 18:24:35 +0200 Subject: Add docs --- .../Data/BaseSqliteRepository.cs | 86 ++++++++++++++++++++++ 1 file changed, 86 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 6e061c154..9bc0bb945 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -16,28 +16,78 @@ namespace Emby.Server.Implementations.Data Logger = logger; } + /// + /// Gets or sets the path to the DB file. + /// + /// Path to the DB file. protected string DbFilePath { get; set; } + /// + /// Gets the logger. + /// + /// The logger. protected ILogger Logger { get; } + /// + /// Gets the default connection flags. + /// + /// The default connection flags. protected virtual ConnectionFlags DefaultConnectionFlags => ConnectionFlags.NoMutex; + /// + /// Gets the transaction mode. + /// + /// The transaction mode.> protected TransactionMode TransactionMode => TransactionMode.Deferred; + /// + /// Gets the transaction mode for read-only operations. + /// + /// The transaction mode. protected TransactionMode ReadTransactionMode => TransactionMode.Deferred; + /// + /// Gets the cache size. + /// + /// The cache size or null. protected virtual int? CacheSize => null; + /// + /// Gets the journal mode. + /// + /// The journal mode. protected virtual string JournalMode => "WAL"; + /// + /// Gets the page size. + /// + /// The page size or null. protected virtual int? PageSize => null; + /// + /// Gets the temp store mode. + /// + /// The temp store mode. + /// protected virtual TempStoreMode TempStore => TempStoreMode.Default; + /// + /// Gets the synchronous mode. + /// + /// The synchronous mode or null. + /// protected virtual SynchronousMode? Synchronous => null; + /// + /// Gets or sets the write lock. + /// + /// The write lock. protected SemaphoreSlim WriteLock { get; set; } = new SemaphoreSlim(1, 1); + /// + /// Gets or sets the write connection. + /// + /// The write connection. protected SQLiteDatabaseConnection WriteConnection { get; set; } protected ManagedConnection GetConnection(bool _ = false) @@ -182,18 +232,54 @@ namespace Emby.Server.Implementations.Data } } + /// + /// The disk synchronization mode, controls how aggressively SQLite will write data + /// all the way out to physical storage. + /// public enum SynchronousMode { + /// + /// SQLite continues without syncing as soon as it has handed data off to the operating system + /// Off = 0, + + /// + /// SQLite database engine will still sync at the most critical moments + /// Normal = 1, + + /// + /// SQLite database engine will use the xSync method of the VFS + /// to ensure that all content is safely written to the disk surface prior to continuing. + /// Full = 2, + + /// + /// EXTRA synchronous is like FULL with the addition that the directory containing a rollback journal + /// is synced after that journal is unlinked to commit a transaction in DELETE mode. + /// Extra = 3 } + /// + /// Storage mode used by temporary database files. + /// public enum TempStoreMode { + /// + /// The compile-time C preprocessor macro SQLITE_TEMP_STORE + /// is used to determine where temporary tables and indices are stored. + /// Default = 0, + + /// + /// Temporary tables and indices are stored in a file. + /// File = 1, + + /// + /// Temporary tables and indices are kept in as if they were pure in-memory databases memory. + /// Memory = 2 } } -- cgit v1.2.3 From 7587fe56d83fd6b3031cb11a91d94b9348c08846 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Thu, 4 Jul 2019 20:54:57 +0200 Subject: Moved VACUUM down to the end of the list. --- Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 821c4b448..eabe7d120 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -202,8 +202,7 @@ namespace Emby.Server.Implementations.Data protected void RunDefaultInitialization(ManagedConnection db) { var queries = new List - { - "VACUUM", + { "PRAGMA journal_mode=WAL", "PRAGMA page_size=4096", "PRAGMA synchronous=Normal" @@ -224,6 +223,8 @@ namespace Emby.Server.Implementations.Data "pragma temp_store = file" }); } + // Configuration and pragmas can affect VACUUM so it needs to be last. + queries.Add("VACUUM"); db.ExecuteAll(string.Join(";", queries)); Logger.LogInformation("PRAGMA synchronous=" + db.Query("PRAGMA synchronous").SelectScalarString().First()); -- cgit v1.2.3 From acf52b9b55ef16809b05092eda6aca2b4cafb964 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Thu, 4 Jul 2019 20:55:49 +0200 Subject: Cleanup extra spaces. --- Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index eabe7d120..aea37ffb1 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -202,7 +202,7 @@ namespace Emby.Server.Implementations.Data protected void RunDefaultInitialization(ManagedConnection db) { var queries = new List - { + { "PRAGMA journal_mode=WAL", "PRAGMA page_size=4096", "PRAGMA synchronous=Normal" -- cgit v1.2.3 From 0f897589ed6349bb3c88919b06861daa80aec1be Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 21 May 2019 19:28:34 +0200 Subject: Streamline authentication proccess --- .../Cryptography/CryptographyProvider.cs | 86 ++++--- .../Library/DefaultAuthenticationProvider.cs | 115 +++++---- .../Library/DefaultPasswordResetProvider.cs | 257 ++++++++++----------- .../Library/InvalidAuthProvider.cs | 11 +- Emby.Server.Implementations/Library/UserManager.cs | 49 ++-- MediaBrowser.Api/LiveTv/LiveTvService.cs | 19 -- .../Authentication/AuthenticationException.cs | 28 +++ .../Authentication/IAuthenticationProvider.cs | 3 +- .../Authentication/IPasswordResetProvider.cs | 1 + MediaBrowser.Model/Cryptography/ICryptoProvider.cs | 6 +- MediaBrowser.Model/Cryptography/PasswordHash.cs | 152 ++++++------ 11 files changed, 365 insertions(+), 362 deletions(-) create mode 100644 MediaBrowser.Controller/Authentication/AuthenticationException.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs index 6d7193ce2..f726dae2e 100644 --- a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs +++ b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs @@ -8,7 +8,7 @@ using MediaBrowser.Model.Cryptography; namespace Emby.Server.Implementations.Cryptography { - public class CryptographyProvider : ICryptoProvider + public class CryptographyProvider : ICryptoProvider, IDisposable { private static readonly HashSet _supportedHashMethods = new HashSet() { @@ -28,26 +28,28 @@ namespace Emby.Server.Implementations.Cryptography "System.Security.Cryptography.SHA512" }; - public string DefaultHashMethod => "PBKDF2"; - private RandomNumberGenerator _randomNumberGenerator; private const int _defaultIterations = 1000; + private bool _disposed = false; + public CryptographyProvider() { - //FIXME: When we get DotNet Standard 2.1 we need to revisit how we do the crypto - //Currently supported hash methods from https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptoconfig?view=netcore-2.1 - //there might be a better way to autogenerate this list as dotnet updates, but I couldn't find one - //Please note the default method of PBKDF2 is not included, it cannot be used to generate hashes cleanly as it is actually a pbkdf with sha1 + // FIXME: When we get DotNet Standard 2.1 we need to revisit how we do the crypto + // Currently supported hash methods from https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptoconfig?view=netcore-2.1 + // there might be a better way to autogenerate this list as dotnet updates, but I couldn't find one + // Please note the default method of PBKDF2 is not included, it cannot be used to generate hashes cleanly as it is actually a pbkdf with sha1 _randomNumberGenerator = RandomNumberGenerator.Create(); } + public string DefaultHashMethod => "PBKDF2"; + + [Obsolete("Use System.Security.Cryptography.MD5 directly")] public Guid GetMD5(string str) - { - return new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str))); - } + => new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str))); + [Obsolete("Use System.Security.Cryptography.SHA1 directly")] public byte[] ComputeSHA1(byte[] bytes) { using (var provider = SHA1.Create()) @@ -56,6 +58,7 @@ namespace Emby.Server.Implementations.Cryptography } } + [Obsolete("Use System.Security.Cryptography.MD5 directly")] public byte[] ComputeMD5(Stream str) { using (var provider = MD5.Create()) @@ -64,6 +67,7 @@ namespace Emby.Server.Implementations.Cryptography } } + [Obsolete("Use System.Security.Cryptography.MD5 directly")] public byte[] ComputeMD5(byte[] bytes) { using (var provider = MD5.Create()) @@ -73,9 +77,7 @@ namespace Emby.Server.Implementations.Cryptography } public IEnumerable GetSupportedHashMethods() - { - return _supportedHashMethods; - } + => _supportedHashMethods; private byte[] PBKDF2(string method, byte[] bytes, byte[] salt, int iterations) { @@ -93,14 +95,10 @@ namespace Emby.Server.Implementations.Cryptography } public byte[] ComputeHash(string hashMethod, byte[] bytes) - { - return ComputeHash(hashMethod, bytes, Array.Empty()); - } + => ComputeHash(hashMethod, bytes, Array.Empty()); public byte[] ComputeHashWithDefaultMethod(byte[] bytes) - { - return ComputeHash(DefaultHashMethod, bytes); - } + => ComputeHash(DefaultHashMethod, bytes); public byte[] ComputeHash(string hashMethod, byte[] bytes, byte[] salt) { @@ -125,37 +123,27 @@ namespace Emby.Server.Implementations.Cryptography } } } - else - { - throw new CryptographicException($"Requested hash method is not supported: {hashMethod}"); - } + + throw new CryptographicException($"Requested hash method is not supported: {hashMethod}"); + } public byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt) - { - return PBKDF2(DefaultHashMethod, bytes, salt, _defaultIterations); - } + => PBKDF2(DefaultHashMethod, bytes, salt, _defaultIterations); public byte[] ComputeHash(PasswordHash hash) { int iterations = _defaultIterations; if (!hash.Parameters.ContainsKey("iterations")) { - hash.Parameters.Add("iterations", _defaultIterations.ToString(CultureInfo.InvariantCulture)); + hash.Parameters.Add("iterations", iterations.ToString(CultureInfo.InvariantCulture)); } - else + else if (!int.TryParse(hash.Parameters["iterations"], out iterations)) { - try - { - iterations = int.Parse(hash.Parameters["iterations"]); - } - catch (Exception e) - { - throw new InvalidDataException($"Couldn't successfully parse iterations value from string: {hash.Parameters["iterations"]}", e); - } + throw new InvalidDataException($"Couldn't successfully parse iterations value from string: {hash.Parameters["iterations"]}"); } - return PBKDF2(hash.Id, hash.HashBytes, hash.SaltBytes, iterations); + return PBKDF2(hash.Id, hash.Hash, hash.Salt, iterations); } public byte[] GenerateSalt() @@ -164,5 +152,29 @@ namespace Emby.Server.Implementations.Cryptography _randomNumberGenerator.GetBytes(salt); return salt; } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + if (disposing) + { + _randomNumberGenerator.Dispose(); + } + + _randomNumberGenerator = null; + + _disposed = true; + } } } diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs index fe09b07ff..b07244fda 100644 --- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs +++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs @@ -11,9 +11,9 @@ namespace Emby.Server.Implementations.Library public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser { private readonly ICryptoProvider _cryptographyProvider; - public DefaultAuthenticationProvider(ICryptoProvider crypto) + public DefaultAuthenticationProvider(ICryptoProvider cryptographyProvider) { - _cryptographyProvider = crypto; + _cryptographyProvider = cryptographyProvider; } public string Name => "Default"; @@ -28,17 +28,17 @@ namespace Emby.Server.Implementations.Library throw new NotImplementedException(); } - // This is the verson that we need to use for local users. Because reasons. + // This is the version that we need to use for local users. Because reasons. public Task Authenticate(string username, string password, User resolvedUser) { bool success = false; if (resolvedUser == null) { - throw new Exception("Invalid username or password"); + throw new ArgumentNullException(nameof(resolvedUser)); } // As long as jellyfin supports passwordless users, we need this little block here to accomodate - if (IsPasswordEmpty(resolvedUser, password)) + if (!HasPassword(resolvedUser) && string.IsNullOrEmpty(password)) { return Task.FromResult(new ProviderAuthenticationResult { @@ -50,37 +50,24 @@ namespace Emby.Server.Implementations.Library byte[] passwordbytes = Encoding.UTF8.GetBytes(password); PasswordHash readyHash = new PasswordHash(resolvedUser.Password); - byte[] calculatedHash; - string calculatedHashString; - if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id) || _cryptographyProvider.DefaultHashMethod == readyHash.Id) + if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id) + || _cryptographyProvider.DefaultHashMethod == readyHash.Id) { - if (string.IsNullOrEmpty(readyHash.Salt)) - { - calculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes); - calculatedHashString = BitConverter.ToString(calculatedHash).Replace("-", string.Empty); - } - else - { - calculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes, readyHash.SaltBytes); - calculatedHashString = BitConverter.ToString(calculatedHash).Replace("-", string.Empty); - } + byte[] calculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes, readyHash.Salt); - if (calculatedHashString == readyHash.Hash) + if (calculatedHash.SequenceEqual(readyHash.Hash)) { success = true; - // throw new Exception("Invalid username or password"); } } else { - throw new Exception(string.Format($"Requested crypto method not available in provider: {readyHash.Id}")); + throw new AuthenticationException($"Requested crypto method not available in provider: {readyHash.Id}"); } - // var success = string.Equals(GetPasswordHash(resolvedUser), GetHashedString(resolvedUser, password), StringComparison.OrdinalIgnoreCase); - if (!success) { - throw new Exception("Invalid username or password"); + throw new AuthenticationException("Invalid username or password"); } return Task.FromResult(new ProviderAuthenticationResult @@ -98,29 +85,22 @@ namespace Emby.Server.Implementations.Library return; } - if (!user.Password.Contains("$")) + if (user.Password.IndexOf('$') == -1) { string hash = user.Password; user.Password = string.Format("$SHA1${0}", hash); } - if (user.EasyPassword != null && !user.EasyPassword.Contains("$")) + if (user.EasyPassword != null + && user.EasyPassword.IndexOf('$') == -1) { string hash = user.EasyPassword; user.EasyPassword = string.Format("$SHA1${0}", hash); } } - public Task HasPassword(User user) - { - var hasConfiguredPassword = !IsPasswordEmpty(user, GetPasswordHash(user)); - return Task.FromResult(hasConfiguredPassword); - } - - private bool IsPasswordEmpty(User user, string password) - { - return (string.IsNullOrEmpty(user.Password) && string.IsNullOrEmpty(password)); - } + public bool HasPassword(User user) + => !string.IsNullOrEmpty(user.Password); public Task ChangePassword(User user, string newPassword) { @@ -129,30 +109,24 @@ namespace Emby.Server.Implementations.Library if (string.IsNullOrEmpty(user.Password)) { PasswordHash newPasswordHash = new PasswordHash(_cryptographyProvider); - newPasswordHash.SaltBytes = _cryptographyProvider.GenerateSalt(); - newPasswordHash.Salt = PasswordHash.ConvertToByteString(newPasswordHash.SaltBytes); + newPasswordHash.Salt = _cryptographyProvider.GenerateSalt(); newPasswordHash.Id = _cryptographyProvider.DefaultHashMethod; - newPasswordHash.Hash = GetHashedStringChangeAuth(newPassword, newPasswordHash); + newPasswordHash.Hash = GetHashedChangeAuth(newPassword, newPasswordHash); user.Password = newPasswordHash.ToString(); return Task.CompletedTask; } PasswordHash passwordHash = new PasswordHash(user.Password); - if (passwordHash.Id == "SHA1" && string.IsNullOrEmpty(passwordHash.Salt)) + if (passwordHash.Id == "SHA1" + && passwordHash.Salt.Length == 0) { - passwordHash.SaltBytes = _cryptographyProvider.GenerateSalt(); - passwordHash.Salt = PasswordHash.ConvertToByteString(passwordHash.SaltBytes); + passwordHash.Salt = _cryptographyProvider.GenerateSalt(); passwordHash.Id = _cryptographyProvider.DefaultHashMethod; - passwordHash.Hash = GetHashedStringChangeAuth(newPassword, passwordHash); + passwordHash.Hash = GetHashedChangeAuth(newPassword, passwordHash); } else if (newPassword != null) { - passwordHash.Hash = GetHashedString(user, newPassword); - } - - if (string.IsNullOrWhiteSpace(passwordHash.Hash)) - { - throw new ArgumentNullException(nameof(passwordHash.Hash)); + passwordHash.Hash = GetHashed(user, newPassword); } user.Password = passwordHash.ToString(); @@ -160,11 +134,6 @@ namespace Emby.Server.Implementations.Library return Task.CompletedTask; } - public string GetPasswordHash(User user) - { - return user.Password; - } - public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash) { ConvertPasswordFormat(user); @@ -190,13 +159,13 @@ namespace Emby.Server.Implementations.Library return string.IsNullOrEmpty(user.EasyPassword) ? null - : (new PasswordHash(user.EasyPassword)).Hash; + : PasswordHash.ConvertToByteString(new PasswordHash(user.EasyPassword).Hash); } - public string GetHashedStringChangeAuth(string newPassword, PasswordHash passwordHash) + internal byte[] GetHashedChangeAuth(string newPassword, PasswordHash passwordHash) { - passwordHash.HashBytes = Encoding.UTF8.GetBytes(newPassword); - return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash)); + passwordHash.Hash = Encoding.UTF8.GetBytes(newPassword); + return _cryptographyProvider.ComputeHash(passwordHash); } /// @@ -215,10 +184,10 @@ namespace Emby.Server.Implementations.Library passwordHash = new PasswordHash(user.Password); } - if (passwordHash.SaltBytes != null) + if (passwordHash.Salt != null) { // the password is modern format with PBKDF and we should take advantage of that - passwordHash.HashBytes = Encoding.UTF8.GetBytes(str); + passwordHash.Hash = Encoding.UTF8.GetBytes(str); return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash)); } else @@ -227,5 +196,31 @@ namespace Emby.Server.Implementations.Library return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash.Id, Encoding.UTF8.GetBytes(str))); } } + + public byte[] GetHashed(User user, string str) + { + PasswordHash passwordHash; + if (string.IsNullOrEmpty(user.Password)) + { + passwordHash = new PasswordHash(_cryptographyProvider); + } + else + { + ConvertPasswordFormat(user); + passwordHash = new PasswordHash(user.Password); + } + + if (passwordHash.Salt != null) + { + // the password is modern format with PBKDF and we should take advantage of that + passwordHash.Hash = Encoding.UTF8.GetBytes(str); + return _cryptographyProvider.ComputeHash(passwordHash); + } + else + { + // the password has no salt and should be called with the older method for safety + return _cryptographyProvider.ComputeHash(passwordHash.Id, Encoding.UTF8.GetBytes(str)); + } + } } } diff --git a/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs b/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs index e218749d9..c7044820c 100644 --- a/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs +++ b/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs @@ -1,132 +1,125 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Authentication; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Cryptography; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Users; - -namespace Emby.Server.Implementations.Library -{ - public class DefaultPasswordResetProvider : IPasswordResetProvider - { - public string Name => "Default Password Reset Provider"; - - public bool IsEnabled => true; - - private readonly string _passwordResetFileBase; - private readonly string _passwordResetFileBaseDir; - private readonly string _passwordResetFileBaseName = "passwordreset"; - - private readonly IJsonSerializer _jsonSerializer; - private readonly IUserManager _userManager; - private readonly ICryptoProvider _crypto; - - public DefaultPasswordResetProvider(IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IUserManager userManager, ICryptoProvider cryptoProvider) - { - _passwordResetFileBaseDir = configurationManager.ApplicationPaths.ProgramDataPath; - _passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, _passwordResetFileBaseName); - _jsonSerializer = jsonSerializer; - _userManager = userManager; - _crypto = cryptoProvider; - } - - public async Task RedeemPasswordResetPin(string pin) - { - SerializablePasswordReset spr; - HashSet usersreset = new HashSet(); - foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{_passwordResetFileBaseName}*")) - { - using (var str = File.OpenRead(resetfile)) - { - spr = await _jsonSerializer.DeserializeFromStreamAsync(str).ConfigureAwait(false); - } - - if (spr.ExpirationDate < DateTime.Now) - { - File.Delete(resetfile); - } - else if (spr.Pin.Replace("-", "").Equals(pin.Replace("-", ""), StringComparison.InvariantCultureIgnoreCase)) - { - var resetUser = _userManager.GetUserByName(spr.UserName); - if (resetUser == null) - { - throw new Exception($"User with a username of {spr.UserName} not found"); - } - - await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false); - usersreset.Add(resetUser.Name); - File.Delete(resetfile); - } - } - - if (usersreset.Count < 1) - { - throw new ResourceNotFoundException($"No Users found with a password reset request matching pin {pin}"); - } - else - { - return new PinRedeemResult - { - Success = true, - UsersReset = usersreset.ToArray() - }; - } - } - - public async Task StartForgotPasswordProcess(MediaBrowser.Controller.Entities.User user, bool isInNetwork) - { - string pin = string.Empty; - using (var cryptoRandom = System.Security.Cryptography.RandomNumberGenerator.Create()) - { - byte[] bytes = new byte[4]; - cryptoRandom.GetBytes(bytes); - pin = BitConverter.ToString(bytes); - } - - DateTime expireTime = DateTime.Now.AddMinutes(30); - string filePath = _passwordResetFileBase + user.InternalId + ".json"; - SerializablePasswordReset spr = new SerializablePasswordReset - { - ExpirationDate = expireTime, - Pin = pin, - PinFile = filePath, - UserName = user.Name - }; - - try - { - using (FileStream fileStream = File.OpenWrite(filePath)) - { - _jsonSerializer.SerializeToStream(spr, fileStream); - await fileStream.FlushAsync().ConfigureAwait(false); - } - } - catch (Exception e) - { - throw new Exception($"Error serializing or writing password reset for {user.Name} to location: {filePath}", e); - } - - return new ForgotPasswordResult - { - Action = ForgotPasswordAction.PinCode, - PinExpirationDate = expireTime, - PinFile = filePath - }; - } - - private class SerializablePasswordReset : PasswordPinCreationResult - { - public string Pin { get; set; } - - public string UserName { get; set; } - } - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Authentication; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Cryptography; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Users; + +namespace Emby.Server.Implementations.Library +{ + public class DefaultPasswordResetProvider : IPasswordResetProvider + { + public string Name => "Default Password Reset Provider"; + + public bool IsEnabled => true; + + private readonly string _passwordResetFileBase; + private readonly string _passwordResetFileBaseDir; + private readonly string _passwordResetFileBaseName = "passwordreset"; + + private readonly IJsonSerializer _jsonSerializer; + private readonly IUserManager _userManager; + private readonly ICryptoProvider _crypto; + + public DefaultPasswordResetProvider(IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IUserManager userManager, ICryptoProvider cryptoProvider) + { + _passwordResetFileBaseDir = configurationManager.ApplicationPaths.ProgramDataPath; + _passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, _passwordResetFileBaseName); + _jsonSerializer = jsonSerializer; + _userManager = userManager; + _crypto = cryptoProvider; + } + + public async Task RedeemPasswordResetPin(string pin) + { + SerializablePasswordReset spr; + HashSet usersreset = new HashSet(); + foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{_passwordResetFileBaseName}*")) + { + using (var str = File.OpenRead(resetfile)) + { + spr = await _jsonSerializer.DeserializeFromStreamAsync(str).ConfigureAwait(false); + } + + if (spr.ExpirationDate < DateTime.Now) + { + File.Delete(resetfile); + } + else if (spr.Pin.Replace("-", "").Equals(pin.Replace("-", ""), StringComparison.InvariantCultureIgnoreCase)) + { + var resetUser = _userManager.GetUserByName(spr.UserName); + if (resetUser == null) + { + throw new Exception($"User with a username of {spr.UserName} not found"); + } + + await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false); + usersreset.Add(resetUser.Name); + File.Delete(resetfile); + } + } + + if (usersreset.Count < 1) + { + throw new ResourceNotFoundException($"No Users found with a password reset request matching pin {pin}"); + } + else + { + return new PinRedeemResult + { + Success = true, + UsersReset = usersreset.ToArray() + }; + } + } + + public async Task StartForgotPasswordProcess(MediaBrowser.Controller.Entities.User user, bool isInNetwork) + { + string pin = string.Empty; + using (var cryptoRandom = System.Security.Cryptography.RandomNumberGenerator.Create()) + { + byte[] bytes = new byte[4]; + cryptoRandom.GetBytes(bytes); + pin = BitConverter.ToString(bytes); + } + + DateTime expireTime = DateTime.Now.AddMinutes(30); + string filePath = _passwordResetFileBase + user.InternalId + ".json"; + SerializablePasswordReset spr = new SerializablePasswordReset + { + ExpirationDate = expireTime, + Pin = pin, + PinFile = filePath, + UserName = user.Name + }; + + using (FileStream fileStream = File.OpenWrite(filePath)) + { + _jsonSerializer.SerializeToStream(spr, fileStream); + await fileStream.FlushAsync().ConfigureAwait(false); + } + + return new ForgotPasswordResult + { + Action = ForgotPasswordAction.PinCode, + PinExpirationDate = expireTime, + PinFile = filePath + }; + } + + private class SerializablePasswordReset : PasswordPinCreationResult + { + public string Pin { get; set; } + + public string UserName { get; set; } + } + } +} diff --git a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs index 25d233137..6956369dc 100644 --- a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs +++ b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Entities; @@ -16,12 +13,12 @@ namespace Emby.Server.Implementations.Library public Task Authenticate(string username, string password) { - throw new SecurityException("User Account cannot login with this provider. The Normal provider for this user cannot be found"); + throw new AuthenticationException("User Account cannot login with this provider. The Normal provider for this user cannot be found"); } - public Task HasPassword(User user) + public bool HasPassword(User user) { - return Task.FromResult(true); + return true; } public Task ChangePassword(User user, string newPassword) @@ -31,7 +28,7 @@ namespace Emby.Server.Implementations.Library public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash) { - // Nothing here + // Nothing here } public string GetPasswordHash(User user) diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 1701ced42..c8c8a108d 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -266,6 +266,7 @@ namespace Emby.Server.Implementations.Library builder.Append(c); } } + return builder.ToString(); } @@ -286,17 +287,17 @@ namespace Emby.Server.Implementations.Library if (user != null) { var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false); - authenticationProvider = authResult.Item1; - updatedUsername = authResult.Item2; - success = authResult.Item3; + authenticationProvider = authResult.authenticationProvider; + updatedUsername = authResult.username; + success = authResult.success; } else { // user is null var authResult = await AuthenticateLocalUser(username, password, hashedPassword, null, remoteEndPoint).ConfigureAwait(false); - authenticationProvider = authResult.Item1; - updatedUsername = authResult.Item2; - success = authResult.Item3; + authenticationProvider = authResult.authenticationProvider; + updatedUsername = authResult.username; + success = authResult.success; if (success && authenticationProvider != null && !(authenticationProvider is DefaultAuthenticationProvider)) { @@ -331,22 +332,25 @@ namespace Emby.Server.Implementations.Library if (user == null) { - throw new SecurityException("Invalid username or password entered."); + throw new AuthenticationException("Invalid username or password entered."); } if (user.Policy.IsDisabled) { - throw new SecurityException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name)); + throw new AuthenticationException(string.Format( + CultureInfo.InvariantCulture, + "The {0} account is currently disabled. Please consult with your administrator.", + user.Name)); } if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint)) { - throw new SecurityException("Forbidden."); + throw new AuthenticationException("Forbidden."); } if (!user.IsParentalScheduleAllowed()) { - throw new SecurityException("User is not allowed access at this time."); + throw new AuthenticationException("User is not allowed access at this time."); } // Update LastActivityDate and LastLoginDate, then save @@ -357,6 +361,7 @@ namespace Emby.Server.Implementations.Library user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow; UpdateUser(user); } + UpdateInvalidLoginAttemptCount(user, 0); } else @@ -429,7 +434,7 @@ namespace Emby.Server.Implementations.Library return providers; } - private async Task> AuthenticateWithProvider(IAuthenticationProvider provider, string username, string password, User resolvedUser) + private async Task<(string username, bool success)> AuthenticateWithProvider(IAuthenticationProvider provider, string username, string password, User resolvedUser) { try { @@ -444,23 +449,23 @@ namespace Emby.Server.Implementations.Library authenticationResult = await provider.Authenticate(username, password).ConfigureAwait(false); } - if(authenticationResult.Username != username) + if (authenticationResult.Username != username) { _logger.LogDebug("Authentication provider provided updated username {1}", authenticationResult.Username); username = authenticationResult.Username; } - return new Tuple(username, true); + return (username, true); } - catch (Exception ex) + catch (AuthenticationException ex) { - _logger.LogError(ex, "Error authenticating with provider {provider}", provider.Name); + _logger.LogError(ex, "Error authenticating with provider {Provider}", provider.Name); - return new Tuple(username, false); + return (username, false); } } - private async Task> AuthenticateLocalUser(string username, string password, string hashedPassword, User user, string remoteEndPoint) + private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)> AuthenticateLocalUser(string username, string password, string hashedPassword, User user, string remoteEndPoint) { string updatedUsername = null; bool success = false; @@ -475,15 +480,15 @@ namespace Emby.Server.Implementations.Library if (password == null) { // legacy - success = string.Equals(GetAuthenticationProvider(user).GetPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); + success = string.Equals(user.Password, hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); } else { foreach (var provider in GetAuthenticationProviders(user)) { var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false); - updatedUsername = providerAuthResult.Item1; - success = providerAuthResult.Item2; + updatedUsername = providerAuthResult.username; + success = providerAuthResult.success; if (success) { @@ -510,7 +515,7 @@ namespace Emby.Server.Implementations.Library } } - return new Tuple(authenticationProvider, username, success); + return (authenticationProvider, username, success); } private void UpdateInvalidLoginAttemptCount(User user, int newValue) @@ -593,7 +598,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(user)); } - bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user).Result; + bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user); bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(user)); bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ? diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index e41ad540a..8a4d6e216 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -599,7 +599,6 @@ namespace MediaBrowser.Api.LiveTv { public bool ValidateLogin { get; set; } public bool ValidateListings { get; set; } - public string Pw { get; set; } } [Route("/LiveTv/ListingProviders", "DELETE", Summary = "Deletes a listing provider")] @@ -867,28 +866,10 @@ namespace MediaBrowser.Api.LiveTv public async Task Post(AddListingProvider request) { - if (request.Pw != null) - { - request.Password = GetHashedString(request.Pw); - } - - request.Pw = null; - var result = await _liveTvManager.SaveListingProvider(request, request.ValidateLogin, request.ValidateListings).ConfigureAwait(false); return ToOptimizedResult(result); } - /// - /// Gets the hashed string. - /// - private string GetHashedString(string str) - { - // legacy - return BitConverter.ToString( - _cryptographyProvider.ComputeSHA1(Encoding.UTF8.GetBytes(str))) - .Replace("-", string.Empty).ToLowerInvariant(); - } - public void Delete(DeleteListingProvider request) { _liveTvManager.DeleteListingsProvider(request.Id); diff --git a/MediaBrowser.Controller/Authentication/AuthenticationException.cs b/MediaBrowser.Controller/Authentication/AuthenticationException.cs new file mode 100644 index 000000000..045cbcdae --- /dev/null +++ b/MediaBrowser.Controller/Authentication/AuthenticationException.cs @@ -0,0 +1,28 @@ +using System; +namespace MediaBrowser.Controller.Authentication +{ + /// + /// The exception that is thrown when an attempt to authenticate fails. + /// + public class AuthenticationException : Exception + { + /// + public AuthenticationException() : base() + { + + } + + /// + public AuthenticationException(string message) : base(message) + { + + } + + /// + public AuthenticationException(string message, Exception innerException) + : base(message, innerException) + { + + } + } +} diff --git a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs index 2cf531eed..f5571065f 100644 --- a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs +++ b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs @@ -9,10 +9,9 @@ namespace MediaBrowser.Controller.Authentication string Name { get; } bool IsEnabled { get; } Task Authenticate(string username, string password); - Task HasPassword(User user); + bool HasPassword(User user); Task ChangePassword(User user, string newPassword); void ChangeEasyPassword(User user, string newPassword, string newPasswordHash); - string GetPasswordHash(User user); string GetEasyPasswordHash(User user); } diff --git a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs index 9e5cd8816..2639960e7 100644 --- a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs +++ b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs @@ -12,6 +12,7 @@ namespace MediaBrowser.Controller.Authentication Task StartForgotPasswordProcess(User user, bool isInNetwork); Task RedeemPasswordResetPin(string pin); } + public class PasswordPinCreationResult { public string PinFile { get; set; } diff --git a/MediaBrowser.Model/Cryptography/ICryptoProvider.cs b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs index 5988112c2..9e85beb43 100644 --- a/MediaBrowser.Model/Cryptography/ICryptoProvider.cs +++ b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs @@ -6,9 +6,14 @@ namespace MediaBrowser.Model.Cryptography { public interface ICryptoProvider { + string DefaultHashMethod { get; } + [Obsolete("Use System.Security.Cryptography.MD5 directly")] Guid GetMD5(string str); + [Obsolete("Use System.Security.Cryptography.MD5 directly")] byte[] ComputeMD5(Stream str); + [Obsolete("Use System.Security.Cryptography.MD5 directly")] byte[] ComputeMD5(byte[] bytes); + [Obsolete("Use System.Security.Cryptography.SHA1 directly")] byte[] ComputeSHA1(byte[] bytes); IEnumerable GetSupportedHashMethods(); byte[] ComputeHash(string HashMethod, byte[] bytes); @@ -17,6 +22,5 @@ namespace MediaBrowser.Model.Cryptography byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt); byte[] ComputeHash(PasswordHash hash); byte[] GenerateSalt(); - string DefaultHashMethod { get; } } } diff --git a/MediaBrowser.Model/Cryptography/PasswordHash.cs b/MediaBrowser.Model/Cryptography/PasswordHash.cs index f15b27d32..df32fdb00 100644 --- a/MediaBrowser.Model/Cryptography/PasswordHash.cs +++ b/MediaBrowser.Model/Cryptography/PasswordHash.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text; namespace MediaBrowser.Model.Cryptography @@ -16,86 +17,71 @@ namespace MediaBrowser.Model.Cryptography private Dictionary _parameters = new Dictionary(); - private string _salt; + private byte[] _salt; - private byte[] _saltBytes; - - private string _hash; - - private byte[] _hashBytes; - - public string Id { get => _id; set => _id = value; } - - public Dictionary Parameters { get => _parameters; set => _parameters = value; } - - public string Salt { get => _salt; set => _salt = value; } - - public byte[] SaltBytes { get => _saltBytes; set => _saltBytes = value; } - - public string Hash { get => _hash; set => _hash = value; } - - public byte[] HashBytes { get => _hashBytes; set => _hashBytes = value; } + private byte[] _hash; public PasswordHash(string storageString) { string[] splitted = storageString.Split('$'); - _id = splitted[1]; - if (splitted[2].Contains("=")) + // The string should at least contain the hash function and the hash itself + if (splitted.Length < 3) + { + throw new ArgumentException("String doesn't contain enough segments", nameof(storageString)); + } + + // Start at 1, the first index shouldn't contain any data + int index = 1; + + // Name of the hash function + _id = splitted[index++]; + + // Optional parameters + if (splitted[index].IndexOf('=') != -1) { - foreach (string paramset in (splitted[2].Split(','))) + foreach (string paramset in splitted[index++].Split(',')) { - if (!string.IsNullOrEmpty(paramset)) + if (string.IsNullOrEmpty(paramset)) { - string[] fields = paramset.Split('='); - if (fields.Length == 2) - { - _parameters.Add(fields[0], fields[1]); - } - else - { - throw new Exception($"Malformed parameter in password hash string {paramset}"); - } + continue; } + + string[] fields = paramset.Split('='); + if (fields.Length != 2) + { + throw new InvalidDataException($"Malformed parameter in password hash string {paramset}"); + } + + _parameters.Add(fields[0], fields[1]); } - if (splitted.Length == 5) - { - _salt = splitted[3]; - _saltBytes = ConvertFromByteString(_salt); - _hash = splitted[4]; - _hashBytes = ConvertFromByteString(_hash); - } - else - { - _salt = string.Empty; - _hash = splitted[3]; - _hashBytes = ConvertFromByteString(_hash); - } + } + + // Check if the string also contains a salt + if (splitted.Length - index == 2) + { + _salt = ConvertFromByteString(splitted[index++]); + _hash = ConvertFromByteString(splitted[index++]); } else { - if (splitted.Length == 4) - { - _salt = splitted[2]; - _saltBytes = ConvertFromByteString(_salt); - _hash = splitted[3]; - _hashBytes = ConvertFromByteString(_hash); - } - else - { - _salt = string.Empty; - _hash = splitted[2]; - _hashBytes = ConvertFromByteString(_hash); - } - + _salt = Array.Empty(); + _hash = ConvertFromByteString(splitted[index++]); } - } + public string Id { get => _id; set => _id = value; } + + public Dictionary Parameters { get => _parameters; set => _parameters = value; } + + public byte[] Salt { get => _salt; set => _salt = value; } + + public byte[] Hash { get => _hash; set => _hash = value; } + public PasswordHash(ICryptoProvider cryptoProvider) { _id = cryptoProvider.DefaultHashMethod; - _saltBytes = cryptoProvider.GenerateSalt(); - _salt = ConvertToByteString(SaltBytes); + _salt = cryptoProvider.GenerateSalt(); + _hash = Array.Empty(); } public static byte[] ConvertFromByteString(string byteString) @@ -111,43 +97,45 @@ namespace MediaBrowser.Model.Cryptography } public static string ConvertToByteString(byte[] bytes) - { - return BitConverter.ToString(bytes).Replace("-", ""); - } + => BitConverter.ToString(bytes).Replace("-", string.Empty); - private string SerializeParameters() + private void SerializeParameters(StringBuilder stringBuilder) { - string returnString = string.Empty; - foreach (var KVP in _parameters) + if (_parameters.Count == 0) { - returnString += $",{KVP.Key}={KVP.Value}"; + return; } - if ((!string.IsNullOrEmpty(returnString)) && returnString[0] == ',') + stringBuilder.Append('$'); + foreach (var pair in _parameters) { - returnString = returnString.Remove(0, 1); + stringBuilder.Append(pair.Key); + stringBuilder.Append('='); + stringBuilder.Append(pair.Value); + stringBuilder.Append(','); } - return returnString; + // Remove last ',' + stringBuilder.Length -= 1; } public override string ToString() { - string outString = "$" + _id; - string paramstring = SerializeParameters(); - if (!string.IsNullOrEmpty(paramstring)) - { - outString += $"${paramstring}"; - } + var str = new StringBuilder(); + str.Append('$'); + str.Append(_id); + SerializeParameters(str); - if (!string.IsNullOrEmpty(_salt)) + if (_salt.Length == 0) { - outString += $"${_salt}"; + str.Append('$'); + str.Append(ConvertToByteString(_salt)); } - outString += $"${_hash}"; - return outString; + str.Append('$'); + str.Append(ConvertToByteString(_hash)); + + return str.ToString(); } } - } -- cgit v1.2.3 From ab7ef9c9cb951111eec5132e9c6a00aa0d1b111b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 6 Jul 2019 16:15:38 +0200 Subject: Fix style issues --- .../Library/CoreResolutionIgnoreRule.cs | 69 +++++----------------- .../Library/Resolvers/Movies/MovieResolver.cs | 27 +++++---- .../Resolvers/IResolverIgnoreRule.cs | 6 ++ 3 files changed, 35 insertions(+), 67 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index a70077163..f1ae2fc9c 100644 --- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -5,7 +5,6 @@ using System.Text.RegularExpressions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; namespace Emby.Server.Implementations.Library @@ -17,12 +16,10 @@ namespace Emby.Server.Implementations.Library { private readonly ILibraryManager _libraryManager; - private bool _ignoreDotPrefix; - /// - /// Any folder named in this list will be ignored - can be added to at runtime for extensibility + /// Any folder named in this list will be ignored /// - public static readonly string[] IgnoreFolders = + private static readonly string[] _ignoreFolders = { "metadata", "ps3_update", @@ -43,25 +40,14 @@ namespace Emby.Server.Implementations.Library "$RECYCLE.BIN", "System Volume Information", ".grab", - - // macos - ".AppleDouble" - }; public CoreResolutionIgnoreRule(ILibraryManager libraryManager) { _libraryManager = libraryManager; - - _ignoreDotPrefix = Environment.OSVersion.Platform != PlatformID.Win32NT; } - /// - /// Shoulds the ignore. - /// - /// The file information. - /// The parent. - /// true if XXXX, false otherwise + /// public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent) { // Don't ignore top level folders @@ -73,46 +59,17 @@ namespace Emby.Server.Implementations.Library var filename = fileInfo.Name; var path = fileInfo.FullName; - // Handle mac .DS_Store - // https://github.com/MediaBrowser/MediaBrowser/issues/427 - if (_ignoreDotPrefix) + // Ignore hidden files on UNIX + if (Environment.OSVersion.Platform != PlatformID.Win32NT + && filename[0] == '.') { - if (filename.IndexOf('.') == 0) - { - return true; - } + return true; } - // Ignore hidden files and folders - //if (fileInfo.IsHidden) - //{ - // if (parent == null) - // { - // var parentFolderName = Path.GetFileName(_fileSystem.GetDirectoryName(path)); - - // if (string.Equals(parentFolderName, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) - // { - // return false; - // } - // if (string.Equals(parentFolderName, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase)) - // { - // return false; - // } - // } - - // // Sometimes these are marked hidden - // if (_fileSystem.IsRootPath(path)) - // { - // return false; - // } - - // return true; - //} - if (fileInfo.IsDirectory) { // Ignore any folders in our list - if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase)) + if (_ignoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase)) { return true; } @@ -120,8 +77,9 @@ namespace Emby.Server.Implementations.Library if (parent != null) { // Ignore trailer folders but allow it at the collection level - if (string.Equals(filename, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase) && - !(parent is AggregateFolder) && !(parent is UserRootFolder)) + if (string.Equals(filename, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase) + && !(parent is AggregateFolder) + && !(parent is UserRootFolder)) { return true; } @@ -142,14 +100,15 @@ namespace Emby.Server.Implementations.Library if (parent != null) { // Don't resolve these into audio files - if (string.Equals(Path.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename) && _libraryManager.IsAudioFile(filename)) + if (string.Equals(Path.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename) + && _libraryManager.IsAudioFile(filename)) { return true; } } // Ignore samples - Match m = Regex.Match(filename,@"\bsample\b",RegexOptions.IgnoreCase); + Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase); return m.Success; } diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 47c3e71d7..1b63b00a3 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -12,7 +12,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; namespace Emby.Server.Implementations.Library.Resolvers.Movies @@ -28,7 +27,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies /// The priority. public override ResolverPriority Priority => ResolverPriority.Third; - public MultiItemResolverResult ResolveMultiple(Folder parent, + public MultiItemResolverResult ResolveMultiple( + Folder parent, List files, string collectionType, IDirectoryService directoryService) @@ -46,7 +46,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return result; } - private MultiItemResolverResult ResolveMultipleInternal(Folder parent, + private MultiItemResolverResult ResolveMultipleInternal( + Folder parent, List files, string collectionType, IDirectoryService directoryService) @@ -91,7 +92,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return null; } - private MultiItemResolverResult ResolveVideos(Folder parent, IEnumerable fileSystemEntries, IDirectoryService directoryService, bool suppportMultiEditions, string collectionType, bool parseName) + private MultiItemResolverResult ResolveVideos( + Folder parent, + IEnumerable fileSystemEntries, + IDirectoryService directoryService, + bool suppportMultiEditions, + string collectionType, + bool parseName) where T : Video, new() { var files = new List(); @@ -104,8 +111,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies // This is a hack but currently no better way to resolve a sometimes ambiguous situation if (string.IsNullOrEmpty(collectionType)) { - if (string.Equals(child.Name, "tvshow.nfo", StringComparison.OrdinalIgnoreCase) || - string.Equals(child.Name, "season.nfo", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(child.Name, "tvshow.nfo", StringComparison.OrdinalIgnoreCase) + || string.Equals(child.Name, "season.nfo", StringComparison.OrdinalIgnoreCase)) { return null; } @@ -115,11 +122,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies { leftOver.Add(child); } - else if (IsIgnored(child.Name)) - { - - } - else + else if (!IsIgnored(child.Name)) { files.Add(child); } @@ -168,7 +171,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies private static bool IsIgnored(string filename) { // Ignore samples - Match m = Regex.Match(filename,@"\bsample\b",RegexOptions.IgnoreCase); + Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase); return m.Success; } diff --git a/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs b/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs index b40cc157a..bb80e6025 100644 --- a/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs +++ b/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs @@ -8,6 +8,12 @@ namespace MediaBrowser.Controller.Resolvers /// public interface IResolverIgnoreRule { + /// + /// Checks whether or not the file should be ignored. + /// + /// The file information. + /// The parent BaseItem. + /// True if the file should be ignored. bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent); } } -- cgit v1.2.3 From d405a400aaa5f9676cc2ce9159b562f94233dcd5 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 14 Jun 2019 16:32:37 +0200 Subject: Fixes issues with HttpClientManager --- Emby.Dlna/PlayTo/SsdpHttpClient.cs | 22 ++-- Emby.Server.Implementations/ApplicationHost.cs | 13 +- .../HttpClientManager/HttpClientManager.cs | 143 ++++++++++----------- .../LiveTv/EmbyTV/DirectRecorder.cs | 2 +- .../LiveTv/Listings/SchedulesDirect.cs | 20 +-- .../LiveTv/Listings/XmlTvListingsProvider.cs | 4 +- .../LiveTv/TunerHosts/SharedHttpStream.cs | 2 +- Jellyfin.Server/Program.cs | 12 ++ MediaBrowser.Common/Net/HttpRequestOptions.cs | 29 +++-- MediaBrowser.Common/Net/HttpResponseInfo.cs | 12 +- 10 files changed, 119 insertions(+), 140 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs index 1ad99fac5..780b0a889 100644 --- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs +++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs @@ -34,16 +34,13 @@ namespace Emby.Dlna.PlayTo { var cancellationToken = CancellationToken.None; - using (var response = await PostSoapDataAsync(NormalizeServiceUrl(baseUrl, service.ControlUrl), "\"" + service.ServiceType + "#" + command + "\"", postData, header, logRequest, cancellationToken) + var url = NormalizeServiceUrl(baseUrl, service.ControlUrl); + using (var response = await PostSoapDataAsync(url, '\"' + service.ServiceType + '#' + command + '\"', postData, header, logRequest, cancellationToken) .ConfigureAwait(false)) + using (var stream = response.Content) + using (var reader = new StreamReader(stream, Encoding.UTF8)) { - using (var stream = response.Content) - { - using (var reader = new StreamReader(stream, Encoding.UTF8)) - { - return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace); - } - } + return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace); } } @@ -121,15 +118,18 @@ namespace Emby.Dlna.PlayTo } } - private Task PostSoapDataAsync(string url, + private Task PostSoapDataAsync( + string url, string soapAction, string postData, string header, bool logRequest, CancellationToken cancellationToken) { - if (!soapAction.StartsWith("\"")) - soapAction = "\"" + soapAction + "\""; + if (soapAction[0] != '\"') + { + soapAction = '\"' + soapAction + '\"'; + } var options = new HttpRequestOptions { diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 02d661d15..c64922a93 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -430,7 +430,7 @@ namespace Emby.Server.Implementations /// Gets the current application user agent /// /// The application user agent. - public string ApplicationUserAgent => Name.Replace(' ','-') + "/" + ApplicationVersion; + public string ApplicationUserAgent => Name.Replace(' ','-') + '/' + ApplicationVersion; /// /// Gets the email address for use within a comment section of a user agent field. @@ -690,11 +690,6 @@ namespace Emby.Server.Implementations await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, CancellationToken.None).ConfigureAwait(false); } - protected virtual IHttpClient CreateHttpClient() - { - return new HttpClientManager.HttpClientManager(ApplicationPaths, LoggerFactory, FileSystemManager, () => ApplicationUserAgent); - } - public static IStreamHelper StreamHelper { get; set; } /// @@ -720,7 +715,11 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(FileSystemManager); serviceCollection.AddSingleton(); - HttpClient = CreateHttpClient(); + HttpClient = new HttpClientManager.HttpClientManager( + ApplicationPaths, + LoggerFactory.CreateLogger(), + FileSystemManager, + () => ApplicationUserAgent); serviceCollection.AddSingleton(HttpClient); serviceCollection.AddSingleton(NetworkManager); diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index b82d55d0e..987657bcb 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -23,11 +23,6 @@ namespace Emby.Server.Implementations.HttpClientManager /// public class HttpClientManager : IHttpClient { - /// - /// When one request to a host times out, we'll ban all other requests for this period of time, to prevent scans from stalling - /// - private const int TimeoutSeconds = 30; - /// /// The _logger /// @@ -46,7 +41,7 @@ namespace Emby.Server.Implementations.HttpClientManager /// public HttpClientManager( IApplicationPaths appPaths, - ILoggerFactory loggerFactory, + ILogger logger, IFileSystem fileSystem, Func defaultUserAgentFn) { @@ -55,18 +50,15 @@ namespace Emby.Server.Implementations.HttpClientManager throw new ArgumentNullException(nameof(appPaths)); } - if (loggerFactory == null) + if (logger == null) { - throw new ArgumentNullException(nameof(loggerFactory)); + throw new ArgumentNullException(nameof(logger)); } - _logger = loggerFactory.CreateLogger(nameof(HttpClientManager)); + _logger = logger; _fileSystem = fileSystem; _appPaths = appPaths; _defaultUserAgentFn = defaultUserAgentFn; - - // http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c - ServicePointManager.Expect100Continue = false; } /// @@ -83,13 +75,12 @@ namespace Emby.Server.Implementations.HttpClientManager /// if set to true [enable HTTP compression]. /// HttpClient. /// host - private HttpClient GetHttpClient(string url, bool enableHttpCompression) + private HttpClient GetHttpClient(string url) { - var key = GetHostFromUrl(url) + enableHttpCompression; + var key = GetHostFromUrl(url); if (!_httpClients.TryGetValue(key, out var client)) { - client = new HttpClient() { BaseAddress = new Uri(url) @@ -109,24 +100,27 @@ namespace Emby.Server.Implementations.HttpClientManager if (!string.IsNullOrWhiteSpace(userInfo)) { _logger.LogWarning("Found userInfo in url: {0} ... url: {1}", userInfo, url); - url = url.Replace(userInfo + "@", string.Empty); + url = url.Replace(userInfo + '@', string.Empty); } var request = new HttpRequestMessage(method, url); AddRequestHeaders(request, options); - if (options.EnableHttpCompression) + switch (options.DecompressionMethod) { - if (options.DecompressionMethod.HasValue - && options.DecompressionMethod.Value == CompressionMethod.Gzip) - { + case CompressionMethod.Deflate | CompressionMethod.Gzip: request.Headers.Add(HeaderNames.AcceptEncoding, new[] { "gzip", "deflate" }); - } - else - { + break; + case CompressionMethod.Deflate: request.Headers.Add(HeaderNames.AcceptEncoding, "deflate"); - } + break; + case CompressionMethod.Gzip: + request.Headers.Add(HeaderNames.AcceptEncoding, "gzip"); + break; + case 0: + default: + break; } if (options.EnableKeepAlive) @@ -134,20 +128,8 @@ namespace Emby.Server.Implementations.HttpClientManager request.Headers.Add(HeaderNames.Connection, "Keep-Alive"); } - if (!string.IsNullOrEmpty(options.Host)) - { - request.Headers.Add(HeaderNames.Host, options.Host); - } - - if (!string.IsNullOrEmpty(options.Referer)) - { - request.Headers.Add(HeaderNames.Referer, options.Referer); - } - //request.Headers.Add(HeaderNames.CacheControl, "no-cache"); - //request.Headers.Add(HeaderNames., options.TimeoutMs; - /* if (!string.IsNullOrWhiteSpace(userInfo)) { @@ -188,9 +170,7 @@ namespace Emby.Server.Implementations.HttpClientManager /// The options. /// Task{HttpResponseInfo}. public Task GetResponse(HttpRequestOptions options) - { - return SendAsync(options, HttpMethod.Get); - } + => SendAsync(options, HttpMethod.Get); /// /// Performs a GET request and returns the resulting stream @@ -324,18 +304,29 @@ namespace Emby.Server.Implementations.HttpClientManager options.CancellationToken.ThrowIfCancellationRequested(); - var client = GetHttpClient(options.Url, options.EnableHttpCompression); + var client = GetHttpClient(options.Url); var httpWebRequest = GetRequestMessage(options, httpMethod); - if (options.RequestContentBytes != null || - !string.IsNullOrEmpty(options.RequestContent) || - httpMethod == HttpMethod.Post) + if (options.RequestContentBytes != null + || !string.IsNullOrEmpty(options.RequestContent) + || httpMethod == HttpMethod.Post) { try { - httpWebRequest.Content = new StringContent(Encoding.UTF8.GetString(options.RequestContentBytes) ?? options.RequestContent ?? string.Empty); - + if (options.RequestContentBytes != null) + { + httpWebRequest.Content = new ByteArrayContent(options.RequestContentBytes); + } + else if (options.RequestContent != null) + { + httpWebRequest.Content = new StringContent(options.RequestContent); + } + else + { + httpWebRequest.Content = new ByteArrayContent(Array.Empty()); + } + /* var contentType = options.RequestContentType ?? "application/x-www-form-urlencoded"; if (options.AppendCharsetToMimeType) @@ -343,8 +334,11 @@ namespace Emby.Server.Implementations.HttpClientManager contentType = contentType.TrimEnd(';') + "; charset=\"utf-8\""; } - httpWebRequest.Headers.Add(HeaderNames.ContentType, contentType); - await client.SendAsync(httpWebRequest).ConfigureAwait(false); + httpWebRequest.Headers.Add(HeaderNames.ContentType, contentType);*/ + using (var response = await client.SendAsync(httpWebRequest).ConfigureAwait(false)) + { + return await HandleResponseAsync(response, options).ConfigureAwait(false); + } } catch (Exception ex) { @@ -374,18 +368,7 @@ namespace Emby.Server.Implementations.HttpClientManager using (var response = await client.SendAsync(httpWebRequest).ConfigureAwait(false)) { - await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); - - options.CancellationToken.ThrowIfCancellationRequested(); - - using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) - { - var memoryStream = new MemoryStream(); - await stream.CopyToAsync(memoryStream).ConfigureAwait(false); - memoryStream.Position = 0; - - return GetResponseInfo(response, memoryStream, memoryStream.Length, null); - } + return await HandleResponseAsync(response, options).ConfigureAwait(false); } } catch (OperationCanceledException ex) @@ -394,9 +377,25 @@ namespace Emby.Server.Implementations.HttpClientManager } } - private HttpResponseInfo GetResponseInfo(HttpResponseMessage httpResponse, Stream content, long? contentLength, IDisposable disposable) + private async Task HandleResponseAsync(HttpResponseMessage response, HttpRequestOptions options) + { + await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); + + options.CancellationToken.ThrowIfCancellationRequested(); + + using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + { + var memoryStream = new MemoryStream(); + await stream.CopyToAsync(memoryStream, 81920, options.CancellationToken).ConfigureAwait(false); + memoryStream.Position = 0; + + return GetResponseInfo(response, memoryStream, memoryStream.Length); + } + } + + private HttpResponseInfo GetResponseInfo(HttpResponseMessage httpResponse, Stream content, long? contentLength) { - var responseInfo = new HttpResponseInfo(disposable) + var responseInfo = new HttpResponseInfo() { Content = content, StatusCode = httpResponse.StatusCode, @@ -433,16 +432,14 @@ namespace Emby.Server.Implementations.HttpClientManager private static void SetHeaders(HttpContentHeaders headers, HttpResponseInfo responseInfo) { - foreach (var key in headers) + foreach (var header in headers) { - responseInfo.Headers[key.Key] = string.Join(", ", key.Value); + responseInfo.Headers[header.Key] = string.Join(", ", header.Value); } } public Task Post(HttpRequestOptions options) - { - return SendAsync(options, HttpMethod.Post); - } + => SendAsync(options, HttpMethod.Post); /// /// Downloads the contents of a given url into a temporary location @@ -451,10 +448,8 @@ namespace Emby.Server.Implementations.HttpClientManager /// Task{System.String}. public async Task GetTempFile(HttpRequestOptions options) { - using (var response = await GetTempFileResponse(options).ConfigureAwait(false)) - { - return response.TempFilePath; - } + var response = await GetTempFileResponse(options).ConfigureAwait(false); + return response.TempFilePath; } public async Task GetTempFileResponse(HttpRequestOptions options) @@ -481,13 +476,13 @@ namespace Emby.Server.Implementations.HttpClientManager _logger.LogDebug("HttpClientManager.GetTempFileResponse url: {0}", options.Url); } - var client = GetHttpClient(options.Url, options.EnableHttpCompression); + var client = GetHttpClient(options.Url); try { options.CancellationToken.ThrowIfCancellationRequested(); - using (var response = (await client.SendAsync(httpWebRequest).ConfigureAwait(false))) + using (var response = (await client.SendAsync(httpWebRequest, options.CancellationToken).ConfigureAwait(false))) { await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); @@ -530,7 +525,7 @@ namespace Emby.Server.Implementations.HttpClientManager { if (options.LogErrors) { - _logger.LogError(webException, "Error {status} getting response from {url}", webException.Status, options.Url); + _logger.LogError(webException, "Error {Status} getting response from {Url}", webException.Status, options.Url); } var exception = new HttpException(webException.Message, webException); @@ -565,7 +560,7 @@ namespace Emby.Server.Implementations.HttpClientManager if (options.LogErrors) { - _logger.LogError(ex, "Error getting response from {url}", options.Url); + _logger.LogError(ex, "Error getting response from {Url}", options.Url); } return ex; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index dd636e6cd..8dee7046e 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -71,7 +71,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV UserAgent = "Emby/3.0", // Shouldn't matter but may cause issues - EnableHttpCompression = false + DecompressionMethod = CompressionMethod.None }; using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false)) diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index f3f747718..f5dffc22a 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -627,15 +627,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings ListingsProviderInfo providerInfo) { // Schedules direct requires that the client support compression and will return a 400 response without it - options.EnableHttpCompression = true; - - // On windows 7 under .net core, this header is not getting added -#if NETSTANDARD2_0 - if (Environment.OSVersion.Platform == PlatformID.Win32NT) - { - options.RequestHeaders[HeaderNames.AcceptEncoding] = "deflate"; - } -#endif + options.DecompressionMethod = CompressionMethod.Deflate; try { @@ -665,15 +657,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings ListingsProviderInfo providerInfo) { // Schedules direct requires that the client support compression and will return a 400 response without it - options.EnableHttpCompression = true; - - // On windows 7 under .net core, this header is not getting added -#if NETSTANDARD2_0 - if (Environment.OSVersion.Platform == PlatformID.Win32NT) - { - options.RequestHeaders[HeaderNames.AcceptEncoding] = "deflate"; - } -#endif + options.DecompressionMethod = CompressionMethod.Deflate; try { diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 69b10e6da..d39c91783 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -73,11 +73,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings CancellationToken = cancellationToken, Url = path, Progress = new SimpleProgress(), - DecompressionMethod = CompressionMethod.Gzip, - // It's going to come back gzipped regardless of this value // So we need to make sure the decompression method is set to gzip - EnableHttpCompression = true, + DecompressionMethod = CompressionMethod.Gzip, UserAgent = "Emby/3.0" diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index e8b34da0c..60c914898 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts CancellationToken = CancellationToken.None, BufferContent = false, - EnableHttpCompression = false, + DecompressionMethod = CompressionMethod.None, }; foreach (var header in mediaSource.RequiredHttpHeaders) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index d4b10c8c8..95ce08f26 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -118,8 +118,20 @@ namespace Jellyfin.Server SQLitePCL.Batteries_V2.Init(); + // Increase the max http request limit + // The default connection limit is 10 for ASP.NET hosted applications and 2 for all others. + ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit); + + // Disable the "Expect: 100-Continue" header by default + // http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c + ServicePointManager.Expect100Continue = false; + +// CA5359: Do Not Disable Certificate Validation +#pragma warning disable CA5359 + // Allow all https requests ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); +#pragma warning restore CA5359 var fileSystem = new ManagedFileSystem(_loggerFactory, appPaths); diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs index 38e0ff0f5..432e389d3 100644 --- a/MediaBrowser.Common/Net/HttpRequestOptions.cs +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading; using Microsoft.Net.Http.Headers; @@ -17,7 +16,7 @@ namespace MediaBrowser.Common.Net /// The URL. public string Url { get; set; } - public CompressionMethod? DecompressionMethod { get; set; } + public CompressionMethod DecompressionMethod { get; set; } /// /// Gets or sets the accept header. @@ -49,13 +48,21 @@ namespace MediaBrowser.Common.Net /// Gets or sets the referrer. /// /// The referrer. - public string Referer { get; set; } + public string Referer + { + get => GetHeaderValue(HeaderNames.Referer); + set => RequestHeaders[HeaderNames.Referer] = value; + } /// /// Gets or sets the host. /// /// The host. - public string Host { get; set; } + public string Host + { + get => GetHeaderValue(HeaderNames.Host); + set => RequestHeaders[HeaderNames.Host] = value; + } /// /// Gets or sets the progress. @@ -63,12 +70,6 @@ namespace MediaBrowser.Common.Net /// The progress. public IProgress Progress { get; set; } - /// - /// Gets or sets a value indicating whether [enable HTTP compression]. - /// - /// true if [enable HTTP compression]; otherwise, false. - public bool EnableHttpCompression { get; set; } - public Dictionary RequestHeaders { get; private set; } public string RequestContentType { get; set; } @@ -104,13 +105,12 @@ namespace MediaBrowser.Common.Net /// public HttpRequestOptions() { - EnableHttpCompression = true; - RequestHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); LogRequest = true; LogErrors = true; CacheMode = CacheMode.None; + DecompressionMethod = CompressionMethod.Deflate; } } @@ -122,7 +122,8 @@ namespace MediaBrowser.Common.Net public enum CompressionMethod { - Deflate, - Gzip + None = 0b00000001, + Deflate = 0b00000010, + Gzip = 0b00000100 } } diff --git a/MediaBrowser.Common/Net/HttpResponseInfo.cs b/MediaBrowser.Common/Net/HttpResponseInfo.cs index 186674167..aa496adac 100644 --- a/MediaBrowser.Common/Net/HttpResponseInfo.cs +++ b/MediaBrowser.Common/Net/HttpResponseInfo.cs @@ -52,13 +52,6 @@ namespace MediaBrowser.Common.Net /// The headers. public Dictionary Headers { get; set; } - private readonly IDisposable _disposable; - - public HttpResponseInfo(IDisposable disposable) - { - _disposable = disposable; - Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); - } public HttpResponseInfo() { Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -66,10 +59,7 @@ namespace MediaBrowser.Common.Net public void Dispose() { - if (_disposable != null) - { - _disposable.Dispose(); - } + // Only IDisposable for backwards compatibility } } } -- cgit v1.2.3 From 3603c64fa6033eca4a92c4f537fddc182817998a Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 14 Jun 2019 17:14:42 +0200 Subject: Use HttpResponseHeaders instead of a dictionary --- .../HttpClientManager/HttpClientManager.cs | 67 ++++++---------------- MediaBrowser.Common/Net/HttpResponseInfo.cs | 11 +++- 2 files changed, 26 insertions(+), 52 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 987657bcb..7af0efc17 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -4,8 +4,6 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; -using System.Net.Http.Headers; -using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; @@ -389,52 +387,16 @@ namespace Emby.Server.Implementations.HttpClientManager await stream.CopyToAsync(memoryStream, 81920, options.CancellationToken).ConfigureAwait(false); memoryStream.Position = 0; - return GetResponseInfo(response, memoryStream, memoryStream.Length); - } - } - - private HttpResponseInfo GetResponseInfo(HttpResponseMessage httpResponse, Stream content, long? contentLength) - { - var responseInfo = new HttpResponseInfo() - { - Content = content, - StatusCode = httpResponse.StatusCode, - ContentType = httpResponse.Content.Headers.ContentType?.MediaType, - ContentLength = contentLength, - ResponseUrl = httpResponse.Content.Headers.ContentLocation?.ToString() - }; - - if (httpResponse.Headers != null) - { - SetHeaders(httpResponse.Content.Headers, responseInfo); - } - - return responseInfo; - } - - private HttpResponseInfo GetResponseInfo(HttpResponseMessage httpResponse, string tempFile, long? contentLength) - { - var responseInfo = new HttpResponseInfo - { - TempFilePath = tempFile, - StatusCode = httpResponse.StatusCode, - ContentType = httpResponse.Content.Headers.ContentType?.MediaType, - ContentLength = contentLength - }; - - if (httpResponse.Headers != null) - { - SetHeaders(httpResponse.Content.Headers, responseInfo); - } - - return responseInfo; - } + var responseInfo = new HttpResponseInfo(response.Headers) + { + Content = memoryStream, + StatusCode = response.StatusCode, + ContentType = response.Content.Headers.ContentType?.MediaType, + ContentLength = memoryStream.Length, + ResponseUrl = response.Content.Headers.ContentLocation?.ToString() + }; - private static void SetHeaders(HttpContentHeaders headers, HttpResponseInfo responseInfo) - { - foreach (var header in headers) - { - responseInfo.Headers[header.Key] = string.Join(", ", header.Value); + return responseInfo; } } @@ -496,8 +458,15 @@ namespace Emby.Server.Implementations.HttpClientManager options.Progress.Report(100); - var contentLength = response.Content.Headers.ContentLength; - return GetResponseInfo(response, tempFile, contentLength); + var responseInfo = new HttpResponseInfo(response.Headers) + { + TempFilePath = tempFile, + StatusCode = response.StatusCode, + ContentType = response.Content.Headers.ContentType?.MediaType, + ContentLength = response.Content.Headers.ContentLength + }; + + return responseInfo; } } catch (Exception ex) diff --git a/MediaBrowser.Common/Net/HttpResponseInfo.cs b/MediaBrowser.Common/Net/HttpResponseInfo.cs index aa496adac..cd9feabfe 100644 --- a/MediaBrowser.Common/Net/HttpResponseInfo.cs +++ b/MediaBrowser.Common/Net/HttpResponseInfo.cs @@ -1,7 +1,7 @@ using System; -using System.Collections.Generic; using System.IO; using System.Net; +using System.Net.Http.Headers; namespace MediaBrowser.Common.Net { @@ -50,11 +50,16 @@ namespace MediaBrowser.Common.Net /// Gets or sets the headers. /// /// The headers. - public Dictionary Headers { get; set; } + public HttpResponseHeaders Headers { get; set; } public HttpResponseInfo() { - Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + + } + + public HttpResponseInfo(HttpResponseHeaders headers) + { + Headers = headers; } public void Dispose() -- cgit v1.2.3 From b117b364f2f60db33a100a46d12ff5f2b2c4193d Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 14 Jun 2019 17:31:56 +0200 Subject: Remove duplicate code --- .../HttpClientManager/HttpClientManager.cs | 112 +++++++++------------ 1 file changed, 45 insertions(+), 67 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 7af0efc17..b8c52a53f 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -310,38 +310,28 @@ namespace Emby.Server.Implementations.HttpClientManager || !string.IsNullOrEmpty(options.RequestContent) || httpMethod == HttpMethod.Post) { - try - { - if (options.RequestContentBytes != null) - { - httpWebRequest.Content = new ByteArrayContent(options.RequestContentBytes); - } - else if (options.RequestContent != null) - { - httpWebRequest.Content = new StringContent(options.RequestContent); - } - else - { - httpWebRequest.Content = new ByteArrayContent(Array.Empty()); - } - /* - var contentType = options.RequestContentType ?? "application/x-www-form-urlencoded"; - - if (options.AppendCharsetToMimeType) - { - contentType = contentType.TrimEnd(';') + "; charset=\"utf-8\""; - } - httpWebRequest.Headers.Add(HeaderNames.ContentType, contentType);*/ - using (var response = await client.SendAsync(httpWebRequest).ConfigureAwait(false)) - { - return await HandleResponseAsync(response, options).ConfigureAwait(false); - } + if (options.RequestContentBytes != null) + { + httpWebRequest.Content = new ByteArrayContent(options.RequestContentBytes); + } + else if (options.RequestContent != null) + { + httpWebRequest.Content = new StringContent(options.RequestContent); } - catch (Exception ex) + else + { + httpWebRequest.Content = new ByteArrayContent(Array.Empty()); + } + /* + var contentType = options.RequestContentType ?? "application/x-www-form-urlencoded"; + + if (options.AppendCharsetToMimeType) { - throw new HttpException(ex.Message) { IsTimedOut = true }; + contentType = contentType.TrimEnd(';') + "; charset=\"utf-8\""; } + + httpWebRequest.Headers.Add(HeaderNames.ContentType, contentType);*/ } if (options.LogRequest) @@ -349,54 +339,42 @@ namespace Emby.Server.Implementations.HttpClientManager _logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToString(), options.Url); } - try - { - options.CancellationToken.ThrowIfCancellationRequested(); + options.CancellationToken.ThrowIfCancellationRequested(); - /*if (!options.BufferContent) - { - var response = await client.HttpClient.SendAsync(httpWebRequest).ConfigureAwait(false); + /*if (!options.BufferContent) + { + var response = await client.HttpClient.SendAsync(httpWebRequest).ConfigureAwait(false); - await EnsureSuccessStatusCode(client, response, options).ConfigureAwait(false); + await EnsureSuccessStatusCode(client, response, options).ConfigureAwait(false); - options.CancellationToken.ThrowIfCancellationRequested(); + options.CancellationToken.ThrowIfCancellationRequested(); - return GetResponseInfo(response, await response.Content.ReadAsStreamAsync().ConfigureAwait(false), response.Content.Headers.ContentLength, response); - }*/ + return GetResponseInfo(response, await response.Content.ReadAsStreamAsync().ConfigureAwait(false), response.Content.Headers.ContentLength, response); + }*/ - using (var response = await client.SendAsync(httpWebRequest).ConfigureAwait(false)) - { - return await HandleResponseAsync(response, options).ConfigureAwait(false); - } - } - catch (OperationCanceledException ex) + using (var response = await client.SendAsync(httpWebRequest, options.CancellationToken).ConfigureAwait(false)) { - throw GetCancellationException(options, options.CancellationToken, ex); - } - } - - private async Task HandleResponseAsync(HttpResponseMessage response, HttpRequestOptions options) - { - await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); - - options.CancellationToken.ThrowIfCancellationRequested(); + await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); - using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) - { - var memoryStream = new MemoryStream(); - await stream.CopyToAsync(memoryStream, 81920, options.CancellationToken).ConfigureAwait(false); - memoryStream.Position = 0; + options.CancellationToken.ThrowIfCancellationRequested(); - var responseInfo = new HttpResponseInfo(response.Headers) + using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { - Content = memoryStream, - StatusCode = response.StatusCode, - ContentType = response.Content.Headers.ContentType?.MediaType, - ContentLength = memoryStream.Length, - ResponseUrl = response.Content.Headers.ContentLocation?.ToString() - }; + var memoryStream = new MemoryStream(); + await stream.CopyToAsync(memoryStream, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); + memoryStream.Position = 0; - return responseInfo; + var responseInfo = new HttpResponseInfo(response.Headers) + { + Content = memoryStream, + StatusCode = response.StatusCode, + ContentType = response.Content.Headers.ContentType?.MediaType, + ContentLength = memoryStream.Length, + ResponseUrl = response.Content.Headers.ContentLocation?.ToString() + }; + + return responseInfo; + } } } @@ -603,7 +581,7 @@ namespace Emby.Server.Implementations.HttpClientManager } var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - _logger.LogError(msg); + _logger.LogError("HTTP request failed with message: {Message}", msg); throw new HttpException(response.ReasonPhrase) { -- cgit v1.2.3 From 5fc4ad6c4e9aab8246e70a064c8506d050cf2147 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 1 Jul 2019 19:24:42 +0200 Subject: Address comments --- .../HttpClientManager/HttpClientManager.cs | 58 ++++++++++------------ MediaBrowser.Common/Net/HttpRequestOptions.cs | 1 + 2 files changed, 26 insertions(+), 33 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index b8c52a53f..ae62f34e0 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -21,19 +21,18 @@ namespace Emby.Server.Implementations.HttpClientManager /// public class HttpClientManager : IHttpClient { - /// - /// The _logger - /// private readonly ILogger _logger; - - /// - /// The _app paths - /// private readonly IApplicationPaths _appPaths; - private readonly IFileSystem _fileSystem; private readonly Func _defaultUserAgentFn; + /// + /// Holds a dictionary of http clients by host. Use GetHttpClient(host) to retrieve or create a client for web requests. + /// DON'T dispose it after use. + /// + /// The HTTP clients. + private readonly ConcurrentDictionary _httpClients = new ConcurrentDictionary(); + /// /// Initializes a new instance of the class. /// @@ -60,19 +59,10 @@ namespace Emby.Server.Implementations.HttpClientManager } /// - /// Holds a dictionary of http clients by host. Use GetHttpClient(host) to retrieve or create a client for web requests. - /// DON'T dispose it after use. - /// - /// The HTTP clients. - private readonly ConcurrentDictionary _httpClients = new ConcurrentDictionary(); - - /// - /// Gets + /// Gets the correct http client for the given url. /// - /// The host. - /// if set to true [enable HTTP compression]. + /// The url. /// HttpClient. - /// host private HttpClient GetHttpClient(string url) { var key = GetHostFromUrl(url); @@ -116,7 +106,6 @@ namespace Emby.Server.Implementations.HttpClientManager case CompressionMethod.Gzip: request.Headers.Add(HeaderNames.AcceptEncoding, "gzip"); break; - case 0: default: break; } @@ -187,8 +176,6 @@ namespace Emby.Server.Implementations.HttpClientManager /// The options. /// The HTTP method. /// Task{HttpResponseInfo}. - /// - /// public Task SendAsync(HttpRequestOptions options, string httpMethod) { var httpMethod2 = GetHttpMethod(httpMethod); @@ -201,8 +188,6 @@ namespace Emby.Server.Implementations.HttpClientManager /// The options. /// The HTTP method. /// Task{HttpResponseInfo}. - /// - /// public async Task SendAsync(HttpRequestOptions options, HttpMethod httpMethod) { if (options.CacheMode == CacheMode.None) @@ -310,7 +295,6 @@ namespace Emby.Server.Implementations.HttpClientManager || !string.IsNullOrEmpty(options.RequestContent) || httpMethod == HttpMethod.Post) { - if (options.RequestContentBytes != null) { httpWebRequest.Content = new ByteArrayContent(options.RequestContentBytes); @@ -323,6 +307,8 @@ namespace Emby.Server.Implementations.HttpClientManager { httpWebRequest.Content = new ByteArrayContent(Array.Empty()); } + + // TODO: add correct content type /* var contentType = options.RequestContentType ?? "application/x-www-form-urlencoded"; @@ -341,16 +327,24 @@ namespace Emby.Server.Implementations.HttpClientManager options.CancellationToken.ThrowIfCancellationRequested(); - /*if (!options.BufferContent) + if (!options.BufferContent) { - var response = await client.HttpClient.SendAsync(httpWebRequest).ConfigureAwait(false); + var response = await client.SendAsync(httpWebRequest, options.CancellationToken).ConfigureAwait(false); - await EnsureSuccessStatusCode(client, response, options).ConfigureAwait(false); + await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); options.CancellationToken.ThrowIfCancellationRequested(); - return GetResponseInfo(response, await response.Content.ReadAsStreamAsync().ConfigureAwait(false), response.Content.Headers.ContentLength, response); - }*/ + var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + return new HttpResponseInfo(response.Headers) + { + Content = stream, + StatusCode = response.StatusCode, + ContentType = response.Content.Headers.ContentType?.MediaType, + ContentLength = stream.Length, + ResponseUrl = response.Content.Headers.ContentLocation?.ToString() + }; + } using (var response = await client.SendAsync(httpWebRequest, options.CancellationToken).ConfigureAwait(false)) { @@ -364,7 +358,7 @@ namespace Emby.Server.Implementations.HttpClientManager await stream.CopyToAsync(memoryStream, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); memoryStream.Position = 0; - var responseInfo = new HttpResponseInfo(response.Headers) + return new HttpResponseInfo(response.Headers) { Content = memoryStream, StatusCode = response.StatusCode, @@ -372,8 +366,6 @@ namespace Emby.Server.Implementations.HttpClientManager ContentLength = memoryStream.Length, ResponseUrl = response.Content.Headers.ContentLocation?.ToString() }; - - return responseInfo; } } } diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs index 432e389d3..0576a1a5d 100644 --- a/MediaBrowser.Common/Net/HttpRequestOptions.cs +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -120,6 +120,7 @@ namespace MediaBrowser.Common.Net Unconditional = 1 } + [Flags] public enum CompressionMethod { None = 0b00000001, -- cgit v1.2.3 From 0dbc294836bbf1f11e370efeeae0904d3bb099e0 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Sat, 6 Jul 2019 23:50:06 +0200 Subject: Move VACUUM command to fix merge error This fixes a syntax error. --- Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 919453d2a..010ad6384 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -124,6 +124,9 @@ namespace Emby.Server.Implementations.Data } WriteConnection.Execute("PRAGMA temp_store=" + (int)TempStore); + + // Configuration and pragmas can affect VACUUM so it needs to be last. + WriteConnection.Execute("VACUUM"); return new ManagedConnection(WriteConnection, WriteLock); } @@ -170,8 +173,6 @@ namespace Emby.Server.Implementations.Data columnNames.Add(name); } } - // Configuration and pragmas can affect VACUUM so it needs to be last. - queries.Add("VACUUM"); return columnNames; } -- cgit v1.2.3 From b1bd0627098e67169b9ceaa318a32f4d2a9fba30 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 7 Jul 2019 18:04:06 +0200 Subject: Properly set content type --- Emby.Dlna/PlayTo/SsdpHttpClient.cs | 1 - .../HttpClientManager/HttpClientManager.cs | 16 ++++------------ MediaBrowser.Common/Net/HttpRequestOptions.cs | 2 -- Mono.Nat/Upnp/Messages/UpnpMessage.cs | 5 +---- 4 files changed, 5 insertions(+), 19 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs index 780b0a889..22aaa6885 100644 --- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs +++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs @@ -155,7 +155,6 @@ namespace Emby.Dlna.PlayTo } options.RequestContentType = "text/xml"; - options.AppendCharsetToMimeType = true; options.RequestContent = postData; return _httpClient.Post(options); diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index ae62f34e0..681859f69 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -301,23 +301,15 @@ namespace Emby.Server.Implementations.HttpClientManager } else if (options.RequestContent != null) { - httpWebRequest.Content = new StringContent(options.RequestContent); + httpWebRequest.Content = new StringContent( + options.RequestContent, + null, + options.RequestContentType); } else { httpWebRequest.Content = new ByteArrayContent(Array.Empty()); } - - // TODO: add correct content type - /* - var contentType = options.RequestContentType ?? "application/x-www-form-urlencoded"; - - if (options.AppendCharsetToMimeType) - { - contentType = contentType.TrimEnd(';') + "; charset=\"utf-8\""; - } - - httpWebRequest.Headers.Add(HeaderNames.ContentType, contentType);*/ } if (options.LogRequest) diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs index 0576a1a5d..76bd35e57 100644 --- a/MediaBrowser.Common/Net/HttpRequestOptions.cs +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -91,8 +91,6 @@ namespace MediaBrowser.Common.Net public bool EnableDefaultUserAgent { get; set; } - public bool AppendCharsetToMimeType { get; set; } - private string GetHeaderValue(string name) { RequestHeaders.TryGetValue(name, out var value); diff --git a/Mono.Nat/Upnp/Messages/UpnpMessage.cs b/Mono.Nat/Upnp/Messages/UpnpMessage.cs index 1151dd997..ade9df50b 100644 --- a/Mono.Nat/Upnp/Messages/UpnpMessage.cs +++ b/Mono.Nat/Upnp/Messages/UpnpMessage.cs @@ -57,10 +57,9 @@ namespace Mono.Nat.Upnp req.Url = ss; req.EnableKeepAlive = false; req.RequestContentType = "text/xml"; - req.AppendCharsetToMimeType = true; req.RequestHeaders.Add("SOAPACTION", "\"" + device.ServiceType + "#" + upnpMethod + "\""); - string bodyString = "" + "" @@ -70,8 +69,6 @@ namespace Mono.Nat.Upnp + "" + "" + "\r\n\r\n"; - - req.RequestContentBytes = System.Text.Encoding.UTF8.GetBytes(bodyString); return req; } -- cgit v1.2.3 From 7bb504d491746d5602b95ab74ed60863cd2e7078 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 7 Jul 2019 18:26:01 +0200 Subject: Create a new HttpMethod from the function name --- .../HttpClientManager/HttpClientManager.cs | 39 +--------------------- 1 file changed, 1 insertion(+), 38 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 681859f69..613545e21 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -177,10 +177,7 @@ namespace Emby.Server.Implementations.HttpClientManager /// The HTTP method. /// Task{HttpResponseInfo}. public Task SendAsync(HttpRequestOptions options, string httpMethod) - { - var httpMethod2 = GetHttpMethod(httpMethod); - return SendAsync(options, httpMethod2); - } + => SendAsync(options, new HttpMethod(httpMethod)); /// /// send as an asynchronous operation. @@ -216,40 +213,6 @@ namespace Emby.Server.Implementations.HttpClientManager return response; } - private HttpMethod GetHttpMethod(string httpMethod) - { - if (httpMethod.Equals("DELETE", StringComparison.OrdinalIgnoreCase)) - { - return HttpMethod.Delete; - } - else if (httpMethod.Equals("GET", StringComparison.OrdinalIgnoreCase)) - { - return HttpMethod.Get; - } - else if (httpMethod.Equals("HEAD", StringComparison.OrdinalIgnoreCase)) - { - return HttpMethod.Head; - } - else if (httpMethod.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase)) - { - return HttpMethod.Options; - } - else if (httpMethod.Equals("POST", StringComparison.OrdinalIgnoreCase)) - { - return HttpMethod.Post; - } - else if (httpMethod.Equals("PUT", StringComparison.OrdinalIgnoreCase)) - { - return HttpMethod.Put; - } - else if (httpMethod.Equals("TRACE", StringComparison.OrdinalIgnoreCase)) - { - return HttpMethod.Trace; - } - - throw new ArgumentException("Invalid HTTP method", nameof(httpMethod)); - } - private HttpResponseInfo GetCachedResponse(string responseCachePath, TimeSpan cacheLength, string url) { if (File.Exists(responseCachePath) -- cgit v1.2.3 From b294b802a8bfd80958f8b273895ab430f24cc9d0 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 8 Jul 2019 14:50:15 +0200 Subject: Try to fix XmlTvListingsProvider --- .../HttpClientManager/HttpClientManager.cs | 6 +- .../LiveTv/Listings/XmlTvListingsProvider.cs | 66 ++++++++++++---------- MediaBrowser.Common/Net/HttpResponseInfo.cs | 9 ++- 3 files changed, 47 insertions(+), 34 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index ae62f34e0..45cf4acec 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -336,7 +336,7 @@ namespace Emby.Server.Implementations.HttpClientManager options.CancellationToken.ThrowIfCancellationRequested(); var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - return new HttpResponseInfo(response.Headers) + return new HttpResponseInfo(response.Headers, response.Content.Headers) { Content = stream, StatusCode = response.StatusCode, @@ -358,7 +358,7 @@ namespace Emby.Server.Implementations.HttpClientManager await stream.CopyToAsync(memoryStream, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); memoryStream.Position = 0; - return new HttpResponseInfo(response.Headers) + return new HttpResponseInfo(response.Headers, response.Content.Headers) { Content = memoryStream, StatusCode = response.StatusCode, @@ -428,7 +428,7 @@ namespace Emby.Server.Implementations.HttpClientManager options.Progress.Report(100); - var responseInfo = new HttpResponseInfo(response.Headers) + var responseInfo = new HttpResponseInfo(response.Headers, response.Content.Headers) { TempFilePath = tempFile, StatusCode = response.StatusCode, diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index d39c91783..c1468121a 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -2,7 +2,9 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.IO.Compression; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Emby.XmlTv.Classes; @@ -52,7 +54,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings private async Task GetXml(string path, CancellationToken cancellationToken) { - _logger.LogInformation("xmltv path: {path}", path); + _logger.LogInformation("xmltv path: {Path}", path); if (!path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { @@ -66,24 +68,38 @@ namespace Emby.Server.Implementations.LiveTv.Listings return UnzipIfNeeded(path, cacheFile); } - _logger.LogInformation("Downloading xmltv listings from {path}", path); - - string tempFile = await _httpClient.GetTempFile(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = path, - Progress = new SimpleProgress(), - // It's going to come back gzipped regardless of this value - // So we need to make sure the decompression method is set to gzip - DecompressionMethod = CompressionMethod.Gzip, - - UserAgent = "Emby/3.0" - - }).ConfigureAwait(false); + _logger.LogInformation("Downloading xmltv listings from {Path}", path); Directory.CreateDirectory(Path.GetDirectoryName(cacheFile)); - File.Copy(tempFile, cacheFile, true); + using (var res = await _httpClient.SendAsync( + new HttpRequestOptions + { + CancellationToken = cancellationToken, + Url = path, + Progress = new SimpleProgress(), + // It's going to come back gzipped regardless of this value + // So we need to make sure the decompression method is set to gzip + DecompressionMethod = CompressionMethod.Gzip, + + UserAgent = "Emby/3.0" + }, + HttpMethod.Get).ConfigureAwait(false)) + using (var stream = res.Content) + using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew)) + { + if (res.ContentHeaders.ContentEncoding.Contains("gzip")) + { + using (var gzStream = new GZipStream(stream, CompressionMode.Decompress)) + { + await gzStream.CopyToAsync(fileStream).ConfigureAwait(false); + } + } + else + { + await stream.CopyToAsync(fileStream).ConfigureAwait(false); + } + } return UnzipIfNeeded(path, cacheFile); } @@ -159,20 +175,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings throw new ArgumentNullException(nameof(channelId)); } - /* - if (!await EmbyTV.EmbyTVRegistration.Instance.EnableXmlTv().ConfigureAwait(false)) - { - var length = endDateUtc - startDateUtc; - if (length.TotalDays > 1) - { - endDateUtc = startDateUtc.AddDays(1); - } - }*/ - - _logger.LogDebug("Getting xmltv programs for channel {id}", channelId); + _logger.LogDebug("Getting xmltv programs for channel {Id}", channelId); string path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false); - _logger.LogDebug("Opening XmlTvReader for {path}", path); + _logger.LogDebug("Opening XmlTvReader for {Path}", path); var reader = new XmlTvReader(path, GetLanguage(info)); return reader.GetProgrammes(channelId, startDateUtc, endDateUtc, cancellationToken) @@ -265,7 +271,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { // In theory this should never be called because there is always only one lineup string path = await GetXml(info.Path, CancellationToken.None).ConfigureAwait(false); - _logger.LogDebug("Opening XmlTvReader for {path}", path); + _logger.LogDebug("Opening XmlTvReader for {Path}", path); var reader = new XmlTvReader(path, GetLanguage(info)); IEnumerable results = reader.GetChannels(); @@ -277,7 +283,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { // In theory this should never be called because there is always only one lineup string path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false); - _logger.LogDebug("Opening XmlTvReader for {path}", path); + _logger.LogDebug("Opening XmlTvReader for {Path}", path); var reader = new XmlTvReader(path, GetLanguage(info)); var results = reader.GetChannels(); diff --git a/MediaBrowser.Common/Net/HttpResponseInfo.cs b/MediaBrowser.Common/Net/HttpResponseInfo.cs index cd9feabfe..d65ce897a 100644 --- a/MediaBrowser.Common/Net/HttpResponseInfo.cs +++ b/MediaBrowser.Common/Net/HttpResponseInfo.cs @@ -52,14 +52,21 @@ namespace MediaBrowser.Common.Net /// The headers. public HttpResponseHeaders Headers { get; set; } + /// + /// Gets or sets the content headers. + /// + /// The content headers. + public HttpContentHeaders ContentHeaders { get; set; } + public HttpResponseInfo() { } - public HttpResponseInfo(HttpResponseHeaders headers) + public HttpResponseInfo(HttpResponseHeaders headers, HttpContentHeaders contentHeader) { Headers = headers; + ContentHeaders = contentHeader; } public void Dispose() -- cgit v1.2.3 From 5d9fa06675963fd3ed12baeede541321d5211f1d Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 8 Jul 2019 15:14:51 +0200 Subject: Cleanup --- .../LiveTv/Listings/XmlTvListingsProvider.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index c1468121a..94225a0aa 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -11,7 +11,6 @@ using Emby.XmlTv.Classes; using Emby.XmlTv.Entities; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; -using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Dto; @@ -29,7 +28,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings private readonly IFileSystem _fileSystem; private readonly IZipClient _zipClient; - public XmlTvListingsProvider(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IFileSystem fileSystem, IZipClient zipClient) + public XmlTvListingsProvider( + IServerConfigurationManager config, + IHttpClient httpClient, + ILogger logger, + IFileSystem fileSystem, + IZipClient zipClient) { _config = config; _httpClient = httpClient; @@ -77,12 +81,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { CancellationToken = cancellationToken, Url = path, - Progress = new SimpleProgress(), - // It's going to come back gzipped regardless of this value - // So we need to make sure the decompression method is set to gzip DecompressionMethod = CompressionMethod.Gzip, - - UserAgent = "Emby/3.0" }, HttpMethod.Get).ConfigureAwait(false)) using (var stream = res.Content) @@ -117,7 +116,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings } catch (Exception ex) { - _logger.LogError(ex, "Error extracting from gz file {file}", file); + _logger.LogError(ex, "Error extracting from gz file {File}", file); } try @@ -127,7 +126,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings } catch (Exception ex) { - _logger.LogError(ex, "Error extracting from zip file {file}", file); + _logger.LogError(ex, "Error extracting from zip file {File}", file); } } -- cgit v1.2.3 From 886c88576c04eda76c076d056194afa9140815a7 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Fri, 19 Jul 2019 23:22:30 +0200 Subject: Use HttpCompletionOption.ResponseHeadersRead and resort to Content-Length header for non-buffered content --- Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 2752e9324..a4cd98b40 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -284,7 +284,7 @@ namespace Emby.Server.Implementations.HttpClientManager if (!options.BufferContent) { - var response = await client.SendAsync(httpWebRequest, options.CancellationToken).ConfigureAwait(false); + var response = await client.SendAsync(httpWebRequest, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false); await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); @@ -296,7 +296,7 @@ namespace Emby.Server.Implementations.HttpClientManager Content = stream, StatusCode = response.StatusCode, ContentType = response.Content.Headers.ContentType?.MediaType, - ContentLength = stream.Length, + ContentLength = response.Content.Headers.ContentLength, ResponseUrl = response.Content.Headers.ContentLocation?.ToString() }; } -- cgit v1.2.3 From dadfc09c01ee34b67bfc02f655ac06e4de953805 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Sat, 20 Jul 2019 13:36:59 +0200 Subject: Add HttpCompletionOption.ResponseHeadersRead to the buffering option to avoid potentially having 2 copies in memory --- Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index a4cd98b40..528053c21 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -301,7 +301,7 @@ namespace Emby.Server.Implementations.HttpClientManager }; } - using (var response = await client.SendAsync(httpWebRequest, options.CancellationToken).ConfigureAwait(false)) + using (var response = await client.SendAsync(httpWebRequest, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false)) { await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); -- cgit v1.2.3 From 0d9cdb98f2dc570d9a8dd130f10b7df0e035f6ac Mon Sep 17 00:00:00 2001 From: ancarvalho Date: Tue, 2 Jul 2019 03:01:17 +0000 Subject: Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_BR/ --- Emby.Server.Implementations/Localization/Core/pt-BR.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json index dbc9c4c4b..dd3ef57df 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-BR.json +++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json @@ -21,8 +21,8 @@ "HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteArtists": "Artistas Favoritos", "HeaderFavoriteEpisodes": "Episódios Favoritos", - "HeaderFavoriteShows": "Séries Favoritas", - "HeaderFavoriteSongs": "Músicas Favoritas", + "HeaderFavoriteShows": "Shows Favoritos", + "HeaderFavoriteSongs": "Musicas Favoritas", "HeaderLiveTV": "TV ao Vivo", "HeaderNextUp": "Próximos", "HeaderRecordingGroups": "Grupos de Gravação", -- cgit v1.2.3 From 6e29b8ad6faf81b8a5c88b4d25f2c8435f161259 Mon Sep 17 00:00:00 2001 From: ZhiGang Zhung Date: Sat, 29 Jun 2019 17:17:32 +0000 Subject: Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/ --- Emby.Server.Implementations/Localization/Core/zh-CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index 6f7d362d3..b94efffcd 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -5,7 +5,7 @@ "Artists": "艺术家", "AuthenticationSucceededWithUserName": "{0} 成功验证", "Books": "书籍", - "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", + "CameraImageUploadedFrom": "已从 {0} 上传了一张新的相机图像", "Channels": "频道", "ChapterNameValue": "章节 {0}", "Collections": "合集", -- cgit v1.2.3 From cd504e6ee533a4c40f14b1b1d0fefe4bbfd36b6d Mon Sep 17 00:00:00 2001 From: Matsuri Date: Sat, 29 Jun 2019 17:21:34 +0000 Subject: Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/ --- Emby.Server.Implementations/Localization/Core/zh-CN.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index b94efffcd..63aa6a557 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -44,7 +44,7 @@ "NameInstallFailed": "{0} 安装失败", "NameSeasonNumber": "季 {0}", "NameSeasonUnknown": "未知季", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", + "NewVersionIsAvailable": "Jellyfin Server 有新版本可以下载。", "NotificationOptionApplicationUpdateAvailable": "有可用的应用程序更新", "NotificationOptionApplicationUpdateInstalled": "应用程序更新已安装", "NotificationOptionAudioPlayback": "音频开始播放", @@ -75,7 +75,7 @@ "Songs": "歌曲", "StartupEmbyServerIsLoading": "Jellyfin 服务器加载中。请稍后再试。", "SubtitleDownloadFailureForItem": "为 {0} 下载字幕失败", - "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", + "SubtitleDownloadFailureFromForItem": "无法从 {0} 下载 {1} 的字幕", "SubtitlesDownloadedForItem": "已为 {0} 下载了字幕", "Sync": "同步", "System": "系统", @@ -91,7 +91,7 @@ "UserPolicyUpdatedWithName": "用户协议已经被更新为 {0}", "UserStartedPlayingItemWithValues": "{0} 已开始播放 {1}", "UserStoppedPlayingItemWithValues": "{0} 已停止播放 {1}", - "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", + "ValueHasBeenAddedToLibrary": "{0} 已添加至您的媒体库中", "ValueSpecialEpisodeName": "特典 - {0}", "VersionNumber": "版本 {0}" } -- cgit v1.2.3 From 8c94187c759330a77571f1a8eb65b9c2a7417f04 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 10 Jul 2019 11:22:02 +0000 Subject: Translated using Weblate (German) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/de/ --- Emby.Server.Implementations/Localization/Core/de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index 6fd63a514..0db201769 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -27,7 +27,7 @@ "HeaderNextUp": "Als Nächstes", "HeaderRecordingGroups": "Aufnahme-Gruppen", "HomeVideos": "Heimvideos", - "Inherit": "Übernehmen", + "Inherit": "Vererben", "ItemAddedWithName": "{0} wurde der Bibliothek hinzugefügt", "ItemRemovedWithName": "{0} wurde aus der Bibliothek entfernt", "LabelIpAddressValue": "IP-Adresse: {0}", -- cgit v1.2.3 From 00c92e88c575c4c5adda6269bbd06c82e79f3804 Mon Sep 17 00:00:00 2001 From: exveria1015 Date: Tue, 4 Jun 2019 16:22:41 +0000 Subject: Translated using Weblate (Japanese) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ja/ --- Emby.Server.Implementations/Localization/Core/ja.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json index 53a43a125..4aa0637c5 100644 --- a/Emby.Server.Implementations/Localization/Core/ja.json +++ b/Emby.Server.Implementations/Localization/Core/ja.json @@ -13,17 +13,17 @@ "DeviceOnlineWithName": "{0} が接続されました", "FailedLoginAttemptWithUserName": "ログインを試行しましたが {0}によって失敗しました", "Favorites": "お気に入り", - "Folders": "フォルダ", + "Folders": "フォルダー", "Genres": "ジャンル", "HeaderAlbumArtists": "アルバムアーティスト", "HeaderCameraUploads": "カメラアップロード", - "HeaderContinueWatching": "視聴中", + "HeaderContinueWatching": "視聴を続ける", "HeaderFavoriteAlbums": "お気に入りのアルバム", "HeaderFavoriteArtists": "お気に入りのアーティスト", "HeaderFavoriteEpisodes": "お気に入りのエピソード", "HeaderFavoriteShows": "お気に入りの番組", "HeaderFavoriteSongs": "お気に入りの曲", - "HeaderLiveTV": "ライブ テレビ", + "HeaderLiveTV": "ライブTV", "HeaderNextUp": "次", "HeaderRecordingGroups": "レコーディンググループ", "HomeVideos": "ホームビデオ", -- cgit v1.2.3 From b337df889e760ff8a268091d56254fc85df0f8a1 Mon Sep 17 00:00:00 2001 From: polVRtong Date: Sun, 21 Jul 2019 06:21:35 +0000 Subject: Translated using Weblate (Korean) Currently translated at 97.8% (92 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ko/ --- Emby.Server.Implementations/Localization/Core/ko.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index 5a7ba8ba7..f2b7c408c 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -20,7 +20,7 @@ "HeaderContinueWatching": "계속 시청하기", "HeaderFavoriteAlbums": "좋아하는 앨범", "HeaderFavoriteArtists": "좋아하는 아티스트", - "HeaderFavoriteEpisodes": "Favorite Episodes", + "HeaderFavoriteEpisodes": "좋아하는 에피소드", "HeaderFavoriteShows": "즐겨찾는 쇼", "HeaderFavoriteSongs": "좋아하는 노래", "HeaderLiveTV": "TV 방송", -- cgit v1.2.3 From ea41155c6b8434e685ef5dc99a7dc7502628a3ca Mon Sep 17 00:00:00 2001 From: vaheed Date: Fri, 14 Jun 2019 14:11:17 +0000 Subject: Translated using Weblate (Persian) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fa/ --- Emby.Server.Implementations/Localization/Core/fa.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json index 0a0c7553b..faa658ed5 100644 --- a/Emby.Server.Implementations/Localization/Core/fa.json +++ b/Emby.Server.Implementations/Localization/Core/fa.json @@ -5,7 +5,7 @@ "Artists": "هنرمندان", "AuthenticationSucceededWithUserName": "{0} با موفقیت تایید اعتبار شد", "Books": "کتاب ها", - "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", + "CameraImageUploadedFrom": "یک عکس جدید از دوربین ارسال شده {0}", "Channels": "کانال ها", "ChapterNameValue": "فصل {0}", "Collections": "کلکسیون ها", @@ -34,17 +34,17 @@ "LabelRunningTimeValue": "زمان اجرا: {0}", "Latest": "آخرین", "MessageApplicationUpdated": "سرور Jellyfin بروزرسانی شد", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", + "MessageApplicationUpdatedTo": "سرور جلیفین آپدیت شده به نسخه {0}", "MessageNamedServerConfigurationUpdatedWithValue": "پکربندی بخش {0} سرور بروزرسانی شد", "MessageServerConfigurationUpdated": "پیکربندی سرور بروزرسانی شد", "MixedContent": "محتوای درهم", "Movies": "فیلم های سینمایی", "Music": "موسیقی", "MusicVideos": "موزیک ویدیوها", - "NameInstallFailed": "{0} installation failed", + "NameInstallFailed": "{0} نصب با مشکل مواجه شده", "NameSeasonNumber": "فصل {0}", "NameSeasonUnknown": "فصل های ناشناخته", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", + "NewVersionIsAvailable": "یک نسخه جدید جلیفین برای بروزرسانی آماده میباشد.", "NotificationOptionApplicationUpdateAvailable": "بروزرسانی برنامه موجود است", "NotificationOptionApplicationUpdateInstalled": "بروزرسانی برنامه نصب شد", "NotificationOptionAudioPlayback": "پخش صدا آغاز شد", @@ -70,12 +70,12 @@ "ProviderValue": "ارائه دهنده: {0}", "ScheduledTaskFailedWithName": "{0} ناموفق بود", "ScheduledTaskStartedWithName": "{0} شروع شد", - "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", + "ServerNameNeedsToBeRestarted": "{0} احتیاج به راه اندازی مجدد", "Shows": "سریال ها", "Songs": "آهنگ ها", "StartupEmbyServerIsLoading": "سرور Jellyfin در حال بارگیری است. لطفا کمی بعد دوباره تلاش کنید.", "SubtitleDownloadFailureForItem": "دانلود زیرنویس برای {0} ناموفق بود", - "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", + "SubtitleDownloadFailureFromForItem": "زیرنویس برای دانلود با مشکل مواجه شده از {0} برای {1}", "SubtitlesDownloadedForItem": "زیرنویس {0} دانلود شد", "Sync": "همگامسازی", "System": "سیستم", @@ -91,7 +91,7 @@ "UserPolicyUpdatedWithName": "سیاست کاربری برای {0} بروزرسانی شد", "UserStartedPlayingItemWithValues": "{0} شروع به پخش {1} کرد", "UserStoppedPlayingItemWithValues": "{0} پخش {1} را متوقف کرد", - "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", + "ValueHasBeenAddedToLibrary": "{0} اضافه شده به کتابخانه رسانه شما", "ValueSpecialEpisodeName": "ویژه- {0}", "VersionNumber": "نسخه {0}" } -- cgit v1.2.3 From b3aaa9216de2a7d8f626fea5632eccc12dd8afe8 Mon Sep 17 00:00:00 2001 From: Tradutor da Silva Date: Tue, 23 Jul 2019 02:25:54 +0000 Subject: Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_BR/ --- Emby.Server.Implementations/Localization/Core/pt-BR.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json index dd3ef57df..5d68416e9 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-BR.json +++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json @@ -5,7 +5,7 @@ "Artists": "Artistas", "AuthenticationSucceededWithUserName": "{0} autenticado com sucesso", "Books": "Livros", - "CameraImageUploadedFrom": "Uma nova imagem da câmera foi submetida de {0}", + "CameraImageUploadedFrom": "Uma nova imagem da câmera foi enviada de {0}", "Channels": "Canais", "ChapterNameValue": "Capítulo {0}", "Collections": "Coletâneas", @@ -79,7 +79,7 @@ "SubtitlesDownloadedForItem": "Legendas baixadas para {0}", "Sync": "Sincronizar", "System": "Sistema", - "TvShows": "Séries de TV", + "TvShows": "Séries", "User": "Usuário", "UserCreatedWithName": "O usuário {0} foi criado", "UserDeletedWithName": "O usuário {0} foi excluído", -- cgit v1.2.3 From d56725a43d66354bd0c03b6b9848b063e80d1121 Mon Sep 17 00:00:00 2001 From: tluciomiranda Date: Mon, 15 Jul 2019 11:38:35 +0000 Subject: Translated using Weblate (Portuguese (Portugal)) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_PT/ --- .../Localization/Core/pt-PT.json | 174 ++++++++++----------- 1 file changed, 87 insertions(+), 87 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json index dc69d8af2..387604f03 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-PT.json +++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json @@ -1,97 +1,97 @@ { - "Albums": "Albums", - "AppDeviceValues": "App: {0}, Device: {1}", + "Albums": "Álbuns", + "AppDeviceValues": "Aplicação {0}, Dispositivo:{1}", "Application": "Aplicação", - "Artists": "Artists", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", - "Books": "Books", - "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", - "Channels": "Channels", - "ChapterNameValue": "Chapter {0}", - "Collections": "Collections", - "DeviceOfflineWithName": "{0} has disconnected", - "DeviceOnlineWithName": "{0} is connected", - "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", - "Favorites": "Favorites", - "Folders": "Folders", - "Genres": "Genres", - "HeaderAlbumArtists": "Album Artists", + "Artists": "Artistas", + "AuthenticationSucceededWithUserName": "{0} autenticado com sucesso", + "Books": "Livros", + "CameraImageUploadedFrom": "Uma nova imagem proveniente de uma câmara foi enviada a partir de {0}", + "Channels": "Canais", + "ChapterNameValue": "Capítulo {0}", + "Collections": "Coleções", + "DeviceOfflineWithName": "{0} desligou-se", + "DeviceOnlineWithName": "{0} ligou-se", + "FailedLoginAttemptWithUserName": "Tentativa de login falhada a partir de {0}", + "Favorites": "Favoritos", + "Folders": "Pastas", + "Genres": "Géneros", + "HeaderAlbumArtists": "Artistas do Álbum", "HeaderCameraUploads": "Camera Uploads", - "HeaderContinueWatching": "Continuar a ver", - "HeaderFavoriteAlbums": "Favorite Albums", - "HeaderFavoriteArtists": "Favorite Artists", - "HeaderFavoriteEpisodes": "Favorite Episodes", + "HeaderContinueWatching": "Continuar a Ver", + "HeaderFavoriteAlbums": "Álbuns Favoritos", + "HeaderFavoriteArtists": "Artistas Favoritos", + "HeaderFavoriteEpisodes": "Episódios Favoritos", "HeaderFavoriteShows": "Séries Favoritas", - "HeaderFavoriteSongs": "Favorite Songs", - "HeaderLiveTV": "Live TV", - "HeaderNextUp": "Next Up", - "HeaderRecordingGroups": "Recording Groups", + "HeaderFavoriteSongs": "Músicas Favoritas", + "HeaderLiveTV": "TV em Direto", + "HeaderNextUp": "A Seguir", + "HeaderRecordingGroups": "Grupos de Gravação", "HomeVideos": "Home videos", "Inherit": "Inherit", - "ItemAddedWithName": "{0} was added to the library", - "ItemRemovedWithName": "{0} was removed from the library", - "LabelIpAddressValue": "Ip address: {0}", - "LabelRunningTimeValue": "Running time: {0}", - "Latest": "Latest", - "MessageApplicationUpdated": "Jellyfin Server has been updated", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", - "MessageServerConfigurationUpdated": "Server configuration has been updated", - "MixedContent": "Mixed content", - "Movies": "Movies", - "Music": "Music", - "MusicVideos": "Music videos", - "NameInstallFailed": "{0} installation failed", - "NameSeasonNumber": "Season {0}", - "NameSeasonUnknown": "Season Unknown", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", - "NotificationOptionApplicationUpdateAvailable": "Application update available", - "NotificationOptionApplicationUpdateInstalled": "Application update installed", - "NotificationOptionAudioPlayback": "Audio playback started", - "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", + "ItemAddedWithName": "{0} foi adicionado à biblioteca", + "ItemRemovedWithName": "{0} foi removido da biblioteca", + "LabelIpAddressValue": "Endereço IP: {0}", + "LabelRunningTimeValue": "Duração: {0}", + "Latest": "Mais Recente", + "MessageApplicationUpdated": "O servidor Jellyfin foi atualizado", + "MessageApplicationUpdatedTo": "O servidor Jellyfin foi atualizado para a versão {0}", + "MessageNamedServerConfigurationUpdatedWithValue": "Configurações de servidor na secção {0} foram atualizadas", + "MessageServerConfigurationUpdated": "A configuração do servidor foi atualizada", + "MixedContent": "Conteúdo Misto", + "Movies": "Filmes", + "Music": "Música", + "MusicVideos": "Videoclips", + "NameInstallFailed": "{0} falha na instalação", + "NameSeasonNumber": "Temporada {0}", + "NameSeasonUnknown": "Temporada Desconhecida", + "NewVersionIsAvailable": "Está disponível para transferência uma nova versão do servidor Jellyfin.", + "NotificationOptionApplicationUpdateAvailable": "Atualização de aplicação disponível", + "NotificationOptionApplicationUpdateInstalled": "Atualização de aplicação instalada", + "NotificationOptionAudioPlayback": "Reprodução Iniciada", + "NotificationOptionAudioPlaybackStopped": "Reprodução Parada", "NotificationOptionCameraImageUploaded": "Camera image uploaded", - "NotificationOptionInstallationFailed": "Installation failure", - "NotificationOptionNewLibraryContent": "New content added", - "NotificationOptionPluginError": "Plugin failure", - "NotificationOptionPluginInstalled": "Plugin installed", - "NotificationOptionPluginUninstalled": "Plugin uninstalled", - "NotificationOptionPluginUpdateInstalled": "Plugin update installed", - "NotificationOptionServerRestartRequired": "Server restart required", - "NotificationOptionTaskFailed": "Scheduled task failure", - "NotificationOptionUserLockedOut": "User locked out", - "NotificationOptionVideoPlayback": "Video playback started", - "NotificationOptionVideoPlaybackStopped": "Video playback stopped", - "Photos": "Photos", - "Playlists": "Playlists", - "Plugin": "Plugin", - "PluginInstalledWithName": "{0} was installed", - "PluginUninstalledWithName": "{0} was uninstalled", - "PluginUpdatedWithName": "{0} was updated", + "NotificationOptionInstallationFailed": "Falha na instalação", + "NotificationOptionNewLibraryContent": "Novo conteúdo adicionado", + "NotificationOptionPluginError": "Falha na extensão", + "NotificationOptionPluginInstalled": "Extensão instalada", + "NotificationOptionPluginUninstalled": "Extensão desinstalada", + "NotificationOptionPluginUpdateInstalled": "Extensão atualizada", + "NotificationOptionServerRestartRequired": "Necessário reiniciar o servidor", + "NotificationOptionTaskFailed": "Falha em tarefa agendada", + "NotificationOptionUserLockedOut": "Utilizador bloqueado", + "NotificationOptionVideoPlayback": "Reprodução do vídeo iniciada", + "NotificationOptionVideoPlaybackStopped": "Reprodução do vídeo parada", + "Photos": "Fotografias", + "Playlists": "Listas de Reprodução", + "Plugin": "Extensão", + "PluginInstalledWithName": "{0} foi instalado", + "PluginUninstalledWithName": "{0} foi desinstalado", + "PluginUpdatedWithName": "{0} foi atualizado", "ProviderValue": "Provider: {0}", - "ScheduledTaskFailedWithName": "{0} failed", - "ScheduledTaskStartedWithName": "{0} started", - "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", - "Shows": "Shows", - "Songs": "Songs", - "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", + "ScheduledTaskFailedWithName": "{0} falhou", + "ScheduledTaskStartedWithName": "{0} iniciou", + "ServerNameNeedsToBeRestarted": "{0} necessita de ser reiniciado", + "Shows": "Séries", + "Songs": "Músicas", + "StartupEmbyServerIsLoading": "O servidor Jellyfin está a iniciar. Tente novamente mais tarde.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", - "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", - "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", - "Sync": "Sync", - "System": "System", - "TvShows": "TV Shows", - "User": "User", - "UserCreatedWithName": "User {0} has been created", - "UserDeletedWithName": "User {0} has been deleted", - "UserDownloadingItemWithValues": "{0} is downloading {1}", - "UserLockedOutWithName": "User {0} has been locked out", - "UserOfflineFromDevice": "{0} has disconnected from {1}", - "UserOnlineFromDevice": "{0} is online from {1}", - "UserPasswordChangedWithName": "Password has been changed for user {0}", - "UserPolicyUpdatedWithName": "User policy has been updated for {0}", - "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}", - "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}", - "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", - "ValueSpecialEpisodeName": "Especial - {0}", - "VersionNumber": "Version {0}" + "SubtitleDownloadFailureFromForItem": "Falha na transferência de legendas a partir de {0} para {1}", + "SubtitlesDownloadedForItem": "Transferidas legendas para {0}", + "Sync": "Sincronização", + "System": "Sistema", + "TvShows": "Programas TV", + "User": "Utilizador", + "UserCreatedWithName": "Utilizador {0} criado", + "UserDeletedWithName": "Utilizador {0} apagado", + "UserDownloadingItemWithValues": "{0} está a transferir {1}", + "UserLockedOutWithName": "Utilizador {0} bloqueado", + "UserOfflineFromDevice": "{0} desligou-se a partir de {1}", + "UserOnlineFromDevice": "{0} ligou-se a partir de {1}", + "UserPasswordChangedWithName": "Palavra-passe alterada para o utilizador {0}", + "UserPolicyUpdatedWithName": "Política de utilizador alterada para {0}", + "UserStartedPlayingItemWithValues": "{0} está a reproduzir {1} em {2}", + "UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}", + "ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia", + "ValueSpecialEpisodeName": "Especial - {0}", + "VersionNumber": "Versão {0}" } -- cgit v1.2.3 From 18bc6c69d561a1c25d44cef98bf6576f57cf9f12 Mon Sep 17 00:00:00 2001 From: Vladimir Jendrol Date: Mon, 8 Jul 2019 19:46:40 +0000 Subject: Translated using Weblate (Slovak) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sk/ --- .../Localization/Core/sk.json | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json index bc7e7184e..6eade7942 100644 --- a/Emby.Server.Implementations/Localization/Core/sk.json +++ b/Emby.Server.Implementations/Localization/Core/sk.json @@ -1,6 +1,6 @@ { "Albums": "Albumy", - "AppDeviceValues": "Aplikácia: {0}, Zariadenie: {1}", + "AppDeviceValues": "Apka: {0}, Zariadenie: {1}", "Application": "Aplikácia", "Artists": "Umelci", "AuthenticationSucceededWithUserName": "{0} úspešne overený", @@ -9,14 +9,14 @@ "Channels": "Kanály", "ChapterNameValue": "Kapitola {0}", "Collections": "Zbierky", - "DeviceOfflineWithName": "{0} je odpojený", + "DeviceOfflineWithName": "{0} sa odpojil", "DeviceOnlineWithName": "{0} je pripojený", "FailedLoginAttemptWithUserName": "Neúspešný pokus o prihlásenie z {0}", "Favorites": "Obľúbené", "Folders": "Priečinky", "Genres": "Žánre", "HeaderAlbumArtists": "Album Artists", - "HeaderCameraUploads": "Camera Uploads", + "HeaderCameraUploads": "Nahrané fotografie", "HeaderContinueWatching": "Pokračujte v pozeraní", "HeaderFavoriteAlbums": "Obľúbené albumy", "HeaderFavoriteArtists": "Obľúbení umelci", @@ -25,7 +25,7 @@ "HeaderFavoriteSongs": "Obľúbené pesničky", "HeaderLiveTV": "Živá TV", "HeaderNextUp": "Nasleduje", - "HeaderRecordingGroups": "Recording Groups", + "HeaderRecordingGroups": "Skupiny nahrávok", "HomeVideos": "Domáce videá", "Inherit": "Zdediť", "ItemAddedWithName": "{0} bol pridaný do knižnice", @@ -34,17 +34,17 @@ "LabelRunningTimeValue": "Dĺžka: {0}", "Latest": "Najnovšie", "MessageApplicationUpdated": "Jellyfin Server bol aktualizovaný", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", + "MessageApplicationUpdatedTo": "Jellyfin Server bol aktualizový na {0}", "MessageNamedServerConfigurationUpdatedWithValue": "Sekcia {0} konfigurácie servera bola aktualizovaná", - "MessageServerConfigurationUpdated": "Konfigurácia servera aktualizovaná", + "MessageServerConfigurationUpdated": "Konfigurácia servera bola aktualizovaná", "MixedContent": "Zmiešaný obsah", "Movies": "Filmy", "Music": "Hudba", "MusicVideos": "Hudobné videá", - "NameInstallFailed": "{0} installation failed", + "NameInstallFailed": "Inštalácia {0} zlyhala", "NameSeasonNumber": "Sezóna {0}", "NameSeasonUnknown": "Neznáma sezóna", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", + "NewVersionIsAvailable": "Nová verzia Jellyfin Server je dostupná na stiahnutie.", "NotificationOptionApplicationUpdateAvailable": "Je dostupná aktualizácia aplikácie", "NotificationOptionApplicationUpdateInstalled": "Aktualizácia aplikácie nainštalovaná", "NotificationOptionAudioPlayback": "Spustené prehrávanie audia", @@ -70,16 +70,16 @@ "ProviderValue": "Poskytovateľ: {0}", "ScheduledTaskFailedWithName": "{0} zlyhalo", "ScheduledTaskStartedWithName": "{0} started", - "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", + "ServerNameNeedsToBeRestarted": "{0} vyžaduje reštart", "Shows": "Series", "Songs": "Skladby", "StartupEmbyServerIsLoading": "Jellyfin Server sa spúšťa. Skúste to prosím o chvíľu znova.", "SubtitleDownloadFailureForItem": "Sťahovanie titulkov pre {0} zlyhalo", - "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", + "SubtitleDownloadFailureFromForItem": "Sťahovanie titulkov z {0} pre {1} zlyhalo", "SubtitlesDownloadedForItem": "Titulky pre {0} stiahnuté", - "Sync": "Sync", + "Sync": "Synchronizácia", "System": "Systém", - "TvShows": "TV Shows", + "TvShows": "TV seriály", "User": "Používateľ", "UserCreatedWithName": "Používateľ {0} bol vytvorený", "UserDeletedWithName": "Používateľ {0} bol vymazaný", @@ -91,7 +91,7 @@ "UserPolicyUpdatedWithName": "User policy has been updated for {0}", "UserStartedPlayingItemWithValues": "{0} spustil prehrávanie {1}", "UserStoppedPlayingItemWithValues": "{0} zastavil prehrávanie {1}", - "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", + "ValueHasBeenAddedToLibrary": "{0} bolo pridané do vašej knižnice médií", "ValueSpecialEpisodeName": "Špeciál - {0}", "VersionNumber": "Verzia {0}" } -- cgit v1.2.3 From 773af2eef907186b6552b845eb61820909154413 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 24 Jul 2019 23:46:58 +0200 Subject: Don't copy the complete response stream --- .../HttpClientManager/HttpClientManager.cs | 51 ++++++---------------- 1 file changed, 14 insertions(+), 37 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 3e23150d8..331b5e29d 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -284,47 +284,24 @@ namespace Emby.Server.Implementations.HttpClientManager options.CancellationToken.ThrowIfCancellationRequested(); - if (!options.BufferContent) - { - var response = await client.SendAsync(httpWebRequest, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false); - - await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); + var response = await client.SendAsync( + httpWebRequest, + options.BufferContent ? HttpCompletionOption.ResponseContentRead : HttpCompletionOption.ResponseHeadersRead, + options.CancellationToken).ConfigureAwait(false); - options.CancellationToken.ThrowIfCancellationRequested(); + await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); - var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - return new HttpResponseInfo(response.Headers, response.Content.Headers) - { - Content = stream, - StatusCode = response.StatusCode, - ContentType = response.Content.Headers.ContentType?.MediaType, - ContentLength = response.Content.Headers.ContentLength, - ResponseUrl = response.Content.Headers.ContentLocation?.ToString() - }; - } + options.CancellationToken.ThrowIfCancellationRequested(); - using (var response = await client.SendAsync(httpWebRequest, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false)) + var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + return new HttpResponseInfo(response.Headers, response.Content.Headers) { - await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); - - options.CancellationToken.ThrowIfCancellationRequested(); - - using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) - { - var memoryStream = new MemoryStream(); - await stream.CopyToAsync(memoryStream, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); - memoryStream.Position = 0; - - return new HttpResponseInfo(response.Headers, response.Content.Headers) - { - Content = memoryStream, - StatusCode = response.StatusCode, - ContentType = response.Content.Headers.ContentType?.MediaType, - ContentLength = memoryStream.Length, - ResponseUrl = response.Content.Headers.ContentLocation?.ToString() - }; - } - } + Content = stream, + StatusCode = response.StatusCode, + ContentType = response.Content.Headers.ContentType?.MediaType, + ContentLength = response.Content.Headers.ContentLength, + ResponseUrl = response.Content.Headers.ContentLocation?.ToString() + }; } public Task Post(HttpRequestOptions options) -- cgit v1.2.3 From ddd1a282ea6398ee26d74321338d0445d0a0c796 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 7 Jul 2019 21:03:26 +0200 Subject: Remove IpAddressInfo and IpEndPointInfo classes --- Emby.Dlna/Main/DlnaEntryPoint.cs | 3 +- Emby.Dlna/PlayTo/PlayToManager.cs | 4 +- Emby.Server.Implementations/ApplicationHost.cs | 53 +++-- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 9 +- .../TunerHosts/HdHomerun/HdHomerunManager.cs | 16 +- .../TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 17 +- Emby.Server.Implementations/Net/SocketFactory.cs | 14 +- Emby.Server.Implementations/Net/UdpSocket.cs | 30 +-- .../Networking/NetworkManager.cs | 232 +++++---------------- Emby.Server.Implementations/Udp/UdpServer.cs | 15 +- Jellyfin.Server/Program.cs | 2 +- MediaBrowser.Common/Net/INetworkManager.cs | 14 +- MediaBrowser.Controller/IServerApplicationHost.cs | 6 +- MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs | 4 +- MediaBrowser.Model/Net/ISocket.cs | 5 +- MediaBrowser.Model/Net/ISocketFactory.cs | 11 +- MediaBrowser.Model/Net/IpAddressInfo.cs | 38 ---- MediaBrowser.Model/Net/IpEndPointInfo.cs | 29 --- MediaBrowser.Model/Net/SocketReceiveResult.cs | 6 +- RSSDP/DeviceAvailableEventArgs.cs | 53 +++-- RSSDP/ISsdpCommunicationsServer.cs | 8 +- RSSDP/RequestReceivedEventArgs.cs | 12 +- RSSDP/ResponseReceivedEventArgs.cs | 12 +- RSSDP/SsdpCommunicationsServer.cs | 52 ++--- RSSDP/SsdpDevice.cs | 3 - RSSDP/SsdpDeviceLocator.cs | 17 +- RSSDP/SsdpDevicePublisher.cs | 32 ++- RSSDP/SsdpRootDevice.cs | 10 +- 28 files changed, 262 insertions(+), 445 deletions(-) delete mode 100644 MediaBrowser.Model/Net/IpAddressInfo.cs delete mode 100644 MediaBrowser.Model/Net/IpEndPointInfo.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 5fbe70ded..206a873e1 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -1,4 +1,5 @@ using System; +using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; using Emby.Dlna.PlayTo; @@ -247,7 +248,7 @@ namespace Emby.Dlna.Main foreach (var address in addresses) { - if (address.AddressFamily == IpAddressFamily.InterNetworkV6) + if (address.AddressFamily == AddressFamily.InterNetworkV6) { // Not support IPv6 right now continue; diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index 28e70d046..c0a441871 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Net; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Extensions; @@ -14,7 +15,6 @@ using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Events; using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; @@ -172,7 +172,7 @@ namespace Emby.Dlna.PlayTo _sessionManager.UpdateDeviceName(sessionInfo.Id, deviceName); string serverAddress; - if (info.LocalIpAddress == null || info.LocalIpAddress.Equals(IpAddressInfo.Any) || info.LocalIpAddress.Equals(IpAddressInfo.IPv6Any)) + if (info.LocalIpAddress == null || info.LocalIpAddress.Equals(IPAddress.Any) || info.LocalIpAddress.Equals(IPAddress.IPv6Any)) { serverAddress = await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false); } diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 120aade39..c6ba2326a 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Sockets; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; @@ -1546,14 +1547,22 @@ namespace Emby.Server.Implementations return null; } - public string GetLocalApiUrl(IpAddressInfo ipAddress) + public string GetLocalApiUrl(IPAddress ipAddress) { - if (ipAddress.AddressFamily == IpAddressFamily.InterNetworkV6) + if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) { - return GetLocalApiUrl("[" + ipAddress.Address + "]"); + // Remove the scope id from IPv6 addresses + var str = ipAddress.ToString(); + var index = str.IndexOf('%'); + if (index != -1) + { + str = str.Substring(0, index); + } + + return GetLocalApiUrl("[" + str + "]"); } - return GetLocalApiUrl(ipAddress.Address); + return GetLocalApiUrl(ipAddress.ToString()); } public string GetLocalApiUrl(string host) @@ -1564,19 +1573,28 @@ namespace Emby.Server.Implementations host, HttpsPort.ToString(CultureInfo.InvariantCulture)); } + return string.Format("http://{0}:{1}", host, HttpPort.ToString(CultureInfo.InvariantCulture)); } - public string GetWanApiUrl(IpAddressInfo ipAddress) + public string GetWanApiUrl(IPAddress ipAddress) { - if (ipAddress.AddressFamily == IpAddressFamily.InterNetworkV6) + if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) { - return GetWanApiUrl("[" + ipAddress.Address + "]"); + // Remove the scope id from IPv6 addresses + var str = ipAddress.ToString(); + var index = str.IndexOf('%'); + if (index != -1) + { + str = str.Substring(0, index); + } + + return GetWanApiUrl("[" + str + "]"); } - return GetWanApiUrl(ipAddress.Address); + return GetWanApiUrl(ipAddress.ToString()); } public string GetWanApiUrl(string host) @@ -1587,17 +1605,18 @@ namespace Emby.Server.Implementations host, ServerConfigurationManager.Configuration.PublicHttpsPort.ToString(CultureInfo.InvariantCulture)); } + return string.Format("http://{0}:{1}", host, ServerConfigurationManager.Configuration.PublicPort.ToString(CultureInfo.InvariantCulture)); } - public Task> GetLocalIpAddresses(CancellationToken cancellationToken) + public Task> GetLocalIpAddresses(CancellationToken cancellationToken) { return GetLocalIpAddressesInternal(true, 0, cancellationToken); } - private async Task> GetLocalIpAddressesInternal(bool allowLoopback, int limit, CancellationToken cancellationToken) + private async Task> GetLocalIpAddressesInternal(bool allowLoopback, int limit, CancellationToken cancellationToken) { var addresses = ServerConfigurationManager .Configuration @@ -1611,13 +1630,13 @@ namespace Emby.Server.Implementations addresses.AddRange(NetworkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces)); } - var resultList = new List(); + var resultList = new List(); foreach (var address in addresses) { if (!allowLoopback) { - if (address.Equals(IpAddressInfo.Loopback) || address.Equals(IpAddressInfo.IPv6Loopback)) + if (address.Equals(IPAddress.Loopback) || address.Equals(IPAddress.IPv6Loopback)) { continue; } @@ -1638,7 +1657,7 @@ namespace Emby.Server.Implementations return resultList; } - private IpAddressInfo NormalizeConfiguredLocalAddress(string address) + private IPAddress NormalizeConfiguredLocalAddress(string address) { var index = address.Trim('/').IndexOf('/'); @@ -1647,7 +1666,7 @@ namespace Emby.Server.Implementations address = address.Substring(index + 1); } - if (NetworkManager.TryParseIpAddress(address.Trim('/'), out IpAddressInfo result)) + if (IPAddress.TryParse(address.Trim('/'), out IPAddress result)) { return result; } @@ -1657,10 +1676,10 @@ namespace Emby.Server.Implementations private readonly ConcurrentDictionary _validAddressResults = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - private async Task IsIpAddressValidAsync(IpAddressInfo address, CancellationToken cancellationToken) + private async Task IsIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken) { - if (address.Equals(IpAddressInfo.Loopback) || - address.Equals(IpAddressInfo.IPv6Loopback)) + if (address.Equals(IPAddress.Loopback) || + address.Equals(IPAddress.IPv6Loopback)) { return true; } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 761275f8f..ed524cae3 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; @@ -11,7 +12,6 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -20,7 +20,6 @@ using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun @@ -259,7 +258,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun using (var manager = new HdHomerunManager(_socketFactory, Logger)) { // Legacy HdHomeruns are IPv4 only - var ipInfo = _networkManager.ParseIpAddress(uri.Host); + var ipInfo = IPAddress.Parse(uri.Host); for (int i = 0; i < model.TunerCount; ++i) { @@ -675,13 +674,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun // Need a way to set the Receive timeout on the socket otherwise this might never timeout? try { - await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IpEndPointInfo(new IpAddressInfo("255.255.255.255", IpAddressFamily.InterNetwork), 65001), cancellationToken); + await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken); var receiveBuffer = new byte[8192]; while (!cancellationToken.IsCancellationRequested) { var response = await udpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false); - var deviceIp = response.RemoteEndPoint.IpAddress.Address; + var deviceIp = response.RemoteEndPoint.Address.ToString(); // check to make sure we have enough bytes received to be a valid message and make sure the 2nd byte is the discover reply byte if (response.ReceivedBytes > 13 && response.Buffer[1] == 3) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index 2205c0ecc..6e79441da 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -89,7 +89,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private uint? _lockkey = null; private int _activeTuner = -1; private readonly ISocketFactory _socketFactory; - private IpAddressInfo _remoteIp; + private IPAddress _remoteIp; private ILogger _logger; private ISocket _currentTcpSocket; @@ -114,7 +114,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } } - public async Task CheckTunerAvailability(IpAddressInfo remoteIp, int tuner, CancellationToken cancellationToken) + public async Task CheckTunerAvailability(IPAddress remoteIp, int tuner, CancellationToken cancellationToken) { using (var socket = _socketFactory.CreateTcpSocket(remoteIp, HdHomeRunPort)) { @@ -122,9 +122,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } } - private static async Task CheckTunerAvailability(ISocket socket, IpAddressInfo remoteIp, int tuner, CancellationToken cancellationToken) + private static async Task CheckTunerAvailability(ISocket socket, IPAddress remoteIp, int tuner, CancellationToken cancellationToken) { - var ipEndPoint = new IpEndPointInfo(remoteIp, HdHomeRunPort); + var ipEndPoint = new IPEndPoint(remoteIp, HdHomeRunPort); var lockkeyMsg = CreateGetMessage(tuner, "lockkey"); await socket.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken); @@ -137,7 +137,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase); } - public async Task StartStreaming(IpAddressInfo remoteIp, IPAddress localIp, int localPort, IHdHomerunChannelCommands commands, int numTuners, CancellationToken cancellationToken) + public async Task StartStreaming(IPAddress remoteIp, IPAddress localIp, int localPort, IHdHomerunChannelCommands commands, int numTuners, CancellationToken cancellationToken) { _remoteIp = remoteIp; @@ -154,7 +154,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var lockKeyValue = _lockkey.Value; - var ipEndPoint = new IpEndPointInfo(_remoteIp, HdHomeRunPort); + var ipEndPoint = new IPEndPoint(_remoteIp, HdHomeRunPort); for (int i = 0; i < numTuners; ++i) { @@ -217,7 +217,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun foreach (Tuple command in commandList) { var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey); - await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false); + await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, new IPEndPoint(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false); var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out string returnVal)) @@ -242,7 +242,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { _logger.LogInformation("HdHomerunManager.ReleaseLockkey {0}", lockKeyValue); - var ipEndPoint = new IpEndPointInfo(_remoteIp, HdHomeRunPort); + var ipEndPoint = new IPEndPoint(_remoteIp, HdHomeRunPort); var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue); await tcpClient.SendToAsync(releaseTarget, 0, releaseTarget.Length, ipEndPoint, CancellationToken.None).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 7f426ea31..ec708cf20 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -25,7 +25,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private readonly int _numTuners; private readonly INetworkManager _networkManager; - public HdHomerunUdpStream(MediaSourceInfo mediaSource, TunerHostInfo tunerHostInfo, string originalStreamId, IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, MediaBrowser.Model.Net.ISocketFactory socketFactory, INetworkManager networkManager) + public HdHomerunUdpStream( + MediaSourceInfo mediaSource, + TunerHostInfo tunerHostInfo, + string originalStreamId, + IHdHomerunChannelCommands channelCommands, + int numTuners, + IFileSystem fileSystem, + IHttpClient httpClient, + ILogger logger, + IServerApplicationPaths appPaths, + IServerApplicationHost appHost, + MediaBrowser.Model.Net.ISocketFactory socketFactory, + INetworkManager networkManager) : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths) { _appHost = appHost; @@ -58,7 +70,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun Logger.LogInformation("Opening HDHR UDP Live stream from {host}", uri.Host); var remoteAddress = IPAddress.Parse(uri.Host); - var embyRemoteAddress = _networkManager.ParseIpAddress(uri.Host); IPAddress localAddress = null; using (var tcpSocket = CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) { @@ -81,7 +92,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun try { // send url to start streaming - await hdHomerunManager.StartStreaming(embyRemoteAddress, localAddress, localPort, _channelCommands, _numTuners, openCancellationToken).ConfigureAwait(false); + await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelCommands, _numTuners, openCancellationToken).ConfigureAwait(false); } catch (Exception ex) { diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs index 492f48abe..42ffa4e22 100644 --- a/Emby.Server.Implementations/Net/SocketFactory.cs +++ b/Emby.Server.Implementations/Net/SocketFactory.cs @@ -16,14 +16,14 @@ namespace Emby.Server.Implementations.Net // but that wasn't really the point so kept to YAGNI principal for now, even if the // interfaces are a bit ugly, specific and make assumptions. - public ISocket CreateTcpSocket(IpAddressInfo remoteAddress, int remotePort) + public ISocket CreateTcpSocket(IPAddress remoteAddress, int remotePort) { if (remotePort < 0) { throw new ArgumentException("remotePort cannot be less than zero.", nameof(remotePort)); } - var addressFamily = remoteAddress.AddressFamily == IpAddressFamily.InterNetwork + var addressFamily = remoteAddress.AddressFamily == AddressFamily.InterNetwork ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6; @@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.Net try { - return new UdpSocket(retVal, new IpEndPointInfo(remoteAddress, remotePort)); + return new UdpSocket(retVal, new IPEndPoint(remoteAddress, remotePort)); } catch { @@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Net /// Creates a new UDP acceptSocket that is a member of the SSDP multicast local admin group and binds it to the specified local port. /// /// An implementation of the interface used by RSSDP components to perform acceptSocket operations. - public ISocket CreateSsdpUdpSocket(IpAddressInfo localIpAddress, int localPort) + public ISocket CreateSsdpUdpSocket(IPAddress localIpAddress, int localPort) { if (localPort < 0) { @@ -115,10 +115,8 @@ namespace Emby.Server.Implementations.Net retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4); - var localIp = NetworkManager.ToIPAddress(localIpAddress); - - retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), localIp)); - return new UdpSocket(retVal, localPort, localIp); + retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), localIpAddress)); + return new UdpSocket(retVal, localPort, localIpAddress); } catch { diff --git a/Emby.Server.Implementations/Net/UdpSocket.cs b/Emby.Server.Implementations/Net/UdpSocket.cs index 6c55085c8..2908ee9af 100644 --- a/Emby.Server.Implementations/Net/UdpSocket.cs +++ b/Emby.Server.Implementations/Net/UdpSocket.cs @@ -19,7 +19,7 @@ namespace Emby.Server.Implementations.Net public Socket Socket => _socket; - public IpAddressInfo LocalIPAddress { get; } + public IPAddress LocalIPAddress { get; } private readonly SocketAsyncEventArgs _receiveSocketAsyncEventArgs = new SocketAsyncEventArgs() { @@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.Net _socket = socket; _localPort = localPort; - LocalIPAddress = NetworkManager.ToIpAddressInfo(ip); + LocalIPAddress = ip; _socket.Bind(new IPEndPoint(ip, _localPort)); @@ -71,7 +71,7 @@ namespace Emby.Server.Implementations.Net { Buffer = e.Buffer, ReceivedBytes = e.BytesTransferred, - RemoteEndPoint = ToIpEndPointInfo(e.RemoteEndPoint as IPEndPoint), + RemoteEndPoint = e.RemoteEndPoint as IPEndPoint, LocalIPAddress = LocalIPAddress }); } @@ -100,12 +100,12 @@ namespace Emby.Server.Implementations.Net } } - public UdpSocket(Socket socket, IpEndPointInfo endPoint) + public UdpSocket(Socket socket, IPEndPoint endPoint) { if (socket == null) throw new ArgumentNullException(nameof(socket)); _socket = socket; - _socket.Connect(NetworkManager.ToIPEndPoint(endPoint)); + _socket.Connect(endPoint); InitReceiveSocketAsyncEventArgs(); } @@ -140,7 +140,7 @@ namespace Emby.Server.Implementations.Net return new SocketReceiveResult { ReceivedBytes = receivedBytes, - RemoteEndPoint = ToIpEndPointInfo((IPEndPoint)remoteEndPoint), + RemoteEndPoint = (IPEndPoint)remoteEndPoint, Buffer = buffer, LocalIPAddress = LocalIPAddress }; @@ -191,7 +191,7 @@ namespace Emby.Server.Implementations.Net return ReceiveAsync(buffer, 0, buffer.Length, cancellationToken); } - public Task SendToAsync(byte[] buffer, int offset, int size, IpEndPointInfo endPoint, CancellationToken cancellationToken) + public Task SendToAsync(byte[] buffer, int offset, int size, IPEndPoint endPoint, CancellationToken cancellationToken) { ThrowIfDisposed(); @@ -227,13 +227,11 @@ namespace Emby.Server.Implementations.Net return taskCompletion.Task; } - public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, IpEndPointInfo endPoint, AsyncCallback callback, object state) + public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, IPEndPoint endPoint, AsyncCallback callback, object state) { ThrowIfDisposed(); - var ipEndPoint = NetworkManager.ToIPEndPoint(endPoint); - - return _socket.BeginSendTo(buffer, offset, size, SocketFlags.None, ipEndPoint, callback, state); + return _socket.BeginSendTo(buffer, offset, size, SocketFlags.None, endPoint, callback, state); } public int EndSendTo(IAsyncResult result) @@ -268,15 +266,5 @@ namespace Emby.Server.Implementations.Net _disposed = true; } - - private static IpEndPointInfo ToIpEndPointInfo(IPEndPoint endpoint) - { - if (endpoint == null) - { - return null; - } - - return NetworkManager.ToIpEndPointInfo(endpoint); - } } } diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index c102f9eb5..3cacacf07 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -9,55 +9,38 @@ using System.Threading.Tasks; using MediaBrowser.Common.Net; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; -using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; -using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace Emby.Server.Implementations.Networking { public class NetworkManager : INetworkManager { - protected ILogger Logger { get; private set; } + private readonly ILogger _logger; - public event EventHandler NetworkChanged; - public Func LocalSubnetsFn { get; set; } + private IPAddress[] _localIpAddresses; + private readonly object _localIpAddressSyncLock = new object(); - public NetworkManager(ILoggerFactory loggerFactory) + public NetworkManager(ILogger logger) { - Logger = loggerFactory.CreateLogger(nameof(NetworkManager)); - - // In FreeBSD these events cause a crash - if (OperatingSystem.Id != OperatingSystemId.BSD) - { - try - { - NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged; - } - catch (Exception ex) - { - Logger.LogError(ex, "Error binding to NetworkAddressChanged event"); - } + _logger = logger; - try - { - NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged; - } - catch (Exception ex) - { - Logger.LogError(ex, "Error binding to NetworkChange_NetworkAvailabilityChanged event"); - } - } + NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged; + NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged; } - private void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) + public Func LocalSubnetsFn { get; set; } + + public event EventHandler NetworkChanged; + + private void OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) { - Logger.LogDebug("NetworkAvailabilityChanged"); + _logger.LogDebug("NetworkAvailabilityChanged"); OnNetworkChanged(); } - private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e) + private void OnNetworkAddressChanged(object sender, EventArgs e) { - Logger.LogDebug("NetworkAddressChanged"); + _logger.LogDebug("NetworkAddressChanged"); OnNetworkChanged(); } @@ -68,39 +51,35 @@ namespace Emby.Server.Implementations.Networking _localIpAddresses = null; _macAddresses = null; } + if (NetworkChanged != null) { NetworkChanged(this, EventArgs.Empty); } } - private IpAddressInfo[] _localIpAddresses; - private readonly object _localIpAddressSyncLock = new object(); - - public IpAddressInfo[] GetLocalIpAddresses(bool ignoreVirtualInterface = true) + public IPAddress[] GetLocalIpAddresses(bool ignoreVirtualInterface = true) { lock (_localIpAddressSyncLock) { if (_localIpAddresses == null) { - var addresses = GetLocalIpAddressesInternal(ignoreVirtualInterface).Result.Select(ToIpAddressInfo).ToArray(); + var addresses = GetLocalIpAddressesInternal(ignoreVirtualInterface).ToArray(); _localIpAddresses = addresses; - - return addresses; } + return _localIpAddresses; } } - private async Task> GetLocalIpAddressesInternal(bool ignoreVirtualInterface) + private List GetLocalIpAddressesInternal(bool ignoreVirtualInterface) { - var list = GetIPsDefault(ignoreVirtualInterface) - .ToList(); + var list = GetIPsDefault(ignoreVirtualInterface).ToList(); if (list.Count == 0) { - list.AddRange(await GetLocalIpAddressesFallback().ConfigureAwait(false)); + list = GetLocalIpAddressesFallback().GetAwaiter().GetResult().ToList(); } var listClone = list.ToList(); @@ -351,13 +330,13 @@ namespace Emby.Server.Implementations.Networking try { var host = uri.DnsSafeHost; - Logger.LogDebug("Resolving host {0}", host); + _logger.LogDebug("Resolving host {0}", host); address = GetIpAddresses(host).Result.FirstOrDefault(); if (address != null) { - Logger.LogDebug("{0} resolved to {1}", host, address); + _logger.LogDebug("{0} resolved to {1}", host, address); return IsInLocalNetworkInternal(address.ToString(), false); } @@ -368,7 +347,7 @@ namespace Emby.Server.Implementations.Networking } catch (Exception ex) { - Logger.LogError(ex, "Error resolving hostname"); + _logger.LogError(ex, "Error resolving hostname"); } } } @@ -381,56 +360,41 @@ namespace Emby.Server.Implementations.Networking return Dns.GetHostAddressesAsync(hostName); } - private List GetIPsDefault(bool ignoreVirtualInterface) + private IEnumerable GetIPsDefault(bool ignoreVirtualInterface) { - NetworkInterface[] interfaces; + IEnumerable interfaces; try { - var validStatuses = new[] { OperationalStatus.Up, OperationalStatus.Unknown }; - interfaces = NetworkInterface.GetAllNetworkInterfaces() - .Where(i => validStatuses.Contains(i.OperationalStatus)) - .ToArray(); + .Where(x => x.OperationalStatus == OperationalStatus.Up + || x.OperationalStatus == OperationalStatus.Unknown); } - catch (Exception ex) + catch (NetworkInformationException ex) { - Logger.LogError(ex, "Error in GetAllNetworkInterfaces"); - return new List(); + _logger.LogError(ex, "Error in GetAllNetworkInterfaces"); + return Enumerable.Empty(); } return interfaces.SelectMany(network => { + var ipProperties = network.GetIPProperties(); - try - { - // suppress logging because it might be causing nas device wake up - //logger.LogDebug("Querying interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus); - - var ipProperties = network.GetIPProperties(); - - // Try to exclude virtual adapters - // http://stackoverflow.com/questions/8089685/c-sharp-finding-my-machines-local-ip-address-and-not-the-vms - var addr = ipProperties.GatewayAddresses.FirstOrDefault(); - if (addr == null || ignoreVirtualInterface && string.Equals(addr.Address.ToString(), "0.0.0.0", StringComparison.OrdinalIgnoreCase)) - { - return new List(); - } - - return ipProperties.UnicastAddresses - .Select(i => i.Address) - .Where(i => i.AddressFamily == AddressFamily.InterNetwork || i.AddressFamily == AddressFamily.InterNetworkV6) - .ToList(); - } - catch (Exception ex) + // Try to exclude virtual adapters + // http://stackoverflow.com/questions/8089685/c-sharp-finding-my-machines-local-ip-address-and-not-the-vms + var addr = ipProperties.GatewayAddresses.FirstOrDefault(); + if (addr == null + || (ignoreVirtualInterface + && (addr.Address.Equals(IPAddress.Any) || addr.Address.Equals(IPAddress.IPv6Any)))) { - Logger.LogError(ex, "Error querying network interface"); - return new List(); + return Enumerable.Empty(); } + return ipProperties.UnicastAddresses + .Select(i => i.Address) + .Where(i => i.AddressFamily == AddressFamily.InterNetwork || i.AddressFamily == AddressFamily.InterNetworkV6); }).GroupBy(i => i.ToString()) - .Select(x => x.First()) - .ToList(); + .Select(x => x.First()); } private static async Task> GetLocalIpAddressesFallback() @@ -612,32 +576,10 @@ namespace Emby.Server.Implementations.Networking return hosts[0]; } - public IpAddressInfo ParseIpAddress(string ipAddress) - { - if (TryParseIpAddress(ipAddress, out var info)) - { - return info; - } - - throw new ArgumentException("Invalid ip address: " + ipAddress); - } - - public bool TryParseIpAddress(string ipAddress, out IpAddressInfo ipAddressInfo) + public bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask) { - if (IPAddress.TryParse(ipAddress, out var address)) - { - ipAddressInfo = ToIpAddressInfo(address); - return true; - } - - ipAddressInfo = null; - return false; - } - - public bool IsInSameSubnet(IpAddressInfo address1, IpAddressInfo address2, IpAddressInfo subnetMask) - { - IPAddress network1 = GetNetworkAddress(ToIPAddress(address1), ToIPAddress(subnetMask)); - IPAddress network2 = GetNetworkAddress(ToIPAddress(address2), ToIPAddress(subnetMask)); + IPAddress network1 = GetNetworkAddress(address1, subnetMask); + IPAddress network2 = GetNetworkAddress(address2, subnetMask); return network1.Equals(network2); } @@ -656,13 +598,13 @@ namespace Emby.Server.Implementations.Networking { broadcastAddress[i] = (byte)(ipAdressBytes[i] & (subnetMaskBytes[i])); } + return new IPAddress(broadcastAddress); } - public IpAddressInfo GetLocalIpSubnetMask(IpAddressInfo address) + public IPAddress GetLocalIpSubnetMask(IPAddress address) { NetworkInterface[] interfaces; - IPAddress ipaddress = ToIPAddress(address); try { @@ -674,7 +616,7 @@ namespace Emby.Server.Implementations.Networking } catch (Exception ex) { - Logger.LogError(ex, "Error in GetAllNetworkInterfaces"); + _logger.LogError(ex, "Error in GetAllNetworkInterfaces"); return null; } @@ -684,83 +626,15 @@ namespace Emby.Server.Implementations.Networking { foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses) { - if (ip.Address.Equals(ipaddress) && ip.IPv4Mask != null) + if (ip.Address.Equals(address) && ip.IPv4Mask != null) { - return ToIpAddressInfo(ip.IPv4Mask); + return ip.IPv4Mask; } } } } - return null; - } - - public static IpEndPointInfo ToIpEndPointInfo(IPEndPoint endpoint) - { - if (endpoint == null) - { - return null; - } - - return new IpEndPointInfo(ToIpAddressInfo(endpoint.Address), endpoint.Port); - } - - public static IPEndPoint ToIPEndPoint(IpEndPointInfo endpoint) - { - if (endpoint == null) - { - return null; - } - - return new IPEndPoint(ToIPAddress(endpoint.IpAddress), endpoint.Port); - } - public static IPAddress ToIPAddress(IpAddressInfo address) - { - if (address.Equals(IpAddressInfo.Any)) - { - return IPAddress.Any; - } - if (address.Equals(IpAddressInfo.IPv6Any)) - { - return IPAddress.IPv6Any; - } - if (address.Equals(IpAddressInfo.Loopback)) - { - return IPAddress.Loopback; - } - if (address.Equals(IpAddressInfo.IPv6Loopback)) - { - return IPAddress.IPv6Loopback; - } - - return IPAddress.Parse(address.Address); - } - - public static IpAddressInfo ToIpAddressInfo(IPAddress address) - { - if (address.Equals(IPAddress.Any)) - { - return IpAddressInfo.Any; - } - if (address.Equals(IPAddress.IPv6Any)) - { - return IpAddressInfo.IPv6Any; - } - if (address.Equals(IPAddress.Loopback)) - { - return IpAddressInfo.Loopback; - } - if (address.Equals(IPAddress.IPv6Loopback)) - { - return IpAddressInfo.IPv6Loopback; - } - return new IpAddressInfo(address.ToString(), address.AddressFamily == AddressFamily.InterNetworkV6 ? IpAddressFamily.InterNetworkV6 : IpAddressFamily.InterNetwork); - } - - public async Task GetHostAddressesAsync(string host) - { - var addresses = await Dns.GetHostAddressesAsync(host).ConfigureAwait(false); - return addresses.Select(ToIpAddressInfo).ToArray(); + return null; } /// diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs index e7cda2993..185a282ac 100644 --- a/Emby.Server.Implementations/Udp/UdpServer.cs +++ b/Emby.Server.Implementations/Udp/UdpServer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -25,7 +26,7 @@ namespace Emby.Server.Implementations.Udp private bool _isDisposed; - private readonly List>> _responders = new List>>(); + private readonly List>> _responders = new List>>(); private readonly IServerApplicationHost _appHost; private readonly IJsonSerializer _json; @@ -43,9 +44,9 @@ namespace Emby.Server.Implementations.Udp AddMessageResponder("who is JellyfinServer?", true, RespondToV2Message); } - private void AddMessageResponder(string message, bool isSubstring, Func responder) + private void AddMessageResponder(string message, bool isSubstring, Func responder) { - _responders.Add(new Tuple>(message, isSubstring, responder)); + _responders.Add(new Tuple>(message, isSubstring, responder)); } /// @@ -83,7 +84,7 @@ namespace Emby.Server.Implementations.Udp } } - private Tuple>> GetResponder(byte[] buffer, int bytesReceived, Encoding encoding) + private Tuple>> GetResponder(byte[] buffer, int bytesReceived, Encoding encoding) { var text = encoding.GetString(buffer, 0, bytesReceived); var responder = _responders.FirstOrDefault(i => @@ -99,10 +100,10 @@ namespace Emby.Server.Implementations.Udp { return null; } - return new Tuple>>(text, responder); + return new Tuple>>(text, responder); } - private async Task RespondToV2Message(string messageText, IpEndPointInfo endpoint, Encoding encoding, CancellationToken cancellationToken) + private async Task RespondToV2Message(string messageText, IPEndPoint endpoint, Encoding encoding, CancellationToken cancellationToken) { var parts = messageText.Split('|'); @@ -254,7 +255,7 @@ namespace Emby.Server.Implementations.Udp } } - public async Task SendAsync(byte[] bytes, IpEndPointInfo remoteEndPoint, CancellationToken cancellationToken) + public async Task SendAsync(byte[] bytes, IPEndPoint remoteEndPoint, CancellationToken cancellationToken) { if (_isDisposed) { diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 94308a98e..08c0983be 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -143,7 +143,7 @@ namespace Jellyfin.Server options, new ManagedFileSystem(_loggerFactory.CreateLogger(), appPaths), new NullImageEncoder(), - new NetworkManager(_loggerFactory), + new NetworkManager(_loggerFactory.CreateLogger()), appConfig)) { await appHost.InitAsync(new ServiceCollection()).ConfigureAwait(false); diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index 34c6f5866..61f2bc2f9 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Net; using System.Threading.Tasks; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; @@ -53,17 +54,12 @@ namespace MediaBrowser.Common.Net /// true if [is in local network] [the specified endpoint]; otherwise, false. bool IsInLocalNetwork(string endpoint); - IpAddressInfo[] GetLocalIpAddresses(bool ignoreVirtualInterface); - - IpAddressInfo ParseIpAddress(string ipAddress); - - bool TryParseIpAddress(string ipAddress, out IpAddressInfo ipAddressInfo); - - Task GetHostAddressesAsync(string host); + IPAddress[] GetLocalIpAddresses(bool ignoreVirtualInterface); bool IsAddressInSubnets(string addressString, string[] subnets); - bool IsInSameSubnet(IpAddressInfo address1, IpAddressInfo address2, IpAddressInfo subnetMask); - IpAddressInfo GetLocalIpSubnetMask(IpAddressInfo address); + bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask); + + IPAddress GetLocalIpSubnetMask(IPAddress address); } } diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 81b9ff0a5..3f8cc0b83 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; +using System.Net; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; -using MediaBrowser.Model.Net; using MediaBrowser.Model.System; namespace MediaBrowser.Controller @@ -59,7 +59,7 @@ namespace MediaBrowser.Controller /// Gets the local ip address. /// /// The local ip address. - Task> GetLocalIpAddresses(CancellationToken cancellationToken); + Task> GetLocalIpAddresses(CancellationToken cancellationToken); /// /// Gets the local API URL. @@ -77,7 +77,7 @@ namespace MediaBrowser.Controller /// /// Gets the local API URL. /// - string GetLocalApiUrl(IpAddressInfo address); + string GetLocalApiUrl(IPAddress address); void LaunchUrl(string url); diff --git a/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs index 4edbb503b..c443a8ad1 100644 --- a/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs +++ b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using MediaBrowser.Model.Net; +using System.Net; namespace MediaBrowser.Model.Dlna { @@ -8,7 +8,7 @@ namespace MediaBrowser.Model.Dlna { public Uri Location { get; set; } public Dictionary Headers { get; set; } - public IpAddressInfo LocalIpAddress { get; set; } + public IPAddress LocalIpAddress { get; set; } public int LocalPort { get; set; } } } diff --git a/MediaBrowser.Model/Net/ISocket.cs b/MediaBrowser.Model/Net/ISocket.cs index 992ccb49b..f80de5524 100644 --- a/MediaBrowser.Model/Net/ISocket.cs +++ b/MediaBrowser.Model/Net/ISocket.cs @@ -1,4 +1,5 @@ using System; +using System.Net; using System.Threading; using System.Threading.Tasks; @@ -9,7 +10,7 @@ namespace MediaBrowser.Model.Net /// public interface ISocket : IDisposable { - IpAddressInfo LocalIPAddress { get; } + IPAddress LocalIPAddress { get; } Task ReceiveAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken); @@ -21,6 +22,6 @@ namespace MediaBrowser.Model.Net /// /// Sends a UDP message to a particular end point (uni or multicast). /// - Task SendToAsync(byte[] buffer, int offset, int bytes, IpEndPointInfo endPoint, CancellationToken cancellationToken); + Task SendToAsync(byte[] buffer, int offset, int bytes, IPEndPoint endPoint, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs index 69fe134bc..e58f4cc14 100644 --- a/MediaBrowser.Model/Net/ISocketFactory.cs +++ b/MediaBrowser.Model/Net/ISocketFactory.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Net; namespace MediaBrowser.Model.Net { @@ -8,7 +9,7 @@ namespace MediaBrowser.Model.Net public interface ISocketFactory { /// - /// Createa a new unicast socket using the specified local port number. + /// Creates a new unicast socket using the specified local port number. /// /// The local port to bind to. /// A implementation. @@ -16,15 +17,15 @@ namespace MediaBrowser.Model.Net ISocket CreateUdpBroadcastSocket(int localPort); - ISocket CreateTcpSocket(IpAddressInfo remoteAddress, int remotePort); + ISocket CreateTcpSocket(IPAddress remoteAddress, int remotePort); /// - /// Createa a new unicast socket using the specified local port number. + /// Creates a new unicast socket using the specified local port number. /// - ISocket CreateSsdpUdpSocket(IpAddressInfo localIp, int localPort); + ISocket CreateSsdpUdpSocket(IPAddress localIp, int localPort); /// - /// Createa a new multicast socket using the specified multicast IP address, multicast time to live and local port. + /// Creates a new multicast socket using the specified multicast IP address, multicast time to live and local port. /// /// The multicast IP address to bind to. /// The multicast time to live value. Actually a maximum number of network hops for UDP packets. diff --git a/MediaBrowser.Model/Net/IpAddressInfo.cs b/MediaBrowser.Model/Net/IpAddressInfo.cs deleted file mode 100644 index 87fa55bca..000000000 --- a/MediaBrowser.Model/Net/IpAddressInfo.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; - -namespace MediaBrowser.Model.Net -{ - public class IpAddressInfo - { - public static IpAddressInfo Any = new IpAddressInfo("0.0.0.0", IpAddressFamily.InterNetwork); - public static IpAddressInfo IPv6Any = new IpAddressInfo("00000000000000000000", IpAddressFamily.InterNetworkV6); - public static IpAddressInfo Loopback = new IpAddressInfo("127.0.0.1", IpAddressFamily.InterNetwork); - public static IpAddressInfo IPv6Loopback = new IpAddressInfo("::1", IpAddressFamily.InterNetworkV6); - - public string Address { get; set; } - public IpAddressInfo SubnetMask { get; set; } - public IpAddressFamily AddressFamily { get; set; } - - public IpAddressInfo(string address, IpAddressFamily addressFamily) - { - Address = address; - AddressFamily = addressFamily; - } - - public bool Equals(IpAddressInfo address) - { - return string.Equals(address.Address, Address, StringComparison.OrdinalIgnoreCase); - } - - public override string ToString() - { - return Address; - } - } - - public enum IpAddressFamily - { - InterNetwork, - InterNetworkV6 - } -} diff --git a/MediaBrowser.Model/Net/IpEndPointInfo.cs b/MediaBrowser.Model/Net/IpEndPointInfo.cs deleted file mode 100644 index f8c125144..000000000 --- a/MediaBrowser.Model/Net/IpEndPointInfo.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Globalization; - -namespace MediaBrowser.Model.Net -{ - public class IpEndPointInfo - { - public IpAddressInfo IpAddress { get; set; } - - public int Port { get; set; } - - public IpEndPointInfo() - { - - } - - public IpEndPointInfo(IpAddressInfo address, int port) - { - IpAddress = address; - Port = port; - } - - public override string ToString() - { - var ipAddresString = IpAddress == null ? string.Empty : IpAddress.ToString(); - - return ipAddresString + ":" + Port.ToString(CultureInfo.InvariantCulture); - } - } -} diff --git a/MediaBrowser.Model/Net/SocketReceiveResult.cs b/MediaBrowser.Model/Net/SocketReceiveResult.cs index 8c394f7c7..cd7a2e55f 100644 --- a/MediaBrowser.Model/Net/SocketReceiveResult.cs +++ b/MediaBrowser.Model/Net/SocketReceiveResult.cs @@ -1,3 +1,5 @@ +using System.Net; + namespace MediaBrowser.Model.Net { /// @@ -18,7 +20,7 @@ namespace MediaBrowser.Model.Net /// /// The the data was received from. /// - public IpEndPointInfo RemoteEndPoint { get; set; } - public IpAddressInfo LocalIPAddress { get; set; } + public IPEndPoint RemoteEndPoint { get; set; } + public IPAddress LocalIPAddress { get; set; } } } diff --git a/RSSDP/DeviceAvailableEventArgs.cs b/RSSDP/DeviceAvailableEventArgs.cs index 9106e27e5..21ac7c631 100644 --- a/RSSDP/DeviceAvailableEventArgs.cs +++ b/RSSDP/DeviceAvailableEventArgs.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using MediaBrowser.Model.Net; +using System.Net; namespace Rssdp { @@ -11,12 +8,12 @@ namespace Rssdp /// public sealed class DeviceAvailableEventArgs : EventArgs { - public IpAddressInfo LocalIpAddress { get; set; } + public IPAddress LocalIpAddress { get; set; } #region Fields private readonly DiscoveredSsdpDevice _DiscoveredDevice; - private readonly bool _IsNewlyDiscovered; + private readonly bool _IsNewlyDiscovered; #endregion @@ -29,34 +26,34 @@ namespace Rssdp /// A boolean value indicating whether or not this device came from the cache. See for more detail. /// Thrown if the parameter is null. public DeviceAvailableEventArgs(DiscoveredSsdpDevice discoveredDevice, bool isNewlyDiscovered) - { - if (discoveredDevice == null) throw new ArgumentNullException(nameof(discoveredDevice)); + { + if (discoveredDevice == null) throw new ArgumentNullException(nameof(discoveredDevice)); - _DiscoveredDevice = discoveredDevice; - _IsNewlyDiscovered = isNewlyDiscovered; - } + _DiscoveredDevice = discoveredDevice; + _IsNewlyDiscovered = isNewlyDiscovered; + } - #endregion + #endregion - #region Public Properties + #region Public Properties - /// - /// Returns true if the device was discovered due to an alive notification, or a search and was not already in the cache. Returns false if the item came from the cache but matched the current search request. - /// - public bool IsNewlyDiscovered - { - get { return _IsNewlyDiscovered; } - } + /// + /// Returns true if the device was discovered due to an alive notification, or a search and was not already in the cache. Returns false if the item came from the cache but matched the current search request. + /// + public bool IsNewlyDiscovered + { + get { return _IsNewlyDiscovered; } + } /// /// A reference to a instance containing the discovered details and allowing access to the full device description. /// public DiscoveredSsdpDevice DiscoveredDevice - { - get { return _DiscoveredDevice; } - } - - #endregion - - } -} \ No newline at end of file + { + get { return _DiscoveredDevice; } + } + + #endregion + + } +} diff --git a/RSSDP/ISsdpCommunicationsServer.cs b/RSSDP/ISsdpCommunicationsServer.cs index c99d684a1..8cf65df11 100644 --- a/RSSDP/ISsdpCommunicationsServer.cs +++ b/RSSDP/ISsdpCommunicationsServer.cs @@ -1,7 +1,7 @@ using System; +using System.Net; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.Net; namespace Rssdp.Infrastructure { @@ -40,13 +40,13 @@ namespace Rssdp.Infrastructure /// /// Sends a message to a particular address (uni or multicast) and port. /// - Task SendMessage(byte[] messageData, IpEndPointInfo destination, IpAddressInfo fromLocalIpAddress, CancellationToken cancellationToken); + Task SendMessage(byte[] messageData, IPEndPoint destination, IPAddress fromLocalIpAddress, CancellationToken cancellationToken); /// /// Sends a message to the SSDP multicast address and port. /// - Task SendMulticastMessage(string message, IpAddressInfo fromLocalIpAddress, CancellationToken cancellationToken); - Task SendMulticastMessage(string message, int sendCount, IpAddressInfo fromLocalIpAddress, CancellationToken cancellationToken); + Task SendMulticastMessage(string message, IPAddress fromLocalIpAddress, CancellationToken cancellationToken); + Task SendMulticastMessage(string message, int sendCount, IPAddress fromLocalIpAddress, CancellationToken cancellationToken); #endregion diff --git a/RSSDP/RequestReceivedEventArgs.cs b/RSSDP/RequestReceivedEventArgs.cs index fd3cd9e3a..b753950f0 100644 --- a/RSSDP/RequestReceivedEventArgs.cs +++ b/RSSDP/RequestReceivedEventArgs.cs @@ -1,10 +1,6 @@ using System; -using System.Collections.Generic; using System.Net; using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using MediaBrowser.Model.Net; namespace Rssdp.Infrastructure { @@ -16,18 +12,18 @@ namespace Rssdp.Infrastructure #region Fields private readonly HttpRequestMessage _Message; - private readonly IpEndPointInfo _ReceivedFrom; + private readonly IPEndPoint _ReceivedFrom; #endregion - public IpAddressInfo LocalIpAddress { get; private set; } + public IPAddress LocalIpAddress { get; private set; } #region Constructors /// /// Full constructor. /// - public RequestReceivedEventArgs(HttpRequestMessage message, IpEndPointInfo receivedFrom, IpAddressInfo localIpAddress) + public RequestReceivedEventArgs(HttpRequestMessage message, IPEndPoint receivedFrom, IPAddress localIpAddress) { _Message = message; _ReceivedFrom = receivedFrom; @@ -49,7 +45,7 @@ namespace Rssdp.Infrastructure /// /// The the request came from. /// - public IpEndPointInfo ReceivedFrom + public IPEndPoint ReceivedFrom { get { return _ReceivedFrom; } } diff --git a/RSSDP/ResponseReceivedEventArgs.cs b/RSSDP/ResponseReceivedEventArgs.cs index 5ed9664ed..f9f9c3040 100644 --- a/RSSDP/ResponseReceivedEventArgs.cs +++ b/RSSDP/ResponseReceivedEventArgs.cs @@ -1,10 +1,6 @@ using System; -using System.Collections.Generic; using System.Net; using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using MediaBrowser.Model.Net; namespace Rssdp.Infrastructure { @@ -14,12 +10,12 @@ namespace Rssdp.Infrastructure public sealed class ResponseReceivedEventArgs : EventArgs { - public IpAddressInfo LocalIpAddress { get; set; } + public IPAddress LocalIpAddress { get; set; } #region Fields private readonly HttpResponseMessage _Message; - private readonly IpEndPointInfo _ReceivedFrom; + private readonly IPEndPoint _ReceivedFrom; #endregion @@ -28,7 +24,7 @@ namespace Rssdp.Infrastructure /// /// Full constructor. /// - public ResponseReceivedEventArgs(HttpResponseMessage message, IpEndPointInfo receivedFrom) + public ResponseReceivedEventArgs(HttpResponseMessage message, IPEndPoint receivedFrom) { _Message = message; _ReceivedFrom = receivedFrom; @@ -49,7 +45,7 @@ namespace Rssdp.Infrastructure /// /// The the response came from. /// - public IpEndPointInfo ReceivedFrom + public IPEndPoint ReceivedFrom { get { return _ReceivedFrom; } } diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index 5d2afc37a..0aa985a26 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Net.Http; using System.Net.Sockets; using System.Text; @@ -163,7 +164,7 @@ namespace Rssdp.Infrastructure /// /// Sends a message to a particular address (uni or multicast) and port. /// - public async Task SendMessage(byte[] messageData, IpEndPointInfo destination, IpAddressInfo fromLocalIpAddress, CancellationToken cancellationToken) + public async Task SendMessage(byte[] messageData, IPEndPoint destination, IPAddress fromLocalIpAddress, CancellationToken cancellationToken) { if (messageData == null) throw new ArgumentNullException(nameof(messageData)); @@ -186,7 +187,7 @@ namespace Rssdp.Infrastructure } } - private async Task SendFromSocket(ISocket socket, byte[] messageData, IpEndPointInfo destination, CancellationToken cancellationToken) + private async Task SendFromSocket(ISocket socket, byte[] messageData, IPEndPoint destination, CancellationToken cancellationToken) { try { @@ -206,7 +207,7 @@ namespace Rssdp.Infrastructure } } - private List GetSendSockets(IpAddressInfo fromLocalIpAddress, IpEndPointInfo destination) + private List GetSendSockets(IPAddress fromLocalIpAddress, IPEndPoint destination) { EnsureSendSocketCreated(); @@ -215,24 +216,24 @@ namespace Rssdp.Infrastructure var sockets = _sendSockets.Where(i => i.LocalIPAddress.AddressFamily == fromLocalIpAddress.AddressFamily); // Send from the Any socket and the socket with the matching address - if (fromLocalIpAddress.AddressFamily == IpAddressFamily.InterNetwork) + if (fromLocalIpAddress.AddressFamily == AddressFamily.InterNetwork) { - sockets = sockets.Where(i => i.LocalIPAddress.Equals(IpAddressInfo.Any) || fromLocalIpAddress.Equals(i.LocalIPAddress)); + sockets = sockets.Where(i => i.LocalIPAddress.Equals(IPAddress.Any) || fromLocalIpAddress.Equals(i.LocalIPAddress)); // If sending to the loopback address, filter the socket list as well - if (destination.IpAddress.Equals(IpAddressInfo.Loopback)) + if (destination.Address.Equals(IPAddress.Loopback)) { - sockets = sockets.Where(i => i.LocalIPAddress.Equals(IpAddressInfo.Any) || i.LocalIPAddress.Equals(IpAddressInfo.Loopback)); + sockets = sockets.Where(i => i.LocalIPAddress.Equals(IPAddress.Any) || i.LocalIPAddress.Equals(IPAddress.Loopback)); } } - else if (fromLocalIpAddress.AddressFamily == IpAddressFamily.InterNetworkV6) + else if (fromLocalIpAddress.AddressFamily == AddressFamily.InterNetworkV6) { - sockets = sockets.Where(i => i.LocalIPAddress.Equals(IpAddressInfo.IPv6Any) || fromLocalIpAddress.Equals(i.LocalIPAddress)); + sockets = sockets.Where(i => i.LocalIPAddress.Equals(IPAddress.IPv6Any) || fromLocalIpAddress.Equals(i.LocalIPAddress)); // If sending to the loopback address, filter the socket list as well - if (destination.IpAddress.Equals(IpAddressInfo.IPv6Loopback)) + if (destination.Address.Equals(IPAddress.IPv6Loopback)) { - sockets = sockets.Where(i => i.LocalIPAddress.Equals(IpAddressInfo.IPv6Any) || i.LocalIPAddress.Equals(IpAddressInfo.IPv6Loopback)); + sockets = sockets.Where(i => i.LocalIPAddress.Equals(IPAddress.IPv6Any) || i.LocalIPAddress.Equals(IPAddress.IPv6Loopback)); } } @@ -240,7 +241,7 @@ namespace Rssdp.Infrastructure } } - public Task SendMulticastMessage(string message, IpAddressInfo fromLocalIpAddress, CancellationToken cancellationToken) + public Task SendMulticastMessage(string message, IPAddress fromLocalIpAddress, CancellationToken cancellationToken) { return SendMulticastMessage(message, SsdpConstants.UdpResendCount, fromLocalIpAddress, cancellationToken); } @@ -248,7 +249,7 @@ namespace Rssdp.Infrastructure /// /// Sends a message to the SSDP multicast address and port. /// - public async Task SendMulticastMessage(string message, int sendCount, IpAddressInfo fromLocalIpAddress, CancellationToken cancellationToken) + public async Task SendMulticastMessage(string message, int sendCount, IPAddress fromLocalIpAddress, CancellationToken cancellationToken) { if (message == null) throw new ArgumentNullException(nameof(message)); @@ -263,12 +264,13 @@ namespace Rssdp.Infrastructure // SSDP spec recommends sending messages multiple times (not more than 3) to account for possible packet loss over UDP. for (var i = 0; i < sendCount; i++) { - await SendMessageIfSocketNotDisposed(messageData, new IpEndPointInfo - { - IpAddress = new IpAddressInfo(SsdpConstants.MulticastLocalAdminAddress, IpAddressFamily.InterNetwork), - Port = SsdpConstants.MulticastPort - - }, fromLocalIpAddress, cancellationToken).ConfigureAwait(false); + await SendMessageIfSocketNotDisposed( + messageData, + new IPEndPoint( + IPAddress.Parse(SsdpConstants.MulticastLocalAdminAddress), + SsdpConstants.MulticastPort), + fromLocalIpAddress, + cancellationToken).ConfigureAwait(false); await Task.Delay(100, cancellationToken).ConfigureAwait(false); } @@ -336,7 +338,7 @@ namespace Rssdp.Infrastructure #region Private Methods - private Task SendMessageIfSocketNotDisposed(byte[] messageData, IpEndPointInfo destination, IpAddressInfo fromLocalIpAddress, CancellationToken cancellationToken) + private Task SendMessageIfSocketNotDisposed(byte[] messageData, IPEndPoint destination, IPAddress fromLocalIpAddress, CancellationToken cancellationToken) { var sockets = _sendSockets; if (sockets != null) @@ -364,13 +366,13 @@ namespace Rssdp.Infrastructure { var sockets = new List(); - sockets.Add(_SocketFactory.CreateSsdpUdpSocket(IpAddressInfo.Any, _LocalPort)); + sockets.Add(_SocketFactory.CreateSsdpUdpSocket(IPAddress.Any, _LocalPort)); if (_enableMultiSocketBinding) { foreach (var address in _networkManager.GetLocalIpAddresses(_config.Configuration.IgnoreVirtualInterfaces)) { - if (address.AddressFamily == IpAddressFamily.InterNetworkV6) + if (address.AddressFamily == AddressFamily.InterNetworkV6) { // Not support IPv6 right now continue; @@ -439,7 +441,7 @@ namespace Rssdp.Infrastructure } } - private void ProcessMessage(string data, IpEndPointInfo endPoint, IpAddressInfo receivedOnLocalIpAddress) + private void ProcessMessage(string data, IPEndPoint endPoint, IPAddress receivedOnLocalIpAddress) { // Responses start with the HTTP version, prefixed with HTTP/ while // requests start with a method which can vary and might be one we haven't @@ -481,7 +483,7 @@ namespace Rssdp.Infrastructure } } - private void OnRequestReceived(HttpRequestMessage data, IpEndPointInfo remoteEndPoint, IpAddressInfo receivedOnLocalIpAddress) + private void OnRequestReceived(HttpRequestMessage data, IPEndPoint remoteEndPoint, IPAddress receivedOnLocalIpAddress) { //SSDP specification says only * is currently used but other uri's might //be implemented in the future and should be ignored unless understood. @@ -496,7 +498,7 @@ namespace Rssdp.Infrastructure handlers(this, new RequestReceivedEventArgs(data, remoteEndPoint, receivedOnLocalIpAddress)); } - private void OnResponseReceived(HttpResponseMessage data, IpEndPointInfo endPoint, IpAddressInfo localIpAddress) + private void OnResponseReceived(HttpResponseMessage data, IPEndPoint endPoint, IPAddress localIpAddress) { var handlers = this.ResponseReceived; if (handlers != null) diff --git a/RSSDP/SsdpDevice.cs b/RSSDP/SsdpDevice.cs index b4c4a88fd..09f729e83 100644 --- a/RSSDP/SsdpDevice.cs +++ b/RSSDP/SsdpDevice.cs @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Text; -using System.Threading.Tasks; -using System.Xml; using Rssdp.Infrastructure; namespace Rssdp diff --git a/RSSDP/SsdpDeviceLocator.cs b/RSSDP/SsdpDeviceLocator.cs index e17e14c1a..59a2710d5 100644 --- a/RSSDP/SsdpDeviceLocator.cs +++ b/RSSDP/SsdpDeviceLocator.cs @@ -1,13 +1,10 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.IO; using System.Linq; +using System.Net; using System.Net.Http; -using System.Text; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.Net; namespace Rssdp.Infrastructure { @@ -213,7 +210,7 @@ namespace Rssdp.Infrastructure /// Raises the event. /// /// - protected virtual void OnDeviceAvailable(DiscoveredSsdpDevice device, bool isNewDevice, IpAddressInfo localIpAddress) + protected virtual void OnDeviceAvailable(DiscoveredSsdpDevice device, bool isNewDevice, IPAddress localIpAddress) { if (this.IsDisposed) return; @@ -295,7 +292,7 @@ namespace Rssdp.Infrastructure #region Discovery/Device Add - private void AddOrUpdateDiscoveredDevice(DiscoveredSsdpDevice device, IpAddressInfo localIpAddress) + private void AddOrUpdateDiscoveredDevice(DiscoveredSsdpDevice device, IPAddress localIpAddress) { bool isNewDevice = false; lock (_Devices) @@ -316,7 +313,7 @@ namespace Rssdp.Infrastructure DeviceFound(device, isNewDevice, localIpAddress); } - private void DeviceFound(DiscoveredSsdpDevice device, bool isNewDevice, IpAddressInfo localIpAddress) + private void DeviceFound(DiscoveredSsdpDevice device, bool isNewDevice, IPAddress localIpAddress) { if (!NotificationTypeMatchesFilter(device)) return; @@ -357,7 +354,7 @@ namespace Rssdp.Infrastructure return _CommunicationsServer.SendMulticastMessage(message, null, cancellationToken); } - private void ProcessSearchResponseMessage(HttpResponseMessage message, IpAddressInfo localIpAddress) + private void ProcessSearchResponseMessage(HttpResponseMessage message, IPAddress localIpAddress) { if (!message.IsSuccessStatusCode) return; @@ -378,7 +375,7 @@ namespace Rssdp.Infrastructure } } - private void ProcessNotificationMessage(HttpRequestMessage message, IpAddressInfo localIpAddress) + private void ProcessNotificationMessage(HttpRequestMessage message, IPAddress localIpAddress) { if (String.Compare(message.Method.Method, "Notify", StringComparison.OrdinalIgnoreCase) != 0) return; @@ -389,7 +386,7 @@ namespace Rssdp.Infrastructure ProcessByeByeNotification(message); } - private void ProcessAliveNotification(HttpRequestMessage message, IpAddressInfo localIpAddress) + private void ProcessAliveNotification(HttpRequestMessage message, IPAddress localIpAddress) { var location = GetFirstHeaderUriValue("Location", message); if (location != null) diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs index 921f33c21..7f3e56394 100644 --- a/RSSDP/SsdpDevicePublisher.cs +++ b/RSSDP/SsdpDevicePublisher.cs @@ -2,13 +2,10 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Net.Http; -using System.Text; +using System.Net; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.Net; using MediaBrowser.Common.Net; -using Rssdp; namespace Rssdp.Infrastructure { @@ -199,7 +196,12 @@ namespace Rssdp.Infrastructure } } - private void ProcessSearchRequest(string mx, string searchTarget, IpEndPointInfo remoteEndPoint, IpAddressInfo receivedOnlocalIpAddress, CancellationToken cancellationToken) + private void ProcessSearchRequest( + string mx, + string searchTarget, + IPEndPoint remoteEndPoint, + IPAddress receivedOnlocalIpAddress, + CancellationToken cancellationToken) { if (String.IsNullOrEmpty(searchTarget)) { @@ -258,7 +260,7 @@ namespace Rssdp.Infrastructure foreach (var device in deviceList) { if (!_sendOnlyMatchedHost || - _networkManager.IsInSameSubnet(device.ToRootDevice().Address, remoteEndPoint.IpAddress, device.ToRootDevice().SubnetMask)) + _networkManager.IsInSameSubnet(device.ToRootDevice().Address, remoteEndPoint.Address, device.ToRootDevice().SubnetMask)) { SendDeviceSearchResponses(device, remoteEndPoint, receivedOnlocalIpAddress, cancellationToken); } @@ -276,7 +278,11 @@ namespace Rssdp.Infrastructure return _Devices.Union(_Devices.SelectManyRecursive((d) => d.Devices)); } - private void SendDeviceSearchResponses(SsdpDevice device, IpEndPointInfo endPoint, IpAddressInfo receivedOnlocalIpAddress, CancellationToken cancellationToken) + private void SendDeviceSearchResponses( + SsdpDevice device, + IPEndPoint endPoint, + IPAddress receivedOnlocalIpAddress, + CancellationToken cancellationToken) { bool isRootDevice = (device as SsdpRootDevice) != null; if (isRootDevice) @@ -296,7 +302,13 @@ namespace Rssdp.Infrastructure return String.Format("{0}::{1}", udn, fullDeviceType); } - private async void SendSearchResponse(string searchTarget, SsdpDevice device, string uniqueServiceName, IpEndPointInfo endPoint, IpAddressInfo receivedOnlocalIpAddress, CancellationToken cancellationToken) + private async void SendSearchResponse( + string searchTarget, + SsdpDevice device, + string uniqueServiceName, + IPEndPoint endPoint, + IPAddress receivedOnlocalIpAddress, + CancellationToken cancellationToken) { var rootDevice = device.ToRootDevice(); @@ -333,7 +345,7 @@ namespace Rssdp.Infrastructure //WriteTrace(String.Format("Sent search response to " + endPoint.ToString()), device); } - private bool IsDuplicateSearchRequest(string searchTarget, IpEndPointInfo endPoint) + private bool IsDuplicateSearchRequest(string searchTarget, IPEndPoint endPoint) { var isDuplicateRequest = false; @@ -556,7 +568,7 @@ namespace Rssdp.Infrastructure private class SearchRequest { - public IpEndPointInfo EndPoint { get; set; } + public IPEndPoint EndPoint { get; set; } public DateTime Received { get; set; } public string SearchTarget { get; set; } diff --git a/RSSDP/SsdpRootDevice.cs b/RSSDP/SsdpRootDevice.cs index d918b9040..0f2de7b15 100644 --- a/RSSDP/SsdpRootDevice.cs +++ b/RSSDP/SsdpRootDevice.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; -using System.Text; -using System.Xml; -using Rssdp.Infrastructure; -using MediaBrowser.Model.Net; +using System.Net; namespace Rssdp { @@ -56,12 +52,12 @@ namespace Rssdp /// /// Gets or sets the Address used to check if the received message from same interface with this device/tree. Required. /// - public IpAddressInfo Address { get; set; } + public IPAddress Address { get; set; } /// /// Gets or sets the SubnetMask used to check if the received message from same interface with this device/tree. Required. /// - public IpAddressInfo SubnetMask { get; set; } + public IPAddress SubnetMask { get; set; } /// /// The base URL to use for all relative url's provided in other propertise (and those of child devices). Optional. -- cgit v1.2.3 From 8270d0cc91783c0c8c052b43af0d633edb8b6b04 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 25 Jul 2019 00:23:56 +0200 Subject: Move IPv6 scope id removal logic to it's own function --- Emby.Server.Implementations/ApplicationHost.cs | 32 +++++++++++++++----------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c6ba2326a..ef2f59d30 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1547,17 +1547,27 @@ namespace Emby.Server.Implementations return null; } + /// + /// Removes the scope id from IPv6 addresses. + /// + /// The IPv6 address. + /// The IPv6 address without the scope id. + private string RemoveScopeId(string address) + { + var index = address.IndexOf('%'); + if (index == -1) + { + return address; + } + + return address.Substring(0, index); + } + public string GetLocalApiUrl(IPAddress ipAddress) { if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) { - // Remove the scope id from IPv6 addresses - var str = ipAddress.ToString(); - var index = str.IndexOf('%'); - if (index != -1) - { - str = str.Substring(0, index); - } + var str = RemoveScopeId(ipAddress.ToString()); return GetLocalApiUrl("[" + str + "]"); } @@ -1583,13 +1593,7 @@ namespace Emby.Server.Implementations { if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) { - // Remove the scope id from IPv6 addresses - var str = ipAddress.ToString(); - var index = str.IndexOf('%'); - if (index != -1) - { - str = str.Substring(0, index); - } + var str = RemoveScopeId(ipAddress.ToString()); return GetWanApiUrl("[" + str + "]"); } -- cgit v1.2.3 From 5c9d0414234d49a94f323228ac65cf191e80949b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 29 Jul 2019 13:57:36 +0200 Subject: Ignore Ipv6 link-local addresses --- Emby.Server.Implementations/Networking/NetworkManager.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index 3cacacf07..da06dc5c5 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -95,9 +95,8 @@ namespace Emby.Server.Implementations.Networking private static bool FilterIpAddress(IPAddress address) { - var addressString = address.ToString(); - - if (addressString.StartsWith("169.", StringComparison.OrdinalIgnoreCase)) + if (address.IsIPv6LinkLocal + || address.ToString().StartsWith("169.", StringComparison.OrdinalIgnoreCase)) { return false; } -- cgit v1.2.3 From 998017a76d7e0fcc340bf9dbc3436caef583f6b4 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 29 Jul 2019 16:01:14 +0200 Subject: Include library via NuGet instead of via source --- .../Emby.Server.Implementations.csproj | 1 + Emby.Server.Implementations/Net/UdpSocket.cs | 1 - .../Networking/IPNetwork/BigIntegerExt.cs | 167 -- .../Networking/IPNetwork/IPAddressCollection.cs | 94 - .../Networking/IPNetwork/IPNetwork.cs | 2008 -------------------- .../Networking/IPNetwork/IPNetworkCollection.cs | 129 -- .../Networking/IPNetwork/LICENSE.txt | 24 - .../Networking/NetworkManager.cs | 2 +- 8 files changed, 2 insertions(+), 2424 deletions(-) delete mode 100644 Emby.Server.Implementations/Networking/IPNetwork/BigIntegerExt.cs delete mode 100644 Emby.Server.Implementations/Networking/IPNetwork/IPAddressCollection.cs delete mode 100644 Emby.Server.Implementations/Networking/IPNetwork/IPNetwork.cs delete mode 100644 Emby.Server.Implementations/Networking/IPNetwork/IPNetworkCollection.cs delete mode 100644 Emby.Server.Implementations/Networking/IPNetwork/LICENSE.txt (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 49015a07e..abbaef26b 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -20,6 +20,7 @@ + diff --git a/Emby.Server.Implementations/Net/UdpSocket.cs b/Emby.Server.Implementations/Net/UdpSocket.cs index 2908ee9af..dde4a2a34 100644 --- a/Emby.Server.Implementations/Net/UdpSocket.cs +++ b/Emby.Server.Implementations/Net/UdpSocket.cs @@ -3,7 +3,6 @@ using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; -using Emby.Server.Implementations.Networking; using MediaBrowser.Model.Net; namespace Emby.Server.Implementations.Net diff --git a/Emby.Server.Implementations/Networking/IPNetwork/BigIntegerExt.cs b/Emby.Server.Implementations/Networking/IPNetwork/BigIntegerExt.cs deleted file mode 100644 index e4e944839..000000000 --- a/Emby.Server.Implementations/Networking/IPNetwork/BigIntegerExt.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; -using System.Text; - -namespace Emby.Server.Implementations.Networking.IPNetwork -{ - /// - /// Extension methods to convert - /// instances to hexadecimal, octal, and binary strings. - /// - public static class BigIntegerExtensions - { - /// - /// Converts a to a binary string. - /// - /// A . - /// - /// A containing a binary - /// representation of the supplied . - /// - public static string ToBinaryString(this BigInteger bigint) - { - var bytes = bigint.ToByteArray(); - var idx = bytes.Length - 1; - - // Create a StringBuilder having appropriate capacity. - var base2 = new StringBuilder(bytes.Length * 8); - - // Convert first byte to binary. - var binary = Convert.ToString(bytes[idx], 2); - - // Ensure leading zero exists if value is positive. - if (binary[0] != '0' && bigint.Sign == 1) - { - base2.Append('0'); - } - - // Append binary string to StringBuilder. - base2.Append(binary); - - // Convert remaining bytes adding leading zeros. - for (idx--; idx >= 0; idx--) - { - base2.Append(Convert.ToString(bytes[idx], 2).PadLeft(8, '0')); - } - - return base2.ToString(); - } - - /// - /// Converts a to a hexadecimal string. - /// - /// A . - /// - /// A containing a hexadecimal - /// representation of the supplied . - /// - public static string ToHexadecimalString(this BigInteger bigint) - { - return bigint.ToString("X"); - } - - /// - /// Converts a to a octal string. - /// - /// A . - /// - /// A containing an octal - /// representation of the supplied . - /// - public static string ToOctalString(this BigInteger bigint) - { - var bytes = bigint.ToByteArray(); - var idx = bytes.Length - 1; - - // Create a StringBuilder having appropriate capacity. - var base8 = new StringBuilder(((bytes.Length / 3) + 1) * 8); - - // Calculate how many bytes are extra when byte array is split - // into three-byte (24-bit) chunks. - var extra = bytes.Length % 3; - - // If no bytes are extra, use three bytes for first chunk. - if (extra == 0) - { - extra = 3; - } - - // Convert first chunk (24-bits) to integer value. - int int24 = 0; - for (; extra != 0; extra--) - { - int24 <<= 8; - int24 += bytes[idx--]; - } - - // Convert 24-bit integer to octal without adding leading zeros. - var octal = Convert.ToString(int24, 8); - - // Ensure leading zero exists if value is positive. - if (octal[0] != '0') - { - if (bigint.Sign == 1) - { - base8.Append('0'); - } - } - - // Append first converted chunk to StringBuilder. - base8.Append(octal); - - // Convert remaining 24-bit chunks, adding leading zeros. - for (; idx >= 0; idx -= 3) - { - int24 = (bytes[idx] << 16) + (bytes[idx - 1] << 8) + bytes[idx - 2]; - base8.Append(Convert.ToString(int24, 8).PadLeft(8, '0')); - } - - return base8.ToString(); - } - - /// - /// - /// Reverse a Positive BigInteger ONLY - /// Bitwise ~ operator - /// - /// Input : FF FF FF FF - /// Width : 4 - /// Result : 00 00 00 00 - /// - /// - /// Input : 00 00 00 00 - /// Width : 4 - /// Result : FF FF FF FF - /// - /// Input : FF FF FF FF - /// Width : 8 - /// Result : FF FF FF FF 00 00 00 00 - /// - /// - /// Input : 00 00 00 00 - /// Width : 8 - /// Result : FF FF FF FF FF FF FF FF - /// - /// - /// - /// - /// - public static BigInteger PositiveReverse(this BigInteger input, int width) - { - - var result = new List(); - var bytes = input.ToByteArray(); - var work = new byte[width]; - Array.Copy(bytes, 0, work, 0, bytes.Length - 1); // Length -1 : positive BigInteger - - for (int i = 0; i < work.Length; i++) - { - result.Add((byte)(~work[i])); - } - result.Add(0); // positive BigInteger - return new BigInteger(result.ToArray()); - - } - } -} diff --git a/Emby.Server.Implementations/Networking/IPNetwork/IPAddressCollection.cs b/Emby.Server.Implementations/Networking/IPNetwork/IPAddressCollection.cs deleted file mode 100644 index a0c5f73af..000000000 --- a/Emby.Server.Implementations/Networking/IPNetwork/IPAddressCollection.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Net; -using System.Numerics; - -namespace Emby.Server.Implementations.Networking.IPNetwork -{ - public class IPAddressCollection : IEnumerable, IEnumerator - { - - private IPNetwork _ipnetwork; - private BigInteger _enumerator; - - internal IPAddressCollection(IPNetwork ipnetwork) - { - this._ipnetwork = ipnetwork; - this._enumerator = -1; - } - - - #region Count, Array, Enumerator - - public BigInteger Count => this._ipnetwork.Total; - - public IPAddress this[BigInteger i] - { - get - { - if (i >= this.Count) - { - throw new ArgumentOutOfRangeException(nameof(i)); - } - byte width = this._ipnetwork.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork ? (byte)32 : (byte)128; - var ipn = this._ipnetwork.Subnet(width); - return ipn[i].Network; - } - } - - #endregion - - #region IEnumerable Members - - IEnumerator IEnumerable.GetEnumerator() - { - return this; - } - - IEnumerator IEnumerable.GetEnumerator() - { - return this; - } - - #region IEnumerator Members - - public IPAddress Current => this[this._enumerator]; - - #endregion - - #region IDisposable Members - - public void Dispose() - { - // nothing to dispose - return; - } - - #endregion - - #region IEnumerator Members - - object IEnumerator.Current => this.Current; - - public bool MoveNext() - { - this._enumerator++; - if (this._enumerator >= this.Count) - { - return false; - } - return true; - - } - - public void Reset() - { - this._enumerator = -1; - } - - #endregion - - #endregion - } -} diff --git a/Emby.Server.Implementations/Networking/IPNetwork/IPNetwork.cs b/Emby.Server.Implementations/Networking/IPNetwork/IPNetwork.cs deleted file mode 100644 index d6de61c0c..000000000 --- a/Emby.Server.Implementations/Networking/IPNetwork/IPNetwork.cs +++ /dev/null @@ -1,2008 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Numerics; -using System.Text.RegularExpressions; - -namespace Emby.Server.Implementations.Networking.IPNetwork -{ - /// - /// IP Network utility class. - /// Use IPNetwork.Parse to create instances. - /// - public class IPNetwork : IComparable - { - - #region properties - - //private uint _network; - private BigInteger _ipaddress; - private AddressFamily _family; - //private uint _netmask; - //private uint _broadcast; - //private uint _firstUsable; - //private uint _lastUsable; - //private uint _usable; - private byte _cidr; - - #endregion - - #region accessors - - private BigInteger _network - { - get - { - var uintNetwork = this._ipaddress & this._netmask; - return uintNetwork; - } - } - - /// - /// Network address - /// - public IPAddress Network => IPNetwork.ToIPAddress(this._network, this._family); - - /// - /// Address Family - /// - public AddressFamily AddressFamily => this._family; - - private BigInteger _netmask => IPNetwork.ToUint(this._cidr, this._family); - - /// - /// Netmask - /// - public IPAddress Netmask => IPNetwork.ToIPAddress(this._netmask, this._family); - - private BigInteger _broadcast - { - get - { - - int width = this._family == System.Net.Sockets.AddressFamily.InterNetwork ? 4 : 16; - var uintBroadcast = this._network + this._netmask.PositiveReverse(width); - return uintBroadcast; - } - } - - /// - /// Broadcast address - /// - public IPAddress Broadcast - { - get - { - if (this._family == System.Net.Sockets.AddressFamily.InterNetworkV6) - { - return null; - } - return IPNetwork.ToIPAddress(this._broadcast, this._family); - } - } - - /// - /// First usable IP adress in Network - /// - public IPAddress FirstUsable - { - get - { - var fisrt = this._family == System.Net.Sockets.AddressFamily.InterNetworkV6 - ? this._network - : (this.Usable <= 0) ? this._network : this._network + 1; - return IPNetwork.ToIPAddress(fisrt, this._family); - } - } - - /// - /// Last usable IP adress in Network - /// - public IPAddress LastUsable - { - get - { - var last = this._family == System.Net.Sockets.AddressFamily.InterNetworkV6 - ? this._broadcast - : (this.Usable <= 0) ? this._network : this._broadcast - 1; - return IPNetwork.ToIPAddress(last, this._family); - } - } - - /// - /// Number of usable IP adress in Network - /// - public BigInteger Usable - { - get - { - - if (this._family == System.Net.Sockets.AddressFamily.InterNetworkV6) - { - return this.Total; - } - byte[] mask = new byte[] { 0xff, 0xff, 0xff, 0xff, 0x00 }; - var bmask = new BigInteger(mask); - var usableIps = (_cidr > 30) ? 0 : ((bmask >> _cidr) - 1); - return usableIps; - } - } - - /// - /// Number of IP adress in Network - /// - public BigInteger Total - { - get - { - - int max = this._family == System.Net.Sockets.AddressFamily.InterNetwork ? 32 : 128; - var count = BigInteger.Pow(2, (max - _cidr)); - return count; - } - } - - - /// - /// The CIDR netmask notation - /// - public byte Cidr => this._cidr; - - #endregion - - #region constructor - -#if TRAVISCI - public -#else - internal -#endif - - IPNetwork(BigInteger ipaddress, AddressFamily family, byte cidr) - { - - int maxCidr = family == System.Net.Sockets.AddressFamily.InterNetwork ? 32 : 128; - if (cidr > maxCidr) - { - throw new ArgumentOutOfRangeException(nameof(cidr)); - } - - this._ipaddress = ipaddress; - this._family = family; - this._cidr = cidr; - - } - - #endregion - - #region parsers - - /// - /// 192.168.168.100 - 255.255.255.0 - /// - /// Network : 192.168.168.0 - /// Netmask : 255.255.255.0 - /// Cidr : 24 - /// Start : 192.168.168.1 - /// End : 192.168.168.254 - /// Broadcast : 192.168.168.255 - /// - /// - /// - /// - public static IPNetwork Parse(string ipaddress, string netmask) - { - - IPNetwork ipnetwork = null; - IPNetwork.InternalParse(false, ipaddress, netmask, out ipnetwork); - return ipnetwork; - } - - /// - /// 192.168.168.100/24 - /// - /// Network : 192.168.168.0 - /// Netmask : 255.255.255.0 - /// Cidr : 24 - /// Start : 192.168.168.1 - /// End : 192.168.168.254 - /// Broadcast : 192.168.168.255 - /// - /// - /// - /// - public static IPNetwork Parse(string ipaddress, byte cidr) - { - - IPNetwork ipnetwork = null; - IPNetwork.InternalParse(false, ipaddress, cidr, out ipnetwork); - return ipnetwork; - - } - - /// - /// 192.168.168.100 255.255.255.0 - /// - /// Network : 192.168.168.0 - /// Netmask : 255.255.255.0 - /// Cidr : 24 - /// Start : 192.168.168.1 - /// End : 192.168.168.254 - /// Broadcast : 192.168.168.255 - /// - /// - /// - /// - public static IPNetwork Parse(IPAddress ipaddress, IPAddress netmask) - { - - IPNetwork ipnetwork = null; - IPNetwork.InternalParse(false, ipaddress, netmask, out ipnetwork); - return ipnetwork; - - } - - /// - /// 192.168.0.1/24 - /// 192.168.0.1 255.255.255.0 - /// - /// Network : 192.168.0.0 - /// Netmask : 255.255.255.0 - /// Cidr : 24 - /// Start : 192.168.0.1 - /// End : 192.168.0.254 - /// Broadcast : 192.168.0.255 - /// - /// - /// - public static IPNetwork Parse(string network) - { - - IPNetwork ipnetwork = null; - IPNetwork.InternalParse(false, network, out ipnetwork); - return ipnetwork; - - } - - #endregion - - #region TryParse - - - - /// - /// 192.168.168.100 - 255.255.255.0 - /// - /// Network : 192.168.168.0 - /// Netmask : 255.255.255.0 - /// Cidr : 24 - /// Start : 192.168.168.1 - /// End : 192.168.168.254 - /// Broadcast : 192.168.168.255 - /// - /// - /// - /// - public static bool TryParse(string ipaddress, string netmask, out IPNetwork ipnetwork) - { - - IPNetwork ipnetwork2 = null; - IPNetwork.InternalParse(true, ipaddress, netmask, out ipnetwork2); - bool parsed = (ipnetwork2 != null); - ipnetwork = ipnetwork2; - return parsed; - - } - - - - /// - /// 192.168.168.100/24 - /// - /// Network : 192.168.168.0 - /// Netmask : 255.255.255.0 - /// Cidr : 24 - /// Start : 192.168.168.1 - /// End : 192.168.168.254 - /// Broadcast : 192.168.168.255 - /// - /// - /// - /// - public static bool TryParse(string ipaddress, byte cidr, out IPNetwork ipnetwork) - { - - IPNetwork ipnetwork2 = null; - IPNetwork.InternalParse(true, ipaddress, cidr, out ipnetwork2); - bool parsed = (ipnetwork2 != null); - ipnetwork = ipnetwork2; - return parsed; - - } - - /// - /// 192.168.0.1/24 - /// 192.168.0.1 255.255.255.0 - /// - /// Network : 192.168.0.0 - /// Netmask : 255.255.255.0 - /// Cidr : 24 - /// Start : 192.168.0.1 - /// End : 192.168.0.254 - /// Broadcast : 192.168.0.255 - /// - /// - /// - /// - public static bool TryParse(string network, out IPNetwork ipnetwork) - { - - IPNetwork ipnetwork2 = null; - IPNetwork.InternalParse(true, network, out ipnetwork2); - bool parsed = (ipnetwork2 != null); - ipnetwork = ipnetwork2; - return parsed; - - } - - /// - /// 192.168.0.1/24 - /// 192.168.0.1 255.255.255.0 - /// - /// Network : 192.168.0.0 - /// Netmask : 255.255.255.0 - /// Cidr : 24 - /// Start : 192.168.0.1 - /// End : 192.168.0.254 - /// Broadcast : 192.168.0.255 - /// - /// - /// - /// - /// - public static bool TryParse(IPAddress ipaddress, IPAddress netmask, out IPNetwork ipnetwork) - { - - IPNetwork ipnetwork2 = null; - IPNetwork.InternalParse(true, ipaddress, netmask, out ipnetwork2); - bool parsed = (ipnetwork2 != null); - ipnetwork = ipnetwork2; - return parsed; - - } - - - #endregion - - #region InternalParse - - /// - /// 192.168.168.100 - 255.255.255.0 - /// - /// Network : 192.168.168.0 - /// Netmask : 255.255.255.0 - /// Cidr : 24 - /// Start : 192.168.168.1 - /// End : 192.168.168.254 - /// Broadcast : 192.168.168.255 - /// - /// - /// - /// - private static void InternalParse(bool tryParse, string ipaddress, string netmask, out IPNetwork ipnetwork) - { - - if (string.IsNullOrEmpty(ipaddress)) - { - if (tryParse == false) - { - throw new ArgumentNullException(nameof(ipaddress)); - } - ipnetwork = null; - return; - } - - if (string.IsNullOrEmpty(netmask)) - { - if (tryParse == false) - { - throw new ArgumentNullException(nameof(netmask)); - } - ipnetwork = null; - return; - } - - IPAddress ip = null; - bool ipaddressParsed = IPAddress.TryParse(ipaddress, out ip); - if (ipaddressParsed == false) - { - if (tryParse == false) - { - throw new ArgumentException("ipaddress"); - } - ipnetwork = null; - return; - } - - IPAddress mask = null; - bool netmaskParsed = IPAddress.TryParse(netmask, out mask); - if (netmaskParsed == false) - { - if (tryParse == false) - { - throw new ArgumentException("netmask"); - } - ipnetwork = null; - return; - } - - IPNetwork.InternalParse(tryParse, ip, mask, out ipnetwork); - } - - private static void InternalParse(bool tryParse, string network, out IPNetwork ipnetwork) - { - - if (string.IsNullOrEmpty(network)) - { - if (tryParse == false) - { - throw new ArgumentNullException(nameof(network)); - } - ipnetwork = null; - return; - } - - network = Regex.Replace(network, @"[^0-9a-fA-F\.\/\s\:]+", ""); - network = Regex.Replace(network, @"\s{2,}", " "); - network = network.Trim(); - string[] args = network.Split(new char[] { ' ', '/' }); - byte cidr = 0; - if (args.Length == 1) - { - - if (IPNetwork.TryGuessCidr(args[0], out cidr)) - { - IPNetwork.InternalParse(tryParse, args[0], cidr, out ipnetwork); - return; - } - - if (tryParse == false) - { - throw new ArgumentException("network"); - } - ipnetwork = null; - return; - } - - if (byte.TryParse(args[1], out cidr)) - { - IPNetwork.InternalParse(tryParse, args[0], cidr, out ipnetwork); - return; - } - - IPNetwork.InternalParse(tryParse, args[0], args[1], out ipnetwork); - return; - - } - - - - /// - /// 192.168.168.100 255.255.255.0 - /// - /// Network : 192.168.168.0 - /// Netmask : 255.255.255.0 - /// Cidr : 24 - /// Start : 192.168.168.1 - /// End : 192.168.168.254 - /// Broadcast : 192.168.168.255 - /// - /// - /// - /// - private static void InternalParse(bool tryParse, IPAddress ipaddress, IPAddress netmask, out IPNetwork ipnetwork) - { - - if (ipaddress == null) - { - if (tryParse == false) - { - throw new ArgumentNullException(nameof(ipaddress)); - } - ipnetwork = null; - return; - } - - if (netmask == null) - { - if (tryParse == false) - { - throw new ArgumentNullException(nameof(netmask)); - } - ipnetwork = null; - return; - } - - var uintIpAddress = IPNetwork.ToBigInteger(ipaddress); - bool parsed = IPNetwork.TryToCidr(netmask, out var cidr2); - if (parsed == false) - { - if (tryParse == false) - { - throw new ArgumentException("netmask"); - } - ipnetwork = null; - return; - } - byte cidr = (byte)cidr2; - - var ipnet = new IPNetwork(uintIpAddress, ipaddress.AddressFamily, cidr); - ipnetwork = ipnet; - - return; - } - - - - /// - /// 192.168.168.100/24 - /// - /// Network : 192.168.168.0 - /// Netmask : 255.255.255.0 - /// Cidr : 24 - /// Start : 192.168.168.1 - /// End : 192.168.168.254 - /// Broadcast : 192.168.168.255 - /// - /// - /// - /// - private static void InternalParse(bool tryParse, string ipaddress, byte cidr, out IPNetwork ipnetwork) - { - - if (string.IsNullOrEmpty(ipaddress)) - { - if (tryParse == false) - { - throw new ArgumentNullException(nameof(ipaddress)); - } - ipnetwork = null; - return; - } - - - IPAddress ip = null; - bool ipaddressParsed = IPAddress.TryParse(ipaddress, out ip); - if (ipaddressParsed == false) - { - if (tryParse == false) - { - throw new ArgumentException("ipaddress"); - } - ipnetwork = null; - return; - } - - IPAddress mask = null; - bool parsedNetmask = IPNetwork.TryToNetmask(cidr, ip.AddressFamily, out mask); - if (parsedNetmask == false) - { - if (tryParse == false) - { - throw new ArgumentException("cidr"); - } - ipnetwork = null; - return; - } - - - IPNetwork.InternalParse(tryParse, ip, mask, out ipnetwork); - } - - #endregion - - #region converters - - #region ToUint - - /// - /// Convert an ipadress to decimal - /// 0.0.0.0 -> 0 - /// 0.0.1.0 -> 256 - /// - /// - /// - public static BigInteger ToBigInteger(IPAddress ipaddress) - { - IPNetwork.InternalToBigInteger(false, ipaddress, out var uintIpAddress); - return (BigInteger)uintIpAddress; - - } - - /// - /// Convert an ipadress to decimal - /// 0.0.0.0 -> 0 - /// 0.0.1.0 -> 256 - /// - /// - /// - public static bool TryToBigInteger(IPAddress ipaddress, out BigInteger? uintIpAddress) - { - IPNetwork.InternalToBigInteger(true, ipaddress, out var uintIpAddress2); - bool parsed = (uintIpAddress2 != null); - uintIpAddress = uintIpAddress2; - return parsed; - } - -#if TRAVISCI - public -#else - internal -#endif - static void InternalToBigInteger(bool tryParse, IPAddress ipaddress, out BigInteger? uintIpAddress) - { - - if (ipaddress == null) - { - if (tryParse == false) - { - throw new ArgumentNullException(nameof(ipaddress)); - } - uintIpAddress = null; - return; - } - - byte[] bytes = ipaddress.GetAddressBytes(); - /// 20180217 lduchosal - /// code impossible to reach, GetAddressBytes returns either 4 or 16 bytes length addresses - /// if (bytes.Length != 4 && bytes.Length != 16) { - /// if (tryParse == false) { - /// throw new ArgumentException("bytes"); - /// } - /// uintIpAddress = null; - /// return; - /// } - - Array.Reverse(bytes); - var unsigned = new List(bytes); - unsigned.Add(0); - uintIpAddress = new BigInteger(unsigned.ToArray()); - return; - } - - - /// - /// Convert a cidr to BigInteger netmask - /// - /// - /// - public static BigInteger ToUint(byte cidr, AddressFamily family) - { - IPNetwork.InternalToBigInteger(false, cidr, family, out var uintNetmask); - return (BigInteger)uintNetmask; - } - - - /// - /// Convert a cidr to uint netmask - /// - /// - /// - public static bool TryToUint(byte cidr, AddressFamily family, out BigInteger? uintNetmask) - { - IPNetwork.InternalToBigInteger(true, cidr, family, out var uintNetmask2); - bool parsed = (uintNetmask2 != null); - uintNetmask = uintNetmask2; - return parsed; - } - - /// - /// Convert a cidr to uint netmask - /// - /// - /// -#if TRAVISCI - public -#else - internal -#endif - static void InternalToBigInteger(bool tryParse, byte cidr, AddressFamily family, out BigInteger? uintNetmask) - { - - if (family == AddressFamily.InterNetwork && cidr > 32) - { - if (tryParse == false) - { - throw new ArgumentOutOfRangeException(nameof(cidr)); - } - uintNetmask = null; - return; - } - - if (family == AddressFamily.InterNetworkV6 && cidr > 128) - { - if (tryParse == false) - { - throw new ArgumentOutOfRangeException(nameof(cidr)); - } - uintNetmask = null; - return; - } - - if (family != AddressFamily.InterNetwork - && family != AddressFamily.InterNetworkV6) - { - if (tryParse == false) - { - throw new NotSupportedException(family.ToString()); - } - uintNetmask = null; - return; - } - - if (family == AddressFamily.InterNetwork) - { - - uintNetmask = cidr == 0 ? 0 : 0xffffffff << (32 - cidr); - return; - } - - var mask = new BigInteger(new byte[] { - 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, - 0x00 - }); - - var masked = cidr == 0 ? 0 : mask << (128 - cidr); - byte[] m = masked.ToByteArray(); - byte[] bmask = new byte[17]; - int copy = m.Length > 16 ? 16 : m.Length; - Array.Copy(m, 0, bmask, 0, copy); - uintNetmask = new BigInteger(bmask); - - - } - - #endregion - - #region ToCidr - - /// - /// Convert netmask to CIDR - /// 255.255.255.0 -> 24 - /// 255.255.0.0 -> 16 - /// 255.0.0.0 -> 8 - /// - /// - /// - private static void InternalToCidr(bool tryParse, BigInteger netmask, AddressFamily family, out byte? cidr) - { - - if (!IPNetwork.InternalValidNetmask(netmask, family)) - { - if (tryParse == false) - { - throw new ArgumentException("netmask"); - } - cidr = null; - return; - } - - byte cidr2 = IPNetwork.BitsSet(netmask, family); - cidr = cidr2; - return; - - } - /// - /// Convert netmask to CIDR - /// 255.255.255.0 -> 24 - /// 255.255.0.0 -> 16 - /// 255.0.0.0 -> 8 - /// - /// - /// - public static byte ToCidr(IPAddress netmask) - { - IPNetwork.InternalToCidr(false, netmask, out var cidr); - return (byte)cidr; - } - - /// - /// Convert netmask to CIDR - /// 255.255.255.0 -> 24 - /// 255.255.0.0 -> 16 - /// 255.0.0.0 -> 8 - /// - /// - /// - public static bool TryToCidr(IPAddress netmask, out byte? cidr) - { - IPNetwork.InternalToCidr(true, netmask, out var cidr2); - bool parsed = (cidr2 != null); - cidr = cidr2; - return parsed; - } - - private static void InternalToCidr(bool tryParse, IPAddress netmask, out byte? cidr) - { - - if (netmask == null) - { - if (tryParse == false) - { - throw new ArgumentNullException(nameof(netmask)); - } - cidr = null; - return; - } - - bool parsed = IPNetwork.TryToBigInteger(netmask, out var uintNetmask2); - - /// 20180217 lduchosal - /// impossible to reach code. - /// if (parsed == false) { - /// if (tryParse == false) { - /// throw new ArgumentException("netmask"); - /// } - /// cidr = null; - /// return; - /// } - var uintNetmask = (BigInteger)uintNetmask2; - - IPNetwork.InternalToCidr(tryParse, uintNetmask, netmask.AddressFamily, out var cidr2); - cidr = cidr2; - - return; - - } - - - #endregion - - #region ToNetmask - - /// - /// Convert CIDR to netmask - /// 24 -> 255.255.255.0 - /// 16 -> 255.255.0.0 - /// 8 -> 255.0.0.0 - /// - /// - /// - /// - public static IPAddress ToNetmask(byte cidr, AddressFamily family) - { - - IPAddress netmask = null; - IPNetwork.InternalToNetmask(false, cidr, family, out netmask); - return netmask; - } - - /// - /// Convert CIDR to netmask - /// 24 -> 255.255.255.0 - /// 16 -> 255.255.0.0 - /// 8 -> 255.0.0.0 - /// - /// - /// - /// - public static bool TryToNetmask(byte cidr, AddressFamily family, out IPAddress netmask) - { - - IPAddress netmask2 = null; - IPNetwork.InternalToNetmask(true, cidr, family, out netmask2); - bool parsed = (netmask2 != null); - netmask = netmask2; - return parsed; - } - - -#if TRAVISCI - public -#else - internal -#endif - static void InternalToNetmask(bool tryParse, byte cidr, AddressFamily family, out IPAddress netmask) - { - - if (family != AddressFamily.InterNetwork - && family != AddressFamily.InterNetworkV6) - { - if (tryParse == false) - { - throw new ArgumentException("family"); - } - netmask = null; - return; - } - - /// 20180217 lduchosal - /// impossible to reach code, byte cannot be negative : - /// - /// if (cidr < 0) { - /// if (tryParse == false) { - /// throw new ArgumentOutOfRangeException("cidr"); - /// } - /// netmask = null; - /// return; - /// } - - int maxCidr = family == System.Net.Sockets.AddressFamily.InterNetwork ? 32 : 128; - if (cidr > maxCidr) - { - if (tryParse == false) - { - throw new ArgumentOutOfRangeException(nameof(cidr)); - } - netmask = null; - return; - } - - var mask = IPNetwork.ToUint(cidr, family); - var netmask2 = IPNetwork.ToIPAddress(mask, family); - netmask = netmask2; - - return; - } - - #endregion - - #endregion - - #region utils - - #region BitsSet - - /// - /// Count bits set to 1 in netmask - /// - /// - /// - /// - private static byte BitsSet(BigInteger netmask, AddressFamily family) - { - - string s = netmask.ToBinaryString(); - return (byte)s.Replace("0", "") - .ToCharArray() - .Length; - - } - - - /// - /// Count bits set to 1 in netmask - /// - /// - /// - public static uint BitsSet(IPAddress netmask) - { - var uintNetmask = IPNetwork.ToBigInteger(netmask); - uint bits = IPNetwork.BitsSet(uintNetmask, netmask.AddressFamily); - return bits; - } - - #endregion - - #region ValidNetmask - - /// - /// return true if netmask is a valid netmask - /// 255.255.255.0, 255.0.0.0, 255.255.240.0, ... - /// - /// - /// - /// - public static bool ValidNetmask(IPAddress netmask) - { - - if (netmask == null) - { - throw new ArgumentNullException(nameof(netmask)); - } - var uintNetmask = IPNetwork.ToBigInteger(netmask); - bool valid = IPNetwork.InternalValidNetmask(uintNetmask, netmask.AddressFamily); - return valid; - } - -#if TRAVISCI - public -#else - internal -#endif - static bool InternalValidNetmask(BigInteger netmask, AddressFamily family) - { - - if (family != AddressFamily.InterNetwork - && family != AddressFamily.InterNetworkV6) - { - throw new ArgumentException("family"); - } - - var mask = family == AddressFamily.InterNetwork - ? new BigInteger(0x0ffffffff) - : new BigInteger(new byte[]{ - 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, - 0x00 - }); - - var neg = ((~netmask) & (mask)); - bool isNetmask = ((neg + 1) & neg) == 0; - return isNetmask; - - } - - #endregion - - #region ToIPAddress - - /// - /// Transform a uint ipaddress into IPAddress object - /// - /// - /// - public static IPAddress ToIPAddress(BigInteger ipaddress, AddressFamily family) - { - - int width = family == AddressFamily.InterNetwork ? 4 : 16; - byte[] bytes = ipaddress.ToByteArray(); - byte[] bytes2 = new byte[width]; - int copy = bytes.Length > width ? width : bytes.Length; - Array.Copy(bytes, 0, bytes2, 0, copy); - Array.Reverse(bytes2); - - byte[] sized = Resize(bytes2, family); - var ip = new IPAddress(sized); - return ip; - } - -#if TRAVISCI - public -#else - internal -#endif - static byte[] Resize(byte[] bytes, AddressFamily family) - { - - if (family != AddressFamily.InterNetwork - && family != AddressFamily.InterNetworkV6) - { - throw new ArgumentException("family"); - } - - int width = family == AddressFamily.InterNetwork ? 4 : 16; - - if (bytes.Length > width) - { - throw new ArgumentException("bytes"); - } - - byte[] result = new byte[width]; - Array.Copy(bytes, 0, result, 0, bytes.Length); - return result; - } - - #endregion - - #endregion - - #region contains - - /// - /// return true if ipaddress is contained in network - /// - /// - /// - public bool Contains(IPAddress ipaddress) - { - - if (ipaddress == null) - { - throw new ArgumentNullException(nameof(ipaddress)); - } - - if (AddressFamily != ipaddress.AddressFamily) - { - return false; - } - - var uintNetwork = _network; - var uintBroadcast = _broadcast; - var uintAddress = IPNetwork.ToBigInteger(ipaddress); - - bool contains = (uintAddress >= uintNetwork - && uintAddress <= uintBroadcast); - - return contains; - - } - - /// - /// return true is network2 is fully contained in network - /// - /// - /// - public bool Contains(IPNetwork network2) - { - - if (network2 == null) - { - throw new ArgumentNullException(nameof(network2)); - } - - var uintNetwork = _network; - var uintBroadcast = _broadcast; - - var uintFirst = network2._network; - var uintLast = network2._broadcast; - - bool contains = (uintFirst >= uintNetwork - && uintLast <= uintBroadcast); - - return contains; - } - - #endregion - - #region overlap - - /// - /// return true is network2 overlap network - /// - /// - /// - public bool Overlap(IPNetwork network2) - { - - if (network2 == null) - { - throw new ArgumentNullException(nameof(network2)); - } - - var uintNetwork = _network; - var uintBroadcast = _broadcast; - - var uintFirst = network2._network; - var uintLast = network2._broadcast; - - bool overlap = - (uintFirst >= uintNetwork && uintFirst <= uintBroadcast) - || (uintLast >= uintNetwork && uintLast <= uintBroadcast) - || (uintFirst <= uintNetwork && uintLast >= uintBroadcast) - || (uintFirst >= uintNetwork && uintLast <= uintBroadcast); - - return overlap; - } - - #endregion - - #region ToString - - public override string ToString() - { - return string.Format("{0}/{1}", this.Network, this.Cidr); - } - - #endregion - - #region IANA block - - private static readonly Lazy _iana_ablock_reserved = new Lazy(() => IPNetwork.Parse("10.0.0.0/8")); - private static readonly Lazy _iana_bblock_reserved = new Lazy(() => IPNetwork.Parse("172.16.0.0/12")); - private static readonly Lazy _iana_cblock_reserved = new Lazy(() => IPNetwork.Parse("192.168.0.0/16")); - - /// - /// 10.0.0.0/8 - /// - /// - public static IPNetwork IANA_ABLK_RESERVED1 => _iana_ablock_reserved.Value; - - /// - /// 172.12.0.0/12 - /// - /// - public static IPNetwork IANA_BBLK_RESERVED1 => _iana_bblock_reserved.Value; - - /// - /// 192.168.0.0/16 - /// - /// - public static IPNetwork IANA_CBLK_RESERVED1 => _iana_cblock_reserved.Value; - - /// - /// return true if ipaddress is contained in - /// IANA_ABLK_RESERVED1, IANA_BBLK_RESERVED1, IANA_CBLK_RESERVED1 - /// - /// - /// - public static bool IsIANAReserved(IPAddress ipaddress) - { - - if (ipaddress == null) - { - throw new ArgumentNullException(nameof(ipaddress)); - } - - return IPNetwork.IANA_ABLK_RESERVED1.Contains(ipaddress) - || IPNetwork.IANA_BBLK_RESERVED1.Contains(ipaddress) - || IPNetwork.IANA_CBLK_RESERVED1.Contains(ipaddress); - } - - /// - /// return true if ipnetwork is contained in - /// IANA_ABLK_RESERVED1, IANA_BBLK_RESERVED1, IANA_CBLK_RESERVED1 - /// - /// - public bool IsIANAReserved() - { - return IPNetwork.IANA_ABLK_RESERVED1.Contains(this) - || IPNetwork.IANA_BBLK_RESERVED1.Contains(this) - || IPNetwork.IANA_CBLK_RESERVED1.Contains(this); - } - - #endregion - - #region Subnet - - /// - /// Subnet a network into multiple nets of cidr mask - /// Subnet 192.168.0.0/24 into cidr 25 gives 192.168.0.0/25, 192.168.0.128/25 - /// Subnet 10.0.0.0/8 into cidr 9 gives 10.0.0.0/9, 10.128.0.0/9 - /// - /// - /// - public IPNetworkCollection Subnet(byte cidr) - { - IPNetworkCollection ipnetworkCollection = null; - IPNetwork.InternalSubnet(false, this, cidr, out ipnetworkCollection); - return ipnetworkCollection; - } - - /// - /// Subnet a network into multiple nets of cidr mask - /// Subnet 192.168.0.0/24 into cidr 25 gives 192.168.0.0/25, 192.168.0.128/25 - /// Subnet 10.0.0.0/8 into cidr 9 gives 10.0.0.0/9, 10.128.0.0/9 - /// - /// - /// - public bool TrySubnet(byte cidr, out IPNetworkCollection ipnetworkCollection) - { - IPNetworkCollection inc = null; - IPNetwork.InternalSubnet(true, this, cidr, out inc); - if (inc == null) - { - ipnetworkCollection = null; - return false; - } - - ipnetworkCollection = inc; - return true; - } - -#if TRAVISCI - public -#else - internal -#endif - static void InternalSubnet(bool trySubnet, IPNetwork network, byte cidr, out IPNetworkCollection ipnetworkCollection) - { - - if (network == null) - { - if (trySubnet == false) - { - throw new ArgumentNullException(nameof(network)); - } - ipnetworkCollection = null; - return; - } - - int maxCidr = network._family == System.Net.Sockets.AddressFamily.InterNetwork ? 32 : 128; - if (cidr > maxCidr) - { - if (trySubnet == false) - { - throw new ArgumentOutOfRangeException(nameof(cidr)); - } - ipnetworkCollection = null; - return; - } - - if (cidr < network.Cidr) - { - if (trySubnet == false) - { - throw new ArgumentException("cidr"); - } - ipnetworkCollection = null; - return; - } - - ipnetworkCollection = new IPNetworkCollection(network, cidr); - return; - } - - - - #endregion - - #region Supernet - - /// - /// Supernet two consecutive cidr equal subnet into a single one - /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23 - /// 10.1.0.0/16 + 10.0.0.0/16 = 10.0.0.0/15 - /// 192.168.0.0/24 + 192.168.0.0/25 = 192.168.0.0/24 - /// - /// - /// - public IPNetwork Supernet(IPNetwork network2) - { - IPNetwork supernet = null; - IPNetwork.InternalSupernet(false, this, network2, out supernet); - return supernet; - } - - /// - /// Try to supernet two consecutive cidr equal subnet into a single one - /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23 - /// 10.1.0.0/16 + 10.0.0.0/16 = 10.0.0.0/15 - /// 192.168.0.0/24 + 192.168.0.0/25 = 192.168.0.0/24 - /// - /// - /// - public bool TrySupernet(IPNetwork network2, out IPNetwork supernet) - { - - IPNetwork outSupernet = null; - IPNetwork.InternalSupernet(true, this, network2, out outSupernet); - bool parsed = (outSupernet != null); - supernet = outSupernet; - return parsed; - } - -#if TRAVISCI - public -#else - internal -#endif - static void InternalSupernet(bool trySupernet, IPNetwork network1, IPNetwork network2, out IPNetwork supernet) - { - - if (network1 == null) - { - if (trySupernet == false) - { - throw new ArgumentNullException(nameof(network1)); - } - supernet = null; - return; - } - - if (network2 == null) - { - if (trySupernet == false) - { - throw new ArgumentNullException(nameof(network2)); - } - supernet = null; - return; - } - - - if (network1.Contains(network2)) - { - supernet = new IPNetwork(network1._network, network1._family, network1.Cidr); - return; - } - - if (network2.Contains(network1)) - { - supernet = new IPNetwork(network2._network, network2._family, network2.Cidr); - return; - } - - if (network1._cidr != network2._cidr) - { - if (trySupernet == false) - { - throw new ArgumentException("cidr"); - } - supernet = null; - return; - } - - var first = (network1._network < network2._network) ? network1 : network2; - var last = (network1._network > network2._network) ? network1 : network2; - - /// Starting from here : - /// network1 and network2 have the same cidr, - /// network1 does not contain network2, - /// network2 does not contain network1, - /// first is the lower subnet - /// last is the higher subnet - - - if ((first._broadcast + 1) != last._network) - { - if (trySupernet == false) - { - throw new ArgumentOutOfRangeException(nameof(trySupernet), "TrySupernet was false while the first and last networks are not adjacent."); - } - supernet = null; - return; - } - - var uintSupernet = first._network; - byte cidrSupernet = (byte)(first._cidr - 1); - - var networkSupernet = new IPNetwork(uintSupernet, first._family, cidrSupernet); - if (networkSupernet._network != first._network) - { - if (trySupernet == false) - { - throw new ArgumentException("network"); - } - supernet = null; - return; - } - supernet = networkSupernet; - return; - } - - #endregion - - #region GetHashCode - - public override int GetHashCode() - { - return string.Format("{0}|{1}|{2}", - this._ipaddress.GetHashCode(), - this._network.GetHashCode(), - this._cidr.GetHashCode()).GetHashCode(); - } - - #endregion - - #region SupernetArray - - /// - /// Supernet a list of subnet - /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23 - /// 192.168.0.0/24 + 192.168.1.0/24 + 192.168.2.0/24 + 192.168.3.0/24 = 192.168.0.0/22 - /// - /// The IP networks - /// - public static IPNetwork[] Supernet(IPNetwork[] ipnetworks) - { - InternalSupernet(false, ipnetworks, out var supernet); - return supernet; - } - - /// - /// Supernet a list of subnet - /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23 - /// 192.168.0.0/24 + 192.168.1.0/24 + 192.168.2.0/24 + 192.168.3.0/24 = 192.168.0.0/22 - /// - /// - /// - /// - public static bool TrySupernet(IPNetwork[] ipnetworks, out IPNetwork[] supernet) - { - bool supernetted = InternalSupernet(true, ipnetworks, out supernet); - return supernetted; - - } - -#if TRAVISCI - public -#else - internal -#endif - static bool InternalSupernet(bool trySupernet, IPNetwork[] ipnetworks, out IPNetwork[] supernet) - { - - if (ipnetworks == null) - { - if (trySupernet == false) - { - throw new ArgumentNullException(nameof(ipnetworks)); - } - supernet = null; - return false; - } - - if (ipnetworks.Length <= 0) - { - supernet = new IPNetwork[0]; - return true; - } - - var supernetted = new List(); - var ipns = IPNetwork.Array2List(ipnetworks); - var current = IPNetwork.List2Stack(ipns); - int previousCount = 0; - int currentCount = current.Count; - - while (previousCount != currentCount) - { - - supernetted.Clear(); - while (current.Count > 1) - { - var ipn1 = current.Pop(); - var ipn2 = current.Peek(); - - IPNetwork outNetwork = null; - bool success = ipn1.TrySupernet(ipn2, out outNetwork); - if (success) - { - current.Pop(); - current.Push(outNetwork); - } - else - { - supernetted.Add(ipn1); - } - } - if (current.Count == 1) - { - supernetted.Add(current.Pop()); - } - - previousCount = currentCount; - currentCount = supernetted.Count; - current = IPNetwork.List2Stack(supernetted); - - } - supernet = supernetted.ToArray(); - return true; - } - - private static Stack List2Stack(List list) - { - var stack = new Stack(); - list.ForEach(new Action( - delegate (IPNetwork ipn) - { - stack.Push(ipn); - } - )); - return stack; - } - - private static List Array2List(IPNetwork[] array) - { - var ipns = new List(); - ipns.AddRange(array); - IPNetwork.RemoveNull(ipns); - ipns.Sort(new Comparison( - delegate (IPNetwork ipn1, IPNetwork ipn2) - { - int networkCompare = ipn1._network.CompareTo(ipn2._network); - if (networkCompare == 0) - { - int cidrCompare = ipn1._cidr.CompareTo(ipn2._cidr); - return cidrCompare; - } - return networkCompare; - } - )); - ipns.Reverse(); - - return ipns; - } - - private static void RemoveNull(List ipns) - { - ipns.RemoveAll(new Predicate( - delegate (IPNetwork ipn) - { - if (ipn == null) - { - return true; - } - return false; - } - )); - - } - - #endregion - - #region WideSubnet - - public static IPNetwork WideSubnet(string start, string end) - { - - if (string.IsNullOrEmpty(start)) - { - throw new ArgumentNullException(nameof(start)); - } - - if (string.IsNullOrEmpty(end)) - { - throw new ArgumentNullException(nameof(end)); - } - - if (!IPAddress.TryParse(start, out var startIP)) - { - throw new ArgumentException("start"); - } - - if (!IPAddress.TryParse(end, out var endIP)) - { - throw new ArgumentException("end"); - } - - if (startIP.AddressFamily != endIP.AddressFamily) - { - throw new NotSupportedException("MixedAddressFamily"); - } - - var ipnetwork = new IPNetwork(0, startIP.AddressFamily, 0); - for (byte cidr = 32; cidr >= 0; cidr--) - { - var wideSubnet = IPNetwork.Parse(start, cidr); - if (wideSubnet.Contains(endIP)) - { - ipnetwork = wideSubnet; - break; - } - } - return ipnetwork; - - } - - public static bool TryWideSubnet(IPNetwork[] ipnetworks, out IPNetwork ipnetwork) - { - IPNetwork ipn = null; - IPNetwork.InternalWideSubnet(true, ipnetworks, out ipn); - if (ipn == null) - { - ipnetwork = null; - return false; - } - ipnetwork = ipn; - return true; - } - - public static IPNetwork WideSubnet(IPNetwork[] ipnetworks) - { - IPNetwork ipn = null; - IPNetwork.InternalWideSubnet(false, ipnetworks, out ipn); - return ipn; - } - - internal static void InternalWideSubnet(bool tryWide, IPNetwork[] ipnetworks, out IPNetwork ipnetwork) - { - - if (ipnetworks == null) - { - if (tryWide == false) - { - throw new ArgumentNullException(nameof(ipnetworks)); - } - ipnetwork = null; - return; - } - - - IPNetwork[] nnin = Array.FindAll(ipnetworks, new Predicate( - delegate (IPNetwork ipnet) - { - return ipnet != null; - } - )); - - if (nnin.Length <= 0) - { - if (tryWide == false) - { - throw new ArgumentException("ipnetworks"); - } - ipnetwork = null; - return; - } - - if (nnin.Length == 1) - { - var ipn0 = nnin[0]; - ipnetwork = ipn0; - return; - } - - Array.Sort(nnin); - var nnin0 = nnin[0]; - var uintNnin0 = nnin0._ipaddress; - - var nninX = nnin[nnin.Length - 1]; - var ipaddressX = nninX.Broadcast; - - var family = ipnetworks[0]._family; - foreach (var ipnx in ipnetworks) - { - if (ipnx._family != family) - { - throw new ArgumentException("MixedAddressFamily"); - } - } - - var ipn = new IPNetwork(0, family, 0); - for (byte cidr = nnin0._cidr; cidr >= 0; cidr--) - { - var wideSubnet = new IPNetwork(uintNnin0, family, cidr); - if (wideSubnet.Contains(ipaddressX)) - { - ipn = wideSubnet; - break; - } - } - - ipnetwork = ipn; - return; - } - - #endregion - - #region Print - - /// - /// Print an ipnetwork in a clear representation string - /// - /// - public string Print() - { - - var sw = new StringWriter(); - - sw.WriteLine("IPNetwork : {0}", ToString()); - sw.WriteLine("Network : {0}", Network); - sw.WriteLine("Netmask : {0}", Netmask); - sw.WriteLine("Cidr : {0}", Cidr); - sw.WriteLine("Broadcast : {0}", Broadcast); - sw.WriteLine("FirstUsable : {0}", FirstUsable); - sw.WriteLine("LastUsable : {0}", LastUsable); - sw.WriteLine("Usable : {0}", Usable); - - return sw.ToString(); - } - - #endregion - - #region TryGuessCidr - - /// - /// - /// Class Leading bits Default netmask - /// A (CIDR /8) 00 255.0.0.0 - /// A (CIDR /8) 01 255.0.0.0 - /// B (CIDR /16) 10 255.255.0.0 - /// C (CIDR /24) 11 255.255.255.0 - /// - /// - /// - /// - /// - public static bool TryGuessCidr(string ip, out byte cidr) - { - - IPAddress ipaddress = null; - bool parsed = IPAddress.TryParse(string.Format("{0}", ip), out ipaddress); - if (parsed == false) - { - cidr = 0; - return false; - } - - if (ipaddress.AddressFamily == AddressFamily.InterNetworkV6) - { - cidr = 64; - return true; - } - var uintIPAddress = IPNetwork.ToBigInteger(ipaddress); - uintIPAddress = uintIPAddress >> 29; - if (uintIPAddress <= 3) - { - cidr = 8; - return true; - } - else if (uintIPAddress <= 5) - { - cidr = 16; - return true; - } - else if (uintIPAddress <= 6) - { - cidr = 24; - return true; - } - - cidr = 0; - return false; - - } - - /// - /// Try to parse cidr. Have to be >= 0 and <= 32 or 128 - /// - /// - /// - /// - public static bool TryParseCidr(string sidr, AddressFamily family, out byte? cidr) - { - - byte b = 0; - if (!byte.TryParse(sidr, out b)) - { - cidr = null; - return false; - } - - IPAddress netmask = null; - if (!IPNetwork.TryToNetmask(b, family, out netmask)) - { - cidr = null; - return false; - } - - cidr = b; - return true; - } - - #endregion - - #region ListIPAddress - - public IPAddressCollection ListIPAddress() - { - return new IPAddressCollection(this); - } - - #endregion - - /** - * Need a better way to do it - * -#region TrySubstractNetwork - - public static bool TrySubstractNetwork(IPNetwork[] ipnetworks, IPNetwork substract, out IEnumerable result) { - - if (ipnetworks == null) { - result = null; - return false; - } - if (ipnetworks.Length <= 0) { - result = null; - return false; - } - if (substract == null) { - result = null; - return false; - } - var results = new List(); - foreach (var ipn in ipnetworks) { - if (!Overlap(ipn, substract)) { - results.Add(ipn); - continue; - } - - var collection = ipn.Subnet(substract.Cidr); - var rtemp = new List(); - foreach(var subnet in collection) { - if (subnet != substract) { - rtemp.Add(subnet); - } - } - var supernets = Supernet(rtemp.ToArray()); - results.AddRange(supernets); - } - result = results; - return true; - } -#endregion - * **/ - - #region IComparable Members - - public static int Compare(IPNetwork left, IPNetwork right) - { - // two null IPNetworks are equal - if (ReferenceEquals(left, null) && ReferenceEquals(right, null)) return 0; - - // two same IPNetworks are equal - if (ReferenceEquals(left, right)) return 0; - - // null is always sorted first - if (ReferenceEquals(left, null)) return -1; - if (ReferenceEquals(right, null)) return 1; - - // first test the network - var result = left._network.CompareTo(right._network); - if (result != 0) return result; - - // then test the cidr - result = left._cidr.CompareTo(right._cidr); - return result; - } - - public int CompareTo(IPNetwork other) - { - return Compare(this, other); - } - - public int CompareTo(object obj) - { - // null is at less - if (obj == null) return 1; - - // convert to a proper Cidr object - var other = obj as IPNetwork; - - // type problem if null - if (other == null) - { - throw new ArgumentException( - "The supplied parameter is an invalid type. Please supply an IPNetwork type.", - nameof(obj)); - } - - // perform the comparision - return CompareTo(other); - } - - #endregion - - #region IEquatable Members - - public static bool Equals(IPNetwork left, IPNetwork right) - { - return Compare(left, right) == 0; - } - - public bool Equals(IPNetwork other) - { - return Equals(this, other); - } - - public override bool Equals(object obj) - { - return Equals(this, obj as IPNetwork); - } - - #endregion - - #region Operators - - public static bool operator ==(IPNetwork left, IPNetwork right) - { - return Equals(left, right); - } - - public static bool operator !=(IPNetwork left, IPNetwork right) - { - return !Equals(left, right); - } - - public static bool operator <(IPNetwork left, IPNetwork right) - { - return Compare(left, right) < 0; - } - - public static bool operator >(IPNetwork left, IPNetwork right) - { - return Compare(left, right) > 0; - } - - #endregion - - } -} diff --git a/Emby.Server.Implementations/Networking/IPNetwork/IPNetworkCollection.cs b/Emby.Server.Implementations/Networking/IPNetwork/IPNetworkCollection.cs deleted file mode 100644 index 4cda421e5..000000000 --- a/Emby.Server.Implementations/Networking/IPNetwork/IPNetworkCollection.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Numerics; - -namespace Emby.Server.Implementations.Networking.IPNetwork -{ - public class IPNetworkCollection : IEnumerable, IEnumerator - { - - private BigInteger _enumerator; - private byte _cidrSubnet; - private IPNetwork _ipnetwork; - - private byte _cidr => this._ipnetwork.Cidr; - - private BigInteger _broadcast => IPNetwork.ToBigInteger(this._ipnetwork.Broadcast); - - private BigInteger _lastUsable => IPNetwork.ToBigInteger(this._ipnetwork.LastUsable); - private BigInteger _network => IPNetwork.ToBigInteger(this._ipnetwork.Network); -#if TRAVISCI - public -#else - internal -#endif - IPNetworkCollection(IPNetwork ipnetwork, byte cidrSubnet) - { - - int maxCidr = ipnetwork.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork ? 32 : 128; - if (cidrSubnet > maxCidr) - { - throw new ArgumentOutOfRangeException(nameof(cidrSubnet)); - } - - if (cidrSubnet < ipnetwork.Cidr) - { - throw new ArgumentException("cidr"); - } - - this._cidrSubnet = cidrSubnet; - this._ipnetwork = ipnetwork; - this._enumerator = -1; - } - - #region Count, Array, Enumerator - - public BigInteger Count - { - get - { - var count = BigInteger.Pow(2, this._cidrSubnet - this._cidr); - return count; - } - } - - public IPNetwork this[BigInteger i] - { - get - { - if (i >= this.Count) - { - throw new ArgumentOutOfRangeException(nameof(i)); - } - - var last = this._ipnetwork.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6 - ? this._lastUsable : this._broadcast; - var increment = (last - this._network) / this.Count; - var uintNetwork = this._network + ((increment + 1) * i); - var ipn = new IPNetwork(uintNetwork, this._ipnetwork.AddressFamily, this._cidrSubnet); - return ipn; - } - } - - #endregion - - #region IEnumerable Members - - IEnumerator IEnumerable.GetEnumerator() - { - return this; - } - - IEnumerator IEnumerable.GetEnumerator() - { - return this; - } - - #region IEnumerator Members - - public IPNetwork Current => this[this._enumerator]; - - #endregion - - #region IDisposable Members - - public void Dispose() - { - // nothing to dispose - return; - } - - #endregion - - #region IEnumerator Members - - object IEnumerator.Current => this.Current; - - public bool MoveNext() - { - this._enumerator++; - if (this._enumerator >= this.Count) - { - return false; - } - return true; - - } - - public void Reset() - { - this._enumerator = -1; - } - - #endregion - - #endregion - - } -} diff --git a/Emby.Server.Implementations/Networking/IPNetwork/LICENSE.txt b/Emby.Server.Implementations/Networking/IPNetwork/LICENSE.txt deleted file mode 100644 index 45d7392ac..000000000 --- a/Emby.Server.Implementations/Networking/IPNetwork/LICENSE.txt +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2015, lduchosal -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index 3cacacf07..82add242a 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -258,7 +258,7 @@ namespace Emby.Server.Implementations.Networking if (normalizedSubnet.IndexOf('/') != -1) { - var ipnetwork = IPNetwork.IPNetwork.Parse(normalizedSubnet); + var ipnetwork = IPNetwork.Parse(normalizedSubnet); if (ipnetwork.Contains(address)) { return true; -- cgit v1.2.3 From 0e6417c9fa04ea58638a1594245365da6be6f8c1 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Sat, 3 Aug 2019 12:37:02 +0200 Subject: Set log level to debug for HTTP range requests --- Emby.Server.Implementations/HttpServer/FileWriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs index c6b7d31a8..ec41cc0a9 100644 --- a/Emby.Server.Implementations/HttpServer/FileWriter.cs +++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs @@ -106,7 +106,7 @@ namespace Emby.Server.Implementations.HttpServer var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}"; Headers[HeaderNames.ContentRange] = rangeString; - Logger.LogInformation("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString); + Logger.LogDebug("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString); } /// -- cgit v1.2.3 From b637cdabaef9703063fed23719bcac5a1709373e Mon Sep 17 00:00:00 2001 From: Michał Date: Fri, 2 Aug 2019 15:32:42 +0000 Subject: Translated using Weblate (Polish) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pl/ --- .../Localization/Core/pl.json | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json index 92b12409d..e72f1a262 100644 --- a/Emby.Server.Implementations/Localization/Core/pl.json +++ b/Emby.Server.Implementations/Localization/Core/pl.json @@ -18,11 +18,11 @@ "HeaderAlbumArtists": "Wykonawcy albumów", "HeaderCameraUploads": "Przekazane obrazy", "HeaderContinueWatching": "Kontynuuj odtwarzanie", - "HeaderFavoriteAlbums": "Albumy ulubione", - "HeaderFavoriteArtists": "Wykonawcy ulubieni", - "HeaderFavoriteEpisodes": "Odcinki ulubione", - "HeaderFavoriteShows": "Seriale ulubione", - "HeaderFavoriteSongs": "Utwory ulubione", + "HeaderFavoriteAlbums": "Ulubione albumy", + "HeaderFavoriteArtists": "Ulubieni wykonawcy", + "HeaderFavoriteEpisodes": "Ulubione odcinki", + "HeaderFavoriteShows": "Ulubione seriale", + "HeaderFavoriteSongs": "Ulubione utwory", "HeaderLiveTV": "Telewizja", "HeaderNextUp": "Do obejrzenia", "HeaderRecordingGroups": "Grupy nagrań", @@ -41,26 +41,26 @@ "Movies": "Filmy", "Music": "Muzyka", "MusicVideos": "Teledyski", - "NameInstallFailed": "Instalacja {0} nieudana.", + "NameInstallFailed": "Instalacja {0} nieudana", "NameSeasonNumber": "Sezon {0}", - "NameSeasonUnknown": "Sezon nieznany", + "NameSeasonUnknown": "Nieznany sezon", "NewVersionIsAvailable": "Nowa wersja serwera Jellyfin jest dostępna do pobrania.", "NotificationOptionApplicationUpdateAvailable": "Dostępna aktualizacja aplikacji", - "NotificationOptionApplicationUpdateInstalled": "Zainstalowano aktualizację aplikacji", + "NotificationOptionApplicationUpdateInstalled": "Zaktualizowano aplikację", "NotificationOptionAudioPlayback": "Rozpoczęto odtwarzanie muzyki", "NotificationOptionAudioPlaybackStopped": "Odtwarzane dźwięku zatrzymane", - "NotificationOptionCameraImageUploaded": "Przekazano obraz z urządzenia mobilnego", - "NotificationOptionInstallationFailed": "Niepowodzenie instalacji", + "NotificationOptionCameraImageUploaded": "Przekazano obraz z urządzenia przenośnego", + "NotificationOptionInstallationFailed": "Nieudana instalacja", "NotificationOptionNewLibraryContent": "Dodano nową zawartość", "NotificationOptionPluginError": "Awaria wtyczki", "NotificationOptionPluginInstalled": "Zainstalowano wtyczkę", "NotificationOptionPluginUninstalled": "Odinstalowano wtyczkę", - "NotificationOptionPluginUpdateInstalled": "Zainstalowano aktualizację wtyczki", + "NotificationOptionPluginUpdateInstalled": "Zaktualizowano wtyczkę", "NotificationOptionServerRestartRequired": "Wymagane ponowne uruchomienie serwera", "NotificationOptionTaskFailed": "Awaria zaplanowanego zadania", "NotificationOptionUserLockedOut": "Użytkownik zablokowany", "NotificationOptionVideoPlayback": "Rozpoczęto odtwarzanie wideo", - "NotificationOptionVideoPlaybackStopped": "Odtwarzanie wideo zatrzymane", + "NotificationOptionVideoPlaybackStopped": "Zatrzymano odtwarzanie wideo", "Photos": "Zdjęcia", "Playlists": "Listy odtwarzania", "Plugin": "Wtyczka", @@ -73,7 +73,7 @@ "ServerNameNeedsToBeRestarted": "{0} wymaga ponownego uruchomienia", "Shows": "Seriale", "Songs": "Utwory", - "StartupEmbyServerIsLoading": "Twa wczytywanie serwera Jellyfin. Spróbuj ponownie za chwilę.", + "StartupEmbyServerIsLoading": "Trwa wczytywanie serwera Jellyfin. Spróbuj ponownie za chwilę.", "SubtitleDownloadFailureForItem": "Pobieranie napisów dla {0} zakończone niepowodzeniem", "SubtitleDownloadFailureFromForItem": "Nieudane pobieranie napisów z {0} dla {1}", "SubtitlesDownloadedForItem": "Pobrano napisy dla {0}", @@ -91,7 +91,7 @@ "UserPolicyUpdatedWithName": "Zmieniono zasady użytkowania dla {0}", "UserStartedPlayingItemWithValues": "{0} odtwarza {1} na {2}", "UserStoppedPlayingItemWithValues": "{0} zakończył odtwarzanie {1} na {2}", - "ValueHasBeenAddedToLibrary": "{0} został dodany to biblioteki mediów", + "ValueHasBeenAddedToLibrary": "{0} został dodany do biblioteki mediów", "ValueSpecialEpisodeName": "Specjalne - {0}", "VersionNumber": "Wersja {0}" } -- cgit v1.2.3 From 20cbbd4f4c99d9a603756f83e0132caa71c16c13 Mon Sep 17 00:00:00 2001 From: AndersMachmueller Date: Sun, 4 Aug 2019 19:18:50 +0000 Subject: Translated using Weblate (Danish) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/da/ --- Emby.Server.Implementations/Localization/Core/da.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index cb8f4576a..b01abafa1 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -18,11 +18,11 @@ "HeaderAlbumArtists": "Albumkunstnere", "HeaderCameraUploads": "Kamera Uploads", "HeaderContinueWatching": "Fortsæt Afspilning", - "HeaderFavoriteAlbums": "Favoritalbum", + "HeaderFavoriteAlbums": "Favoritalbummer", "HeaderFavoriteArtists": "Favoritkunstnere", - "HeaderFavoriteEpisodes": "Favorit-afsnit", - "HeaderFavoriteShows": "Favorit-serier", - "HeaderFavoriteSongs": "Favorit-sange", + "HeaderFavoriteEpisodes": "Favoritepisoder", + "HeaderFavoriteShows": "Favoritserier", + "HeaderFavoriteSongs": "Favoritsange", "HeaderLiveTV": "Live TV", "HeaderNextUp": "Næste", "HeaderRecordingGroups": "Optagelsesgrupper", -- cgit v1.2.3 From 5e6e52d3977c679871f5633c59d82cbb3f6c2cce Mon Sep 17 00:00:00 2001 From: Tamás Mogyorósi Date: Thu, 25 Jul 2019 13:56:40 +0000 Subject: Translated using Weblate (Hungarian) Currently translated at 100.0% (94 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hu/ --- Emby.Server.Implementations/Localization/Core/hu.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json index c0f988abe..a2fc126a2 100644 --- a/Emby.Server.Implementations/Localization/Core/hu.json +++ b/Emby.Server.Implementations/Localization/Core/hu.json @@ -19,9 +19,9 @@ "HeaderCameraUploads": "Kamera feltöltések", "HeaderContinueWatching": "Folyamatban lévő filmek", "HeaderFavoriteAlbums": "Kedvenc Albumok", - "HeaderFavoriteArtists": "Kedvenc Művészek", + "HeaderFavoriteArtists": "Kedvenc Előadók", "HeaderFavoriteEpisodes": "Kedvenc Epizódok", - "HeaderFavoriteShows": "Kedvenc Műsorok", + "HeaderFavoriteShows": "Kedvenc Sorozatok", "HeaderFavoriteSongs": "Kedvenc Dalok", "HeaderLiveTV": "Élő TV", "HeaderNextUp": "Következik", -- cgit v1.2.3 From 89f592687ee7ae7f0e0fffd884dbf2890476410a Mon Sep 17 00:00:00 2001 From: Tradutor da Silva Date: Tue, 23 Jul 2019 02:28:08 +0000 Subject: Translated using Weblate (Portuguese (Brazil)) Currently translated at 95.7% (90 of 94 strings) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_BR/ --- Emby.Server.Implementations/Localization/Core/pt-BR.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json index 5d68416e9..c4ce16dc8 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-BR.json +++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json @@ -21,8 +21,8 @@ "HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteArtists": "Artistas Favoritos", "HeaderFavoriteEpisodes": "Episódios Favoritos", - "HeaderFavoriteShows": "Shows Favoritos", - "HeaderFavoriteSongs": "Musicas Favoritas", + "HeaderFavoriteShows": "Séries Favoritas", + "HeaderFavoriteSongs": "Músicas Favoritas", "HeaderLiveTV": "TV ao Vivo", "HeaderNextUp": "Próximos", "HeaderRecordingGroups": "Grupos de Gravação", @@ -32,19 +32,19 @@ "ItemRemovedWithName": "{0} foi removido da biblioteca", "LabelIpAddressValue": "Endereço IP: {0}", "LabelRunningTimeValue": "Tempo de execução: {0}", - "Latest": "Recente", - "MessageApplicationUpdated": "O servidor Jellyfin foi atualizado", - "MessageApplicationUpdatedTo": "O Servidor Jellyfin foi atualizado para {0}", + "Latest": "Recentes", + "MessageApplicationUpdated": "Servidor Jellyfin atualizado", + "MessageApplicationUpdatedTo": "Servidor Jellyfin atualizado para {0}", "MessageNamedServerConfigurationUpdatedWithValue": "A seção {0} da configuração do servidor foi atualizada", "MessageServerConfigurationUpdated": "A configuração do servidor foi atualizada", "MixedContent": "Conteúdo misto", "Movies": "Filmes", "Music": "Música", - "MusicVideos": "Vídeos musicais", + "MusicVideos": "Clipes", "NameInstallFailed": "A instalação de {0} falhou", "NameSeasonNumber": "Temporada {0}", "NameSeasonUnknown": "Temporada Desconhecida", - "NewVersionIsAvailable": "Uma nova versão do servidor Jellyfin está disponível para download.", + "NewVersionIsAvailable": "Uma nova versão do Servidor Jellyfin está disponível para download.", "NotificationOptionApplicationUpdateAvailable": "Atualização de aplicativo disponível", "NotificationOptionApplicationUpdateInstalled": "Atualização de aplicativo instalada", "NotificationOptionAudioPlayback": "Reprodução de áudio iniciada", -- cgit v1.2.3 From 9fff4b060e06569ca77636643901aa42767e318d Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 28 Jul 2019 23:53:19 +0200 Subject: Replace custom code with Asp.Net Core code --- Emby.Server.Implementations/ApplicationHost.cs | 6 +- .../HttpServer/FileWriter.cs | 174 +++--- .../HttpServer/HttpListenerHost.cs | 154 ++--- .../HttpServer/ResponseFilter.cs | 26 +- .../HttpServer/Security/AuthService.cs | 48 +- Emby.Server.Implementations/Services/HttpResult.cs | 9 +- .../Services/ResponseHelper.cs | 31 +- .../Services/ServiceController.cs | 4 - .../Services/ServiceExec.cs | 2 +- .../Services/ServiceHandler.cs | 103 ++-- .../SocketSharp/RequestMono.cs | 647 --------------------- .../SocketSharp/WebSocketSharpRequest.cs | 230 +++----- .../SocketSharp/WebSocketSharpResponse.cs | 98 ---- MediaBrowser.Api/Playback/BaseStreamingService.cs | 2 +- .../Net/AuthenticatedAttribute.cs | 3 +- MediaBrowser.Model/Services/IHasRequestFilter.cs | 4 +- MediaBrowser.Model/Services/IRequest.cs | 30 +- 17 files changed, 370 insertions(+), 1201 deletions(-) delete mode 100644 Emby.Server.Implementations/SocketSharp/RequestMono.cs delete mode 100644 Emby.Server.Implementations/SocketSharp/WebSocketSharpResponse.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ef2f59d30..a89cf95d3 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -676,7 +676,7 @@ namespace Emby.Server.Implementations var localPath = context.Request.Path.ToString(); var req = new WebSocketSharpRequest(request, response, request.Path, Logger); - await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, CancellationToken.None).ConfigureAwait(false); + await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false); } public static IStreamHelper StreamHelper { get; set; } @@ -785,7 +785,7 @@ namespace Emby.Server.Implementations HttpServer = new HttpListenerHost( this, - LoggerFactory, + LoggerFactory.CreateLogger(), ServerConfigurationManager, _configuration, NetworkManager, @@ -873,7 +873,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(authContext); serviceCollection.AddSingleton(new SessionContext(UserManager, authContext, SessionManager)); - AuthService = new AuthService(UserManager, authContext, ServerConfigurationManager, SessionManager, NetworkManager); + AuthService = new AuthService(authContext, ServerConfigurationManager, SessionManager, NetworkManager); serviceCollection.AddSingleton(AuthService); SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(LibraryManager, LoggerFactory, ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, ProcessFactory); diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs index ec41cc0a9..2890cca7c 100644 --- a/Emby.Server.Implementations/HttpServer/FileWriter.cs +++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs @@ -1,50 +1,43 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Net; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using Emby.Server.Implementations.IO; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.Http; using Microsoft.Net.Http.Headers; namespace Emby.Server.Implementations.HttpServer { public class FileWriter : IHttpResult { - private readonly IStreamHelper _streamHelper; - private ILogger Logger { get; set; } - private readonly IFileSystem _fileSystem; - - private string RangeHeader { get; set; } - private bool IsHeadRequest { get; set; } - - private long RangeStart { get; set; } - private long RangeEnd { get; set; } - private long RangeLength { get; set; } - public long TotalContentLength { get; set; } + private static readonly CultureInfo UsCulture = CultureInfo.ReadOnly(new CultureInfo("en-US")); - public Action OnComplete { get; set; } - public Action OnError { get; set; } - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public List Cookies { get; private set; } + private static readonly string[] _skipLogExtensions = { + ".js", + ".html", + ".css" + }; - public FileShareMode FileShare { get; set; } + private readonly IStreamHelper _streamHelper; + private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; /// /// The _options /// private readonly IDictionary _options = new Dictionary(); + /// - /// Gets the options. + /// The _requested ranges /// - /// The options. - public IDictionary Headers => _options; - - public string Path { get; set; } + private List> _requestedRanges; public FileWriter(string path, string contentType, string rangeHeader, ILogger logger, IFileSystem fileSystem, IStreamHelper streamHelper) { @@ -57,7 +50,7 @@ namespace Emby.Server.Implementations.HttpServer _fileSystem = fileSystem; Path = path; - Logger = logger; + _logger = logger; RangeHeader = rangeHeader; Headers[HeaderNames.ContentType] = contentType; @@ -80,39 +73,34 @@ namespace Emby.Server.Implementations.HttpServer Cookies = new List(); } - /// - /// Sets the range values. - /// - private void SetRangeValues() - { - var requestedRange = RequestedRanges[0]; + private string RangeHeader { get; set; } - // If the requested range is "0-", we can optimize by just doing a stream copy - if (!requestedRange.Value.HasValue) - { - RangeEnd = TotalContentLength - 1; - } - else - { - RangeEnd = requestedRange.Value.Value; - } + private bool IsHeadRequest { get; set; } - RangeStart = requestedRange.Key; - RangeLength = 1 + RangeEnd - RangeStart; + private long RangeStart { get; set; } - // Content-Length is the length of what we're serving, not the original content - var lengthString = RangeLength.ToString(CultureInfo.InvariantCulture); - Headers[HeaderNames.ContentLength] = lengthString; - var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}"; - Headers[HeaderNames.ContentRange] = rangeString; + private long RangeEnd { get; set; } - Logger.LogDebug("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString); - } + private long RangeLength { get; set; } + + public long TotalContentLength { get; set; } + + public Action OnComplete { get; set; } + + public Action OnError { get; set; } + + public List Cookies { get; private set; } + + public FileShareMode FileShare { get; set; } /// - /// The _requested ranges + /// Gets the options. /// - private List> _requestedRanges; + /// The options. + public IDictionary Headers => _options; + + public string Path { get; set; } + /// /// Gets the requested ranges. /// @@ -139,6 +127,7 @@ namespace Emby.Server.Implementations.HttpServer { start = long.Parse(vals[0], UsCulture); } + if (!string.IsNullOrEmpty(vals[1])) { end = long.Parse(vals[1], UsCulture); @@ -152,13 +141,50 @@ namespace Emby.Server.Implementations.HttpServer } } - private static readonly string[] SkipLogExtensions = { - ".js", - ".html", - ".css" - }; + public string ContentType { get; set; } + + public IRequest RequestContext { get; set; } + + public object Response { get; set; } + + public int Status { get; set; } + + public HttpStatusCode StatusCode + { + get => (HttpStatusCode)Status; + set => Status = (int)value; + } + + /// + /// Sets the range values. + /// + private void SetRangeValues() + { + var requestedRange = RequestedRanges[0]; + + // If the requested range is "0-", we can optimize by just doing a stream copy + if (!requestedRange.Value.HasValue) + { + RangeEnd = TotalContentLength - 1; + } + else + { + RangeEnd = requestedRange.Value.Value; + } + + RangeStart = requestedRange.Key; + RangeLength = 1 + RangeEnd - RangeStart; + + // Content-Length is the length of what we're serving, not the original content + var lengthString = RangeLength.ToString(CultureInfo.InvariantCulture); + Headers[HeaderNames.ContentLength] = lengthString; + var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}"; + Headers[HeaderNames.ContentRange] = rangeString; - public async Task WriteToAsync(IResponse response, CancellationToken cancellationToken) + _logger.LogInformation("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString); + } + + public async Task WriteToAsync(HttpResponse response, CancellationToken cancellationToken) { try { @@ -176,16 +202,16 @@ namespace Emby.Server.Implementations.HttpServer { var extension = System.IO.Path.GetExtension(path); - if (extension == null || !SkipLogExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) + if (extension == null || !_skipLogExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { - Logger.LogDebug("Transmit file {0}", path); + _logger.LogDebug("Transmit file {0}", path); } offset = 0; count = 0; } - await response.TransmitFile(path, offset, count, FileShare, _fileSystem, _streamHelper, cancellationToken).ConfigureAwait(false); + await TransmitFile(response.Body, path, offset, count, FileShare, cancellationToken).ConfigureAwait(false); } finally { @@ -193,18 +219,32 @@ namespace Emby.Server.Implementations.HttpServer } } - public string ContentType { get; set; } - - public IRequest RequestContext { get; set; } + public async Task TransmitFile(Stream stream, string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) + { + var fileOpenOptions = FileOpenOptions.SequentialScan; - public object Response { get; set; } + // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + fileOpenOptions |= FileOpenOptions.Asynchronous; + } - public int Status { get; set; } + using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, fileOpenOptions)) + { + if (offset > 0) + { + fs.Position = offset; + } - public HttpStatusCode StatusCode - { - get => (HttpStatusCode)Status; - set => Status = (int)value; + if (count > 0) + { + await _streamHelper.CopyToAsync(fs, stream, count, cancellationToken).ConfigureAwait(false); + } + else + { + await fs.CopyToAsync(stream, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false); + } + } } } } diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index d8938964f..4c233456c 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -5,7 +5,6 @@ using System.IO; using System.Linq; using System.Net.Sockets; using System.Reflection; -using System.Text; using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.Net; @@ -30,11 +29,7 @@ namespace Emby.Server.Implementations.HttpServer { public class HttpListenerHost : IHttpServer, IDisposable { - private string DefaultRedirectPath { get; set; } - public string[] UrlPrefixes { get; private set; } - - public event EventHandler> WebSocketConnected; - + private readonly ILogger _logger; private readonly IServerConfigurationManager _config; private readonly INetworkManager _networkManager; private readonly IServerApplicationHost _appHost; @@ -42,18 +37,15 @@ namespace Emby.Server.Implementations.HttpServer private readonly IXmlSerializer _xmlSerializer; private readonly IHttpListener _socketListener; private readonly Func> _funcParseFn; - - public Action[] ResponseFilters { get; set; } - + private readonly string _defaultRedirectPath; private readonly Dictionary ServiceOperationsMap = new Dictionary(); - public static HttpListenerHost Instance { get; protected set; } - private IWebSocketListener[] _webSocketListeners = Array.Empty(); private readonly List _webSocketConnections = new List(); + private bool _disposed = false; public HttpListenerHost( IServerApplicationHost applicationHost, - ILoggerFactory loggerFactory, + ILogger logger, IServerConfigurationManager config, IConfiguration configuration, INetworkManager networkManager, @@ -62,9 +54,9 @@ namespace Emby.Server.Implementations.HttpServer IHttpListener socketListener) { _appHost = applicationHost; - Logger = loggerFactory.CreateLogger("HttpServer"); + _logger = logger; _config = config; - DefaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"]; + _defaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"]; _networkManager = networkManager; _jsonSerializer = jsonSerializer; _xmlSerializer = xmlSerializer; @@ -74,12 +66,20 @@ namespace Emby.Server.Implementations.HttpServer _funcParseFn = t => s => JsvReader.GetParseFn(t)(s); Instance = this; - ResponseFilters = Array.Empty>(); + ResponseFilters = Array.Empty>(); } + public Action[] ResponseFilters { get; set; } + + public static HttpListenerHost Instance { get; protected set; } + + public string[] UrlPrefixes { get; private set; } + public string GlobalResponse { get; set; } - protected ILogger Logger { get; } + public ServiceController ServiceController { get; private set; } + + public event EventHandler> WebSocketConnected; public object CreateInstance(Type type) { @@ -91,7 +91,7 @@ namespace Emby.Server.Implementations.HttpServer /// and no more processing should be done. /// /// - public void ApplyRequestFilters(IRequest req, IResponse res, object requestDto) + public void ApplyRequestFilters(IRequest req, HttpResponse res, object requestDto) { //Exec all RequestFilter attributes with Priority < 0 var attributes = GetRequestFilterAttributes(requestDto.GetType()); @@ -145,7 +145,7 @@ namespace Emby.Server.Implementations.HttpServer return; } - var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, Logger) + var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger) { OnReceive = ProcessWebSocketMessageReceived, Url = e.Url, @@ -215,16 +215,16 @@ namespace Emby.Server.Implementations.HttpServer if (logExceptionStackTrace) { - Logger.LogError(ex, "Error processing request"); + _logger.LogError(ex, "Error processing request"); } else if (logExceptionMessage) { - Logger.LogError(ex.Message); + _logger.LogError(ex.Message); } var httpRes = httpReq.Response; - if (httpRes.OriginalResponse.HasStarted) + if (httpRes.HasStarted) { return; } @@ -233,11 +233,11 @@ namespace Emby.Server.Implementations.HttpServer httpRes.StatusCode = statusCode; httpRes.ContentType = "text/html"; - await Write(httpRes, NormalizeExceptionMessage(ex.Message)).ConfigureAwait(false); + await httpRes.WriteAsync(NormalizeExceptionMessage(ex.Message)).ConfigureAwait(false); } catch (Exception errorEx) { - Logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response)"); + _logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response)"); } } @@ -431,7 +431,7 @@ namespace Emby.Server.Implementations.HttpServer { httpRes.StatusCode = 503; httpRes.ContentType = "text/plain"; - await Write(httpRes, "Server shutting down").ConfigureAwait(false); + await httpRes.WriteAsync("Server shutting down", cancellationToken).ConfigureAwait(false); return; } @@ -439,7 +439,7 @@ namespace Emby.Server.Implementations.HttpServer { httpRes.StatusCode = 400; httpRes.ContentType = "text/plain"; - await Write(httpRes, "Invalid host").ConfigureAwait(false); + await httpRes.WriteAsync("Invalid host", cancellationToken).ConfigureAwait(false); return; } @@ -447,7 +447,7 @@ namespace Emby.Server.Implementations.HttpServer { httpRes.StatusCode = 403; httpRes.ContentType = "text/plain"; - await Write(httpRes, "Forbidden").ConfigureAwait(false); + await httpRes.WriteAsync("Forbidden", cancellationToken).ConfigureAwait(false); return; } @@ -460,28 +460,27 @@ namespace Emby.Server.Implementations.HttpServer if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) { httpRes.StatusCode = 200; - httpRes.AddHeader("Access-Control-Allow-Origin", "*"); - httpRes.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); - httpRes.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization"); + httpRes.Headers.Add("Access-Control-Allow-Origin", "*"); + httpRes.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); + httpRes.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization"); httpRes.ContentType = "text/plain"; - await Write(httpRes, string.Empty).ConfigureAwait(false); + await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false); return; } urlToLog = GetUrlToLog(urlString); - Logger.LogDebug("HTTP {HttpMethod} {Url} UserAgent: {UserAgent} \nHeaders: {@Headers}", urlToLog, httpReq.UserAgent ?? string.Empty, httpReq.HttpMethod, httpReq.Headers); if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) || string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase)) { - RedirectToUrl(httpRes, DefaultRedirectPath); + httpRes.Redirect(_defaultRedirectPath); return; } if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) || string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase)) { - RedirectToUrl(httpRes, "emby/" + DefaultRedirectPath); + httpRes.Redirect("emby/" + _defaultRedirectPath); return; } @@ -494,9 +493,10 @@ namespace Emby.Server.Implementations.HttpServer if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) { - await Write(httpRes, + await httpRes.WriteAsync( "EmbyPlease update your Emby bookmark to " + newUrl + "").ConfigureAwait(false); + newUrl + "\">" + newUrl + "", + cancellationToken).ConfigureAwait(false); return; } } @@ -511,34 +511,35 @@ namespace Emby.Server.Implementations.HttpServer if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) { - await Write(httpRes, + await httpRes.WriteAsync( "EmbyPlease update your Emby bookmark to " + newUrl + "").ConfigureAwait(false); + newUrl + "\">" + newUrl + "", + cancellationToken).ConfigureAwait(false); return; } } if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase)) { - RedirectToUrl(httpRes, DefaultRedirectPath); + httpRes.Redirect(_defaultRedirectPath); return; } if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase)) { - RedirectToUrl(httpRes, "../" + DefaultRedirectPath); + httpRes.Redirect("../" + _defaultRedirectPath); return; } if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)) { - RedirectToUrl(httpRes, DefaultRedirectPath); + httpRes.Redirect(_defaultRedirectPath); return; } if (string.IsNullOrEmpty(localPath)) { - RedirectToUrl(httpRes, "/" + DefaultRedirectPath); + httpRes.Redirect("/" + _defaultRedirectPath); return; } @@ -546,12 +547,12 @@ namespace Emby.Server.Implementations.HttpServer { if (localPath.EndsWith("web/dashboard.html", StringComparison.OrdinalIgnoreCase)) { - RedirectToUrl(httpRes, "index.html#!/dashboard.html"); + httpRes.Redirect("index.html#!/dashboard.html"); } if (localPath.EndsWith("web/home.html", StringComparison.OrdinalIgnoreCase)) { - RedirectToUrl(httpRes, "index.html"); + httpRes.Redirect("index.html"); } } @@ -562,7 +563,7 @@ namespace Emby.Server.Implementations.HttpServer { httpRes.StatusCode = 503; httpRes.ContentType = "text/html"; - await Write(httpRes, GlobalResponse).ConfigureAwait(false); + await httpRes.WriteAsync(GlobalResponse, cancellationToken).ConfigureAwait(false); return; } } @@ -571,7 +572,7 @@ namespace Emby.Server.Implementations.HttpServer if (handler != null) { - await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, cancellationToken).ConfigureAwait(false); + await handler.ProcessRequestAsync(this, httpReq, httpRes, _logger, cancellationToken).ConfigureAwait(false); } else { @@ -598,11 +599,7 @@ namespace Emby.Server.Implementations.HttpServer var elapsed = stopWatch.Elapsed; if (elapsed.TotalMilliseconds > 500) { - Logger.LogWarning("HTTP Response {StatusCode} to {RemoteIp}. Time (slow): {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog); - } - else - { - Logger.LogDebug("HTTP Response {StatusCode} to {RemoteIp}. Time: {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog); + _logger.LogWarning("HTTP Response {StatusCode} to {RemoteIp}. Time (slow): {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog); } } } @@ -619,18 +616,11 @@ namespace Emby.Server.Implementations.HttpServer return new ServiceHandler(restPath, contentType); } - Logger.LogError("Could not find handler for {PathInfo}", pathInfo); + _logger.LogError("Could not find handler for {PathInfo}", pathInfo); return null; } - private static Task Write(IResponse response, string text) - { - var bOutput = Encoding.UTF8.GetBytes(text); - response.OriginalResponse.ContentLength = bOutput.Length; - return response.OutputStream.WriteAsync(bOutput, 0, bOutput.Length); - } - - private void RedirectToSecureUrl(IHttpRequest httpReq, IResponse httpRes, string url) + private void RedirectToSecureUrl(IHttpRequest httpReq, HttpResponse httpRes, string url) { if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)) { @@ -640,23 +630,11 @@ namespace Emby.Server.Implementations.HttpServer Scheme = "https" }; url = builder.Uri.ToString(); - - RedirectToUrl(httpRes, url); - } - else - { - RedirectToUrl(httpRes, url); } - } - public static void RedirectToUrl(IResponse httpRes, string url) - { - httpRes.StatusCode = 302; - httpRes.AddHeader("Location", url); + httpRes.Redirect(url); } - public ServiceController ServiceController { get; private set; } - /// /// Adds the rest handlers. /// @@ -672,9 +650,9 @@ namespace Emby.Server.Implementations.HttpServer var types = services.Select(r => r.GetType()); ServiceController.Init(this, types); - ResponseFilters = new Action[] + ResponseFilters = new Action[] { - new ResponseFilter(Logger).FilterResponse + new ResponseFilter(_logger).FilterResponse }; } @@ -772,24 +750,23 @@ namespace Emby.Server.Implementations.HttpServer return "emby/emby/" + path; } - private bool _disposed; - private readonly object _disposeLock = new object(); + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } protected virtual void Dispose(bool disposing) { if (_disposed) return; - lock (_disposeLock) + if (disposing) { - if (_disposed) return; - - _disposed = true; - - if (disposing) - { - Stop(); - } + Stop(); } + + _disposed = true; } /// @@ -803,7 +780,7 @@ namespace Emby.Server.Implementations.HttpServer return Task.CompletedTask; } - Logger.LogDebug("Websocket message received: {0}", result.MessageType); + _logger.LogDebug("Websocket message received: {0}", result.MessageType); IEnumerable GetTasks() { @@ -815,10 +792,5 @@ namespace Emby.Server.Implementations.HttpServer return Task.WhenAll(GetTasks()); } - - public void Dispose() - { - Dispose(true); - } } } diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs index 08f424757..3e731366e 100644 --- a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs +++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs @@ -2,6 +2,7 @@ using System; using System.Globalization; using System.Text; using MediaBrowser.Model.Services; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; @@ -9,7 +10,7 @@ namespace Emby.Server.Implementations.HttpServer { public class ResponseFilter { - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US")); private readonly ILogger _logger; public ResponseFilter(ILogger logger) @@ -23,12 +24,12 @@ namespace Emby.Server.Implementations.HttpServer /// The req. /// The res. /// The dto. - public void FilterResponse(IRequest req, IResponse res, object dto) + public void FilterResponse(IRequest req, HttpResponse res, object dto) { // Try to prevent compatibility view - res.AddHeader("Access-Control-Allow-Headers", "Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Authorization"); - res.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); - res.AddHeader("Access-Control-Allow-Origin", "*"); + res.Headers.Add("Access-Control-Allow-Headers", "Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Authorization"); + res.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); + res.Headers.Add("Access-Control-Allow-Origin", "*"); if (dto is Exception exception) { @@ -39,7 +40,7 @@ namespace Emby.Server.Implementations.HttpServer var error = exception.Message.Replace(Environment.NewLine, " "); error = RemoveControlCharacters(error); - res.AddHeader("X-Application-Error-Code", error); + res.Headers.Add("X-Application-Error-Code", error); } } @@ -54,12 +55,11 @@ namespace Emby.Server.Implementations.HttpServer if (hasHeaders.Headers.TryGetValue(HeaderNames.ContentLength, out string contentLength) && !string.IsNullOrEmpty(contentLength)) { - var length = long.Parse(contentLength, UsCulture); + var length = long.Parse(contentLength, _usCulture); if (length > 0) { - res.OriginalResponse.ContentLength = length; - res.SendChunked = false; + res.ContentLength = length; } } } @@ -72,9 +72,12 @@ namespace Emby.Server.Implementations.HttpServer /// System.String. public static string RemoveControlCharacters(string inString) { - if (inString == null) return null; + if (inString == null) + { + return null; + } - var newString = new StringBuilder(); + var newString = new StringBuilder(inString.Length); foreach (var ch in inString) { @@ -83,6 +86,7 @@ namespace Emby.Server.Implementations.HttpServer newString.Append(ch); } } + return newString.ToString(); } } diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs index 1027883ed..3d3f67ca2 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs @@ -3,7 +3,6 @@ using System.Linq; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Session; @@ -13,28 +12,23 @@ namespace Emby.Server.Implementations.HttpServer.Security { public class AuthService : IAuthService { + private readonly IAuthorizationContext _authorizationContext; + private readonly ISessionManager _sessionManager; private readonly IServerConfigurationManager _config; + private readonly INetworkManager _networkManager; - public AuthService(IUserManager userManager, IAuthorizationContext authorizationContext, IServerConfigurationManager config, ISessionManager sessionManager, INetworkManager networkManager) + public AuthService( + IAuthorizationContext authorizationContext, + IServerConfigurationManager config, + ISessionManager sessionManager, + INetworkManager networkManager) { - AuthorizationContext = authorizationContext; + _authorizationContext = authorizationContext; _config = config; - SessionManager = sessionManager; - UserManager = userManager; - NetworkManager = networkManager; + _sessionManager = sessionManager; + _networkManager = networkManager; } - public IUserManager UserManager { get; private set; } - public IAuthorizationContext AuthorizationContext { get; private set; } - public ISessionManager SessionManager { get; private set; } - public INetworkManager NetworkManager { get; private set; } - - /// - /// Redirect the client to a specific URL if authentication failed. - /// If this property is null, simply `401 Unauthorized` is returned. - /// - public string HtmlRedirect { get; set; } - public void Authenticate(IRequest request, IAuthenticationAttributes authAttribtues) { ValidateUser(request, authAttribtues); @@ -43,7 +37,7 @@ namespace Emby.Server.Implementations.HttpServer.Security private void ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues) { // This code is executed before the service - var auth = AuthorizationContext.GetAuthorizationInfo(request); + var auth = _authorizationContext.GetAuthorizationInfo(request); if (!IsExemptFromAuthenticationToken(authAttribtues, request)) { @@ -80,7 +74,7 @@ namespace Emby.Server.Implementations.HttpServer.Security !string.IsNullOrEmpty(auth.Client) && !string.IsNullOrEmpty(auth.Device)) { - SessionManager.LogSessionActivity(auth.Client, + _sessionManager.LogSessionActivity(auth.Client, auth.Version, auth.DeviceId, auth.Device, @@ -89,7 +83,9 @@ namespace Emby.Server.Implementations.HttpServer.Security } } - private void ValidateUserAccess(User user, IRequest request, + private void ValidateUserAccess( + User user, + IRequest request, IAuthenticationAttributes authAttribtues, AuthorizationInfo auth) { @@ -101,7 +97,7 @@ namespace Emby.Server.Implementations.HttpServer.Security }; } - if (!user.Policy.EnableRemoteAccess && !NetworkManager.IsInLocalNetwork(request.RemoteIp)) + if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(request.RemoteIp)) { throw new SecurityException("User account has been disabled.") { @@ -109,11 +105,11 @@ namespace Emby.Server.Implementations.HttpServer.Security }; } - if (!user.Policy.IsAdministrator && - !authAttribtues.EscapeParentalControl && - !user.IsParentalScheduleAllowed()) + if (!user.Policy.IsAdministrator + && !authAttribtues.EscapeParentalControl + && !user.IsParentalScheduleAllowed()) { - request.Response.AddHeader("X-Application-Error-Code", "ParentalControl"); + request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl"); throw new SecurityException("This user account is not allowed access at this time.") { @@ -183,6 +179,7 @@ namespace Emby.Server.Implementations.HttpServer.Security }; } } + if (roles.Contains("delete", StringComparer.OrdinalIgnoreCase)) { if (user == null || !user.Policy.EnableContentDeletion) @@ -193,6 +190,7 @@ namespace Emby.Server.Implementations.HttpServer.Security }; } } + if (roles.Contains("download", StringComparer.OrdinalIgnoreCase)) { if (user == null || !user.Policy.EnableContentDownloading) diff --git a/Emby.Server.Implementations/Services/HttpResult.cs b/Emby.Server.Implementations/Services/HttpResult.cs index 2b5963a77..095193828 100644 --- a/Emby.Server.Implementations/Services/HttpResult.cs +++ b/Emby.Server.Implementations/Services/HttpResult.cs @@ -10,8 +10,6 @@ namespace Emby.Server.Implementations.Services public class HttpResult : IHttpResult, IAsyncStreamWriter { - public object Response { get; set; } - public HttpResult(object response, string contentType, HttpStatusCode statusCode) { this.Headers = new Dictionary(); @@ -21,6 +19,8 @@ namespace Emby.Server.Implementations.Services this.StatusCode = statusCode; } + public object Response { get; set; } + public string ContentType { get; set; } public IDictionary Headers { get; private set; } @@ -37,7 +37,7 @@ namespace Emby.Server.Implementations.Services public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken) { - var response = RequestContext == null ? null : RequestContext.Response; + var response = RequestContext?.Response; if (this.Response is byte[] bytesResponse) { @@ -45,13 +45,14 @@ namespace Emby.Server.Implementations.Services if (response != null) { - response.OriginalResponse.ContentLength = contentLength; + response.ContentLength = contentLength; } if (contentLength > 0) { await responseStream.WriteAsync(bytesResponse, 0, contentLength, cancellationToken).ConfigureAwait(false); } + return; } diff --git a/Emby.Server.Implementations/Services/ResponseHelper.cs b/Emby.Server.Implementations/Services/ResponseHelper.cs index 251ba3529..ca2b22fe0 100644 --- a/Emby.Server.Implementations/Services/ResponseHelper.cs +++ b/Emby.Server.Implementations/Services/ResponseHelper.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using System.Net; @@ -7,13 +6,14 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.HttpServer; +using Microsoft.AspNetCore.Http; using MediaBrowser.Model.Services; namespace Emby.Server.Implementations.Services { public static class ResponseHelper { - public static Task WriteToResponse(IResponse response, IRequest request, object result, CancellationToken cancellationToken) + public static Task WriteToResponse(HttpResponse response, IRequest request, object result, CancellationToken cancellationToken) { if (result == null) { @@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.Services response.StatusCode = (int)HttpStatusCode.NoContent; } - response.OriginalResponse.ContentLength = 0; + response.ContentLength = 0; return Task.CompletedTask; } @@ -41,7 +41,6 @@ namespace Emby.Server.Implementations.Services httpResult.RequestContext = request; response.StatusCode = httpResult.Status; - response.StatusDescription = httpResult.StatusCode.ToString(); } var responseOptions = result as IHasHeaders; @@ -51,11 +50,11 @@ namespace Emby.Server.Implementations.Services { if (string.Equals(responseHeaders.Key, "Content-Length", StringComparison.OrdinalIgnoreCase)) { - response.OriginalResponse.ContentLength = long.Parse(responseHeaders.Value, CultureInfo.InvariantCulture); + response.ContentLength = long.Parse(responseHeaders.Value, CultureInfo.InvariantCulture); continue; } - response.AddHeader(responseHeaders.Key, responseHeaders.Value); + response.Headers.Add(responseHeaders.Key, responseHeaders.Value); } } @@ -74,31 +73,31 @@ namespace Emby.Server.Implementations.Services switch (result) { case IAsyncStreamWriter asyncStreamWriter: - return asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken); + return asyncStreamWriter.WriteToAsync(response.Body, cancellationToken); case IStreamWriter streamWriter: - streamWriter.WriteTo(response.OutputStream); + streamWriter.WriteTo(response.Body); return Task.CompletedTask; case FileWriter fileWriter: return fileWriter.WriteToAsync(response, cancellationToken); case Stream stream: - return CopyStream(stream, response.OutputStream); + return CopyStream(stream, response.Body); case byte[] bytes: response.ContentType = "application/octet-stream"; - response.OriginalResponse.ContentLength = bytes.Length; + response.ContentLength = bytes.Length; if (bytes.Length > 0) { - return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken); + return response.Body.WriteAsync(bytes, 0, bytes.Length, cancellationToken); } return Task.CompletedTask; case string responseText: var responseTextAsBytes = Encoding.UTF8.GetBytes(responseText); - response.OriginalResponse.ContentLength = responseTextAsBytes.Length; + response.ContentLength = responseTextAsBytes.Length; if (responseTextAsBytes.Length > 0) { - return response.OutputStream.WriteAsync(responseTextAsBytes, 0, responseTextAsBytes.Length, cancellationToken); + return response.Body.WriteAsync(responseTextAsBytes, 0, responseTextAsBytes.Length, cancellationToken); } return Task.CompletedTask; @@ -115,7 +114,7 @@ namespace Emby.Server.Implementations.Services } } - public static async Task WriteObject(IRequest request, object result, IResponse response) + public static async Task WriteObject(IRequest request, object result, HttpResponse response) { var contentType = request.ResponseContentType; var serializer = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType); @@ -127,11 +126,11 @@ namespace Emby.Server.Implementations.Services ms.Position = 0; var contentLength = ms.Length; - response.OriginalResponse.ContentLength = contentLength; + response.ContentLength = contentLength; if (contentLength > 0) { - await ms.CopyToAsync(response.OutputStream).ConfigureAwait(false); + await ms.CopyToAsync(response.Body).ConfigureAwait(false); } } } diff --git a/Emby.Server.Implementations/Services/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs index 5e3d529c6..d963f9043 100644 --- a/Emby.Server.Implementations/Services/ServiceController.cs +++ b/Emby.Server.Implementations/Services/ServiceController.cs @@ -147,7 +147,6 @@ namespace Emby.Server.Implementations.Services public Task Execute(HttpListenerHost httpHost, object requestDto, IRequest req) { - req.Dto = requestDto; var requestType = requestDto.GetType(); req.OperationName = requestType.Name; @@ -161,9 +160,6 @@ namespace Emby.Server.Implementations.Services serviceRequiresContext.Request = req; } - if (req.Dto == null) // Don't override existing batched DTO[] - req.Dto = requestDto; - //Executes the service and returns the result return ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName()); } diff --git a/Emby.Server.Implementations/Services/ServiceExec.cs b/Emby.Server.Implementations/Services/ServiceExec.cs index 38952628d..9124b9c14 100644 --- a/Emby.Server.Implementations/Services/ServiceExec.cs +++ b/Emby.Server.Implementations/Services/ServiceExec.cs @@ -78,7 +78,7 @@ namespace Emby.Server.Implementations.Services foreach (var requestFilter in actionContext.RequestFilters) { requestFilter.RequestFilter(request, request.Response, requestDto); - if (request.Response.OriginalResponse.HasStarted) + if (request.Response.HasStarted) { Task.FromResult(null); } diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs index d32fce1c7..cf15247bb 100644 --- a/Emby.Server.Implementations/Services/ServiceHandler.cs +++ b/Emby.Server.Implementations/Services/ServiceHandler.cs @@ -5,20 +5,21 @@ using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.HttpServer; using MediaBrowser.Model.Services; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Services { public class ServiceHandler { - public RestPath RestPath { get; } + private RestPath _restPath; - public string ResponseContentType { get; } + private string _responseContentType; internal ServiceHandler(RestPath restPath, string responseContentType) { - RestPath = restPath; - ResponseContentType = responseContentType; + _restPath = restPath; + _responseContentType = responseContentType; } protected static Task CreateContentTypeRequest(HttpListenerHost host, IRequest httpReq, Type requestType, string contentType) @@ -54,7 +55,7 @@ namespace Emby.Server.Implementations.Services private static string GetFormatContentType(string format) { - //built-in formats + // built-in formats switch (format) { case "json": return "application/json"; @@ -63,16 +64,16 @@ namespace Emby.Server.Implementations.Services } } - public async Task ProcessRequestAsync(HttpListenerHost httpHost, IRequest httpReq, IResponse httpRes, ILogger logger, CancellationToken cancellationToken) + public async Task ProcessRequestAsync(HttpListenerHost httpHost, IRequest httpReq, HttpResponse httpRes, ILogger logger, CancellationToken cancellationToken) { - httpReq.Items["__route"] = RestPath; + httpReq.Items["__route"] = _restPath; - if (ResponseContentType != null) + if (_responseContentType != null) { - httpReq.ResponseContentType = ResponseContentType; + httpReq.ResponseContentType = _responseContentType; } - var request = httpReq.Dto = await CreateRequest(httpHost, httpReq, RestPath, logger).ConfigureAwait(false); + var request = await CreateRequest(httpHost, httpReq, _restPath, logger).ConfigureAwait(false); httpHost.ApplyRequestFilters(httpReq, httpRes, request); @@ -94,7 +95,7 @@ namespace Emby.Server.Implementations.Services if (RequireqRequestStream(requestType)) { // Used by IRequiresRequestStream - var requestParams = await GetRequestParams(httpReq).ConfigureAwait(false); + var requestParams = GetRequestParams(httpReq.Response.HttpContext.Request); var request = ServiceHandler.CreateRequest(httpReq, restPath, requestParams, host.CreateInstance(requestType)); var rawReq = (IRequiresRequestStream)request; @@ -103,7 +104,7 @@ namespace Emby.Server.Implementations.Services } else { - var requestParams = await GetFlattenedRequestParams(httpReq).ConfigureAwait(false); + var requestParams = GetFlattenedRequestParams(httpReq.Response.HttpContext.Request); var requestDto = await CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType).ConfigureAwait(false); @@ -130,56 +131,42 @@ namespace Emby.Server.Implementations.Services /// /// Duplicate Params are given a unique key by appending a #1 suffix /// - private static async Task> GetRequestParams(IRequest request) + private static Dictionary GetRequestParams(HttpRequest request) { var map = new Dictionary(); - foreach (var name in request.QueryString.Keys) + foreach (var pair in request.Query) { - if (name == null) - { - // thank you ASP.NET - continue; - } - - var values = request.QueryString[name]; + var values = pair.Value; if (values.Count == 1) { - map[name] = values[0]; + map[pair.Key] = values[0]; } else { for (var i = 0; i < values.Count; i++) { - map[name + (i == 0 ? "" : "#" + i)] = values[i]; + map[pair.Key + (i == 0 ? "" : "#" + i)] = values[i]; } } } - if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT"))) + if ( + (IsMethod(request.Method, "POST") || IsMethod(request.Method, "PUT")) + && request.HasFormContentType) { - var formData = await request.GetFormData().ConfigureAwait(false); - if (formData != null) + foreach (var pair in request.Form) { - foreach (var name in formData.Keys) + var values = pair.Value; + if (values.Count == 1) { - if (name == null) - { - // thank you ASP.NET - continue; - } - - var values = formData.GetValues(name); - if (values.Count == 1) - { - map[name] = values[0]; - } - else + map[pair.Key] = values[0]; + } + else + { + for (var i = 0; i < values.Count; i++) { - for (var i = 0; i < values.Count; i++) - { - map[name + (i == 0 ? "" : "#" + i)] = values[i]; - } + map[pair.Key + (i == 0 ? "" : "#" + i)] = values[i]; } } } @@ -196,36 +183,22 @@ namespace Emby.Server.Implementations.Services /// /// Duplicate params have their values joined together in a comma-delimited string /// - private static async Task> GetFlattenedRequestParams(IRequest request) + private static Dictionary GetFlattenedRequestParams(HttpRequest request) { var map = new Dictionary(); - foreach (var name in request.QueryString.Keys) + foreach (var pair in request.Query) { - if (name == null) - { - // thank you ASP.NET - continue; - } - - map[name] = request.QueryString[name]; + map[pair.Key] = pair.Value; } - if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT"))) + if ( + (IsMethod(request.Method, "POST") || IsMethod(request.Method, "PUT")) + && request.HasFormContentType) { - var formData = await request.GetFormData().ConfigureAwait(false); - if (formData != null) + foreach (var pair in request.Form) { - foreach (var name in formData.Keys) - { - if (name == null) - { - // thank you ASP.NET - continue; - } - - map[name] = formData[name]; - } + map[pair.Key] = pair.Value; } } diff --git a/Emby.Server.Implementations/SocketSharp/RequestMono.cs b/Emby.Server.Implementations/SocketSharp/RequestMono.cs deleted file mode 100644 index ec637186f..000000000 --- a/Emby.Server.Implementations/SocketSharp/RequestMono.cs +++ /dev/null @@ -1,647 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Net; -using System.Text; -using System.Threading.Tasks; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Primitives; -using Microsoft.Net.Http.Headers; - -namespace Emby.Server.Implementations.SocketSharp -{ - public partial class WebSocketSharpRequest : IHttpRequest - { - internal static string GetParameter(ReadOnlySpan header, string attr) - { - int ap = header.IndexOf(attr.AsSpan(), StringComparison.Ordinal); - if (ap == -1) - { - return null; - } - - ap += attr.Length; - if (ap >= header.Length) - { - return null; - } - - char ending = header[ap]; - if (ending != '"') - { - ending = ' '; - } - - var slice = header.Slice(ap + 1); - int end = slice.IndexOf(ending); - if (end == -1) - { - return ending == '"' ? null : header.Slice(ap).ToString(); - } - - return slice.Slice(0, end - ap - 1).ToString(); - } - - private async Task LoadMultiPart(WebROCollection form) - { - string boundary = GetParameter(ContentType.AsSpan(), "; boundary="); - if (boundary == null) - { - return; - } - - using (var requestStream = InputStream) - { - // DB: 30/01/11 - Hack to get around non-seekable stream and received HTTP request - // Not ending with \r\n? - var ms = new MemoryStream(32 * 1024); - await requestStream.CopyToAsync(ms).ConfigureAwait(false); - - var input = ms; - ms.WriteByte((byte)'\r'); - ms.WriteByte((byte)'\n'); - - input.Position = 0; - - // Uncomment to debug - // var content = new StreamReader(ms).ReadToEnd(); - // Console.WriteLine(boundary + "::" + content); - // input.Position = 0; - - var multi_part = new HttpMultipart(input, boundary, ContentEncoding); - - HttpMultipart.Element e; - while ((e = multi_part.ReadNextElement()) != null) - { - if (e.Filename == null) - { - byte[] copy = new byte[e.Length]; - - input.Position = e.Start; - await input.ReadAsync(copy, 0, (int)e.Length).ConfigureAwait(false); - - form.Add(e.Name, (e.Encoding ?? ContentEncoding).GetString(copy, 0, copy.Length)); - } - else - { - // We use a substream, as in 2.x we will support large uploads streamed to disk, - files[e.Name] = new HttpPostedFile(e.Filename, e.ContentType, input, e.Start, e.Length); - } - } - } - } - - public async Task GetFormData() - { - var form = new WebROCollection(); - files = new Dictionary(); - - if (IsContentType("multipart/form-data")) - { - await LoadMultiPart(form).ConfigureAwait(false); - } - else if (IsContentType("application/x-www-form-urlencoded")) - { - await LoadWwwForm(form).ConfigureAwait(false); - } - - if (validate_form && !checked_form) - { - checked_form = true; - ValidateNameValueCollection("Form", form); - } - - return form; - } - - public string Accept => StringValues.IsNullOrEmpty(request.Headers[HeaderNames.Accept]) ? null : request.Headers[HeaderNames.Accept].ToString(); - - public string Authorization => StringValues.IsNullOrEmpty(request.Headers[HeaderNames.Authorization]) ? null : request.Headers[HeaderNames.Authorization].ToString(); - - protected bool validate_form { get; set; } - protected bool checked_form { get; set; } - - private static void ThrowValidationException(string name, string key, string value) - { - string v = "\"" + value + "\""; - if (v.Length > 20) - { - v = v.Substring(0, 16) + "...\""; - } - - string msg = string.Format( - CultureInfo.InvariantCulture, - "A potentially dangerous Request.{0} value was detected from the client ({1}={2}).", - name, - key, - v); - - throw new Exception(msg); - } - - private static void ValidateNameValueCollection(string name, QueryParamCollection coll) - { - if (coll == null) - { - return; - } - - foreach (var pair in coll) - { - var key = pair.Name; - var val = pair.Value; - if (val != null && val.Length > 0 && IsInvalidString(val)) - { - ThrowValidationException(name, key, val); - } - } - } - - internal static bool IsInvalidString(string val) - => IsInvalidString(val, out var validationFailureIndex); - - internal static bool IsInvalidString(string val, out int validationFailureIndex) - { - validationFailureIndex = 0; - - int len = val.Length; - if (len < 2) - { - return false; - } - - char current = val[0]; - for (int idx = 1; idx < len; idx++) - { - char next = val[idx]; - - // See http://secunia.com/advisories/14325 - if (current == '<' || current == '\xff1c') - { - if (next == '!' || next < ' ' - || (next >= 'a' && next <= 'z') - || (next >= 'A' && next <= 'Z')) - { - validationFailureIndex = idx - 1; - return true; - } - } - else if (current == '&' && next == '#') - { - validationFailureIndex = idx - 1; - return true; - } - - current = next; - } - - return false; - } - - private bool IsContentType(string ct) - { - if (ContentType == null) - { - return false; - } - - return ContentType.StartsWith(ct, StringComparison.OrdinalIgnoreCase); - } - - private async Task LoadWwwForm(WebROCollection form) - { - using (var input = InputStream) - { - using (var ms = new MemoryStream()) - { - await input.CopyToAsync(ms).ConfigureAwait(false); - ms.Position = 0; - - using (var s = new StreamReader(ms, ContentEncoding)) - { - var key = new StringBuilder(); - var value = new StringBuilder(); - int c; - - while ((c = s.Read()) != -1) - { - if (c == '=') - { - value.Length = 0; - while ((c = s.Read()) != -1) - { - if (c == '&') - { - AddRawKeyValue(form, key, value); - break; - } - else - { - value.Append((char)c); - } - } - - if (c == -1) - { - AddRawKeyValue(form, key, value); - return; - } - } - else if (c == '&') - { - AddRawKeyValue(form, key, value); - } - else - { - key.Append((char)c); - } - } - - if (c == -1) - { - AddRawKeyValue(form, key, value); - } - } - } - } - } - - private static void AddRawKeyValue(WebROCollection form, StringBuilder key, StringBuilder value) - { - form.Add(WebUtility.UrlDecode(key.ToString()), WebUtility.UrlDecode(value.ToString())); - - key.Length = 0; - value.Length = 0; - } - - private Dictionary files; - - private class WebROCollection : QueryParamCollection - { - public override string ToString() - { - var result = new StringBuilder(); - foreach (var pair in this) - { - if (result.Length > 0) - { - result.Append('&'); - } - - var key = pair.Name; - if (key != null && key.Length > 0) - { - result.Append(key); - result.Append('='); - } - - result.Append(pair.Value); - } - - return result.ToString(); - } - } - private class HttpMultipart - { - - public class Element - { - public string ContentType { get; set; } - - public string Name { get; set; } - - public string Filename { get; set; } - - public Encoding Encoding { get; set; } - - public long Start { get; set; } - - public long Length { get; set; } - - public override string ToString() - { - return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " + - Start.ToString(CultureInfo.CurrentCulture) + ", Length " + Length.ToString(CultureInfo.CurrentCulture); - } - } - - private const byte LF = (byte)'\n'; - - private const byte CR = (byte)'\r'; - - private Stream data; - - private string boundary; - - private byte[] boundaryBytes; - - private byte[] buffer; - - private bool atEof; - - private Encoding encoding; - - private StringBuilder sb; - - // See RFC 2046 - // In the case of multipart entities, in which one or more different - // sets of data are combined in a single body, a "multipart" media type - // field must appear in the entity's header. The body must then contain - // one or more body parts, each preceded by a boundary delimiter line, - // and the last one followed by a closing boundary delimiter line. - // After its boundary delimiter line, each body part then consists of a - // header area, a blank line, and a body area. Thus a body part is - // similar to an RFC 822 message in syntax, but different in meaning. - - public HttpMultipart(Stream data, string b, Encoding encoding) - { - this.data = data; - boundary = b; - boundaryBytes = encoding.GetBytes(b); - buffer = new byte[boundaryBytes.Length + 2]; // CRLF or '--' - this.encoding = encoding; - sb = new StringBuilder(); - } - - public Element ReadNextElement() - { - if (atEof || ReadBoundary()) - { - return null; - } - - var elem = new Element(); - ReadOnlySpan header; - while ((header = ReadLine().AsSpan()).Length != 0) - { - if (header.StartsWith("Content-Disposition:".AsSpan(), StringComparison.OrdinalIgnoreCase)) - { - elem.Name = GetContentDispositionAttribute(header, "name"); - elem.Filename = StripPath(GetContentDispositionAttributeWithEncoding(header, "filename")); - } - else if (header.StartsWith("Content-Type:".AsSpan(), StringComparison.OrdinalIgnoreCase)) - { - elem.ContentType = header.Slice("Content-Type:".Length).Trim().ToString(); - elem.Encoding = GetEncoding(elem.ContentType); - } - } - - long start = data.Position; - elem.Start = start; - long pos = MoveToNextBoundary(); - if (pos == -1) - { - return null; - } - - elem.Length = pos - start; - return elem; - } - - private string ReadLine() - { - // CRLF or LF are ok as line endings. - bool got_cr = false; - int b = 0; - sb.Length = 0; - while (true) - { - b = data.ReadByte(); - if (b == -1) - { - return null; - } - - if (b == LF) - { - break; - } - - got_cr = b == CR; - sb.Append((char)b); - } - - if (got_cr) - { - sb.Length--; - } - - return sb.ToString(); - } - - private static string GetContentDispositionAttribute(ReadOnlySpan l, string name) - { - int idx = l.IndexOf((name + "=\"").AsSpan(), StringComparison.Ordinal); - if (idx < 0) - { - return null; - } - - int begin = idx + name.Length + "=\"".Length; - int end = l.Slice(begin).IndexOf('"'); - if (end < 0) - { - return null; - } - - if (begin == end) - { - return string.Empty; - } - - return l.Slice(begin, end - begin).ToString(); - } - - private string GetContentDispositionAttributeWithEncoding(ReadOnlySpan l, string name) - { - int idx = l.IndexOf((name + "=\"").AsSpan(), StringComparison.Ordinal); - if (idx < 0) - { - return null; - } - - int begin = idx + name.Length + "=\"".Length; - int end = l.Slice(begin).IndexOf('"'); - if (end < 0) - { - return null; - } - - if (begin == end) - { - return string.Empty; - } - - ReadOnlySpan temp = l.Slice(begin, end - begin); - byte[] source = new byte[temp.Length]; - for (int i = temp.Length - 1; i >= 0; i--) - { - source[i] = (byte)temp[i]; - } - - return encoding.GetString(source, 0, source.Length); - } - - private bool ReadBoundary() - { - try - { - string line; - do - { - line = ReadLine(); - } - while (line.Length == 0); - - if (line[0] != '-' || line[1] != '-') - { - return false; - } - - if (!line.EndsWith(boundary, StringComparison.Ordinal)) - { - return true; - } - } - catch - { - - } - - return false; - } - - private static bool CompareBytes(byte[] orig, byte[] other) - { - for (int i = orig.Length - 1; i >= 0; i--) - { - if (orig[i] != other[i]) - { - return false; - } - } - - return true; - } - - private long MoveToNextBoundary() - { - long retval = 0; - bool got_cr = false; - - int state = 0; - int c = data.ReadByte(); - while (true) - { - if (c == -1) - { - return -1; - } - - if (state == 0 && c == LF) - { - retval = data.Position - 1; - if (got_cr) - { - retval--; - } - - state = 1; - c = data.ReadByte(); - } - else if (state == 0) - { - got_cr = c == CR; - c = data.ReadByte(); - } - else if (state == 1 && c == '-') - { - c = data.ReadByte(); - if (c == -1) - { - return -1; - } - - if (c != '-') - { - state = 0; - got_cr = false; - continue; // no ReadByte() here - } - - int nread = data.Read(buffer, 0, buffer.Length); - int bl = buffer.Length; - if (nread != bl) - { - return -1; - } - - if (!CompareBytes(boundaryBytes, buffer)) - { - state = 0; - data.Position = retval + 2; - if (got_cr) - { - data.Position++; - got_cr = false; - } - - c = data.ReadByte(); - continue; - } - - if (buffer[bl - 2] == '-' && buffer[bl - 1] == '-') - { - atEof = true; - } - else if (buffer[bl - 2] != CR || buffer[bl - 1] != LF) - { - state = 0; - data.Position = retval + 2; - if (got_cr) - { - data.Position++; - got_cr = false; - } - - c = data.ReadByte(); - continue; - } - - data.Position = retval + 2; - if (got_cr) - { - data.Position++; - } - - break; - } - else - { - // state == 1 - state = 0; // no ReadByte() here - } - } - - return retval; - } - - private static string StripPath(string path) - { - if (path == null || path.Length == 0) - { - return path; - } - - if (path.IndexOf(":\\", StringComparison.Ordinal) != 1 - && !path.StartsWith("\\\\", StringComparison.Ordinal)) - { - return path; - } - - return path.Substring(path.LastIndexOf('\\') + 1); - } - } - } -} diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs index 7a630bf10..43f71a69c 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs @@ -6,7 +6,6 @@ using System.Net; using System.Linq; using System.Text; using MediaBrowser.Common.Net; -using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Logging; @@ -14,44 +13,49 @@ using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; using IHttpFile = MediaBrowser.Model.Services.IHttpFile; using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest; -using IResponse = MediaBrowser.Model.Services.IResponse; namespace Emby.Server.Implementations.SocketSharp { public partial class WebSocketSharpRequest : IHttpRequest { - private readonly HttpRequest request; + public const string FormUrlEncoded = "application/x-www-form-urlencoded"; + public const string MultiPartFormData = "multipart/form-data"; + public const string Soap11 = "text/xml; charset=utf-8"; + + private string _remoteIp; + private Dictionary _items; + private string _responseContentType; + private IHttpFile[] _httpFiles; + private Dictionary _files; - public WebSocketSharpRequest(HttpRequest httpContext, HttpResponse response, string operationName, ILogger logger) + public WebSocketSharpRequest(HttpRequest httpRequest, HttpResponse httpResponse, string operationName, ILogger logger) { this.OperationName = operationName; - this.request = httpContext; - this.Response = new WebSocketSharpResponse(logger, response); + this.Request = httpRequest; + this.Response = httpResponse; } - public HttpRequest HttpRequest => request; + public string Accept => StringValues.IsNullOrEmpty(Request.Headers[HeaderNames.Accept]) ? null : Request.Headers[HeaderNames.Accept].ToString(); - public IResponse Response { get; } + public string Authorization => StringValues.IsNullOrEmpty(Request.Headers[HeaderNames.Authorization]) ? null : Request.Headers[HeaderNames.Authorization].ToString(); - public string OperationName { get; set; } + public HttpRequest Request { get; } - public object Dto { get; set; } + public HttpResponse Response { get; } - public string RawUrl => request.GetEncodedPathAndQuery(); + public string OperationName { get; set; } - public string AbsoluteUri => request.GetDisplayUrl().TrimEnd('/'); - // Header[name] returns "" when undefined + public string RawUrl => Request.GetEncodedPathAndQuery(); - private string GetHeader(string name) => request.Headers[name].ToString(); + public string AbsoluteUri => Request.GetDisplayUrl().TrimEnd('/'); - private string remoteIp; public string RemoteIp { get { - if (remoteIp != null) + if (_remoteIp != null) { - return remoteIp; + return _remoteIp; } IPAddress ip; @@ -62,14 +66,84 @@ namespace Emby.Server.Implementations.SocketSharp { if (!IPAddress.TryParse(GetHeader(CustomHeaderNames.XRealIP), out ip)) { - ip = request.HttpContext.Connection.RemoteIpAddress; + ip = Request.HttpContext.Connection.RemoteIpAddress; } } - return remoteIp = NormalizeIp(ip).ToString(); + return _remoteIp = NormalizeIp(ip).ToString(); } } + public string[] AcceptTypes => Request.Headers.GetCommaSeparatedValues(HeaderNames.Accept); + + public Dictionary Items => _items ?? (_items = new Dictionary()); + + public string ResponseContentType + { + get => + _responseContentType + ?? (_responseContentType = GetResponseContentType(Request)); + set => this._responseContentType = value; + } + + public string PathInfo => Request.Path.Value; + + public string UserAgent => Request.Headers[HeaderNames.UserAgent]; + + public IHeaderDictionary Headers => Request.Headers; + + public IQueryCollection QueryString => Request.Query; + + public bool IsLocal => Request.HttpContext.Connection.LocalIpAddress.Equals(Request.HttpContext.Connection.RemoteIpAddress); + + + public string HttpMethod => Request.Method; + + public string Verb => HttpMethod; + + public string ContentType => Request.ContentType; + + public Uri UrlReferrer => Request.GetTypedHeaders().Referer; + + public Stream InputStream => Request.Body; + + public long ContentLength => Request.ContentLength ?? 0; + + + public IHttpFile[] Files + { + get + { + if (_httpFiles != null) + { + return _httpFiles; + } + + if (_files == null) + { + return _httpFiles = Array.Empty(); + } + + var values = _files.Values; + _httpFiles = new IHttpFile[values.Count]; + for (int i = 0; i < values.Count; i++) + { + var reqFile = values.ElementAt(i); + _httpFiles[i] = new HttpFile + { + ContentType = reqFile.ContentType, + ContentLength = reqFile.ContentLength, + FileName = reqFile.FileName, + InputStream = reqFile.InputStream, + }; + } + + return _httpFiles; + } + } + + private string GetHeader(string name) => Request.Headers[name].ToString(); + private static IPAddress NormalizeIp(IPAddress ip) { if (ip.IsIPv4MappedToIPv6) @@ -80,22 +154,6 @@ namespace Emby.Server.Implementations.SocketSharp return ip; } - public string[] AcceptTypes => request.Headers.GetCommaSeparatedValues(HeaderNames.Accept); - - private Dictionary items; - public Dictionary Items => items ?? (items = new Dictionary()); - - private string responseContentType; - public string ResponseContentType - { - get => - responseContentType - ?? (responseContentType = GetResponseContentType(HttpRequest)); - set => this.responseContentType = value; - } - - public const string FormUrlEncoded = "application/x-www-form-urlencoded"; - public const string MultiPartFormData = "multipart/form-data"; public static string GetResponseContentType(HttpRequest httpReq) { var specifiedContentType = GetQueryStringContentType(httpReq); @@ -152,8 +210,6 @@ namespace Emby.Server.Implementations.SocketSharp return serverDefaultContentType; } - public const string Soap11 = "text/xml; charset=utf-8"; - public static bool HasAnyOfContentTypes(HttpRequest request, params string[] contentTypes) { if (contentTypes == null || request.ContentType == null) @@ -224,105 +280,5 @@ namespace Emby.Server.Implementations.SocketSharp var pos = strVal.IndexOf(needle); return pos == -1 ? strVal : strVal.Slice(0, pos); } - - public string PathInfo => this.request.Path.Value; - - public string UserAgent => request.Headers[HeaderNames.UserAgent]; - - public IHeaderDictionary Headers => request.Headers; - - public IQueryCollection QueryString => request.Query; - - public bool IsLocal => string.Equals(request.HttpContext.Connection.LocalIpAddress.ToString(), request.HttpContext.Connection.RemoteIpAddress.ToString()); - - private string httpMethod; - public string HttpMethod => - httpMethod - ?? (httpMethod = request.Method); - - public string Verb => HttpMethod; - - public string ContentType => request.ContentType; - - private Encoding ContentEncoding - { - get - { - // TODO is this necessary? - if (UserAgent != null && CultureInfo.InvariantCulture.CompareInfo.IsPrefix(UserAgent, "UP")) - { - string postDataCharset = Headers["x-up-devcap-post-charset"]; - if (!string.IsNullOrEmpty(postDataCharset)) - { - try - { - return Encoding.GetEncoding(postDataCharset); - } - catch (ArgumentException) - { - } - } - } - - return request.GetTypedHeaders().ContentType.Encoding ?? Encoding.UTF8; - } - } - - public Uri UrlReferrer => request.GetTypedHeaders().Referer; - - public static Encoding GetEncoding(string contentTypeHeader) - { - var param = GetParameter(contentTypeHeader.AsSpan(), "charset="); - if (param == null) - { - return null; - } - - try - { - return Encoding.GetEncoding(param); - } - catch (ArgumentException) - { - return null; - } - } - - public Stream InputStream => request.Body; - - public long ContentLength => request.ContentLength ?? 0; - - private IHttpFile[] httpFiles; - public IHttpFile[] Files - { - get - { - if (httpFiles != null) - { - return httpFiles; - } - - if (files == null) - { - return httpFiles = Array.Empty(); - } - - var values = files.Values; - httpFiles = new IHttpFile[values.Count]; - for (int i = 0; i < values.Count; i++) - { - var reqFile = values.ElementAt(i); - httpFiles[i] = new HttpFile - { - ContentType = reqFile.ContentType, - ContentLength = reqFile.ContentLength, - FileName = reqFile.FileName, - InputStream = reqFile.InputStream, - }; - } - - return httpFiles; - } - } } } diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpResponse.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpResponse.cs deleted file mode 100644 index 0f67eaa62..000000000 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpResponse.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Services; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using IRequest = MediaBrowser.Model.Services.IRequest; - -namespace Emby.Server.Implementations.SocketSharp -{ - public class WebSocketSharpResponse : IResponse - { - private readonly ILogger _logger; - - public WebSocketSharpResponse(ILogger logger, HttpResponse response) - { - _logger = logger; - OriginalResponse = response; - } - - public HttpResponse OriginalResponse { get; } - - public int StatusCode - { - get => OriginalResponse.StatusCode; - set => OriginalResponse.StatusCode = value; - } - - public string StatusDescription { get; set; } - - public string ContentType - { - get => OriginalResponse.ContentType; - set => OriginalResponse.ContentType = value; - } - - public void AddHeader(string name, string value) - { - if (string.Equals(name, "Content-Type", StringComparison.OrdinalIgnoreCase)) - { - ContentType = value; - return; - } - - OriginalResponse.Headers.Add(name, value); - } - - public void Redirect(string url) - { - OriginalResponse.Redirect(url); - } - - public Stream OutputStream => OriginalResponse.Body; - - public bool SendChunked { get; set; } - - const int StreamCopyToBufferSize = 81920; - public async Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, IFileSystem fileSystem, IStreamHelper streamHelper, CancellationToken cancellationToken) - { - var allowAsync = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - - //if (count <= 0) - //{ - // allowAsync = true; - //} - - var fileOpenOptions = FileOpenOptions.SequentialScan; - - if (allowAsync) - { - fileOpenOptions |= FileOpenOptions.Asynchronous; - } - - // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 - - using (var fs = fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, fileOpenOptions)) - { - if (offset > 0) - { - fs.Position = offset; - } - - if (count > 0) - { - await streamHelper.CopyToAsync(fs, OutputStream, count, cancellationToken).ConfigureAwait(false); - } - else - { - await fs.CopyToAsync(OutputStream, StreamCopyToBufferSize, cancellationToken).ConfigureAwait(false); - } - } - } - } -} diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 399401624..f0d5147a8 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1019,7 +1019,7 @@ namespace MediaBrowser.Api.Playback foreach (var item in responseHeaders) { - Request.Response.AddHeader(item.Key, item.Value); + Request.Response.Headers.Add(item.Key, item.Value); } } diff --git a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs index 64c2294e3..29fb81e32 100644 --- a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs +++ b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs @@ -1,5 +1,6 @@ using System; using MediaBrowser.Model.Services; +using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller.Net { @@ -33,7 +34,7 @@ namespace MediaBrowser.Controller.Net /// The http request wrapper /// The http response wrapper /// The request DTO - public void RequestFilter(IRequest request, IResponse response, object requestDto) + public void RequestFilter(IRequest request, HttpResponse response, object requestDto) { AuthService.Authenticate(request, this); } diff --git a/MediaBrowser.Model/Services/IHasRequestFilter.cs b/MediaBrowser.Model/Services/IHasRequestFilter.cs index d4e6aa8e0..81a2dba69 100644 --- a/MediaBrowser.Model/Services/IHasRequestFilter.cs +++ b/MediaBrowser.Model/Services/IHasRequestFilter.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; + namespace MediaBrowser.Model.Services { public interface IHasRequestFilter @@ -15,6 +17,6 @@ namespace MediaBrowser.Model.Services /// The http request wrapper /// The http response wrapper /// The request DTO - void RequestFilter(IRequest req, IResponse res, object requestDto); + void RequestFilter(IRequest req, HttpResponse res, object requestDto); } } diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs index 4f6ddb476..3852b1dd7 100644 --- a/MediaBrowser.Model/Services/IRequest.cs +++ b/MediaBrowser.Model/Services/IRequest.cs @@ -10,7 +10,7 @@ namespace MediaBrowser.Model.Services { public interface IRequest { - IResponse Response { get; } + HttpResponse Response { get; } /// /// The name of the service being called (e.g. Request DTO Name) @@ -22,11 +22,6 @@ namespace MediaBrowser.Model.Services /// string Verb { get; } - /// - /// The Request DTO, after it has been deserialized. - /// - object Dto { get; set; } - /// /// The request ContentType /// @@ -50,8 +45,6 @@ namespace MediaBrowser.Model.Services IQueryCollection QueryString { get; } - Task GetFormData(); - string RawUrl { get; } string AbsoluteUri { get; } @@ -98,25 +91,4 @@ namespace MediaBrowser.Model.Services { IRequest Request { get; set; } } - - public interface IResponse - { - HttpResponse OriginalResponse { get; } - - int StatusCode { get; set; } - - string StatusDescription { get; set; } - - string ContentType { get; set; } - - void AddHeader(string name, string value); - - void Redirect(string url); - - Stream OutputStream { get; } - - Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, IFileSystem fileSystem, IStreamHelper streamHelper, CancellationToken cancellationToken); - - bool SendChunked { get; set; } - } } -- cgit v1.2.3 From cf7290343ff4a440537c61332cdc76c5fbcbb7e9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 29 Jul 2019 00:33:28 +0200 Subject: Fix build --- .../SocketSharp/WebSocketSharpRequest.cs | 38 ---------------------- MediaBrowser.Api/Devices/DeviceService.cs | 15 +++++---- MediaBrowser.Model/Services/IRequest.cs | 8 ----- 3 files changed, 8 insertions(+), 53 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs index 43f71a69c..332ce3903 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs @@ -1,17 +1,14 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Net; using System.Linq; -using System.Text; using MediaBrowser.Common.Net; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; -using IHttpFile = MediaBrowser.Model.Services.IHttpFile; using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest; namespace Emby.Server.Implementations.SocketSharp @@ -25,8 +22,6 @@ namespace Emby.Server.Implementations.SocketSharp private string _remoteIp; private Dictionary _items; private string _responseContentType; - private IHttpFile[] _httpFiles; - private Dictionary _files; public WebSocketSharpRequest(HttpRequest httpRequest, HttpResponse httpResponse, string operationName, ILogger logger) { @@ -109,39 +104,6 @@ namespace Emby.Server.Implementations.SocketSharp public long ContentLength => Request.ContentLength ?? 0; - - public IHttpFile[] Files - { - get - { - if (_httpFiles != null) - { - return _httpFiles; - } - - if (_files == null) - { - return _httpFiles = Array.Empty(); - } - - var values = _files.Values; - _httpFiles = new IHttpFile[values.Count]; - for (int i = 0; i < values.Count; i++) - { - var reqFile = values.ElementAt(i); - _httpFiles[i] = new HttpFile - { - ContentType = reqFile.ContentType, - ContentLength = reqFile.ContentLength, - FileName = reqFile.FileName, - InputStream = reqFile.InputStream, - }; - } - - return _httpFiles; - } - } - private string GetHeader(string name) => Request.Headers[name].ToString(); private static IPAddress NormalizeIp(IPAddress ip) diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs index dc211af6b..697a84f5c 100644 --- a/MediaBrowser.Api/Devices/DeviceService.cs +++ b/MediaBrowser.Api/Devices/DeviceService.cs @@ -133,12 +133,15 @@ namespace MediaBrowser.Api.Devices var album = Request.QueryString["Album"]; var id = Request.QueryString["Id"]; var name = Request.QueryString["Name"]; + var req = Request.Response.HttpContext.Request; - if (Request.ContentType.IndexOf("multi", StringComparison.OrdinalIgnoreCase) == -1) + if (req.HasFormContentType) { - return _deviceManager.AcceptCameraUpload(deviceId, request.RequestStream, new LocalFileInfo + var file = req.Form.Files.Count == 0 ? null : req.Form.Files[0]; + + return _deviceManager.AcceptCameraUpload(deviceId, file.OpenReadStream(), new LocalFileInfo { - MimeType = Request.ContentType, + MimeType = file.ContentType, Album = album, Name = name, Id = id @@ -146,11 +149,9 @@ namespace MediaBrowser.Api.Devices } else { - var file = Request.Files.Length == 0 ? null : Request.Files[0]; - - return _deviceManager.AcceptCameraUpload(deviceId, file.InputStream, new LocalFileInfo + return _deviceManager.AcceptCameraUpload(deviceId, request.RequestStream, new LocalFileInfo { - MimeType = file.ContentType, + MimeType = Request.ContentType, Album = album, Name = name, Id = id diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs index 3852b1dd7..7a4152698 100644 --- a/MediaBrowser.Model/Services/IRequest.cs +++ b/MediaBrowser.Model/Services/IRequest.cs @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.IO; using Microsoft.AspNetCore.Http; namespace MediaBrowser.Model.Services @@ -67,11 +64,6 @@ namespace MediaBrowser.Model.Services long ContentLength { get; } - /// - /// Access to the multi-part/formdata files posted on this request - /// - IHttpFile[] Files { get; } - /// /// The value of the Referrer, null if not available /// -- cgit v1.2.3 From 0116190050be69b2ac61d46681f2c7478a1318b9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 9 Aug 2019 22:37:44 +0200 Subject: Minor changes --- Emby.Server.Implementations/Services/ServiceHandler.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs index cf15247bb..934560de3 100644 --- a/Emby.Server.Implementations/Services/ServiceHandler.cs +++ b/Emby.Server.Implementations/Services/ServiceHandler.cs @@ -122,7 +122,7 @@ namespace Emby.Server.Implementations.Services public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary requestParams, object requestDto) { var pathInfo = !restPath.IsWildCardPath - ? GetSanitizedPathInfo(httpReq.PathInfo, out string contentType) + ? GetSanitizedPathInfo(httpReq.PathInfo, out _) : httpReq.PathInfo; return restPath.CreateRequest(pathInfo, requestParams, requestDto); @@ -146,13 +146,12 @@ namespace Emby.Server.Implementations.Services { for (var i = 0; i < values.Count; i++) { - map[pair.Key + (i == 0 ? "" : "#" + i)] = values[i]; + map[pair.Key + (i == 0 ? string.Empty : "#" + i)] = values[i]; } } } - if ( - (IsMethod(request.Method, "POST") || IsMethod(request.Method, "PUT")) + if ((IsMethod(request.Method, "POST") || IsMethod(request.Method, "PUT")) && request.HasFormContentType) { foreach (var pair in request.Form) @@ -166,7 +165,7 @@ namespace Emby.Server.Implementations.Services { for (var i = 0; i < values.Count; i++) { - map[pair.Key + (i == 0 ? "" : "#" + i)] = values[i]; + map[pair.Key + (i == 0 ? string.Empty : "#" + i)] = values[i]; } } } @@ -176,9 +175,7 @@ namespace Emby.Server.Implementations.Services } private static bool IsMethod(string method, string expected) - { - return string.Equals(method, expected, StringComparison.OrdinalIgnoreCase); - } + => string.Equals(method, expected, StringComparison.OrdinalIgnoreCase); /// /// Duplicate params have their values joined together in a comma-delimited string @@ -192,8 +189,7 @@ namespace Emby.Server.Implementations.Services map[pair.Key] = pair.Value; } - if ( - (IsMethod(request.Method, "POST") || IsMethod(request.Method, "PUT")) + if ((IsMethod(request.Method, "POST") || IsMethod(request.Method, "PUT")) && request.HasFormContentType) { foreach (var pair in request.Form) -- cgit v1.2.3 From 1cad93c276fef512d6d4731d32704e05433aed01 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 29 Jul 2019 15:26:17 +0200 Subject: Use System.Net abstractions instead of raw socket --- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 2 +- .../TunerHosts/HdHomerun/HdHomerunManager.cs | 207 ++++++++++++--------- .../TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 139 +------------- Emby.Server.Implementations/Net/SocketFactory.cs | 42 ----- MediaBrowser.Model/Net/ISocketFactory.cs | 12 -- 5 files changed, 127 insertions(+), 275 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index ed524cae3..42acfbc5c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -255,7 +255,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var uri = new Uri(GetApiUrl(info)); - using (var manager = new HdHomerunManager(_socketFactory, Logger)) + using (var manager = new HdHomerunManager(Logger)) { // Legacy HdHomeruns are IPv4 only var ipInfo = IPAddress.Parse(uri.Host); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index 6e79441da..a6538be78 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -1,12 +1,13 @@ using System; +using System.Buffers; using System.Collections.Generic; using System.Net; +using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun @@ -78,37 +79,36 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public class HdHomerunManager : IDisposable { - public static int HdHomeRunPort = 65001; + public const int HdHomeRunPort = 65001; // Message constants - private static byte GetSetName = 3; - private static byte GetSetValue = 4; - private static byte GetSetLockkey = 21; - private static ushort GetSetRequest = 4; - private static ushort GetSetReply = 5; + private const byte GetSetName = 3; + private const byte GetSetValue = 4; + private const byte GetSetLockkey = 21; + private const ushort GetSetRequest = 4; + private const ushort GetSetReply = 5; + + private readonly ILogger _logger; private uint? _lockkey = null; private int _activeTuner = -1; - private readonly ISocketFactory _socketFactory; - private IPAddress _remoteIp; + private IPEndPoint _remoteEndPoint; - private ILogger _logger; - private ISocket _currentTcpSocket; + private TcpClient _tcpClient; - public HdHomerunManager(ISocketFactory socketFactory, ILogger logger) + public HdHomerunManager(ILogger logger) { - _socketFactory = socketFactory; _logger = logger; } public void Dispose() { - using (var socket = _currentTcpSocket) + using (var socket = _tcpClient) { if (socket != null) { - _currentTcpSocket = null; + _tcpClient = null; - var task = StopStreaming(socket); + var task = StopStreaming(_tcpClient); Task.WaitAll(task); } } @@ -116,35 +116,38 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public async Task CheckTunerAvailability(IPAddress remoteIp, int tuner, CancellationToken cancellationToken) { - using (var socket = _socketFactory.CreateTcpSocket(remoteIp, HdHomeRunPort)) + using (var client = new TcpClient(new IPEndPoint(remoteIp, HdHomeRunPort))) + using (var stream = client.GetStream()) { - return await CheckTunerAvailability(socket, remoteIp, tuner, cancellationToken).ConfigureAwait(false); + return await CheckTunerAvailability(stream, tuner, cancellationToken).ConfigureAwait(false); } } - private static async Task CheckTunerAvailability(ISocket socket, IPAddress remoteIp, int tuner, CancellationToken cancellationToken) + private static async Task CheckTunerAvailability(NetworkStream stream, int tuner, CancellationToken cancellationToken) { - var ipEndPoint = new IPEndPoint(remoteIp, HdHomeRunPort); - var lockkeyMsg = CreateGetMessage(tuner, "lockkey"); - await socket.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken); + await stream.WriteAsync(lockkeyMsg, 0, lockkeyMsg.Length, cancellationToken).ConfigureAwait(false); - var receiveBuffer = new byte[8192]; - var response = await socket.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false); + byte[] buffer = ArrayPool.Shared.Rent(8192); + try + { + int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); - ParseReturnMessage(response.Buffer, response.ReceivedBytes, out string returnVal); + ParseReturnMessage(buffer, receivedBytes, out string returnVal); - return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase); + return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase); + } + finally + { + ArrayPool.Shared.Return(buffer); + } } public async Task StartStreaming(IPAddress remoteIp, IPAddress localIp, int localPort, IHdHomerunChannelCommands commands, int numTuners, CancellationToken cancellationToken) { - _remoteIp = remoteIp; + _remoteEndPoint = new IPEndPoint(remoteIp, HdHomeRunPort); - var tcpClient = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort); - _currentTcpSocket = tcpClient; - - var receiveBuffer = new byte[8192]; + _tcpClient = new TcpClient(_remoteEndPoint); if (!_lockkey.HasValue) { @@ -153,51 +156,61 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } var lockKeyValue = _lockkey.Value; + var stream = _tcpClient.GetStream(); - var ipEndPoint = new IPEndPoint(_remoteIp, HdHomeRunPort); - - for (int i = 0; i < numTuners; ++i) + byte[] buffer = ArrayPool.Shared.Rent(8192); + try { - if (!await CheckTunerAvailability(tcpClient, _remoteIp, i, cancellationToken).ConfigureAwait(false)) - continue; - - _activeTuner = i; - var lockKeyString = string.Format("{0:d}", lockKeyValue); - var lockkeyMsg = CreateSetMessage(i, "lockkey", lockKeyString, null); - await tcpClient.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false); - var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false); - // parse response to make sure it worked - if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out var returnVal)) - continue; - - var commandList = commands.GetCommands(); - foreach (Tuple command in commandList) + for (int i = 0; i < numTuners; ++i) { - var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue); - await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false); - response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false); + if (!await CheckTunerAvailability(stream, i, cancellationToken).ConfigureAwait(false)) + { + continue; + } + + _activeTuner = i; + var lockKeyString = string.Format("{0:d}", lockKeyValue); + var lockkeyMsg = CreateSetMessage(i, "lockkey", lockKeyString, null); + await stream.WriteAsync(lockkeyMsg, 0, lockkeyMsg.Length, cancellationToken).ConfigureAwait(false); + int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked - if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal)) + if (!ParseReturnMessage(buffer, receivedBytes, out var returnVal)) { - await ReleaseLockkey(tcpClient, lockKeyValue).ConfigureAwait(false); continue; } - } + var commandList = commands.GetCommands(); + foreach (Tuple command in commandList) + { + var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue); + await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false); + receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + // parse response to make sure it worked + if (!ParseReturnMessage(buffer, receivedBytes, out returnVal)) + { + await ReleaseLockkey(_tcpClient, lockKeyValue).ConfigureAwait(false); + continue; + } + } - var targetValue = string.Format("rtp://{0}:{1}", localIp, localPort); - var targetMsg = CreateSetMessage(i, "target", targetValue, lockKeyValue); + var targetValue = string.Format("rtp://{0}:{1}", localIp, localPort); + var targetMsg = CreateSetMessage(i, "target", targetValue, lockKeyValue); - await tcpClient.SendToAsync(targetMsg, 0, targetMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false); - response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false); - // parse response to make sure it worked - if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal)) - { - await ReleaseLockkey(tcpClient, lockKeyValue).ConfigureAwait(false); - continue; - } + await stream.WriteAsync(targetMsg, 0, targetMsg.Length, cancellationToken).ConfigureAwait(false); + receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + // parse response to make sure it worked + if (!ParseReturnMessage(buffer, receivedBytes, out returnVal)) + { + await ReleaseLockkey(_tcpClient, lockKeyValue).ConfigureAwait(false); + continue; + } - return; + return; + } + } + finally + { + ArrayPool.Shared.Return(buffer); } _activeTuner = -1; @@ -207,53 +220,70 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public async Task ChangeChannel(IHdHomerunChannelCommands commands, CancellationToken cancellationToken) { if (!_lockkey.HasValue) + { return; + } - using (var tcpClient = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort)) + using (var tcpClient = new TcpClient(_remoteEndPoint)) + using (var stream = tcpClient.GetStream()) { var commandList = commands.GetCommands(); - var receiveBuffer = new byte[8192]; - - foreach (Tuple command in commandList) + byte[] buffer = ArrayPool.Shared.Rent(8192); + try { - var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey); - await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, new IPEndPoint(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false); - var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false); - // parse response to make sure it worked - if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out string returnVal)) + foreach (Tuple command in commandList) { - return; + var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey); + await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false); + int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + // parse response to make sure it worked + if (!ParseReturnMessage(buffer, receivedBytes, out string returnVal)) + { + return; + } } } + finally + { + ArrayPool.Shared.Return(buffer); + } } } - public Task StopStreaming(ISocket socket) + public Task StopStreaming(TcpClient client) { var lockKey = _lockkey; if (!lockKey.HasValue) + { return Task.CompletedTask; + } - return ReleaseLockkey(socket, lockKey.Value); + return ReleaseLockkey(client, lockKey.Value); } - private async Task ReleaseLockkey(ISocket tcpClient, uint lockKeyValue) + private async Task ReleaseLockkey(TcpClient client, uint lockKeyValue) { _logger.LogInformation("HdHomerunManager.ReleaseLockkey {0}", lockKeyValue); - var ipEndPoint = new IPEndPoint(_remoteIp, HdHomeRunPort); + var stream = client.GetStream(); var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue); - await tcpClient.SendToAsync(releaseTarget, 0, releaseTarget.Length, ipEndPoint, CancellationToken.None).ConfigureAwait(false); - - var receiveBuffer = new byte[8192]; + await stream.WriteAsync(releaseTarget, 0, releaseTarget.Length, CancellationToken.None).ConfigureAwait(false); - await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false); - var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", lockKeyValue); - _lockkey = null; - await tcpClient.SendToAsync(releaseKeyMsg, 0, releaseKeyMsg.Length, ipEndPoint, CancellationToken.None).ConfigureAwait(false); - await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false); + var buffer = ArrayPool.Shared.Rent(8192); + try + { + await stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None).ConfigureAwait(false); + var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", lockKeyValue); + _lockkey = null; + await stream.WriteAsync(releaseKeyMsg, 0, releaseKeyMsg.Length, CancellationToken.None).ConfigureAwait(false); + await stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None).ConfigureAwait(false); + } + finally + { + ArrayPool.Shared.Return(buffer); + } } private static byte[] CreateGetMessage(int tuner, string name) @@ -270,7 +300,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun // calculate crc and insert at the end of the message var crcBytes = BitConverter.GetBytes(HdHomerunCrc.GetCrc32(message, messageLength - 4)); if (flipEndian) + { Array.Reverse(crcBytes); + } + Buffer.BlockCopy(crcBytes, 0, message, offset, 4); return message; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index ec708cf20..1d79a5f96 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -49,13 +49,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun EnableStreamSharing = true; } - private static Socket CreateSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType) - { - var socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp); - - return socket; - } - public override async Task Open(CancellationToken openCancellationToken) { LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested(); @@ -71,13 +64,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var remoteAddress = IPAddress.Parse(uri.Host); IPAddress localAddress = null; - using (var tcpSocket = CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) + using (var tcpClient = new TcpClient()) { try { - tcpSocket.Connect(new IPEndPoint(remoteAddress, HdHomerunManager.HdHomeRunPort)); - localAddress = ((IPEndPoint)tcpSocket.LocalEndPoint).Address; - tcpSocket.Close(); + await tcpClient.ConnectAsync(remoteAddress, HdHomerunManager.HdHomeRunPort).ConfigureAwait(false); + localAddress = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address; + tcpClient.Close(); } catch (Exception ex) { @@ -87,7 +80,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } var udpClient = _socketFactory.CreateUdpSocket(localPort); - var hdHomerunManager = new HdHomerunManager(_socketFactory, Logger); + var hdHomerunManager = new HdHomerunManager(Logger); try { @@ -103,6 +96,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { Logger.LogError(ex, "Error opening live stream:"); } + throw; } } @@ -199,126 +193,5 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } } } - - public class UdpClientStream : Stream - { - private static int RtpHeaderBytes = 12; - private static int PacketSize = 1316; - private readonly MediaBrowser.Model.Net.ISocket _udpClient; - bool disposed; - - public UdpClientStream(MediaBrowser.Model.Net.ISocket udpClient) : base() - { - _udpClient = udpClient; - } - - public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (buffer == null) - throw new ArgumentNullException(nameof(buffer)); - - if (offset + count < 0) - throw new ArgumentOutOfRangeException(nameof(offset), "offset + count must not be negative"); - - if (offset + count > buffer.Length) - throw new ArgumentException("offset + count must not be greater than the length of buffer"); - - if (disposed) - throw new ObjectDisposedException(nameof(UdpClientStream)); - - // This will always receive a 1328 packet size (PacketSize + RtpHeaderSize) - // The RTP header will be stripped so see how many reads we need to make to fill the buffer. - int numReads = count / PacketSize; - int totalBytesRead = 0; - byte[] receiveBuffer = new byte[81920]; - - for (int i = 0; i < numReads; ++i) - { - var data = await _udpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false); - - var bytesRead = data.ReceivedBytes - RtpHeaderBytes; - - // remove rtp header - Buffer.BlockCopy(data.Buffer, RtpHeaderBytes, buffer, offset, bytesRead); - offset += bytesRead; - totalBytesRead += bytesRead; - } - return totalBytesRead; - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException(nameof(buffer)); - - if (offset + count < 0) - throw new ArgumentOutOfRangeException("offset + count must not be negative", "offset+count"); - - if (offset + count > buffer.Length) - throw new ArgumentException("offset + count must not be greater than the length of buffer"); - - if (disposed) - throw new ObjectDisposedException(nameof(UdpClientStream)); - - // This will always receive a 1328 packet size (PacketSize + RtpHeaderSize) - // The RTP header will be stripped so see how many reads we need to make to fill the buffer. - int numReads = count / PacketSize; - int totalBytesRead = 0; - byte[] receiveBuffer = new byte[81920]; - - for (int i = 0; i < numReads; ++i) - { - var receivedBytes = _udpClient.Receive(receiveBuffer, 0, receiveBuffer.Length); - - var bytesRead = receivedBytes - RtpHeaderBytes; - - // remove rtp header - Buffer.BlockCopy(receiveBuffer, RtpHeaderBytes, buffer, offset, bytesRead); - offset += bytesRead; - totalBytesRead += bytesRead; - } - return totalBytesRead; - } - - protected override void Dispose(bool disposing) - { - disposed = true; - } - - public override bool CanRead => throw new NotImplementedException(); - - public override bool CanSeek => throw new NotImplementedException(); - - public override bool CanWrite => throw new NotImplementedException(); - - public override long Length => throw new NotImplementedException(); - - public override long Position - { - get => throw new NotImplementedException(); - - set => throw new NotImplementedException(); - } - - public override void Flush() - { - throw new NotImplementedException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotImplementedException(); - } - - public override void SetLength(long value) - { - throw new NotImplementedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotImplementedException(); - } - } } } diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs index 42ffa4e22..cb53ce50c 100644 --- a/Emby.Server.Implementations/Net/SocketFactory.cs +++ b/Emby.Server.Implementations/Net/SocketFactory.cs @@ -2,54 +2,12 @@ using System; using System.IO; using System.Net; using System.Net.Sockets; -using Emby.Server.Implementations.Networking; using MediaBrowser.Model.Net; namespace Emby.Server.Implementations.Net { public class SocketFactory : ISocketFactory { - // THIS IS A LINKED FILE - SHARED AMONGST MULTIPLE PLATFORMS - // Be careful to check any changes compile and work for all platform projects it is shared in. - - // Not entirely happy with this. Would have liked to have done something more generic/reusable, - // but that wasn't really the point so kept to YAGNI principal for now, even if the - // interfaces are a bit ugly, specific and make assumptions. - - public ISocket CreateTcpSocket(IPAddress remoteAddress, int remotePort) - { - if (remotePort < 0) - { - throw new ArgumentException("remotePort cannot be less than zero.", nameof(remotePort)); - } - - var addressFamily = remoteAddress.AddressFamily == AddressFamily.InterNetwork - ? AddressFamily.InterNetwork - : AddressFamily.InterNetworkV6; - - var retVal = new Socket(addressFamily, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp); - - try - { - retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - } - catch (SocketException) - { - // This is not supported on all operating systems (qnap) - } - - try - { - return new UdpSocket(retVal, new IPEndPoint(remoteAddress, remotePort)); - } - catch - { - retVal?.Dispose(); - - throw; - } - } - /// /// Creates a new UDP acceptSocket and binds it to the specified local port. /// diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs index e58f4cc14..2f857f1af 100644 --- a/MediaBrowser.Model/Net/ISocketFactory.cs +++ b/MediaBrowser.Model/Net/ISocketFactory.cs @@ -17,8 +17,6 @@ namespace MediaBrowser.Model.Net ISocket CreateUdpBroadcastSocket(int localPort); - ISocket CreateTcpSocket(IPAddress remoteAddress, int remotePort); - /// /// Creates a new unicast socket using the specified local port number. /// @@ -35,14 +33,4 @@ namespace MediaBrowser.Model.Net Stream CreateNetworkStream(ISocket socket, bool ownsSocket); } - - public enum SocketType - { - Stream - } - - public enum ProtocolType - { - Tcp - } } -- cgit v1.2.3 From a5cb069f26ed6f4f1842ceb984342a4ef9c98d08 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 29 Jul 2019 16:30:16 +0200 Subject: Update HdHomerunManager.cs --- .../LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index a6538be78..c19552428 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -108,7 +108,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { _tcpClient = null; - var task = StopStreaming(_tcpClient); + var task = StopStreaming(socket); Task.WaitAll(task); } } -- cgit v1.2.3 From 2a58c643d24354ad8b6c45451bcedf82caa344c0 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 9 Aug 2019 23:16:24 +0200 Subject: Fix more warnings --- Emby.Server.Implementations/ApplicationHost.cs | 45 +++++++++++----------- Emby.Server.Implementations/Devices/DeviceId.cs | 1 - .../LiveTv/TunerHosts/M3uParser.cs | 1 + .../Networking/NetworkManager.cs | 38 +++++------------- Jellyfin.Server/Program.cs | 11 +++--- MediaBrowser.Common/Net/INetworkManager.cs | 4 +- MediaBrowser.Controller/IServerApplicationHost.cs | 2 +- MediaBrowser.Model/System/WakeOnLanInfo.cs | 37 ++++++++++++++++++ 8 files changed, 77 insertions(+), 62 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ef2f59d30..f0aa60428 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -108,9 +108,9 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; using ServiceStack; using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; @@ -386,7 +386,7 @@ namespace Emby.Server.Implementations fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); - NetworkManager.NetworkChanged += NetworkManager_NetworkChanged; + NetworkManager.NetworkChanged += OnNetworkChanged; } public string ExpandVirtualPath(string path) @@ -410,7 +410,7 @@ namespace Emby.Server.Implementations return ServerConfigurationManager.Configuration.LocalNetworkSubnets; } - private void NetworkManager_NetworkChanged(object sender, EventArgs e) + private void OnNetworkChanged(object sender, EventArgs e) { _validAddressResults.Clear(); } @@ -421,7 +421,7 @@ namespace Emby.Server.Implementations /// Gets the current application user agent /// /// The application user agent. - public string ApplicationUserAgent => Name.Replace(' ','-') + '/' + ApplicationVersion; + public string ApplicationUserAgent => Name.Replace(' ', '-') + "/" + ApplicationVersion; /// /// Gets the email address for use within a comment section of a user agent field. @@ -429,14 +429,11 @@ namespace Emby.Server.Implementations /// public string ApplicationUserAgentAddress { get; } = "team@jellyfin.org"; - private string _productName; - /// - /// Gets the current application name + /// Gets the current application name. /// /// The application name. - public string ApplicationProductName - => _productName ?? (_productName = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).ProductName); + public string ApplicationProductName { get; } = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).ProductName; private DeviceId _deviceId; @@ -604,10 +601,15 @@ namespace Emby.Server.Implementations foreach (var plugin in Plugins) { - pluginBuilder.AppendLine(string.Format("{0} {1}", plugin.Name, plugin.Version)); + pluginBuilder.AppendLine( + string.Format( + CultureInfo.InvariantCulture, + "{0} {1}", + plugin.Name, + plugin.Version)); } - Logger.LogInformation("Plugins: {plugins}", pluginBuilder.ToString()); + Logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString()); } DiscoverTypes(); @@ -629,7 +631,7 @@ namespace Emby.Server.Implementations if (EnableHttps && Certificate != null) { - options.ListenAnyIP(HttpsPort, listenOptions => { listenOptions.UseHttps(Certificate); }); + options.ListenAnyIP(HttpsPort, listenOptions => listenOptions.UseHttps(Certificate)); } }) .UseContentRoot(contentRoot) @@ -643,6 +645,7 @@ namespace Emby.Server.Implementations app.UseWebSockets(); app.UseResponseCompression(); + // TODO app.UseMiddleware(); app.Use(ExecuteWebsocketHandlerAsync); app.Use(ExecuteHttpHandlerAsync); @@ -1044,8 +1047,8 @@ namespace Emby.Server.Implementations .Cast() .ToList(); - await Task.WhenAll(StartEntryPoints(entries, true)); - await Task.WhenAll(StartEntryPoints(entries, false)); + await Task.WhenAll(StartEntryPoints(entries, true)).ConfigureAwait(false); + await Task.WhenAll(StartEntryPoints(entries, false)).ConfigureAwait(false); } /// @@ -1458,15 +1461,10 @@ namespace Emby.Server.Implementations }; } - public WakeOnLanInfo[] GetWakeOnLanInfo() - { - return NetworkManager.GetMacAddresses() - .Select(i => new WakeOnLanInfo - { - MacAddress = i - }) - .ToArray(); - } + public IEnumerable GetWakeOnLanInfo() + => NetworkManager.GetMacAddresses() + .Select(i => new WakeOnLanInfo(i)) + .ToList(); public async Task GetPublicSystemInfo(CancellationToken cancellationToken) { @@ -1482,6 +1480,7 @@ namespace Emby.Server.Implementations { wanAddress = GetWanApiUrl(ServerConfigurationManager.Configuration.WanDdns); } + return new PublicSystemInfo { Version = ApplicationVersion, diff --git a/Emby.Server.Implementations/Devices/DeviceId.cs b/Emby.Server.Implementations/Devices/DeviceId.cs index 495c3436a..a470fc482 100644 --- a/Emby.Server.Implementations/Devices/DeviceId.cs +++ b/Emby.Server.Implementations/Devices/DeviceId.cs @@ -2,7 +2,6 @@ using System; using System.IO; using System.Text; using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Devices diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 814031b12..a632db3eb 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -58,6 +58,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts UserAgent = _appHost.ApplicationUserAgent }); } + return Task.FromResult((Stream)File.OpenRead(url)); } diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index f613dc295..7d85a0666 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -425,47 +425,27 @@ namespace Emby.Server.Implementations.Networking var localEndPoint = new IPEndPoint(IPAddress.Any, 0); using (var udpClient = new UdpClient(localEndPoint)) { - var port = ((IPEndPoint)(udpClient.Client.LocalEndPoint)).Port; + var port = ((IPEndPoint)udpClient.Client.LocalEndPoint).Port; return port; } } - private List _macAddresses; - public List GetMacAddresses() + private List _macAddresses; + public List GetMacAddresses() { if (_macAddresses == null) { - _macAddresses = GetMacAddressesInternal(); + _macAddresses = GetMacAddressesInternal().ToList(); } + return _macAddresses; } - private static List GetMacAddressesInternal() - { - return NetworkInterface.GetAllNetworkInterfaces() + private static IEnumerable GetMacAddressesInternal() + => NetworkInterface.GetAllNetworkInterfaces() .Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback) - .Select(i => - { - try - { - var physicalAddress = i.GetPhysicalAddress(); - - if (physicalAddress == null) - { - return null; - } - - return physicalAddress.ToString(); - } - catch (Exception) - { - //TODO Log exception. - return null; - } - }) - .Where(i => i != null) - .ToList(); - } + .Select(x => x.GetPhysicalAddress()) + .Where(x => x != null && x != PhysicalAddress.None); /// /// Parses the specified endpointstring. diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 08c0983be..952990493 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -18,7 +18,6 @@ using Jellyfin.Drawing.Skia; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -41,12 +40,12 @@ namespace Jellyfin.Server // For backwards compatibility. // Modify any input arguments now which start with single-hyphen to POSIX standard // double-hyphen to allow parsing by CommandLineParser package. - const string pattern = @"^(-[^-\s]{2})"; // Match -xx, not -x, not --xx, not xx - const string substitution = @"-$1"; // Prepend with additional single-hyphen - var regex = new Regex(pattern); + const string Pattern = @"^(-[^-\s]{2})"; // Match -xx, not -x, not --xx, not xx + const string Substitution = @"-$1"; // Prepend with additional single-hyphen + var regex = new Regex(Pattern); for (var i = 0; i < args.Length; i++) { - args[i] = regex.Replace(args[i], substitution); + args[i] = regex.Replace(args[i], Substitution); } // Parse the command line arguments and either start the app or exit indicating error @@ -134,7 +133,7 @@ namespace Jellyfin.Server Batteries_V2.Init(); if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK) { - Console.WriteLine("WARN: Failed to enable shared cache for SQLite"); + _logger.LogWarning("Failed to enable shared cache for SQLite"); } using (var appHost = new CoreAppHost( diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index 61f2bc2f9..1df74d995 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Net; -using System.Threading.Tasks; +using System.Net.NetworkInformation; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; @@ -25,7 +25,7 @@ namespace MediaBrowser.Common.Net /// Returns MAC Address from first Network Card in Computer /// /// [string] MAC Address - List GetMacAddresses(); + List GetMacAddresses(); /// /// Determines whether [is in private address space] [the specified endpoint]. diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 3f8cc0b83..61b2c15ae 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -83,7 +83,7 @@ namespace MediaBrowser.Controller void EnableLoopback(string appName); - WakeOnLanInfo[] GetWakeOnLanInfo(); + IEnumerable GetWakeOnLanInfo(); string ExpandVirtualPath(string path); string ReverseVirtualPath(string path); diff --git a/MediaBrowser.Model/System/WakeOnLanInfo.cs b/MediaBrowser.Model/System/WakeOnLanInfo.cs index 031458735..534ad19ec 100644 --- a/MediaBrowser.Model/System/WakeOnLanInfo.cs +++ b/MediaBrowser.Model/System/WakeOnLanInfo.cs @@ -1,10 +1,47 @@ +using System.Net.NetworkInformation; + namespace MediaBrowser.Model.System { + /// + /// Provides the MAC address and port for wake-on-LAN functionality. + /// public class WakeOnLanInfo { + /// + /// Returns the MAC address of the device. + /// + /// The MAC address. public string MacAddress { get; set; } + + /// + /// Returns the wake-on-LAN port. + /// + /// The wake-on-LAN port. public int Port { get; set; } + /// + /// Initializes a new instance of the class. + /// + /// The MAC address. + public WakeOnLanInfo(PhysicalAddress macAddress) + { + MacAddress = macAddress.ToString(); + Port = 9; + } + + /// + /// Initializes a new instance of the class. + /// + /// The MAC address. + public WakeOnLanInfo(string macAddress) + { + MacAddress = macAddress; + Port = 9; + } + + /// + /// Initializes a new instance of the class. + /// public WakeOnLanInfo() { Port = 9; -- cgit v1.2.3 From 6032f31aa660e3b0fe1936217109f9fb47853ba3 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 28 Feb 2019 23:22:57 +0100 Subject: Use CultureInvariant string conversion for Guids --- Emby.Dlna/Didl/DidlBuilder.cs | 4 ++-- Emby.Dlna/DlnaManager.cs | 5 ++-- Emby.Dlna/Eventing/EventManager.cs | 2 +- Emby.Dlna/Main/DlnaEntryPoint.cs | 3 ++- Emby.Dlna/PlayTo/PlayToManager.cs | 5 ++-- Emby.Drawing/ImageProcessor.cs | 6 ++--- Emby.Notifications/NotificationManager.cs | 5 ++-- .../Activity/ActivityLogEntryPoint.cs | 4 ++-- .../Activity/ActivityRepository.cs | 4 ++-- Emby.Server.Implementations/ApplicationHost.cs | 2 +- .../Channels/ChannelManager.cs | 17 +++++++------- .../Collections/CollectionManager.cs | 3 ++- .../Data/SqliteDisplayPreferencesRepository.cs | 3 ++- .../Data/SqliteItemRepository.cs | 19 +++++++-------- Emby.Server.Implementations/Devices/DeviceId.cs | 4 ++-- .../Devices/DeviceManager.cs | 4 ++-- Emby.Server.Implementations/Dto/DtoService.cs | 17 +++++++------- .../EntryPoints/LibraryChangedNotifier.cs | 16 ++++++------- .../EntryPoints/ServerEventNotifier.cs | 3 ++- .../EntryPoints/UserDataChangeNotifier.cs | 6 ++--- .../HttpClientManager/HttpClientManager.cs | 2 +- .../IO/ManagedFileSystem.cs | 3 ++- .../Images/BaseDynamicImageProvider.cs | 3 ++- .../Library/ExclusiveLiveStream.cs | 3 ++- .../Library/LibraryManager.cs | 14 +++++------ .../Library/LiveStreamHelper.cs | 2 +- .../Library/MediaSourceManager.cs | 6 ++--- .../Library/UserDataManager.cs | 2 +- .../Library/UserViewManager.cs | 9 ++++---- .../Library/Validators/ArtistsValidator.cs | 3 ++- .../Library/Validators/PeopleValidator.cs | 4 ++-- .../Library/Validators/StudiosValidator.cs | 3 ++- .../LiveTv/EmbyTV/EmbyTV.cs | 8 +++---- .../LiveTv/Listings/XmlTvListingsProvider.cs | 4 ++-- .../LiveTv/LiveTvDtoService.cs | 27 +++++++++++----------- .../LiveTv/LiveTvManager.cs | 23 +++++++++--------- .../LiveTv/LiveTvMediaSourceProvider.cs | 3 ++- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 3 ++- .../LiveTv/TunerHosts/LiveStream.cs | 3 ++- .../LiveTv/TunerHosts/M3UTunerHost.cs | 7 +++--- .../LiveTv/TunerHosts/M3uParser.cs | 4 ++-- .../Playlists/PlaylistManager.cs | 7 +++--- .../ScheduledTasks/ScheduledTaskWorker.cs | 4 ++-- .../Security/AuthenticationRepository.cs | 2 +- .../Serialization/JsonSerializer.cs | 3 ++- .../Session/HttpSessionController.cs | 2 +- .../Session/SessionManager.cs | 14 ++++++----- Emby.Server.Implementations/TV/TVSeriesManager.cs | 3 ++- MediaBrowser.Api/Images/ImageService.cs | 3 ++- .../Library/LibraryStructureService.cs | 3 ++- MediaBrowser.Api/Movies/MoviesService.cs | 6 ++--- MediaBrowser.Api/Playback/BaseStreamingService.cs | 8 ++++++- MediaBrowser.Api/Playback/MediaInfoService.cs | 3 ++- MediaBrowser.Api/SearchService.cs | 5 ++-- MediaBrowser.Api/Session/SessionsService.cs | 3 ++- MediaBrowser.Api/Subtitles/SubtitleService.cs | 4 ++-- MediaBrowser.Api/TvShowsService.cs | 3 ++- MediaBrowser.Api/UserLibrary/ItemsService.cs | 2 +- MediaBrowser.Api/UserLibrary/UserViewsService.cs | 3 ++- MediaBrowser.Api/VideosService.cs | 3 ++- MediaBrowser.Controller/Channels/Channel.cs | 7 +++--- MediaBrowser.Controller/Entities/BaseItem.cs | 14 +++++------ MediaBrowser.Controller/Entities/Folder.cs | 5 ++-- MediaBrowser.Controller/Entities/LinkedChild.cs | 5 ++-- MediaBrowser.Controller/Entities/TV/Series.cs | 3 ++- MediaBrowser.Controller/Entities/User.cs | 3 ++- .../Entities/UserViewBuilder.cs | 3 ++- MediaBrowser.Controller/LiveTv/LiveTvChannel.cs | 4 ++-- MediaBrowser.Controller/LiveTv/LiveTvProgram.cs | 3 ++- MediaBrowser.Controller/Playlists/Playlist.cs | 3 ++- .../Providers/MetadataResult.cs | 3 ++- MediaBrowser.Providers/Manager/ProviderManager.cs | 8 +++---- .../MediaInfo/AudioImageProvider.cs | 7 +++--- .../Subtitles/SubtitleManager.cs | 5 ++-- jellyfin.ruleset | 4 ++++ 75 files changed, 240 insertions(+), 186 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index a21aff9f9..26adfde83 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -1082,7 +1082,7 @@ namespace Emby.Dlna.Didl public static string GetClientId(Guid idValue, StubType? stubType) { - var id = idValue.ToString("N"); + var id = idValue.ToString("N", CultureInfo.InvariantCulture); if (stubType.HasValue) { @@ -1096,7 +1096,7 @@ namespace Emby.Dlna.Didl { var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0", _serverAddress, - info.ItemId.ToString("N"), + info.ItemId.ToString("N", CultureInfo.InvariantCulture), info.Type, info.ImageTag, format, diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 2b76d2702..d5d788021 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -300,7 +301,7 @@ namespace Emby.Dlna profile = ReserializeProfile(tempProfile); - profile.Id = path.ToLowerInvariant().GetMD5().ToString("N"); + profile.Id = path.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture); _profiles[path] = new Tuple(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile); @@ -352,7 +353,7 @@ namespace Emby.Dlna Info = new DeviceProfileInfo { - Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N"), + Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture), Name = _fileSystem.GetFileNameWithoutExtension(file), Type = type } diff --git a/Emby.Dlna/Eventing/EventManager.cs b/Emby.Dlna/Eventing/EventManager.cs index b4ff3ec1d..4b542a820 100644 --- a/Emby.Dlna/Eventing/EventManager.cs +++ b/Emby.Dlna/Eventing/EventManager.cs @@ -55,7 +55,7 @@ namespace Emby.Dlna.Eventing public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl) { var timeout = ParseTimeout(requestedTimeoutString) ?? 300; - var id = "uuid:" + Guid.NewGuid().ToString("N"); + var id = "uuid:" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); // Remove logging for now because some devices are sending this very frequently // TODO re-enable with dlna debug logging setting diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 206a873e1..77bde0ca2 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -1,5 +1,6 @@ using System; using System.Net.Sockets; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using Emby.Dlna.PlayTo; @@ -307,7 +308,7 @@ namespace Emby.Dlna.Main { guid = text.GetMD5(); } - return guid.ToString("N"); + return guid.ToString("N", CultureInfo.InvariantCulture); } private void SetProperies(SsdpDevice device, string fullDeviceType) diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index c0a441871..a3a013096 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using System.Net; using System.Threading; @@ -141,7 +142,7 @@ namespace Emby.Dlna.PlayTo return usn; } - return usn.GetMD5().ToString("N"); + return usn.GetMD5().ToString("N", CultureInfo.InvariantCulture); } private async Task AddDevice(UpnpDeviceInfo info, string location, CancellationToken cancellationToken) @@ -156,7 +157,7 @@ namespace Emby.Dlna.PlayTo } else { - uuid = location.GetMD5().ToString("N"); + uuid = location.GetMD5().ToString("N", CultureInfo.InvariantCulture); } var sessionInfo = _sessionManager.LogSessionActivity("DLNA", _appHost.ApplicationVersion, uuid, null, uri.OriginalString, null); diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 6d209d8d0..a7d95eb20 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -454,14 +454,14 @@ namespace Emby.Drawing // Optimization if (imageEnhancers.Length == 0) { - return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N"); + return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture); } // Cache name is created with supported enhancers combined with the last config change so we pick up new config changes var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList(); cacheKeys.Add(originalImagePath + dateModified.Ticks); - return string.Join("|", cacheKeys).GetMD5().ToString("N"); + return string.Join("|", cacheKeys).GetMD5().ToString("N", CultureInfo.InvariantCulture); } private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified) @@ -480,7 +480,7 @@ namespace Emby.Drawing { try { - string filename = (originalImagePath + dateModified.Ticks.ToString(UsCulture)).GetMD5().ToString("N"); + string filename = (originalImagePath + dateModified.Ticks.ToString(UsCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture); string cacheExtension = _mediaEncoder().SupportsEncoder("libwebp") ? ".webp" : ".png"; var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension); diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs index 3d1d4722d..a767e541e 100644 --- a/Emby.Notifications/NotificationManager.cs +++ b/Emby.Notifications/NotificationManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -101,7 +102,7 @@ namespace Emby.Notifications var config = GetConfiguration(); return _userManager.Users - .Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N"), i.Policy)) + .Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N", CultureInfo.InvariantCulture), i.Policy)) .Select(i => i.Id); } @@ -197,7 +198,7 @@ namespace Emby.Notifications return _services.Select(i => new NameIdPair { Name = i.Name, - Id = i.Name.GetMD5().ToString("N") + Id = i.Name.GetMD5().ToString("N", CultureInfo.InvariantCulture) }).OrderBy(i => i.Name); } diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index 0530a251c..3e44c9c0a 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -75,7 +76,6 @@ namespace Emby.Server.Implementations.Activity _sessionManager.AuthenticationFailed += OnAuthenticationFailed; _sessionManager.AuthenticationSucceeded += OnAuthenticationSucceeded; _sessionManager.SessionEnded += OnSessionEnded; - _sessionManager.PlaybackStart += OnPlaybackStart; _sessionManager.PlaybackStopped += OnPlaybackStopped; @@ -117,7 +117,7 @@ namespace Emby.Server.Implementations.Activity { Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, Notifications.Notifications.GetItemName(e.Item)), Type = "SubtitleDownloadFailure", - ItemId = e.Item.Id.ToString("N"), + ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture), ShortOverview = e.Exception.Message }); } diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index de46ab965..91371b833 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Activity } else { - statement.TryBind("@UserId", entry.UserId.ToString("N")); + statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture)); } statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue()); @@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.Activity } else { - statement.TryBind("@UserId", entry.UserId.ToString("N")); + statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture)); } statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue()); diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ef2f59d30..4ff90a04b 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1220,7 +1220,7 @@ namespace Emby.Server.Implementations // Generate self-signed cert var certHost = GetHostnameFromExternalDns(ServerConfigurationManager.Configuration.WanDdns); - var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "2").GetMD5().ToString("N") + ".pfx"); + var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "2").GetMD5().ToString("N", CultureInfo.InvariantCulture) + ".pfx"); const string Password = "embycert"; return new CertificateInfo diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index e9961e8bd..8e5f5b561 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -206,7 +207,7 @@ namespace Emby.Server.Implementations.Channels try { - return GetChannelProvider(i).IsEnabledFor(user.Id.ToString("N")); + return GetChannelProvider(i).IsEnabledFor(user.Id.ToString("N", CultureInfo.InvariantCulture)); } catch { @@ -511,7 +512,7 @@ namespace Emby.Server.Implementations.Channels IncludeItemTypes = new[] { typeof(Channel).Name }, OrderBy = new ValueTuple[] { new ValueTuple(ItemSortBy.SortName, SortOrder.Ascending) } - }).Select(i => GetChannelFeatures(i.ToString("N"))).ToArray(); + }).Select(i => GetChannelFeatures(i.ToString("N", CultureInfo.InvariantCulture))).ToArray(); } public ChannelFeatures GetChannelFeatures(string id) @@ -552,7 +553,7 @@ namespace Emby.Server.Implementations.Channels SupportsSortOrderToggle = features.SupportsSortOrderToggle, SupportsLatestMedia = supportsLatest, Name = channel.Name, - Id = channel.Id.ToString("N"), + Id = channel.Id.ToString("N", CultureInfo.InvariantCulture), SupportsContentDownloading = features.SupportsContentDownloading, AutoRefreshLevels = features.AutoRefreshLevels }; @@ -740,7 +741,7 @@ namespace Emby.Server.Implementations.Channels bool sortDescending, CancellationToken cancellationToken) { - var userId = user == null ? null : user.Id.ToString("N"); + var userId = user == null ? null : user.Id.ToString("N", CultureInfo.InvariantCulture); var cacheLength = CacheLength; var cachePath = GetChannelDataCachePath(channel, userId, externalFolderId, sortField, sortDescending); @@ -836,7 +837,7 @@ namespace Emby.Server.Implementations.Channels ChannelItemSortField? sortField, bool sortDescending) { - var channelId = GetInternalChannelId(channel.Name).ToString("N"); + var channelId = GetInternalChannelId(channel.Name).ToString("N", CultureInfo.InvariantCulture); var userCacheKey = string.Empty; @@ -846,10 +847,10 @@ namespace Emby.Server.Implementations.Channels userCacheKey = hasCacheKey.GetCacheKey(userId) ?? string.Empty; } - var filename = string.IsNullOrEmpty(externalFolderId) ? "root" : externalFolderId.GetMD5().ToString("N"); + var filename = string.IsNullOrEmpty(externalFolderId) ? "root" : externalFolderId.GetMD5().ToString("N", CultureInfo.InvariantCulture); filename += userCacheKey; - var version = ((channel.DataVersion ?? string.Empty) + "2").GetMD5().ToString("N"); + var version = ((channel.DataVersion ?? string.Empty) + "2").GetMD5().ToString("N", CultureInfo.InvariantCulture); if (sortField.HasValue) { @@ -860,7 +861,7 @@ namespace Emby.Server.Implementations.Channels filename += "-sortDescending"; } - filename = filename.GetMD5().ToString("N"); + filename = filename.GetMD5().ToString("N", CultureInfo.InvariantCulture); return Path.Combine(_config.ApplicationPaths.CachePath, "channels", diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 2b99e0ddf..bb5057b1c 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -182,7 +183,7 @@ namespace Emby.Server.Implementations.Collections public void AddToCollection(Guid collectionId, IEnumerable ids) { - AddToCollection(collectionId, ids.Select(i => i.ToString("N")), true, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))); + AddToCollection(collectionId, ids.Select(i => i.ToString("N", CultureInfo.InvariantCulture)), true, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))); } private void AddToCollection(Guid collectionId, IEnumerable ids, bool fireEvent, MetadataRefreshOptions refreshOptions) diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs index 01ef9851d..87096e72f 100644 --- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Threading; using MediaBrowser.Common.Configuration; @@ -182,7 +183,7 @@ namespace Emby.Server.Implementations.Data return new DisplayPreferences { - Id = guidId.ToString("N") + Id = guidId.ToString("N", CultureInfo.InvariantCulture) }; } } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 1cefcec7c..bb4c34f02 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -696,7 +696,7 @@ namespace Emby.Server.Implementations.Data saveItemStatement.TryBindNull("@EndDate"); } - saveItemStatement.TryBind("@ChannelId", item.ChannelId.Equals(Guid.Empty) ? null : item.ChannelId.ToString("N")); + saveItemStatement.TryBind("@ChannelId", item.ChannelId.Equals(Guid.Empty) ? null : item.ChannelId.ToString("N", CultureInfo.InvariantCulture)); if (item is IHasProgramAttributes hasProgramAttributes) { @@ -851,7 +851,7 @@ namespace Emby.Server.Implementations.Data } else { - saveItemStatement.TryBind("@TopParentId", topParent.Id.ToString("N")); + saveItemStatement.TryBind("@TopParentId", topParent.Id.ToString("N", CultureInfo.InvariantCulture)); } if (item is Trailer trailer && trailer.TrailerTypes.Length > 0) @@ -3548,12 +3548,12 @@ namespace Emby.Server.Implementations.Data whereClauses.Add("ChannelId=@ChannelId"); if (statement != null) { - statement.TryBind("@ChannelId", query.ChannelIds[0].ToString("N")); + statement.TryBind("@ChannelId", query.ChannelIds[0].ToString("N", CultureInfo.InvariantCulture)); } } else if (query.ChannelIds.Length > 1) { - var inClause = string.Join(",", query.ChannelIds.Select(i => "'" + i.ToString("N") + "'")); + var inClause = string.Join(",", query.ChannelIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'")); whereClauses.Add($"ChannelId in ({inClause})"); } @@ -4537,12 +4537,12 @@ namespace Emby.Server.Implementations.Data } if (statement != null) { - statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N")); + statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N", CultureInfo.InvariantCulture)); } } else if (queryTopParentIds.Length > 1) { - var val = string.Join(",", queryTopParentIds.Select(i => "'" + i.ToString("N") + "'")); + var val = string.Join(",", queryTopParentIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'")); if (enableItemsByName && includedItemByNameTypes.Count == 1) { @@ -4574,7 +4574,7 @@ namespace Emby.Server.Implementations.Data } if (query.AncestorIds.Length > 1) { - var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N") + "'")); + var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'")); whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause)); } if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey)) @@ -4637,7 +4637,7 @@ namespace Emby.Server.Implementations.Data foreach (var folderId in query.BoxSetLibraryFolders) { - folderIdQueries.Add("data like '%" + folderId.ToString("N") + "%'"); + folderIdQueries.Add("data like '%" + folderId.ToString("N", CultureInfo.InvariantCulture) + "%'"); } whereClauses.Add("(" + string.Join(" OR ", folderIdQueries) + ")"); @@ -5161,7 +5161,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type var ancestorId = ancestorIds[i]; statement.TryBind("@AncestorId" + index, ancestorId.ToGuidBlob()); - statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N")); + statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N", CultureInfo.InvariantCulture)); } statement.Reset(); @@ -5579,6 +5579,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type { counts.TrailerCount = value; } + counts.ItemCount += value; } diff --git a/Emby.Server.Implementations/Devices/DeviceId.cs b/Emby.Server.Implementations/Devices/DeviceId.cs index 495c3436a..7344dc72f 100644 --- a/Emby.Server.Implementations/Devices/DeviceId.cs +++ b/Emby.Server.Implementations/Devices/DeviceId.cs @@ -1,8 +1,8 @@ using System; +using System.Globalization; using System.IO; using System.Text; using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Devices @@ -67,7 +67,7 @@ namespace Emby.Server.Implementations.Devices private static string GetNewId() { - return Guid.NewGuid().ToString("N"); + return Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); } private string GetDeviceId() diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index 7d6529a67..d1704b373 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Entities; @@ -195,7 +195,7 @@ namespace Emby.Server.Implementations.Devices private string GetDevicePath(string id) { - return Path.Combine(GetDevicesPath(), id.GetMD5().ToString("N")); + return Path.Combine(GetDevicesPath(), id.GetMD5().ToString("N", CultureInfo.InvariantCulture)); } public ContentUploadHistory GetCameraUploadHistory(string deviceId) diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 2f1b60be9..6e7aa1313 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -213,7 +214,7 @@ namespace Emby.Server.Implementations.Dto if (options.ContainsField(ItemFields.DisplayPreferencesId)) { - dto.DisplayPreferencesId = item.DisplayPreferencesId.ToString("N"); + dto.DisplayPreferencesId = item.DisplayPreferencesId.ToString("N", CultureInfo.InvariantCulture); } if (user != null) @@ -444,7 +445,7 @@ namespace Emby.Server.Implementations.Dto /// item public string GetDtoId(BaseItem item) { - return item.Id.ToString("N"); + return item.Id.ToString("N", CultureInfo.InvariantCulture); } private static void SetBookProperties(BaseItemDto dto, Book item) @@ -608,7 +609,7 @@ namespace Emby.Server.Implementations.Dto if (dictionary.TryGetValue(person.Name, out Person entity)) { baseItemPerson.PrimaryImageTag = GetImageCacheTag(entity, ImageType.Primary); - baseItemPerson.Id = entity.Id.ToString("N"); + baseItemPerson.Id = entity.Id.ToString("N", CultureInfo.InvariantCulture); list.Add(baseItemPerson); } } @@ -893,7 +894,7 @@ namespace Emby.Server.Implementations.Dto //var artistItems = _libraryManager.GetArtists(new InternalItemsQuery //{ // EnableTotalRecordCount = false, - // ItemIds = new[] { item.Id.ToString("N") } + // ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) } //}); //dto.ArtistItems = artistItems.Items @@ -903,7 +904,7 @@ namespace Emby.Server.Implementations.Dto // return new NameIdPair // { // Name = artist.Name, - // Id = artist.Id.ToString("N") + // Id = artist.Id.ToString("N", CultureInfo.InvariantCulture) // }; // }) // .ToList(); @@ -946,7 +947,7 @@ namespace Emby.Server.Implementations.Dto //var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery //{ // EnableTotalRecordCount = false, - // ItemIds = new[] { item.Id.ToString("N") } + // ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) } //}); //dto.AlbumArtists = artistItems.Items @@ -956,7 +957,7 @@ namespace Emby.Server.Implementations.Dto // return new NameIdPair // { // Name = artist.Name, - // Id = artist.Id.ToString("N") + // Id = artist.Id.ToString("N", CultureInfo.InvariantCulture) // }; // }) // .ToList(); @@ -1044,7 +1045,7 @@ namespace Emby.Server.Implementations.Dto } else { - string id = item.Id.ToString("N"); + string id = item.Id.ToString("N", CultureInfo.InvariantCulture); mediaStreams = dto.MediaSources.Where(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)) .SelectMany(i => i.MediaStreams) .ToArray(); diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 8369f4f59..9c0db2cf5 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -100,7 +100,7 @@ namespace Emby.Server.Implementations.EntryPoints _lastProgressMessageTimes[item.Id] = DateTime.UtcNow; var dict = new Dictionary(); - dict["ItemId"] = item.Id.ToString("N"); + dict["ItemId"] = item.Id.ToString("N", CultureInfo.InvariantCulture); dict["Progress"] = progress.ToString(CultureInfo.InvariantCulture); try @@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.EntryPoints foreach (var collectionFolder in collectionFolders) { var collectionFolderDict = new Dictionary(); - collectionFolderDict["ItemId"] = collectionFolder.Id.ToString("N"); + collectionFolderDict["ItemId"] = collectionFolder.Id.ToString("N", CultureInfo.InvariantCulture); collectionFolderDict["Progress"] = (collectionFolder.GetRefreshProgress() ?? 0).ToString(CultureInfo.InvariantCulture); try @@ -378,15 +378,15 @@ namespace Emby.Server.Implementations.EntryPoints return new LibraryUpdateInfo { - ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(), + ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(), + ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)).Select(i => i.Id.ToString("N")).Distinct().ToArray(), + ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(), + FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(), + FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), CollectionFolders = GetTopParentIds(newAndRemoved, allUserRootChildren).ToArray() }; @@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.EntryPoints var collectionFolders = _libraryManager.GetCollectionFolders(item, allUserRootChildren); foreach (var folder in allUserRootChildren) { - list.Add(folder.Id.ToString("N")); + list.Add(folder.Id.ToString("N", CultureInfo.InvariantCulture)); } } diff --git a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs index 091dd6a45..141e72958 100644 --- a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Plugins; @@ -134,7 +135,7 @@ namespace Emby.Server.Implementations.EntryPoints /// The e. void userManager_UserDeleted(object sender, GenericEventArgs e) { - SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N")); + SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture)); } void _userManager_UserPolicyUpdated(object sender, GenericEventArgs e) diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs index a5badacee..bae3422ff 100644 --- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -8,7 +9,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; @@ -125,12 +125,12 @@ namespace Emby.Server.Implementations.EntryPoints .Select(i => { var dto = _userDataManager.GetUserDataDto(i, user); - dto.ItemId = i.Id.ToString("N"); + dto.ItemId = i.Id.ToString("N", CultureInfo.InvariantCulture); return dto; }) .ToArray(); - var userIdString = userId.ToString("N"); + var userIdString = userId.ToString("N", CultureInfo.InvariantCulture); return new UserDataChangeInfo { diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 331b5e29d..9ca33d7db 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -195,7 +195,7 @@ namespace Emby.Server.Implementations.HttpClientManager } var url = options.Url; - var urlHash = url.ToLowerInvariant().GetMD5().ToString("N"); + var urlHash = url.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture); var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash); diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 8517abed6..ae8371a32 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Diagnostics; using System.IO; using System.Linq; @@ -555,7 +556,7 @@ namespace Emby.Server.Implementations.IO throw new ArgumentNullException(nameof(file2)); } - var temp1 = Path.Combine(_tempPath, Guid.NewGuid().ToString("N")); + var temp1 = Path.Combine(_tempPath, Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)); // Copying over will fail against hidden files SetHidden(file1, false); diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs index 46f209b4b..d8faceadb 100644 --- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -89,7 +90,7 @@ namespace Emby.Server.Implementations.Images ImageType imageType, CancellationToken cancellationToken) { - var outputPathWithoutExtension = Path.Combine(ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N")); + var outputPathWithoutExtension = Path.Combine(ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)); Directory.CreateDirectory(Path.GetDirectoryName(outputPathWithoutExtension)); string outputPath = CreateImage(item, itemsWithImages, outputPathWithoutExtension, imageType, 0); diff --git a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs index 45a33a296..a3c879f12 100644 --- a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs +++ b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Library; @@ -26,7 +27,7 @@ namespace Emby.Server.Implementations.Library EnableStreamSharing = false; _closeFn = closeFn; ConsumerCount = 1; - UniqueId = Guid.NewGuid().ToString("N"); + UniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); } public Task Close() diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 4b5063ada..30ff855cc 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1187,12 +1187,12 @@ namespace Emby.Server.Implementations.Library if (libraryFolder != null && libraryFolder.HasImage(ImageType.Primary)) { - info.PrimaryImageItemId = libraryFolder.Id.ToString("N"); + info.PrimaryImageItemId = libraryFolder.Id.ToString("N", CultureInfo.InvariantCulture); } if (libraryFolder != null) { - info.ItemId = libraryFolder.Id.ToString("N"); + info.ItemId = libraryFolder.Id.ToString("N", CultureInfo.InvariantCulture); info.LibraryOptions = GetLibraryOptions(libraryFolder); if (refreshQueue != null) @@ -2135,12 +2135,12 @@ namespace Emby.Server.Implementations.Library string viewType, string sortName) { - var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N"); - var idValues = "38_namedview_" + name + user.Id.ToString("N") + (parentIdString ?? string.Empty) + (viewType ?? string.Empty); + var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N", CultureInfo.InvariantCulture); + var idValues = "38_namedview_" + name + user.Id.ToString("N", CultureInfo.InvariantCulture) + (parentIdString ?? string.Empty) + (viewType ?? string.Empty); var id = GetNewItemId(idValues, typeof(UserView)); - var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N")); + var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture)); var item = GetItemById(id) as UserView; @@ -2271,7 +2271,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(name)); } - var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N"); + var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N", CultureInfo.InvariantCulture); var idValues = "37_namedview_" + name + (parentIdString ?? string.Empty) + (viewType ?? string.Empty); if (!string.IsNullOrEmpty(uniqueId)) { @@ -2280,7 +2280,7 @@ namespace Emby.Server.Implementations.Library var id = GetNewItemId(idValues, typeof(UserView)); - var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N")); + var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture)); var item = GetItemById(id) as UserView; diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index c3082a78a..33e6f2434 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.Library var now = DateTime.UtcNow; MediaInfo mediaInfo = null; - var cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N") + ".json"); + var cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N", CultureInfo.InvariantCulture) + ".json"); if (!string.IsNullOrEmpty(cacheKey)) { diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 24ab8e761..d83e1fc02 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -269,7 +269,7 @@ namespace Emby.Server.Implementations.Library private static void SetKeyProperties(IMediaSourceProvider provider, MediaSourceInfo mediaSource) { - var prefix = provider.GetType().FullName.GetMD5().ToString("N") + LiveStreamIdDelimeter; + var prefix = provider.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture) + LiveStreamIdDelimeter; if (!string.IsNullOrEmpty(mediaSource.OpenToken) && !mediaSource.OpenToken.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { @@ -626,7 +626,7 @@ namespace Emby.Server.Implementations.Library var now = DateTime.UtcNow; MediaInfo mediaInfo = null; - var cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N") + ".json"); + var cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N", CultureInfo.InvariantCulture) + ".json"); if (!string.IsNullOrEmpty(cacheKey)) { @@ -854,7 +854,7 @@ namespace Emby.Server.Implementations.Library var keys = key.Split(new[] { LiveStreamIdDelimeter }, 2); - var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), keys[0], StringComparison.OrdinalIgnoreCase)); + var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture), keys[0], StringComparison.OrdinalIgnoreCase)); var splitIndex = key.IndexOf(LiveStreamIdDelimeter); var keyId = key.Substring(splitIndex + 1); diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index dfa1edaff..36adc0b9c 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -152,7 +152,7 @@ namespace Emby.Server.Implementations.Library /// System.String. private static string GetCacheKey(long internalUserId, Guid itemId) { - return internalUserId.ToString(CultureInfo.InvariantCulture) + "-" + itemId.ToString("N"); + return internalUserId.ToString(CultureInfo.InvariantCulture) + "-" + itemId.ToString("N", CultureInfo.InvariantCulture); } public UserItemData GetUserData(User user, BaseItem item) diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index e9ce682ee..71f16ac3e 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using MediaBrowser.Controller.Channels; @@ -117,7 +118,7 @@ namespace Emby.Server.Implementations.Library if (!query.IncludeHidden) { - list = list.Where(i => !user.Configuration.MyMediaExcludes.Contains(i.Id.ToString("N"))).ToList(); + list = list.Where(i => !user.Configuration.MyMediaExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))).ToList(); } var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList(); @@ -127,7 +128,7 @@ namespace Emby.Server.Implementations.Library return list .OrderBy(i => { - var index = orders.IndexOf(i.Id.ToString("N")); + var index = orders.IndexOf(i.Id.ToString("N", CultureInfo.InvariantCulture)); if (index == -1) { @@ -136,7 +137,7 @@ namespace Emby.Server.Implementations.Library { if (!view.DisplayParentId.Equals(Guid.Empty)) { - index = orders.IndexOf(view.DisplayParentId.ToString("N")); + index = orders.IndexOf(view.DisplayParentId.ToString("N", CultureInfo.InvariantCulture)); } } } @@ -269,7 +270,7 @@ namespace Emby.Server.Implementations.Library { parents = _libraryManager.GetUserRootFolder().GetChildren(user, true) .Where(i => i is Folder) - .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N"))) + .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))) .ToList(); } diff --git a/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs b/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs index 294348660..b584cc649 100644 --- a/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs +++ b/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -91,7 +92,7 @@ namespace Emby.Server.Implementations.Library.Validators continue; } - _logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N"), item.Name, item.GetType().Name); + _logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name); _libraryManager.DeleteItem(item, new DeleteOptions { diff --git a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs index 7899cf01b..d00c6cde1 100644 --- a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs +++ b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs @@ -1,7 +1,7 @@ using System; +using System.Globalization; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -96,7 +96,7 @@ namespace Emby.Server.Implementations.Library.Validators foreach (var item in deadEntities) { - _logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N"), item.Name, item.GetType().Name); + _logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name); _libraryManager.DeleteItem(item, new DeleteOptions { diff --git a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs index da4645a11..93ded9e7b 100644 --- a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs +++ b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; @@ -76,7 +77,7 @@ namespace Emby.Server.Implementations.Library.Validators foreach (var item in deadEntities) { - _logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N"), item.Name, item.GetType().Name); + _logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name); _libraryManager.DeleteItem(item, new DeleteOptions { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 7b210d231..d7411af50 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -681,7 +681,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - timer.Id = Guid.NewGuid().ToString("N"); + timer.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); LiveTvProgram programInfo = null; @@ -713,7 +713,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public async Task CreateSeriesTimer(SeriesTimerInfo info, CancellationToken cancellationToken) { - info.Id = Guid.NewGuid().ToString("N"); + info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); // populate info.seriesID var program = GetProgramInfoFromCache(info.ProgramId); @@ -1059,7 +1059,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var json = _jsonSerializer.SerializeToString(mediaSource); mediaSource = _jsonSerializer.DeserializeFromString(json); - mediaSource.Id = Guid.NewGuid().ToString("N") + "_" + mediaSource.Id; + mediaSource.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture) + "_" + mediaSource.Id; //if (mediaSource.DateLiveStreamOpened.HasValue && enableStreamSharing) //{ @@ -2529,7 +2529,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var timer = new TimerInfo { ChannelId = channelId, - Id = (seriesTimer.Id + parent.ExternalId).GetMD5().ToString("N"), + Id = (seriesTimer.Id + parent.ExternalId).GetMD5().ToString("N", CultureInfo.InvariantCulture), StartDate = parent.StartDate, EndDate = parent.EndDate.Value, ProgramId = parent.ExternalId, diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 94225a0aa..88693f22a 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -211,7 +211,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings HasImage = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source), OfficialRating = program.Rating != null && !string.IsNullOrEmpty(program.Rating.Value) ? program.Rating.Value : null, CommunityRating = program.StarRating, - SeriesId = program.Episode == null ? null : program.Title.GetMD5().ToString("N") + SeriesId = program.Episode == null ? null : program.Title.GetMD5().ToString("N", CultureInfo.InvariantCulture) }; if (string.IsNullOrWhiteSpace(program.ProgramId)) @@ -227,7 +227,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings uniqueString = "-" + programInfo.EpisodeNumber.Value.ToString(CultureInfo.InvariantCulture); } - programInfo.ShowId = uniqueString.GetMD5().ToString("N"); + programInfo.ShowId = uniqueString.GetMD5().ToString("N", CultureInfo.InvariantCulture); // If we don't have valid episode info, assume it's a unique program, otherwise recordings might be skipped if (programInfo.IsSeries diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs index 1144c9ab1..e584664c9 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -52,7 +53,7 @@ namespace Emby.Server.Implementations.LiveTv ExternalId = info.Id, ChannelId = GetInternalChannelId(service.Name, info.ChannelId), Status = info.Status, - SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null : GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N"), + SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null : GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N", CultureInfo.InvariantCulture), PrePaddingSeconds = info.PrePaddingSeconds, PostPaddingSeconds = info.PostPaddingSeconds, IsPostPaddingRequired = info.IsPostPaddingRequired, @@ -69,7 +70,7 @@ namespace Emby.Server.Implementations.LiveTv if (!string.IsNullOrEmpty(info.ProgramId)) { - dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N"); + dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N", CultureInfo.InvariantCulture); } if (program != null) @@ -107,7 +108,7 @@ namespace Emby.Server.Implementations.LiveTv { var dto = new SeriesTimerInfoDto { - Id = GetInternalSeriesTimerId(info.Id).ToString("N"), + Id = GetInternalSeriesTimerId(info.Id).ToString("N", CultureInfo.InvariantCulture), Overview = info.Overview, EndDate = info.EndDate, Name = info.Name, @@ -139,7 +140,7 @@ namespace Emby.Server.Implementations.LiveTv if (!string.IsNullOrEmpty(info.ProgramId)) { - dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N"); + dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N", CultureInfo.InvariantCulture); } dto.DayPattern = info.Days == null ? null : GetDayPattern(info.Days.ToArray()); @@ -169,7 +170,7 @@ namespace Emby.Server.Implementations.LiveTv try { dto.ParentThumbImageTag = _imageProcessor.GetImageCacheTag(librarySeries, image); - dto.ParentThumbItemId = librarySeries.Id.ToString("N"); + dto.ParentThumbItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -185,7 +186,7 @@ namespace Emby.Server.Implementations.LiveTv { _imageProcessor.GetImageCacheTag(librarySeries, image) }; - dto.ParentBackdropItemId = librarySeries.Id.ToString("N"); + dto.ParentBackdropItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -213,7 +214,7 @@ namespace Emby.Server.Implementations.LiveTv try { dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image); - dto.ParentPrimaryImageItemId = program.Id.ToString("N"); + dto.ParentPrimaryImageItemId = program.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -232,7 +233,7 @@ namespace Emby.Server.Implementations.LiveTv { _imageProcessor.GetImageCacheTag(program, image) }; - dto.ParentBackdropItemId = program.Id.ToString("N"); + dto.ParentBackdropItemId = program.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -263,7 +264,7 @@ namespace Emby.Server.Implementations.LiveTv try { dto.ParentThumbImageTag = _imageProcessor.GetImageCacheTag(librarySeries, image); - dto.ParentThumbItemId = librarySeries.Id.ToString("N"); + dto.ParentThumbItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -279,7 +280,7 @@ namespace Emby.Server.Implementations.LiveTv { _imageProcessor.GetImageCacheTag(librarySeries, image) }; - dto.ParentBackdropItemId = librarySeries.Id.ToString("N"); + dto.ParentBackdropItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -320,7 +321,7 @@ namespace Emby.Server.Implementations.LiveTv try { dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image); - dto.ParentPrimaryImageItemId = program.Id.ToString("N"); + dto.ParentPrimaryImageItemId = program.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -339,7 +340,7 @@ namespace Emby.Server.Implementations.LiveTv { _imageProcessor.GetImageCacheTag(program, image) }; - dto.ParentBackdropItemId = program.Id.ToString("N"); + dto.ParentBackdropItemId = program.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -407,7 +408,7 @@ namespace Emby.Server.Implementations.LiveTv { var name = ServiceName + externalId + InternalVersionNumber; - return name.ToLowerInvariant().GetMD5().ToString("N"); + return name.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture); } public Guid GetInternalSeriesTimerId(string externalId) diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 9093d9740..1e5198dd6 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -258,7 +259,7 @@ namespace Emby.Server.Implementations.LiveTv } info.RequiresClosing = true; - var idPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_"; + var idPrefix = service.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture) + "_"; info.LiveStreamId = idPrefix + info.Id; @@ -820,7 +821,7 @@ namespace Emby.Server.Implementations.LiveTv if (!string.IsNullOrWhiteSpace(query.SeriesTimerId)) { var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery { }, cancellationToken).ConfigureAwait(false); - var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N"), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase)); + var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N", CultureInfo.InvariantCulture), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase)); if (seriesTimer != null) { internalQuery.ExternalSeriesId = seriesTimer.SeriesId; @@ -997,7 +998,7 @@ namespace Emby.Server.Implementations.LiveTv if (!string.IsNullOrEmpty(timer.SeriesTimerId)) { program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(timer.SeriesTimerId) - .ToString("N"); + .ToString("N", CultureInfo.InvariantCulture); foundSeriesTimer = true; } @@ -1018,7 +1019,7 @@ namespace Emby.Server.Implementations.LiveTv if (seriesTimer != null) { program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(seriesTimer.Id) - .ToString("N"); + .ToString("N", CultureInfo.InvariantCulture); } } } @@ -1472,7 +1473,7 @@ namespace Emby.Server.Implementations.LiveTv dto.SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null - : _tvDtoService.GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N"); + : _tvDtoService.GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N", CultureInfo.InvariantCulture); dto.TimerId = string.IsNullOrEmpty(info.Id) ? null @@ -2027,7 +2028,7 @@ namespace Emby.Server.Implementations.LiveTv info.StartDate = program.StartDate; info.Name = program.Name; info.Overview = program.Overview; - info.ProgramId = programDto.Id.ToString("N"); + info.ProgramId = programDto.Id.ToString("N", CultureInfo.InvariantCulture); info.ExternalProgramId = program.ExternalId; if (program.EndDate.HasValue) @@ -2088,7 +2089,7 @@ namespace Emby.Server.Implementations.LiveTv if (service is ISupportsNewTimerIds supportsNewTimerIds) { newTimerId = await supportsNewTimerIds.CreateSeriesTimer(info, cancellationToken).ConfigureAwait(false); - newTimerId = _tvDtoService.GetInternalSeriesTimerId(newTimerId).ToString("N"); + newTimerId = _tvDtoService.GetInternalSeriesTimerId(newTimerId).ToString("N", CultureInfo.InvariantCulture); } else { @@ -2192,7 +2193,7 @@ namespace Emby.Server.Implementations.LiveTv info.EnabledUsers = _userManager.Users .Where(IsLiveTvEnabled) - .Select(i => i.Id.ToString("N")) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) .ToArray(); return info; @@ -2219,7 +2220,7 @@ namespace Emby.Server.Implementations.LiveTv { var parts = id.Split(new[] { '_' }, 2); - var service = _services.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), parts[0], StringComparison.OrdinalIgnoreCase)); + var service = _services.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture), parts[0], StringComparison.OrdinalIgnoreCase)); if (service == null) { @@ -2269,7 +2270,7 @@ namespace Emby.Server.Implementations.LiveTv if (index == -1 || string.IsNullOrWhiteSpace(info.Id)) { - info.Id = Guid.NewGuid().ToString("N"); + info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); list.Add(info); config.TunerHosts = list.ToArray(); } @@ -2312,7 +2313,7 @@ namespace Emby.Server.Implementations.LiveTv if (index == -1 || string.IsNullOrWhiteSpace(info.Id)) { - info.Id = Guid.NewGuid().ToString("N"); + info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); list.Add(info); config.ListingProviders = list.ToArray(); } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index cd1731de5..52d60c004 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -101,7 +102,7 @@ namespace Emby.Server.Implementations.LiveTv { var openKeys = new List(); openKeys.Add(item.GetType().Name); - openKeys.Add(item.Id.ToString("N")); + openKeys.Add(item.Id.ToString("N", CultureInfo.InvariantCulture)); openKeys.Add(source.Id ?? string.Empty); source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray()); } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index ed524cae3..db016ec70 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -460,7 +461,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { id = "native"; } - id += "_" + channelId.GetMD5().ToString("N") + "_" + url.GetMD5().ToString("N"); + id += "_" + channelId.GetMD5().ToString("N", CultureInfo.InvariantCulture) + "_" + url.GetMD5().ToString("N", CultureInfo.InvariantCulture); var mediaSource = new MediaSourceInfo { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index ece2cbd54..b4395e2e1 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -42,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts MediaSource = mediaSource; Logger = logger; EnableStreamSharing = true; - UniqueId = Guid.NewGuid().ToString("N"); + UniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); if (tuner != null) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 2d9bec53f..6c5c80827 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts private string GetFullChannelIdPrefix(TunerHostInfo info) { - return ChannelIdPrefix + info.Url.GetMD5().ToString("N"); + return ChannelIdPrefix + info.Url.GetMD5().ToString("N", CultureInfo.InvariantCulture); } protected override async Task> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) @@ -61,7 +62,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts Name = Name, SourceType = Type, Status = LiveTvTunerStatus.Available, - Id = i.Url.GetMD5().ToString("N"), + Id = i.Url.GetMD5().ToString("N", CultureInfo.InvariantCulture), Url = i.Url }) .ToList(); @@ -173,7 +174,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts ReadAtNativeFramerate = false, - Id = channel.Path.GetMD5().ToString("N"), + Id = channel.Path.GetMD5().ToString("N", CultureInfo.InvariantCulture), IsInfiniteStream = true, IsRemote = isRemote, diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 814031b12..e8cd129f5 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -92,11 +92,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts var channel = GetChannelnfo(extInf, tunerHostId, line); if (string.IsNullOrWhiteSpace(channel.Id)) { - channel.Id = channelIdPrefix + line.GetMD5().ToString("N"); + channel.Id = channelIdPrefix + line.GetMD5().ToString("N", CultureInfo.InvariantCulture); } else { - channel.Id = channelIdPrefix + channel.Id.GetMD5().ToString("N"); + channel.Id = channelIdPrefix + channel.Id.GetMD5().ToString("N", CultureInfo.InvariantCulture); } channel.Path = line; diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 29836e0bf..40b568b40 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -129,7 +130,7 @@ namespace Emby.Server.Implementations.Playlists { new Share { - UserId = options.UserId.Equals(Guid.Empty) ? null : options.UserId.ToString("N"), + UserId = options.UserId.Equals(Guid.Empty) ? null : options.UserId.ToString("N", CultureInfo.InvariantCulture), CanEdit = true } } @@ -144,7 +145,7 @@ namespace Emby.Server.Implementations.Playlists if (options.ItemIdList.Length > 0) { - AddToPlaylistInternal(playlist.Id.ToString("N"), options.ItemIdList, user, new DtoOptions(false) + AddToPlaylistInternal(playlist.Id.ToString("N", CultureInfo.InvariantCulture), options.ItemIdList, user, new DtoOptions(false) { EnableImages = true }); @@ -152,7 +153,7 @@ namespace Emby.Server.Implementations.Playlists return new PlaylistCreationResult { - Id = playlist.Id.ToString("N") + Id = playlist.Id.ToString("N", CultureInfo.InvariantCulture) }; } finally diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 08bb39578..83226b07f 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -287,7 +287,7 @@ namespace Emby.Server.Implementations.ScheduledTasks { if (_id == null) { - _id = ScheduledTask.GetType().FullName.GetMD5().ToString("N"); + _id = ScheduledTask.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture); } return _id; diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs index 545e11bf9..26a08cbe9 100644 --- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs +++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs @@ -174,7 +174,7 @@ namespace Emby.Server.Implementations.Security if (!query.UserId.Equals(Guid.Empty)) { - statement.TryBind("@UserId", query.UserId.ToString("N")); + statement.TryBind("@UserId", query.UserId.ToString("N", CultureInfo.InvariantCulture)); } if (!string.IsNullOrEmpty(query.DeviceId)) diff --git a/Emby.Server.Implementations/Serialization/JsonSerializer.cs b/Emby.Server.Implementations/Serialization/JsonSerializer.cs index 8ae7fd90c..36196ee36 100644 --- a/Emby.Server.Implementations/Serialization/JsonSerializer.cs +++ b/Emby.Server.Implementations/Serialization/JsonSerializer.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.IO; using System.Threading.Tasks; using MediaBrowser.Model.IO; @@ -245,7 +246,7 @@ namespace Emby.Server.Implementations.Serialization return null; } - return guid.ToString("N"); + return guid.ToString("N", CultureInfo.InvariantCulture); } /// diff --git a/Emby.Server.Implementations/Session/HttpSessionController.cs b/Emby.Server.Implementations/Session/HttpSessionController.cs index 9281f82b3..1104a7a85 100644 --- a/Emby.Server.Implementations/Session/HttpSessionController.cs +++ b/Emby.Server.Implementations/Session/HttpSessionController.cs @@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.Session { var dict = new Dictionary(); - dict["ItemIds"] = string.Join(",", command.ItemIds.Select(i => i.ToString("N")).ToArray()); + dict["ItemIds"] = string.Join(",", command.ItemIds.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray()); if (command.StartPositionTicks.HasValue) { diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 53ed5fc22..7ee573da5 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -327,7 +327,7 @@ namespace Emby.Server.Implementations.Session { if (string.IsNullOrEmpty(info.MediaSourceId)) { - info.MediaSourceId = info.ItemId.ToString("N"); + info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture); } if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null) @@ -463,7 +463,7 @@ namespace Emby.Server.Implementations.Session Client = appName, DeviceId = deviceId, ApplicationVersion = appVersion, - Id = key.GetMD5().ToString("N"), + Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture), ServerId = _appHost.SystemId }; @@ -845,7 +845,7 @@ namespace Emby.Server.Implementations.Session // Normalize if (string.IsNullOrEmpty(info.MediaSourceId)) { - info.MediaSourceId = info.ItemId.ToString("N"); + info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture); } if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null) @@ -1029,7 +1029,7 @@ namespace Emby.Server.Implementations.Session private static async Task SendMessageToSession(SessionInfo session, string name, T data, CancellationToken cancellationToken) { var controllers = session.SessionControllers.ToArray(); - var messageId = Guid.NewGuid().ToString("N"); + var messageId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); foreach (var controller in controllers) { @@ -1234,7 +1234,7 @@ namespace Emby.Server.Implementations.Session AssertCanControl(session, controllingSession); if (!controllingSession.UserId.Equals(Guid.Empty)) { - command.ControllingUserId = controllingSession.UserId.ToString("N"); + command.ControllingUserId = controllingSession.UserId.ToString("N", CultureInfo.InvariantCulture); } } @@ -1484,7 +1484,7 @@ namespace Emby.Server.Implementations.Session DeviceId = deviceId, DeviceName = deviceName, UserId = user.Id, - AccessToken = Guid.NewGuid().ToString("N"), + AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture), UserName = user.Name }; @@ -1822,6 +1822,7 @@ namespace Emby.Server.Implementations.Session CheckDisposed(); var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)); + return SendMessageToSessions(sessions, name, data, cancellationToken); } @@ -1831,6 +1832,7 @@ namespace Emby.Server.Implementations.Session var sessions = Sessions .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i)); + return SendMessageToSessions(sessions, name, data, cancellationToken); } diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index 630ef4893..4c2f24e6f 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; @@ -73,7 +74,7 @@ namespace Emby.Server.Implementations.TV { parents = _libraryManager.GetUserRootFolder().GetChildren(user, true) .Where(i => i is Folder) - .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N"))) + .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))) .ToArray(); } diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 10bbc9e5d..23c7339d2 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -537,7 +538,7 @@ namespace MediaBrowser.Api.Images if (item == null) { - throw new ResourceNotFoundException(string.Format("Item {0} not found.", itemId.ToString("N"))); + throw new ResourceNotFoundException(string.Format("Item {0} not found.", itemId.ToString("N", CultureInfo.InvariantCulture))); } } diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index d6bcf7878..7266bf9f9 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/MediaBrowser.Api/Library/LibraryStructureService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -272,7 +273,7 @@ namespace MediaBrowser.Api.Library // Changing capitalization. Handle windows case insensitivity if (string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase)) { - var tempPath = Path.Combine(rootFolderPath, Guid.NewGuid().ToString("N")); + var tempPath = Path.Combine(rootFolderPath, Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)); Directory.Move(currentPath, tempPath); currentPath = tempPath; } diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index 91766255f..d601fb500 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; @@ -11,7 +12,6 @@ using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Services; @@ -268,7 +268,7 @@ namespace MediaBrowser.Api.Movies EnableGroupByMetadataKey = true, DtoOptions = dtoOptions - }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N")) + }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) .Select(x => x.First()) .Take(itemLimit) .ToList(); @@ -309,7 +309,7 @@ namespace MediaBrowser.Api.Movies EnableGroupByMetadataKey = true, DtoOptions = dtoOptions - }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N")) + }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) .Select(x => x.First()) .Take(itemLimit) .ToList(); diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 399401624..114d3f7a2 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -142,7 +142,7 @@ namespace MediaBrowser.Api.Playback data += "-" + (state.Request.DeviceId ?? string.Empty) + "-" + (state.Request.PlaySessionId ?? string.Empty); - var filename = data.GetMD5().ToString("N"); + var filename = data.GetMD5().ToString("N", CultureInfo.InvariantCulture); var ext = outputFileExtension.ToLowerInvariant(); var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath; @@ -215,6 +215,12 @@ namespace MediaBrowser.Api.Playback var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions(); +<<<<<<< HEAD +======= + var transcodingId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); + var commandLineArgs = GetCommandLineArguments(outputPath, encodingOptions, state, true); + +>>>>>>> Use CultureInvariant string conversion for Guids var process = new Process() { StartInfo = new ProcessStartInfo() diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index ab3994a63..da8f99a3d 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -306,7 +307,7 @@ namespace MediaBrowser.Api.Playback { result.MediaSources = Clone(result.MediaSources); - result.PlaySessionId = Guid.NewGuid().ToString("N"); + result.PlaySessionId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); } return result; diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs index ecf07c912..6c67d4fb1 100644 --- a/MediaBrowser.Api/SearchService.cs +++ b/MediaBrowser.Api/SearchService.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; @@ -305,7 +306,7 @@ namespace MediaBrowser.Api if (tag != null) { hint.ThumbImageTag = tag; - hint.ThumbImageItemId = itemWithImage.Id.ToString("N"); + hint.ThumbImageItemId = itemWithImage.Id.ToString("N", CultureInfo.InvariantCulture); } } } @@ -326,7 +327,7 @@ namespace MediaBrowser.Api if (tag != null) { hint.BackdropImageTag = tag; - hint.BackdropImageItemId = itemWithImage.Id.ToString("N"); + hint.BackdropImageItemId = itemWithImage.Id.ToString("N", CultureInfo.InvariantCulture); } } } diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs index 4109b12bf..76392e27c 100644 --- a/MediaBrowser.Api/Session/SessionsService.cs +++ b/MediaBrowser.Api/Session/SessionsService.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -316,7 +317,7 @@ namespace MediaBrowser.Api.Session _authRepo.Create(new AuthenticationInfo { AppName = request.App, - AccessToken = Guid.NewGuid().ToString("N"), + AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture), DateCreated = DateTime.UtcNow, DeviceId = _appHost.SystemId, DeviceName = _appHost.FriendlyName, diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index 08aa540a5..52043d3df 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -168,7 +168,7 @@ namespace MediaBrowser.Api.Subtitles builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0"); builder.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD"); - long positionTicks = 0; + long positionTicks = 0; var accessToken = _authContext.GetAuthorizationInfo(Request).Token; @@ -206,7 +206,7 @@ namespace MediaBrowser.Api.Subtitles { var item = (Video)_libraryManager.GetItemById(request.Id); - var idString = request.Id.ToString("N"); + var idString = request.Id.ToString("N", CultureInfo.InvariantCulture); var mediaSource = _mediaSourceManager.GetStaticMediaSources(item, false, null) .First(i => string.Equals(i.Id, request.MediaSourceId ?? idString)); diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index b0900a554..2951fa6b4 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Dto; @@ -470,7 +471,7 @@ namespace MediaBrowser.Api if (!string.IsNullOrWhiteSpace(request.StartItemId)) { - episodes = episodes.SkipWhile(i => !string.Equals(i.Id.ToString("N"), request.StartItemId, StringComparison.OrdinalIgnoreCase)).ToList(); + episodes = episodes.SkipWhile(i => !string.Equals(i.Id.ToString("N", CultureInfo.InvariantCulture), request.StartItemId, StringComparison.OrdinalIgnoreCase)).ToList(); } // This must be the last filter diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index f842230ee..c605cd396 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -99,7 +99,7 @@ namespace MediaBrowser.Api.UserLibrary { ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true) .Where(i => i is Folder) - .Where(i => !excludeFolderIds.Contains(i.Id.ToString("N"))) + .Where(i => !excludeFolderIds.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))) .Select(i => i.Id) .ToArray(); } diff --git a/MediaBrowser.Api/UserLibrary/UserViewsService.cs b/MediaBrowser.Api/UserLibrary/UserViewsService.cs index 1d61c5c1e..2fa5d8933 100644 --- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs +++ b/MediaBrowser.Api/UserLibrary/UserViewsService.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -116,7 +117,7 @@ namespace MediaBrowser.Api.UserLibrary .Select(i => new SpecialViewOption { Name = i.Name, - Id = i.Id.ToString("N") + Id = i.Id.ToString("N", CultureInfo.InvariantCulture) }) .OrderBy(i => i.Name) diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs index 061f72438..474036f5c 100644 --- a/MediaBrowser.Api/VideosService.cs +++ b/MediaBrowser.Api/VideosService.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using System.Threading; using MediaBrowser.Controller.Configuration; @@ -168,7 +169,7 @@ namespace MediaBrowser.Api foreach (var item in items.Where(i => i.Id != primaryVersion.Id)) { - item.SetPrimaryVersionId(primaryVersion.Id.ToString("N")); + item.SetPrimaryVersionId(primaryVersion.Id.ToString("N", CultureInfo.InvariantCulture)); item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs index adf03fb66..89159973b 100644 --- a/MediaBrowser.Controller/Channels/Channel.cs +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using System.Threading; using MediaBrowser.Common.Progress; @@ -14,14 +15,14 @@ namespace MediaBrowser.Controller.Channels { if (user.Policy.BlockedChannels != null) { - if (user.Policy.BlockedChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) + if (user.Policy.BlockedChannels.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase)) { return false; } } else { - if (!user.Policy.EnableAllChannels && !user.Policy.EnabledChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) + if (!user.Policy.EnableAllChannels && !user.Policy.EnabledChannels.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase)) { return false; } @@ -60,7 +61,7 @@ namespace MediaBrowser.Controller.Channels public static string GetInternalMetadataPath(string basePath, Guid id) { - return System.IO.Path.Combine(basePath, "channels", id.ToString("N"), "metadata"); + return System.IO.Path.Combine(basePath, "channels", id.ToString("N", CultureInfo.InvariantCulture), "metadata"); } public override bool CanDelete() diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 10a603e42..2ae856b02 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -503,7 +503,7 @@ namespace MediaBrowser.Controller.Entities foreach (var folder in collectionFolders) { - if (allowed.Contains(folder.Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) + if (allowed.Contains(folder.Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase)) { return true; } @@ -664,10 +664,10 @@ namespace MediaBrowser.Controller.Entities { if (SourceType == SourceType.Channel) { - return System.IO.Path.Combine(basePath, "channels", ChannelId.ToString("N"), Id.ToString("N")); + return System.IO.Path.Combine(basePath, "channels", ChannelId.ToString("N", CultureInfo.InvariantCulture), Id.ToString("N", CultureInfo.InvariantCulture)); } - var idString = Id.ToString("N"); + var idString = Id.ToString("N", CultureInfo.InvariantCulture); basePath = System.IO.Path.Combine(basePath, "library"); @@ -1095,7 +1095,7 @@ namespace MediaBrowser.Controller.Entities var info = new MediaSourceInfo { - Id = item.Id.ToString("N"), + Id = item.Id.ToString("N", CultureInfo.InvariantCulture), Protocol = protocol ?? MediaProtocol.File, MediaStreams = MediaSourceManager.GetMediaStreams(item.Id), Name = GetMediaSourceName(item), @@ -1113,7 +1113,7 @@ namespace MediaBrowser.Controller.Entities if (info.Protocol == MediaProtocol.File) { - info.ETag = item.DateModified.Ticks.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N"); + info.ETag = item.DateModified.Ticks.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N", CultureInfo.InvariantCulture); } var video = item as Video; @@ -1626,7 +1626,7 @@ namespace MediaBrowser.Controller.Entities public virtual string CreatePresentationUniqueKey() { - return Id.ToString("N"); + return Id.ToString("N", CultureInfo.InvariantCulture); } [IgnoreDataMember] @@ -2736,7 +2736,7 @@ namespace MediaBrowser.Controller.Entities { var list = GetEtagValues(user); - return string.Join("|", list.ToArray()).GetMD5().ToString("N"); + return string.Join("|", list.ToArray()).GetMD5().ToString("N", CultureInfo.InvariantCulture); } protected virtual List GetEtagValues(User user) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index c056bc0b4..d841b7ef8 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -177,7 +178,7 @@ namespace MediaBrowser.Controller.Entities { if (user.Policy.BlockedMediaFolders != null) { - if (user.Policy.BlockedMediaFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase) || + if (user.Policy.BlockedMediaFolders.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase) || // Backwards compatibility user.Policy.BlockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase)) @@ -187,7 +188,7 @@ namespace MediaBrowser.Controller.Entities } else { - if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) + if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase)) { return false; } diff --git a/MediaBrowser.Controller/Entities/LinkedChild.cs b/MediaBrowser.Controller/Entities/LinkedChild.cs index bb2d03246..823060488 100644 --- a/MediaBrowser.Controller/Entities/LinkedChild.cs +++ b/MediaBrowser.Controller/Entities/LinkedChild.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; @@ -29,7 +30,7 @@ namespace MediaBrowser.Controller.Entities if (string.IsNullOrEmpty(child.Path)) { - child.LibraryItemId = item.Id.ToString("N"); + child.LibraryItemId = item.Id.ToString("N", CultureInfo.InvariantCulture); } return child; @@ -37,7 +38,7 @@ namespace MediaBrowser.Controller.Entities public LinkedChild() { - Id = Guid.NewGuid().ToString("N"); + Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); } } diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index eae834f6f..1aacc13c9 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -91,7 +92,7 @@ namespace MediaBrowser.Controller.Entities.TV } var folders = LibraryManager.GetCollectionFolders(this) - .Select(i => i.Id.ToString("N")) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) .ToArray(); if (folders.Length == 0) diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index 9952ba418..968d72579 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -230,7 +231,7 @@ namespace MediaBrowser.Controller.Entities // TODO: Remove idPath and just use usernamePath for future releases var usernamePath = System.IO.Path.Combine(parentPath, username); - var idPath = System.IO.Path.Combine(parentPath, Id.ToString("N")); + var idPath = System.IO.Path.Combine(parentPath, Id.ToString("N", CultureInfo.InvariantCulture)); if (!Directory.Exists(usernamePath) && Directory.Exists(idPath)) { Directory.Move(idPath, usernamePath); diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index e483c8f34..454bdc4ae 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Movies; @@ -987,7 +988,7 @@ namespace MediaBrowser.Controller.Entities private UserView GetUserViewWithName(string name, string type, string sortName, BaseItem parent) { - return _userViewManager.GetUserSubView(parent.Id, parent.Id.ToString("N"), type, sortName); + return _userViewManager.GetUserSubView(parent.Id, parent.Id.ToString("N", CultureInfo.InvariantCulture), type, sortName); } private UserView GetUserView(string type, string localizationKey, string sortName, BaseItem parent) diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 55f47aae9..351662b29 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -89,7 +89,7 @@ namespace MediaBrowser.Controller.LiveTv var info = new MediaSourceInfo { - Id = Id.ToString("N"), + Id = Id.ToString("N", CultureInfo.InvariantCulture), Protocol = PathProtocol ?? MediaProtocol.File, MediaStreams = new List(), Name = Name, @@ -111,7 +111,7 @@ namespace MediaBrowser.Controller.LiveTv protected override string GetInternalMetadataPath(string basePath) { - return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N"), "metadata"); + return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N", CultureInfo.InvariantCulture), "metadata"); } public override bool CanDelete() diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index 8bde6a5da..bdaf10d00 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; @@ -188,7 +189,7 @@ namespace MediaBrowser.Controller.LiveTv protected override string GetInternalMetadataPath(string basePath) { - return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N")); + return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N", CultureInfo.InvariantCulture)); } public override bool CanDelete() diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index e83260725..aff687f88 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -239,7 +240,7 @@ namespace MediaBrowser.Controller.Playlists return base.IsVisible(user); } - var userId = user.Id.ToString("N"); + var userId = user.Id.ToString("N", CultureInfo.InvariantCulture); foreach (var share in shares) { if (string.Equals(share.UserId, userId, StringComparison.OrdinalIgnoreCase)) diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs index f4b915c06..ebff81b7f 100644 --- a/MediaBrowser.Controller/Providers/MetadataResult.cs +++ b/MediaBrowser.Controller/Providers/MetadataResult.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Providers @@ -55,7 +56,7 @@ namespace MediaBrowser.Controller.Providers foreach (var i in UserDataList) { - if (string.Equals(userId, i.UserId.ToString("N"), StringComparison.OrdinalIgnoreCase)) + if (string.Equals(userId, i.UserId.ToString("N", CultureInfo.InvariantCulture), StringComparison.OrdinalIgnoreCase)) { userData = i; } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 860ea13cf..a22eaaa51 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -934,7 +934,7 @@ namespace MediaBrowser.Providers.Manager public void OnRefreshStart(BaseItem item) { - //_logger.LogInformation("OnRefreshStart {0}", item.Id.ToString("N")); + //_logger.LogInformation("OnRefreshStart {0}", item.Id.ToString("N", CultureInfo.InvariantCulture)); var id = item.Id; lock (_activeRefreshes) @@ -947,7 +947,7 @@ namespace MediaBrowser.Providers.Manager public void OnRefreshComplete(BaseItem item) { - //_logger.LogInformation("OnRefreshComplete {0}", item.Id.ToString("N")); + //_logger.LogInformation("OnRefreshComplete {0}", item.Id.ToString("N", CultureInfo.InvariantCulture)); lock (_activeRefreshes) { _activeRefreshes.Remove(item.Id); @@ -971,7 +971,7 @@ namespace MediaBrowser.Providers.Manager public void OnRefreshProgress(BaseItem item, double progress) { - //_logger.LogInformation("OnRefreshProgress {0} {1}", item.Id.ToString("N"), progress); + //_logger.LogInformation("OnRefreshProgress {0} {1}", item.Id.ToString("N", CultureInfo.InvariantCulture), progress); var id = item.Id; lock (_activeRefreshes) @@ -985,7 +985,7 @@ namespace MediaBrowser.Providers.Manager else { // TODO: Need to hunt down the conditions for this happening - //throw new Exception(string.Format("Refresh for item {0} {1} is not in progress", item.GetType().Name, item.Id.ToString("N"))); + //throw new Exception(string.Format("Refresh for item {0} {1} is not in progress", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture))); } } } diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index 61a8a122b..7023ef706 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -99,11 +100,11 @@ namespace MediaBrowser.Providers.MediaInfo if (!string.IsNullOrWhiteSpace(item.Album) && !string.IsNullOrWhiteSpace(albumArtist)) { - filename = (item.Album + "-" + albumArtist).GetMD5().ToString("N"); + filename = (item.Album + "-" + albumArtist).GetMD5().ToString("N", CultureInfo.InvariantCulture); } else { - filename = item.Id.ToString("N"); + filename = item.Id.ToString("N", CultureInfo.InvariantCulture); } filename += ".jpg"; @@ -111,7 +112,7 @@ namespace MediaBrowser.Providers.MediaInfo else { // If it's an audio book or audio podcast, allow unique image per item - filename = item.Id.ToString("N") + ".jpg"; + filename = item.Id.ToString("N", CultureInfo.InvariantCulture) + ".jpg"; } var prefix = filename.Substring(0, 1); diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index 7fc6909f5..b4a4c36e5 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -1,12 +1,11 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; @@ -296,7 +295,7 @@ namespace MediaBrowser.Providers.Subtitles private string GetProviderId(string name) { - return name.ToLowerInvariant().GetMD5().ToString("N"); + return name.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture); } private ISubtitleProvider GetProvider(string id) diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 1249a60c0..e7e02a7d5 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -27,6 +27,10 @@ + + -- cgit v1.2.3 From 52c1b45feb75c55075135005167a308269013400 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 2 Jul 2019 21:47:36 +0200 Subject: Fix build --- .../HttpClientManager/HttpClientManager.cs | 1 + Emby.Server.Implementations/Security/AuthenticationRepository.cs | 4 ++-- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- MediaBrowser.Api/Playback/BaseStreamingService.cs | 8 +------- 4 files changed, 5 insertions(+), 10 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 9ca33d7db..a933b53f5 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Concurrent; +using System.Globalization; using System.IO; using System.Linq; using System.Net; diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs index 26a08cbe9..0b5ee5d03 100644 --- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs +++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs @@ -97,7 +97,7 @@ namespace Emby.Server.Implementations.Security statement.TryBind("@AppName", info.AppName); statement.TryBind("@AppVersion", info.AppVersion); statement.TryBind("@DeviceName", info.DeviceName); - statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N"))); + statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N", CultureInfo.InvariantCulture))); statement.TryBind("@UserName", info.UserName); statement.TryBind("@IsActive", true); statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue()); @@ -131,7 +131,7 @@ namespace Emby.Server.Implementations.Security statement.TryBind("@AppName", info.AppName); statement.TryBind("@AppVersion", info.AppVersion); statement.TryBind("@DeviceName", info.DeviceName); - statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N"))); + statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N", CultureInfo.InvariantCulture))); statement.TryBind("@UserName", info.UserName); statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue()); statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue()); diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 7ee573da5..0347100a4 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1041,7 +1041,7 @@ namespace Emby.Server.Implementations.Session { IEnumerable GetTasks() { - var messageId = Guid.NewGuid().ToString("N"); + var messageId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); foreach (var session in sessions) { var controllers = session.SessionControllers; diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 114d3f7a2..d9f4a583c 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -215,12 +215,6 @@ namespace MediaBrowser.Api.Playback var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions(); -<<<<<<< HEAD -======= - var transcodingId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); - var commandLineArgs = GetCommandLineArguments(outputPath, encodingOptions, state, true); - ->>>>>>> Use CultureInvariant string conversion for Guids var process = new Process() { StartInfo = new ProcessStartInfo() @@ -246,7 +240,7 @@ namespace MediaBrowser.Api.Playback var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, state.Request.PlaySessionId, state.MediaSource.LiveStreamId, - Guid.NewGuid().ToString("N"), + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture), TranscodingJobType, process, state.Request.DeviceId, -- cgit v1.2.3 From 25917db07a835bf0e5c6afdc0fec165851fffab2 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 9 Aug 2019 23:50:40 +0200 Subject: Fix doc releated warnings --- .../AppBase/BaseApplicationPaths.cs | 37 +++++---- .../AppBase/BaseConfigurationManager.cs | 92 ++++++++++++---------- Emby.Server.Implementations/ApplicationHost.cs | 6 +- .../ConfigurationOptions.cs | 4 +- .../Emby.Server.Implementations.csproj | 1 + .../ServerApplicationPaths.cs | 27 ++++--- 6 files changed, 91 insertions(+), 76 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs index 00cfa0c9a..f67a09daa 100644 --- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs +++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs @@ -10,6 +10,8 @@ namespace Emby.Server.Implementations.AppBase /// public abstract class BaseApplicationPaths : IApplicationPaths { + private string _dataPath; + /// /// Initializes a new instance of the class. /// @@ -30,27 +32,27 @@ namespace Emby.Server.Implementations.AppBase } /// - /// Gets the path to the program data folder + /// Gets the path to the program data folder. /// /// The program data path. - public string ProgramDataPath { get; private set; } + public string ProgramDataPath { get; } /// - /// Gets the path to the web UI resources folder + /// Gets the path to the web UI resources folder. /// /// The web UI resources path. - public string WebPath { get; set; } + public string WebPath { get; } /// - /// Gets the path to the system folder + /// Gets the path to the system folder. /// + /// The path to the system folder. public string ProgramSystemPath { get; } = AppContext.BaseDirectory; /// - /// Gets the folder path to the data directory + /// Gets the folder path to the data directory. /// /// The data directory. - private string _dataPath; public string DataPath { get => _dataPath; @@ -58,8 +60,9 @@ namespace Emby.Server.Implementations.AppBase } /// - /// Gets the magic strings used for virtual path manipulation. + /// Gets the magic string used for virtual path manipulation. /// + /// The magic string used for virtual path manipulation. public string VirtualDataPath { get; } = "%AppDataPath%"; /// @@ -69,43 +72,43 @@ namespace Emby.Server.Implementations.AppBase public string ImageCachePath => Path.Combine(CachePath, "images"); /// - /// Gets the path to the plugin directory + /// Gets the path to the plugin directory. /// /// The plugins path. public string PluginsPath => Path.Combine(ProgramDataPath, "plugins"); /// - /// Gets the path to the plugin configurations directory + /// Gets the path to the plugin configurations directory. /// /// The plugin configurations path. public string PluginConfigurationsPath => Path.Combine(PluginsPath, "configurations"); /// - /// Gets the path to the log directory + /// Gets the path to the log directory. /// /// The log directory path. - public string LogDirectoryPath { get; private set; } + public string LogDirectoryPath { get; } /// - /// Gets the path to the application configuration root directory + /// Gets the path to the application configuration root directory. /// /// The configuration directory path. - public string ConfigurationDirectoryPath { get; private set; } + public string ConfigurationDirectoryPath { get; } /// - /// Gets the path to the system configuration file + /// Gets the path to the system configuration file. /// /// The system configuration file path. public string SystemConfigurationFilePath => Path.Combine(ConfigurationDirectoryPath, "system.xml"); /// - /// Gets the folder path to the cache directory + /// Gets or sets the folder path to the cache directory. /// /// The cache directory. public string CachePath { get; set; } /// - /// Gets the folder path to the temp directory within the cache folder + /// Gets the folder path to the temp directory within the cache folder. /// /// The temp directory. public string TempDirectory => Path.Combine(CachePath, "temp"); diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index af60a8dce..4832c19c4 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -19,11 +20,44 @@ namespace Emby.Server.Implementations.AppBase /// public abstract class BaseConfigurationManager : IConfigurationManager { + private readonly IFileSystem _fileSystem; + + private readonly ConcurrentDictionary _configurations = new ConcurrentDictionary(); + + private ConfigurationStore[] _configurationStores = Array.Empty(); + private IConfigurationFactory[] _configurationFactories = Array.Empty(); + /// - /// Gets the type of the configuration. + /// The _configuration loaded. /// - /// The type of the configuration. - protected abstract Type ConfigurationType { get; } + private bool _configurationLoaded; + + /// + /// The _configuration sync lock. + /// + private object _configurationSyncLock = new object(); + + /// + /// The _configuration. + /// + private BaseApplicationConfiguration _configuration; + + /// + /// Initializes a new instance of the class. + /// + /// The application paths. + /// The logger factory. + /// The XML serializer. + /// The file system + protected BaseConfigurationManager(IApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IXmlSerializer xmlSerializer, IFileSystem fileSystem) + { + CommonApplicationPaths = applicationPaths; + XmlSerializer = xmlSerializer; + _fileSystem = fileSystem; + Logger = loggerFactory.CreateLogger(GetType().Name); + + UpdateCachePath(); + } /// /// Occurs when [configuration updated]. @@ -40,6 +74,12 @@ namespace Emby.Server.Implementations.AppBase /// public event EventHandler NamedConfigurationUpdated; + /// + /// Gets the type of the configuration. + /// + /// The type of the configuration. + protected abstract Type ConfigurationType { get; } + /// /// Gets the logger. /// @@ -56,20 +96,7 @@ namespace Emby.Server.Implementations.AppBase /// /// The application paths. public IApplicationPaths CommonApplicationPaths { get; private set; } - public readonly IFileSystem FileSystem; - /// - /// The _configuration loaded - /// - private bool _configurationLoaded; - /// - /// The _configuration sync lock - /// - private object _configurationSyncLock = new object(); - /// - /// The _configuration - /// - private BaseApplicationConfiguration _configuration; /// /// Gets the system configuration /// @@ -90,26 +117,6 @@ namespace Emby.Server.Implementations.AppBase } } - private ConfigurationStore[] _configurationStores = { }; - private IConfigurationFactory[] _configurationFactories = { }; - - /// - /// Initializes a new instance of the class. - /// - /// The application paths. - /// The logger factory. - /// The XML serializer. - /// The file system - protected BaseConfigurationManager(IApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IXmlSerializer xmlSerializer, IFileSystem fileSystem) - { - CommonApplicationPaths = applicationPaths; - XmlSerializer = xmlSerializer; - FileSystem = fileSystem; - Logger = loggerFactory.CreateLogger(GetType().Name); - - UpdateCachePath(); - } - public virtual void AddParts(IEnumerable factories) { _configurationFactories = factories.ToArray(); @@ -171,6 +178,7 @@ namespace Emby.Server.Implementations.AppBase private void UpdateCachePath() { string cachePath; + // If the configuration file has no entry (i.e. not set in UI) if (string.IsNullOrWhiteSpace(CommonConfiguration.CachePath)) { @@ -207,12 +215,16 @@ namespace Emby.Server.Implementations.AppBase var newPath = newConfig.CachePath; if (!string.IsNullOrWhiteSpace(newPath) - && !string.Equals(CommonConfiguration.CachePath ?? string.Empty, newPath)) + && !string.Equals(CommonConfiguration.CachePath ?? string.Empty, newPath, StringComparison.Ordinal)) { // Validate if (!Directory.Exists(newPath)) { - throw new FileNotFoundException(string.Format("{0} does not exist.", newPath)); + throw new FileNotFoundException( + string.Format( + CultureInfo.InvariantCulture, + "{0} does not exist.", + newPath)); } EnsureWriteAccess(newPath); @@ -223,11 +235,9 @@ namespace Emby.Server.Implementations.AppBase { var file = Path.Combine(path, Guid.NewGuid().ToString()); File.WriteAllText(file, string.Empty); - FileSystem.DeleteFile(file); + _fileSystem.DeleteFile(file); } - private readonly ConcurrentDictionary _configurations = new ConcurrentDictionary(); - private string GetConfigurationFile(string key) { return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLowerInvariant() + ".xml"); diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index f0aa60428..0493fd9f5 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -418,7 +418,7 @@ namespace Emby.Server.Implementations public string ApplicationVersion { get; } = typeof(ApplicationHost).Assembly.GetName().Version.ToString(3); /// - /// Gets the current application user agent + /// Gets the current application user agent. /// /// The application user agent. public string ApplicationUserAgent => Name.Replace(' ', '-') + "/" + ApplicationVersion; @@ -467,8 +467,8 @@ namespace Emby.Server.Implementations /// /// Creates an instance of type and resolves all constructor dependencies /// - /// /// The type - /// T + /// /// The type. + /// T. public T CreateInstance() => ActivatorUtilities.CreateInstance(_serviceProvider); diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs index 9bc60972a..62408ee70 100644 --- a/Emby.Server.Implementations/ConfigurationOptions.cs +++ b/Emby.Server.Implementations/ConfigurationOptions.cs @@ -6,8 +6,8 @@ namespace Emby.Server.Implementations { public static readonly Dictionary Configuration = new Dictionary { - {"HttpListenerHost:DefaultRedirectPath", "web/index.html"}, - {"MusicBrainz:BaseUrl", "https://www.musicbrainz.org"} + { "HttpListenerHost:DefaultRedirectPath", "web/index.html" }, + { "MusicBrainz:BaseUrl", "https://www.musicbrainz.org" } }; } } diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index abbaef26b..f07f8e3bf 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -44,6 +44,7 @@ netstandard2.0 false + true diff --git a/Emby.Server.Implementations/ServerApplicationPaths.cs b/Emby.Server.Implementations/ServerApplicationPaths.cs index adaf23234..2f5a8af80 100644 --- a/Emby.Server.Implementations/ServerApplicationPaths.cs +++ b/Emby.Server.Implementations/ServerApplicationPaths.cs @@ -10,8 +10,12 @@ namespace Emby.Server.Implementations /// public class ServerApplicationPaths : BaseApplicationPaths, IServerApplicationPaths { + private string _defaultTranscodingTempPath; + private string _transcodingTempPath; + private string _internalMetadataPath; + /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public ServerApplicationPaths( string programDataPath, @@ -30,7 +34,7 @@ namespace Emby.Server.Implementations public string ApplicationResourcesPath { get; } = AppContext.BaseDirectory; /// - /// Gets the path to the base root media directory + /// Gets the path to the base root media directory. /// /// The root folder path. public string RootFolderPath => Path.Combine(ProgramDataPath, "root"); @@ -48,7 +52,7 @@ namespace Emby.Server.Implementations public string LocalizationPath => Path.Combine(ProgramDataPath, "localization"); /// - /// Gets the path to the People directory + /// Gets the path to the People directory. /// /// The people path. public string PeoplePath => Path.Combine(InternalMetadataPath, "People"); @@ -56,37 +60,37 @@ namespace Emby.Server.Implementations public string ArtistsPath => Path.Combine(InternalMetadataPath, "artists"); /// - /// Gets the path to the Genre directory + /// Gets the path to the Genre directory. /// /// The genre path. public string GenrePath => Path.Combine(InternalMetadataPath, "Genre"); /// - /// Gets the path to the Genre directory + /// Gets the path to the Genre directory. /// /// The genre path. public string MusicGenrePath => Path.Combine(InternalMetadataPath, "MusicGenre"); /// - /// Gets the path to the Studio directory + /// Gets the path to the Studio directory. /// /// The studio path. public string StudioPath => Path.Combine(InternalMetadataPath, "Studio"); /// - /// Gets the path to the Year directory + /// Gets the path to the Year directory. /// /// The year path. public string YearPath => Path.Combine(InternalMetadataPath, "Year"); /// - /// Gets the path to the General IBN directory + /// Gets the path to the General IBN directory. /// /// The general path. public string GeneralPath => Path.Combine(InternalMetadataPath, "general"); /// - /// Gets the path to the Ratings IBN directory + /// Gets the path to the Ratings IBN directory. /// /// The ratings path. public string RatingsPath => Path.Combine(InternalMetadataPath, "ratings"); @@ -98,15 +102,13 @@ namespace Emby.Server.Implementations public string MediaInfoImagesPath => Path.Combine(InternalMetadataPath, "mediainfo"); /// - /// Gets the path to the user configuration directory + /// Gets the path to the user configuration directory. /// /// The user configuration directory path. public string UserConfigurationDirectoryPath => Path.Combine(ConfigurationDirectoryPath, "users"); - private string _defaultTranscodingTempPath; public string DefaultTranscodingTempPath => _defaultTranscodingTempPath ?? (_defaultTranscodingTempPath = Path.Combine(ProgramDataPath, "transcoding-temp")); - private string _transcodingTempPath; public string TranscodingTempPath { get => _transcodingTempPath ?? (_transcodingTempPath = DefaultTranscodingTempPath); @@ -139,7 +141,6 @@ namespace Emby.Server.Implementations return path; } - private string _internalMetadataPath; public string InternalMetadataPath { get => _internalMetadataPath ?? (_internalMetadataPath = Path.Combine(DataPath, "metadata")); -- cgit v1.2.3 From c987203f5aaa5b838e32aa0f7446942c76383708 Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 5 Aug 2019 16:22:43 -0700 Subject: remove old routes from http server --- .../HttpServer/HttpListenerHost.cs | 61 ---------------------- 1 file changed, 61 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 4c233456c..2d2be7135 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -484,53 +484,6 @@ namespace Emby.Server.Implementations.HttpServer return; } - if (localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1) - { - httpRes.StatusCode = 200; - httpRes.ContentType = "text/html"; - var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) - .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); - - if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) - { - await httpRes.WriteAsync( - "EmbyPlease update your Emby bookmark to " + newUrl + "", - cancellationToken).ConfigureAwait(false); - return; - } - } - - if (localPath.IndexOf("dashboard/", StringComparison.OrdinalIgnoreCase) != -1 && - localPath.IndexOf("web/dashboard", StringComparison.OrdinalIgnoreCase) == -1) - { - httpRes.StatusCode = 200; - httpRes.ContentType = "text/html"; - var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) - .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); - - if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) - { - await httpRes.WriteAsync( - "EmbyPlease update your Emby bookmark to " + newUrl + "", - cancellationToken).ConfigureAwait(false); - return; - } - } - - if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase)) - { - httpRes.Redirect(_defaultRedirectPath); - return; - } - - if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase)) - { - httpRes.Redirect("../" + _defaultRedirectPath); - return; - } - if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)) { httpRes.Redirect(_defaultRedirectPath); @@ -543,19 +496,6 @@ namespace Emby.Server.Implementations.HttpServer return; } - if (!string.Equals(httpReq.QueryString["r"], "0", StringComparison.OrdinalIgnoreCase)) - { - if (localPath.EndsWith("web/dashboard.html", StringComparison.OrdinalIgnoreCase)) - { - httpRes.Redirect("index.html#!/dashboard.html"); - } - - if (localPath.EndsWith("web/home.html", StringComparison.OrdinalIgnoreCase)) - { - httpRes.Redirect("index.html"); - } - } - if (!string.IsNullOrEmpty(GlobalResponse)) { // We don't want the address pings in ApplicationHost to fail @@ -569,7 +509,6 @@ namespace Emby.Server.Implementations.HttpServer } var handler = GetServiceHandler(httpReq); - if (handler != null) { await handler.ProcessRequestAsync(this, httpReq, httpRes, _logger, cancellationToken).ConfigureAwait(false); -- cgit v1.2.3 From d521e5c36a0396a6e0ee562d8bda6fb5ffc6b0a7 Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 5 Aug 2019 16:25:18 -0700 Subject: add base url to server configuration --- .../HttpServer/HttpListenerHost.cs | 58 +++------------------- .../Configuration/ServerConfiguration.cs | 2 + 2 files changed, 10 insertions(+), 50 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 2d2be7135..48b229e88 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -7,6 +7,7 @@ using System.Net.Sockets; using System.Reflection; using System.Threading; using System.Threading.Tasks; +using Emby.Server.Implementations.Configuration; using Emby.Server.Implementations.Net; using Emby.Server.Implementations.Services; using MediaBrowser.Common.Extensions; @@ -470,17 +471,10 @@ namespace Emby.Server.Implementations.HttpServer urlToLog = GetUrlToLog(urlString); - if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) || - string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(localPath, "/" + _config.Configuration.BaseUrl + "/", StringComparison.OrdinalIgnoreCase) + || string.Equals(localPath, "/" + _config.Configuration.BaseUrl, StringComparison.OrdinalIgnoreCase)) { - httpRes.Redirect(_defaultRedirectPath); - return; - } - - if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) || - string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase)) - { - httpRes.Redirect("emby/" + _defaultRedirectPath); + httpRes.Redirect("/" + _config.Configuration.BaseUrl + "/" + _defaultRedirectPath); return; } @@ -602,22 +596,7 @@ namespace Emby.Server.Implementations.HttpServer foreach (var route in clone) { - routes.Add(new RouteAttribute(NormalizeEmbyRoutePath(route.Path), route.Verbs) - { - Notes = route.Notes, - Priority = route.Priority, - Summary = route.Summary - }); - - routes.Add(new RouteAttribute(NormalizeMediaBrowserRoutePath(route.Path), route.Verbs) - { - Notes = route.Notes, - Priority = route.Priority, - Summary = route.Summary - }); - - // needed because apps add /emby, and some users also add /emby, thereby double prefixing - routes.Add(new RouteAttribute(DoubleNormalizeEmbyRoutePath(route.Path), route.Verbs) + routes.Add(new RouteAttribute(NormalizeCustomRoutePath(_config.Configuration.BaseUrl, route.Path), route.Verbs) { Notes = route.Notes, Priority = route.Priority, @@ -658,35 +637,14 @@ namespace Emby.Server.Implementations.HttpServer return _socketListener.ProcessWebSocketRequest(context); } - //TODO Add Jellyfin Route Path Normalizer - private static string NormalizeEmbyRoutePath(string path) - { - if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) - { - return "/emby" + path; - } - - return "emby/" + path; - } - - private static string NormalizeMediaBrowserRoutePath(string path) - { - if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) - { - return "/mediabrowser" + path; - } - - return "mediabrowser/" + path; - } - - private static string DoubleNormalizeEmbyRoutePath(string path) + private static string NormalizeCustomRoutePath(string baseUrl, string path) { if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { - return "/emby/emby" + path; + return "/" + baseUrl + path; } - return "emby/emby/" + path; + return baseUrl + "/" + path; } /// diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 2673597ca..d64ea35eb 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -163,6 +163,7 @@ namespace MediaBrowser.Model.Configuration public string ServerName { get; set; } public string WanDdns { get; set; } + public string BaseUrl { get; set; } public string UICulture { get; set; } @@ -243,6 +244,7 @@ namespace MediaBrowser.Model.Configuration SortRemoveCharacters = new[] { ",", "&", "-", "{", "}", "'" }; SortRemoveWords = new[] { "the", "a", "an" }; + BaseUrl = "jellyfin"; UICulture = "en-US"; MetadataOptions = new[] -- cgit v1.2.3 From 97d6c2db6b5749c12ef2f92f6622c28555173c48 Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 5 Aug 2019 16:27:10 -0700 Subject: keep old base url for now --- .../HttpServer/HttpListenerHost.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 48b229e88..bdcf5d0b7 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -602,6 +602,13 @@ namespace Emby.Server.Implementations.HttpServer Priority = route.Priority, Summary = route.Summary }); + + routes.Add(new RouteAttribute(NormalizeOldRoutePath(route.Path), route.Verbs) + { + Notes = route.Notes, + Priority = route.Priority, + Summary = route.Summary + }); } return routes.ToArray(); @@ -637,6 +644,17 @@ namespace Emby.Server.Implementations.HttpServer return _socketListener.ProcessWebSocketRequest(context); } + // this method was left for compatibility with third party clients + private static string NormalizeOldRoutePath(string path) + { + if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) + { + return "/emby" + path; + } + + return "emby/" + path; + } + private static string NormalizeCustomRoutePath(string baseUrl, string path) { if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) -- cgit v1.2.3 From 003238ef5e5151c43738fedcc90f83fd5064580a Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 11 Aug 2019 15:11:53 +0200 Subject: Update deps + document startup project * Fixed the release build * Documented all public/internal members of Jellyfin.Server * Enable TreatWarningsAsErrors for debug builds for Jellyfin.Server This will ensure that any new public/internal members of Jellyfin.Server are documented --- Emby.Naming/Emby.Naming.csproj | 2 +- Emby.Photos/Emby.Photos.csproj | 2 +- .../Emby.Server.Implementations.csproj | 10 +++----- Jellyfin.Server/CoreAppHost.cs | 17 ++++++++++++++ Jellyfin.Server/Jellyfin.Server.csproj | 10 ++++---- Jellyfin.Server/Program.cs | 27 +++++++++++++++++----- Jellyfin.Server/StartupOptions.cs | 26 +++++++++++++++++++++ .../MediaBrowser.MediaEncoding.csproj | 2 +- 8 files changed, 74 insertions(+), 22 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 9e2a4950f..0b1ce2fce 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -23,7 +23,7 @@ - + diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index c9830abc5..8a79bf7e1 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -10,7 +10,7 @@ - + diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 73a64b0cd..c78d96d4a 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -32,7 +32,7 @@ - + @@ -48,17 +48,13 @@ - + latest - - true - - - + diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index b9b0cc382..8b4b61e29 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -9,8 +9,21 @@ using Microsoft.Extensions.Logging; namespace Jellyfin.Server { + /// + /// Implementation of the abstract class. + /// public class CoreAppHost : ApplicationHost { + /// + /// Initializes a new instance of the class. + /// + /// The to be used by the . + /// The to be used by the . + /// The to be used by the . + /// The to be used by the . + /// The to be used by the . + /// The to be used by the . + /// The to be used by the . public CoreAppHost( ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, @@ -30,15 +43,19 @@ namespace Jellyfin.Server { } + /// public override bool CanSelfRestart => StartupOptions.RestartPath != null; + /// protected override void RestartInternal() => Program.Restart(); + /// protected override IEnumerable GetAssembliesWithPartsInternal() { yield return typeof(CoreAppHost).Assembly; } + /// protected override void ShutdownInternal() => Program.Shutdown(); } } diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 641b3f182..e87283477 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -9,10 +9,8 @@ - + latest - - SA1600;SA1601;SA1629;CS1591 true @@ -26,7 +24,7 @@ - + @@ -36,7 +34,7 @@ - + @@ -45,7 +43,7 @@ - + diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 952990493..82b903198 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -28,6 +28,9 @@ using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Jellyfin.Server { + /// + /// Class containing the entry point of the application. + /// public static class Program { private static readonly CancellationTokenSource _tokenSource = new CancellationTokenSource(); @@ -35,6 +38,11 @@ namespace Jellyfin.Server private static ILogger _logger; private static bool _restartOnShutdown; + /// + /// The entry point of the application. + /// + /// The command line arguments passed. + /// . public static Task Main(string[] args) { // For backwards compatibility. @@ -53,7 +61,10 @@ namespace Jellyfin.Server .MapResult(StartApp, _ => Task.CompletedTask); } - public static void Shutdown() + /// + /// Shuts down the application. + /// + internal static void Shutdown() { if (!_tokenSource.IsCancellationRequested) { @@ -61,7 +72,10 @@ namespace Jellyfin.Server } } - public static void Restart() + /// + /// Restarts the application. + /// + internal static void Restart() { _restartOnShutdown = true; @@ -171,11 +185,12 @@ namespace Jellyfin.Server /// /// Create the data, config and log paths from the variety of inputs(command line args, /// environment variables) or decide on what default to use. For Windows it's %AppPath% - /// for everything else the XDG approach is followed: - /// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + /// for everything else the + /// XDG approach + /// is followed. /// - /// StartupOptions - /// ServerApplicationPaths + /// The for this instance. + /// . private static ServerApplicationPaths CreateApplicationPaths(StartupOptions options) { // dataDir diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs index 8296d414e..bb0adaf63 100644 --- a/Jellyfin.Server/StartupOptions.cs +++ b/Jellyfin.Server/StartupOptions.cs @@ -8,36 +8,62 @@ namespace Jellyfin.Server /// public class StartupOptions : IStartupOptions { + /// + /// Gets or sets the path to the data directory. + /// + /// The path to the data directory. [Option('d', "datadir", Required = false, HelpText = "Path to use for the data folder (database files, etc.).")] public string DataDir { get; set; } + /// + /// Gets or sets the path to the web directory. + /// + /// The path to the web directory. [Option('w', "webdir", Required = false, HelpText = "Path to the Jellyfin web UI resources.")] public string WebDir { get; set; } + /// + /// Gets or sets the path to the cache directory. + /// + /// The path to the cache directory. [Option('C', "cachedir", Required = false, HelpText = "Path to use for caching.")] public string CacheDir { get; set; } + /// + /// Gets or sets the path to the config directory. + /// + /// The path to the config directory. [Option('c', "configdir", Required = false, HelpText = "Path to use for configuration data (user settings and pictures).")] public string ConfigDir { get; set; } + /// + /// Gets or sets the path to the log directory. + /// + /// The path to the log directory. [Option('l', "logdir", Required = false, HelpText = "Path to use for writing log files.")] public string LogDir { get; set; } + /// [Option("ffmpeg", Required = false, HelpText = "Path to external FFmpeg executable to use in place of default found in PATH.")] public string FFmpegPath { get; set; } + /// [Option("service", Required = false, HelpText = "Run as headless service.")] public bool IsService { get; set; } + /// [Option("noautorunwebapp", Required = false, HelpText = "Run headless if startup wizard is complete.")] public bool NoAutoRunWebApp { get; set; } + /// [Option("package-name", Required = false, HelpText = "Used when packaging Jellyfin (example, synology).")] public string PackageName { get; set; } + /// [Option("restartpath", Required = false, HelpText = "Path to restart script.")] public string RestartPath { get; set; } + /// [Option("restartargs", Required = false, HelpText = "Arguments for restart script.")] public string RestartArgs { get; set; } } diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index c0f92ac4a..681a2e372 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -18,7 +18,7 @@ - + -- cgit v1.2.3 From 5eaf5465a55df0359f85077b7922ca4a45681831 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 29 Jul 2019 23:47:25 +0200 Subject: Check checksum for plugin downloads * Compare the MD5 checksum when downloading plugins * Reduced log spam due to http requests * Removed 'GetTempFileResponse' function from HttpClientManager * Fixed caching for HttpClientManager --- Emby.Dlna/PlayTo/SsdpHttpClient.cs | 10 - Emby.Server.Implementations/ApplicationHost.cs | 14 +- .../HttpClientManager/HttpClientManager.cs | 206 ++------------------- .../ScheduledTasks/Tasks/PluginUpdateTask.cs | 5 +- .../Updates/InstallationManager.cs | 140 +++++--------- MediaBrowser.Api/PackageService.cs | 2 +- MediaBrowser.Common/Extensions/HexHelper.cs | 22 +++ MediaBrowser.Common/Net/HttpRequestOptions.cs | 12 -- MediaBrowser.Common/Net/IHttpClient.cs | 16 -- .../Updates/IInstallationManager.cs | 7 +- MediaBrowser.Model/Cryptography/PasswordHash.cs | 1 + MediaBrowser.Model/Updates/InstallationInfo.cs | 6 - .../Studios/StudiosImageProvider.cs | 28 ++- Mono.Nat/Upnp/Messages/GetServicesMessage.cs | 39 ++-- Mono.Nat/Upnp/Messages/UpnpMessage.cs | 46 ++--- Mono.Nat/Upnp/UpnpNatDevice.cs | 4 +- jellyfin.ruleset | 2 + 17 files changed, 141 insertions(+), 419 deletions(-) create mode 100644 MediaBrowser.Common/Extensions/HexHelper.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs index 22aaa6885..217ea3a4b 100644 --- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs +++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs @@ -73,9 +73,6 @@ namespace Emby.Dlna.PlayTo UserAgent = USERAGENT, LogErrorResponseBody = true, BufferContent = false, - - // The periodic requests may keep some devices awake - LogRequestAsDebug = true }; options.RequestHeaders["HOST"] = ip + ":" + port.ToString(_usCulture); @@ -98,9 +95,6 @@ namespace Emby.Dlna.PlayTo LogErrorResponseBody = true, BufferContent = false, - // The periodic requests may keep some devices awake - LogRequestAsDebug = true, - CancellationToken = cancellationToken }; @@ -135,13 +129,9 @@ namespace Emby.Dlna.PlayTo { Url = url, UserAgent = USERAGENT, - LogRequest = logRequest || _config.GetDlnaConfiguration().EnableDebugLog, LogErrorResponseBody = true, BufferContent = false, - // The periodic requests may keep some devices awake - LogRequestAsDebug = true, - CancellationToken = cancellationToken }; diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 09847b2f8..6074e5218 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1528,8 +1528,6 @@ namespace Emby.Server.Implementations { Url = Url, LogErrorResponseBody = false, - LogErrors = false, - LogRequest = false, BufferContent = false, CancellationToken = cancellationToken }).ConfigureAwait(false)) @@ -1681,8 +1679,8 @@ namespace Emby.Server.Implementations private async Task IsIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken) { - if (address.Equals(IPAddress.Loopback) || - address.Equals(IPAddress.IPv6Loopback)) + if (address.Equals(IPAddress.Loopback) + || address.Equals(IPAddress.IPv6Loopback)) { return true; } @@ -1695,12 +1693,6 @@ namespace Emby.Server.Implementations return cachedResult; } -#if DEBUG - const bool LogPing = true; -#else - const bool LogPing = false; -#endif - try { using (var response = await HttpClient.SendAsync( @@ -1708,8 +1700,6 @@ namespace Emby.Server.Implementations { Url = apiUrl, LogErrorResponseBody = false, - LogErrors = LogPing, - LogRequest = LogPing, BufferContent = false, CancellationToken = cancellationToken }, HttpMethod.Post).ConfigureAwait(false)) diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index a933b53f5..0dd4d4ca5 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -5,9 +5,6 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; -using System.Net.Http.Headers; -using System.Text; -using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; @@ -20,7 +17,7 @@ using Microsoft.Net.Http.Headers; namespace Emby.Server.Implementations.HttpClientManager { /// - /// Class HttpClientManager + /// Class HttpClientManager. /// public class HttpClientManager : IHttpClient { @@ -45,19 +42,9 @@ namespace Emby.Server.Implementations.HttpClientManager IFileSystem fileSystem, Func defaultUserAgentFn) { - if (appPaths == null) - { - throw new ArgumentNullException(nameof(appPaths)); - } - - if (logger == null) - { - throw new ArgumentNullException(nameof(logger)); - } - - _logger = logger; + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _fileSystem = fileSystem; - _appPaths = appPaths; + _appPaths = appPaths ?? throw new ArgumentNullException(nameof(appPaths)); _defaultUserAgentFn = defaultUserAgentFn; } @@ -118,7 +105,7 @@ namespace Emby.Server.Implementations.HttpClientManager request.Headers.Add(HeaderNames.Connection, "Keep-Alive"); } - //request.Headers.Add(HeaderNames.CacheControl, "no-cache"); + // request.Headers.Add(HeaderNames.CacheControl, "no-cache"); /* if (!string.IsNullOrWhiteSpace(userInfo)) @@ -196,7 +183,7 @@ namespace Emby.Server.Implementations.HttpClientManager } var url = options.Url; - var urlHash = url.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture); + var urlHash = url.ToUpperInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture); var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash); @@ -239,7 +226,13 @@ namespace Emby.Server.Implementations.HttpClientManager { Directory.CreateDirectory(Path.GetDirectoryName(responseCachePath)); - using (var fileStream = _fileSystem.GetFileStream(responseCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true)) + using (var fileStream = new FileStream( + responseCachePath, + FileMode.Create, + FileAccess.Write, + FileShare.None, + StreamDefaults.DefaultFileStreamBufferSize, + true)) { await response.Content.CopyToAsync(fileStream).ConfigureAwait(false); @@ -278,16 +271,11 @@ namespace Emby.Server.Implementations.HttpClientManager } } - if (options.LogRequest) - { - _logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToString(), options.Url); - } - options.CancellationToken.ThrowIfCancellationRequested(); var response = await client.SendAsync( httpWebRequest, - options.BufferContent ? HttpCompletionOption.ResponseContentRead : HttpCompletionOption.ResponseHeadersRead, + options.BufferContent || options.CacheMode == CacheMode.Unconditional ? HttpCompletionOption.ResponseContentRead : HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false); await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); @@ -308,138 +296,6 @@ namespace Emby.Server.Implementations.HttpClientManager public Task Post(HttpRequestOptions options) => SendAsync(options, HttpMethod.Post); - /// - /// Downloads the contents of a given url into a temporary location - /// - /// The options. - /// Task{System.String}. - public async Task GetTempFile(HttpRequestOptions options) - { - var response = await GetTempFileResponse(options).ConfigureAwait(false); - return response.TempFilePath; - } - - public async Task GetTempFileResponse(HttpRequestOptions options) - { - ValidateParams(options); - - Directory.CreateDirectory(_appPaths.TempDirectory); - - var tempFile = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".tmp"); - - if (options.Progress == null) - { - throw new ArgumentException("Options did not have a Progress value.", nameof(options)); - } - - options.CancellationToken.ThrowIfCancellationRequested(); - - var httpWebRequest = GetRequestMessage(options, HttpMethod.Get); - - options.Progress.Report(0); - - if (options.LogRequest) - { - _logger.LogDebug("HttpClientManager.GetTempFileResponse url: {0}", options.Url); - } - - var client = GetHttpClient(options.Url); - - try - { - options.CancellationToken.ThrowIfCancellationRequested(); - - using (var response = (await client.SendAsync(httpWebRequest, options.CancellationToken).ConfigureAwait(false))) - { - await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); - - options.CancellationToken.ThrowIfCancellationRequested(); - - using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) - using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) - { - await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); - } - - options.Progress.Report(100); - - var responseInfo = new HttpResponseInfo(response.Headers, response.Content.Headers) - { - TempFilePath = tempFile, - StatusCode = response.StatusCode, - ContentType = response.Content.Headers.ContentType?.MediaType, - ContentLength = response.Content.Headers.ContentLength - }; - - return responseInfo; - } - } - catch (Exception ex) - { - if (File.Exists(tempFile)) - { - File.Delete(tempFile); - } - - throw GetException(ex, options); - } - } - - private Exception GetException(Exception ex, HttpRequestOptions options) - { - if (ex is HttpException) - { - return ex; - } - - var webException = ex as WebException - ?? ex.InnerException as WebException; - - if (webException != null) - { - if (options.LogErrors) - { - _logger.LogError(webException, "Error {Status} getting response from {Url}", webException.Status, options.Url); - } - - var exception = new HttpException(webException.Message, webException); - - using (var response = webException.Response as HttpWebResponse) - { - if (response != null) - { - exception.StatusCode = response.StatusCode; - } - } - - if (!exception.StatusCode.HasValue) - { - if (webException.Status == WebExceptionStatus.NameResolutionFailure || - webException.Status == WebExceptionStatus.ConnectFailure) - { - exception.IsTimedOut = true; - } - } - - return exception; - } - - var operationCanceledException = ex as OperationCanceledException - ?? ex.InnerException as OperationCanceledException; - - if (operationCanceledException != null) - { - return GetCancellationException(options, options.CancellationToken, operationCanceledException); - } - - if (options.LogErrors) - { - _logger.LogError(ex, "Error getting response from {Url}", options.Url); - } - - return ex; - } - private void ValidateParams(HttpRequestOptions options) { if (string.IsNullOrEmpty(options.Url)) @@ -471,35 +327,6 @@ namespace Emby.Server.Implementations.HttpClientManager return url; } - /// - /// Throws the cancellation exception. - /// - /// The options. - /// The cancellation token. - /// The exception. - /// Exception. - private Exception GetCancellationException(HttpRequestOptions options, CancellationToken cancellationToken, OperationCanceledException exception) - { - // If the HttpClient's timeout is reached, it will cancel the Task internally - if (!cancellationToken.IsCancellationRequested) - { - var msg = string.Format("Connection to {0} timed out", options.Url); - - if (options.LogErrors) - { - _logger.LogError(msg); - } - - // Throw an HttpException so that the caller doesn't think it was cancelled by user code - return new HttpException(msg, exception) - { - IsTimedOut = true - }; - } - - return exception; - } - private async Task EnsureSuccessStatusCode(HttpResponseMessage response, HttpRequestOptions options) { if (response.IsSuccessStatusCode) @@ -507,8 +334,11 @@ namespace Emby.Server.Implementations.HttpClientManager return; } - var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - _logger.LogError("HTTP request failed with message: {Message}", msg); + if (options.LogErrorResponseBody) + { + var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + _logger.LogError("HTTP request failed with message: {Message}", msg); + } throw new HttpException(response.ReasonPhrase) { diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs index bde7d5c81..7afeba9dd 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs @@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.ScheduledTasks try { - await _installationManager.InstallPackage(package, new SimpleProgress(), cancellationToken).ConfigureAwait(false); + await _installationManager.InstallPackage(package, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { @@ -87,8 +87,7 @@ namespace Emby.Server.Implementations.ScheduledTasks // Update progress lock (progress) { - numComplete++; - progress.Report(90.0 * numComplete / packagesToInstall.Count + 10); + progress.Report((90.0 * ++numComplete / packagesToInstall.Count) + 10); } } diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 9bc85633d..c60319964 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -4,13 +4,14 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; +using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; -using MediaBrowser.Common.Progress; using MediaBrowser.Common.Updates; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Events; @@ -126,13 +127,16 @@ namespace Emby.Server.Implementations.Updates /// Task{List{PackageInfo}}. public async Task> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken) { - using (var response = await _httpClient.SendAsync(new HttpRequestOptions - { - Url = "https://repo.jellyfin.org/releases/plugin/manifest.json", - CancellationToken = cancellationToken, - CacheLength = GetCacheLength() - }, HttpMethod.Get).ConfigureAwait(false)) - using (var stream = response.Content) + using (var response = await _httpClient.SendAsync( + new HttpRequestOptions + { + Url = "https://repo.jellyfin.org/releases/plugin/manifest.json", + CancellationToken = cancellationToken, + CacheMode = CacheMode.Unconditional, + CacheLength = GetCacheLength() + }, + HttpMethod.Get).ConfigureAwait(false)) + using (Stream stream = response.Content) { return FilterPackages(await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false)); } @@ -309,27 +313,14 @@ namespace Emby.Server.Implementations.Updates .Where(p => !string.IsNullOrEmpty(p.sourceUrl) && !CompletedInstallations.Any(i => string.Equals(i.AssemblyGuid, p.guid, StringComparison.OrdinalIgnoreCase))); } - /// - /// Installs the package. - /// - /// The package. - /// if set to true [is plugin]. - /// The progress. - /// The cancellation token. - /// Task. - /// package - public async Task InstallPackage(PackageVersionInfo package, IProgress progress, CancellationToken cancellationToken) + /// + public async Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken) { if (package == null) { throw new ArgumentNullException(nameof(package)); } - if (progress == null) - { - throw new ArgumentNullException(nameof(progress)); - } - var installationInfo = new InstallationInfo { Id = Guid.NewGuid(), @@ -349,16 +340,6 @@ namespace Emby.Server.Implementations.Updates _currentInstallations.Add(tuple); } - var innerProgress = new ActionableProgress(); - - // Whenever the progress updates, update the outer progress object and InstallationInfo - innerProgress.RegisterAction(percent => - { - progress.Report(percent); - - installationInfo.PercentComplete = percent; - }); - var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token; var installationEventArgs = new InstallationEventArgs @@ -371,7 +352,7 @@ namespace Emby.Server.Implementations.Updates try { - await InstallPackageInternal(package, innerProgress, linkedToken).ConfigureAwait(false); + await InstallPackageInternal(package, linkedToken).ConfigureAwait(false); lock (_currentInstallations) { @@ -423,20 +404,16 @@ namespace Emby.Server.Implementations.Updates /// Installs the package internal. /// /// The package. - /// if set to true [is plugin]. - /// The progress. /// The cancellation token. - /// Task. - private async Task InstallPackageInternal(PackageVersionInfo package, IProgress progress, CancellationToken cancellationToken) + /// . + private async Task InstallPackageInternal(PackageVersionInfo package, CancellationToken cancellationToken) { // Set last update time if we were installed before IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase)) ?? _applicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase)); - string targetPath = plugin == null ? null : plugin.AssemblyFilePath; - // Do the install - await PerformPackageInstallation(progress, targetPath, package, cancellationToken).ConfigureAwait(false); + await PerformPackageInstallation(package, cancellationToken).ConfigureAwait(false); // Do plugin-specific processing if (plugin == null) @@ -455,76 +432,57 @@ namespace Emby.Server.Implementations.Updates _applicationHost.NotifyPendingRestart(); } - private async Task PerformPackageInstallation(IProgress progress, string target, PackageVersionInfo package, CancellationToken cancellationToken) + private async Task PerformPackageInstallation(PackageVersionInfo package, CancellationToken cancellationToken) { - // TODO: Remove the `string target` argument as it is not used any longer - var extension = Path.GetExtension(package.targetFilename); - var isArchive = string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase); - - if (!isArchive) + if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase)) { _logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.targetFilename); return; } // Always override the passed-in target (which is a file) and figure it out again - target = Path.Combine(_appPaths.PluginsPath, package.name); - _logger.LogDebug("Installing plugin to {Filename}.", target); - - // Download to temporary file so that, if interrupted, it won't destroy the existing installation - _logger.LogDebug("Downloading ZIP."); - var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions - { - Url = package.sourceUrl, - CancellationToken = cancellationToken, - Progress = progress - - }).ConfigureAwait(false); + string targetDir = Path.Combine(_appPaths.PluginsPath, package.name); - cancellationToken.ThrowIfCancellationRequested(); - - // TODO: Validate with a checksum, *properly* - - // Check if the target directory already exists, and remove it if so - if (Directory.Exists(target)) - { - _logger.LogDebug("Deleting existing plugin at {Filename}.", target); - Directory.Delete(target, true); - } +// CA5351: Do Not Use Broken Cryptographic Algorithms +#pragma warning disable CA5351 + using (var res = await _httpClient.SendAsync( + new HttpRequestOptions + { + Url = package.sourceUrl, + CancellationToken = cancellationToken, + // We need it to be buffered for setting the position + BufferContent = true + }, + HttpMethod.Get).ConfigureAwait(false)) + using (var stream = res.Content) + using (var md5 = MD5.Create()) + { + cancellationToken.ThrowIfCancellationRequested(); + + var hash = HexHelper.ToHexString(md5.ComputeHash(stream)); + if (!string.Equals(package.checksum, hash, StringComparison.OrdinalIgnoreCase)) + { + _logger.LogDebug("{0}, {1}", package.checksum, hash); + throw new InvalidDataException($"The checksums didn't match while installing {package.name}."); + } - // Success - move it to the real target - try - { - _logger.LogDebug("Extracting ZIP {TempFile} to {Filename}.", tempFile, target); - using (var stream = File.OpenRead(tempFile)) + if (Directory.Exists(targetDir)) { - _zipClient.ExtractAllFromZip(stream, target, true); + Directory.Delete(targetDir); } - } - catch (IOException ex) - { - _logger.LogError(ex, "Error attempting to extract {TempFile} to {TargetFile}", tempFile, target); - throw; - } - try - { - _logger.LogDebug("Deleting temporary file {Filename}.", tempFile); - _fileSystem.DeleteFile(tempFile); - } - catch (IOException ex) - { - // Don't fail because of this - _logger.LogError(ex, "Error deleting temp file {TempFile}", tempFile); + stream.Position = 0; + _zipClient.ExtractAllFromZip(stream, targetDir, true); } + +#pragma warning restore CA5351 } /// /// Uninstalls a plugin /// /// The plugin. - /// public void UninstallPlugin(IPlugin plugin) { plugin.OnUninstalling(); diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs index cf1e08d53..915c9784e 100644 --- a/MediaBrowser.Api/PackageService.cs +++ b/MediaBrowser.Api/PackageService.cs @@ -197,7 +197,7 @@ namespace MediaBrowser.Api throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name)); } - await _installationManager.InstallPackage(package, new SimpleProgress(), CancellationToken.None); + await _installationManager.InstallPackage(package, CancellationToken.None); } /// diff --git a/MediaBrowser.Common/Extensions/HexHelper.cs b/MediaBrowser.Common/Extensions/HexHelper.cs new file mode 100644 index 000000000..3d80d94ac --- /dev/null +++ b/MediaBrowser.Common/Extensions/HexHelper.cs @@ -0,0 +1,22 @@ +using System; +using System.Globalization; + +namespace MediaBrowser.Common.Extensions +{ + public static class HexHelper + { + public static byte[] FromHexString(string str) + { + byte[] bytes = new byte[str.Length / 2]; + for (int i = 0; i < str.Length; i += 2) + { + bytes[i / 2] = byte.Parse(str.Substring(i, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); + } + + return bytes; + } + + public static string ToHexString(byte[] bytes) + => BitConverter.ToString(bytes).Replace("-", ""); + } +} diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs index 76bd35e57..94b972a02 100644 --- a/MediaBrowser.Common/Net/HttpRequestOptions.cs +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -64,12 +64,6 @@ namespace MediaBrowser.Common.Net set => RequestHeaders[HeaderNames.Host] = value; } - /// - /// Gets or sets the progress. - /// - /// The progress. - public IProgress Progress { get; set; } - public Dictionary RequestHeaders { get; private set; } public string RequestContentType { get; set; } @@ -79,10 +73,6 @@ namespace MediaBrowser.Common.Net public bool BufferContent { get; set; } - public bool LogRequest { get; set; } - public bool LogRequestAsDebug { get; set; } - public bool LogErrors { get; set; } - public bool LogErrorResponseBody { get; set; } public bool EnableKeepAlive { get; set; } @@ -105,8 +95,6 @@ namespace MediaBrowser.Common.Net { RequestHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); - LogRequest = true; - LogErrors = true; CacheMode = CacheMode.None; DecompressionMethod = CompressionMethod.Deflate; } diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs index db69c6f2c..d84a4d664 100644 --- a/MediaBrowser.Common/Net/IHttpClient.cs +++ b/MediaBrowser.Common/Net/IHttpClient.cs @@ -47,21 +47,5 @@ namespace MediaBrowser.Common.Net /// The options. /// Task{HttpResponseInfo}. Task Post(HttpRequestOptions options); - - /// - /// Downloads the contents of a given url into a temporary location - /// - /// The options. - /// Task{System.String}. - /// progress - /// - Task GetTempFile(HttpRequestOptions options); - - /// - /// Gets the temporary file response. - /// - /// The options. - /// Task{HttpResponseInfo}. - Task GetTempFileResponse(HttpRequestOptions options); } } diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs index 3472a5692..d43024ac8 100644 --- a/MediaBrowser.Common/Updates/IInstallationManager.cs +++ b/MediaBrowser.Common/Updates/IInstallationManager.cs @@ -97,12 +97,9 @@ namespace MediaBrowser.Common.Updates /// Installs the package. /// /// The package. - /// if set to true [is plugin]. - /// The progress. /// The cancellation token. - /// Task. - /// package - Task InstallPackage(PackageVersionInfo package, IProgress progress, CancellationToken cancellationToken); + /// . + Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken); /// /// Uninstalls a plugin diff --git a/MediaBrowser.Model/Cryptography/PasswordHash.cs b/MediaBrowser.Model/Cryptography/PasswordHash.cs index df32fdb00..4bcf0c117 100644 --- a/MediaBrowser.Model/Cryptography/PasswordHash.cs +++ b/MediaBrowser.Model/Cryptography/PasswordHash.cs @@ -84,6 +84,7 @@ namespace MediaBrowser.Model.Cryptography _hash = Array.Empty(); } + // TODO: move this class and use the HexHelper class public static byte[] ConvertFromByteString(string byteString) { byte[] bytes = new byte[byteString.Length / 2]; diff --git a/MediaBrowser.Model/Updates/InstallationInfo.cs b/MediaBrowser.Model/Updates/InstallationInfo.cs index a3f19e236..7554e9fe2 100644 --- a/MediaBrowser.Model/Updates/InstallationInfo.cs +++ b/MediaBrowser.Model/Updates/InstallationInfo.cs @@ -36,11 +36,5 @@ namespace MediaBrowser.Model.Updates /// /// The update class. public PackageVersionClass UpdateClass { get; set; } - - /// - /// Gets or sets the percent complete. - /// - /// The percent complete. - public double? PercentComplete { get; set; } } } diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs index 4b41589f1..ef412db5a 100644 --- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs +++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs @@ -2,10 +2,10 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; -using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; @@ -143,26 +143,20 @@ namespace MediaBrowser.Providers.Studios if (!fileInfo.Exists || (DateTime.UtcNow - fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays > 1) { - var temp = await httpClient.GetTempFile(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Progress = new SimpleProgress(), - Url = url - - }).ConfigureAwait(false); - Directory.CreateDirectory(Path.GetDirectoryName(file)); - try - { - File.Copy(temp, file, true); - } - catch + using (var res = await httpClient.SendAsync( + new HttpRequestOptions + { + CancellationToken = cancellationToken, + Url = url + }, + HttpMethod.Get).ConfigureAwait(false)) + using (var content = res.Content) + using (var fileStream = new FileStream(file, FileMode.Create)) { - + await content.CopyToAsync(fileStream).ConfigureAwait(false); } - - return temp; } return file; diff --git a/Mono.Nat/Upnp/Messages/GetServicesMessage.cs b/Mono.Nat/Upnp/Messages/GetServicesMessage.cs index 72b4c2b25..f619f5ca4 100644 --- a/Mono.Nat/Upnp/Messages/GetServicesMessage.cs +++ b/Mono.Nat/Upnp/Messages/GetServicesMessage.cs @@ -25,50 +25,37 @@ // using System; -using System.Diagnostics; using System.Net; using MediaBrowser.Common.Net; -using Microsoft.Extensions.Logging; namespace Mono.Nat.Upnp { internal class GetServicesMessage : MessageBase { - private string servicesDescriptionUrl; - private EndPoint hostAddress; - private readonly ILogger _logger; + private string _servicesDescriptionUrl; + private EndPoint _hostAddress; - public GetServicesMessage(string description, EndPoint hostAddress, ILogger logger) + public GetServicesMessage(string description, EndPoint hostAddress) : base(null) { if (string.IsNullOrEmpty(description)) - _logger.LogWarning("Description is null"); - - if (hostAddress == null) - _logger.LogWarning("hostaddress is null"); - - this.servicesDescriptionUrl = description; - this.hostAddress = hostAddress; - _logger = logger; - } - - public override string Method - { - get { - return "GET"; + throw new ArgumentException("Description is null/empty", nameof(description)); } + + this._servicesDescriptionUrl = description; + this._hostAddress = hostAddress ?? throw new ArgumentNullException(nameof(hostAddress)); } + public override string Method => "GET"; + public override HttpRequestOptions Encode() { - var req = new HttpRequestOptions(); - - // The periodic request logging may keep some devices awake - req.LogRequestAsDebug = true; - req.LogErrors = false; + var req = new HttpRequestOptions() + { + Url = $"http://{this._hostAddress}{this._servicesDescriptionUrl}" + }; - req.Url = "http://" + this.hostAddress.ToString() + this.servicesDescriptionUrl; req.RequestHeaders.Add("ACCEPT-LANGUAGE", "en"); return req; diff --git a/Mono.Nat/Upnp/Messages/UpnpMessage.cs b/Mono.Nat/Upnp/Messages/UpnpMessage.cs index ade9df50b..d47241d4a 100644 --- a/Mono.Nat/Upnp/Messages/UpnpMessage.cs +++ b/Mono.Nat/Upnp/Messages/UpnpMessage.cs @@ -24,13 +24,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -using System; -using System.Diagnostics; using System.Xml; -using System.Net; -using System.IO; using System.Text; -using System.Globalization; using MediaBrowser.Common.Net; namespace Mono.Nat.Upnp @@ -46,38 +41,31 @@ namespace Mono.Nat.Upnp protected HttpRequestOptions CreateRequest(string upnpMethod, string methodParameters) { - string ss = "http://" + this.device.HostEndPoint.ToString() + this.device.ControlUrl; + var req = new HttpRequestOptions() + { + Url = $"http://{this.device.HostEndPoint}{this.device.ControlUrl}", + EnableKeepAlive = false, + RequestContentType = "text/xml", + RequestContent = "" + + "" + + "" + + methodParameters + + "" + + "" + + "\r\n\r\n" + }; - var req = new HttpRequestOptions(); - req.LogErrors = false; - - // The periodic request logging may keep some devices awake - req.LogRequestAsDebug = true; - - req.Url = ss; - req.EnableKeepAlive = false; - req.RequestContentType = "text/xml"; req.RequestHeaders.Add("SOAPACTION", "\"" + device.ServiceType + "#" + upnpMethod + "\""); - req.RequestContent = "" - + "" - + "" - + methodParameters - + "" - + "" - + "\r\n\r\n"; return req; } public abstract HttpRequestOptions Encode(); - public virtual string Method - { - get { return "POST"; } - } + public virtual string Method => "POST"; protected void WriteFullElement(XmlWriter writer, string element, string value) { diff --git a/Mono.Nat/Upnp/UpnpNatDevice.cs b/Mono.Nat/Upnp/UpnpNatDevice.cs index fd408ee63..3ff1eeb90 100644 --- a/Mono.Nat/Upnp/UpnpNatDevice.cs +++ b/Mono.Nat/Upnp/UpnpNatDevice.cs @@ -27,11 +27,9 @@ // using System; -using System.IO; using System.Net; using System.Xml; using System.Text; -using System.Diagnostics; using System.Threading.Tasks; using MediaBrowser.Common.Net; using Microsoft.Extensions.Logging; @@ -96,7 +94,7 @@ namespace Mono.Nat.Upnp public async Task GetServicesList() { // Create a HTTPWebRequest to download the list of services the device offers - var message = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint, _logger); + var message = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint); using (var response = await _httpClient.SendAsync(message.Encode(), message.Method).ConfigureAwait(false)) { diff --git a/jellyfin.ruleset b/jellyfin.ruleset index e7e02a7d5..8ea1d6b16 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -38,5 +38,7 @@ + + -- cgit v1.2.3 From 72436892154892c53c75ee5fdcbcb3bf843ac85c Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 11 Aug 2019 15:57:36 +0200 Subject: Minor improvements --- Emby.Server.Implementations/Updates/InstallationManager.cs | 8 +------- MediaBrowser.Api/PackageService.cs | 2 +- MediaBrowser.Common/Updates/IInstallationManager.cs | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index c60319964..2f84b91ec 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -279,12 +279,7 @@ namespace Emby.Server.Implementations.Updates var package = availablePackages.FirstOrDefault(p => string.Equals(p.guid, guid ?? "none", StringComparison.OrdinalIgnoreCase)) ?? availablePackages.FirstOrDefault(p => p.name.Equals(name, StringComparison.OrdinalIgnoreCase)); - if (package == null) - { - return null; - } - - return package.versions + return package?.versions .OrderByDescending(x => x.Version) .FirstOrDefault(v => v.classification <= classification && IsPackageVersionUpToDate(v, currentServerVersion)); } @@ -308,7 +303,6 @@ namespace Emby.Server.Implementations.Updates var latestPluginInfo = GetLatestCompatibleVersion(catalog, p.Name, p.Id.ToString(), applicationVersion, systemUpdateLevel); return latestPluginInfo != null && latestPluginInfo.Version > p.Version ? latestPluginInfo : null; - }).Where(i => i != null) .Where(p => !string.IsNullOrEmpty(p.sourceUrl) && !CompletedInstallations.Any(i => string.Equals(i.AssemblyGuid, p.guid, StringComparison.OrdinalIgnoreCase))); } diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs index 915c9784e..baa6f7bb9 100644 --- a/MediaBrowser.Api/PackageService.cs +++ b/MediaBrowser.Api/PackageService.cs @@ -197,7 +197,7 @@ namespace MediaBrowser.Api throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name)); } - await _installationManager.InstallPackage(package, CancellationToken.None); + await _installationManager.InstallPackage(package); } /// diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs index d43024ac8..88ac7e473 100644 --- a/MediaBrowser.Common/Updates/IInstallationManager.cs +++ b/MediaBrowser.Common/Updates/IInstallationManager.cs @@ -99,7 +99,7 @@ namespace MediaBrowser.Common.Updates /// The package. /// The cancellation token. /// . - Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken); + Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken = default); /// /// Uninstalls a plugin -- cgit v1.2.3 From 838e5d05d51c4c171c07d512f741304a6dc58f24 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 11 Aug 2019 16:52:37 +0200 Subject: Document all public/internal members of Emby.Drawing Forces all new public/internal members to be documented. Enables TreatWarningsAsErrors for Emby.Drawing --- BDInfo/BDInfo.csproj | 1 + DvdLib/DvdLib.csproj | 1 + Emby.Dlna/Emby.Dlna.csproj | 1 + Emby.Drawing/Emby.Drawing.csproj | 2 + Emby.Drawing/ImageProcessor.cs | 159 ++++++++------------- Emby.Drawing/NullImageEncoder.cs | 34 +++-- Emby.IsoMounting/IsoMounter/IsoMounter.csproj | 1 + Emby.Notifications/Emby.Notifications.csproj | 1 + Emby.Photos/Emby.Photos.csproj | 1 + Emby.Server.Implementations/ApplicationHost.cs | 21 +-- Emby.Server.Implementations/Dto/DtoService.cs | 2 +- Emby.XmlTv/Emby.XmlTv/Emby.XmlTv.csproj | 1 + Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj | 1 + MediaBrowser.Api/Images/ImageService.cs | 8 +- MediaBrowser.Api/MediaBrowser.Api.csproj | 1 + MediaBrowser.Common/IApplicationHost.cs | 8 +- MediaBrowser.Common/MediaBrowser.Common.csproj | 1 + MediaBrowser.Controller/Drawing/IImageEncoder.cs | 21 +-- MediaBrowser.Controller/Drawing/IImageProcessor.cs | 30 ++-- .../Drawing/ImageProcessingOptions.cs | 5 +- .../MediaBrowser.Controller.csproj | 1 + .../MediaBrowser.LocalMetadata.csproj | 1 + .../MediaBrowser.MediaEncoding.csproj | 1 + MediaBrowser.Model/MediaBrowser.Model.csproj | 1 + .../MediaBrowser.Providers.csproj | 1 + .../MediaBrowser.WebDashboard.csproj | 1 + .../MediaBrowser.XbmcMetadata.csproj | 1 + 27 files changed, 141 insertions(+), 166 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/BDInfo/BDInfo.csproj b/BDInfo/BDInfo.csproj index b2c752d0c..9dbaa9e2f 100644 --- a/BDInfo/BDInfo.csproj +++ b/BDInfo/BDInfo.csproj @@ -11,6 +11,7 @@ netstandard2.0 false + true diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj index b2c752d0c..9dbaa9e2f 100644 --- a/DvdLib/DvdLib.csproj +++ b/DvdLib/DvdLib.csproj @@ -11,6 +11,7 @@ netstandard2.0 false + true diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 4c07087c5..34b49120b 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -14,6 +14,7 @@ netstandard2.0 false + true diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index 9f97baf77..716de059d 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -3,6 +3,8 @@ netstandard2.0 false + true + true diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index a7d95eb20..ce8089e59 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -22,42 +22,47 @@ using Microsoft.Extensions.Logging; namespace Emby.Drawing { /// - /// Class ImageProcessor + /// Class ImageProcessor. /// public class ImageProcessor : IImageProcessor, IDisposable { - /// - /// The us culture - /// - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); + // Increment this when there's a change requiring caches to be invalidated + private const string Version = "3"; - /// - /// Gets the list of currently registered image processors - /// Image processors are specialized metadata providers that run after the normal ones - /// - /// The image enhancers. - public IImageEnhancer[] ImageEnhancers { get; private set; } + private static readonly HashSet _transparentImageTypes + = new HashSet(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" }; /// /// The _logger /// private readonly ILogger _logger; - private readonly IFileSystem _fileSystem; private readonly IServerApplicationPaths _appPaths; private IImageEncoder _imageEncoder; private readonly Func _libraryManager; private readonly Func _mediaEncoder; + private readonly Dictionary _locks = new Dictionary(); + private bool _disposed = false; + + /// + /// + /// + /// + /// + /// + /// + /// + /// public ImageProcessor( - ILoggerFactory loggerFactory, + ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IImageEncoder imageEncoder, Func libraryManager, Func mediaEncoder) { - _logger = loggerFactory.CreateLogger(nameof(ImageProcessor)); + _logger = logger; _fileSystem = fileSystem; _imageEncoder = imageEncoder; _libraryManager = libraryManager; @@ -69,20 +74,11 @@ namespace Emby.Drawing ImageHelper.ImageProcessor = this; } - public IImageEncoder ImageEncoder - { - get => _imageEncoder; - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } + private string ResizedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "resized-images"); - _imageEncoder = value; - } - } + private string EnhancedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "enhanced-images"); + /// public IReadOnlyCollection SupportedInputFormats => new HashSet(StringComparer.OrdinalIgnoreCase) { @@ -115,18 +111,20 @@ namespace Emby.Drawing "wbmp" }; + /// + public IReadOnlyCollection ImageEnhancers { get; set; } + /// public bool SupportsImageCollageCreation => _imageEncoder.SupportsImageCollageCreation; - private string ResizedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "resized-images"); - - private string EnhancedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "enhanced-images"); - - public void AddParts(IEnumerable enhancers) + /// + public IImageEncoder ImageEncoder { - ImageEnhancers = enhancers.ToArray(); + get => _imageEncoder; + set => _imageEncoder = value ?? throw new ArgumentNullException(nameof(value)); } + /// public async Task ProcessImage(ImageProcessingOptions options, Stream toStream) { var file = await ProcessImage(options).ConfigureAwait(false); @@ -137,15 +135,15 @@ namespace Emby.Drawing } } + /// public IReadOnlyCollection GetSupportedImageOutputFormats() => _imageEncoder.SupportedOutputFormats; - private static readonly HashSet TransparentImageTypes - = new HashSet(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" }; - + /// public bool SupportsTransparency(string path) - => TransparentImageTypes.Contains(Path.GetExtension(path)); + => _transparentImageTypes.Contains(Path.GetExtension(path)); + /// public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options) { if (options == null) @@ -187,9 +185,9 @@ namespace Emby.Drawing } dateModified = supportedImageInfo.dateModified; - bool requiresTransparency = TransparentImageTypes.Contains(Path.GetExtension(originalImagePath)); + bool requiresTransparency = _transparentImageTypes.Contains(Path.GetExtension(originalImagePath)); - if (options.Enhancers.Length > 0) + if (options.Enhancers.Count > 0) { if (item == null) { @@ -279,7 +277,7 @@ namespace Emby.Drawing } } - private ImageFormat GetOutputFormat(ImageFormat[] clientSupportedFormats, bool requiresTransparency) + private ImageFormat GetOutputFormat(IReadOnlyCollection clientSupportedFormats, bool requiresTransparency) { var serverFormats = GetSupportedImageOutputFormats(); @@ -320,11 +318,6 @@ namespace Emby.Drawing } } - /// - /// Increment this when there's a change requiring caches to be invalidated - /// - private const string Version = "3"; - /// /// Gets the cache file path based on a set of parameters /// @@ -372,9 +365,11 @@ namespace Emby.Drawing return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLowerInvariant()); } + /// public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info) => GetImageDimensions(item, info, true); + /// public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem) { int width = info.Width; @@ -400,26 +395,19 @@ namespace Emby.Drawing return size; } - /// - /// Gets the size of the image. - /// + /// public ImageDimensions GetImageDimensions(string path) => _imageEncoder.GetImageSize(path); - /// - /// Gets the image cache tag. - /// - /// The item. - /// The image. - /// Guid. - /// item + /// public string GetImageCacheTag(BaseItem item, ItemImageInfo image) { - var supportedEnhancers = GetSupportedEnhancers(item, image.Type); + var supportedEnhancers = GetSupportedEnhancers(item, image.Type).ToArray(); return GetImageCacheTag(item, image, supportedEnhancers); } + /// public string GetImageCacheTag(BaseItem item, ChapterInfo chapter) { try @@ -437,22 +425,15 @@ namespace Emby.Drawing } } - /// - /// Gets the image cache tag. - /// - /// The item. - /// The image. - /// The image enhancers. - /// Guid. - /// item - public string GetImageCacheTag(BaseItem item, ItemImageInfo image, IImageEnhancer[] imageEnhancers) + /// + public string GetImageCacheTag(BaseItem item, ItemImageInfo image, IReadOnlyCollection imageEnhancers) { string originalImagePath = image.Path; DateTime dateModified = image.DateModified; ImageType imageType = image.Type; // Optimization - if (imageEnhancers.Length == 0) + if (imageEnhancers.Count == 0) { return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture); } @@ -480,7 +461,7 @@ namespace Emby.Drawing { try { - string filename = (originalImagePath + dateModified.Ticks.ToString(UsCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture); + string filename = (originalImagePath + dateModified.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture); string cacheExtension = _mediaEncoder().SupportsEncoder("libwebp") ? ".webp" : ".png"; var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension); @@ -507,16 +488,10 @@ namespace Emby.Drawing return (originalImagePath, dateModified); } - /// - /// Gets the enhanced image. - /// - /// The item. - /// Type of the image. - /// Index of the image. - /// Task{System.String}. + /// public async Task GetEnhancedImage(BaseItem item, ImageType imageType, int imageIndex) { - var enhancers = GetSupportedEnhancers(item, imageType); + var enhancers = GetSupportedEnhancers(item, imageType).ToArray(); ItemImageInfo imageInfo = item.GetImageInfo(imageType, imageIndex); @@ -532,7 +507,7 @@ namespace Emby.Drawing bool inputImageSupportsTransparency, BaseItem item, int imageIndex, - IImageEnhancer[] enhancers, + IReadOnlyCollection enhancers, CancellationToken cancellationToken) { var originalImagePath = image.Path; @@ -573,6 +548,7 @@ namespace Emby.Drawing /// Index of the image. /// The supported enhancers. /// The cache unique identifier. + /// The cancellation token. /// Task<System.String>. /// /// originalImagePath @@ -584,9 +560,9 @@ namespace Emby.Drawing BaseItem item, ImageType imageType, int imageIndex, - IImageEnhancer[] supportedEnhancers, + IReadOnlyCollection supportedEnhancers, string cacheGuid, - CancellationToken cancellationToken) + CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(originalImagePath)) { @@ -680,6 +656,7 @@ namespace Emby.Drawing { throw new ArgumentNullException(nameof(path)); } + if (string.IsNullOrEmpty(uniqueName)) { throw new ArgumentNullException(nameof(uniqueName)); @@ -722,6 +699,7 @@ namespace Emby.Drawing return Path.Combine(path, prefix, filename); } + /// public void CreateImageCollage(ImageCollageOptions options) { _logger.LogInformation("Creating image collage and saving to {Path}", options.OutputPath); @@ -731,38 +709,25 @@ namespace Emby.Drawing _logger.LogInformation("Completed creation of image collage and saved to {Path}", options.OutputPath); } - public IImageEnhancer[] GetSupportedEnhancers(BaseItem item, ImageType imageType) + /// + public IEnumerable GetSupportedEnhancers(BaseItem item, ImageType imageType) { - List list = null; - foreach (var i in ImageEnhancers) { - try - { - if (i.Supports(item, imageType)) - { - if (list == null) - { - list = new List(); - } - list.Add(i); - } - } - catch (Exception ex) + if (i.Supports(item, imageType)) { - _logger.LogError(ex, "Error in image enhancer: {0}", i.GetType().Name); + yield return i; } } - - return list == null ? Array.Empty() : list.ToArray(); } - private Dictionary _locks = new Dictionary(); + private class LockInfo { public SemaphoreSlim Lock = new SemaphoreSlim(1, 1); public int Count = 1; } + private LockInfo GetLock(string key) { lock (_locks) @@ -795,7 +760,7 @@ namespace Emby.Drawing } } - private bool _disposed; + /// public void Dispose() { _disposed = true; diff --git a/Emby.Drawing/NullImageEncoder.cs b/Emby.Drawing/NullImageEncoder.cs index fc4a5af9f..5af7f1622 100644 --- a/Emby.Drawing/NullImageEncoder.cs +++ b/Emby.Drawing/NullImageEncoder.cs @@ -5,36 +5,40 @@ using MediaBrowser.Model.Drawing; namespace Emby.Drawing { + /// + /// A fallback implementation of . + /// public class NullImageEncoder : IImageEncoder { + /// public IReadOnlyCollection SupportedInputFormats => new HashSet(StringComparer.OrdinalIgnoreCase) { "png", "jpeg", "jpg" }; + /// public IReadOnlyCollection SupportedOutputFormats => new HashSet() { ImageFormat.Jpg, ImageFormat.Png }; - public void CropWhiteSpace(string inputPath, string outputPath) - { - throw new NotImplementedException(); - } - - public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat) - { - throw new NotImplementedException(); - } - - public void CreateImageCollage(ImageCollageOptions options) - { - throw new NotImplementedException(); - } - + /// public string Name => "Null Image Encoder"; + /// public bool SupportsImageCollageCreation => false; + /// public bool SupportsImageEncoding => false; + /// public ImageDimensions GetImageSize(string path) + => throw new NotImplementedException(); + + /// + public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat) + { + throw new NotImplementedException(); + } + + /// + public void CreateImageCollage(ImageCollageOptions options) { throw new NotImplementedException(); } diff --git a/Emby.IsoMounting/IsoMounter/IsoMounter.csproj b/Emby.IsoMounting/IsoMounter/IsoMounter.csproj index dafa51cd5..0778b987b 100644 --- a/Emby.IsoMounting/IsoMounter/IsoMounter.csproj +++ b/Emby.IsoMounting/IsoMounter/IsoMounter.csproj @@ -12,6 +12,7 @@ netstandard2.0 false + true diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj index 5c68e48c8..cbd3bde4f 100644 --- a/Emby.Notifications/Emby.Notifications.csproj +++ b/Emby.Notifications/Emby.Notifications.csproj @@ -3,6 +3,7 @@ netstandard2.0 false + true diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index c9830abc5..39b97a3bf 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -16,6 +16,7 @@ netstandard2.0 false + true diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 09847b2f8..b620f6be2 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -510,13 +510,8 @@ namespace Emby.Server.Implementations return AllConcreteTypes.Where(i => currentType.IsAssignableFrom(i)); } - /// - /// Gets the exports. - /// - /// The type - /// if set to true [manage lifetime]. - /// IEnumerable{``0}. - public IEnumerable GetExports(bool manageLifetime = true) + /// + public IReadOnlyCollection GetExports(bool manageLifetime = true) { var parts = GetExportTypes() .Select(CreateInstanceSafe) @@ -538,6 +533,7 @@ namespace Emby.Server.Implementations /// /// Runs the startup tasks. /// + /// . public async Task RunStartupTasksAsync() { Logger.LogInformation("Running startup tasks"); @@ -550,7 +546,7 @@ namespace Emby.Server.Implementations Logger.LogInformation("ServerId: {0}", SystemId); - var entryPoints = GetExports().ToList(); + var entryPoints = GetExports(); var stopWatch = new Stopwatch(); stopWatch.Start(); @@ -801,7 +797,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(HttpServer); - ImageProcessor = GetImageProcessor(); + ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder); serviceCollection.AddSingleton(ImageProcessor); TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager); @@ -953,11 +949,6 @@ namespace Emby.Server.Implementations } } - private IImageProcessor GetImageProcessor() - { - return new ImageProcessor(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder); - } - /// /// Gets the user repository. /// @@ -1087,7 +1078,7 @@ namespace Emby.Server.Implementations GetExports(), GetExports()); - ImageProcessor.AddParts(GetExports()); + ImageProcessor.ImageEnhancers = GetExports(); LiveTvManager.AddParts(GetExports(), GetExports(), GetExports()); diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 6e7aa1313..1a7f10634 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -1364,7 +1364,7 @@ namespace Emby.Server.Implementations.Dto return null; } - var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary); + var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToArray(); ImageDimensions size; diff --git a/Emby.XmlTv/Emby.XmlTv/Emby.XmlTv.csproj b/Emby.XmlTv/Emby.XmlTv/Emby.XmlTv.csproj index 0225be2c2..04f558173 100644 --- a/Emby.XmlTv/Emby.XmlTv/Emby.XmlTv.csproj +++ b/Emby.XmlTv/Emby.XmlTv/Emby.XmlTv.csproj @@ -3,6 +3,7 @@ netstandard2.0 false + true diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index f023bc55d..396bdd4b7 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -3,6 +3,7 @@ netstandard2.0 false + true diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 23c7339d2..6d3037b24 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -550,14 +550,14 @@ namespace MediaBrowser.Api.Images } IImageEnhancer[] supportedImageEnhancers; - if (_imageProcessor.ImageEnhancers.Length > 0) + if (_imageProcessor.ImageEnhancers.Count > 0) { if (item == null) { item = _libraryManager.GetItemById(itemId); } - supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.GetSupportedEnhancers(item, request.Type) : Array.Empty(); + supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.GetSupportedEnhancers(item, request.Type).ToArray() : Array.Empty(); } else { @@ -606,8 +606,8 @@ namespace MediaBrowser.Api.Images ImageRequest request, ItemImageInfo image, bool cropwhitespace, - ImageFormat[] supportedFormats, - IImageEnhancer[] enhancers, + IReadOnlyCollection supportedFormats, + IReadOnlyCollection enhancers, TimeSpan? cacheDuration, IDictionary headers, bool isHeadRequest) diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index ba29c656b..f653270a6 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -12,6 +12,7 @@ netstandard2.0 false + true diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index cb7343440..2248e9c85 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -75,10 +75,10 @@ namespace MediaBrowser.Common /// /// Gets the exports. /// - /// - /// if set to true [manage liftime]. - /// IEnumerable{``0}. - IEnumerable GetExports(bool manageLifetime = true); + /// The type. + /// If set to true [manage lifetime]. + /// . + IReadOnlyCollection GetExports(bool manageLifetime = true); /// /// Resolves this instance. diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 05b48a2a1..1b753aa29 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -23,6 +23,7 @@ netstandard2.0 false + true diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs index 4eaecd0a0..a0f9ae46e 100644 --- a/MediaBrowser.Controller/Drawing/IImageEncoder.cs +++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs @@ -17,16 +17,6 @@ namespace MediaBrowser.Controller.Drawing /// The supported output formats. IReadOnlyCollection SupportedOutputFormats { get; } - /// - /// Encodes the image. - /// - string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat); - - /// - /// Creates the image collage. - /// - /// The options. - void CreateImageCollage(ImageCollageOptions options); /// /// Gets the name. /// @@ -46,5 +36,16 @@ namespace MediaBrowser.Controller.Drawing bool SupportsImageEncoding { get; } ImageDimensions GetImageSize(string path); + + /// + /// Encodes the image. + /// + string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat); + + /// + /// Creates the image collage. + /// + /// The options. + void CreateImageCollage(ImageCollageOptions options); } } diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index a11e2186f..a58a11bd1 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -24,7 +24,15 @@ namespace MediaBrowser.Controller.Drawing /// Gets the image enhancers. /// /// The image enhancers. - IImageEnhancer[] ImageEnhancers { get; } + IReadOnlyCollection ImageEnhancers { get; set; } + + /// + /// Gets a value indicating whether [supports image collage creation]. + /// + /// true if [supports image collage creation]; otherwise, false. + bool SupportsImageCollageCreation { get; } + + IImageEncoder ImageEncoder { get; set; } /// /// Gets the dimensions of the image. @@ -50,19 +58,13 @@ namespace MediaBrowser.Controller.Drawing /// ImageDimensions ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem); - /// - /// Adds the parts. - /// - /// The enhancers. - void AddParts(IEnumerable enhancers); - /// /// Gets the supported enhancers. /// /// The item. /// Type of the image. /// IEnumerable{IImageEnhancer}. - IImageEnhancer[] GetSupportedEnhancers(BaseItem item, ImageType imageType); + IEnumerable GetSupportedEnhancers(BaseItem item, ImageType imageType); /// /// Gets the image cache tag. @@ -80,7 +82,7 @@ namespace MediaBrowser.Controller.Drawing /// The image. /// The image enhancers. /// Guid. - string GetImageCacheTag(BaseItem item, ItemImageInfo image, IImageEnhancer[] imageEnhancers); + string GetImageCacheTag(BaseItem item, ItemImageInfo image, IReadOnlyCollection imageEnhancers); /// /// Processes the image. @@ -109,7 +111,7 @@ namespace MediaBrowser.Controller.Drawing /// /// Gets the supported image output formats. /// - /// IReadOnlyCollection{ImageOutput}. + /// . IReadOnlyCollection GetSupportedImageOutputFormats(); /// @@ -118,14 +120,6 @@ namespace MediaBrowser.Controller.Drawing /// The options. void CreateImageCollage(ImageCollageOptions options); - /// - /// Gets a value indicating whether [supports image collage creation]. - /// - /// true if [supports image collage creation]; otherwise, false. - bool SupportsImageCollageCreation { get; } - - IImageEncoder ImageEncoder { get; set; } - bool SupportsTransparency(string path); } } diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs index db432f500..29addf6e6 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using MediaBrowser.Controller.Entities; @@ -33,9 +34,9 @@ namespace MediaBrowser.Controller.Drawing public int Quality { get; set; } - public IImageEnhancer[] Enhancers { get; set; } + public IReadOnlyCollection Enhancers { get; set; } - public ImageFormat[] SupportedOutputFormats { get; set; } + public IReadOnlyCollection SupportedOutputFormats { get; set; } public bool AddPlayedIndicator { get; set; } diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 01893f1b5..c6bca2518 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -19,6 +19,7 @@ netstandard2.0 false + true diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index 867b82ede..a8f8da9b8 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -12,6 +12,7 @@ netstandard2.0 false + true diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index c0f92ac4a..05af9fd03 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -3,6 +3,7 @@ netstandard2.0 false + true diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 3de2cca2d..e9f43ea56 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -10,6 +10,7 @@ netstandard2.0 false + true diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 5941ed436..ab4759c61 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -21,6 +21,7 @@ netstandard2.0 false + true diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index c099e77d6..883986894 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -18,6 +18,7 @@ netstandard2.0 false + true diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj index ba29c656b..f653270a6 100644 --- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj +++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj @@ -12,6 +12,7 @@ netstandard2.0 false + true -- cgit v1.2.3 From 4762e2fc6c87b462d375fa8d3063cd9c9d6a9d4a Mon Sep 17 00:00:00 2001 From: Anthony Lavado Date: Wed, 14 Aug 2019 01:51:46 -0400 Subject: Add a task to clean up transcode cache --- .../Tasks/DeleteTranscodingTempTask.cs | 157 +++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs new file mode 100644 index 000000000..6ec079989 --- /dev/null +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Tasks; +using Microsoft.Extensions.Logging; + +namespace Emby.Server.Implementations.ScheduledTasks.Tasks +{ + /// + /// Deletes all transcoding temp files + /// + public class DeleteTranscodingTempTask : IScheduledTask, IConfigurableScheduledTask + { + /// + /// Gets or sets the application paths. + /// + /// The application paths. + //private IApplicationPaths ApplicationPaths { get; set; } + protected ServerApplicationPaths ApplicationPaths { get; set; } + + + private readonly ILogger _logger; + + private readonly IFileSystem _fileSystem; + + /// + /// Initializes a new instance of the class. + /// + public DeleteTranscodingTempTask(ServerApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem) + { + ApplicationPaths = appPaths; + _logger = logger; + _fileSystem = fileSystem; + } + + /// + /// Creates the triggers that define when the task will run + /// + /// IEnumerable{BaseTaskTrigger}. + public IEnumerable GetDefaultTriggers() => new List(); + + /// + /// Returns the task to be executed + /// + /// The cancellation token. + /// The progress. + /// Task. + public Task Execute(CancellationToken cancellationToken, IProgress progress) + { + var minDateModified = DateTime.UtcNow.AddDays(-1); + progress.Report(50); + + try + { + DeleteTempFilesFromDirectory(cancellationToken, ApplicationPaths.TranscodingTempPath, minDateModified, progress); + } + catch (DirectoryNotFoundException) + { + // No biggie here. Nothing to delete + } + + return Task.CompletedTask; + } + + + /// + /// Deletes the transcoded temp files from directory with a last write time less than a given date + /// + /// The task cancellation token. + /// The directory. + /// The min date modified. + /// The progress. + private void DeleteTempFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress progress) + { + var filesToDelete = _fileSystem.GetFiles(directory, true) + .Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified) + .ToList(); + + var index = 0; + + foreach (var file in filesToDelete) + { + double percent = index; + percent /= filesToDelete.Count; + + progress.Report(100 * percent); + + cancellationToken.ThrowIfCancellationRequested(); + + DeleteFile(file.FullName); + + index++; + } + + DeleteEmptyFolders(directory); + + progress.Report(100); + } + + private void DeleteEmptyFolders(string parent) + { + foreach (var directory in _fileSystem.GetDirectoryPaths(parent)) + { + DeleteEmptyFolders(directory); + if (!_fileSystem.GetFileSystemEntryPaths(directory).Any()) + { + try + { + Directory.Delete(directory, false); + } + catch (UnauthorizedAccessException ex) + { + _logger.LogError(ex, "Error deleting directory {path}", directory); + } + catch (IOException ex) + { + _logger.LogError(ex, "Error deleting directory {path}", directory); + } + } + } + } + + private void DeleteFile(string path) + { + try + { + _fileSystem.DeleteFile(path); + } + catch (UnauthorizedAccessException ex) + { + _logger.LogError(ex, "Error deleting file {path}", path); + } + catch (IOException ex) + { + _logger.LogError(ex, "Error deleting file {path}", path); + } + } + + public string Name => "Transcoding temp cleanup"; + + public string Description => "Deletes transcoding temp files older than 24 hours."; + + public string Category => "Maintenance"; + + public string Key => "DeleteTranscodingTempFiles"; + + public bool IsHidden => false; + + public bool IsEnabled => false; + + public bool IsLogged => true; + } +} -- cgit v1.2.3 From 35da4ffa3efec3292bd9422154d7536ea89da89f Mon Sep 17 00:00:00 2001 From: Anthony Lavado Date: Wed, 14 Aug 2019 11:59:14 -0400 Subject: Remove unneeded comment --- .../ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs | 1 - 1 file changed, 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs index 6ec079989..ad9b56535 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs @@ -19,7 +19,6 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks /// Gets or sets the application paths. /// /// The application paths. - //private IApplicationPaths ApplicationPaths { get; set; } protected ServerApplicationPaths ApplicationPaths { get; set; } -- cgit v1.2.3 From e5b163b86aa9d8d8236a9aad832cc6009af762c4 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 14 Aug 2019 20:24:44 +0200 Subject: Fix possible nullref --- Emby.Server.Implementations/ApplicationHost.cs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 09847b2f8..38e61605a 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -121,6 +121,8 @@ namespace Emby.Server.Implementations /// public abstract class ApplicationHost : IServerApplicationHost, IDisposable { + private SqliteUserRepository _userRepository; + /// /// Gets a value indicating whether this instance can self restart. /// @@ -291,8 +293,6 @@ namespace Emby.Server.Implementations /// The user data repository. private IUserDataManager UserDataManager { get; set; } - private IUserRepository UserRepository { get; set; } - internal SqliteItemRepository ItemRepository { get; set; } private INotificationManager NotificationManager { get; set; } @@ -766,9 +766,9 @@ namespace Emby.Server.Implementations AuthenticationRepository = GetAuthenticationRepository(); serviceCollection.AddSingleton(AuthenticationRepository); - UserRepository = GetUserRepository(); + _userRepository = GetUserRepository(); - UserManager = new UserManager(LoggerFactory, ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager); + UserManager = new UserManager(LoggerFactory, ServerConfigurationManager, _userRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager); serviceCollection.AddSingleton(UserManager); LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager); @@ -961,8 +961,8 @@ namespace Emby.Server.Implementations /// /// Gets the user repository. /// - /// Task{IUserRepository}. - private IUserRepository GetUserRepository() + /// . + private SqliteUserRepository GetUserRepository() { var repo = new SqliteUserRepository(LoggerFactory, ApplicationPaths, JsonSerializer); @@ -1910,11 +1910,9 @@ namespace Emby.Server.Implementations } } - UserRepository.Dispose(); + _userRepository?.Dispose(); } - UserRepository = null; - _disposed = true; } } -- cgit v1.2.3 From 2fdf7f1098a1de41d7459b66620f82b79f27c4b8 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 14 Aug 2019 20:35:36 +0200 Subject: Properly dispose DisplayPreferencesRepository --- Emby.Server.Implementations/ApplicationHost.cs | 21 +++++++++++++++++---- .../Data/SqliteDisplayPreferencesRepository.cs | 4 ++-- .../Data/SqliteUserRepository.cs | 4 ++-- 3 files changed, 21 insertions(+), 8 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 38e61605a..966abfc41 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -123,6 +123,8 @@ namespace Emby.Server.Implementations { private SqliteUserRepository _userRepository; + private SqliteDisplayPreferencesRepository _displayPreferencesRepository; + /// /// Gets a value indicating whether this instance can self restart. /// @@ -757,8 +759,12 @@ namespace Emby.Server.Implementations UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, () => UserManager); serviceCollection.AddSingleton(UserDataManager); - var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LoggerFactory, JsonSerializer, ApplicationPaths, FileSystemManager); - serviceCollection.AddSingleton(displayPreferencesRepo); + _displayPreferencesRepository = new SqliteDisplayPreferencesRepository( + LoggerFactory.CreateLogger(), + JsonSerializer, + ApplicationPaths, + FileSystemManager); + serviceCollection.AddSingleton(_displayPreferencesRepository); ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, JsonSerializer, LoggerFactory, LocalizationManager); serviceCollection.AddSingleton(ItemRepository); @@ -884,7 +890,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager)); - displayPreferencesRepo.Initialize(); + _displayPreferencesRepository.Initialize(); var userDataRepo = new SqliteUserDataRepository(LoggerFactory, ApplicationPaths); @@ -964,7 +970,10 @@ namespace Emby.Server.Implementations /// . private SqliteUserRepository GetUserRepository() { - var repo = new SqliteUserRepository(LoggerFactory, ApplicationPaths, JsonSerializer); + var repo = new SqliteUserRepository( + LoggerFactory.CreateLogger(), + ApplicationPaths, + JsonSerializer); repo.Initialize(); @@ -1911,8 +1920,12 @@ namespace Emby.Server.Implementations } _userRepository?.Dispose(); + _displayPreferencesRepository.Dispose(); } + _userRepository = null; + _displayPreferencesRepository = null; + _disposed = true; } } diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs index 87096e72f..77f5d9479 100644 --- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs @@ -21,8 +21,8 @@ namespace Emby.Server.Implementations.Data { private readonly IFileSystem _fileSystem; - public SqliteDisplayPreferencesRepository(ILoggerFactory loggerFactory, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IFileSystem fileSystem) - : base(loggerFactory.CreateLogger(nameof(SqliteDisplayPreferencesRepository))) + public SqliteDisplayPreferencesRepository(ILogger logger, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IFileSystem fileSystem) + : base(logger) { _jsonSerializer = jsonSerializer; _fileSystem = fileSystem; diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs index de2354eef..d6d250fb3 100644 --- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs @@ -18,10 +18,10 @@ namespace Emby.Server.Implementations.Data private readonly IJsonSerializer _jsonSerializer; public SqliteUserRepository( - ILoggerFactory loggerFactory, + ILogger logger, IServerApplicationPaths appPaths, IJsonSerializer jsonSerializer) - : base(loggerFactory.CreateLogger(nameof(SqliteUserRepository))) + : base(logger) { _jsonSerializer = jsonSerializer; -- cgit v1.2.3 From d62a3f0e5735347ecca0bf7e00c4388b3783e54b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 15 Aug 2019 00:00:21 +0200 Subject: Fix master --- Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 4 ++-- Jellyfin.Server/Jellyfin.Server.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 010ad6384..a5bb47afb 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -124,7 +124,7 @@ namespace Emby.Server.Implementations.Data } WriteConnection.Execute("PRAGMA temp_store=" + (int)TempStore); - + // Configuration and pragmas can affect VACUUM so it needs to be last. WriteConnection.Execute("VACUUM"); @@ -218,7 +218,7 @@ namespace Emby.Server.Implementations.Data WriteLock.Wait(); try { - WriteConnection.Dispose(); + WriteConnection?.Dispose(); } finally { diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index e87283477..ec7c026e5 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -43,7 +43,7 @@ - + -- cgit v1.2.3 From c699c546e4124da8c1978b86d1a424d24d3d77d6 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 16 Aug 2019 17:31:47 +0200 Subject: Improve LocalizationManager --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- .../Localization/LocalizationManager.cs | 171 ++++++++++----------- MediaBrowser.Api/ItemUpdateService.cs | 4 +- MediaBrowser.Api/LocalizationService.cs | 5 +- MediaBrowser.Model/Globalization/CultureDto.cs | 1 + .../Globalization/ILocalizationManager.cs | 35 +++-- .../TV/TheTVDB/TvdbSeriesProvider.cs | 17 +- 7 files changed, 116 insertions(+), 119 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 68b02d4a2..0b3b81f94 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -746,7 +746,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(ServerConfigurationManager); - LocalizationManager = new LocalizationManager(ServerConfigurationManager, JsonSerializer, LoggerFactory); + LocalizationManager = new LocalizationManager(ServerConfigurationManager, JsonSerializer, LoggerFactory.CreateLogger()); await LocalizationManager.LoadAll().ConfigureAwait(false); serviceCollection.AddSingleton(LocalizationManager); diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 8c49b6405..13cdc50ca 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -17,43 +17,49 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Localization { /// - /// Class LocalizationManager + /// Class LocalizationManager. /// public class LocalizationManager : ILocalizationManager { - /// - /// The _configuration manager - /// - private readonly IServerConfigurationManager _configurationManager; + private const string DefaultCulture = "en-US"; + private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly; + private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; /// - /// The us culture + /// The _configuration manager. /// - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + private readonly IServerConfigurationManager _configurationManager; + private readonly IJsonSerializer _jsonSerializer; + private readonly ILogger _logger; private readonly Dictionary> _allParentalRatings = new Dictionary>(StringComparer.OrdinalIgnoreCase); - private readonly IJsonSerializer _jsonSerializer; - private readonly ILogger _logger; - private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly; + private readonly ConcurrentDictionary> _dictionaries = + new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); + + private List _cultures; /// /// Initializes a new instance of the class. /// /// The configuration manager. /// The json serializer. - /// The logger factory + /// The logger. public LocalizationManager( IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, - ILoggerFactory loggerFactory) + ILogger logger) { _configurationManager = configurationManager; _jsonSerializer = jsonSerializer; - _logger = loggerFactory.CreateLogger(nameof(LocalizationManager)); + _logger = logger; } + /// + /// Loads all resources into memory. + /// + /// . public async Task LoadAll() { const string RatingsResource = "Emby.Server.Implementations.Localization.Ratings."; @@ -82,9 +88,10 @@ namespace Emby.Server.Implementations.Localization string[] parts = line.Split(','); if (parts.Length == 2 - && int.TryParse(parts[1], NumberStyles.Integer, UsCulture, out var value)) + && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) { - dict.Add(parts[0], new ParentalRating { Name = parts[0], Value = value }); + var name = parts[0]; + dict.Add(name, new ParentalRating(name, value)); } #if DEBUG else @@ -101,16 +108,11 @@ namespace Emby.Server.Implementations.Localization await LoadCultures().ConfigureAwait(false); } - public string NormalizeFormKD(string text) - => text.Normalize(NormalizationForm.FormKD); - - private CultureDto[] _cultures; - /// /// Gets the cultures. /// - /// IEnumerable{CultureDto}. - public CultureDto[] GetCultures() + /// . + public IEnumerable GetCultures() => _cultures; private async Task LoadCultures() @@ -168,9 +170,10 @@ namespace Emby.Server.Implementations.Localization } } - _cultures = list.ToArray(); + _cultures = list; } + /// public CultureDto FindLanguageInfo(string language) => GetCultures() .FirstOrDefault(i => @@ -179,25 +182,19 @@ namespace Emby.Server.Implementations.Localization || i.ThreeLetterISOLanguageNames.Contains(language, StringComparer.OrdinalIgnoreCase) || string.Equals(i.TwoLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase)); - /// - /// Gets the countries. - /// - /// IEnumerable{CountryInfo}. - public Task GetCountries() - => _jsonSerializer.DeserializeFromStreamAsync( + /// + public IEnumerable GetCountries() + => _jsonSerializer.DeserializeFromStream>( _assembly.GetManifestResourceStream("Emby.Server.Implementations.Localization.countries.json")); - /// - /// Gets the parental ratings. - /// - /// IEnumerable{ParentalRating}. + /// public IEnumerable GetParentalRatings() => GetParentalRatingsDictionary().Values; /// /// Gets the parental ratings dictionary. /// - /// Dictionary{System.StringParentalRating}. + /// . private Dictionary GetParentalRatingsDictionary() { var countryCode = _configurationManager.Configuration.MetadataCountryCode; @@ -207,14 +204,14 @@ namespace Emby.Server.Implementations.Localization countryCode = "us"; } - return GetRatings(countryCode) ?? GetRatings("us"); + return GetRatings(countryCode) ?? GetRatings("us"); } /// /// Gets the ratings. /// /// The country code. - /// The ratings + /// The ratings. private Dictionary GetRatings(string countryCode) { _allParentalRatings.TryGetValue(countryCode, out var value); @@ -222,14 +219,7 @@ namespace Emby.Server.Implementations.Localization return value; } - private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; - /// - /// - /// Gets the rating level. - /// - /// Rating field - /// The rating level> public int? GetRatingLevel(string rating) { if (string.IsNullOrEmpty(rating)) @@ -277,6 +267,7 @@ namespace Emby.Server.Implementations.Localization return null; } + /// public bool HasUnicodeCategory(string value, UnicodeCategory category) { foreach (var chr in value) @@ -290,11 +281,13 @@ namespace Emby.Server.Implementations.Localization return false; } + /// public string GetLocalizedString(string phrase) { return GetLocalizedString(phrase, _configurationManager.Configuration.UICulture); } + /// public string GetLocalizedString(string phrase, string culture) { if (string.IsNullOrEmpty(culture)) @@ -317,12 +310,7 @@ namespace Emby.Server.Implementations.Localization return phrase; } - private const string DefaultCulture = "en-US"; - - private readonly ConcurrentDictionary> _dictionaries = - new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); - - public Dictionary GetLocalizationDictionary(string culture) + private Dictionary GetLocalizationDictionary(string culture) { if (string.IsNullOrEmpty(culture)) { @@ -332,8 +320,9 @@ namespace Emby.Server.Implementations.Localization const string prefix = "Core"; var key = prefix + culture; - return _dictionaries.GetOrAdd(key, - f => GetDictionary(prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult()); + return _dictionaries.GetOrAdd( + key, + f => GetDictionary(prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult()); } private async Task> GetDictionary(string prefix, string culture, string baseFilename) @@ -390,45 +379,45 @@ namespace Emby.Server.Implementations.Localization return culture + ".json"; } - public LocalizationOption[] GetLocalizationOptions() - => new LocalizationOption[] - { - new LocalizationOption("Arabic", "ar"), - new LocalizationOption("Bulgarian (Bulgaria)", "bg-BG"), - new LocalizationOption("Catalan", "ca"), - new LocalizationOption("Chinese Simplified", "zh-CN"), - new LocalizationOption("Chinese Traditional", "zh-TW"), - new LocalizationOption("Croatian", "hr"), - new LocalizationOption("Czech", "cs"), - new LocalizationOption("Danish", "da"), - new LocalizationOption("Dutch", "nl"), - new LocalizationOption("English (United Kingdom)", "en-GB"), - new LocalizationOption("English (United States)", "en-US"), - new LocalizationOption("French", "fr"), - new LocalizationOption("French (Canada)", "fr-CA"), - new LocalizationOption("German", "de"), - new LocalizationOption("Greek", "el"), - new LocalizationOption("Hebrew", "he"), - new LocalizationOption("Hungarian", "hu"), - new LocalizationOption("Italian", "it"), - new LocalizationOption("Kazakh", "kk"), - new LocalizationOption("Korean", "ko"), - new LocalizationOption("Lithuanian", "lt-LT"), - new LocalizationOption("Malay", "ms"), - new LocalizationOption("Norwegian Bokmål", "nb"), - new LocalizationOption("Persian", "fa"), - new LocalizationOption("Polish", "pl"), - new LocalizationOption("Portuguese (Brazil)", "pt-BR"), - new LocalizationOption("Portuguese (Portugal)", "pt-PT"), - new LocalizationOption("Russian", "ru"), - new LocalizationOption("Slovak", "sk"), - new LocalizationOption("Slovenian (Slovenia)", "sl-SI"), - new LocalizationOption("Spanish", "es"), - new LocalizationOption("Spanish (Argentina)", "es-AR"), - new LocalizationOption("Spanish (Mexico)", "es-MX"), - new LocalizationOption("Swedish", "sv"), - new LocalizationOption("Swiss German", "gsw"), - new LocalizationOption("Turkish", "tr") - }; + /// + public IEnumerable GetLocalizationOptions() + { + yield return new LocalizationOption("Arabic", "ar"); + yield return new LocalizationOption("Bulgarian (Bulgaria)", "bg-BG"); + yield return new LocalizationOption("Catalan", "ca"); + yield return new LocalizationOption("Chinese Simplified", "zh-CN"); + yield return new LocalizationOption("Chinese Traditional", "zh-TW"); + yield return new LocalizationOption("Croatian", "hr"); + yield return new LocalizationOption("Czech", "cs"); + yield return new LocalizationOption("Danish", "da"); + yield return new LocalizationOption("Dutch", "nl"); + yield return new LocalizationOption("English (United Kingdom)", "en-GB"); + yield return new LocalizationOption("English (United States)", "en-US"); + yield return new LocalizationOption("French", "fr"); + yield return new LocalizationOption("French (Canada)", "fr-CA"); + yield return new LocalizationOption("German", "de"); + yield return new LocalizationOption("Greek", "el"); + yield return new LocalizationOption("Hebrew", "he"); + yield return new LocalizationOption("Hungarian", "hu"); + yield return new LocalizationOption("Italian", "it"); + yield return new LocalizationOption("Kazakh", "kk"); + yield return new LocalizationOption("Korean", "ko"); + yield return new LocalizationOption("Lithuanian", "lt-LT"); + yield return new LocalizationOption("Malay", "ms"); + yield return new LocalizationOption("Norwegian Bokmål", "nb"); + yield return new LocalizationOption("Persian", "fa"); + yield return new LocalizationOption("Polish", "pl"); + yield return new LocalizationOption("Portuguese (Brazil)", "pt-BR"); + yield return new LocalizationOption("Portuguese (Portugal)", "pt-PT"); + yield return new LocalizationOption("Russian", "ru"); + yield return new LocalizationOption("Slovak", "sk"); + yield return new LocalizationOption("Slovenian (Slovenia)", "sl-SI"); + yield return new LocalizationOption("Spanish", "es"); + yield return new LocalizationOption("Spanish (Argentina)", "es-AR"); + yield return new LocalizationOption("Spanish (Mexico)", "es-MX"); + yield return new LocalizationOption("Swedish", "sv"); + yield return new LocalizationOption("Swiss German", "gsw"); + yield return new LocalizationOption("Turkish", "tr"); + } } } diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 825732888..d6514d62e 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -69,8 +69,8 @@ namespace MediaBrowser.Api { ParentalRatingOptions = _localizationManager.GetParentalRatings().ToArray(), ExternalIdInfos = _providerManager.GetExternalIdInfos(item).ToArray(), - Countries = await _localizationManager.GetCountries(), - Cultures = _localizationManager.GetCultures() + Countries = _localizationManager.GetCountries().ToArray(), + Cultures = _localizationManager.GetCultures().ToArray() }; if (!item.IsVirtualItem && !(item is ICollectionFolder) && !(item is UserView) && !(item is AggregateFolder) && !(item is LiveTvChannel) && !(item is IItemByName) && diff --git a/MediaBrowser.Api/LocalizationService.cs b/MediaBrowser.Api/LocalizationService.cs index eeff67e13..3b2e18852 100644 --- a/MediaBrowser.Api/LocalizationService.cs +++ b/MediaBrowser.Api/LocalizationService.cs @@ -1,4 +1,3 @@ -using System.Threading.Tasks; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; @@ -82,9 +81,9 @@ namespace MediaBrowser.Api /// /// The request. /// System.Object. - public async Task Get(GetCountries request) + public object Get(GetCountries request) { - var result = await _localization.GetCountries(); + var result = _localization.GetCountries(); return ToOptimizedResult(result); } diff --git a/MediaBrowser.Model/Globalization/CultureDto.cs b/MediaBrowser.Model/Globalization/CultureDto.cs index f229f2055..a213d4147 100644 --- a/MediaBrowser.Model/Globalization/CultureDto.cs +++ b/MediaBrowser.Model/Globalization/CultureDto.cs @@ -38,6 +38,7 @@ namespace MediaBrowser.Model.Globalization { return vals[0]; } + return null; } } diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs index a9ce60a2a..91d946db8 100644 --- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs +++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Globalization; -using System.Threading.Tasks; using MediaBrowser.Model.Entities; namespace MediaBrowser.Model.Globalization @@ -13,23 +12,26 @@ namespace MediaBrowser.Model.Globalization /// /// Gets the cultures. /// - /// IEnumerable{CultureDto}. - CultureDto[] GetCultures(); + /// . + IEnumerable GetCultures(); + /// /// Gets the countries. /// - /// IEnumerable{CountryInfo}. - Task GetCountries(); + /// . + IEnumerable GetCountries(); + /// /// Gets the parental ratings. /// - /// IEnumerable{ParentalRating}. + /// . IEnumerable GetParentalRatings(); + /// /// Gets the rating level. /// /// The rating. - /// System.Int32. + /// or null. int? GetRatingLevel(string rating); /// @@ -37,7 +39,7 @@ namespace MediaBrowser.Model.Globalization /// /// The phrase. /// The culture. - /// System.String. + /// . string GetLocalizedString(string phrase, string culture); /// @@ -50,13 +52,22 @@ namespace MediaBrowser.Model.Globalization /// /// Gets the localization options. /// - /// IEnumerable{LocalizatonOption}. - LocalizationOption[] GetLocalizationOptions(); - - string NormalizeFormKD(string text); + /// . + IEnumerable GetLocalizationOptions(); + /// + /// Checks if the string contains a character with the specified unicode category. + /// + /// The string. + /// The unicode category. + /// Wether or not the string contains a character with the specified unicode category. bool HasUnicodeCategory(string value, UnicodeCategory category); + /// + /// Returns the correct for the given language. + /// + /// The language. + /// The correct for the given language. CultureDto FindLanguageInfo(string language); } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index c739f3f49..1578e4341 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -285,7 +285,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private string GetComparableName(string name) { name = name.ToLowerInvariant(); - name = _localizationManager.NormalizeFormKD(name); + name = name.Normalize(NormalizationForm.FormKD); var sb = new StringBuilder(); foreach (var c in name) { @@ -310,19 +310,16 @@ namespace MediaBrowser.Providers.TV.TheTVDB sb.Append(c); } } - name = sb.ToString(); - name = name.Replace(", the", ""); - name = name.Replace("the ", " "); - name = name.Replace(" the ", " "); + sb.Replace(", the", string.Empty).Replace("the ", " ").Replace(" the ", " "); - string prevName; + int prevLength; do { - prevName = name; - name = name.Replace(" ", " "); - } while (name.Length != prevName.Length); + prevLength = sb.Length; + sb.Replace(" ", " "); + } while (name.Length != prevLength); - return name.Trim(); + return sb.ToString().Trim(); } private void MapSeriesToResult(MetadataResult result, TvDbSharper.Dto.Series tvdbSeries, string metadataLanguage) -- cgit v1.2.3 From dc662beefeb918b6ae967acb9849a2856cfc3672 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 16 Aug 2019 21:03:45 +0200 Subject: Add analysers to Emby.IsoMounting and enable TreatWarningsAsErrors --- .../Configuration/PluginConfiguration.cs | 3 + Emby.IsoMounting/IsoMounter/IsoMounter.csproj | 12 + Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs | 477 +++++++-------------- Emby.IsoMounting/IsoMounter/LinuxMount.cs | 73 ++-- Emby.IsoMounting/IsoMounter/Plugin.cs | 25 +- Emby.Server.Implementations/IO/IsoManager.cs | 26 +- .../SocketSharp/WebSocketSharpListener.cs | 2 +- MediaBrowser.Model/IO/IIsoManager.cs | 2 +- MediaBrowser.Model/IO/IIsoMounter.cs | 2 +- .../Plugins/BasePluginConfiguration.cs | 2 +- 10 files changed, 215 insertions(+), 409 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.IsoMounting/IsoMounter/Configuration/PluginConfiguration.cs b/Emby.IsoMounting/IsoMounter/Configuration/PluginConfiguration.cs index 4755e4e82..ca6f40cc4 100644 --- a/Emby.IsoMounting/IsoMounter/Configuration/PluginConfiguration.cs +++ b/Emby.IsoMounting/IsoMounter/Configuration/PluginConfiguration.cs @@ -2,6 +2,9 @@ using MediaBrowser.Model.Plugins; namespace IsoMounter.Configuration { + /// + /// Class PluginConfiguration. + /// public class PluginConfiguration : BasePluginConfiguration { } diff --git a/Emby.IsoMounting/IsoMounter/IsoMounter.csproj b/Emby.IsoMounting/IsoMounter/IsoMounter.csproj index 0778b987b..4fa07fbf1 100644 --- a/Emby.IsoMounting/IsoMounter/IsoMounter.csproj +++ b/Emby.IsoMounting/IsoMounter/IsoMounter.csproj @@ -13,6 +13,18 @@ netstandard2.0 false true + true + + + + + + + + + + + ../../jellyfin.ruleset diff --git a/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs b/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs index 2f0003be8..48cb2e1d5 100644 --- a/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs +++ b/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs @@ -1,9 +1,10 @@ using System; +using System.Diagnostics; +using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.IO; using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; @@ -11,441 +12,274 @@ using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace IsoMounter { + /// + /// The ISO manager implementation for Linux. + /// public class LinuxIsoManager : IIsoMounter { - [DllImport("libc", SetLastError = true)] - static extern uint getuid(); + private const string MountCommand = "mount"; + private const string UnmountCommand = "umount"; + private const string SudoCommand = "sudo"; - #region Private Fields - - private readonly bool ExecutablesAvailable; private readonly ILogger _logger; - private readonly string MountCommand; - private readonly string MountPointRoot; - private readonly IProcessFactory ProcessFactory; - private readonly string SudoCommand; - private readonly string UmountCommand; - - #endregion + private readonly string _mountPointRoot; - #region Constructor(s) - - public LinuxIsoManager(ILogger logger, IProcessFactory processFactory) + /// + /// Initializes a new instance of the class. + /// + /// The logger. + public LinuxIsoManager(ILogger logger) { _logger = logger; - ProcessFactory = processFactory; - MountPointRoot = Path.DirectorySeparatorChar + "tmp" + Path.DirectorySeparatorChar + "Emby"; + _mountPointRoot = Path.DirectorySeparatorChar + "tmp" + Path.DirectorySeparatorChar + "Emby"; _logger.LogDebug( "[{0}] System PATH is currently set to [{1}].", Name, - Environment.GetEnvironmentVariable("PATH") ?? "" - ); + Environment.GetEnvironmentVariable("PATH") ?? string.Empty); _logger.LogDebug( "[{0}] System path separator is [{1}].", Name, - Path.PathSeparator - ); + Path.PathSeparator); _logger.LogDebug( "[{0}] Mount point root is [{1}].", Name, - MountPointRoot - ); - - // - // Get the location of the executables we need to support mounting/unmounting ISO images. - // - - SudoCommand = GetFullPathForExecutable("sudo"); - - _logger.LogInformation( - "[{0}] Using version of [sudo] located at [{1}].", - Name, - SudoCommand - ); - - MountCommand = GetFullPathForExecutable("mount"); - - _logger.LogInformation( - "[{0}] Using version of [mount] located at [{1}].", - Name, - MountCommand - ); - - UmountCommand = GetFullPathForExecutable("umount"); - - _logger.LogInformation( - "[{0}] Using version of [umount] located at [{1}].", - Name, - UmountCommand - ); - - if (!string.IsNullOrEmpty(SudoCommand) && !string.IsNullOrEmpty(MountCommand) && !string.IsNullOrEmpty(UmountCommand)) - { - ExecutablesAvailable = true; - } - else - { - ExecutablesAvailable = false; - } - + _mountPointRoot); } - #endregion - - #region Interface Implementation for IIsoMounter - - public bool IsInstalled => true; - + /// public string Name => "LinuxMount"; - public bool RequiresInstallation => false; +#pragma warning disable SA1300 +#pragma warning disable SA1400 + [DllImport("libc", SetLastError = true)] + static extern uint getuid(); + +#pragma warning restore SA1300 +#pragma warning restore SA1400 + /// public bool CanMount(string path) { - if (OperatingSystem.Id != OperatingSystemId.Linux) { return false; } + _logger.LogInformation( - "[{0}] Checking we can attempt to mount [{1}], Extension = [{2}], Operating System = [{3}], Executables Available = [{4}].", + "[{0}] Checking we can attempt to mount [{1}], Extension = [{2}], Operating System = [{3}].", Name, path, Path.GetExtension(path), - OperatingSystem.Name, - ExecutablesAvailable - ); + OperatingSystem.Name); - if (ExecutablesAvailable) - { - return string.Equals(Path.GetExtension(path), ".iso", StringComparison.OrdinalIgnoreCase); - } - else - { - return false; - } - } - - public Task Install(CancellationToken cancellationToken) - { - return Task.FromResult(false); + return string.Equals(Path.GetExtension(path), ".iso", StringComparison.OrdinalIgnoreCase); } + /// public Task Mount(string isoPath, CancellationToken cancellationToken) { - if (MountISO(isoPath, out LinuxMount mountedISO)) - { - return Task.FromResult(mountedISO); - } - else + string cmdArguments; + string cmdFilename; + string mountPoint = Path.Combine(_mountPointRoot, Guid.NewGuid().ToString()); + + if (string.IsNullOrEmpty(isoPath)) { - throw new IOException(string.Format( - "An error occurred trying to mount image [$0].", - isoPath - )); + throw new ArgumentNullException(nameof(isoPath)); } - } - #endregion - - #region Interface Implementation for IDisposable - - // Flag: Has Dispose already been called? - private bool disposed = false; - - public void Dispose() - { - - // Dispose of unmanaged resources. - Dispose(true); - - // Suppress finalization. - GC.SuppressFinalize(this); + _logger.LogInformation( + "[{Name}] Attempting to mount [{Path}].", + Name, + isoPath); - } + _logger.LogDebug( + "[{Name}] ISO will be mounted at [{Path}].", + Name, + mountPoint); - protected virtual void Dispose(bool disposing) - { + try + { + Directory.CreateDirectory(mountPoint); + } + catch (UnauthorizedAccessException ex) + { + throw new IOException("Unable to create mount point(Permission denied) for " + isoPath, ex); + } + catch (Exception ex) + { + throw new IOException("Unable to create mount point for " + isoPath, ex); + } - if (disposed) + if (GetUID() == 0) + { + cmdFilename = MountCommand; + cmdArguments = string.Format( + CultureInfo.InvariantCulture, + "\"{0}\" \"{1}\"", + isoPath, + mountPoint); + } + else { - return; + cmdFilename = SudoCommand; + cmdArguments = string.Format( + CultureInfo.InvariantCulture, + "\"{0}\" \"{1}\" \"{2}\"", + MountCommand, + isoPath, + mountPoint); } - _logger.LogInformation( - "[{0}] Disposing [{1}].", + _logger.LogDebug( + "[{0}] Mount command [{1}], mount arguments [{2}].", Name, - disposing - ); + cmdFilename, + cmdArguments); - if (disposing) + int exitcode = ExecuteCommand(cmdFilename, cmdArguments); + if (exitcode == 0) { + _logger.LogInformation( + "[{0}] ISO mount completed successfully.", + Name); - // - // Free managed objects here. - // - + return Task.FromResult(new LinuxMount(this, isoPath, mountPoint)); } - // - // Free any unmanaged objects here. - // - - disposed = true; - - } - - #endregion - - #region Private Methods - - private string GetFullPathForExecutable(string name) - { + _logger.LogInformation( + "[{0}] ISO mount completed with errors.", + Name); - foreach (string test in (Environment.GetEnvironmentVariable("PATH") ?? "").Split(Path.PathSeparator)) + try { - string path = test.Trim(); - - if (!string.IsNullOrEmpty(path) && File.Exists(path = Path.Combine(path, name))) - { - return Path.GetFullPath(path); - } + Directory.Delete(mountPoint, false); + } + catch (Exception ex) + { + _logger.LogError(ex, "[{Name}] Unhandled exception removing mount point.", Name); + throw; } - return string.Empty; + throw new ExternalException("Mount command failed", exitcode); } private uint GetUID() { - var uid = getuid(); _logger.LogDebug( "[{0}] GetUserId() returned [{2}].", Name, - uid - ); + uid); return uid; - } - private bool ExecuteCommand(string cmdFilename, string cmdArguments) + private int ExecuteCommand(string cmdFilename, string cmdArguments) { - - bool processFailed = false; - - var process = ProcessFactory.Create( - new ProcessOptions - { - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - FileName = cmdFilename, - Arguments = cmdArguments, - IsHidden = true, - ErrorDialog = false, - EnableRaisingEvents = true - } - ); + var startInfo = new ProcessStartInfo + { + FileName = cmdFilename, + Arguments = cmdArguments, + UseShellExecute = false, + CreateNoWindow = true, + ErrorDialog = false, + RedirectStandardOutput = true, + RedirectStandardError = true + }; + + var process = new Process() + { + StartInfo = startInfo + }; try { process.Start(); - //StreamReader outputReader = process.StandardOutput.; - //StreamReader errorReader = process.StandardError; - _logger.LogDebug( "[{Name}] Standard output from process is [{Error}].", Name, - process.StandardOutput.ReadToEnd() - ); + process.StandardOutput.ReadToEnd()); _logger.LogDebug( "[{Name}] Standard error from process is [{Error}].", Name, - process.StandardError.ReadToEnd() - ); + process.StandardError.ReadToEnd()); + + return process.ExitCode; } catch (Exception ex) { - processFailed = true; _logger.LogDebug(ex, "[{Name}] Unhandled exception executing command.", Name); + throw; } - - if (!processFailed && process.ExitCode == 0) + finally { - return true; + process?.Dispose(); } - else - { - return false; - } - } - private bool MountISO(string isoPath, out LinuxMount mountedISO) + /// + /// Unmounts the specified mount. + /// + /// The mount. + internal void OnUnmount(LinuxMount mount) { - - string cmdArguments; - string cmdFilename; - string mountPoint = Path.Combine(MountPointRoot, Guid.NewGuid().ToString()); - - if (!string.IsNullOrEmpty(isoPath)) - { - - _logger.LogInformation( - "[{Name}] Attempting to mount [{Path}].", - Name, - isoPath - ); - - _logger.LogDebug( - "[{Name}] ISO will be mounted at [{Path}].", - Name, - mountPoint - ); - - } - else + if (mount == null) { - - throw new ArgumentNullException(nameof(isoPath)); - - } - - try - { - Directory.CreateDirectory(mountPoint); - } - catch (UnauthorizedAccessException) - { - throw new IOException("Unable to create mount point(Permission denied) for " + isoPath); - } - catch (Exception) - { - throw new IOException("Unable to create mount point for " + isoPath); - } - - if (GetUID() == 0) - { - cmdFilename = MountCommand; - cmdArguments = string.Format("\"{0}\" \"{1}\"", isoPath, mountPoint); - } - else - { - cmdFilename = SudoCommand; - cmdArguments = string.Format("\"{0}\" \"{1}\" \"{2}\"", MountCommand, isoPath, mountPoint); + throw new ArgumentNullException(nameof(mount)); } - _logger.LogDebug( - "[{0}] Mount command [{1}], mount arguments [{2}].", + _logger.LogInformation( + "[{0}] Attempting to unmount ISO [{1}] mounted on [{2}].", Name, - cmdFilename, - cmdArguments - ); - - if (ExecuteCommand(cmdFilename, cmdArguments)) - { - - _logger.LogInformation( - "[{0}] ISO mount completed successfully.", - Name - ); - - mountedISO = new LinuxMount(this, isoPath, mountPoint); - - } - else - { - - _logger.LogInformation( - "[{0}] ISO mount completed with errors.", - Name - ); - - try - { - Directory.Delete(mountPoint, false); - } - catch (Exception ex) - { - _logger.LogInformation(ex, "[{Name}] Unhandled exception removing mount point.", Name); - } - - mountedISO = null; - - } - - return mountedISO != null; - - } - - private void UnmountISO(LinuxMount mount) - { + mount.IsoPath, + mount.MountedPath); string cmdArguments; string cmdFilename; - if (mount != null) - { - - _logger.LogInformation( - "[{0}] Attempting to unmount ISO [{1}] mounted on [{2}].", - Name, - mount.IsoPath, - mount.MountedPath - ); - - } - else - { - - throw new ArgumentNullException(nameof(mount)); - - } - if (GetUID() == 0) { - cmdFilename = UmountCommand; - cmdArguments = string.Format("\"{0}\"", mount.MountedPath); + cmdFilename = UnmountCommand; + cmdArguments = string.Format( + CultureInfo.InvariantCulture, + "\"{0}\"", + mount.MountedPath); } else { cmdFilename = SudoCommand; - cmdArguments = string.Format("\"{0}\" \"{1}\"", UmountCommand, mount.MountedPath); + cmdArguments = string.Format( + CultureInfo.InvariantCulture, + "\"{0}\" \"{1}\"", + UnmountCommand, + mount.MountedPath); } _logger.LogDebug( "[{0}] Umount command [{1}], umount arguments [{2}].", Name, cmdFilename, - cmdArguments - ); + cmdArguments); - if (ExecuteCommand(cmdFilename, cmdArguments)) + int exitcode = ExecuteCommand(cmdFilename, cmdArguments); + if (exitcode == 0) { - _logger.LogInformation( "[{0}] ISO unmount completed successfully.", - Name - ); - + Name); } else { - _logger.LogInformation( "[{0}] ISO unmount completed with errors.", - Name - ); - + Name); } try @@ -454,24 +288,11 @@ namespace IsoMounter } catch (Exception ex) { - _logger.LogInformation(ex, "[{Name}] Unhandled exception removing mount point.", Name); + _logger.LogError(ex, "[{Name}] Unhandled exception removing mount point.", Name); + throw; } - } - - #endregion - - #region Internal Methods - - internal void OnUnmount(LinuxMount mount) - { - - UnmountISO(mount); + throw new ExternalException("Mount command failed", exitcode); } - - #endregion - } - } - diff --git a/Emby.IsoMounting/IsoMounter/LinuxMount.cs b/Emby.IsoMounting/IsoMounter/LinuxMount.cs index b8636822b..ccad8ce20 100644 --- a/Emby.IsoMounting/IsoMounter/LinuxMount.cs +++ b/Emby.IsoMounting/IsoMounter/LinuxMount.cs @@ -3,81 +3,56 @@ using MediaBrowser.Model.IO; namespace IsoMounter { + /// + /// Class LinuxMount. + /// internal class LinuxMount : IIsoMount { + private readonly LinuxIsoManager _linuxIsoManager; - #region Private Fields - - private readonly LinuxIsoManager linuxIsoManager; - - #endregion - - #region Constructor(s) + private bool _disposed = false; + /// + /// Initializes a new instance of the class. + /// + /// The ISO manager that mounted this ISO file. + /// The path to the ISO file. + /// The folder the ISO is mounted in. internal LinuxMount(LinuxIsoManager isoManager, string isoPath, string mountFolder) { - - linuxIsoManager = isoManager; + _linuxIsoManager = isoManager; IsoPath = isoPath; MountedPath = mountFolder; - } - #endregion - - #region Interface Implementation for IDisposable + /// + public string IsoPath { get; } - // Flag: Has Dispose already been called? - private bool disposed = false; + /// + public string MountedPath { get; } + /// public void Dispose() { - - // Dispose of unmanaged resources. Dispose(true); - - // Suppress finalization. GC.SuppressFinalize(this); - } + /// + /// Releases the unmanaged resources and disposes of the managed resources used. + /// + /// Whether or not the managed resources should be disposed. protected virtual void Dispose(bool disposing) { - - if (disposed) + if (_disposed) { return; } - if (disposing) - { - - // - // Free managed objects here. - // - - linuxIsoManager.OnUnmount(this); - - } - - // - // Free any unmanaged objects here. - // - - disposed = true; + _linuxIsoManager.OnUnmount(this); + _disposed = true; } - - #endregion - - #region Interface Implementation for IIsoMount - - public string IsoPath { get; private set; } - public string MountedPath { get; private set; } - - #endregion - } - } diff --git a/Emby.IsoMounting/IsoMounter/Plugin.cs b/Emby.IsoMounting/IsoMounter/Plugin.cs index f45b39d3e..433294d74 100644 --- a/Emby.IsoMounting/IsoMounter/Plugin.cs +++ b/Emby.IsoMounting/IsoMounter/Plugin.cs @@ -6,25 +6,28 @@ using MediaBrowser.Model.Serialization; namespace IsoMounter { + /// + /// The LinuxMount plugin class. + /// public class Plugin : BasePlugin { - public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) : base(applicationPaths, xmlSerializer) + /// + /// Initializes a new instance of the class. + /// + /// The application paths. + /// The XML serializer. + public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) + : base(applicationPaths, xmlSerializer) { } - private Guid _id = new Guid("4682DD4C-A675-4F1B-8E7C-79ADF137A8F8"); - public override Guid Id => _id; + /// + public override Guid Id { get; } = new Guid("4682DD4C-A675-4F1B-8E7C-79ADF137A8F8"); - /// - /// Gets the name of the plugin - /// - /// The name. + /// public override string Name => "Iso Mounter"; - /// - /// Gets the description. - /// - /// The description. + /// public override string Description => "Mount and stream ISO contents"; } } diff --git a/Emby.Server.Implementations/IO/IsoManager.cs b/Emby.Server.Implementations/IO/IsoManager.cs index f0a15097c..94e92c2a6 100644 --- a/Emby.Server.Implementations/IO/IsoManager.cs +++ b/Emby.Server.Implementations/IO/IsoManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -8,12 +9,12 @@ using MediaBrowser.Model.IO; namespace Emby.Server.Implementations.IO { /// - /// Class IsoManager + /// Class IsoManager. /// public class IsoManager : IIsoManager { /// - /// The _mounters + /// The _mounters. /// private readonly List _mounters = new List(); @@ -22,9 +23,7 @@ namespace Emby.Server.Implementations.IO /// /// The iso path. /// The cancellation token. - /// IsoMount. - /// isoPath - /// + /// . public Task Mount(string isoPath, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(isoPath)) @@ -36,7 +35,11 @@ namespace Emby.Server.Implementations.IO if (mounter == null) { - throw new ArgumentException(string.Format("No mounters are able to mount {0}", isoPath)); + throw new ArgumentException( + string.Format( + CultureInfo.InvariantCulture, + "No mounters are able to mount {0}", + isoPath)); } return mounter.Mount(isoPath, cancellationToken); @@ -60,16 +63,5 @@ namespace Emby.Server.Implementations.IO { _mounters.AddRange(mounters); } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - foreach (var mounter in _mounters) - { - mounter.Dispose(); - } - } } } diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs index dd313b336..e93bff124 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs @@ -117,7 +117,7 @@ namespace Emby.Server.Implementations.SocketSharp /// /// Releases the unmanaged resources and disposes of the managed resources used. /// - /// Whether or not the managed resources should be disposed + /// Whether or not the managed resources should be disposed. protected virtual void Dispose(bool disposing) { if (_disposed) diff --git a/MediaBrowser.Model/IO/IIsoManager.cs b/MediaBrowser.Model/IO/IIsoManager.cs index 24b6e5f05..eb0cb4bfb 100644 --- a/MediaBrowser.Model/IO/IIsoManager.cs +++ b/MediaBrowser.Model/IO/IIsoManager.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Model.IO { - public interface IIsoManager : IDisposable + public interface IIsoManager { /// /// Mounts the specified iso path. diff --git a/MediaBrowser.Model/IO/IIsoMounter.cs b/MediaBrowser.Model/IO/IIsoMounter.cs index f0153a928..766a9e4e6 100644 --- a/MediaBrowser.Model/IO/IIsoMounter.cs +++ b/MediaBrowser.Model/IO/IIsoMounter.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Model.IO { - public interface IIsoMounter : IDisposable + public interface IIsoMounter { /// /// Mounts the specified iso path. diff --git a/MediaBrowser.Model/Plugins/BasePluginConfiguration.cs b/MediaBrowser.Model/Plugins/BasePluginConfiguration.cs index 39db22133..ac540782c 100644 --- a/MediaBrowser.Model/Plugins/BasePluginConfiguration.cs +++ b/MediaBrowser.Model/Plugins/BasePluginConfiguration.cs @@ -1,7 +1,7 @@ namespace MediaBrowser.Model.Plugins { /// - /// Class BasePluginConfiguration + /// Class BasePluginConfiguration. /// public class BasePluginConfiguration { -- cgit v1.2.3 From 8d3b5c851ded74b9f34eb2cd963187761a3f6f61 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 9 Jun 2019 22:08:01 +0200 Subject: Improvements to UserManager --- Emby.Notifications/NotificationManager.cs | 2 +- Emby.Server.Implementations/ApplicationHost.cs | 3 +- .../Data/SqliteUserDataRepository.cs | 4 +- .../EntryPoints/RefreshUsersMetadata.cs | 4 +- .../Library/DefaultPasswordResetProvider.cs | 44 +-- Emby.Server.Implementations/Library/UserManager.cs | 358 ++++++++++----------- .../Session/SessionManager.cs | 8 +- MediaBrowser.Api/UserService.cs | 11 +- .../Authentication/AuthenticationException.cs | 1 + MediaBrowser.Controller/Entities/User.cs | 66 +--- MediaBrowser.Controller/Library/IUserManager.cs | 10 +- 11 files changed, 237 insertions(+), 274 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs index a767e541e..eecbbea07 100644 --- a/Emby.Notifications/NotificationManager.cs +++ b/Emby.Notifications/NotificationManager.cs @@ -89,7 +89,7 @@ namespace Emby.Notifications return _userManager.Users.Where(i => i.Policy.IsAdministrator) .Select(i => i.Id); case SendToUserType.All: - return _userManager.Users.Select(i => i.Id); + return _userManager.UsersIds; case SendToUserType.Custom: return request.UserIds; default: diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0b3b81f94..c390ba635 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -770,7 +770,8 @@ namespace Emby.Server.Implementations _userRepository = GetUserRepository(); - UserManager = new UserManager(LoggerFactory, ServerConfigurationManager, _userRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager); + UserManager = new UserManager(LoggerFactory.CreateLogger(), _userRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager); + serviceCollection.AddSingleton(UserManager); LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager); diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index 4035bb99d..9d4855bcf 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -44,7 +44,7 @@ namespace Emby.Server.Implementations.Data var userDatasTableExists = TableExists(connection, "UserDatas"); var userDataTableExists = TableExists(connection, "userdata"); - var users = userDatasTableExists ? null : userManager.Users.ToArray(); + var users = userDatasTableExists ? null : userManager.Users; connection.RunInTransaction(db => { @@ -84,7 +84,7 @@ namespace Emby.Server.Implementations.Data } } - private void ImportUserIds(IDatabaseConnection db, User[] users) + private void ImportUserIds(IDatabaseConnection db, IEnumerable users) { var userIdsWithUserData = GetAllUserIdsWithUserData(db); diff --git a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs index b7565adec..b2328121e 100644 --- a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs +++ b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs @@ -50,9 +50,7 @@ namespace Emby.Server.Implementations.EntryPoints public async Task Execute(CancellationToken cancellationToken, IProgress progress) { - var users = _userManager.Users.ToList(); - - foreach (var user in users) + foreach (var user in _userManager.Users) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs b/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs index c7044820c..fa6bbcf91 100644 --- a/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs +++ b/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs @@ -1,15 +1,12 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; -using System.Linq; -using System.Text; +using System.Security.Cryptography; using System.Threading.Tasks; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Users; @@ -17,32 +14,37 @@ namespace Emby.Server.Implementations.Library { public class DefaultPasswordResetProvider : IPasswordResetProvider { - public string Name => "Default Password Reset Provider"; + private const string BaseResetFileName = "passwordreset"; - public bool IsEnabled => true; + private readonly IJsonSerializer _jsonSerializer; + private readonly IUserManager _userManager; private readonly string _passwordResetFileBase; private readonly string _passwordResetFileBaseDir; - private readonly string _passwordResetFileBaseName = "passwordreset"; - private readonly IJsonSerializer _jsonSerializer; - private readonly IUserManager _userManager; - private readonly ICryptoProvider _crypto; - - public DefaultPasswordResetProvider(IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IUserManager userManager, ICryptoProvider cryptoProvider) + public DefaultPasswordResetProvider( + IServerConfigurationManager configurationManager, + IJsonSerializer jsonSerializer, + IUserManager userManager) { _passwordResetFileBaseDir = configurationManager.ApplicationPaths.ProgramDataPath; - _passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, _passwordResetFileBaseName); + _passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, BaseResetFileName); _jsonSerializer = jsonSerializer; _userManager = userManager; - _crypto = cryptoProvider; } + /// + public string Name => "Default Password Reset Provider"; + + /// + public bool IsEnabled => true; + + /// public async Task RedeemPasswordResetPin(string pin) { SerializablePasswordReset spr; - HashSet usersreset = new HashSet(); - foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{_passwordResetFileBaseName}*")) + List usersreset = new List(); + foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{BaseResetFileName}*")) { using (var str = File.OpenRead(resetfile)) { @@ -53,12 +55,15 @@ namespace Emby.Server.Implementations.Library { File.Delete(resetfile); } - else if (spr.Pin.Replace("-", "").Equals(pin.Replace("-", ""), StringComparison.InvariantCultureIgnoreCase)) + else if (string.Equals( + spr.Pin.Replace("-", string.Empty), + pin.Replace("-", string.Empty), + StringComparison.InvariantCultureIgnoreCase)) { var resetUser = _userManager.GetUserByName(spr.UserName); if (resetUser == null) { - throw new Exception($"User with a username of {spr.UserName} not found"); + throw new ResourceNotFoundException($"User with a username of {spr.UserName} not found"); } await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false); @@ -81,10 +86,11 @@ namespace Emby.Server.Implementations.Library } } + /// public async Task StartForgotPasswordProcess(MediaBrowser.Controller.Entities.User user, bool isInNetwork) { string pin = string.Empty; - using (var cryptoRandom = System.Security.Cryptography.RandomNumberGenerator.Create()) + using (var cryptoRandom = RandomNumberGenerator.Create()) { byte[] bytes = new byte[4]; cryptoRandom.GetBytes(bytes); diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index c8c8a108d..086527883 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -11,13 +12,11 @@ using MediaBrowser.Common.Events; using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Authentication; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Providers; @@ -40,35 +39,20 @@ namespace Emby.Server.Implementations.Library /// public class UserManager : IUserManager { - /// - /// Gets the users. - /// - /// The users. - public IEnumerable Users => _users; - - private User[] _users; - /// /// The _logger /// private readonly ILogger _logger; - /// - /// Gets or sets the configuration manager. - /// - /// The configuration manager. - private IServerConfigurationManager ConfigurationManager { get; set; } + private readonly object _policySyncLock = new object(); /// /// Gets the active user repository /// /// The user repository. - private IUserRepository UserRepository { get; set; } - public event EventHandler> UserPasswordChanged; - + private readonly IUserRepository _userRepository; private readonly IXmlSerializer _xmlSerializer; private readonly IJsonSerializer _jsonSerializer; - private readonly INetworkManager _networkManager; private readonly Func _imageProcessorFactory; @@ -76,6 +60,8 @@ namespace Emby.Server.Implementations.Library private readonly IServerApplicationHost _appHost; private readonly IFileSystem _fileSystem; + private ConcurrentDictionary _users; + private IAuthenticationProvider[] _authenticationProviders; private DefaultAuthenticationProvider _defaultAuthenticationProvider; @@ -85,8 +71,7 @@ namespace Emby.Server.Implementations.Library private DefaultPasswordResetProvider _defaultPasswordResetProvider; public UserManager( - ILoggerFactory loggerFactory, - IServerConfigurationManager configurationManager, + ILogger logger, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, @@ -96,8 +81,8 @@ namespace Emby.Server.Implementations.Library IJsonSerializer jsonSerializer, IFileSystem fileSystem) { - _logger = loggerFactory.CreateLogger(nameof(UserManager)); - UserRepository = userRepository; + _logger = logger; + _userRepository = userRepository; _xmlSerializer = xmlSerializer; _networkManager = networkManager; _imageProcessorFactory = imageProcessorFactory; @@ -105,8 +90,51 @@ namespace Emby.Server.Implementations.Library _appHost = appHost; _jsonSerializer = jsonSerializer; _fileSystem = fileSystem; - ConfigurationManager = configurationManager; - _users = Array.Empty(); + _users = null; + } + + public event EventHandler> UserPasswordChanged; + + /// + /// Occurs when [user updated]. + /// + public event EventHandler> UserUpdated; + + public event EventHandler> UserPolicyUpdated; + + public event EventHandler> UserConfigurationUpdated; + + public event EventHandler> UserLockedOut; + + public event EventHandler> UserCreated; + + /// + /// Occurs when [user deleted]. + /// + public event EventHandler> UserDeleted; + + /// + public IEnumerable Users => _users.Values; + + /// + public IEnumerable UsersIds => _users.Keys; + + /// + /// Called when [user updated]. + /// + /// The user. + private void OnUserUpdated(User user) + { + UserUpdated?.Invoke(this, new GenericEventArgs { Argument = user }); + } + + /// + /// Called when [user deleted]. + /// + /// The user. + private void OnUserDeleted(User user) + { + UserDeleted?.Invoke(this, new GenericEventArgs { Argument = user }); } public NameIdPair[] GetAuthenticationProviders() @@ -137,7 +165,7 @@ namespace Emby.Server.Implementations.Library .ToArray(); } - public void AddParts(IEnumerable authenticationProviders,IEnumerable passwordResetProviders) + public void AddParts(IEnumerable authenticationProviders, IEnumerable passwordResetProviders) { _authenticationProviders = authenticationProviders.ToArray(); @@ -150,54 +178,21 @@ namespace Emby.Server.Implementations.Library _defaultPasswordResetProvider = passwordResetProviders.OfType().First(); } - #region UserUpdated Event /// - /// Occurs when [user updated]. - /// - public event EventHandler> UserUpdated; - public event EventHandler> UserPolicyUpdated; - public event EventHandler> UserConfigurationUpdated; - public event EventHandler> UserLockedOut; - - /// - /// Called when [user updated]. - /// - /// The user. - private void OnUserUpdated(User user) - { - UserUpdated?.Invoke(this, new GenericEventArgs { Argument = user }); - } - #endregion - - #region UserDeleted Event - /// - /// Occurs when [user deleted]. - /// - public event EventHandler> UserDeleted; - /// - /// Called when [user deleted]. - /// - /// The user. - private void OnUserDeleted(User user) - { - UserDeleted?.Invoke(this, new GenericEventArgs { Argument = user }); - } - #endregion - - /// - /// Gets a User by Id + /// Gets a User by Id. /// /// The id. /// User. - /// + /// public User GetUserById(Guid id) { if (id == Guid.Empty) { - throw new ArgumentException(nameof(id), "Guid can't be empty"); + throw new ArgumentException("Guid can't be empty", nameof(id)); } - return Users.FirstOrDefault(u => u.Id == id); + _users.TryGetValue(id, out User user); + return user; } /// @@ -206,15 +201,13 @@ namespace Emby.Server.Implementations.Library /// The identifier. /// User. public User GetUserById(string id) - { - return GetUserById(new Guid(id)); - } + => GetUserById(new Guid(id)); public User GetUserByName(string name) { if (string.IsNullOrWhiteSpace(name)) { - throw new ArgumentNullException(nameof(name)); + throw new ArgumentException("Invalid username", nameof(name)); } return Users.FirstOrDefault(u => string.Equals(u.Name, name, StringComparison.OrdinalIgnoreCase)); @@ -222,8 +215,9 @@ namespace Emby.Server.Implementations.Library public void Initialize() { - var users = LoadUsers(); - _users = users.ToArray(); + LoadUsers(); + + var users = Users; // If there are no local users with admin rights, make them all admins if (!users.Any(i => i.Policy.IsAdministrator)) @@ -240,14 +234,12 @@ namespace Emby.Server.Implementations.Library { // This is some regex that matches only on unicode "word" characters, as well as -, _ and @ // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness - // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), and periods (.) + // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), and periods (.) return Regex.IsMatch(username, @"^[\w\-'._@]*$"); } private static bool IsValidUsernameCharacter(char i) - { - return IsValidUsername(i.ToString()); - } + => IsValidUsername(i.ToString(CultureInfo.InvariantCulture)); public string MakeValidUsername(string username) { @@ -277,8 +269,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(username)); } - var user = Users - .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase)); + var user = Users.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase)); var success = false; string updatedUsername = null; @@ -299,13 +290,12 @@ namespace Emby.Server.Implementations.Library updatedUsername = authResult.username; success = authResult.success; - if (success && authenticationProvider != null && !(authenticationProvider is DefaultAuthenticationProvider)) + if (success + && authenticationProvider != null + && !(authenticationProvider is DefaultAuthenticationProvider)) { // We should trust the user that the authprovider says, not what was typed - if (updatedUsername != username) - { - username = updatedUsername; - } + username = updatedUsername; // Search the database for the user again; the authprovider might have created it user = Users @@ -337,10 +327,11 @@ namespace Emby.Server.Implementations.Library if (user.Policy.IsDisabled) { - throw new AuthenticationException(string.Format( - CultureInfo.InvariantCulture, - "The {0} account is currently disabled. Please consult with your administrator.", - user.Name)); + throw new AuthenticationException( + string.Format( + CultureInfo.InvariantCulture, + "The {0} account is currently disabled. Please consult with your administrator.", + user.Name)); } if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint)) @@ -386,7 +377,7 @@ namespace Emby.Server.Implementations.Library private IAuthenticationProvider GetAuthenticationProvider(User user) { - return GetAuthenticationProviders(user).First(); + return GetAuthenticationProviders(user)[0]; } private IPasswordResetProvider GetPasswordResetProvider(User user) @@ -396,7 +387,7 @@ namespace Emby.Server.Implementations.Library private IAuthenticationProvider[] GetAuthenticationProviders(User user) { - var authenticationProviderId = user == null ? null : user.Policy.AuthenticationProviderId; + var authenticationProviderId = user?.Policy.AuthenticationProviderId; var providers = _authenticationProviders.Where(i => i.IsEnabled).ToArray(); @@ -438,16 +429,10 @@ namespace Emby.Server.Implementations.Library { try { - var requiresResolvedUser = provider as IRequiresResolvedUser; - ProviderAuthenticationResult authenticationResult = null; - if (requiresResolvedUser != null) - { - authenticationResult = await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false); - } - else - { - authenticationResult = await provider.Authenticate(username, password).ConfigureAwait(false); - } + + var authenticationResult = provider is IRequiresResolvedUser requiresResolvedUser + ? await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false) + : await provider.Authenticate(username, password).ConfigureAwait(false); if (authenticationResult.Username != username) { @@ -467,7 +452,6 @@ namespace Emby.Server.Implementations.Library private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)> AuthenticateLocalUser(string username, string password, string hashedPassword, User user, string remoteEndPoint) { - string updatedUsername = null; bool success = false; IAuthenticationProvider authenticationProvider = null; @@ -487,7 +471,7 @@ namespace Emby.Server.Implementations.Library foreach (var provider in GetAuthenticationProviders(user)) { var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false); - updatedUsername = providerAuthResult.username; + var updatedUsername = providerAuthResult.username; success = providerAuthResult.success; if (success) @@ -499,25 +483,32 @@ namespace Emby.Server.Implementations.Library } } - if (user != null) + if (user != null + && !success + && _networkManager.IsInLocalNetwork(remoteEndPoint) + && user.Configuration.EnableLocalPassword) { - if (!success && _networkManager.IsInLocalNetwork(remoteEndPoint) && user.Configuration.EnableLocalPassword) + if (password == null) { - if (password == null) - { - // legacy - success = string.Equals(GetAuthenticationProvider(user).GetEasyPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); - } - else - { - success = string.Equals(GetAuthenticationProvider(user).GetEasyPasswordHash(user), _defaultAuthenticationProvider.GetHashedString(user, password), StringComparison.OrdinalIgnoreCase); - } + // legacy + success = string.Equals(GetLocalPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); + } + else + { + success = string.Equals(GetLocalPasswordHash(user), _defaultAuthenticationProvider.GetHashedString(user, password), StringComparison.OrdinalIgnoreCase); } } return (authenticationProvider, username, success); } + private string GetLocalPasswordHash(User user) + { + return string.IsNullOrEmpty(user.EasyPassword) + ? null + : PasswordHash.ConvertToByteString(new PasswordHash(user.EasyPassword).Hash); + } + private void UpdateInvalidLoginAttemptCount(User user, int newValue) { if (user.Policy.InvalidLoginAttemptCount == newValue || newValue <= 0) @@ -556,17 +547,17 @@ namespace Emby.Server.Implementations.Library } /// - /// Loads the users from the repository + /// Loads the users from the repository. /// - /// IEnumerable{User}. - private List LoadUsers() + private void LoadUsers() { - var users = UserRepository.RetrieveAllUsers(); + var users = _userRepository.RetrieveAllUsers(); // There always has to be at least one user. if (users.Count != 0) { - return users; + _users = new ConcurrentDictionary( + users.Select(x => new KeyValuePair(x.Id, x))); } var defaultName = Environment.UserName; @@ -581,14 +572,15 @@ namespace Emby.Server.Implementations.Library user.DateLastSaved = DateTime.UtcNow; - UserRepository.CreateUser(user); + _userRepository.CreateUser(user); user.Policy.IsAdministrator = true; user.Policy.EnableContentDeletion = true; user.Policy.EnableRemoteControlOfOtherUsers = true; UpdateUserPolicy(user, user.Policy, false); - return new List { user }; + _users = new ConcurrentDictionary(); + _users[user.Id] = user; } public UserDto GetUserDto(User user, string remoteEndPoint = null) @@ -619,7 +611,7 @@ namespace Emby.Server.Implementations.Library Policy = user.Policy }; - if (!hasPassword && Users.Count() == 1) + if (!hasPassword && _users.Count == 1) { dto.EnableAutoLogin = true; } @@ -694,22 +686,26 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(user)); } - if (string.IsNullOrEmpty(newName)) + if (string.IsNullOrWhiteSpace(newName)) { - throw new ArgumentNullException(nameof(newName)); + throw new ArgumentException("Invalid username", nameof(newName)); } - if (Users.Any(u => u.Id != user.Id && u.Name.Equals(newName, StringComparison.OrdinalIgnoreCase))) + if (user.Name.Equals(newName, StringComparison.OrdinalIgnoreCase)) { - throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", newName)); + throw new ArgumentException("The new and old names must be different."); } - if (user.Name.Equals(newName, StringComparison.Ordinal)) + if (Users.Any( + u => u.Id != user.Id && u.Name.Equals(newName, StringComparison.OrdinalIgnoreCase))) { - throw new ArgumentException("The new and old names must be different."); + throw new ArgumentException(string.Format( + CultureInfo.InvariantCulture, + "A user with the name '{0}' already exists.", + newName)); } - await user.Rename(newName); + await user.Rename(newName).ConfigureAwait(false); OnUserUpdated(user); } @@ -727,23 +723,30 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(user)); } - if (user.Id.Equals(Guid.Empty) || !Users.Any(u => u.Id.Equals(user.Id))) + if (user.Id == Guid.Empty) { - throw new ArgumentException(string.Format("User with name '{0}' and Id {1} does not exist.", user.Name, user.Id)); + throw new ArgumentException("Id can't be empty.", nameof(user)); + } + + if (!_users.ContainsKey(user.Id)) + { + throw new ArgumentException( + string.Format( + CultureInfo.InvariantCulture, + "A user '{0}' with Id {1} does not exist.", + user.Name, + user.Id), + nameof(user)); } user.DateModified = DateTime.UtcNow; user.DateLastSaved = DateTime.UtcNow; - UserRepository.UpdateUser(user); + _userRepository.UpdateUser(user); OnUserUpdated(user); } - public event EventHandler> UserCreated; - - private readonly SemaphoreSlim _userListLock = new SemaphoreSlim(1, 1); - /// /// Creates the user. /// @@ -751,7 +754,7 @@ namespace Emby.Server.Implementations.Library /// User. /// name /// - public async Task CreateUser(string name) + public User CreateUser(string name) { if (string.IsNullOrWhiteSpace(name)) { @@ -768,28 +771,17 @@ namespace Emby.Server.Implementations.Library throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", name)); } - await _userListLock.WaitAsync(CancellationToken.None).ConfigureAwait(false); - - try - { - var user = InstantiateNewUser(name); + var user = InstantiateNewUser(name); - var list = Users.ToList(); - list.Add(user); - _users = list.ToArray(); + _users[user.Id] = user; - user.DateLastSaved = DateTime.UtcNow; + user.DateLastSaved = DateTime.UtcNow; - UserRepository.CreateUser(user); + _userRepository.CreateUser(user); - EventHelper.QueueEventIfNotNull(UserCreated, this, new GenericEventArgs { Argument = user }, _logger); + EventHelper.QueueEventIfNotNull(UserCreated, this, new GenericEventArgs { Argument = user }, _logger); - return user; - } - finally - { - _userListLock.Release(); - } + return user; } /// @@ -799,57 +791,59 @@ namespace Emby.Server.Implementations.Library /// Task. /// user /// - public async Task DeleteUser(User user) + public void DeleteUser(User user) { if (user == null) { throw new ArgumentNullException(nameof(user)); } - var allUsers = Users.ToList(); - - if (allUsers.FirstOrDefault(u => u.Id == user.Id) == null) + if (!_users.ContainsKey(user.Id)) { - throw new ArgumentException(string.Format("The user cannot be deleted because there is no user with the Name {0} and Id {1}.", user.Name, user.Id)); + throw new ArgumentException(string.Format( + CultureInfo.InvariantCulture, + "The user cannot be deleted because there is no user with the Name {0} and Id {1}.", + user.Name, + user.Id)); } - if (allUsers.Count == 1) + if (_users.Count == 1) { - throw new ArgumentException(string.Format("The user '{0}' cannot be deleted because there must be at least one user in the system.", user.Name)); + throw new ArgumentException(string.Format( + CultureInfo.InvariantCulture, + "The user '{0}' cannot be deleted because there must be at least one user in the system.", + user.Name)); } - if (user.Policy.IsAdministrator && allUsers.Count(i => i.Policy.IsAdministrator) == 1) + if (user.Policy.IsAdministrator + && Users.Count(i => i.Policy.IsAdministrator) == 1) { - throw new ArgumentException(string.Format("The user '{0}' cannot be deleted because there must be at least one admin user in the system.", user.Name)); + throw new ArgumentException( + string.Format( + CultureInfo.InvariantCulture, + "The user '{0}' cannot be deleted because there must be at least one admin user in the system.", + user.Name), + nameof(user)); } - await _userListLock.WaitAsync(CancellationToken.None).ConfigureAwait(false); + var configPath = GetConfigurationFilePath(user); + + _userRepository.DeleteUser(user); try { - var configPath = GetConfigurationFilePath(user); - - UserRepository.DeleteUser(user); - - try - { - _fileSystem.DeleteFile(configPath); - } - catch (IOException ex) - { - _logger.LogError(ex, "Error deleting file {path}", configPath); - } - - DeleteUserPolicy(user); - - _users = allUsers.Where(i => i.Id != user.Id).ToArray(); - - OnUserDeleted(user); + _fileSystem.DeleteFile(configPath); } - finally + catch (IOException ex) { - _userListLock.Release(); + _logger.LogError(ex, "Error deleting file {path}", configPath); } + + DeleteUserPolicy(user); + + _users.TryRemove(user.Id, out _); + + OnUserDeleted(user); } /// @@ -906,8 +900,7 @@ namespace Emby.Server.Implementations.Library Name = name, Id = Guid.NewGuid(), DateCreated = DateTime.UtcNow, - DateModified = DateTime.UtcNow, - UsesIdForConfigurationPath = true + DateModified = DateTime.UtcNow }; } @@ -989,7 +982,6 @@ namespace Emby.Server.Implementations.Library }; } - private readonly object _policySyncLock = new object(); public void UpdateUserPolicy(Guid userId, UserPolicy userPolicy) { var user = GetUserById(userId); diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 0347100a4..61329160a 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1375,16 +1375,14 @@ namespace Emby.Server.Implementations.Session CheckDisposed(); User user = null; - if (!request.UserId.Equals(Guid.Empty)) + if (request.UserId != Guid.Empty) { - user = _userManager.Users - .FirstOrDefault(i => i.Id == request.UserId); + user = _userManager.GetUserById(request.UserId); } if (user == null) { - user = _userManager.Users - .FirstOrDefault(i => string.Equals(request.Username, i.Name, StringComparison.OrdinalIgnoreCase)); + user = _userManager.GetUserByName(request.Username); } if (user != null) diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index fa70a52aa..21a94a4e0 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -365,8 +365,8 @@ namespace MediaBrowser.Api } _sessionMananger.RevokeUserTokens(user.Id, null); - - return _userManager.DeleteUser(user); + _userManager.DeleteUser(user); + return Task.CompletedTask; } /// @@ -503,9 +503,14 @@ namespace MediaBrowser.Api } } + /// + /// Posts the specified request. + /// + /// The request. + /// System.Object. public async Task Post(CreateUserByName request) { - var newUser = await _userManager.CreateUser(request.Name).ConfigureAwait(false); + var newUser = _userManager.CreateUser(request.Name); // no need to authenticate password for new user if (request.Password != null) diff --git a/MediaBrowser.Controller/Authentication/AuthenticationException.cs b/MediaBrowser.Controller/Authentication/AuthenticationException.cs index 045cbcdae..62eca3ea9 100644 --- a/MediaBrowser.Controller/Authentication/AuthenticationException.cs +++ b/MediaBrowser.Controller/Authentication/AuthenticationException.cs @@ -1,4 +1,5 @@ using System; + namespace MediaBrowser.Controller.Authentication { /// diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index 968d72579..7d245d4aa 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -17,13 +17,6 @@ namespace MediaBrowser.Controller.Entities public class User : BaseItem { public static IUserManager UserManager { get; set; } - public static IXmlSerializer XmlSerializer { get; set; } - - /// - /// From now on all user paths will be Id-based. - /// This is for backwards compatibility. - /// - public bool UsesIdForConfigurationPath { get; set; } /// /// Gets or sets the password. @@ -31,7 +24,6 @@ namespace MediaBrowser.Controller.Entities /// The password. public string Password { get; set; } public string EasyPassword { get; set; } - public string Salt { get; set; } // Strictly to remove IgnoreDataMember public override ItemImageInfo[] ImageInfos @@ -148,46 +140,23 @@ namespace MediaBrowser.Controller.Entities /// public Task Rename(string newName) { - if (string.IsNullOrEmpty(newName)) - { - throw new ArgumentNullException(nameof(newName)); - } - - // If only the casing is changing, leave the file system alone - if (!UsesIdForConfigurationPath && !string.Equals(newName, Name, StringComparison.OrdinalIgnoreCase)) + if (string.IsNullOrWhiteSpace(newName)) { - UsesIdForConfigurationPath = true; - - // Move configuration - var newConfigDirectory = GetConfigurationDirectoryPath(newName); - var oldConfigurationDirectory = ConfigurationDirectoryPath; - - // Exceptions will be thrown if these paths already exist - if (Directory.Exists(newConfigDirectory)) - { - Directory.Delete(newConfigDirectory, true); - } - - if (Directory.Exists(oldConfigurationDirectory)) - { - Directory.Move(oldConfigurationDirectory, newConfigDirectory); - } - else - { - Directory.CreateDirectory(newConfigDirectory); - } + throw new ArgumentException("Username can't be empty", nameof(newName)); } Name = newName; - return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)) - { - ReplaceAllMetadata = true, - ImageRefreshMode = MetadataRefreshMode.FullRefresh, - MetadataRefreshMode = MetadataRefreshMode.FullRefresh, - ForceSave = true + return RefreshMetadata( + new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)) + { + ReplaceAllMetadata = true, + ImageRefreshMode = MetadataRefreshMode.FullRefresh, + MetadataRefreshMode = MetadataRefreshMode.FullRefresh, + ForceSave = true - }, CancellationToken.None); + }, + CancellationToken.None); } public override void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken) @@ -216,19 +185,6 @@ namespace MediaBrowser.Controller.Entities { var parentPath = ConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath; - // Legacy - if (!UsesIdForConfigurationPath) - { - if (string.IsNullOrEmpty(username)) - { - throw new ArgumentNullException(nameof(username)); - } - - var safeFolderName = FileSystem.GetValidFilename(username); - - return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, safeFolderName); - } - // TODO: Remove idPath and just use usernamePath for future releases var usernamePath = System.IO.Path.Combine(parentPath, username); var idPath = System.IO.Path.Combine(parentPath, Id.ToString("N", CultureInfo.InvariantCulture)); diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 7f7370893..bbedc0442 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -22,6 +22,12 @@ namespace MediaBrowser.Controller.Library /// The users. IEnumerable Users { get; } + /// + /// Gets the user ids. + /// + /// The users ids. + IEnumerable UsersIds { get; } + /// /// Occurs when [user updated]. /// @@ -92,7 +98,7 @@ namespace MediaBrowser.Controller.Library /// User. /// name /// - Task CreateUser(string name); + User CreateUser(string name); /// /// Deletes the user. @@ -101,7 +107,7 @@ namespace MediaBrowser.Controller.Library /// Task. /// user /// - Task DeleteUser(User user); + void DeleteUser(User user); /// /// Resets the password. -- cgit v1.2.3 From 237db8ae92ab33885d6b0f9b024bbe38f3552c3b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 7 Jul 2019 16:39:35 +0200 Subject: Clean up livestreaming code --- Emby.Dlna/PlayTo/SsdpHttpClient.cs | 36 ++-- Emby.Server.Implementations/ApplicationHost.cs | 24 +-- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 184 +++++++++++---------- .../TunerHosts/HdHomerun/HdHomerunManager.cs | 115 +++++++------ .../TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 77 ++++----- .../LiveTv/TunerHosts/LiveStream.cs | 78 +++++---- .../LiveTv/TunerHosts/M3UTunerHost.cs | 19 ++- .../LiveTv/TunerHosts/SharedHttpStream.cs | 21 ++- MediaBrowser.Model/IO/StreamDefaults.cs | 2 +- 9 files changed, 295 insertions(+), 261 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs index 217ea3a4b..66c634150 100644 --- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs +++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs @@ -16,6 +16,8 @@ namespace Emby.Dlna.PlayTo private const string USERAGENT = "Microsoft-Windows/6.2 UPnP/1.0 Microsoft-DLNA DLNADOC/1.50"; private const string FriendlyName = "Jellyfin"; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private readonly IHttpClient _httpClient; private readonly IServerConfigurationManager _config; @@ -25,7 +27,8 @@ namespace Emby.Dlna.PlayTo _config = config; } - public async Task SendCommandAsync(string baseUrl, + public async Task SendCommandAsync( + string baseUrl, DeviceService service, string command, string postData, @@ -35,12 +38,20 @@ namespace Emby.Dlna.PlayTo var cancellationToken = CancellationToken.None; var url = NormalizeServiceUrl(baseUrl, service.ControlUrl); - using (var response = await PostSoapDataAsync(url, '\"' + service.ServiceType + '#' + command + '\"', postData, header, logRequest, cancellationToken) + using (var response = await PostSoapDataAsync( + url, + $"\"{service.ServiceType}#{command}\"", + postData, + header, + logRequest, + cancellationToken) .ConfigureAwait(false)) using (var stream = response.Content) using (var reader = new StreamReader(stream, Encoding.UTF8)) { - return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace); + return XDocument.Parse( + await reader.ReadToEndAsync().ConfigureAwait(false), + LoadOptions.PreserveWhitespace); } } @@ -58,9 +69,8 @@ namespace Emby.Dlna.PlayTo return baseUrl + serviceUrl; } - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - public async Task SubscribeAsync(string url, + public async Task SubscribeAsync( + string url, string ip, int port, string localIp, @@ -101,14 +111,12 @@ namespace Emby.Dlna.PlayTo options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName; using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false)) + using (var stream = response.Content) + using (var reader = new StreamReader(stream, Encoding.UTF8)) { - using (var stream = response.Content) - { - using (var reader = new StreamReader(stream, Encoding.UTF8)) - { - return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace); - } - } + return XDocument.Parse( + await reader.ReadToEndAsync().ConfigureAwait(false), + LoadOptions.PreserveWhitespace); } } @@ -122,7 +130,7 @@ namespace Emby.Dlna.PlayTo { if (soapAction[0] != '\"') { - soapAction = '\"' + soapAction + '\"'; + soapAction = $"\"{soapAction}\""; } var options = new HttpRequestOptions diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0b3b81f94..000255472 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -315,8 +315,6 @@ namespace Emby.Server.Implementations private IMediaSourceManager MediaSourceManager { get; set; } - private IPlaylistManager PlaylistManager { get; set; } - private readonly IConfiguration _configuration; /// @@ -325,14 +323,6 @@ namespace Emby.Server.Implementations /// The installation manager. protected IInstallationManager InstallationManager { get; private set; } - /// - /// Gets or sets the zip client. - /// - /// The zip client. - protected IZipClient ZipClient { get; private set; } - - protected IHttpResultFactory HttpResultFactory { get; private set; } - protected IAuthService AuthService { get; private set; } public IStartupOptions StartupOptions { get; } @@ -680,8 +670,6 @@ namespace Emby.Server.Implementations await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false); } - public static IStreamHelper StreamHelper { get; set; } - /// /// Registers resources that classes will depend on /// @@ -725,8 +713,7 @@ namespace Emby.Server.Implementations ProcessFactory = new ProcessFactory(); serviceCollection.AddSingleton(ProcessFactory); - ApplicationHost.StreamHelper = new StreamHelper(); - serviceCollection.AddSingleton(StreamHelper); + serviceCollection.AddSingleton(typeof(IStreamHelper), typeof(StreamHelper)); serviceCollection.AddSingleton(typeof(ICryptoProvider), typeof(CryptographyProvider)); @@ -735,11 +722,9 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(typeof(IInstallationManager), typeof(InstallationManager)); - ZipClient = new ZipClient(); - serviceCollection.AddSingleton(ZipClient); + serviceCollection.AddSingleton(typeof(IZipClient), typeof(ZipClient)); - HttpResultFactory = new HttpResultFactory(LoggerFactory, FileSystemManager, JsonSerializer, StreamHelper); - serviceCollection.AddSingleton(HttpResultFactory); + serviceCollection.AddSingleton(typeof(IHttpResultFactory), typeof(HttpResultFactory)); serviceCollection.AddSingleton(this); serviceCollection.AddSingleton(ApplicationPaths); @@ -836,8 +821,7 @@ namespace Emby.Server.Implementations CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, LoggerFactory, ProviderManager); serviceCollection.AddSingleton(CollectionManager); - PlaylistManager = new PlaylistManager(LibraryManager, FileSystemManager, LibraryMonitor, LoggerFactory, UserManager, ProviderManager); - serviceCollection.AddSingleton(PlaylistManager); + serviceCollection.AddSingleton(typeof(IPlaylistManager), typeof(PlaylistManager)); LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager); serviceCollection.AddSingleton(LiveTvManager); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index ed254accb..85754ca8b 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; @@ -31,6 +32,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private readonly IServerApplicationHost _appHost; private readonly ISocketFactory _socketFactory; private readonly INetworkManager _networkManager; + private readonly IStreamHelper _streamHelper; public HdHomerunHost( IServerConfigurationManager config, @@ -40,29 +42,25 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun IHttpClient httpClient, IServerApplicationHost appHost, ISocketFactory socketFactory, - INetworkManager networkManager) + INetworkManager networkManager, + IStreamHelper streamHelper) : base(config, logger, jsonSerializer, fileSystem) { _httpClient = httpClient; _appHost = appHost; _socketFactory = socketFactory; _networkManager = networkManager; + _streamHelper = streamHelper; } public string Name => "HD Homerun"; - public override string Type => DeviceType; - - public static string DeviceType => "hdhomerun"; + public override string Type => "hdhomerun"; protected override string ChannelIdPrefix => "hdhr_"; private string GetChannelId(TunerHostInfo info, Channels i) - { - var id = ChannelIdPrefix + i.GuideNumber; - - return id; - } + => ChannelIdPrefix + i.GuideNumber; private async Task> GetLineup(TunerHostInfo info, CancellationToken cancellationToken) { @@ -74,19 +72,18 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun CancellationToken = cancellationToken, BufferContent = false }; - using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false)) - { - using (var stream = response.Content) - { - var lineup = await JsonSerializer.DeserializeFromStreamAsync>(stream).ConfigureAwait(false) ?? new List(); - if (info.ImportFavoritesOnly) - { - lineup = lineup.Where(i => i.Favorite).ToList(); - } + using (var response = await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false)) + using (var stream = response.Content) + { + var lineup = await JsonSerializer.DeserializeFromStreamAsync>(stream).ConfigureAwait(false) ?? new List(); - return lineup.Where(i => !i.DRM).ToList(); + if (info.ImportFavoritesOnly) + { + lineup = lineup.Where(i => i.Favorite).ToList(); } + + return lineup.Where(i => !i.DRM).ToList(); } } @@ -139,23 +136,20 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun Url = string.Format("{0}/discover.json", GetApiUrl(info)), CancellationToken = cancellationToken, BufferContent = false - - }, "GET").ConfigureAwait(false)) + }, HttpMethod.Get).ConfigureAwait(false)) + using (var stream = response.Content) { - using (var stream = response.Content) - { - var discoverResponse = await JsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + var discoverResponse = await JsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); - if (!string.IsNullOrEmpty(cacheKey)) + if (!string.IsNullOrEmpty(cacheKey)) + { + lock (_modelCache) { - lock (_modelCache) - { - _modelCache[cacheKey] = discoverResponse; - } + _modelCache[cacheKey] = discoverResponse; } - - return discoverResponse; } + + return discoverResponse; } } catch (HttpException ex) @@ -186,36 +180,36 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); - using (var stream = await _httpClient.Get(new HttpRequestOptions() + using (var response = await _httpClient.SendAsync(new HttpRequestOptions() { Url = string.Format("{0}/tuners.html", GetApiUrl(info)), CancellationToken = cancellationToken, BufferContent = false - })) + }, HttpMethod.Get)) + using (var stream = response.Content) + using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8)) { var tuners = new List(); - using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8)) + while (!sr.EndOfStream) { - while (!sr.EndOfStream) + string line = StripXML(sr.ReadLine()); + if (line.Contains("Channel")) { - string line = StripXML(sr.ReadLine()); - if (line.Contains("Channel")) + LiveTvTunerStatus status; + var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase); + var name = line.Substring(0, index - 1); + var currentChannel = line.Substring(index + 7); + if (currentChannel != "none") { status = LiveTvTunerStatus.LiveTv; } else { status = LiveTvTunerStatus.Available; } + tuners.Add(new LiveTvTunerInfo { - LiveTvTunerStatus status; - var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase); - var name = line.Substring(0, index - 1); - var currentChannel = line.Substring(index + 7); - if (currentChannel != "none") { status = LiveTvTunerStatus.LiveTv; } else { status = LiveTvTunerStatus.Available; } - tuners.Add(new LiveTvTunerInfo - { - Name = name, - SourceType = string.IsNullOrWhiteSpace(model.ModelNumber) ? Name : model.ModelNumber, - ProgramName = currentChannel, - Status = status - }); - } + Name = name, + SourceType = string.IsNullOrWhiteSpace(model.ModelNumber) ? Name : model.ModelNumber, + ProgramName = currentChannel, + Status = status + }); } } + return tuners; } } @@ -245,6 +239,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun bufferIndex++; } } + return new string(buffer, 0, bufferIndex); } @@ -256,7 +251,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var uri = new Uri(GetApiUrl(info)); - using (var manager = new HdHomerunManager(Logger)) + using (var manager = new HdHomerunManager()) { // Legacy HdHomeruns are IPv4 only var ipInfo = IPAddress.Parse(uri.Host); @@ -276,6 +271,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun }); } } + return tuners; } @@ -434,12 +430,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { videoCodec = channelInfo.VideoCodec; } + string audioCodec = channelInfo.AudioCodec; if (!videoBitrate.HasValue) { videoBitrate = isHd ? 15000000 : 2000000; } + int? audioBitrate = isHd ? 448000 : 192000; // normalize @@ -461,6 +459,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { id = "native"; } + id += "_" + channelId.GetMD5().ToString("N", CultureInfo.InvariantCulture) + "_" + url.GetMD5().ToString("N", CultureInfo.InvariantCulture); var mediaSource = new MediaSourceInfo @@ -527,29 +526,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } else { - try - { - var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); + var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); - if (modelInfo != null && modelInfo.SupportsTranscoding) + if (modelInfo != null && modelInfo.SupportsTranscoding) + { + if (info.AllowHWTranscoding) { - if (info.AllowHWTranscoding) - { - list.Add(GetMediaSource(info, hdhrId, channelInfo, "heavy")); - - list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet540")); - list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet480")); - list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet360")); - list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet240")); - list.Add(GetMediaSource(info, hdhrId, channelInfo, "mobile")); - } + list.Add(GetMediaSource(info, hdhrId, channelInfo, "heavy")); - list.Add(GetMediaSource(info, hdhrId, channelInfo, "native")); + list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet540")); + list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet480")); + list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet360")); + list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet240")); + list.Add(GetMediaSource(info, hdhrId, channelInfo, "mobile")); } - } - catch - { + list.Add(GetMediaSource(info, hdhrId, channelInfo, "native")); } if (list.Count == 0) @@ -582,7 +574,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner) { - return new HdHomerunUdpStream(mediaSource, info, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Path), modelInfo.TunerCount, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); + return new HdHomerunUdpStream( + mediaSource, + info, + streamId, + new LegacyHdHomerunChannelCommands(hdhomerunChannel.Path), + modelInfo.TunerCount, + FileSystem, + Logger, + Config.ApplicationPaths, + _appHost, + _socketFactory, + _networkManager, + _streamHelper); } var enableHttpStream = true; @@ -599,10 +603,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } mediaSource.Path = httpUrl; - return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); - } - - return new HdHomerunUdpStream(mediaSource, info, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number, profile), modelInfo.TunerCount, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); + return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _streamHelper); + } + + return new HdHomerunUdpStream( + mediaSource, + info, + streamId, + new HdHomerunChannelCommands(hdhomerunChannel.Number, profile), + modelInfo.TunerCount, + FileSystem, + Logger, + Config.ApplicationPaths, + _appHost, + _socketFactory, + _networkManager, + _streamHelper); } public async Task Validate(TunerHostInfo info) @@ -701,9 +717,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun catch (OperationCanceledException) { } - catch + catch (Exception ex) { // Socket timeout indicates all messages have been received. + Logger.LogError(ex, "Error while sending discovery message"); } } @@ -718,21 +735,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun Url = url }; - try - { - var modelInfo = await GetModelInfo(hostInfo, false, cancellationToken).ConfigureAwait(false); - - hostInfo.DeviceId = modelInfo.DeviceID; - hostInfo.FriendlyName = modelInfo.FriendlyName; + var modelInfo = await GetModelInfo(hostInfo, false, cancellationToken).ConfigureAwait(false); - return hostInfo; - } - catch - { - // logged at lower levels - } + hostInfo.DeviceId = modelInfo.DeviceID; + hostInfo.FriendlyName = modelInfo.FriendlyName; - return null; + return hostInfo; } } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index c19552428..0238ee458 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -14,7 +14,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { public interface IHdHomerunChannelCommands { - IEnumerable> GetCommands(); + IEnumerable<(string, string)> GetCommands(); } public class LegacyHdHomerunChannelCommands : IHdHomerunChannelCommands @@ -33,16 +33,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } } - public IEnumerable> GetCommands() + public IEnumerable<(string, string)> GetCommands() { - var commands = new List>(); - if (!string.IsNullOrEmpty(_channel)) - commands.Add(Tuple.Create("channel", _channel)); + { + yield return ("channel", _channel); + } if (!string.IsNullOrEmpty(_program)) - commands.Add(Tuple.Create("program", _program)); - return commands; + { + yield return ("program", _program); + } } } @@ -57,23 +58,20 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun _profile = profile; } - public IEnumerable> GetCommands() + public IEnumerable<(string, string)> GetCommands() { - var commands = new List>(); - if (!string.IsNullOrEmpty(_channel)) { - if (!string.IsNullOrEmpty(_profile) && !string.Equals(_profile, "native", StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrEmpty(_profile) + && !string.Equals(_profile, "native", StringComparison.OrdinalIgnoreCase)) { - commands.Add(Tuple.Create("vchannel", string.Format("{0} transcode={1}", _channel, _profile))); + yield return ("vchannel", $"{_channel} transcode={_profile}"); } else { - commands.Add(Tuple.Create("vchannel", _channel)); + yield return ("vchannel", _channel); } } - - return commands; } } @@ -87,19 +85,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private const ushort GetSetRequest = 4; private const ushort GetSetReply = 5; - private readonly ILogger _logger; - private uint? _lockkey = null; private int _activeTuner = -1; private IPEndPoint _remoteEndPoint; private TcpClient _tcpClient; - public HdHomerunManager(ILogger logger) - { - _logger = logger; - } - public void Dispose() { using (var socket = _tcpClient) @@ -108,8 +99,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { _tcpClient = null; - var task = StopStreaming(socket); - Task.WaitAll(task); + StopStreaming(socket).GetAwaiter().GetResult(); } } } @@ -174,19 +164,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun await stream.WriteAsync(lockkeyMsg, 0, lockkeyMsg.Length, cancellationToken).ConfigureAwait(false); int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked - if (!ParseReturnMessage(buffer, receivedBytes, out var returnVal)) + if (!ParseReturnMessage(buffer, receivedBytes, out _)) { continue; } var commandList = commands.GetCommands(); - foreach (Tuple command in commandList) + foreach (var command in commandList) { var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue); await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false); receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked - if (!ParseReturnMessage(buffer, receivedBytes, out returnVal)) + if (!ParseReturnMessage(buffer, receivedBytes, out _)) { await ReleaseLockkey(_tcpClient, lockKeyValue).ConfigureAwait(false); continue; @@ -199,7 +189,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun await stream.WriteAsync(targetMsg, 0, targetMsg.Length, cancellationToken).ConfigureAwait(false); receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked - if (!ParseReturnMessage(buffer, receivedBytes, out returnVal)) + if (!ParseReturnMessage(buffer, receivedBytes, out _)) { await ReleaseLockkey(_tcpClient, lockKeyValue).ConfigureAwait(false); continue; @@ -231,13 +221,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun byte[] buffer = ArrayPool.Shared.Rent(8192); try { - foreach (Tuple command in commandList) + foreach (var command in commandList) { var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey); await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false); int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked - if (!ParseReturnMessage(buffer, receivedBytes, out string returnVal)) + if (!ParseReturnMessage(buffer, receivedBytes, out _)) { return; } @@ -264,21 +254,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private async Task ReleaseLockkey(TcpClient client, uint lockKeyValue) { - _logger.LogInformation("HdHomerunManager.ReleaseLockkey {0}", lockKeyValue); - var stream = client.GetStream(); var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue); - await stream.WriteAsync(releaseTarget, 0, releaseTarget.Length, CancellationToken.None).ConfigureAwait(false); + await stream.WriteAsync(releaseTarget, 0, releaseTarget.Length).ConfigureAwait(false); var buffer = ArrayPool.Shared.Rent(8192); try { - await stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None).ConfigureAwait(false); + await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", lockKeyValue); _lockkey = null; - await stream.WriteAsync(releaseKeyMsg, 0, releaseKeyMsg.Length, CancellationToken.None).ConfigureAwait(false); - await stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None).ConfigureAwait(false); + await stream.WriteAsync(releaseKeyMsg, 0, releaseKeyMsg.Length).ConfigureAwait(false); + await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); } finally { @@ -316,7 +304,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun int messageLength = byteName.Length + byteValue.Length + 12; if (lockkey.HasValue) + { messageLength += 6; + } var message = new byte[messageLength]; @@ -324,21 +314,20 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun bool flipEndian = BitConverter.IsLittleEndian; - message[offset] = GetSetValue; - offset++; - message[offset] = Convert.ToByte(byteValue.Length); - offset++; + message[offset++] = GetSetValue; + message[offset++] = Convert.ToByte(byteValue.Length); Buffer.BlockCopy(byteValue, 0, message, offset, byteValue.Length); offset += byteValue.Length; if (lockkey.HasValue) { - message[offset] = GetSetLockkey; - offset++; - message[offset] = (byte)4; - offset++; + message[offset++] = GetSetLockkey; + message[offset++] = (byte)4; var lockKeyBytes = BitConverter.GetBytes(lockkey.Value); if (flipEndian) + { Array.Reverse(lockKeyBytes); + } + Buffer.BlockCopy(lockKeyBytes, 0, message, offset, 4); offset += 4; } @@ -346,7 +335,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun // calculate crc and insert at the end of the message var crcBytes = BitConverter.GetBytes(HdHomerunCrc.GetCrc32(message, messageLength - 4)); if (flipEndian) + { Array.Reverse(crcBytes); + } + Buffer.BlockCopy(crcBytes, 0, message, offset, 4); return message; @@ -375,10 +367,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun offset += 2; // insert tag name and length - message[offset] = GetSetName; - offset++; - message[offset] = Convert.ToByte(byteName.Length); - offset++; + message[offset++] = GetSetName; + message[offset++] = Convert.ToByte(byteName.Length); // insert name string Buffer.BlockCopy(byteName, 0, message, offset, byteName.Length); @@ -392,7 +382,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun returnVal = string.Empty; if (numBytes < 4) + { return false; + } var flipEndian = BitConverter.IsLittleEndian; int offset = 0; @@ -400,45 +392,49 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun Buffer.BlockCopy(buf, offset, msgTypeBytes, 0, msgTypeBytes.Length); if (flipEndian) + { Array.Reverse(msgTypeBytes); + } var msgType = BitConverter.ToUInt16(msgTypeBytes, 0); offset += 2; if (msgType != GetSetReply) + { return false; + } byte[] msgLengthBytes = new byte[2]; Buffer.BlockCopy(buf, offset, msgLengthBytes, 0, msgLengthBytes.Length); if (flipEndian) + { Array.Reverse(msgLengthBytes); + } var msgLength = BitConverter.ToUInt16(msgLengthBytes, 0); offset += 2; if (numBytes < msgLength + 8) + { return false; + } - var nameTag = buf[offset]; - offset++; + var nameTag = buf[offset++]; - var nameLength = buf[offset]; - offset++; + var nameLength = buf[offset++]; // skip the name field to get to value for return offset += nameLength; - var valueTag = buf[offset]; - offset++; + var valueTag = buf[offset++]; - var valueLength = buf[offset]; - offset++; + var valueLength = buf[offset++]; returnVal = Encoding.UTF8.GetString(buf, offset, valueLength - 1); // remove null terminator return true; } - private class HdHomerunCrc + private static class HdHomerunCrc { private static uint[] crc_table = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, @@ -510,15 +506,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { var hash = 0xffffffff; for (var i = 0; i < numBytes; i++) + { hash = (hash >> 8) ^ crc_table[(hash ^ bytes[i]) & 0xff]; + } var tmp = ~hash & 0xffffffff; var b0 = tmp & 0xff; var b1 = (tmp >> 8) & 0xff; var b2 = (tmp >> 16) & 0xff; var b3 = (tmp >> 24) & 0xff; - hash = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; - return hash; + return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; } } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 1d79a5f96..cb085fff8 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Collections.Generic; using System.IO; using System.Net; @@ -18,6 +19,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { public class HdHomerunUdpStream : LiveStream, IDirectStreamProvider { + private const int RtpHeaderBytes = 12; + private readonly IServerApplicationHost _appHost; private readonly MediaBrowser.Model.Net.ISocketFactory _socketFactory; @@ -32,13 +35,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, - IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, MediaBrowser.Model.Net.ISocketFactory socketFactory, - INetworkManager networkManager) - : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths) + INetworkManager networkManager, + IStreamHelper streamHelper) + : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths, streamHelper) { _appHost = appHost; _socketFactory = socketFactory; @@ -80,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } var udpClient = _socketFactory.CreateUdpSocket(localPort); - var hdHomerunManager = new HdHomerunManager(Logger); + var hdHomerunManager = new HdHomerunManager(); try { @@ -103,7 +106,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var taskCompletionSource = new TaskCompletionSource(); - await StartStreaming(udpClient, hdHomerunManager, remoteAddress, taskCompletionSource, LiveStreamCancellationTokenSource.Token); + await StartStreaming( + udpClient, + hdHomerunManager, + remoteAddress, + taskCompletionSource, + LiveStreamCancellationTokenSource.Token).ConfigureAwait(false); //OpenedMediaSource.Protocol = MediaProtocol.File; //OpenedMediaSource.Path = tempFile; @@ -148,50 +156,43 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun }); } - private static void Resolve(TaskCompletionSource openTaskCompletionSource) - { - Task.Run(() => - { - openTaskCompletionSource.TrySetResult(true); - }); - } - - private const int RtpHeaderBytes = 12; - private async Task CopyTo(MediaBrowser.Model.Net.ISocket udpClient, string file, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) { - var bufferSize = 81920; - - byte[] buffer = new byte[bufferSize]; - int read; - var resolved = false; - - using (var source = _socketFactory.CreateNetworkStream(udpClient, false)) - using (var fileStream = FileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None)) + byte[] buffer = ArrayPool.Shared.Rent(StreamDefaults.DefaultCopyToBufferSize); + try { - var currentCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token).Token; - - while ((read = await source.ReadAsync(buffer, 0, buffer.Length, currentCancellationToken).ConfigureAwait(false)) != 0) + using (var source = _socketFactory.CreateNetworkStream(udpClient, false)) + using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read)) { - cancellationToken.ThrowIfCancellationRequested(); + var currentCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token).Token; + int read; + var resolved = false; + while ((read = await source.ReadAsync(buffer, 0, buffer.Length, currentCancellationToken).ConfigureAwait(false)) != 0) + { + cancellationToken.ThrowIfCancellationRequested(); - currentCancellationToken = cancellationToken; + currentCancellationToken = cancellationToken; - read -= RtpHeaderBytes; + read -= RtpHeaderBytes; - if (read > 0) - { - fileStream.Write(buffer, RtpHeaderBytes, read); - } + if (read > 0) + { + await fileStream.WriteAsync(buffer, RtpHeaderBytes, read).ConfigureAwait(false); + } - if (!resolved) - { - resolved = true; - DateOpened = DateTime.UtcNow; - Resolve(openTaskCompletionSource); + if (!resolved) + { + resolved = true; + DateOpened = DateTime.UtcNow; + openTaskCompletionSource.TrySetResult(true); + } } } } + finally + { + ArrayPool.Shared.Return(buffer); + } } } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index b4395e2e1..d12c96392 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -16,27 +16,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { public class LiveStream : ILiveStream { - public MediaSourceInfo OriginalMediaSource { get; set; } - public MediaSourceInfo MediaSource { get; set; } - - public int ConsumerCount { get; set; } - - public string OriginalStreamId { get; set; } - public bool EnableStreamSharing { get; set; } - public string UniqueId { get; } - protected readonly IFileSystem FileSystem; protected readonly IServerApplicationPaths AppPaths; + protected readonly IStreamHelper StreamHelper; protected string TempFilePath; protected readonly ILogger Logger; protected readonly CancellationTokenSource LiveStreamCancellationTokenSource = new CancellationTokenSource(); - public string TunerHostId { get; } - - public DateTime DateOpened { get; protected set; } - - public LiveStream(MediaSourceInfo mediaSource, TunerHostInfo tuner, IFileSystem fileSystem, ILogger logger, IServerApplicationPaths appPaths) + public LiveStream( + MediaSourceInfo mediaSource, + TunerHostInfo tuner, + IFileSystem fileSystem, + ILogger logger, + IServerApplicationPaths appPaths, + IStreamHelper streamHelper) { OriginalMediaSource = mediaSource; FileSystem = fileSystem; @@ -51,11 +45,27 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts } AppPaths = appPaths; + StreamHelper = streamHelper; ConsumerCount = 1; SetTempFilePath("ts"); } + protected virtual int EmptyReadLimit => 1000; + + public MediaSourceInfo OriginalMediaSource { get; set; } + public MediaSourceInfo MediaSource { get; set; } + + public int ConsumerCount { get; set; } + + public string OriginalStreamId { get; set; } + public bool EnableStreamSharing { get; set; } + public string UniqueId { get; } + + public string TunerHostId { get; } + + public DateTime DateOpened { get; protected set; } + protected void SetTempFilePath(string extension) { TempFilePath = Path.Combine(AppPaths.GetTranscodingTempPath(), UniqueId + "." + extension); @@ -71,24 +81,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { EnableStreamSharing = false; - Logger.LogInformation("Closing " + GetType().Name); + Logger.LogInformation("Closing {Type}", GetType().Name); LiveStreamCancellationTokenSource.Cancel(); return Task.CompletedTask; } - protected Stream GetInputStream(string path, bool allowAsyncFileRead) - { - var fileOpenOptions = FileOpenOptions.SequentialScan; - - if (allowAsyncFileRead) - { - fileOpenOptions |= FileOpenOptions.Asynchronous; - } - - return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions); - } + protected FileStream GetInputStream(string path, bool allowAsyncFileRead) + => new FileStream( + path, + FileMode.Open, + FileAccess.Read, + FileShare.ReadWrite, + StreamDefaults.DefaultFileStreamBufferSize, + allowAsyncFileRead ? FileOptions.SequentialScan | FileOptions.Asynchronous : FileOptions.SequentialScan); public Task DeleteTempFiles() { @@ -144,8 +151,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts bool seekFile = (DateTime.UtcNow - DateOpened).TotalSeconds > 10; var nextFileInfo = GetNextFile(null); - var nextFile = nextFileInfo.Item1; - var isLastFile = nextFileInfo.Item2; + var nextFile = nextFileInfo.file; + var isLastFile = nextFileInfo.isLastFile; while (!string.IsNullOrEmpty(nextFile)) { @@ -155,8 +162,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts seekFile = false; nextFileInfo = GetNextFile(nextFile); - nextFile = nextFileInfo.Item1; - isLastFile = nextFileInfo.Item2; + nextFile = nextFileInfo.file; + isLastFile = nextFileInfo.isLastFile; } Logger.LogInformation("Live Stream ended."); @@ -180,19 +187,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts private async Task CopyFile(string path, bool seekFile, int emptyReadLimit, bool allowAsync, Stream stream, CancellationToken cancellationToken) { - using (var inputStream = (FileStream)GetInputStream(path, allowAsync)) + using (var inputStream = GetInputStream(path, allowAsync)) { if (seekFile) { TrySeek(inputStream, -20000); } - await ApplicationHost.StreamHelper.CopyToAsync(inputStream, stream, 81920, emptyReadLimit, cancellationToken).ConfigureAwait(false); + await StreamHelper.CopyToAsync( + inputStream, + stream, + StreamDefaults.DefaultCopyToBufferSize, + emptyReadLimit, + cancellationToken).ConfigureAwait(false); } } - protected virtual int EmptyReadLimit => 1000; - private void TrySeek(FileStream stream, long offset) { if (!stream.CanSeek) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 6c5c80827..a02a9ade4 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -28,14 +28,25 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts private readonly IServerApplicationHost _appHost; private readonly INetworkManager _networkManager; private readonly IMediaSourceManager _mediaSourceManager; - - public M3UTunerHost(IServerConfigurationManager config, IMediaSourceManager mediaSourceManager, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, INetworkManager networkManager) + private readonly IStreamHelper _streamHelper; + + public M3UTunerHost( + IServerConfigurationManager config, + IMediaSourceManager mediaSourceManager, + ILogger logger, + IJsonSerializer jsonSerializer, + IFileSystem fileSystem, + IHttpClient httpClient, + IServerApplicationHost appHost, + INetworkManager networkManager, + IStreamHelper streamHelper) : base(config, logger, jsonSerializer, fileSystem) { _httpClient = httpClient; _appHost = appHost; _networkManager = networkManager; _mediaSourceManager = mediaSourceManager; + _streamHelper = streamHelper; } public override string Type => "m3u"; @@ -103,11 +114,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts if (!_disallowedSharedStreamExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { - return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); + return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _streamHelper); } } - return new LiveStream(mediaSource, info, FileSystem, Logger, Config.ApplicationPaths); + return new LiveStream(mediaSource, info, FileSystem, Logger, Config.ApplicationPaths, _streamHelper); } public async Task Validate(TunerHostInfo info) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index 7de9931c7..bee4fcd59 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -19,8 +19,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts private readonly IHttpClient _httpClient; private readonly IServerApplicationHost _appHost; - public SharedHttpStream(MediaSourceInfo mediaSource, TunerHostInfo tunerHostInfo, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost) - : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths) + public SharedHttpStream( + MediaSourceInfo mediaSource, + TunerHostInfo tunerHostInfo, + string originalStreamId, + IFileSystem fileSystem, + IHttpClient httpClient, + ILogger logger, + IServerApplicationPaths appPaths, + IServerApplicationHost appHost, + IStreamHelper streamHelper) + : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths, streamHelper) { _httpClient = httpClient; _appHost = appHost; @@ -118,7 +127,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts using (var stream = response.Content) using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None)) { - await ApplicationHost.StreamHelper.CopyToAsync(stream, fileStream, 81920, () => Resolve(openTaskCompletionSource), cancellationToken).ConfigureAwait(false); + await StreamHelper.CopyToAsync( + stream, + fileStream, + StreamDefaults.DefaultFileStreamBufferSize, + () => Resolve(openTaskCompletionSource), + cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) @@ -128,6 +142,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { Logger.LogError(ex, "Error copying live stream."); } + EnableStreamSharing = false; await DeleteTempFiles(new List { TempFilePath }).ConfigureAwait(false); }); diff --git a/MediaBrowser.Model/IO/StreamDefaults.cs b/MediaBrowser.Model/IO/StreamDefaults.cs index bef20e74f..1dc29e06e 100644 --- a/MediaBrowser.Model/IO/StreamDefaults.cs +++ b/MediaBrowser.Model/IO/StreamDefaults.cs @@ -13,6 +13,6 @@ namespace MediaBrowser.Model.IO /// /// The default file stream buffer size /// - public const int DefaultFileStreamBufferSize = 81920; + public const int DefaultFileStreamBufferSize = 4096; } } -- cgit v1.2.3 From 4b37caa63adf9d356eba6e6d240aeed5c8d36978 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 8 Jul 2019 13:28:47 +0200 Subject: Update SharedHttpStream.cs --- Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index bee4fcd59..c6e894560 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -130,7 +130,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts await StreamHelper.CopyToAsync( stream, fileStream, - StreamDefaults.DefaultFileStreamBufferSize, + StreamDefaults.DefaultCopyToBufferSize, () => Resolve(openTaskCompletionSource), cancellationToken).ConfigureAwait(false); } -- cgit v1.2.3 From 1bc9b42c576ae4d029f39d880d4c9a7827e0c96f Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 16 Aug 2019 21:18:37 +0200 Subject: More fixes --- .../LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs | 15 ++++++++++----- .../LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 8 +++++++- 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index 0238ee458..3699b988c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -1,6 +1,7 @@ using System; using System.Buffers; using System.Collections.Generic; +using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; @@ -8,7 +9,6 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.LiveTv; -using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { @@ -78,6 +78,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public class HdHomerunManager : IDisposable { public const int HdHomeRunPort = 65001; + // Message constants private const byte GetSetName = 3; private const byte GetSetValue = 4; @@ -163,6 +164,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var lockkeyMsg = CreateSetMessage(i, "lockkey", lockKeyString, null); await stream.WriteAsync(lockkeyMsg, 0, lockkeyMsg.Length, cancellationToken).ConfigureAwait(false); int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + // parse response to make sure it worked if (!ParseReturnMessage(buffer, receivedBytes, out _)) { @@ -175,6 +177,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue); await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false); receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + // parse response to make sure it worked if (!ParseReturnMessage(buffer, receivedBytes, out _)) { @@ -188,6 +191,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun await stream.WriteAsync(targetMsg, 0, targetMsg.Length, cancellationToken).ConfigureAwait(false); receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + // parse response to make sure it worked if (!ParseReturnMessage(buffer, receivedBytes, out _)) { @@ -226,6 +230,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey); await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false); int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + // parse response to make sure it worked if (!ParseReturnMessage(buffer, receivedBytes, out _)) { @@ -276,7 +281,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private static byte[] CreateGetMessage(int tuner, string name) { - var byteName = Encoding.UTF8.GetBytes(string.Format("/tuner{0}/{1}\0", tuner, name)); + var byteName = Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "/tuner{0}/{1}\0", tuner, name)); int messageLength = byteName.Length + 10; // 4 bytes for header + 4 bytes for crc + 2 bytes for tag name and length var message = new byte[messageLength]; @@ -299,8 +304,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private static byte[] CreateSetMessage(int tuner, string name, string value, uint? lockkey) { - var byteName = Encoding.UTF8.GetBytes(string.Format("/tuner{0}/{1}\0", tuner, name)); - var byteValue = Encoding.UTF8.GetBytes(string.Format("{0}\0", value)); + var byteName = Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "/tuner{0}/{1}\0", tuner, name)); + var byteValue = Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0}\0", value)); int messageLength = byteName.Length + byteValue.Length + 12; if (lockkey.HasValue) @@ -321,7 +326,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun if (lockkey.HasValue) { message[offset++] = GetSetLockkey; - message[offset++] = (byte)4; + message[offset++] = 4; var lockKeyBytes = BitConverter.GetBytes(lockkey.Value); if (flipEndian) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index cb085fff8..fbbab07f8 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -88,7 +88,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun try { // send url to start streaming - await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelCommands, _numTuners, openCancellationToken).ConfigureAwait(false); + await hdHomerunManager.StartStreaming( + remoteAddress, + localAddress, + localPort, + _channelCommands, + _numTuners, + openCancellationToken).ConfigureAwait(false); } catch (Exception ex) { -- cgit v1.2.3 From e7c05dcfaf5b06a98e89c6eee6e8ccb47c44fdf1 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Sun, 18 Aug 2019 17:22:45 +0200 Subject: Speed up BaseItem deserialization --- .../Data/SqliteItemRepository.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index bb4c34f02..a29ef769d 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -1298,18 +1298,13 @@ namespace Emby.Server.Implementations.Data if (TypeRequiresDeserialization(type)) { - using (var stream = new MemoryStream(reader[1].ToBlob())) + try { - stream.Position = 0; - - try - { - item = _jsonSerializer.DeserializeFromStream(stream, type) as BaseItem; - } - catch (SerializationException ex) - { - Logger.LogError(ex, "Error deserializing item"); - } + item = _jsonSerializer.DeserializeFromString(reader[1].ToString(), type) as BaseItem; + } + catch (SerializationException ex) + { + Logger.LogError(ex, "Error deserializing item"); } } -- cgit v1.2.3 From f48eaccc5105f0af8f320e0406b95ff868733a50 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Sun, 18 Aug 2019 17:32:41 +0200 Subject: Use reader.GetString instead of indexing --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index a29ef769d..3ca0728fe 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -1300,7 +1300,7 @@ namespace Emby.Server.Implementations.Data { try { - item = _jsonSerializer.DeserializeFromString(reader[1].ToString(), type) as BaseItem; + item = _jsonSerializer.DeserializeFromString(reader.GetString(1), type) as BaseItem; } catch (SerializationException ex) { -- cgit v1.2.3 From 24fac4b19172eae6a46208d712de09ac97e59d07 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 18 Aug 2019 20:12:25 +0200 Subject: Fix UserNotFoundError --- Emby.Server.Implementations/Library/UserManager.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 086527883..a7ea13ca6 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -558,6 +558,7 @@ namespace Emby.Server.Implementations.Library { _users = new ConcurrentDictionary( users.Select(x => new KeyValuePair(x.Id, x))); + return; } var defaultName = Environment.UserName; -- cgit v1.2.3 From 3fd489d1cb85d654b4b32d2ffd901832a38adbe9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 19 Aug 2019 17:03:21 +0200 Subject: Upgrade SQLitePCL to v2 --- .../Data/SqliteDisplayPreferencesRepository.cs | 8 +------- Emby.Server.Implementations/Data/SqliteExtensions.cs | 7 ++----- Emby.Server.Implementations/Data/SqliteUserRepository.cs | 15 +++++---------- .../Emby.Server.Implementations.csproj | 2 +- Jellyfin.Server/Jellyfin.Server.csproj | 2 +- 5 files changed, 10 insertions(+), 24 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs index 77f5d9479..b1c17b92e 100644 --- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs @@ -215,13 +215,7 @@ namespace Emby.Server.Implementations.Data } private DisplayPreferences Get(IReadOnlyList row) - { - using (var stream = new MemoryStream(row[0].ToBlob())) - { - stream.Position = 0; - return _jsonSerializer.DeserializeFromStream(stream); - } - } + => _jsonSerializer.DeserializeFromString(row.GetString(0)); public void SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client, CancellationToken cancellationToken) { diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs index c0f27b25a..0fb2c10fd 100644 --- a/Emby.Server.Implementations/Data/SqliteExtensions.cs +++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs @@ -18,10 +18,6 @@ namespace Emby.Server.Implementations.Data connection.RunInTransaction(conn => { - //foreach (var query in queries) - //{ - // conn.Execute(query); - //} conn.ExecuteAll(string.Join(";", queries)); }); } @@ -38,7 +34,8 @@ namespace Emby.Server.Implementations.Data public static Guid ReadGuidFromBlob(this IResultSetValue result) { - return new Guid(result.ToBlob()); + // TODO: Remove ToArray when upgrading to netstandard2.1 + return new Guid(result.ToBlob().ToArray()); } public static string ToDateTimeParamValue(this DateTime dateValue) diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs index d6d250fb3..11629b389 100644 --- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs @@ -35,9 +35,8 @@ namespace Emby.Server.Implementations.Data public string Name => "SQLite"; /// - /// Opens the connection to the database + /// Opens the connection to the database. /// - /// Task. public void Initialize() { using (var connection = GetConnection()) @@ -180,14 +179,10 @@ namespace Emby.Server.Implementations.Data var id = row[0].ToInt64(); var guid = row[1].ReadGuidFromBlob(); - using (var stream = new MemoryStream(row[2].ToBlob())) - { - stream.Position = 0; - var user = _jsonSerializer.DeserializeFromStream(stream); - user.InternalId = id; - user.Id = guid; - return user; - } + var user = _jsonSerializer.DeserializeFromString(row.GetString(2)); + user.InternalId = id; + user.Id = guid; + return user; } /// diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index c78d96d4a..b48193c58 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -34,7 +34,7 @@ - + diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index ec7c026e5..e87283477 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -43,7 +43,7 @@ - + -- cgit v1.2.3