aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs95
-rw-r--r--Emby.Server.Implementations/Cryptography/CryptographyProvider.cs65
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs10
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj4
-rw-r--r--Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs31
-rw-r--r--Emby.Server.Implementations/HttpServer/FileWriter.cs2
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs30
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs2
-rw-r--r--Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs1
-rw-r--r--Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs134
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs16
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs109
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs148
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs37
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs37
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs45
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs4
-rw-r--r--Emby.Server.Implementations/Localization/Core/da.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json8
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-BR.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-PT.json10
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/tr.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-CN.json10
-rw-r--r--Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs2
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs15
29 files changed, 322 insertions, 516 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 24f59478c5..1d0293a5f3 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -1198,25 +1198,11 @@ namespace Emby.Server.Implementations
private CertificateInfo GetCertificateInfo(bool generateCertificate)
{
- if (!string.IsNullOrWhiteSpace(ServerConfigurationManager.Configuration.CertificatePath))
- {
- // Custom cert
- return new CertificateInfo
- {
- Path = ServerConfigurationManager.Configuration.CertificatePath,
- Password = ServerConfigurationManager.Configuration.CertificatePassword
- };
- }
-
- // Generate self-signed cert
- var certHost = GetHostnameFromExternalDns(ServerConfigurationManager.Configuration.WanDdns);
- var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "2").GetMD5().ToString("N", CultureInfo.InvariantCulture) + ".pfx");
- const string Password = "embycert";
-
+ // Custom cert
return new CertificateInfo
{
- Path = certPath,
- Password = Password
+ Path = ServerConfigurationManager.Configuration.CertificatePath,
+ Password = ServerConfigurationManager.Configuration.CertificatePassword
};
}
@@ -1403,17 +1389,6 @@ namespace Emby.Server.Implementations
{
var localAddress = await GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
- string wanAddress;
-
- if (string.IsNullOrEmpty(ServerConfigurationManager.Configuration.WanDdns))
- {
- wanAddress = await GetWanApiUrlFromExternal(cancellationToken).ConfigureAwait(false);
- }
- else
- {
- wanAddress = GetWanApiUrl(ServerConfigurationManager.Configuration.WanDdns);
- }
-
return new SystemInfo
{
HasPendingRestart = HasPendingRestart,
@@ -1435,7 +1410,6 @@ namespace Emby.Server.Implementations
OperatingSystemDisplayName = OperatingSystem.Name,
CanSelfRestart = CanSelfRestart,
CanLaunchWebBrowser = CanLaunchWebBrowser,
- WanAddress = wanAddress,
HasUpdateAvailable = HasUpdateAvailable,
TranscodingTempPath = ApplicationPaths.TranscodingTempPath,
ServerName = FriendlyName,
@@ -1457,24 +1431,12 @@ namespace Emby.Server.Implementations
{
var localAddress = await GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
- string wanAddress;
-
- if (string.IsNullOrEmpty(ServerConfigurationManager.Configuration.WanDdns))
- {
- wanAddress = await GetWanApiUrlFromExternal(cancellationToken).ConfigureAwait(false);
- }
- else
- {
- wanAddress = GetWanApiUrl(ServerConfigurationManager.Configuration.WanDdns);
- }
-
return new PublicSystemInfo
{
Version = ApplicationVersion,
ProductName = ApplicationProductName,
Id = SystemId,
OperatingSystem = OperatingSystem.Id.ToString(),
- WanAddress = wanAddress,
ServerName = FriendlyName,
LocalAddress = localAddress
};
@@ -1506,31 +1468,6 @@ namespace Emby.Server.Implementations
return null;
}
- public async Task<string> GetWanApiUrlFromExternal(CancellationToken cancellationToken)
- {
- const string Url = "http://ipv4.icanhazip.com";
- try
- {
- using (var response = await HttpClient.Get(new HttpRequestOptions
- {
- Url = Url,
- LogErrorResponseBody = false,
- BufferContent = false,
- CancellationToken = cancellationToken
- }).ConfigureAwait(false))
- {
- string res = await response.ReadToEndAsync().ConfigureAwait(false);
- return GetWanApiUrl(res.Trim());
- }
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error getting WAN Ip address information");
- }
-
- return null;
- }
-
/// <summary>
/// Removes the scope id from IPv6 addresses.
/// </summary>
@@ -1573,32 +1510,6 @@ namespace Emby.Server.Implementations
HttpPort.ToString(CultureInfo.InvariantCulture));
}
- public string GetWanApiUrl(IPAddress ipAddress)
- {
- if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
- {
- var str = RemoveScopeId(ipAddress.ToString());
-
- return GetWanApiUrl("[" + str + "]");
- }
-
- return GetWanApiUrl(ipAddress.ToString());
- }
-
- public string GetWanApiUrl(string host)
- {
- if (EnableHttps)
- {
- return string.Format("https://{0}:{1}",
- host,
- ServerConfigurationManager.Configuration.PublicHttpsPort.ToString(CultureInfo.InvariantCulture));
- }
-
- return string.Format("http://{0}:{1}",
- host,
- ServerConfigurationManager.Configuration.PublicPort.ToString(CultureInfo.InvariantCulture));
- }
-
public Task<List<IPAddress>> GetLocalIpAddresses(CancellationToken cancellationToken)
{
return GetLocalIpAddressesInternal(true, 0, cancellationToken);
diff --git a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
index f726dae2ee..23b77e2687 100644
--- a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
+++ b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
@@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
using System.Security.Cryptography;
-using System.Text;
using MediaBrowser.Model.Cryptography;
+using static MediaBrowser.Common.Cryptography.Constants;
namespace Emby.Server.Implementations.Cryptography
{
@@ -30,8 +28,6 @@ namespace Emby.Server.Implementations.Cryptography
private RandomNumberGenerator _randomNumberGenerator;
- private const int _defaultIterations = 1000;
-
private bool _disposed = false;
public CryptographyProvider()
@@ -45,44 +41,13 @@ namespace Emby.Server.Implementations.Cryptography
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())
- {
- return provider.ComputeHash(bytes);
- }
- }
-
- [Obsolete("Use System.Security.Cryptography.MD5 directly")]
- public byte[] ComputeMD5(Stream str)
- {
- using (var provider = MD5.Create())
- {
- return provider.ComputeHash(str);
- }
- }
-
- [Obsolete("Use System.Security.Cryptography.MD5 directly")]
- public byte[] ComputeMD5(byte[] bytes)
- {
- using (var provider = MD5.Create())
- {
- return provider.ComputeHash(bytes);
- }
- }
-
public IEnumerable<string> GetSupportedHashMethods()
=> _supportedHashMethods;
private byte[] PBKDF2(string method, byte[] bytes, byte[] salt, int iterations)
{
- //downgrading for now as we need this library to be dotnetstandard compliant
- //with this downgrade we'll add a check to make sure we're on the downgrade method at the moment
+ // downgrading for now as we need this library to be dotnetstandard compliant
+ // with this downgrade we'll add a check to make sure we're on the downgrade method at the moment
if (method == DefaultHashMethod)
{
using (var r = new Rfc2898DeriveBytes(bytes, salt, iterations))
@@ -104,7 +69,7 @@ namespace Emby.Server.Implementations.Cryptography
{
if (hashMethod == DefaultHashMethod)
{
- return PBKDF2(hashMethod, bytes, salt, _defaultIterations);
+ return PBKDF2(hashMethod, bytes, salt, DefaultIterations);
}
else if (_supportedHashMethods.Contains(hashMethod))
{
@@ -129,26 +94,14 @@ namespace Emby.Server.Implementations.Cryptography
}
public byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt)
- => PBKDF2(DefaultHashMethod, bytes, salt, _defaultIterations);
-
- public byte[] ComputeHash(PasswordHash hash)
- {
- int iterations = _defaultIterations;
- if (!hash.Parameters.ContainsKey("iterations"))
- {
- hash.Parameters.Add("iterations", iterations.ToString(CultureInfo.InvariantCulture));
- }
- else if (!int.TryParse(hash.Parameters["iterations"], out iterations))
- {
- throw new InvalidDataException($"Couldn't successfully parse iterations value from string: {hash.Parameters["iterations"]}");
- }
-
- return PBKDF2(hash.Id, hash.Hash, hash.Salt, iterations);
- }
+ => PBKDF2(DefaultHashMethod, bytes, salt, DefaultIterations);
public byte[] GenerateSalt()
+ => GenerateSalt(DefaultSaltLength);
+
+ public byte[] GenerateSalt(int length)
{
- byte[] salt = new byte[64];
+ byte[] salt = new byte[length];
_randomNumberGenerator.GetBytes(salt);
return salt;
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 75192a8f1a..a3201f0bc0 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -218,14 +218,12 @@ namespace Emby.Server.Implementations.Dto
AttachUserSpecificInfo(dto, item, user, options);
}
- if (item is IHasMediaSources hasMediaSources)
+ if (item is IHasMediaSources
+ && options.ContainsField(ItemFields.MediaSources))
{
- if (options.ContainsField(ItemFields.MediaSources))
- {
- dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(item, true, user).ToArray();
+ dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(item, true, user).ToArray();
- NormalizeMediaSourceContainers(dto);
- }
+ NormalizeMediaSourceContainers(dto);
}
if (options.ContainsField(ItemFields.Studios))
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index b48193c58d..2c71f04578 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -21,7 +21,7 @@
<ItemGroup>
<PackageReference Include="IPNetwork2" Version="2.4.0.126" />
- <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.0" />
+ <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
@@ -33,7 +33,7 @@
<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.6.0" />
- <PackageReference Include="sharpcompress" Version="0.23.0" />
+ <PackageReference Include="sharpcompress" Version="0.24.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.0.1" />
</ItemGroup>
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index 0dd4d4ca5b..0e6083773d 100644
--- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
@@ -83,7 +83,16 @@ namespace Emby.Server.Implementations.HttpClientManager
var request = new HttpRequestMessage(method, url);
- AddRequestHeaders(request, options);
+ foreach (var header in options.RequestHeaders)
+ {
+ request.Headers.TryAddWithoutValidation(header.Key, header.Value);
+ }
+
+ if (options.EnableDefaultUserAgent
+ && !request.Headers.TryGetValues(HeaderNames.UserAgent, out _))
+ {
+ request.Headers.Add(HeaderNames.UserAgent, _defaultUserAgentFn());
+ }
switch (options.DecompressionMethod)
{
@@ -121,26 +130,6 @@ namespace Emby.Server.Implementations.HttpClientManager
return request;
}
- private void AddRequestHeaders(HttpRequestMessage request, HttpRequestOptions options)
- {
- var hasUserAgent = false;
-
- foreach (var header in options.RequestHeaders)
- {
- if (string.Equals(header.Key, HeaderNames.UserAgent, StringComparison.OrdinalIgnoreCase))
- {
- hasUserAgent = true;
- }
-
- request.Headers.Add(header.Key, header.Value);
- }
-
- if (!hasUserAgent && options.EnableDefaultUserAgent)
- {
- request.Headers.Add(HeaderNames.UserAgent, _defaultUserAgentFn());
- }
- }
-
/// <summary>
/// Gets the response internal.
/// </summary>
diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs
index 2890cca7ce..2c7e81361d 100644
--- a/Emby.Server.Implementations/HttpServer/FileWriter.cs
+++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs
@@ -181,7 +181,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);
}
public async Task WriteToAsync(HttpResponse response, CancellationToken cancellationToken)
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index bdcf5d0b7c..d60f5c0556 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -94,7 +94,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <returns></returns>
public void ApplyRequestFilters(IRequest req, HttpResponse res, object requestDto)
{
- //Exec all RequestFilter attributes with Priority < 0
+ // Exec all RequestFilter attributes with Priority < 0
var attributes = GetRequestFilterAttributes(requestDto.GetType());
int count = attributes.Count;
@@ -105,7 +105,7 @@ namespace Emby.Server.Implementations.HttpServer
attribute.RequestFilter(req, res, requestDto);
}
- //Exec remaining RequestFilter attributes with Priority >= 0
+ // Exec remaining RequestFilter attributes with Priority >= 0
for (; i < count && attributes[i].Priority >= 0; i++)
{
var attribute = attributes[i];
@@ -276,9 +276,9 @@ namespace Emby.Server.Implementations.HttpServer
{
connection.Dispose();
}
- catch
+ catch (Exception ex)
{
-
+ _logger.LogError(ex, "Error disposing connection");
}
}
}
@@ -603,7 +603,14 @@ namespace Emby.Server.Implementations.HttpServer
Summary = route.Summary
});
- routes.Add(new RouteAttribute(NormalizeOldRoutePath(route.Path), route.Verbs)
+ routes.Add(new RouteAttribute(NormalizeEmbyRoutePath(route.Path), route.Verbs)
+ {
+ Notes = route.Notes,
+ Priority = route.Priority,
+ Summary = route.Summary
+ });
+
+ routes.Add(new RouteAttribute(NormalizeMediaBrowserRoutePath(route.Path), route.Verbs)
{
Notes = route.Notes,
Priority = route.Priority,
@@ -645,7 +652,7 @@ namespace Emby.Server.Implementations.HttpServer
}
// this method was left for compatibility with third party clients
- private static string NormalizeOldRoutePath(string path)
+ private static string NormalizeEmbyRoutePath(string path)
{
if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
{
@@ -655,6 +662,17 @@ namespace Emby.Server.Implementations.HttpServer
return "emby/" + path;
}
+ // this method was left for compatibility with third party clients
+ private static string NormalizeMediaBrowserRoutePath(string path)
+ {
+ if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
+ {
+ return "/mediabrowser" + path;
+ }
+
+ return "mediabrowser/" + path;
+ }
+
private static string NormalizeCustomRoutePath(string baseUrl, string path)
{
if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index 3d3f67ca22..93a61fe67a 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
var user = auth.User;
- if (user == null & !auth.UserId.Equals(Guid.Empty))
+ if (user == null && auth.UserId != Guid.Empty)
{
throw new SecurityException("User with Id " + auth.UserId + " not found");
}
diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index f1ae2fc9c0..8bdb387843 100644
--- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
+++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
@@ -57,7 +57,6 @@ namespace Emby.Server.Implementations.Library
}
var filename = fileInfo.Name;
- var path = fileInfo.FullName;
// Ignore hidden files on UNIX
if (Environment.OSVersion.Platform != PlatformID.Win32NT
diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
index 2282b8efb6..c95b00ede2 100644
--- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
+++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
@@ -2,24 +2,30 @@ using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using MediaBrowser.Common.Cryptography;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Cryptography;
+using static MediaBrowser.Common.HexHelper;
namespace Emby.Server.Implementations.Library
{
public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser
{
private readonly ICryptoProvider _cryptographyProvider;
+
public DefaultAuthenticationProvider(ICryptoProvider cryptographyProvider)
{
_cryptographyProvider = cryptographyProvider;
}
+ /// <inheritdoc />
public string Name => "Default";
+ /// <inheritdoc />
public bool IsEnabled => true;
+ /// <inheritdoc />
// 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
@@ -28,6 +34,7 @@ namespace Emby.Server.Implementations.Library
throw new NotImplementedException();
}
+ /// <inheritdoc />
// This is the version that we need to use for local users. Because reasons.
public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser)
{
@@ -46,10 +53,9 @@ namespace Emby.Server.Implementations.Library
});
}
- ConvertPasswordFormat(resolvedUser);
byte[] passwordbytes = Encoding.UTF8.GetBytes(password);
- PasswordHash readyHash = new PasswordHash(resolvedUser.Password);
+ PasswordHash readyHash = PasswordHash.Parse(resolvedUser.Password);
if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id)
|| _cryptographyProvider.DefaultHashMethod == readyHash.Id)
{
@@ -76,72 +82,31 @@ namespace Emby.Server.Implementations.Library
});
}
- // This allows us to move passwords forward to the newformat without breaking. They are still insecure, unsalted, and dumb before a password change
- // but at least they are in the new format.
- private void ConvertPasswordFormat(User user)
- {
- if (string.IsNullOrEmpty(user.Password))
- {
- return;
- }
-
- if (user.Password.IndexOf('$') == -1)
- {
- string hash = user.Password;
- user.Password = string.Format("$SHA1${0}", hash);
- }
-
- if (user.EasyPassword != null
- && user.EasyPassword.IndexOf('$') == -1)
- {
- string hash = user.EasyPassword;
- user.EasyPassword = string.Format("$SHA1${0}", hash);
- }
- }
-
+ /// <inheritdoc />
public bool HasPassword(User user)
=> !string.IsNullOrEmpty(user.Password);
+ /// <inheritdoc />
public Task ChangePassword(User user, string newPassword)
{
- ConvertPasswordFormat(user);
-
- // This is needed to support changing a no password user to a password user
- if (string.IsNullOrEmpty(user.Password))
+ if (string.IsNullOrEmpty(newPassword))
{
- PasswordHash newPasswordHash = new PasswordHash(_cryptographyProvider);
- newPasswordHash.Salt = _cryptographyProvider.GenerateSalt();
- newPasswordHash.Id = _cryptographyProvider.DefaultHashMethod;
- newPasswordHash.Hash = GetHashedChangeAuth(newPassword, newPasswordHash);
- user.Password = newPasswordHash.ToString();
+ user.Password = null;
return Task.CompletedTask;
}
- PasswordHash passwordHash = new PasswordHash(user.Password);
- if (passwordHash.Id == "SHA1"
- && passwordHash.Salt.Length == 0)
- {
- passwordHash.Salt = _cryptographyProvider.GenerateSalt();
- passwordHash.Id = _cryptographyProvider.DefaultHashMethod;
- passwordHash.Hash = GetHashedChangeAuth(newPassword, passwordHash);
- }
- else if (newPassword != null)
- {
- passwordHash.Hash = GetHashed(user, newPassword);
- }
-
- user.Password = passwordHash.ToString();
+ PasswordHash newPasswordHash = _cryptographyProvider.CreatePasswordHash(newPassword);
+ user.Password = newPasswordHash.ToString();
return Task.CompletedTask;
}
+ /// <inheritdoc />
public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
{
- ConvertPasswordFormat(user);
-
if (newPassword != null)
{
- newPasswordHash = string.Format("$SHA1${0}", GetHashedString(user, newPassword));
+ newPasswordHash = _cryptographyProvider.CreatePasswordHash(newPassword).ToString();
}
if (string.IsNullOrWhiteSpace(newPasswordHash))
@@ -152,21 +117,12 @@ namespace Emby.Server.Implementations.Library
user.EasyPassword = newPasswordHash;
}
+ /// <inheritdoc />
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
- : PasswordHash.ConvertToByteString(new PasswordHash(user.EasyPassword).Hash);
- }
-
- internal byte[] GetHashedChangeAuth(string newPassword, PasswordHash passwordHash)
- {
- passwordHash.Hash = Encoding.UTF8.GetBytes(newPassword);
- return _cryptographyProvider.ComputeHash(passwordHash);
+ : ToHexString(PasswordHash.Parse(user.EasyPassword).Hash);
}
/// <summary>
@@ -174,54 +130,36 @@ namespace Emby.Server.Implementations.Library
/// </summary>
public string GetHashedString(User user, string str)
{
- PasswordHash passwordHash;
if (string.IsNullOrEmpty(user.Password))
{
- passwordHash = new PasswordHash(_cryptographyProvider);
- }
- else
- {
- ConvertPasswordFormat(user);
- passwordHash = new PasswordHash(user.Password);
+ return _cryptographyProvider.CreatePasswordHash(str).ToString();
}
- 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 PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash));
- }
- else
- {
- // the password has no salt and should be called with the older method for safety
- return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash.Id, Encoding.UTF8.GetBytes(str)));
- }
+ // TODO: make use of iterations parameter?
+ PasswordHash passwordHash = PasswordHash.Parse(user.Password);
+ return new PasswordHash(
+ passwordHash.Id,
+ _cryptographyProvider.ComputeHash(
+ passwordHash.Id,
+ Encoding.UTF8.GetBytes(str),
+ passwordHash.Salt),
+ passwordHash.Salt,
+ passwordHash.Parameters.ToDictionary(x => x.Key, y => y.Value)).ToString();
}
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);
+ return _cryptographyProvider.CreatePasswordHash(str).Hash;
}
- 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));
- }
+ // TODO: make use of iterations parameter?
+ PasswordHash passwordHash = PasswordHash.Parse(user.Password);
+ return _cryptographyProvider.ComputeHash(
+ passwordHash.Id,
+ Encoding.UTF8.GetBytes(str),
+ passwordHash.Salt);
}
}
}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 36934f65f0..87e951f25d 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -779,12 +779,23 @@ namespace Emby.Server.Implementations.Library
{
var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+ _logger.LogDebug("Creating userRootPath at {path}", userRootPath);
Directory.CreateDirectory(userRootPath);
- var tmpItem = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder;
+ var newItemId = GetNewItemId(userRootPath, typeof(UserRootFolder));
+ UserRootFolder tmpItem = null;
+ try
+ {
+ tmpItem = GetItemById(newItemId) as UserRootFolder;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error creating UserRootFolder {path}", newItemId);
+ }
if (tmpItem == null)
{
+ _logger.LogDebug("Creating new userRootFolder with DeepCopy");
tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath))).DeepCopy<Folder, UserRootFolder>();
}
@@ -796,6 +807,7 @@ namespace Emby.Server.Implementations.Library
}
_userRootFolder = tmpItem;
+ _logger.LogDebug("Setting userRootFolder: {folder}", _userRootFolder);
}
}
}
@@ -1146,8 +1158,10 @@ namespace Emby.Server.Implementations.Library
public List<VirtualFolderInfo> GetVirtualFolders(bool includeRefreshState)
{
+ _logger.LogDebug("Getting topLibraryFolders");
var topLibraryFolders = GetUserRootFolder().Children.ToList();
+ _logger.LogDebug("Getting refreshQueue");
var refreshQueue = includeRefreshState ? _providerManagerFactory().GetRefreshQueue() : null;
return _fileSystem.GetDirectoryPaths(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath)
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index a7ea13ca61..52b2f56ffc 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -8,6 +8,7 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Cryptography;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
@@ -23,7 +24,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
@@ -31,6 +31,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Users;
using Microsoft.Extensions.Logging;
+using static MediaBrowser.Common.HexHelper;
namespace Emby.Server.Implementations.Library
{
@@ -272,14 +273,12 @@ namespace Emby.Server.Implementations.Library
var user = Users.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
var success = false;
- string updatedUsername = null;
IAuthenticationProvider authenticationProvider = null;
if (user != null)
{
var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
authenticationProvider = authResult.authenticationProvider;
- updatedUsername = authResult.username;
success = authResult.success;
}
else
@@ -287,7 +286,7 @@ namespace Emby.Server.Implementations.Library
// user is null
var authResult = await AuthenticateLocalUser(username, password, hashedPassword, null, remoteEndPoint).ConfigureAwait(false);
authenticationProvider = authResult.authenticationProvider;
- updatedUsername = authResult.username;
+ string updatedUsername = authResult.username;
success = authResult.success;
if (success
@@ -353,11 +352,11 @@ namespace Emby.Server.Implementations.Library
UpdateUser(user);
}
- UpdateInvalidLoginAttemptCount(user, 0);
+ ResetInvalidLoginAttemptCount(user);
}
else
{
- UpdateInvalidLoginAttemptCount(user, user.Policy.InvalidLoginAttemptCount + 1);
+ IncrementInvalidLoginAttemptCount(user);
}
_logger.LogInformation("Authentication request for {0} {1}.", user.Name, success ? "has succeeded" : "has been denied");
@@ -450,53 +449,38 @@ namespace Emby.Server.Implementations.Library
}
}
- private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)> AuthenticateLocalUser(string username, string password, string hashedPassword, User user, string remoteEndPoint)
+ private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)> AuthenticateLocalUser(
+ string username,
+ string password,
+ string hashedPassword,
+ User user,
+ string remoteEndPoint)
{
bool success = false;
IAuthenticationProvider authenticationProvider = null;
- if (password != null && user != null)
+ foreach (var provider in GetAuthenticationProviders(user))
{
- // Doesn't look like this is even possible to be used, because of password == null checks below
- hashedPassword = _defaultAuthenticationProvider.GetHashedString(user, password);
- }
+ var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
+ var updatedUsername = providerAuthResult.username;
+ success = providerAuthResult.success;
- if (password == null)
- {
- // legacy
- success = string.Equals(user.Password, hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
- }
- else
- {
- foreach (var provider in GetAuthenticationProviders(user))
+ if (success)
{
- var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
- var updatedUsername = providerAuthResult.username;
- success = providerAuthResult.success;
-
- if (success)
- {
- authenticationProvider = provider;
- username = updatedUsername;
- break;
- }
+ authenticationProvider = provider;
+ username = updatedUsername;
+ break;
}
}
- if (user != null
- && !success
+ if (!success
&& _networkManager.IsInLocalNetwork(remoteEndPoint)
&& user.Configuration.EnableLocalPassword)
{
- if (password == null)
- {
- // legacy
- success = string.Equals(GetLocalPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
- }
- else
- {
- success = string.Equals(GetLocalPasswordHash(user), _defaultAuthenticationProvider.GetHashedString(user, password), StringComparison.OrdinalIgnoreCase);
- }
+ success = string.Equals(
+ GetLocalPasswordHash(user),
+ _defaultAuthenticationProvider.GetHashedString(user, password),
+ StringComparison.OrdinalIgnoreCase);
}
return (authenticationProvider, username, success);
@@ -506,44 +490,31 @@ namespace Emby.Server.Implementations.Library
{
return string.IsNullOrEmpty(user.EasyPassword)
? null
- : PasswordHash.ConvertToByteString(new PasswordHash(user.EasyPassword).Hash);
+ : ToHexString(PasswordHash.Parse(user.EasyPassword).Hash);
}
- private void UpdateInvalidLoginAttemptCount(User user, int newValue)
+ private void ResetInvalidLoginAttemptCount(User user)
{
- if (user.Policy.InvalidLoginAttemptCount == newValue || newValue <= 0)
- {
- return;
- }
-
- user.Policy.InvalidLoginAttemptCount = newValue;
-
- // Check for users without a value here and then fill in the default value
- // also protect from an always lockout if misconfigured
- if (user.Policy.LoginAttemptsBeforeLockout == null || user.Policy.LoginAttemptsBeforeLockout == 0)
- {
- user.Policy.LoginAttemptsBeforeLockout = user.Policy.IsAdministrator ? 5 : 3;
- }
-
- var maxCount = user.Policy.LoginAttemptsBeforeLockout;
-
- var fireLockout = false;
+ user.Policy.InvalidLoginAttemptCount = 0;
+ UpdateUserPolicy(user, user.Policy, false);
+ }
- // -1 can be used to specify no lockout value
- if (maxCount != -1 && newValue >= maxCount)
+ private void IncrementInvalidLoginAttemptCount(User user)
+ {
+ int invalidLogins = ++user.Policy.InvalidLoginAttemptCount;
+ int maxInvalidLogins = user.Policy.LoginAttemptsBeforeLockout;
+ if (maxInvalidLogins > 0
+ && invalidLogins >= maxInvalidLogins)
{
- _logger.LogDebug("Disabling user {0} due to {1} unsuccessful login attempts.", user.Name, newValue);
user.Policy.IsDisabled = true;
-
- fireLockout = true;
+ UserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
+ _logger.LogWarning(
+ "Disabling user {UserName} due to {Attempts} unsuccessful login attempts.",
+ user.Name,
+ invalidLogins);
}
UpdateUserPolicy(user, user.Policy, false);
-
- if (fireLockout)
- {
- UserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
- }
}
/// <summary>
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index 4d79cae139..88e2a8fa69 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -236,7 +236,7 @@ namespace Emby.Server.Implementations.Library
if (!parentId.Equals(Guid.Empty))
{
var parentItem = _libraryManager.GetItemById(parentId);
- if (parentItem is Channel parentItemChannel)
+ if (parentItem is Channel)
{
return _channelManager.GetLatestChannelItemsInternal(
new InternalItemsQuery(user)
@@ -248,7 +248,7 @@ namespace Emby.Server.Implementations.Library
IncludeItemTypes = request.IncludeItemTypes,
EnableTotalRecordCount = false
},
- CancellationToken.None).Result.Items;
+ CancellationToken.None).GetAwaiter().GetResult().Items;
}
if (parentItem is Folder parent)
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index d7411af502..da0013f128 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_streamHelper = streamHelper;
_seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers.json"));
- _timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers.json"), _logger);
+ _timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers.json"));
_timerProvider.TimerFired += _timerProvider_TimerFired;
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index 3cc0541e74..cc9c8e5d29 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -208,9 +208,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private static string GetAudioArgs(MediaSourceInfo mediaSource)
{
- var mediaStreams = mediaSource.MediaStreams ?? new List<MediaStream>();
- var inputAudioCodec = mediaStreams.Where(i => i.Type == MediaStreamType.Audio).Select(i => i.Codec).FirstOrDefault() ?? string.Empty;
-
return "-codec:a:0 copy";
//var audioChannels = 2;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
index 9c45ee36a2..9055a70a67 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
@@ -10,67 +10,64 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public class ItemDataProvider<T>
where T : class
{
- private readonly object _fileDataLock = new object();
- private List<T> _items;
private readonly IJsonSerializer _jsonSerializer;
- protected readonly ILogger Logger;
private readonly string _dataPath;
- protected readonly Func<T, T, bool> EqualityComparer;
+ private readonly object _fileDataLock = new object();
+ private T[] _items;
- public ItemDataProvider(IJsonSerializer jsonSerializer, ILogger logger, string dataPath, Func<T, T, bool> equalityComparer)
+ public ItemDataProvider(
+ IJsonSerializer jsonSerializer,
+ ILogger logger,
+ string dataPath,
+ Func<T, T, bool> equalityComparer)
{
+ _jsonSerializer = jsonSerializer;
Logger = logger;
_dataPath = dataPath;
EqualityComparer = equalityComparer;
- _jsonSerializer = jsonSerializer;
}
- public IReadOnlyList<T> GetAll()
- {
- lock (_fileDataLock)
- {
- if (_items == null)
- {
- if (!File.Exists(_dataPath))
- {
- return new List<T>();
- }
-
- Logger.LogInformation("Loading live tv data from {0}", _dataPath);
- _items = GetItemsFromFile(_dataPath);
- }
+ protected ILogger Logger { get; }
- return _items.ToList();
- }
- }
+ protected Func<T, T, bool> EqualityComparer { get; }
- private List<T> GetItemsFromFile(string path)
+ private void EnsureLoaded()
{
- try
+ if (_items != null)
{
- return _jsonSerializer.DeserializeFromFile<List<T>>(path);
+ return;
}
- catch (Exception ex)
+
+ if (File.Exists(_dataPath))
{
- Logger.LogError(ex, "Error deserializing {Path}", path);
+ Logger.LogInformation("Loading live tv data from {Path}", _dataPath);
+
+ try
+ {
+ _items = _jsonSerializer.DeserializeFromFile<T[]>(_dataPath);
+ return;
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError(ex, "Error deserializing {Path}", _dataPath);
+ }
}
- return new List<T>();
+ _items = Array.Empty<T>();
}
- private void UpdateList(List<T> newList)
+ private void SaveList()
{
- if (newList == null)
- {
- throw new ArgumentNullException(nameof(newList));
- }
-
Directory.CreateDirectory(Path.GetDirectoryName(_dataPath));
+ _jsonSerializer.SerializeToFile(_items, _dataPath);
+ }
+ public IReadOnlyList<T> GetAll()
+ {
lock (_fileDataLock)
{
- _jsonSerializer.SerializeToFile(newList, _dataPath);
- _items = newList;
+ EnsureLoaded();
+ return (T[])_items.Clone();
}
}
@@ -81,18 +78,20 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
throw new ArgumentNullException(nameof(item));
}
- var list = GetAll().ToList();
-
- var index = list.FindIndex(i => EqualityComparer(i, item));
-
- if (index == -1)
+ lock (_fileDataLock)
{
- throw new ArgumentException("item not found");
- }
+ EnsureLoaded();
- list[index] = item;
+ var index = Array.FindIndex(_items, i => EqualityComparer(i, item));
+ if (index == -1)
+ {
+ throw new ArgumentException("item not found");
+ }
- UpdateList(list);
+ _items[index] = item;
+
+ SaveList();
+ }
}
public virtual void Add(T item)
@@ -102,37 +101,58 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
throw new ArgumentNullException(nameof(item));
}
- var list = GetAll().ToList();
-
- if (list.Any(i => EqualityComparer(i, item)))
+ lock (_fileDataLock)
{
- throw new ArgumentException("item already exists");
- }
+ EnsureLoaded();
- list.Add(item);
+ if (_items.Any(i => EqualityComparer(i, item)))
+ {
+ throw new ArgumentException("item already exists", nameof(item));
+ }
- UpdateList(list);
+ int oldLen = _items.Length;
+ var newList = new T[oldLen + 1];
+ _items.CopyTo(newList, 0);
+ newList[oldLen] = item;
+ _items = newList;
+
+ SaveList();
+ }
}
- public void AddOrUpdate(T item)
+ public virtual void AddOrUpdate(T item)
{
- var list = GetAll().ToList();
-
- if (!list.Any(i => EqualityComparer(i, item)))
- {
- Add(item);
- }
- else
+ lock (_fileDataLock)
{
- Update(item);
+ EnsureLoaded();
+
+ int index = Array.FindIndex(_items, i => EqualityComparer(i, item));
+ if (index == -1)
+ {
+ int oldLen = _items.Length;
+ var newList = new T[oldLen + 1];
+ _items.CopyTo(newList, 0);
+ newList[oldLen] = item;
+ _items = newList;
+ }
+ else
+ {
+ _items[index] = item;
+ }
+
+ SaveList();
}
}
public virtual void Delete(T item)
{
- var list = GetAll().Where(i => !EqualityComparer(i, item)).ToList();
+ lock (_fileDataLock)
+ {
+ EnsureLoaded();
+ _items = _items.Where(i => !EqualityComparer(i, item)).ToArray();
- UpdateList(list);
+ SaveList();
+ }
}
}
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index 3c807a8ead..d09b281d4c 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -14,21 +14,19 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public class TimerManager : ItemDataProvider<TimerInfo>
{
private readonly ConcurrentDictionary<string, Timer> _timers = new ConcurrentDictionary<string, Timer>(StringComparer.OrdinalIgnoreCase);
- private readonly ILogger _logger;
- public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
-
- public TimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1)
+ public TimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath)
: base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
{
- _logger = logger1;
}
+ public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
+
public void RestartTimers()
{
StopTimers();
- foreach (var item in GetAll().ToList())
+ foreach (var item in GetAll())
{
AddOrUpdateSystemTimer(item);
}
@@ -64,16 +62,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return;
}
- var list = GetAll().ToList();
+ base.AddOrUpdate(item);
+ }
- if (!list.Any(i => EqualityComparer(i, item)))
- {
- base.Add(item);
- }
- else
- {
- base.Update(item);
- }
+ public override void AddOrUpdate(TimerInfo item)
+ {
+ base.AddOrUpdate(item);
+ AddOrUpdateSystemTimer(item);
}
public override void Add(TimerInfo item)
@@ -89,8 +84,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private static bool ShouldStartTimer(TimerInfo item)
{
- if (item.Status == RecordingStatus.Completed ||
- item.Status == RecordingStatus.Cancelled)
+ if (item.Status == RecordingStatus.Completed
+ || item.Status == RecordingStatus.Cancelled)
{
return false;
}
@@ -126,12 +121,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (_timers.TryAdd(item.Id, timer))
{
- _logger.LogInformation("Creating recording timer for {id}, {name}. Timer will fire in {minutes} minutes", item.Id, item.Name, dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture));
+ Logger.LogInformation(
+ "Creating recording timer for {Id}, {Name}. Timer will fire in {Minutes} minutes",
+ item.Id,
+ item.Name,
+ dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture));
}
else
{
timer.Dispose();
- _logger.LogWarning("Timer already exists for item {id}", item.Id);
+ Logger.LogWarning("Timer already exists for item {Id}", item.Id);
}
}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index f5dffc22af..9a4c91d0bd 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -17,7 +17,6 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
-using Microsoft.Net.Http.Headers;
namespace Emby.Server.Implementations.LiveTv.Listings
{
@@ -41,6 +40,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private string UserAgent => _appHost.ApplicationUserAgent;
+ /// <inheritdoc />
+ public string Name => "Schedules Direct";
+
+ /// <inheritdoc />
+ public string Type => nameof(SchedulesDirect);
+
private static List<string> GetScheduleRequestDates(DateTime startDateUtc, DateTime endDateUtc)
{
var dates = new List<string>();
@@ -103,7 +108,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
httpOptions.RequestHeaders["token"] = token;
using (var response = await Post(httpOptions, true, info).ConfigureAwait(false))
- using (var reader = new StreamReader(response.Content))
{
var dailySchedules = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.Day>>(response.Content).ConfigureAwait(false);
_logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId);
@@ -122,7 +126,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
httpOptions.RequestContent = "[\"" + string.Join("\", \"", programsID) + "\"]";
using (var innerResponse = await Post(httpOptions, true, info).ConfigureAwait(false))
- using (var innerReader = new StreamReader(innerResponse.Content))
{
var programDetails = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.ProgramDetails>>(innerResponse.Content).ConfigureAwait(false);
var programDict = programDetails.ToDictionary(p => p.programID, y => y);
@@ -152,14 +155,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var imagesWithText = allImages.Where(i => string.Equals(i.text, "yes", StringComparison.OrdinalIgnoreCase));
var imagesWithoutText = allImages.Where(i => string.Equals(i.text, "no", StringComparison.OrdinalIgnoreCase));
- const double desiredAspect = 0.666666667;
+ const double DesiredAspect = 2.0 / 3;
- programEntry.primaryImage = GetProgramImage(ApiUrl, imagesWithText, true, desiredAspect) ??
- GetProgramImage(ApiUrl, allImages, true, desiredAspect);
+ programEntry.primaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
+ GetProgramImage(ApiUrl, allImages, true, DesiredAspect);
- const double wideAspect = 1.77777778;
+ const double WideAspect = 16.0 / 9;
- programEntry.thumbImage = GetProgramImage(ApiUrl, imagesWithText, true, wideAspect);
+ programEntry.thumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
// Don't supply the same image twice
if (string.Equals(programEntry.primaryImage, programEntry.thumbImage, StringComparison.Ordinal))
@@ -167,7 +170,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
programEntry.thumbImage = null;
}
- programEntry.backdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, wideAspect);
+ programEntry.backdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
//programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
// GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
@@ -178,6 +181,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
programsInfo.Add(GetProgram(channelId, schedule, programDict[schedule.programID]));
}
+
return programsInfo;
}
}
@@ -185,12 +189,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private static int GetSizeOrder(ScheduleDirect.ImageData image)
{
- if (!string.IsNullOrWhiteSpace(image.height))
+ if (!string.IsNullOrWhiteSpace(image.height)
+ && int.TryParse(image.height, out int value))
{
- if (int.TryParse(image.height, out int value))
- {
- return value;
- }
+ return value;
}
return 0;
@@ -736,16 +738,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
httpOptions.RequestHeaders["token"] = token;
- using (var response = await _httpClient.SendAsync(httpOptions, "PUT"))
+ using (await _httpClient.SendAsync(httpOptions, "PUT"))
{
}
}
- public string Name => "Schedules Direct";
-
- public static string TypeName = "SchedulesDirect";
- public string Type => TypeName;
-
private async Task<bool> HasLineup(ListingsProviderInfo info, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(info.ListingsId))
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index ee975e19a5..89b92c999e 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -60,16 +60,6 @@ namespace Emby.Server.Implementations.LiveTv
private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
private readonly IFileSystem _fileSystem;
- public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
- public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;
- public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
- public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
-
- public string GetEmbyTvActiveRecordingPath(string id)
- {
- return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id);
- }
-
public LiveTvManager(
IServerApplicationHost appHost,
IServerConfigurationManager config,
@@ -102,17 +92,34 @@ namespace Emby.Server.Implementations.LiveTv
_tvDtoService = new LiveTvDtoService(dtoService, imageProcessor, loggerFactory, appHost, _libraryManager);
}
+ public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
+
+ public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;
+
+ public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
+
+ public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
+
/// <summary>
/// Gets the services.
/// </summary>
/// <value>The services.</value>
public IReadOnlyList<ILiveTvService> Services => _services;
+ public ITunerHost[] TunerHosts => _tunerHosts;
+
+ public IListingsProvider[] ListingProviders => _listingProviders;
+
private LiveTvOptions GetConfiguration()
{
return _config.GetConfiguration<LiveTvOptions>("livetv");
}
+ public string GetEmbyTvActiveRecordingPath(string id)
+ {
+ return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id);
+ }
+
/// <summary>
/// Adds the parts.
/// </summary>
@@ -130,13 +137,13 @@ namespace Emby.Server.Implementations.LiveTv
{
if (service is EmbyTV.EmbyTV embyTv)
{
- embyTv.TimerCreated += EmbyTv_TimerCreated;
- embyTv.TimerCancelled += EmbyTv_TimerCancelled;
+ embyTv.TimerCreated += OnEmbyTvTimerCreated;
+ embyTv.TimerCancelled += OnEmbyTvTimerCancelled;
}
}
}
- private void EmbyTv_TimerCancelled(object sender, GenericEventArgs<string> e)
+ private void OnEmbyTvTimerCancelled(object sender, GenericEventArgs<string> e)
{
var timerId = e.Argument;
@@ -149,10 +156,9 @@ namespace Emby.Server.Implementations.LiveTv
});
}
- private void EmbyTv_TimerCreated(object sender, GenericEventArgs<TimerInfo> e)
+ private void OnEmbyTvTimerCreated(object sender, GenericEventArgs<TimerInfo> e)
{
var timer = e.Argument;
- var service = sender as ILiveTvService;
TimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>
{
@@ -164,10 +170,6 @@ namespace Emby.Server.Implementations.LiveTv
});
}
- public ITunerHost[] TunerHosts => _tunerHosts;
-
- public IListingsProvider[] ListingProviders => _listingProviders;
-
public List<NameIdPair> GetTunerHostTypes()
{
return _tunerHosts.OrderBy(i => i.Name).Select(i => new NameIdPair
@@ -966,9 +968,6 @@ namespace Emby.Server.Implementations.LiveTv
private async Task AddRecordingInfo(IEnumerable<Tuple<BaseItemDto, string, string>> programs, CancellationToken cancellationToken)
{
- var timers = new Dictionary<string, List<TimerInfo>>();
- var seriesTimers = new Dictionary<string, List<SeriesTimerInfo>>();
-
IReadOnlyList<TimerInfo> timerList = null;
IReadOnlyList<SeriesTimerInfo> seriesTimerList = null;
@@ -1601,8 +1600,6 @@ namespace Emby.Server.Implementations.LiveTv
if (!string.IsNullOrEmpty(query.Id))
{
- var guid = new Guid(query.Id);
-
timers = timers
.Where(i => string.Equals(_tvDtoService.GetInternalTimerId(i.Item1.Id), query.Id, StringComparison.OrdinalIgnoreCase));
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index 3699b988c6..9702392b29 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -424,14 +424,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
return false;
}
- var nameTag = buf[offset++];
+ offset++; // Name Tag
var nameLength = buf[offset++];
// skip the name field to get to value for return
offset += nameLength;
- var valueTag = buf[offset++];
+ offset++; // Value Tag
var valueLength = buf[offset++];
diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json
index b01abafa13..cc8b7dbd53 100644
--- a/Emby.Server.Implementations/Localization/Core/da.json
+++ b/Emby.Server.Implementations/Localization/Core/da.json
@@ -23,7 +23,7 @@
"HeaderFavoriteEpisodes": "Favoritepisoder",
"HeaderFavoriteShows": "Favoritserier",
"HeaderFavoriteSongs": "Favoritsange",
- "HeaderLiveTV": "Live TV",
+ "HeaderLiveTV": "Live-TV",
"HeaderNextUp": "Næste",
"HeaderRecordingGroups": "Optagelsesgrupper",
"HomeVideos": "Hjemmevideoer",
@@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Lydafspilning påbegyndt",
"NotificationOptionAudioPlaybackStopped": "Lydafspilning stoppet",
"NotificationOptionCameraImageUploaded": "Kamerabillede uploadet",
- "NotificationOptionInstallationFailed": "Installationsfejl",
+ "NotificationOptionInstallationFailed": "Installationen fejlede",
"NotificationOptionNewLibraryContent": "Nyt indhold tilføjet",
"NotificationOptionPluginError": "Pluginfejl",
"NotificationOptionPluginInstalled": "Plugin installeret",
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index c787949676..ee7479c1c1 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -3,7 +3,7 @@
"AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}",
"Application": "Aplicación",
"Artists": "Artistas",
- "AuthenticationSucceededWithUserName": "{0} autenticado correctamente",
+ "AuthenticationSucceededWithUserName": "{0} identificado correctamente",
"Books": "Libros",
"CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}",
"Channels": "Canales",
@@ -16,7 +16,7 @@
"Folders": "Carpetas",
"Genres": "Géneros",
"HeaderAlbumArtists": "Artistas del álbum",
- "HeaderCameraUploads": "Subidas desde cámara",
+ "HeaderCameraUploads": "Subidas desde la cámara",
"HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
@@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Se inició la reproducción de audio",
"NotificationOptionAudioPlaybackStopped": "Se detuvo la reproducción de audio",
"NotificationOptionCameraImageUploaded": "Imagen de la cámara cargada",
- "NotificationOptionInstallationFailed": "Error de instalación",
+ "NotificationOptionInstallationFailed": "Error en la instalación",
"NotificationOptionNewLibraryContent": "Nuevo contenido añadido",
"NotificationOptionPluginError": "Error en plugin",
"NotificationOptionPluginInstalled": "Plugin instalado",
@@ -85,7 +85,7 @@
"UserDeletedWithName": "El usuario {0} ha sido borrado",
"UserDownloadingItemWithValues": "{0} está descargando {1}",
"UserLockedOutWithName": "El usuario {0} ha sido bloqueado",
- "UserOfflineFromDevice": "{0} se ha desconectado de {1}",
+ "UserOfflineFromDevice": "{0} se ha desconectado desde {1}",
"UserOnlineFromDevice": "{0} está en línea desde {1}",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
"UserPolicyUpdatedWithName": "Actualizada política de usuario para {0}",
diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index c4ce16dc85..faa8499b88 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-BR.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json
@@ -1,7 +1,7 @@
{
"Albums": "Álbuns",
"AppDeviceValues": "App: {0}, Dispositivo: {1}",
- "Application": "Aplicativo",
+ "Application": "Inscrição",
"Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado com sucesso",
"Books": "Livros",
@@ -16,7 +16,7 @@
"Folders": "Pastas",
"Genres": "Gêneros",
"HeaderAlbumArtists": "Artistas do Álbum",
- "HeaderCameraUploads": "Uploads da Câmera",
+ "HeaderCameraUploads": "Envios da Câmera",
"HeaderContinueWatching": "Continuar Assistindo",
"HeaderFavoriteAlbums": "Álbuns Favoritos",
"HeaderFavoriteArtists": "Artistas Favoritos",
diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json
index 387604f03c..b12d391c17 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-PT.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json
@@ -1,11 +1,11 @@
{
"Albums": "Álbuns",
- "AppDeviceValues": "Aplicação {0}, Dispositivo:{1}",
+ "AppDeviceValues": "Aplicação {0}, Dispositivo: {1}",
"Application": "Aplicação",
"Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado com sucesso",
"Books": "Livros",
- "CameraImageUploadedFrom": "Uma nova imagem proveniente de uma câmara foi enviada a partir de {0}",
+ "CameraImageUploadedFrom": "Uma nova imagem de câmara foi enviada a partir de {0}",
"Channels": "Canais",
"ChapterNameValue": "Capítulo {0}",
"Collections": "Coleções",
@@ -16,7 +16,7 @@
"Folders": "Pastas",
"Genres": "Géneros",
"HeaderAlbumArtists": "Artistas do Álbum",
- "HeaderCameraUploads": "Camera Uploads",
+ "HeaderCameraUploads": "Envios a partir da câmara",
"HeaderContinueWatching": "Continuar a Ver",
"HeaderFavoriteAlbums": "Álbuns Favoritos",
"HeaderFavoriteArtists": "Artistas Favoritos",
@@ -27,7 +27,7 @@
"HeaderNextUp": "A Seguir",
"HeaderRecordingGroups": "Grupos de Gravação",
"HomeVideos": "Home videos",
- "Inherit": "Inherit",
+ "Inherit": "Herdar",
"ItemAddedWithName": "{0} foi adicionado à biblioteca",
"ItemRemovedWithName": "{0} foi removido da biblioteca",
"LabelIpAddressValue": "Endereço IP: {0}",
@@ -49,7 +49,7 @@
"NotificationOptionApplicationUpdateInstalled": "Atualização de aplicação instalada",
"NotificationOptionAudioPlayback": "Reprodução Iniciada",
"NotificationOptionAudioPlaybackStopped": "Reprodução Parada",
- "NotificationOptionCameraImageUploaded": "Camera image uploaded",
+ "NotificationOptionCameraImageUploaded": "Imagem da câmara enviada",
"NotificationOptionInstallationFailed": "Falha na instalação",
"NotificationOptionNewLibraryContent": "Novo conteúdo adicionado",
"NotificationOptionPluginError": "Falha na extensão",
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index c0465def8d..0ad4b37aa2 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -15,7 +15,7 @@
"Favorites": "Избранное",
"Folders": "Папки",
"Genres": "Жанры",
- "HeaderAlbumArtists": "Исп-ли альбома",
+ "HeaderAlbumArtists": "Исполнители альбома",
"HeaderCameraUploads": "Камеры",
"HeaderContinueWatching": "Продолжение просмотра",
"HeaderFavoriteAlbums": "Избранные альбомы",
diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json
index 9e00eba62f..3cc95e46ec 100644
--- a/Emby.Server.Implementations/Localization/Core/tr.json
+++ b/Emby.Server.Implementations/Localization/Core/tr.json
@@ -3,7 +3,7 @@
"AppDeviceValues": "Uygulama: {0}, Aygıt: {1}",
"Application": "Uygulama",
"Artists": "Sanatçılar",
- "AuthenticationSucceededWithUserName": "{0} başarı ile giriş yaptı",
+ "AuthenticationSucceededWithUserName": "{0} kimlik başarıyla doğrulandı",
"Books": "Kitaplar",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"Channels": "Kanallar",
@@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
- "NotificationOptionInstallationFailed": "Installation failure",
+ "NotificationOptionInstallationFailed": "Yükleme hatası",
"NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionPluginInstalled": "Plugin installed",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index 63aa6a5577..ba5e939828 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -3,7 +3,7 @@
"AppDeviceValues": "应用: {0}, 设备: {1}",
"Application": "应用程序",
"Artists": "艺术家",
- "AuthenticationSucceededWithUserName": "{0} 成功验证",
+ "AuthenticationSucceededWithUserName": "{0} 认证成功",
"Books": "书籍",
"CameraImageUploadedFrom": "已从 {0} 上传了一张新的相机图像",
"Channels": "频道",
@@ -12,15 +12,15 @@
"DeviceOfflineWithName": "{0} 已断开",
"DeviceOnlineWithName": "{0} 已连接",
"FailedLoginAttemptWithUserName": "来自 {0} 的失败登入",
- "Favorites": "最爱",
+ "Favorites": "我的最爱",
"Folders": "文件夹",
"Genres": "风格",
"HeaderAlbumArtists": "专辑作家",
"HeaderCameraUploads": "相机上传",
"HeaderContinueWatching": "继续观看",
"HeaderFavoriteAlbums": "最爱的专辑",
- "HeaderFavoriteArtists": "最爱作家",
- "HeaderFavoriteEpisodes": "最爱的集",
+ "HeaderFavoriteArtists": "最爱的艺术家",
+ "HeaderFavoriteEpisodes": "最爱的剧集",
"HeaderFavoriteShows": "最爱的节目",
"HeaderFavoriteSongs": "最爱的歌曲",
"HeaderLiveTV": "电视直播",
@@ -30,7 +30,7 @@
"Inherit": "继承",
"ItemAddedWithName": "{0} 已添加到媒体库",
"ItemRemovedWithName": "{0} 已从媒体库中移除",
- "LabelIpAddressValue": "Ip 地址:{0}",
+ "LabelIpAddressValue": "IP 地址:{0}",
"LabelRunningTimeValue": "运行时间:{0}",
"Latest": "最新",
"MessageApplicationUpdated": "Jellyfin 服务器已更新",
diff --git a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
index c27eb76865..23e22afd58 100644
--- a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
+++ b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
@@ -15,7 +15,7 @@ namespace Emby.Server.Implementations.Services
{
PropertySetFn = propertySetFn;
PropertyParseStringFn = propertyParseStringFn;
- PropertyType = PropertyType;
+ PropertyType = propertyType;
}
public Action<object, object> PropertySetFn { get; private set; }
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 2f84b91ecd..0c0c77cda1 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
@@ -9,7 +10,6 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
@@ -19,6 +19,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
using Microsoft.Extensions.Logging;
+using static MediaBrowser.Common.HexHelper;
namespace Emby.Server.Implementations.Updates
{
@@ -454,16 +455,20 @@ namespace Emby.Server.Implementations.Updates
{
cancellationToken.ThrowIfCancellationRequested();
- var hash = HexHelper.ToHexString(md5.ComputeHash(stream));
+ var hash = ToHexString(md5.ComputeHash(stream));
if (!string.Equals(package.checksum, hash, StringComparison.OrdinalIgnoreCase))
{
- _logger.LogDebug("{0}, {1}", package.checksum, hash);
- throw new InvalidDataException($"The checksums didn't match while installing {package.name}.");
+ _logger.LogError(
+ "The checksums didn't match while installing {Package}, expected: {Expected}, got: {Received}",
+ package.name,
+ package.checksum,
+ hash);
+ throw new InvalidDataException("The checksum of the received data doesn't match.");
}
if (Directory.Exists(targetDir))
{
- Directory.Delete(targetDir);
+ Directory.Delete(targetDir, true);
}
stream.Position = 0;