aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs6
-rw-r--r--Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs36
-rw-r--r--Emby.Server.Implementations/Data/SqliteExtensions.cs3
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs41
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserRepository.cs18
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj2
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs93
-rw-r--r--Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs41
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/ko.json90
-rw-r--r--Emby.Server.Implementations/Localization/Core/nb.json20
11 files changed, 169 insertions, 183 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index f36d465dd..294e4f580 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -773,12 +773,11 @@ namespace Emby.Server.Implementations
_displayPreferencesRepository = new SqliteDisplayPreferencesRepository(
LoggerFactory.CreateLogger<SqliteDisplayPreferencesRepository>(),
- JsonSerializer,
ApplicationPaths,
FileSystemManager);
serviceCollection.AddSingleton<IDisplayPreferencesRepository>(_displayPreferencesRepository);
- ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, JsonSerializer, LoggerFactory, LocalizationManager);
+ ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, LoggerFactory.CreateLogger<SqliteItemRepository>(), LocalizationManager);
serviceCollection.AddSingleton<IItemRepository>(ItemRepository);
AuthenticationRepository = GetAuthenticationRepository();
@@ -979,8 +978,7 @@ namespace Emby.Server.Implementations
{
var repo = new SqliteUserRepository(
LoggerFactory.CreateLogger<SqliteUserRepository>(),
- ApplicationPaths,
- JsonSerializer);
+ ApplicationPaths);
repo.Initialize();
diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
index 1a67ab912..2cd4d65b3 100644
--- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
@@ -2,44 +2,44 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using System.Text.Json;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Json;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
{
/// <summary>
- /// Class SQLiteDisplayPreferencesRepository
+ /// Class SQLiteDisplayPreferencesRepository.
/// </summary>
public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository
{
private readonly IFileSystem _fileSystem;
- public SqliteDisplayPreferencesRepository(ILogger<SqliteDisplayPreferencesRepository> logger, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IFileSystem fileSystem)
+ private readonly JsonSerializerOptions _jsonOptions;
+
+ public SqliteDisplayPreferencesRepository(ILogger<SqliteDisplayPreferencesRepository> logger, IApplicationPaths appPaths, IFileSystem fileSystem)
: base(logger)
{
- _jsonSerializer = jsonSerializer;
_fileSystem = fileSystem;
+
+ _jsonOptions = JsonDefaults.GetOptions();
+
DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db");
}
/// <summary>
- /// Gets the name of the repository
+ /// Gets the name of the repository.
/// </summary>
/// <value>The name.</value>
public string Name => "SQLite";
- /// <summary>
- /// The _json serializer
- /// </summary>
- private readonly IJsonSerializer _jsonSerializer;
-
public void Initialize()
{
try
@@ -106,7 +106,7 @@ namespace Emby.Server.Implementations.Data
private void SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, IDatabaseConnection connection)
{
- var serialized = _jsonSerializer.SerializeToBytes(displayPreferences);
+ var serialized = JsonSerializer.SerializeToUtf8Bytes(displayPreferences, _jsonOptions);
using (var statement = connection.PrepareStatement("replace into userdisplaypreferences (id, userid, client, data) values (@id, @userId, @client, @data)"))
{
@@ -198,15 +198,13 @@ namespace Emby.Server.Implementations.Data
var list = new List<DisplayPreferences>();
using (var connection = GetConnection(true))
+ using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId"))
{
- using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId"))
- {
- statement.TryBind("@userId", userId.ToGuidBlob());
+ statement.TryBind("@userId", userId.ToGuidBlob());
- foreach (var row in statement.ExecuteQuery())
- {
- list.Add(Get(row));
- }
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(Get(row));
}
}
@@ -214,7 +212,7 @@ namespace Emby.Server.Implementations.Data
}
private DisplayPreferences Get(IReadOnlyList<IResultSetValue> row)
- => _jsonSerializer.DeserializeFromString<DisplayPreferences>(row.GetString(0));
+ => JsonSerializer.Deserialize<DisplayPreferences>(row[0].ToBlob(), _jsonOptions);
public void SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client, CancellationToken cancellationToken)
=> SaveDisplayPreferences(displayPreferences, new Guid(userId), client, cancellationToken);
diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs
index 0fb2c10fd..e7c394b54 100644
--- a/Emby.Server.Implementations/Data/SqliteExtensions.cs
+++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs
@@ -34,8 +34,7 @@ namespace Emby.Server.Implementations.Data
public static Guid ReadGuidFromBlob(this IResultSetValue result)
{
- // TODO: Remove ToArray when upgrading to netstandard2.1
- return new Guid(result.ToBlob().ToArray());
+ return new Guid(result.ToBlob());
}
public static string ToDateTimeParamValue(this DateTime dateValue)
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 2f083dda4..31a661c5d 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -5,8 +5,11 @@ using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading;
using Emby.Server.Implementations.Playlists;
+using MediaBrowser.Common.Json;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
@@ -38,14 +41,6 @@ namespace Emby.Server.Implementations.Data
{
private const string ChaptersTableName = "Chapters2";
- private readonly TypeMapper _typeMapper;
-
- /// <summary>
- /// Gets the json serializer.
- /// </summary>
- /// <value>The json serializer.</value>
- private readonly IJsonSerializer _jsonSerializer;
-
/// <summary>
/// The _app paths
/// </summary>
@@ -53,33 +48,31 @@ namespace Emby.Server.Implementations.Data
private readonly IServerApplicationHost _appHost;
private readonly ILocalizationManager _localization;
+ private readonly TypeMapper _typeMapper;
+ private readonly JsonSerializerOptions _jsonOptions;
+
/// <summary>
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
/// </summary>
public SqliteItemRepository(
IServerConfigurationManager config,
IServerApplicationHost appHost,
- IJsonSerializer jsonSerializer,
- ILoggerFactory loggerFactory,
+ ILogger<SqliteItemRepository> logger,
ILocalizationManager localization)
- : base(loggerFactory.CreateLogger(nameof(SqliteItemRepository)))
+ : base(logger)
{
if (config == null)
{
throw new ArgumentNullException(nameof(config));
}
- if (jsonSerializer == null)
- {
- throw new ArgumentNullException(nameof(jsonSerializer));
- }
-
- _appHost = appHost;
_config = config;
- _jsonSerializer = jsonSerializer;
- _typeMapper = new TypeMapper();
+ _appHost = appHost;
_localization = localization;
+ _typeMapper = new TypeMapper();
+ _jsonOptions = JsonDefaults.GetOptions();
+
DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db");
}
@@ -671,7 +664,7 @@ namespace Emby.Server.Implementations.Data
if (TypeRequiresDeserialization(item.GetType()))
{
- saveItemStatement.TryBind("@data", _jsonSerializer.SerializeToBytes(item));
+ saveItemStatement.TryBind("@data", JsonSerializer.SerializeToUtf8Bytes(item, _jsonOptions));
}
else
{
@@ -1302,11 +1295,11 @@ namespace Emby.Server.Implementations.Data
{
try
{
- item = _jsonSerializer.DeserializeFromString(reader.GetString(1), type) as BaseItem;
+ item = JsonSerializer.Deserialize(reader[1].ToBlob(), type, _jsonOptions) as BaseItem;
}
- catch (SerializationException ex)
+ catch (JsonException ex)
{
- Logger.LogError(ex, "Error deserializing item");
+ Logger.LogError(ex, "Error deserializing item with JSON: {Data}", reader.GetString(1));
}
}
@@ -2724,7 +2717,7 @@ namespace Emby.Server.Implementations.Data
if (elapsed >= SlowThreshold)
{
- Logger.LogWarning(
+ Logger.LogDebug(
"{Method} query time (slow): {ElapsedMs}ms. Query: {Query}",
methodName,
elapsed,
diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
index 11629b389..80fe278f8 100644
--- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
@@ -1,10 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Text.Json;
+using MediaBrowser.Common.Json;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
using SQLitePCL.pretty;
@@ -15,15 +16,14 @@ namespace Emby.Server.Implementations.Data
/// </summary>
public class SqliteUserRepository : BaseSqliteRepository, IUserRepository
{
- private readonly IJsonSerializer _jsonSerializer;
+ private readonly JsonSerializerOptions _jsonOptions;
public SqliteUserRepository(
ILogger<SqliteUserRepository> logger,
- IServerApplicationPaths appPaths,
- IJsonSerializer jsonSerializer)
+ IServerApplicationPaths appPaths)
: base(logger)
{
- _jsonSerializer = jsonSerializer;
+ _jsonOptions = JsonDefaults.GetOptions();;
DbFilePath = Path.Combine(appPaths.DataPath, "users.db");
}
@@ -84,7 +84,7 @@ namespace Emby.Server.Implementations.Data
}
user.Password = null;
- var serialized = _jsonSerializer.SerializeToBytes(user);
+ var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
connection.RunInTransaction(db =>
{
@@ -108,7 +108,7 @@ namespace Emby.Server.Implementations.Data
throw new ArgumentNullException(nameof(user));
}
- var serialized = _jsonSerializer.SerializeToBytes(user);
+ var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
using (var connection = GetConnection())
{
@@ -142,7 +142,7 @@ namespace Emby.Server.Implementations.Data
throw new ArgumentNullException(nameof(user));
}
- var serialized = _jsonSerializer.SerializeToBytes(user);
+ var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
using (var connection = GetConnection())
{
@@ -179,7 +179,7 @@ namespace Emby.Server.Implementations.Data
var id = row[0].ToInt64();
var guid = row[1].ReadGuidFromBlob();
- var user = _jsonSerializer.DeserializeFromString<User>(row.GetString(2));
+ var user = JsonSerializer.Deserialize<User>(row[2].ToBlob(), _jsonOptions);
user.InternalId = id;
user.Id = guid;
return user;
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 2c71f0457..ea4444268 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -42,7 +42,7 @@
</ItemGroup>
<PropertyGroup>
- <TargetFramework>netstandard2.0</TargetFramework>
+ <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index d60f5c055..e4f98acb9 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -39,6 +39,7 @@ namespace Emby.Server.Implementations.HttpServer
private readonly IHttpListener _socketListener;
private readonly Func<Type, Func<string, object>> _funcParseFn;
private readonly string _defaultRedirectPath;
+ private readonly string _baseUrlPrefix;
private readonly Dictionary<Type, Type> ServiceOperationsMap = new Dictionary<Type, Type>();
private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
@@ -58,6 +59,7 @@ namespace Emby.Server.Implementations.HttpServer
_logger = logger;
_config = config;
_defaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
+ _baseUrlPrefix = _config.Configuration.BaseUrl;
_networkManager = networkManager;
_jsonSerializer = jsonSerializer;
_xmlSerializer = xmlSerializer;
@@ -87,6 +89,20 @@ namespace Emby.Server.Implementations.HttpServer
return _appHost.CreateInstance(type);
}
+ private static string NormalizeUrlPath(string path)
+ {
+ if (path.StartsWith("/"))
+ {
+ // If the path begins with a leading slash, just return it as-is
+ return path;
+ }
+ else
+ {
+ // If the path does not begin with a leading slash, append one for consistency
+ return "/" + path;
+ }
+ }
+
/// <summary>
/// Applies the request filters. Returns whether or not the request has been handled
/// and no more processing should be done.
@@ -208,7 +224,7 @@ namespace Emby.Server.Implementations.HttpServer
}
}
- private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace, bool logExceptionMessage)
+ private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace)
{
try
{
@@ -218,9 +234,9 @@ namespace Emby.Server.Implementations.HttpServer
{
_logger.LogError(ex, "Error processing request");
}
- else if (logExceptionMessage)
+ else
{
- _logger.LogError(ex.Message);
+ _logger.LogError("Error processing request: {Message}", ex.Message);
}
var httpRes = httpReq.Response;
@@ -233,8 +249,10 @@ namespace Emby.Server.Implementations.HttpServer
var statusCode = GetStatusCode(ex);
httpRes.StatusCode = statusCode;
- httpRes.ContentType = "text/html";
- await httpRes.WriteAsync(NormalizeExceptionMessage(ex.Message)).ConfigureAwait(false);
+ var errContent = NormalizeExceptionMessage(ex.Message);
+ httpRes.ContentType = "text/plain";
+ httpRes.ContentLength = errContent.Length;
+ await httpRes.WriteAsync(errContent).ConfigureAwait(false);
}
catch (Exception errorEx)
{
@@ -471,22 +489,15 @@ namespace Emby.Server.Implementations.HttpServer
urlToLog = GetUrlToLog(urlString);
- if (string.Equals(localPath, "/" + _config.Configuration.BaseUrl + "/", StringComparison.OrdinalIgnoreCase)
- || string.Equals(localPath, "/" + _config.Configuration.BaseUrl, StringComparison.OrdinalIgnoreCase))
- {
- httpRes.Redirect("/" + _config.Configuration.BaseUrl + "/" + _defaultRedirectPath);
- return;
- }
-
- if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(localPath, _baseUrlPrefix + "/", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(localPath, _baseUrlPrefix, StringComparison.OrdinalIgnoreCase)
+ || string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)
+ || string.IsNullOrEmpty(localPath)
+ || !localPath.StartsWith(_baseUrlPrefix))
{
- httpRes.Redirect(_defaultRedirectPath);
- return;
- }
-
- if (string.IsNullOrEmpty(localPath))
- {
- httpRes.Redirect("/" + _defaultRedirectPath);
+ // Always redirect back to the default path if the base prefix is invalid or missing
+ _logger.LogDebug("Normalizing a URL at {0}", localPath);
+ httpRes.Redirect(_baseUrlPrefix + "/" + _defaultRedirectPath);
return;
}
@@ -509,22 +520,22 @@ namespace Emby.Server.Implementations.HttpServer
}
else
{
- await ErrorHandler(new FileNotFoundException(), httpReq, false, false).ConfigureAwait(false);
+ await ErrorHandler(new FileNotFoundException(), httpReq, false).ConfigureAwait(false);
}
}
catch (Exception ex) when (ex is SocketException || ex is IOException || ex is OperationCanceledException)
{
- await ErrorHandler(ex, httpReq, false, false).ConfigureAwait(false);
+ await ErrorHandler(ex, httpReq, false).ConfigureAwait(false);
}
catch (SecurityException ex)
{
- await ErrorHandler(ex, httpReq, false, true).ConfigureAwait(false);
+ await ErrorHandler(ex, httpReq, false).ConfigureAwait(false);
}
catch (Exception ex)
{
var logException = !string.Equals(ex.GetType().Name, "SocketException", StringComparison.OrdinalIgnoreCase);
- await ErrorHandler(ex, httpReq, logException, false).ConfigureAwait(false);
+ await ErrorHandler(ex, httpReq, logException).ConfigureAwait(false);
}
finally
{
@@ -596,7 +607,7 @@ namespace Emby.Server.Implementations.HttpServer
foreach (var route in clone)
{
- routes.Add(new RouteAttribute(NormalizeCustomRoutePath(_config.Configuration.BaseUrl, route.Path), route.Verbs)
+ routes.Add(new RouteAttribute(NormalizeCustomRoutePath(route.Path), route.Verbs)
{
Notes = route.Notes,
Priority = route.Priority,
@@ -651,36 +662,22 @@ namespace Emby.Server.Implementations.HttpServer
return _socketListener.ProcessWebSocketRequest(context);
}
- // this method was left for compatibility with third party clients
- private static string NormalizeEmbyRoutePath(string path)
+ private string NormalizeEmbyRoutePath(string path)
{
- if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
- {
- return "/emby" + path;
- }
-
- return "emby/" + path;
+ _logger.LogDebug("Normalizing /emby route");
+ return _baseUrlPrefix + "/emby" + NormalizeUrlPath(path);
}
- // this method was left for compatibility with third party clients
- private static string NormalizeMediaBrowserRoutePath(string path)
+ private string NormalizeMediaBrowserRoutePath(string path)
{
- if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
- {
- return "/mediabrowser" + path;
- }
-
- return "mediabrowser/" + path;
+ _logger.LogDebug("Normalizing /mediabrowser route");
+ return _baseUrlPrefix + "/mediabrowser" + NormalizeUrlPath(path);
}
- private static string NormalizeCustomRoutePath(string baseUrl, string path)
+ private string NormalizeCustomRoutePath(string path)
{
- if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
- {
- return "/" + baseUrl + path;
- }
-
- return baseUrl + "/" + path;
+ _logger.LogDebug("Normalizing custom route {0}", path);
+ return _baseUrlPrefix + NormalizeUrlPath(path);
}
/// <inheritdoc />
diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
index d8faceadb..6afcf567a 100644
--- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
@@ -21,14 +21,6 @@ namespace Emby.Server.Implementations.Images
public abstract class BaseDynamicImageProvider<T> : IHasItemChangeMonitor, IForcedProvider, ICustomMetadataProvider<T>, IHasOrder
where T : BaseItem
{
- protected virtual IReadOnlyCollection<ImageType> SupportedImages { get; }
- = new ImageType[] { ImageType.Primary };
-
- protected IFileSystem FileSystem { get; private set; }
- protected IProviderManager ProviderManager { get; private set; }
- protected IApplicationPaths ApplicationPaths { get; private set; }
- protected IImageProcessor ImageProcessor { get; set; }
-
protected BaseDynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor)
{
ApplicationPaths = applicationPaths;
@@ -37,6 +29,24 @@ namespace Emby.Server.Implementations.Images
ImageProcessor = imageProcessor;
}
+ protected IFileSystem FileSystem { get; }
+
+ protected IProviderManager ProviderManager { get; }
+
+ protected IApplicationPaths ApplicationPaths { get; }
+
+ protected IImageProcessor ImageProcessor { get; set; }
+
+ protected virtual IReadOnlyCollection<ImageType> SupportedImages { get; }
+ = new ImageType[] { ImageType.Primary };
+
+ /// <inheritdoc />
+ public string Name => "Dynamic Image Provider";
+
+ protected virtual int MaxImageAgeDays => 7;
+
+ public int Order => 0;
+
protected virtual bool Supports(BaseItem _) => true;
public async Task<ItemUpdateType> FetchAsync(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
@@ -85,7 +95,8 @@ namespace Emby.Server.Implementations.Images
return FetchToFileInternal(item, items, imageType, cancellationToken);
}
- protected async Task<ItemUpdateType> FetchToFileInternal(BaseItem item,
+ protected async Task<ItemUpdateType> FetchToFileInternal(
+ BaseItem item,
IReadOnlyList<BaseItem> itemsWithImages,
ImageType imageType,
CancellationToken cancellationToken)
@@ -181,8 +192,6 @@ namespace Emby.Server.Implementations.Images
return outputPath;
}
- public string Name => "Dynamic Image Provider";
-
protected virtual string CreateImage(BaseItem item,
IReadOnlyCollection<BaseItem> itemsWithImages,
string outputPathWithoutExtension,
@@ -214,8 +223,6 @@ namespace Emby.Server.Implementations.Images
throw new ArgumentException("Unexpected image type", nameof(imageType));
}
- protected virtual int MaxImageAgeDays => 7;
-
public bool HasChanged(BaseItem item, IDirectoryService directoryServicee)
{
if (!Supports(item))
@@ -263,15 +270,9 @@ namespace Emby.Server.Implementations.Images
protected virtual bool HasChangedByDate(BaseItem item, ItemImageInfo image)
{
var age = DateTime.UtcNow - image.DateModified;
- if (age.TotalDays <= MaxImageAgeDays)
- {
- return false;
- }
- return true;
+ return age.TotalDays > MaxImageAgeDays;
}
- public int Order => 0;
-
protected string CreateSingleImage(IEnumerable<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType)
{
var image = itemsWithImages
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index ee7479c1c..2dcc2c1c8 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -24,7 +24,7 @@
"HeaderFavoriteShows": "Series favoritas",
"HeaderFavoriteSongs": "Canciones favoritas",
"HeaderLiveTV": "Televisión en directo",
- "HeaderNextUp": "Siguiendo",
+ "HeaderNextUp": "Siguiente",
"HeaderRecordingGroups": "Grupos de grabación",
"HomeVideos": "Vídeos caseros",
"Inherit": "Heredar",
diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json
index 9bf4d2797..463db89bc 100644
--- a/Emby.Server.Implementations/Localization/Core/ko.json
+++ b/Emby.Server.Implementations/Localization/Core/ko.json
@@ -1,82 +1,82 @@
{
"Albums": "앨범",
- "AppDeviceValues": "앱: {0}, 디바이스: {1}",
+ "AppDeviceValues": "앱: {0}, 장치: {1}",
"Application": "애플리케이션",
"Artists": "아티스트",
- "AuthenticationSucceededWithUserName": "{0} 인증에 성공했습니다.",
+ "AuthenticationSucceededWithUserName": "{0} 인증이 완료되었습니다",
"Books": "도서",
- "CameraImageUploadedFrom": "새로운 카메라 이미지가 {0}에서 업로드되었습니다.",
+ "CameraImageUploadedFrom": "{0}에서 새로운 카메라 이미지가 업로드되었습니다",
"Channels": "채널",
"ChapterNameValue": "챕터 {0}",
"Collections": "컬렉션",
- "DeviceOfflineWithName": "{0}가 접속이 끊어졌습니다.",
- "DeviceOnlineWithName": "{0}가 접속되었습니다.",
- "FailedLoginAttemptWithUserName": "{0}에서 로그인이 실패했습니다.",
+ "DeviceOfflineWithName": "{0} 연결 끊김",
+ "DeviceOnlineWithName": "{0} 연결됨",
+ "FailedLoginAttemptWithUserName": "{0} 로그인 실패",
"Favorites": "즐겨찾기",
"Folders": "폴더",
"Genres": "장르",
"HeaderAlbumArtists": "앨범 아티스트",
"HeaderCameraUploads": "카메라 업로드",
"HeaderContinueWatching": "계속 시청하기",
- "HeaderFavoriteAlbums": "좋아하는 앨범",
- "HeaderFavoriteArtists": "좋아하는 아티스트",
- "HeaderFavoriteEpisodes": "좋아하는 에피소드",
+ "HeaderFavoriteAlbums": "즐겨찾는 앨범",
+ "HeaderFavoriteArtists": "즐겨찾는 아티스트",
+ "HeaderFavoriteEpisodes": "즐겨찾는 에피소드",
"HeaderFavoriteShows": "즐겨찾는 쇼",
- "HeaderFavoriteSongs": "좋아하는 노래",
- "HeaderLiveTV": "TV 방송",
+ "HeaderFavoriteSongs": "즐겨찾는 노래",
+ "HeaderLiveTV": "실시간 TV",
"HeaderNextUp": "다음으로",
"HeaderRecordingGroups": "녹화 그룹",
"HomeVideos": "홈 비디오",
"Inherit": "상속",
- "ItemAddedWithName": "{0} 라이브러리에 추가됨",
- "ItemRemovedWithName": "{0} 라이브러리에서 제거됨",
+ "ItemAddedWithName": "{0}가 라이브러리에 추가됨",
+ "ItemRemovedWithName": "{0}가 라이브러리에서 제거됨",
"LabelIpAddressValue": "IP 주소: {0}",
"LabelRunningTimeValue": "상영 시간: {0}",
"Latest": "최근",
- "MessageApplicationUpdated": "Jellyfin 서버 업데이트됨",
- "MessageApplicationUpdatedTo": "Jellyfin 서버가 {0}로 업데이트됨",
- "MessageNamedServerConfigurationUpdatedWithValue": "서버 환경 설정 {0} 섹션 업데이트 됨",
- "MessageServerConfigurationUpdated": "서버 환경 설정 업데이드됨",
+ "MessageApplicationUpdated": "Jellyfin 서버가 업데이트되었습니다",
+ "MessageApplicationUpdatedTo": "Jellyfin 서버가 {0}로 업데이트되었습니다",
+ "MessageNamedServerConfigurationUpdatedWithValue": "서버 환경 설정 {0} 섹션이 업데이트되었습니다",
+ "MessageServerConfigurationUpdated": "서버 환경 설정이 업데이트되었습니다",
"MixedContent": "혼합 콘텐츠",
"Movies": "영화",
"Music": "음악",
- "MusicVideos": "뮤직 비디오",
- "NameInstallFailed": "{0} 설치 실패.",
+ "MusicVideos": "뮤직비디오",
+ "NameInstallFailed": "{0} 설치 실패",
"NameSeasonNumber": "시즌 {0}",
"NameSeasonUnknown": "알 수 없는 시즌",
- "NewVersionIsAvailable": "새 버전의 Jellyfin 서버를 사용할 수 있습니다.",
- "NotificationOptionApplicationUpdateAvailable": "애플리케이션 업데이트 사용 가능",
- "NotificationOptionApplicationUpdateInstalled": "애플리케이션 업데이트가 설치됨",
- "NotificationOptionAudioPlayback": "오디오 재생을 시작함",
- "NotificationOptionAudioPlaybackStopped": "오디오 재생이 중지됨",
+ "NewVersionIsAvailable": "새로운 버전의 Jellyfin 서버를 사용할 수 있습니다.",
+ "NotificationOptionApplicationUpdateAvailable": "애플리케이션 업데이트 가능",
+ "NotificationOptionApplicationUpdateInstalled": "애플리케이션 업데이트 완료",
+ "NotificationOptionAudioPlayback": "오디오 재생 시작됨",
+ "NotificationOptionAudioPlaybackStopped": "오디오 재생 중지됨",
"NotificationOptionCameraImageUploaded": "카메라 이미지가 업로드됨",
"NotificationOptionInstallationFailed": "설치 실패",
- "NotificationOptionNewLibraryContent": "새 콘텐트가 추가됨",
+ "NotificationOptionNewLibraryContent": "새 콘텐츠가 추가됨",
"NotificationOptionPluginError": "플러그인 실패",
- "NotificationOptionPluginInstalled": "플러그인이 설치됨",
- "NotificationOptionPluginUninstalled": "플러그인이 설치 제거됨",
- "NotificationOptionPluginUpdateInstalled": "플러그인 업데이트가 설치됨",
- "NotificationOptionServerRestartRequired": "서버를 다시 시작하십시오",
+ "NotificationOptionPluginInstalled": "플러그인 설치됨",
+ "NotificationOptionPluginUninstalled": "플러그인 제거됨",
+ "NotificationOptionPluginUpdateInstalled": "플러그인 업데이트 완료",
+ "NotificationOptionServerRestartRequired": "서버 재시작 필요",
"NotificationOptionTaskFailed": "예약 작업 실패",
- "NotificationOptionUserLockedOut": "사용자가 잠겼습니다",
- "NotificationOptionVideoPlayback": "비디오 재생을 시작함",
- "NotificationOptionVideoPlaybackStopped": "비디오 재생이 중지됨",
+ "NotificationOptionUserLockedOut": "잠긴 사용자",
+ "NotificationOptionVideoPlayback": "비디오 재생 시작됨",
+ "NotificationOptionVideoPlaybackStopped": "비디오 재생 중지됨",
"Photos": "사진",
"Playlists": "재생목록",
"Plugin": "플러그인",
"PluginInstalledWithName": "{0} 설치됨",
- "PluginUninstalledWithName": "{0} 설치 제거됨",
+ "PluginUninstalledWithName": "{0} 제거됨",
"PluginUpdatedWithName": "{0} 업데이트됨",
"ProviderValue": "제공자: {0}",
"ScheduledTaskFailedWithName": "{0} 실패",
"ScheduledTaskStartedWithName": "{0} 시작",
- "ServerNameNeedsToBeRestarted": "{0} 를 재시작하십시오",
- "Shows": "프로그램",
+ "ServerNameNeedsToBeRestarted": "{0}를 재시작해야합니다",
+ "Shows": "쇼",
"Songs": "노래",
- "StartupEmbyServerIsLoading": "Jellyfin 서버를 불러오고 있습니다. 잠시후 다시시도 해주세요.",
+ "StartupEmbyServerIsLoading": "Jellyfin 서버를 불러오고 있습니다. 잠시 후에 다시 시도하십시오.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "{0}에서 {1} 자막 다운로드에 실패했습니다",
- "SubtitlesDownloadedForItem": "{0} 자막을 다운로드했습니다",
+ "SubtitlesDownloadedForItem": "{0} 자막 다운로드 완료",
"Sync": "동기화",
"System": "시스템",
"TvShows": "TV 쇼",
@@ -85,13 +85,13 @@
"UserDeletedWithName": "사용자 {0} 삭제됨",
"UserDownloadingItemWithValues": "{0}이(가) {1}을 다운로드 중입니다",
"UserLockedOutWithName": "유저 {0} 은(는) 잠금처리 되었습니다",
- "UserOfflineFromDevice": "{0} has disconnected from {1}",
- "UserOnlineFromDevice": "{0} is online from {1}",
- "UserPasswordChangedWithName": "Password has been changed for user {0}",
- "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
- "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
- "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
- "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "UserOfflineFromDevice": "{1}로부터 {0}의 연결이 끊겼습니다",
+ "UserOnlineFromDevice": "{0}은 {1}에서 온라인 상태입니다",
+ "UserPasswordChangedWithName": "사용자 {0}의 비밀번호가 변경되었습니다",
+ "UserPolicyUpdatedWithName": "{0}의 사용자 정책이 업데이트되었습니다",
+ "UserStartedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생 중",
+ "UserStoppedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생을 마침",
+ "ValueHasBeenAddedToLibrary": "{0}가 미디어 라이브러리에 추가되었습니다",
"ValueSpecialEpisodeName": "Special - {0}",
- "VersionNumber": "Version {0}"
+ "VersionNumber": "버전 {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json
index daa3f5880..d4d791cb7 100644
--- a/Emby.Server.Implementations/Localization/Core/nb.json
+++ b/Emby.Server.Implementations/Localization/Core/nb.json
@@ -14,16 +14,16 @@
"FailedLoginAttemptWithUserName": "Mislykket påloggingsforsøk fra {0}",
"Favorites": "Favoritter",
"Folders": "Mapper",
- "Genres": "Sjanger",
- "HeaderAlbumArtists": "Albumartist",
+ "Genres": "Sjangre",
+ "HeaderAlbumArtists": "Albumartister",
"HeaderCameraUploads": "Camera Uploads",
- "HeaderContinueWatching": "Forsett og see på",
- "HeaderFavoriteAlbums": "Favoritt albumer",
+ "HeaderContinueWatching": "Forsett å se på",
+ "HeaderFavoriteAlbums": "Favorittalbum",
"HeaderFavoriteArtists": "Favorittartister",
- "HeaderFavoriteEpisodes": "Favoritt episode",
+ "HeaderFavoriteEpisodes": "Favorittepisoder",
"HeaderFavoriteShows": "Favorittserier",
"HeaderFavoriteSongs": "Favorittsanger",
- "HeaderLiveTV": "Direkte TV",
+ "HeaderLiveTV": "Direkte-TV",
"HeaderNextUp": "Neste",
"HeaderRecordingGroups": "Opptak Grupper",
"HomeVideos": "Hjemmelaget filmer",
@@ -61,8 +61,8 @@
"NotificationOptionUserLockedOut": "Bruker er utestengt",
"NotificationOptionVideoPlayback": "Video tilbakespilling startet",
"NotificationOptionVideoPlaybackStopped": "Video avspilling stoppet",
- "Photos": "BIlder",
- "Playlists": "Spilleliste",
+ "Photos": "Bilder",
+ "Playlists": "Spillelister",
"Plugin": "Plugin",
"PluginInstalledWithName": "{0} ble installert",
"PluginUninstalledWithName": "{0} ble avinstallert",
@@ -77,7 +77,7 @@
"SubtitleDownloadFailureForItem": "En feil oppstå under nedlasting av undertekster for {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitlesDownloadedForItem": "Undertekster lastet ned for {0}",
- "Sync": "Synk",
+ "Sync": "Synkroniser",
"System": "System",
"TvShows": "TV Shows",
"User": "Bruker",
@@ -92,6 +92,6 @@
"UserStartedPlayingItemWithValues": "{0} har startet avspilling {1}",
"UserStoppedPlayingItemWithValues": "{0} har stoppet avspilling {1}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
- "ValueSpecialEpisodeName": "Spesial - {0}",
+ "ValueSpecialEpisodeName": "Spesialepisode - {0}",
"VersionNumber": "Versjon {0}"
}