From 369785c184a8e19250c6e3b16b3609c222399552 Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Fri, 8 Mar 2019 20:17:17 +0100
Subject: [PATCH 001/214] 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 | 438 ++++++------------
MediaBrowser.Common/Net/HttpRequestOptions.cs | 1 -
MediaBrowser.Common/Net/IHttpClient.cs | 11 +
4 files changed, 164 insertions(+), 304 deletions(-)
delete mode 100644 Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs
deleted file mode 100644
index f747b01b93..0000000000
--- 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 1bebdd1637..581d6bafd1 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(request, options);
+
+ if (options.EnableHttpCompression)
{
- AddRequestHeaders(httpWebRequest, options);
-
- 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;
- }
-
- httpWebRequest.KeepAlive = options.EnableKeepAlive;
-
- if (!string.IsNullOrEmpty(options.Host))
- {
- httpWebRequest.Host = options.Host;
- }
-
- if (!string.IsNullOrEmpty(options.Referer))
- {
- httpWebRequest.Referer = options.Referer;
+ request.Headers.Add(HeaderNames.AcceptEncoding, "deflate");
}
}
- request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
+ if (options.EnableKeepAlive)
+ {
+ request.Headers.Add(HeaderNames.Connection, "Keep-Alive");
+ }
- request.Method = method;
- request.Timeout = options.TimeoutMs;
+ 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))
{
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))
+ if (string.Equals(header.Key, HeaderNames.UserAgent, StringComparison.OrdinalIgnoreCase))
{
- request.Accept = header.Value;
- }
- else 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);
+ var client = GetHttpClient(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 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 response = await client.HttpClient.SendAsync(httpWebRequest).ConfigureAwait(false);
- var httpResponse = (HttpWebResponse)response;
-
- 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();
+ var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ _logger.LogError(msg);
- _logger.LogError(msg);
- }
- }
- }
- }
- catch
- {
-
- }
- }
-
- 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 bea178517b..874383fedf 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 5aaf7e0be6..d332ab2000 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.
///
From 7f42dcc60fd3aaf30f2408f6bddeb2b8bf921cdf Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Fri, 8 Mar 2019 20:32:14 +0100
Subject: [PATCH 002/214] Remove more unused stuff
---
.../ApplicationHost.cs | 11 +++++-----
.../HttpClientManager/HttpClientManager.cs | 18 ----------------
.../LiveTv/Listings/SchedulesDirect.cs | 10 +--------
.../TunerHosts/HdHomerun/HdHomerunHost.cs | 2 --
.../LiveTv/TunerHosts/SharedHttpStream.cs | 6 ------
MediaBrowser.Common/Net/HttpRequestOptions.cs | 21 +------------------
6 files changed, 7 insertions(+), 61 deletions(-)
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index f7d9bad1b0..2f396f8146 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 581d6bafd1..b82d55d0e6 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 4137760d07..f3f7477180 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 24b100eddc..761275f8f3 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 d74cf3be2d..e8b34da0ca 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 874383fedf..38e0ff0f55 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";
}
}
From 6566c9136057f7b272b0d35501ed85034034d11e Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Mon, 25 Mar 2019 21:27:03 +0100
Subject: [PATCH 003/214] Seperate changes from #1023
The unrelated changes from #1023 (and more)
---
.../Activity/ActivityLogEntryPoint.cs | 33 +++++---
.../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(-)
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index 98cd97c318..f32ad7196e 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 484942946c..ff49c74a3b 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 7e50650d70..e9961e8bd7 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 7b28a22a84..2f1b60be9c 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 c81a937672..29b8dfd3d7 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 f835aa1b5b..7d42b2b510 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
/// 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 373f6d7580..ec637186f0 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 e0a0ee2861..6fdc6a3c8d 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 82a76c6378..fbeb7a2e82 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 8eefbdf2ca..8a5a793dfd 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 8444125462..ee5c1a165a 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 e38f0e259f..0f472a2bc7 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 18712470da..76d816e7bd 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 d9a4b6ac01..5d2afc37a0 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
{
From 93e535d3a143d092effb37e529e5682c3d11802a Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Tue, 26 Mar 2019 22:56:05 +0100
Subject: [PATCH 005/214] Trying to make sense of the streaming code
Mostly small changes as I was looking through the code.
* async void -> async Task
* Properly implemented dispose methods
* Pass the logstream directly to the JobLogger
* Style fixes
---
MediaBrowser.Api/ApiEntryPoint.cs | 15 +-
.../Playback/BaseStreamingService.cs | 152 +++++++-----------
MediaBrowser.Api/Playback/StreamState.cs | 117 +++++---------
.../MediaEncoding/EncodingJobInfo.cs | 58 +++----
.../MediaEncoding/JobLogger.cs | 7 +-
.../Dlna/ContentFeatureBuilder.cs | 20 +--
6 files changed, 151 insertions(+), 218 deletions(-)
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 700cbb9439..a223a4fe38 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -415,7 +415,7 @@ namespace MediaBrowser.Api
public void OnTranscodeEndRequest(TranscodingJob job)
{
job.ActiveRequestCount--;
- //Logger.LogDebug("OnTranscodeEndRequest job.ActiveRequestCount={0}", job.ActiveRequestCount);
+ Logger.LogDebug("OnTranscodeEndRequest job.ActiveRequestCount={0}", job.ActiveRequestCount);
if (job.ActiveRequestCount <= 0)
{
PingTimer(job, false);
@@ -428,7 +428,7 @@ namespace MediaBrowser.Api
throw new ArgumentNullException(nameof(playSessionId));
}
- //Logger.LogDebug("PingTranscodingJob PlaySessionId={0} isUsedPaused: {1}", playSessionId, isUserPaused);
+ Logger.LogDebug("PingTranscodingJob PlaySessionId={0} isUsedPaused: {1}", playSessionId, isUserPaused);
List jobs;
@@ -443,7 +443,7 @@ namespace MediaBrowser.Api
{
if (isUserPaused.HasValue)
{
- //Logger.LogDebug("Setting job.IsUserPaused to {0}. jobId: {1}", isUserPaused, job.Id);
+ Logger.LogDebug("Setting job.IsUserPaused to {0}. jobId: {1}", isUserPaused, job.Id);
job.IsUserPaused = isUserPaused.Value;
}
PingTimer(job, true);
@@ -601,7 +601,6 @@ namespace MediaBrowser.Api
{
Logger.LogInformation("Stopping ffmpeg process with q command for {Path}", job.Path);
- //process.Kill();
process.StandardInput.WriteLine("q");
// Need to wait because killing is asynchronous
@@ -701,7 +700,7 @@ namespace MediaBrowser.Api
{
try
{
- //Logger.LogDebug("Deleting HLS file {0}", file);
+ Logger.LogDebug("Deleting HLS file {0}", file);
_fileSystem.DeleteFile(file);
}
catch (FileNotFoundException)
@@ -840,12 +839,12 @@ namespace MediaBrowser.Api
{
if (KillTimer == null)
{
- //Logger.LogDebug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
+ Logger.LogDebug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
KillTimer = new Timer(new TimerCallback(callback), this, intervalMs, Timeout.Infinite);
}
else
{
- //Logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
+ Logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
KillTimer.Change(intervalMs, Timeout.Infinite);
}
}
@@ -864,7 +863,7 @@ namespace MediaBrowser.Api
{
var intervalMs = PingTimeout;
- //Logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
+ Logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
KillTimer.Change(intervalMs, Timeout.Infinite);
}
}
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index ae259a4f59..b480d055af 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -8,7 +8,6 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
@@ -16,7 +15,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -32,6 +30,8 @@ namespace MediaBrowser.Api.Playback
///
public abstract class BaseStreamingService : BaseApiService
{
+ protected static readonly CultureInfo UsCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
+
///
/// Gets or sets the application paths.
///
@@ -65,15 +65,25 @@ namespace MediaBrowser.Api.Playback
protected IFileSystem FileSystem { get; private set; }
protected IDlnaManager DlnaManager { get; private set; }
+
protected IDeviceManager DeviceManager { get; private set; }
+
protected ISubtitleEncoder SubtitleEncoder { get; private set; }
+
protected IMediaSourceManager MediaSourceManager { get; private set; }
+
protected IJsonSerializer JsonSerializer { get; private set; }
protected IAuthorizationContext AuthorizationContext { get; private set; }
protected EncodingHelper EncodingHelper { get; set; }
+ ///
+ /// Gets the type of the transcoding job.
+ ///
+ /// The type of the transcoding job.
+ protected abstract TranscodingJobType TranscodingJobType { get; }
+
///
/// Initializes a new instance of the class.
///
@@ -112,12 +122,6 @@ namespace MediaBrowser.Api.Playback
///
protected abstract string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding);
- ///
- /// Gets the type of the transcoding job.
- ///
- /// The type of the transcoding job.
- protected abstract TranscodingJobType TranscodingJobType { get; }
-
///
/// Gets the output file extension.
///
@@ -133,31 +137,18 @@ namespace MediaBrowser.Api.Playback
///
private string GetOutputFilePath(StreamState state, EncodingOptions encodingOptions, string outputFileExtension)
{
- var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath;
-
var data = GetCommandLineArguments("dummy\\dummy", encodingOptions, state, false);
- data += "-" + (state.Request.DeviceId ?? string.Empty);
- data += "-" + (state.Request.PlaySessionId ?? string.Empty);
+ data += "-" + (state.Request.DeviceId ?? string.Empty)
+ + "-" + (state.Request.PlaySessionId ?? string.Empty);
- var dataHash = data.GetMD5().ToString("N");
+ var filename = data.GetMD5().ToString("N") + outputFileExtension.ToLowerInvariant();
+ var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath;
- if (EnableOutputInSubFolder)
- {
- return Path.Combine(folder, dataHash, dataHash + (outputFileExtension ?? string.Empty).ToLowerInvariant());
- }
-
- return Path.Combine(folder, dataHash + (outputFileExtension ?? string.Empty).ToLowerInvariant());
+ return Path.Combine(folder, filename);
}
- protected virtual bool EnableOutputInSubFolder => false;
-
- protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- protected virtual string GetDefaultH264Preset()
- {
- return "superfast";
- }
+ protected virtual string GetDefaultH264Preset() => "superfast";
private async Task AcquireResources(StreamState state, CancellationTokenSource cancellationTokenSource)
{
@@ -171,7 +162,6 @@ namespace MediaBrowser.Api.Playback
var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest
{
OpenToken = state.MediaSource.OpenToken
-
}, cancellationTokenSource.Token).ConfigureAwait(false);
EncodingHelper.AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, state.RequestedUrl);
@@ -209,22 +199,16 @@ namespace MediaBrowser.Api.Playback
if (state.VideoRequest != null && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
- if (auth.User != null)
+ if (auth.User != null && !auth.User.Policy.EnableVideoPlaybackTranscoding)
{
- if (!auth.User.Policy.EnableVideoPlaybackTranscoding)
- {
- ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state);
+ ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state);
- throw new ArgumentException("User does not have access to video transcoding");
- }
+ throw new ArgumentException("User does not have access to video transcoding");
}
}
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
- var transcodingId = Guid.NewGuid().ToString("N");
- var commandLineArgs = GetCommandLineArguments(outputPath, encodingOptions, state, true);
-
var process = new Process()
{
StartInfo = new ProcessStartInfo()
@@ -239,7 +223,7 @@ namespace MediaBrowser.Api.Playback
RedirectStandardInput = true,
FileName = MediaEncoder.EncoderPath,
- Arguments = commandLineArgs,
+ Arguments = GetCommandLineArguments(outputPath, encodingOptions, state, true),
WorkingDirectory = string.IsNullOrWhiteSpace(workingDirectory) ? null : workingDirectory,
ErrorDialog = false
@@ -250,7 +234,7 @@ namespace MediaBrowser.Api.Playback
var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
state.Request.PlaySessionId,
state.MediaSource.LiveStreamId,
- transcodingId,
+ Guid.NewGuid().ToString("N"),
TranscodingJobType,
process,
state.Request.DeviceId,
@@ -261,27 +245,26 @@ namespace MediaBrowser.Api.Playback
Logger.LogInformation(commandLineLogMessage);
var logFilePrefix = "ffmpeg-transcode";
- if (state.VideoRequest != null)
+ if (state.VideoRequest != null
+ && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)
- && string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
logFilePrefix = "ffmpeg-directstream";
}
- else if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ else
{
logFilePrefix = "ffmpeg-remux";
}
}
var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, logFilePrefix + "-" + Guid.NewGuid() + ".txt");
- Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
- state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);
+ Stream logStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(Request.AbsoluteUri + Environment.NewLine + Environment.NewLine + JsonSerializer.SerializeToString(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
- await state.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);
+ await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);
process.Exited += (sender, args) => OnFfMpegProcessExited(process, transcodingJob, state);
@@ -298,13 +281,10 @@ namespace MediaBrowser.Api.Playback
throw;
}
- // MUST read both stdout and stderr asynchronously or a deadlock may occurr
- //process.BeginOutputReadLine();
-
state.TranscodingJob = transcodingJob;
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
- new JobLogger(Logger).StartStreamingLog(state, process.StandardError.BaseStream, state.LogFileStream);
+ _ = new JobLogger(Logger).StartStreamingLog(state, process.StandardError.BaseStream, logStream);
// Wait for the file to exist before proceeeding
while (!File.Exists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited)
@@ -368,25 +348,16 @@ namespace MediaBrowser.Api.Playback
Logger.LogDebug("Disposing stream resources");
state.Dispose();
- try
+ if (process.ExitCode == 0)
{
- Logger.LogInformation("FFMpeg exited with code {0}", process.ExitCode);
+ Logger.LogInformation("FFMpeg exited with code 0");
}
- catch
+ else
{
- Logger.LogError("FFMpeg exited with an error.");
+ Logger.LogError("FFMpeg exited with code {0}", process.ExitCode);
}
- // This causes on exited to be called twice:
- //try
- //{
- // // Dispose the process
- // process.Dispose();
- //}
- //catch (Exception ex)
- //{
- // Logger.LogError(ex, "Error disposing ffmpeg.");
- //}
+ process.Dispose();
}
///
@@ -643,11 +614,19 @@ namespace MediaBrowser.Api.Playback
return null;
}
- if (value.IndexOf("npt=", StringComparison.OrdinalIgnoreCase) != 0)
+ if (!value.StartsWith("npt=", StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("Invalid timeseek header");
}
- value = value.Substring(4).Split(new[] { '-' }, 2)[0];
+ int index = value.IndexOf('-');
+ if (index == -1)
+ {
+ value = value.Substring(4);
+ }
+ else
+ {
+ value = value.Substring(4, index);
+ }
if (value.IndexOf(':') == -1)
{
@@ -728,13 +707,10 @@ namespace MediaBrowser.Api.Playback
// state.SegmentLength = 6;
//}
- if (state.VideoRequest != null)
+ if (state.VideoRequest != null && !string.IsNullOrWhiteSpace(state.VideoRequest.VideoCodec))
{
- if (!string.IsNullOrWhiteSpace(state.VideoRequest.VideoCodec))
- {
- state.SupportedVideoCodecs = state.VideoRequest.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
- state.VideoRequest.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault();
- }
+ state.SupportedVideoCodecs = state.VideoRequest.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
+ state.VideoRequest.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault();
}
if (!string.IsNullOrWhiteSpace(request.AudioCodec))
@@ -779,7 +755,7 @@ namespace MediaBrowser.Api.Playback
var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false)).ToList();
mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
- ? mediaSources.First()
+ ? mediaSources[0]
: mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId));
if (mediaSource == null && request.MediaSourceId.Equals(request.Id))
@@ -834,11 +810,11 @@ namespace MediaBrowser.Api.Playback
if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
var resolution = ResolutionNormalizer.Normalize(
- state.VideoStream == null ? (int?)null : state.VideoStream.BitRate,
- state.VideoStream == null ? (int?)null : state.VideoStream.Width,
- state.VideoStream == null ? (int?)null : state.VideoStream.Height,
+ state.VideoStream?.BitRate,
+ state.VideoStream?.Width,
+ state.VideoStream?.Height,
state.OutputVideoBitrate.Value,
- state.VideoStream == null ? null : state.VideoStream.Codec,
+ state.VideoStream?.Codec,
state.OutputVideoCodec,
videoRequest.MaxWidth,
videoRequest.MaxHeight);
@@ -846,17 +822,13 @@ namespace MediaBrowser.Api.Playback
videoRequest.MaxWidth = resolution.MaxWidth;
videoRequest.MaxHeight = resolution.MaxHeight;
}
+ }
- ApplyDeviceProfileSettings(state);
- }
- else
- {
- ApplyDeviceProfileSettings(state);
- }
+ ApplyDeviceProfileSettings(state);
var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
? GetOutputFileExtension(state)
- : ("." + state.OutputContainer);
+ : ('.' + state.OutputContainer);
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
@@ -970,18 +942,18 @@ namespace MediaBrowser.Api.Playback
responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode;
responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*";
- if (string.Equals(GetHeader("getMediaInfo.sec"), "1", StringComparison.OrdinalIgnoreCase))
+ if (state.RunTimeTicks.HasValue)
{
- if (state.RunTimeTicks.HasValue)
+ if (string.Equals(GetHeader("getMediaInfo.sec"), "1", StringComparison.OrdinalIgnoreCase))
{
var ms = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalMilliseconds;
responseHeaders["MediaInfo.sec"] = string.Format("SEC_Duration={0};", Convert.ToInt32(ms).ToString(CultureInfo.InvariantCulture));
}
- }
- if (state.RunTimeTicks.HasValue && !isStaticallyStreamed && profile != null)
- {
- AddTimeSeekResponseHeaders(state, responseHeaders);
+ if (!isStaticallyStreamed && profile != null)
+ {
+ AddTimeSeekResponseHeaders(state, responseHeaders);
+ }
}
if (profile == null)
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index 8d4b0cb3d0..ba3c2bfbaf 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -1,9 +1,7 @@
using System;
-using System.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Playback
@@ -12,6 +10,7 @@ namespace MediaBrowser.Api.Playback
{
private readonly ILogger _logger;
private readonly IMediaSourceManager _mediaSourceManager;
+ private bool _disposed = false;
public string RequestedUrl { get; set; }
@@ -30,11 +29,6 @@ namespace MediaBrowser.Api.Playback
public VideoStreamRequest VideoRequest => Request as VideoStreamRequest;
- ///
- /// Gets or sets the log file stream.
- ///
- /// The log file stream.
- public Stream LogFileStream { get; set; }
public IDirectStreamProvider DirectStreamProvider { get; set; }
public string WaitForPath { get; set; }
@@ -72,6 +66,7 @@ namespace MediaBrowser.Api.Playback
{
return 3;
}
+
return 6;
}
@@ -94,6 +89,16 @@ namespace MediaBrowser.Api.Playback
public string UserAgent { get; set; }
+ public bool EstimateContentLength { get; set; }
+
+ public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
+
+ public bool EnableDlnaHeaders { get; set; }
+
+ public DeviceProfile DeviceProfile { get; set; }
+
+ public TranscodingJob TranscodingJob { get; set; }
+
public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger, TranscodingJobType transcodingType)
: base(transcodingType)
{
@@ -101,75 +106,41 @@ namespace MediaBrowser.Api.Playback
_logger = logger;
}
- public bool EstimateContentLength { get; set; }
- public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
-
- public bool EnableDlnaHeaders { get; set; }
-
- public override void Dispose()
- {
- DisposeTranscodingThrottler();
- DisposeLogStream();
- DisposeLiveStream();
-
- TranscodingJob = null;
- }
-
- private void DisposeTranscodingThrottler()
- {
- if (TranscodingThrottler != null)
- {
- try
- {
- TranscodingThrottler.Dispose();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error disposing TranscodingThrottler");
- }
-
- TranscodingThrottler = null;
- }
- }
-
- private void DisposeLogStream()
- {
- if (LogFileStream != null)
- {
- try
- {
- LogFileStream.Dispose();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error disposing log stream");
- }
-
- LogFileStream = null;
- }
- }
-
- private async void DisposeLiveStream()
- {
- if (MediaSource.RequiresClosing && string.IsNullOrWhiteSpace(Request.LiveStreamId) && !string.IsNullOrWhiteSpace(MediaSource.LiveStreamId))
- {
- try
- {
- await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error closing media source");
- }
- }
- }
-
- public DeviceProfile DeviceProfile { get; set; }
-
- public TranscodingJob TranscodingJob;
public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate)
{
ApiEntryPoint.Instance.ReportTranscodingProgress(TranscodingJob, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, bitRate);
}
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ // REVIEW: Is this the right place for this?
+ if (MediaSource.RequiresClosing
+ && string.IsNullOrWhiteSpace(Request.LiveStreamId)
+ && !string.IsNullOrWhiteSpace(MediaSource.LiveStreamId))
+ {
+ _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).GetAwaiter().GetResult();
+ }
+
+ TranscodingThrottler?.Dispose();
+ }
+
+ TranscodingThrottler = null;
+ TranscodingJob = null;
+
+ _disposed = true;
+ }
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index 916d691b80..34af3b1568 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -374,14 +374,14 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- if (BaseRequest.Static || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (BaseRequest.Static
+ || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
if (AudioStream != null)
{
return AudioStream.SampleRate;
}
}
-
else if (BaseRequest.AudioSampleRate.HasValue)
{
// Don't exceed what the encoder supports
@@ -397,7 +397,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- if (BaseRequest.Static || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (BaseRequest.Static
+ || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
if (AudioStream != null)
{
@@ -405,13 +406,6 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- //else if (BaseRequest.AudioSampleRate.HasValue)
- //{
- // // Don't exceed what the encoder supports
- // // Seeing issues of attempting to encode to 88200
- // return Math.Min(44100, BaseRequest.AudioSampleRate.Value);
- //}
-
return null;
}
}
@@ -446,7 +440,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (BaseRequest.Static
+ || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
return VideoStream?.BitDepth;
}
@@ -463,7 +458,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (BaseRequest.Static
+ || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
return VideoStream?.RefFrames;
}
@@ -479,7 +475,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (BaseRequest.Static
+ || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
return VideoStream == null ? null : (VideoStream.AverageFrameRate ?? VideoStream.RealFrameRate);
}
@@ -545,7 +542,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (BaseRequest.Static
+ || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
return VideoStream?.CodecTag;
}
@@ -558,7 +556,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (BaseRequest.Static
+ || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
return VideoStream?.IsAnamorphic;
}
@@ -571,14 +570,12 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- var codec = OutputVideoCodec;
-
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
return VideoStream?.Codec;
}
- return codec;
+ return OutputVideoCodec;
}
}
@@ -586,14 +583,12 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- var codec = OutputAudioCodec;
-
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
return AudioStream?.Codec;
}
- return codec;
+ return OutputAudioCodec;
}
}
@@ -601,7 +596,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (BaseRequest.Static
+ || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
return VideoStream?.IsInterlaced;
}
@@ -636,6 +632,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue);
}
+
return GetMediaStreamCount(MediaStreamType.Video, 1);
}
}
@@ -648,17 +645,12 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue);
}
+
return GetMediaStreamCount(MediaStreamType.Audio, 1);
}
}
- public int HlsListSize
- {
- get
- {
- return 0;
- }
- }
+ public int HlsListSize => 0;
private int? GetMediaStreamCount(MediaStreamType type, int limit)
{
@@ -677,10 +669,6 @@ namespace MediaBrowser.Controller.MediaEncoding
{
Progress.Report(percentComplete.Value);
}
-
- public virtual void Dispose()
- {
- }
}
///
diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
index 2755bf5814..d0d5ebfd69 100644
--- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
+++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
@@ -3,6 +3,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
+using System.Threading.Tasks;
using MediaBrowser.Model.Extensions;
using Microsoft.Extensions.Logging;
@@ -18,10 +19,11 @@ namespace MediaBrowser.Controller.MediaEncoding
_logger = logger;
}
- public async void StartStreamingLog(EncodingJobInfo state, Stream source, Stream target)
+ public async Task StartStreamingLog(EncodingJobInfo state, Stream source, Stream target)
{
try
{
+ using (target)
using (var reader = new StreamReader(source))
{
while (!reader.EndOfStream && reader.BaseStream.CanRead)
@@ -97,8 +99,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
var currentMs = startMs + val.TotalMilliseconds;
- var percentVal = currentMs / totalMs;
- percent = 100 * percentVal;
+ percent = 100 * currentMs / totalMs;
transcodingPosition = val;
}
diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
index 901d81c5ff..e52951dd08 100644
--- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
+++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
@@ -13,7 +13,8 @@ namespace MediaBrowser.Model.Dlna
_profile = profile;
}
- public string BuildImageHeader(string container,
+ public string BuildImageHeader(
+ string container,
int? width,
int? height,
bool isDirectStream,
@@ -28,8 +29,7 @@ namespace MediaBrowser.Model.Dlna
DlnaFlags.InteractiveTransferMode |
DlnaFlags.DlnaV15;
- string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}",
- DlnaMaps.FlagsToString(flagValue));
+ string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}", DlnaMaps.FlagsToString(flagValue));
ResponseProfile mediaProfile = _profile.GetImageMediaProfile(container,
width,
@@ -37,7 +37,7 @@ namespace MediaBrowser.Model.Dlna
if (string.IsNullOrEmpty(orgPn))
{
- orgPn = mediaProfile == null ? null : mediaProfile.OrgPn;
+ orgPn = mediaProfile?.OrgPn;
}
if (string.IsNullOrEmpty(orgPn))
@@ -50,7 +50,8 @@ namespace MediaBrowser.Model.Dlna
return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
}
- public string BuildAudioHeader(string container,
+ public string BuildAudioHeader(
+ string container,
string audioCodec,
int? audioBitrate,
int? audioSampleRate,
@@ -102,7 +103,8 @@ namespace MediaBrowser.Model.Dlna
return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
}
- public List BuildVideoHeader(string container,
+ public List BuildVideoHeader(
+ string container,
string videoCodec,
string audioCodec,
int? width,
@@ -206,7 +208,7 @@ namespace MediaBrowser.Model.Dlna
return contentFeatureList;
}
- private string GetImageOrgPnValue(string container, int? width, int? height)
+ private static string GetImageOrgPnValue(string container, int? width, int? height)
{
MediaFormatProfile? format = new MediaFormatProfileResolver()
.ResolveImageFormat(container,
@@ -216,7 +218,7 @@ namespace MediaBrowser.Model.Dlna
return format.HasValue ? format.Value.ToString() : null;
}
- private string GetAudioOrgPnValue(string container, int? audioBitrate, int? audioSampleRate, int? audioChannels)
+ private static string GetAudioOrgPnValue(string container, int? audioBitrate, int? audioSampleRate, int? audioChannels)
{
MediaFormatProfile? format = new MediaFormatProfileResolver()
.ResolveAudioFormat(container,
@@ -227,7 +229,7 @@ namespace MediaBrowser.Model.Dlna
return format.HasValue ? format.Value.ToString() : null;
}
- private string[] GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestamp)
+ private static string[] GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestamp)
{
return new MediaFormatProfileResolver().ResolveVideoFormat(container, videoCodec, audioCodec, width, height, timestamp);
}
From ca37ca291fb06e8b46257ae1dbc0f2786c948d36 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Tue, 26 Mar 2019 23:04:53 +0100
Subject: [PATCH 006/214] More style changes
---
MediaBrowser.Api/Playback/BaseStreamingService.cs | 6 +++---
MediaBrowser.Api/Playback/StreamState.cs | 5 +----
2 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index b480d055af..674f3170e4 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -686,7 +686,7 @@ namespace MediaBrowser.Api.Playback
var enableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) /*||
string.Equals(Request.Headers.Get("GetContentFeatures.DLNA.ORG"), "1", StringComparison.OrdinalIgnoreCase)*/;
- var state = new StreamState(MediaSourceManager, Logger, TranscodingJobType)
+ var state = new StreamState(MediaSourceManager, TranscodingJobType)
{
Request = request,
RequestedUrl = url,
@@ -756,11 +756,11 @@ namespace MediaBrowser.Api.Playback
mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
? mediaSources[0]
- : mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId));
+ : mediaSources.Find(i => string.Equals(i.Id, request.MediaSourceId));
if (mediaSource == null && request.MediaSourceId.Equals(request.Id))
{
- mediaSource = mediaSources.First();
+ mediaSource = mediaSources[0];
}
}
}
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index ba3c2bfbaf..7396b5c99b 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -2,13 +2,11 @@ using System;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
-using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Playback
{
public class StreamState : EncodingJobInfo, IDisposable
{
- private readonly ILogger _logger;
private readonly IMediaSourceManager _mediaSourceManager;
private bool _disposed = false;
@@ -99,11 +97,10 @@ namespace MediaBrowser.Api.Playback
public TranscodingJob TranscodingJob { get; set; }
- public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger, TranscodingJobType transcodingType)
+ public StreamState(IMediaSourceManager mediaSourceManager, TranscodingJobType transcodingType)
: base(transcodingType)
{
_mediaSourceManager = mediaSourceManager;
- _logger = logger;
}
public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate)
From 8ed5d154b746c086ccb6b1163772ea4d05067abd Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 27 Mar 2019 16:07:08 +0100
Subject: [PATCH 007/214] Remove duplicate code
---
.../Session/SessionManager.cs | 62 +++++++------------
1 file changed, 23 insertions(+), 39 deletions(-)
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index dc23551db5..b7974dd7e8 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)
From 6c0e2e249ddbab24c67b655b701e473d9343c2ef Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 27 Mar 2019 16:13:36 +0100
Subject: [PATCH 008/214] Even more duplicate code removed
---
.../Session/SessionManager.cs | 56 ++-----------------
1 file changed, 6 insertions(+), 50 deletions(-)
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index b7974dd7e8..6b9573b1dd 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);
}
///
From b647959ec41373c513ee55075e4c973bfb68dbcd Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 27 Mar 2019 16:26:33 +0100
Subject: [PATCH 009/214] Add EnableOutputInSubFolder back
---
MediaBrowser.Api/Playback/BaseStreamingService.cs | 12 ++++++++++--
MediaBrowser.Controller/MediaEncoding/JobLogger.cs | 2 +-
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 674f3170e4..cde6f725aa 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -32,6 +32,8 @@ namespace MediaBrowser.Api.Playback
{
protected static readonly CultureInfo UsCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
+ protected virtual bool EnableOutputInSubFolder => false;
+
///
/// Gets or sets the application paths.
///
@@ -142,10 +144,16 @@ namespace MediaBrowser.Api.Playback
data += "-" + (state.Request.DeviceId ?? string.Empty)
+ "-" + (state.Request.PlaySessionId ?? string.Empty);
- var filename = data.GetMD5().ToString("N") + outputFileExtension.ToLowerInvariant();
+ var filename = data.GetMD5().ToString("N");
+ var ext = outputFileExtension.ToLowerInvariant();
var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath;
- return Path.Combine(folder, filename);
+ if (EnableOutputInSubFolder)
+ {
+ return Path.Combine(folder, filename, filename + ext);
+ }
+
+ return Path.Combine(folder, filename + ext);
}
protected virtual string GetDefaultH264Preset() => "superfast";
diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
index d0d5ebfd69..ac989f6ba1 100644
--- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
+++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
@@ -99,7 +99,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
var currentMs = startMs + val.TotalMilliseconds;
- percent = 100 * currentMs / totalMs;
+ percent = 100.0 * currentMs / totalMs;
transcodingPosition = val;
}
From b69b19ddce63651306bde494adfa656fd658b230 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 27 Mar 2019 16:28:52 +0100
Subject: [PATCH 010/214] Move messageId out of outer loop
---
Emby.Server.Implementations/Session/SessionManager.cs | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 6b9573b1dd..53ed5fc223 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);
From be86ea29828a696aedbbdc21f83ea4222e68b316 Mon Sep 17 00:00:00 2001
From: Claus Vium
Date: Wed, 27 Mar 2019 16:34:56 +0100
Subject: [PATCH 011/214] Update MediaBrowser.Common/Net/IHttpClient.cs
Co-Authored-By: Bond-009
---
MediaBrowser.Common/Net/IHttpClient.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs
index d332ab2000..db69c6f2cd 100644
--- a/MediaBrowser.Common/Net/IHttpClient.cs
+++ b/MediaBrowser.Common/Net/IHttpClient.cs
@@ -24,7 +24,7 @@ namespace MediaBrowser.Common.Net
Task Get(HttpRequestOptions options);
///
- /// Warning: Depricated function,
+ /// Warning: Deprecated function,
/// use 'Task SendAsync(HttpRequestOptions options, HttpMethod httpMethod);' instead
/// Sends the asynchronous.
///
From 9aaeb19418616283de00cdb0d00699ab93d84415 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 27 Mar 2019 16:31:26 +0100
Subject: [PATCH 012/214] Self-documenting code
---
MediaBrowser.Api/Playback/BaseStreamingService.cs | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index cde6f725aa..efbb17fd2a 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -622,18 +622,19 @@ namespace MediaBrowser.Api.Playback
return null;
}
- if (!value.StartsWith("npt=", StringComparison.OrdinalIgnoreCase))
+ const string Npt = "npt=";
+ if (!value.StartsWith(Npt, StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("Invalid timeseek header");
}
int index = value.IndexOf('-');
if (index == -1)
{
- value = value.Substring(4);
+ value = value.Substring(Npt.Length);
}
else
{
- value = value.Substring(4, index);
+ value = value.Substring(Npt.Length, index);
}
if (value.IndexOf(':') == -1)
From e89c8dbf7623fcb0c19c96957f6260df31fc3945 Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Wed, 17 Apr 2019 15:23:03 +0200
Subject: [PATCH 013/214] Use CultureInfo.InvariantCulture
---
.../Playback/BaseStreamingService.cs | 34 +++++++++----------
1 file changed, 16 insertions(+), 18 deletions(-)
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index efbb17fd2a..3994016240 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -30,8 +30,6 @@ namespace MediaBrowser.Api.Playback
///
public abstract class BaseStreamingService : BaseApiService
{
- protected static readonly CultureInfo UsCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
-
protected virtual bool EnableOutputInSubFolder => false;
///
@@ -418,55 +416,55 @@ namespace MediaBrowser.Api.Playback
{
if (videoRequest != null)
{
- videoRequest.AudioStreamIndex = int.Parse(val, UsCulture);
+ videoRequest.AudioStreamIndex = int.Parse(val, CultureInfo.InvariantCulture);
}
}
else if (i == 7)
{
if (videoRequest != null)
{
- videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture);
+ videoRequest.SubtitleStreamIndex = int.Parse(val, CultureInfo.InvariantCulture);
}
}
else if (i == 8)
{
if (videoRequest != null)
{
- videoRequest.VideoBitRate = int.Parse(val, UsCulture);
+ videoRequest.VideoBitRate = int.Parse(val, CultureInfo.InvariantCulture);
}
}
else if (i == 9)
{
- request.AudioBitRate = int.Parse(val, UsCulture);
+ request.AudioBitRate = int.Parse(val, CultureInfo.InvariantCulture);
}
else if (i == 10)
{
- request.MaxAudioChannels = int.Parse(val, UsCulture);
+ request.MaxAudioChannels = int.Parse(val, CultureInfo.InvariantCulture);
}
else if (i == 11)
{
if (videoRequest != null)
{
- videoRequest.MaxFramerate = float.Parse(val, UsCulture);
+ videoRequest.MaxFramerate = float.Parse(val, CultureInfo.InvariantCulture);
}
}
else if (i == 12)
{
if (videoRequest != null)
{
- videoRequest.MaxWidth = int.Parse(val, UsCulture);
+ videoRequest.MaxWidth = int.Parse(val, CultureInfo.InvariantCulture);
}
}
else if (i == 13)
{
if (videoRequest != null)
{
- videoRequest.MaxHeight = int.Parse(val, UsCulture);
+ videoRequest.MaxHeight = int.Parse(val, CultureInfo.InvariantCulture);
}
}
else if (i == 14)
{
- request.StartTimeTicks = long.Parse(val, UsCulture);
+ request.StartTimeTicks = long.Parse(val, CultureInfo.InvariantCulture);
}
else if (i == 15)
{
@@ -479,14 +477,14 @@ namespace MediaBrowser.Api.Playback
{
if (videoRequest != null)
{
- videoRequest.MaxRefFrames = int.Parse(val, UsCulture);
+ videoRequest.MaxRefFrames = int.Parse(val, CultureInfo.InvariantCulture);
}
}
else if (i == 17)
{
if (videoRequest != null)
{
- videoRequest.MaxVideoBitDepth = int.Parse(val, UsCulture);
+ videoRequest.MaxVideoBitDepth = int.Parse(val, CultureInfo.InvariantCulture);
}
}
else if (i == 18)
@@ -535,7 +533,7 @@ namespace MediaBrowser.Api.Playback
}
else if (i == 26)
{
- request.TranscodingMaxAudioChannels = int.Parse(val, UsCulture);
+ request.TranscodingMaxAudioChannels = int.Parse(val, CultureInfo.InvariantCulture);
}
else if (i == 27)
{
@@ -640,7 +638,7 @@ namespace MediaBrowser.Api.Playback
if (value.IndexOf(':') == -1)
{
// Parses npt times in the format of '417.33'
- if (double.TryParse(value, NumberStyles.Any, UsCulture, out var seconds))
+ if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var seconds))
{
return TimeSpan.FromSeconds(seconds).Ticks;
}
@@ -655,7 +653,7 @@ namespace MediaBrowser.Api.Playback
foreach (var time in tokens)
{
- if (double.TryParse(time, NumberStyles.Any, UsCulture, out var digit))
+ if (double.TryParse(time, NumberStyles.Any, CultureInfo.InvariantCulture, out var digit))
{
secondsSum += digit * timeFactor;
}
@@ -1027,8 +1025,8 @@ namespace MediaBrowser.Api.Playback
private void AddTimeSeekResponseHeaders(StreamState state, IDictionary responseHeaders)
{
- var runtimeSeconds = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds.ToString(UsCulture);
- var startSeconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds.ToString(UsCulture);
+ var runtimeSeconds = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds.ToString(CultureInfo.InvariantCulture);
+ var startSeconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds.ToString(CultureInfo.InvariantCulture);
responseHeaders["TimeSeekRange.dlna.org"] = string.Format("npt={0}-{1}/{1}", startSeconds, runtimeSeconds);
responseHeaders["X-AvailableSeekRange"] = string.Format("1 npt={0}-{1}", startSeconds, runtimeSeconds);
From 0794a3edf48eb232030560c4a03587cd77f38b65 Mon Sep 17 00:00:00 2001
From: bugfixin
Date: Thu, 18 Apr 2019 21:47:19 +0000
Subject: [PATCH 014/214] 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(-)
diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index c644d13eab..c9d4e43422 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 8485636794..47c3e71d72 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)
From da842d5a730ea8db2fab8e4c71a501191d54e46c Mon Sep 17 00:00:00 2001
From: bugfixin
Date: Fri, 19 Apr 2019 18:35:28 +0000
Subject: [PATCH 015/214] Fix incorrect escaping in regex pattern
---
Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index c9d4e43422..a70077163e 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;
}
From 5d85076ad5152b60ca2101177330b24944a65411 Mon Sep 17 00:00:00 2001
From: JMCC
Date: Sat, 11 May 2019 17:17:32 +0200
Subject: [PATCH 016/214] Enable Exynos V4L2-m2m HW encoder
---
MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index 3eed891cb3..b00350875c 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -207,6 +207,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
"hevc_omx",
"h264_vaapi",
"hevc_vaapi",
+ "h264_v4l2m2m",
"ac3"
};
From 012e4a3e63f8b0997d5f070b37ac93d257f894e7 Mon Sep 17 00:00:00 2001
From: JMCC
Date: Sat, 11 May 2019 17:19:20 +0200
Subject: [PATCH 017/214] Fix transcode bitrate control
---
.../MediaEncoding/EncodingHelper.cs | 49 ++++++++++++++-----
1 file changed, 36 insertions(+), 13 deletions(-)
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index e378c2b89d..849cf70835 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -1083,27 +1083,50 @@ namespace MediaBrowser.Controller.MediaEncoding
{
var bitrate = request.VideoBitRate;
- // If specific values were requested, then force the caller to supply a bitrate as well
- if (request.Height.HasValue && request.Width.HasValue)
- {
- return bitrate;
- }
-
if (videoStream != null)
{
- if (bitrate.HasValue)
- {
- var inputVideoCodec = videoStream.Codec;
- bitrate = ScaleBitrate(bitrate.Value, inputVideoCodec, outputVideoCodec);
+ var isUpscaling = request.Height.HasValue && videoStream.Height.HasValue &&
+ request.Height.Value > videoStream.Height.Value && request.Width.HasValue && videoStream.Width.HasValue &&
+ request.Width.Value > videoStream.Width.Value;
- // If a max bitrate was requested, don't let the scaled bitrate exceed it
- if (request.VideoBitRate.HasValue)
+ // Don't allow bitrate increases unless upscaling
+ if (!isUpscaling)
+ {
+ if (bitrate.HasValue && videoStream.BitRate.HasValue)
{
- bitrate = Math.Min(bitrate.Value, request.VideoBitRate.Value);
+ bitrate = GetMinBitrate(videoStream.BitRate.Value, bitrate.Value);
}
}
}
+ if (bitrate.HasValue)
+ {
+ var inputVideoCodec = videoStream.Codec;
+ bitrate = ScaleBitrate(bitrate.Value, inputVideoCodec, outputVideoCodec);
+
+ // If a max bitrate was requested, don't let the scaled bitrate exceed it
+ if (request.VideoBitRate.HasValue)
+ {
+ bitrate = Math.Min(bitrate.Value, request.VideoBitRate.Value);
+ }
+ }
+
+ return bitrate;
+ }
+
+ private int GetMinBitrate(int sourceBitrate, int requestedBitrate)
+ {
+ if (sourceBitrate <= 2000000)
+ {
+ sourceBitrate = Convert.ToInt32(sourceBitrate * 2.5);
+ }
+ else if (sourceBitrate <= 3000000)
+ {
+ sourceBitrate = Convert.ToInt32(sourceBitrate * 2);
+ }
+
+ var bitrate = Math.Min(sourceBitrate, requestedBitrate);
+
return bitrate;
}
From 4a9b349c0463b46a3208b949f75d951f092aa735 Mon Sep 17 00:00:00 2001
From: dkanada
Date: Thu, 16 May 2019 16:45:56 -0700
Subject: [PATCH 018/214] only return useful drives
---
Emby.Server.Implementations/IO/ManagedFileSystem.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 0dea5041a1..f0bea0455a 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();
}
From 69ee49bee607d716a857a1525f503575ebf6db7f Mon Sep 17 00:00:00 2001
From: DrPandemic
Date: Sat, 25 May 2019 13:46:55 -0400
Subject: [PATCH 019/214] Format correctly the PIN when updating it
---
.../Library/DefaultAuthenticationProvider.cs | 28 +++++++++++++++++++
.../Library/UserManager.cs | 27 ++++--------------
.../Authentication/IAuthenticationProvider.cs | 3 ++
3 files changed, 36 insertions(+), 22 deletions(-)
diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
index 0527464ffc..fe09b07ff6 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 b396ee51a3..a0b8d4ba42 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 b9f282bd2a..2cf531eed5 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
From a756026962363f2c542799ebb0e3c7abd3a8f541 Mon Sep 17 00:00:00 2001
From: Claus Vium
Date: Fri, 31 May 2019 07:24:52 +0200
Subject: [PATCH 020/214] Fix inverted comparison in the tvdb token refresh
logic
---
MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
index efb8a0fe8f..1d1fbd00f1 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
@@ -33,7 +33,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
get
{
// Refresh if necessary
- if (_tokenCreatedAt > DateTime.Now.Subtract(TimeSpan.FromHours(20)))
+ if (_tokenCreatedAt < DateTime.Now.Subtract(TimeSpan.FromHours(20)))
{
try
{
From 09dfa071dc27820f1820bd56126dfa5cd5e01027 Mon Sep 17 00:00:00 2001
From: dkanada
Date: Fri, 31 May 2019 01:48:20 -0700
Subject: [PATCH 021/214] move fanart image provider to plugin
---
MediaBrowser.Api/Library/LibraryService.cs | 12 -
.../Configuration/FanartOptions.cs | 11 -
.../Configuration/ServerConfiguration.cs | 7 +-
.../Movies/FanartMovieImageProvider.cs | 334 ----------------
.../Music/FanArtAlbumProvider.cs | 201 ----------
.../Music/FanArtArtistProvider.cs | 335 ----------------
.../TV/FanArt/FanArtSeasonProvider.cs | 205 ----------
.../TV/FanArt/FanartSeriesProvider.cs | 378 ------------------
8 files changed, 3 insertions(+), 1480 deletions(-)
delete mode 100644 MediaBrowser.Model/Configuration/FanartOptions.cs
delete mode 100644 MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
delete mode 100644 MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
delete mode 100644 MediaBrowser.Providers/Music/FanArtArtistProvider.cs
delete mode 100644 MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
delete mode 100644 MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index 9f8da9c168..ddf45f9f3c 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -490,18 +490,6 @@ namespace MediaBrowser.Api.Library
{
return false;
}
- else if (string.Equals(name, "FanArt", StringComparison.OrdinalIgnoreCase))
- {
- if (string.Equals(type, "Season", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- if (string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- return true;
- }
else if (string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase))
{
return true;
diff --git a/MediaBrowser.Model/Configuration/FanartOptions.cs b/MediaBrowser.Model/Configuration/FanartOptions.cs
deleted file mode 100644
index 9c8be39be7..0000000000
--- a/MediaBrowser.Model/Configuration/FanartOptions.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace MediaBrowser.Model.Configuration
-{
- public class FanartOptions
- {
- ///
- /// Gets or sets the user API key.
- ///
- /// The user API key.
- public string UserApiKey { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index 0ba36b4b9d..2673597caa 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -49,9 +49,9 @@ namespace MediaBrowser.Model.Configuration
public bool EnableNormalizedItemByNameIds { get; set; }
///
- /// Gets or sets the value pointing to the file system where the ssl certiifcate is located..
+ /// Gets or sets the value pointing to the file system where the ssl certificate is located..
///
- /// The value pointing to the file system where the ssl certiifcate is located..
+ /// The value pointing to the file system where the ssl certificate is located..
public string CertificatePath { get; set; }
public string CertificatePassword { get; set; }
@@ -259,7 +259,7 @@ namespace MediaBrowser.Model.Configuration
{
ItemType = "MusicVideo",
DisabledMetadataFetchers = new [] { "The Open Movie Database" },
- DisabledImageFetchers = new [] { "The Open Movie Database", "FanArt" }
+ DisabledImageFetchers = new [] { "The Open Movie Database" }
},
new MetadataOptions
{
@@ -285,7 +285,6 @@ namespace MediaBrowser.Model.Configuration
{
ItemType = "Season",
DisabledMetadataFetchers = new [] { "TheMovieDb" },
- DisabledImageFetchers = new [] { "FanArt" }
},
new MetadataOptions
{
diff --git a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
deleted file mode 100644
index 70d187bf55..0000000000
--- a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
+++ /dev/null
@@ -1,334 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Music;
-using MediaBrowser.Providers.TV;
-using MediaBrowser.Providers.TV.FanArt;
-
-namespace MediaBrowser.Providers.Movies
-{
- public class FanartMovieImageProvider : IRemoteImageProvider, IHasOrder
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private readonly IServerConfigurationManager _config;
- private readonly IHttpClient _httpClient;
- private readonly IFileSystem _fileSystem;
- private readonly IJsonSerializer _json;
-
- private const string FanArtBaseUrl = "https://webservice.fanart.tv/v3/movies/{1}?api_key={0}";
-
- internal static FanartMovieImageProvider Current;
-
- public FanartMovieImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IJsonSerializer json)
- {
- _config = config;
- _httpClient = httpClient;
- _fileSystem = fileSystem;
- _json = json;
-
- Current = this;
- }
-
- public string Name => ProviderName;
-
- public static string ProviderName => "FanArt";
-
- public bool Supports(BaseItem item)
- {
- return item is Movie || item is BoxSet || item is MusicVideo;
- }
-
- public IEnumerable GetSupportedImages(BaseItem item)
- {
- return new List
- {
- ImageType.Primary,
- ImageType.Thumb,
- ImageType.Art,
- ImageType.Logo,
- ImageType.Disc,
- ImageType.Banner,
- ImageType.Backdrop
- };
- }
-
- public async Task> GetImages(BaseItem item, CancellationToken cancellationToken)
- {
- var baseItem = item;
- var list = new List();
-
- var movieId = baseItem.GetProviderId(MetadataProviders.Tmdb);
-
- if (!string.IsNullOrEmpty(movieId))
- {
- // Bad id entered
- try
- {
- await EnsureMovieJson(movieId, cancellationToken).ConfigureAwait(false);
- }
- catch (HttpException ex)
- {
- if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound)
- {
- throw;
- }
- }
-
- var path = GetFanartJsonPath(movieId);
-
- try
- {
- AddImages(list, path, cancellationToken);
- }
- catch (FileNotFoundException)
- {
- // No biggie. Don't blow up
- }
- catch (IOException)
- {
- // No biggie. Don't blow up
- }
- }
-
- var language = item.GetPreferredMetadataLanguage();
-
- var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase);
-
- // Sort first by width to prioritize HD versions
- return list.OrderByDescending(i => i.Width ?? 0)
- .ThenByDescending(i =>
- {
- if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase))
- {
- return 3;
- }
- if (!isLanguageEn)
- {
- if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase))
- {
- return 2;
- }
- }
- if (string.IsNullOrEmpty(i.Language))
- {
- return isLanguageEn ? 3 : 2;
- }
- return 0;
- })
- .ThenByDescending(i => i.CommunityRating ?? 0);
- }
-
- private void AddImages(List list, string path, CancellationToken cancellationToken)
- {
- var root = _json.DeserializeFromFile(path);
-
- AddImages(list, root, cancellationToken);
- }
-
- private void AddImages(List list, RootObject obj, CancellationToken cancellationToken)
- {
- PopulateImages(list, obj.hdmovieclearart, ImageType.Art, 1000, 562);
- PopulateImages(list, obj.hdmovielogo, ImageType.Logo, 800, 310);
- PopulateImages(list, obj.moviedisc, ImageType.Disc, 1000, 1000);
- PopulateImages(list, obj.movieposter, ImageType.Primary, 1000, 1426);
- PopulateImages(list, obj.movielogo, ImageType.Logo, 400, 155);
- PopulateImages(list, obj.movieart, ImageType.Art, 500, 281);
- PopulateImages(list, obj.moviethumb, ImageType.Thumb, 1000, 562);
- PopulateImages(list, obj.moviebanner, ImageType.Banner, 1000, 185);
- PopulateImages(list, obj.moviebackground, ImageType.Backdrop, 1920, 1080);
- }
-
- private void PopulateImages(List list, List images, ImageType type, int width, int height)
- {
- if (images == null)
- {
- return;
- }
-
- list.AddRange(images.Select(i =>
- {
- var url = i.url;
-
- if (!string.IsNullOrEmpty(url))
- {
- var likesString = i.likes;
-
- var info = new RemoteImageInfo
- {
- RatingType = RatingType.Likes,
- Type = type,
- Width = width,
- Height = height,
- ProviderName = Name,
- Url = url,
- Language = i.lang
- };
-
- if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Integer, _usCulture, out var likes))
- {
- info.CommunityRating = likes;
- }
-
- return info;
- }
-
- return null;
- }).Where(i => i != null));
- }
-
- public int Order => 1;
-
- public Task GetImageResponse(string url, CancellationToken cancellationToken)
- {
- return _httpClient.GetResponse(new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- Url = url
- });
- }
-
- ///
- /// Gets the movie data path.
- ///
- /// The application paths.
- /// The identifier.
- /// System.String.
- internal static string GetMovieDataPath(IApplicationPaths appPaths, string id)
- {
- var dataPath = Path.Combine(GetMoviesDataPath(appPaths), id);
-
- return dataPath;
- }
-
- ///
- /// Gets the movie data path.
- ///
- /// The app paths.
- /// System.String.
- internal static string GetMoviesDataPath(IApplicationPaths appPaths)
- {
- var dataPath = Path.Combine(appPaths.CachePath, "fanart-movies");
-
- return dataPath;
- }
-
- public string GetFanartJsonPath(string id)
- {
- var movieDataPath = GetMovieDataPath(_config.ApplicationPaths, id);
- return Path.Combine(movieDataPath, "fanart.json");
- }
-
- ///
- /// Downloads the movie json.
- ///
- /// The identifier.
- /// The cancellation token.
- /// Task.
- internal async Task DownloadMovieJson(string id, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var url = string.Format(FanArtBaseUrl, FanartArtistProvider.ApiKey, id);
-
- var clientKey = FanartSeriesProvider.Current.GetFanartOptions().UserApiKey;
- if (!string.IsNullOrWhiteSpace(clientKey))
- {
- url += "&client_key=" + clientKey;
- }
-
- var path = GetFanartJsonPath(id);
-
- Directory.CreateDirectory(Path.GetDirectoryName(path));
-
- try
- {
- using (var httpResponse = await _httpClient.SendAsync(new HttpRequestOptions
- {
- Url = url,
- CancellationToken = cancellationToken,
- BufferContent = true
-
- }, "GET").ConfigureAwait(false))
- {
- using (var response = httpResponse.Content)
- {
- using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
- {
- await response.CopyToAsync(fileStream).ConfigureAwait(false);
- }
- }
- }
- }
- catch (HttpException exception)
- {
- if (exception.StatusCode.HasValue && exception.StatusCode.Value == HttpStatusCode.NotFound)
- {
- // If the user has automatic updates enabled, save a dummy object to prevent repeated download attempts
- _json.SerializeToFile(new RootObject(), path);
-
- return;
- }
-
- throw;
- }
- }
-
- internal Task EnsureMovieJson(string id, CancellationToken cancellationToken)
- {
- var path = GetFanartJsonPath(id);
-
- var fileInfo = _fileSystem.GetFileSystemInfo(path);
-
- if (fileInfo.Exists)
- {
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
- {
- return Task.CompletedTask;
- }
- }
-
- return DownloadMovieJson(id, cancellationToken);
- }
-
- public class Image
- {
- public string id { get; set; }
- public string url { get; set; }
- public string lang { get; set; }
- public string likes { get; set; }
- }
-
- public class RootObject
- {
- public string name { get; set; }
- public string tmdb_id { get; set; }
- public string imdb_id { get; set; }
- public List hdmovielogo { get; set; }
- public List moviedisc { get; set; }
- public List movielogo { get; set; }
- public List movieposter { get; set; }
- public List hdmovieclearart { get; set; }
- public List movieart { get; set; }
- public List moviebackground { get; set; }
- public List moviebanner { get; set; }
- public List moviethumb { get; set; }
- }
- }
-}
diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
deleted file mode 100644
index ebb740ffe4..0000000000
--- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
+++ /dev/null
@@ -1,201 +0,0 @@
-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.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Providers.Music
-{
- public class FanartAlbumProvider : IRemoteImageProvider, IHasOrder
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private readonly IServerConfigurationManager _config;
- private readonly IHttpClient _httpClient;
- private readonly IFileSystem _fileSystem;
- private readonly IJsonSerializer _jsonSerializer;
-
- public FanartAlbumProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IJsonSerializer jsonSerializer)
- {
- _config = config;
- _httpClient = httpClient;
- _fileSystem = fileSystem;
- _jsonSerializer = jsonSerializer;
- }
-
- public string Name => ProviderName;
-
- public static string ProviderName => "FanArt";
-
- public bool Supports(BaseItem item)
- {
- return item is MusicAlbum;
- }
-
- public IEnumerable GetSupportedImages(BaseItem item)
- {
- return new List
- {
- ImageType.Primary,
- ImageType.Disc
- };
- }
-
- public async Task> GetImages(BaseItem item, CancellationToken cancellationToken)
- {
- var album = (MusicAlbum)item;
-
- var list = new List();
-
- var musicArtist = album.MusicArtist;
-
- if (musicArtist == null)
- {
- return list;
- }
-
- var artistMusicBrainzId = musicArtist.GetProviderId(MetadataProviders.MusicBrainzArtist);
-
- if (!string.IsNullOrEmpty(artistMusicBrainzId))
- {
- await FanartArtistProvider.Current.EnsureArtistJson(artistMusicBrainzId, cancellationToken).ConfigureAwait(false);
-
- var artistJsonPath = FanartArtistProvider.GetArtistJsonPath(_config.CommonApplicationPaths, artistMusicBrainzId);
-
- var musicBrainzReleaseGroupId = album.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
-
- var musicBrainzId = album.GetProviderId(MetadataProviders.MusicBrainzAlbum);
-
- try
- {
- AddImages(list, artistJsonPath, musicBrainzId, musicBrainzReleaseGroupId, cancellationToken);
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (IOException)
- {
-
- }
- }
-
- var language = item.GetPreferredMetadataLanguage();
-
- var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase);
-
- // Sort first by width to prioritize HD versions
- return list.OrderByDescending(i => i.Width ?? 0)
- .ThenByDescending(i =>
- {
- if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase))
- {
- return 3;
- }
- if (!isLanguageEn)
- {
- if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase))
- {
- return 2;
- }
- }
- if (string.IsNullOrEmpty(i.Language))
- {
- return isLanguageEn ? 3 : 2;
- }
- return 0;
- })
- .ThenByDescending(i => i.CommunityRating ?? 0)
- .ThenByDescending(i => i.VoteCount ?? 0);
- }
-
- ///
- /// Adds the images.
- ///
- /// The list.
- /// The path.
- /// The release identifier.
- /// The release group identifier.
- /// The cancellation token.
- private void AddImages(List list, string path, string releaseId, string releaseGroupId, CancellationToken cancellationToken)
- {
- var obj = _jsonSerializer.DeserializeFromFile(path);
-
- if (obj.albums != null)
- {
- var album = obj.albums.FirstOrDefault(i => string.Equals(i.release_group_id, releaseGroupId, StringComparison.OrdinalIgnoreCase));
-
- if (album != null)
- {
- PopulateImages(list, album.albumcover, ImageType.Primary, 1000, 1000);
- PopulateImages(list, album.cdart, ImageType.Disc, 1000, 1000);
- }
- }
- }
-
- private void PopulateImages(List list,
- List images,
- ImageType type,
- int width,
- int height)
- {
- if (images == null)
- {
- return;
- }
-
- list.AddRange(images.Select(i =>
- {
- var url = i.url;
-
- if (!string.IsNullOrEmpty(url))
- {
- var likesString = i.likes;
-
- var info = new RemoteImageInfo
- {
- RatingType = RatingType.Likes,
- Type = type,
- Width = width,
- Height = height,
- ProviderName = Name,
- Url = url.Replace("http://", "https://", StringComparison.OrdinalIgnoreCase),
- Language = i.lang
- };
-
- if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Integer, _usCulture, out var likes))
- {
- info.CommunityRating = likes;
- }
-
- return info;
- }
-
- return null;
- }).Where(i => i != null));
- }
- // After embedded provider
- public int Order => 1;
-
- public Task GetImageResponse(string url, CancellationToken cancellationToken)
- {
- return _httpClient.GetResponse(new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- Url = url
- });
- }
- }
-}
diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
deleted file mode 100644
index 75b4213c52..0000000000
--- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
+++ /dev/null
@@ -1,335 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.TV;
-using MediaBrowser.Providers.TV.FanArt;
-
-namespace MediaBrowser.Providers.Music
-{
- public class FanartArtistProvider : IRemoteImageProvider, IHasOrder
- {
- internal const string ApiKey = "184e1a2b1fe3b94935365411f919f638";
- private const string FanArtBaseUrl = "https://webservice.fanart.tv/v3.1/music/{1}?api_key={0}";
-
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private readonly IServerConfigurationManager _config;
- private readonly IHttpClient _httpClient;
- private readonly IFileSystem _fileSystem;
- private readonly IJsonSerializer _jsonSerializer;
-
- internal static FanartArtistProvider Current;
-
- public FanartArtistProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IJsonSerializer jsonSerializer)
- {
- _config = config;
- _httpClient = httpClient;
- _fileSystem = fileSystem;
- _jsonSerializer = jsonSerializer;
-
- Current = this;
- }
-
- public string Name => ProviderName;
-
- public static string ProviderName => "FanArt";
-
- public bool Supports(BaseItem item)
- {
- return item is MusicArtist;
- }
-
- public IEnumerable GetSupportedImages(BaseItem item)
- {
- return new List
- {
- ImageType.Primary,
- ImageType.Logo,
- ImageType.Art,
- ImageType.Banner,
- ImageType.Backdrop
- };
- }
-
- public async Task> GetImages(BaseItem item, CancellationToken cancellationToken)
- {
- var artist = (MusicArtist)item;
-
- var list = new List();
-
- var artistMusicBrainzId = artist.GetProviderId(MetadataProviders.MusicBrainzArtist);
-
- if (!string.IsNullOrEmpty(artistMusicBrainzId))
- {
- await EnsureArtistJson(artistMusicBrainzId, cancellationToken).ConfigureAwait(false);
-
- var artistJsonPath = GetArtistJsonPath(_config.CommonApplicationPaths, artistMusicBrainzId);
-
- try
- {
- AddImages(list, artistJsonPath, cancellationToken);
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (IOException)
- {
-
- }
- }
-
- var language = item.GetPreferredMetadataLanguage();
-
- var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase);
-
- // Sort first by width to prioritize HD versions
- return list.OrderByDescending(i => i.Width ?? 0)
- .ThenByDescending(i =>
- {
- if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase))
- {
- return 3;
- }
- if (!isLanguageEn)
- {
- if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase))
- {
- return 2;
- }
- }
- if (string.IsNullOrEmpty(i.Language))
- {
- return isLanguageEn ? 3 : 2;
- }
- return 0;
- })
- .ThenByDescending(i => i.CommunityRating ?? 0)
- .ThenByDescending(i => i.VoteCount ?? 0);
- }
-
- ///
- /// Adds the images.
- ///
- /// The list.
- /// The path.
- /// The cancellation token.
- private void AddImages(List list, string path, CancellationToken cancellationToken)
- {
- var obj = _jsonSerializer.DeserializeFromFile(path);
-
- PopulateImages(list, obj.artistbackground, ImageType.Backdrop, 1920, 1080);
- PopulateImages(list, obj.artistthumb, ImageType.Primary, 500, 281);
- PopulateImages(list, obj.hdmusiclogo, ImageType.Logo, 800, 310);
- PopulateImages(list, obj.musicbanner, ImageType.Banner, 1000, 185);
- PopulateImages(list, obj.musiclogo, ImageType.Logo, 400, 155);
- PopulateImages(list, obj.hdmusicarts, ImageType.Art, 1000, 562);
- PopulateImages(list, obj.musicarts, ImageType.Art, 500, 281);
- }
-
- private void PopulateImages(List list,
- List images,
- ImageType type,
- int width,
- int height)
- {
- if (images == null)
- {
- return;
- }
-
- list.AddRange(images.Select(i =>
- {
- var url = i.url;
-
- if (!string.IsNullOrEmpty(url))
- {
- var likesString = i.likes;
-
- var info = new RemoteImageInfo
- {
- RatingType = RatingType.Likes,
- Type = type,
- Width = width,
- Height = height,
- ProviderName = Name,
- Url = url.Replace("http://", "https://", StringComparison.OrdinalIgnoreCase),
- Language = i.lang
- };
-
- if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Integer, _usCulture, out var likes))
- {
- info.CommunityRating = likes;
- }
-
- return info;
- }
-
- return null;
- }).Where(i => i != null));
- }
-
- public int Order => 0;
-
- public Task GetImageResponse(string url, CancellationToken cancellationToken)
- {
- return _httpClient.GetResponse(new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- Url = url
- });
- }
-
- internal Task EnsureArtistJson(string musicBrainzId, CancellationToken cancellationToken)
- {
- var jsonPath = GetArtistJsonPath(_config.ApplicationPaths, musicBrainzId);
-
- var fileInfo = _fileSystem.GetFileSystemInfo(jsonPath);
-
- if (fileInfo.Exists)
- {
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
- {
- return Task.CompletedTask;
- }
- }
-
- return DownloadArtistJson(musicBrainzId, cancellationToken);
- }
-
- ///
- /// Downloads the artist data.
- ///
- /// The music brainz id.
- /// The cancellation token.
- /// Task{System.Boolean}.
- internal async Task DownloadArtistJson(string musicBrainzId, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var url = string.Format(FanArtBaseUrl, ApiKey, musicBrainzId);
-
- var clientKey = FanartSeriesProvider.Current.GetFanartOptions().UserApiKey;
- if (!string.IsNullOrWhiteSpace(clientKey))
- {
- url += "&client_key=" + clientKey;
- }
-
- var jsonPath = GetArtistJsonPath(_config.ApplicationPaths, musicBrainzId);
-
- Directory.CreateDirectory(Path.GetDirectoryName(jsonPath));
-
- try
- {
- using (var httpResponse = await _httpClient.SendAsync(new HttpRequestOptions
- {
- Url = url,
- CancellationToken = cancellationToken,
- BufferContent = true
-
- }, "GET").ConfigureAwait(false))
- {
- using (var response = httpResponse.Content)
- {
- using (var saveFileStream = _fileSystem.GetFileStream(jsonPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
- {
- await response.CopyToAsync(saveFileStream).ConfigureAwait(false);
- }
- }
- }
- }
- catch (HttpException ex)
- {
- if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
- {
- _jsonSerializer.SerializeToFile(new FanartArtistResponse(), jsonPath);
- }
- else
- {
- throw;
- }
- }
- }
-
- ///
- /// Gets the artist data path.
- ///
- /// The application paths.
- /// The music brainz artist identifier.
- /// System.String.
- private static string GetArtistDataPath(IApplicationPaths appPaths, string musicBrainzArtistId)
- {
- var dataPath = Path.Combine(GetArtistDataPath(appPaths), musicBrainzArtistId);
-
- return dataPath;
- }
-
- ///
- /// Gets the artist data path.
- ///
- /// The application paths.
- /// System.String.
- internal static string GetArtistDataPath(IApplicationPaths appPaths)
- {
- var dataPath = Path.Combine(appPaths.CachePath, "fanart-music");
-
- return dataPath;
- }
-
- internal static string GetArtistJsonPath(IApplicationPaths appPaths, string musicBrainzArtistId)
- {
- var dataPath = GetArtistDataPath(appPaths, musicBrainzArtistId);
-
- return Path.Combine(dataPath, "fanart.json");
- }
-
-
- public class FanartArtistImage
- {
- public string id { get; set; }
- public string url { get; set; }
- public string likes { get; set; }
- public string disc { get; set; }
- public string size { get; set; }
- public string lang { get; set; }
- }
-
- public class Album
- {
- public string release_group_id { get; set; }
- public List cdart { get; set; }
- public List albumcover { get; set; }
- }
-
- public class FanartArtistResponse
- {
- public string name { get; set; }
- public string mbid_id { get; set; }
- public List artistthumb { get; set; }
- public List artistbackground { get; set; }
- public List hdmusiclogo { get; set; }
- public List musicbanner { get; set; }
- public List musiclogo { get; set; }
- public List musicarts { get; set; }
- public List hdmusicarts { get; set; }
- public List albums { get; set; }
- }
- }
-}
diff --git a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
deleted file mode 100644
index 58356910f6..0000000000
--- a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
+++ /dev/null
@@ -1,205 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Providers.TV.FanArt
-{
- public class FanArtSeasonProvider : IRemoteImageProvider, IHasOrder
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private readonly IServerConfigurationManager _config;
- private readonly IHttpClient _httpClient;
- private readonly IFileSystem _fileSystem;
- private readonly IJsonSerializer _json;
-
- public FanArtSeasonProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IJsonSerializer json)
- {
- _config = config;
- _httpClient = httpClient;
- _fileSystem = fileSystem;
- _json = json;
- }
-
- public string Name => ProviderName;
-
- public static string ProviderName => "FanArt";
-
- public bool Supports(BaseItem item)
- {
- return item is Season;
- }
-
- public IEnumerable GetSupportedImages(BaseItem item)
- {
- return new List
- {
- ImageType.Backdrop,
- ImageType.Thumb,
- ImageType.Banner,
- ImageType.Primary
- };
- }
-
- public async Task> GetImages(BaseItem item, CancellationToken cancellationToken)
- {
- var list = new List();
-
- var season = (Season)item;
- var series = season.Series;
-
- if (series != null)
- {
- var id = series.GetProviderId(MetadataProviders.Tvdb);
-
- if (!string.IsNullOrEmpty(id) && season.IndexNumber.HasValue)
- {
- // Bad id entered
- try
- {
- await FanartSeriesProvider.Current.EnsureSeriesJson(id, cancellationToken).ConfigureAwait(false);
- }
- catch (HttpException ex)
- {
- if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound)
- {
- throw;
- }
- }
-
- var path = FanartSeriesProvider.Current.GetFanartJsonPath(id);
-
- try
- {
- AddImages(list, season.IndexNumber.Value, path, cancellationToken);
- }
- catch (FileNotFoundException)
- {
- // No biggie. Don't blow up
- }
- catch (IOException)
- {
- // No biggie. Don't blow up
- }
- }
- }
-
- var language = item.GetPreferredMetadataLanguage();
-
- var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase);
-
- // Sort first by width to prioritize HD versions
- return list.OrderByDescending(i => i.Width ?? 0)
- .ThenByDescending(i =>
- {
- if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase))
- {
- return 3;
- }
- if (!isLanguageEn)
- {
- if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase))
- {
- return 2;
- }
- }
- if (string.IsNullOrEmpty(i.Language))
- {
- return isLanguageEn ? 3 : 2;
- }
- return 0;
- })
- .ThenByDescending(i => i.CommunityRating ?? 0)
- .ThenByDescending(i => i.VoteCount ?? 0);
- }
-
- private void AddImages(List list, int seasonNumber, string path, CancellationToken cancellationToken)
- {
- var root = _json.DeserializeFromFile(path);
-
- AddImages(list, root, seasonNumber, cancellationToken);
- }
-
- private void AddImages(List list, FanartSeriesProvider.RootObject obj, int seasonNumber, CancellationToken cancellationToken)
- {
- PopulateImages(list, obj.seasonposter, ImageType.Primary, 1000, 1426, seasonNumber);
- PopulateImages(list, obj.seasonbanner, ImageType.Banner, 1000, 185, seasonNumber);
- PopulateImages(list, obj.seasonthumb, ImageType.Thumb, 500, 281, seasonNumber);
- PopulateImages(list, obj.showbackground, ImageType.Backdrop, 1920, 1080, seasonNumber);
- }
-
- private void PopulateImages(List list,
- List images,
- ImageType type,
- int width,
- int height,
- int seasonNumber)
- {
- if (images == null)
- {
- return;
- }
-
- list.AddRange(images.Select(i =>
- {
- var url = i.url;
- var season = i.season;
-
- if (!string.IsNullOrEmpty(url) &&
- !string.IsNullOrEmpty(season) &&
- int.TryParse(season, NumberStyles.Integer, _usCulture, out var imageSeasonNumber) &&
- seasonNumber == imageSeasonNumber)
- {
- var likesString = i.likes;
-
- var info = new RemoteImageInfo
- {
- RatingType = RatingType.Likes,
- Type = type,
- Width = width,
- Height = height,
- ProviderName = Name,
- Url = url.Replace("http://", "https://", StringComparison.OrdinalIgnoreCase),
- Language = i.lang
- };
-
- if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Integer, _usCulture, out var likes))
- {
- info.CommunityRating = likes;
- }
-
- return info;
- }
-
- return null;
- }).Where(i => i != null));
- }
-
- public int Order => 1;
-
- public Task GetImageResponse(string url, CancellationToken cancellationToken)
- {
- return _httpClient.GetResponse(new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- Url = url
- });
- }
- }
-}
diff --git a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs
deleted file mode 100644
index 49cd9596e4..0000000000
--- a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs
+++ /dev/null
@@ -1,378 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Music;
-
-namespace MediaBrowser.Providers.TV.FanArt
-{
- public class FanartSeriesProvider : IRemoteImageProvider, IHasOrder
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private readonly IServerConfigurationManager _config;
- private readonly IHttpClient _httpClient;
- private readonly IFileSystem _fileSystem;
- private readonly IJsonSerializer _json;
-
- private const string FanArtBaseUrl = "https://webservice.fanart.tv/v3/tv/{1}?api_key={0}";
-
- internal static FanartSeriesProvider Current { get; private set; }
-
- public FanartSeriesProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IJsonSerializer json)
- {
- _config = config;
- _httpClient = httpClient;
- _fileSystem = fileSystem;
- _json = json;
-
- Current = this;
- }
-
- public string Name => ProviderName;
-
- public static string ProviderName => "FanArt";
-
- public bool Supports(BaseItem item)
- {
- return item is Series;
- }
-
- public IEnumerable GetSupportedImages(BaseItem item)
- {
- return new List
- {
- ImageType.Primary,
- ImageType.Thumb,
- ImageType.Art,
- ImageType.Logo,
- ImageType.Backdrop,
- ImageType.Banner
- };
- }
-
- public async Task> GetImages(BaseItem item, CancellationToken cancellationToken)
- {
- var list = new List();
-
- var series = (Series)item;
-
- var id = series.GetProviderId(MetadataProviders.Tvdb);
-
- if (!string.IsNullOrEmpty(id))
- {
- // Bad id entered
- try
- {
- await EnsureSeriesJson(id, cancellationToken).ConfigureAwait(false);
- }
- catch (HttpException ex)
- {
- if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound)
- {
- throw;
- }
- }
-
- var path = GetFanartJsonPath(id);
-
- try
- {
- AddImages(list, path, cancellationToken);
- }
- catch (FileNotFoundException)
- {
- // No biggie. Don't blow up
- }
- catch (IOException)
- {
- // No biggie. Don't blow up
- }
- }
-
- var language = item.GetPreferredMetadataLanguage();
-
- var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase);
-
- // Sort first by width to prioritize HD versions
- return list.OrderByDescending(i => i.Width ?? 0)
- .ThenByDescending(i =>
- {
- if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase))
- {
- return 3;
- }
- if (!isLanguageEn)
- {
- if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase))
- {
- return 2;
- }
- }
- if (string.IsNullOrEmpty(i.Language))
- {
- return isLanguageEn ? 3 : 2;
- }
- return 0;
- })
- .ThenByDescending(i => i.CommunityRating ?? 0)
- .ThenByDescending(i => i.VoteCount ?? 0);
- }
-
- private void AddImages(List list, string path, CancellationToken cancellationToken)
- {
- var root = _json.DeserializeFromFile(path);
-
- AddImages(list, root, cancellationToken);
- }
-
- private void AddImages(List list, RootObject obj, CancellationToken cancellationToken)
- {
- PopulateImages(list, obj.hdtvlogo, ImageType.Logo, 800, 310);
- PopulateImages(list, obj.hdclearart, ImageType.Art, 1000, 562);
- PopulateImages(list, obj.clearlogo, ImageType.Logo, 400, 155);
- PopulateImages(list, obj.clearart, ImageType.Art, 500, 281);
- PopulateImages(list, obj.showbackground, ImageType.Backdrop, 1920, 1080, true);
- PopulateImages(list, obj.seasonthumb, ImageType.Thumb, 500, 281);
- PopulateImages(list, obj.tvthumb, ImageType.Thumb, 500, 281);
- PopulateImages(list, obj.tvbanner, ImageType.Banner, 1000, 185);
- PopulateImages(list, obj.tvposter, ImageType.Primary, 1000, 1426);
- }
-
- private void PopulateImages(List list,
- List images,
- ImageType type,
- int width,
- int height,
- bool allowSeasonAll = false)
- {
- if (images == null)
- {
- return;
- }
-
- list.AddRange(images.Select(i =>
- {
- var url = i.url;
- var season = i.season;
-
- var isSeasonValid = string.IsNullOrEmpty(season) ||
- (allowSeasonAll && string.Equals(season, "all", StringComparison.OrdinalIgnoreCase));
-
- if (!string.IsNullOrEmpty(url) && isSeasonValid)
- {
- var likesString = i.likes;
-
- var info = new RemoteImageInfo
- {
- RatingType = RatingType.Likes,
- Type = type,
- Width = width,
- Height = height,
- ProviderName = Name,
- Url = url.Replace("http://", "https://", StringComparison.OrdinalIgnoreCase),
- Language = i.lang
- };
-
- if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Integer, _usCulture, out var likes))
- {
- info.CommunityRating = likes;
- }
-
- return info;
- }
-
- return null;
- }).Where(i => i != null));
- }
-
- public int Order => 1;
-
- public Task GetImageResponse(string url, CancellationToken cancellationToken)
- {
- return _httpClient.GetResponse(new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- Url = url
- });
- }
-
- ///
- /// Gets the series data path.
- ///
- /// The app paths.
- /// The series id.
- /// System.String.
- internal static string GetSeriesDataPath(IApplicationPaths appPaths, string seriesId)
- {
- var seriesDataPath = Path.Combine(GetSeriesDataPath(appPaths), seriesId);
-
- return seriesDataPath;
- }
-
- ///
- /// Gets the series data path.
- ///
- /// The app paths.
- /// System.String.
- internal static string GetSeriesDataPath(IApplicationPaths appPaths)
- {
- var dataPath = Path.Combine(appPaths.CachePath, "fanart-tv");
-
- return dataPath;
- }
-
- public string GetFanartJsonPath(string tvdbId)
- {
- var dataPath = GetSeriesDataPath(_config.ApplicationPaths, tvdbId);
- return Path.Combine(dataPath, "fanart.json");
- }
-
- private readonly SemaphoreSlim _ensureSemaphore = new SemaphoreSlim(1, 1);
- internal async Task EnsureSeriesJson(string tvdbId, CancellationToken cancellationToken)
- {
- var path = GetFanartJsonPath(tvdbId);
-
- // Only allow one thread in here at a time since every season will be calling this method, possibly concurrently
- await _ensureSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var fileInfo = _fileSystem.GetFileSystemInfo(path);
-
- if (fileInfo.Exists)
- {
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
- {
- return;
- }
- }
-
- await DownloadSeriesJson(tvdbId, cancellationToken).ConfigureAwait(false);
- }
- finally
- {
- _ensureSemaphore.Release();
- }
- }
-
- public FanartOptions GetFanartOptions()
- {
- return _config.GetConfiguration("fanart");
- }
-
- ///
- /// Downloads the series json.
- ///
- /// The TVDB identifier.
- /// The cancellation token.
- /// Task.
- internal async Task DownloadSeriesJson(string tvdbId, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var url = string.Format(FanArtBaseUrl, FanartArtistProvider.ApiKey, tvdbId);
-
- var clientKey = GetFanartOptions().UserApiKey;
- if (!string.IsNullOrWhiteSpace(clientKey))
- {
- url += "&client_key=" + clientKey;
- }
-
- var path = GetFanartJsonPath(tvdbId);
-
- Directory.CreateDirectory(Path.GetDirectoryName(path));
-
- try
- {
- using (var httpResponse = await _httpClient.SendAsync(new HttpRequestOptions
- {
- Url = url,
- CancellationToken = cancellationToken,
- BufferContent = true
-
- }, "GET").ConfigureAwait(false))
- {
- using (var response = httpResponse.Content)
- {
- using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
- {
- await response.CopyToAsync(fileStream).ConfigureAwait(false);
- }
- }
- }
- }
- catch (HttpException exception)
- {
- if (exception.StatusCode.HasValue && exception.StatusCode.Value == HttpStatusCode.NotFound)
- {
- // If the user has automatic updates enabled, save a dummy object to prevent repeated download attempts
- _json.SerializeToFile(new RootObject(), path);
-
- return;
- }
-
- throw;
- }
- }
-
- public class Image
- {
- public string id { get; set; }
- public string url { get; set; }
- public string lang { get; set; }
- public string likes { get; set; }
- public string season { get; set; }
- }
-
- public class RootObject
- {
- public string name { get; set; }
- public string thetvdb_id { get; set; }
- public List clearlogo { get; set; }
- public List hdtvlogo { get; set; }
- public List clearart { get; set; }
- public List showbackground { get; set; }
- public List tvthumb { get; set; }
- public List seasonposter { get; set; }
- public List seasonthumb { get; set; }
- public List hdclearart { get; set; }
- public List tvbanner { get; set; }
- public List characterart { get; set; }
- public List tvposter { get; set; }
- public List seasonbanner { get; set; }
- }
- }
-
- public class FanartConfigStore : IConfigurationFactory
- {
- public IEnumerable GetConfigurations()
- {
- return new ConfigurationStore[]
- {
- new ConfigurationStore
- {
- Key = "fanart",
- ConfigurationType = typeof(FanartOptions)
- }
- };
- }
- }
-}
From a6f9ceedd82fe34a8d1f088d91f7c217ee070aad Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Fri, 10 May 2019 20:37:42 +0200
Subject: [PATCH 022/214] Fix more warnings
---
Emby.Naming/Audio/AlbumParser.cs | 26 ++++---
Emby.Naming/Audio/MultiPartResult.cs | 2 +
Emby.Naming/AudioBook/AudioBookFileInfo.cs | 29 ++++++-
.../AudioBook/AudioBookFilePathParser.cs | 13 ++--
.../AudioBookFilePathParserResult.cs | 2 +
Emby.Naming/AudioBook/AudioBookInfo.cs | 51 ++++++------
.../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 | 77 ++++++++++---------
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 | 34 ++++----
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, 356 insertions(+), 254 deletions(-)
delete mode 100644 Emby.Naming/StringExtensions.cs
diff --git a/Emby.Naming/Audio/AlbumParser.cs b/Emby.Naming/Audio/AlbumParser.cs
index 7d029a9f49..e8d7655525 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;
+ }
- tmp = tmp.Trim().Split(' ').FirstOrDefault() ?? string.Empty;
+ var tmp = filename.Substring(prefix.Length);
- if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val))
- {
- result.IsMultiPart = true;
- break;
- }
+ tmp = tmp.Trim().Split(' ').FirstOrDefault() ?? string.Empty;
+
+ 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 b1fa6e5639..00e4a9eb2e 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 de66a54022..326ea05ef3 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 5909797947..ea7f06c8cb 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 3a8e3c31f2..f845e82435 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 f6e1d5be43..600d3f05da 100644
--- a/Emby.Naming/AudioBook/AudioBookInfo.cs
+++ b/Emby.Naming/AudioBook/AudioBookInfo.cs
@@ -7,33 +7,40 @@ namespace Emby.Naming.AudioBook
///
public class AudioBookInfo
{
- ///
- /// Gets or sets the name.
- ///
- /// The name.
- public string Name { get; set; }
- 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();
}
+
+ ///
+ /// 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; }
}
}
diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs
index 4e3ad7cac8..414ef11830 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 67ab62e801..4a2b516d0e 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 fd85bf76ad..136d8189dd 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 49cc9ee39b..a7b08bf793 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 2ef0208baf..88a9b46e63 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[] CleanDateTimes { 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 c448ec0ce6..699d893256 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 26c09aeb40..5512127a80 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 7c61922af3..0000000000
--- 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 e4709dfbb6..96fce04d76 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 c8aca7a6f3..de79b8bbaf 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 a8f81a3b85..812bc970ed 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 e1a48bfbc9..996edfc506 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 fccf9bdecb..2d7bcb6382 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 f1dcc50b89..e81b2bb349 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 eab27a4a5f..548dbd5d22 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 74807ef53f..25fa09c488 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 3459b689ac..9f70494d01 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 2df1e9aed1..584bdf2d2f 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 e6f830c583..333a48641e 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 e12494079c..40fc31e082 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 4893002c1b..b8ba42da4f 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 f86bcbdf04..b78244cb33 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 (options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+ if (path == null)
{
- result.IsStub = true;
+ return default(StubResult);
+ }
- path = Path.GetFileNameWithoutExtension(path);
+ var extension = Path.GetExtension(path);
- var token = (Path.GetExtension(path) ?? string.Empty).TrimStart('.');
+ if (!options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+ {
+ return default(StubResult);
+ }
- foreach (var rule in options.StubTypes)
+ var result = new StubResult()
+ {
+ IsStub = true
+ };
+
+ path = Path.GetFileNameWithoutExtension(path);
+ var token = Path.GetExtension(path).TrimStart('.');
+
+ 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 7f9509ca56..7a62e7b981 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 b46050085d..d765321504 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 6a29ada7ea..78f688ca8a 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 d96d0e7571..2e456bda2b 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 afedc30ef2..5fa0041e07 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 a673156517..02a25c4b5b 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 2c79624524..d4e17c42ae 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 1673e37776..4b5063ada6 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 ce1386e91a..3b9e48d978 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 5c95534ec1..1f873d7c6d 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 9346a2d254..81f145abff 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 fab584befc..d8ca121176 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 262121a325..0a60c8c7a2 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -14,12 +14,17 @@
+
+
+
+
+
From 08ac5b6ec3ce829c6691f1751e86480ace3d4b93 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Sat, 18 May 2019 13:43:28 +0200
Subject: [PATCH 023/214] Fix build
---
Emby.Naming/Emby.Naming.csproj | 4 ----
Emby.Naming/TV/EpisodePathParser.cs | 1 -
2 files changed, 5 deletions(-)
diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj
index 699d893256..6e05eb7952 100644
--- a/Emby.Naming/Emby.Naming.csproj
+++ b/Emby.Naming/Emby.Naming.csproj
@@ -21,10 +21,6 @@
true
-
- true
-
-
diff --git a/Emby.Naming/TV/EpisodePathParser.cs b/Emby.Naming/TV/EpisodePathParser.cs
index 812bc970ed..a98e8221a5 100644
--- a/Emby.Naming/TV/EpisodePathParser.cs
+++ b/Emby.Naming/TV/EpisodePathParser.cs
@@ -110,7 +110,6 @@ namespace Emby.Naming.TV
result.Success = true;
}
-
// TODO: Only consider success if date successfully parsed?
result.Success = true;
}
From 430483c7a1206e1e4fa6402f67c291931c55c4f0 Mon Sep 17 00:00:00 2001
From: dracocephalum
Date: Thu, 30 May 2019 06:09:49 +0000
Subject: [PATCH 024/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json
index effff5566e..293fc28a86 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} 下載字幕"
}
From 3431a85adf9a5f9fbcd2ce4551af384c050051e1 Mon Sep 17 00:00:00 2001
From: exveria1015
Date: Thu, 30 May 2019 22:28:51 +0000
Subject: [PATCH 025/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json
index 0967ef424b..53a43a1252 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}"
+}
From a2d9420139639ff339daf77c6658bf2f22502110 Mon Sep 17 00:00:00 2001
From: Hyunsu Nam
Date: Fri, 31 May 2019 01:10:13 +0000
Subject: [PATCH 026/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json
index 21808fd18a..5a7ba8ba7b 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}",
From 4adaeee0547bf67ebaebb200dcdd7b34af964051 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Julio=20Garc=C3=ADa?=
Date: Sat, 1 Jun 2019 11:29:04 +0000
Subject: [PATCH 027/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index 1850b8f255..f03184d5ba 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",
From 6b6776042c0c456c128734056e28765f1717ffc7 Mon Sep 17 00:00:00 2001
From: Juvenal Yescas
Date: Tue, 28 May 2019 14:56:25 +0000
Subject: [PATCH 028/214] 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/
---
.../Localization/Core/es-MX.json | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json
index 2285f2808a..0036329689 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",
From b768ad978efdb653022dc490055688d321f2bf34 Mon Sep 17 00:00:00 2001
From: dkanada
Date: Sun, 2 Jun 2019 21:49:12 -0700
Subject: [PATCH 029/214] split the new command to more than one line
---
Emby.Server.Implementations/IO/ManagedFileSystem.cs | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index f0bea0455a..7c2ea50e2a 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,
From c05b7c382af5c760e1e40e0bf0c7d7f200e5b7e0 Mon Sep 17 00:00:00 2001
From: Claus Vium
Date: Wed, 5 Jun 2019 20:59:06 +0200
Subject: [PATCH 030/214] Change UsCulture to InvariantCulture
---
.../Playback/Hls/BaseHlsService.cs | 19 ++++-----
.../Playback/Hls/DynamicHlsService.cs | 40 +++++++++----------
.../Playback/Hls/VideoHlsService.cs | 7 ++--
3 files changed, 34 insertions(+), 32 deletions(-)
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index 1acc42ea52..3c6d33e7e2 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
@@ -143,10 +144,10 @@ namespace MediaBrowser.Api.Playback.Hls
text = text.Replace("#EXTM3U", "#EXTM3U\n#EXT-X-PLAYLIST-TYPE:EVENT");
- var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(UsCulture);
+ var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(CultureInfo.InvariantCulture);
- text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength - 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase);
- //text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase);
+ text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength - 1).ToString(CultureInfo.InvariantCulture), newDuration, StringComparison.OrdinalIgnoreCase);
+ //text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(CultureInfo.InvariantCulture), newDuration, StringComparison.OrdinalIgnoreCase);
return text;
}
@@ -163,7 +164,7 @@ namespace MediaBrowser.Api.Playback.Hls
var paddedBitrate = Convert.ToInt32(bitrate * 1.15);
// Main stream
- builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + paddedBitrate.ToString(UsCulture));
+ builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + paddedBitrate.ToString(CultureInfo.InvariantCulture));
var playlistUrl = "hls/" + Path.GetFileName(firstPlaylist).Replace(".m3u8", "/stream.m3u8");
builder.AppendLine(playlistUrl);
@@ -231,7 +232,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
var itsOffsetMs = 0;
- var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture));
+ var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(CultureInfo.InvariantCulture));
var videoCodec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
@@ -240,7 +241,7 @@ namespace MediaBrowser.Api.Playback.Hls
var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions);
// If isEncoding is true we're actually starting ffmpeg
- var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0";
+ var startNumberParam = isEncoding ? GetStartNumber(state).ToString(CultureInfo.InvariantCulture) : "0";
var baseUrlParam = string.Empty;
@@ -272,7 +273,7 @@ namespace MediaBrowser.Api.Playback.Hls
EncodingHelper.GetMapArgs(state),
GetVideoArguments(state, encodingOptions),
GetAudioArguments(state, encodingOptions),
- state.SegmentLength.ToString(UsCulture),
+ state.SegmentLength.ToString(CultureInfo.InvariantCulture),
startNumberParam,
outputPath,
outputTsArg,
@@ -293,9 +294,9 @@ namespace MediaBrowser.Api.Playback.Hls
EncodingHelper.GetMapArgs(state),
GetVideoArguments(state, encodingOptions),
GetAudioArguments(state, encodingOptions),
- state.SegmentLength.ToString(UsCulture),
+ state.SegmentLength.ToString(CultureInfo.InvariantCulture),
startNumberParam,
- state.HlsListSize.ToString(UsCulture),
+ state.HlsListSize.ToString(CultureInfo.InvariantCulture),
baseUrlParam,
outputPath
).Trim();
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 45f003cae8..fdae59f569 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -177,7 +177,7 @@ namespace MediaBrowser.Api.Playback.Hls
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
- var requestedIndex = int.Parse(segmentId, NumberStyles.Integer, UsCulture);
+ var requestedIndex = int.Parse(segmentId, NumberStyles.Integer, CultureInfo.InvariantCulture);
var state = await GetState(request, cancellationToken).ConfigureAwait(false);
@@ -364,7 +364,7 @@ namespace MediaBrowser.Api.Playback.Hls
var indexString = Path.GetFileNameWithoutExtension(file.Name).Substring(playlistFilename.Length);
- return int.Parse(indexString, NumberStyles.Integer, UsCulture);
+ return int.Parse(indexString, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
private void DeleteLastFile(string playlistPath, string segmentExtension, int retryCount)
@@ -438,7 +438,7 @@ namespace MediaBrowser.Api.Playback.Hls
segmentId = segmentRequest.SegmentId;
}
- return int.Parse(segmentId, NumberStyles.Integer, UsCulture);
+ return int.Parse(segmentId, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
private string GetSegmentPath(StreamState state, string playlist, int index)
@@ -447,7 +447,7 @@ namespace MediaBrowser.Api.Playback.Hls
var filename = Path.GetFileNameWithoutExtension(playlist);
- return Path.Combine(folder, filename + index.ToString(UsCulture) + GetSegmentFileExtension(state.Request));
+ return Path.Combine(folder, filename + index.ToString(CultureInfo.InvariantCulture) + GetSegmentFileExtension(state.Request));
}
private async Task GetSegmentResult(StreamState state,
@@ -628,8 +628,8 @@ namespace MediaBrowser.Api.Playback.Hls
private string ReplaceBitrate(string url, int oldValue, int newValue)
{
return url.Replace(
- "videobitrate=" + oldValue.ToString(UsCulture),
- "videobitrate=" + newValue.ToString(UsCulture),
+ "videobitrate=" + oldValue.ToString(CultureInfo.InvariantCulture),
+ "videobitrate=" + newValue.ToString(CultureInfo.InvariantCulture),
StringComparison.OrdinalIgnoreCase);
}
@@ -648,8 +648,8 @@ namespace MediaBrowser.Api.Playback.Hls
var url = string.Format("{0}/Subtitles/{1}/subtitles.m3u8?SegmentLength={2}&api_key={3}",
state.Request.MediaSourceId,
- stream.Index.ToString(UsCulture),
- 30.ToString(UsCulture),
+ stream.Index.ToString(CultureInfo.InvariantCulture),
+ 30.ToString(CultureInfo.InvariantCulture),
AuthorizationContext.GetAuthorizationInfo(Request).Token);
var line = string.Format(format,
@@ -705,7 +705,7 @@ namespace MediaBrowser.Api.Playback.Hls
private void AppendPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string subtitleGroup)
{
- var header = "#EXT-X-STREAM-INF:BANDWIDTH=" + bitrate.ToString(UsCulture) + ",AVERAGE-BANDWIDTH=" + bitrate.ToString(UsCulture);
+ var header = "#EXT-X-STREAM-INF:BANDWIDTH=" + bitrate.ToString(CultureInfo.InvariantCulture) + ",AVERAGE-BANDWIDTH=" + bitrate.ToString(CultureInfo.InvariantCulture);
// tvos wants resolution, codecs, framerate
//if (state.TargetFramerate.HasValue)
@@ -770,7 +770,7 @@ namespace MediaBrowser.Api.Playback.Hls
builder.AppendLine("#EXTM3U");
builder.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");
builder.AppendLine("#EXT-X-VERSION:3");
- builder.AppendLine("#EXT-X-TARGETDURATION:" + Math.Ceiling(segmentLengths.Length > 0 ? segmentLengths.Max() : state.SegmentLength).ToString(UsCulture));
+ builder.AppendLine("#EXT-X-TARGETDURATION:" + Math.Ceiling(segmentLengths.Length > 0 ? segmentLengths.Max() : state.SegmentLength).ToString(CultureInfo.InvariantCulture));
builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
var queryStringIndex = Request.RawUrl.IndexOf('?');
@@ -785,12 +785,12 @@ namespace MediaBrowser.Api.Playback.Hls
foreach (var length in segmentLengths)
{
- builder.AppendLine("#EXTINF:" + length.ToString("0.0000", UsCulture) + ", nodesc");
+ builder.AppendLine("#EXTINF:" + length.ToString("0.0000", CultureInfo.InvariantCulture) + ", nodesc");
builder.AppendLine(string.Format("hls1/{0}/{1}{2}{3}",
name,
- index.ToString(UsCulture),
+ index.ToString(CultureInfo.InvariantCulture),
GetSegmentFileExtension(request),
queryString));
@@ -821,17 +821,17 @@ namespace MediaBrowser.Api.Playback.Hls
if (state.OutputAudioBitrate.HasValue)
{
- audioTranscodeParams.Add("-ab " + state.OutputAudioBitrate.Value.ToString(UsCulture));
+ audioTranscodeParams.Add("-ab " + state.OutputAudioBitrate.Value.ToString(CultureInfo.InvariantCulture));
}
if (state.OutputAudioChannels.HasValue)
{
- audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(UsCulture));
+ audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture));
}
if (state.OutputAudioSampleRate.HasValue)
{
- audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture));
+ audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture));
}
audioTranscodeParams.Add("-vn");
@@ -863,12 +863,12 @@ namespace MediaBrowser.Api.Playback.Hls
if (bitrate.HasValue)
{
- args += " -ab " + bitrate.Value.ToString(UsCulture);
+ args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
}
if (state.OutputAudioSampleRate.HasValue)
{
- args += " -ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture);
+ args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture);
}
args += " " + EncodingHelper.GetAudioFilterParam(state, encodingOptions, true);
@@ -905,7 +905,7 @@ namespace MediaBrowser.Api.Playback.Hls
else
{
var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
- state.SegmentLength.ToString(UsCulture));
+ state.SegmentLength.ToString(CultureInfo.InvariantCulture));
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
@@ -953,7 +953,7 @@ namespace MediaBrowser.Api.Playback.Hls
// If isEncoding is true we're actually starting ffmpeg
var startNumber = GetStartNumber(state);
- var startNumberParam = isEncoding ? startNumber.ToString(UsCulture) : "0";
+ var startNumberParam = isEncoding ? startNumber.ToString(CultureInfo.InvariantCulture) : "0";
var mapArgs = state.IsOutputVideo ? EncodingHelper.GetMapArgs(state) : string.Empty;
@@ -984,7 +984,7 @@ namespace MediaBrowser.Api.Playback.Hls
mapArgs,
GetVideoArguments(state, encodingOptions),
GetAudioArguments(state, encodingOptions),
- state.SegmentLength.ToString(UsCulture),
+ state.SegmentLength.ToString(CultureInfo.InvariantCulture),
startNumberParam,
outputPath,
outputTsArg,
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index eb1bbfb74b..3c715c5add 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -1,4 +1,5 @@
using System;
+using System.Globalization;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
@@ -55,12 +56,12 @@ namespace MediaBrowser.Api.Playback.Hls
if (bitrate.HasValue)
{
- args += " -ab " + bitrate.Value.ToString(UsCulture);
+ args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
}
if (state.OutputAudioSampleRate.HasValue)
{
- args += " -ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture);
+ args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture);
}
args += " " + EncodingHelper.GetAudioFilterParam(state, encodingOptions, true);
@@ -100,7 +101,7 @@ namespace MediaBrowser.Api.Playback.Hls
else
{
var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
- state.SegmentLength.ToString(UsCulture));
+ state.SegmentLength.ToString(CultureInfo.InvariantCulture));
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
From 7919dd81da5d17a8799fbc31de2235687c66a5fd Mon Sep 17 00:00:00 2001
From: pjeanjean
Date: Tue, 4 Jun 2019 18:35:14 +0200
Subject: [PATCH 031/214] Skip user permission checking for UserRootFolder
Fix #1436
UserRootFolders are used to represent virtual folders that exist outside
of libraries. As such, it doesn't make sense to check if a user has the
right to access their library (named `Media Folders`).
---
CONTRIBUTORS.md | 1 +
MediaBrowser.Api/UserLibrary/ItemsService.cs | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 81857e57c7..c3fcea1e21 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -24,6 +24,7 @@
- [Lynxy](https://github.com/Lynxy)
- [fasheng](https://github.com/fasheng)
- [ploughpuff](https://github.com/ploughpuff)
+ - [pjeanjean](https://github.com/pjeanjean)
# Emby Contributors
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index 3c7ad1d0a6..f1ae484922 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -224,7 +224,7 @@ namespace MediaBrowser.Api.UserLibrary
request.IncludeItemTypes = "Playlist";
}
- if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id))
+ if (!(item is UserRootFolder) && !user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id))
{
Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Name, item.Name);
return new QueryResult
From 855911333a76999ede37fca5b720b85143e01d3c Mon Sep 17 00:00:00 2001
From: "Joshua M. Boniface"
Date: Thu, 6 Jun 2019 22:45:37 -0400
Subject: [PATCH 032/214] Bump version for 10.3.4
---
Dockerfile | 2 +-
Dockerfile.arm | 2 +-
Dockerfile.arm64 | 2 +-
MediaBrowser.WebDashboard/jellyfin-web | 2 +-
SharedVersion.cs | 4 ++--
build.yaml | 2 +-
deployment/debian-package-x64/pkg-src/changelog | 6 ++++++
deployment/fedora-package-x64/pkg-src/jellyfin.spec | 4 +++-
8 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index fb3bb633fb..be01640f83 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -21,7 +21,7 @@ RUN apt-get update \
COPY --from=ffmpeg / /
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.3
+ARG JELLYFIN_WEB_VERSION=10.3.4
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
diff --git a/Dockerfile.arm b/Dockerfile.arm
index 2f43898fad..2b36682070 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -30,7 +30,7 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.3
+ARG JELLYFIN_WEB_VERSION=10.3.4
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index 5aa29edd56..76098a1d91 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -31,7 +31,7 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.3
+ARG JELLYFIN_WEB_VERSION=10.3.4
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
diff --git a/MediaBrowser.WebDashboard/jellyfin-web b/MediaBrowser.WebDashboard/jellyfin-web
index b0f7a9b67c..37636dae5c 160000
--- a/MediaBrowser.WebDashboard/jellyfin-web
+++ b/MediaBrowser.WebDashboard/jellyfin-web
@@ -1 +1 @@
-Subproject commit b0f7a9b67cc72de98dc357425e9d5c3894c7f377
+Subproject commit 37636dae5c6c0b0711dfc7612f843b864dd59469
diff --git a/SharedVersion.cs b/SharedVersion.cs
index b249520b40..3a8a0aef46 100644
--- a/SharedVersion.cs
+++ b/SharedVersion.cs
@@ -1,4 +1,4 @@
using System.Reflection;
-[assembly: AssemblyVersion("10.3.3")]
-[assembly: AssemblyFileVersion("10.3.3")]
+[assembly: AssemblyVersion("10.3.4")]
+[assembly: AssemblyFileVersion("10.3.4")]
diff --git a/build.yaml b/build.yaml
index ed459582a0..688ba4ca09 100644
--- a/build.yaml
+++ b/build.yaml
@@ -1,7 +1,7 @@
---
# We just wrap `build` so this is really it
name: "jellyfin"
-version: "10.3.3"
+version: "10.3.4"
packages:
- debian-package-x64
- debian-package-armhf
diff --git a/deployment/debian-package-x64/pkg-src/changelog b/deployment/debian-package-x64/pkg-src/changelog
index a47f7e8410..a6cab4cc2b 100644
--- a/deployment/debian-package-x64/pkg-src/changelog
+++ b/deployment/debian-package-x64/pkg-src/changelog
@@ -1,3 +1,9 @@
+jellyfin (10.3.4-1) unstable; urgency=medium
+
+ * New upstream version 10.3.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.4
+
+ -- Jellyfin Packaging Team Thu, 06 Jun 2019 22:45:31 -0400
+
jellyfin (10.3.3-1) unstable; urgency=medium
* New upstream version 10.3.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.3
diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
index e2e814b96a..5294ebfca9 100644
--- a/deployment/fedora-package-x64/pkg-src/jellyfin.spec
+++ b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
@@ -7,7 +7,7 @@
%endif
Name: jellyfin
-Version: 10.3.3
+Version: 10.3.4
Release: 1%{?dist}
Summary: The Free Software Media Browser
License: GPLv2
@@ -140,6 +140,8 @@ fi
%systemd_postun_with_restart jellyfin.service
%changelog
+* Thu Jun 06 2019 Jellyfin Packaging Team
+- New upstream version 10.3.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.4
* Fri May 17 2019 Jellyfin Packaging Team
- New upstream version 10.3.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.3
* Tue Apr 30 2019 Jellyfin Packaging Team
From 247a5e12ab85770faf2f59792f614cd4f727723f Mon Sep 17 00:00:00 2001
From: Andrew Rabert
Date: Fri, 7 Jun 2019 00:00:14 -0400
Subject: [PATCH 033/214] Docker - Update arm* Dockerfiles for latest multiarch
Relates to this change
https://github.com/multiarch/qemu-user-static/commit/7bdafb96ee59fde53d07a291f9446d2b8957d372
---
Dockerfile.arm | 8 ++------
Dockerfile.arm64 | 9 ++-------
2 files changed, 4 insertions(+), 13 deletions(-)
diff --git a/Dockerfile.arm b/Dockerfile.arm
index 2b36682070..972825f3ce 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -3,11 +3,6 @@
ARG DOTNET_VERSION=3.0
-FROM multiarch/qemu-user-static:x86_64-arm as qemu
-FROM alpine as qemu_extract
-COPY --from=qemu /usr/bin qemu-arm-static.tar.gz
-RUN tar -xzvf qemu-arm-static.tar.gz
-
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo
COPY . .
@@ -21,8 +16,9 @@ RUN bash -c "source deployment/common.build.sh && \
build_jellyfin Jellyfin.Server Release linux-arm /jellyfin"
+FROM multiarch/qemu-user-static:x86_64-arm as qemu
FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION}-stretch-slim-arm32v7
-COPY --from=qemu_extract qemu-arm-static /usr/bin
+COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
&& rm -rf /var/lib/apt/lists/* \
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index 76098a1d91..5ee0eaf5b9 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -3,12 +3,6 @@
ARG DOTNET_VERSION=3.0
-FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
-FROM alpine as qemu_extract
-COPY --from=qemu /usr/bin qemu-aarch64-static.tar.gz
-RUN tar -xzvf qemu-aarch64-static.tar.gz
-
-
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo
COPY . .
@@ -22,8 +16,9 @@ RUN bash -c "source deployment/common.build.sh && \
build_jellyfin Jellyfin.Server Release linux-arm64 /jellyfin"
+FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION}-stretch-slim-arm64v8
-COPY --from=qemu_extract qemu-aarch64-static /usr/bin
+COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
&& rm -rf /var/lib/apt/lists/* \
From defc5f1cf9b486357b379c610663e1bad48428ad Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Thu, 28 Mar 2019 22:43:32 +0100
Subject: [PATCH 034/214] Force LF line endings
---
.gitattributes | 2 +
.gitignore | 7 +-
Doxyfile | 5130 ++++++++---------
.../Cryptography/PasswordHash.cs | 306 +-
build | 0
bump_version | 0
deployment/centos-package-x64/clean.sh | 0
deployment/centos-package-x64/docker-build.sh | 0
deployment/centos-package-x64/package.sh | 0
deployment/centos-package-x64/pkg-src | 0
deployment/common.build.sh | 0
deployment/debian-package-armhf/clean.sh | 0
.../debian-package-armhf/docker-build.sh | 0
deployment/debian-package-armhf/package.sh | 0
deployment/debian-package-armhf/pkg-src | 0
deployment/debian-package-x64/clean.sh | 0
deployment/debian-package-x64/docker-build.sh | 0
deployment/debian-package-x64/package.sh | 0
deployment/docker/build.sh | 0
deployment/docker/package.sh | 0
deployment/fedora-package-x64/clean.sh | 0
.../fedora-package-x64/create_tarball.sh | 0
deployment/fedora-package-x64/docker-build.sh | 0
deployment/fedora-package-x64/package.sh | 0
deployment/linux-x64/build.sh | 0
deployment/linux-x64/clean.sh | 0
deployment/linux-x64/package.sh | 0
deployment/macos/build.sh | 0
deployment/macos/clean.sh | 0
deployment/macos/package.sh | 0
deployment/portable/build.sh | 0
deployment/portable/clean.sh | 0
deployment/portable/package.sh | 0
deployment/ubuntu-package-x64/clean.sh | 0
deployment/ubuntu-package-x64/docker-build.sh | 0
deployment/ubuntu-package-x64/package.sh | 0
deployment/ubuntu-package-x64/pkg-src | 0
deployment/win-x64/build.sh | 0
deployment/win-x64/clean.sh | 0
deployment/win-x64/package.sh | 0
deployment/win-x86/build.sh | 0
deployment/win-x86/clean.sh | 0
deployment/win-x86/package.sh | 0
43 files changed, 2721 insertions(+), 2724 deletions(-)
mode change 100755 => 100644 build
mode change 100755 => 100644 bump_version
mode change 100755 => 100644 deployment/centos-package-x64/clean.sh
mode change 100755 => 100644 deployment/centos-package-x64/docker-build.sh
mode change 100755 => 100644 deployment/centos-package-x64/package.sh
mode change 120000 => 100644 deployment/centos-package-x64/pkg-src
mode change 100755 => 100644 deployment/common.build.sh
mode change 100755 => 100644 deployment/debian-package-armhf/clean.sh
mode change 100755 => 100644 deployment/debian-package-armhf/docker-build.sh
mode change 100755 => 100644 deployment/debian-package-armhf/package.sh
mode change 120000 => 100644 deployment/debian-package-armhf/pkg-src
mode change 100755 => 100644 deployment/debian-package-x64/clean.sh
mode change 100755 => 100644 deployment/debian-package-x64/docker-build.sh
mode change 100755 => 100644 deployment/debian-package-x64/package.sh
mode change 100755 => 100644 deployment/docker/build.sh
mode change 100755 => 100644 deployment/docker/package.sh
mode change 100755 => 100644 deployment/fedora-package-x64/clean.sh
mode change 100755 => 100644 deployment/fedora-package-x64/create_tarball.sh
mode change 100755 => 100644 deployment/fedora-package-x64/docker-build.sh
mode change 100755 => 100644 deployment/fedora-package-x64/package.sh
mode change 100755 => 100644 deployment/linux-x64/build.sh
mode change 100755 => 100644 deployment/linux-x64/clean.sh
mode change 100755 => 100644 deployment/linux-x64/package.sh
mode change 100755 => 100644 deployment/macos/build.sh
mode change 100755 => 100644 deployment/macos/clean.sh
mode change 100755 => 100644 deployment/macos/package.sh
mode change 100755 => 100644 deployment/portable/build.sh
mode change 100755 => 100644 deployment/portable/clean.sh
mode change 100755 => 100644 deployment/portable/package.sh
mode change 100755 => 100644 deployment/ubuntu-package-x64/clean.sh
mode change 100755 => 100644 deployment/ubuntu-package-x64/docker-build.sh
mode change 100755 => 100644 deployment/ubuntu-package-x64/package.sh
mode change 120000 => 100644 deployment/ubuntu-package-x64/pkg-src
mode change 100755 => 100644 deployment/win-x64/build.sh
mode change 100755 => 100644 deployment/win-x64/clean.sh
mode change 100755 => 100644 deployment/win-x64/package.sh
mode change 100755 => 100644 deployment/win-x86/build.sh
mode change 100755 => 100644 deployment/win-x86/clean.sh
mode change 100755 => 100644 deployment/win-x86/package.sh
diff --git a/.gitattributes b/.gitattributes
index 0c0ab960a0..d78b0459d8 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,3 @@
+* text=auto eol=lf
+
CONTRIBUTORS.md merge=union
diff --git a/.gitignore b/.gitignore
index a1b7ac59c8..2ce41d76e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -239,11 +239,6 @@ pip-log.txt
##########
.idea/
-##########
-# Visual Studio Code
-##########
-.vscode/
-
#########################
# Build artifacts
#########################
@@ -268,4 +263,4 @@ jellyfin_version.ini
ci/
# Doxygen
-doc/
\ No newline at end of file
+doc/
diff --git a/Doxyfile b/Doxyfile
index 1628b4bde4..c859737cac 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -1,2565 +1,2565 @@
-# Doxyfile 1.8.15
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project.
-#
-# All text after a double hash (##) is considered a comment and is placed in
-# front of the TAG it is preceding.
-#
-# All text after a single hash (#) is considered a comment and will be ignored.
-# The format is:
-# TAG = value [value, ...]
-# For lists, items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (\" \").
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the configuration
-# file that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
-# The default value is: UTF-8.
-
-DOXYFILE_ENCODING = UTF-8
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
-# double-quotes, unless you are using Doxywizard) that should identify the
-# project for which the documentation is generated. This name is used in the
-# title of most generated pages and in a few other places.
-# The default value is: My Project.
-
-PROJECT_NAME = Jellyfin
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
-# could be handy for archiving the generated documentation or if some version
-# control system is used.
-
-PROJECT_NUMBER =
-
-# Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer a
-# quick idea about the purpose of the project. Keep the description short.
-
-PROJECT_BRIEF = "The Free Software Media System"
-
-# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
-# in the documentation. The maximum height of the logo should not exceed 55
-# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
-# the logo to the output directory.
-
-PROJECT_LOGO =
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
-# into which the generated documentation will be written. If a relative path is
-# entered, it will be relative to the location where doxygen was started. If
-# left blank the current directory will be used.
-
-OUTPUT_DIRECTORY = doc
-
-# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
-# directories (in 2 levels) under the output directory of each output format and
-# will distribute the generated files over these directories. Enabling this
-# option can be useful when feeding doxygen a huge amount of source files, where
-# putting all generated files in the same directory would otherwise causes
-# performance problems for the file system.
-# The default value is: NO.
-
-CREATE_SUBDIRS = NO
-
-# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
-# characters to appear in the names of generated files. If set to NO, non-ASCII
-# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
-# U+3044.
-# The default value is: NO.
-
-ALLOW_UNICODE_NAMES = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
-# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
-# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
-# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
-# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
-# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
-# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
-# Ukrainian and Vietnamese.
-# The default value is: English.
-
-OUTPUT_LANGUAGE = English
-
-# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all generated output in the proper direction.
-# Possible values are: None, LTR, RTL and Context.
-# The default value is: None.
-
-OUTPUT_TEXT_DIRECTION = None
-
-# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
-# descriptions after the members that are listed in the file and class
-# documentation (similar to Javadoc). Set to NO to disable this.
-# The default value is: YES.
-
-BRIEF_MEMBER_DESC = YES
-
-# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
-# description of a member or function before the detailed description
-#
-# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-# The default value is: YES.
-
-REPEAT_BRIEF = YES
-
-# This tag implements a quasi-intelligent brief description abbreviator that is
-# used to form the text in various listings. Each string in this list, if found
-# as the leading text of the brief description, will be stripped from the text
-# and the result, after processing the whole list, is used as the annotated
-# text. Otherwise, the brief description is used as-is. If left blank, the
-# following values are used ($name is automatically replaced with the name of
-# the entity):The $name class, The $name widget, The $name file, is, provides,
-# specifies, contains, represents, a, an and the.
-
-ABBREVIATE_BRIEF = "The $name class" \
- "The $name widget" \
- "The $name file" \
- is \
- provides \
- specifies \
- contains \
- represents \
- a \
- an \
- the
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# doxygen will generate a detailed section even if there is only a brief
-# description.
-# The default value is: NO.
-
-ALWAYS_DETAILED_SEC = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-# The default value is: NO.
-
-INLINE_INHERITED_MEMB = NO
-
-# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
-# before files name in the file list and in the header files. If set to NO the
-# shortest path that makes the file name unique will be used
-# The default value is: YES.
-
-FULL_PATH_NAMES = YES
-
-# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
-# Stripping is only done if one of the specified strings matches the left-hand
-# part of the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the path to
-# strip.
-#
-# Note that you can specify absolute paths here, but also relative paths, which
-# will be relative from the directory where doxygen is started.
-# This tag requires that the tag FULL_PATH_NAMES is set to YES.
-
-STRIP_FROM_PATH =
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
-# path mentioned in the documentation of a class, which tells the reader which
-# header file to include in order to use a class. If left blank only the name of
-# the header file containing the class definition is used. Otherwise one should
-# specify the list of include paths that are normally passed to the compiler
-# using the -I flag.
-
-STRIP_FROM_INC_PATH =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
-# less readable) file names. This can be useful is your file systems doesn't
-# support long names like on DOS, Mac, or CD-ROM.
-# The default value is: NO.
-
-SHORT_NAMES = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
-# first line (until the first dot) of a Javadoc-style comment as the brief
-# description. If set to NO, the Javadoc-style will behave just like regular Qt-
-# style comments (thus requiring an explicit @brief command for a brief
-# description.)
-# The default value is: NO.
-
-JAVADOC_AUTOBRIEF = NO
-
-# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
-# line (until the first dot) of a Qt-style comment as the brief description. If
-# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
-# requiring an explicit \brief command for a brief description.)
-# The default value is: NO.
-
-QT_AUTOBRIEF = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
-# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
-# a brief description. This used to be the default behavior. The new default is
-# to treat a multi-line C++ comment block as a detailed description. Set this
-# tag to YES if you prefer the old behavior instead.
-#
-# Note that setting this tag to YES also means that rational rose comments are
-# not recognized any more.
-# The default value is: NO.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
-# documentation from any documented member that it re-implements.
-# The default value is: YES.
-
-INHERIT_DOCS = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
-# page for each member. If set to NO, the documentation of a member will be part
-# of the file/class/namespace that contains it.
-# The default value is: NO.
-
-SEPARATE_MEMBER_PAGES = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
-# uses this value to replace tabs by spaces in code fragments.
-# Minimum value: 1, maximum value: 16, default value: 4.
-
-TAB_SIZE = 4
-
-# This tag can be used to specify a number of aliases that act as commands in
-# the documentation. An alias has the form:
-# name=value
-# For example adding
-# "sideeffect=@par Side Effects:\n"
-# will allow you to put the command \sideeffect (or @sideeffect) in the
-# documentation, which will result in a user-defined paragraph with heading
-# "Side Effects:". You can put \n's in the value part of an alias to insert
-# newlines (in the resulting output). You can put ^^ in the value part of an
-# alias to insert a newline as if a physical newline was in the original file.
-# When you need a literal { or } or , in the value part of an alias you have to
-# escape them by means of a backslash (\), this can lead to conflicts with the
-# commands \{ and \} for these it is advised to use the version @{ and @} or use
-# a double escape (\\{ and \\})
-
-ALIASES =
-
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding "class=itcl::class"
-# will allow you to use the command class in the itcl::class meaning.
-
-TCL_SUBST =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
-# only. Doxygen will then generate output that is more tailored for C. For
-# instance, some of the names that are used will be different. The list of all
-# members will be omitted, etc.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_FOR_C = NO
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
-# Python sources only. Doxygen will then generate output that is more tailored
-# for that language. For instance, namespaces will be presented as packages,
-# qualified scopes will look different, etc.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_JAVA = YES
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources. Doxygen will then generate output that is tailored for Fortran.
-# The default value is: NO.
-
-OPTIMIZE_FOR_FORTRAN = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for VHDL.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_VHDL = NO
-
-# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
-# sources only. Doxygen will then generate output that is more tailored for that
-# language. For instance, namespaces will be presented as modules, types will be
-# separated into more groups, etc.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_SLICE = NO
-
-# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given
-# extension. Doxygen has a built-in mapping, but you can override or extend it
-# using this tag. The format is ext=language, where ext is a file extension, and
-# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
-# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
-# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
-# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
-# tries to guess whether the code is fixed or free formatted code, this is the
-# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
-# .inc files as Fortran files (default is PHP), and .f files as C (default is
-# Fortran), use: inc=Fortran f=C.
-#
-# Note: For files without extension you can use no_extension as a placeholder.
-#
-# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
-# the files are not read by doxygen.
-
-EXTENSION_MAPPING =
-
-# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
-# according to the Markdown format, which allows for more readable
-# documentation. See https://daringfireball.net/projects/markdown/ for details.
-# The output of markdown processing is further processed by doxygen, so you can
-# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
-# case of backward compatibilities issues.
-# The default value is: YES.
-
-MARKDOWN_SUPPORT = YES
-
-# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
-# to that level are automatically included in the table of contents, even if
-# they do not have an id attribute.
-# Note: This feature currently applies only to Markdown headings.
-# Minimum value: 0, maximum value: 99, default value: 0.
-# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
-
-TOC_INCLUDE_HEADINGS = 0
-
-# When enabled doxygen tries to link words that correspond to documented
-# classes, or namespaces to their corresponding documentation. Such a link can
-# be prevented in individual cases by putting a % sign in front of the word or
-# globally by setting AUTOLINK_SUPPORT to NO.
-# The default value is: YES.
-
-AUTOLINK_SUPPORT = YES
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should set this
-# tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string);
-# versus func(std::string) {}). This also make the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-# The default value is: NO.
-
-BUILTIN_STL_SUPPORT = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-# The default value is: NO.
-
-CPP_CLI_SUPPORT = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
-# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
-# will parse them like normal C++ but will assume all classes use public instead
-# of private inheritance when no explicit protection keyword is present.
-# The default value is: NO.
-
-SIP_SUPPORT = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate
-# getter and setter methods for a property. Setting this option to YES will make
-# doxygen to replace the get and set methods by a property in the documentation.
-# This will only work if the methods are indeed getting or setting a simple
-# type. If this is not the case, or you want to show the methods anyway, you
-# should set this option to NO.
-# The default value is: YES.
-
-IDL_PROPERTY_SUPPORT = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-# The default value is: NO.
-
-DISTRIBUTE_GROUP_DOC = NO
-
-# If one adds a struct or class to a group and this option is enabled, then also
-# any nested class or struct is added to the same group. By default this option
-# is disabled and one has to add nested compounds explicitly via \ingroup.
-# The default value is: NO.
-
-GROUP_NESTED_COMPOUNDS = NO
-
-# Set the SUBGROUPING tag to YES to allow class member groups of the same type
-# (for instance a group of public functions) to be put as a subgroup of that
-# type (e.g. under the Public Functions section). Set it to NO to prevent
-# subgrouping. Alternatively, this can be done per class using the
-# \nosubgrouping command.
-# The default value is: YES.
-
-SUBGROUPING = YES
-
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
-# are shown inside the group in which they are included (e.g. using \ingroup)
-# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
-# and RTF).
-#
-# Note that this feature does not work in combination with
-# SEPARATE_MEMBER_PAGES.
-# The default value is: NO.
-
-INLINE_GROUPED_CLASSES = NO
-
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
-# with only public data fields or simple typedef fields will be shown inline in
-# the documentation of the scope in which they are defined (i.e. file,
-# namespace, or group documentation), provided this scope is documented. If set
-# to NO, structs, classes, and unions are shown on a separate page (for HTML and
-# Man pages) or section (for LaTeX and RTF).
-# The default value is: NO.
-
-INLINE_SIMPLE_STRUCTS = NO
-
-# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
-# enum is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically be
-# useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-# The default value is: NO.
-
-TYPEDEF_HIDES_STRUCT = NO
-
-# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
-# cache is used to resolve symbols given their name and scope. Since this can be
-# an expensive process and often the same symbol appears multiple times in the
-# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
-# doxygen will become slower. If the cache is too large, memory is wasted. The
-# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
-# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
-# symbols. At the end of a run doxygen will report the cache usage and suggest
-# the optimal cache size from a speed point of view.
-# Minimum value: 0, maximum value: 9, default value: 0.
-
-LOOKUP_CACHE_SIZE = 0
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
-# documentation are documented, even if no documentation was available. Private
-# class members and static file members will be hidden unless the
-# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
-# Note: This will also disable the warnings about undocumented members that are
-# normally produced when WARNINGS is set to YES.
-# The default value is: NO.
-
-EXTRACT_ALL = YES
-
-# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
-# be included in the documentation.
-# The default value is: NO.
-
-EXTRACT_PRIVATE = NO
-
-# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
-# scope will be included in the documentation.
-# The default value is: NO.
-
-EXTRACT_PACKAGE = NO
-
-# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
-# included in the documentation.
-# The default value is: NO.
-
-EXTRACT_STATIC = NO
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
-# locally in source files will be included in the documentation. If set to NO,
-# only classes defined in header files are included. Does not have any effect
-# for Java sources.
-# The default value is: YES.
-
-EXTRACT_LOCAL_CLASSES = YES
-
-# This flag is only useful for Objective-C code. If set to YES, local methods,
-# which are defined in the implementation section but not in the interface are
-# included in the documentation. If set to NO, only methods in the interface are
-# included.
-# The default value is: NO.
-
-EXTRACT_LOCAL_METHODS = NO
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base name of
-# the file that contains the anonymous namespace. By default anonymous namespace
-# are hidden.
-# The default value is: NO.
-
-EXTRACT_ANON_NSPACES = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
-# undocumented members inside documented classes or files. If set to NO these
-# members will be included in the various overviews, but no documentation
-# section is generated. This option has no effect if EXTRACT_ALL is enabled.
-# The default value is: NO.
-
-HIDE_UNDOC_MEMBERS = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy. If set
-# to NO, these classes will be included in the various overviews. This option
-# has no effect if EXTRACT_ALL is enabled.
-# The default value is: NO.
-
-HIDE_UNDOC_CLASSES = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
-# (class|struct|union) declarations. If set to NO, these declarations will be
-# included in the documentation.
-# The default value is: NO.
-
-HIDE_FRIEND_COMPOUNDS = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
-# documentation blocks found inside the body of a function. If set to NO, these
-# blocks will be appended to the function's detailed documentation block.
-# The default value is: NO.
-
-HIDE_IN_BODY_DOCS = NO
-
-# The INTERNAL_DOCS tag determines if documentation that is typed after a
-# \internal command is included. If the tag is set to NO then the documentation
-# will be excluded. Set it to YES to include the internal documentation.
-# The default value is: NO.
-
-INTERNAL_DOCS = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
-# names in lower-case letters. If set to YES, upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-# The default value is: system dependent.
-
-CASE_SENSE_NAMES = NO
-
-# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
-# their full class and namespace scopes in the documentation. If set to YES, the
-# scope will be hidden.
-# The default value is: NO.
-
-HIDE_SCOPE_NAMES = NO
-
-# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
-# append additional text to a page's title, such as Class Reference. If set to
-# YES the compound reference will be hidden.
-# The default value is: NO.
-
-HIDE_COMPOUND_REFERENCE= NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
-# the files that are included by a file in the documentation of that file.
-# The default value is: YES.
-
-SHOW_INCLUDE_FILES = YES
-
-# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
-# grouped member an include statement to the documentation, telling the reader
-# which file to include in order to use the member.
-# The default value is: NO.
-
-SHOW_GROUPED_MEMB_INC = NO
-
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
-# files with double quotes in the documentation rather than with sharp brackets.
-# The default value is: NO.
-
-FORCE_LOCAL_INCLUDES = NO
-
-# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
-# documentation for inline members.
-# The default value is: YES.
-
-INLINE_INFO = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
-# (detailed) documentation of file and class members alphabetically by member
-# name. If set to NO, the members will appear in declaration order.
-# The default value is: YES.
-
-SORT_MEMBER_DOCS = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
-# descriptions of file, namespace and class members alphabetically by member
-# name. If set to NO, the members will appear in declaration order. Note that
-# this will also influence the order of the classes in the class list.
-# The default value is: NO.
-
-SORT_BRIEF_DOCS = NO
-
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
-# (brief and detailed) documentation of class members so that constructors and
-# destructors are listed first. If set to NO the constructors will appear in the
-# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
-# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
-# member documentation.
-# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
-# detailed member documentation.
-# The default value is: NO.
-
-SORT_MEMBERS_CTORS_1ST = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
-# of group names into alphabetical order. If set to NO the group names will
-# appear in their defined order.
-# The default value is: NO.
-
-SORT_GROUP_NAMES = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
-# fully-qualified names, including namespaces. If set to NO, the class list will
-# be sorted only by class name, not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the alphabetical
-# list.
-# The default value is: NO.
-
-SORT_BY_SCOPE_NAME = NO
-
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
-# type resolution of all parameters of a function it will reject a match between
-# the prototype and the implementation of a member function even if there is
-# only one candidate or it is obvious which candidate to choose by doing a
-# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
-# accept a match between prototype and implementation in such cases.
-# The default value is: NO.
-
-STRICT_PROTO_MATCHING = NO
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
-# list. This list is created by putting \todo commands in the documentation.
-# The default value is: YES.
-
-GENERATE_TODOLIST = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
-# list. This list is created by putting \test commands in the documentation.
-# The default value is: YES.
-
-GENERATE_TESTLIST = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
-# list. This list is created by putting \bug commands in the documentation.
-# The default value is: YES.
-
-GENERATE_BUGLIST = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
-# the deprecated list. This list is created by putting \deprecated commands in
-# the documentation.
-# The default value is: YES.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional documentation
-# sections, marked by \if ... \endif and \cond
-# ... \endcond blocks.
-
-ENABLED_SECTIONS =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
-# initial value of a variable or macro / define can have for it to appear in the
-# documentation. If the initializer consists of more lines than specified here
-# it will be hidden. Use a value of 0 to hide initializers completely. The
-# appearance of the value of individual variables and macros / defines can be
-# controlled using \showinitializer or \hideinitializer command in the
-# documentation regardless of this setting.
-# Minimum value: 0, maximum value: 10000, default value: 30.
-
-MAX_INITIALIZER_LINES = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
-# the bottom of the documentation of classes and structs. If set to YES, the
-# list will mention the files that were used to generate the documentation.
-# The default value is: YES.
-
-SHOW_USED_FILES = YES
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
-# will remove the Files entry from the Quick Index and from the Folder Tree View
-# (if specified).
-# The default value is: YES.
-
-SHOW_FILES = YES
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
-# page. This will remove the Namespaces entry from the Quick Index and from the
-# Folder Tree View (if specified).
-# The default value is: YES.
-
-SHOW_NAMESPACES = YES
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command command input-file, where command is the value of the
-# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
-# by doxygen. Whatever the program writes to standard output is used as the file
-# version. For an example see the documentation.
-
-FILE_VERSION_FILTER =
-
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
-# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. To create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option. You can
-# optionally specify a file name after the option, if omitted DoxygenLayout.xml
-# will be used as the name of the layout file.
-#
-# Note that if you run doxygen from a directory containing a file called
-# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
-# tag is left empty.
-
-LAYOUT_FILE =
-
-# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
-# the reference definitions. This must be a list of .bib files. The .bib
-# extension is automatically appended if omitted. This requires the bibtex tool
-# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
-# For LaTeX the style of the bibliography can be controlled using
-# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
-# search path. See also \cite for info how to create references.
-
-CITE_BIB_FILES =
-
-#---------------------------------------------------------------------------
-# Configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated to
-# standard output by doxygen. If QUIET is set to YES this implies that the
-# messages are off.
-# The default value is: NO.
-
-QUIET = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
-# this implies that the warnings are on.
-#
-# Tip: Turn warnings on while writing the documentation.
-# The default value is: YES.
-
-WARNINGS = YES
-
-# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
-# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
-# will automatically be disabled.
-# The default value is: YES.
-
-WARN_IF_UNDOCUMENTED = YES
-
-# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some parameters
-# in a documented function, or documenting parameters that don't exist or using
-# markup commands wrongly.
-# The default value is: YES.
-
-WARN_IF_DOC_ERROR = YES
-
-# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
-# are documented, but have no documentation for their parameters or return
-# value. If set to NO, doxygen will only warn about wrong or incomplete
-# parameter documentation, but not about the absence of documentation. If
-# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
-# The default value is: NO.
-
-WARN_NO_PARAMDOC = NO
-
-# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
-# a warning is encountered.
-# The default value is: NO.
-
-WARN_AS_ERROR = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that doxygen
-# can produce. The string should contain the $file, $line, and $text tags, which
-# will be replaced by the file and line number from which the warning originated
-# and the warning text. Optionally the format may contain $version, which will
-# be replaced by the version of the file (if it could be obtained via
-# FILE_VERSION_FILTER)
-# The default value is: $file:$line: $text.
-
-WARN_FORMAT = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning and error
-# messages should be written. If left blank the output is written to standard
-# error (stderr).
-
-WARN_LOGFILE =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag is used to specify the files and/or directories that contain
-# documented source files. You may enter file names like myfile.cpp or
-# directories like /usr/src/myproject. Separate the files or directories with
-# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
-# Note: If this tag is empty the current directory is searched.
-
-INPUT =
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
-# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
-# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
-# possible encodings.
-# The default value is: UTF-8.
-
-INPUT_ENCODING = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
-# *.h) to filter out the source-files in the directories.
-#
-# Note that for custom extensions or not directly supported extensions you also
-# need to set EXTENSION_MAPPING for the extension otherwise the files are not
-# read by doxygen.
-#
-# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
-# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
-# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
-# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
-# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
-
-FILE_PATTERNS = *.c \
- *.cc \
- *.cxx \
- *.cpp \
- *.c++ \
- *.java \
- *.ii \
- *.ixx \
- *.ipp \
- *.i++ \
- *.inl \
- *.idl \
- *.ddl \
- *.odl \
- *.h \
- *.hh \
- *.hxx \
- *.hpp \
- *.h++ \
- *.cs \
- *.d \
- *.php \
- *.php4 \
- *.php5 \
- *.phtml \
- *.inc \
- *.m \
- *.markdown \
- *.md \
- *.mm \
- *.dox \
- *.py \
- *.pyw \
- *.f90 \
- *.f95 \
- *.f03 \
- *.f08 \
- *.f \
- *.for \
- *.tcl \
- *.vhd \
- *.vhdl \
- *.ucf \
- *.qsf \
- *.ice
-
-# The RECURSIVE tag can be used to specify whether or not subdirectories should
-# be searched for input files as well.
-# The default value is: NO.
-
-RECURSIVE = YES
-
-# The EXCLUDE tag can be used to specify files and/or directories that should be
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-#
-# Note that relative paths are relative to the directory from which doxygen is
-# run.
-
-EXCLUDE =
-
-# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
-# directories that are symbolic links (a Unix file system feature) are excluded
-# from the input.
-# The default value is: NO.
-
-EXCLUDE_SYMLINKS = NO
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories.
-#
-# Note that the wildcards are matched against the file with absolute path, so to
-# exclude all test directories for example use the pattern */test/*
-
-EXCLUDE_PATTERNS =
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-#
-# Note that the wildcards are matched against the file with absolute path, so to
-# exclude all test directories use the pattern */test/*
-
-EXCLUDE_SYMBOLS =
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or directories
-# that contain example code fragments that are included (see the \include
-# command).
-
-EXAMPLE_PATH =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
-# *.h) to filter out the source-files in the directories. If left blank all
-# files are included.
-
-EXAMPLE_PATTERNS = *
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude commands
-# irrespective of the value of the RECURSIVE tag.
-# The default value is: NO.
-
-EXAMPLE_RECURSIVE = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or directories
-# that contain images that are to be included in the documentation (see the
-# \image command).
-
-IMAGE_PATH =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command:
-#
-#
-#
-# where is the value of the INPUT_FILTER tag, and is the
-# name of an input file. Doxygen will then use the output that the filter
-# program writes to standard output. If FILTER_PATTERNS is specified, this tag
-# will be ignored.
-#
-# Note that the filter must not add or remove lines; it is applied before the
-# code is scanned, but not when the output code is generated. If lines are added
-# or removed, the anchors will not be placed correctly.
-#
-# Note that for custom extensions or not directly supported extensions you also
-# need to set EXTENSION_MAPPING for the extension otherwise the files are not
-# properly processed by doxygen.
-
-INPUT_FILTER =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis. Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match. The filters are a list of the form: pattern=filter
-# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
-# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
-# patterns match the file name, INPUT_FILTER is applied.
-#
-# Note that for custom extensions or not directly supported extensions you also
-# need to set EXTENSION_MAPPING for the extension otherwise the files are not
-# properly processed by doxygen.
-
-FILTER_PATTERNS =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will also be used to filter the input files that are used for
-# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
-# The default value is: NO.
-
-FILTER_SOURCE_FILES = NO
-
-# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
-# it is also possible to disable source filtering for a specific pattern using
-# *.ext= (so without naming a filter).
-# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
-
-FILTER_SOURCE_PATTERNS =
-
-# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
-# is part of the input, its contents will be placed on the main page
-# (index.html). This can be useful if you have a project on for instance GitHub
-# and want to reuse the introduction page also for the doxygen output.
-
-USE_MDFILE_AS_MAINPAGE = ./README.md
-
-#---------------------------------------------------------------------------
-# Configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
-# generated. Documented entities will be cross-referenced with these sources.
-#
-# Note: To get rid of all source code in the generated output, make sure that
-# also VERBATIM_HEADERS is set to NO.
-# The default value is: NO.
-
-SOURCE_BROWSER = YES
-
-# Setting the INLINE_SOURCES tag to YES will include the body of functions,
-# classes and enums directly into the documentation.
-# The default value is: NO.
-
-INLINE_SOURCES = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
-# special comment blocks from generated source code fragments. Normal C, C++ and
-# Fortran comments will always remain visible.
-# The default value is: YES.
-
-STRIP_CODE_COMMENTS = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
-# entity all documented functions referencing it will be listed.
-# The default value is: NO.
-
-REFERENCED_BY_RELATION = NO
-
-# If the REFERENCES_RELATION tag is set to YES then for each documented function
-# all documented entities called/used by that function will be listed.
-# The default value is: NO.
-
-REFERENCES_RELATION = NO
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
-# to YES then the hyperlinks from functions in REFERENCES_RELATION and
-# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
-# link to the documentation.
-# The default value is: YES.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
-# source code will show a tooltip with additional information such as prototype,
-# brief description and links to the definition and documentation. Since this
-# will make the HTML file larger and loading of large files a bit slower, you
-# can opt to disable this feature.
-# The default value is: YES.
-# This tag requires that the tag SOURCE_BROWSER is set to YES.
-
-SOURCE_TOOLTIPS = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code will
-# point to the HTML generated by the htags(1) tool instead of doxygen built-in
-# source browser. The htags tool is part of GNU's global source tagging system
-# (see https://www.gnu.org/software/global/global.html). You will need version
-# 4.8.6 or higher.
-#
-# To use it do the following:
-# - Install the latest version of global
-# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
-# - Make sure the INPUT points to the root of the source tree
-# - Run doxygen as normal
-#
-# Doxygen will invoke htags (and that will in turn invoke gtags), so these
-# tools must be available from the command line (i.e. in the search path).
-#
-# The result: instead of the source browser generated by doxygen, the links to
-# source code will now point to the output of htags.
-# The default value is: NO.
-# This tag requires that the tag SOURCE_BROWSER is set to YES.
-
-USE_HTAGS = NO
-
-# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
-# verbatim copy of the header file for each class for which an include is
-# specified. Set to NO to disable this.
-# See also: Section \class.
-# The default value is: YES.
-
-VERBATIM_HEADERS = YES
-
-# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
-# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
-# cost of reduced performance. This can be particularly helpful with template
-# rich C++ code for which doxygen's built-in parser lacks the necessary type
-# information.
-# Note: The availability of this option depends on whether or not doxygen was
-# generated with the -Duse_libclang=ON option for CMake.
-# The default value is: NO.
-
-CLANG_ASSISTED_PARSING = NO
-
-# If clang assisted parsing is enabled you can provide the compiler with command
-# line options that you would normally use when invoking the compiler. Note that
-# the include paths will already be set by doxygen for the files and directories
-# specified with INPUT and INCLUDE_PATH.
-# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
-
-CLANG_OPTIONS =
-
-# If clang assisted parsing is enabled you can provide the clang parser with the
-# path to the compilation database (see:
-# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files
-# were built. This is equivalent to specifying the "-p" option to a clang tool,
-# such as clang-check. These options will then be passed to the parser.
-# Note: The availability of this option depends on whether or not doxygen was
-# generated with the -Duse_libclang=ON option for CMake.
-
-CLANG_DATABASE_PATH =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
-# compounds will be generated. Enable this if the project contains a lot of
-# classes, structs, unions or interfaces.
-# The default value is: YES.
-
-ALPHABETICAL_INDEX = YES
-
-# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
-# which the alphabetical index list will be split.
-# Minimum value: 1, maximum value: 20, default value: 5.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-COLS_IN_ALPHA_INDEX = 5
-
-# In case all classes in a project start with a common prefix, all classes will
-# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
-# can be used to specify a prefix (or a list of prefixes) that should be ignored
-# while generating the index headers.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-IGNORE_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
-# The default value is: YES.
-
-GENERATE_HTML = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it.
-# The default directory is: html.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_OUTPUT = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
-# generated HTML page (for example: .htm, .php, .asp).
-# The default value is: .html.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_FILE_EXTENSION = .html
-
-# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
-# each generated HTML page. If the tag is left blank doxygen will generate a
-# standard header.
-#
-# To get valid HTML the header file that includes any scripts and style sheets
-# that doxygen needs, which is dependent on the configuration options used (e.g.
-# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
-# default header using
-# doxygen -w html new_header.html new_footer.html new_stylesheet.css
-# YourConfigFile
-# and then modify the file new_header.html. See also section "Doxygen usage"
-# for information on how to generate the default header that doxygen normally
-# uses.
-# Note: The header is subject to change so you typically have to regenerate the
-# default header when upgrading to a newer version of doxygen. For a description
-# of the possible markers and block names see the documentation.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_HEADER =
-
-# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
-# generated HTML page. If the tag is left blank doxygen will generate a standard
-# footer. See HTML_HEADER for more information on how to generate a default
-# footer and what special commands can be used inside the footer. See also
-# section "Doxygen usage" for information on how to generate the default footer
-# that doxygen normally uses.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_FOOTER =
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
-# sheet that is used by each HTML page. It can be used to fine-tune the look of
-# the HTML output. If left blank doxygen will generate a default style sheet.
-# See also section "Doxygen usage" for information on how to generate the style
-# sheet that doxygen normally uses.
-# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
-# it is more robust and this tag (HTML_STYLESHEET) will in the future become
-# obsolete.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_STYLESHEET =
-
-# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
-# cascading style sheets that are included after the standard style sheets
-# created by doxygen. Using this option one can overrule certain style aspects.
-# This is preferred over using HTML_STYLESHEET since it does not replace the
-# standard style sheet and is therefore more robust against future updates.
-# Doxygen will copy the style sheet files to the output directory.
-# Note: The order of the extra style sheet files is of importance (e.g. the last
-# style sheet in the list overrules the setting of the previous ones in the
-# list). For an example see the documentation.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_EXTRA_STYLESHEET =
-
-# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the HTML output directory. Note
-# that these files will be copied to the base HTML output directory. Use the
-# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
-# files will be copied as-is; there are no commands or markers available.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_EXTRA_FILES =
-
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
-# will adjust the colors in the style sheet and background images according to
-# this color. Hue is specified as an angle on a colorwheel, see
-# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
-# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
-# purple, and 360 is red again.
-# Minimum value: 0, maximum value: 359, default value: 220.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_HUE = 195
-
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
-# in the HTML output. For a value of 0 the output will use grayscales only. A
-# value of 255 will produce the most vivid colors.
-# Minimum value: 0, maximum value: 255, default value: 100.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_SAT = 100
-
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
-# luminance component of the colors in the HTML output. Values below 100
-# gradually make the output lighter, whereas values above 100 make the output
-# darker. The value divided by 100 is the actual gamma applied, so 80 represents
-# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
-# change the gamma.
-# Minimum value: 40, maximum value: 240, default value: 80.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_GAMMA = 86
-
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting this
-# to YES can help to show when doxygen was last run and thus if the
-# documentation is up to date.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_TIMESTAMP = NO
-
-# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
-# documentation will contain a main index with vertical navigation menus that
-# are dynamically created via Javascript. If disabled, the navigation index will
-# consists of multiple levels of tabs that are statically embedded in every HTML
-# page. Disable this option to support browsers that do not have Javascript,
-# like the Qt help browser.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_DYNAMIC_MENUS = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_DYNAMIC_SECTIONS = NO
-
-# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
-# shown in the various tree structured indices initially; the user can expand
-# and collapse entries dynamically later on. Doxygen will expand the tree to
-# such a level that at most the specified number of entries are visible (unless
-# a fully collapsed tree already exceeds this amount). So setting the number of
-# entries 1 will produce a full collapsed tree by default. 0 is a special value
-# representing an infinite number of entries and will result in a full expanded
-# tree by default.
-# Minimum value: 0, maximum value: 9999, default value: 100.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_INDEX_NUM_ENTRIES = 100
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files will be
-# generated that can be used as input for Apple's Xcode 3 integrated development
-# environment (see: https://developer.apple.com/xcode/), introduced with OSX
-# 10.5 (Leopard). To create a documentation set, doxygen will generate a
-# Makefile in the HTML output directory. Running make will produce the docset in
-# that directory and running make install will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
-# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
-# genXcode/_index.html for more information.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_DOCSET = NO
-
-# This tag determines the name of the docset feed. A documentation feed provides
-# an umbrella under which multiple documentation sets from a single provider
-# (such as a company or product suite) can be grouped.
-# The default value is: Doxygen generated docs.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_FEEDNAME = "Doxygen generated docs"
-
-# This tag specifies a string that should uniquely identify the documentation
-# set bundle. This should be a reverse domain-name style string, e.g.
-# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_BUNDLE_ID = org.doxygen.Project
-
-# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
-# the documentation publisher. This should be a reverse domain-name style
-# string, e.g. com.mycompany.MyDocSet.documentation.
-# The default value is: org.doxygen.Publisher.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_PUBLISHER_ID = org.doxygen.Publisher
-
-# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
-# The default value is: Publisher.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_PUBLISHER_NAME = Publisher
-
-# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
-# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
-# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
-# Windows.
-#
-# The HTML Help Workshop contains a compiler that can convert all HTML output
-# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
-# files are now used as the Windows 98 help format, and will replace the old
-# Windows help format (.hlp) on all Windows platforms in the future. Compressed
-# HTML files also contain an index, a table of contents, and you can search for
-# words in the documentation. The HTML workshop also contains a viewer for
-# compressed HTML files.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_HTMLHELP = NO
-
-# The CHM_FILE tag can be used to specify the file name of the resulting .chm
-# file. You can add a path in front of the file if the result should not be
-# written to the html output directory.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-CHM_FILE =
-
-# The HHC_LOCATION tag can be used to specify the location (absolute path
-# including file name) of the HTML help compiler (hhc.exe). If non-empty,
-# doxygen will try to run the HTML help compiler on the generated index.hhp.
-# The file has to be specified with full path.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-HHC_LOCATION =
-
-# The GENERATE_CHI flag controls if a separate .chi index file is generated
-# (YES) or that it should be included in the master .chm file (NO).
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-GENERATE_CHI = NO
-
-# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
-# and project file content.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-CHM_INDEX_ENCODING =
-
-# The BINARY_TOC flag controls whether a binary table of contents is generated
-# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
-# enables the Previous and Next buttons.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-BINARY_TOC = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members to
-# the table of contents of the HTML help documentation and to the tree view.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-TOC_EXPAND = NO
-
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
-# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
-# (.qch) of the generated HTML documentation.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_QHP = NO
-
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
-# the file name of the resulting .qch file. The path specified is relative to
-# the HTML output folder.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QCH_FILE =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
-# Project output. For more information please see Qt Help Project / Namespace
-# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_NAMESPACE = org.doxygen.Project
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
-# Help Project output. For more information please see Qt Help Project / Virtual
-# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
-# folders).
-# The default value is: doc.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_VIRTUAL_FOLDER = doc
-
-# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
-# filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
-# filters).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_CUST_FILTER_NAME =
-
-# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
-# filters).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_CUST_FILTER_ATTRS =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's filter section matches. Qt Help Project / Filter Attributes (see:
-# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_SECT_FILTER_ATTRS =
-
-# The QHG_LOCATION tag can be used to specify the location of Qt's
-# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
-# generated .qhp file.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHG_LOCATION =
-
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
-# generated, together with the HTML files, they form an Eclipse help plugin. To
-# install this plugin and make it available under the help contents menu in
-# Eclipse, the contents of the directory containing the HTML and XML files needs
-# to be copied into the plugins directory of eclipse. The name of the directory
-# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
-# After copying Eclipse needs to be restarted before the help appears.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_ECLIPSEHELP = NO
-
-# A unique identifier for the Eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have this
-# name. Each documentation set should have its own identifier.
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
-
-ECLIPSE_DOC_ID = org.doxygen.Project
-
-# If you want full control over the layout of the generated HTML pages it might
-# be necessary to disable the index and replace it with your own. The
-# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
-# of each HTML page. A value of NO enables the index and the value YES disables
-# it. Since the tabs in the index contain the same information as the navigation
-# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-DISABLE_INDEX = NO
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information. If the tag
-# value is set to YES, a side panel will be generated containing a tree-like
-# index structure (just like the one that is generated for HTML Help). For this
-# to work a browser that supports JavaScript, DHTML, CSS and frames is required
-# (i.e. any modern browser). Windows users are probably better off using the
-# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
-# further fine-tune the look of the index. As an example, the default style
-# sheet generated by doxygen has an example that shows how to put an image at
-# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
-# the same information as the tab index, you could consider setting
-# DISABLE_INDEX to YES when enabling this option.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_TREEVIEW = YES
-
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
-# doxygen will group on one line in the generated HTML documentation.
-#
-# Note that a value of 0 will completely suppress the enum values from appearing
-# in the overview section.
-# Minimum value: 0, maximum value: 20, default value: 4.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-ENUM_VALUES_PER_LINE = 4
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
-# to set the initial width (in pixels) of the frame in which the tree is shown.
-# Minimum value: 0, maximum value: 1500, default value: 250.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-TREEVIEW_WIDTH = 250
-
-# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
-# external symbols imported via tag files in a separate window.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-EXT_LINKS_IN_WINDOW = NO
-
-# Use this tag to change the font size of LaTeX formulas included as images in
-# the HTML documentation. When you change the font size after a successful
-# doxygen run you need to manually remove any form_*.png images from the HTML
-# output directory to force them to be regenerated.
-# Minimum value: 8, maximum value: 50, default value: 10.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-FORMULA_FONTSIZE = 10
-
-# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are not
-# supported properly for IE 6.0, but are supported on all modern browsers.
-#
-# Note that when changing this option you need to delete any form_*.png files in
-# the HTML output directory before the changes have effect.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-FORMULA_TRANSPARENT = YES
-
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
-# https://www.mathjax.org) which uses client side Javascript for the rendering
-# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
-# installed or if you want to formulas look prettier in the HTML output. When
-# enabled you may also need to install MathJax separately and configure the path
-# to it using the MATHJAX_RELPATH option.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-USE_MATHJAX = NO
-
-# When MathJax is enabled you can set the default output format to be used for
-# the MathJax output. See the MathJax site (see:
-# http://docs.mathjax.org/en/latest/output.html) for more details.
-# Possible values are: HTML-CSS (which is slower, but has the best
-# compatibility), NativeMML (i.e. MathML) and SVG.
-# The default value is: HTML-CSS.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_FORMAT = HTML-CSS
-
-# When MathJax is enabled you need to specify the location relative to the HTML
-# output directory using the MATHJAX_RELPATH option. The destination directory
-# should contain the MathJax.js script. For instance, if the mathjax directory
-# is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
-# Content Delivery Network so you can quickly see the result without installing
-# MathJax. However, it is strongly recommended to install a local copy of
-# MathJax from https://www.mathjax.org before deployment.
-# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/
-
-# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
-# extension names that should be enabled during MathJax rendering. For example
-# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_EXTENSIONS =
-
-# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
-# of code that will be used on startup of the MathJax code. See the MathJax site
-# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
-# example see the documentation.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_CODEFILE =
-
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
-# the HTML output. The underlying search engine uses javascript and DHTML and
-# should work on any modern browser. Note that when using HTML help
-# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
-# there is already a search function so this one should typically be disabled.
-# For large projects the javascript based search engine can be slow, then
-# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
-# search using the keyboard; to jump to the search box use + S
-# (what the is depends on the OS and browser, but it is typically
-# , /
@@ -52,7 +51,7 @@
-
+
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 81f145abff..641b3f1827 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 e4757543ea..c0f92ac4a4 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 cfbb85ea6b..5941ed4369 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 0a60c8c7a2..1249a60c05 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -25,10 +25,14 @@
+
+
+
+
From 067200be83ff220c0aae501d138b0ab284370b60 Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Fri, 8 Mar 2019 20:17:17 +0100
Subject: [PATCH 054/214] 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 | 438 ++++++------------
MediaBrowser.Common/Net/HttpRequestOptions.cs | 1 -
MediaBrowser.Common/Net/IHttpClient.cs | 11 +
4 files changed, 164 insertions(+), 304 deletions(-)
delete mode 100644 Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs
deleted file mode 100644
index f747b01b93..0000000000
--- 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 1bebdd1637..581d6bafd1 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(request, options);
+
+ if (options.EnableHttpCompression)
{
- AddRequestHeaders(httpWebRequest, options);
-
- 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;
- }
-
- httpWebRequest.KeepAlive = options.EnableKeepAlive;
-
- if (!string.IsNullOrEmpty(options.Host))
- {
- httpWebRequest.Host = options.Host;
- }
-
- if (!string.IsNullOrEmpty(options.Referer))
- {
- httpWebRequest.Referer = options.Referer;
+ request.Headers.Add(HeaderNames.AcceptEncoding, "deflate");
}
}
- request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
+ if (options.EnableKeepAlive)
+ {
+ request.Headers.Add(HeaderNames.Connection, "Keep-Alive");
+ }
- request.Method = method;
- request.Timeout = options.TimeoutMs;
+ 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))
{
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))
+ if (string.Equals(header.Key, HeaderNames.UserAgent, StringComparison.OrdinalIgnoreCase))
{
- request.Accept = header.Value;
- }
- else 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);
+ var client = GetHttpClient(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 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 response = await client.HttpClient.SendAsync(httpWebRequest).ConfigureAwait(false);
- var httpResponse = (HttpWebResponse)response;
-
- 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();
+ var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ _logger.LogError(msg);
- _logger.LogError(msg);
- }
- }
- }
- }
- catch
- {
-
- }
- }
-
- 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 bea178517b..874383fedf 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 5aaf7e0be6..d332ab2000 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.
///
From 0a5550b13d3a32ae7158dafb38eca6ed94e17d60 Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Fri, 8 Mar 2019 20:32:14 +0100
Subject: [PATCH 055/214] Remove more unused stuff
---
.../ApplicationHost.cs | 11 +++++-----
.../HttpClientManager/HttpClientManager.cs | 18 ----------------
.../LiveTv/Listings/SchedulesDirect.cs | 10 +--------
.../TunerHosts/HdHomerun/HdHomerunHost.cs | 2 --
.../LiveTv/TunerHosts/SharedHttpStream.cs | 6 ------
MediaBrowser.Common/Net/HttpRequestOptions.cs | 21 +------------------
6 files changed, 8 insertions(+), 60 deletions(-)
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 5d5a63a635..02d661d15a 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 581d6bafd1..b82d55d0e6 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 4137760d07..f3f7477180 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 24b100eddc..761275f8f3 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 d74cf3be2d..e8b34da0ca 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 874383fedf..38e0ff0f55 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";
}
}
From 7eb94e967471065009d82985129c3d722b386e1f Mon Sep 17 00:00:00 2001
From: Claus Vium
Date: Wed, 27 Mar 2019 16:34:56 +0100
Subject: [PATCH 056/214] Update MediaBrowser.Common/Net/IHttpClient.cs
Co-Authored-By: Bond-009
---
MediaBrowser.Common/Net/IHttpClient.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs
index d332ab2000..db69c6f2cd 100644
--- a/MediaBrowser.Common/Net/IHttpClient.cs
+++ b/MediaBrowser.Common/Net/IHttpClient.cs
@@ -24,7 +24,7 @@ namespace MediaBrowser.Common.Net
Task Get(HttpRequestOptions options);
///
- /// Warning: Depricated function,
+ /// Warning: Deprecated function,
/// use 'Task SendAsync(HttpRequestOptions options, HttpMethod httpMethod);' instead
/// Sends the asynchronous.
///
From 04784b4e4301bb150e8af5d66154e969e71d9efc Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Wed, 19 Jun 2019 18:41:52 +0200
Subject: [PATCH 057/214] Create Media Playback issue template (#1451)
Added media playback issue template. We might want to add some more information requests in there.
---
.github/ISSUE_TEMPLATE/media_playback.md | 32 ++++++++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 .github/ISSUE_TEMPLATE/media_playback.md
diff --git a/.github/ISSUE_TEMPLATE/media_playback.md b/.github/ISSUE_TEMPLATE/media_playback.md
new file mode 100644
index 0000000000..93af33fbfd
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/media_playback.md
@@ -0,0 +1,32 @@
+---
+name: Media playback issue
+about: Create a media playback issue report
+title: ''
+labels: mediaplayback
+assignees: ''
+
+---
+
+**Media Info of the file**
+
+
+**Logs**
+
+
+**Stats for Nerds Screenshots**
+
+
+**Server System (please complete the following information):**
+ - OS: [e.g. Docker on Linux, Docker on Windows, Debian, Windows]
+ - Jellyfin Version: [e.g. 10.0.1]
+ - Hardware settings & device: [e.g. NVENC on GTX1060, VAAPI on Intel i7 8700K]
+ - Reverse proxy: [e.g. no, nginx, apache, etc.]
+ - Other hardware notes: [e.g. Media mounted in CIFS/SMB share, Media mounted from Google Drive]
+
+**Client System (please complete the following information):**
+ - Device: [e.g. Apple iPhone XS, Xbox One S, LG OLED55C8, Samsung Galaxy Note9, Custom HTPC]
+ - OS: [e.g. iOS, Android, Windows, macOS]
+ - Client: [e.g. Web/Browser, webOS, Android, Android TV, Electron]
+ - Browser (if Web client): [e.g. Firefox, Chrome, Safari]
+ - Client and Browser Version: [e.g. 10.3.4 and 68.0]
+
From 65fa61a6369bef8c7aa7cc2b9e6be383a2de3cb6 Mon Sep 17 00:00:00 2001
From: dkanada
Date: Thu, 20 Jun 2019 16:44:27 -0700
Subject: [PATCH 058/214] add comment explaining GetMinBitrate
---
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 849cf70835..2984efec37 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -1086,7 +1086,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (videoStream != null)
{
var isUpscaling = request.Height.HasValue && videoStream.Height.HasValue &&
- request.Height.Value > videoStream.Height.Value && request.Width.HasValue && videoStream.Width.HasValue &&
+ request.Height.Value > videoStream.Height.Value && request.Width.HasValue && videoStream.Width.HasValue &&
request.Width.Value > videoStream.Width.Value;
// Don't allow bitrate increases unless upscaling
@@ -1116,6 +1116,7 @@ namespace MediaBrowser.Controller.MediaEncoding
private int GetMinBitrate(int sourceBitrate, int requestedBitrate)
{
+ // these values were chosen from testing to improve low bitrate streams
if (sourceBitrate <= 2000000)
{
sourceBitrate = Convert.ToInt32(sourceBitrate * 2.5);
From c2ab0ad641b60b453a0697918e9db7773e89a6f6 Mon Sep 17 00:00:00 2001
From: Claus Vium
Date: Fri, 21 Jun 2019 19:08:04 +0200
Subject: [PATCH 059/214] Wait for the async authentication to finish when the
JTW token expires
---
.../TV/TheTVDB/TvDbClientManager.cs | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
index 1d1fbd00f1..85833223e1 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
@@ -24,24 +24,28 @@ namespace MediaBrowser.Providers.TV.TheTVDB
{
_cache = memoryCache;
_tvDbClient = new TvDbClient();
- _tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey);
- _tokenCreatedAt = DateTime.Now;
}
- public TvDbClient TvDbClient
+ private TvDbClient TvDbClient
{
get
{
+ if (string.IsNullOrEmpty(_tvDbClient.Authentication.Token))
+ {
+ _tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey).GetAwaiter().GetResult();
+ _tokenCreatedAt = DateTime.Now;
+ }
+
// Refresh if necessary
if (_tokenCreatedAt < DateTime.Now.Subtract(TimeSpan.FromHours(20)))
{
try
{
- _tvDbClient.Authentication.RefreshTokenAsync();
+ _tvDbClient.Authentication.RefreshTokenAsync().GetAwaiter().GetResult();
}
catch
{
- _tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey);
+ _tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey).GetAwaiter().GetResult();
}
_tokenCreatedAt = DateTime.Now;
From 18e6cd429ae026d87259eb05c45bf049616235dc Mon Sep 17 00:00:00 2001
From: SenorSmartyPants
Date: Sat, 22 Jun 2019 16:11:47 -0500
Subject: [PATCH 060/214] Update TVDB provider to search based on series
display order
---
.../TV/TheTVDB/TvDbClientManager.cs | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
index 1d1fbd00f1..9fd72b65c1 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
@@ -158,8 +158,20 @@ namespace MediaBrowser.Providers.TV.TheTVDB
// Prefer SxE over premiere date as it is more robust
if (searchInfo.IndexNumber.HasValue && searchInfo.ParentIndexNumber.HasValue)
{
- episodeQuery.AiredEpisode = searchInfo.IndexNumber.Value;
- episodeQuery.AiredSeason = searchInfo.ParentIndexNumber.Value;
+ switch (searchInfo.SeriesDisplayOrder)
+ {
+ case "dvd":
+ episodeQuery.DvdEpisode = searchInfo.IndexNumber.Value;
+ episodeQuery.DvdSeason = searchInfo.ParentIndexNumber.Value;
+ break;
+ case "absolute":
+ episodeQuery.AbsoluteNumber = searchInfo.IndexNumber.Value;
+ break;
+ default: //aired order
+ episodeQuery.AiredEpisode = searchInfo.IndexNumber.Value;
+ episodeQuery.AiredSeason = searchInfo.ParentIndexNumber.Value;
+ break;
+ }
}
else if (searchInfo.PremiereDate.HasValue)
{
From 394d96246bf8a70c87f2fbcc2920edca9b5748e9 Mon Sep 17 00:00:00 2001
From: DrPandemic
Date: Sun, 23 Jun 2019 10:13:50 -0400
Subject: [PATCH 061/214] Check path before opening image
---
CONTRIBUTORS.md | 3 ++-
Jellyfin.Drawing.Skia/SkiaEncoder.cs | 5 +++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index c3fcea1e21..29456598f1 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -23,8 +23,9 @@
- [fruhnow](https://github.com/fruhnow)
- [Lynxy](https://github.com/Lynxy)
- [fasheng](https://github.com/fasheng)
- - [ploughpuff](https://github.com/ploughpuff)
+ - [ploughpuff](https://github.com/ploughpuff)
- [pjeanjean](https://github.com/pjeanjean)
+ - [DrPandemic](https://github.com/drpandemic)
# Emby Contributors
diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
index 5060476bad..1eda817a39 100644
--- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs
+++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
@@ -185,6 +185,11 @@ namespace Jellyfin.Drawing.Skia
public ImageDimensions GetImageSize(string path)
{
+ if (!File.Exists(path))
+ {
+ throw new FileNotFoundException("File not found", path);
+ }
+
using (var s = new SKFileStream(path))
using (var codec = SKCodec.Create(s))
{
From 1fd81647560c12728e2eb64430360d0570336f61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rn?=
Date: Mon, 24 Jun 2019 15:06:49 -0700
Subject: [PATCH 062/214] fix issue #1347 introduced in pr #930
---
CONTRIBUTORS.md | 1 +
MediaBrowser.Api/UserLibrary/ItemsService.cs | 12 +++++++++++-
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 29456598f1..9168dccc8c 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -26,6 +26,7 @@
- [ploughpuff](https://github.com/ploughpuff)
- [pjeanjean](https://github.com/pjeanjean)
- [DrPandemic](https://github.com/drpandemic)
+ - [joern-h](https://github.com/joern-h)
# Emby Contributors
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index f1ae484922..f842230ee3 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -224,7 +224,17 @@ namespace MediaBrowser.Api.UserLibrary
request.IncludeItemTypes = "Playlist";
}
- if (!(item is UserRootFolder) && !user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id))
+ bool isInEnabledFolder = user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id);
+ var collectionFolders = _libraryManager.GetCollectionFolders(item);
+ foreach (var collectionFolder in collectionFolders)
+ {
+ if (user.Policy.EnabledFolders.Contains(collectionFolder.Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
+ {
+ isInEnabledFolder = true;
+ }
+ }
+
+ if (!(item is UserRootFolder) && !user.Policy.EnableAllFolders && !isInEnabledFolder)
{
Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Name, item.Name);
return new QueryResult
From c08c0272b5c801fecbb13b146b9e24429aa6df62 Mon Sep 17 00:00:00 2001
From: "Joshua M. Boniface"
Date: Thu, 27 Jun 2019 18:05:03 -0400
Subject: [PATCH 063/214] Add nicer restart script
The old restart script was buggy, as reported in #1320. This updated
script seems to work far more reliably and conforms to the existing
jellyfin-sudoers packages sudo configuration.
---
.../debian-package-x64/pkg-src/bin/restart.sh | 48 ++++++++++++-------
.../fedora-package-x64/pkg-src/restart.sh | 40 ++++++++++++++--
2 files changed, 67 insertions(+), 21 deletions(-)
diff --git a/deployment/debian-package-x64/pkg-src/bin/restart.sh b/deployment/debian-package-x64/pkg-src/bin/restart.sh
index 738f86727c..acfb0c5a25 100644
--- a/deployment/debian-package-x64/pkg-src/bin/restart.sh
+++ b/deployment/debian-package-x64/pkg-src/bin/restart.sh
@@ -1,20 +1,36 @@
#!/bin/bash
-NAME=jellyfin
+# restart.sh - Jellyfin server restart script
+# Part of the Jellyfin project (https://github.com/jellyfin)
+#
+# This script restarts the Jellyfin daemon on Linux when using
+# the Restart button on the admin dashboard. It supports the
+# systemctl, service, and traditional /etc/init.d (sysv) restart
+# methods, chosen automatically by which one is found first (in
+# that order).
+#
+# This script is used by the Debian/Ubuntu/Fedora/CentOS packages.
-restart_cmds=(
- "systemctl restart ${NAME}"
- "service ${NAME} restart"
- "/etc/init.d/${NAME} restart"
- "s6-svc -t /var/run/s6/services/${NAME}"
-)
+get_service_command() {
+ for command in systemctl service; do
+ if which $command &>/dev/null; then
+ echo $command && return
+ fi
+ done
+ echo "sysv"
+}
-for restart_cmd in "${restart_cmds[@]}"; do
- cmd=$(echo "$restart_cmd" | awk '{print $1}')
- cmd_loc=$(command -v ${cmd})
- if [[ -n "$cmd_loc" ]]; then
- restart_cmd=$(echo "$restart_cmd" | sed -e "s%${cmd}%${cmd_loc}%")
- echo "sleep 2; sudo $restart_cmd > /dev/null 2>&1" | at now > /dev/null 2>&1
- exit 0
- fi
-done
+cmd="$( get_service_command )"
+echo "Using system control command '$cmd' to restart Jellyfin..."
+case $cmd in
+ 'systemctl')
+ echo "sleep 2; /usr/bin/sudo /usr/bin/systemctl restart jellyfin" | at now
+ ;;
+ 'service')
+ echo "sleep 2; /usr/bin/sudo /sbin/service jellyfin restart" | at now
+ ;;
+ 'sysv')
+ echo "sleep 2; /usr/bin/sudo /etc/init.d/jellyfin restart" | at now
+ ;;
+esac
+exit 0
diff --git a/deployment/fedora-package-x64/pkg-src/restart.sh b/deployment/fedora-package-x64/pkg-src/restart.sh
index e84dca587f..acfb0c5a25 100644
--- a/deployment/fedora-package-x64/pkg-src/restart.sh
+++ b/deployment/fedora-package-x64/pkg-src/restart.sh
@@ -1,6 +1,36 @@
-#!/bin/sh
+#!/bin/bash
-NAME=jellyfin
-restart_cmd="/usr/bin/systemctl restart ${NAME}"
-echo "sleep 2; sudo $restart_cmd > /dev/null 2>&1" | at now > /dev/null 2>&1
-exit 0
\ No newline at end of file
+# restart.sh - Jellyfin server restart script
+# Part of the Jellyfin project (https://github.com/jellyfin)
+#
+# This script restarts the Jellyfin daemon on Linux when using
+# the Restart button on the admin dashboard. It supports the
+# systemctl, service, and traditional /etc/init.d (sysv) restart
+# methods, chosen automatically by which one is found first (in
+# that order).
+#
+# This script is used by the Debian/Ubuntu/Fedora/CentOS packages.
+
+get_service_command() {
+ for command in systemctl service; do
+ if which $command &>/dev/null; then
+ echo $command && return
+ fi
+ done
+ echo "sysv"
+}
+
+cmd="$( get_service_command )"
+echo "Using system control command '$cmd' to restart Jellyfin..."
+case $cmd in
+ 'systemctl')
+ echo "sleep 2; /usr/bin/sudo /usr/bin/systemctl restart jellyfin" | at now
+ ;;
+ 'service')
+ echo "sleep 2; /usr/bin/sudo /sbin/service jellyfin restart" | at now
+ ;;
+ 'sysv')
+ echo "sleep 2; /usr/bin/sudo /etc/init.d/jellyfin restart" | at now
+ ;;
+esac
+exit 0
From cec22ad10daf7abef2f27f846e4022d5a35faccf Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Wed, 20 Feb 2019 14:26:49 +0100
Subject: [PATCH 064/214] Simplify db code
---
.../Activity/ActivityRepository.cs | 5 +-
.../Data/BaseSqliteRepository.cs | 255 ++----
.../Data/ManagedConnection.cs | 79 --
.../SqliteDisplayPreferencesRepository.cs | 70 +-
.../Data/SqliteExtensions.cs | 2 +-
.../Data/SqliteItemRepository.cs | 785 ++++++++----------
.../Data/SqliteUserDataRepository.cs | 76 +-
.../Data/SqliteUserRepository.cs | 96 +--
.../Security/AuthenticationRepository.cs | 212 +++--
9 files changed, 621 insertions(+), 959 deletions(-)
delete mode 100644 Emby.Server.Implementations/Data/ManagedConnection.cs
diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs
index aeed8b6f1a..495d969776 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 fba81306b3..29edddb3a7 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)
+ //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);
+
+ try
+ {
+ if (string.IsNullOrWhiteSpace(_defaultWal))
{
- _versionLogged = true;
- Logger.LogInformation("Sqlite version: " + SQLite3.Version);
- Logger.LogInformation("Sqlite compiler options: " + string.Join(",", SQLite3.CompilerOptions.ToArray()));
+ _defaultWal = db.Query("PRAGMA journal_mode").SelectScalarString().First();
+
+ Logger.LogInformation("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal);
}
- ConnectionFlags connectionFlags;
-
- if (isReadOnly)
+ var queries = new List
{
- //Logger.LogInformation("Opening read connection");
- //connectionFlags = ConnectionFlags.ReadOnly;
- connectionFlags = ConnectionFlags.Create;
- connectionFlags |= ConnectionFlags.ReadWrite;
+ //"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
{
- //Logger.LogInformation("Opening write connection");
- connectionFlags = ConnectionFlags.Create;
- connectionFlags |= ConnectionFlags.ReadWrite;
+ queries.Add("PRAGMA temp_store = file");
}
- if (EnableSingleConnection)
+ foreach (var query in queries)
{
- connectionFlags |= ConnectionFlags.PrivateCache;
+ db.Execute(query);
}
- else
- {
- connectionFlags |= ConnectionFlags.SharedCached;
- }
-
- connectionFlags |= ConnectionFlags.NoMutex;
-
- var db = SQLite3.Open(DbFilePath, connectionFlags, null);
-
- try
- {
- 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);
- }
- }
- catch
- {
- using (db)
- {
-
- }
-
- throw;
- }
-
- _connection = new ManagedConnection(db, false);
-
- return _connection;
}
+ catch
+ {
+ using (db)
+ {
+
+ }
+
+ 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)
+ if (_disposed)
{
- DisposeConnection();
+ return;
}
- }
- private void DisposeConnection()
- {
- try
- {
- lock (_disposeLock)
- {
- using (WriteLock.Write())
- {
- if (_connection != null)
- {
- using (_connection)
- {
- _connection.Close();
- }
- _connection = null;
- }
-
- CloseConnection();
- }
- }
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error disposing database");
- }
- }
-
- 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 b8f1e581ab..0000000000
--- 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 47552806d4..86a7c1551f 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);
+ statement.TryBind("@id", guidId.ToGuidBlob());
+ statement.TryBind("@userId", userId.ToGuidBlob());
+ statement.TryBind("@client", client);
- foreach (var row in statement.ExecuteQuery())
- {
- return Get(row);
- }
+ foreach (var row in statement.ExecuteQuery())
+ {
+ return Get(row);
}
-
- return new DisplayPreferences
- {
- Id = guidId.ToString("N")
- };
}
+
+ 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 a486cb1a06..c0f27b25aa 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 8841a9a504..baa5b713a2 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);
+
+ foreach (var row in statement.ExecuteQuery())
{
- statement.TryBind("@guid", id);
-
- foreach (var row in statement.ExecuteQuery())
- {
- return GetItem(row, new InternalItemsQuery());
- }
+ 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);
- }
-
- BindSimilarParams(query, statement);
- BindSearchParams(query, statement);
-
- // Running this again will bind the params
- GetWhereClauses(query, statement);
-
- var count = statement.ExecuteQuery().SelectScalarInt().First();
- LogQueryTime("GetCount", commandText, now);
- return count;
+ statement.TryBind("@UserId", query.User.InternalId);
}
- }
+ BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
+
+ // Running this again will bind the params
+ GetWhereClauses(query, statement);
+
+ 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();
+
+ using (var statement = PrepareStatementSafe(connection, commandText))
{
- var list = new List();
-
- using (var statement = PrepareStatementSafe(connection, commandText))
+ if (EnableJoinUserData(query))
{
- if (EnableJoinUserData(query))
- {
- statement.TryBind("@UserId", query.User.InternalId);
- }
-
- 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);
-
- foreach (var row in statement.ExecuteQuery())
- {
- var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields);
- if (item != null)
- {
- list.Add(item);
- }
- }
+ statement.TryBind("@UserId", query.User.InternalId);
}
- // Hack for right now since we currently don't support filtering out these duplicates within a query
- if (query.EnableGroupByMetadataKey)
+ 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);
+
+ foreach (var row in statement.ExecuteQuery())
{
- var limit = query.Limit ?? int.MaxValue;
- limit -= 4;
- var newList = new List();
-
- foreach (var item in list)
+ var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields);
+ if (item != null)
{
- AddItem(newList, item);
-
- if (newList.Count >= limit)
- {
- break;
- }
+ list.Add(item);
}
-
- list = newList;
}
-
- LogQueryTime("GetItemList", commandText, now);
-
- return list;
}
+
+ // 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)
+ {
+ AddItem(newList, item);
+
+ if (newList.Count >= limit)
+ {
+ break;
+ }
+ }
+
+ 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);
+
+ if (!isReturningZeroItems)
{
- var result = new QueryResult();
- var statements = PrepareAllSafe(db, statementTexts);
-
- 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();
+
+ using (var statement = PrepareStatementSafe(connection, commandText))
{
- var list = new List();
-
- using (var statement = PrepareStatementSafe(connection, commandText))
+ if (EnableJoinUserData(query))
{
- if (EnableJoinUserData(query))
- {
- statement.TryBind("@UserId", query.User.InternalId);
- }
-
- 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());
- }
+ statement.TryBind("@UserId", query.User.InternalId);
}
- LogQueryTime("GetItemList", commandText, now);
+ BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
- return list;
+ // Running this again will bind the params
+ GetWhereClauses(query, statement);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(row[0].ReadGuidFromBlob());
+ }
}
+
+ LogQueryTime("GetItemList", commandText, now);
+
+ 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);
+ }
+
+ // Running this again will bind the params
+ GetWhereClauses(query, statement);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ var id = row.GetGuid(0);
+ string path = null;
+
+ if (!row.IsDBNull(1))
{
- statement.TryBind("@UserId", query.User.InternalId);
- }
-
- // Running this again will bind the params
- GetWhereClauses(query, statement);
-
- 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));
+ path = row.GetString(1);
}
+ list.Add(new Tuple(id, path));
}
}
-
- LogQueryTime("GetItemIdsWithPath", commandText, now);
-
- return list;
}
+
+ LogQueryTime("GetItemIdsWithPath", commandText, now);
+
+ 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 statements = PrepareAllSafe(db, statementTexts);
+
+ if (!isReturningZeroItems)
{
- var result = new QueryResult();
-
- var statements = PrepareAllSafe(db, statementTexts);
-
- 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);
-
- return list;
}
+
+ LogQueryTime("GetItemValueNames", commandText, now);
+
+ 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 statements = PrepareAllSafe(db, statementTexts);
+
+ if (!isReturningZeroItems)
{
- var list = new List<(BaseItem, ItemCounts)>();
- var result = new QueryResult<(BaseItem, ItemCounts)>();
-
- var statements = PrepareAllSafe(db, statementTexts);
-
- 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);
- }
-
- 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 countStartColumn = columns.Count - 1;
-
- list.Add((item, GetItemCounts(row, countStartColumn, typesToCount)));
- }
- }
-
- LogQueryTime("GetItemValues", commandText, now);
+ statement.TryBind("@UserId", query.User.InternalId);
}
- }
- if (query.EnableTotalRecordCount)
- {
- commandText = "select "
- + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" }))
- + GetFromText()
- + GetJoinUserDataText(query)
- + whereText;
-
- using (var statement = statements[statements.Count - 1])
+ if (typeSubQuery != null)
{
- statement.TryBind("@SelectType", returnType);
- if (EnableJoinUserData(query))
- {
- statement.TryBind("@UserId", query.User.InternalId);
- }
-
- if (typeSubQuery != null)
- {
- GetWhereClauses(typeSubQuery, null);
- }
- BindSimilarParams(query, statement);
- BindSearchParams(query, statement);
- GetWhereClauses(innerQuery, statement);
- GetWhereClauses(outerQuery, statement);
-
- result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
-
- LogQueryTime("GetItemValues", commandText, now);
+ GetWhereClauses(typeSubQuery, null);
}
- }
+ BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
+ GetWhereClauses(innerQuery, statement);
+ GetWhereClauses(outerQuery, statement);
- if (result.TotalRecordCount == 0)
+ 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 countStartColumn = columns.Count - 1;
+
+ list.Add((item, GetItemCounts(row, countStartColumn, typesToCount)));
+ }
+ }
+
+ LogQueryTime("GetItemValues", commandText, now);
+ }
+ }
+
+ if (query.EnableTotalRecordCount)
+ {
+ commandText = "select "
+ + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" }))
+ + GetFromText()
+ + GetJoinUserDataText(query)
+ + whereText;
+
+ using (var statement = statements[statements.Count - 1])
{
- result.TotalRecordCount = list.Count;
+ statement.TryBind("@SelectType", returnType);
+ if (EnableJoinUserData(query))
+ {
+ statement.TryBind("@UserId", query.User.InternalId);
+ }
+
+ if (typeSubQuery != null)
+ {
+ GetWhereClauses(typeSubQuery, null);
+ }
+ BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
+ GetWhereClauses(innerQuery, statement);
+ GetWhereClauses(outerQuery, statement);
+
+ result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
+
+ LogQueryTime("GetItemValues", commandText, now);
}
- result.Items = list.ToArray();
+ }
- return result;
+ if (result.TotalRecordCount == 0)
+ {
+ result.TotalRecordCount = list.Count;
+ }
+ result.Items = list.ToArray();
- }, ReadTransactionMode);
- }
+ return result;
+
+ }, 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);
+ InsertPeople(itemIdBlob, people, db);
- }, TransactionMode);
-
- }
+ }, 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);
- }
-
- foreach (var row in statement.ExecuteQuery())
- {
- list.Add(GetMediaStream(row));
- }
+ statement.TryBind("@StreamType", query.Type.Value.ToString());
}
- return list;
+ if (query.Index.HasValue)
+ {
+ statement.TryBind("@StreamIndex", query.Index.Value);
+ }
+
+ 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 4109b7ad1f..f544dfac4c 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);
+
+ foreach (var row in statement.ExecuteQuery())
{
- statement.TryBind("@UserId", internalUserId);
- statement.TryBind("@Key", key);
-
- foreach (var row in statement.ExecuteQuery())
- {
- return ReadRow(row);
- }
+ 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 5957b29031..785452ad39 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 29b8dfd3d7..abc23239e2 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("@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.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();
- }
+ statement.MoveNext();
+ }
- }, TransactionMode);
- }
+ }, 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("@Id", info.Id);
- statement.TryBind("@AccessToken", info.AccessToken);
+ 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.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.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 statementTexts = new List();
+ statementTexts.Add(commandText);
+ statementTexts.Add("select count (Id) from Tokens" + whereTextWithoutPaging);
+
+ var statements = PrepareAllSafe(db, statementTexts)
+ .ToList();
+
+ using (var statement = statements[0])
{
- var result = new QueryResult();
+ BindAuthenticationQueryParams(query, statement);
- var statementTexts = new List();
- statementTexts.Add(commandText);
- statementTexts.Add("select count (Id) from Tokens" + whereTextWithoutPaging);
-
- var statements = PrepareAllSafe(db, statementTexts)
- .ToList();
-
- using (var statement = statements[0])
+ foreach (var row in statement.ExecuteQuery())
{
- BindAuthenticationQueryParams(query, statement);
-
- foreach (var row in statement.ExecuteQuery())
- {
- list.Add(Get(row));
- }
-
- using (var totalCountStatement = statements[1])
- {
- BindAuthenticationQueryParams(query, totalCountStatement);
-
- result.TotalRecordCount = totalCountStatement.ExecuteQuery()
- .SelectScalarInt()
- .First();
- }
+ list.Add(Get(row));
}
- result.Items = list.ToArray();
- return result;
+ using (var totalCountStatement = statements[1])
+ {
+ BindAuthenticationQueryParams(query, totalCountStatement);
- }, ReadTransactionMode);
- }
+ result.TotalRecordCount = totalCountStatement.ExecuteQuery()
+ .SelectScalarInt()
+ .First();
+ }
+ }
+
+ result.Items = list.ToArray();
+ return result;
+
+ }, 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);
+
+ var result = new DeviceOptions();
+
+ foreach (var row in statement.ExecuteQuery())
{
- statement.TryBind("@DeviceId", deviceId);
-
- var result = new DeviceOptions();
-
- 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.TryBind("@Id", deviceId);
-
- if (string.IsNullOrWhiteSpace(options.CustomName))
- {
- statement.TryBindNull("@CustomName");
- }
- else
- {
- statement.TryBind("@CustomName", options.CustomName);
- }
-
- statement.MoveNext();
+ statement.TryBindNull("@CustomName");
+ }
+ else
+ {
+ statement.TryBind("@CustomName", options.CustomName);
}
- }, TransactionMode);
- }
+ statement.MoveNext();
+ }
+
+ }, TransactionMode);
}
}
}
From c30ba14c1f9701638bbb47e81d3e7027cb778135 Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Tue, 26 Feb 2019 18:30:13 +0100
Subject: [PATCH 065/214] Use a connection pool instead of creating new
connections
---
.../Activity/ActivityRepository.cs | 11 +-
.../Data/BaseSqliteRepository.cs | 116 +++++++++---------
.../Data/ManagedConnection.cs | 87 +++++++++++++
.../SqliteDisplayPreferencesRepository.cs | 11 +-
.../Data/SqliteItemRepository.cs | 47 ++++---
.../Data/SqliteUserDataRepository.cs | 11 +-
.../Data/SqliteUserRepository.cs | 15 +--
.../Security/AuthenticationRepository.cs | 17 +--
8 files changed, 200 insertions(+), 115 deletions(-)
create mode 100644 Emby.Server.Implementations/Data/ManagedConnection.cs
diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs
index 495d969776..a38cb38d7e 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 29edddb3a7..6a19ea373e 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 (WriteConnection == null)
+ {
+ WriteConnection = SQLite3.Open(
+ DbFilePath,
+ DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite,
+ null);
+ }
+
if (string.IsNullOrWhiteSpace(_defaultWal))
{
- _defaultWal = db.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);
}
- 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");
+ 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 0000000000..71b934a9bb
--- /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 86a7c1551f..d620f3962f 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 baa5b713a2..e96b6ce3a0 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 f544dfac4c..9dc31d5972 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 785452ad39..ef8ae60b3c 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 abc23239e2..dfcd6af0df 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 =>
{
From e5248cfaa25a4aaa00e9cc8ba7d23a2b3c690b6e Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Tue, 26 Feb 2019 18:58:33 +0100
Subject: [PATCH 066/214] Properly dispose
---
.../Data/BaseSqliteRepository.cs | 53 +++++++++++--------
1 file changed, 30 insertions(+), 23 deletions(-)
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 6a19ea373e..33bbbbfe4f 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;
}
From 30842656a7fb1d23eef245dcfb57287ebdb960a0 Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Tue, 26 Feb 2019 19:01:18 +0100
Subject: [PATCH 067/214] Properly dispose
---
Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 1 -
Emby.Server.Implementations/Data/SqliteItemRepository.cs | 8 ++++----
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 33bbbbfe4f..db63f68d0f 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 e96b6ce3a0..a2f490c4a6 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)
{
From 27c29bbb4c75d5fc5c9111c5552c37a1f137dd58 Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Mon, 11 Mar 2019 22:33:27 +0100
Subject: [PATCH 068/214] Back to a single connection
---
.../Activity/ActivityRepository.cs | 1 -
.../Data/BaseSqliteRepository.cs | 94 ++++++-------------
.../Data/ManagedConnection.cs | 13 +--
.../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, 30 insertions(+), 88 deletions(-)
diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs
index a38cb38d7e..63931e1348 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 db63f68d0f..33a0b7ddfa 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()
- {
- await WriteLock.WaitAsync().ConfigureAwait(false);
-
- try
- {
- if (WriteConnection == null)
- {
- WriteConnection = SQLite3.Open(
- DbFilePath,
- DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite,
- null);
- }
-
- if (string.IsNullOrWhiteSpace(_defaultWal))
- {
- _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");
- }
- else
- {
- WriteConnection.Execute("PRAGMA temp_store = file");
- }
- }
- catch
- {
-
- throw;
- }
- finally
- {
- WriteLock.Release();
- }
-
- // 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)
+ WriteLock.Wait();
+ if (WriteConnection != null)
{
- return new ManagedConnection(ReadConnectionPool.Take(), ReadConnectionPool);
+ return new ManagedConnection(WriteConnection, WriteLock);
+ }
+
+ WriteConnection = SQLite3.Open(
+ DbFilePath,
+ DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite,
+ null);
+
+
+ if (string.IsNullOrWhiteSpace(_defaultWal))
+ {
+ _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");
}
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 71b934a9bb..4c34244100 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 d620f3962f..7f8df76262 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 a2f490c4a6..9e96d77451 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 9dc31d5972..355755014c 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 ef8ae60b3c..e79b3d6015 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 dfcd6af0df..efe56c0816 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 d24a187430..d9530ffb79 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 b79e9f84b1..f1a6622fb5 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;
From b6954f3bfd68c87923348444a5923406cf672f9b Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Mon, 11 Mar 2019 23:07:38 +0100
Subject: [PATCH 069/214] More
---
.../Activity/ActivityRepository.cs | 8 ++--
.../Data/BaseSqliteRepository.cs | 42 ++++++++-----------
.../SqliteDisplayPreferencesRepository.cs | 6 +--
Jellyfin.Server/Program.cs | 7 +++-
4 files changed, 30 insertions(+), 33 deletions(-)
diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs
index 63931e1348..cac1a9feb4 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 33a0b7ddfa..f3bd07bb09 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 readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1);
- private SQLiteDatabaseConnection WriteConnection;
-
- 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 7f8df76262..1d44b0b292 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 91752a16df..11c09db983 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,
From e88ebd748d98cf9bd2a3978d36254f1644ce751a Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Tue, 2 Apr 2019 22:15:18 +0200
Subject: [PATCH 070/214] 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(-)
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index f3bd07bb09..63cef80b04 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 9e96d77451..462d91e41f 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 355755014c..6ac3989372 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 efe56c0816..29afb9f646 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 11c09db983..9454b28d6d 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,
From 02b864e41b80c07941d262d9d3723c0e7967d557 Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Tue, 2 Apr 2019 22:18:13 +0200
Subject: [PATCH 071/214] Last line?
---
Emby.Server.Implementations/Activity/ActivityRepository.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs
index cac1a9feb4..e45add355f 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)
From d00ad28efd10e2bb312c9a08055f83df26065494 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 3 Apr 2019 13:46:07 +0200
Subject: [PATCH 072/214] 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(-)
diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs
index e45add355f..f8a1b32afe 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 63cef80b04..c5af156bbd 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 462d91e41f..5dc104347a 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 6ac3989372..0580203c56 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 29afb9f646..c8ecd7e6e6 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);
}
}
From edfd2d0cd959020cdcd2196320850d28ae10094d Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 3 Apr 2019 14:22:17 +0200
Subject: [PATCH 073/214] Fix startup
---
.../ApplicationHost.cs | 11 ++++----
.../Data/SqliteUserRepository.cs | 27 +++++++++----------
2 files changed, 18 insertions(+), 20 deletions(-)
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 62cc6ec477..3aa2dbf9a6 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 e79b3d6015..a0c6d2903b 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);
}
-
}
///
From 7898af4cebe58bc11d120552594098041fff56fb Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 3 Apr 2019 17:34:54 +0200
Subject: [PATCH 074/214] Reworked PRAGMA statements use
---
.../Activity/ActivityRepository.cs | 2 -
.../Data/BaseSqliteRepository.cs | 92 +++++++++----------
.../SqliteDisplayPreferencesRepository.cs | 2 -
.../Data/SqliteItemRepository.cs | 4 +-
.../Data/SqliteUserDataRepository.cs | 2 -
.../Data/SqliteUserRepository.cs | 2 -
.../Security/AuthenticationRepository.cs | 2 -
7 files changed, 46 insertions(+), 60 deletions(-)
diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs
index f8a1b32afe..de46ab9655 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 c5af156bbd..4da6665c2c 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();
-
- Logger.LogInformation("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal);
+ WriteConnection.Execute("PRAGMA cache_size=" + (int)CacheSize.Value);
}
- if (EnableTempStoreMemory)
+ if (!string.IsNullOrWhiteSpace(JournalMode))
{
- WriteConnection.Execute("PRAGMA temp_store = memory");
+ WriteConnection.Execute("PRAGMA journal_mode=" + JournalMode);
}
- else
+
+ if (Synchronous.HasValue)
{
- WriteConnection.Execute("PRAGMA temp_store = file");
+ WriteConnection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value);
}
+ if (PageSize.HasValue)
+ {
+ 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 1d44b0b292..01ef9851dc 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 5dc104347a..8a56e16bb7 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 0580203c56..4035bb99d1 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 a0c6d2903b..cd364e7f44 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 c8ecd7e6e6..545e11bf9f 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 = {
From db2765aae5556fd4e05e1c310027bdbd699327d2 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 3 Apr 2019 18:02:43 +0200
Subject: [PATCH 075/214] Last bit of cleanup
---
.../Data/BaseSqliteRepository.cs | 56 +++++++++----------
1 file changed, 27 insertions(+), 29 deletions(-)
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 4da6665c2c..7938d6b7e0 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
From d961278b3dcab57910b260115cd45d9831e6443e Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 3 Apr 2019 18:15:04 +0200
Subject: [PATCH 076/214] Reduce amount of raw sql
---
.../Data/SqliteUserRepository.cs | 23 +++++----
.../Library/UserManager.cs | 50 +++++++++----------
2 files changed, 38 insertions(+), 35 deletions(-)
diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
index cd364e7f44..de2354eefc 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 ff375e5900..1701ced426 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);
-
- var user = InstantiateNewUser(name);
-
- user.DateLastSaved = DateTime.UtcNow;
-
- UserRepository.CreateUser(user);
-
- users.Add(user);
-
- user.Policy.IsAdministrator = true;
- user.Policy.EnableContentDeletion = true;
- user.Policy.EnableRemoteControlOfOtherUsers = true;
- UpdateUserPolicy(user, user.Policy, false);
+ return users;
}
- return users.ToArray();
+ var defaultName = Environment.UserName;
+ if (string.IsNullOrWhiteSpace(defaultName))
+ {
+ defaultName = "MyJellyfinUser";
+ }
+
+ var name = MakeValidUsername(defaultName);
+
+ var user = InstantiateNewUser(name);
+
+ user.DateLastSaved = DateTime.UtcNow;
+
+ UserRepository.CreateUser(user);
+
+ user.Policy.IsAdministrator = true;
+ user.Policy.EnableContentDeletion = true;
+ user.Policy.EnableRemoteControlOfOtherUsers = true;
+ UpdateUserPolicy(user, user.Policy, false);
+
+ return new List { user };
}
public UserDto GetUserDto(User user, string remoteEndPoint = null)
From 2e4c0fee774329b5c2370915a1459a7981b2a232 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Fri, 28 Jun 2019 12:16:51 +0200
Subject: [PATCH 077/214] Add removed line
---
Jellyfin.Server/Program.cs | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 9454b28d6d..11c09db983 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -128,6 +128,10 @@ 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,
From ecb8d8991b1ea4e1e26a69c7c9e0a217927d27d4 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Fri, 28 Jun 2019 12:22:33 +0200
Subject: [PATCH 078/214] Fix whitespace
---
Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +-
Emby.Server.Implementations/Updates/InstallationManager.cs | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 6c50698b2a..48c915edee 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 b3d76124bd..9bc85633d4 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
From a629f209b931e37a2e3e5db5ee9827460e5f3bbf Mon Sep 17 00:00:00 2001
From: "Joshua M. Boniface"
Date: Fri, 28 Jun 2019 11:06:55 -0400
Subject: [PATCH 079/214] Make message wording more consistent
---
deployment/debian-package-x64/pkg-src/bin/restart.sh | 2 +-
deployment/fedora-package-x64/pkg-src/restart.sh | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/deployment/debian-package-x64/pkg-src/bin/restart.sh b/deployment/debian-package-x64/pkg-src/bin/restart.sh
index acfb0c5a25..4c664d404e 100644
--- a/deployment/debian-package-x64/pkg-src/bin/restart.sh
+++ b/deployment/debian-package-x64/pkg-src/bin/restart.sh
@@ -21,7 +21,7 @@ get_service_command() {
}
cmd="$( get_service_command )"
-echo "Using system control command '$cmd' to restart Jellyfin..."
+echo "Detected service control platform '$cmd'; using it to restart Jellyfin..."
case $cmd in
'systemctl')
echo "sleep 2; /usr/bin/sudo /usr/bin/systemctl restart jellyfin" | at now
diff --git a/deployment/fedora-package-x64/pkg-src/restart.sh b/deployment/fedora-package-x64/pkg-src/restart.sh
index acfb0c5a25..4c664d404e 100644
--- a/deployment/fedora-package-x64/pkg-src/restart.sh
+++ b/deployment/fedora-package-x64/pkg-src/restart.sh
@@ -21,7 +21,7 @@ get_service_command() {
}
cmd="$( get_service_command )"
-echo "Using system control command '$cmd' to restart Jellyfin..."
+echo "Detected service control platform '$cmd'; using it to restart Jellyfin..."
case $cmd in
'systemctl')
echo "sleep 2; /usr/bin/sudo /usr/bin/systemctl restart jellyfin" | at now
From 62105c249f59e4343a8db2ff6d2cb8af809745d5 Mon Sep 17 00:00:00 2001
From: "Joshua M. Boniface"
Date: Fri, 28 Jun 2019 11:15:08 -0400
Subject: [PATCH 080/214] Use which to find the service binary path
---
deployment/debian-package-x64/pkg-src/bin/restart.sh | 4 ++--
deployment/fedora-package-x64/pkg-src/restart.sh | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/deployment/debian-package-x64/pkg-src/bin/restart.sh b/deployment/debian-package-x64/pkg-src/bin/restart.sh
index 4c664d404e..9b64b6d728 100644
--- a/deployment/debian-package-x64/pkg-src/bin/restart.sh
+++ b/deployment/debian-package-x64/pkg-src/bin/restart.sh
@@ -24,10 +24,10 @@ cmd="$( get_service_command )"
echo "Detected service control platform '$cmd'; using it to restart Jellyfin..."
case $cmd in
'systemctl')
- echo "sleep 2; /usr/bin/sudo /usr/bin/systemctl restart jellyfin" | at now
+ echo "sleep 2; /usr/bin/sudo $( which systemctl ) restart jellyfin" | at now
;;
'service')
- echo "sleep 2; /usr/bin/sudo /sbin/service jellyfin restart" | at now
+ echo "sleep 2; /usr/bin/sudo $( which service ) jellyfin restart" | at now
;;
'sysv')
echo "sleep 2; /usr/bin/sudo /etc/init.d/jellyfin restart" | at now
diff --git a/deployment/fedora-package-x64/pkg-src/restart.sh b/deployment/fedora-package-x64/pkg-src/restart.sh
index 4c664d404e..9b64b6d728 100644
--- a/deployment/fedora-package-x64/pkg-src/restart.sh
+++ b/deployment/fedora-package-x64/pkg-src/restart.sh
@@ -24,10 +24,10 @@ cmd="$( get_service_command )"
echo "Detected service control platform '$cmd'; using it to restart Jellyfin..."
case $cmd in
'systemctl')
- echo "sleep 2; /usr/bin/sudo /usr/bin/systemctl restart jellyfin" | at now
+ echo "sleep 2; /usr/bin/sudo $( which systemctl ) restart jellyfin" | at now
;;
'service')
- echo "sleep 2; /usr/bin/sudo /sbin/service jellyfin restart" | at now
+ echo "sleep 2; /usr/bin/sudo $( which service ) jellyfin restart" | at now
;;
'sysv')
echo "sleep 2; /usr/bin/sudo /etc/init.d/jellyfin restart" | at now
From 9079b3e8da3905e4fa6dafa883f14762498199ef Mon Sep 17 00:00:00 2001
From: dkanada
Date: Sun, 30 Jun 2019 01:58:01 -0700
Subject: [PATCH 081/214] add optional password field on user creation
---
MediaBrowser.Api/UserService.cs | 22 +++++++++-------------
1 file changed, 9 insertions(+), 13 deletions(-)
diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs
index 497800d263..fa70a52aaf 100644
--- a/MediaBrowser.Api/UserService.cs
+++ b/MediaBrowser.Api/UserService.cs
@@ -214,6 +214,9 @@ namespace MediaBrowser.Api
{
[ApiMember(Name = "Name", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string Name { get; set; }
+
+ [ApiMember(Name = "Password", IsRequired = false, DataType = "string", ParameterType = "body", Verb = "POST")]
+ public string Password { get; set; }
}
[Route("/Users/ForgotPassword", "POST", Summary = "Initiates the forgot password process for a local user")]
@@ -406,7 +409,6 @@ namespace MediaBrowser.Api
PasswordSha1 = request.Password,
RemoteEndPoint = Request.RemoteIp,
Username = request.Username
-
}).ConfigureAwait(false);
return ToOptimizedResult(result);
@@ -501,27 +503,21 @@ namespace MediaBrowser.Api
}
}
- ///
- /// Posts the specified request.
- ///
- /// The request.
- /// System.Object.
public async Task Post(CreateUserByName request)
{
- var dtoUser = request;
+ var newUser = await _userManager.CreateUser(request.Name).ConfigureAwait(false);
- var newUser = await _userManager.CreateUser(dtoUser.Name).ConfigureAwait(false);
+ // no need to authenticate password for new user
+ if (request.Password != null)
+ {
+ await _userManager.ChangePassword(newUser, request.Password).ConfigureAwait(false);
+ }
var result = _userManager.GetUserDto(newUser, Request.RemoteIp);
return ToOptimizedResult(result);
}
- ///
- /// Posts the specified request.
- ///
- /// The request.
- /// System.Object.
public async Task Post(ForgotPassword request)
{
var isLocal = Request.IsLocal || _networkManager.IsInLocalNetwork(Request.RemoteIp);
From 45c13141f9ac3da07f4d15c1fb90442d3eb855e2 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Mon, 1 Jul 2019 17:59:01 +0200
Subject: [PATCH 082/214] Address comments
---
.../Data/BaseSqliteRepository.cs | 15 ++++----
.../Data/SqliteItemRepository.cs | 38 +++++++++----------
2 files changed, 27 insertions(+), 26 deletions(-)
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 7938d6b7e0..6e061c1548 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 8a56e16bb7..1cefcec7ca 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;
}
}
From 29ae7b9aeb46ac297ba5c3253d73cd4cd325c042 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Mon, 1 Jul 2019 18:24:35 +0200
Subject: [PATCH 083/214] Add docs
---
.../Data/BaseSqliteRepository.cs | 86 +++++++++++++++++++
1 file changed, 86 insertions(+)
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 6e061c1548..9bc0bb9456 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
}
}
From b719ca5a333504d2297bb1c667a3fea8447e324b Mon Sep 17 00:00:00 2001
From: trumblejoe
Date: Tue, 2 Jul 2019 13:29:28 -0400
Subject: [PATCH 084/214] Qualified Namespace of pscredential
Qualified Namespace of pscredential, otherwise script fails in Powershell builds <3.0.
---
deployment/windows/install-jellyfin.ps1 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/deployment/windows/install-jellyfin.ps1 b/deployment/windows/install-jellyfin.ps1
index 0cd7f5236f..e909a0468e 100644
--- a/deployment/windows/install-jellyfin.ps1
+++ b/deployment/windows/install-jellyfin.ps1
@@ -3,7 +3,7 @@
param(
[Switch]$Quiet,
[Switch]$InstallAsService,
- [pscredential]$ServiceUser,
+ [System.Management.Automation.pscredential]$ServiceUser,
[switch]$CreateDesktopShorcut,
[switch]$LaunchJellyfin,
[switch]$MigrateEmbyLibrary,
From 7587fe56d83fd6b3031cb11a91d94b9348c08846 Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Thu, 4 Jul 2019 20:54:57 +0200
Subject: [PATCH 085/214] Moved VACUUM down to the end of the list.
---
Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 821c4b448a..eabe7d120a 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());
From acf52b9b55ef16809b05092eda6aca2b4cafb964 Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Thu, 4 Jul 2019 20:55:49 +0200
Subject: [PATCH 086/214] Cleanup extra spaces.
---
Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index eabe7d120a..aea37ffb17 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"
From 268fe5efe8cd677d7637162e400f349f35aae181 Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Fri, 5 Jul 2019 11:11:46 +0200
Subject: [PATCH 087/214] Small attempts at making previous build downloads
work.
---
.ci/azure-pipelines.yml | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index b3389caba9..3a583b409e 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -122,19 +122,14 @@ jobs:
displayName: Download the Reference Assembly Build Artifact
inputs:
buildType: 'specific' # Options: current, specific
- project: $(System.TeamProjectId) # Required when buildType == Specific
- pipeline: $(System.DefinitionId) # Required when buildType == Specific, not sure if this will take a name too
+ project: '$(System.TeamProjectId)' # Required when buildType == Specific
+ pipeline: '$(System.DefinitionId)' # Required when buildType == Specific, not sure if this will take a name too
#specificBuildWithTriggering: false # Optional
buildVersionToDownload: 'latestFromBranch' # Required when buildType == Specific# Options: latest, latestFromBranch, specific
- allowPartiallySucceededBuilds: false # Optional
- branchName: '$(System.PullRequest.TargetBranch)' # Required when buildType == Specific && BuildVersionToDownload == LatestFromBranch
- #buildId: # Required when buildType == Specific && BuildVersionToDownload == Specific
- #tags: # Optional
+ branchName: 'refs/heads/$(System.PullRequest.TargetBranch)' # Required when buildType == Specific && BuildVersionToDownload == LatestFromBranch
downloadType: 'single' # Options: single, specific
- artifactName: '$(NugetPackageName)'# Required when downloadType == Single
- #itemPattern: '**' # Optional
+ artifactName: '$(NugetPackageName)' # Required when downloadType == Single
downloadPath: '$(System.ArtifactsDirectory)/current-artifacts'
- #parallelizationLimit: '8' # Optional
- task: CopyFiles@2
displayName: Copy Nuget Assembly to current-release folder
From 34621471957a94fb6ab15c6c8b7cd57f2aa38f64 Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Fri, 5 Jul 2019 11:17:48 +0200
Subject: [PATCH 088/214] Switched to latest ubuntu image and hardcoded
definitionId.
---
.ci/azure-pipelines.yml | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 3a583b409e..8737a03226 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -16,7 +16,7 @@ jobs:
- job: main_build
displayName: Main Build
pool:
- vmImage: ubuntu-16.04
+ vmImage: ubuntu-latest
strategy:
matrix:
release:
@@ -97,9 +97,9 @@ jobs:
- job: dotnet_compat
displayName: Compatibility Check
pool:
- vmImage: ubuntu-16.04
+ vmImage: ubuntu-latest
dependsOn: main_build
- condition: false #and(succeeded(), variables['System.PullRequest.PullRequestNumber']) # Only execute if the pullrequest numer is defined. (So not for normal CI builds)
+ condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber']) # Only execute if the pullrequest numer is defined. (So not for normal CI builds)
strategy:
matrix:
Naming:
@@ -122,8 +122,8 @@ jobs:
displayName: Download the Reference Assembly Build Artifact
inputs:
buildType: 'specific' # Options: current, specific
- project: '$(System.TeamProjectId)' # Required when buildType == Specific
- pipeline: '$(System.DefinitionId)' # Required when buildType == Specific, not sure if this will take a name too
+ project: 'jellyfin' #$(System.TeamProjectId)' # Required when buildType == Specific
+ pipeline: 1 #'$(System.DefinitionId)' # Required when buildType == Specific, not sure if this will take a name too
#specificBuildWithTriggering: false # Optional
buildVersionToDownload: 'latestFromBranch' # Required when buildType == Specific# Options: latest, latestFromBranch, specific
branchName: 'refs/heads/$(System.PullRequest.TargetBranch)' # Required when buildType == Specific && BuildVersionToDownload == LatestFromBranch
From 46623bc9859992ad76f9e63c3a99cbb5e290baa9 Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Fri, 5 Jul 2019 11:32:25 +0200
Subject: [PATCH 089/214] Try with hardcoded name.
---
.ci/azure-pipelines.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 8737a03226..8242958080 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -123,7 +123,7 @@ jobs:
inputs:
buildType: 'specific' # Options: current, specific
project: 'jellyfin' #$(System.TeamProjectId)' # Required when buildType == Specific
- pipeline: 1 #'$(System.DefinitionId)' # Required when buildType == Specific, not sure if this will take a name too
+ pipeline: 'Jellyfin CI' #'$(System.DefinitionId)' # Required when buildType == Specific, not sure if this will take a name too
#specificBuildWithTriggering: false # Optional
buildVersionToDownload: 'latestFromBranch' # Required when buildType == Specific# Options: latest, latestFromBranch, specific
branchName: 'refs/heads/$(System.PullRequest.TargetBranch)' # Required when buildType == Specific && BuildVersionToDownload == LatestFromBranch
From 272691aacd3b5c247ea3fc5a9952a27dbc3e4a30 Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Fri, 5 Jul 2019 11:37:14 +0200
Subject: [PATCH 090/214] Switched to specific download.
---
.ci/azure-pipelines.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 8242958080..9b276604a2 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -126,8 +126,8 @@ jobs:
pipeline: 'Jellyfin CI' #'$(System.DefinitionId)' # Required when buildType == Specific, not sure if this will take a name too
#specificBuildWithTriggering: false # Optional
buildVersionToDownload: 'latestFromBranch' # Required when buildType == Specific# Options: latest, latestFromBranch, specific
- branchName: 'refs/heads/$(System.PullRequest.TargetBranch)' # Required when buildType == Specific && BuildVersionToDownload == LatestFromBranch
- downloadType: 'single' # Options: single, specific
+ branchName: '$(System.PullRequest.TargetBranch)' # Required when buildType == Specific && BuildVersionToDownload == LatestFromBranch
+ downloadType: 'specific' # Options: single, specific
artifactName: '$(NugetPackageName)' # Required when downloadType == Single
downloadPath: '$(System.ArtifactsDirectory)/current-artifacts'
From ff373621b30412f9c633b207d2fec894f8cbd92e Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Fri, 5 Jul 2019 12:02:35 +0200
Subject: [PATCH 091/214] Switch to next gen artifacts.
---
.ci/azure-pipelines.yml | 58 +++++++++++++++++++++++------------------
1 file changed, 33 insertions(+), 25 deletions(-)
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 9b276604a2..08de99c4e6 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -66,32 +66,32 @@ jobs:
# artifactName: 'jellyfin-build-$(BuildConfiguration)'
# zipAfterPublish: true
- - task: PublishBuildArtifacts@1
+ - task: PublishPipelineArtifact@0
displayName: 'Publish Artifact Naming'
condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
inputs:
- PathtoPublish: '$(build.artifactstagingdirectory)/Jellyfin.Server/Emby.Naming.dll'
+ targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/Emby.Naming.dll'
artifactName: 'Jellyfin.Naming'
- - task: PublishBuildArtifacts@1
+ - task: PublishPipelineArtifact@0
displayName: 'Publish Artifact Controller'
condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
inputs:
- PathtoPublish: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Controller.dll'
+ targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Controller.dll'
artifactName: 'Jellyfin.Controller'
- - task: PublishBuildArtifacts@1
+ - task: PublishPipelineArtifact@0
displayName: 'Publish Artifact Model'
condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
inputs:
- PathtoPublish: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Model.dll'
+ targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Model.dll'
artifactName: 'Jellyfin.Model'
- - task: PublishBuildArtifacts@1
+ - task: PublishPipelineArtifact@0
displayName: 'Publish Artifact Common'
condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
inputs:
- PathtoPublish: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Common.dll'
+ targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Common.dll'
artifactName: 'Jellyfin.Common'
- job: dotnet_compat
@@ -118,18 +118,20 @@ jobs:
steps:
- checkout: none
- - task: DownloadBuildArtifacts@0
+ - task: DownloadPipelineArtifact@2
displayName: Download the Reference Assembly Build Artifact
inputs:
- buildType: 'specific' # Options: current, specific
- project: 'jellyfin' #$(System.TeamProjectId)' # Required when buildType == Specific
- pipeline: 'Jellyfin CI' #'$(System.DefinitionId)' # Required when buildType == Specific, not sure if this will take a name too
- #specificBuildWithTriggering: false # Optional
- buildVersionToDownload: 'latestFromBranch' # Required when buildType == Specific# Options: latest, latestFromBranch, specific
- branchName: '$(System.PullRequest.TargetBranch)' # Required when buildType == Specific && BuildVersionToDownload == LatestFromBranch
- downloadType: 'specific' # Options: single, specific
- artifactName: '$(NugetPackageName)' # Required when downloadType == Single
- downloadPath: '$(System.ArtifactsDirectory)/current-artifacts'
+ source: 'specific' # Options: current, specific
+ #preferTriggeringPipeline: false # Optional
+ #tags: # Optional
+ artifact: '$(NugetPackageName)' # Optional
+ #patterns: '**' # Optional
+ path: '$(System.ArtifactsDirectory)/current-artifacts'
+ project: '$(System.TeamProjectId)' # Required when source == Specific
+ pipeline: '$(System.DefinitionId)' # Required when source == Specific
+ runVersion: 'latestFromBranch' # Required when source == Specific. Options: latest, latestFromBranch, specific
+ runBranch: 'refs/heads/$(System.PullRequest.TargetBranch)' # Required when source == Specific && runVersion == LatestFromBranch
+ #runId: # Required when source == Specific && runVersion == Specific
- task: CopyFiles@2
displayName: Copy Nuget Assembly to current-release folder
@@ -139,16 +141,22 @@ jobs:
targetFolder: $(System.ArtifactsDirectory)/current-release
cleanTargetFolder: true # Optional
overWrite: true # Optional
- flattenFolders: true # Optional
+ flattenFolders: true # Optional
- - task: DownloadBuildArtifacts@0
+ - task: DownloadPipelineArtifact@2
displayName: Download the New Assembly Build Artifact
inputs:
- buildType: 'current' # Options: current, specific
- allowPartiallySucceededBuilds: false # Optional
- downloadType: 'single' # Options: single, specific
- artifactName: '$(NugetPackageName)' # Required when downloadType == Single
- downloadPath: '$(System.ArtifactsDirectory)/new-artifacts'
+ source: 'current' # Options: current, specific
+ #preferTriggeringPipeline: false # Optional
+ #tags: # Optional
+ artifact: '$(NugetPackageName)' # Optional
+ #patterns: '**' # Optional
+ path: '$(System.ArtifactsDirectory)/new-artifacts'
+ #project: # Required when source == Specific
+ #pipeline: # Required when source == Specific
+ runVersion: 'latest' # Required when source == Specific. Options: latest, latestFromBranch, specific
+ #runBranch: 'refs/heads/master' # Required when source == Specific && runVersion == LatestFromBranch
+ #runId: # Required when source == Specific && runVersion == Specific
- task: CopyFiles@2
displayName: Copy Artifact Assembly to new-release folder
From dc68fa2c8b2663e8e593ef1e9cb7c5e6e42a8d33 Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Fri, 5 Jul 2019 12:06:01 +0200
Subject: [PATCH 092/214] Disable seperate build and restore.
---
.ci/azure-pipelines.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 08de99c4e6..2a96bec071 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -35,12 +35,14 @@ jobs:
inputs:
command: restore
projects: '$(RestoreBuildProjects)'
+ enabled: false
- task: DotNetCoreCLI@2
displayName: Build
inputs:
projects: '$(RestoreBuildProjects)'
arguments: '--configuration $(BuildConfiguration)'
+ enabled: false
- task: DotNetCoreCLI@2
displayName: Test
From 08421311b969b517889a180c64cdebef09e8c807 Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Fri, 5 Jul 2019 12:10:57 +0200
Subject: [PATCH 093/214] Switch download order around.
---
.ci/azure-pipelines.yml | 50 ++++++++++++++++++++---------------------
1 file changed, 25 insertions(+), 25 deletions(-)
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 2a96bec071..3ab0aa37b0 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -120,31 +120,6 @@ jobs:
steps:
- checkout: none
- - task: DownloadPipelineArtifact@2
- displayName: Download the Reference Assembly Build Artifact
- inputs:
- source: 'specific' # Options: current, specific
- #preferTriggeringPipeline: false # Optional
- #tags: # Optional
- artifact: '$(NugetPackageName)' # Optional
- #patterns: '**' # Optional
- path: '$(System.ArtifactsDirectory)/current-artifacts'
- project: '$(System.TeamProjectId)' # Required when source == Specific
- pipeline: '$(System.DefinitionId)' # Required when source == Specific
- runVersion: 'latestFromBranch' # Required when source == Specific. Options: latest, latestFromBranch, specific
- runBranch: 'refs/heads/$(System.PullRequest.TargetBranch)' # Required when source == Specific && runVersion == LatestFromBranch
- #runId: # Required when source == Specific && runVersion == Specific
-
- - task: CopyFiles@2
- displayName: Copy Nuget Assembly to current-release folder
- inputs:
- sourceFolder: $(System.ArtifactsDirectory)/current-artifacts # Optional
- contents: '**/*.dll'
- targetFolder: $(System.ArtifactsDirectory)/current-release
- cleanTargetFolder: true # Optional
- overWrite: true # Optional
- flattenFolders: true # Optional
-
- task: DownloadPipelineArtifact@2
displayName: Download the New Assembly Build Artifact
inputs:
@@ -170,6 +145,31 @@ jobs:
overWrite: true # Optional
flattenFolders: true # Optional
+ - task: DownloadPipelineArtifact@2
+ displayName: Download the Reference Assembly Build Artifact
+ inputs:
+ source: 'specific' # Options: current, specific
+ #preferTriggeringPipeline: false # Optional
+ #tags: # Optional
+ artifact: '$(NugetPackageName)' # Optional
+ #patterns: '**' # Optional
+ path: '$(System.ArtifactsDirectory)/current-artifacts'
+ project: '$(System.TeamProjectId)' # Required when source == Specific
+ pipeline: '$(System.DefinitionId)' # Required when source == Specific
+ runVersion: 'latestFromBranch' # Required when source == Specific. Options: latest, latestFromBranch, specific
+ runBranch: 'refs/heads/$(System.PullRequest.TargetBranch)' # Required when source == Specific && runVersion == LatestFromBranch
+ #runId: # Required when source == Specific && runVersion == Specific
+
+ - task: CopyFiles@2
+ displayName: Copy Nuget Assembly to current-release folder
+ inputs:
+ sourceFolder: $(System.ArtifactsDirectory)/current-artifacts # Optional
+ contents: '**/*.dll'
+ targetFolder: $(System.ArtifactsDirectory)/current-release
+ cleanTargetFolder: true # Optional
+ overWrite: true # Optional
+ flattenFolders: true # Optional
+
- task: DownloadGitHubRelease@0
displayName: Download ABI compatibility check tool from GitHub
inputs:
From 2c8df077535f10377dd7bb59a08f94617869721e Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Fri, 5 Jul 2019 13:03:34 +0200
Subject: [PATCH 094/214] Fix some task names.
---
.ci/azure-pipelines.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 3ab0aa37b0..3884765ec1 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -136,7 +136,7 @@ jobs:
#runId: # Required when source == Specific && runVersion == Specific
- task: CopyFiles@2
- displayName: Copy Artifact Assembly to new-release folder
+ displayName: Copy New Assembly to new-release folder
inputs:
sourceFolder: $(System.ArtifactsDirectory)/new-artifacts # Optional
contents: '**/*.dll'
@@ -161,7 +161,7 @@ jobs:
#runId: # Required when source == Specific && runVersion == Specific
- task: CopyFiles@2
- displayName: Copy Nuget Assembly to current-release folder
+ displayName: Copy Reference Assembly to current-release folder
inputs:
sourceFolder: $(System.ArtifactsDirectory)/current-artifacts # Optional
contents: '**/*.dll'
From 3b9766f58c15f6300a99151f9064775deccb66a3 Mon Sep 17 00:00:00 2001
From: crankdoofus <52436708+crankdoofus@users.noreply.github.com>
Date: Sat, 6 Jul 2019 11:41:33 +1000
Subject: [PATCH 095/214] Added option for NSIS
This change will
1. download NSIS zip,
2. unzip in temp folder,
3. use nsis to build the installer
---
deployment/windows/build-jellyfin.ps1 | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/deployment/windows/build-jellyfin.ps1 b/deployment/windows/build-jellyfin.ps1
index 2999912b3a..bac907047a 100644
--- a/deployment/windows/build-jellyfin.ps1
+++ b/deployment/windows/build-jellyfin.ps1
@@ -1,5 +1,6 @@
[CmdletBinding()]
param(
+ [switch]$MakeNSIS,
[switch]$InstallFFMPEG,
[switch]$InstallNSSM,
[switch]$GenerateZip,
@@ -96,6 +97,23 @@ function Install-NSSM {
Remove-Item "$tempdir/nssm.zip" -Force -ErrorAction Continue | Write-Verbose
}
+function Make-NSIS {
+ param(
+ [string]$InstallLocation
+ )
+ Write-Verbose "Downloading NSIS"
+ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+ Invoke-WebRequest -Uri https://nchc.dl.sourceforge.net/project/nsis/NSIS%203/3.04/nsis-3.04.zip -UseBasicParsing -OutFile "$tempdir/nsis.zip" | Write-Verbose
+
+ Expand-Archive "$tempdir/nsis.zip" -DestinationPath "$tempdir/nsis/" | Write-Verbose
+ $env:InstallLocation = $InstallLocation
+ & "$tempdir/nsis/nsis-3.04/makensis.exe" ".\deployment\windows\jellyfin.nsi"
+ Copy-Item .\deployment\windows\Jellyfin.Installer.*.exe $InstallLocation\..\
+
+ Remove-Item "$tempdir/nsis/" -Recurse -Force -ErrorAction Continue | Write-Verbose
+ Remove-Item "$tempdir/nsis.zip" -Force -ErrorAction Continue | Write-Verbose
+}
+
Write-Verbose "Starting Build Process: Selected Environment is $WindowsVersion-$Architecture"
Build-JellyFin
if($InstallFFMPEG.IsPresent -or ($InstallFFMPEG -eq $true)){
@@ -106,6 +124,10 @@ if($InstallNSSM.IsPresent -or ($InstallNSSM -eq $true)){
Write-Verbose "Starting NSSM Install"
Install-NSSM $InstallLocation $Architecture
}
+if($MakeNSIS.IsPresent -or ($MakeNSIS -eq $true)){
+ Write-Verbose "Starting NSIS Package creation"
+ Make-NSIS $InstallLocation
+}
Copy-Item .\deployment\windows\install-jellyfin.ps1 $InstallLocation\install-jellyfin.ps1
Copy-Item .\deployment\windows\install.bat $InstallLocation\install.bat
if($GenerateZip.IsPresent -or ($GenerateZip -eq $true)){
From 1fd827fa775e706704ef976f5b8d179416df41a6 Mon Sep 17 00:00:00 2001
From: crankdoofus <52436708+crankdoofus@users.noreply.github.com>
Date: Sat, 6 Jul 2019 11:43:20 +1000
Subject: [PATCH 096/214] Create jellyfin.nsi
---
deployment/windows/jellyfin.nsi | 185 ++++++++++++++++++++++++++++++++
1 file changed, 185 insertions(+)
create mode 100644 deployment/windows/jellyfin.nsi
diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi
new file mode 100644
index 0000000000..b4a927610a
--- /dev/null
+++ b/deployment/windows/jellyfin.nsi
@@ -0,0 +1,185 @@
+!verbose 4
+;--------------------------------
+;Include Modern UI
+
+ !include "MUI2.nsh"
+ Var JellyfinVersion
+ Var defaultEmbyDataDir
+ Var JELLYFINDATADIR
+ Var ServiceInstalled
+;--------------------------------
+;General
+
+ ;Name and file
+ !getdllversion "$%InstallLocation%\jellyfin.dll" expv_
+ !echo "jellyfin.dll version is ${expv_1}.${expv_2}.${expv_3}.${expv_4}"
+ Name "Jellyfin ${expv_1}.${expv_2}.${expv_3}.${expv_4}"
+ OutFile "Jellyfin.Installer.${expv_1}.${expv_2}.${expv_3}.${expv_4}.exe"
+ BrandingText "Jellyfin ${expv_1}.${expv_2}.${expv_3}.${expv_4} Installer"
+ VIProductVersion "${expv_1}.${expv_2}.${expv_3}.${expv_4}"
+ VIFileVersion "${expv_1}.${expv_2}.${expv_3}.${expv_4}"
+ VIAddVersionKey "ProductName" "Jellyfin"
+ VIAddVersionKey "FileVersion" "${expv_1}.${expv_2}.${expv_3}.${expv_4}"
+
+ ;Default installation folder
+ InstallDir "$APPDATA\Jellyfin"
+
+ ;Get installation folder from registry if available
+ InstallDirRegKey HKLM "Software\Jellyfin" "InstallLocation"
+
+ ;Request application privileges for Windows Vista
+ RequestExecutionLevel admin
+ CRCCheck on
+ !define MUI_ABORTWARNING
+
+;--------------------------------
+;Pages
+
+; !insertmacro MUI_PAGE_LICENSE "${NSISDIR}\Docs\Modern UI\License.txt"
+ !insertmacro MUI_PAGE_COMPONENTS
+ !insertmacro MUI_PAGE_DIRECTORY
+
+ !define MUI_PAGE_HEADER_TEXT "MUI_PAGE_HEADER_TEXT"
+ !define MUI_PAGE_HEADER_SUBTEXT "MUI_PAGE_HEADER_SUBTEXT"
+ !define MUI_DIRECTORYPAGE_TEXT_TOP "MUI_DIRECTORYPAGE_TEXT_TOP"
+ !define MUI_DIRECTORYPAGE_TEXT_DESTINATION "APP Folder"
+ !define MUI_PAGE_CUSTOMFUNCTION_PRE ShowEmbyLibraryPage
+ !define MUI_DIRECTORYPAGE_VARIABLE $defaultEmbyDataDir
+ !insertmacro MUI_PAGE_DIRECTORY
+
+
+ !insertmacro MUI_PAGE_INSTFILES
+
+ !insertmacro MUI_UNPAGE_CONFIRM
+ !insertmacro MUI_UNPAGE_INSTFILES
+
+;--------------------------------
+;Languages
+
+ !insertmacro MUI_LANGUAGE "English"
+
+
+;--------------------------------
+;Installer Sections
+
+Section "Install Jellyfin (required)" InstallJellyfin
+ SetOutPath "$INSTDIR"
+;Create uninstaller
+
+ File /r $%InstallLocation%\*
+; Write the installation path into the registry
+ WriteRegStr HKLM "Software\Jellyfin" "InstallLocation" "$INSTDIR"
+
+; Write the uninstall keys for Windows
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "DisplayName" "Jellyfin $JellyfinVersion"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "UninstallString" '"$INSTDIR\Uninstall.exe"'
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "DisplayIcon" '"$INSTDIR\Jellyfin.exe",0'
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "Publisher" "The Jellyfin project"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "URLInfoAbout" "https://jellyfin.github.io/"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "DisplayVersion" "$JellyfinVersion"
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "NoModify" 1
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "NoRepair" 1
+ WriteUninstaller "$INSTDIR\Uninstall.exe"
+
+SectionEnd
+
+
+Section "Jellyfin Service" InstallService
+ ExecWait '"$INSTDIR"\nssm.exe install Jellyfin "$INSTDIR"\jellyfin.exe --datadir "$JELLYFINDATADIR"' $0
+ MessageBox MB_OK "Service install Error : $0"
+ Sleep 3000
+ ExecWait '"$INSTDIR"\nssm.exe set Jellyfin Start SERVICE_DELAYED_AUTO_START' $0
+ MessageBox MB_OK "Service setting Error : $0"
+ StrCpy $ServiceInstalled "YES"
+SectionEnd
+
+Section "Desktop shortcut" DesktopShortcut
+ SetShellVarContext current
+ CreateShortCut "$DESKTOP\Jellyfin.lnk" "$INSTDIR\jellyfin.exe"
+SectionEnd
+
+;TODO
+Section "Launch Jellyfin" LaunchJellyfin
+ !echo "Binaries at : $%InstallLocation%"
+; either start the service or launch jellyfin standalone
+ StrCmp $ServiceInstalled "YES" ServiceStart Standalone
+
+ ServiceStart:
+ ExecWait 'C:\Windows\System32\sc.exe start Jellyfin' $0
+ MessageBox MB_OK "Service start Error : $0"
+ Return
+
+ Standalone:
+ ExecWait '"$INSTDIR"\jellyfin.exe' $0
+ MessageBox MB_OK "start Error : $0"
+
+SectionEnd
+
+;TODO
+Section "Migrate Emby Library" MigrateEmbyLibrary
+
+ CopyFiles $defaultEmbyDataDir/config $JELLYFINDATADIR
+ CopyFiles $defaultEmbyDataDir/cache $JELLYFINDATADIR
+ CopyFiles $defaultEmbyDataDir/data $JELLYFINDATADIR
+ CopyFiles $defaultEmbyDataDir/metadata $JELLYFINDATADIR
+ CopyFiles $defaultEmbyDataDir/root $JELLYFINDATADIR
+
+SectionEnd
+
+
+;--------------------------------
+;Descriptions
+
+ ;Language strings
+ LangString DESC_InstallJellyfin ${LANG_ENGLISH} "Install Jellyfin"
+ LangString DESC_InstallService ${LANG_ENGLISH} "Install As a Service"
+ LangString DESC_DesktopShortcut ${LANG_ENGLISH} "Create a desktop shortcut"
+ LangString DESC_LaunchJellyfin ${LANG_ENGLISH} "Start Jellyfin after Install"
+ LangString DESC_MigrateEmbyLibrary ${LANG_ENGLISH} "Migrate existing Emby Library"
+
+ ;Assign language strings to sections
+ !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+ !insertmacro MUI_DESCRIPTION_TEXT ${InstallJellyfin} $(DESC_InstallJellyfin)
+ !insertmacro MUI_DESCRIPTION_TEXT ${InstallService} $(DESC_InstallService)
+ !insertmacro MUI_DESCRIPTION_TEXT ${LaunchJellyfin} $(DESC_LaunchJellyfin)
+ !insertmacro MUI_DESCRIPTION_TEXT ${MigrateEmbyLibrary} $(DESC_MigrateEmbyLibrary)
+ !insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+;--------------------------------
+;Uninstaller Section
+
+Section "Uninstall"
+
+
+;TODO
+; stop service or running instance
+ MessageBox MB_OK "uninstall $INSTDIR, $JELLYFINDATADIR"
+
+ Delete "$INSTDIR\Uninstall.exe"
+ RMDir /r "$INSTDIR"
+ RMDir /r "$JELLYFINDATADIR"
+ DeleteRegKey HKLM "Software\Jellyfin"
+ DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin"
+ Delete "$DESKTOP\Jellyfin.lnk"
+
+SectionEnd
+
+
+Function .onInit
+ SetShellVarContext all
+ !getdllversion "$%InstallLocation%\jellyfin.dll" expv_
+ StrCpy $JellyfinVersion "${expv_1}.${expv_2}.${expv_3}.${expv_4}"
+ StrCpy $JELLYFINDATADIR "$LOCALAPPDATA\jellyfin\"
+ StrCpy $ServiceInstalled "NO"
+ SectionSetFlags ${InstallJellyfin} 17
+FunctionEnd
+
+Function ShowEmbyLibraryPage
+ SectionGetFlags ${MigrateEmbyLibrary} $R0
+ IntOp $R0 $R0 & ${SF_SELECTED}
+ IntCmp $R0 ${SF_SELECTED} show
+
+ Abort ; Dont show the Emby folder selection window if Emby migrartion is not selected
+
+ show:
+FunctionEnd
From 43989800baa4e147a67c4b278e05face3ceadb5f Mon Sep 17 00:00:00 2001
From: crankdoofus <52436708+crankdoofus@users.noreply.github.com>
Date: Sat, 6 Jul 2019 12:16:34 +1000
Subject: [PATCH 097/214] Added -Force to nsis extraction
---
deployment/windows/build-jellyfin.ps1 | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/deployment/windows/build-jellyfin.ps1 b/deployment/windows/build-jellyfin.ps1
index bac907047a..0cfd78dfa5 100644
--- a/deployment/windows/build-jellyfin.ps1
+++ b/deployment/windows/build-jellyfin.ps1
@@ -30,7 +30,7 @@ function Build-JellyFin {
Write-Verbose "windowsversion-Architecture: $windowsversion-$Architecture"
Write-Verbose "InstallLocation: $InstallLocation"
Write-Verbose "DotNetVerbosity: $DotNetVerbosity"
- dotnet publish -c $BuildType -r `"$windowsversion-$Architecture`" MediaBrowser.sln -o $InstallLocation -v $DotNetVerbosity
+# dotnet publish -c $BuildType -r `"$windowsversion-$Architecture`" MediaBrowser.sln -o $InstallLocation -v $DotNetVerbosity
}
function Install-FFMPEG {
@@ -105,7 +105,7 @@ function Make-NSIS {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest -Uri https://nchc.dl.sourceforge.net/project/nsis/NSIS%203/3.04/nsis-3.04.zip -UseBasicParsing -OutFile "$tempdir/nsis.zip" | Write-Verbose
- Expand-Archive "$tempdir/nsis.zip" -DestinationPath "$tempdir/nsis/" | Write-Verbose
+ Expand-Archive "$tempdir/nsis.zip" -DestinationPath "$tempdir/nsis/" -Force | Write-Verbose
$env:InstallLocation = $InstallLocation
& "$tempdir/nsis/nsis-3.04/makensis.exe" ".\deployment\windows\jellyfin.nsi"
Copy-Item .\deployment\windows\Jellyfin.Installer.*.exe $InstallLocation\..\
@@ -114,6 +114,7 @@ function Make-NSIS {
Remove-Item "$tempdir/nsis.zip" -Force -ErrorAction Continue | Write-Verbose
}
+
Write-Verbose "Starting Build Process: Selected Environment is $WindowsVersion-$Architecture"
Build-JellyFin
if($InstallFFMPEG.IsPresent -or ($InstallFFMPEG -eq $true)){
From de9ee10abc8d0401f11dc5efed7adc4d36695db8 Mon Sep 17 00:00:00 2001
From: crankdoofus <52436708+crankdoofus@users.noreply.github.com>
Date: Sat, 6 Jul 2019 12:18:20 +1000
Subject: [PATCH 098/214] Uncomment accidental commenting of compilation
---
deployment/windows/build-jellyfin.ps1 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/deployment/windows/build-jellyfin.ps1 b/deployment/windows/build-jellyfin.ps1
index 0cfd78dfa5..7b04c93103 100644
--- a/deployment/windows/build-jellyfin.ps1
+++ b/deployment/windows/build-jellyfin.ps1
@@ -30,7 +30,7 @@ function Build-JellyFin {
Write-Verbose "windowsversion-Architecture: $windowsversion-$Architecture"
Write-Verbose "InstallLocation: $InstallLocation"
Write-Verbose "DotNetVerbosity: $DotNetVerbosity"
-# dotnet publish -c $BuildType -r `"$windowsversion-$Architecture`" MediaBrowser.sln -o $InstallLocation -v $DotNetVerbosity
+ dotnet publish -c $BuildType -r `"$windowsversion-$Architecture`" MediaBrowser.sln -o $InstallLocation -v $DotNetVerbosity
}
function Install-FFMPEG {
From a6819ffd1d7ed36f88316d7bfe82cf2e1979bd68 Mon Sep 17 00:00:00 2001
From: crankdoofus <52436708+crankdoofus@users.noreply.github.com>
Date: Sat, 6 Jul 2019 12:19:57 +1000
Subject: [PATCH 099/214] Cleaned up code
---
deployment/windows/jellyfin.nsi | 118 ++++++++++++++++++--------------
1 file changed, 65 insertions(+), 53 deletions(-)
diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi
index b4a927610a..6187cf9fdd 100644
--- a/deployment/windows/jellyfin.nsi
+++ b/deployment/windows/jellyfin.nsi
@@ -1,25 +1,30 @@
+; Shows a lot of debug information while compiling
+; This can be removed once stable.
!verbose 4
;--------------------------------
;Include Modern UI
!include "MUI2.nsh"
- Var JellyfinVersion
- Var defaultEmbyDataDir
- Var JELLYFINDATADIR
- Var ServiceInstalled
+ Var _JELLYFINVERSION_
+ Var _DEFAULTEMBYDATADIR_
+ Var _JELLYFINDATADIR_
+ Var _SERVICEINSTALLED_
;--------------------------------
;General
- ;Name and file
- !getdllversion "$%InstallLocation%\jellyfin.dll" expv_
- !echo "jellyfin.dll version is ${expv_1}.${expv_2}.${expv_3}.${expv_4}"
- Name "Jellyfin ${expv_1}.${expv_2}.${expv_3}.${expv_4}"
- OutFile "Jellyfin.Installer.${expv_1}.${expv_2}.${expv_3}.${expv_4}.exe"
- BrandingText "Jellyfin ${expv_1}.${expv_2}.${expv_3}.${expv_4} Installer"
- VIProductVersion "${expv_1}.${expv_2}.${expv_3}.${expv_4}"
- VIFileVersion "${expv_1}.${expv_2}.${expv_3}.${expv_4}"
+; Align installer version with jellyfin.dll version
+ !getdllversion "$%InstallLocation%\jellyfin.dll" ver_
+ !echo "jellyfin.dll version is ${ver_1}.${ver_2}.${ver_3}.${ver_4}" ;!echo will print it while building
+
+ Name "Jellyfin ${ver_1}.${ver_2}.${ver_3}.${ver_4}"
+ OutFile "Jellyfin.Installer.${ver_1}.${ver_2}.${ver_3}.${ver_4}.exe"
+ BrandingText "Jellyfin ${ver_1}.${ver_2}.${ver_3}.${ver_4} Installer"
+
+; installer attributes
+ VIProductVersion "${ver_1}.${ver_2}.${ver_3}.${ver_4}"
+ VIFileVersion "${ver_1}.${ver_2}.${ver_3}.${ver_4}"
VIAddVersionKey "ProductName" "Jellyfin"
- VIAddVersionKey "FileVersion" "${expv_1}.${expv_2}.${expv_3}.${expv_4}"
+ VIAddVersionKey "FileVersion" "${ver_1}.${ver_2}.${ver_3}.${ver_4}"
;Default installation folder
InstallDir "$APPDATA\Jellyfin"
@@ -35,19 +40,21 @@
;--------------------------------
;Pages
-; !insertmacro MUI_PAGE_LICENSE "${NSISDIR}\Docs\Modern UI\License.txt"
+;TODO
+;find a license to displayed before installer is started
+; !insertmacro MUI_PAGE_LICENSE "
Date: Sat, 6 Jul 2019 18:02:00 +1000
Subject: [PATCH 100/214] Changed order to include install scripts in installer
---
deployment/windows/build-jellyfin.ps1 | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/deployment/windows/build-jellyfin.ps1 b/deployment/windows/build-jellyfin.ps1
index 7b04c93103..9a6be81d02 100644
--- a/deployment/windows/build-jellyfin.ps1
+++ b/deployment/windows/build-jellyfin.ps1
@@ -125,12 +125,12 @@ if($InstallNSSM.IsPresent -or ($InstallNSSM -eq $true)){
Write-Verbose "Starting NSSM Install"
Install-NSSM $InstallLocation $Architecture
}
+Copy-Item .\deployment\windows\install-jellyfin.ps1 $InstallLocation\install-jellyfin.ps1
+Copy-Item .\deployment\windows\install.bat $InstallLocation\install.bat
if($MakeNSIS.IsPresent -or ($MakeNSIS -eq $true)){
Write-Verbose "Starting NSIS Package creation"
Make-NSIS $InstallLocation
}
-Copy-Item .\deployment\windows\install-jellyfin.ps1 $InstallLocation\install-jellyfin.ps1
-Copy-Item .\deployment\windows\install.bat $InstallLocation\install.bat
if($GenerateZip.IsPresent -or ($GenerateZip -eq $true)){
Compress-Archive -Path $InstallLocation -DestinationPath "$InstallLocation/jellyfin.zip" -Force
}
From cea6a2217ecf1aaa7e1cb59bc90c2867f68dda7a Mon Sep 17 00:00:00 2001
From: crankdoofus <52436708+crankdoofus@users.noreply.github.com>
Date: Sat, 6 Jul 2019 18:34:48 +1000
Subject: [PATCH 101/214] Correct Service handling & LocalAppData folder
The service is now completely controlled by nssm as with the install-jellyfin.ps1
The LocalAppData had the global context, its now
Corrected order of Mandatory and Optional components.
---
deployment/windows/jellyfin.nsi | 39 +++++++++++++++++++--------------
1 file changed, 22 insertions(+), 17 deletions(-)
diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi
index 6187cf9fdd..57008a6085 100644
--- a/deployment/windows/jellyfin.nsi
+++ b/deployment/windows/jellyfin.nsi
@@ -70,6 +70,8 @@
Section "Install Jellyfin (required)" InstallJellyfin
SetOutPath "$INSTDIR"
+ SetShellVarContext current
+ StrCpy $_JELLYFINDATADIR_ "$LOCALAPPDATA\jellyfin\"
; Pack all the files that were just compiled
File /r $%InstallLocation%\*
@@ -92,31 +94,27 @@ Section "Install Jellyfin (required)" InstallJellyfin
SectionEnd
-;TODO
-; This section hasn't been tested completely
-Section /o "Jellyfin Service" InstallService
- ExecWait '"$INSTDIR"\nssm.exe install Jellyfin "$INSTDIR"\jellyfin.exe --datadir "$_JELLYFINDATADIR_"' $0
- DetailPrint "Jellyfin Service install, $0"
- Sleep 3000
- ExecWait '"$INSTDIR"\nssm.exe set Jellyfin Start SERVICE_DELAYED_AUTO_START' $0
- DetailPrint "Jellyfin Service setting, $0"
- StrCpy $_SERVICEINSTALLED_ "YES"
-SectionEnd
-
Section "Jellyfin desktop shortcut" DesktopShortcut
SetShellVarContext current
DetailPrint "Creating desktop shortcut"
CreateShortCut "$DESKTOP\Jellyfin.lnk" "$INSTDIR\jellyfin.exe"
SectionEnd
-;TODO
-; This section hasn't been tested completely.
+Section /o "Jellyfin Service" InstallService
+ ExecWait '"$INSTDIR\nssm.exe" install Jellyfin "$INSTDIR\jellyfin.exe" --datadir "$_JELLYFINDATADIR_"' $0
+ DetailPrint "Jellyfin Service install, $0"
+ Sleep 3000
+ ExecWait '"$INSTDIR\nssm.exe" set Jellyfin Start SERVICE_DELAYED_AUTO_START' $0
+ DetailPrint "Jellyfin Service setting, $0"
+ StrCpy $_SERVICEINSTALLED_ "YES"
+SectionEnd
+
Section /o "Start Jellyfin after installation" LaunchJellyfin
; either start the service or launch jellyfin standalone
StrCmp $_SERVICEINSTALLED_ "YES" ServiceStart Standalone
ServiceStart:
- ExecWait 'C:\Windows\System32\sc.exe start Jellyfin' $0
+ ExecWait '"$INSTDIR\nssm.exe" start Jellyfin' $0
DetailPrint "Jellyfin service start, $0"
Return
@@ -162,9 +160,14 @@ SectionEnd
;Uninstaller Section
Section "Uninstall"
+ SetShellVarContext current
+ StrCpy $_JELLYFINDATADIR_ "$LOCALAPPDATA\jellyfin\"
;TODO
-; stop service or running instance
-; Figure out a way to stop Jellyfin - either standalone or service when uninstaller is invoked
+; stop running instance
+ ExecWait '"$INSTDIR\nssm.exe" stop Jellyfin' $0
+ DetailPrint "Jellyfin service stop, $0"
+ ExecWait '"$INSTDIR\nssm.exe" remove Jellyfin confirm' $0
+ DetailPrint "Jellyfin Service remove, $0"
Delete "$INSTDIR\Uninstall.exe"
RMDir /r "$INSTDIR"
@@ -181,7 +184,9 @@ Function .onInit
; Align installer version with jellyfin.dll version
!getdllversion "$%InstallLocation%\jellyfin.dll" ver_
StrCpy $_JELLYFINVERSION_ "${ver_1}.${ver_2}.${ver_3}.${ver_4}"
- StrCpy $_JELLYFINDATADIR_ "$LOCALAPPDATA\jellyfin\"
+ SetShellVarContext current
+ StrCpy $_JELLYFINDATADIR_ "$LOCALAPPDATA\jellyfin\"
+ DetailPrint "_JELLYFINDATADIR_ : $_JELLYFINDATADIR_"
StrCpy $_SERVICEINSTALLED_ "NO"
SectionSetFlags ${InstallJellyfin} 17 ; this makes the InstallJellyfin section mandatory
FunctionEnd
From 0f897589ed6349bb3c88919b06861daa80aec1be Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Tue, 21 May 2019 19:28:34 +0200
Subject: [PATCH 102/214] Streamline authentication proccess
---
.../Cryptography/CryptographyProvider.cs | 88 +++---
.../Library/DefaultAuthenticationProvider.cs | 115 ++++----
.../Library/DefaultPasswordResetProvider.cs | 257 +++++++++---------
.../Library/InvalidAuthProvider.cs | 11 +-
.../Library/UserManager.cs | 49 ++--
MediaBrowser.Api/LiveTv/LiveTvService.cs | 19 --
.../Authentication/AuthenticationException.cs | 28 ++
.../Authentication/IAuthenticationProvider.cs | 3 +-
.../Authentication/IPasswordResetProvider.cs | 1 +
.../Cryptography/ICryptoProvider.cs | 6 +-
.../Cryptography/PasswordHash.cs | 164 ++++++-----
11 files changed, 372 insertions(+), 369 deletions(-)
create mode 100644 MediaBrowser.Controller/Authentication/AuthenticationException.cs
diff --git a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
index 6d7193ce20..f726dae2ee 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 Guid GetMD5(string str)
- {
- return new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str)));
- }
+ public string DefaultHashMethod => "PBKDF2";
+ [Obsolete("Use System.Security.Cryptography.MD5 directly")]
+ public Guid GetMD5(string 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 fe09b07ff6..b07244fda1 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 e218749d90..c7044820c7 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 25d2331373..6956369dc1 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 1701ced426..c8c8a108d5 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 e41ad540ad..8a4d6e2161 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 0000000000..045cbcdaed
--- /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 2cf531eed5..f5571065f2 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 9e5cd88160..2639960e76 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 5988112c2e..9e85beb43c 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 f15b27d325..df32fdb003 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 byte[] _hash;
- private string _hash;
+ public PasswordHash(string storageString)
+ {
+ string[] splitted = storageString.Split('$');
+ // 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));
+ }
- private byte[] _hashBytes;
+ // 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[index++].Split(','))
+ {
+ if (string.IsNullOrEmpty(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]);
+ }
+ }
+
+ // Check if the string also contains a salt
+ if (splitted.Length - index == 2)
+ {
+ _salt = ConvertFromByteString(splitted[index++]);
+ _hash = ConvertFromByteString(splitted[index++]);
+ }
+ else
+ {
+ _salt = Array.Empty();
+ _hash = ConvertFromByteString(splitted[index++]);
+ }
+ }
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[] 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; }
-
- public PasswordHash(string storageString)
- {
- string[] splitted = storageString.Split('$');
- _id = splitted[1];
- if (splitted[2].Contains("="))
- {
- foreach (string paramset in (splitted[2].Split(',')))
- {
- 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}");
- }
- }
- }
- 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);
- }
- }
- 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);
- }
-
- }
-
- }
+ 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))
+ var str = new StringBuilder();
+ str.Append('$');
+ str.Append(_id);
+ SerializeParameters(str);
+
+ if (_salt.Length == 0)
{
- outString += $"${paramstring}";
+ str.Append('$');
+ str.Append(ConvertToByteString(_salt));
}
- if (!string.IsNullOrEmpty(_salt))
- {
- outString += $"${_salt}";
- }
+ str.Append('$');
+ str.Append(ConvertToByteString(_hash));
- outString += $"${_hash}";
- return outString;
+ return str.ToString();
}
}
-
}
From ab7ef9c9cb951111eec5132e9c6a00aa0d1b111b Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Sat, 6 Jul 2019 16:15:38 +0200
Subject: [PATCH 103/214] Fix style issues
---
.../Library/CoreResolutionIgnoreRule.cs | 69 ++++---------------
.../Library/Resolvers/Movies/MovieResolver.cs | 27 ++++----
.../Resolvers/IResolverIgnoreRule.cs | 6 ++
3 files changed, 35 insertions(+), 67 deletions(-)
diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index a70077163e..f1ae2fc9c0 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 47c3e71d72..1b63b00a34 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 b40cc157a8..bb80e60256 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);
}
}
From b3f9d045012310216aca5bbf70f3bcc8c36febc9 Mon Sep 17 00:00:00 2001
From: "Joshua M. Boniface"
Date: Sat, 6 Jul 2019 13:34:38 -0400
Subject: [PATCH 104/214] Bump version for 10.3.6
---
Dockerfile | 2 +-
Dockerfile.arm | 2 +-
Dockerfile.arm64 | 2 +-
MediaBrowser.WebDashboard/jellyfin-web | 2 +-
SharedVersion.cs | 4 ++--
build.yaml | 2 +-
deployment/debian-package-x64/pkg-src/changelog | 6 ++++++
deployment/fedora-package-x64/pkg-src/jellyfin.spec | 4 +++-
8 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index c971f1cc13..864cfa445b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -21,7 +21,7 @@ RUN apt-get update \
COPY --from=ffmpeg / /
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.5
+ARG JELLYFIN_WEB_VERSION=10.3.6
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
diff --git a/Dockerfile.arm b/Dockerfile.arm
index 4847c726be..8167390632 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -26,7 +26,7 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.5
+ARG JELLYFIN_WEB_VERSION=10.3.6
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index a26cfc7b3b..1843f7224b 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -26,7 +26,7 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.5
+ARG JELLYFIN_WEB_VERSION=10.3.6
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
diff --git a/MediaBrowser.WebDashboard/jellyfin-web b/MediaBrowser.WebDashboard/jellyfin-web
index 37636dae5c..c9e70d9564 160000
--- a/MediaBrowser.WebDashboard/jellyfin-web
+++ b/MediaBrowser.WebDashboard/jellyfin-web
@@ -1 +1 @@
-Subproject commit 37636dae5c6c0b0711dfc7612f843b864dd59469
+Subproject commit c9e70d95643e84437189dd500b0380ec0fbbf659
diff --git a/SharedVersion.cs b/SharedVersion.cs
index 27ba1cf2cb..1e74d8f7d6 100644
--- a/SharedVersion.cs
+++ b/SharedVersion.cs
@@ -1,4 +1,4 @@
using System.Reflection;
-[assembly: AssemblyVersion("10.3.5")]
-[assembly: AssemblyFileVersion("10.3.5")]
+[assembly: AssemblyVersion("10.3.6")]
+[assembly: AssemblyFileVersion("10.3.6")]
diff --git a/build.yaml b/build.yaml
index cb010ed597..7a5e66b112 100644
--- a/build.yaml
+++ b/build.yaml
@@ -1,7 +1,7 @@
---
# We just wrap `build` so this is really it
name: "jellyfin"
-version: "10.3.5"
+version: "10.3.6"
packages:
- debian-package-x64
- debian-package-armhf
diff --git a/deployment/debian-package-x64/pkg-src/changelog b/deployment/debian-package-x64/pkg-src/changelog
index 94d0c87dfc..64912a11d1 100644
--- a/deployment/debian-package-x64/pkg-src/changelog
+++ b/deployment/debian-package-x64/pkg-src/changelog
@@ -1,3 +1,9 @@
+jellyfin (10.3.6-1) unstable; urgency=medium
+
+ * New upstream version 10.3.6; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.6
+
+ -- Jellyfin Packaging Team Sat, 06 Jul 2019 13:34:19 -0400
+
jellyfin (10.3.5-1) unstable; urgency=medium
* New upstream version 10.3.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.5
diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
index aeea7ecd00..809bde39d6 100644
--- a/deployment/fedora-package-x64/pkg-src/jellyfin.spec
+++ b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
@@ -7,7 +7,7 @@
%endif
Name: jellyfin
-Version: 10.3.5
+Version: 10.3.6
Release: 1%{?dist}
Summary: The Free Software Media Browser
License: GPLv2
@@ -140,6 +140,8 @@ fi
%systemd_postun_with_restart jellyfin.service
%changelog
+* Sat Jul 06 2019 Jellyfin Packaging Team
+- New upstream version 10.3.6; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.6
* Sun Jun 09 2019 Jellyfin Packaging Team
- New upstream version 10.3.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.5
* Thu Jun 06 2019 Jellyfin Packaging Team
From d405a400aaa5f9676cc2ce9159b562f94233dcd5 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Fri, 14 Jun 2019 16:32:37 +0200
Subject: [PATCH 105/214] Fixes issues with HttpClientManager
---
Emby.Dlna/PlayTo/SsdpHttpClient.cs | 22 +--
.../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(-)
diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
index 1ad99fac5b..780b0a8897 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 02d661d15a..c64922a931 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 b82d55d0e6..987657bcb1 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)
{
- var responseInfo = new HttpResponseInfo(disposable)
+ 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()
{
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 dd636e6cdf..8dee7046e7 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 f3f7477180..f5dffc22af 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 69b10e6daa..d39c917835 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 e8b34da0ca..60c914898c 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 d4b10c8c84..95ce08f269 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 38e0ff0f55..432e389d3b 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 1866741676..aa496adaca 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
}
}
}
From 3603c64fa6033eca4a92c4f537fddc182817998a Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Fri, 14 Jun 2019 17:14:42 +0200
Subject: [PATCH 106/214] Use HttpResponseHeaders instead of a dictionary
---
.../HttpClientManager/HttpClientManager.cs | 67 +++++--------------
MediaBrowser.Common/Net/HttpResponseInfo.cs | 11 ++-
2 files changed, 26 insertions(+), 52 deletions(-)
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index 987657bcb1..7af0efc173 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);
- }
- }
+ 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 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;
- }
-
- 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 aa496adaca..cd9feabfe0 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()
From b117b364f2f60db33a100a46d12ff5f2b2c4193d Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Fri, 14 Jun 2019 17:31:56 +0200
Subject: [PATCH 107/214] Remove duplicate code
---
.../HttpClientManager/HttpClientManager.cs | 122 +++++++-----------
1 file changed, 50 insertions(+), 72 deletions(-)
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index 7af0efc173..b8c52a53fb 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);
- }
- }
- catch (Exception ex)
+ if (options.RequestContentBytes != null)
{
- throw new HttpException(ex.Message) { IsTimedOut = true };
+ 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);*/
}
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();
-
- /*if (!options.BufferContent)
- {
- var response = await client.HttpClient.SendAsync(httpWebRequest).ConfigureAwait(false);
-
- await EnsureSuccessStatusCode(client, response, options).ConfigureAwait(false);
-
- options.CancellationToken.ThrowIfCancellationRequested();
-
- 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)
- {
- throw GetCancellationException(options, options.CancellationToken, ex);
- }
- }
-
- 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))
+ /*if (!options.BufferContent)
{
- var memoryStream = new MemoryStream();
- await stream.CopyToAsync(memoryStream, 81920, options.CancellationToken).ConfigureAwait(false);
- memoryStream.Position = 0;
+ var response = await client.HttpClient.SendAsync(httpWebRequest).ConfigureAwait(false);
- var responseInfo = new HttpResponseInfo(response.Headers)
+ await EnsureSuccessStatusCode(client, response, options).ConfigureAwait(false);
+
+ options.CancellationToken.ThrowIfCancellationRequested();
+
+ return GetResponseInfo(response, await response.Content.ReadAsStreamAsync().ConfigureAwait(false), response.Content.Headers.ContentLength, response);
+ }*/
+
+ 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))
{
- 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)
{
From 5fc4ad6c4e9aab8246e70a064c8506d050cf2147 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Mon, 1 Jul 2019 19:24:42 +0200
Subject: [PATCH 108/214] Address comments
---
.../HttpClientManager/HttpClientManager.cs | 58 ++++++++-----------
MediaBrowser.Common/Net/HttpRequestOptions.cs | 1 +
2 files changed, 26 insertions(+), 33 deletions(-)
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index b8c52a53fb..ae62f34e06 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.
+ /// Gets the correct http client for the given url.
///
- /// The HTTP clients.
- private readonly ConcurrentDictionary _httpClients = new ConcurrentDictionary();
-
- ///
- /// Gets
- ///
- /// 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 432e389d3b..0576a1a5d9 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,
From 0dbc294836bbf1f11e370efeeae0904d3bb099e0 Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Sat, 6 Jul 2019 23:50:06 +0200
Subject: [PATCH 109/214] 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(-)
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 919453d2a4..010ad63846 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;
}
From fc55b44e4bc2c00817d69f59deb4448afbcd6123 Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Sun, 7 Jul 2019 00:03:59 +0200
Subject: [PATCH 110/214] Update the GitHub connection name to use the GitHub
App token
---
.ci/azure-pipelines.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 3884765ec1..9dc1133339 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -173,7 +173,7 @@ jobs:
- task: DownloadGitHubRelease@0
displayName: Download ABI compatibility check tool from GitHub
inputs:
- connection: Jellyfin GitHub
+ connection: jellyfin
userRepository: EraYaN/dotnet-compatibility
defaultVersionType: 'latest' # Options: latest, specificVersion, specificTag
#version: # Required when defaultVersionType != Latest
From 440177a43db1ddbb3cf4b0dc52d6dfb6d79c2b1c Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Sun, 7 Jul 2019 00:20:37 +0200
Subject: [PATCH 111/214] Update the GitHub token to a Grant Auth token.
---
.ci/azure-pipelines.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 9dc1133339..efd69680e4 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -173,7 +173,7 @@ jobs:
- task: DownloadGitHubRelease@0
displayName: Download ABI compatibility check tool from GitHub
inputs:
- connection: jellyfin
+ connection: Jellyfin Release Download
userRepository: EraYaN/dotnet-compatibility
defaultVersionType: 'latest' # Options: latest, specificVersion, specificTag
#version: # Required when defaultVersionType != Latest
From c7d12cc48134ea4fd8f7169f730a47dd3a176bae Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Sun, 7 Jul 2019 00:43:43 +0200
Subject: [PATCH 112/214] Fix merge errors
---
Jellyfin.Server/Program.cs | 7 -------
1 file changed, 7 deletions(-)
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 75b820b8e1..94308a98e8 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -120,13 +120,6 @@ 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
-
- // 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;
From 3d0e7f6cb6aac6622ad0df413c57b85d83679693 Mon Sep 17 00:00:00 2001
From: crankdoofus <52436708+crankdoofus@users.noreply.github.com>
Date: Sun, 7 Jul 2019 09:10:35 +1000
Subject: [PATCH 113/214] Include License file with installation
---
deployment/windows/build-jellyfin.ps1 | 1 +
1 file changed, 1 insertion(+)
diff --git a/deployment/windows/build-jellyfin.ps1 b/deployment/windows/build-jellyfin.ps1
index 9a6be81d02..b049c351a2 100644
--- a/deployment/windows/build-jellyfin.ps1
+++ b/deployment/windows/build-jellyfin.ps1
@@ -127,6 +127,7 @@ if($InstallNSSM.IsPresent -or ($InstallNSSM -eq $true)){
}
Copy-Item .\deployment\windows\install-jellyfin.ps1 $InstallLocation\install-jellyfin.ps1
Copy-Item .\deployment\windows\install.bat $InstallLocation\install.bat
+Copy-Item .\LICENSE $InstallLocation\LICENSE
if($MakeNSIS.IsPresent -or ($MakeNSIS -eq $true)){
Write-Verbose "Starting NSIS Package creation"
Make-NSIS $InstallLocation
From da71354e82276647c1e687ca677acc3b4e1c3a54 Mon Sep 17 00:00:00 2001
From: crankdoofus <52436708+crankdoofus@users.noreply.github.com>
Date: Sun, 7 Jul 2019 09:13:27 +1000
Subject: [PATCH 114/214] Remove Emby migration section, include License
---
deployment/windows/jellyfin.nsi | 60 ++++++++++++++++-----------------
1 file changed, 30 insertions(+), 30 deletions(-)
diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi
index 57008a6085..98bf5d366d 100644
--- a/deployment/windows/jellyfin.nsi
+++ b/deployment/windows/jellyfin.nsi
@@ -6,7 +6,7 @@
!include "MUI2.nsh"
Var _JELLYFINVERSION_
- Var _DEFAULTEMBYDATADIR_
+; Var _DEFAULTEMBYDATADIR_
Var _JELLYFINDATADIR_
Var _SERVICEINSTALLED_
;--------------------------------
@@ -40,20 +40,20 @@
;--------------------------------
;Pages
-;TODO
-;find a license to displayed before installer is started
-; !insertmacro MUI_PAGE_LICENSE "
Date: Sun, 7 Jul 2019 09:17:03 +1000
Subject: [PATCH 115/214] Correct comment
---
deployment/windows/jellyfin.nsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi
index 98bf5d366d..d9a30a0fc5 100644
--- a/deployment/windows/jellyfin.nsi
+++ b/deployment/windows/jellyfin.nsi
@@ -32,7 +32,7 @@
;Get installation folder from registry if available
InstallDirRegKey HKLM "Software\Jellyfin" "InstallLocation"
- ;Request application privileges for Windows Vista
+ ;Request application privileges for service installation
RequestExecutionLevel admin
CRCCheck on
!define MUI_ABORTWARNING
From aff72323c6d95e15efad88c4415bb33565e62ca2 Mon Sep 17 00:00:00 2001
From: crankdoofus <52436708+crankdoofus@users.noreply.github.com>
Date: Tue, 9 Jul 2019 22:56:23 +1000
Subject: [PATCH 116/214] Update code for in-place upgrades
---
deployment/windows/jellyfin.nsi | 99 +++++++++++++++++++++++++++++----
1 file changed, 89 insertions(+), 10 deletions(-)
diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi
index d9a30a0fc5..6198f9c039 100644
--- a/deployment/windows/jellyfin.nsi
+++ b/deployment/windows/jellyfin.nsi
@@ -9,6 +9,8 @@
; Var _DEFAULTEMBYDATADIR_
Var _JELLYFINDATADIR_
Var _SERVICEINSTALLED_
+ Var _EXISTINGINSTALLATION_
+ Var _EXISTINGSERVICE_
;--------------------------------
;General
@@ -25,14 +27,16 @@
VIFileVersion "${ver_1}.${ver_2}.${ver_3}.${ver_4}"
VIAddVersionKey "ProductName" "Jellyfin"
VIAddVersionKey "FileVersion" "${ver_1}.${ver_2}.${ver_3}.${ver_4}"
-
- ;Default installation folder
+ VIAddVersionKey "LegalCopyright" "Jellyfin, Free Software Media System"
+ VIAddVersionKey "FileDescription" "Jellyfin, Free Software Media System"
+
+;Default installation folder
InstallDir "$APPDATA\Jellyfin"
- ;Get installation folder from registry if available
+;Get installation folder from registry if available
InstallDirRegKey HKLM "Software\Jellyfin" "InstallLocation"
- ;Request application privileges for service installation
+;Request application privileges for service installation
RequestExecutionLevel admin
CRCCheck on
!define MUI_ABORTWARNING
@@ -42,6 +46,7 @@
!insertmacro MUI_PAGE_LICENSE "$%InstallLocation%\LICENSE"
!insertmacro MUI_PAGE_COMPONENTS
+ !define MUI_PAGE_CUSTOMFUNCTION_PRE HideDirectoryPage
!insertmacro MUI_PAGE_DIRECTORY
; Custom Directory page to ask for Emby Library location in case its needed
@@ -69,6 +74,20 @@
;Installer Sections
Section "Install Jellyfin (required)" InstallJellyfin
+ StrCmp $_EXISTINGINSTALLATION_ "YES" CheckService CarryOn
+
+ CheckService:
+ StrCmp $_EXISTINGSERVICE_ "YES" StopService ExistingInstallButNotService
+
+ StopService: ; we stop the service to copy files in use
+ ExecWait '"$INSTDIR\nssm.exe" stop Jellyfin' $0
+ DetailPrint "Jellyfin service stop, $0"
+
+ ExistingInstallButNotService:
+;TODO, find a way to kill the process in case it was started as standalone
+ MessageBox MB_OK|MB_ICONINFORMATION "Please stop Jellyfin manually before proceeding further."
+
+ CarryOn:
SetOutPath "$INSTDIR"
SetShellVarContext current
StrCpy $_JELLYFINDATADIR_ "$LOCALAPPDATA\jellyfin\"
@@ -94,7 +113,7 @@ Section "Install Jellyfin (required)" InstallJellyfin
SectionEnd
-Section "Jellyfin desktop shortcut" DesktopShortcut
+Section /o "Jellyfin desktop shortcut" DesktopShortcut
SetShellVarContext current
DetailPrint "Creating desktop shortcut"
CreateShortCut "$DESKTOP\Jellyfin.lnk" "$INSTDIR\jellyfin.exe"
@@ -119,8 +138,9 @@ Section /o "Start Jellyfin after installation" LaunchJellyfin
Return
Standalone:
- ExecWait '"$INSTDIR"\jellyfin.exe' $0
+ ExecWait '"$INSTDIR\jellyfin.exe"' $0
DetailPrint "$INSTDIR\jellyfin.exe start, $0"
+ Return
SectionEnd
@@ -161,16 +181,26 @@ SectionEnd
Section "Uninstall"
SetShellVarContext current
StrCpy $_JELLYFINDATADIR_ "$LOCALAPPDATA\jellyfin\"
-;TODO
-; stop running instance
+
+; Currently we try to stop & remove a running service even if it doesn't exist
+; not really sure about nssm statuscode detection method
+; nothing to loose with brute force stop & remove method
ExecWait '"$INSTDIR\nssm.exe" stop Jellyfin' $0
DetailPrint "Jellyfin service stop, $0"
ExecWait '"$INSTDIR\nssm.exe" remove Jellyfin confirm' $0
DetailPrint "Jellyfin Service remove, $0"
Delete "$INSTDIR\Uninstall.exe"
- RMDir /r "$INSTDIR"
- RMDir /r "$_JELLYFINDATADIR_"
+
+;TODO
+; stop running instance gracefully, in case its running, the /REBOOTOK flag will delete it on reboot.
+ RMDir /r /REBOOTOK "$INSTDIR" ;
+
+ MessageBox MB_YESNO|MB_ICONINFORMATION "Do you want to retain Jellyfin settings ? The media will not be touched in any case." /SD IDYES IDYES PreserveData
+ RMDir /r /REBOOTOK "$_JELLYFINDATADIR_"
+
+ PreserveData:
+
DeleteRegKey HKLM "Software\Jellyfin"
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin"
Delete "$DESKTOP\Jellyfin.lnk"
@@ -179,6 +209,46 @@ SectionEnd
Function .onInit
+ StrCpy $_EXISTINGINSTALLATION_ "NO"
+ StrCpy $_EXISTINGSERVICE_ "NO"
+;Detect if Jellyfin is already installed.
+; In case it is installed, let the user choose either
+; 1. Exit installer
+; 2. Upgrade without messing with data
+; 2a. Don't ask for any installation folder
+; 2b. If there is a service, don't ask for service installation or launch
+; 2c. If no service, ask for autolaunch, and start as standalone
+
+; Read Registry for installation
+ ClearErrors
+ ReadRegStr "$0" HKLM "Software\Jellyfin" "InstallLocation"
+ IfErrors NoExisitingInstall
+
+ DetailPrint "Existing Jellyfin detected at: $0"
+ StrCpy "$INSTDIR" "$0"
+ StrCpy $_EXISTINGINSTALLATION_ "YES"
+ SectionSetText ${InstallJellyfin} "Upgrade Jellyfin" ; Change install text to "Upgrade"
+
+; check if there is a service called Jellyfin
+; hack : nssm statuscode Jellyfin will return non zero return code in case it exists
+ ExecWait '"$INSTDIR\nssm.exe" statuscode Jellyfin' $0
+ DetailPrint "Jellyfin service statuscode, $0"
+ IntCmp $0 0 NoService ; service doesn't exist
+
+ StrCpy $_EXISTINGSERVICE_ "YES"
+ SectionSetText ${InstallService} "" ; hide service install option if old install was a service
+ SectionSetText ${LaunchJellyfin} "" ; hide service start option if old install was a service
+ SectionSetText ${DesktopShortcut} "" ; hide desktop shortcut option too
+
+ NoService:
+; if detected, let the user know that we'll upgrade and its ok to quit
+ MessageBox MB_OKCANCEL|MB_ICONINFORMATION "Existing installation of Jellyfin was detected, it'll be upgraded, settings will be retained" /SD IDOK IDOK Proceed
+ Quit ; Quit if the user is not sure about upgrade
+
+ Proceed:
+
+ NoExisitingInstall:
+
SetShellVarContext all
; Align installer version with jellyfin.dll version
!getdllversion "$%InstallLocation%\jellyfin.dll" ver_
@@ -188,6 +258,15 @@ Function .onInit
DetailPrint "_JELLYFINDATADIR_ : $_JELLYFINDATADIR_"
StrCpy $_SERVICEINSTALLED_ "NO"
SectionSetFlags ${InstallJellyfin} 17 ; this makes the InstallJellyfin section mandatory
+
+FunctionEnd
+
+Function HideDirectoryPage
+ StrCmp $_EXISTINGINSTALLATION_ "NO" show
+
+ Abort ; Dont show folder selection if just upgrading
+
+ show:
FunctionEnd
; This can be uncommented in case Emby Migration is planned later
From e31851d25ee08f648bc9a31a6cea8cc2e67ce6cd Mon Sep 17 00:00:00 2001
From: crankdoofus <52436708+crankdoofus@users.noreply.github.com>
Date: Sat, 13 Jul 2019 21:16:56 +1000
Subject: [PATCH 117/214] Update to uninstall silently if already installed
---
deployment/windows/jellyfin.nsi | 95 ++++++++++++---------------------
1 file changed, 33 insertions(+), 62 deletions(-)
diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi
index 6198f9c039..3a11bf1d83 100644
--- a/deployment/windows/jellyfin.nsi
+++ b/deployment/windows/jellyfin.nsi
@@ -2,15 +2,15 @@
; This can be removed once stable.
!verbose 4
;--------------------------------
-;Include Modern UI
!include "MUI2.nsh"
+
Var _JELLYFINVERSION_
-; Var _DEFAULTEMBYDATADIR_
Var _JELLYFINDATADIR_
Var _SERVICEINSTALLED_
Var _EXISTINGINSTALLATION_
Var _EXISTINGSERVICE_
+
;--------------------------------
;General
@@ -47,21 +47,9 @@
!insertmacro MUI_PAGE_LICENSE "$%InstallLocation%\LICENSE"
!insertmacro MUI_PAGE_COMPONENTS
!define MUI_PAGE_CUSTOMFUNCTION_PRE HideDirectoryPage
- !insertmacro MUI_PAGE_DIRECTORY
-
-; Custom Directory page to ask for Emby Library location in case its needed
-; Commented for now to avoid showing this.
-; This can be uncommented in case Emby Migration is planned later
-; !define MUI_PAGE_HEADER_TEXT "Emby Library locaton"
-; !define MUI_PAGE_HEADER_SUBTEXT ""
-; !define MUI_DIRECTORYPAGE_TEXT_TOP "Please select the folder where Emby library is present. This will have Enby folders like config, cache, data, metadata, etc."
-; !define MUI_DIRECTORYPAGE_TEXT_DESTINATION "Emby Library location"
-; !define MUI_PAGE_CUSTOMFUNCTION_PRE ShowEmbyLibraryPage
-; !define MUI_DIRECTORYPAGE_VARIABLE $_DEFAULTEMBYDATADIR_
-; !insertmacro MUI_PAGE_DIRECTORY
-
+ !insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
-
+
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
@@ -74,25 +62,29 @@
;Installer Sections
Section "Install Jellyfin (required)" InstallJellyfin
- StrCmp $_EXISTINGINSTALLATION_ "YES" CheckService CarryOn
-
- CheckService:
- StrCmp $_EXISTINGSERVICE_ "YES" StopService ExistingInstallButNotService
-
- StopService: ; we stop the service to copy files in use
- ExecWait '"$INSTDIR\nssm.exe" stop Jellyfin' $0
- DetailPrint "Jellyfin service stop, $0"
-
- ExistingInstallButNotService:
-;TODO, find a way to kill the process in case it was started as standalone
- MessageBox MB_OK|MB_ICONINFORMATION "Please stop Jellyfin manually before proceeding further."
+; If previous installation is present, run the uninstaller silently
+ StrCmp "$_EXISTINGINSTALLATION_" "YES" RunUninstaller CarryOn
+ RunUninstaller:
+ DetailPrint "Looking for uninstaller at $INSTDIR"
+ FindFirst $0 $1 "$INSTDIR\Uninstall.exe"
+ FindClose $0
+ StrCmp $1 "" CarryOn ; the registry key was there but uninstaller was not found
+
+ DetailPrint "Silently running the uninstaller at $INSTDIR"
+ ExecWait '"$INSTDIR\Uninstall.exe" /S _?=$INSTDIR' $0
+ DetailPrint "Uninstall finished, $0"
+ Delete "$INSTDIR\Uninstall.exe"
+
CarryOn:
+
SetOutPath "$INSTDIR"
- SetShellVarContext current
+
+ SetShellVarContext current ; Local App Data folder for current user only
StrCpy $_JELLYFINDATADIR_ "$LOCALAPPDATA\jellyfin\"
; Pack all the files that were just compiled
+; These will be exploded to $INSTDIR
File /r $%InstallLocation%\*
; Write the installation path into the registry
@@ -114,12 +106,12 @@ Section "Install Jellyfin (required)" InstallJellyfin
SectionEnd
Section /o "Jellyfin desktop shortcut" DesktopShortcut
- SetShellVarContext current
+ SetShellVarContext current ; to create the shortcut for current user only
DetailPrint "Creating desktop shortcut"
CreateShortCut "$DESKTOP\Jellyfin.lnk" "$INSTDIR\jellyfin.exe"
SectionEnd
-Section /o "Jellyfin Service" InstallService
+Section "Jellyfin Service" InstallService
ExecWait '"$INSTDIR\nssm.exe" install Jellyfin "$INSTDIR\jellyfin.exe" --datadir "$_JELLYFINDATADIR_"' $0
DetailPrint "Jellyfin Service install, $0"
Sleep 3000
@@ -128,7 +120,7 @@ Section /o "Jellyfin Service" InstallService
StrCpy $_SERVICEINSTALLED_ "YES"
SectionEnd
-Section /o "Start Jellyfin after installation" LaunchJellyfin
+Section "Start Jellyfin after installation" LaunchJellyfin
; either start the service or launch jellyfin standalone
StrCmp $_SERVICEINSTALLED_ "YES" ServiceStart Standalone
@@ -144,35 +136,21 @@ Section /o "Start Jellyfin after installation" LaunchJellyfin
SectionEnd
-; This can be uncommented in case Emby Migration is planned later
-;Section /o "Migrate Emby Library" MigrateEmbyLibrary
-; DetailPrint "Migrating Emby Library"
-; CopyFiles $_DEFAULTEMBYDATADIR_/config $_JELLYFINDATADIR_
-; CopyFiles $_DEFAULTEMBYDATADIR_/cache $_JELLYFINDATADIR_
-; CopyFiles $_DEFAULTEMBYDATADIR_/data $_JELLYFINDATADIR_
-; CopyFiles $_DEFAULTEMBYDATADIR_/metadata $_JELLYFINDATADIR_
-; CopyFiles $_DEFAULTEMBYDATADIR_/root $_JELLYFINDATADIR_
-
-;SectionEnd
-
-
;--------------------------------
;Descriptions
- ;Language strings
+;Language strings
LangString DESC_InstallJellyfin ${LANG_ENGLISH} "Install Jellyfin"
LangString DESC_InstallService ${LANG_ENGLISH} "Install As a Service"
LangString DESC_DesktopShortcut ${LANG_ENGLISH} "Create a desktop shortcut"
LangString DESC_LaunchJellyfin ${LANG_ENGLISH} "Start Jellyfin after Install"
-; LangString DESC_MigrateEmbyLibrary ${LANG_ENGLISH} "Migrate existing Emby Library"
- ;Assign language strings to sections
+;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${InstallJellyfin} $(DESC_InstallJellyfin)
!insertmacro MUI_DESCRIPTION_TEXT ${DesktopShortcut} $(DESC_DesktopShortcut)
!insertmacro MUI_DESCRIPTION_TEXT ${InstallService} $(DESC_InstallService)
!insertmacro MUI_DESCRIPTION_TEXT ${LaunchJellyfin} $(DESC_LaunchJellyfin)
-; !insertmacro MUI_DESCRIPTION_TEXT ${MigrateEmbyLibrary} $(DESC_MigrateEmbyLibrary)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
;--------------------------------
@@ -208,7 +186,9 @@ Section "Uninstall"
SectionEnd
+
Function .onInit
+
StrCpy $_EXISTINGINSTALLATION_ "NO"
StrCpy $_EXISTINGSERVICE_ "NO"
;Detect if Jellyfin is already installed.
@@ -228,7 +208,7 @@ Function .onInit
StrCpy "$INSTDIR" "$0"
StrCpy $_EXISTINGINSTALLATION_ "YES"
SectionSetText ${InstallJellyfin} "Upgrade Jellyfin" ; Change install text to "Upgrade"
-
+
; check if there is a service called Jellyfin
; hack : nssm statuscode Jellyfin will return non zero return code in case it exists
ExecWait '"$INSTDIR\nssm.exe" statuscode Jellyfin' $0
@@ -246,13 +226,15 @@ Function .onInit
Quit ; Quit if the user is not sure about upgrade
Proceed:
-
+
NoExisitingInstall:
SetShellVarContext all
+
; Align installer version with jellyfin.dll version
!getdllversion "$%InstallLocation%\jellyfin.dll" ver_
StrCpy $_JELLYFINVERSION_ "${ver_1}.${ver_2}.${ver_3}.${ver_4}"
+
SetShellVarContext current
StrCpy $_JELLYFINDATADIR_ "$LOCALAPPDATA\jellyfin\"
DetailPrint "_JELLYFINDATADIR_ : $_JELLYFINDATADIR_"
@@ -264,18 +246,7 @@ FunctionEnd
Function HideDirectoryPage
StrCmp $_EXISTINGINSTALLATION_ "NO" show
- Abort ; Dont show folder selection if just upgrading
+ Abort ; Don't show folder selection if just upgrading
show:
FunctionEnd
-
-; This can be uncommented in case Emby Migration is planned later
-;Function ShowEmbyLibraryPage
-; SectionGetFlags ${MigrateEmbyLibrary} $R0
-; IntOp $R0 $R0 & ${SF_SELECTED}
-; IntCmp $R0 ${SF_SELECTED} show
-
-; Abort ; Dont show the Emby folder selection window if Emby migrartion is not selected
-
-; show:
-;FunctionEnd
From 881f385a619210732b0d56ae68485680b549810c Mon Sep 17 00:00:00 2001
From: "Joshua M. Boniface"
Date: Sat, 13 Jul 2019 12:04:26 -0400
Subject: [PATCH 118/214] Use dash or underscore on mv command
Required for nightlies, which would not match this file format with the
underscore then wildcard. Enables nightly builds.
---
deployment/debian-package-arm64/docker-build.sh | 2 +-
deployment/debian-package-armhf/docker-build.sh | 2 +-
deployment/debian-package-x64/docker-build.sh | 2 +-
deployment/ubuntu-package-arm64/docker-build.sh | 2 +-
deployment/ubuntu-package-armhf/docker-build.sh | 2 +-
deployment/ubuntu-package-x64/docker-build.sh | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/deployment/debian-package-arm64/docker-build.sh b/deployment/debian-package-arm64/docker-build.sh
index cee96e1369..1c75ece8eb 100755
--- a/deployment/debian-package-arm64/docker-build.sh
+++ b/deployment/debian-package-arm64/docker-build.sh
@@ -17,5 +17,5 @@ dpkg-buildpackage -us -uc -aarm64
# Move the artifacts out
mkdir -p ${ARTIFACT_DIR}/deb
-mv /jellyfin_* ${ARTIFACT_DIR}/deb/
+mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
diff --git a/deployment/debian-package-armhf/docker-build.sh b/deployment/debian-package-armhf/docker-build.sh
index 56227b5880..df35345bdd 100755
--- a/deployment/debian-package-armhf/docker-build.sh
+++ b/deployment/debian-package-armhf/docker-build.sh
@@ -17,5 +17,5 @@ dpkg-buildpackage -us -uc -aarmhf
# Move the artifacts out
mkdir -p ${ARTIFACT_DIR}/deb
-mv /jellyfin_* ${ARTIFACT_DIR}/deb/
+mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
diff --git a/deployment/debian-package-x64/docker-build.sh b/deployment/debian-package-x64/docker-build.sh
index 07f726dcc8..9781879f6f 100755
--- a/deployment/debian-package-x64/docker-build.sh
+++ b/deployment/debian-package-x64/docker-build.sh
@@ -16,5 +16,5 @@ dpkg-buildpackage -us -uc
# Move the artifacts out
mkdir -p ${ARTIFACT_DIR}/deb
-mv /jellyfin_* ${ARTIFACT_DIR}/deb/
+mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
diff --git a/deployment/ubuntu-package-arm64/docker-build.sh b/deployment/ubuntu-package-arm64/docker-build.sh
index cee96e1369..1c75ece8eb 100755
--- a/deployment/ubuntu-package-arm64/docker-build.sh
+++ b/deployment/ubuntu-package-arm64/docker-build.sh
@@ -17,5 +17,5 @@ dpkg-buildpackage -us -uc -aarm64
# Move the artifacts out
mkdir -p ${ARTIFACT_DIR}/deb
-mv /jellyfin_* ${ARTIFACT_DIR}/deb/
+mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
diff --git a/deployment/ubuntu-package-armhf/docker-build.sh b/deployment/ubuntu-package-armhf/docker-build.sh
index 56227b5880..df35345bdd 100755
--- a/deployment/ubuntu-package-armhf/docker-build.sh
+++ b/deployment/ubuntu-package-armhf/docker-build.sh
@@ -17,5 +17,5 @@ dpkg-buildpackage -us -uc -aarmhf
# Move the artifacts out
mkdir -p ${ARTIFACT_DIR}/deb
-mv /jellyfin_* ${ARTIFACT_DIR}/deb/
+mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
diff --git a/deployment/ubuntu-package-x64/docker-build.sh b/deployment/ubuntu-package-x64/docker-build.sh
index 07f726dcc8..9781879f6f 100755
--- a/deployment/ubuntu-package-x64/docker-build.sh
+++ b/deployment/ubuntu-package-x64/docker-build.sh
@@ -16,5 +16,5 @@ dpkg-buildpackage -us -uc
# Move the artifacts out
mkdir -p ${ARTIFACT_DIR}/deb
-mv /jellyfin_* ${ARTIFACT_DIR}/deb/
+mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
From 0f8e2600e353aa7caf468fe15541c4ce7e6e72c8 Mon Sep 17 00:00:00 2001
From: "Joshua M. Boniface"
Date: Sat, 13 Jul 2019 14:23:18 -0400
Subject: [PATCH 119/214] Fix broken symlinks
These were removed somehow in defc5f1cf9b486357b379c610663e1bad48428ad;
restore them to their proper link state.
---
deployment/centos-package-x64/pkg-src | 2 +-
deployment/debian-package-armhf/pkg-src | 2 +-
deployment/ubuntu-package-x64/pkg-src | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
mode change 100644 => 120000 deployment/centos-package-x64/pkg-src
mode change 100644 => 120000 deployment/debian-package-armhf/pkg-src
mode change 100644 => 120000 deployment/ubuntu-package-x64/pkg-src
diff --git a/deployment/centos-package-x64/pkg-src b/deployment/centos-package-x64/pkg-src
deleted file mode 100644
index dfd6497cfd..0000000000
--- a/deployment/centos-package-x64/pkg-src
+++ /dev/null
@@ -1 +0,0 @@
-../fedora-package-x64/pkg-src
\ No newline at end of file
diff --git a/deployment/centos-package-x64/pkg-src b/deployment/centos-package-x64/pkg-src
new file mode 120000
index 0000000000..3ff4d3cbf5
--- /dev/null
+++ b/deployment/centos-package-x64/pkg-src
@@ -0,0 +1 @@
+../fedora-package-x64/pkg-src/
\ No newline at end of file
diff --git a/deployment/debian-package-armhf/pkg-src b/deployment/debian-package-armhf/pkg-src
deleted file mode 100644
index 4c695fea17..0000000000
--- a/deployment/debian-package-armhf/pkg-src
+++ /dev/null
@@ -1 +0,0 @@
-../debian-package-x64/pkg-src
\ No newline at end of file
diff --git a/deployment/debian-package-armhf/pkg-src b/deployment/debian-package-armhf/pkg-src
new file mode 120000
index 0000000000..0bb6d55249
--- /dev/null
+++ b/deployment/debian-package-armhf/pkg-src
@@ -0,0 +1 @@
+../debian-package-x64/pkg-src/
\ No newline at end of file
diff --git a/deployment/ubuntu-package-x64/pkg-src b/deployment/ubuntu-package-x64/pkg-src
deleted file mode 100644
index 4c695fea17..0000000000
--- a/deployment/ubuntu-package-x64/pkg-src
+++ /dev/null
@@ -1 +0,0 @@
-../debian-package-x64/pkg-src
\ No newline at end of file
diff --git a/deployment/ubuntu-package-x64/pkg-src b/deployment/ubuntu-package-x64/pkg-src
new file mode 120000
index 0000000000..0bb6d55249
--- /dev/null
+++ b/deployment/ubuntu-package-x64/pkg-src
@@ -0,0 +1 @@
+../debian-package-x64/pkg-src/
\ No newline at end of file
From b1bd0627098e67169b9ceaa318a32f4d2a9fba30 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Sun, 7 Jul 2019 18:04:06 +0200
Subject: [PATCH 120/214] 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(-)
diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
index 780b0a8897..22aaa68858 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 ae62f34e06..681859f693 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 0576a1a5d9..76bd35e578 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 1151dd997d..ade9df50b5 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;
}
From 7bb504d491746d5602b95ab74ed60863cd2e7078 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Sun, 7 Jul 2019 18:26:01 +0200
Subject: [PATCH 121/214] Create a new HttpMethod from the function name
---
.../HttpClientManager/HttpClientManager.cs | 39 +------------------
1 file changed, 1 insertion(+), 38 deletions(-)
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index 681859f693..613545e21d 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)
From b294b802a8bfd80958f8b273895ab430f24cc9d0 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Mon, 8 Jul 2019 14:50:15 +0200
Subject: [PATCH 122/214] 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(-)
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index ae62f34e06..45cf4aceca 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 d39c917835..c1468121a8 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 cd9feabfe0..d65ce897ab 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()
From 5d9fa06675963fd3ed12baeede541321d5211f1d Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Mon, 8 Jul 2019 15:14:51 +0200
Subject: [PATCH 123/214] Cleanup
---
.../LiveTv/Listings/XmlTvListingsProvider.cs | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index c1468121a8..94225a0aa9 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);
}
}
From 953f077f9d6e4272ebc38a6ef50111b04045a3a3 Mon Sep 17 00:00:00 2001
From: "Joshua M. Boniface"
Date: Sun, 14 Jul 2019 23:11:17 -0400
Subject: [PATCH 124/214] Override the installinit name
Without this, when building the `jellyfin-nightly` package, it attempts
to find service/init files with the name `jellyfin-nightly` instead of
the proper name. This override prevents this by forcing the name to
`jellyfin`. Required for nightly builds.
---
deployment/debian-package-x64/pkg-src/rules | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/deployment/debian-package-x64/pkg-src/rules b/deployment/debian-package-x64/pkg-src/rules
index ee41e0e24b..2a5d41a696 100644
--- a/deployment/debian-package-x64/pkg-src/rules
+++ b/deployment/debian-package-x64/pkg-src/rules
@@ -45,3 +45,7 @@ override_dh_auto_build:
override_dh_auto_clean:
dotnet clean -maxcpucount:1 --configuration $(CONFIG) Jellyfin.Server || true
rm -rf '$(CURDIR)/usr'
+
+# Force the service name to jellyfin even if we're building jellyfin-nightly
+override_dh_installinit:
+ dh_installinit --name=jellyfin
From 59518ec87e828aff21e5f0f67e82f00fc8fa13ba Mon Sep 17 00:00:00 2001
From: Khinenw
Date: Sun, 14 Jul 2019 23:05:45 +0900
Subject: [PATCH 125/214] Fixed SMI to SRT UTF-16 Encoding bug
---
CONTRIBUTORS.md | 1 +
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs | 8 +++++++-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 9168dccc8c..c96228f31d 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -27,6 +27,7 @@
- [pjeanjean](https://github.com/pjeanjean)
- [DrPandemic](https://github.com/drpandemic)
- [joern-h](https://github.com/joern-h)
+ - [Khinenw](https://github.com/HelloWorld017)
# Emby Contributors
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 8677b363fb..9ddfb9b01e 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -425,7 +425,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var encodingParam = await GetSubtitleFileCharacterSet(inputPath, language, inputProtocol, cancellationToken).ConfigureAwait(false);
- if (!string.IsNullOrEmpty(encodingParam))
+ // FFmpeg automatically convert character encoding when it is UTF-16
+ // If we specify character encoding, it rejects with "do not specify a character encoding" and "Unable to recode subtitle event"
+ if ((inputPath.EndsWith(".smi") || inputPath.EndsWith(".sami")) && (encodingParam == "UTF-16BE" || encodingParam == "UTF-16LE"))
+ {
+ encodingParam = "";
+ }
+ else if (!string.IsNullOrEmpty(encodingParam))
{
encodingParam = " -sub_charenc " + encodingParam;
}
From 886c88576c04eda76c076d056194afa9140815a7 Mon Sep 17 00:00:00 2001
From: Claus Vium
Date: Fri, 19 Jul 2019 23:22:30 +0200
Subject: [PATCH 126/214] Use HttpCompletionOption.ResponseHeadersRead and
resort to Content-Length header for non-buffered content
---
.../HttpClientManager/HttpClientManager.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index 2752e9324b..a4cd98b400 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()
};
}
From dadfc09c01ee34b67bfc02f655ac06e4de953805 Mon Sep 17 00:00:00 2001
From: Claus Vium
Date: Sat, 20 Jul 2019 13:36:59 +0200
Subject: [PATCH 127/214] Add HttpCompletionOption.ResponseHeadersRead to the
buffering option to avoid potentially having 2 copies in memory
---
.../HttpClientManager/HttpClientManager.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index a4cd98b400..528053c21b 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);
From 67e32a2c4418cb2129f5e71eec71eae904599228 Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Sun, 21 Jul 2019 01:07:39 +0200
Subject: [PATCH 128/214] Add Stale app configuration starting point
Link: https://github.com/apps/stale
---
.github/stale.yml | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 .github/stale.yml
diff --git a/.github/stale.yml b/.github/stale.yml
new file mode 100644
index 0000000000..3e76aa75d1
--- /dev/null
+++ b/.github/stale.yml
@@ -0,0 +1,20 @@
+# Number of days of inactivity before an issue becomes stale
+daysUntilStale: 60
+# Number of days of inactivity before a stale issue is closed
+daysUntilClose: 7
+# Issues with these labels will never be considered stale
+exemptLabels:
+ - regression
+ - security
+ - dotnet-3.0-future
+ - roadmap
+ - future
+# Label to use when marking an issue as stale
+staleLabel: stale
+# Comment to post when marking an issue as stale. Set to `false` to disable
+markComment: >
+ This issue has been automatically marked as stale because it has not had
+ recent activity. It will be closed if no further activity occurs. Thank you
+ for your contributions.
+# Comment to post when closing a stale issue. Set to `false` to disable
+closeComment: false
From 0d9cdb98f2dc570d9a8dd130f10b7df0e035f6ac Mon Sep 17 00:00:00 2001
From: ancarvalho
Date: Tue, 2 Jul 2019 03:01:17 +0000
Subject: [PATCH 129/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index dbc9c4c4b8..dd3ef57dfd 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",
From 6e29b8ad6faf81b8a5c88b4d25f2c8435f161259 Mon Sep 17 00:00:00 2001
From: ZhiGang Zhung
Date: Sat, 29 Jun 2019 17:17:32 +0000
Subject: [PATCH 130/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index 6f7d362d3b..b94efffcd3 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": "合集",
From cd504e6ee533a4c40f14b1b1d0fefe4bbfd36b6d Mon Sep 17 00:00:00 2001
From: Matsuri
Date: Sat, 29 Jun 2019 17:21:34 +0000
Subject: [PATCH 131/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index b94efffcd3..63aa6a5577 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}"
}
From 8c94187c759330a77571f1a8eb65b9c2a7417f04 Mon Sep 17 00:00:00 2001
From: Moritz
Date: Wed, 10 Jul 2019 11:22:02 +0000
Subject: [PATCH 132/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index 6fd63a5140..0db2017692 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}",
From 00c92e88c575c4c5adda6269bbd06c82e79f3804 Mon Sep 17 00:00:00 2001
From: exveria1015
Date: Tue, 4 Jun 2019 16:22:41 +0000
Subject: [PATCH 133/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json
index 53a43a1252..4aa0637c59 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": "ホームビデオ",
From b337df889e760ff8a268091d56254fc85df0f8a1 Mon Sep 17 00:00:00 2001
From: polVRtong
Date: Sun, 21 Jul 2019 06:21:35 +0000
Subject: [PATCH 134/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json
index 5a7ba8ba7b..f2b7c408ce 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 방송",
From ea41155c6b8434e685ef5dc99a7dc7502628a3ca Mon Sep 17 00:00:00 2001
From: vaheed
Date: Fri, 14 Jun 2019 14:11:17 +0000
Subject: [PATCH 135/214] 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/
---
.../Localization/Core/fa.json | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json
index 0a0c7553b8..faa658ed58 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}"
}
From b3aaa9216de2a7d8f626fea5632eccc12dd8afe8 Mon Sep 17 00:00:00 2001
From: Tradutor da Silva
Date: Tue, 23 Jul 2019 02:25:54 +0000
Subject: [PATCH 136/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index dd3ef57dfd..5d68416e9f 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",
From d56725a43d66354bd0c03b6b9848b063e80d1121 Mon Sep 17 00:00:00 2001
From: tluciomiranda
Date: Mon, 15 Jul 2019 11:38:35 +0000
Subject: [PATCH 137/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json
index dc69d8af24..387604f03c 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}"
}
From 18bc6c69d561a1c25d44cef98bf6576f57cf9f12 Mon Sep 17 00:00:00 2001
From: Vladimir Jendrol
Date: Mon, 8 Jul 2019 19:46:40 +0000
Subject: [PATCH 138/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json
index bc7e7184e3..6eade79429 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}"
}
From 595a68b822654b03c32a35060bf97234dbc6ef7c Mon Sep 17 00:00:00 2001
From: "Joshua M. Boniface"
Date: Wed, 24 Jul 2019 10:48:35 -0400
Subject: [PATCH 139/214] Bump version for 10.3.7
---
Dockerfile | 2 +-
Dockerfile.arm | 2 +-
Dockerfile.arm64 | 2 +-
MediaBrowser.WebDashboard/jellyfin-web | 2 +-
SharedVersion.cs | 4 ++--
build.yaml | 2 +-
deployment/debian-package-x64/pkg-src/changelog | 6 ++++++
deployment/fedora-package-x64/pkg-src/jellyfin.spec | 4 +++-
8 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index 864cfa445b..057d4b0413 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -21,7 +21,7 @@ RUN apt-get update \
COPY --from=ffmpeg / /
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.6
+ARG JELLYFIN_WEB_VERSION=10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
diff --git a/Dockerfile.arm b/Dockerfile.arm
index 8167390632..dad4da1f14 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -26,7 +26,7 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.6
+ARG JELLYFIN_WEB_VERSION=10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index 1843f7224b..5d4e86bff4 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -26,7 +26,7 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.6
+ARG JELLYFIN_WEB_VERSION=10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
diff --git a/MediaBrowser.WebDashboard/jellyfin-web b/MediaBrowser.WebDashboard/jellyfin-web
index c9e70d9564..1d0fd79eb1 160000
--- a/MediaBrowser.WebDashboard/jellyfin-web
+++ b/MediaBrowser.WebDashboard/jellyfin-web
@@ -1 +1 @@
-Subproject commit c9e70d95643e84437189dd500b0380ec0fbbf659
+Subproject commit 1d0fd79eb1e4d0bf6a9f62f769a951970383bcf0
diff --git a/SharedVersion.cs b/SharedVersion.cs
index 1e74d8f7d6..500e6fe551 100644
--- a/SharedVersion.cs
+++ b/SharedVersion.cs
@@ -1,4 +1,4 @@
using System.Reflection;
-[assembly: AssemblyVersion("10.3.6")]
-[assembly: AssemblyFileVersion("10.3.6")]
+[assembly: AssemblyVersion("10.3.7")]
+[assembly: AssemblyFileVersion("10.3.7")]
diff --git a/build.yaml b/build.yaml
index 7a5e66b112..2d4bc29f43 100644
--- a/build.yaml
+++ b/build.yaml
@@ -1,7 +1,7 @@
---
# We just wrap `build` so this is really it
name: "jellyfin"
-version: "10.3.6"
+version: "10.3.7"
packages:
- debian-package-x64
- debian-package-armhf
diff --git a/deployment/debian-package-x64/pkg-src/changelog b/deployment/debian-package-x64/pkg-src/changelog
index 64912a11d1..aa15827a72 100644
--- a/deployment/debian-package-x64/pkg-src/changelog
+++ b/deployment/debian-package-x64/pkg-src/changelog
@@ -1,3 +1,9 @@
+jellyfin (10.3.7-1) unstable; urgency=medium
+
+ * New upstream version 10.3.7; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.7
+
+ -- Jellyfin Packaging Team Wed, 24 Jul 2019 10:48:28 -0400
+
jellyfin (10.3.6-1) unstable; urgency=medium
* New upstream version 10.3.6; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.6
diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
index 809bde39d6..91b74ffe1e 100644
--- a/deployment/fedora-package-x64/pkg-src/jellyfin.spec
+++ b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
@@ -7,7 +7,7 @@
%endif
Name: jellyfin
-Version: 10.3.6
+Version: 10.3.7
Release: 1%{?dist}
Summary: The Free Software Media Browser
License: GPLv2
@@ -140,6 +140,8 @@ fi
%systemd_postun_with_restart jellyfin.service
%changelog
+* Wed Jul 24 2019 Jellyfin Packaging Team
+- New upstream version 10.3.7; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.7
* Sat Jul 06 2019 Jellyfin Packaging Team
- New upstream version 10.3.6; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.6
* Sun Jun 09 2019 Jellyfin Packaging Team
From 773af2eef907186b6552b845eb61820909154413 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Wed, 24 Jul 2019 23:46:58 +0200
Subject: [PATCH 140/214] Don't copy the complete response stream
---
.../HttpClientManager/HttpClientManager.cs | 57 ++++++-------------
1 file changed, 17 insertions(+), 40 deletions(-)
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index 3e23150d88..331b5e29d7 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,
+ options.BufferContent ? HttpCompletionOption.ResponseContentRead : HttpCompletionOption.ResponseHeadersRead,
+ options.CancellationToken).ConfigureAwait(false);
+
+ await EnsureSuccessStatusCode(response, options).ConfigureAwait(false);
+
+ options.CancellationToken.ThrowIfCancellationRequested();
+
+ var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ return new HttpResponseInfo(response.Headers, response.Content.Headers)
{
- var response = await client.SendAsync(httpWebRequest, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false);
-
- await EnsureSuccessStatusCode(response, options).ConfigureAwait(false);
-
- options.CancellationToken.ThrowIfCancellationRequested();
-
- 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()
- };
- }
-
- using (var response = await client.SendAsync(httpWebRequest, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).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, 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)
From ddd1a282ea6398ee26d74321338d0445d0a0c796 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Sun, 7 Jul 2019 21:03:26 +0200
Subject: [PATCH 141/214] Remove IpAddressInfo and IpEndPointInfo classes
---
Emby.Dlna/Main/DlnaEntryPoint.cs | 3 +-
Emby.Dlna/PlayTo/PlayToManager.cs | 4 +-
.../ApplicationHost.cs | 53 ++--
.../TunerHosts/HdHomerun/HdHomerunHost.cs | 9 +-
.../TunerHosts/HdHomerun/HdHomerunManager.cs | 16 +-
.../HdHomerun/HdHomerunUdpStream.cs | 17 +-
.../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 +-
.../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 | 51 ++--
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, 261 insertions(+), 444 deletions(-)
delete mode 100644 MediaBrowser.Model/Net/IpAddressInfo.cs
delete mode 100644 MediaBrowser.Model/Net/IpEndPointInfo.cs
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index 5fbe70dedb..206a873e1d 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 28e70d0469..c0a4418711 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 120aade392..c6ba2326aa 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 761275f8f3..ed524cae30 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 2205c0ecca..6e79441dab 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 7f426ea31f..ec708cf20f 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 492f48abe8..42ffa4e221 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 6c55085c83..2908ee9afd 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 c102f9eb55..3cacacf075 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));
+ _logger = logger;
- // 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");
- }
-
- 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
+ // 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))))
{
- // 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)
- {
- 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)
+ public bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask)
{
- if (TryParseIpAddress(ipAddress, out var info))
- {
- return info;
- }
-
- throw new ArgumentException("Invalid ip address: " + ipAddress);
- }
-
- public bool TryParseIpAddress(string ipAddress, out IpAddressInfo ipAddressInfo)
- {
- 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,85 +626,17 @@ 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();
- }
-
///
/// Gets the network shares.
///
diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs
index e7cda2993f..185a282ac4 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 94308a98e8..08c0983bef 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 34c6f58665..61f2bc2f9f 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 81b9ff0a57..3f8cc0b83f 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 4edbb503bb..c443a8ad18 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 992ccb49b0..f80de5524c 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 69fe134bc7..e58f4cc14d 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 87fa55bcae..0000000000
--- 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 f8c125144c..0000000000
--- 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 8c394f7c7d..cd7a2e55f8 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 9106e27e53..21ac7c6311 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; }
- }
+ {
+ get { return _DiscoveredDevice; }
+ }
- #endregion
-
- }
-}
\ No newline at end of file
+ #endregion
+
+ }
+}
diff --git a/RSSDP/ISsdpCommunicationsServer.cs b/RSSDP/ISsdpCommunicationsServer.cs
index c99d684a13..8cf65df116 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 fd3cd9e3a2..b753950f0a 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 5ed9664eda..f9f9c3040c 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 5d2afc37a0..0aa985a261 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 b4c4a88fdd..09f729e83a 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 e17e14c1a6..59a2710d58 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 921f33c212..7f3e56394b 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 d918b9040d..0f2de7b156 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.
From 8270d0cc91783c0c8c052b43af0d633edb8b6b04 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Thu, 25 Jul 2019 00:23:56 +0200
Subject: [PATCH 142/214] Move IPv6 scope id removal logic to it's own function
---
.../ApplicationHost.cs | 32 +++++++++++--------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index c6ba2326aa..ef2f59d303 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 + "]");
}
From fbbcba95d3712b0f7f61e5c2026914bf42f2b59b Mon Sep 17 00:00:00 2001
From: crankdoofus <52436708+crankdoofus@users.noreply.github.com>
Date: Thu, 25 Jul 2019 21:51:53 +1000
Subject: [PATCH 143/214] Update installer name to copy
---
deployment/windows/build-jellyfin.ps1 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/deployment/windows/build-jellyfin.ps1 b/deployment/windows/build-jellyfin.ps1
index b049c351a2..aaa4bcf6c4 100644
--- a/deployment/windows/build-jellyfin.ps1
+++ b/deployment/windows/build-jellyfin.ps1
@@ -108,7 +108,7 @@ function Make-NSIS {
Expand-Archive "$tempdir/nsis.zip" -DestinationPath "$tempdir/nsis/" -Force | Write-Verbose
$env:InstallLocation = $InstallLocation
& "$tempdir/nsis/nsis-3.04/makensis.exe" ".\deployment\windows\jellyfin.nsi"
- Copy-Item .\deployment\windows\Jellyfin.Installer.*.exe $InstallLocation\..\
+ Copy-Item .\deployment\windows\jellyfin_*.exe $InstallLocation\..\
Remove-Item "$tempdir/nsis/" -Recurse -Force -ErrorAction Continue | Write-Verbose
Remove-Item "$tempdir/nsis.zip" -Force -ErrorAction Continue | Write-Verbose
From 5517d912bfb1d77388805d4d759e875541589265 Mon Sep 17 00:00:00 2001
From: crankdoofus <52436708+crankdoofus@users.noreply.github.com>
Date: Thu, 25 Jul 2019 21:52:44 +1000
Subject: [PATCH 144/214] Rework based on review comments
---
deployment/windows/jellyfin.nsi | 389 +++++++++++++++++++++-----------
1 file changed, 259 insertions(+), 130 deletions(-)
diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi
index 3a11bf1d83..5e3dcac841 100644
--- a/deployment/windows/jellyfin.nsi
+++ b/deployment/windows/jellyfin.nsi
@@ -2,68 +2,113 @@
; This can be removed once stable.
!verbose 4
;--------------------------------
+!define SF_USELECTED 0 ; used to check selected options status, rest are inherited from Sections.nsh
!include "MUI2.nsh"
-
+ !include "Sections.nsh"
+ !include "LogicLib.nsh"
+
+; Global variables that we'll use
Var _JELLYFINVERSION_
Var _JELLYFINDATADIR_
- Var _SERVICEINSTALLED_
+ Var _INSTALLSERVICE_
+ Var _SERVICESTART_
+ Var _NETWORKSERVICEACCOUNT_
Var _EXISTINGINSTALLATION_
Var _EXISTINGSERVICE_
+ Var _CUSTOMDATAFOLDER_
+
+!if ${NSIS_PTR_SIZE} > 4
+ !define BITS 64
+ !define NAMESUFFIX " (64 bit)"
+!else
+ !define BITS 32
+ !define NAMESUFFIX ""
+!endif
;--------------------------------
-;General
-; Align installer version with jellyfin.dll version
- !getdllversion "$%InstallLocation%\jellyfin.dll" ver_
- !echo "jellyfin.dll version is ${ver_1}.${ver_2}.${ver_3}.${ver_4}" ;!echo will print it while building
-
- Name "Jellyfin ${ver_1}.${ver_2}.${ver_3}.${ver_4}"
- OutFile "Jellyfin.Installer.${ver_1}.${ver_2}.${ver_3}.${ver_4}.exe"
- BrandingText "Jellyfin ${ver_1}.${ver_2}.${ver_3}.${ver_4} Installer"
-
-; installer attributes
- VIProductVersion "${ver_1}.${ver_2}.${ver_3}.${ver_4}"
- VIFileVersion "${ver_1}.${ver_2}.${ver_3}.${ver_4}"
- VIAddVersionKey "ProductName" "Jellyfin"
- VIAddVersionKey "FileVersion" "${ver_1}.${ver_2}.${ver_3}.${ver_4}"
- VIAddVersionKey "LegalCopyright" "Jellyfin, Free Software Media System"
- VIAddVersionKey "FileDescription" "Jellyfin, Free Software Media System"
+ !define REG_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" ;Registry to show up in Add/Remove Programs
-;Default installation folder
- InstallDir "$APPDATA\Jellyfin"
+ !getdllversion "$%InstallLocation%\jellyfin.dll" ver_ ;Align installer version with jellyfin.dll version
+ Name "Jellyfin Server ${ver_1}.${ver_2}.${ver_3}" ; This is referred in various header text labels
+ OutFile "jellyfin_${ver_1}.${ver_2}.${ver_3}_windows.exe" ; Naming convention jellyfin_{version}_windows-{arch].exe
+ BrandingText "Jellyfin Server ${ver_1}.${ver_2}.${ver_3} Installer" ; This shows in just over the buttons
+
+; installer attributes, these show up in details tab on installer properties
+ VIProductVersion "${ver_1}.${ver_2}.${ver_3}.0" ; VIProductVersion format, should be X.X.X.X
+ VIFileVersion "${ver_1}.${ver_2}.${ver_3}.0" ; VIFileVersion format, should be X.X.X.X
+ VIAddVersionKey "ProductName" "Jellyfin Server"
+ VIAddVersionKey "FileVersion" "${ver_1}.${ver_2}.${ver_3}.0"
+ VIAddVersionKey "LegalCopyright" "Jellyfin, Free Software Media System"
+ VIAddVersionKey "FileDescription" "Jellyfin Server"
+
+;TODO, check defaults
+ InstallDir "$PROGRAMFILES\Jellyfin" ;Default installation folder
+ InstallDirRegKey HKLM "Software\Jellyfin" "InstallFolder" ;Read the registry for install folder,
-;Get installation folder from registry if available
- InstallDirRegKey HKLM "Software\Jellyfin" "InstallLocation"
-
-;Request application privileges for service installation
- RequestExecutionLevel admin
- CRCCheck on
- !define MUI_ABORTWARNING
+ RequestExecutionLevel admin ; ask it upfront for service control, and installing in priv folders
+
+ CRCCheck on ; make sure the installer wasn't corrupted while downloading
+
+ !define MUI_ABORTWARNING ;Prompts user in case of aborting install
+
+; TODO: Replace with nice Jellyfin Icons
+ !define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\nsis3-install.ico" ; Installer Icon
+ !define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\nsis3-uninstall.ico" ; Uninstaller Icon
+
+ !define MUI_HEADERIMAGE
+ !define MUI_HEADERIMAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Header\nsis3-branding.bmp"
+ !define MUI_WELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\nsis3-branding.bmp"
;--------------------------------
;Pages
- !insertmacro MUI_PAGE_LICENSE "$%InstallLocation%\LICENSE"
+; Welcome Page
+ !define MUI_WELCOMEPAGE_TEXT "The installer will ask for details to install Jellyfin Server.$\r$\n$\r$\n$\r$\n\
+ ADVANCED:$\r$\n\
+ The default service install uses Local System account and is sufficient for most users. $\r$\n$\r$\n\
+ You can choose to install using Network Service account under advanced options. This also affects where Jellyfin Server and Jellyfin data can be installed. The installer will NOT check this, you should know what you are doing.$\r$\n$\r$\n\
+ You can choose the folder for Jellyfin Metadata under advanced options based on your needs."
+ !insertmacro MUI_PAGE_WELCOME
+; License Page
+ !insertmacro MUI_PAGE_LICENSE "$%InstallLocation%\LICENSE" ; picking up generic GPL
+; Components Page
+ !define MUI_COMPONENTSPAGE_SMALLDESC
!insertmacro MUI_PAGE_COMPONENTS
- !define MUI_PAGE_CUSTOMFUNCTION_PRE HideDirectoryPage
- !insertmacro MUI_PAGE_DIRECTORY
- !insertmacro MUI_PAGE_INSTFILES
-
+ !define MUI_PAGE_CUSTOMFUNCTION_PRE HideInstallDirectoryPage ; Controls when to hide / show
+ !define MUI_DIRECTORYPAGE_TEXT_DESTINATION "Install folder" ; shows just above the folder selection dialog
+ !insertmacro MUI_PAGE_DIRECTORY
+
+; Metadata folder Page
+ !define MUI_PAGE_CUSTOMFUNCTION_PRE HideDataDirectoryPage ; Controls when to hide / show
+ !define MUI_PAGE_HEADER_SUBTEXT "Choose the folder in which to install the Jellyfin Server metadata."
+ !define MUI_DIRECTORYPAGE_TEXT_TOP "The installer will set the following folder for Jellyfin Server metadata. To install in a differenct folder, click Browse and select another folder. Please make sure the folder exists. Click Next to continue."
+ !define MUI_DIRECTORYPAGE_TEXT_DESTINATION "Metadata folder"
+ !define MUI_DIRECTORYPAGE_VARIABLE $_JELLYFINDATADIR_
+ !insertmacro MUI_PAGE_DIRECTORY
+
+; Confirmation Page
+ Page custom ConfirmationPage ; just letting the user know what they chose to install
+
+; Actual Installion Page
+ !insertmacro MUI_PAGE_INSTFILES
+
+
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
+ !insertmacro MUI_UNPAGE_FINISH
;--------------------------------
-;Languages
-
+;Languages; Add more languages later here if needed
!insertmacro MUI_LANGUAGE "English"
;--------------------------------
;Installer Sections
-
-Section "Install Jellyfin (required)" InstallJellyfin
-; If previous installation is present, run the uninstaller silently
- StrCmp "$_EXISTINGINSTALLATION_" "YES" RunUninstaller CarryOn
+Section "Jellyfin Server (required)" InstallJellyfin
+ SectionIn RO ; Mandatory section, isn't this the whole purpose to run the installer.
+
+ StrCmp "$_EXISTINGINSTALLATION_" "YES" RunUninstaller CarryOn ; Silently uninstall in case of previous installation
RunUninstaller:
DetailPrint "Looking for uninstaller at $INSTDIR"
@@ -74,179 +119,263 @@ Section "Install Jellyfin (required)" InstallJellyfin
DetailPrint "Silently running the uninstaller at $INSTDIR"
ExecWait '"$INSTDIR\Uninstall.exe" /S _?=$INSTDIR' $0
DetailPrint "Uninstall finished, $0"
- Delete "$INSTDIR\Uninstall.exe"
CarryOn:
SetOutPath "$INSTDIR"
-
- SetShellVarContext current ; Local App Data folder for current user only
- StrCpy $_JELLYFINDATADIR_ "$LOCALAPPDATA\jellyfin\"
-; Pack all the files that were just compiled
-; These will be exploded to $INSTDIR
File /r $%InstallLocation%\*
-; Write the installation path into the registry
- WriteRegStr HKLM "Software\Jellyfin" "InstallLocation" "$INSTDIR"
+; Write the InstallFolder, DataFolder, Network Service info into the registry for later use
+ WriteRegExpandStr HKLM "Software\Jellyfin" "InstallFolder" "$INSTDIR"
+ WriteRegExpandStr HKLM "Software\Jellyfin" "DataFolder" "$_JELLYFINDATADIR_"
+ WriteRegStr HKLM "Software\Jellyfin" "NetworkService" "$_NETWORKSERVICEACCOUNT_"
+
+ !getdllversion "$%InstallLocation%\jellyfin.dll" ver_
+ StrCpy $_JELLYFINVERSION_ "${ver_1}.${ver_2}.${ver_3}" ;
; Write the uninstall keys for Windows
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "DisplayName" "Jellyfin $_JELLYFINVERSION_"
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "UninstallString" '"$INSTDIR\Uninstall.exe"'
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "DisplayIcon" '"$INSTDIR\Jellyfin.exe",0'
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "Publisher" "The Jellyfin project"
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "URLInfoAbout" "https://jellyfin.github.io/"
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "DisplayVersion" "$_JELLYFINVERSION_"
- WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "NoModify" 1
- WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin" "NoRepair" 1
+ WriteRegStr HKLM "${REG_UNINST_KEY}" "DisplayName" "Jellyfin $_JELLYFINVERSION_"
+ WriteRegExpandStr HKLM "${REG_UNINST_KEY}" "UninstallString" '"$INSTDIR\Uninstall.exe"'
+ WriteRegStr HKLM "${REG_UNINST_KEY}" "DisplayIcon" '"$INSTDIR\Jellyfin.exe",0'
+ WriteRegStr HKLM "${REG_UNINST_KEY}" "Publisher" "The Jellyfin project"
+ WriteRegStr HKLM "${REG_UNINST_KEY}" "URLInfoAbout" "https://jellyfin.github.io/"
+ WriteRegStr HKLM "${REG_UNINST_KEY}" "DisplayVersion" "$_JELLYFINVERSION_"
+ WriteRegDWORD HKLM "${REG_UNINST_KEY}" "NoModify" 1
+ WriteRegDWORD HKLM "${REG_UNINST_KEY}" "NoRepair" 1
;Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"
SectionEnd
-Section /o "Jellyfin desktop shortcut" DesktopShortcut
- SetShellVarContext current ; to create the shortcut for current user only
- DetailPrint "Creating desktop shortcut"
- CreateShortCut "$DESKTOP\Jellyfin.lnk" "$INSTDIR\jellyfin.exe"
-SectionEnd
-
Section "Jellyfin Service" InstallService
+
ExecWait '"$INSTDIR\nssm.exe" install Jellyfin "$INSTDIR\jellyfin.exe" --datadir "$_JELLYFINDATADIR_"' $0
DetailPrint "Jellyfin Service install, $0"
- Sleep 3000
+
+ Sleep 3000 ; Give time for Windows to catchup
+
ExecWait '"$INSTDIR\nssm.exe" set Jellyfin Start SERVICE_DELAYED_AUTO_START' $0
DetailPrint "Jellyfin Service setting, $0"
- StrCpy $_SERVICEINSTALLED_ "YES"
+
+ Sleep 3000
+ ${If} $_NETWORKSERVICEACCOUNT_ == "YES"
+ DetailPrint "Attempting to change service account to Network Service"
+ ExecWait '"$INSTDIR\nssm.exe" set Jellyfin Objectname "Network Service"' $0
+ DetailPrint "Jellyfin service account change, $0"
+ ${EndIf}
+
SectionEnd
-Section "Start Jellyfin after installation" LaunchJellyfin
-; either start the service or launch jellyfin standalone
- StrCmp $_SERVICEINSTALLED_ "YES" ServiceStart Standalone
-
- ServiceStart:
+Section "Start Jellyfin service after install" StartService
+
ExecWait '"$INSTDIR\nssm.exe" start Jellyfin' $0
DetailPrint "Jellyfin service start, $0"
- Return
- Standalone:
- ExecWait '"$INSTDIR\jellyfin.exe"' $0
- DetailPrint "$INSTDIR\jellyfin.exe start, $0"
- Return
-
SectionEnd
+SectionGroup "Advanced"
+Section /o "Use Network Service account" NetworkServiceAccount
+ ; The section is for user choice, nothing to do here
+SectionEnd
+Section /o "Custom Jellyfin metadata folder" CustomDataFolder
+ ; The section is for user choice, nothing to do here
+SectionEnd
+SectionGroupEnd
+
+
;--------------------------------
;Descriptions
;Language strings
- LangString DESC_InstallJellyfin ${LANG_ENGLISH} "Install Jellyfin"
+ LangString DESC_InstallJellyfin ${LANG_ENGLISH} "Install Jellyfin Server"
LangString DESC_InstallService ${LANG_ENGLISH} "Install As a Service"
- LangString DESC_DesktopShortcut ${LANG_ENGLISH} "Create a desktop shortcut"
- LangString DESC_LaunchJellyfin ${LANG_ENGLISH} "Start Jellyfin after Install"
+ LangString DESC_StartService ${LANG_ENGLISH} "Start Jellyfin service after Install"
+ LangString DESC_NetworkServiceAccount ${LANG_ENGLISH} "Use Network Service account to start windows service"
+ LangString DESC_CustomDataFolder ${LANG_ENGLISH} "Choose Jellyfin Server metadata folder in subsequent steps"
;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${InstallJellyfin} $(DESC_InstallJellyfin)
- !insertmacro MUI_DESCRIPTION_TEXT ${DesktopShortcut} $(DESC_DesktopShortcut)
!insertmacro MUI_DESCRIPTION_TEXT ${InstallService} $(DESC_InstallService)
- !insertmacro MUI_DESCRIPTION_TEXT ${LaunchJellyfin} $(DESC_LaunchJellyfin)
+ !insertmacro MUI_DESCRIPTION_TEXT ${StartService} $(DESC_StartService)
+ !insertmacro MUI_DESCRIPTION_TEXT ${NetworkServiceAccount} $(DESC_NetworkServiceAccount)
+ !insertmacro MUI_DESCRIPTION_TEXT ${CustomDataFolder} $(DESC_CustomDataFolder)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
;--------------------------------
;Uninstaller Section
Section "Uninstall"
- SetShellVarContext current
- StrCpy $_JELLYFINDATADIR_ "$LOCALAPPDATA\jellyfin\"
+
+ ReadRegStr $INSTDIR HKLM "Software\Jellyfin" "InstallFolder" ; read the installation folder
+ ReadRegStr $_JELLYFINDATADIR_ HKLM "Software\Jellyfin" "DataFolder" ; read the metadata folder
+
+ DetailPrint "Jellyfin Install location : $INSTDIR"
+ DetailPrint "Jellyfin data folder : $_JELLYFINDATADIR_"
-; Currently we try to stop & remove a running service even if it doesn't exist
-; not really sure about nssm statuscode detection method
-; nothing to loose with brute force stop & remove method
+ MessageBox MB_YESNO|MB_ICONINFORMATION "Do you want to retain Jellyfin metadata folder? The media will not be touched. $\r$\nIf unsure choose YES." /SD IDYES IDYES PreserveData
+
+ RMDir /r /REBOOTOK "$_JELLYFINDATADIR_"
+
+ PreserveData:
+
+ DetailPrint "Attempting to stop Jellyfin Server"
ExecWait '"$INSTDIR\nssm.exe" stop Jellyfin' $0
DetailPrint "Jellyfin service stop, $0"
+ DetailPrint "Attempting to remove Jellyfin service"
ExecWait '"$INSTDIR\nssm.exe" remove Jellyfin confirm' $0
DetailPrint "Jellyfin Service remove, $0"
Delete "$INSTDIR\Uninstall.exe"
-;TODO
-; stop running instance gracefully, in case its running, the /REBOOTOK flag will delete it on reboot.
- RMDir /r /REBOOTOK "$INSTDIR" ;
-
- MessageBox MB_YESNO|MB_ICONINFORMATION "Do you want to retain Jellyfin settings ? The media will not be touched in any case." /SD IDYES IDYES PreserveData
- RMDir /r /REBOOTOK "$_JELLYFINDATADIR_"
-
- PreserveData:
+ RMDir /r /REBOOTOK "$INSTDIR"
DeleteRegKey HKLM "Software\Jellyfin"
- DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Jellyfin"
- Delete "$DESKTOP\Jellyfin.lnk"
+ DeleteRegKey HKLM "${REG_UNINST_KEY}"
SectionEnd
Function .onInit
-
+; Setting up defaults
+ StrCpy $_INSTALLSERVICE_ "YES"
+ StrCpy $_SERVICESTART_ "YES"
+ StrCpy $_CUSTOMDATAFOLDER_ "NO"
+ StrCpy $_NETWORKSERVICEACCOUNT_ "NO"
StrCpy $_EXISTINGINSTALLATION_ "NO"
StrCpy $_EXISTINGSERVICE_ "NO"
+
+ SetShellVarContext current
+ StrCpy $_JELLYFINDATADIR_ "$%ProgramData%\jellyfin\"
+
;Detect if Jellyfin is already installed.
; In case it is installed, let the user choose either
; 1. Exit installer
; 2. Upgrade without messing with data
-; 2a. Don't ask for any installation folder
-; 2b. If there is a service, don't ask for service installation or launch
-; 2c. If no service, ask for autolaunch, and start as standalone
+; 2a. Don't ask for any details, uninstall and install afresh with old settings
-; Read Registry for installation
+; Read Registry for previous installation
ClearErrors
- ReadRegStr "$0" HKLM "Software\Jellyfin" "InstallLocation"
+ ReadRegStr "$0" HKLM "Software\Jellyfin" "InstallFolder"
IfErrors NoExisitingInstall
DetailPrint "Existing Jellyfin detected at: $0"
- StrCpy "$INSTDIR" "$0"
- StrCpy $_EXISTINGINSTALLATION_ "YES"
- SectionSetText ${InstallJellyfin} "Upgrade Jellyfin" ; Change install text to "Upgrade"
+ StrCpy "$INSTDIR" "$0" ; set the location fro registry as new default
+
+ StrCpy $_EXISTINGINSTALLATION_ "YES" ; Set our flag to be used later
+ SectionSetText ${InstallJellyfin} "Upgrade Jellyfin Server(required)" ; Change install text to "Upgrade"
-; check if there is a service called Jellyfin
+; check if there is a service called Jellyfin, there should be
; hack : nssm statuscode Jellyfin will return non zero return code in case it exists
ExecWait '"$INSTDIR\nssm.exe" statuscode Jellyfin' $0
DetailPrint "Jellyfin service statuscode, $0"
- IntCmp $0 0 NoService ; service doesn't exist
-
+ IntCmp $0 0 NoService ; service doesn't exist, may be run from desktop shortcut
+
+ ; if service was detected, set defaults going forward.
StrCpy $_EXISTINGSERVICE_ "YES"
- SectionSetText ${InstallService} "" ; hide service install option if old install was a service
- SectionSetText ${LaunchJellyfin} "" ; hide service start option if old install was a service
- SectionSetText ${DesktopShortcut} "" ; hide desktop shortcut option too
+ StrCpy $_INSTALLSERVICE_ "YES"
+ StrCpy $_SERVICESTART_ "YES"
+
+ ; check if service was run using Network Service account
+ ClearErrors
+ ReadRegStr "$_NETWORKSERVICEACCOUNT_" HKLM "Software\Jellyfin" "NetworkService" ; in case of error _NETWORKSERVICEACCOUNT_ will be NO as default
+
+ ClearErrors
+ ReadRegStr $_JELLYFINDATADIR_ HKLM "Software\Jellyfin" "DataFolder" ; in case of error, the default holds
+
+ ; Hide sections which will not be needed in case of previous install
+ SectionSetText ${InstallService} ""
+ SectionSetText ${StartService} ""
+ SectionSetText ${NetworkServiceAccount} ""
+ SectionSetText ${CustomDataFolder} ""
- NoService:
-; if detected, let the user know that we'll upgrade and its ok to quit
- MessageBox MB_OKCANCEL|MB_ICONINFORMATION "Existing installation of Jellyfin was detected, it'll be upgraded, settings will be retained" /SD IDOK IDOK Proceed
+ NoService: ; existing install was present but no service was detected
+
+; Let the user know that we'll upgrade and provide an option to quit.
+ MessageBox MB_OKCANCEL|MB_ICONINFORMATION "Existing installation of Jellyfin was detected, it'll be upgraded, settings will be retained. \
+ $\r$\nClick OK to proceed, Cancel to exit installer." /SD IDOK IDOK ProceedWithUpgrade
Quit ; Quit if the user is not sure about upgrade
- Proceed:
+ ProceedWithUpgrade:
NoExisitingInstall:
-
- SetShellVarContext all
+; by this time, the variables have been correctly set to reflect previous install details
-; Align installer version with jellyfin.dll version
- !getdllversion "$%InstallLocation%\jellyfin.dll" ver_
- StrCpy $_JELLYFINVERSION_ "${ver_1}.${ver_2}.${ver_3}.${ver_4}"
-
- SetShellVarContext current
- StrCpy $_JELLYFINDATADIR_ "$LOCALAPPDATA\jellyfin\"
- DetailPrint "_JELLYFINDATADIR_ : $_JELLYFINDATADIR_"
- StrCpy $_SERVICEINSTALLED_ "NO"
- SectionSetFlags ${InstallJellyfin} 17 ; this makes the InstallJellyfin section mandatory
-
FunctionEnd
-Function HideDirectoryPage
- StrCmp $_EXISTINGINSTALLATION_ "NO" show
-
- Abort ; Don't show folder selection if just upgrading
-
- show:
+Function HideInstallDirectoryPage
+ ${If} $_EXISTINGINSTALLATION_ == "YES" ; Existing installation detected, so don't ask for InstallFolder
+ Abort
+ ${EndIf}
+FunctionEnd
+
+; Don't show custom folder option in case it wasn't chosen
+Function HideDataDirectoryPage
+ ${If} $_CUSTOMDATAFOLDER_ == "NO"
+ Abort
+ ${EndIf}
+FunctionEnd
+
+; This function handles the choices during component selection
+Function .onSelChange
+ SectionGetFlags ${CustomDataFolder} $0
+ ${If} $0 = ${SF_SELECTED}
+ StrCpy $_CUSTOMDATAFOLDER_ "YES"
+ ${Else}
+ StrCpy $_CUSTOMDATAFOLDER_ "NO"
+ ${EndIf}
+
+; If we are not installing service, we don't need to set the NetworkService account or StartService
+ SectionGetFlags ${InstallService} $0
+ ${If} $0 = ${SF_SELECTED}
+ StrCpy $_INSTALLSERVICE_ "YES"
+ SectionGetFlags ${NetworkServiceAccount} $0
+ IntOp $0 $0 | ${SF_RO}
+ IntOp $0 $0 ^ ${SF_RO}
+ SectionSetFlags ${NetworkServiceAccount} $0
+ SectionGetFlags ${StartService} $0
+ IntOp $0 $0 | ${SF_RO}
+ IntOp $0 $0 ^ ${SF_RO}
+ SectionSetFlags ${StartService} $0
+ ${Else}
+ StrCpy $_INSTALLSERVICE_ "NO"
+ IntOp $0 ${SF_USELECTED} | ${SF_RO}
+ SectionSetFlags ${NetworkServiceAccount} $0
+ SectionSetFlags ${StartService} $0
+ ${EndIf}
+
+ SectionGetFlags ${StartService} $0
+ ${If} $0 = ${SF_SELECTED}
+ StrCpy $_SERVICESTART_ "YES"
+ ${Else}
+ StrCpy $_SERVICESTART_ "NO"
+ ${EndIf}
+
+ SectionGetFlags ${NetworkServiceAccount} $0
+ ${If} $0 = ${SF_SELECTED}
+ StrCpy $_NETWORKSERVICEACCOUNT_ "YES"
+ ${Else}
+ StrCpy $_NETWORKSERVICEACCOUNT_ "NO"
+ ${EndIf}
+
+
+FunctionEnd
+
+Function ConfirmationPage
+ !insertmacro MUI_HEADER_TEXT "Confirmation Page" "Please confirm your choices for Jellyfin Server installation"
+
+ nsDialogs::Create 1018
+
+ ${NSD_CreateLabel} 0 0 100% 100% "The installer will proceed based on the following inputs gathered on earlier screens.$\r$\n$\r$\n\
+ Installation Folder : $INSTDIR$\r$\n\
+ Service install : $_INSTALLSERVICE_$\r$\n\
+ Service start : $_SERVICESTART_$\r$\n\
+ Network Service Account : $_NETWORKSERVICEACCOUNT_$\r$\n\
+ Custom Metadata folder : $_CUSTOMDATAFOLDER_$\r$\n\
+ Jellyfin Metadata Folder: $_JELLYFINDATADIR_"
+ nsDialogs::Show
+
FunctionEnd
From d39decf9185a8221eb8f28ca752a233446f54ceb Mon Sep 17 00:00:00 2001
From: Anthony Lavado
Date: Sat, 27 Jul 2019 02:30:42 -0400
Subject: [PATCH 145/214] Adds null check on studio/network name
---
MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
index 5ea73dfbf5..80f2ceae69 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
@@ -355,7 +355,10 @@ namespace MediaBrowser.Providers.TV.TheTVDB
series.AddGenre(genre);
}
- series.AddStudio(tvdbSeries.Network);
+ if (!String.IsNullOrEmpty(tvdbSeries.Network))
+ {
+ series.AddStudio(tvdbSeries.Network);
+ }
if (result.Item.Status.HasValue && result.Item.Status.Value == SeriesStatus.Ended)
{
From c6111a7fb57a7d919ebf453ff3b9248cb8c3f6f0 Mon Sep 17 00:00:00 2001
From: crankdoofus <52436708+crankdoofus@users.noreply.github.com>
Date: Sat, 27 Jul 2019 20:23:22 +1000
Subject: [PATCH 146/214] Change service install user
The default is Network Service, with advanced option to use Local System
---
deployment/windows/jellyfin.nsi | 47 ++++++++++++++++++---------------
1 file changed, 26 insertions(+), 21 deletions(-)
diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi
index 5e3dcac841..a374ebe45e 100644
--- a/deployment/windows/jellyfin.nsi
+++ b/deployment/windows/jellyfin.nsi
@@ -13,7 +13,7 @@
Var _JELLYFINDATADIR_
Var _INSTALLSERVICE_
Var _SERVICESTART_
- Var _NETWORKSERVICEACCOUNT_
+ Var _LOCALSYSTEMACCOUNT_
Var _EXISTINGINSTALLATION_
Var _EXISTINGSERVICE_
Var _CUSTOMDATAFOLDER_
@@ -67,8 +67,8 @@
; Welcome Page
!define MUI_WELCOMEPAGE_TEXT "The installer will ask for details to install Jellyfin Server.$\r$\n$\r$\n$\r$\n\
ADVANCED:$\r$\n\
- The default service install uses Local System account and is sufficient for most users. $\r$\n$\r$\n\
- You can choose to install using Network Service account under advanced options. This also affects where Jellyfin Server and Jellyfin data can be installed. The installer will NOT check this, you should know what you are doing.$\r$\n$\r$\n\
+ The default service install uses Network Service account and is sufficient for most users. $\r$\n$\r$\n\
+ You can choose to install using Local System account under Advanced options. This also affects where Jellyfin Server and Jellyfin data can be installed. The installer will NOT check this, you should know what you are doing.$\r$\n$\r$\n\
You can choose the folder for Jellyfin Metadata under advanced options based on your needs."
!insertmacro MUI_PAGE_WELCOME
; License Page
@@ -94,7 +94,6 @@
; Actual Installion Page
!insertmacro MUI_PAGE_INSTFILES
-
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
@@ -129,7 +128,7 @@ Section "Jellyfin Server (required)" InstallJellyfin
; Write the InstallFolder, DataFolder, Network Service info into the registry for later use
WriteRegExpandStr HKLM "Software\Jellyfin" "InstallFolder" "$INSTDIR"
WriteRegExpandStr HKLM "Software\Jellyfin" "DataFolder" "$_JELLYFINDATADIR_"
- WriteRegStr HKLM "Software\Jellyfin" "NetworkService" "$_NETWORKSERVICEACCOUNT_"
+ WriteRegStr HKLM "Software\Jellyfin" "LocalSystemAccount" "$_LOCALSYSTEMACCOUNT_"
!getdllversion "$%InstallLocation%\jellyfin.dll" ver_
StrCpy $_JELLYFINVERSION_ "${ver_1}.${ver_2}.${ver_3}" ;
@@ -160,10 +159,10 @@ Section "Jellyfin Service" InstallService
DetailPrint "Jellyfin Service setting, $0"
Sleep 3000
- ${If} $_NETWORKSERVICEACCOUNT_ == "YES"
+ ${If} $_LOCALSYSTEMACCOUNT_ == "NO" ; the default install using NSSM is Local System
DetailPrint "Attempting to change service account to Network Service"
ExecWait '"$INSTDIR\nssm.exe" set Jellyfin Objectname "Network Service"' $0
- DetailPrint "Jellyfin service account change, $0"
+ DetailPrint "Jellyfin service account change, $0"
${EndIf}
SectionEnd
@@ -175,8 +174,8 @@ Section "Start Jellyfin service after install" StartService
SectionEnd
-SectionGroup "Advanced"
-Section /o "Use Network Service account" NetworkServiceAccount
+SectionGroup "Advanced" Advanced
+Section /o "Use Local System account" LocalSystemAccount
; The section is for user choice, nothing to do here
SectionEnd
Section /o "Custom Jellyfin metadata folder" CustomDataFolder
@@ -192,7 +191,7 @@ SectionGroupEnd
LangString DESC_InstallJellyfin ${LANG_ENGLISH} "Install Jellyfin Server"
LangString DESC_InstallService ${LANG_ENGLISH} "Install As a Service"
LangString DESC_StartService ${LANG_ENGLISH} "Start Jellyfin service after Install"
- LangString DESC_NetworkServiceAccount ${LANG_ENGLISH} "Use Network Service account to start windows service"
+ LangString DESC_LocalSystemAccount ${LANG_ENGLISH} "Use Local System account to start windows service"
LangString DESC_CustomDataFolder ${LANG_ENGLISH} "Choose Jellyfin Server metadata folder in subsequent steps"
;Assign language strings to sections
@@ -200,7 +199,7 @@ SectionGroupEnd
!insertmacro MUI_DESCRIPTION_TEXT ${InstallJellyfin} $(DESC_InstallJellyfin)
!insertmacro MUI_DESCRIPTION_TEXT ${InstallService} $(DESC_InstallService)
!insertmacro MUI_DESCRIPTION_TEXT ${StartService} $(DESC_StartService)
- !insertmacro MUI_DESCRIPTION_TEXT ${NetworkServiceAccount} $(DESC_NetworkServiceAccount)
+ !insertmacro MUI_DESCRIPTION_TEXT ${LocalSystemAccount} $(DESC_LocalSystemAccount)
!insertmacro MUI_DESCRIPTION_TEXT ${CustomDataFolder} $(DESC_CustomDataFolder)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
@@ -244,7 +243,7 @@ Function .onInit
StrCpy $_INSTALLSERVICE_ "YES"
StrCpy $_SERVICESTART_ "YES"
StrCpy $_CUSTOMDATAFOLDER_ "NO"
- StrCpy $_NETWORKSERVICEACCOUNT_ "NO"
+ StrCpy $_LOCALSYSTEMACCOUNT_ "NO"
StrCpy $_EXISTINGINSTALLATION_ "NO"
StrCpy $_EXISTINGSERVICE_ "NO"
@@ -281,7 +280,7 @@ Function .onInit
; check if service was run using Network Service account
ClearErrors
- ReadRegStr "$_NETWORKSERVICEACCOUNT_" HKLM "Software\Jellyfin" "NetworkService" ; in case of error _NETWORKSERVICEACCOUNT_ will be NO as default
+ ReadRegStr "$_LOCALSYSTEMACCOUNT_" HKLM "Software\Jellyfin" "LocalSystemAccount" ; in case of error _LOCALSYSTEMACCOUNT_ will be NO as default
ClearErrors
ReadRegStr $_JELLYFINDATADIR_ HKLM "Software\Jellyfin" "DataFolder" ; in case of error, the default holds
@@ -289,8 +288,10 @@ Function .onInit
; Hide sections which will not be needed in case of previous install
SectionSetText ${InstallService} ""
SectionSetText ${StartService} ""
- SectionSetText ${NetworkServiceAccount} ""
+ SectionSetText ${LocalSystemAccount} ""
SectionSetText ${CustomDataFolder} ""
+ SectionSetText ${Advanced} ""
+
NoService: ; existing install was present but no service was detected
@@ -332,10 +333,10 @@ Function .onSelChange
SectionGetFlags ${InstallService} $0
${If} $0 = ${SF_SELECTED}
StrCpy $_INSTALLSERVICE_ "YES"
- SectionGetFlags ${NetworkServiceAccount} $0
+ SectionGetFlags ${LocalSystemAccount} $0
IntOp $0 $0 | ${SF_RO}
IntOp $0 $0 ^ ${SF_RO}
- SectionSetFlags ${NetworkServiceAccount} $0
+ SectionSetFlags ${LocalSystemAccount} $0
SectionGetFlags ${StartService} $0
IntOp $0 $0 | ${SF_RO}
IntOp $0 $0 ^ ${SF_RO}
@@ -343,7 +344,7 @@ Function .onSelChange
${Else}
StrCpy $_INSTALLSERVICE_ "NO"
IntOp $0 ${SF_USELECTED} | ${SF_RO}
- SectionSetFlags ${NetworkServiceAccount} $0
+ SectionSetFlags ${LocalSystemAccount} $0
SectionSetFlags ${StartService} $0
${EndIf}
@@ -354,11 +355,11 @@ Function .onSelChange
StrCpy $_SERVICESTART_ "NO"
${EndIf}
- SectionGetFlags ${NetworkServiceAccount} $0
+ SectionGetFlags ${LocalSystemAccount} $0
${If} $0 = ${SF_SELECTED}
- StrCpy $_NETWORKSERVICEACCOUNT_ "YES"
+ StrCpy $_LOCALSYSTEMACCOUNT_ "YES"
${Else}
- StrCpy $_NETWORKSERVICEACCOUNT_ "NO"
+ StrCpy $_LOCALSYSTEMACCOUNT_ "NO"
${EndIf}
@@ -373,9 +374,13 @@ Function ConfirmationPage
Installation Folder : $INSTDIR$\r$\n\
Service install : $_INSTALLSERVICE_$\r$\n\
Service start : $_SERVICESTART_$\r$\n\
- Network Service Account : $_NETWORKSERVICEACCOUNT_$\r$\n\
+ Local System account for service: $_LOCALSYSTEMACCOUNT_$\r$\n\
Custom Metadata folder : $_CUSTOMDATAFOLDER_$\r$\n\
Jellyfin Metadata Folder: $_JELLYFINDATADIR_"
nsDialogs::Show
FunctionEnd
+
+Function .onInstSuccess
+ ExecShell "open" "http://localhost:8096"
+FunctionEnd
From a1efe4caca8cc444f9d3caa6a45edea7e959c41a Mon Sep 17 00:00:00 2001
From: Erik Larsson
Date: Sat, 27 Jul 2019 19:55:18 +0200
Subject: [PATCH 147/214] Add DLNA headers if requested by the client.
And remove the code which adds the headers as ResponseHelper.WriteToResponse
will do it.
---
MediaBrowser.Api/Playback/BaseStreamingService.cs | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 3994016240..4a198661cc 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -690,8 +690,8 @@ namespace MediaBrowser.Api.Playback
request.AudioCodec = EncodingHelper.InferAudioCodec(url);
}
- var enableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) /*||
- string.Equals(Request.Headers.Get("GetContentFeatures.DLNA.ORG"), "1", StringComparison.OrdinalIgnoreCase)*/;
+ var enableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) ||
+ string.Equals(GetHeader("GetContentFeatures.DLNA.ORG"), "1", StringComparison.OrdinalIgnoreCase);
var state = new StreamState(MediaSourceManager, TranscodingJobType)
{
@@ -1016,11 +1016,6 @@ namespace MediaBrowser.Api.Playback
).FirstOrDefault() ?? string.Empty;
}
-
- foreach (var item in responseHeaders)
- {
- Request.Response.AddHeader(item.Key, item.Value);
- }
}
private void AddTimeSeekResponseHeaders(StreamState state, IDictionary responseHeaders)
From 7faf3ab04aafc02a916b6fb7e010d73d3211e1ef Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Sun, 28 Jul 2019 19:41:36 +0200
Subject: [PATCH 148/214] Delete feature_request.md
---
.github/ISSUE_TEMPLATE/feature_request.md | 14 --------------
1 file changed, 14 deletions(-)
delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index 3cbc8cbb91..0000000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-name: Feature request
-about: Suggest a new feature
-title: ''
-labels: feature
-assignees: ''
-
----
-
-**Describe the feature you'd like**
-
-
-**Additional context**
-
From 8b1bd7ac6ba8ca9f1d8a57542a46e66107277ceb Mon Sep 17 00:00:00 2001
From: Bond-009
Date: Wed, 13 Feb 2019 16:37:18 +0100
Subject: [PATCH 149/214] Use a dictionary to look up mimetypes
---
Emby.Dlna/Didl/DidlBuilder.cs | 30 +-
Emby.Dlna/Profiles/DishHopperJoeyProfile.cs | 2 +-
Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml | 2 +-
MediaBrowser.Model/Net/MimeTypes.cs | 374 ++++++--------------
4 files changed, 123 insertions(+), 285 deletions(-)
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index 992a50e676..a21aff9f91 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -181,19 +181,6 @@ namespace Emby.Dlna.Didl
writer.WriteFullEndElement();
}
- private string GetMimeType(string input)
- {
- var mime = MimeTypes.GetMimeType(input);
-
- // TODO: Instead of being hard-coded here, this should probably be moved into all of the existing profiles
- if (string.Equals(mime, "video/mp2t", StringComparison.OrdinalIgnoreCase))
- {
- mime = "video/mpeg";
- }
-
- return mime;
- }
-
private void AddVideoResource(DlnaOptions options, XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null)
{
if (streamInfo == null)
@@ -384,7 +371,7 @@ namespace Emby.Dlna.Didl
var filename = url.Substring(0, url.IndexOf('?'));
var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
- ? GetMimeType(filename)
+ ? MimeTypes.GetMimeType(filename)
: mediaProfile.MimeType;
writer.WriteAttributeString("protocolInfo", string.Format(
@@ -520,7 +507,7 @@ namespace Emby.Dlna.Didl
var filename = url.Substring(0, url.IndexOf('?'));
var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
- ? GetMimeType(filename)
+ ? MimeTypes.GetMimeType(filename)
: mediaProfile.MimeType;
var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container,
@@ -545,17 +532,10 @@ namespace Emby.Dlna.Didl
}
public static bool IsIdRoot(string id)
- {
- if (string.IsNullOrWhiteSpace(id)
+ => string.IsNullOrWhiteSpace(id)
|| string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
// Samsung sometimes uses 1 as root
- || string.Equals(id, "1", StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
-
- return false;
- }
+ || string.Equals(id, "1", StringComparison.OrdinalIgnoreCase);
public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null)
{
@@ -971,7 +951,7 @@ namespace Emby.Dlna.Didl
writer.WriteAttributeString("protocolInfo", string.Format(
"http-get:*:{0}:{1}",
- GetMimeType("file." + format),
+ MimeTypes.GetMimeType("file." + format),
contentFeatures
));
diff --git a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
index d501cce0d2..8d8ab41cad 100644
--- a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
+++ b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
@@ -9,7 +9,7 @@ namespace Emby.Dlna.Profiles
{
Name = "Dish Hopper-Joey";
- ProtocolInfo = "http-get:*:video/mp2t:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*";
+ ProtocolInfo = "http-get:*:video/mp2t:*,http-get:*:video/mpeg:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*";
Identification = new DeviceIdentification
{
diff --git a/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml b/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
index 5ff4975604..5b299577e1 100644
--- a/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
+++ b/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
@@ -28,7 +28,7 @@
140000000
192000
- http-get:*:video/mp2t:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*
+ http-get:*:video/mp2t:http-get:*:video/mpeg:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*
0
false
false
diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs
index 659abe84c9..d7bf956bbd 100644
--- a/MediaBrowser.Model/Net/MimeTypes.cs
+++ b/MediaBrowser.Model/Net/MimeTypes.cs
@@ -12,113 +12,133 @@ namespace MediaBrowser.Model.Net
public static class MimeTypes
{
///
- /// Any extension in this list is considered a video file - can be added to at runtime for extensibility
+ /// Any extension in this list is considered a video file
///
- private static readonly string[] VideoFileExtensions = new string[]
- {
- ".mkv",
- ".m2t",
- ".m2ts",
- ".img",
- ".iso",
- ".mk3d",
- ".ts",
- ".rmvb",
- ".mov",
- ".avi",
- ".mpg",
- ".mpeg",
- ".wmv",
- ".mp4",
- ".divx",
- ".dvr-ms",
- ".wtv",
- ".ogm",
- ".ogv",
- ".asf",
- ".m4v",
- ".flv",
- ".f4v",
- ".3gp",
- ".webm",
- ".mts",
- ".m2v",
- ".rec"
- };
-
- private static Dictionary GetVideoFileExtensionsDictionary()
+ private static readonly HashSet _videoFileExtensions = new HashSet(StringComparer.OrdinalIgnoreCase)
{
- var dict = new Dictionary(StringComparer.OrdinalIgnoreCase);
-
- foreach (string ext in VideoFileExtensions)
- {
- dict[ext] = ext;
- }
-
- return dict;
- }
-
- private static readonly Dictionary VideoFileExtensionsDictionary = GetVideoFileExtensionsDictionary();
+ ".mkv",
+ ".m2t",
+ ".m2ts",
+ ".img",
+ ".iso",
+ ".mk3d",
+ ".ts",
+ ".rmvb",
+ ".mov",
+ ".avi",
+ ".mpg",
+ ".mpeg",
+ ".wmv",
+ ".mp4",
+ ".divx",
+ ".dvr-ms",
+ ".wtv",
+ ".ogm",
+ ".ogv",
+ ".asf",
+ ".m4v",
+ ".flv",
+ ".f4v",
+ ".3gp",
+ ".webm",
+ ".mts",
+ ".m2v",
+ ".rec"
+ };
// http://en.wikipedia.org/wiki/Internet_media_type
// Add more as needed
-
- private static Dictionary GetMimeTypeLookup()
+ private static readonly Dictionary _mimeTypeLookup = new Dictionary(StringComparer.OrdinalIgnoreCase)
{
- var dict = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ // Type application
+ { ".cbz", "application/x-cbz" },
+ { ".cbr", "application/epub+zip" },
+ { ".eot", "application/vnd.ms-fontobject" },
+ { ".epub", "application/epub+zip" },
+ { ".js", "application/x-javascript" },
+ { ".json", "application/json" },
+ { ".map", "application/x-javascript" },
+ { ".pdf", "application/pdf" },
+ { ".ttml", "application/ttml+xml" },
+ { ".m3u8", "application/x-mpegURL" },
+ { ".mobi", "application/x-mobipocket-ebook" },
+ { ".xml", "application/xml" },
- dict.Add(".jpg", "image/jpeg");
- dict.Add(".jpeg", "image/jpeg");
- dict.Add(".tbn", "image/jpeg");
- dict.Add(".png", "image/png");
- dict.Add(".gif", "image/gif");
- dict.Add(".tiff", "image/tiff");
- dict.Add(".webp", "image/webp");
- dict.Add(".ico", "image/vnd.microsoft.icon");
- dict.Add(".mpg", "video/mpeg");
- dict.Add(".mpeg", "video/mpeg");
- dict.Add(".ogv", "video/ogg");
- dict.Add(".mov", "video/quicktime");
- dict.Add(".webm", "video/webm");
- dict.Add(".mkv", "video/x-matroska");
- dict.Add(".wmv", "video/x-ms-wmv");
- dict.Add(".flv", "video/x-flv");
- dict.Add(".avi", "video/x-msvideo");
- dict.Add(".asf", "video/x-ms-asf");
- dict.Add(".m4v", "video/x-m4v");
- dict.Add(".m4s", "video/mp4");
- dict.Add(".cbz", "application/x-cbz");
- dict.Add(".cbr", "application/epub+zip");
- dict.Add(".epub", "application/epub+zip");
- dict.Add(".pdf", "application/pdf");
- dict.Add(".mobi", "application/x-mobipocket-ebook");
+ // Type image
+ { ".jpg", "image/jpeg" },
+ { ".jpeg", "image/jpeg" },
+ { ".tbn", "image/jpeg" },
+ { ".png", "image/png" },
+ { ".gif", "image/gif" },
+ { ".tiff", "image/tiff" },
+ { ".webp", "image/webp" },
+ { ".ico", "image/vnd.microsoft.icon" },
+ { ".svg", "image/svg+xml" },
+ { ".svgz", "image/svg+xml" },
- dict.Add(".ass", "text/x-ssa");
- dict.Add(".ssa", "text/x-ssa");
+ // Type font
+ { ".ttf" , "font/ttf" },
+ { ".woff" , "font/woff" },
- return dict;
- }
+ // Type text
+ { ".ass", "text/x-ssa" },
+ { ".ssa", "text/x-ssa" },
+ { ".css", "text/css" },
+ { ".csv", "text/csv" },
+ { ".txt", "text/plain" },
+ { ".vtt", "text/vtt" },
- private static readonly Dictionary MimeTypeLookup = GetMimeTypeLookup();
+ // Type video
+ { ".mpg", "video/mpeg" },
+ { ".ogv", "video/ogg" },
+ { ".mov", "video/quicktime" },
+ { ".webm", "video/webm" },
+ { ".mkv", "video/x-matroska" },
+ { ".wmv", "video/x-ms-wmv" },
+ { ".flv", "video/x-flv" },
+ { ".avi", "video/x-msvideo" },
+ { ".asf", "video/x-ms-asf" },
+ { ".m4v", "video/x-m4v" },
+ { ".m4s", "video/mp4" },
+ { ".3gp", "video/3gpp" },
+ { ".3g2", "video/3gpp2" },
+ { ".mpd", "video/vnd.mpeg.dash.mpd" },
+ { ".ts", "video/mp2t" },
- private static readonly Dictionary ExtensionLookup = CreateExtensionLookup();
+ // Type audio
+ { ".mp3", "audio/mpeg" },
+ { ".m4a", "audio/mp4" },
+ { ".aac", "audio/mp4" },
+ { ".webma", "audio/webm" },
+ { ".wav", "audio/wav" },
+ { ".wma", "audio/x-ms-wma" },
+ { ".ogg", "audio/ogg" },
+ { ".oga", "audio/ogg" },
+ { ".opus", "audio/ogg" },
+ { ".ac3", "audio/ac3" },
+ { ".dsf", "audio/dsf" },
+ { ".m4b", "audio/m4b" },
+ { ".xsp", "audio/xsp" },
+ { ".dsp", "audio/dsp" },
+ };
+
+ private static readonly Dictionary _extensionLookup = CreateExtensionLookup();
private static Dictionary CreateExtensionLookup()
{
- var dict = MimeTypeLookup
+ var dict = _mimeTypeLookup
.GroupBy(i => i.Value)
.ToDictionary(x => x.Key, x => x.First().Key, StringComparer.OrdinalIgnoreCase);
dict["image/jpg"] = ".jpg";
dict["image/x-png"] = ".png";
+ dict["audio/x-aac"] = ".aac";
+
return dict;
}
- public static string GetMimeType(string path)
- {
- return GetMimeType(path, true);
- }
+ public static string GetMimeType(string path) => GetMimeType(path, true);
///
/// Gets the type of the MIME.
@@ -130,138 +150,31 @@ namespace MediaBrowser.Model.Net
throw new ArgumentNullException(nameof(path));
}
- var ext = Path.GetExtension(path) ?? string.Empty;
+ var ext = Path.GetExtension(path);
- if (MimeTypeLookup.TryGetValue(ext, out string result))
+ if (_mimeTypeLookup.TryGetValue(ext, out string result))
{
return result;
}
- // Type video
- if (StringHelper.EqualsIgnoreCase(ext, ".3gp"))
- {
- return "video/3gpp";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".3g2"))
- {
- return "video/3gpp2";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".ts"))
- {
- return "video/mp2t";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".mpd"))
- {
- return "video/vnd.mpeg.dash.mpd";
- }
-
// Catch-all for all video types that don't require specific mime types
- if (VideoFileExtensionsDictionary.ContainsKey(ext))
+ if (_videoFileExtensions.Contains(ext))
{
- return "video/" + ext.TrimStart('.').ToLowerInvariant();
+ return "video/" + ext.Substring(1);
}
// Type text
- if (StringHelper.EqualsIgnoreCase(ext, ".css"))
- {
- return "text/css";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".csv"))
- {
- return "text/csv";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".html"))
+ if (StringHelper.EqualsIgnoreCase(ext, ".html")
+ || StringHelper.EqualsIgnoreCase(ext, ".htm"))
{
return "text/html; charset=UTF-8";
}
- if (StringHelper.EqualsIgnoreCase(ext, ".htm"))
- {
- return "text/html; charset=UTF-8";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".txt"))
+
+ if (StringHelper.EqualsIgnoreCase(ext, ".log")
+ || StringHelper.EqualsIgnoreCase(ext, ".srt"))
{
return "text/plain";
}
- if (StringHelper.EqualsIgnoreCase(ext, ".log"))
- {
- return "text/plain";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".xml"))
- {
- return "application/xml";
- }
-
- // Type audio
- if (StringHelper.EqualsIgnoreCase(ext, ".mp3"))
- {
- return "audio/mpeg";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".m4a"))
- {
- return "audio/mp4";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".aac"))
- {
- return "audio/mp4";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".webma"))
- {
- return "audio/webm";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".wav"))
- {
- return "audio/wav";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".wma"))
- {
- return "audio/x-ms-wma";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".flac"))
- {
- return "audio/flac";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".aac"))
- {
- return "audio/x-aac";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".ogg"))
- {
- return "audio/ogg";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".oga"))
- {
- return "audio/ogg";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".opus"))
- {
- return "audio/ogg";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".ac3"))
- {
- return "audio/ac3";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".dsf"))
- {
- return "audio/dsf";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".m4b"))
- {
- return "audio/m4b";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".xsp"))
- {
- return "audio/xsp";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".dsp"))
- {
- return "audio/dsp";
- }
-
- // Playlists
- if (StringHelper.EqualsIgnoreCase(ext, ".m3u8"))
- {
- return "application/x-mpegURL";
- }
// Misc
if (StringHelper.EqualsIgnoreCase(ext, ".dll"))
@@ -269,63 +182,7 @@ namespace MediaBrowser.Model.Net
return "application/octet-stream";
}
- // Web
- if (StringHelper.EqualsIgnoreCase(ext, ".js"))
- {
- return "application/x-javascript";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".json"))
- {
- return "application/json";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".map"))
- {
- return "application/x-javascript";
- }
-
- if (StringHelper.EqualsIgnoreCase(ext, ".woff"))
- {
- return "font/woff";
- }
-
- if (StringHelper.EqualsIgnoreCase(ext, ".ttf"))
- {
- return "font/ttf";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".eot"))
- {
- return "application/vnd.ms-fontobject";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".svg"))
- {
- return "image/svg+xml";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".svgz"))
- {
- return "image/svg+xml";
- }
-
- if (StringHelper.EqualsIgnoreCase(ext, ".srt"))
- {
- return "text/plain";
- }
-
- if (StringHelper.EqualsIgnoreCase(ext, ".vtt"))
- {
- return "text/vtt";
- }
-
- if (StringHelper.EqualsIgnoreCase(ext, ".ttml"))
- {
- return "application/ttml+xml";
- }
-
- if (enableStreamDefault)
- {
- return "application/octet-stream";
- }
-
- return null;
+ return enableStreamDefault ? "application/octet-stream" : null;
}
public static string ToExtension(string mimeType)
@@ -338,10 +195,11 @@ namespace MediaBrowser.Model.Net
// handle text/html; charset=UTF-8
mimeType = mimeType.Split(';')[0];
- if (ExtensionLookup.TryGetValue(mimeType, out string result))
+ if (_extensionLookup.TryGetValue(mimeType, out string result))
{
return result;
}
+
return null;
}
}
From 9faf03541361d0d5e2adca14575dfd94807b29bf Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Sun, 28 Jul 2019 21:01:18 +0200
Subject: [PATCH 150/214] Add the --azure-pipelines switch to the compat
checker
---
.ci/azure-pipelines.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index efd69680e4..46db0d9fe5 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -190,7 +190,7 @@ jobs:
- task: CmdLine@2
displayName: Execute ABI compatibility check tool
inputs:
- script: 'dotnet tools/CompatibilityCheckerCoreCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName)'
+ script: 'dotnet tools/CompatibilityCheckerCoreCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines'
workingDirectory: $(System.ArtifactsDirectory) # Optional
#failOnStderr: false # Optional
From b4f446fe42eb86b6edf6e29cc7c9dba15c4feacd Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Sun, 28 Jul 2019 22:50:41 +0200
Subject: [PATCH 151/214] Delete enhancement-request.md
---
.github/ISSUE_TEMPLATE/enhancement-request.md | 20 -------------------
1 file changed, 20 deletions(-)
delete mode 100644 .github/ISSUE_TEMPLATE/enhancement-request.md
diff --git a/.github/ISSUE_TEMPLATE/enhancement-request.md b/.github/ISSUE_TEMPLATE/enhancement-request.md
deleted file mode 100644
index a655b60f53..0000000000
--- a/.github/ISSUE_TEMPLATE/enhancement-request.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-name: Enhancement request
-about: Suggest an modification to an existing feature
-title: ''
-labels: enhancement
-assignees: ''
-
----
-
-**Is your feature request related to a problem? Please describe.**
-
-
-**Describe the solution you'd like**
-
-
-**Describe alternatives you've considered**
-
-
-**Additional context**
-
From e6ef6088fffd8d532bd59c2bf2aca1f68cae2137 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Mon, 29 Jul 2019 00:43:57 +0200
Subject: [PATCH 152/214] Fix #1388 and #1472
---
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 2984efec37..b9ccc93efc 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -573,7 +573,9 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// TODO: Perhaps also use original_size=1920x800 ??
- return string.Format("subtitles=filename='{0}'{1}{2}{3}",
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "subtitles=filename='{0}'{1}{2}",
_mediaEncoder.EscapeSubtitleFilterPath(subtitlePath),
charsetParam,
// fallbackFontParam,
@@ -582,7 +584,9 @@ namespace MediaBrowser.Controller.MediaEncoding
var mediaPath = state.MediaPath ?? string.Empty;
- return string.Format("subtitles='{0}:si={1}'{2}",
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "subtitles='{0}:si={1}'{2}",
_mediaEncoder.EscapeSubtitleFilterPath(mediaPath),
state.InternalSubtitleStreamOffset.ToString(_usCulture),
// fallbackFontParam,
From 5c9d0414234d49a94f323228ac65cf191e80949b Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Mon, 29 Jul 2019 13:57:36 +0200
Subject: [PATCH 153/214] Ignore Ipv6 link-local addresses
---
Emby.Server.Implementations/Networking/NetworkManager.cs | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs
index 3cacacf075..da06dc5c57 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;
}
From 998017a76d7e0fcc340bf9dbc3436caef583f6b4 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Mon, 29 Jul 2019 16:01:14 +0200
Subject: [PATCH 154/214] 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 --
.../IPNetwork/IPAddressCollection.cs | 94 -
.../Networking/IPNetwork/IPNetwork.cs | 2008 -----------------
.../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
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 49015a07ec..abbaef26b8 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 2908ee9afd..dde4a2a34c 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 e4e944839a..0000000000
--- 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 a0c5f73af2..0000000000
--- 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 d6de61c0c9..0000000000
--- 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 4cda421e57..0000000000
--- 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 45d7392ac4..0000000000
--- 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 3cacacf075..82add242ac 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;
From 9e1adec5e00dd9a6c058c16b10ea42fef999b996 Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Mon, 29 Jul 2019 19:42:38 +0200
Subject: [PATCH 155/214] Update message for stale issues and also ignore
feature and enhancement labels
---
.github/stale.yml | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/.github/stale.yml b/.github/stale.yml
index 3e76aa75d1..011f763177 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -9,12 +9,14 @@ exemptLabels:
- dotnet-3.0-future
- roadmap
- future
+ - feature
+ - enhancement
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
- This issue has been automatically marked as stale because it has not had
- recent activity. It will be closed if no further activity occurs. Thank you
- for your contributions.
+ Issues go stale after 60d of inactivity. Mark the issue as fresh by adding a comment or commit. Stale issues close after an additional 7d of inactivity.
+ If this issue is safe to close now please do so.
+ If you have any questions you can reach us on [Matrix or Social Media](https://jellyfin.readthedocs.io/en/latest/getting-help/).
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
From 487ba2b928f800bcab0173067e0ad46ca8f18615 Mon Sep 17 00:00:00 2001
From: Erwin de Haan
Date: Mon, 29 Jul 2019 20:21:47 +0200
Subject: [PATCH 156/214] Add Fider to README
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 643c59a731..8106b6e4e7 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@
+
From d05440d267821b1242ecb81d77ac94325c0d06d0 Mon Sep 17 00:00:00 2001
From: Anthony Lavado
Date: Wed, 31 Jul 2019 12:21:46 -0400
Subject: [PATCH 157/214] Update
MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
Co-Authored-By: Claus Vium
---
MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
index 80f2ceae69..c739f3f494 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
@@ -355,7 +355,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
series.AddGenre(genre);
}
- if (!String.IsNullOrEmpty(tvdbSeries.Network))
+ if (!string.IsNullOrEmpty(tvdbSeries.Network))
{
series.AddStudio(tvdbSeries.Network);
}
From 358665d944a8bcf238b57f2fb1583796ed839dd4 Mon Sep 17 00:00:00 2001
From: Andrew Rabert
Date: Wed, 31 Jul 2019 16:58:36 -0400
Subject: [PATCH 158/214] Docker - make web version more configurable This
allows for building against jellyfin-web master. Ex.
docker build . --build-arg JELLYFIN_WEB_VERSION=master
---
Dockerfile | 4 ++--
Dockerfile.arm | 4 ++--
Dockerfile.arm64 | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index 057d4b0413..1644a8967d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -21,8 +21,8 @@ RUN apt-get update \
COPY --from=ffmpeg / /
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.7
-RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
+ARG JELLYFIN_WEB_VERSION=v10.3.7
+RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
diff --git a/Dockerfile.arm b/Dockerfile.arm
index dad4da1f14..b0cf7a8a46 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -26,8 +26,8 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.7
-RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
+ARG JELLYFIN_WEB_VERSION=v10.3.7
+RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index 5d4e86bff4..f8ff0612b9 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -26,8 +26,8 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.7
-RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
+ARG JELLYFIN_WEB_VERSION=v10.3.7
+RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
From 0e6417c9fa04ea58638a1594245365da6be6f8c1 Mon Sep 17 00:00:00 2001
From: Erik Larsson
Date: Sat, 3 Aug 2019 12:37:02 +0200
Subject: [PATCH 159/214] Set log level to debug for HTTP range requests
---
Emby.Server.Implementations/HttpServer/FileWriter.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs
index c6b7d31a83..ec41cc0a91 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);
}
///
From b637cdabaef9703063fed23719bcac5a1709373e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82?=
Date: Fri, 2 Aug 2019 15:32:42 +0000
Subject: [PATCH 160/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json
index 92b12409d1..e72f1a2621 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}"
}
From 20cbbd4f4c99d9a603756f83e0132caa71c16c13 Mon Sep 17 00:00:00 2001
From: AndersMachmueller
Date: Sun, 4 Aug 2019 19:18:50 +0000
Subject: [PATCH 161/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json
index cb8f4576ad..b01abafa13 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",
From 5e6e52d3977c679871f5633c59d82cbb3f6c2cce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tam=C3=A1s=20Mogyor=C3=B3si?=
Date: Thu, 25 Jul 2019 13:56:40 +0000
Subject: [PATCH 162/214] 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(-)
diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index c0f988abec..a2fc126a25 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",
From 89f592687ee7ae7f0e0fffd884dbf2890476410a Mon Sep 17 00:00:00 2001
From: Tradutor da Silva
Date: Tue, 23 Jul 2019 02:28:08 +0000
Subject: [PATCH 163/214] 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/
---
.../Localization/Core/pt-BR.json | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index 5d68416e9f..c4ce16dc85 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",
From ff93b162ee67b05c785a7f167277625700172471 Mon Sep 17 00:00:00 2001
From: Anthony Lavado
Date: Tue, 6 Aug 2019 12:59:45 -0400
Subject: [PATCH 164/214] Update the Stale-bot config
Updates the Stale bot to mark an issue after 90 days, and allows 14
days before closing the issue.
---
.github/stale.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/stale.yml b/.github/stale.yml
index 011f763177..ce9fb01a1a 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -1,7 +1,7 @@
# Number of days of inactivity before an issue becomes stale
-daysUntilStale: 60
+daysUntilStale: 90
# Number of days of inactivity before a stale issue is closed
-daysUntilClose: 7
+daysUntilClose: 14
# Issues with these labels will never be considered stale
exemptLabels:
- regression
@@ -15,7 +15,7 @@ exemptLabels:
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
- Issues go stale after 60d of inactivity. Mark the issue as fresh by adding a comment or commit. Stale issues close after an additional 7d of inactivity.
+ Issues go stale after 90d of inactivity. Mark the issue as fresh by adding a comment or commit. Stale issues close after an additional 14d of inactivity.
If this issue is safe to close now please do so.
If you have any questions you can reach us on [Matrix or Social Media](https://jellyfin.readthedocs.io/en/latest/getting-help/).
# Comment to post when closing a stale issue. Set to `false` to disable
From da0137629457093f6dbbe997963ed2d65fd1dba7 Mon Sep 17 00:00:00 2001
From: Andrew Rabert
Date: Tue, 6 Aug 2019 23:57:39 -0400
Subject: [PATCH 165/214] Fix Docker build
---
Dockerfile | 2 +-
Dockerfile.arm | 2 +-
Dockerfile.arm64 | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index 1644a8967d..f8e6fec318 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -24,7 +24,7 @@ COPY --from=builder /jellyfin /jellyfin
ARG JELLYFIN_WEB_VERSION=v10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
- && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
+ && mv jellyfin-web-* /jellyfin/jellyfin-web
EXPOSE 8096
VOLUME /cache /config /media
diff --git a/Dockerfile.arm b/Dockerfile.arm
index b0cf7a8a46..651bdeff49 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -29,7 +29,7 @@ COPY --from=builder /jellyfin /jellyfin
ARG JELLYFIN_WEB_VERSION=v10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
- && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
+ && mv jellyfin-web-* /jellyfin/jellyfin-web
EXPOSE 8096
VOLUME /cache /config /media
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index f8ff0612b9..f6bd81e4c2 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -29,7 +29,7 @@ COPY --from=builder /jellyfin /jellyfin
ARG JELLYFIN_WEB_VERSION=v10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
- && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
+ && mv jellyfin-web-* /jellyfin/jellyfin-web
EXPOSE 8096
VOLUME /cache /config /media
From cf0460c7f98938109cb63cd029dc35981d7b0496 Mon Sep 17 00:00:00 2001
From: dkanada
Date: Wed, 7 Aug 2019 02:24:56 -0700
Subject: [PATCH 166/214] move comment to separate line
---
MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
index 9fd72b65c1..3dcfba26aa 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
@@ -167,7 +167,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB
case "absolute":
episodeQuery.AbsoluteNumber = searchInfo.IndexNumber.Value;
break;
- default: //aired order
+ default:
+ //aired order
episodeQuery.AiredEpisode = searchInfo.IndexNumber.Value;
episodeQuery.AiredSeason = searchInfo.ParentIndexNumber.Value;
break;
From 6c58ac5c55180f12fe2428b315477659b71b59d4 Mon Sep 17 00:00:00 2001
From: "Mathias L. Baumann"
Date: Fri, 9 Aug 2019 10:00:16 +0200
Subject: [PATCH 167/214] Add link to feature request hub to readme
---
README.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 8106b6e4e7..800a72da1d 100644
--- a/README.md
+++ b/README.md
@@ -36,6 +36,10 @@ For more information about the project, please see our [about page](https://jell
Check out our documentation for guidelines.
-New idea or improvement? Something not working right?
+New idea or improvement?
+Check out our feature request hub.
+
+
+Something not working right?
Open an Issue.
From 9fff4b060e06569ca77636643901aa42767e318d Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Sun, 28 Jul 2019 23:53:19 +0200
Subject: [PATCH 168/214] Replace custom code with Asp.Net Core code
---
.../ApplicationHost.cs | 6 +-
.../HttpServer/FileWriter.cs | 218 +++---
.../HttpServer/HttpListenerHost.cs | 154 ++---
.../HttpServer/ResponseFilter.cs | 26 +-
.../HttpServer/Security/AuthService.cs | 48 +-
.../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 | 232 +++----
.../SocketSharp/WebSocketSharpResponse.cs | 98 ---
.../Playback/BaseStreamingService.cs | 2 +-
.../Net/AuthenticatedAttribute.cs | 3 +-
.../Services/IHasRequestFilter.cs | 4 +-
MediaBrowser.Model/Services/IRequest.cs | 30 +-
17 files changed, 393 insertions(+), 1224 deletions(-)
delete mode 100644 Emby.Server.Implementations/SocketSharp/RequestMono.cs
delete mode 100644 Emby.Server.Implementations/SocketSharp/WebSocketSharpResponse.cs
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index ef2f59d303..a89cf95d3b 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 ec41cc0a91..2890cca7ce 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 static readonly CultureInfo UsCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
+
+ private static readonly string[] _skipLogExtensions = {
+ ".js",
+ ".html",
+ ".css"
+ };
+
private readonly IStreamHelper _streamHelper;
- private ILogger Logger { get; set; }
+ private readonly ILogger _logger;
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; }
-
- 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; }
-
- public FileShareMode FileShare { get; set; }
-
///
/// The _options
///
private readonly IDictionary _options = new Dictionary();
- ///
- /// Gets the options.
- ///
- /// The options.
- public IDictionary Headers => _options;
- public string Path { get; set; }
+ ///
+ /// The _requested ranges
+ ///
+ 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,6 +73,88 @@ namespace Emby.Server.Implementations.HttpServer
Cookies = new List();
}
+ 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; }
+
+ public Action OnComplete { get; set; }
+
+ public Action OnError { get; set; }
+
+ public List Cookies { get; private set; }
+
+ public FileShareMode FileShare { get; set; }
+
+ ///
+ /// Gets the options.
+ ///
+ /// The options.
+ public IDictionary Headers => _options;
+
+ public string Path { get; set; }
+
+ ///
+ /// Gets the requested ranges.
+ ///
+ /// The requested ranges.
+ protected List> RequestedRanges
+ {
+ get
+ {
+ if (_requestedRanges == null)
+ {
+ _requestedRanges = new List>();
+
+ // Example: bytes=0-,32-63
+ var ranges = RangeHeader.Split('=')[1].Split(',');
+
+ foreach (var range in ranges)
+ {
+ var vals = range.Split('-');
+
+ long start = 0;
+ long? end = null;
+
+ if (!string.IsNullOrEmpty(vals[0]))
+ {
+ start = long.Parse(vals[0], UsCulture);
+ }
+
+ if (!string.IsNullOrEmpty(vals[1]))
+ {
+ end = long.Parse(vals[1], UsCulture);
+ }
+
+ _requestedRanges.Add(new KeyValuePair(start, end));
+ }
+ }
+
+ return _requestedRanges;
+ }
+ }
+
+ 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.
///
@@ -106,59 +181,10 @@ namespace Emby.Server.Implementations.HttpServer
var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
Headers[HeaderNames.ContentRange] = rangeString;
- Logger.LogDebug("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString);
+ _logger.LogInformation("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString);
}
- ///
- /// The _requested ranges
- ///
- private List> _requestedRanges;
- ///
- /// Gets the requested ranges.
- ///
- /// The requested ranges.
- protected List> RequestedRanges
- {
- get
- {
- if (_requestedRanges == null)
- {
- _requestedRanges = new List>();
-
- // Example: bytes=0-,32-63
- var ranges = RangeHeader.Split('=')[1].Split(',');
-
- foreach (var range in ranges)
- {
- var vals = range.Split('-');
-
- long start = 0;
- long? end = null;
-
- if (!string.IsNullOrEmpty(vals[0]))
- {
- start = long.Parse(vals[0], UsCulture);
- }
- if (!string.IsNullOrEmpty(vals[1]))
- {
- end = long.Parse(vals[1], UsCulture);
- }
-
- _requestedRanges.Add(new KeyValuePair(start, end));
- }
- }
-
- return _requestedRanges;
- }
- }
-
- private static readonly string[] SkipLogExtensions = {
- ".js",
- ".html",
- ".css"
- };
-
- public async Task WriteToAsync(IResponse response, CancellationToken cancellationToken)
+ 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 object Response { get; set; }
-
- public int Status { get; set; }
-
- public HttpStatusCode StatusCode
+ public async Task TransmitFile(Stream stream, string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
{
- get => (HttpStatusCode)Status;
- set => Status = (int)value;
+ var fileOpenOptions = FileOpenOptions.SequentialScan;
+
+ // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ fileOpenOptions |= FileOpenOptions.Asynchronous;
+ }
+
+ 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, 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 d8938964fa..4c233456c4 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 + "