mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-04-20 17:14:42 +01:00
Merge branch 'master' into update_tvdb
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
12
Emby.Server.Implementations/ConfigurationOptions.cs
Normal file
12
Emby.Server.Implementations/ConfigurationOptions.cs
Normal 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"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>>();
|
||||
|
||||
@@ -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].
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}"
|
||||
}
|
||||
|
||||
@@ -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}"
|
||||
}
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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}"
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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}"
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "Júı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}"
|
||||
}
|
||||
|
||||
@@ -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}",
|
||||
|
||||
@@ -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}"
|
||||
}
|
||||
|
||||
@@ -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": "Система",
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
|
||||
/// <summary>
|
||||
/// Class ChapterImagesTask
|
||||
/// </summary>
|
||||
class ChapterImagesTask : IScheduledTask
|
||||
public class ChapterImagesTask : IScheduledTask
|
||||
{
|
||||
/// <summary>
|
||||
/// The _logger
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user