aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs33
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs16
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs9
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs28
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj9
-rw-r--r--Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs18
-rw-r--r--Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs448
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs14
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs10
-rw-r--r--Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs34
-rw-r--r--Emby.Server.Implementations/Library/InvalidAuthProvider.cs47
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs52
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs4
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs38
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs10
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs6
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-MX.json10
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/ja.json96
-rw-r--r--Emby.Server.Implementations/Localization/Core/ko.json156
-rw-r--r--Emby.Server.Implementations/Localization/Core/lt-LT.json16
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-TW.json5
-rw-r--r--Emby.Server.Implementations/Security/AuthenticationRepository.cs4
-rw-r--r--Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs6
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs155
-rw-r--r--Emby.Server.Implementations/Session/SessionWebSocketListener.cs6
-rw-r--r--Emby.Server.Implementations/SocketSharp/RequestMono.cs16
-rw-r--r--Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs38
31 files changed, 574 insertions, 718 deletions
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index 190e4d55c..0530a251c 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<ActivityLogEntryPoint> 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()
@@ -122,7 +128,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;
}
@@ -153,7 +159,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;
}
@@ -201,6 +207,7 @@ namespace Emby.Server.Implementations.Activity
{
return NotificationType.AudioPlayback.ToString();
}
+
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
return NotificationType.VideoPlayback.ToString();
@@ -215,6 +222,7 @@ namespace Emby.Server.Implementations.Activity
{
return NotificationType.AudioPlaybackStopped.ToString();
}
+
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
return NotificationType.VideoPlaybackStopped.ToString();
@@ -403,6 +411,7 @@ namespace Emby.Server.Implementations.Activity
{
vals.Add(e.Result.ErrorMessage);
}
+
if (!string.IsNullOrEmpty(e.Result.LongErrorMessage))
{
vals.Add(e.Result.LongErrorMessage);
@@ -412,7 +421,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
});
@@ -489,6 +498,7 @@ namespace Emby.Server.Implementations.Activity
{
values.Add(CreateValueString(span.Hours, "hour"));
}
+
// Number of minutes
if (span.Minutes >= 1)
{
@@ -512,6 +522,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 53bc85b28..fc4e88878 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;
@@ -826,10 +827,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<IDlnaManager>(
@@ -1014,7 +1015,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;
@@ -1539,12 +1539,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)
@@ -1697,15 +1697,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/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 7e50650d7..e9961e8bd 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -6,7 +6,6 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
@@ -20,7 +19,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization;
@@ -40,11 +38,8 @@ namespace Emby.Server.Implementations.Channels
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer;
- private readonly IHttpClient _httpClient;
private readonly IProviderManager _providerManager;
- private readonly ILocalizationManager _localization;
-
public ChannelManager(
IUserManager userManager,
IDtoService dtoService,
@@ -54,8 +49,6 @@ namespace Emby.Server.Implementations.Channels
IFileSystem fileSystem,
IUserDataManager userDataManager,
IJsonSerializer jsonSerializer,
- ILocalizationManager localization,
- IHttpClient httpClient,
IProviderManager providerManager)
{
_userManager = userManager;
@@ -66,8 +59,6 @@ namespace Emby.Server.Implementations.Channels
_fileSystem = fileSystem;
_userDataManager = userDataManager;
_jsonSerializer = jsonSerializer;
- _localization = localization;
- _httpClient = httpClient;
_providerManager = providerManager;
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 7b28a22a8..2f1b60be9 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -89,14 +89,11 @@ namespace Emby.Server.Implementations.Dto
var channelTuples = new List<Tuple<BaseItemDto, LiveTvChannel>>();
var index = 0;
- var allCollectionFolders = _libraryManager.GetUserRootFolder().Children.OfType<Folder>().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<BaseItemDto, LiveTvChannel>(dto, tvChannel));
}
@@ -105,9 +102,7 @@ namespace Emby.Server.Implementations.Dto
programTuples.Add(new Tuple<BaseItem, BaseItemDto>(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<Folder>().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<Folder> 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<BaseItem> taggedItems, User user = null)
{
- var allCollectionFolders = _libraryManager.GetUserRootFolder().Children.OfType<Folder>().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/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 2c7962452..49015a07e 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -31,10 +31,9 @@
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
- <PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
- <PackageReference Include="sharpcompress" Version="0.22.0" />
+ <PackageReference Include="ServiceStack.Text.Core" Version="5.5.0" />
+ <PackageReference Include="sharpcompress" Version="0.23.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="1.0.0" />
- <PackageReference Include="UTF.Unknown" Version="1.0.0-beta1" />
</ItemGroup>
<ItemGroup>
@@ -52,8 +51,8 @@
<!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.6.3" />
- <PackageReference Include="StyleCop.Analyzers" Version="1.0.2" />
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.3" />
+ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
</ItemGroup>
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs
deleted file mode 100644
index f747b01b9..000000000
--- a/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-using System.Net.Http;
-
-namespace Emby.Server.Implementations.HttpClientManager
-{
- /// <summary>
- /// Class HttpClientInfo
- /// </summary>
- public class HttpClientInfo
- {
- /// <summary>
- /// Gets or sets the last timeout.
- /// </summary>
- /// <value>The last timeout.</value>
- public DateTime LastTimeout { get; set; }
- public HttpClient HttpClient { get; set; }
- }
-}
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index 1bebdd163..b82d55d0e 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.
/// </summary>
/// <value>The HTTP clients.</value>
- private readonly ConcurrentDictionary<string, HttpClientInfo> _httpClients = new ConcurrentDictionary<string, HttpClientInfo>();
+ private readonly ConcurrentDictionary<string, HttpClient> _httpClients = new ConcurrentDictionary<string, HttpClient>();
/// <summary>
/// Gets
/// </summary>
- /// <param name="host">The host.</param>
+ /// <param name="url">The host.</param>
/// <param name="enableHttpCompression">if set to <c>true</c> [enable HTTP compression].</param>
/// <returns>HttpClient.</returns>
/// <exception cref="ArgumentNullException">host</exception>
- private HttpClientInfo GetHttpClient(string host, bool enableHttpCompression)
+ private HttpClient GetHttpClient(string url, bool enableHttpCompression)
{
- if (string.IsNullOrEmpty(host))
- {
- throw new ArgumentNullException(nameof(host));
- }
-
- var key = host + enableHttpCompression;
+ var key = GetHostFromUrl(url) + enableHttpCompression;
if (!_httpClients.TryGetValue(key, out var client))
{
- client = new HttpClientInfo();
+
+ client = new HttpClient()
+ {
+ BaseAddress = new Uri(url)
+ };
_httpClients.TryAdd(key, client);
}
@@ -102,110 +101,87 @@ namespace Emby.Server.Implementations.HttpClientManager
return client;
}
- private WebRequest GetRequest(HttpRequestOptions options, string method)
+ private HttpRequestMessage GetRequestMessage(HttpRequestOptions options, HttpMethod method)
{
string url = options.Url;
-
var uriAddress = new Uri(url);
string userInfo = uriAddress.UserInfo;
if (!string.IsNullOrWhiteSpace(userInfo))
{
- _logger.LogInformation("Found userInfo in url: {0} ... url: {1}", userInfo, url);
+ _logger.LogWarning("Found userInfo in url: {0} ... url: {1}", userInfo, url);
url = url.Replace(userInfo + "@", string.Empty);
}
- var request = WebRequest.Create(url);
+ var request = new HttpRequestMessage(method, url);
- if (request is HttpWebRequest httpWebRequest)
- {
- AddRequestHeaders(httpWebRequest, options);
+ AddRequestHeaders(request, options);
- if (options.EnableHttpCompression)
+ if (options.EnableHttpCompression)
+ {
+ if (options.DecompressionMethod.HasValue
+ && options.DecompressionMethod.Value == CompressionMethod.Gzip)
{
- httpWebRequest.AutomaticDecompression = DecompressionMethods.Deflate;
- if (options.DecompressionMethod.HasValue
- && options.DecompressionMethod.Value == CompressionMethod.Gzip)
- {
- httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip;
- }
+ request.Headers.Add(HeaderNames.AcceptEncoding, new[] { "gzip", "deflate" });
}
else
{
- httpWebRequest.AutomaticDecompression = DecompressionMethods.None;
+ request.Headers.Add(HeaderNames.AcceptEncoding, "deflate");
}
+ }
- httpWebRequest.KeepAlive = options.EnableKeepAlive;
+ if (options.EnableKeepAlive)
+ {
+ request.Headers.Add(HeaderNames.Connection, "Keep-Alive");
+ }
- if (!string.IsNullOrEmpty(options.Host))
- {
- httpWebRequest.Host = options.Host;
- }
+ if (!string.IsNullOrEmpty(options.Host))
+ {
+ request.Headers.Add(HeaderNames.Host, options.Host);
+ }
- if (!string.IsNullOrEmpty(options.Referer))
- {
- httpWebRequest.Referer = options.Referer;
- }
+ if (!string.IsNullOrEmpty(options.Referer))
+ {
+ request.Headers.Add(HeaderNames.Referer, options.Referer);
}
- request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
+ //request.Headers.Add(HeaderNames.CacheControl, "no-cache");
- request.Method = method;
- request.Timeout = options.TimeoutMs;
+ //request.Headers.Add(HeaderNames., options.TimeoutMs;
+ /*
if (!string.IsNullOrWhiteSpace(userInfo))
{
var parts = userInfo.Split(':');
if (parts.Length == 2)
{
- request.Credentials = GetCredential(url, parts[0], parts[1]);
- // TODO: .net core ??
- request.PreAuthenticate = true;
+ request.Headers.Add(HeaderNames., GetCredential(url, parts[0], parts[1]);
}
}
+ */
return request;
}
- private static CredentialCache GetCredential(string url, string username, string password)
- {
- //ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
- var credentialCache = new CredentialCache();
- credentialCache.Add(new Uri(url), "Basic", new NetworkCredential(username, password));
- return credentialCache;
- }
-
- private void AddRequestHeaders(HttpWebRequest request, HttpRequestOptions options)
+ private void AddRequestHeaders(HttpRequestMessage request, HttpRequestOptions options)
{
var hasUserAgent = false;
foreach (var header in options.RequestHeaders)
{
- if (string.Equals(header.Key, HeaderNames.Accept, StringComparison.OrdinalIgnoreCase))
+ 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;
- }
-
/// <summary>
/// Gets the response internal.
/// </summary>
@@ -213,7 +189,7 @@ namespace Emby.Server.Implementations.HttpClientManager
/// <returns>Task{HttpResponseInfo}.</returns>
public Task<HttpResponseInfo> GetResponse(HttpRequestOptions options)
{
- return SendAsync(options, "GET");
+ return SendAsync(options, HttpMethod.Get);
}
/// <summary>
@@ -235,7 +211,21 @@ namespace Emby.Server.Implementations.HttpClientManager
/// <returns>Task{HttpResponseInfo}.</returns>
/// <exception cref="HttpException">
/// </exception>
- public async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod)
+ public Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod)
+ {
+ var httpMethod2 = GetHttpMethod(httpMethod);
+ return SendAsync(options, httpMethod2);
+ }
+
+ /// <summary>
+ /// send as an asynchronous operation.
+ /// </summary>
+ /// <param name="options">The options.</param>
+ /// <param name="httpMethod">The HTTP method.</param>
+ /// <returns>Task{HttpResponseInfo}.</returns>
+ /// <exception cref="HttpException">
+ /// </exception>
+ public async Task<HttpResponseInfo> 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<HttpResponseInfo> SendAsyncInternal(HttpRequestOptions options, string httpMethod)
+ private async Task<HttpResponseInfo> 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)
{
@@ -336,143 +352,96 @@ namespace Emby.Server.Implementations.HttpClientManager
}
}
- if (options.ResourcePool != null)
- {
- 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);
- }
- finally
- {
- options.ResourcePool?.Release();
+ throw GetCancellationException(options, options.CancellationToken, ex);
}
}
- 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<HttpResponseInfo> Post(HttpRequestOptions options)
{
- return SendAsync(options, "POST");
- }
-
- /// <summary>
- /// Performs a POST request
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="postData">Params to add to the POST data.</param>
- /// <returns>stream on success, null on failure</returns>
- public async Task<Stream> Post(HttpRequestOptions options, Dictionary<string, string> postData)
- {
- options.SetPostData(postData);
-
- var response = await Post(options).ConfigureAwait(false);
-
- return response.Content;
+ return SendAsync(options, HttpMethod.Post);
}
/// <summary>
@@ -482,9 +451,10 @@ namespace Emby.Server.Implementations.HttpClientManager
/// <returns>Task{System.String}.</returns>
public async Task<string> 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<HttpResponseInfo> GetTempFileResponse(HttpRequestOptions options)
@@ -502,44 +472,28 @@ namespace Emby.Server.Implementations.HttpClientManager
options.CancellationToken.ThrowIfCancellationRequested();
- var httpWebRequest = GetRequest(options, "GET");
-
- if (options.ResourcePool != null)
- {
- await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false);
- }
+ var httpWebRequest = GetRequestMessage(options, HttpMethod.Get);
options.Progress.Report(0);
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,35 +501,22 @@ 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);
- }
- finally
- {
- options.ResourcePool?.Release();
- }
- }
-
- private static long? GetContentLength(HttpWebResponse response)
- {
- var length = response.ContentLength;
+ if (File.Exists(tempFile))
+ {
+ File.Delete(tempFile);
+ }
- if (length == 0)
- {
- return null;
+ throw GetException(ex, options);
}
-
- 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 +540,6 @@ namespace Emby.Server.Implementations.HttpClientManager
if (response != null)
{
exception.StatusCode = response.StatusCode;
-
- if ((int)response.StatusCode == 429)
- {
- client.LastTimeout = DateTime.UtcNow;
- }
}
}
@@ -624,7 +560,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 +571,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 +606,10 @@ namespace Emby.Server.Implementations.HttpClientManager
/// Throws the cancellation exception.
/// </summary>
/// <param name="options">The options.</param>
- /// <param name="client">The client.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="exception">The exception.</param>
/// <returns>Exception.</returns>
- 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 +621,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 +631,20 @@ namespace Emby.Server.Implementations.HttpClientManager
return exception;
}
- private void EnsureSuccessStatusCode(HttpClientInfo client, HttpWebResponse response, HttpRequestOptions options)
+ private async Task EnsureSuccessStatusCode(HttpResponseMessage response, HttpRequestOptions options)
{
- var statusCode = response.StatusCode;
-
- var isSuccessful = statusCode >= HttpStatusCode.OK && statusCode <= (HttpStatusCode)299;
-
- if (isSuccessful)
+ if (response.IsSuccessStatusCode)
{
return;
}
- if (options.LogErrorResponseBody)
- {
- try
- {
- using (var stream = response.GetResponseStream())
- {
- if (stream != null)
- {
- using (var reader = new StreamReader(stream))
- {
- var msg = reader.ReadToEnd();
-
- _logger.LogError(msg);
- }
- }
- }
- }
- catch
- {
-
- }
- }
+ var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ _logger.LogError(msg);
- throw new HttpException(response.StatusDescription)
+ throw new HttpException(response.ReasonPhrase)
{
StatusCode = response.StatusCode
};
}
-
- private static Task<WebResponse> GetResponseAsync(WebRequest request, TimeSpan timeout)
- {
- var taskCompletion = new TaskCompletionSource<WebResponse>();
-
- 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<WebResponse> taskCompletion;
-
- public void OnSuccess(Task<WebResponse> task)
- {
- taskCompletion.TrySetResult(task.Result);
- }
-
- public void OnError(Task<WebResponse> task)
- {
- if (task.Exception == null)
- {
- taskCompletion.TrySetException(Enumerable.Empty<Exception>());
- }
- else
- {
- taskCompletion.TrySetException(task.Exception);
- }
- }
- }
}
}
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index b3d2b9cc2..d8938964f 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -805,19 +805,15 @@ namespace Emby.Server.Implementations.HttpServer
Logger.LogDebug("Websocket message received: {0}", result.MessageType);
- var tasks = _webSocketListeners.Select(i => Task.Run(async () =>
+ IEnumerable<Task> GetTasks()
{
- try
- {
- await i.ProcessMessage(result).ConfigureAwait(false);
- }
- catch (Exception ex)
+ foreach (var x in _webSocketListeners)
{
- Logger.LogError(ex, "{0} failed processing WebSocket message {1}", i.GetType().Name, result.MessageType ?? string.Empty);
+ yield return x.ProcessMessageAsync(result);
}
- }));
+ }
- return Task.WhenAll(tasks);
+ return Task.WhenAll(GetTasks());
}
public void Dispose()
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 0dea5041a..7c2ea50e2 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -647,7 +647,6 @@ namespace Emby.Server.Implementations.IO
public virtual bool IsPathFile(string path)
{
// Cannot use Path.IsPathRooted because it returns false under mono when using windows-based paths, e.g. C:\\
-
if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) != -1 &&
!path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
{
@@ -655,8 +654,6 @@ namespace Emby.Server.Implementations.IO
}
return true;
-
- //return Path.IsPathRooted(path);
}
public virtual void DeleteFile(string path)
@@ -667,13 +664,14 @@ namespace Emby.Server.Implementations.IO
public virtual List<FileSystemMetadata> 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();
}
diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
index 3d15a8afb..fe09b07ff 100644
--- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
+++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
@@ -19,7 +19,7 @@ namespace Emby.Server.Implementations.Library
public string Name => "Default";
public bool IsEnabled => true;
-
+
// This is dumb and an artifact of the backwards way auth providers were designed.
// This version of authenticate was never meant to be called, but needs to be here for interface compat
// Only the providers that don't provide local user support use this
@@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Library
{
throw new NotImplementedException();
}
-
+
// This is the verson that we need to use for local users. Because reasons.
public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser)
{
@@ -103,7 +103,7 @@ namespace Emby.Server.Implementations.Library
string hash = user.Password;
user.Password = string.Format("$SHA1${0}", hash);
}
-
+
if (user.EasyPassword != null && !user.EasyPassword.Contains("$"))
{
string hash = user.EasyPassword;
@@ -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/InvalidAuthProvider.cs b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs
new file mode 100644
index 000000000..25d233137
--- /dev/null
+++ b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Net;
+
+namespace Emby.Server.Implementations.Library
+{
+ public class InvalidAuthProvider : IAuthenticationProvider
+ {
+ public string Name => "InvalidOrMissingAuthenticationProvider";
+
+ public bool IsEnabled => true;
+
+ public Task<ProviderAuthenticationResult> Authenticate(string username, string password)
+ {
+ throw new SecurityException("User Account cannot login with this provider. The Normal provider for this user cannot be found");
+ }
+
+ public Task<bool> HasPassword(User user)
+ {
+ return Task.FromResult(true);
+ }
+
+ public Task ChangePassword(User user, string newPassword)
+ {
+ return Task.CompletedTask;
+ }
+
+ public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
+ {
+ // Nothing here
+ }
+
+ public string GetPasswordHash(User user)
+ {
+ return string.Empty;
+ }
+
+ public string GetEasyPasswordHash(User user)
+ {
+ return string.Empty;
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 1673e3777..4b5063ada 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -2368,7 +2368,7 @@ namespace Emby.Server.Implementations.Library
public int? GetSeasonNumberFromPath(string path)
{
- return new SeasonPathParser(GetNamingOptions()).Parse(path, true, true).SeasonNumber;
+ return new SeasonPathParser().Parse(path, true, true).SeasonNumber;
}
public bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh)
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index db270c398..8171c010b 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -14,6 +14,18 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
private readonly IImageProcessor _imageProcessor;
private readonly ILibraryManager _libraryManager;
+ private static readonly HashSet<string> _ignoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ {
+ "folder",
+ "thumb",
+ "landscape",
+ "fanart",
+ "backdrop",
+ "poster",
+ "cover",
+ "logo",
+ "default"
+ };
public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager)
{
@@ -31,10 +43,10 @@ namespace Emby.Server.Implementations.Library.Resolvers
if (!args.IsDirectory)
{
// Must be an image file within a photo collection
- var collectionType = args.GetCollectionType();
+ var collectionType = args.CollectionType;
- if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase) ||
- (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.GetLibraryOptions().EnablePhotos))
+ if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase)
+ || (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.GetLibraryOptions().EnablePhotos))
{
if (IsImageFile(args.Path, _imageProcessor))
{
@@ -74,43 +86,29 @@ namespace Emby.Server.Implementations.Library.Resolvers
}
internal static bool IsOwnedByResolvedMedia(ILibraryManager libraryManager, LibraryOptions libraryOptions, string file, string imageFilename)
+ => imageFilename.StartsWith(Path.GetFileNameWithoutExtension(file), StringComparison.OrdinalIgnoreCase);
+
+ internal static bool IsImageFile(string path, IImageProcessor imageProcessor)
{
- if (imageFilename.StartsWith(Path.GetFileNameWithoutExtension(file), StringComparison.OrdinalIgnoreCase))
+ if (path == null)
{
- return true;
+ throw new ArgumentNullException(nameof(path));
}
- return false;
- }
-
- private static readonly HashSet<string> IgnoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
- {
- "folder",
- "thumb",
- "landscape",
- "fanart",
- "backdrop",
- "poster",
- "cover",
- "logo",
- "default"
- };
-
- internal static bool IsImageFile(string path, IImageProcessor imageProcessor)
- {
- var filename = Path.GetFileNameWithoutExtension(path) ?? string.Empty;
+ var filename = Path.GetFileNameWithoutExtension(path);
- if (IgnoreFiles.Contains(filename))
+ if (_ignoreFiles.Contains(filename))
{
return false;
}
- if (IgnoreFiles.Any(i => filename.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1))
+ if (_ignoreFiles.Any(i => filename.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1))
{
return false;
}
- return imageProcessor.SupportedInputFormats.Contains(Path.GetExtension(path).TrimStart('.'), StringComparer.Ordinal);
+ string extension = Path.GetExtension(path).TrimStart('.');
+ return imageProcessor.SupportedInputFormats.Contains(extension, StringComparer.OrdinalIgnoreCase);
}
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
index ce1386e91..3b9e48d97 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
@@ -52,7 +52,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
var path = args.Path;
- var seasonParserResult = new SeasonPathParser(namingOptions).Parse(path, true, true);
+ var seasonParserResult = new SeasonPathParser().Parse(path, true, true);
var season = new Season
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index 5c95534ec..1f873d7c6 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -194,9 +194,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// <returns><c>true</c> if [is season folder] [the specified path]; otherwise, <c>false</c>.</returns>
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/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index c33bb7740..ff375e590 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -79,6 +79,8 @@ namespace Emby.Server.Implementations.Library
private IAuthenticationProvider[] _authenticationProviders;
private DefaultAuthenticationProvider _defaultAuthenticationProvider;
+ private InvalidAuthProvider _invalidAuthProvider;
+
private IPasswordResetProvider[] _passwordResetProviders;
private DefaultPasswordResetProvider _defaultPasswordResetProvider;
@@ -141,6 +143,8 @@ namespace Emby.Server.Implementations.Library
_defaultAuthenticationProvider = _authenticationProviders.OfType<DefaultAuthenticationProvider>().First();
+ _invalidAuthProvider = _authenticationProviders.OfType<InvalidAuthProvider>().First();
+
_passwordResetProviders = passwordResetProviders.ToArray();
_defaultPasswordResetProvider = passwordResetProviders.OfType<DefaultPasswordResetProvider>().First();
@@ -307,8 +311,7 @@ namespace Emby.Server.Implementations.Library
user = Users
.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
- var hasNewUserPolicy = authenticationProvider as IHasNewUserPolicy;
- if (hasNewUserPolicy != null)
+ if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy)
{
var policy = hasNewUserPolicy.GetNewUserPolicy();
UpdateUserPolicy(user, policy, true);
@@ -400,7 +403,9 @@ namespace Emby.Server.Implementations.Library
if (providers.Length == 0)
{
- providers = new IAuthenticationProvider[] { _defaultAuthenticationProvider };
+ // Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found
+ _logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user.Name, user.Policy.AuthenticationProviderId);
+ providers = new IAuthenticationProvider[] { _invalidAuthProvider };
}
return providers;
@@ -471,7 +476,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 +502,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 +551,6 @@ namespace Emby.Server.Implementations.Library
}
}
- private string GetLocalPasswordHash(User user)
- {
- return string.IsNullOrEmpty(user.EasyPassword)
- ? null
- : user.EasyPassword;
- }
-
/// <summary>
/// Loads the users from the repository
/// </summary>
@@ -596,7 +594,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 +882,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/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 4137760d0..f3f747718 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -96,8 +96,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
Url = ApiUrl + "/schedules",
UserAgent = UserAgent,
CancellationToken = cancellationToken,
- // The data can be large so give it some extra time
- TimeoutMs = 60000,
LogErrorResponseBody = true,
RequestContent = requestString
};
@@ -115,9 +113,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
Url = ApiUrl + "/programs",
UserAgent = UserAgent,
CancellationToken = cancellationToken,
- LogErrorResponseBody = true,
- // The data can be large so give it some extra time
- TimeoutMs = 60000
+ LogErrorResponseBody = true
};
httpOptions.RequestHeaders["token"] = token;
@@ -483,8 +479,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
CancellationToken = cancellationToken,
RequestContent = imageIdString,
LogErrorResponseBody = true,
- // The data can be large so give it some extra time
- TimeoutMs = 60000
};
try
@@ -871,8 +865,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
UserAgent = UserAgent,
CancellationToken = cancellationToken,
LogErrorResponseBody = true,
- // The data can be large so give it some extra time
- TimeoutMs = 60000
};
httpOptions.RequestHeaders["token"] = token;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 24b100edd..761275f8f 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -138,7 +138,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
Url = string.Format("{0}/discover.json", GetApiUrl(info)),
CancellationToken = cancellationToken,
- TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(10).TotalMilliseconds),
BufferContent = false
}, "GET").ConfigureAwait(false))
@@ -191,7 +190,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
Url = string.Format("{0}/tuners.html", GetApiUrl(info)),
CancellationToken = cancellationToken,
- TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds),
BufferContent = false
}))
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index d74cf3be2..e8b34da0c 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -47,13 +47,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
CancellationToken = CancellationToken.None,
BufferContent = false,
- // Increase a little bit
- TimeoutMs = 30000,
-
EnableHttpCompression = false,
-
- LogResponse = true,
- LogResponseHeaders = true
};
foreach (var header in mediaSource.RequiredHttpHeaders)
diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json
index 2285f2808..003632968 100644
--- a/Emby.Server.Implementations/Localization/Core/es-MX.json
+++ b/Emby.Server.Implementations/Localization/Core/es-MX.json
@@ -18,11 +18,11 @@
"HeaderAlbumArtists": "Artistas del Álbum",
"HeaderCameraUploads": "Subidos desde Camara",
"HeaderContinueWatching": "Continuar Viendo",
- "HeaderFavoriteAlbums": "Álbumes Favoritos",
- "HeaderFavoriteArtists": "Artistas Favoritos",
- "HeaderFavoriteEpisodes": "Episodios Preferidos",
- "HeaderFavoriteShows": "Programas Preferidos",
- "HeaderFavoriteSongs": "Canciones Favoritas",
+ "HeaderFavoriteAlbums": "Álbumes favoritos",
+ "HeaderFavoriteArtists": "Artistas favoritos",
+ "HeaderFavoriteEpisodes": "Episodios favoritos",
+ "HeaderFavoriteShows": "Programas favoritos",
+ "HeaderFavoriteSongs": "Canciones favoritas",
"HeaderLiveTV": "TV en Vivo",
"HeaderNextUp": "A Continuación",
"HeaderRecordingGroups": "Grupos de Grabaciones",
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index 1850b8f25..f03184d5b 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -21,7 +21,7 @@
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteEpisodes": "Episodios favoritos",
- "HeaderFavoriteShows": "Programas favoritos",
+ "HeaderFavoriteShows": "Series favoritas",
"HeaderFavoriteSongs": "Canciones favoritas",
"HeaderLiveTV": "TV en directo",
"HeaderNextUp": "Siguiendo",
diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json
new file mode 100644
index 000000000..53a43a125
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/ja.json
@@ -0,0 +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}"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json
index 21808fd18..5a7ba8ba7 100644
--- a/Emby.Server.Implementations/Localization/Core/ko.json
+++ b/Emby.Server.Implementations/Localization/Core/ko.json
@@ -1,88 +1,88 @@
{
- "Albums": "Albums",
- "AppDeviceValues": "App: {0}, Device: {1}",
- "Application": "Application",
- "Artists": "Artists",
- "AuthenticationSucceededWithUserName": "{0} successfully authenticated",
- "Books": "Books",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
- "Channels": "Channels",
- "ChapterNameValue": "Chapter {0}",
- "Collections": "Collections",
- "DeviceOfflineWithName": "{0} has disconnected",
- "DeviceOnlineWithName": "{0} is connected",
- "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
- "Favorites": "Favorites",
- "Folders": "Folders",
- "Genres": "Genres",
+ "Albums": "앨범",
+ "AppDeviceValues": "앱: {0}, 디바이스: {1}",
+ "Application": "애플리케이션",
+ "Artists": "아티스트",
+ "AuthenticationSucceededWithUserName": "{0} 인증에 성공했습니다.",
+ "Books": "책",
+ "CameraImageUploadedFrom": "새로운 카메라 이미지가 {0}에서 업로드되었습니다.",
+ "Channels": "채널",
+ "ChapterNameValue": "챕터 {0}",
+ "Collections": "컬렉션",
+ "DeviceOfflineWithName": "{0}가 접속이 끊어졌습니다.",
+ "DeviceOnlineWithName": "{0}가 접속되었습니다.",
+ "FailedLoginAttemptWithUserName": "{0}에서 로그인이 실패했습니다.",
+ "Favorites": "즐겨찾기",
+ "Folders": "폴더",
+ "Genres": "장르",
"HeaderAlbumArtists": "앨범 아티스트",
- "HeaderCameraUploads": "Camera Uploads",
+ "HeaderCameraUploads": "카메라 업로드",
"HeaderContinueWatching": "계속 시청하기",
- "HeaderFavoriteAlbums": "Favorite Albums",
- "HeaderFavoriteArtists": "Favorite Artists",
+ "HeaderFavoriteAlbums": "좋아하는 앨범",
+ "HeaderFavoriteArtists": "좋아하는 아티스트",
"HeaderFavoriteEpisodes": "Favorite Episodes",
"HeaderFavoriteShows": "즐겨찾는 쇼",
- "HeaderFavoriteSongs": "Favorite Songs",
- "HeaderLiveTV": "Live TV",
- "HeaderNextUp": "Next Up",
- "HeaderRecordingGroups": "Recording Groups",
- "HomeVideos": "Home videos",
- "Inherit": "Inherit",
- "ItemAddedWithName": "{0} was added to the library",
- "ItemRemovedWithName": "{0} was removed from the library",
- "LabelIpAddressValue": "Ip address: {0}",
- "LabelRunningTimeValue": "Running time: {0}",
- "Latest": "Latest",
- "MessageApplicationUpdated": "Jellyfin Server has been updated",
- "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
- "MessageServerConfigurationUpdated": "Server configuration has been updated",
- "MixedContent": "Mixed content",
- "Movies": "Movies",
- "Music": "Music",
- "MusicVideos": "Music videos",
- "NameInstallFailed": "{0} installation failed",
- "NameSeasonNumber": "Season {0}",
- "NameSeasonUnknown": "Season Unknown",
- "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
- "NotificationOptionApplicationUpdateAvailable": "Application update available",
- "NotificationOptionApplicationUpdateInstalled": "Application update installed",
- "NotificationOptionAudioPlayback": "Audio playback started",
- "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
- "NotificationOptionCameraImageUploaded": "Camera image uploaded",
- "NotificationOptionInstallationFailed": "Installation failure",
- "NotificationOptionNewLibraryContent": "New content added",
- "NotificationOptionPluginError": "Plugin failure",
- "NotificationOptionPluginInstalled": "Plugin installed",
- "NotificationOptionPluginUninstalled": "Plugin uninstalled",
- "NotificationOptionPluginUpdateInstalled": "Plugin update installed",
- "NotificationOptionServerRestartRequired": "Server restart required",
- "NotificationOptionTaskFailed": "Scheduled task failure",
- "NotificationOptionUserLockedOut": "User locked out",
- "NotificationOptionVideoPlayback": "Video playback started",
- "NotificationOptionVideoPlaybackStopped": "Video playback stopped",
- "Photos": "Photos",
- "Playlists": "Playlists",
- "Plugin": "Plugin",
- "PluginInstalledWithName": "{0} was installed",
- "PluginUninstalledWithName": "{0} was uninstalled",
- "PluginUpdatedWithName": "{0} was updated",
- "ProviderValue": "Provider: {0}",
- "ScheduledTaskFailedWithName": "{0} failed",
- "ScheduledTaskStartedWithName": "{0} started",
- "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
- "Shows": "Shows",
- "Songs": "Songs",
+ "HeaderFavoriteSongs": "좋아하는 노래",
+ "HeaderLiveTV": "TV 방송",
+ "HeaderNextUp": "다음으로",
+ "HeaderRecordingGroups": "녹화 그룹",
+ "HomeVideos": "홈 비디오",
+ "Inherit": "상속",
+ "ItemAddedWithName": "{0} 라이브러리에 추가됨",
+ "ItemRemovedWithName": "{0} 라이브러리에서 제거됨",
+ "LabelIpAddressValue": "IP 주소: {0}",
+ "LabelRunningTimeValue": "상영 시간: {0}",
+ "Latest": "최근",
+ "MessageApplicationUpdated": "Jellyfin 서버 업데이트됨",
+ "MessageApplicationUpdatedTo": "Jellyfin 서버가 {0}로 업데이트됨",
+ "MessageNamedServerConfigurationUpdatedWithValue": "서버 환경 설정 {0} 섹션 업데이트 됨",
+ "MessageServerConfigurationUpdated": "서버 환경 설정 업데이드됨",
+ "MixedContent": "혼합 콘텐츠",
+ "Movies": "영화",
+ "Music": "음악",
+ "MusicVideos": "뮤직 비디오",
+ "NameInstallFailed": "{0} 설치 실패.",
+ "NameSeasonNumber": "시즌 {0}",
+ "NameSeasonUnknown": "알 수 없는 시즌",
+ "NewVersionIsAvailable": "새 버전의 Jellyfin 서버를 사용할 수 있습니다.",
+ "NotificationOptionApplicationUpdateAvailable": "애플리케이션 업데이트 사용 가능",
+ "NotificationOptionApplicationUpdateInstalled": "애플리케이션 업데이트가 설치됨",
+ "NotificationOptionAudioPlayback": "오디오 재생을 시작함",
+ "NotificationOptionAudioPlaybackStopped": "오디오 재생이 중지됨",
+ "NotificationOptionCameraImageUploaded": "카메라 이미지가 업로드됨",
+ "NotificationOptionInstallationFailed": "설치 실패",
+ "NotificationOptionNewLibraryContent": "새 콘텐트가 추가됨",
+ "NotificationOptionPluginError": "플러그인 실패",
+ "NotificationOptionPluginInstalled": "플러그인이 설치됨",
+ "NotificationOptionPluginUninstalled": "플러그인이 설치 제거됨",
+ "NotificationOptionPluginUpdateInstalled": "플러그인 업데이트가 설치됨",
+ "NotificationOptionServerRestartRequired": "서버를 다시 시작하십시오",
+ "NotificationOptionTaskFailed": "예약 작업 실패",
+ "NotificationOptionUserLockedOut": "사용자가 잠겼습니다",
+ "NotificationOptionVideoPlayback": "비디오 재생을 시작함",
+ "NotificationOptionVideoPlaybackStopped": "비디오 재생이 중지됨",
+ "Photos": "사진",
+ "Playlists": "재생목록",
+ "Plugin": "플러그인",
+ "PluginInstalledWithName": "{0} 설치됨",
+ "PluginUninstalledWithName": "{0} 설치 제거됨",
+ "PluginUpdatedWithName": "{0} 업데이트됨",
+ "ProviderValue": "제공자: {0}",
+ "ScheduledTaskFailedWithName": "{0} 실패",
+ "ScheduledTaskStartedWithName": "{0} 시작",
+ "ServerNameNeedsToBeRestarted": "{0} 를 재시작하십시오",
+ "Shows": "프로그램",
+ "Songs": "노래",
"StartupEmbyServerIsLoading": "Jellyfin 서버를 불러오고 있습니다. 잠시후 다시시도 해주세요.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
- "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
- "Sync": "Sync",
- "System": "System",
- "TvShows": "TV Shows",
- "User": "User",
- "UserCreatedWithName": "User {0} has been created",
- "UserDeletedWithName": "User {0} has been deleted",
+ "SubtitleDownloadFailureFromForItem": "{0}에서 {1} 자막 다운로드에 실패했습니다",
+ "SubtitlesDownloadedForItem": "{0} 자막을 다운로드했습니다",
+ "Sync": "동기화",
+ "System": "시스템",
+ "TvShows": "TV 쇼",
+ "User": "사용자",
+ "UserCreatedWithName": "사용자 {0} 생성됨",
+ "UserDeletedWithName": "사용자 {0} 삭제됨",
"UserDownloadingItemWithValues": "{0} is downloading {1}",
"UserLockedOutWithName": "User {0} has been locked out",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json
index 558904f06..e2f3ba3dc 100644
--- a/Emby.Server.Implementations/Localization/Core/lt-LT.json
+++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json
@@ -1,21 +1,21 @@
{
- "Albums": "Albums",
+ "Albums": "Albumai",
"AppDeviceValues": "App: {0}, Device: {1}",
"Application": "Application",
- "Artists": "Artists",
+ "Artists": "Atlikėjai",
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
- "Books": "Books",
+ "Books": "Knygos",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
- "Channels": "Channels",
+ "Channels": "Kanalai",
"ChapterNameValue": "Chapter {0}",
- "Collections": "Collections",
+ "Collections": "Kolekcijos",
"DeviceOfflineWithName": "{0} has disconnected",
"DeviceOnlineWithName": "{0} is connected",
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
- "Favorites": "Favorites",
- "Folders": "Folders",
+ "Favorites": "Mėgstami",
+ "Folders": "Katalogai",
"Genres": "Žanrai",
- "HeaderAlbumArtists": "Album Artists",
+ "HeaderAlbumArtists": "Albumo atlikėjai",
"HeaderCameraUploads": "Camera Uploads",
"HeaderContinueWatching": "Žiūrėti toliau",
"HeaderFavoriteAlbums": "Favorite Albums",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json
index effff5566..293fc28a8 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-TW.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json
@@ -89,5 +89,8 @@
"UserStoppedPlayingItemWithValues": "{0} 已停止在 {2} 播放 {1}",
"ValueHasBeenAddedToLibrary": "{0} 已新增至您的媒體庫",
"ValueSpecialEpisodeName": "特典 - {0}",
- "VersionNumber": "版本 {0}"
+ "VersionNumber": "版本 {0}",
+ "HeaderRecordingGroups": "錄製組",
+ "Inherit": "繼承",
+ "SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕"
}
diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
index c81a93767..29b8dfd3d 100644
--- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs
+++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
@@ -15,13 +15,9 @@ namespace Emby.Server.Implementations.Security
{
public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository
{
- private readonly IServerConfigurationManager _config;
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
public AuthenticationRepository(ILoggerFactory loggerFactory, IServerConfigurationManager config)
: base(loggerFactory.CreateLogger(nameof(AuthenticationRepository)))
{
- _config = config;
DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "authentication.db");
}
diff --git a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
index 6a522fbef..c27eb7686 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<object, object> GetSetPropertyMethod(Type type, PropertyInfo propertyInfo)
+ public static Action<object, object> GetSetPropertyMethod(PropertyInfo propertyInfo)
{
if (!propertyInfo.CanWrite || propertyInfo.GetIndexParameters().Length > 0)
{
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 985748caf..53ed5fc22 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -7,7 +7,6 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Devices;
@@ -25,7 +24,6 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging;
@@ -53,8 +51,6 @@ namespace Emby.Server.Implementations.Session
private readonly IImageProcessor _imageProcessor;
private readonly IMediaSourceManager _mediaSourceManager;
- private readonly IHttpClient _httpClient;
- private readonly IJsonSerializer _jsonSerializer;
private readonly IServerApplicationHost _appHost;
private readonly IAuthenticationRepository _authRepo;
@@ -96,9 +92,7 @@ namespace Emby.Server.Implementations.Session
IMusicManager musicManager,
IDtoService dtoService,
IImageProcessor imageProcessor,
- IJsonSerializer jsonSerializer,
IServerApplicationHost appHost,
- IHttpClient httpClient,
IAuthenticationRepository authRepo,
IDeviceManager deviceManager,
IMediaSourceManager mediaSourceManager)
@@ -110,9 +104,7 @@ namespace Emby.Server.Implementations.Session
_musicManager = musicManager;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
- _jsonSerializer = jsonSerializer;
_appHost = appHost;
- _httpClient = httpClient;
_authRepo = authRepo;
_deviceManager = deviceManager;
_mediaSourceManager = mediaSourceManager;
@@ -347,8 +339,7 @@ namespace Emby.Server.Implementations.Session
var runtimeTicks = libraryItem.RunTimeTicks;
MediaSourceInfo mediaSource = null;
- var hasMediaSources = libraryItem as IHasMediaSources;
- if (hasMediaSources != null)
+ if (libraryItem is IHasMediaSources hasMediaSources)
{
mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false);
@@ -1046,6 +1037,24 @@ namespace Emby.Server.Implementations.Session
}
}
+ private static Task SendMessageToSessions<T>(IEnumerable<SessionInfo> sessions, string name, T data, CancellationToken cancellationToken)
+ {
+ IEnumerable<Task> GetTasks()
+ {
+ var messageId = Guid.NewGuid().ToString("N");
+ foreach (var session in sessions)
+ {
+ var controllers = session.SessionControllers;
+ 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();
@@ -1232,12 +1241,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));
@@ -1249,26 +1259,11 @@ namespace Emby.Server.Implementations.Session
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- 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);
}
/// <summary>
@@ -1280,22 +1275,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);
}
/// <summary>
@@ -1309,22 +1289,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);
}
/// <summary>
@@ -1841,64 +1806,23 @@ namespace Emby.Server.Implementations.Session
var data = dataFn();
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error sending message", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
+ return SendMessageToSessions(sessions, name, data, cancellationToken);
}
public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, T data, CancellationToken cancellationToken)
{
CheckDisposed();
- var sessions = Sessions.Where(i => userIds.Any(i.ContainsUser)).ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error sending message", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
+ var sessions = Sessions.Where(i => userIds.Any(i.ContainsUser));
+ return SendMessageToSessions(sessions, name, data, cancellationToken);
}
public Task SendMessageToUserDeviceSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken)
{
CheckDisposed();
- var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)).ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error sending message", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
+ var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase));
+ return SendMessageToSessions(sessions, name, data, cancellationToken);
}
public Task SendMessageToUserDeviceAndAdminSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken)
@@ -1906,23 +1830,8 @@ namespace Emby.Server.Implementations.Session
CheckDisposed();
var sessions = Sessions
- .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i))
- .ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error sending message", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
+ .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i));
+ return SendMessageToSessions(sessions, name, data, cancellationToken);
}
private bool IsAdminSession(SessionInfo s)
diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
index a551433ed..63ec75762 100644
--- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -89,10 +89,8 @@ namespace Emby.Server.Implementations.Session
/// </summary>
/// <param name="message">The message.</param>
/// <returns>Task.</returns>
- public Task ProcessMessage(WebSocketMessageInfo message)
- {
- return Task.CompletedTask;
- }
+ public Task ProcessMessageAsync(WebSocketMessageInfo message)
+ => Task.CompletedTask;
private void EnsureController(SessionInfo session, IWebSocketConnection connection)
{
diff --git a/Emby.Server.Implementations/SocketSharp/RequestMono.cs b/Emby.Server.Implementations/SocketSharp/RequestMono.cs
index 373f6d758..ec637186f 100644
--- a/Emby.Server.Implementations/SocketSharp/RequestMono.cs
+++ b/Emby.Server.Implementations/SocketSharp/RequestMono.cs
@@ -86,8 +86,7 @@ namespace Emby.Server.Implementations.SocketSharp
else
{
// We use a substream, as in 2.x we will support large uploads streamed to disk,
- var sub = new HttpPostedFile(e.Filename, e.ContentType, input, e.Start, e.Length);
- files[e.Name] = sub;
+ files[e.Name] = new HttpPostedFile(e.Filename, e.ContentType, input, e.Start, e.Length);
}
}
}
@@ -374,7 +373,7 @@ namespace Emby.Server.Implementations.SocketSharp
var elem = new Element();
ReadOnlySpan<char> 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 00465b63e..7a630bf10 100644
--- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs
+++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
+using System.Linq;
using System.Text;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Services;
@@ -296,27 +297,28 @@ namespace Emby.Server.Implementations.SocketSharp
{
get
{
- if (httpFiles == null)
+ if (httpFiles != null)
{
- if (files == null)
- {
- return httpFiles = Array.Empty<IHttpFile>();
- }
+ return httpFiles;
+ }
- httpFiles = new IHttpFile[files.Count];
- var i = 0;
- foreach (var pair in files)
+ if (files == null)
+ {
+ return httpFiles = Array.Empty<IHttpFile>();
+ }
+
+ 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;