Merge branch 'master' into update_tvdb

This commit is contained in:
Vasily
2019-02-20 14:46:07 +03:00
committed by GitHub
146 changed files with 1801 additions and 2393 deletions

View File

@@ -72,12 +72,6 @@ namespace Emby.Server.Implementations.AppBase
/// <value>The plugin configurations path.</value>
public string PluginConfigurationsPath => Path.Combine(PluginsPath, "configurations");
/// <summary>
/// Gets the path to where temporary update files will be stored
/// </summary>
/// <value>The plugin configurations path.</value>
public string TempUpdatePath => Path.Combine(ProgramDataPath, "updates");
/// <summary>
/// Gets the path to the log directory
/// </summary>

View File

@@ -79,7 +79,7 @@ namespace Emby.Server.Implementations.AppBase
get
{
// Lazy load
LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer, FileSystem));
LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer));
return _configuration;
}
protected set

View File

@@ -1,7 +1,6 @@
using System;
using System.IO;
using System.Linq;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
namespace Emby.Server.Implementations.AppBase
@@ -18,9 +17,8 @@ namespace Emby.Server.Implementations.AppBase
/// <param name="type">The type.</param>
/// <param name="path">The path.</param>
/// <param name="xmlSerializer">The XML serializer.</param>
/// <param name="fileSystem">The file system</param>
/// <returns>System.Object.</returns>
public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer)
{
object configuration;

View File

@@ -105,10 +105,10 @@ using MediaBrowser.Providers.Subtitles;
using MediaBrowser.Providers.TV.TheTVDB;
using MediaBrowser.WebDashboard.Api;
using MediaBrowser.XbmcMetadata.Providers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using ServiceStack;
using ServiceStack.Text.Jsv;
using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate;
namespace Emby.Server.Implementations
@@ -124,12 +124,6 @@ namespace Emby.Server.Implementations
/// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
public abstract bool CanSelfRestart { get; }
/// <summary>
/// Gets or sets a value indicating whether this instance can self update.
/// </summary>
/// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value>
public virtual bool CanSelfUpdate => false;
public virtual bool CanLaunchWebBrowser
{
get
@@ -319,6 +313,8 @@ namespace Emby.Server.Implementations
private IMediaSourceManager MediaSourceManager { get; set; }
private IPlaylistManager PlaylistManager { get; set; }
private readonly IConfiguration _configuration;
/// <summary>
/// Gets or sets the installation manager.
/// </summary>
@@ -357,8 +353,10 @@ namespace Emby.Server.Implementations
IFileSystem fileSystem,
IEnvironmentInfo environmentInfo,
IImageEncoder imageEncoder,
INetworkManager networkManager)
INetworkManager networkManager,
IConfiguration configuration)
{
_configuration = configuration;
// hack alert, until common can target .net core
BaseExtensions.CryptographyProvider = CryptographyProvider;
@@ -434,7 +432,7 @@ namespace Emby.Server.Implementations
{
if (_deviceId == null)
{
_deviceId = new DeviceId(ApplicationPaths, LoggerFactory, FileSystemManager);
_deviceId = new DeviceId(ApplicationPaths, LoggerFactory);
}
return _deviceId.Value;
@@ -472,7 +470,7 @@ namespace Emby.Server.Implementations
{
try
{
Logger.LogWarning("Creating instance of {Type}", type);
Logger.LogDebug("Creating instance of {Type}", type);
return ActivatorUtilities.CreateInstance(_serviceProvider, type);
}
catch (Exception ex)
@@ -668,10 +666,10 @@ namespace Emby.Server.Implementations
SocketFactory = new SocketFactory();
serviceCollection.AddSingleton(SocketFactory);
InstallationManager = new InstallationManager(LoggerFactory, this, ApplicationPaths, HttpClient, JsonSerializer, ServerConfigurationManager, FileSystemManager, CryptographyProvider, PackageRuntime);
InstallationManager = new InstallationManager(LoggerFactory, this, ApplicationPaths, HttpClient, JsonSerializer, ServerConfigurationManager, FileSystemManager, CryptographyProvider, ZipClient, PackageRuntime);
serviceCollection.AddSingleton(InstallationManager);
ZipClient = new ZipClient(FileSystemManager);
ZipClient = new ZipClient();
serviceCollection.AddSingleton(ZipClient);
HttpResultFactory = new HttpResultFactory(LoggerFactory, FileSystemManager, JsonSerializer, CreateBrotliCompressor());
@@ -709,7 +707,7 @@ namespace Emby.Server.Implementations
AuthenticationRepository = GetAuthenticationRepository();
serviceCollection.AddSingleton(AuthenticationRepository);
UserManager = new UserManager(LoggerFactory, ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager, CryptographyProvider);
UserManager = new UserManager(LoggerFactory, ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager);
serviceCollection.AddSingleton(UserManager);
LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager);
@@ -719,7 +717,7 @@ namespace Emby.Server.Implementations
var musicManager = new MusicManager(LibraryManager);
serviceCollection.AddSingleton<IMusicManager>(new MusicManager(LibraryManager));
LibraryMonitor = new LibraryMonitor(LoggerFactory, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager, EnvironmentInfo);
LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager, EnvironmentInfo);
serviceCollection.AddSingleton(LibraryMonitor);
serviceCollection.AddSingleton<ISearchEngine>(new SearchEngine(LoggerFactory, LibraryManager, UserManager));
@@ -730,11 +728,10 @@ namespace Emby.Server.Implementations
HttpServer = new HttpListenerHost(this,
LoggerFactory,
ServerConfigurationManager,
"web/index.html",
_configuration,
NetworkManager,
JsonSerializer,
XmlSerializer,
GetParseFn);
XmlSerializer);
HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
serviceCollection.AddSingleton(HttpServer);
@@ -748,7 +745,7 @@ namespace Emby.Server.Implementations
var encryptionManager = new EncryptionManager();
serviceCollection.AddSingleton<IEncryptionManager>(encryptionManager);
DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LoggerFactory, NetworkManager);
DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager);
serviceCollection.AddSingleton(DeviceManager);
MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder);
@@ -760,7 +757,7 @@ namespace Emby.Server.Implementations
ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, LoggerFactory, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer);
serviceCollection.AddSingleton(ProviderManager);
DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager, this, () => DeviceManager, () => MediaSourceManager, () => LiveTvManager);
DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager);
serviceCollection.AddSingleton(DtoService);
ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient, ProviderManager);
@@ -834,11 +831,6 @@ namespace Emby.Server.Implementations
return null;
}
private static Func<string, object> GetParseFn(Type propertyType)
{
return s => JsvReader.GetParseFn(propertyType)(s);
}
public virtual string PackageRuntime => "netcore";
public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths, EnvironmentInfo.EnvironmentInfo environmentInfo)
@@ -950,7 +942,7 @@ namespace Emby.Server.Implementations
protected virtual FFMpegInfo GetFFMpegInfo()
{
return new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, GetFfmpegInstallInfo())
return new FFMpegLoader(ApplicationPaths, FileSystemManager, GetFfmpegInstallInfo())
.GetFFMpegInfo(StartupOptions);
}
@@ -1461,7 +1453,6 @@ namespace Emby.Server.Implementations
OperatingSystem = EnvironmentInfo.OperatingSystem.ToString(),
OperatingSystemDisplayName = EnvironmentInfo.OperatingSystemName,
CanSelfRestart = CanSelfRestart,
CanSelfUpdate = CanSelfUpdate,
CanLaunchWebBrowser = CanLaunchWebBrowser,
WanAddress = wanAddress,
HasUpdateAvailable = HasUpdateAvailable,
@@ -1760,21 +1751,6 @@ namespace Emby.Server.Implementations
Plugins = list.ToArray();
}
/// <summary>
/// Updates the application.
/// </summary>
/// <param name="package">The package that contains the update</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
public async Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress<double> progress)
{
await InstallationManager.InstallPackage(package, false, progress, cancellationToken).ConfigureAwait(false);
HasUpdateAvailable = false;
OnApplicationUpdated(package);
}
/// <summary>
/// This returns localhost in the case of no external dns, and the hostname if the
/// dns is prefixed with a valid Uri prefix.

View File

@@ -14,11 +14,9 @@ namespace Emby.Server.Implementations.Archiving
/// </summary>
public class ZipClient : IZipClient
{
private readonly IFileSystem _fileSystem;
public ZipClient(IFileSystem fileSystem)
public ZipClient()
{
_fileSystem = fileSystem;
}
/// <summary>

View File

@@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Channels
{
class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly IChannelManager _channelManager;
private readonly IUserManager _userManager;

View File

@@ -10,14 +10,18 @@ using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Collections
{
public class CollectionImageProvider : BaseDynamicImageProvider<BoxSet>
{
public CollectionImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
public CollectionImageProvider(
IFileSystem fileSystem,
IProviderManager providerManager,
IApplicationPaths applicationPaths,
IImageProcessor imageProcessor)
: base(fileSystem, providerManager, applicationPaths, imageProcessor)
{
}

View File

@@ -342,14 +342,12 @@ namespace Emby.Server.Implementations.Collections
{
private readonly CollectionManager _collectionManager;
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
private ILogger _logger;
public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger)
public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, ILogger logger)
{
_collectionManager = (CollectionManager)collectionManager;
_config = config;
_fileSystem = fileSystem;
_logger = logger;
}

View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace Emby.Server.Implementations
{
public static class ConfigurationOptions
{
public static readonly Dictionary<string, string> Configuration = new Dictionary<string, string>
{
{"HttpListenerHost:DefaultRedirectPath", "web/index.html"}
};
}
}

View File

@@ -224,7 +224,7 @@ namespace Emby.Server.Implementations.Data
});
}
db.ExecuteAll(string.Join(";", queries.ToArray()));
db.ExecuteAll(string.Join(";", queries));
Logger.LogInformation("PRAGMA synchronous=" + db.Query("PRAGMA synchronous").SelectScalarString().First());
}
@@ -232,23 +232,6 @@ namespace Emby.Server.Implementations.Data
protected virtual int? CacheSize => null;
internal static void CheckOk(int rc)
{
string msg = "";
if (raw.SQLITE_OK != rc)
{
throw CreateException((ErrorCode)rc, msg);
}
}
internal static Exception CreateException(ErrorCode rc, string msg)
{
var exp = new Exception(msg);
return exp;
}
private bool _disposed;
protected void CheckDisposed()
{
@@ -375,13 +358,6 @@ namespace Emby.Server.Implementations.Data
}
}
public class DummyToken : IDisposable
{
public void Dispose()
{
}
}
public static IDisposable Read(this ReaderWriterLockSlim obj)
{
//if (BaseSqliteRepository.ThreadSafeMode > 0)
@@ -390,6 +366,7 @@ namespace Emby.Server.Implementations.Data
//}
return new WriteLockToken(obj);
}
public static IDisposable Write(this ReaderWriterLockSlim obj)
{
//if (BaseSqliteRepository.ThreadSafeMode > 0)

View File

@@ -1,11 +1,8 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Data
@@ -13,18 +10,12 @@ namespace Emby.Server.Implementations.Data
public class CleanDatabaseScheduledTask : ILibraryPostScanTask
{
private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo;
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IApplicationPaths _appPaths;
public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IFileSystem fileSystem, IApplicationPaths appPaths)
public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger logger)
{
_libraryManager = libraryManager;
_itemRepo = itemRepo;
_logger = logger;
_fileSystem = fileSystem;
_appPaths = appPaths;
}
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)

View File

@@ -536,7 +536,7 @@ namespace Emby.Server.Implementations.Data
throw new ArgumentNullException(nameof(item));
}
SaveItems(new List<BaseItem> { item }, cancellationToken);
SaveItems(new [] { item }, cancellationToken);
}
public void SaveImages(BaseItem item)
@@ -576,7 +576,7 @@ namespace Emby.Server.Implementations.Data
/// or
/// cancellationToken
/// </exception>
public void SaveItems(List<BaseItem> items, CancellationToken cancellationToken)
public void SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken)
{
if (items == null)
{
@@ -587,7 +587,7 @@ namespace Emby.Server.Implementations.Data
CheckDisposed();
var tuples = new List<Tuple<BaseItem, List<Guid>, BaseItem, string, List<string>>>();
var tuples = new List<(BaseItem, List<Guid>, BaseItem, string, List<string>)>();
foreach (var item in items)
{
var ancestorIds = item.SupportsAncestors ?
@@ -599,7 +599,7 @@ namespace Emby.Server.Implementations.Data
var userdataKey = item.GetUserDataKeys().FirstOrDefault();
var inheritedTags = item.GetInheritedTags();
tuples.Add(new Tuple<BaseItem, List<Guid>, BaseItem, string, List<string>>(item, ancestorIds, topParent, userdataKey, inheritedTags));
tuples.Add((item, ancestorIds, topParent, userdataKey, inheritedTags));
}
using (WriteLock.Write())
@@ -615,7 +615,7 @@ namespace Emby.Server.Implementations.Data
}
}
private void SaveItemsInTranscation(IDatabaseConnection db, List<Tuple<BaseItem, List<Guid>, BaseItem, string, List<string>>> tuples)
private void SaveItemsInTranscation(IDatabaseConnection db, IEnumerable<(BaseItem, List<Guid>, BaseItem, string, List<string>)> tuples)
{
var statements = PrepareAllSafe(db, new string[]
{
@@ -966,7 +966,7 @@ namespace Emby.Server.Implementations.Data
if (item.ExtraIds.Length > 0)
{
saveItemStatement.TryBind("@ExtraIds", string.Join("|", item.ExtraIds.ToArray()));
saveItemStatement.TryBind("@ExtraIds", string.Join("|", item.ExtraIds));
}
else
{
@@ -1183,9 +1183,9 @@ namespace Emby.Server.Implementations.Data
/// <exception cref="ArgumentException"></exception>
public BaseItem RetrieveItem(Guid id)
{
if (id.Equals(Guid.Empty))
if (id == Guid.Empty)
{
throw new ArgumentNullException(nameof(id));
throw new ArgumentException(nameof(id), "Guid can't be empty");
}
CheckDisposed();
@@ -2079,14 +2079,14 @@ namespace Emby.Server.Implementations.Data
return false;
}
var sortingFields = query.OrderBy.Select(i => i.Item1);
var sortingFields = new HashSet<string>(query.OrderBy.Select(i => i.Item1), StringComparer.OrdinalIgnoreCase);
return sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase)
|| sortingFields.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase)
|| sortingFields.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase)
|| sortingFields.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase)
|| sortingFields.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase)
|| sortingFields.Contains(ItemSortBy.SeriesDatePlayed, StringComparer.OrdinalIgnoreCase)
return sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked)
|| sortingFields.Contains(ItemSortBy.IsPlayed)
|| sortingFields.Contains(ItemSortBy.IsUnplayed)
|| sortingFields.Contains(ItemSortBy.PlayCount)
|| sortingFields.Contains(ItemSortBy.DatePlayed)
|| sortingFields.Contains(ItemSortBy.SeriesDatePlayed)
|| query.IsFavoriteOrLiked.HasValue
|| query.IsFavorite.HasValue
|| query.IsResumable.HasValue
@@ -2094,9 +2094,9 @@ namespace Emby.Server.Implementations.Data
|| query.IsLiked.HasValue;
}
private readonly List<ItemFields> allFields = Enum.GetNames(typeof(ItemFields))
private readonly ItemFields[] _allFields = Enum.GetNames(typeof(ItemFields))
.Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
.ToList();
.ToArray();
private string[] GetColumnNamesFromField(ItemFields field)
{
@@ -2151,18 +2151,26 @@ namespace Emby.Server.Implementations.Data
}
}
private static readonly HashSet<string> _programExcludeParentTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Series",
"Season",
"MusicAlbum",
"MusicArtist",
"PhotoAlbum"
};
private static readonly HashSet<string> _programTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Program",
"TvChannel",
"LiveTvProgram",
"LiveTvTvChannel"
};
private bool HasProgramAttributes(InternalItemsQuery query)
{
var excludeParentTypes = new string[]
{
"Series",
"Season",
"MusicAlbum",
"MusicArtist",
"PhotoAlbum"
};
if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
if (_programExcludeParentTypes.Contains(query.ParentType))
{
return false;
}
@@ -2172,29 +2180,18 @@ namespace Emby.Server.Implementations.Data
return true;
}
var types = new string[]
{
"Program",
"TvChannel",
"LiveTvProgram",
"LiveTvTvChannel"
};
return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
return query.IncludeItemTypes.Any(x => _programTypes.Contains(x));
}
private static readonly HashSet<string> _serviceTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"TvChannel",
"LiveTvTvChannel"
};
private bool HasServiceName(InternalItemsQuery query)
{
var excludeParentTypes = new string[]
{
"Series",
"Season",
"MusicAlbum",
"MusicArtist",
"PhotoAlbum"
};
if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
if (_programExcludeParentTypes.Contains(query.ParentType))
{
return false;
}
@@ -2204,27 +2201,18 @@ namespace Emby.Server.Implementations.Data
return true;
}
var types = new string[]
{
"TvChannel",
"LiveTvTvChannel"
};
return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
return query.IncludeItemTypes.Any(x => _serviceTypes.Contains(x));
}
private static readonly HashSet<string> _startDateTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Program",
"LiveTvProgram"
};
private bool HasStartDate(InternalItemsQuery query)
{
var excludeParentTypes = new string[]
{
"Series",
"Season",
"MusicAlbum",
"MusicArtist",
"PhotoAlbum"
};
if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
if (_programExcludeParentTypes.Contains(query.ParentType))
{
return false;
}
@@ -2234,13 +2222,7 @@ namespace Emby.Server.Implementations.Data
return true;
}
var types = new string[]
{
"Program",
"LiveTvProgram"
};
return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
return query.IncludeItemTypes.Any(x => _startDateTypes.Contains(x));
}
private bool HasEpisodeAttributes(InternalItemsQuery query)
@@ -2263,16 +2245,26 @@ namespace Emby.Server.Implementations.Data
return query.IncludeItemTypes.Contains("Trailer", StringComparer.OrdinalIgnoreCase);
}
private static readonly HashSet<string> _artistExcludeParentTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Series",
"Season",
"PhotoAlbum"
};
private static readonly HashSet<string> _artistsTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Audio",
"MusicAlbum",
"MusicVideo",
"AudioBook",
"AudioPodcast"
};
private bool HasArtistFields(InternalItemsQuery query)
{
var excludeParentTypes = new string[]
{
"Series",
"Season",
"PhotoAlbum"
};
if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
if (_artistExcludeParentTypes.Contains(query.ParentType))
{
return false;
}
@@ -2282,18 +2274,18 @@ namespace Emby.Server.Implementations.Data
return true;
}
var types = new string[]
{
"Audio",
"MusicAlbum",
"MusicVideo",
"AudioBook",
"AudioPodcast"
};
return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
return query.IncludeItemTypes.Any(x => _artistsTypes.Contains(x));
}
private static readonly HashSet<string> _seriesTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Audio",
"MusicAlbum",
"MusicVideo",
"AudioBook",
"AudioPodcast"
};
private bool HasSeriesFields(InternalItemsQuery query)
{
if (string.Equals(query.ParentType, "PhotoAlbum", StringComparison.OrdinalIgnoreCase))
@@ -2306,26 +2298,18 @@ namespace Emby.Server.Implementations.Data
return true;
}
var types = new string[]
{
"Book",
"AudioBook",
"Episode",
"Season"
};
return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
return query.IncludeItemTypes.Any(x => _seriesTypes.Contains(x));
}
private string[] GetFinalColumnsToSelect(InternalItemsQuery query, string[] startColumns)
private List<string> GetFinalColumnsToSelect(InternalItemsQuery query, IEnumerable<string> startColumns)
{
var list = startColumns.ToList();
foreach (var field in allFields)
foreach (var field in _allFields)
{
if (!HasField(query, field))
{
foreach (var fieldToRemove in GetColumnNamesFromField(field).ToList())
foreach (var fieldToRemove in GetColumnNamesFromField(field))
{
list.Remove(fieldToRemove);
}
@@ -2419,11 +2403,14 @@ namespace Emby.Server.Implementations.Data
list.Add(builder.ToString());
var excludeIds = query.ExcludeItemIds.ToList();
excludeIds.Add(item.Id);
excludeIds.AddRange(item.ExtraIds);
var oldLen = query.ExcludeItemIds.Length;
var newLen = oldLen + item.ExtraIds.Length + 1;
var excludeIds = new Guid[newLen];
query.ExcludeItemIds.CopyTo(excludeIds, 0);
excludeIds[oldLen] = item.Id;
item.ExtraIds.CopyTo(excludeIds, oldLen + 1);
query.ExcludeItemIds = excludeIds.ToArray();
query.ExcludeItemIds = excludeIds;
query.ExcludeProviderIds = item.ProviderIds;
}
@@ -2444,7 +2431,7 @@ namespace Emby.Server.Implementations.Data
list.Add(builder.ToString());
}
return list.ToArray();
return list;
}
private void BindSearchParams(InternalItemsQuery query, IStatement statement)
@@ -2723,18 +2710,17 @@ namespace Emby.Server.Implementations.Data
private void AddItem(List<BaseItem> items, BaseItem newItem)
{
var providerIds = newItem.ProviderIds.ToList();
for (var i = 0; i < items.Count; i++)
{
var item = items[i];
foreach (var providerId in providerIds)
foreach (var providerId in newItem.ProviderIds)
{
if (providerId.Key == MetadataProviders.TmdbCollection.ToString())
{
continue;
}
if (item.GetProviderId(providerId.Key) == providerId.Value)
{
if (newItem.SourceType == SourceType.Library)
@@ -2753,10 +2739,10 @@ namespace Emby.Server.Implementations.Data
{
var elapsed = (DateTime.UtcNow - startDate).TotalMilliseconds;
int slowThreshold = 1000;
int slowThreshold = 100;
#if DEBUG
slowThreshold = 250;
slowThreshold = 10;
#endif
if (elapsed >= slowThreshold)
@@ -2806,7 +2792,7 @@ namespace Emby.Server.Implementations.Data
var whereText = whereClauses.Count == 0 ?
string.Empty :
" where " + string.Join(" AND ", whereClauses.ToArray());
" where " + string.Join(" AND ", whereClauses);
commandText += whereText
+ GetGroupBy(query)
@@ -2930,25 +2916,31 @@ namespace Emby.Server.Implementations.Data
private string GetOrderByText(InternalItemsQuery query)
{
var orderBy = query.OrderBy.ToList();
var enableOrderInversion = false;
if (query.SimilarTo != null && orderBy.Count == 0)
if (string.IsNullOrEmpty(query.SearchTerm))
{
orderBy.Add(new ValueTuple<string, SortOrder>("SimilarityScore", SortOrder.Descending));
orderBy.Add(new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
int oldLen = query.OrderBy.Length;
if (query.SimilarTo != null && oldLen == 0)
{
var arr = new (string, SortOrder)[oldLen + 2];
query.OrderBy.CopyTo(arr, 0);
arr[oldLen] = ("SimilarityScore", SortOrder.Descending);
arr[oldLen + 1] = (ItemSortBy.Random, SortOrder.Ascending);
query.OrderBy = arr;
}
}
else
{
query.OrderBy = new []
{
("SearchScore", SortOrder.Descending),
(ItemSortBy.SortName, SortOrder.Ascending)
};
}
if (!string.IsNullOrEmpty(query.SearchTerm))
{
orderBy = new List<(string, SortOrder)>();
orderBy.Add(new ValueTuple<string, SortOrder>("SearchScore", SortOrder.Descending));
orderBy.Add(new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending));
}
var orderBy = query.OrderBy;
query.OrderBy = orderBy.ToArray();
if (orderBy.Count == 0)
if (orderBy.Length == 0)
{
return string.Empty;
}
@@ -2957,6 +2949,7 @@ namespace Emby.Server.Implementations.Data
{
var columnMap = MapOrderByField(i.Item1, query);
var columnAscending = i.Item2 == SortOrder.Ascending;
const bool enableOrderInversion = false;
if (columnMap.Item2 && enableOrderInversion)
{
columnAscending = !columnAscending;
@@ -2968,7 +2961,7 @@ namespace Emby.Server.Implementations.Data
}));
}
private ValueTuple<string, bool> MapOrderByField(string name, InternalItemsQuery query)
private (string, bool) MapOrderByField(string name, InternalItemsQuery query)
{
if (string.Equals(name, ItemSortBy.AirTime, StringComparison.OrdinalIgnoreCase))
{
@@ -3218,7 +3211,7 @@ namespace Emby.Server.Implementations.Data
var whereText = whereClauses.Count == 0 ?
string.Empty :
" where " + string.Join(" AND ", whereClauses.ToArray());
" where " + string.Join(" AND ", whereClauses);
commandText += whereText
+ GetGroupBy(query)
@@ -4378,7 +4371,7 @@ namespace Emby.Server.Implementations.Data
}
else if (query.Years.Length > 1)
{
var val = string.Join(",", query.Years.ToArray());
var val = string.Join(",", query.Years);
whereClauses.Add("ProductionYear in (" + val + ")");
}
@@ -4952,7 +4945,12 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return result;
}
return new[] { value }.Where(IsValidType);
if (IsValidType(value))
{
return new[] { value };
}
return Array.Empty<string>();
}
public void DeleteItem(Guid id, CancellationToken cancellationToken)
@@ -5215,32 +5213,32 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
}
}
public QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query)
public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 0, 1 }, typeof(MusicArtist).FullName);
}
public QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query)
public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 0 }, typeof(MusicArtist).FullName);
}
public QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query)
public QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 1 }, typeof(MusicArtist).FullName);
}
public QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query)
public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 3 }, typeof(Studio).FullName);
}
public QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query)
public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 2 }, typeof(Genre).FullName);
}
public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query)
public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 2 }, typeof(MusicGenre).FullName);
}
@@ -5317,7 +5315,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
}
}
private QueryResult<Tuple<BaseItem, ItemCounts>> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType)
private QueryResult<(BaseItem, ItemCounts)> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType)
{
if (query == null)
{
@@ -5335,7 +5333,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
var typeClause = itemValueTypes.Length == 1 ?
("Type=" + itemValueTypes[0].ToString(CultureInfo.InvariantCulture)) :
("Type in (" + string.Join(",", itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray()) + ")");
("Type in (" + string.Join(",", itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture))) + ")");
InternalItemsQuery typeSubQuery = null;
@@ -5363,11 +5361,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
whereClauses.Add("guid in (select ItemId from ItemValues where ItemValues.CleanValue=A.CleanName AND " + typeClause + ")");
var typeWhereText = whereClauses.Count == 0 ?
string.Empty :
" where " + string.Join(" AND ", whereClauses);
itemCountColumnQuery += typeWhereText;
itemCountColumnQuery += " where " + string.Join(" AND ", whereClauses);
itemCountColumns = new Dictionary<string, string>()
{
@@ -5400,7 +5394,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
IsSeries = query.IsSeries
};
columns = GetFinalColumnsToSelect(query, columns.ToArray()).ToList();
columns = GetFinalColumnsToSelect(query, columns);
var commandText = "select "
+ string.Join(",", columns)
@@ -5492,8 +5486,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
return connection.RunInTransaction(db =>
{
var list = new List<Tuple<BaseItem, ItemCounts>>();
var result = new QueryResult<Tuple<BaseItem, ItemCounts>>();
var list = new List<(BaseItem, ItemCounts)>();
var result = new QueryResult<(BaseItem, ItemCounts)>();
var statements = PrepareAllSafe(db, statementTexts);
@@ -5531,7 +5525,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
var countStartColumn = columns.Count - 1;
list.Add(new Tuple<BaseItem, ItemCounts>(item, GetItemCounts(row, countStartColumn, typesToCount)));
list.Add((item, GetItemCounts(row, countStartColumn, typesToCount)));
}
}
@@ -6198,6 +6192,5 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return item;
}
}
}

View File

@@ -11,7 +11,6 @@ namespace Emby.Server.Implementations.Devices
{
private readonly IApplicationPaths _appPaths;
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly object _syncLock = new object();
@@ -86,19 +85,10 @@ namespace Emby.Server.Implementations.Devices
private string _id;
public DeviceId(
IApplicationPaths appPaths,
ILoggerFactory loggerFactory,
IFileSystem fileSystem)
public DeviceId(IApplicationPaths appPaths, ILoggerFactory loggerFactory)
{
if (fileSystem == null)
{
throw new ArgumentNullException(nameof(fileSystem));
}
_appPaths = appPaths;
_logger = loggerFactory.CreateLogger("SystemId");
_fileSystem = fileSystem;
}
public string Value => _id ?? (_id = GetDeviceId());

View File

@@ -34,8 +34,6 @@ namespace Emby.Server.Implementations.Devices
private readonly IFileSystem _fileSystem;
private readonly ILibraryMonitor _libraryMonitor;
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
private readonly INetworkManager _network;
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localizationManager;
@@ -55,17 +53,13 @@ namespace Emby.Server.Implementations.Devices
IUserManager userManager,
IFileSystem fileSystem,
ILibraryMonitor libraryMonitor,
IServerConfigurationManager config,
ILoggerFactory loggerFactory,
INetworkManager network)
IServerConfigurationManager config)
{
_json = json;
_userManager = userManager;
_fileSystem = fileSystem;
_libraryMonitor = libraryMonitor;
_config = config;
_logger = loggerFactory.CreateLogger(nameof(DeviceManager));
_network = network;
_libraryManager = libraryManager;
_localizationManager = localizationManager;
_authRepo = authRepo;
@@ -414,14 +408,12 @@ namespace Emby.Server.Implementations.Devices
{
private readonly DeviceManager _deviceManager;
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
private ILogger _logger;
public DeviceManagerEntryPoint(IDeviceManager deviceManager, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger)
public DeviceManagerEntryPoint(IDeviceManager deviceManager, IServerConfigurationManager config, ILogger logger)
{
_deviceManager = (DeviceManager)deviceManager;
_config = config;
_fileSystem = fileSystem;
_logger = logger;
}

View File

@@ -36,13 +36,9 @@ namespace Emby.Server.Implementations.Dto
private readonly IItemRepository _itemRepo;
private readonly IImageProcessor _imageProcessor;
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
private readonly IProviderManager _providerManager;
private readonly Func<IChannelManager> _channelManagerFactory;
private readonly IApplicationHost _appHost;
private readonly Func<IDeviceManager> _deviceManager;
private readonly Func<IMediaSourceManager> _mediaSourceManager;
private readonly Func<ILiveTvManager> _livetvManager;
@@ -52,12 +48,8 @@ namespace Emby.Server.Implementations.Dto
IUserDataManager userDataRepository,
IItemRepository itemRepo,
IImageProcessor imageProcessor,
IServerConfigurationManager config,
IFileSystem fileSystem,
IProviderManager providerManager,
Func<IChannelManager> channelManagerFactory,
IApplicationHost appHost,
Func<IDeviceManager> deviceManager,
Func<IMediaSourceManager> mediaSourceManager,
Func<ILiveTvManager> livetvManager)
{
@@ -66,12 +58,8 @@ namespace Emby.Server.Implementations.Dto
_userDataRepository = userDataRepository;
_itemRepo = itemRepo;
_imageProcessor = imageProcessor;
_config = config;
_fileSystem = fileSystem;
_providerManager = providerManager;
_channelManagerFactory = channelManagerFactory;
_appHost = appHost;
_deviceManager = deviceManager;
_mediaSourceManager = mediaSourceManager;
_livetvManager = livetvManager;
}

View File

@@ -24,6 +24,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
<PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
<PackageReference Include="sharpcompress" Version="0.22.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="1.0.0" />

View File

@@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints
{
class UserDataChangeNotifier : IServerEntryPoint
public class UserDataChangeNotifier : IServerEntryPoint
{
private readonly ISessionManager _sessionManager;
private readonly ILogger _logger;

View File

@@ -3,27 +3,19 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.FFMpeg
{
public class FFMpegLoader
{
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
private readonly ILogger _logger;
private readonly IZipClient _zipClient;
private readonly IFileSystem _fileSystem;
private readonly FFMpegInstallInfo _ffmpegInstallInfo;
public FFMpegLoader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient, IFileSystem fileSystem, FFMpegInstallInfo ffmpegInstallInfo)
public FFMpegLoader(IApplicationPaths appPaths, IFileSystem fileSystem, FFMpegInstallInfo ffmpegInstallInfo)
{
_logger = logger;
_appPaths = appPaths;
_httpClient = httpClient;
_zipClient = zipClient;
_fileSystem = fileSystem;
_ffmpegInstallInfo = ffmpegInstallInfo;
}
@@ -115,8 +107,7 @@ namespace Emby.Server.Implementations.FFMpeg
var encoderFilename = Path.GetFileName(info.EncoderPath);
var probeFilename = Path.GetFileName(info.ProbePath);
foreach (var directory in _fileSystem.GetDirectoryPaths(rootEncoderPath)
.ToList())
foreach (var directory in _fileSystem.GetDirectoryPaths(rootEncoderPath))
{
var allFiles = _fileSystem.GetFilePaths(directory, true).ToList();

View File

@@ -66,11 +66,6 @@ namespace Emby.Server.Implementations.HttpClientManager
// http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c
ServicePointManager.Expect100Continue = false;
#if NET46
// Trakt requests sometimes fail without this
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
#endif
}
/// <summary>
@@ -106,23 +101,6 @@ namespace Emby.Server.Implementations.HttpClientManager
return client;
}
private static WebRequest CreateWebRequest(string url)
{
try
{
return WebRequest.Create(url);
}
catch (NotSupportedException)
{
//Webrequest creation does fail on MONO randomly when using WebRequest.Create
//the issue occurs in the GetCreator method here: http://www.oschina.net/code/explore/mono-2.8.1/mcs/class/System/System.Net/WebRequest.cs
var type = Type.GetType("System.Net.HttpRequestCreator, System, Version=4.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089");
var creator = Activator.CreateInstance(type, nonPublic: true) as IWebRequestCreate;
return creator.Create(new Uri(url)) as HttpWebRequest;
}
}
private WebRequest GetRequest(HttpRequestOptions options, string method)
{
string url = options.Url;
@@ -135,7 +113,7 @@ namespace Emby.Server.Implementations.HttpClientManager
url = url.Replace(userInfo + "@", string.Empty);
}
var request = CreateWebRequest(url);
var request = WebRequest.Create(url);
if (request is HttpWebRequest httpWebRequest)
{
@@ -627,14 +605,16 @@ namespace Emby.Server.Implementations.HttpClientManager
var exception = new HttpException(webException.Message, webException);
var response = webException.Response as HttpWebResponse;
if (response != null)
using (var response = webException.Response as HttpWebResponse)
{
exception.StatusCode = response.StatusCode;
if ((int)response.StatusCode == 429)
if (response != null)
{
client.LastTimeout = DateTime.UtcNow;
exception.StatusCode = response.StatusCode;
if ((int)response.StatusCode == 429)
{
client.LastTimeout = DateTime.UtcNow;
}
}
}

View File

@@ -19,7 +19,9 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using ServiceStack.Text.Jsv;
namespace Emby.Server.Implementations.HttpServer
{
@@ -53,20 +55,20 @@ namespace Emby.Server.Implementations.HttpServer
IServerApplicationHost applicationHost,
ILoggerFactory loggerFactory,
IServerConfigurationManager config,
string defaultRedirectPath,
IConfiguration configuration,
INetworkManager networkManager,
IJsonSerializer jsonSerializer,
IXmlSerializer xmlSerializer,
Func<Type, Func<string, object>> funcParseFn)
IXmlSerializer xmlSerializer)
{
_appHost = applicationHost;
_logger = loggerFactory.CreateLogger("HttpServer");
_config = config;
DefaultRedirectPath = defaultRedirectPath;
DefaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
_networkManager = networkManager;
_jsonSerializer = jsonSerializer;
_xmlSerializer = xmlSerializer;
_funcParseFn = funcParseFn;
_funcParseFn = t => s => JsvReader.GetParseFn(t)(s);
Instance = this;
ResponseFilters = Array.Empty<Action<IRequest, IResponse, object>>();

View File

@@ -90,7 +90,7 @@ namespace Emby.Server.Implementations.HttpServer
/// </summary>
private IHasHeaders GetHttpResult(IRequest requestContext, Stream content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
{
var result = new StreamWriter(content, contentType, _logger);
var result = new StreamWriter(content, contentType);
if (responseHeaders == null)
{
@@ -131,7 +131,7 @@ namespace Emby.Server.Implementations.HttpServer
content = Array.Empty<byte>();
}
result = new StreamWriter(content, contentType, contentLength, _logger);
result = new StreamWriter(content, contentType, contentLength);
}
else
{
@@ -143,7 +143,7 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders = new Dictionary<string, string>();
}
if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string expires))
if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string _))
{
responseHeaders["Expires"] = "-1";
}
@@ -175,7 +175,7 @@ namespace Emby.Server.Implementations.HttpServer
bytes = Array.Empty<byte>();
}
result = new StreamWriter(bytes, contentType, contentLength, _logger);
result = new StreamWriter(bytes, contentType, contentLength);
}
else
{
@@ -187,7 +187,7 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders = new Dictionary<string, string>();
}
if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string expires))
if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string _))
{
responseHeaders["Expires"] = "-1";
}
@@ -277,9 +277,10 @@ namespace Emby.Server.Implementations.HttpServer
private object ToOptimizedResultInternal<T>(IRequest request, T dto, IDictionary<string, string> responseHeaders = null)
{
var contentType = request.ResponseContentType;
// TODO: @bond use Span and .Equals
var contentType = request.ResponseContentType?.Split(';')[0].Trim().ToLowerInvariant();
switch (GetRealContentType(contentType))
switch (contentType)
{
case "application/xml":
case "text/xml":
@@ -333,13 +334,13 @@ namespace Emby.Server.Implementations.HttpServer
if (isHeadRequest)
{
var result = new StreamWriter(Array.Empty<byte>(), contentType, contentLength, _logger);
var result = new StreamWriter(Array.Empty<byte>(), contentType, contentLength);
AddResponseHeaders(result, responseHeaders);
return result;
}
else
{
var result = new StreamWriter(content, contentType, contentLength, _logger);
var result = new StreamWriter(content, contentType, contentLength);
AddResponseHeaders(result, responseHeaders);
return result;
}
@@ -348,13 +349,19 @@ namespace Emby.Server.Implementations.HttpServer
private byte[] Compress(byte[] bytes, string compressionType)
{
if (string.Equals(compressionType, "br", StringComparison.OrdinalIgnoreCase))
{
return CompressBrotli(bytes);
}
if (string.Equals(compressionType, "deflate", StringComparison.OrdinalIgnoreCase))
{
return Deflate(bytes);
}
if (string.Equals(compressionType, "gzip", StringComparison.OrdinalIgnoreCase))
{
return GZip(bytes);
}
throw new NotSupportedException(compressionType);
}
@@ -390,13 +397,6 @@ namespace Emby.Server.Implementations.HttpServer
}
}
public static string GetRealContentType(string contentType)
{
return contentType == null
? null
: contentType.Split(';')[0].ToLowerInvariant().Trim();
}
private static string SerializeToXmlString(object from)
{
using (var ms = new MemoryStream())
@@ -422,18 +422,20 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// Pres the process optimized result.
/// </summary>
private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, Guid cacheKey, string cacheKeyString, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType)
private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, StaticResultOptions options)
{
bool noCache = (requestContext.Headers.Get("Cache-Control") ?? string.Empty).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
AddCachingHeaders(responseHeaders, options.CacheDuration, noCache, options.DateLastModified);
if (!noCache)
{
if (IsNotModified(requestContext, cacheKey))
{
AddAgeHeader(responseHeaders, lastDateModified);
AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration);
DateTime.TryParse(requestContext.Headers.Get("If-Modified-Since"), out var ifModifiedSinceHeader);
var result = new HttpResult(Array.Empty<byte>(), contentType ?? "text/html", HttpStatusCode.NotModified);
if (IsNotModified(ifModifiedSinceHeader, options.CacheDuration, options.DateLastModified))
{
AddAgeHeader(responseHeaders, options.DateLastModified);
var result = new HttpResult(Array.Empty<byte>(), options.ContentType ?? "text/html", HttpStatusCode.NotModified);
AddResponseHeaders(result, responseHeaders);
@@ -441,8 +443,6 @@ namespace Emby.Server.Implementations.HttpServer
}
}
AddCachingHeaders(responseHeaders, cacheKeyString, cacheDuration);
return null;
}
@@ -487,9 +487,6 @@ namespace Emby.Server.Implementations.HttpServer
options.DateLastModified = _fileSystem.GetLastWriteTimeUtc(path);
}
var cacheKey = path + options.DateLastModified.Value.Ticks;
options.CacheKey = cacheKey.GetMD5();
options.ContentFactory = () => Task.FromResult(GetFileStream(path, fileShare));
options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@@ -520,7 +517,6 @@ namespace Emby.Server.Implementations.HttpServer
return GetStaticResult(requestContext, new StaticResultOptions
{
CacheDuration = cacheDuration,
CacheKey = cacheKey,
ContentFactory = factoryFn,
ContentType = contentType,
DateLastModified = lastDateModified,
@@ -534,14 +530,10 @@ namespace Emby.Server.Implementations.HttpServer
options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var contentType = options.ContentType;
var etag = requestContext.Headers.Get("If-None-Match");
var cacheKey = etag != null ? new Guid(etag.Trim('\"')) : Guid.Empty;
if (!cacheKey.Equals(Guid.Empty))
if (!string.IsNullOrEmpty(requestContext.Headers.Get("If-Modified-Since")))
{
var key = cacheKey.ToString("N");
// See if the result is already cached in the browser
var result = GetCachedResult(requestContext, options.ResponseHeaders, cacheKey, key, options.DateLastModified, options.CacheDuration, contentType);
var result = GetCachedResult(requestContext, options.ResponseHeaders, options);
if (result != null)
{
@@ -553,6 +545,8 @@ namespace Emby.Server.Implementations.HttpServer
var isHeadRequest = options.IsHeadRequest || string.Equals(requestContext.Verb, "HEAD", StringComparison.OrdinalIgnoreCase);
var factoryFn = options.ContentFactory;
var responseHeaders = options.ResponseHeaders;
AddCachingHeaders(responseHeaders, options.CacheDuration, false, options.DateLastModified);
AddAgeHeader(responseHeaders, options.DateLastModified);
var rangeHeader = requestContext.Headers.Get("Range");
@@ -566,21 +560,10 @@ namespace Emby.Server.Implementations.HttpServer
};
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
// Generate an ETag based on identifying information - TODO read contents from filesystem instead?
var responseId = $"{hasHeaders.ContentType}{options.Path}{hasHeaders.TotalContentLength}";
var hashedId = MD5.Create().ComputeHash(Encoding.Default.GetBytes(responseId));
hasHeaders.Headers["ETag"] = new Guid(hashedId).ToString("N");
return hasHeaders;
}
var stream = await factoryFn().ConfigureAwait(false);
// Generate an etag based on stream content
var streamHash = MD5.Create().ComputeHash(stream);
var newEtag = new Guid(streamHash).ToString("N");
// reset position so the response can re-use it -- TODO is this ok?
stream.Position = 0;
var totalContentLength = options.ContentLength;
if (!totalContentLength.HasValue)
@@ -603,7 +586,6 @@ namespace Emby.Server.Implementations.HttpServer
};
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
hasHeaders.Headers["ETag"] = newEtag;
return hasHeaders;
}
else
@@ -621,14 +603,13 @@ namespace Emby.Server.Implementations.HttpServer
}
}
var hasHeaders = new StreamWriter(stream, contentType, _logger)
var hasHeaders = new StreamWriter(stream, contentType)
{
OnComplete = options.OnComplete,
OnError = options.OnError
};
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
hasHeaders.Headers["ETag"] = newEtag;
return hasHeaders;
}
}
@@ -641,37 +622,28 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// Adds the caching responseHeaders.
/// </summary>
private void AddCachingHeaders(IDictionary<string, string> responseHeaders, string cacheKey, TimeSpan? cacheDuration)
private void AddCachingHeaders(IDictionary<string, string> responseHeaders, TimeSpan? cacheDuration,
bool noCache, DateTime? lastModifiedDate)
{
if (cacheDuration.HasValue)
{
responseHeaders["Cache-Control"] = "public, max-age=" + Convert.ToInt32(cacheDuration.Value.TotalSeconds);
}
else if (!string.IsNullOrEmpty(cacheKey))
{
responseHeaders["Cache-Control"] = "public";
}
else
if (noCache)
{
responseHeaders["Cache-Control"] = "no-cache, no-store, must-revalidate";
responseHeaders["pragma"] = "no-cache, no-store, must-revalidate";
return;
}
AddExpiresHeader(responseHeaders, cacheKey, cacheDuration);
}
/// <summary>
/// Adds the expires header.
/// </summary>
private static void AddExpiresHeader(IDictionary<string, string> responseHeaders, string cacheKey, TimeSpan? cacheDuration)
{
if (cacheDuration.HasValue)
{
responseHeaders["Expires"] = DateTime.UtcNow.Add(cacheDuration.Value).ToString("r");
responseHeaders["Cache-Control"] = "public, max-age=" + cacheDuration.Value.TotalSeconds;
}
else if (string.IsNullOrEmpty(cacheKey))
else
{
responseHeaders["Expires"] = "-1";
responseHeaders["Cache-Control"] = "public";
}
if (lastModifiedDate.HasValue)
{
responseHeaders["Last-Modified"] = lastModifiedDate.ToString();
}
}
@@ -687,32 +659,6 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders["Age"] = Convert.ToInt64((DateTime.UtcNow - lastDateModified.Value).TotalSeconds).ToString(CultureInfo.InvariantCulture);
}
}
/// <summary>
/// Determines whether [is not modified] [the specified cache key].
/// </summary>
/// <param name="requestContext">The request context.</param>
/// <param name="cacheKey">The cache key.</param>
/// <param name="lastDateModified">The last date modified.</param>
/// <param name="cacheDuration">Duration of the cache.</param>
/// <returns><c>true</c> if [is not modified] [the specified cache key]; otherwise, <c>false</c>.</returns>
private bool IsNotModified(IRequest requestContext, Guid cacheKey)
{
var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match");
bool hasCacheKey = !cacheKey.Equals(Guid.Empty);
// Validate If-None-Match
if (hasCacheKey && !string.IsNullOrEmpty(ifNoneMatchHeader))
{
if (Guid.TryParse(ifNoneMatchHeader, out var ifNoneMatch)
&& cacheKey.Equals(ifNoneMatch))
{
return true;
}
}
return false;
}
/// <summary>
/// Determines whether [is not modified] [the specified if modified since].

View File

@@ -14,8 +14,6 @@ namespace Emby.Server.Implementations.HttpServer
/// </summary>
public class StreamWriter : IAsyncStreamWriter, IHasHeaders
{
private ILogger Logger { get; set; }
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
/// <summary>
@@ -45,7 +43,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="source">The source.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="logger">The logger.</param>
public StreamWriter(Stream source, string contentType, ILogger logger)
public StreamWriter(Stream source, string contentType)
{
if (string.IsNullOrEmpty(contentType))
{
@@ -53,7 +51,6 @@ namespace Emby.Server.Implementations.HttpServer
}
SourceStream = source;
Logger = logger;
Headers["Content-Type"] = contentType;
@@ -69,7 +66,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="source">The source.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="logger">The logger.</param>
public StreamWriter(byte[] source, string contentType, int contentLength, ILogger logger)
public StreamWriter(byte[] source, string contentType, int contentLength)
{
if (string.IsNullOrEmpty(contentType))
{
@@ -77,7 +74,6 @@ namespace Emby.Server.Implementations.HttpServer
}
SourceBytes = source;
Logger = logger;
Headers["Content-Type"] = contentType;

View File

@@ -17,31 +17,23 @@ namespace Emby.Server.Implementations.IO
public class FileRefresher : IDisposable
{
private ILogger Logger { get; set; }
private ITaskManager TaskManager { get; set; }
private ILibraryManager LibraryManager { get; set; }
private IServerConfigurationManager ConfigurationManager { get; set; }
private readonly IFileSystem _fileSystem;
private readonly List<string> _affectedPaths = new List<string>();
private Timer _timer;
private readonly object _timerLock = new object();
public string Path { get; private set; }
public event EventHandler<EventArgs> Completed;
private readonly IEnvironmentInfo _environmentInfo;
private readonly ILibraryManager _libraryManager;
public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger, IEnvironmentInfo environmentInfo, ILibraryManager libraryManager1)
public FileRefresher(string path, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ILogger logger)
{
logger.LogDebug("New file refresher created for {0}", path);
Path = path;
_fileSystem = fileSystem;
ConfigurationManager = configurationManager;
LibraryManager = libraryManager;
TaskManager = taskManager;
Logger = logger;
_environmentInfo = environmentInfo;
_libraryManager = libraryManager1;
AddPath(path);
}

View File

@@ -34,7 +34,7 @@ namespace Emby.Server.Implementations.IO
/// <summary>
/// Any file name ending in any of these will be ignored by the watchers
/// </summary>
private readonly HashSet<string> _alwaysIgnoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
private static readonly HashSet<string> _alwaysIgnoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"small.jpg",
"albumart.jpg",
@@ -44,7 +44,7 @@ namespace Emby.Server.Implementations.IO
"TempSBE"
};
private readonly string[] _alwaysIgnoreSubstrings = new string[]
private static readonly string[] _alwaysIgnoreSubstrings = new string[]
{
// Synology
"eaDir",
@@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.IO
".actors"
};
private readonly HashSet<string> _alwaysIgnoreExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
private static readonly HashSet<string> _alwaysIgnoreExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
// thumbs.db
".db",
@@ -123,12 +123,6 @@ namespace Emby.Server.Implementations.IO
/// <value>The logger.</value>
private ILogger Logger { get; set; }
/// <summary>
/// Gets or sets the task manager.
/// </summary>
/// <value>The task manager.</value>
private ITaskManager TaskManager { get; set; }
private ILibraryManager LibraryManager { get; set; }
private IServerConfigurationManager ConfigurationManager { get; set; }
@@ -140,19 +134,12 @@ namespace Emby.Server.Implementations.IO
/// </summary>
public LibraryMonitor(
ILoggerFactory loggerFactory,
ITaskManager taskManager,
ILibraryManager libraryManager,
IServerConfigurationManager configurationManager,
IFileSystem fileSystem,
IEnvironmentInfo environmentInfo)
{
if (taskManager == null)
{
throw new ArgumentNullException(nameof(taskManager));
}
LibraryManager = libraryManager;
TaskManager = taskManager;
Logger = loggerFactory.CreateLogger(GetType().Name);
ConfigurationManager = configurationManager;
_fileSystem = fileSystem;
@@ -541,7 +528,7 @@ namespace Emby.Server.Implementations.IO
}
}
var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger, _environmentInfo, LibraryManager);
var newRefresher = new FileRefresher(path, ConfigurationManager, LibraryManager, Logger);
newRefresher.Completed += NewRefresher_Completed;
_activeRefreshers.Add(newRefresher);
}

View File

@@ -4,8 +4,10 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.IO
@@ -20,61 +22,27 @@ namespace Emby.Server.Implementations.IO
private readonly bool _supportsAsyncFileStreams;
private char[] _invalidFileNameChars;
private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
private bool EnableSeparateFileAndDirectoryQueries;
private string _tempPath;
private readonly string _tempPath;
private IEnvironmentInfo _environmentInfo;
private bool _isEnvironmentCaseInsensitive;
private string _defaultDirectory;
private readonly IEnvironmentInfo _environmentInfo;
private readonly bool _isEnvironmentCaseInsensitive;
public ManagedFileSystem(
ILoggerFactory loggerFactory,
IEnvironmentInfo environmentInfo,
string defaultDirectory,
string tempPath,
bool enableSeparateFileAndDirectoryQueries)
IApplicationPaths applicationPaths)
{
Logger = loggerFactory.CreateLogger("FileSystem");
_supportsAsyncFileStreams = true;
_tempPath = tempPath;
_tempPath = applicationPaths.TempDirectory;
_environmentInfo = environmentInfo;
_defaultDirectory = defaultDirectory;
// On Linux with mono, this needs to be true or symbolic links are ignored
EnableSeparateFileAndDirectoryQueries = enableSeparateFileAndDirectoryQueries;
SetInvalidFileNameChars(environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows);
_isEnvironmentCaseInsensitive = environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows;
}
public virtual string DefaultDirectory
{
get
{
var value = _defaultDirectory;
if (!string.IsNullOrEmpty(value))
{
try
{
if (Directory.Exists(value))
{
return value;
}
}
catch
{
}
}
return null;
}
}
public virtual void AddShortcutHandler(IShortcutHandler handler)
{
_shortcutHandlers.Add(handler);
@@ -718,7 +686,7 @@ namespace Emby.Server.Implementations.IO
SetAttributes(path, false, false);
File.Delete(path);
}
public virtual List<FileSystemMetadata> GetDrives()
{
// Only include drives in the ready state or this method could end up being very slow, waiting for drives to timeout
@@ -777,20 +745,15 @@ namespace Emby.Server.Implementations.IO
var directoryInfo = new DirectoryInfo(path);
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
if (EnableSeparateFileAndDirectoryQueries)
{
return ToMetadata(directoryInfo.EnumerateDirectories("*", searchOption))
.Concat(ToMetadata(directoryInfo.EnumerateFiles("*", searchOption)));
}
return ToMetadata(directoryInfo.EnumerateFileSystemInfos("*", searchOption));
return ToMetadata(directoryInfo.EnumerateDirectories("*", searchOption))
.Concat(ToMetadata(directoryInfo.EnumerateFiles("*", searchOption)));
}
private IEnumerable<FileSystemMetadata> ToMetadata(IEnumerable<FileSystemInfo> infos)
{
return infos.Select(GetFileSystemMetadata);
}
public virtual IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
{
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Controller.Entities;
@@ -7,7 +6,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library
{
@@ -16,16 +14,14 @@ namespace Emby.Server.Implementations.Library
/// </summary>
public class CoreResolutionIgnoreRule : IResolverIgnoreRule
{
private readonly IFileSystem _fileSystem;
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
private bool _ignoreDotPrefix;
/// <summary>
/// Any folder named in this list will be ignored - can be added to at runtime for extensibility
/// </summary>
public static readonly Dictionary<string, string> IgnoreFolders = new List<string>
public static readonly string[] IgnoreFolders =
{
"metadata",
"ps3_update",
@@ -50,13 +46,11 @@ namespace Emby.Server.Implementations.Library
// macos
".AppleDouble"
}.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
};
public CoreResolutionIgnoreRule(IFileSystem fileSystem, ILibraryManager libraryManager, ILogger logger)
public CoreResolutionIgnoreRule(ILibraryManager libraryManager)
{
_fileSystem = fileSystem;
_libraryManager = libraryManager;
_logger = logger;
_ignoreDotPrefix = Environment.OSVersion.Platform != PlatformID.Win32NT;
}
@@ -117,7 +111,7 @@ namespace Emby.Server.Implementations.Library
if (fileInfo.IsDirectory)
{
// Ignore any folders in our list
if (IgnoreFolders.ContainsKey(filename))
if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase))
{
return true;
}

View File

@@ -986,7 +986,7 @@ namespace Emby.Server.Implementations.Library
// Ensure the location is available.
Directory.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath);
return new PeopleValidator(this, _logger, ConfigurationManager, _fileSystem).ValidatePeople(cancellationToken, progress);
return new PeopleValidator(this, _logger, _fileSystem).ValidatePeople(cancellationToken, progress);
}
/// <summary>
@@ -1225,9 +1225,9 @@ namespace Emby.Server.Implementations.Library
/// <exception cref="ArgumentNullException">id</exception>
public BaseItem GetItemById(Guid id)
{
if (id.Equals(Guid.Empty))
if (id == Guid.Empty)
{
throw new ArgumentNullException(nameof(id));
throw new ArgumentException(nameof(id), "Guid can't be empty");
}
if (LibraryItemsCache.TryGetValue(id, out BaseItem item))
@@ -1237,8 +1237,6 @@ namespace Emby.Server.Implementations.Library
item = RetrieveItem(id);
//_logger.LogDebug("GetitemById {0}", id);
if (item != null)
{
RegisterItem(item);
@@ -1333,7 +1331,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetItemIdsList(query);
}
public QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query)
public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1344,7 +1342,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetStudios(query);
}
public QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query)
public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1355,7 +1353,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetGenres(query);
}
public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query)
public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1366,7 +1364,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetMusicGenres(query);
}
public QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query)
public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1377,7 +1375,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetAllArtists(query);
}
public QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query)
public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1421,7 +1419,7 @@ namespace Emby.Server.Implementations.Library
}
}
public QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query)
public QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1808,18 +1806,16 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task.</returns>
public void CreateItems(IEnumerable<BaseItem> items, BaseItem parent, CancellationToken cancellationToken)
{
var list = items.ToList();
ItemRepository.SaveItems(items, cancellationToken);
ItemRepository.SaveItems(list, cancellationToken);
foreach (var item in list)
foreach (var item in items)
{
RegisterItem(item);
}
if (ItemAdded != null)
{
foreach (var item in list)
foreach (var item in items)
{
// With the live tv guide this just creates too much noise
if (item.SourceType != SourceType.Library)
@@ -1853,7 +1849,7 @@ namespace Emby.Server.Implementations.Library
/// <summary>
/// Updates the item.
/// </summary>
public void UpdateItems(List<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
public void UpdateItems(IEnumerable<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
{
foreach (var item in items)
{
@@ -1908,7 +1904,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task.</returns>
public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
{
UpdateItems(new List<BaseItem> { item }, parent, updateReason, cancellationToken);
UpdateItems(new [] { item }, parent, updateReason, cancellationToken);
}
/// <summary>
@@ -2005,9 +2001,7 @@ namespace Emby.Server.Implementations.Library
.FirstOrDefault();
}
var options = collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions();
return options;
return collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions();
}
public string GetContentType(BaseItem item)
@@ -2017,11 +2011,13 @@ namespace Emby.Server.Implementations.Library
{
return configuredContentType;
}
configuredContentType = GetConfiguredContentType(item, true);
if (!string.IsNullOrEmpty(configuredContentType))
{
return configuredContentType;
}
return GetInheritedContentType(item);
}
@@ -2056,6 +2052,7 @@ namespace Emby.Server.Implementations.Library
{
return collectionFolder.CollectionType;
}
return GetContentTypeOverride(item.ContainingFolderPath, inheritConfiguredPath);
}
@@ -2066,6 +2063,7 @@ namespace Emby.Server.Implementations.Library
{
return nameValuePair.Value;
}
return null;
}
@@ -2108,9 +2106,9 @@ namespace Emby.Server.Implementations.Library
string viewType,
string sortName)
{
var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views");
path = Path.Combine(path, _fileSystem.GetValidFilename(viewType));
var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath,
"views",
_fileSystem.GetValidFilename(viewType));
var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView));
@@ -2543,7 +2541,7 @@ namespace Emby.Server.Implementations.Library
var resolvers = new IItemResolver[]
{
new GenericVideoResolver<Trailer>(this, _fileSystem)
new GenericVideoResolver<Trailer>(this)
};
return ResolvePaths(files, directoryService, null, new LibraryOptions(), null, resolvers)

View File

@@ -6,7 +6,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers
{
@@ -18,11 +17,9 @@ namespace Emby.Server.Implementations.Library.Resolvers
where T : Video, new()
{
protected readonly ILibraryManager LibraryManager;
protected readonly IFileSystem FileSystem;
protected BaseVideoResolver(ILibraryManager libraryManager, IFileSystem fileSystem)
protected BaseVideoResolver(ILibraryManager libraryManager)
{
FileSystem = fileSystem;
LibraryManager = libraryManager;
}

View File

@@ -548,7 +548,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
private IImageProcessor _imageProcessor;
public MovieResolver(ILibraryManager libraryManager, IFileSystem fileSystem, IImageProcessor imageProcessor) : base(libraryManager, fileSystem)
public MovieResolver(ILibraryManager libraryManager, IImageProcessor imageProcessor)
: base(libraryManager)
{
_imageProcessor = imageProcessor;
}

View File

@@ -7,7 +7,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers
{
@@ -15,13 +14,11 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
private readonly IImageProcessor _imageProcessor;
private readonly ILibraryManager _libraryManager;
private readonly IFileSystem _fileSystem;
public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager, IFileSystem fileSystem)
public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager)
{
_imageProcessor = imageProcessor;
_libraryManager = libraryManager;
_fileSystem = fileSystem;
}
/// <summary>
@@ -113,8 +110,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
return false;
}
return imageProcessor.SupportedInputFormats.Contains((Path.GetExtension(path) ?? string.Empty).TrimStart('.'));
return imageProcessor.SupportedInputFormats.Contains(Path.GetExtension(path).TrimStart('.'), StringComparer.Ordinal);
}
}
}

View File

@@ -9,7 +9,7 @@ using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers
{
class SpecialFolderResolver : FolderResolver<Folder>
public class SpecialFolderResolver : FolderResolver<Folder>
{
private readonly IFileSystem _fileSystem;
private readonly IServerApplicationPaths _appPaths;

View File

@@ -3,7 +3,6 @@ using System.Linq;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers.TV
{
@@ -74,7 +73,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
public EpisodeResolver(ILibraryManager libraryManager, IFileSystem fileSystem) : base(libraryManager, fileSystem)
public EpisodeResolver(ILibraryManager libraryManager)
: base(libraryManager)
{
}
}

View File

@@ -1,13 +1,13 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers
{
public class GenericVideoResolver<T> : BaseVideoResolver<T>
where T : Video, new()
{
public GenericVideoResolver(ILibraryManager libraryManager, IFileSystem fileSystem) : base(libraryManager, fileSystem)
public GenericVideoResolver(ILibraryManager libraryManager)
: base(libraryManager)
{
}
}

View File

@@ -74,7 +74,6 @@ namespace Emby.Server.Implementations.Library
private readonly Func<IDtoService> _dtoServiceFactory;
private readonly IServerApplicationHost _appHost;
private readonly IFileSystem _fileSystem;
private readonly ICryptoProvider _cryptographyProvider;
private IAuthenticationProvider[] _authenticationProviders;
private DefaultAuthenticationProvider _defaultAuthenticationProvider;
@@ -89,8 +88,7 @@ namespace Emby.Server.Implementations.Library
Func<IDtoService> dtoServiceFactory,
IServerApplicationHost appHost,
IJsonSerializer jsonSerializer,
IFileSystem fileSystem,
ICryptoProvider cryptographyProvider)
IFileSystem fileSystem)
{
_logger = loggerFactory.CreateLogger(nameof(UserManager));
UserRepository = userRepository;
@@ -101,7 +99,6 @@ namespace Emby.Server.Implementations.Library
_appHost = appHost;
_jsonSerializer = jsonSerializer;
_fileSystem = fileSystem;
_cryptographyProvider = cryptographyProvider;
ConfigurationManager = configurationManager;
_users = Array.Empty<User>();
@@ -171,9 +168,9 @@ namespace Emby.Server.Implementations.Library
/// <exception cref="ArgumentNullException"></exception>
public User GetUserById(Guid id)
{
if (id.Equals(Guid.Empty))
if (id == Guid.Empty)
{
throw new ArgumentNullException(nameof(id));
throw new ArgumentException(nameof(id), "Guid can't be empty");
}
return Users.FirstOrDefault(u => u.Id == id);

View File

@@ -24,7 +24,6 @@ namespace Emby.Server.Implementations.Library.Validators
/// </summary>
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
/// <summary>
@@ -32,11 +31,10 @@ namespace Emby.Server.Implementations.Library.Validators
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem)
public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem)
{
_libraryManager = libraryManager;
_logger = logger;
_config = config;
_fileSystem = fileSystem;
}

View File

@@ -105,8 +105,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_mediaSourceManager = mediaSourceManager;
_streamHelper = streamHelper;
_seriesTimerProvider = new SeriesTimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
_timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger);
_seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
_timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger);
_timerProvider.TimerFired += _timerProvider_TimerFired;
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
@@ -1708,7 +1708,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
if (mediaSource.RequiresLooping || !(mediaSource.Container ?? string.Empty).EndsWith("ts", StringComparison.OrdinalIgnoreCase) || (mediaSource.Protocol != MediaProtocol.File && mediaSource.Protocol != MediaProtocol.Http))
{
return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _httpClient, _processFactory, _config, _assemblyInfo);
return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _processFactory, _config);
}
return new DirectRecorder(_logger, _httpClient, _fileSystem, _streamHelper);

View File

@@ -7,7 +7,6 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
@@ -17,7 +16,6 @@ using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Reflection;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
@@ -27,7 +25,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IHttpClient _httpClient;
private readonly IMediaEncoder _mediaEncoder;
private readonly IServerApplicationPaths _appPaths;
private bool _hasExited;
@@ -38,19 +35,23 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private readonly IJsonSerializer _json;
private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
private readonly IServerConfigurationManager _config;
private readonly IAssemblyInfo _assemblyInfo;
public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IServerApplicationPaths appPaths, IJsonSerializer json, IHttpClient httpClient, IProcessFactory processFactory, IServerConfigurationManager config, IAssemblyInfo assemblyInfo)
public EncodedRecorder(
ILogger logger,
IFileSystem fileSystem,
IMediaEncoder mediaEncoder,
IServerApplicationPaths appPaths,
IJsonSerializer json,
IProcessFactory processFactory,
IServerConfigurationManager config)
{
_logger = logger;
_fileSystem = fileSystem;
_mediaEncoder = mediaEncoder;
_appPaths = appPaths;
_json = json;
_httpClient = httpClient;
_processFactory = processFactory;
_config = config;
_assemblyInfo = assemblyInfo;
}
private static bool CopySubtitles => false;

View File

@@ -17,15 +17,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
protected readonly ILogger Logger;
private readonly string _dataPath;
protected readonly Func<T, T, bool> EqualityComparer;
private readonly IFileSystem _fileSystem;
public ItemDataProvider(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, Func<T, T, bool> equalityComparer)
public ItemDataProvider(IJsonSerializer jsonSerializer, ILogger logger, string dataPath, Func<T, T, bool> equalityComparer)
{
Logger = logger;
_dataPath = dataPath;
EqualityComparer = equalityComparer;
_jsonSerializer = jsonSerializer;
_fileSystem = fileSystem;
}
public IReadOnlyList<T> GetAll()

View File

@@ -1,6 +1,5 @@
using System;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
@@ -8,8 +7,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class SeriesTimerManager : ItemDataProvider<SeriesTimerInfo>
{
public SeriesTimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath)
: base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
public SeriesTimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath)
: base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
{
}

View File

@@ -5,7 +5,6 @@ using System.Linq;
using System.Threading;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
@@ -19,8 +18,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1)
: base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
public TimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1)
: base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
{
_logger = logger1;
}

View File

@@ -184,7 +184,7 @@ namespace Emby.Server.Implementations.LiveTv
public QueryResult<BaseItem> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
{
var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId);
var user = query.UserId == Guid.Empty ? null : _userManager.GetUserById(query.UserId);
var topFolder = GetInternalLiveTvFolder(cancellationToken);

View File

@@ -9,7 +9,6 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
@@ -23,18 +22,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected readonly IServerConfigurationManager Config;
protected readonly ILogger Logger;
protected IJsonSerializer JsonSerializer;
protected readonly IMediaEncoder MediaEncoder;
protected readonly IFileSystem FileSystem;
private readonly ConcurrentDictionary<string, ChannelCache> _channelCache =
new ConcurrentDictionary<string, ChannelCache>(StringComparer.OrdinalIgnoreCase);
protected BaseTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem)
protected BaseTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem)
{
Config = config;
Logger = logger;
JsonSerializer = jsonSerializer;
MediaEncoder = mediaEncoder;
FileSystem = fileSystem;
}

View File

@@ -31,15 +31,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly IServerApplicationHost _appHost;
private readonly ISocketFactory _socketFactory;
private readonly INetworkManager _networkManager;
private readonly IEnvironmentInfo _environment;
public HdHomerunHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager, IEnvironmentInfo environment) : base(config, logger, jsonSerializer, mediaEncoder, fileSystem)
public HdHomerunHost(
IServerConfigurationManager config,
ILogger logger,
IJsonSerializer jsonSerializer,
IFileSystem fileSystem,
IHttpClient httpClient,
IServerApplicationHost appHost,
ISocketFactory socketFactory,
INetworkManager networkManager)
: base(config, logger, jsonSerializer, fileSystem)
{
_httpClient = httpClient;
_appHost = appHost;
_socketFactory = socketFactory;
_networkManager = networkManager;
_environment = environment;
}
public string Name => "HD Homerun";

View File

@@ -26,15 +26,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
private readonly IHttpClient _httpClient;
private readonly IServerApplicationHost _appHost;
private readonly IEnvironmentInfo _environment;
private readonly INetworkManager _networkManager;
private readonly IMediaSourceManager _mediaSourceManager;
public M3UTunerHost(IServerConfigurationManager config, IMediaSourceManager mediaSourceManager, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, IEnvironmentInfo environment, INetworkManager networkManager) : base(config, logger, jsonSerializer, mediaEncoder, fileSystem)
public M3UTunerHost(IServerConfigurationManager config, IMediaSourceManager mediaSourceManager, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, INetworkManager networkManager)
: base(config, logger, jsonSerializer, fileSystem)
{
_httpClient = httpClient;
_appHost = appHost;
_environment = environment;
_networkManager = networkManager;
_mediaSourceManager = mediaSourceManager;
}
@@ -52,7 +51,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
var channelIdPrefix = GetFullChannelIdPrefix(info);
var result = await new M3uParser(Logger, FileSystem, _httpClient, _appHost).Parse(info.Url, channelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false);
var result = await new M3uParser(Logger, _httpClient, _appHost).Parse(info.Url, channelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false);
return result.Cast<ChannelInfo>().ToList();
}
@@ -115,7 +114,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public async Task Validate(TunerHostInfo info)
{
using (var stream = await new M3uParser(Logger, FileSystem, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
using (var stream = await new M3uParser(Logger, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
{
}

View File

@@ -19,14 +19,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public class M3uParser
{
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IHttpClient _httpClient;
private readonly IServerApplicationHost _appHost;
public M3uParser(ILogger logger, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost)
public M3uParser(ILogger logger, IHttpClient httpClient, IServerApplicationHost appHost)
{
_logger = logger;
_fileSystem = fileSystem;
_httpClient = httpClient;
_appHost = appHost;
}

View File

@@ -94,7 +94,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var now = DateTime.UtcNow;
var _ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
_ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
//OpenedMediaSource.Protocol = MediaProtocol.File;
//OpenedMediaSource.Path = tempFile;

View File

@@ -2,10 +2,10 @@
"Albums": "Album",
"AppDeviceValues": "App: {0}, Enhed: {1}",
"Application": "Applikation",
"Artists": "Kunstner",
"Artists": "Kunstnere",
"AuthenticationSucceededWithUserName": "{0} bekræftet med succes",
"Books": "Bøger",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"CameraImageUploadedFrom": "Et nyt kamerabillede er blevet uploadet fra {0}",
"Channels": "Kanaler",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Samlinger",
@@ -14,41 +14,41 @@
"FailedLoginAttemptWithUserName": "Fejlet loginforsøg fra {0}",
"Favorites": "Favoritter",
"Folders": "Mapper",
"Genres": "Genre",
"Genres": "Genrer",
"HeaderAlbumArtists": "Albumkunstnere",
"HeaderCameraUploads": "Camera Uploads",
"HeaderCameraUploads": "Kamera Uploads",
"HeaderContinueWatching": "Fortsæt Afspilning",
"HeaderFavoriteAlbums": "Favoritalbum",
"HeaderFavoriteArtists": "Favoritkunstnere",
"HeaderFavoriteEpisodes": "Favoritepisoder",
"HeaderFavoriteShows": "Favorit serier",
"HeaderFavoriteSongs": "Favoritsange",
"HeaderFavoriteEpisodes": "Favorit-afsnit",
"HeaderFavoriteShows": "Favorit-serier",
"HeaderFavoriteSongs": "Favorit-sange",
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Næste",
"HeaderRecordingGroups": "Optagegrupper",
"HeaderRecordingGroups": "Optagelsesgrupper",
"HomeVideos": "Hjemmevideoer",
"Inherit": "Arv",
"Inherit": "Nedarv",
"ItemAddedWithName": "{0} blev tilføjet til biblioteket",
"ItemRemovedWithName": "{0} blev fjernet fra biblioteket",
"LabelIpAddressValue": "IP-adresse: {0}",
"LabelRunningTimeValue": "Spilletid: {0}",
"Latest": "Seneste",
"MessageApplicationUpdated": "Jellyfin Server er blevet opdateret",
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Server konfigurationssektion {0} er blevet opdateret",
"MessageServerConfigurationUpdated": "Serverkonfiguration er blevet opdateret",
"MessageApplicationUpdatedTo": "Jellyfin Server er blevet opdateret til {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Serverkonfigurationsafsnit {0} er blevet opdateret",
"MessageServerConfigurationUpdated": "Serverkonfigurationen er blevet opdateret",
"MixedContent": "Blandet indhold",
"Movies": "Film",
"Music": "Musik",
"MusicVideos": "Musikvideoer",
"NameInstallFailed": "{0} installation failed",
"NameInstallFailed": "{0} installationen mislykkedes",
"NameSeasonNumber": "Sæson {0}",
"NameSeasonUnknown": "Season Unknown",
"NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
"NameSeasonUnknown": "Ukendt Sæson",
"NewVersionIsAvailable": "En ny version af Jellyfin Server er tilgængelig til download.",
"NotificationOptionApplicationUpdateAvailable": "Opdatering til applikation tilgængelig",
"NotificationOptionApplicationUpdateInstalled": "Opdatering til applikation installeret",
"NotificationOptionAudioPlayback": "Audioafspilning påbegyndt",
"NotificationOptionAudioPlaybackStopped": "Audioafspilning stoppet",
"NotificationOptionAudioPlayback": "Lydafspilning påbegyndt",
"NotificationOptionAudioPlaybackStopped": "Lydafspilning stoppet",
"NotificationOptionCameraImageUploaded": "Kamerabillede uploadet",
"NotificationOptionInstallationFailed": "Installationsfejl",
"NotificationOptionNewLibraryContent": "Nyt indhold tilføjet",
@@ -70,16 +70,16 @@
"ProviderValue": "Udbyder: {0}",
"ScheduledTaskFailedWithName": "{0} fejlet",
"ScheduledTaskStartedWithName": "{0} påbegyndt",
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"Shows": "Shows",
"ServerNameNeedsToBeRestarted": "{0} skal genstartes",
"Shows": "Serier",
"Songs": "Sange",
"StartupEmbyServerIsLoading": "Jellyfin Server indlæser. Prøv venligst igen om kort tid.",
"StartupEmbyServerIsLoading": "Jellyfin Server er i gang med at starte op. Prøv venligst igen om lidt.",
"SubtitleDownloadFailureForItem": "Fejlet i download af undertekster for {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke downloades fra {0} til {1}",
"SubtitlesDownloadedForItem": "Undertekster downloadet for {0}",
"Sync": "Synk",
"System": "System",
"TvShows": "TV Shows",
"TvShows": "TV serier",
"User": "Bruger",
"UserCreatedWithName": "Bruger {0} er blevet oprettet",
"UserDeletedWithName": "Brugeren {0} er blevet slettet",
@@ -88,10 +88,10 @@
"UserOfflineFromDevice": "{0} har afbrudt fra {1}",
"UserOnlineFromDevice": "{0} er online fra {1}",
"UserPasswordChangedWithName": "Adgangskode er ændret for bruger {0}",
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"UserPolicyUpdatedWithName": "Brugerpolitik er blevet opdateret for {0}",
"UserStartedPlayingItemWithValues": "{0} har påbegyndt afspilning af {1}",
"UserStoppedPlayingItemWithValues": "{0} har afsluttet afspilning af {1}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"UserStoppedPlayingItemWithValues": "{0} har afsluttet afspilning af {1} på {2}",
"ValueHasBeenAddedToLibrary": "{0} er blevet tilføjet til dit mediebibliotek",
"ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Version {0}"
}

View File

@@ -3,61 +3,61 @@
"AppDeviceValues": "App: {0}, Gerät: {1}",
"Application": "Anwendung",
"Artists": "Interpreten",
"AuthenticationSucceededWithUserName": "{0} erfolgreich authentifiziert",
"AuthenticationSucceededWithUserName": "{0} hat sich angemeldet",
"Books": "Bücher",
"CameraImageUploadedFrom": "Ein neues Bild wurde hochgeladen von {0}",
"CameraImageUploadedFrom": "Ein neues Foto wurde hochgeladen von {0}",
"Channels": "Kanäle",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Sammlungen",
"DeviceOfflineWithName": "{0} wurde getrennt",
"DeviceOnlineWithName": "{0} ist verbunden",
"DeviceOnlineWithName": "{0} hat sich verbunden",
"FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}",
"Favorites": "Favoriten",
"Folders": "Verzeichnisse",
"Genres": "Genres",
"HeaderAlbumArtists": "Album-Künstler",
"HeaderCameraUploads": "Kamera Uploads",
"HeaderAlbumArtists": "Album-Interpreten",
"HeaderCameraUploads": "Kamera-Uploads",
"HeaderContinueWatching": "Weiterschauen",
"HeaderFavoriteAlbums": "Lieblingsalben",
"HeaderFavoriteArtists": "Interpreten Favoriten",
"HeaderFavoriteArtists": "Lieblings-Interpreten",
"HeaderFavoriteEpisodes": "Lieblingsepisoden",
"HeaderFavoriteShows": "Lieblingsserien",
"HeaderFavoriteSongs": "Lieder Favoriten",
"HeaderLiveTV": "Live TV",
"HeaderFavoriteSongs": "Lieblingslieder",
"HeaderLiveTV": "Live-TV",
"HeaderNextUp": "Als Nächstes",
"HeaderRecordingGroups": "Aufnahme-Gruppen",
"HomeVideos": "Heimvideos",
"Inherit": "Übernehmen",
"ItemAddedWithName": "{0} wurde der Bibliothek hinzugefügt",
"ItemRemovedWithName": "{0} wurde aus der Bibliothek entfernt",
"LabelIpAddressValue": "IP Adresse: {0}",
"LabelIpAddressValue": "IP-Adresse: {0}",
"LabelRunningTimeValue": "Laufzeit: {0}",
"Latest": "Neueste",
"MessageApplicationUpdated": "Jellyfin Server wurde auf den neusten Stand gebracht.",
"MessageApplicationUpdatedTo": "Jellyfin Server wurde auf Version {0} aktualisiert",
"MessageApplicationUpdated": "Jellyfin-Server wurde aktualisiert",
"MessageApplicationUpdatedTo": "Jellyfin-Server wurde auf Version {0} aktualisiert",
"MessageNamedServerConfigurationUpdatedWithValue": "Der Server Einstellungsbereich {0} wurde aktualisiert",
"MessageServerConfigurationUpdated": "Server Einstellungen wurden aktualisiert",
"MessageServerConfigurationUpdated": "Servereinstellungen wurden aktualisiert",
"MixedContent": "Gemischte Inhalte",
"Movies": "Filme",
"Music": "Musik",
"MusicVideos": "Musikvideos",
"NameInstallFailed": "{0} Installation fehlgeschlagen",
"NameInstallFailed": "Installation von {0} fehlgeschlagen",
"NameSeasonNumber": "Staffel {0}",
"NameSeasonUnknown": "Staffel unbekannt",
"NewVersionIsAvailable": "Eine neue Version von Jellyfin Server steht zum Download bereit.",
"NewVersionIsAvailable": "Eine neue Version von Jellyfin-Server steht zum Download bereit.",
"NotificationOptionApplicationUpdateAvailable": "Anwendungsaktualisierung verfügbar",
"NotificationOptionApplicationUpdateInstalled": "Anwendungsaktualisierung installiert",
"NotificationOptionAudioPlayback": "Audiowiedergabe gestartet",
"NotificationOptionAudioPlaybackStopped": "Audiowiedergabe gestoppt",
"NotificationOptionCameraImageUploaded": "Kamera Bild hochgeladen",
"NotificationOptionCameraImageUploaded": "Foto hochgeladen",
"NotificationOptionInstallationFailed": "Installationsfehler",
"NotificationOptionNewLibraryContent": "Neuer Inhalt hinzugefügt",
"NotificationOptionPluginError": "Plugin Fehler",
"NotificationOptionPluginError": "Plugin-Fehler",
"NotificationOptionPluginInstalled": "Plugin installiert",
"NotificationOptionPluginUninstalled": "Plugin deinstalliert",
"NotificationOptionPluginUpdateInstalled": "Pluginaktualisierung installiert",
"NotificationOptionServerRestartRequired": "Serverneustart notwendig",
"NotificationOptionTaskFailed": "Geplante Aufgaben fehlgeschlagen",
"NotificationOptionTaskFailed": "Geplante Aufgabe fehlgeschlagen",
"NotificationOptionUserLockedOut": "Benutzer ausgeschlossen",
"NotificationOptionVideoPlayback": "Videowiedergabe gestartet",
"NotificationOptionVideoPlaybackStopped": "Videowiedergabe gestoppt",
@@ -68,18 +68,18 @@
"PluginUninstalledWithName": "{0} wurde deinstalliert",
"PluginUpdatedWithName": "{0} wurde aktualisiert",
"ProviderValue": "Anbieter: {0}",
"ScheduledTaskFailedWithName": "{0} fehlgeschlagen",
"ScheduledTaskStartedWithName": "{0} gestartet",
"ScheduledTaskFailedWithName": "{0} ist fehlgeschlagen",
"ScheduledTaskStartedWithName": "{0} wurde gestartet",
"ServerNameNeedsToBeRestarted": "{0} muss neu gestartet werden",
"Shows": "Serien",
"Songs": "Songs",
"StartupEmbyServerIsLoading": "Jellyfin Server startet, bitte versuche es gleich noch einmal.",
"StartupEmbyServerIsLoading": "Jellyfin-Server startet, bitte versuche es gleich noch einmal.",
"SubtitleDownloadFailureForItem": "Download der Untertitel fehlgeschlagen für {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitleDownloadFailureFromForItem": "Untertitel von {0} für {1} konnten nicht heruntergeladen werden",
"SubtitlesDownloadedForItem": "Untertitel heruntergeladen für {0}",
"Sync": "Synchronisation",
"System": "System",
"TvShows": "TV Sendungen",
"TvShows": "TV-Sendungen",
"User": "Benutzer",
"UserCreatedWithName": "Benutzer {0} wurde erstellt",
"UserDeletedWithName": "Benutzer {0} wurde gelöscht",
@@ -88,10 +88,10 @@
"UserOfflineFromDevice": "{0} wurde getrennt von {1}",
"UserOnlineFromDevice": "{0} ist online von {1}",
"UserPasswordChangedWithName": "Das Passwort für Benutzer {0} wurde geändert",
"UserPolicyUpdatedWithName": "Benutzerrichtlinie wurde für {0} aktualisiert",
"UserStartedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} gestartet",
"UserStoppedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} beendet",
"ValueHasBeenAddedToLibrary": "{0} wurde ihrer Bibliothek hinzugefügt",
"ValueSpecialEpisodeName": "Special - {0}",
"UserPolicyUpdatedWithName": "Benutzerrichtlinie von {0} wurde aktualisiert",
"UserStartedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} auf {2} gestartet",
"UserStoppedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} auf {2} beendet",
"ValueHasBeenAddedToLibrary": "{0} wurde deiner Bibliothek hinzugefügt",
"ValueSpecialEpisodeName": "Extra - {0}",
"VersionNumber": "Version {0}"
}

View File

@@ -90,7 +90,7 @@
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"UserStartedPlayingItemWithValues": "{0} has started playing {1}",
"UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
"UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Version {0}"

View File

@@ -30,7 +30,7 @@
"Inherit": "Inherit",
"ItemAddedWithName": "{0} was added to the library",
"ItemRemovedWithName": "{0} was removed from the library",
"LabelIpAddressValue": "Ip address: {0}",
"LabelIpAddressValue": "IP address: {0}",
"LabelRunningTimeValue": "Running time: {0}",
"Latest": "Latest",
"MessageApplicationUpdated": "Jellyfin Server has been updated",

View File

@@ -5,46 +5,46 @@
"Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado correctamente",
"Books": "Libros",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}",
"Channels": "Canales",
"ChapterNameValue": "Capítulo {0}",
"Collections": "Colecciones",
"DeviceOfflineWithName": "{0} se ha desconectado",
"DeviceOnlineWithName": "{0} está conectado",
"FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión a partir de {0}",
"FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión desde {0}",
"Favorites": "Favoritos",
"Folders": "Carpetas",
"Genres": "Géneros",
"HeaderAlbumArtists": "Artistas del Álbum",
"HeaderCameraUploads": "Camera Uploads",
"HeaderAlbumArtists": "Artistas del álbum",
"HeaderCameraUploads": "Subidas desde cámara",
"HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteShows": "Programas favoritos",
"HeaderFavoriteSongs": "Canciones favoritas",
"HeaderLiveTV": "TV en vivo",
"HeaderLiveTV": "TV en directo",
"HeaderNextUp": "Siguiendo",
"HeaderRecordingGroups": "Grupos de grabación",
"HomeVideos": "Vídeos de inicio",
"HomeVideos": "Vídeos caseros",
"Inherit": "Heredar",
"ItemAddedWithName": "{0} se ha añadido a la biblioteca",
"ItemRemovedWithName": "{0} se elimina de la biblioteca",
"ItemRemovedWithName": "{0} ha sido eliminado de la biblioteca",
"LabelIpAddressValue": "Dirección IP: {0}",
"LabelRunningTimeValue": "Tiempo de funcionamiento: {0}",
"Latest": "Últimos",
"MessageApplicationUpdated": "Se ha actualizado el servidor Jellyfin",
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "La sección de configuración del servidor {0} ha sido actualizado",
"MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "La sección {0} de configuración del servidor ha sido actualizada",
"MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
"MixedContent": "Contenido mixto",
"Movies": "Peliculas",
"Movies": "Películas",
"Music": "Música",
"MusicVideos": "Videos musicales",
"NameInstallFailed": "{0} installation failed",
"MusicVideos": "Vídeos musicales",
"NameInstallFailed": "{0} error de instalación",
"NameSeasonNumber": "Temporada {0}",
"NameSeasonUnknown": "Season Unknown",
"NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
"NameSeasonUnknown": "Temporada desconocida",
"NewVersionIsAvailable": "Disponible una nueva versión de Jellyfin para descargar.",
"NotificationOptionApplicationUpdateAvailable": "Actualización de la aplicación disponible",
"NotificationOptionApplicationUpdateInstalled": "Actualización de la aplicación instalada",
"NotificationOptionAudioPlayback": "Se inició la reproducción de audio",
@@ -56,13 +56,13 @@
"NotificationOptionPluginInstalled": "Plugin instalado",
"NotificationOptionPluginUninstalled": "Plugin desinstalado",
"NotificationOptionPluginUpdateInstalled": "Actualización del complemento instalada",
"NotificationOptionServerRestartRequired": "Requiere reinicio del servidor",
"NotificationOptionServerRestartRequired": "Se requiere reinicio del servidor",
"NotificationOptionTaskFailed": "Error de tarea programada",
"NotificationOptionUserLockedOut": "Usuario bloqueado",
"NotificationOptionVideoPlayback": "Se inició la reproducción de vídeo",
"NotificationOptionVideoPlaybackStopped": "Reproducción de vídeo detenida",
"Photos": "Fotos",
"Playlists": "Listas reproducción",
"Playlists": "Listas de reproducción",
"Plugin": "Plugin",
"PluginInstalledWithName": "{0} se ha instalado",
"PluginUninstalledWithName": "{0} se ha desinstalado",
@@ -70,16 +70,16 @@
"ProviderValue": "Proveedor: {0}",
"ScheduledTaskFailedWithName": "{0} falló",
"ScheduledTaskStartedWithName": "{0} iniciada",
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Series",
"Songs": "Canciones",
"StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
"SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitleDownloadFailureFromForItem": "Fallo de descarga de subtítulos desde {0} para {1}",
"SubtitlesDownloadedForItem": "Descargar subtítulos para {0}",
"Sync": "Sincronizar",
"System": "Sistema",
"TvShows": "Series TV",
"TvShows": "Series de TV",
"User": "Usuario",
"UserCreatedWithName": "El usuario {0} ha sido creado",
"UserDeletedWithName": "El usuario {0} ha sido borrado",
@@ -88,10 +88,10 @@
"UserOfflineFromDevice": "{0} se ha desconectado de {1}",
"UserOnlineFromDevice": "{0} está en línea desde {1}",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"UserStartedPlayingItemWithValues": "{0} ha comenzado reproducir {1}",
"UserStoppedPlayingItemWithValues": "{0} ha parado de reproducir {1}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"UserPolicyUpdatedWithName": "Actualizada política de usuario para {0}",
"UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
"ValueHasBeenAddedToLibrary": "{0} ha sido añadido a tu biblioteca multimedia",
"ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versión {0}"
}

View File

@@ -36,7 +36,7 @@
"MessageApplicationUpdated": "Le serveur Jellyfin a été mis à jour",
"MessageApplicationUpdatedTo": "Jellyfin Serveur a été mis à jour en version {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "La configuration de la section {0} du serveur a été mise à jour",
"MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour.",
"MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour",
"MixedContent": "Contenu mixte",
"Movies": "Films",
"Music": "Musique",

View File

@@ -5,48 +5,48 @@
"Artists": "Előadók",
"AuthenticationSucceededWithUserName": "{0} sikeresen azonosítva",
"Books": "Könyvek",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"CameraImageUploadedFrom": "Új kamerakép került feltöltésre {0}",
"Channels": "Csatornák",
"ChapterNameValue": "Jelenet {0}",
"Collections": "Gyűjtemények",
"DeviceOfflineWithName": "{0} kijelentkezett",
"DeviceOnlineWithName": "{0} belépett",
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"FailedLoginAttemptWithUserName": "Sikertelen bejelentkezési kísérlet {0}",
"Favorites": "Kedvencek",
"Folders": "Könyvtárak",
"Genres": "Műfajok",
"HeaderAlbumArtists": "Album Előadók",
"HeaderCameraUploads": "Camera Uploads",
"HeaderContinueWatching": "Vetítés(ek) folytatása",
"HeaderCameraUploads": "Kamera feltöltések",
"HeaderContinueWatching": "Folyamatban lévő filmek",
"HeaderFavoriteAlbums": "Kedvenc Albumok",
"HeaderFavoriteArtists": "Kedvenc Művészek",
"HeaderFavoriteEpisodes": "Kedvenc Epizódok",
"HeaderFavoriteShows": "Kedvenc Műsorok",
"HeaderFavoriteSongs": "Kedvenc Dalok",
"HeaderLiveTV": "Live TV",
"HeaderLiveTV": "Élő TV",
"HeaderNextUp": "Következik",
"HeaderRecordingGroups": "Recording Groups",
"HeaderRecordingGroups": "Felvételi csoportok",
"HomeVideos": "Házi videók",
"Inherit": "Inherit",
"ItemAddedWithName": "{0} was added to the library",
"ItemRemovedWithName": "{0} was removed from the library",
"LabelIpAddressValue": "Ip cím: {0}",
"LabelRunningTimeValue": "Running time: {0}",
"ItemAddedWithName": "{0} hozzáadva a könyvtárhoz",
"ItemRemovedWithName": "{0} eltávolítva a könyvtárból",
"LabelIpAddressValue": "IP cím: {0}",
"LabelRunningTimeValue": "Futási idő: {0}",
"Latest": "Legújabb",
"MessageApplicationUpdated": "Jellyfin Szerver frissítve",
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
"MessageApplicationUpdatedTo": "Jellyfin Szerver frissítve lett a következőre {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Szerver konfigurációs rész {0} frissítve",
"MessageServerConfigurationUpdated": "Szerver konfiguráció frissítve",
"MixedContent": "Vegyes tartalom",
"Movies": "Filmek",
"Music": "Zene",
"MusicVideos": "Zenei Videók",
"NameInstallFailed": "{0} installation failed",
"NameSeasonNumber": "Season {0}",
"NameSeasonUnknown": "Season Unknown",
"NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
"NotificationOptionApplicationUpdateAvailable": "Program frissítés elérhető",
"NotificationOptionApplicationUpdateInstalled": "Program frissítés telepítve",
"NameInstallFailed": "{0} sikertelen telepítés",
"NameSeasonNumber": "Évad {0}",
"NameSeasonUnknown": "Ismeretlen évad",
"NewVersionIsAvailable": "Letölthető a Jellyfin Szerver új verziója.",
"NotificationOptionApplicationUpdateAvailable": "Új programfrissítés érhető el",
"NotificationOptionApplicationUpdateInstalled": "Programfrissítés telepítve",
"NotificationOptionAudioPlayback": "Audió lejátszás elkezdve",
"NotificationOptionAudioPlaybackStopped": "Audió lejátszás befejezve",
"NotificationOptionCameraImageUploaded": "Kamera kép feltöltve",
@@ -57,7 +57,7 @@
"NotificationOptionPluginUninstalled": "Bővítmény eltávolítva",
"NotificationOptionPluginUpdateInstalled": "Bővítmény frissítés telepítve",
"NotificationOptionServerRestartRequired": "Szerver újraindítás szükséges",
"NotificationOptionTaskFailed": "Scheduled task failure",
"NotificationOptionTaskFailed": "Ütemezett feladat hiba",
"NotificationOptionUserLockedOut": "Felhasználó tiltva",
"NotificationOptionVideoPlayback": "Videó lejátszás elkezdve",
"NotificationOptionVideoPlaybackStopped": "Videó lejátszás befejezve",
@@ -68,30 +68,30 @@
"PluginUninstalledWithName": "{0} eltávolítva",
"PluginUpdatedWithName": "{0} frissítve",
"ProviderValue": "Provider: {0}",
"ScheduledTaskFailedWithName": "{0} failed",
"ScheduledTaskStartedWithName": "{0} started",
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"ScheduledTaskFailedWithName": "{0} hiba",
"ScheduledTaskStartedWithName": "{0} elkezdve",
"ServerNameNeedsToBeRestarted": "{0}-t újra kell indítani",
"Shows": "Műsorok",
"Songs": "Dalok",
"StartupEmbyServerIsLoading": "Jellyfin Szerver betöltődik. Kérjük, próbáld meg újra később.",
"StartupEmbyServerIsLoading": "A Jellyfin Szerver betöltődik. Kérlek próbáld újra később.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
"SubtitleDownloadFailureFromForItem": "Nem sikerült a felirat letöltése innen: {0} ehhez: {1}",
"SubtitlesDownloadedForItem": "Letöltött feliratok a következőhöz {0}",
"Sync": "Szinkronizál",
"System": "Rendszer",
"TvShows": "TV Műsorok",
"User": "Felhasználó",
"UserCreatedWithName": "User {0} has been created",
"UserDeletedWithName": "User {0} has been deleted",
"UserCreatedWithName": "{0} felhasználó létrehozva",
"UserDeletedWithName": "{0} felhasználó törölve",
"UserDownloadingItemWithValues": "{0} letölti {1}",
"UserLockedOutWithName": "User {0} has been locked out",
"UserOfflineFromDevice": "{0} kijelentkezett innen {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} elkezdte játszani a következőt {1}",
"UserStoppedPlayingItemWithValues": "{0} befejezte a következőt {1}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"UserLockedOutWithName": "{0} felhasználó zárolva van",
"UserOfflineFromDevice": "{0} kijelentkezett innen: {1}",
"UserOnlineFromDevice": "{0} online itt: {1}",
"UserPasswordChangedWithName": "Jelszó megváltozott a következő felhasználó számára: {0}",
"UserPolicyUpdatedWithName": "A felhasználói házirend frissítve lett {0}",
"UserStartedPlayingItemWithValues": "{0} elkezdte játszani a következőt: {1} itt: {2}",
"UserStoppedPlayingItemWithValues": "{0} befejezte a következőt: {1} itt: {2}",
"ValueHasBeenAddedToLibrary": "{0} hozzáadva a médiatárhoz",
"ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Verzió {0}"
"VersionNumber": "Verzió: {0}"
}

View File

@@ -5,13 +5,13 @@
"Artists": "Artisti",
"AuthenticationSucceededWithUserName": "{0} autenticato con successo",
"Books": "Libri",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera {0}",
"Channels": "Canali",
"ChapterNameValue": "Capitolo {0}",
"Collections": "Collezioni",
"DeviceOfflineWithName": "{0} è stato disconnesso",
"DeviceOnlineWithName": "{0} è connesso",
"FailedLoginAttemptWithUserName": "Tentativo di accesso fallito da {0}",
"FailedLoginAttemptWithUserName": "Tentativo di accesso fallito da {0}",
"Favorites": "Preferiti",
"Folders": "Cartelle",
"Genres": "Generi",
@@ -19,9 +19,9 @@
"HeaderCameraUploads": "Caricamenti Fotocamera",
"HeaderContinueWatching": "Continua a guardare",
"HeaderFavoriteAlbums": "Album preferiti",
"HeaderFavoriteArtists": "Artisti preferiti",
"HeaderFavoriteArtists": "Artisti Preferiti",
"HeaderFavoriteEpisodes": "Episodi Preferiti",
"HeaderFavoriteShows": "Show preferiti",
"HeaderFavoriteShows": "Serie TV Preferite",
"HeaderFavoriteSongs": "Brani Preferiti",
"HeaderLiveTV": "Diretta TV",
"HeaderNextUp": "Prossimo",

View File

@@ -1,97 +1,97 @@
{
"Albums": "Альбомдар",
"AppDeviceValues": "Қолданба: {0}, Құрылғы: {1}",
"Application": "Қолданба",
"Artists": "Орындаушылар",
"AuthenticationSucceededWithUserName": "{0} түпнұсқалығын расталуы сәтті",
"Books": "Кітаптар",
"CameraImageUploadedFrom": "Жаңа сурет {0} камерасынан жүктеп алынды",
"Channels": "Арналар",
"ChapterNameValue": "{0}-сахна",
"Collections": "Жиынтықтар",
"DeviceOfflineWithName": "{0} ажыратылған",
"DeviceOnlineWithName": "{0} қосылған",
"FailedLoginAttemptWithUserName": "{0} тарапынан кіру әрекеті сәтсіз",
"Favorites": "Таңдаулылар",
"Folders": "Қалталар",
"Genres": "Жанрлар",
"HeaderAlbumArtists": "Альбом орындаушылары",
"HeaderCameraUploads": "Камерадан жүктелгендер",
"HeaderContinueWatching": "Қарауды жалғастыру",
"HeaderFavoriteAlbums": "Таңдаулы альбомдар",
"HeaderFavoriteArtists": "Таңдаулы орындаушылар",
"HeaderFavoriteEpisodes": "Таңдаулы бөлімдер",
"HeaderFavoriteShows": "Таңдаулы көрсетімдер",
"HeaderFavoriteSongs": "Таңдаулы әуендер",
"HeaderLiveTV": "Эфир",
"HeaderNextUp": "Кезекті",
"HeaderRecordingGroups": "Жазба топтары",
"HomeVideos": "Үйлік бейнелер",
"Inherit": "Мұраға иелену",
"ItemAddedWithName": "{0} тасығышханаға үстелінді",
"ItemRemovedWithName": "{0} тасығышханадан аласталды",
"LabelIpAddressValue": "IP-мекенжайы: {0}",
"LabelRunningTimeValue": "Іске қосылу уақыты: {0}",
"Latest": "Ең кейінгі",
"MessageApplicationUpdated": "Jellyfin Server жаңартылды.",
"MessageApplicationUpdatedTo": "Jellyfin Server {0} үшін жаңартылды",
"MessageNamedServerConfigurationUpdatedWithValue": "Сервер теңшелімі ({0} бөлімі) жаңартылды",
"MessageServerConfigurationUpdated": "Сервер теңшелімі жаңартылды",
"MixedContent": "Аралас мазмұн",
"Movies": "Фильмдер",
"Music": "Музыка",
"MusicVideos": "Музыкалық бейнелер",
"NameInstallFailed": "{0} орнатылуы сәтсіз",
"NameSeasonNumber": "{0}-маусым",
"NameSeasonUnknown": "Белгісіз маусым",
"NewVersionIsAvailable": "Жаңа Jellyfin Server нұсқасы жүктеп алуға қолжетімді.",
"NotificationOptionApplicationUpdateAvailable": "Қолданба жаңартуы қолжетімді",
"NotificationOptionApplicationUpdateInstalled": "Қолданба жаңартуы орнатылды",
"NotificationOptionAudioPlayback": "Дыбыс ойнатуы басталды",
"NotificationOptionAudioPlaybackStopped": "Дыбыс ойнатуы тоқтатылды",
"NotificationOptionCameraImageUploaded": "Камерадан фотосурет кері қотарылған",
"NotificationOptionInstallationFailed": "Орнату сәтсіздігі",
"NotificationOptionNewLibraryContent": "Жаңа мазмұн үстелген",
"NotificationOptionPluginError": "Плагин сәтсіздігі",
"NotificationOptionPluginInstalled": "Плагин орнатылды",
"NotificationOptionPluginUninstalled": "Плагин орнатуы болдырылмады",
"NotificationOptionPluginUpdateInstalled": "Плагин жаңартуы орнатылды",
"NotificationOptionServerRestartRequired": "Серверді қайта іске қосу қажет",
"NotificationOptionTaskFailed": "Жоспарлаған тапсырма сәтсіздігі",
"NotificationOptionUserLockedOut": "Пайдаланушы құрсаулы",
"NotificationOptionVideoPlayback": "Бейне ойнатуы басталды",
"NotificationOptionVideoPlaybackStopped": "Бейне ойнатуы тоқтатылды",
"Photos": "Фотосуреттер",
"Playlists": "Ойнату тізімдері",
"Plugin": "Плагин",
"PluginInstalledWithName": "{0} орнатылды",
"PluginUninstalledWithName": "{0} жойылды",
"PluginUpdatedWithName": "{0} жаңартылды",
"ProviderValue": "Жеткізуші: {0}",
"ScheduledTaskFailedWithName": "{0} сәтсіз",
"ScheduledTaskStartedWithName": "{0} іске қосылды",
"ServerNameNeedsToBeRestarted": "{0} қайта іске қосу қажет",
"Shows": "Көрсетімдер",
"Songs": "Әуендер",
"StartupEmbyServerIsLoading": "Jellyfin Server жүктелуде. Әрекетті көп ұзамай қайталаңыз.",
"Albums": "Álbomdar",
"AppDeviceValues": "Qoldanba: {0}, Qurylǵy: {1}",
"Application": "Qoldanba",
"Artists": "Oryndaýshylar",
"AuthenticationSucceededWithUserName": "{0} túpnusqalyǵyn rastalýy sátti",
"Books": "Kitaptar",
"CameraImageUploadedFrom": "Jańa sýret {0} kamerasynan júktep alyndy",
"Channels": "Arnalar",
"ChapterNameValue": "{0}-sahna",
"Collections": "Jıyntyqtar",
"DeviceOfflineWithName": "{0} ajyratylǵan",
"DeviceOnlineWithName": "{0} qosylǵan",
"FailedLoginAttemptWithUserName": "{0} tarapynan kirý áreketi sátsiz",
"Favorites": "Tańdaýlylar",
"Folders": "Qaltalar",
"Genres": "Janrlar",
"HeaderAlbumArtists": "Álbom oryndaýshylary",
"HeaderCameraUploads": "Kameradan júktelgender",
"HeaderContinueWatching": "Qaraýdy jalǵastyrý",
"HeaderFavoriteAlbums": "Tańdaýly álbomdar",
"HeaderFavoriteArtists": "Tańdaýly oryndaýshylar",
"HeaderFavoriteEpisodes": "Tańdaýly bólimder",
"HeaderFavoriteShows": "Tańdaýly kórsetimder",
"HeaderFavoriteSongs": "Tańdaýly áýender",
"HeaderLiveTV": "Efır",
"HeaderNextUp": "Kezekti",
"HeaderRecordingGroups": "Jazba toptary",
"HomeVideos": "Úılik beıneler",
"Inherit": "Muraǵa ıelený",
"ItemAddedWithName": "{0} tasyǵyshhanaǵa ústelindi",
"ItemRemovedWithName": "{0} tasyǵyshhanadan alastaldy",
"LabelIpAddressValue": "IP-mekenjaıy: {0}",
"LabelRunningTimeValue": "Oınatý ýaqyty: {0}",
"Latest": "Eń keıingi",
"MessageApplicationUpdated": "Jellyfin Serveri jańartyldy",
"MessageApplicationUpdatedTo": "Jellyfin Serveri {0} deńgeıge jańartyldy",
"MessageNamedServerConfigurationUpdatedWithValue": "Server teńsheliminiń {0} bólimi jańartyldy",
"MessageServerConfigurationUpdated": "Server teńshelimi jańartyldy",
"MixedContent": "Aralas mazmun",
"Movies": "Fılmder",
"Music": "Mýzyka",
"MusicVideos": "Mýzykalyq beıneler",
"NameInstallFailed": "{0} ornatylýy sátsiz",
"NameSeasonNumber": "{0}-maýsym",
"NameSeasonUnknown": "Belgisiz maýsym",
"NewVersionIsAvailable": "Jańa Jellyfin Server nusqasy júktep alýǵa qoljetimdi.",
"NotificationOptionApplicationUpdateAvailable": "Qoldanba jańartýy qoljetimdi",
"NotificationOptionApplicationUpdateInstalled": "Qoldanba jańartýy ornatyldy",
"NotificationOptionAudioPlayback": "Dybys oınatýy bastaldy",
"NotificationOptionAudioPlaybackStopped": "Dybys oınatýy toqtatyldy",
"NotificationOptionCameraImageUploaded": "Kameradan fotosýret keri qotarylǵan",
"NotificationOptionInstallationFailed": "Ornatý sátsizdigi",
"NotificationOptionNewLibraryContent": "Jańa mazmun ústelgen",
"NotificationOptionPluginError": "Plagın sátsizdigi",
"NotificationOptionPluginInstalled": "Plagın ornatyldy",
"NotificationOptionPluginUninstalled": "Plagın ornatýy boldyrylmady",
"NotificationOptionPluginUpdateInstalled": "Plagın jańartýy ornatyldy",
"NotificationOptionServerRestartRequired": "Serverdi qaıta iske qosý qajet",
"NotificationOptionTaskFailed": "Josparlaǵan tapsyrma sátsizdigi",
"NotificationOptionUserLockedOut": "Paıdalanýshy qursaýly",
"NotificationOptionVideoPlayback": "Beıne oınatýy bastaldy",
"NotificationOptionVideoPlaybackStopped": "Beıne oınatýy toqtatyldy",
"Photos": "Fotosýretter",
"Playlists": "Oınatý tizimderi",
"Plugin": "Plagın",
"PluginInstalledWithName": "{0} ornatyldy",
"PluginUninstalledWithName": "{0} joıyldy",
"PluginUpdatedWithName": "{0} jańartyldy",
"ProviderValue": "Jetkizýshi: {0}",
"ScheduledTaskFailedWithName": "{0} sátsiz",
"ScheduledTaskStartedWithName": "{0} iske qosyldy",
"ServerNameNeedsToBeRestarted": "{0} qaıta iske qosý qajet",
"Shows": "Kórsetimder",
"Songs": "Áýender",
"StartupEmbyServerIsLoading": "Jellyfin Server júktelýde. Áreketti kóp uzamaı qaıtalańyz.",
"SubtitleDownloadFailureForItem": "Субтитрлер {0} үшін жүктеліп алынуы сәтсіз",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitlesDownloadedForItem": "{0} үшін субтитрлер жүктеліп алынды",
"Sync": "Үндестіру",
"System": "Жүйе",
"TvShows": "ТД-көрсетімдер",
"User": "Пайдаланушы",
"UserCreatedWithName": "Пайдаланушы {0} жасалған",
"UserDeletedWithName": "Пайдаланушы {0} жойылған",
"UserDownloadingItemWithValues": "{0} мынаны жүктеп алуда: {1}",
"UserLockedOutWithName": "Пайдаланушы {0} құрсаулы",
"UserOfflineFromDevice": "{0} - {1} тарапынан ажыратылған",
"UserOnlineFromDevice": "{0} - {1} арқылы қосылған",
"UserPasswordChangedWithName": "Пайдаланушы {0} үшін құпия сөз өзгертілді",
"UserPolicyUpdatedWithName": "Пайдаланушы {0} үшін саясаттары жаңартылды",
"UserStartedPlayingItemWithValues": "{0} - {1} ойнатуын {2} бастады",
"UserStoppedPlayingItemWithValues": "{0} - {1} ойнатуын {2} тоқтатты",
"ValueHasBeenAddedToLibrary": "{0} (тасығышханаға үстелінді)",
"ValueSpecialEpisodeName": "Арнайы - {0}",
"VersionNumber": "Нұсқасы: {0}"
"SubtitleDownloadFailureFromForItem": "{1} úshin sýbtıtrlerdi {0} kózinen júktep alý sátsiz",
"SubtitlesDownloadedForItem": "{0} úshin sýbtıtrler júktelip alyndy",
"Sync": "Úndestirý",
"System": "ıe",
"TvShows": "TD-kórsetimder",
"User": "Paıdalanýshy",
"UserCreatedWithName": "Paıdalanýshy {0} jasalǵan",
"UserDeletedWithName": "Paıdalanýshy {0} joıylǵan",
"UserDownloadingItemWithValues": "{0} mynany júktep alýda: {1}",
"UserLockedOutWithName": "Paıdalanýshy {0} qursaýly",
"UserOfflineFromDevice": "{0} - {1} tarapynan ajyratylǵan",
"UserOnlineFromDevice": "{0} - {1} arqyly qosylǵan",
"UserPasswordChangedWithName": "Paıdalanýshy {0} úshin paról ózgertildi",
"UserPolicyUpdatedWithName": "Paıdalanýshy {0} úshin saıasattary jańartyldy",
"UserStartedPlayingItemWithValues": "{0} - {1} oınatýyn {2} bastady",
"UserStoppedPlayingItemWithValues": "{0} - {1} oınatýyn {2} toqtatty",
"ValueHasBeenAddedToLibrary": "{0} (tasyǵyshhanaǵa ústelindi)",
"ValueSpecialEpisodeName": "Arnaıy - {0}",
"VersionNumber": "Nusqasy {0}"
}

View File

@@ -1,10 +1,10 @@
{
"Albums": "Albums",
"Albums": "Album-album",
"AppDeviceValues": "App: {0}, Device: {1}",
"Application": "Application",
"Artists": "Artists",
"Artists": "Artis-artis",
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"Books": "Books",
"Books": "Buku-buku",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"Channels": "Channels",
"ChapterNameValue": "Chapter {0}",

View File

@@ -5,28 +5,28 @@
"Artists": "Artiesten",
"AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd",
"Books": "Boeken",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"CameraImageUploadedFrom": "Er is een nieuwe foto toegevoegd via {0}",
"Channels": "Kanalen",
"ChapterNameValue": "Hoofdstuk {0}",
"Collections": "Collecties",
"DeviceOfflineWithName": "{0} is losgekoppeld",
"DeviceOfflineWithName": "{0} heeft de verbinding verbroken",
"DeviceOnlineWithName": "{0} is verbonden",
"FailedLoginAttemptWithUserName": "Mislukte aanmeld poging van {0}",
"Favorites": "Favorieten",
"Folders": "Mappen",
"Genres": "Genres",
"HeaderAlbumArtists": "Album artiesten",
"HeaderCameraUploads": "Camera uploads",
"HeaderAlbumArtists": "Albumartiesten",
"HeaderCameraUploads": "Camera-uploads",
"HeaderContinueWatching": "Kijken hervatten",
"HeaderFavoriteAlbums": "Favoriete albums",
"HeaderFavoriteArtists": "Favoriete artiesten",
"HeaderFavoriteEpisodes": "Favoriete afleveringen",
"HeaderFavoriteShows": "Favoriete shows",
"HeaderFavoriteSongs": "Favoriete titels",
"HeaderFavoriteSongs": "Favoriete nummers",
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Volgende",
"HeaderRecordingGroups": "Opnamegroepen",
"HomeVideos": "Thuis video's",
"HomeVideos": "Start video's",
"Inherit": "Overerven",
"ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek",
"ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek",
@@ -34,22 +34,22 @@
"LabelRunningTimeValue": "Looptijd: {0}",
"Latest": "Nieuwste",
"MessageApplicationUpdated": "Jellyfin Server is bijgewerkt",
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
"MessageApplicationUpdatedTo": "Jellyfin Server is bijgewerkt naar {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Sectie {0} van de server configuratie is bijgewerkt",
"MessageServerConfigurationUpdated": "Server configuratie is bijgewerkt",
"MixedContent": "Gemengde inhoud",
"Movies": "Films",
"Music": "Muziek",
"MusicVideos": "Muziekvideo's",
"NameInstallFailed": "{0} installation failed",
"NameInstallFailed": "{0} installatie mislukt",
"NameSeasonNumber": "Seizoen {0}",
"NameSeasonUnknown": "Seizoen onbekend",
"NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
"NewVersionIsAvailable": "Een nieuwe versie van Jellyfin Server is beschikbaar om te downloaden.",
"NotificationOptionApplicationUpdateAvailable": "Programma-update beschikbaar",
"NotificationOptionApplicationUpdateInstalled": "Programma-update geïnstalleerd",
"NotificationOptionAudioPlayback": "Geluid gestart",
"NotificationOptionAudioPlaybackStopped": "Geluid gestopt",
"NotificationOptionCameraImageUploaded": "Camera afbeelding geüpload",
"NotificationOptionAudioPlayback": "Muziek gestart",
"NotificationOptionAudioPlaybackStopped": "Muziek gestopt",
"NotificationOptionCameraImageUploaded": "Camera-afbeelding geüpload",
"NotificationOptionInstallationFailed": "Installatie mislukt",
"NotificationOptionNewLibraryContent": "Nieuwe content toegevoegd",
"NotificationOptionPluginError": "Plug-in fout",
@@ -70,12 +70,12 @@
"ProviderValue": "Aanbieder: {0}",
"ScheduledTaskFailedWithName": "{0} is mislukt",
"ScheduledTaskStartedWithName": "{0} is gestart",
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"ServerNameNeedsToBeRestarted": "{0} moet herstart worden",
"Shows": "Series",
"Songs": "Titels",
"Songs": "Nummers",
"StartupEmbyServerIsLoading": "Jellyfin Server is aan het laden, probeer het later opnieuw.",
"SubtitleDownloadFailureForItem": "Downloaden van ondertiteling voor {0} is mislukt",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitleDownloadFailureFromForItem": "Ondertitels konden niet gedownload worden van {0} voor {1}",
"SubtitlesDownloadedForItem": "Ondertiteling voor {0} is gedownload",
"Sync": "Synchronisatie",
"System": "Systeem",
@@ -89,9 +89,9 @@
"UserOnlineFromDevice": "{0} heeft verbinding met {1}",
"UserPasswordChangedWithName": "Wachtwoord voor {0} is gewijzigd",
"UserPolicyUpdatedWithName": "Gebruikersbeleid gewijzigd voor {0}",
"UserStartedPlayingItemWithValues": "{0} heeft afspelen van {1} gestart",
"UserStoppedPlayingItemWithValues": "{0} heeft afspelen van {1} gestopt",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"UserStartedPlayingItemWithValues": "{0} heeft afspelen van {1} gestart op {2}",
"UserStoppedPlayingItemWithValues": "{0} heeft afspelen van {1} gestopt op {2}",
"ValueHasBeenAddedToLibrary": "{0} is toegevoegd aan je mediabibliotheek",
"ValueSpecialEpisodeName": "Speciaal - {0}",
"VersionNumber": "Versie {0}"
}

View File

@@ -5,7 +5,7 @@
"Artists": "Исполнители",
"AuthenticationSucceededWithUserName": "{0} - авторизация успешна",
"Books": "Литература",
"CameraImageUploadedFrom": "Новое фото было выложено с {0}",
"CameraImageUploadedFrom": "Новое фото было выложено с камеры {0}",
"Channels": "Каналы",
"ChapterNameValue": "Сцена {0}",
"Collections": "Коллекции",
@@ -31,20 +31,20 @@
"ItemAddedWithName": "{0} - добавлено в медиатеку",
"ItemRemovedWithName": "{0} - изъято из медиатеки",
"LabelIpAddressValue": "IP-адрес: {0}",
"LabelRunningTimeValue": "Время выполнения: {0}",
"LabelRunningTimeValue": "Длительность: {0}",
"Latest": "Новейшее",
"MessageApplicationUpdated": "Jellyfin Server был обновлён",
"MessageApplicationUpdatedTo": "Jellyfin Server был обновлён до {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Конфиг-ия сервера (раздел {0}) была обновлена",
"MessageServerConfigurationUpdated": "Конфиг-ия сервера была обновлена",
"MixedContent": "Смешанное содержание",
"MixedContent": "Смешанное содержимое",
"Movies": "Кино",
"Music": "Музыка",
"MusicVideos": "Муз. видео",
"NameInstallFailed": "Установка {0} неудачна",
"NameSeasonNumber": "Сезон {0}",
"NameSeasonUnknown": "Сезон неопознан",
"NewVersionIsAvailable": "Имеется новая версия Jellyfin Server",
"NewVersionIsAvailable": "Новая версия Jellyfin Server доступна для загрузки.",
"NotificationOptionApplicationUpdateAvailable": "Имеется обновление приложения",
"NotificationOptionApplicationUpdateInstalled": "Обновление приложения установлено",
"NotificationOptionAudioPlayback": "Воспр-ие аудио зап-но",
@@ -75,7 +75,7 @@
"Songs": "Композиции",
"StartupEmbyServerIsLoading": "Jellyfin Server загружается. Повторите попытку в ближайшее время.",
"SubtitleDownloadFailureForItem": "Субтитры к {0} не удалось загрузить",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitleDownloadFailureFromForItem": "Субтитры к {1} не удалось загрузить с {0}",
"SubtitlesDownloadedForItem": "Субтитры к {0} загружены",
"Sync": "Синхро",
"System": "Система",

View File

@@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <summary>
/// Class ChapterImagesTask
/// </summary>
class ChapterImagesTask : IScheduledTask
public class ChapterImagesTask : IScheduledTask
{
/// <summary>
/// The _logger

View File

@@ -41,6 +41,27 @@ namespace Emby.Server.Implementations.Serialization
ServiceStack.Text.JsonSerializer.SerializeToStream(obj, obj.GetType(), stream);
}
/// <summary>
/// Serializes to stream.
/// </summary>
/// <param name="obj">The obj.</param>
/// <param name="stream">The stream.</param>
/// <exception cref="ArgumentNullException">obj</exception>
public void SerializeToStream<T>(T obj, Stream stream)
{
if (obj == null)
{
throw new ArgumentNullException(nameof(obj));
}
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
ServiceStack.Text.JsonSerializer.SerializeToStream<T>(obj, stream);
}
/// <summary>
/// Serializes to file.
/// </summary>

View File

@@ -6,7 +6,7 @@ using MediaBrowser.Model.Querying;
namespace Emby.Server.Implementations.Sorting
{
class AiredEpisodeOrderComparer : IBaseItemComparer
public class AiredEpisodeOrderComparer : IBaseItemComparer
{
/// <summary>
/// Compares the specified x.

View File

@@ -5,7 +5,7 @@ using MediaBrowser.Model.Querying;
namespace Emby.Server.Implementations.Sorting
{
class SeriesSortNameComparer : IBaseItemComparer
public class SeriesSortNameComparer : IBaseItemComparer
{
/// <summary>
/// Compares the specified x.

View File

@@ -116,6 +116,7 @@ namespace Emby.Server.Implementations.Updates
private readonly IApplicationHost _applicationHost;
private readonly ICryptoProvider _cryptographyProvider;
private readonly IZipClient _zipClient;
// netframework or netcore
private readonly string _packageRuntime;
@@ -129,6 +130,7 @@ namespace Emby.Server.Implementations.Updates
IServerConfigurationManager config,
IFileSystem fileSystem,
ICryptoProvider cryptographyProvider,
IZipClient zipClient,
string packageRuntime)
{
if (loggerFactory == null)
@@ -146,6 +148,7 @@ namespace Emby.Server.Implementations.Updates
_config = config;
_fileSystem = fileSystem;
_cryptographyProvider = cryptographyProvider;
_zipClient = zipClient;
_packageRuntime = packageRuntime;
_logger = loggerFactory.CreateLogger(nameof(InstallationManager));
}
@@ -526,14 +529,18 @@ namespace Emby.Server.Implementations.Updates
private async Task PerformPackageInstallation(IProgress<double> progress, string target, PackageVersionInfo package, CancellationToken cancellationToken)
{
// Target based on if it is an archive or single assembly
// zip archives are assumed to contain directory structures relative to our ProgramDataPath
var extension = Path.GetExtension(package.targetFilename);
var isArchive = string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".rar", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".7z", StringComparison.OrdinalIgnoreCase);
var isArchive = string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase);
if (!isArchive)
{
_logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.targetFilename);
return;
}
if (target == null)
{
target = Path.Combine(isArchive ? _appPaths.TempUpdatePath : _appPaths.PluginsPath, package.targetFilename);
target = Path.Combine(_appPaths.PluginsPath, Path.GetFileNameWithoutExtension(package.targetFilename));
}
// Download to temporary file so that, if interrupted, it won't destroy the existing installation
@@ -547,36 +554,19 @@ namespace Emby.Server.Implementations.Updates
cancellationToken.ThrowIfCancellationRequested();
// Validate with a checksum
var packageChecksum = string.IsNullOrWhiteSpace(package.checksum) ? Guid.Empty : new Guid(package.checksum);
if (!packageChecksum.Equals(Guid.Empty)) // support for legacy uploads for now
{
using (var stream = File.OpenRead(tempFile))
{
var check = Guid.Parse(BitConverter.ToString(_cryptographyProvider.ComputeMD5(stream)).Replace("-", string.Empty));
if (check != packageChecksum)
{
throw new Exception(string.Format("Download validation failed for {0}. Probably corrupted during transfer.", package.name));
}
}
}
cancellationToken.ThrowIfCancellationRequested();
// TODO: Validate with a checksum, *properly*
// Success - move it to the real target
try
{
Directory.CreateDirectory(Path.GetDirectoryName(target));
File.Copy(tempFile, target, true);
//If it is an archive - write out a version file so we know what it is
if (isArchive)
using (var stream = File.OpenRead(tempFile))
{
File.WriteAllText(target + ".ver", package.versionStr);
_zipClient.ExtractAllFromZip(stream, target, true);
}
}
catch (IOException ex)
{
_logger.LogError(ex, "Error attempting to move file from {TempFile} to {TargetFile}", tempFile, target);
_logger.LogError(ex, "Error attempting to extract {TempFile} to {TargetFile}", tempFile, target);
throw;
}