diff options
199 files changed, 1930 insertions, 1462 deletions
diff --git a/BDInfo/BDROM.cs b/BDInfo/BDROM.cs index 123d1afe5..d86648364 100644 --- a/BDInfo/BDROM.cs +++ b/BDInfo/BDROM.cs @@ -96,7 +96,7 @@ namespace BDInfo } DirectoryRoot = - _fileSystem.GetDirectoryInfo(Path.GetDirectoryName(DirectoryBDMV.FullName)); + _fileSystem.GetDirectoryInfo(_fileSystem.GetDirectoryName(DirectoryBDMV.FullName)); DirectoryBDJO = GetDirectory("BDJO", DirectoryBDMV, 0); DirectoryCLIPINF = @@ -349,7 +349,7 @@ namespace BDInfo { return dir; } - var parentFolder = Path.GetDirectoryName(dir.FullName); + var parentFolder = _fileSystem.GetDirectoryName(dir.FullName); if (string.IsNullOrEmpty(parentFolder)) { dir = null; diff --git a/Emby.Common.Implementations/Devices/DeviceId.cs b/Emby.Common.Implementations/Devices/DeviceId.cs index 3d23ab872..1de76456c 100644 --- a/Emby.Common.Implementations/Devices/DeviceId.cs +++ b/Emby.Common.Implementations/Devices/DeviceId.cs @@ -57,7 +57,7 @@ namespace Emby.Common.Implementations.Devices { var path = CachePath; - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); lock (_syncLock) { diff --git a/Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs b/Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs index ad6e35700..27fc642f1 100644 --- a/Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs +++ b/Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs @@ -131,4 +131,4 @@ namespace Emby.Common.Implementations.EnvironmentInfo Environment.SetEnvironmentVariable(name, value); } } -} +}
\ No newline at end of file diff --git a/Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs index 23f33f06c..eb9bc1bd0 100644 --- a/Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -418,7 +418,7 @@ namespace Emby.Common.Implementations.HttpClientManager private async Task CacheResponse(HttpResponseInfo response, string responseCachePath) { - _fileSystem.CreateDirectory(Path.GetDirectoryName(responseCachePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(responseCachePath)); using (var responseStream = response.Content) { diff --git a/Emby.Common.Implementations/IO/ManagedFileSystem.cs b/Emby.Common.Implementations/IO/ManagedFileSystem.cs index ba73c1ba2..3ed4f650f 100644 --- a/Emby.Common.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Common.Implementations/IO/ManagedFileSystem.cs @@ -546,24 +546,6 @@ namespace Emby.Common.Implementations.IO return Path.DirectorySeparatorChar; } - public bool AreEqual(string path1, string path2) - { - if (path1 == null && path2 == null) - { - return true; - } - - if (path1 == null || path2 == null) - { - return false; - } - - path1 = path1.TrimEnd(GetDirectorySeparatorChar(path1)); - path2 = path2.TrimEnd(GetDirectorySeparatorChar(path2)); - - return string.Equals(path1, path2, StringComparison.OrdinalIgnoreCase); - } - public bool ContainsSubPath(string parentPath, string path) { if (string.IsNullOrEmpty(parentPath)) @@ -588,7 +570,7 @@ namespace Emby.Common.Implementations.IO throw new ArgumentNullException("path"); } - var parent = Path.GetDirectoryName(path); + var parent = GetDirectoryName(path); if (!string.IsNullOrEmpty(parent)) { @@ -598,6 +580,16 @@ namespace Emby.Common.Implementations.IO return true; } + public string GetDirectoryName(string path) + { + if (_sharpCifsFileSystem.IsEnabledForPath(path)) + { + return _sharpCifsFileSystem.GetDirectoryName(path); + } + + return Path.GetDirectoryName(path); + } + public string NormalizePath(string path) { if (string.IsNullOrEmpty(path)) @@ -605,6 +597,11 @@ namespace Emby.Common.Implementations.IO throw new ArgumentNullException("path"); } + if (_sharpCifsFileSystem.IsEnabledForPath(path)) + { + return _sharpCifsFileSystem.NormalizePath(path); + } + if (path.EndsWith(":\\", StringComparison.OrdinalIgnoreCase)) { return path; @@ -613,6 +610,21 @@ namespace Emby.Common.Implementations.IO return path.TrimEnd(GetDirectorySeparatorChar(path)); } + public bool AreEqual(string path1, string path2) + { + if (path1 == null && path2 == null) + { + return true; + } + + if (path1 == null || path2 == null) + { + return false; + } + + return string.Equals(NormalizePath(path1), NormalizePath(path2), StringComparison.OrdinalIgnoreCase); + } + public string GetFileNameWithoutExtension(FileSystemMetadata info) { if (info.IsDirectory) @@ -637,11 +649,17 @@ namespace Emby.Common.Implementations.IO // Cannot use Path.IsPathRooted because it returns false under mono when using windows-based paths, e.g. C:\\ + if (_sharpCifsFileSystem.IsEnabledForPath(path)) + { + return true; + } + if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) != -1 && !path.StartsWith("file://", StringComparison.OrdinalIgnoreCase)) { return false; } + return true; //return Path.IsPathRooted(path); diff --git a/Emby.Common.Implementations/IO/SharpCifsFileSystem.cs b/Emby.Common.Implementations/IO/SharpCifsFileSystem.cs index c1e429dc9..0a407d64f 100644 --- a/Emby.Common.Implementations/IO/SharpCifsFileSystem.cs +++ b/Emby.Common.Implementations/IO/SharpCifsFileSystem.cs @@ -30,6 +30,34 @@ namespace Emby.Common.Implementations.IO return path.StartsWith("smb://", StringComparison.OrdinalIgnoreCase) || IsUncPath(path); } + public string NormalizePath(string path) + { + if (path.StartsWith("smb://", StringComparison.OrdinalIgnoreCase)) + { + return path; + } + + if (IsUncPath(path)) + { + return ConvertUncToSmb(path); + } + + return path; + } + + public string GetDirectoryName(string path) + { + var separator = GetDirectorySeparatorChar(path); + var result = Path.GetDirectoryName(path); + + if (separator == '/') + { + result = result.Replace('\\', '/'); + } + + return result; + } + public char GetDirectorySeparatorChar(string path) { if (path.IndexOf('/') != -1) diff --git a/Emby.Common.Implementations/Net/NetAcceptSocket.cs b/Emby.Common.Implementations/Net/NetAcceptSocket.cs index 3721709e6..82e7e9b00 100644 --- a/Emby.Common.Implementations/Net/NetAcceptSocket.cs +++ b/Emby.Common.Implementations/Net/NetAcceptSocket.cs @@ -100,7 +100,7 @@ namespace Emby.Common.Implementations.Net #if NET46 public Task SendFile(string path, byte[] preBuffer, byte[] postBuffer, CancellationToken cancellationToken) { - var options = TransmitFileOptions.UseKernelApc; + var options = TransmitFileOptions.UseDefaultWorkerThread; var completionSource = new TaskCompletionSource<bool>(); diff --git a/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index f0518f69e..ac1c55b6b 100644 --- a/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -158,7 +158,7 @@ namespace Emby.Common.Implementations.ScheduledTasks _lastExecutionResult = value; var path = GetHistoryFilePath(); - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); lock (_lastExecutionResultSyncLock) { @@ -575,7 +575,7 @@ namespace Emby.Common.Implementations.ScheduledTasks { var path = GetConfigurationFilePath(); - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); JsonSerializer.SerializeToFile(triggers, path); } diff --git a/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs index 77482d56b..500f57aad 100644 --- a/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs +++ b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs @@ -91,7 +91,7 @@ namespace Emby.Drawing.ImageMagick try { var tmpPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".webp"); - _fileSystem.CreateDirectory(Path.GetDirectoryName(tmpPath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(tmpPath)); using (var wand = new MagickWand(1, 1, new PixelWand("none", 1))) { diff --git a/Emby.Drawing.ImageMagick/PlayedIndicatorDrawer.cs b/Emby.Drawing.ImageMagick/PlayedIndicatorDrawer.cs index 14fb0ddf1..58c6cfe82 100644 --- a/Emby.Drawing.ImageMagick/PlayedIndicatorDrawer.cs +++ b/Emby.Drawing.ImageMagick/PlayedIndicatorDrawer.cs @@ -68,7 +68,7 @@ namespace Emby.Drawing.ImageMagick var namespacePath = typeof(PlayedIndicatorDrawer).Namespace + ".fonts." + name; var tempPath = Path.Combine(paths.TempDirectory, Guid.NewGuid().ToString("N") + ".ttf"); - fileSystem.CreateDirectory(Path.GetDirectoryName(tempPath)); + fileSystem.CreateDirectory(fileSystem.GetDirectoryName(tempPath)); using (var stream = typeof(PlayedIndicatorDrawer).Assembly.GetManifestResourceStream(namespacePath)) { @@ -78,7 +78,7 @@ namespace Emby.Drawing.ImageMagick } } - fileSystem.CreateDirectory(Path.GetDirectoryName(filePath)); + fileSystem.CreateDirectory(fileSystem.GetDirectoryName(filePath)); try { @@ -108,7 +108,7 @@ namespace Emby.Drawing.ImageMagick }).ConfigureAwait(false); - fileSystem.CreateDirectory(Path.GetDirectoryName(filePath)); + fileSystem.CreateDirectory(fileSystem.GetDirectoryName(filePath)); try { diff --git a/Emby.Drawing.Net/GDIImageEncoder.cs b/Emby.Drawing.Net/GDIImageEncoder.cs index 831a57979..638415afd 100644 --- a/Emby.Drawing.Net/GDIImageEncoder.cs +++ b/Emby.Drawing.Net/GDIImageEncoder.cs @@ -81,7 +81,7 @@ namespace Emby.Drawing.Net { using (var croppedImage = image.CropWhitespace()) { - _fileSystem.CreateDirectory(Path.GetDirectoryName(outputPath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(outputPath)); using (var outputStream = _fileSystem.GetFileStream(outputPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, false)) { @@ -135,7 +135,7 @@ namespace Emby.Drawing.Net var outputFormat = GetOutputFormat(originalImage, selectedOutputFormat); - _fileSystem.CreateDirectory(Path.GetDirectoryName(cacheFilePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFilePath)); // Save to the cache location using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, false)) diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index f19e3e037..bee0e9b69 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -244,9 +244,9 @@ namespace Emby.Drawing var newWidth = Convert.ToInt32(newSize.Width); var newHeight = Convert.ToInt32(newSize.Height); - _fileSystem.CreateDirectory(Path.GetDirectoryName(cacheFilePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFilePath)); var tmpPath = Path.ChangeExtension(Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")), Path.GetExtension(cacheFilePath)); - _fileSystem.CreateDirectory(Path.GetDirectoryName(tmpPath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(tmpPath)); _imageEncoder.EncodeImage(originalImagePath, tmpPath, AutoOrient(options.Item), newWidth, newHeight, quality, options, outputFormat); CopyFile(tmpPath, cacheFilePath); @@ -418,9 +418,9 @@ namespace Emby.Drawing try { - _fileSystem.CreateDirectory(Path.GetDirectoryName(croppedImagePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(croppedImagePath)); var tmpPath = Path.ChangeExtension(Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")), Path.GetExtension(croppedImagePath)); - _fileSystem.CreateDirectory(Path.GetDirectoryName(tmpPath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(tmpPath)); _imageEncoder.CropWhiteSpace(originalImagePath, tmpPath); CopyFile(tmpPath, croppedImagePath); @@ -592,7 +592,7 @@ namespace Emby.Drawing try { var path = ImageSizeFile; - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); _jsonSerializer.SerializeToFile(_cachedImagedSizes, path); } catch (Exception ex) @@ -765,10 +765,10 @@ namespace Emby.Drawing return enhancedImagePath; } - _fileSystem.CreateDirectory(Path.GetDirectoryName(enhancedImagePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(enhancedImagePath)); var tmpPath = Path.Combine(_appPaths.TempDirectory, Path.ChangeExtension(Guid.NewGuid().ToString(), Path.GetExtension(enhancedImagePath))); - _fileSystem.CreateDirectory(Path.GetDirectoryName(tmpPath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(tmpPath)); await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, tmpPath, item, imageType, imageIndex).ConfigureAwait(false); diff --git a/Emby.Server.Core/ApplicationHost.cs b/Emby.Server.Core/ApplicationHost.cs index b7309de66..78bdc1189 100644 --- a/Emby.Server.Core/ApplicationHost.cs +++ b/Emby.Server.Core/ApplicationHost.cs @@ -257,7 +257,7 @@ namespace Emby.Server.Core internal IPowerManagement PowerManagement { get; private set; } internal IImageEncoder ImageEncoder { get; private set; } - private readonly Action<string, string> _certificateGenerator; + private readonly Action<string, string, string> _certificateGenerator; private readonly Func<string> _defaultUserNameFactory; /// <summary> @@ -274,7 +274,7 @@ namespace Emby.Server.Core ISystemEvents systemEvents, IMemoryStreamFactory memoryStreamFactory, INetworkManager networkManager, - Action<string, string> certificateGenerator, + Action<string, string, string> certificateGenerator, Func<string> defaultUsernameFactory) : base(applicationPaths, logManager, @@ -609,8 +609,8 @@ namespace Emby.Server.Core RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LogManager, LibraryManager, UserManager)); - CertificatePath = GetCertificatePath(true); - Certificate = GetCertificate(CertificatePath); + CertificateInfo = GetCertificateInfo(true); + Certificate = GetCertificate(CertificateInfo); HttpServer = HttpServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamFactory, "Emby", "web/index.html", textEncoding, SocketFactory, CryptographyProvider, JsonSerializer, XmlSerializer, EnvironmentInfo, Certificate, FileSystemManager, SupportsDualModeSockets); HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading"); @@ -745,8 +745,10 @@ namespace Emby.Server.Core } } - private ICertificate GetCertificate(string certificateLocation) + private ICertificate GetCertificate(CertificateInfo info) { + var certificateLocation = info == null ? null : info.Path; + if (string.IsNullOrWhiteSpace(certificateLocation)) { return null; @@ -759,7 +761,7 @@ namespace Emby.Server.Core return null; } - X509Certificate2 localCert = new X509Certificate2(certificateLocation); + X509Certificate2 localCert = new X509Certificate2(certificateLocation, info.Password); //localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA; if (!localCert.HasPrivateKey) { @@ -1064,7 +1066,7 @@ namespace Emby.Server.Core SyncManager.AddParts(GetExports<ISyncProvider>()); } - private string CertificatePath { get; set; } + private CertificateInfo CertificateInfo { get; set; } private ICertificate Certificate { get; set; } private IEnumerable<string> GetUrlPrefixes() @@ -1080,7 +1082,7 @@ namespace Emby.Server.Core "http://"+i+":" + HttpPort + "/" }; - if (!string.IsNullOrWhiteSpace(CertificatePath)) + if (CertificateInfo != null) { prefixes.Add("https://" + i + ":" + HttpsPort + "/"); } @@ -1123,27 +1125,31 @@ namespace Emby.Server.Core } } - private string GetCertificatePath(bool generateCertificate) + private CertificateInfo GetCertificateInfo(bool generateCertificate) { if (!string.IsNullOrWhiteSpace(ServerConfigurationManager.Configuration.CertificatePath)) { // Custom cert - return ServerConfigurationManager.Configuration.CertificatePath; + return new CertificateInfo + { + Path = ServerConfigurationManager.Configuration.CertificatePath + }; } // Generate self-signed cert var certHost = GetHostnameFromExternalDns(ServerConfigurationManager.Configuration.WanDdns); - var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "1").GetMD5().ToString("N") + ".pfx"); + var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "2").GetMD5().ToString("N") + ".pfx"); + var password = "embycert"; if (generateCertificate) { if (!FileSystemManager.FileExists(certPath)) { - FileSystemManager.CreateDirectory(Path.GetDirectoryName(certPath)); + FileSystemManager.CreateDirectory(FileSystemManager.GetDirectoryName(certPath)); try { - _certificateGenerator(certPath, certHost); + _certificateGenerator(certPath, certHost, password); } catch (Exception ex) { @@ -1153,7 +1159,11 @@ namespace Emby.Server.Core } } - return certPath; + return new CertificateInfo + { + Path = certPath, + Password = password + }; } /// <summary> @@ -1189,7 +1199,11 @@ namespace Emby.Server.Core requiresRestart = true; } - if (!string.Equals(CertificatePath, GetCertificatePath(false), StringComparison.OrdinalIgnoreCase)) + var currentCertPath = CertificateInfo == null ? null : CertificateInfo.Path; + var newCertInfo = GetCertificateInfo(false); + var newCertPath = newCertInfo == null ? null : newCertInfo.Path; + + if (!string.Equals(currentCertPath, newCertPath, StringComparison.OrdinalIgnoreCase)) { requiresRestart = true; } @@ -1779,6 +1793,11 @@ namespace Emby.Server.Core { Container.Register(typeInterface, typeImplementation); } + } + internal class CertificateInfo + { + public string Path { get; set; } + public string Password { get; set; } } } diff --git a/Emby.Server.Core/IO/LibraryMonitor.cs b/Emby.Server.Core/IO/LibraryMonitor.cs index ae7b66597..0f6a2e9a2 100644 --- a/Emby.Server.Core/IO/LibraryMonitor.cs +++ b/Emby.Server.Core/IO/LibraryMonitor.cs @@ -453,7 +453,7 @@ namespace Emby.Server.Core.IO // If the parent of an ignored path has a change event, ignore that too if (tempIgnorePaths.Any(i => { - if (string.Equals(i, path, StringComparison.OrdinalIgnoreCase)) + if (_fileSystem.AreEqual(i, path)) { Logger.Debug("Ignoring change to {0}", path); return true; @@ -466,10 +466,10 @@ namespace Emby.Server.Core.IO } // Go up a level - var parent = Path.GetDirectoryName(i); + var parent = _fileSystem.GetDirectoryName(i); if (!string.IsNullOrEmpty(parent)) { - if (string.Equals(parent, path, StringComparison.OrdinalIgnoreCase)) + if (_fileSystem.AreEqual(parent, path)) { Logger.Debug("Ignoring change to {0}", path); return true; @@ -492,7 +492,7 @@ namespace Emby.Server.Core.IO private void CreateRefresher(string path) { - var parentPath = Path.GetDirectoryName(path); + var parentPath = _fileSystem.GetDirectoryName(path); lock (_activeRefreshers) { @@ -500,7 +500,7 @@ namespace Emby.Server.Core.IO foreach (var refresher in refreshers) { // Path is already being refreshed - if (string.Equals(path, refresher.Path, StringComparison.Ordinal)) + if (_fileSystem.AreEqual(path, refresher.Path)) { refresher.RestartTimer(); return; @@ -521,7 +521,7 @@ namespace Emby.Server.Core.IO } // They are siblings. Rebase the refresher to the parent folder. - if (string.Equals(parentPath, Path.GetDirectoryName(refresher.Path), StringComparison.Ordinal)) + if (string.Equals(parentPath, _fileSystem.GetDirectoryName(refresher.Path), StringComparison.Ordinal)) { refresher.ResetPath(parentPath, path); return; diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 8f03fa7a4..0cdd934b7 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -268,24 +268,14 @@ namespace Emby.Server.Implementations.Channels return; } - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); _jsonSerializer.SerializeToFile(mediaSources, path); } public async Task<IEnumerable<MediaSourceInfo>> GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken) { - IEnumerable<ChannelMediaInfo> results = new List<ChannelMediaInfo>(); - var video = item as Video; - if (video != null) - { - results = video.ChannelMediaSources; - } - var audio = item as Audio; - if (audio != null) - { - results = audio.ChannelMediaSources ?? GetSavedMediaSources(audio); - } + IEnumerable<ChannelMediaInfo> results = GetSavedMediaSources(item); return SortMediaInfoResults(results) .Select(i => GetMediaSource(item, i)) @@ -1115,7 +1105,7 @@ namespace Emby.Server.Implementations.Channels { try { - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); _jsonSerializer.SerializeToFile(result, path); } @@ -1378,7 +1368,6 @@ namespace Emby.Server.Implementations.Channels if (channelVideoItem != null) { channelVideoItem.ExtraType = info.ExtraType; - channelVideoItem.ChannelMediaSources = info.MediaSources; var mediaSource = info.MediaSources.FirstOrDefault(); item.Path = mediaSource == null ? null : mediaSource.Path; @@ -1427,7 +1416,7 @@ namespace Emby.Server.Implementations.Channels if (!_refreshedItems.ContainsKey(program.Id)) { _refreshedItems.TryAdd(program.Id, true); - _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem)); + _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.Low); } } diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index d0bd76c35..9c26655fc 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Collections } else { - _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(_fileSystem)); + _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.High); } EventHelper.FireEventIfNotNull(CollectionCreated, this, new CollectionCreatedEventArgs @@ -191,7 +191,7 @@ namespace Emby.Server.Implementations.Collections await collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - _providerManager.QueueRefresh(collection.Id, refreshOptions); + _providerManager.QueueRefresh(collection.Id, refreshOptions, RefreshPriority.High); if (fireEvent) { @@ -244,7 +244,7 @@ namespace Emby.Server.Implementations.Collections collection.UpdateRatingToContent(); await collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(_fileSystem)); + _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.High); EventHelper.FireEventIfNotNull(ItemsRemovedFromCollection, this, new CollectionModifiedEventArgs { diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs index 0096f2284..e25955782 100644 --- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs +++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs @@ -71,10 +71,9 @@ namespace Emby.Server.Implementations.Data double newPercentCommplete = 45 + .55 * p; progress.Report(newPercentCommplete); }); - await CleanDeletedItems(cancellationToken, innerProgress).ConfigureAwait(false); - progress.Report(100); await _itemRepo.UpdateInheritedValues(cancellationToken).ConfigureAwait(false); + progress.Report(100); } private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress) @@ -115,115 +114,6 @@ namespace Emby.Server.Implementations.Data progress.Report(100); } - private async Task CleanDeletedItems(CancellationToken cancellationToken, IProgress<double> progress) - { - var result = _itemRepo.GetItemIdsWithPath(new InternalItemsQuery - { - LocationTypes = new[] { LocationType.FileSystem }, - //Limit = limit, - - // These have their own cleanup routines - ExcludeItemTypes = new[] - { - typeof(Person).Name, - typeof(Genre).Name, - typeof(MusicGenre).Name, - typeof(GameGenre).Name, - typeof(Studio).Name, - typeof(Year).Name, - typeof(Channel).Name, - typeof(AggregateFolder).Name, - typeof(CollectionFolder).Name - } - }); - - var numComplete = 0; - var numItems = result.Count; - - var allLibraryPaths = _libraryManager - .GetVirtualFolders() - .SelectMany(i => i.Locations) - .ToList(); - - foreach (var item in result) - { - cancellationToken.ThrowIfCancellationRequested(); - - var path = item.Item2; - - try - { - var isPathInLibrary = false; - - if (allLibraryPaths.Any(i => path.StartsWith(i, StringComparison.Ordinal)) || - allLibraryPaths.Contains(path, StringComparer.Ordinal) || - path.StartsWith(_appPaths.ProgramDataPath, StringComparison.Ordinal)) - { - isPathInLibrary = true; - - if (_fileSystem.FileExists(path) || _fileSystem.DirectoryExists(path)) - { - continue; - } - } - - var libraryItem = _libraryManager.GetItemById(item.Item1); - - if (libraryItem == null) - { - continue; - } - - if (libraryItem.IsTopParent) - { - continue; - } - - var hasDualAccess = libraryItem as IHasDualAccess; - if (hasDualAccess != null && hasDualAccess.IsAccessedByName) - { - continue; - } - - var libraryItemPath = libraryItem.Path; - if (!string.Equals(libraryItemPath, path, StringComparison.OrdinalIgnoreCase)) - { - _logger.Error("CleanDeletedItems aborting delete for item {0}-{1} because paths don't match. {2}---{3}", libraryItem.Id, libraryItem.Name, libraryItem.Path ?? string.Empty, path ?? string.Empty); - continue; - } - - if (Folder.IsPathOffline(path, allLibraryPaths)) - { - continue; - } - - if (isPathInLibrary) - { - _logger.Info("Deleting item from database {0} because path no longer exists. type: {1} path: {2}", libraryItem.Name, libraryItem.GetType().Name, libraryItemPath ?? string.Empty); - } - else - { - _logger.Info("Deleting item from database {0} because path is no longer in the server library. type: {1} path: {2}", libraryItem.Name, libraryItem.GetType().Name, libraryItemPath ?? string.Empty); - } - - await libraryItem.OnFileDeleted().ConfigureAwait(false); - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception ex) - { - _logger.ErrorException("Error in CleanDeletedItems. File {0}", ex, path); - } - - numComplete++; - double percent = numComplete; - percent /= numItems; - progress.Report(percent * 100); - } - } - /// <summary> /// Creates the triggers that define when the task will run /// </summary> diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 51f91acf3..61dce9bba 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -201,7 +201,6 @@ namespace Emby.Server.Implementations.Data AddColumn(db, "TypedBaseItems", "SortName", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "RunTimeTicks", "BIGINT", existingColumnNames); - AddColumn(db, "TypedBaseItems", "OfficialRatingDescription", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "HomePageUrl", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "VoteCount", "INT", existingColumnNames); AddColumn(db, "TypedBaseItems", "DisplayMediaType", "Text", existingColumnNames); @@ -209,7 +208,6 @@ namespace Emby.Server.Implementations.Data AddColumn(db, "TypedBaseItems", "DateModified", "DATETIME", existingColumnNames); AddColumn(db, "TypedBaseItems", "ForcedSortName", "Text", existingColumnNames); - AddColumn(db, "TypedBaseItems", "LocationType", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "IsSeries", "BIT", existingColumnNames); AddColumn(db, "TypedBaseItems", "IsLive", "BIT", existingColumnNames); @@ -240,7 +238,6 @@ namespace Emby.Server.Implementations.Data AddColumn(db, "TypedBaseItems", "SourceType", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "TrailerTypes", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "CriticRating", "Float", existingColumnNames); - AddColumn(db, "TypedBaseItems", "CriticRatingSummary", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "InheritedTags", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "CleanName", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "PresentationUniqueKey", "Text", existingColumnNames); @@ -255,7 +252,6 @@ namespace Emby.Server.Implementations.Data AddColumn(db, "TypedBaseItems", "SeasonName", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "SeasonId", "GUID", existingColumnNames); AddColumn(db, "TypedBaseItems", "SeriesId", "GUID", existingColumnNames); - AddColumn(db, "TypedBaseItems", "SeriesSortName", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "ExternalSeriesId", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "Tagline", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "Keywords", "Text", existingColumnNames); @@ -429,7 +425,6 @@ namespace Emby.Server.Implementations.Data "ParentIndexNumber", "ProductionYear", "OfficialRating", - "OfficialRatingDescription", "HomePageUrl", "DisplayMediaType", "ForcedSortName", @@ -454,13 +449,11 @@ namespace Emby.Server.Implementations.Data "DateLastMediaAdded", "Album", "CriticRating", - "CriticRatingSummary", "IsVirtualItem", "SeriesName", "SeasonName", "SeasonId", "SeriesId", - "SeriesSortName", "PresentationUniqueKey", "InheritedParentalRatingValue", "InheritedTags", @@ -552,14 +545,12 @@ namespace Emby.Server.Implementations.Data "InheritedParentalRatingValue", "SortName", "RunTimeTicks", - "OfficialRatingDescription", "HomePageUrl", "VoteCount", "DisplayMediaType", "DateCreated", "DateModified", "ForcedSortName", - "LocationType", "PreferredMetadataLanguage", "PreferredMetadataCountryCode", "IsHD", @@ -579,7 +570,6 @@ namespace Emby.Server.Implementations.Data "SourceType", "TrailerTypes", "CriticRating", - "CriticRatingSummary", "InheritedTags", "CleanName", "PresentationUniqueKey", @@ -594,7 +584,6 @@ namespace Emby.Server.Implementations.Data "SeasonName", "SeasonId", "SeriesId", - "SeriesSortName", "ExternalSeriesId", "Tagline", "Keywords", @@ -833,7 +822,6 @@ namespace Emby.Server.Implementations.Data saveItemStatement.TryBind("@SortName", item.SortName); saveItemStatement.TryBind("@RunTimeTicks", item.RunTimeTicks); - saveItemStatement.TryBind("@OfficialRatingDescription", item.OfficialRatingDescription); saveItemStatement.TryBind("@HomePageUrl", item.HomePageUrl); saveItemStatement.TryBind("@VoteCount", item.VoteCount); saveItemStatement.TryBind("@DisplayMediaType", item.DisplayMediaType); @@ -841,7 +829,6 @@ namespace Emby.Server.Implementations.Data saveItemStatement.TryBind("@DateModified", item.DateModified); saveItemStatement.TryBind("@ForcedSortName", item.ForcedSortName); - saveItemStatement.TryBind("@LocationType", item.LocationType.ToString()); saveItemStatement.TryBind("@PreferredMetadataLanguage", item.PreferredMetadataLanguage); saveItemStatement.TryBind("@PreferredMetadataCountryCode", item.PreferredMetadataCountryCode); @@ -942,7 +929,6 @@ namespace Emby.Server.Implementations.Data } saveItemStatement.TryBind("@CriticRating", item.CriticRating); - saveItemStatement.TryBind("@CriticRatingSummary", item.CriticRatingSummary); var inheritedTags = item.InheritedTags; if (inheritedTags.Count > 0) @@ -1024,13 +1010,11 @@ namespace Emby.Server.Implementations.Data if (hasSeries != null) { saveItemStatement.TryBind("@SeriesId", hasSeries.SeriesId); - saveItemStatement.TryBind("@SeriesSortName", hasSeries.SeriesSortName); saveItemStatement.TryBind("@SeriesPresentationUniqueKey", hasSeries.SeriesPresentationUniqueKey); } else { saveItemStatement.TryBindNull("@SeriesId"); - saveItemStatement.TryBindNull("@SeriesSortName"); saveItemStatement.TryBindNull("@SeriesPresentationUniqueKey"); } @@ -1290,22 +1274,10 @@ namespace Emby.Server.Implementations.Data { return false; } - if (type == typeof(Year)) - { - return false; - } - if (type == typeof(Book)) - { - return false; - } if (type == typeof(Person)) { return false; } - if (type == typeof(RecordingGroup)) - { - return false; - } if (type == typeof(Channel)) { return false; @@ -1339,31 +1311,42 @@ namespace Emby.Server.Implementations.Data return false; } } - if (_config.Configuration.SkipDeserializationForPrograms) + + if (type == typeof(Year)) + { + return false; + } + if (type == typeof(Book)) { - if (type == typeof(LiveTvProgram)) - { - return false; - } + return false; + } + if (type == typeof(RecordingGroup)) + { + return false; } + if (type == typeof(LiveTvProgram)) + { + return false; + } + if (type == typeof(LiveTvAudioRecording)) + { + return false; + } + if (type == typeof(AudioPodcast)) + { + return false; + } + if (type == typeof(AudioBook)) + { + return false; + } + if (_config.Configuration.SkipDeserializationForAudio) { if (type == typeof(Audio)) { return false; } - if (type == typeof(LiveTvAudioRecording)) - { - return false; - } - if (type == typeof(AudioPodcast)) - { - return false; - } - if (type == typeof(AudioBook)) - { - return false; - } if (type == typeof(MusicAlbum)) { return false; @@ -1609,15 +1592,6 @@ namespace Emby.Server.Implementations.Data } index++; - if (query.HasField(ItemFields.OfficialRatingDescription)) - { - if (!reader.IsDBNull(index)) - { - item.OfficialRatingDescription = reader.GetString(index); - } - index++; - } - if (query.HasField(ItemFields.HomePageUrl)) { if (!reader.IsDBNull(index)) @@ -1803,15 +1777,6 @@ namespace Emby.Server.Implementations.Data } index++; - if (query.HasField(ItemFields.CriticRatingSummary)) - { - if (!reader.IsDBNull(index)) - { - item.CriticRatingSummary = reader.GetString(index); - } - index++; - } - if (!reader.IsDBNull(index)) { item.IsVirtualItem = reader.GetBoolean(index); @@ -1856,15 +1821,6 @@ namespace Emby.Server.Implementations.Data } index++; - if (hasSeries != null) - { - if (!reader.IsDBNull(index)) - { - hasSeries.SeriesSortName = reader.GetString(index); - } - } - index++; - if (!reader.IsDBNull(index)) { item.PresentationUniqueKey = reader.GetString(index); @@ -2893,6 +2849,10 @@ namespace Emby.Server.Implementations.Data { return new Tuple<string, bool>("(Select MAX(LastPlayedDate) from TypedBaseItems B" + GetJoinUserDataText(query) + " where Played=1 and B.SeriesPresentationUniqueKey=A.PresentationUniqueKey)", false); } + if (string.Equals(name, ItemSortBy.SeriesSortName, StringComparison.OrdinalIgnoreCase)) + { + return new Tuple<string, bool>("(Select SortName from TypedBaseItems where B.Guid=A.SeriesId)", false); + } return new Tuple<string, bool>(name, false); } @@ -4100,27 +4060,6 @@ namespace Emby.Server.Implementations.Data whereClauses.Add("ProductionYear in (" + val + ")"); } - if (query.LocationTypes.Length == 1) - { - if (query.LocationTypes[0] == LocationType.Virtual && _config.Configuration.SchemaVersion >= 90) - { - query.IsVirtualItem = true; - } - else - { - whereClauses.Add("LocationType=@LocationType"); - if (statement != null) - { - statement.TryBind("@LocationType", query.LocationTypes[0].ToString()); - } - } - } - else if (query.LocationTypes.Length > 1) - { - var val = string.Join(",", query.LocationTypes.Select(i => "'" + i + "'").ToArray()); - - whereClauses.Add("LocationType in (" + val + ")"); - } if (query.IsVirtualItem.HasValue) { whereClauses.Add("IsVirtualItem=@IsVirtualItem"); diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 7c0baf9c2..78d76fd76 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -499,7 +499,7 @@ namespace Emby.Server.Implementations.Dto if (fields.Contains(ItemFields.BasicSyncInfo) || fields.Contains(ItemFields.SyncInfo)) { - var userCanSync = user != null && user.Policy.EnableSync; + var userCanSync = user != null && user.Policy.EnableContentDownloading; if (userCanSync && _syncManager.SupportsSync(item)) { dto.SupportsSync = true; @@ -967,11 +967,6 @@ namespace Emby.Server.Implementations.Dto dto.CriticRating = item.CriticRating; - if (fields.Contains(ItemFields.CriticRatingSummary)) - { - dto.CriticRatingSummary = item.CriticRatingSummary; - } - var hasTrailers = item as IHasTrailers; if (hasTrailers != null) { diff --git a/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs index 0a9c67285..86c8c5f68 100644 --- a/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs +++ b/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs @@ -352,7 +352,7 @@ namespace Emby.Server.Implementations.FileOrganization _libraryMonitor.ReportFileSystemChangeBeginning(path); var renameRelatedFiles = !hasRenamedFiles && - string.Equals(Path.GetDirectoryName(path), Path.GetDirectoryName(result.TargetPath), StringComparison.OrdinalIgnoreCase); + string.Equals(_fileSystem.GetDirectoryName(path), _fileSystem.GetDirectoryName(result.TargetPath), StringComparison.OrdinalIgnoreCase); if (renameRelatedFiles) { @@ -432,7 +432,7 @@ namespace Emby.Server.Implementations.FileOrganization // Now find other files var originalFilenameWithoutExtension = Path.GetFileNameWithoutExtension(path); - var directory = Path.GetDirectoryName(path); + var directory = _fileSystem.GetDirectoryName(path); if (!string.IsNullOrWhiteSpace(originalFilenameWithoutExtension) && !string.IsNullOrWhiteSpace(directory)) { @@ -445,7 +445,7 @@ namespace Emby.Server.Implementations.FileOrganization foreach (var file in files) { - directory = Path.GetDirectoryName(file); + directory = _fileSystem.GetDirectoryName(file); var filename = Path.GetFileName(file); filename = filename.Replace(originalFilenameWithoutExtension, targetFilenameWithoutExtension, @@ -470,7 +470,7 @@ namespace Emby.Server.Implementations.FileOrganization return new List<string>(); } - var episodePaths = series.GetRecursiveChildren() + var episodePaths = series.GetRecursiveChildren(i => i is Episode) .OfType<Episode>() .Where(i => { @@ -499,7 +499,7 @@ namespace Emby.Server.Implementations.FileOrganization .Select(i => i.Path) .ToList(); - var folder = Path.GetDirectoryName(targetPath); + var folder = _fileSystem.GetDirectoryName(targetPath); var targetFileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(targetPath); try @@ -529,7 +529,7 @@ namespace Emby.Server.Implementations.FileOrganization _libraryMonitor.ReportFileSystemChangeBeginning(result.TargetPath); - _fileSystem.CreateDirectory(Path.GetDirectoryName(result.TargetPath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(result.TargetPath)); var targetAlreadyExists = _fileSystem.FileExists(result.TargetPath); diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs index dbaf97b1e..8cb7b5dbf 100644 --- a/Emby.Server.Implementations/HttpServer/FileWriter.cs +++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs @@ -58,6 +58,7 @@ namespace Emby.Server.Implementations.HttpServer Headers["Content-Type"] = contentType; TotalContentLength = fileSystem.GetFileInfo(path).Length; + Headers["Accept-Ranges"] = "bytes"; if (string.IsNullOrWhiteSpace(rangeHeader)) { @@ -66,7 +67,6 @@ namespace Emby.Server.Implementations.HttpServer } else { - Headers["Accept-Ranges"] = "bytes"; StatusCode = HttpStatusCode.PartialContent; SetRangeValues(); } @@ -96,8 +96,12 @@ namespace Emby.Server.Implementations.HttpServer RangeLength = 1 + RangeEnd - RangeStart; // Content-Length is the length of what we're serving, not the original content - Headers["Content-Length"] = RangeLength.ToString(UsCulture); - Headers["Content-Range"] = string.Format("bytes {0}-{1}/{2}", RangeStart, RangeEnd, TotalContentLength); + var lengthString = RangeLength.ToString(UsCulture); + Headers["Content-Length"] = lengthString; + var rangeString = string.Format("bytes {0}-{1}/{2}", RangeStart, RangeEnd, TotalContentLength); + Headers["Content-Range"] = rangeString; + + Logger.Info("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString); } /// <summary> diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 310161d41..687bd62b0 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -501,7 +501,7 @@ namespace Emby.Server.Implementations.HttpServer private bool ShouldCompressResponse(IRequest requestContext, string contentType) { // It will take some work to support compression with byte range requests - if (!string.IsNullOrEmpty(requestContext.Headers.Get("Range"))) + if (!string.IsNullOrWhiteSpace(requestContext.Headers.Get("Range"))) { return false; } @@ -566,7 +566,7 @@ namespace Emby.Server.Implementations.HttpServer }; } - if (!string.IsNullOrEmpty(rangeHeader)) + if (!string.IsNullOrWhiteSpace(rangeHeader)) { var stream = await factoryFn().ConfigureAwait(false); @@ -621,6 +621,7 @@ namespace Emby.Server.Implementations.HttpServer responseHeaders["Content-Encoding"] = requestedCompressionType; } + responseHeaders["Vary"] = "Accept-Encoding"; responseHeaders["Content-Length"] = content.Length.ToString(UsCulture); if (isHeadRequest) diff --git a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs index e88994bec..7c967949b 100644 --- a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs +++ b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs @@ -189,10 +189,15 @@ namespace Emby.Server.Implementations.HttpServer private async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength) { var array = new byte[BufferSize]; - int count; - while ((count = await source.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0) + int bytesRead; + while ((bytesRead = await source.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0) { - var bytesToCopy = Math.Min(count, copyLength); + if (bytesRead == 0) + { + break; + } + + var bytesToCopy = Math.Min(bytesRead, copyLength); await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy)).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs index 6d9d7d921..57eef5db0 100644 --- a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs +++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs @@ -26,8 +26,8 @@ namespace Emby.Server.Implementations.HttpServer public void FilterResponse(IRequest req, IResponse res, object dto) { // Try to prevent compatibility view - res.AddHeader("X-UA-Compatible", "IE=Edge"); - res.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization"); + //res.AddHeader("X-UA-Compatible", "IE=Edge"); + res.AddHeader("Access-Control-Allow-Headers", "Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Authorization"); res.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); res.AddHeader("Access-Control-Allow-Origin", "*"); @@ -46,8 +46,6 @@ namespace Emby.Server.Implementations.HttpServer } } - var vary = "Accept-Encoding"; - var hasHeaders = dto as IHasHeaders; var sharpResponse = res as WebSocketSharpResponse; @@ -86,23 +84,9 @@ namespace Emby.Server.Implementations.HttpServer } } } - - string hasHeadersVary; - if (hasHeaders.Headers.TryGetValue("Vary", out hasHeadersVary)) - { - vary = hasHeadersVary; - } - - hasHeaders.Headers["Vary"] = vary; } //res.KeepAlive = false; - - // Per Google PageSpeed - // This instructs the proxies to cache two versions of the resource: one compressed, and one uncompressed. - // The correct version of the resource is delivered based on the client request header. - // This is a good choice for applications that are singly homed and depend on public proxies for user locality. - res.AddHeader("Vary", vary); } /// <summary> diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs index c64672685..033cbd8b0 100644 --- a/Emby.Server.Implementations/IO/FileRefresher.cs +++ b/Emby.Server.Implementations/IO/FileRefresher.cs @@ -207,7 +207,7 @@ namespace Emby.Server.Implementations.IO { item = LibraryManager.FindByPath(path, null); - path = System.IO.Path.GetDirectoryName(path); + path = _fileSystem.GetDirectoryName(path); } if (item != null) diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs index 38908c2bd..2677f7b2a 100644 --- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs @@ -139,7 +139,7 @@ namespace Emby.Server.Implementations.Images CancellationToken cancellationToken) { var outputPathWithoutExtension = Path.Combine(ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N")); - FileSystem.CreateDirectory(Path.GetDirectoryName(outputPathWithoutExtension)); + FileSystem.CreateDirectory(FileSystem.GetDirectoryName(outputPathWithoutExtension)); string outputPath = await CreateImage(item, itemsWithImages, outputPathWithoutExtension, imageType, 0).ConfigureAwait(false); if (string.IsNullOrWhiteSpace(outputPath)) @@ -205,7 +205,7 @@ namespace Emby.Server.Implementations.Images private async Task<string> CreateCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath, int width, int height) { - FileSystem.CreateDirectory(Path.GetDirectoryName(outputPath)); + FileSystem.CreateDirectory(FileSystem.GetDirectoryName(outputPath)); var options = new ImageCollageOptions { diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index d782f5b88..64f025d93 100644 --- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -79,7 +79,7 @@ namespace Emby.Server.Implementations.Library { if (parent == null) { - var parentFolderName = Path.GetFileName(Path.GetDirectoryName(path)); + var parentFolderName = Path.GetFileName(_fileSystem.GetDirectoryName(path)); if (string.Equals(parentFolderName, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) { diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 16a73f392..685c794b7 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1962,8 +1962,34 @@ namespace Emby.Server.Implementations.Library return new List<Folder>(); } - return GetUserRootFolder().Children - .OfType<Folder>() + return GetCollectionFoldersInternal(item, GetUserRootFolder().Children.OfType<Folder>().ToList()); + } + + public List<Folder> GetCollectionFolders(BaseItem item, List<Folder> allUserRootChildren) + { + while (item != null) + { + var parent = item.GetParent(); + + if (parent == null || parent is AggregateFolder) + { + break; + } + + item = parent; + } + + if (item == null) + { + return new List<Folder>(); + } + + return GetCollectionFoldersInternal(item, allUserRootChildren); + } + + private List<Folder> GetCollectionFoldersInternal(BaseItem item, List<Folder> allUserRootChildren) + { + return allUserRootChildren .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path, StringComparer.OrdinalIgnoreCase)) .ToList(); } @@ -2126,7 +2152,8 @@ namespace Emby.Server.Implementations.Library // Not sure why this is necessary but need to figure it out // View images are not getting utilized without this ForceSave = true - }); + + }, RefreshPriority.Normal); } return item; @@ -2188,7 +2215,8 @@ namespace Emby.Server.Implementations.Library { // Need to force save to increment DateLastSaved ForceSave = true - }); + + }, RefreshPriority.Normal); } return item; @@ -2252,7 +2280,8 @@ namespace Emby.Server.Implementations.Library { // Need to force save to increment DateLastSaved ForceSave = true - }); + + }, RefreshPriority.Normal); } return item; @@ -2328,7 +2357,7 @@ namespace Emby.Server.Implementations.Library { // Need to force save to increment DateLastSaved ForceSave = true - }); + }, RefreshPriority.Normal); } return item; diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs index 9d07837c6..b15c01125 100644 --- a/Emby.Server.Implementations/Library/MusicManager.cs +++ b/Emby.Server.Implementations/Library/MusicManager.cs @@ -5,6 +5,7 @@ using MediaBrowser.Controller.Playlists; using System; using System.Collections.Generic; using System.Linq; +using MediaBrowser.Model.Querying; namespace Emby.Server.Implementations.Library { @@ -27,35 +28,14 @@ namespace Emby.Server.Implementations.Library return list.Concat(GetInstantMixFromGenres(item.Genres, user)); } - public IEnumerable<Audio> GetInstantMixFromArtist(MusicArtist artist, User user) + public IEnumerable<Audio> GetInstantMixFromArtist(MusicArtist item, User user) { - var genres = user.RootFolder - .GetRecursiveChildren(user, new InternalItemsQuery(user) - { - IncludeItemTypes = new[] { typeof(Audio).Name } - }) - .Cast<Audio>() - .Where(i => i.HasAnyArtist(artist.Name)) - .SelectMany(i => i.Genres) - .Concat(artist.Genres) - .Distinct(StringComparer.OrdinalIgnoreCase); - - return GetInstantMixFromGenres(genres, user); + return GetInstantMixFromGenres(item.Genres, user); } public IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user) { - var genres = item - .GetRecursiveChildren(user, new InternalItemsQuery(user) - { - IncludeItemTypes = new[] { typeof(Audio).Name } - }) - .Cast<Audio>() - .SelectMany(i => i.Genres) - .Concat(item.Genres) - .DistinctNames(); - - return GetInstantMixFromGenres(genres, user); + return GetInstantMixFromGenres(item.Genres, user); } public IEnumerable<Audio> GetInstantMixFromFolder(Folder item, User user) @@ -63,7 +43,7 @@ namespace Emby.Server.Implementations.Library var genres = item .GetRecursiveChildren(user, new InternalItemsQuery(user) { - IncludeItemTypes = new[] {typeof(Audio).Name} + IncludeItemTypes = new[] { typeof(Audio).Name } }) .Cast<Audio>() .SelectMany(i => i.Genres) @@ -75,41 +55,40 @@ namespace Emby.Server.Implementations.Library public IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user) { - var genres = item - .GetRecursiveChildren(user, new InternalItemsQuery(user) - { - IncludeItemTypes = new[] { typeof(Audio).Name } - }) - .Cast<Audio>() - .SelectMany(i => i.Genres) - .Concat(item.Genres) - .DistinctNames(); - - return GetInstantMixFromGenres(genres, user); + return GetInstantMixFromGenres(item.Genres, user); } public IEnumerable<Audio> GetInstantMixFromGenres(IEnumerable<string> genres, User user) { - var genreList = genres.ToList(); + var genreIds = genres.DistinctNames().Select(i => + { + try + { + return _libraryManager.GetMusicGenre(i).Id.ToString("N"); + } + catch + { + return null; + } + + }).Where(i => i != null); - var inputItems = _libraryManager.GetItemList(new InternalItemsQuery(user) + return GetInstantMixFromGenreIds(genreIds, user); + } + + public IEnumerable<Audio> GetInstantMixFromGenreIds(IEnumerable<string> genreIds, User user) + { + return _libraryManager.GetItemList(new InternalItemsQuery(user) { IncludeItemTypes = new[] { typeof(Audio).Name }, - Genres = genreList.ToArray() + GenreIds = genreIds.ToArray(), - }); + Limit = 200, - var genresDictionary = genreList.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); + SortBy = new[] { ItemSortBy.Random } - return inputItems - .Cast<Audio>() - .Select(i => new Tuple<Audio, int>(i, i.Genres.Count(genresDictionary.ContainsKey))) - .OrderByDescending(i => i.Item2) - .ThenBy(i => Guid.NewGuid()) - .Select(i => i.Item1) - .Take(200) - .OrderBy(i => Guid.NewGuid()); + }).Cast<Audio>(); } public IEnumerable<Audio> GetInstantMixFromItem(BaseItem item, User user) @@ -117,7 +96,7 @@ namespace Emby.Server.Implementations.Library var genre = item as MusicGenre; if (genre != null) { - return GetInstantMixFromGenres(new[] { item.Name }, user); + return GetInstantMixFromGenreIds(new[] { item.Id.ToString("N") }, user); } var playlist = item as Playlist; diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs index 0968e8ea2..8bbda3b62 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs @@ -6,6 +6,7 @@ using System; using System.IO; using System.Linq; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.IO; namespace Emby.Server.Implementations.Library.Resolvers { @@ -13,11 +14,13 @@ namespace Emby.Server.Implementations.Library.Resolvers { private readonly IImageProcessor _imageProcessor; private readonly ILibraryManager _libraryManager; + private readonly IFileSystem _fileSystem; - public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager) + public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager, IFileSystem fileSystem) { _imageProcessor = imageProcessor; _libraryManager = libraryManager; + _fileSystem = fileSystem; } /// <summary> @@ -41,7 +44,7 @@ namespace Emby.Server.Implementations.Library.Resolvers var filename = Path.GetFileNameWithoutExtension(args.Path); // Make sure the image doesn't belong to a video file - if (args.DirectoryService.GetFilePaths(Path.GetDirectoryName(args.Path)).Any(i => IsOwnedByMedia(args.GetLibraryOptions(), i, filename))) + if (args.DirectoryService.GetFilePaths(_fileSystem.GetDirectoryName(args.Path)).Any(i => IsOwnedByMedia(args.GetLibraryOptions(), i, filename))) { return null; } diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs index cf37366fb..bdab8552a 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs @@ -57,7 +57,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV { episode.SeriesId = series.Id; episode.SeriesName = series.Name; - episode.SeriesSortName = series.SortName; } if (season != null) { diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs index c065feda1..84ceac65e 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs @@ -44,7 +44,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV { IndexNumber = new SeasonPathParser(namingOptions, new RegexProvider()).Parse(args.Path, true, true).SeasonNumber, SeriesId = series.Id, - SeriesSortName = series.SortName, SeriesName = series.Name }; diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index a47a3322e..f640ae2b1 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/Emby.Server.Implementations/Library/SearchEngine.cs @@ -165,7 +165,16 @@ namespace Emby.Server.Implementations.Library ExcludeItemTypes = excludeItemTypes.ToArray(), IncludeItemTypes = includeItemTypes.ToArray(), Limit = query.Limit, - IncludeItemsByName = true + IncludeItemsByName = string.IsNullOrWhiteSpace(query.ParentId), + ParentId = string.IsNullOrWhiteSpace(query.ParentId) ? (Guid?)null : new Guid(query.ParentId), + SortBy = new[] { ItemSortBy.SortName }, + Recursive = true, + + IsKids = query.IsKids, + IsMovie = query.IsMovie, + IsNews = query.IsNews, + IsSeries = query.IsSeries, + IsSports = query.IsSports }); // Add search hints based on item name diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 89b7198ca..0b9027291 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -942,7 +942,8 @@ namespace Emby.Server.Implementations.Library { return new UserPolicy { - EnableSync = true + EnableContentDownloading = true, + EnableSyncTranscoding = true }; } @@ -964,7 +965,7 @@ namespace Emby.Server.Implementations.Library var path = GetPolifyFilePath(user); - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); lock (_policySyncLock) { @@ -1051,7 +1052,7 @@ namespace Emby.Server.Implementations.Library config = _jsonSerializer.DeserializeFromString<UserConfiguration>(json); } - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); lock (_configSyncLock) { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 666c1fdcd..f1b3f41b4 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { if (!string.IsNullOrWhiteSpace(epgChannel.Name)) { - tunerChannel.Name = epgChannel.Name; + //tunerChannel.Name = epgChannel.Name; } if (!string.IsNullOrWhiteSpace(epgChannel.ImageUrl)) { @@ -1231,7 +1231,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV RequiresOpening = false, RequiresClosing = false, Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http, - BufferMs = 0 + BufferMs = 0, + IgnoreDts = true }; var isAudio = false; @@ -1496,7 +1497,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _libraryManager.RegisterIgnoredPath(recordPath); _libraryMonitor.ReportFileSystemChangeBeginning(recordPath); - _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(recordPath)); activeRecordingInfo.Path = recordPath; var duration = recordingEndDate - DateTime.UtcNow; @@ -1516,8 +1517,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV EnforceKeepUpTo(timer, seriesPath); }; - await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken) - .ConfigureAwait(false); + await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false); recordingStatus = RecordingStatus.Completed; _logger.Info("Recording completed: {0}", recordPath); @@ -1725,7 +1725,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV while (FileExists(path, timerId)) { - var parent = Path.GetDirectoryName(originalPath); + var parent = _fileSystem.GetDirectoryName(originalPath); var name = Path.GetFileNameWithoutExtension(originalPath); name += "-" + index.ToString(CultureInfo.InvariantCulture); @@ -1765,7 +1765,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (regInfo.IsValid) { - return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, config, _httpClient, _processFactory); + return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, config, _httpClient, _processFactory, _config); } } @@ -1892,7 +1892,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return; } - var imageSavePath = Path.Combine(Path.GetDirectoryName(recordingPath), imageSaveFilenameWithoutExtension); + var imageSavePath = Path.Combine(_fileSystem.GetDirectoryName(recordingPath), imageSaveFilenameWithoutExtension); // preserve original image extension imageSavePath = Path.ChangeExtension(imageSavePath, Path.GetExtension(image.Path)); @@ -2155,11 +2155,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV writer.WriteElementString("mpaa", item.OfficialRating); } - if (!string.IsNullOrEmpty(item.OfficialRatingDescription)) - { - writer.WriteElementString("mpaadescription", item.OfficialRatingDescription); - } - var overview = (item.Overview ?? string.Empty) .StripHtml() .Replace(""", "'"); @@ -2251,11 +2246,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV writer.WriteElementString("criticrating", item.CriticRating.Value.ToString(CultureInfo.InvariantCulture)); } - if (!string.IsNullOrEmpty(item.CriticRatingSummary)) - { - writer.WriteElementString("criticratingsummary", item.CriticRatingSummary); - } - if (!string.IsNullOrWhiteSpace(item.Tagline)) { writer.WriteElementString("tagline", item.Tagline); @@ -2550,7 +2540,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private void SaveEpgDataForChannel(string channelId, List<ProgramInfo> epgData) { var path = GetChannelEpgCachePath(channelId); - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); lock (_epgLock) { _jsonSerializer.SerializeToFile(epgData, path); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 6cc5b6920..790e6c27d 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -11,14 +11,16 @@ using MediaBrowser.Model.IO; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller; -using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; +using MediaBrowser.Common.Configuration; namespace Emby.Server.Implementations.LiveTv.EmbyTV { @@ -37,8 +39,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private readonly IProcessFactory _processFactory; private readonly IJsonSerializer _json; private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>(); + private readonly IServerConfigurationManager _config; - public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IServerApplicationPaths appPaths, IJsonSerializer json, LiveTvOptions liveTvOptions, IHttpClient httpClient, IProcessFactory processFactory) + public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IServerApplicationPaths appPaths, IJsonSerializer json, LiveTvOptions liveTvOptions, IHttpClient httpClient, IProcessFactory processFactory, IServerConfigurationManager config) { _logger = logger; _fileSystem = fileSystem; @@ -48,6 +51,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _liveTvOptions = liveTvOptions; _httpClient = httpClient; _processFactory = processFactory; + _config = config; } private string OutputFormat @@ -76,23 +80,35 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public string GetOutputPath(MediaSourceInfo mediaSource, string targetFile) { - return Path.ChangeExtension(targetFile, "." + OutputFormat); + var extension = OutputFormat; + + if (string.Equals(extension, "mpegts", StringComparison.OrdinalIgnoreCase)) + { + extension = "ts"; + } + + return Path.ChangeExtension(targetFile, "." + extension); } public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { - var durationToken = new CancellationTokenSource(duration); - cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; + //var durationToken = new CancellationTokenSource(duration); + //cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; await RecordFromFile(mediaSource, mediaSource.Path, targetFile, duration, onStarted, cancellationToken).ConfigureAwait(false); _logger.Info("Recording completed to file {0}", targetFile); } + private EncodingOptions GetEncodingOptions() + { + return _config.GetConfiguration<EncodingOptions>("encoding"); + } + private Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { _targetPath = targetFile; - _fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(targetFile)); var process = _processFactory.Create(new ProcessOptions { @@ -118,7 +134,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _logger.Info(commandLineLogMessage); var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "record-transcode-" + Guid.NewGuid() + ".txt"); - _fileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(logFilePath)); // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. _logFileStream = _fileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true); @@ -162,28 +178,32 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks); - var inputModifiers = "-fflags +genpts -async 1 -vsync -1"; - var commandLineArgs = "-i \"{0}\"{5} {2} -map_metadata -1 -threads 0 {3}{4}{6} -y \"{1}\""; - - long startTimeTicks = 0; - //if (mediaSource.DateLiveStreamOpened.HasValue) - //{ - // var elapsed = DateTime.UtcNow - mediaSource.DateLiveStreamOpened.Value; - // elapsed -= TimeSpan.FromSeconds(10); - // if (elapsed.TotalSeconds >= 0) - // { - // startTimeTicks = elapsed.Ticks + startTimeTicks; - // } - //} - if (mediaSource.ReadAtNativeFramerate) + var flags = new List<string>(); + if (mediaSource.IgnoreDts) { - inputModifiers += " -re"; + flags.Add("+igndts"); + } + if (mediaSource.IgnoreIndex) + { + flags.Add("+ignidx"); } - if (startTimeTicks > 0) + var inputModifiers = "-async 1 -vsync -1"; + + if (flags.Count > 0) + { + inputModifiers += " -fflags " + string.Join("", flags.ToArray()); + } + + if (!string.IsNullOrWhiteSpace(GetEncodingOptions().HardwareAccelerationType)) + { + inputModifiers += " -hwaccel auto"; + } + + if (mediaSource.ReadAtNativeFramerate) { - inputModifiers = "-ss " + _mediaEncoder.GetTimeParameter(startTimeTicks) + " " + inputModifiers; + inputModifiers += " -re"; } var analyzeDurationSeconds = 5; @@ -193,11 +213,20 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var subtitleArgs = CopySubtitles ? " -codec:s copy" : " -sn"; - var outputParam = string.Equals(Path.GetExtension(targetFile), ".mp4", StringComparison.OrdinalIgnoreCase) ? - " -f mp4 -movflags frag_keyframe+empty_moov" : - string.Empty; + //var outputParam = string.Equals(Path.GetExtension(targetFile), ".mp4", StringComparison.OrdinalIgnoreCase) ? + // " -f mp4 -movflags frag_keyframe+empty_moov" : + // string.Empty; + + var outputParam = string.Empty; - commandLineArgs = string.Format(commandLineArgs, inputTempFile, targetFile, videoArgs, GetAudioArgs(mediaSource), subtitleArgs, durationParam, outputParam); + var commandLineArgs = string.Format("-i \"{0}\"{5} {2} -map_metadata -1 -threads 0 {3}{4}{6} -y \"{1}\"", + inputTempFile, + targetFile, + videoArgs, + GetAudioArgs(mediaSource), + subtitleArgs, + durationParam, + outputParam); return inputModifiers + " " + commandLineArgs; } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index 16ae26d45..953cb8e41 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -72,7 +72,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } var file = _dataPath + ".json"; - _fileSystem.CreateDirectory(Path.GetDirectoryName(file)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(file)); lock (_fileDataLock) { diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index fc0a826b4..b50f5ac92 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -86,7 +86,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings }).ConfigureAwait(false); - _fileSystem.CreateDirectory(Path.GetDirectoryName(cacheFile)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFile)); using (var stream = _fileSystem.OpenRead(tempFile)) { diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index a898d3084..fa86ac36d 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -857,7 +857,8 @@ namespace Emby.Server.Implementations.LiveTv _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem) { MetadataRefreshMode = metadataRefreshMode - }); + + }, RefreshPriority.Normal); } return item.Id; @@ -1395,11 +1396,11 @@ namespace Emby.Server.Implementations.LiveTv foreach (var program in newPrograms) { - _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem)); + _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.Low); } foreach (var program in updatedPrograms) { - _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem)); + _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.Low); } currentChannel.IsMovie = isMovie; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 0a21603ee..d724a9fbc 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -420,7 +420,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun SupportsDirectPlay = false, SupportsDirectStream = true, SupportsTranscoding = true, - IsInfiniteStream = true + IsInfiniteStream = true, + IgnoreDts = true }; mediaSource.InferTotalBitrate(); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs index abd7a00a9..02ebbcf16 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs @@ -29,6 +29,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts byte[] buffer = new byte[BufferSize]; + if (source == null) + { + throw new ArgumentNullException("source"); + } + while (!cancellationToken.IsCancellationRequested) { var bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/Logging/UnhandledExceptionWriter.cs b/Emby.Server.Implementations/Logging/UnhandledExceptionWriter.cs index 5183f3a0b..7104935e1 100644 --- a/Emby.Server.Implementations/Logging/UnhandledExceptionWriter.cs +++ b/Emby.Server.Implementations/Logging/UnhandledExceptionWriter.cs @@ -29,15 +29,17 @@ namespace Emby.Server.Implementations.Logging _logManager.Flush(); var path = Path.Combine(_appPaths.LogDirectoryPath, "unhandled_" + Guid.NewGuid() + ".txt"); - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); var builder = LogHelper.GetLogMessage(ex); // Write to console just in case file logging fails _console.WriteLine("UnhandledException"); - _console.WriteLine(builder.ToString()); - _fileSystem.WriteAllText(path, builder.ToString()); + var logMessage = builder.ToString(); + _console.WriteLine(logMessage); + + _fileSystem.WriteAllText(path, logMessage); } } } diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs index 204e04061..884a001f0 100644 --- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -138,7 +138,7 @@ namespace Emby.Server.Implementations.MediaEncoder var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, video.PlayableStreamFileNames); - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); var container = video.Container; diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 386da73c6..18042b587 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -201,7 +201,8 @@ namespace Emby.Server.Implementations.Playlists _providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions(_fileSystem) { ForceSave = true - }); + + }, RefreshPriority.High); } public async Task RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds) @@ -228,7 +229,8 @@ namespace Emby.Server.Implementations.Playlists _providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions(_fileSystem) { ForceSave = true - }); + + }, RefreshPriority.High); } public async Task MoveItem(string playlistId, string entryId, int newIndex) diff --git a/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs index 5f37025e2..d1c70ba1d 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs @@ -136,7 +136,7 @@ namespace Emby.Server.Implementations.ScheduledTasks { previouslyFailedImages.Add(key); - var parentPath = Path.GetDirectoryName(failHistoryPath); + var parentPath = _fileSystem.GetDirectoryName(failHistoryPath); _fileSystem.CreateDirectory(parentPath); diff --git a/Emby.Server.Implementations/Security/MBLicenseFile.cs b/Emby.Server.Implementations/Security/MBLicenseFile.cs index c791d6a52..dc0e8b161 100644 --- a/Emby.Server.Implementations/Security/MBLicenseFile.cs +++ b/Emby.Server.Implementations/Security/MBLicenseFile.cs @@ -193,7 +193,7 @@ namespace Emby.Server.Implementations.Security } var licenseFile = Filename; - _fileSystem.CreateDirectory(Path.GetDirectoryName(licenseFile)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(licenseFile)); lock (_fileLock) { _fileSystem.WriteAllLines(licenseFile, lines); diff --git a/Emby.Server.Implementations/Session/HttpSessionController.cs b/Emby.Server.Implementations/Session/HttpSessionController.cs index cea5d9b40..2acc3902f 100644 --- a/Emby.Server.Implementations/Session/HttpSessionController.cs +++ b/Emby.Server.Implementations/Session/HttpSessionController.cs @@ -129,7 +129,7 @@ namespace Emby.Server.Implementations.Session public Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken) { - return Task.FromResult(true); + return SendMessage("LibraryChanged", info, cancellationToken); } public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 0a2312735..de00cf239 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -715,7 +715,8 @@ namespace Emby.Server.Implementations.Session ClientName = session.Client, DeviceId = session.DeviceId, IsPaused = info.IsPaused, - PlaySessionId = info.PlaySessionId + PlaySessionId = info.PlaySessionId, + IsAutomated = isAutomated }, _logger); diff --git a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs index b315d33c3..b441a29c1 100644 --- a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs +++ b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs @@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.Sorting { var hasSeries = item as IHasSeries; - return hasSeries != null ? hasSeries.SeriesSortName : null; + return hasSeries != null ? hasSeries.FindSeriesSortName() : null; } /// <summary> diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 840c7ce0d..57019cc4e 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -250,7 +250,7 @@ namespace Emby.Server.Implementations.Updates }).ConfigureAwait(false); - _fileSystem.CreateDirectory(Path.GetDirectoryName(PackageCachePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(PackageCachePath)); _fileSystem.CopyFile(tempFile, PackageCachePath, true); _lastPackageUpdateTime = DateTime.UtcNow; @@ -627,7 +627,7 @@ namespace Emby.Server.Implementations.Updates // Success - move it to the real target try { - _fileSystem.CreateDirectory(Path.GetDirectoryName(target)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(target)); _fileSystem.CopyFile(tempFile, target, true); //If it is an archive - write out a version file so we know what it is if (isArchive) diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index a59ab33a9..53e474982 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -633,7 +633,7 @@ namespace MediaBrowser.Api /// <param name="outputFilePath">The output file path.</param> private void DeleteHlsPartialStreamFiles(string outputFilePath) { - var directory = Path.GetDirectoryName(outputFilePath); + var directory = _fileSystem.GetDirectoryName(outputFilePath); var name = Path.GetFileNameWithoutExtension(outputFilePath); var filesToDelete = _fileSystem.GetFilePaths(directory) diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 1f21a1dd1..a802e56af 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -66,7 +66,7 @@ namespace MediaBrowser.Api return ResultFactory.GetOptimizedResult(Request, result); } - protected void AssertCanUpdateUser(IAuthorizationContext authContext, IUserManager userManager, string userId) + protected void AssertCanUpdateUser(IAuthorizationContext authContext, IUserManager userManager, string userId, bool restrictUserPreferences) { var auth = authContext.GetAuthorizationInfo(Request); @@ -80,7 +80,7 @@ namespace MediaBrowser.Api throw new SecurityException("Unauthorized access."); } } - else + else if (restrictUserPreferences) { if (!authenticatedUser.Policy.EnableUserPreferenceAccess) { diff --git a/MediaBrowser.Api/ChannelService.cs b/MediaBrowser.Api/ChannelService.cs index 1eeb92530..bce1e6682 100644 --- a/MediaBrowser.Api/ChannelService.cs +++ b/MediaBrowser.Api/ChannelService.cs @@ -98,7 +98,7 @@ namespace MediaBrowser.Api [ApiMember(Name = "SortBy", Description = "Optional. Specify one or more sort orders, comma delimeted. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string SortBy { get; set; } - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } /// <summary> @@ -145,7 +145,7 @@ namespace MediaBrowser.Api [ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Filters { get; set; } - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } [ApiMember(Name = "ChannelIds", Description = "Optional. Specify one or more channel id's, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs index bab538c91..c4cb0cb1d 100644 --- a/MediaBrowser.Api/EnvironmentService.cs +++ b/MediaBrowser.Api/EnvironmentService.cs @@ -278,7 +278,7 @@ namespace MediaBrowser.Api public object Get(GetParentPath request) { - var parent = Path.GetDirectoryName(request.Path); + var parent = _fileSystem.GetDirectoryName(request.Path); if (string.IsNullOrEmpty(parent)) { diff --git a/MediaBrowser.Api/Images/ImageByNameService.cs b/MediaBrowser.Api/Images/ImageByNameService.cs index c4cfd7307..2c8fc2d61 100644 --- a/MediaBrowser.Api/Images/ImageByNameService.cs +++ b/MediaBrowser.Api/Images/ImageByNameService.cs @@ -160,7 +160,7 @@ namespace MediaBrowser.Api.Images private string GetThemeName(string path, string rootImagePath) { - var parentName = Path.GetDirectoryName(path); + var parentName = _fileSystem.GetDirectoryName(path); if (string.Equals(parentName, rootImagePath, StringComparison.OrdinalIgnoreCase)) { diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 392654aa2..9f144c8e4 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -427,7 +427,7 @@ namespace MediaBrowser.Api.Images public void Post(PostUserImage request) { var userId = GetPathValue(1); - AssertCanUpdateUser(_authContext, _userManager, userId); + AssertCanUpdateUser(_authContext, _userManager, userId, true); request.Type = (ImageType)Enum.Parse(typeof(ImageType), GetPathValue(3), true); @@ -462,7 +462,7 @@ namespace MediaBrowser.Api.Images public void Delete(DeleteUserImage request) { var userId = request.Id; - AssertCanUpdateUser(_authContext, _userManager, userId); + AssertCanUpdateUser(_authContext, _userManager, userId, true); var item = _userManager.GetUserById(userId); diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs index d7ccf8f6d..ebd8b8951 100644 --- a/MediaBrowser.Api/Images/RemoteImageService.cs +++ b/MediaBrowser.Api/Images/RemoteImageService.cs @@ -278,7 +278,7 @@ namespace MediaBrowser.Api.Images var fullCachePath = GetFullCachePath(urlHash + "." + ext); - _fileSystem.CreateDirectory(Path.GetDirectoryName(fullCachePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(fullCachePath)); using (var stream = result.Content) { using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) @@ -287,7 +287,7 @@ namespace MediaBrowser.Api.Images } } - _fileSystem.CreateDirectory(Path.GetDirectoryName(pointerCachePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(pointerCachePath)); _fileSystem.WriteAllText(pointerCachePath, fullCachePath); } diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs index b5c51bfef..a454642a4 100644 --- a/MediaBrowser.Api/ItemLookupService.cs +++ b/MediaBrowser.Api/ItemLookupService.cs @@ -299,7 +299,7 @@ namespace MediaBrowser.Api var fullCachePath = GetFullCachePath(urlHash + "." + ext); - _fileSystem.CreateDirectory(Path.GetDirectoryName(fullCachePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(fullCachePath)); using (var stream = result.Content) { using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) @@ -308,7 +308,7 @@ namespace MediaBrowser.Api } } - _fileSystem.CreateDirectory(Path.GetDirectoryName(pointerCachePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(pointerCachePath)); _fileSystem.WriteAllText(pointerCachePath, fullCachePath); } diff --git a/MediaBrowser.Api/ItemRefreshService.cs b/MediaBrowser.Api/ItemRefreshService.cs index 81547637e..bca292241 100644 --- a/MediaBrowser.Api/ItemRefreshService.cs +++ b/MediaBrowser.Api/ItemRefreshService.cs @@ -62,14 +62,7 @@ namespace MediaBrowser.Api var options = GetRefreshOptions(request); - if (item is Folder) - { - _providerManager.QueueRefresh(item.Id, options); - } - else - { - _providerManager.RefreshFullItem(item, options, CancellationToken.None); - } + _providerManager.QueueRefresh(item.Id, options, RefreshPriority.High); } private MetadataRefreshOptions GetRefreshOptions(BaseRefreshRequest request) diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 686a3a296..29530688c 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -240,7 +240,6 @@ namespace MediaBrowser.Api item.OriginalTitle = string.IsNullOrWhiteSpace(request.OriginalTitle) ? null : request.OriginalTitle; item.CriticRating = request.CriticRating; - item.CriticRatingSummary = request.CriticRatingSummary; item.DisplayMediaType = request.DisplayMediaType; item.CommunityRating = request.CommunityRating; diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 2d8744f70..9fcdb4be5 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -98,7 +98,7 @@ namespace MediaBrowser.Api.LiveTv /// Fields to return within the items, in addition to basic information /// </summary> /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } [ApiMember(Name = "AddCurrentProgram", Description = "Optional. Adds current program info to each channel", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -189,7 +189,7 @@ namespace MediaBrowser.Api.LiveTv /// Fields to return within the items, in addition to basic information /// </summary> /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } public bool EnableTotalRecordCount { get; set; } @@ -251,7 +251,7 @@ namespace MediaBrowser.Api.LiveTv /// Fields to return within the items, in addition to basic information /// </summary> /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } public bool EnableTotalRecordCount { get; set; } @@ -399,7 +399,7 @@ namespace MediaBrowser.Api.LiveTv /// Fields to return within the items, in addition to basic information /// </summary> /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } public GetPrograms() @@ -459,7 +459,7 @@ namespace MediaBrowser.Api.LiveTv /// Fields to return within the items, in addition to basic information /// </summary> /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 699c4bbb2..daec00e10 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -198,7 +198,7 @@ namespace MediaBrowser.Api.Playback CancellationTokenSource cancellationTokenSource, string workingDirectory = null) { - FileSystem.CreateDirectory(Path.GetDirectoryName(outputPath)); + FileSystem.CreateDirectory(FileSystem.GetDirectoryName(outputPath)); await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false); @@ -263,7 +263,7 @@ namespace MediaBrowser.Api.Playback } var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, logFilePrefix + "-" + Guid.NewGuid() + ".txt"); - FileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath)); + FileSystem.CreateDirectory(FileSystem.GetDirectoryName(logFilePath)); // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true); @@ -315,8 +315,6 @@ namespace MediaBrowser.Api.Playback StartThrottler(state, transcodingJob); } - ReportUsage(state); - return transcodingJob; } @@ -677,7 +675,8 @@ namespace MediaBrowser.Api.Playback { Request = request, RequestedUrl = url, - UserAgent = Request.UserAgent + UserAgent = Request.UserAgent, + EnableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) }; var auth = AuthorizationContext.GetAuthorizationInfo(Request); @@ -720,6 +719,13 @@ namespace MediaBrowser.Api.Playback state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); + //var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ?? + // item.Parents.Select(i => i.GetImageInfo(ImageType.Primary, 0)).FirstOrDefault(i => i != null); + //if (primaryImage != null) + //{ + // state.AlbumCoverPath = primaryImage.Path; + //} + MediaSourceInfo mediaSource = null; if (string.IsNullOrWhiteSpace(request.LiveStreamId)) { @@ -903,123 +909,6 @@ namespace MediaBrowser.Api.Playback } } - private async void ReportUsage(StreamState state) - { - try - { - await ReportUsageInternal(state).ConfigureAwait(false); - } - catch - { - - } - } - - private Task ReportUsageInternal(StreamState state) - { - if (!ServerConfigurationManager.Configuration.EnableAnonymousUsageReporting) - { - return Task.FromResult(true); - } - - if (!MediaEncoder.IsDefaultEncoderPath) - { - return Task.FromResult(true); - } - return Task.FromResult(true); - - //var dict = new Dictionary<string, string>(); - - //var outputAudio = GetAudioEncoder(state); - //if (!string.IsNullOrWhiteSpace(outputAudio)) - //{ - // dict["outputAudio"] = outputAudio; - //} - - //var outputVideo = GetVideoEncoder(state); - //if (!string.IsNullOrWhiteSpace(outputVideo)) - //{ - // dict["outputVideo"] = outputVideo; - //} - - //if (ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputAudio ?? string.Empty, StringComparer.OrdinalIgnoreCase) && - // ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputVideo ?? string.Empty, StringComparer.OrdinalIgnoreCase)) - //{ - // return Task.FromResult(true); - //} - - //dict["id"] = AppHost.SystemId; - //dict["type"] = state.VideoRequest == null ? "Audio" : "Video"; - - //var audioStream = state.AudioStream; - //if (audioStream != null && !string.IsNullOrWhiteSpace(audioStream.Codec)) - //{ - // dict["inputAudio"] = audioStream.Codec; - //} - - //var videoStream = state.VideoStream; - //if (videoStream != null && !string.IsNullOrWhiteSpace(videoStream.Codec)) - //{ - // dict["inputVideo"] = videoStream.Codec; - //} - - //var cert = GetType().Assembly.GetModules().First().GetSignerCertificate(); - //if (cert != null) - //{ - // dict["assemblySig"] = cert.GetCertHashString(); - // dict["certSubject"] = cert.Subject ?? string.Empty; - // dict["certIssuer"] = cert.Issuer ?? string.Empty; - //} - //else - //{ - // return Task.FromResult(true); - //} - - //if (state.SupportedAudioCodecs.Count > 0) - //{ - // dict["supportedAudioCodecs"] = string.Join(",", state.SupportedAudioCodecs.ToArray()); - //} - - //var auth = AuthorizationContext.GetAuthorizationInfo(Request); - - //dict["appName"] = auth.Client ?? string.Empty; - //dict["appVersion"] = auth.Version ?? string.Empty; - //dict["device"] = auth.Device ?? string.Empty; - //dict["deviceId"] = auth.DeviceId ?? string.Empty; - //dict["context"] = "streaming"; - - ////Logger.Info(JsonSerializer.SerializeToString(dict)); - //if (!ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputAudio ?? string.Empty, StringComparer.OrdinalIgnoreCase)) - //{ - // var list = ServerConfigurationManager.Configuration.CodecsUsed.ToList(); - // list.Add(outputAudio); - // ServerConfigurationManager.Configuration.CodecsUsed = list.ToArray(); - //} - - //if (!ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputVideo ?? string.Empty, StringComparer.OrdinalIgnoreCase)) - //{ - // var list = ServerConfigurationManager.Configuration.CodecsUsed.ToList(); - // list.Add(outputVideo); - // ServerConfigurationManager.Configuration.CodecsUsed = list.ToArray(); - //} - - //ServerConfigurationManager.SaveConfiguration(); - - ////Logger.Info(JsonSerializer.SerializeToString(dict)); - //var options = new HttpRequestOptions() - //{ - // Url = "https://mb3admin.com/admin/service/transcoding/report", - // CancellationToken = CancellationToken.None, - // LogRequest = false, - // LogErrors = false, - // BufferContent = false - //}; - //options.RequestContent = JsonSerializer.SerializeToString(dict); - //options.RequestContentType = "application/json"; - - //return HttpClient.Post(options); - } - /// <summary> /// Adds the dlna headers. /// </summary> @@ -1029,6 +918,11 @@ namespace MediaBrowser.Api.Playback /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> protected void AddDlnaHeaders(StreamState state, IDictionary<string, string> responseHeaders, bool isStaticallyStreamed) { + if (!state.EnableDlnaHeaders) + { + return; + } + var profile = state.DeviceProfile; var transferMode = GetHeader("transferMode.dlna.org"); diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 53813860a..fcf57cebe 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -13,10 +13,7 @@ using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Net; -using MediaBrowser.Model.IO; namespace MediaBrowser.Api.Playback.Hls { @@ -271,7 +268,7 @@ namespace MediaBrowser.Api.Playback.Hls var useGenericSegmenter = true; if (useGenericSegmenter) { - var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request); + var outputTsArg = Path.Combine(FileSystem.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request); var timeDeltaParam = String.Empty; diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 038d76245..0fe1e533d 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -89,6 +89,7 @@ namespace MediaBrowser.Api.Playback.Hls public string SegmentId { get; set; } } + [Authenticated] public class DynamicHlsService : BaseHlsService { @@ -378,7 +379,7 @@ namespace MediaBrowser.Api.Playback.Hls private static FileSystemMetadata GetLastTranscodingFile(string playlist, string segmentExtension, IFileSystem fileSystem) { - var folder = Path.GetDirectoryName(playlist); + var folder = fileSystem.GetDirectoryName(playlist); var filePrefix = Path.GetFileNameWithoutExtension(playlist) ?? string.Empty; @@ -415,7 +416,7 @@ namespace MediaBrowser.Api.Playback.Hls private string GetSegmentPath(StreamState state, string playlist, int index) { - var folder = Path.GetDirectoryName(playlist); + var folder = FileSystem.GetDirectoryName(playlist); var filename = Path.GetFileNameWithoutExtension(playlist); @@ -807,7 +808,7 @@ namespace MediaBrowser.Api.Playback.Hls if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) { - return "-codec:a:0 copy"; + return "-codec:a:0 copy -copypriorss:a:0 0"; } var args = "-codec:a:0 " + codec; @@ -925,7 +926,7 @@ namespace MediaBrowser.Api.Playback.Hls if (useGenericSegmenter) { - var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request); + var outputTsArg = Path.Combine(FileSystem.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request); var timeDeltaParam = String.Empty; diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs index ae049a83a..0ff52e63f 100644 --- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs +++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs @@ -15,6 +15,8 @@ namespace MediaBrowser.Api.Playback.Hls /// <summary> /// Class GetHlsAudioSegment /// </summary> + // Can't require authentication just yet due to seeing some requests come from Chrome without full query string + //[Authenticated] [Route("/Audio/{Id}/hls/{SegmentId}/stream.mp3", "GET")] [Route("/Audio/{Id}/hls/{SegmentId}/stream.aac", "GET")] public class GetHlsAudioSegmentLegacy @@ -38,6 +40,7 @@ namespace MediaBrowser.Api.Playback.Hls /// Class GetHlsVideoSegment /// </summary> [Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")] + [Authenticated] public class GetHlsPlaylistLegacy { // TODO: Deprecate with new iOS app @@ -52,6 +55,7 @@ namespace MediaBrowser.Api.Playback.Hls } [Route("/Videos/ActiveEncodings", "DELETE")] + [Authenticated] public class StopEncodingProcess { [ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] @@ -64,6 +68,8 @@ namespace MediaBrowser.Api.Playback.Hls /// <summary> /// Class GetHlsVideoSegment /// </summary> + // Can't require authentication just yet due to seeing some requests come from Chrome without full query string + //[Authenticated] [Route("/Videos/{Id}/hls/{PlaylistId}/{SegmentId}.{SegmentContainer}", "GET")] public class GetHlsVideoSegmentLegacy : VideoStreamRequest { diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index a813d7a87..22c6202e4 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -20,6 +20,7 @@ namespace MediaBrowser.Api.Playback.Hls /// <summary> /// Class VideoHlsService /// </summary> + [Authenticated] public class VideoHlsService : BaseHlsService { public object Get(GetLiveHlsStream request) diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index a64523e40..f0386d5ba 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -59,42 +59,7 @@ namespace MediaBrowser.Api.Playback.Progressive { var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions(); - var audioTranscodeParams = new List<string>(); - - var bitrate = state.OutputAudioBitrate; - - if (bitrate.HasValue) - { - audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(UsCulture)); - } - - if (state.OutputAudioChannels.HasValue) - { - audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(UsCulture)); - } - - // opus will fail on 44100 - if (!string.Equals(state.OutputAudioCodec, "opus", global::System.StringComparison.OrdinalIgnoreCase)) - { - if (state.OutputAudioSampleRate.HasValue) - { - audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture)); - } - } - - const string vn = " -vn"; - - var threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, false); - - var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions); - - return string.Format("{0} {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"", - inputModifier, - EncodingHelper.GetInputArgument(state, encodingOptions), - threads, - vn, - string.Join(" ", audioTranscodeParams.ToArray()), - outputPath).Trim(); + return EncodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath); } public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor) diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 8394e016c..c36a27690 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -62,6 +62,8 @@ namespace MediaBrowser.Api.Playback.Progressive /// <summary> /// Class VideoService /// </summary> + // TODO: In order to autheneticate this in the future, Dlna playback will require updating + //[Authenticated] public class VideoService : BaseProgressiveStreamingService { public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor) diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 7f146466d..4b1687d68 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -138,6 +138,8 @@ namespace MediaBrowser.Api.Playback return MimeTypes.GetMimeType(outputPath); } + public bool EnableDlnaHeaders { get; set; } + public void Dispose() { DisposeTranscodingThrottler(); diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs index bb2bc449b..9f37bb70a 100644 --- a/MediaBrowser.Api/PlaylistService.cs +++ b/MediaBrowser.Api/PlaylistService.cs @@ -102,7 +102,7 @@ namespace MediaBrowser.Api /// Fields to return within the items, in addition to basic information /// </summary> /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] diff --git a/MediaBrowser.Api/Reports/ReportsService.cs b/MediaBrowser.Api/Reports/ReportsService.cs index 5e13c1653..9490c301d 100644 --- a/MediaBrowser.Api/Reports/ReportsService.cs +++ b/MediaBrowser.Api/Reports/ReportsService.cs @@ -300,11 +300,6 @@ namespace MediaBrowser.Api.Reports } } - if (!string.IsNullOrEmpty(request.LocationTypes)) - { - query.LocationTypes = request.LocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray(); - } - // Min official rating if (!string.IsNullOrWhiteSpace(request.MinOfficialRating)) { diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs index d6fa4030d..3df815cda 100644 --- a/MediaBrowser.Api/SearchService.cs +++ b/MediaBrowser.Api/SearchService.cs @@ -9,6 +9,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Search; using System.Linq; using System.Threading.Tasks; +using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Services; namespace MediaBrowser.Api @@ -66,6 +67,23 @@ namespace MediaBrowser.Api [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string IncludeItemTypes { get; set; } + public string ParentId { get; set; } + + [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsMovie { get; set; } + + [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsSeries { get; set; } + + [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsNews { get; set; } + + [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsKids { get; set; } + + [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsSports { get; set; } + public GetSearchHints() { IncludeArtists = true; @@ -135,7 +153,14 @@ namespace MediaBrowser.Api IncludeStudios = request.IncludeStudios, StartIndex = request.StartIndex, UserId = request.UserId, - IncludeItemTypes = (request.IncludeItemTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray() + IncludeItemTypes = (request.IncludeItemTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(), + ParentId = request.ParentId, + + IsKids = request.IsKids, + IsMovie = request.IsMovie, + IsNews = request.IsNews, + IsSeries = request.IsSeries, + IsSports = request.IsSports }).ConfigureAwait(false); @@ -167,11 +192,11 @@ namespace MediaBrowser.Api MatchedTerm = hintInfo.MatchedTerm, DisplayMediaType = item.DisplayMediaType, RunTimeTicks = item.RunTimeTicks, - ProductionYear = item.ProductionYear + ProductionYear = item.ProductionYear, + ChannelId = item.ChannelId, + EndDate = item.EndDate }; - result.ChannelId = item.ChannelId; - var primaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary); if (primaryImageTag != null) @@ -183,12 +208,27 @@ namespace MediaBrowser.Api SetThumbImageInfo(result, item); SetBackdropImageInfo(result, item); + var program = item as LiveTvProgram; + if (program != null) + { + result.StartDate = program.StartDate; + } + var hasSeries = item as IHasSeries; if (hasSeries != null) { result.Series = hasSeries.SeriesName; } + var series = item as Series; + if (series != null) + { + if (series.Status.HasValue) + { + result.Status = series.Status.Value.ToString(); + } + } + var album = item as MusicAlbum; if (album != null) diff --git a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs index b90a71852..e133a88a1 100644 --- a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs +++ b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs @@ -58,7 +58,7 @@ namespace MediaBrowser.Api.Session void _sessionManager_PlaybackProgress(object sender, PlaybackProgressEventArgs e) { - SendData(false); + SendData(!e.IsAutomated); } void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e) diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs index 892c9f698..5463ae982 100644 --- a/MediaBrowser.Api/SimilarItemsHelper.cs +++ b/MediaBrowser.Api/SimilarItemsHelper.cs @@ -61,7 +61,7 @@ namespace MediaBrowser.Api /// Fields to return within the items, in addition to basic information /// </summary> /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } } diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index 5a1074b7b..7e13512aa 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -95,12 +95,12 @@ namespace MediaBrowser.Api config.EnableStandaloneMusicKeys = true; config.EnableCaseSensitiveItemIds = true; config.SkipDeserializationForBasicTypes = true; - config.SkipDeserializationForPrograms = true; config.SkipDeserializationForAudio = true; config.EnableSeriesPresentationUniqueKey = true; config.EnableLocalizedGuids = true; config.EnableSimpleArtistDetection = true; config.EnableNormalizedItemByNameIds = true; + config.DisableLiveTvChannelUserDataName = true; } public void Post(UpdateStartupConfiguration request) diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index 47d442e79..798004a5e 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -288,7 +288,7 @@ namespace MediaBrowser.Api.Subtitles await _subtitleManager.DownloadSubtitles(video, request.SubtitleId, CancellationToken.None) .ConfigureAwait(false); - _providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions(_fileSystem)); + _providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.High); } catch (Exception ex) { diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index 126f1c753..4b279031e 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Api /// Fields to return within the items, in addition to basic information /// </summary> /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } [ApiMember(Name = "SeriesId", Description = "Optional. Filter by series id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -108,7 +108,7 @@ namespace MediaBrowser.Api /// Fields to return within the items, in addition to basic information /// </summary> /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } /// <summary> @@ -150,7 +150,7 @@ namespace MediaBrowser.Api /// Fields to return within the items, in addition to basic information /// </summary> /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } [ApiMember(Name = "Id", Description = "The series id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -215,7 +215,7 @@ namespace MediaBrowser.Api /// Fields to return within the items, in addition to basic information /// </summary> /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } [ApiMember(Name = "Id", Description = "The series id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -497,7 +497,7 @@ namespace MediaBrowser.Api } else { - episodes = series.GetSeasonEpisodes(season, user); + episodes = season.GetEpisodes(user); } } else diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index 1acbce6db..55c23841c 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -146,7 +146,7 @@ namespace MediaBrowser.Api.UserLibrary /// Fields to return within the items, in addition to basic information /// </summary> /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } /// <summary> diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 5d267d059..f8580d328 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -328,7 +328,15 @@ namespace MediaBrowser.Api.UserLibrary if (!string.IsNullOrEmpty(request.LocationTypes)) { - query.LocationTypes = request.LocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray(); + var requestedLocationTypes = + request.LocationTypes.Split(',') + .Select(d => (LocationType) Enum.Parse(typeof (LocationType), d, true)) + .ToList(); + + if (requestedLocationTypes.Count > 0 && requestedLocationTypes.Count < 4) + { + query.IsVirtualItem = requestedLocationTypes.Contains(LocationType.Virtual); + } } // Min official rating diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index 32f3a1f00..b1f196f15 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -225,7 +225,7 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string ParentId { get; set; } - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index fde03e1f2..49b7f6c15 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -444,7 +444,7 @@ namespace MediaBrowser.Api public async Task PostAsync(UpdateUserPassword request) { - AssertCanUpdateUser(_authContext, _userManager, request.Id); + AssertCanUpdateUser(_authContext, _userManager, request.Id, true); var user = _userManager.GetUserById(request.Id); @@ -482,7 +482,7 @@ namespace MediaBrowser.Api public async Task PostAsync(UpdateUserEasyPassword request) { - AssertCanUpdateUser(_authContext, _userManager, request.Id); + AssertCanUpdateUser(_authContext, _userManager, request.Id, true); var user = _userManager.GetUserById(request.Id); @@ -518,7 +518,7 @@ namespace MediaBrowser.Api // https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs var id = GetPathValue(1); - AssertCanUpdateUser(_authContext, _userManager, id); + AssertCanUpdateUser(_authContext, _userManager, id, false); var dtoUser = request; @@ -568,7 +568,7 @@ namespace MediaBrowser.Api public void Post(UpdateUserConfiguration request) { - AssertCanUpdateUser(_authContext, _userManager, request.Id); + AssertCanUpdateUser(_authContext, _userManager, request.Id, false); var task = _userManager.UpdateConfiguration(request.Id, request); diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 59b5a3869..92d8d95bc 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -9,7 +9,6 @@ using System.Globalization; using System.Linq; using System.Threading; using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Channels; using MediaBrowser.Model.Serialization; namespace MediaBrowser.Controller.Entities.Audio @@ -24,8 +23,6 @@ namespace MediaBrowser.Controller.Entities.Audio IHasLookupInfo<SongInfo>, IHasMediaSources { - public List<ChannelMediaInfo> ChannelMediaSources { get; set; } - /// <summary> /// Gets or sets the artist. /// </summary> diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs index e26e0dfce..2f99e530e 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -112,7 +112,7 @@ namespace MediaBrowser.Controller.Entities.Audio public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query) { - query.Genres = new[] { Name }; + query.GenreIds = new[] { Id.ToString("N") }; query.IncludeItemTypes = new[] { typeof(MusicVideo).Name, typeof(Audio).Name, typeof(MusicAlbum).Name, typeof(MusicArtist).Name }; return LibraryManager.GetItemList(query); diff --git a/MediaBrowser.Controller/Entities/AudioBook.cs b/MediaBrowser.Controller/Entities/AudioBook.cs index 8b1c338f1..1bdcfb881 100644 --- a/MediaBrowser.Controller/Entities/AudioBook.cs +++ b/MediaBrowser.Controller/Entities/AudioBook.cs @@ -31,12 +31,10 @@ namespace MediaBrowser.Controller.Entities public string SeriesName { get; set; } [IgnoreDataMember] public Guid? SeriesId { get; set; } - [IgnoreDataMember] - public string SeriesSortName { get; set; } public string FindSeriesSortName() { - return SeriesSortName; + return SeriesName; } public string FindSeriesName() { diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index c8ea4c506..999f6db3f 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -84,6 +84,7 @@ namespace MediaBrowser.Controller.Entities public long? Size { get; set; } public string Container { get; set; } + [IgnoreDataMember] public string Tagline { get; set; } @@ -288,7 +289,7 @@ namespace MediaBrowser.Controller.Entities return Path; } - return System.IO.Path.GetDirectoryName(Path); + return FileSystem.GetDirectoryName(Path); } } @@ -835,20 +836,6 @@ namespace MediaBrowser.Controller.Entities public float? CriticRating { get; set; } /// <summary> - /// Gets or sets the critic rating summary. - /// </summary> - /// <value>The critic rating summary.</value> - [IgnoreDataMember] - public string CriticRatingSummary { get; set; } - - /// <summary> - /// Gets or sets the official rating description. - /// </summary> - /// <value>The official rating description.</value> - [IgnoreDataMember] - public string OfficialRatingDescription { get; set; } - - /// <summary> /// Gets or sets the custom rating. /// </summary> /// <value>The custom rating.</value> @@ -1824,7 +1811,7 @@ namespace MediaBrowser.Controller.Entities /// <returns>Task.</returns> public virtual Task ChangedExternally() { - ProviderManager.QueueRefresh(Id, new MetadataRefreshOptions(FileSystem)); + ProviderManager.QueueRefresh(Id, new MetadataRefreshOptions(FileSystem), RefreshPriority.High); return Task.FromResult(true); } @@ -1924,7 +1911,7 @@ namespace MediaBrowser.Controller.Entities { var allFiles = ImageInfos .Where(i => i.IsLocalFile) - .Select(i => System.IO.Path.GetDirectoryName(i.Path)) + .Select(i => FileSystem.GetDirectoryName(i.Path)) .Distinct(StringComparer.OrdinalIgnoreCase) .SelectMany(directoryService.GetFilePaths) .ToList(); @@ -2099,7 +2086,7 @@ namespace MediaBrowser.Controller.Entities var extensions = new[] { ".nfo", ".xml", ".srt" }.ToList(); extensions.AddRange(SupportedImageExtensionsList); - return FileSystem.GetFiles(System.IO.Path.GetDirectoryName(Path), extensions.ToArray(), false, false) + return FileSystem.GetFiles(FileSystem.GetDirectoryName(Path), extensions.ToArray(), false, false) .Where(i => System.IO.Path.GetFileNameWithoutExtension(i.FullName).StartsWith(filename, StringComparison.OrdinalIgnoreCase)) .ToList(); } @@ -2298,16 +2285,6 @@ namespace MediaBrowser.Controller.Entities ownedItem.CustomRating = item.CustomRating; newOptions.ForceSave = true; } - if (!string.Equals(item.CriticRatingSummary, ownedItem.CriticRatingSummary, StringComparison.Ordinal)) - { - ownedItem.CriticRatingSummary = item.CriticRatingSummary; - newOptions.ForceSave = true; - } - if (!string.Equals(item.OfficialRatingDescription, ownedItem.OfficialRatingDescription, StringComparison.Ordinal)) - { - ownedItem.OfficialRatingDescription = item.OfficialRatingDescription; - newOptions.ForceSave = true; - } } return ownedItem.RefreshMetadata(newOptions, cancellationToken); diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs index a6da389f0..7cb242589 100644 --- a/MediaBrowser.Controller/Entities/Book.cs +++ b/MediaBrowser.Controller/Entities/Book.cs @@ -24,12 +24,10 @@ namespace MediaBrowser.Controller.Entities public string SeriesName { get; set; } [IgnoreDataMember] public Guid? SeriesId { get; set; } - [IgnoreDataMember] - public string SeriesSortName { get; set; } public string FindSeriesSortName() { - return SeriesSortName; + return SeriesName; } public string FindSeriesName() { diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 62ea21a79..24474ba55 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -213,7 +213,7 @@ namespace MediaBrowser.Controller.Entities .SelectMany(c => c.LinkedChildren) .ToList(); - var changed = !linkedChildren.SequenceEqual(LinkedChildren, new LinkedChildComparer()); + var changed = !linkedChildren.SequenceEqual(LinkedChildren, new LinkedChildComparer(FileSystem)); LinkedChildren = linkedChildren; @@ -332,13 +332,13 @@ namespace MediaBrowser.Controller.Entities .OfType<Folder>() .ToList(); - return PhysicalLocations.Where(i => !string.Equals(i, Path, StringComparison.OrdinalIgnoreCase)).SelectMany(i => GetPhysicalParents(i, rootChildren)).DistinctBy(i => i.Id); + return PhysicalLocations.Where(i => !FileSystem.AreEqual(i, Path)).SelectMany(i => GetPhysicalParents(i, rootChildren)).DistinctBy(i => i.Id); } private IEnumerable<Folder> GetPhysicalParents(string path, List<Folder> rootChildren) { var result = rootChildren - .Where(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase)) + .Where(i => FileSystem.AreEqual(i.Path, path)) .ToList(); if (result.Count == 0) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 268fefbd3..edac27f99 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -640,7 +640,7 @@ namespace MediaBrowser.Controller.Entities return true; } - path = System.IO.Path.GetDirectoryName(path); + path = FileSystem.GetDirectoryName(path); } return allLibraryPaths.Any(i => ContainsPath(i, originalPath)); @@ -1206,11 +1206,17 @@ namespace MediaBrowser.Controller.Entities return GetLinkedChildren(); } - var locations = user.RootFolder - .Children + if (LinkedChildren.Count == 0) + { + return new List<BaseItem>(); + } + + var allUserRootChildren = user.RootFolder.Children.OfType<Folder>().ToList(); + + var collectionFolderIds = allUserRootChildren .OfType<CollectionFolder>() .Where(i => i.IsVisible(user)) - .SelectMany(i => i.PhysicalLocations) + .Select(i => i.Id) .ToList(); return LinkedChildren @@ -1228,9 +1234,16 @@ namespace MediaBrowser.Controller.Entities return null; } } - else if (childLocationType == LocationType.FileSystem && !locations.Any(l => FileSystem.ContainsSubPath(l, child.Path))) + else if (childLocationType == LocationType.FileSystem) { - return null; + var itemCollectionFolderIds = + LibraryManager.GetCollectionFolders(child, allUserRootChildren) + .Select(f => f.Id).ToList(); + + if (!itemCollectionFolderIds.Any(collectionFolderIds.Contains)) + { + return null; + } } } @@ -1323,7 +1336,7 @@ namespace MediaBrowser.Controller.Entities } else { newShortcutLinks = new List<LinkedChild>(); } - if (!newShortcutLinks.SequenceEqual(currentShortcutLinks, new LinkedChildComparer())) + if (!newShortcutLinks.SequenceEqual(currentShortcutLinks, new LinkedChildComparer(FileSystem))) { Logger.Info("Shortcut links have changed for {0}", Path); diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs index d19552c07..baefc9dfa 100644 --- a/MediaBrowser.Controller/Entities/Game.cs +++ b/MediaBrowser.Controller/Entities/Game.cs @@ -105,7 +105,7 @@ namespace MediaBrowser.Controller.Entities return new[] { new FileSystemMetadata { - FullName = System.IO.Path.GetDirectoryName(Path), + FullName = FileSystem.GetDirectoryName(Path), IsDirectory = true } }; diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs index 4187167b9..093b191b6 100644 --- a/MediaBrowser.Controller/Entities/GameGenre.cs +++ b/MediaBrowser.Controller/Entities/GameGenre.cs @@ -81,7 +81,7 @@ namespace MediaBrowser.Controller.Entities public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query) { - query.Genres = new[] { Name }; + query.GenreIds = new[] { Id.ToString("N") }; query.IncludeItemTypes = new[] { typeof(Game).Name }; return LibraryManager.GetItemList(query); diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index 9769efdd0..6569a1e6c 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -93,7 +93,7 @@ namespace MediaBrowser.Controller.Entities public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query) { - query.Genres = new[] { Name }; + query.GenreIds = new[] { Id.ToString("N") }; query.ExcludeItemTypes = new[] { typeof(Game).Name, typeof(MusicVideo).Name, typeof(Audio.Audio).Name, typeof(MusicAlbum).Name, typeof(MusicArtist).Name }; return LibraryManager.GetItemList(query); diff --git a/MediaBrowser.Controller/Entities/IHasSeries.cs b/MediaBrowser.Controller/Entities/IHasSeries.cs index 203be93e8..20efdc2b8 100644 --- a/MediaBrowser.Controller/Entities/IHasSeries.cs +++ b/MediaBrowser.Controller/Entities/IHasSeries.cs @@ -11,7 +11,6 @@ namespace MediaBrowser.Controller.Entities /// <value>The name of the series.</value> string SeriesName { get; set; } string FindSeriesName(); - string SeriesSortName { get; set; } string FindSeriesSortName(); Guid? SeriesId { get; set; } Guid? FindSeriesId(); diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index ea4d60a44..092461c84 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -129,7 +129,6 @@ namespace MediaBrowser.Controller.Entities public string[] AncestorIds { get; set; } public string[] TopParentIds { get; set; } - public LocationType[] LocationTypes { get; set; } public string[] PresetViews { get; set; } public SourceType[] SourceTypes { get; set; } public SourceType[] ExcludeSourceTypes { get; set; } @@ -176,7 +175,6 @@ namespace MediaBrowser.Controller.Entities case ItemFields.DateCreated: case ItemFields.SortName: case ItemFields.Overview: - case ItemFields.OfficialRatingDescription: case ItemFields.HomePageUrl: case ItemFields.VoteCount: case ItemFields.DisplayMediaType: @@ -187,7 +185,6 @@ namespace MediaBrowser.Controller.Entities case ItemFields.OriginalTitle: case ItemFields.Tags: case ItemFields.DateLastMediaAdded: - case ItemFields.CriticRatingSummary: return fields.Count == 0 || fields.Contains(name); default: return true; @@ -230,7 +227,6 @@ namespace MediaBrowser.Controller.Entities TopParentIds = new string[] { }; ExcludeTags = new string[] { }; ExcludeInheritedTags = new string[] { }; - LocationTypes = new LocationType[] { }; PresetViews = new string[] { }; SourceTypes = new SourceType[] { }; ExcludeSourceTypes = new SourceType[] { }; diff --git a/MediaBrowser.Controller/Entities/LinkedChild.cs b/MediaBrowser.Controller/Entities/LinkedChild.cs index 4d3c13c6e..6031a2448 100644 --- a/MediaBrowser.Controller/Entities/LinkedChild.cs +++ b/MediaBrowser.Controller/Entities/LinkedChild.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; namespace MediaBrowser.Controller.Entities @@ -40,11 +41,18 @@ namespace MediaBrowser.Controller.Entities public class LinkedChildComparer : IEqualityComparer<LinkedChild> { + private readonly IFileSystem _fileSystem; + + public LinkedChildComparer(IFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + public bool Equals(LinkedChild x, LinkedChild y) { if (x.Type == y.Type) { - return string.Equals(x.Path, y.Path, StringComparison.OrdinalIgnoreCase); + return _fileSystem.AreEqual(x.Path, y.Path); } return false; } diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 030831717..3f733ce03 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -152,9 +152,7 @@ namespace MediaBrowser.Controller.Entities.Movies var currentOfficialRating = OfficialRating; // Gather all possible ratings - var ratings = GetRecursiveChildren() - .Concat(GetLinkedChildren()) - .Where(i => i is Movie || i is Series || i is MusicAlbum || i is Game) + var ratings = GetRecursiveChildren(i => i is Movie || i is Series || i is MusicAlbum || i is Game) .Select(i => i.OfficialRating) .Where(i => !string.IsNullOrEmpty(i)) .Distinct(StringComparer.OrdinalIgnoreCase) @@ -205,7 +203,7 @@ namespace MediaBrowser.Controller.Entities.Movies if (base.IsVisible(user)) { - return GetChildren(user, true).Any(); + return base.GetChildren(user, true).Any(); } return false; diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 31bf8d28b..c2f7a6168 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -57,13 +57,10 @@ namespace MediaBrowser.Controller.Entities.TV /// <value>The index number.</value> public int? IndexNumberEnd { get; set; }
- [IgnoreDataMember]
- public string SeriesSortName { get; set; }
-
public string FindSeriesSortName()
{
var series = Series;
- return series == null ? SeriesSortName : series.SortName;
+ return series == null ? SeriesName : series.SortName;
}
[IgnoreDataMember] diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index be268782d..ed04b5ddc 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -51,9 +51,6 @@ namespace MediaBrowser.Controller.Entities.TV get { return SeriesId; } } - [IgnoreDataMember] - public string SeriesSortName { get; set; } - public override double? GetDefaultPrimaryImageAspectRatio() { double value = 2; @@ -65,7 +62,7 @@ namespace MediaBrowser.Controller.Entities.TV public string FindSeriesSortName() { var series = Series; - return series == null ? SeriesSortName : series.SortName; + return series == null ? SeriesName : series.SortName; } // Genre, Rating and Stuido will all be the same @@ -125,7 +122,7 @@ namespace MediaBrowser.Controller.Entities.TV return series.Path; } - return System.IO.Path.GetDirectoryName(Path); + return FileSystem.GetDirectoryName(Path); } } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index f879d0fd8..8da069f62 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -586,7 +586,7 @@ namespace MediaBrowser.Controller.Entities { query.Recursive = true; query.ParentId = queryParent.Id; - query.Genres = new[] { displayParent.Name }; + query.GenreIds = new[] { displayParent.Id.ToString("N") }; query.SetUser(user); query.IncludeItemTypes = new[] { typeof(Movie).Name }; @@ -729,7 +729,7 @@ namespace MediaBrowser.Controller.Entities { query.Recursive = true; query.ParentId = queryParent.Id; - query.Genres = new[] { displayParent.Name }; + query.GenreIds = new[] { displayParent.Id.ToString("N") }; query.SetUser(user); query.IncludeItemTypes = new[] { typeof(Series).Name }; @@ -905,6 +905,11 @@ namespace MediaBrowser.Controller.Entities return false; } + if (request.GenreIds.Length > 0) + { + return false; + } + if (request.HasImdbId.HasValue) { return false; @@ -1768,26 +1773,6 @@ namespace MediaBrowser.Controller.Entities return new List<Folder> { parent }; } - private IEnumerable<BaseItem> GetRecursiveChildren(Folder parent, User user, IEnumerable<string> viewTypes) - { - if (parent == null || parent is UserView) - { - if (user == null) - { - return GetMediaFolders(null, viewTypes).SelectMany(i => i.GetRecursiveChildren()); - } - - return GetMediaFolders(user, viewTypes).SelectMany(i => i.GetRecursiveChildren(user)); - } - - if (user == null) - { - return parent.GetRecursiveChildren(); - } - - return parent.GetRecursiveChildren(user); - } - private async Task<QueryResult<BaseItem>> GetLiveTvView(Folder queryParent, User user, InternalItemsQuery query) { if (query.Recursive) diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 0618fc489..90aa3690a 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -33,7 +33,6 @@ namespace MediaBrowser.Controller.Entities public List<string> AdditionalParts { get; set; } public List<string> LocalAlternateVersions { get; set; } public List<LinkedChild> LinkedAlternateVersions { get; set; } - public List<ChannelMediaInfo> ChannelMediaSources { get; set; } [IgnoreDataMember] public override bool SupportsPlayedStatus @@ -158,7 +157,6 @@ namespace MediaBrowser.Controller.Entities PlayableStreamFileNames = new List<string>(); AdditionalParts = new List<string>(); LocalAlternateVersions = new List<string>(); - Tags = new List<string>(); SubtitleFiles = new List<string>(); LinkedAlternateVersions = new List<LinkedChild>(); } @@ -313,7 +311,7 @@ namespace MediaBrowser.Controller.Entities { if (IsStacked) { - return System.IO.Path.GetDirectoryName(Path); + return FileSystem.GetDirectoryName(Path); } if (!IsPlaceHolder) @@ -591,41 +589,46 @@ namespace MediaBrowser.Controller.Entities .ToList(); } - private static MediaSourceInfo GetVersionInfo(bool enablePathSubstitution, Video i, MediaSourceType type) + private static MediaSourceInfo GetVersionInfo(bool enablePathSubstitution, Video media, MediaSourceType type) { - var mediaStreams = MediaSourceManager.GetMediaStreams(i.Id) + if (media == null) + { + throw new ArgumentNullException("media"); + } + + var mediaStreams = MediaSourceManager.GetMediaStreams(media.Id) .ToList(); - var locationType = i.LocationType; + var locationType = media.LocationType; var info = new MediaSourceInfo { - Id = i.Id.ToString("N"), - IsoType = i.IsoType, + Id = media.Id.ToString("N"), + IsoType = media.IsoType, Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File, MediaStreams = mediaStreams, - Name = GetMediaSourceName(i, mediaStreams), - Path = enablePathSubstitution ? GetMappedPath(i, i.Path, locationType) : i.Path, - RunTimeTicks = i.RunTimeTicks, - Video3DFormat = i.Video3DFormat, - VideoType = i.VideoType, - Container = i.Container, - Size = i.Size, - Timestamp = i.Timestamp, + Name = GetMediaSourceName(media, mediaStreams), + Path = enablePathSubstitution ? GetMappedPath(media, media.Path, locationType) : media.Path, + RunTimeTicks = media.RunTimeTicks, + Video3DFormat = media.Video3DFormat, + VideoType = media.VideoType, + Container = media.Container, + Size = media.Size, + Timestamp = media.Timestamp, Type = type, - PlayableStreamFileNames = i.PlayableStreamFileNames.ToList(), - SupportsDirectStream = i.VideoType == VideoType.VideoFile, - IsRemote = i.IsShortcut + PlayableStreamFileNames = media.PlayableStreamFileNames.ToList(), + SupportsDirectStream = media.VideoType == VideoType.VideoFile, + IsRemote = media.IsShortcut }; if (info.Protocol == MediaProtocol.File) { - info.ETag = i.DateModified.Ticks.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N"); + info.ETag = media.DateModified.Ticks.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N"); } - if (i.IsShortcut) + if (media.IsShortcut) { - info.Path = i.ShortcutPath; + info.Path = media.ShortcutPath; if (info.Path.StartsWith("Http", StringComparison.OrdinalIgnoreCase)) { @@ -647,16 +650,16 @@ namespace MediaBrowser.Controller.Entities if (string.IsNullOrEmpty(info.Container)) { - if (i.VideoType == VideoType.VideoFile || i.VideoType == VideoType.Iso) + if (media.VideoType == VideoType.VideoFile || media.VideoType == VideoType.Iso) { - if (!string.IsNullOrWhiteSpace(i.Path) && locationType != LocationType.Remote && locationType != LocationType.Virtual) + if (!string.IsNullOrWhiteSpace(media.Path) && locationType != LocationType.Remote && locationType != LocationType.Virtual) { - info.Container = System.IO.Path.GetExtension(i.Path).TrimStart('.'); + info.Container = System.IO.Path.GetExtension(media.Path).TrimStart('.'); } } } - info.Bitrate = i.TotalBitrate; + info.Bitrate = media.TotalBitrate; info.InferTotalBitrate(); return info; diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index ebebe71a3..dd2379940 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -456,6 +456,8 @@ namespace MediaBrowser.Controller.Library /// <returns>IEnumerable<Folder>.</returns> List<Folder> GetCollectionFolders(BaseItem item); + List<Folder> GetCollectionFolders(BaseItem item, List<Folder> allUserRootChildren); + LibraryOptions GetLibraryOptions(BaseItem item); /// <summary> diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index 763d27eba..3aa4d4ee2 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -114,7 +114,7 @@ namespace MediaBrowser.Controller.Library return false; } - var parentDir = System.IO.Path.GetDirectoryName(Path) ?? string.Empty; + var parentDir = BaseItem.FileSystem.GetDirectoryName(Path) ?? string.Empty; return parentDir.Length > _appPaths.RootFolderPath.Length && parentDir.StartsWith(_appPaths.RootFolderPath, StringComparison.OrdinalIgnoreCase); @@ -130,7 +130,7 @@ namespace MediaBrowser.Controller.Library { get { - return IsDirectory && string.Equals(Path, _appPaths.RootFolderPath, StringComparison.OrdinalIgnoreCase); + return IsDirectory && BaseItem.FileSystem.AreEqual(Path, _appPaths.RootFolderPath); } } @@ -300,7 +300,7 @@ namespace MediaBrowser.Controller.Library if (args != null) { if (args.Path == null && Path == null) return true; - return args.Path != null && args.Path.Equals(Path, StringComparison.OrdinalIgnoreCase); + return args.Path != null && BaseItem.FileSystem.AreEqual(args.Path, Path); } return false; } diff --git a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs index bcf39558e..0644719b6 100644 --- a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs +++ b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs @@ -16,6 +16,7 @@ namespace MediaBrowser.Controller.Library public BaseItemInfo MediaInfo { get; set; } public string MediaSourceId { get; set; } public bool IsPaused { get; set; } + public bool IsAutomated { get; set; } public string DeviceId { get; set; } public string DeviceName { get; set; } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index fede81faf..6e2fe2495 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -16,7 +16,11 @@ namespace MediaBrowser.Controller.LiveTv { var list = base.GetUserDataKeys(); - list.Insert(0, GetClientTypeName() + "-" + Name); + if (!ConfigurationManager.Configuration.DisableLiveTvChannelUserDataName) + { + list.Insert(0, GetClientTypeName() + "-" + Name); + } + return list; } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 22d09f34a..ca0b97a9f 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -732,7 +732,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)) { - param += " -x264opts:0 subme=0:rc_lookahead=10:me_range=4:me=dia:no_chroma_me:8x8dct=0:partitions=none"; + param += " -x264opts:0 subme=0:me_range=4:rc_lookahead=10:me=dia:no_chroma_me:8x8dct=0:partitions=none"; } if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) && @@ -1517,12 +1517,6 @@ namespace MediaBrowser.Controller.MediaEncoding inputModifier += " " + GetFastSeekCommandLineParameter(state.BaseRequest); inputModifier = inputModifier.Trim(); - //inputModifier += " -fflags +genpts+ignidx+igndts"; - //if (state.IsVideoRequest && genPts) - //{ - // inputModifier += " -fflags +genpts"; - //} - if (!string.IsNullOrEmpty(state.InputAudioSync)) { inputModifier += " -async " + state.InputAudioSync; @@ -1538,6 +1532,21 @@ namespace MediaBrowser.Controller.MediaEncoding inputModifier += " -re"; } + var flags = new List<string>(); + if (state.IgnoreDts) + { + flags.Add("+igndts"); + } + if (state.IgnoreIndex) + { + flags.Add("+ignidx"); + } + + if (flags.Count > 0) + { + inputModifier += " -fflags " + string.Join("", flags.ToArray()); + } + var videoDecoder = GetVideoDecoder(state, encodingOptions); if (!string.IsNullOrWhiteSpace(videoDecoder)) { @@ -1725,6 +1734,11 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec)) { + if (!string.IsNullOrWhiteSpace(encodingOptions.HardwareAccelerationType)) + { + return "-hwaccel auto"; + } + if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) { switch (state.MediaSource.VideoStream.Codec.ToLower()) @@ -1974,5 +1988,65 @@ namespace MediaBrowser.Controller.MediaEncoding return args; } + + public string GetProgressiveAudioFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, string outputPath) + { + var audioTranscodeParams = new List<string>(); + + var bitrate = state.OutputAudioBitrate; + + if (bitrate.HasValue) + { + audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(_usCulture)); + } + + if (state.OutputAudioChannels.HasValue) + { + audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(_usCulture)); + } + + // opus will fail on 44100 + if (!string.Equals(state.OutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)) + { + if (state.OutputAudioSampleRate.HasValue) + { + audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(_usCulture)); + } + } + + var albumCoverInput = string.Empty; + var mapArgs = string.Empty; + var metadata = string.Empty; + var vn = string.Empty; + + var hasArt = !string.IsNullOrWhiteSpace(state.AlbumCoverPath); + + if (hasArt) + { + albumCoverInput = " -i \"" + state.AlbumCoverPath + "\""; + mapArgs = " -map 0:a -map 1:v -c:1:v copy"; + metadata = " -metadata:s:v title=\"Album cover\" -metadata:s:v comment=\"Cover(Front)\""; + } + else + { + vn = " -vn"; + } + + var threads = GetNumberOfThreads(state, encodingOptions, false); + + var inputModifier = GetInputModifier(state, encodingOptions); + + return string.Format("{0} {1}{7}{8} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1{6} -y \"{5}\"", + inputModifier, + GetInputArgument(state, encodingOptions), + threads, + vn, + string.Join(" ", audioTranscodeParams.ToArray()), + outputPath, + metadata, + albumCoverInput, + mapArgs).Trim(); + } + } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index f3e6280aa..28ada9dae 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -39,6 +39,16 @@ namespace MediaBrowser.Controller.MediaEncoding public bool ReadInputAtNativeFramerate { get; set; } + public bool IgnoreDts + { + get { return MediaSource.IgnoreDts; } + } + + public bool IgnoreIndex + { + get { return MediaSource.IgnoreIndex; } + } + public string OutputContainer { get; set; } public string OutputVideoSync @@ -56,6 +66,8 @@ namespace MediaBrowser.Controller.MediaEncoding } } + public string AlbumCoverPath { get; set; } + public string InputAudioSync { get; set; } public string InputVideoSync { get; set; } public TransportStreamTimestamp InputTimestamp { get; set; } diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 78ed1dc59..bac9807a9 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -126,7 +126,6 @@ namespace MediaBrowser.Controller.MediaEncoding Task UpdateEncoderPath(string path, string pathType); bool SupportsEncoder(string encoder); - bool IsDefaultEncoderPath { get; } void SetLogFilename(string name); void ClearLogFilename(); diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 0eb435375..fb1410f4a 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -134,34 +134,27 @@ namespace MediaBrowser.Controller.Playlists var musicGenre = item as MusicGenre; if (musicGenre != null) { - var items = LibraryManager.GetItemList(new InternalItemsQuery(user) + return LibraryManager.GetItemList(new InternalItemsQuery(user) { Recursive = true, IncludeItemTypes = new[] { typeof(Audio).Name }, - Genres = new[] { musicGenre.Name } + GenreIds = new[] { musicGenre.Id.ToString("N") }, + SortBy = new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, + SortOrder = SortOrder.Ascending }); - - return LibraryManager.Sort(items, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending); } var musicArtist = item as MusicArtist; if (musicArtist != null) { - Func<BaseItem, bool> filter = i => + return LibraryManager.GetItemList(new InternalItemsQuery(user) { - var audio = i as Audio; - return audio != null && audio.HasAnyArtist(musicArtist.Name); - }; - - var items = user == null - ? LibraryManager.RootFolder.GetRecursiveChildren(filter) - : user.RootFolder.GetRecursiveChildren(user, new InternalItemsQuery(user) - { - IncludeItemTypes = new[] { typeof(Audio).Name }, - ArtistIds = new[] { musicArtist.Id.ToString("N") } - }); - - return LibraryManager.Sort(items, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending); + Recursive = true, + IncludeItemTypes = new[] { typeof(Audio).Name }, + ArtistIds = new[] { musicArtist.Id.ToString("N") }, + SortBy = new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, + SortOrder = SortOrder.Ascending + }); } var folder = item as Folder; diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs index 40093df3a..62db007b9 100644 --- a/MediaBrowser.Controller/Providers/DirectoryService.cs +++ b/MediaBrowser.Controller/Providers/DirectoryService.cs @@ -63,11 +63,11 @@ namespace MediaBrowser.Controller.Providers //_logger.Debug("Getting files for " + path); entries = new Dictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase); - + try { // using EnumerateFileSystemInfos doesn't handle reparse points (symlinks) - var list = _fileSystem.GetFileSystemEntries(path) + var list = _fileSystem.GetFileSystemEntries(path) .ToList(); // Seeing dupes on some users file system for some reason @@ -80,7 +80,7 @@ namespace MediaBrowser.Controller.Providers { } - //var group = entries.ToLookup(i => Path.GetDirectoryName(i.FullName)).ToList(); + //var group = entries.ToLookup(i => _fileSystem.GetDirectoryName(i.FullName)).ToList(); _cache.TryAdd(path, entries); } diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index f4d45c7e0..c0bc90214 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -20,9 +20,7 @@ namespace MediaBrowser.Controller.Providers /// <summary> /// Queues the refresh. /// </summary> - /// <param name="itemId">The item identifier.</param> - /// <param name="options">The options.</param> - void QueueRefresh(Guid itemId, MetadataRefreshOptions options); + void QueueRefresh(Guid itemId, MetadataRefreshOptions options, RefreshPriority priority); /// <summary> /// Refreshes the full item. @@ -161,4 +159,11 @@ namespace MediaBrowser.Controller.Providers /// <returns>Task{HttpResponseInfo}.</returns> Task<HttpResponseInfo> GetSearchImage(string providerName, string url, CancellationToken cancellationToken); } + + public enum RefreshPriority + { + High = 0, + Normal = 1, + Low = 2 + } }
\ No newline at end of file diff --git a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs index e005ad224..bd33dece8 100644 --- a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs @@ -38,7 +38,7 @@ namespace MediaBrowser.LocalMetadata.Images public List<LocalImageInfo> GetImages(IHasImages item, IDirectoryService directoryService) { - var parentPath = Path.GetDirectoryName(item.Path); + var parentPath = _fileSystem.GetDirectoryName(item.Path); var parentPathFiles = directoryService.GetFileSystemEntries(parentPath) .ToList(); diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index e56bfb49c..2500ee482 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -246,18 +246,6 @@ namespace MediaBrowser.LocalMetadata.Parsers break; } - case "CriticRatingSummary": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - item.CriticRatingSummary = val; - } - - break; - } - case "Language": { var val = reader.ReadElementContentAsString(); @@ -377,17 +365,6 @@ namespace MediaBrowser.LocalMetadata.Parsers break; } - case "MPAADescription": - { - var rating = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(rating)) - { - item.OfficialRatingDescription = rating; - } - break; - } - case "CustomRating": { var val = reader.ReadElementContentAsString(); diff --git a/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs index 9adc4e5a9..69d799b18 100644 --- a/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs @@ -84,7 +84,7 @@ namespace MediaBrowser.LocalMetadata.Parsers // even though it's actually using the metadata folder. filename = Path.GetFileName(filename); - var parentFolder = Path.GetDirectoryName(_xmlPath); + var parentFolder = _fileSystem.GetDirectoryName(_xmlPath); filename = Path.Combine(parentFolder, filename); var file = _fileSystem.GetFileInfo(filename); diff --git a/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs index 7e0f1707f..fda8ea6d0 100644 --- a/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs @@ -39,7 +39,7 @@ namespace MediaBrowser.LocalMetadata.Providers protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) { - var metadataPath = Path.GetDirectoryName(info.Path); + var metadataPath = FileSystem.GetDirectoryName(info.Path); metadataPath = Path.Combine(metadataPath, "metadata"); var metadataFile = Path.Combine(metadataPath, Path.ChangeExtension(Path.GetFileName(info.Path), ".xml")); diff --git a/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs index 888eff416..8ed18df64 100644 --- a/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs @@ -35,7 +35,7 @@ namespace MediaBrowser.LocalMetadata.Providers var specificFile = Path.ChangeExtension(info.Path, ".xml"); var file = FileSystem.GetFileInfo(specificFile); - return info.IsInMixedFolder || file.Exists ? file : FileSystem.GetFileInfo(Path.Combine(Path.GetDirectoryName(info.Path), "game.xml")); + return info.IsInMixedFolder || file.Exists ? file : FileSystem.GetFileInfo(Path.Combine(FileSystem.GetDirectoryName(info.Path), "game.xml")); } } } diff --git a/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs index 0e59db75f..2d4059a1a 100644 --- a/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs @@ -39,7 +39,7 @@ namespace MediaBrowser.LocalMetadata.Providers { var fileInfo = fileSystem.GetFileSystemInfo(info.Path); - var directoryInfo = fileInfo.IsDirectory ? fileInfo : fileSystem.GetDirectoryInfo(Path.GetDirectoryName(info.Path)); + var directoryInfo = fileInfo.IsDirectory ? fileInfo : fileSystem.GetDirectoryInfo(fileSystem.GetDirectoryName(info.Path)); var directoryPath = directoryInfo.FullName; diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs index 105e685f4..a3a55176c 100644 --- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs @@ -46,7 +46,6 @@ namespace MediaBrowser.LocalMetadata.Savers "Countries", "CustomRating", "CriticRating", - "CriticRatingSummary", "DeathDate", "DisplayOrder", "EndDate", @@ -72,8 +71,6 @@ namespace MediaBrowser.LocalMetadata.Savers // Deprecated. No longer saving in this field. "MPAARating", - "MPAADescription", - "MusicBrainzArtistId", "MusicBrainzAlbumArtistId", "MusicBrainzAlbumId", @@ -210,7 +207,7 @@ namespace MediaBrowser.LocalMetadata.Savers private void SaveToFile(Stream stream, string path) { - FileSystem.CreateDirectory(Path.GetDirectoryName(path)); + FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path)); var file = FileSystem.GetFileInfo(path); @@ -309,11 +306,6 @@ namespace MediaBrowser.LocalMetadata.Savers writer.WriteElementString("ContentRating", item.OfficialRating); } - if (!string.IsNullOrEmpty(item.OfficialRatingDescription)) - { - writer.WriteElementString("MPAADescription", item.OfficialRatingDescription); - } - writer.WriteElementString("Added", item.DateCreated.ToLocalTime().ToString("G")); writer.WriteElementString("LockData", item.IsLocked.ToString().ToLower()); @@ -333,11 +325,6 @@ namespace MediaBrowser.LocalMetadata.Savers writer.WriteElementString("CriticRating", item.CriticRating.Value.ToString(UsCulture)); } - if (!string.IsNullOrEmpty(item.CriticRatingSummary)) - { - writer.WriteElementString("CriticRatingSummary", item.CriticRatingSummary); - } - if (!string.IsNullOrEmpty(item.Overview)) { writer.WriteElementString("Overview", item.Overview); diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs index 05b3ca5fc..566e7946d 100644 --- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs @@ -5,8 +5,6 @@ using MediaBrowser.Controller.Session; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using System; -using System.Collections.Generic; -using System.Threading.Tasks; using MediaBrowser.Model.Diagnostics; namespace MediaBrowser.MediaEncoding.Encoder @@ -19,66 +17,9 @@ namespace MediaBrowser.MediaEncoding.Encoder protected override string GetCommandLineArguments(EncodingJob state) { - var audioTranscodeParams = new List<string>(); - - var bitrate = state.OutputAudioBitrate; - - if (bitrate.HasValue) - { - audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(UsCulture)); - } - - if (state.OutputAudioChannels.HasValue) - { - audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(UsCulture)); - } - - // opus will fail on 44100 - if (!string.Equals(state.OutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)) - { - if (state.OutputAudioSampleRate.HasValue) - { - audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture)); - } - } - var encodingOptions = GetEncodingOptions(); - var threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, false); - - var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions); - - var albumCoverInput = string.Empty; - var mapArgs = string.Empty; - var metadata = string.Empty; - var vn = string.Empty; - - var hasArt = !string.IsNullOrWhiteSpace(state.AlbumCoverPath); - hasArt = false; - - if (hasArt) - { - albumCoverInput = " -i \"" + state.AlbumCoverPath + "\""; - mapArgs = " -map 0:a -map 1:v -c:v copy"; - metadata = " -metadata:s:v title=\"Album cover\" -metadata:s:v comment=\"Cover(Front)\""; - } - else - { - vn = " -vn"; - } - - var result = string.Format("{0} {1}{6}{7} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1{8} -y \"{5}\"", - inputModifier, - EncodingHelper.GetInputArgument(state, GetEncodingOptions()), - threads, - vn, - string.Join(" ", audioTranscodeParams.ToArray()), - state.OutputFilePath, - albumCoverInput, - mapArgs, - metadata).Trim(); - - return result; + return EncodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, state.OutputFilePath); } protected override string GetOutputFileExtension(EncodingJob state) diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index e402c2bac..3672e4e84 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -70,7 +70,7 @@ namespace MediaBrowser.MediaEncoding.Encoder .CreateJob(options, EncodingHelper, IsVideoEncoder, progress, cancellationToken).ConfigureAwait(false); encodingJob.OutputFilePath = GetOutputFilePath(encodingJob); - FileSystem.CreateDirectory(Path.GetDirectoryName(encodingJob.OutputFilePath)); + FileSystem.CreateDirectory(FileSystem.GetDirectoryName(encodingJob.OutputFilePath)); encodingJob.ReadInputAtNativeFramerate = options.ReadInputAtNativeFramerate; @@ -108,7 +108,7 @@ namespace MediaBrowser.MediaEncoding.Encoder Logger.Info(commandLineLogMessage); var logFilePath = Path.Combine(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath, "transcode-" + Guid.NewGuid() + ".txt"); - FileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath)); + FileSystem.CreateDirectory(FileSystem.GetDirectoryName(logFilePath)); // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. encodingJob.LogFileStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true); diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs index f74dbce98..9d518c431 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs @@ -41,8 +41,6 @@ namespace MediaBrowser.MediaEncoding.Encoder public string ItemType { get; set; } - public string AlbumCoverPath { get; set; } - public string GetMimeType(string outputPath) { if (!string.IsNullOrEmpty(MimeType)) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs index 498df214f..dc3cb5f5e 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs @@ -45,6 +45,11 @@ namespace MediaBrowser.MediaEncoding.Encoder /// <returns>System.String.</returns> private static string GetFileInputArgument(string path) { + if (path.IndexOf("://") != -1) + { + return string.Format("\"{0}\"", path); + } + // Quotes are valid path characters in linux and they need to be escaped here with a leading \ path = NormalizePath(path); diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 816f14f82..c58a18b0f 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -49,11 +49,6 @@ namespace MediaBrowser.MediaEncoding.Encoder /// </summary> private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(1, 1); - /// <summary> - /// The FF probe resource pool - /// </summary> - private readonly SemaphoreSlim _ffProbeResourcePool = new SemaphoreSlim(2, 2); - public string FFMpegPath { get; private set; } public string FFProbePath { get; private set; } @@ -192,18 +187,6 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - public bool IsDefaultEncoderPath - { - get - { - var path = FFMpegPath; - - var parentPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg", "20160410"); - - return FileSystem.ContainsSubPath(parentPath, path); - } - } - private bool IsSystemInstalledPath(string path) { if (path.IndexOf("/", StringComparison.Ordinal) == -1 && path.IndexOf("\\", StringComparison.Ordinal) == -1) @@ -227,12 +210,11 @@ namespace MediaBrowser.MediaEncoding.Encoder if (EnableEncoderFontFile) { - var directory = Path.GetDirectoryName(FFMpegPath); + var directory = FileSystem.GetDirectoryName(FFMpegPath); if (!string.IsNullOrWhiteSpace(directory) && FileSystem.ContainsSubPath(ConfigurationManager.ApplicationPaths.ProgramDataPath, directory)) { - await new FontConfigLoader(_httpClient, ConfigurationManager.ApplicationPaths, _logger, _zipClient, - FileSystem).DownloadFonts(directory).ConfigureAwait(false); + await new FontConfigLoader(_httpClient, ConfigurationManager.ApplicationPaths, _logger, _zipClient, FileSystem).DownloadFonts(directory).ConfigureAwait(false); } } } @@ -433,7 +415,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private string GetProbePathFromEncoderPath(string appPath) { - return FileSystem.GetFilePaths(Path.GetDirectoryName(appPath)) + return FileSystem.GetFilePaths(FileSystem.GetDirectoryName(appPath)) .FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase)); } @@ -591,20 +573,7 @@ namespace MediaBrowser.MediaEncoding.Encoder using (var processWrapper = new ProcessWrapper(process, this, _logger)) { - await _ffProbeResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - StartProcess(processWrapper); - } - catch (Exception ex) - { - _ffProbeResourcePool.Release(); - - _logger.ErrorException("Error starting ffprobe", ex); - - throw; - } + StartProcess(processWrapper); try { @@ -655,10 +624,6 @@ namespace MediaBrowser.MediaEncoding.Encoder throw; } - finally - { - _ffProbeResourcePool.Release(); - } } } @@ -739,7 +704,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } var tempExtractPath = Path.Combine(ConfigurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid() + ".jpg"); - FileSystem.CreateDirectory(Path.GetDirectoryName(tempExtractPath)); + FileSystem.CreateDirectory(FileSystem.GetDirectoryName(tempExtractPath)); // apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar then scale to width 600. // This filter chain may have adverse effects on recorded tv thumbnails if ar changes during presentation ex. commercials @ diff ar diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 231a2ae85..1e983ca15 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -412,7 +412,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles throw new ArgumentNullException("outputPath"); } - _fileSystem.CreateDirectory(Path.GetDirectoryName(outputPath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(outputPath)); var encodingParam = await GetSubtitleFileCharacterSet(inputPath, language, inputProtocol, cancellationToken).ConfigureAwait(false); @@ -524,8 +524,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { if (!_fileSystem.FileExists(outputPath)) { - await ExtractTextSubtitleInternal(_mediaEncoder.GetInputArgument(inputFiles, protocol), subtitleStreamIndex, - outputCodec, outputPath, cancellationToken).ConfigureAwait(false); + await ExtractTextSubtitleInternal(_mediaEncoder.GetInputArgument(inputFiles, protocol), subtitleStreamIndex, outputCodec, outputPath, cancellationToken).ConfigureAwait(false); } } finally @@ -547,7 +546,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles throw new ArgumentNullException("outputPath"); } - _fileSystem.CreateDirectory(Path.GetDirectoryName(outputPath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(outputPath)); var processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s {2} \"{3}\"", inputPath, subtitleStreamIndex, outputCodec, outputPath); diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 9d795cfab..838111a38 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -68,6 +68,8 @@ namespace MediaBrowser.Model.Configuration /// <value><c>true</c> if [enable case sensitive item ids]; otherwise, <c>false</c>.</value> public bool EnableCaseSensitiveItemIds { get; set; } + public bool DisableLiveTvChannelUserDataName { get; set; } + /// <summary> /// Gets or sets the metadata path. /// </summary> @@ -162,7 +164,6 @@ namespace MediaBrowser.Model.Configuration public bool EnableAutomaticRestart { get; set; } public bool SkipDeserializationForBasicTypes { get; set; } - public bool SkipDeserializationForPrograms { get; set; } public bool SkipDeserializationForAudio { get; set; } public string ServerName { get; set; } diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs index b0760d91f..950d3680d 100644 --- a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs +++ b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs @@ -12,7 +12,9 @@ namespace MediaBrowser.Model.Dlna new ResolutionConfiguration(426, 320000), new ResolutionConfiguration(640, 400000), new ResolutionConfiguration(720, 950000), - new ResolutionConfiguration(1280, 2500000) + new ResolutionConfiguration(1280, 2500000), + new ResolutionConfiguration(1920, 4000000), + new ResolutionConfiguration(3840, 35000000) }; public static ResolutionOptions Normalize(int? inputBitrate, @@ -35,19 +37,15 @@ namespace MediaBrowser.Model.Dlna } } - foreach (var config in Configurations) + var resolutionConfig = GetResolutionConfiguration(outputBitrate); + if (resolutionConfig != null) { - if (outputBitrate <= config.MaxBitrate) - { - var originvalValue = maxWidth; - - maxWidth = Math.Min(config.MaxWidth, maxWidth ?? config.MaxWidth); - if (!originvalValue.HasValue || originvalValue.Value != maxWidth.Value) - { - maxHeight = null; - } + var originvalValue = maxWidth; - break; + maxWidth = Math.Min(resolutionConfig.MaxWidth, maxWidth ?? resolutionConfig.MaxWidth); + if (!originvalValue.HasValue || originvalValue.Value != maxWidth.Value) + { + maxHeight = null; } } @@ -58,6 +56,19 @@ namespace MediaBrowser.Model.Dlna }; } + private static ResolutionConfiguration GetResolutionConfiguration(int outputBitrate) + { + foreach (var config in Configurations) + { + if (outputBitrate <= config.MaxBitrate) + { + return config; + } + } + + return null; + } + private static double GetVideoBitrateScaleFactor(string codec) { if (StringHelper.EqualsIgnoreCase(codec, "h265") || diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index ae7d13868..1a752892e 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -165,12 +165,6 @@ namespace MediaBrowser.Model.Dto public string[] ProductionLocations { get; set; } - /// <summary> - /// Gets or sets the critic rating summary. - /// </summary> - /// <value>The critic rating summary.</value> - public string CriticRatingSummary { get; set; } - public List<string> MultiPartGameFiles { get; set; } /// <summary> diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index 131583024..d0655d90b 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -29,6 +29,8 @@ namespace MediaBrowser.Model.Dto public string ETag { get; set; } public long? RunTimeTicks { get; set; } public bool ReadAtNativeFramerate { get; set; } + public bool IgnoreDts { get; set; } + public bool IgnoreIndex { get; set; } public bool SupportsTranscoding { get; set; } public bool SupportsDirectStream { get; set; } public bool SupportsDirectPlay { get; set; } diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs index 6773acbfa..26de9332e 100644 --- a/MediaBrowser.Model/IO/IFileSystem.cs +++ b/MediaBrowser.Model/IO/IFileSystem.cs @@ -146,6 +146,8 @@ namespace MediaBrowser.Model.IO /// <returns>System.String.</returns> string NormalizePath(string path); + string GetDirectoryName(string path); + /// <summary> /// Gets the file name without extension. /// </summary> diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index 0caf64538..75ba09b60 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -43,11 +43,6 @@ ChildCount, /// <summary> - /// The critic rating summary - /// </summary> - CriticRatingSummary, - - /// <summary> /// The cumulative run time ticks /// </summary> CumulativeRunTimeTicks, @@ -122,8 +117,6 @@ /// </summary> MediaSources, - OfficialRatingDescription, - OriginalTitle, /// <summary> diff --git a/MediaBrowser.Model/Search/SearchHint.cs b/MediaBrowser.Model/Search/SearchHint.cs index cea15a2a7..3ca0eafe6 100644 --- a/MediaBrowser.Model/Search/SearchHint.cs +++ b/MediaBrowser.Model/Search/SearchHint.cs @@ -1,4 +1,6 @@ -namespace MediaBrowser.Model.Search +using System; + +namespace MediaBrowser.Model.Search { /// <summary> /// Class SearchHintResult @@ -94,13 +96,18 @@ /// </summary> /// <value>The display type of the media.</value> public string DisplayMediaType { get; set; } - + + public DateTime? StartDate { get; set; } + public DateTime? EndDate { get; set; } + /// <summary> /// Gets or sets the series. /// </summary> /// <value>The series.</value> public string Series { get; set; } + public string Status { get; set; } + /// <summary> /// Gets or sets the album. /// </summary> diff --git a/MediaBrowser.Model/Search/SearchQuery.cs b/MediaBrowser.Model/Search/SearchQuery.cs index 678dfd39d..897a9db52 100644 --- a/MediaBrowser.Model/Search/SearchQuery.cs +++ b/MediaBrowser.Model/Search/SearchQuery.cs @@ -34,6 +34,17 @@ namespace MediaBrowser.Model.Search public bool IncludeArtists { get; set; } public string[] IncludeItemTypes { get; set; } + public string ParentId { get; set; } + + public bool? IsMovie { get; set; } + + public bool? IsSeries { get; set; } + + public bool? IsNews { get; set; } + + public bool? IsKids { get; set; } + + public bool? IsSports { get; set; } public SearchQuery() { diff --git a/MediaBrowser.Model/Session/GeneralCommandType.cs b/MediaBrowser.Model/Session/GeneralCommandType.cs index 6cceb162a..1a9651559 100644 --- a/MediaBrowser.Model/Session/GeneralCommandType.cs +++ b/MediaBrowser.Model/Session/GeneralCommandType.cs @@ -37,6 +37,7 @@ SetRepeatMode = 29, ChannelUp = 30, ChannelDown = 31, - SetMaxStreamingBitrate = 31 + SetMaxStreamingBitrate = 31, + Guide = 32 } }
\ No newline at end of file diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index 3917b1662..2a987ceb1 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -50,7 +50,6 @@ namespace MediaBrowser.Model.Users /// Gets or sets a value indicating whether [enable synchronize]. /// </summary> /// <value><c>true</c> if [enable synchronize]; otherwise, <c>false</c>.</value> - public bool EnableSync { get; set; } public bool EnableSyncTranscoding { get; set; } public string[] EnabledDevices { get; set; } @@ -71,7 +70,6 @@ namespace MediaBrowser.Model.Users public UserPolicy() { - EnableSync = true; EnableSyncTranscoding = true; EnableMediaPlayback = true; diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs index 547420092..6175c3622 100644 --- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs +++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs @@ -158,7 +158,7 @@ namespace MediaBrowser.Providers.BoxSets var dataFilePath = GetDataFilePath(_config.ApplicationPaths, tmdbId, preferredMetadataLanguage); - _fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath)); _json.SerializeToFile(mainResult, dataFilePath); } diff --git a/MediaBrowser.Providers/ImagesByName/ImageUtils.cs b/MediaBrowser.Providers/ImagesByName/ImageUtils.cs index bbcbbda90..566378dd0 100644 --- a/MediaBrowser.Providers/ImagesByName/ImageUtils.cs +++ b/MediaBrowser.Providers/ImagesByName/ImageUtils.cs @@ -6,8 +6,6 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.ImagesByName @@ -21,36 +19,37 @@ namespace MediaBrowser.Providers.ImagesByName /// <param name="file">The file.</param> /// <param name="httpClient">The HTTP client.</param> /// <param name="fileSystem">The file system.</param> - /// <param name="semaphore">The semaphore.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - public static async Task EnsureList(string url, string file, IHttpClient httpClient, IFileSystem fileSystem, SemaphoreSlim semaphore, CancellationToken cancellationToken) + public static async Task<string> EnsureList(string url, string file, IHttpClient httpClient, IFileSystem fileSystem, CancellationToken cancellationToken) { var fileInfo = fileSystem.GetFileInfo(file); if (!fileInfo.Exists || (DateTime.UtcNow - fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays > 1) { - await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - try + var temp = await httpClient.GetTempFile(new HttpRequestOptions { - var temp = await httpClient.GetTempFile(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Progress = new Progress<double>(), - Url = url + CancellationToken = cancellationToken, + Progress = new Progress<double>(), + Url = url - }).ConfigureAwait(false); + }).ConfigureAwait(false); - fileSystem.CreateDirectory(Path.GetDirectoryName(file)); + fileSystem.CreateDirectory(fileSystem.GetDirectoryName(file)); - fileSystem.CopyFile(temp, file, true); + try + { + fileSystem.CopyFile(temp, file, true); } - finally + catch { - semaphore.Release(); + } + + return temp; } + + return file; } public static string FindMatch(IHasImages item, IEnumerable<string> images) diff --git a/MediaBrowser.Providers/Manager/GenericPriorityQueue.cs b/MediaBrowser.Providers/Manager/GenericPriorityQueue.cs new file mode 100644 index 000000000..03bb0f68c --- /dev/null +++ b/MediaBrowser.Providers/Manager/GenericPriorityQueue.cs @@ -0,0 +1,429 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Priority_Queue +{ + /// <summary> + /// Credit: https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp + /// A copy of StablePriorityQueue which also has generic priority-type + /// </summary> + /// <typeparam name="TItem">The values in the queue. Must extend the GenericPriorityQueue class</typeparam> + /// <typeparam name="TPriority">The priority-type. Must extend IComparable<TPriority></typeparam> + public sealed class GenericPriorityQueue<TItem, TPriority> : IFixedSizePriorityQueue<TItem, TPriority> + where TItem : GenericPriorityQueueNode<TPriority> + where TPriority : IComparable<TPriority> + { + private int _numNodes; + private TItem[] _nodes; + private long _numNodesEverEnqueued; + + /// <summary> + /// Instantiate a new Priority Queue + /// </summary> + /// <param name="maxNodes">The max nodes ever allowed to be enqueued (going over this will cause undefined behavior)</param> + public GenericPriorityQueue(int maxNodes) + { +#if DEBUG + if (maxNodes <= 0) + { + throw new InvalidOperationException("New queue size cannot be smaller than 1"); + } +#endif + + _numNodes = 0; + _nodes = new TItem[maxNodes + 1]; + _numNodesEverEnqueued = 0; + } + + /// <summary> + /// Returns the number of nodes in the queue. + /// O(1) + /// </summary> + public int Count + { + get + { + return _numNodes; + } + } + + /// <summary> + /// Returns the maximum number of items that can be enqueued at once in this queue. Once you hit this number (ie. once Count == MaxSize), + /// attempting to enqueue another item will cause undefined behavior. O(1) + /// </summary> + public int MaxSize + { + get + { + return _nodes.Length - 1; + } + } + + /// <summary> + /// Removes every node from the queue. + /// O(n) (So, don't do this often!) + /// </summary> +#if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public void Clear() + { + Array.Clear(_nodes, 1, _numNodes); + _numNodes = 0; + } + + /// <summary> + /// Returns (in O(1)!) whether the given node is in the queue. O(1) + /// </summary> +#if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public bool Contains(TItem node) + { +#if DEBUG + if (node == null) + { + throw new ArgumentNullException("node"); + } + if (node.QueueIndex < 0 || node.QueueIndex >= _nodes.Length) + { + throw new InvalidOperationException("node.QueueIndex has been corrupted. Did you change it manually? Or add this node to another queue?"); + } +#endif + + return (_nodes[node.QueueIndex] == node); + } + + /// <summary> + /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out. + /// If the queue is full, the result is undefined. + /// If the node is already enqueued, the result is undefined. + /// O(log n) + /// </summary> +#if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public void Enqueue(TItem node, TPriority priority) + { +#if DEBUG + if (node == null) + { + throw new ArgumentNullException("node"); + } + if (_numNodes >= _nodes.Length - 1) + { + throw new InvalidOperationException("Queue is full - node cannot be added: " + node); + } + if (Contains(node)) + { + throw new InvalidOperationException("Node is already enqueued: " + node); + } +#endif + + node.Priority = priority; + _numNodes++; + _nodes[_numNodes] = node; + node.QueueIndex = _numNodes; + node.InsertionIndex = _numNodesEverEnqueued++; + CascadeUp(_nodes[_numNodes]); + } + +#if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + private void Swap(TItem node1, TItem node2) + { + //Swap the nodes + _nodes[node1.QueueIndex] = node2; + _nodes[node2.QueueIndex] = node1; + + //Swap their indicies + int temp = node1.QueueIndex; + node1.QueueIndex = node2.QueueIndex; + node2.QueueIndex = temp; + } + + //Performance appears to be slightly better when this is NOT inlined o_O + private void CascadeUp(TItem node) + { + //aka Heapify-up + int parent = node.QueueIndex / 2; + while (parent >= 1) + { + TItem parentNode = _nodes[parent]; + if (HasHigherPriority(parentNode, node)) + break; + + //Node has lower priority value, so move it up the heap + Swap(node, parentNode); //For some reason, this is faster with Swap() rather than (less..?) individual operations, like in CascadeDown() + + parent = node.QueueIndex / 2; + } + } + +#if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + private void CascadeDown(TItem node) + { + //aka Heapify-down + TItem newParent; + int finalQueueIndex = node.QueueIndex; + while (true) + { + newParent = node; + int childLeftIndex = 2 * finalQueueIndex; + + //Check if the left-child is higher-priority than the current node + if (childLeftIndex > _numNodes) + { + //This could be placed outside the loop, but then we'd have to check newParent != node twice + node.QueueIndex = finalQueueIndex; + _nodes[finalQueueIndex] = node; + break; + } + + TItem childLeft = _nodes[childLeftIndex]; + if (HasHigherPriority(childLeft, newParent)) + { + newParent = childLeft; + } + + //Check if the right-child is higher-priority than either the current node or the left child + int childRightIndex = childLeftIndex + 1; + if (childRightIndex <= _numNodes) + { + TItem childRight = _nodes[childRightIndex]; + if (HasHigherPriority(childRight, newParent)) + { + newParent = childRight; + } + } + + //If either of the children has higher (smaller) priority, swap and continue cascading + if (newParent != node) + { + //Move new parent to its new index. node will be moved once, at the end + //Doing it this way is one less assignment operation than calling Swap() + _nodes[finalQueueIndex] = newParent; + + int temp = newParent.QueueIndex; + newParent.QueueIndex = finalQueueIndex; + finalQueueIndex = temp; + } + else + { + //See note above + node.QueueIndex = finalQueueIndex; + _nodes[finalQueueIndex] = node; + break; + } + } + } + + /// <summary> + /// Returns true if 'higher' has higher priority than 'lower', false otherwise. + /// Note that calling HasHigherPriority(node, node) (ie. both arguments the same node) will return false + /// </summary> +#if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + private bool HasHigherPriority(TItem higher, TItem lower) + { + var cmp = higher.Priority.CompareTo(lower.Priority); + return (cmp < 0 || (cmp == 0 && higher.InsertionIndex < lower.InsertionIndex)); + } + + /// <summary> + /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it. + /// If queue is empty, result is undefined + /// O(log n) + /// </summary> + public bool TryDequeue(out TItem item) + { + if (_numNodes <= 0) + { + item = default(TItem); + return false; + } + +#if DEBUG + + if (!IsValidQueue()) + { + throw new InvalidOperationException("Queue has been corrupted (Did you update a node priority manually instead of calling UpdatePriority()?" + + "Or add the same node to two different queues?)"); + } +#endif + + TItem returnMe = _nodes[1]; + Remove(returnMe); + item = returnMe; + return true; + } + + /// <summary> + /// Resize the queue so it can accept more nodes. All currently enqueued nodes are remain. + /// Attempting to decrease the queue size to a size too small to hold the existing nodes results in undefined behavior + /// O(n) + /// </summary> + public void Resize(int maxNodes) + { +#if DEBUG + if (maxNodes <= 0) + { + throw new InvalidOperationException("Queue size cannot be smaller than 1"); + } + + if (maxNodes < _numNodes) + { + throw new InvalidOperationException("Called Resize(" + maxNodes + "), but current queue contains " + _numNodes + " nodes"); + } +#endif + + TItem[] newArray = new TItem[maxNodes + 1]; + int highestIndexToCopy = Math.Min(maxNodes, _numNodes); + for (int i = 1; i <= highestIndexToCopy; i++) + { + newArray[i] = _nodes[i]; + } + _nodes = newArray; + } + + /// <summary> + /// Returns the head of the queue, without removing it (use Dequeue() for that). + /// If the queue is empty, behavior is undefined. + /// O(1) + /// </summary> + public TItem First + { + get + { +#if DEBUG + if (_numNodes <= 0) + { + throw new InvalidOperationException("Cannot call .First on an empty queue"); + } +#endif + + return _nodes[1]; + } + } + + /// <summary> + /// This method must be called on a node every time its priority changes while it is in the queue. + /// <b>Forgetting to call this method will result in a corrupted queue!</b> + /// Calling this method on a node not in the queue results in undefined behavior + /// O(log n) + /// </summary> +#if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public void UpdatePriority(TItem node, TPriority priority) + { +#if DEBUG + if (node == null) + { + throw new ArgumentNullException("node"); + } + if (!Contains(node)) + { + throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + node); + } +#endif + + node.Priority = priority; + OnNodeUpdated(node); + } + + private void OnNodeUpdated(TItem node) + { + //Bubble the updated node up or down as appropriate + int parentIndex = node.QueueIndex / 2; + TItem parentNode = _nodes[parentIndex]; + + if (parentIndex > 0 && HasHigherPriority(node, parentNode)) + { + CascadeUp(node); + } + else + { + //Note that CascadeDown will be called if parentNode == node (that is, node is the root) + CascadeDown(node); + } + } + + /// <summary> + /// Removes a node from the queue. The node does not need to be the head of the queue. + /// If the node is not in the queue, the result is undefined. If unsure, check Contains() first + /// O(log n) + /// </summary> + public void Remove(TItem node) + { +#if DEBUG + if (node == null) + { + throw new ArgumentNullException("node"); + } + if (!Contains(node)) + { + throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + node); + } +#endif + + //If the node is already the last node, we can remove it immediately + if (node.QueueIndex == _numNodes) + { + _nodes[_numNodes] = null; + _numNodes--; + return; + } + + //Swap the node with the last node + TItem formerLastNode = _nodes[_numNodes]; + Swap(node, formerLastNode); + _nodes[_numNodes] = null; + _numNodes--; + + //Now bubble formerLastNode (which is no longer the last node) up or down as appropriate + OnNodeUpdated(formerLastNode); + } + + public IEnumerator<TItem> GetEnumerator() + { + for (int i = 1; i <= _numNodes; i++) + yield return _nodes[i]; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// <summary> + /// <b>Should not be called in production code.</b> + /// Checks to make sure the queue is still in a valid state. Used for testing/debugging the queue. + /// </summary> + public bool IsValidQueue() + { + for (int i = 1; i < _nodes.Length; i++) + { + if (_nodes[i] != null) + { + int childLeftIndex = 2 * i; + if (childLeftIndex < _nodes.Length && _nodes[childLeftIndex] != null && HasHigherPriority(_nodes[childLeftIndex], _nodes[i])) + return false; + + int childRightIndex = childLeftIndex + 1; + if (childRightIndex < _nodes.Length && _nodes[childRightIndex] != null && HasHigherPriority(_nodes[childRightIndex], _nodes[i])) + return false; + } + } + return true; + } + } +} diff --git a/MediaBrowser.Providers/Manager/GenericPriorityQueueNode.cs b/MediaBrowser.Providers/Manager/GenericPriorityQueueNode.cs new file mode 100644 index 000000000..e6e93e443 --- /dev/null +++ b/MediaBrowser.Providers/Manager/GenericPriorityQueueNode.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Priority_Queue +{ + /// Credit: https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp + public class GenericPriorityQueueNode<TPriority> + { + /// <summary> + /// The Priority to insert this node at. Must be set BEFORE adding a node to the queue (ideally just once, in the node's constructor). + /// Should not be manually edited once the node has been enqueued - use queue.UpdatePriority() instead + /// </summary> + public TPriority Priority { get; protected internal set; } + + /// <summary> + /// Represents the current position in the queue + /// </summary> + public int QueueIndex { get; internal set; } + + /// <summary> + /// Represents the order the node was inserted in + /// </summary> + public long InsertionIndex { get; internal set; } + } +} diff --git a/MediaBrowser.Providers/Manager/IFixedSizePriorityQueue.cs b/MediaBrowser.Providers/Manager/IFixedSizePriorityQueue.cs new file mode 100644 index 000000000..8da88e1c6 --- /dev/null +++ b/MediaBrowser.Providers/Manager/IFixedSizePriorityQueue.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Priority_Queue +{ + /// <summary> + /// Credit: https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp + /// A helper-interface only needed to make writing unit tests a bit easier (hence the 'internal' access modifier) + /// </summary> + internal interface IFixedSizePriorityQueue<TItem, in TPriority> : IPriorityQueue<TItem, TPriority> + where TPriority : IComparable<TPriority> + { + /// <summary> + /// Resize the queue so it can accept more nodes. All currently enqueued nodes are remain. + /// Attempting to decrease the queue size to a size too small to hold the existing nodes results in undefined behavior + /// </summary> + void Resize(int maxNodes); + + /// <summary> + /// Returns the maximum number of items that can be enqueued at once in this queue. Once you hit this number (ie. once Count == MaxSize), + /// attempting to enqueue another item will cause undefined behavior. + /// </summary> + int MaxSize { get; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Providers/Manager/IPriorityQueue.cs b/MediaBrowser.Providers/Manager/IPriorityQueue.cs new file mode 100644 index 000000000..425992b18 --- /dev/null +++ b/MediaBrowser.Providers/Manager/IPriorityQueue.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Priority_Queue +{ + /// <summary> + /// Credit: https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp + /// The IPriorityQueue interface. This is mainly here for purists, and in case I decide to add more implementations later. + /// For speed purposes, it is actually recommended that you *don't* access the priority queue through this interface, since the JIT can + /// (theoretically?) optimize method calls from concrete-types slightly better. + /// </summary> + public interface IPriorityQueue<TItem, in TPriority> : IEnumerable<TItem> + where TPriority : IComparable<TPriority> + { + /// <summary> + /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out. + /// See implementation for how duplicates are handled. + /// </summary> + void Enqueue(TItem node, TPriority priority); + + /// <summary> + /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it. + /// </summary> + bool TryDequeue(out TItem item); + + /// <summary> + /// Removes every node from the queue. + /// </summary> + void Clear(); + + /// <summary> + /// Returns whether the given node is in the queue. + /// </summary> + bool Contains(TItem node); + + /// <summary> + /// Removes a node from the queue. The node does not need to be the head of the queue. + /// </summary> + void Remove(TItem node); + + /// <summary> + /// Call this method to change the priority of a node. + /// </summary> + void UpdatePriority(TItem node, TPriority priority); + + /// <summary> + /// Returns the head of the queue, without removing it (use Dequeue() for that). + /// </summary> + TItem First { get; } + + /// <summary> + /// Returns the number of nodes in the queue. + /// </summary> + int Count { get; } + } +} diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 7c797133f..1d8275c26 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -238,7 +238,7 @@ namespace MediaBrowser.Providers.Manager { _logger.Debug("Saving image to {0}", path); - var parentFolder = Path.GetDirectoryName(path); + var parentFolder = _fileSystem.GetDirectoryName(path); await _imageSaveSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); @@ -247,7 +247,7 @@ namespace MediaBrowser.Providers.Manager _libraryMonitor.ReportFileSystemChangeBeginning(path); _libraryMonitor.ReportFileSystemChangeBeginning(parentFolder); - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); // If the file is currently hidden we'll have to remove that or the save will fail var file = _fileSystem.GetFileInfo(path); @@ -449,7 +449,7 @@ namespace MediaBrowser.Providers.Manager { if (type == ImageType.Primary && item is Episode) { - path = Path.Combine(Path.GetDirectoryName(item.Path), "metadata", filename + extension); + path = Path.Combine(_fileSystem.GetDirectoryName(item.Path), "metadata", filename + extension); } else if (item.DetectIsInMixedFolder()) @@ -581,7 +581,7 @@ namespace MediaBrowser.Providers.Manager if (item is Episode) { - var seasonFolder = Path.GetDirectoryName(item.Path); + var seasonFolder = _fileSystem.GetDirectoryName(item.Path); var imageFilename = _fileSystem.GetFileNameWithoutExtension(item.Path) + "-thumb" + extension; @@ -629,7 +629,7 @@ namespace MediaBrowser.Providers.Manager { imageFilename = "poster"; } - var folder = Path.GetDirectoryName(item.Path); + var folder = _fileSystem.GetDirectoryName(item.Path); return Path.Combine(folder, _fileSystem.GetFileNameWithoutExtension(item.Path) + "-" + imageFilename + extension); } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 0b8dca2eb..7ff018c7b 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -24,6 +24,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; +using Priority_Queue; namespace MediaBrowser.Providers.Manager { @@ -577,7 +578,6 @@ namespace MediaBrowser.Providers.Manager return SaveMetadata(item, updateType, _savers.Where(i => savers.Contains(i.Name, StringComparer.OrdinalIgnoreCase))); } - private readonly SemaphoreSlim _saveLock = new SemaphoreSlim(1, 1); /// <summary> /// Saves the metadata. /// </summary> @@ -607,8 +607,6 @@ namespace MediaBrowser.Providers.Manager continue; } - await _saveLock.WaitAsync().ConfigureAwait(false); - try { _libraryMonitor.ReportFileSystemChangeBeginning(path); @@ -620,7 +618,6 @@ namespace MediaBrowser.Providers.Manager } finally { - _saveLock.Release(); _libraryMonitor.ReportFileSystemChangeComplete(path, false); } } @@ -851,20 +848,20 @@ namespace MediaBrowser.Providers.Manager }); } - private readonly ConcurrentQueue<Tuple<Guid, MetadataRefreshOptions>> _refreshQueue = - new ConcurrentQueue<Tuple<Guid, MetadataRefreshOptions>>(); + private readonly SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>> _refreshQueue = + new SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>>(); private readonly object _refreshQueueLock = new object(); private bool _isProcessingRefreshQueue; - public void QueueRefresh(Guid id, MetadataRefreshOptions options) + public void QueueRefresh(Guid id, MetadataRefreshOptions options, RefreshPriority priority) { if (_disposed) { return; } - _refreshQueue.Enqueue(new Tuple<Guid, MetadataRefreshOptions>(id, options)); + _refreshQueue.Enqueue(new Tuple<Guid, MetadataRefreshOptions>(id, options), (int)priority); lock (_refreshQueueLock) { diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs index 34cf63350..f544c09dc 100644 --- a/MediaBrowser.Providers/Manager/ProviderUtils.cs +++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs @@ -4,6 +4,8 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; +using System.Linq; +using MediaBrowser.Controller.Extensions; namespace MediaBrowser.Providers.Manager { @@ -89,11 +91,6 @@ namespace MediaBrowser.Providers.Manager } } - if (replaceData || string.IsNullOrEmpty(target.OfficialRatingDescription)) - { - target.OfficialRatingDescription = source.OfficialRatingDescription; - } - if (replaceData || string.IsNullOrEmpty(target.CustomRating)) { target.CustomRating = source.CustomRating; @@ -122,6 +119,11 @@ namespace MediaBrowser.Providers.Manager if (replaceData || targetResult.People == null || targetResult.People.Count == 0) { targetResult.People = sourceResult.People; + + } + else if (targetResult.People != null && sourceResult.People != null) + { + MergePeople(sourceResult.People, targetResult.People); } } @@ -205,6 +207,31 @@ namespace MediaBrowser.Providers.Manager } } + private static void MergePeople(List<PersonInfo> source, List<PersonInfo> target) + { + foreach (var person in target) + { + var normalizedName = person.Name.RemoveDiacritics(); + var personInSource = source.FirstOrDefault(i => string.Equals(i.Name.RemoveDiacritics(), normalizedName, StringComparison.OrdinalIgnoreCase)); + + if (personInSource != null) + { + foreach (var providerId in personInSource.ProviderIds) + { + if (!person.ProviderIds.ContainsKey(providerId.Key)) + { + person.ProviderIds[providerId.Key] = providerId.Value; + } + } + + if (string.IsNullOrWhiteSpace(person.ImageUrl)) + { + person.ImageUrl = personInSource.ImageUrl; + } + } + } + } + public static void MergeMetadataSettings(BaseItem source, BaseItem target) { @@ -265,11 +292,6 @@ namespace MediaBrowser.Providers.Manager { target.CriticRating = source.CriticRating; } - - if (replaceData || string.IsNullOrEmpty(target.CriticRatingSummary)) - { - target.CriticRatingSummary = source.CriticRatingSummary; - } } private static void MergeTrailers(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData) diff --git a/MediaBrowser.Providers/Manager/SimplePriorityQueue.cs b/MediaBrowser.Providers/Manager/SimplePriorityQueue.cs new file mode 100644 index 000000000..f4c261a81 --- /dev/null +++ b/MediaBrowser.Providers/Manager/SimplePriorityQueue.cs @@ -0,0 +1,251 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Priority_Queue +{ + /// <summary> + /// Credit: https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp + /// A simplified priority queue implementation. Is stable, auto-resizes, and thread-safe, at the cost of being slightly slower than + /// FastPriorityQueue + /// </summary> + /// <typeparam name="TItem">The type to enqueue</typeparam> + /// <typeparam name="TPriority">The priority-type to use for nodes. Must extend IComparable<TPriority></typeparam> + public class SimplePriorityQueue<TItem, TPriority> : IPriorityQueue<TItem, TPriority> + where TPriority : IComparable<TPriority> + { + private class SimpleNode : GenericPriorityQueueNode<TPriority> + { + public TItem Data { get; private set; } + + public SimpleNode(TItem data) + { + Data = data; + } + } + + private const int INITIAL_QUEUE_SIZE = 10; + private readonly GenericPriorityQueue<SimpleNode, TPriority> _queue; + + public SimplePriorityQueue() + { + _queue = new GenericPriorityQueue<SimpleNode, TPriority>(INITIAL_QUEUE_SIZE); + } + + /// <summary> + /// Given an item of type T, returns the exist SimpleNode in the queue + /// </summary> + private SimpleNode GetExistingNode(TItem item) + { + var comparer = EqualityComparer<TItem>.Default; + foreach (var node in _queue) + { + if (comparer.Equals(node.Data, item)) + { + return node; + } + } + throw new InvalidOperationException("Item cannot be found in queue: " + item); + } + + /// <summary> + /// Returns the number of nodes in the queue. + /// O(1) + /// </summary> + public int Count + { + get + { + lock (_queue) + { + return _queue.Count; + } + } + } + + + /// <summary> + /// Returns the head of the queue, without removing it (use Dequeue() for that). + /// Throws an exception when the queue is empty. + /// O(1) + /// </summary> + public TItem First + { + get + { + lock (_queue) + { + if (_queue.Count <= 0) + { + throw new InvalidOperationException("Cannot call .First on an empty queue"); + } + + SimpleNode first = _queue.First; + return (first != null ? first.Data : default(TItem)); + } + } + } + + /// <summary> + /// Removes every node from the queue. + /// O(n) + /// </summary> + public void Clear() + { + lock (_queue) + { + _queue.Clear(); + } + } + + /// <summary> + /// Returns whether the given item is in the queue. + /// O(n) + /// </summary> + public bool Contains(TItem item) + { + lock (_queue) + { + var comparer = EqualityComparer<TItem>.Default; + foreach (var node in _queue) + { + if (comparer.Equals(node.Data, item)) + { + return true; + } + } + return false; + } + } + + /// <summary> + /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it. + /// If queue is empty, throws an exception + /// O(log n) + /// </summary> + public bool TryDequeue(out TItem item) + { + lock (_queue) + { + if (_queue.Count <= 0) + { + item = default(TItem); + return false; + } + + SimpleNode node; + if (_queue.TryDequeue(out node)) + { + item = node.Data; + return true; + } + + item = default(TItem); + return false; + } + } + + /// <summary> + /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out. + /// This queue automatically resizes itself, so there's no concern of the queue becoming 'full'. + /// Duplicates are allowed. + /// O(log n) + /// </summary> + public void Enqueue(TItem item, TPriority priority) + { + lock (_queue) + { + SimpleNode node = new SimpleNode(item); + if (_queue.Count == _queue.MaxSize) + { + _queue.Resize(_queue.MaxSize * 2 + 1); + } + _queue.Enqueue(node, priority); + } + } + + /// <summary> + /// Removes an item from the queue. The item does not need to be the head of the queue. + /// If the item is not in the queue, an exception is thrown. If unsure, check Contains() first. + /// If multiple copies of the item are enqueued, only the first one is removed. + /// O(n) + /// </summary> + public void Remove(TItem item) + { + lock (_queue) + { + try + { + _queue.Remove(GetExistingNode(item)); + } + catch (InvalidOperationException ex) + { + throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + item, ex); + } + } + } + + /// <summary> + /// Call this method to change the priority of an item. + /// Calling this method on a item not in the queue will throw an exception. + /// If the item is enqueued multiple times, only the first one will be updated. + /// (If your requirements are complex enough that you need to enqueue the same item multiple times <i>and</i> be able + /// to update all of them, please wrap your items in a wrapper class so they can be distinguished). + /// O(n) + /// </summary> + public void UpdatePriority(TItem item, TPriority priority) + { + lock (_queue) + { + try + { + SimpleNode updateMe = GetExistingNode(item); + _queue.UpdatePriority(updateMe, priority); + } + catch (InvalidOperationException ex) + { + throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + item, ex); + } + } + } + + public IEnumerator<TItem> GetEnumerator() + { + List<TItem> queueData = new List<TItem>(); + lock (_queue) + { + //Copy to a separate list because we don't want to 'yield return' inside a lock + foreach (var node in _queue) + { + queueData.Add(node.Data); + } + } + + return queueData.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public bool IsValidQueue() + { + lock (_queue) + { + return _queue.IsValidQueue(); + } + } + } + + /// <summary> + /// A simplified priority queue implementation. Is stable, auto-resizes, and thread-safe, at the cost of being slightly slower than + /// FastPriorityQueue + /// This class is kept here for backwards compatibility. It's recommended you use Simple + /// </summary> + /// <typeparam name="TItem">The type to enqueue</typeparam> + public class SimplePriorityQueue<TItem> : SimplePriorityQueue<TItem, float> { } +}
\ No newline at end of file diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index fe554545f..9d20ec423 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -65,10 +65,15 @@ <Compile Include="LiveTv\ChannelMetadataService.cs" /> <Compile Include="LiveTv\ProgramMetadataService.cs" /> <Compile Include="LiveTv\VideoRecordingService.cs" /> + <Compile Include="Manager\GenericPriorityQueue.cs" /> + <Compile Include="Manager\GenericPriorityQueueNode.cs" /> + <Compile Include="Manager\IFixedSizePriorityQueue.cs" /> <Compile Include="Manager\ImageSaver.cs" /> + <Compile Include="Manager\IPriorityQueue.cs" /> <Compile Include="Manager\ItemImageProvider.cs" /> <Compile Include="Manager\ProviderManager.cs" /> <Compile Include="Manager\MetadataService.cs" /> + <Compile Include="Manager\SimplePriorityQueue.cs" /> <Compile Include="MediaInfo\FFProbeAudioInfo.cs" /> <Compile Include="MediaInfo\FFProbeProvider.cs" /> <Compile Include="MediaInfo\FFProbeVideoInfo.cs" /> diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index d65084287..cc5e7aef9 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -62,7 +62,7 @@ namespace MediaBrowser.Providers.MediaInfo if (!_fileSystem.FileExists(path)) { - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); var imageStream = imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("front", StringComparison.OrdinalIgnoreCase) != -1) ?? imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1) ?? diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index afcf4b226..04e549526 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -78,7 +78,7 @@ namespace MediaBrowser.Providers.MediaInfo }, cancellationToken).ConfigureAwait(false); - //Directory.CreateDirectory(Path.GetDirectoryName(cachePath)); + //Directory.CreateDirectory(_fileSystem.GetDirectoryName(cachePath)); //_json.SerializeToFile(result, cachePath); return result; diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 0aaa56bab..c686e1d2e 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -150,7 +150,7 @@ namespace MediaBrowser.Providers.MediaInfo }, cancellationToken).ConfigureAwait(false); - //Directory.CreateDirectory(Path.GetDirectoryName(cachePath)); + //Directory.CreateDirectory(_fileSystem.GetDirectoryName(cachePath)); //_json.SerializeToFile(result, cachePath); return result; @@ -356,11 +356,6 @@ namespace MediaBrowser.Providers.MediaInfo } } - if (!string.IsNullOrWhiteSpace(data.OfficialRatingDescription) || isFullRefresh) - { - video.OfficialRatingDescription = data.OfficialRatingDescription; - } - if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Genres)) { if (video.Genres.Count == 0 || isFullRefresh) diff --git a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs index dd2cad1f9..84d8d20ad 100644 --- a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs +++ b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs @@ -277,14 +277,13 @@ namespace MediaBrowser.Providers.Movies var path = GetFanartJsonPath(id); - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); try { using (var response = await _httpClient.Get(new HttpRequestOptions { Url = url, - ResourcePool = FanartArtistProvider.Current.FanArtResourcePool, CancellationToken = cancellationToken, BufferContent = true diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs index 0101478b8..670215479 100644 --- a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs +++ b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs @@ -94,7 +94,7 @@ namespace MediaBrowser.Providers.Movies tmdbId = movieInfo.id.ToString(_usCulture); dataFilePath = MovieDbProvider.Current.GetDataFilePath(tmdbId, language); - _fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath)); _jsonSerializer.SerializeToFile(movieInfo, dataFilePath); } } diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index 8e4b86519..d6aef5d1c 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -30,8 +30,6 @@ namespace MediaBrowser.Providers.Movies /// </summary> public class MovieDbProvider : IRemoteMetadataProvider<Movie, MovieInfo>, IDisposable, IHasOrder { - internal readonly SemaphoreSlim MovieDbResourcePool = new SemaphoreSlim(1, 1); - internal static MovieDbProvider Current { get; private set; } private readonly IJsonSerializer _jsonSerializer; @@ -137,10 +135,6 @@ namespace MediaBrowser.Providers.Movies /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> protected virtual void Dispose(bool dispose) { - if (dispose) - { - MovieDbResourcePool.Dispose(); - } } /// <summary> @@ -214,7 +208,7 @@ namespace MediaBrowser.Providers.Movies var dataFilePath = GetDataFilePath(id, preferredMetadataLanguage); - _fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath)); _jsonSerializer.SerializeToFile(mainResult, dataFilePath); } @@ -431,7 +425,6 @@ namespace MediaBrowser.Providers.Movies await Task.Delay(Convert.ToInt32(delayMs)).ConfigureAwait(false); } - options.ResourcePool = MovieDbResourcePool; _lastRequestTicks = DateTime.UtcNow.Ticks; options.BufferContent = true; diff --git a/MediaBrowser.Providers/Music/AlbumImageFromSongProvider.cs b/MediaBrowser.Providers/Music/AlbumImageFromSongProvider.cs index 7abe9cc07..0cb1a7ff1 100644 --- a/MediaBrowser.Providers/Music/AlbumImageFromSongProvider.cs +++ b/MediaBrowser.Providers/Music/AlbumImageFromSongProvider.cs @@ -20,8 +20,7 @@ namespace MediaBrowser.Providers.Music { var album = (MusicAlbum)item; - var image = album.GetRecursiveChildren() - .OfType<Audio>() + var image = album.GetRecursiveChildren(i => !i.IsFolder) .Select(i => i.GetImageInfo(type, 0)) .FirstOrDefault(i => i != null && i.IsLocalFile); diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs index 96eab63cd..6d9b20689 100644 --- a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs @@ -159,7 +159,7 @@ namespace MediaBrowser.Providers.Music var path = GetAlbumInfoPath(_config.ApplicationPaths, musicBrainzReleaseGroupId); - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); using (var response = await _httpClient.Get(new HttpRequestOptions { diff --git a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs b/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs index 0f0c31e6e..c960e59a3 100644 --- a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs +++ b/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs @@ -27,7 +27,6 @@ namespace MediaBrowser.Providers.Music public static AudioDbArtistProvider Current; - public SemaphoreSlim AudioDbResourcePool = new SemaphoreSlim(2, 2); private const string ApiKey = "49jhsf8248yfahka89724011"; public const string BaseUrl = "http://www.theaudiodb.com/api/v1/json/" + ApiKey; @@ -151,13 +150,12 @@ namespace MediaBrowser.Providers.Music using (var response = await _httpClient.Get(new HttpRequestOptions { Url = url, - ResourcePool = AudioDbResourcePool, CancellationToken = cancellationToken, BufferContent = true }).ConfigureAwait(false)) { - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs index 6fd0d82bd..977f81414 100644 --- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs @@ -27,7 +27,6 @@ namespace MediaBrowser.Providers.Music { public class FanartArtistProvider : IRemoteImageProvider, IHasOrder { - internal readonly SemaphoreSlim FanArtResourcePool = new SemaphoreSlim(3, 3); internal const string ApiKey = "5c6b04c68e904cfed1e6cbc9a9e683d4"; private const string FanArtBaseUrl = "https://webservice.fanart.tv/v3.1/music/{1}?api_key={0}"; @@ -248,14 +247,13 @@ namespace MediaBrowser.Providers.Music var jsonPath = GetArtistJsonPath(_config.ApplicationPaths, musicBrainzId); - _fileSystem.CreateDirectory(Path.GetDirectoryName(jsonPath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(jsonPath)); try { using (var response = await _httpClient.Get(new HttpRequestOptions { Url = url, - ResourcePool = FanArtResourcePool, CancellationToken = cancellationToken, BufferContent = true diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs index 4aefb62c8..b77fcf1b2 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs @@ -548,11 +548,6 @@ namespace MediaBrowser.Providers.Music return null; } - /// <summary> - /// The _music brainz resource pool - /// </summary> - private readonly SemaphoreSlim _musicBrainzResourcePool = new SemaphoreSlim(1, 1); - private long _lastMbzUrlQueryTicks = 0; private List<MbzUrl> _mbzUrls = null; private MbzUrl _chosenUrl; @@ -656,7 +651,6 @@ namespace MediaBrowser.Providers.Music Url = url, CancellationToken = cancellationToken, UserAgent = _appHost.Name + "/" + _appHost.ApplicationVersion, - ResourcePool = _musicBrainzResourcePool, BufferContent = throttleMs > 0 }; diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index 2c368c97b..d1c3b2214 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -19,7 +19,6 @@ namespace MediaBrowser.Providers.Omdb { public class OmdbProvider { - internal static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1); private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; @@ -104,17 +103,17 @@ namespace MediaBrowser.Providers.Omdb ParseAdditionalMetadata(itemResult, result); } - public async Task<bool> FetchEpisodeData<T>(MetadataResult<T> itemResult, int episodeNumber, int seasonNumber, string imdbId, string language, string country, CancellationToken cancellationToken) + public async Task<bool> FetchEpisodeData<T>(MetadataResult<T> itemResult, int episodeNumber, int seasonNumber, string episodeImdbId, string seriesImdbId, string language, string country, CancellationToken cancellationToken) where T : BaseItem { - if (string.IsNullOrWhiteSpace(imdbId)) + if (string.IsNullOrWhiteSpace(seriesImdbId)) { - throw new ArgumentNullException("imdbId"); + throw new ArgumentNullException("seriesImdbId"); } T item = itemResult.Item; - var seasonResult = await GetSeasonRootObject(imdbId, seasonNumber, cancellationToken).ConfigureAwait(false); + var seasonResult = await GetSeasonRootObject(seriesImdbId, seasonNumber, cancellationToken).ConfigureAwait(false); if (seasonResult == null) { @@ -123,12 +122,28 @@ namespace MediaBrowser.Providers.Omdb RootObject result = null; - foreach (var episode in (seasonResult.Episodes ?? new RootObject[] { })) + if (!string.IsNullOrWhiteSpace(episodeImdbId)) { - if (episode.Episode == episodeNumber) + foreach (var episode in (seasonResult.Episodes ?? new RootObject[] { })) { - result = episode; - break; + if (string.Equals(episodeImdbId, episode.imdbID, StringComparison.OrdinalIgnoreCase)) + { + result = episode; + break; + } + } + } + + // finally, search by numbers + if (result == null) + { + foreach (var episode in (seasonResult.Episodes ?? new RootObject[] { })) + { + if (episode.Episode == episodeNumber) + { + result = episode; + break; + } } } @@ -283,7 +298,7 @@ namespace MediaBrowser.Providers.Omdb using (var stream = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false)) { var rootObject = _jsonSerializer.DeserializeFromStream<RootObject>(stream); - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); _jsonSerializer.SerializeToFile(rootObject, path); } @@ -318,7 +333,7 @@ namespace MediaBrowser.Providers.Omdb using (var stream = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false)) { var rootObject = _jsonSerializer.DeserializeFromStream<SeasonRootObject>(stream); - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); _jsonSerializer.SerializeToFile(rootObject, path); } @@ -330,7 +345,6 @@ namespace MediaBrowser.Providers.Omdb return httpClient.Get(new HttpRequestOptions { Url = url, - ResourcePool = ResourcePool, CancellationToken = cancellationToken, BufferContent = true, EnableDefaultUserAgent = true diff --git a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs index 3645a5f8d..19b8f292c 100644 --- a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs +++ b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs @@ -250,7 +250,7 @@ namespace MediaBrowser.Providers.People }).ConfigureAwait(false)) { - _fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath)); using (var fs = _fileSystem.GetFileStream(dataFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs index bf017d148..8579bd16b 100644 --- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs +++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs @@ -4,15 +4,12 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; -using MediaBrowser.Providers.Genres; using MediaBrowser.Providers.ImagesByName; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Studios @@ -23,8 +20,6 @@ namespace MediaBrowser.Providers.Studios private readonly IHttpClient _httpClient; private readonly IFileSystem _fileSystem; - private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1); - public StudiosImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) { _config = config; @@ -69,7 +64,7 @@ namespace MediaBrowser.Providers.Studios { var posterPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotestudioposters.txt"); - await EnsurePosterList(posterPath, cancellationToken).ConfigureAwait(false); + posterPath = await EnsurePosterList(posterPath, cancellationToken).ConfigureAwait(false); list.Add(GetImage(item, posterPath, ImageType.Primary, "folder")); } @@ -80,7 +75,7 @@ namespace MediaBrowser.Providers.Studios { var thumbsPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotestudiothumbs.txt"); - await EnsureThumbsList(thumbsPath, cancellationToken).ConfigureAwait(false); + thumbsPath = await EnsureThumbsList(thumbsPath, cancellationToken).ConfigureAwait(false); list.Add(GetImage(item, thumbsPath, ImageType.Thumb, "thumb")); } @@ -114,18 +109,18 @@ namespace MediaBrowser.Providers.Studios return string.Format("https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studios/{0}/{1}.jpg", image, filename); } - private Task EnsureThumbsList(string file, CancellationToken cancellationToken) + private Task<string> EnsureThumbsList(string file, CancellationToken cancellationToken) { const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studiothumbs.txt"; - return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken); + return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, cancellationToken); } - private Task EnsurePosterList(string file, CancellationToken cancellationToken) + private Task<string> EnsurePosterList(string file, CancellationToken cancellationToken) { const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studioposters.txt"; - return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken); + return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, cancellationToken); } public int Order diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index cd741bed5..1cf965e02 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -115,7 +115,7 @@ namespace MediaBrowser.Providers.Subtitles using (var stream = response.Stream) { - var savePath = Path.Combine(Path.GetDirectoryName(video.Path), + var savePath = Path.Combine(_fileSystem.GetDirectoryName(video.Path), _fileSystem.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower()); if (response.IsForced) @@ -256,12 +256,7 @@ namespace MediaBrowser.Providers.Subtitles _monitor.ReportFileSystemChangeComplete(path, false); } - return _libraryManager.GetItemById(itemId).RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) - { - ImageRefreshMode = ImageRefreshMode.ValidationOnly, - MetadataRefreshMode = MetadataRefreshMode.ValidationOnly - - }, CancellationToken.None); + return _libraryManager.GetItemById(itemId).ChangedExternally(); } public Task<SubtitleResponse> GetRemoteSubtitles(string id, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs index fd4d041b2..279447a18 100644 --- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs +++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs @@ -53,8 +53,8 @@ namespace MediaBrowser.Providers.TV private async Task<bool> AddDummySeasonFolders(Series series, CancellationToken cancellationToken) { - var episodesInSeriesFolder = series.GetRecursiveChildren() - .OfType<Episode>() + var episodesInSeriesFolder = series.GetRecursiveChildren(i => i is Episode) + .Cast<Episode>() .Where(i => !i.IsInSeasonFolder) .ToList(); @@ -125,8 +125,7 @@ namespace MediaBrowser.Providers.TV Id = _libraryManager.GetNewItemId((series.Id + (seasonNumber ?? -1).ToString(_usCulture) + seasonName), typeof(Season)), IsVirtualItem = isVirtualItem, SeriesId = series.Id, - SeriesName = series.Name, - SeriesSortName = series.SortName + SeriesName = series.Name }; season.SetParent(series); diff --git a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs index 538d96c17..bef4d8815 100644 --- a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs +++ b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs @@ -27,13 +27,6 @@ namespace MediaBrowser.Providers.TV updateType |= ItemUpdateType.MetadataImport; } - var seriesSortName = item.FindSeriesSortName(); - if (!string.Equals(item.SeriesSortName, seriesSortName, StringComparison.Ordinal)) - { - item.SeriesSortName = seriesSortName; - updateType |= ItemUpdateType.MetadataImport; - } - var seasonName = item.FindSeasonName(); if (!string.Equals(item.SeasonName, seasonName, StringComparison.Ordinal)) { diff --git a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs index 8db3eaa79..35178e1fe 100644 --- a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs @@ -312,14 +312,13 @@ namespace MediaBrowser.Providers.TV var path = GetFanartJsonPath(tvdbId); - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); try { using (var response = await _httpClient.Get(new HttpRequestOptions { Url = url, - ResourcePool = FanartArtistProvider.Current.FanArtResourcePool, CancellationToken = cancellationToken, BufferContent = true diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index d5154b1d3..5b9e5d5eb 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -203,7 +203,7 @@ namespace MediaBrowser.Providers.TV CancellationToken cancellationToken) { var existingEpisodes = (from s in series - from c in s.GetRecursiveChildren().OfType<Episode>() + from c in s.GetRecursiveChildren(i => i is Episode).Cast<Episode>() select new Tuple<int, Episode>((c.ParentIndexNumber ?? 0) , c)) .ToList(); @@ -275,13 +275,16 @@ namespace MediaBrowser.Providers.TV return hasChanges; } - private Series DetermineAppropriateSeries(IEnumerable<Series> series, int seasonNumber) + private Series DetermineAppropriateSeries(List<Series> series, int seasonNumber) { - var seriesAndOffsets = series.ToList(); + if (series.Count == 1) + { + return series[0]; + } - return seriesAndOffsets.FirstOrDefault(s => s.GetRecursiveChildren().OfType<Season>().Any(season => (season.IndexNumber) == seasonNumber)) ?? - seriesAndOffsets.FirstOrDefault(s => s.GetRecursiveChildren().OfType<Season>().Any(season => (season.IndexNumber) == 1)) ?? - seriesAndOffsets.OrderBy(s => s.GetRecursiveChildren().OfType<Season>().Select(season => season.IndexNumber).Min()).First(); + return series.FirstOrDefault(s => s.GetRecursiveChildren(i => i is Season).Any(season => (season.IndexNumber) == seasonNumber)) ?? + series.FirstOrDefault(s => s.GetRecursiveChildren(i => i is Season).Any(season => (season.IndexNumber) == 1)) ?? + series.OrderBy(s => s.GetRecursiveChildren(i => i is Season).Select(season => season.IndexNumber).Min()).First(); } /// <summary> @@ -292,7 +295,7 @@ namespace MediaBrowser.Providers.TV bool allowMissingEpisodes) { var existingEpisodes = (from s in series - from c in s.GetRecursiveChildren().OfType<Episode>() + from c in s.GetRecursiveChildren(i => i is Episode).Cast<Episode>() select new { Episode = c }) .ToList(); @@ -402,7 +405,7 @@ namespace MediaBrowser.Providers.TV // Season does not have a number // Remove if there are no episodes directly in series without a season number - return i.Series.GetRecursiveChildren().OfType<Episode>().All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder); + return i.Series.GetRecursiveChildren(e => e is Episode).Cast<Episode>().All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder); }) .ToList(); diff --git a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs index 56aa3967c..f73244cdf 100644 --- a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs @@ -58,11 +58,10 @@ namespace MediaBrowser.Providers.TV string seriesImdbId; if (info.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out seriesImdbId) && !string.IsNullOrEmpty(seriesImdbId)) { - if (info.IndexNumber.HasValue && - info.ParentIndexNumber.HasValue) + if (info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue) { result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager) - .FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); + .FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, info.GetProviderId(MetadataProviders.Imdb), seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); } } diff --git a/MediaBrowser.Providers/TV/SeasonMetadataService.cs b/MediaBrowser.Providers/TV/SeasonMetadataService.cs index af7dea59e..74c8b4ec3 100644 --- a/MediaBrowser.Providers/TV/SeasonMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeasonMetadataService.cs @@ -44,13 +44,6 @@ namespace MediaBrowser.Providers.TV updateType |= ItemUpdateType.MetadataImport; } - var seriesSortName = item.FindSeriesSortName(); - if (!string.Equals(item.SeriesSortName, seriesSortName, StringComparison.Ordinal)) - { - item.SeriesSortName = seriesSortName; - updateType |= ItemUpdateType.MetadataImport; - } - var seriesPresentationUniqueKey = item.FindSeriesPresentationUniqueKey(); if (!string.Equals(item.SeriesPresentationUniqueKey, seriesPresentationUniqueKey, StringComparison.Ordinal)) { diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs index 38831feb6..b2d70918c 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs @@ -106,7 +106,7 @@ namespace MediaBrowser.Providers.TV var dataFilePath = GetDataFilePath(id, seasonNumber, episodeNumber, preferredMetadataLanguage); - _fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath)); _jsonSerializer.SerializeToFile(mainResult, dataFilePath); } @@ -142,8 +142,7 @@ namespace MediaBrowser.Providers.TV return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = MovieDbProvider.Current.MovieDbResourcePool + Url = url }); } diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs index 1f0cc9e52..f41e254ca 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs @@ -190,7 +190,7 @@ namespace MediaBrowser.Providers.TV var dataFilePath = GetDataFilePath(id, seasonNumber, preferredMetadataLanguage); - _fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath)); _jsonSerializer.SerializeToFile(mainResult, dataFilePath); } diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs index 5b4ae9745..f29024737 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs @@ -198,7 +198,7 @@ namespace MediaBrowser.Providers.TV tmdbId = seriesInfo.id.ToString(_usCulture); dataFilePath = GetDataFilePath(tmdbId, language); - _fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath)); _jsonSerializer.SerializeToFile(seriesInfo, dataFilePath); await EnsureSeriesInfo(tmdbId, language, cancellationToken).ConfigureAwait(false); @@ -330,7 +330,7 @@ namespace MediaBrowser.Providers.TV var dataFilePath = GetDataFilePath(id, preferredMetadataLanguage); - _fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath)); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath)); _jsonSerializer.SerializeToFile(mainResult, dataFilePath); } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index 989748846..030150e4d 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -175,8 +175,7 @@ namespace MediaBrowser.Providers.TV return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index 4a52b972f..24c2b507a 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -226,7 +226,7 @@ namespace MediaBrowser.Providers.TV if (searchInfo.IndexNumber.HasValue) { - var files = GetEpisodeXmlFiles(searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, Path.GetDirectoryName(xmlFile)); + var files = GetEpisodeXmlFiles(searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, _fileSystem.GetDirectoryName(xmlFile)); list = files.Select(GetXmlReader).ToList(); } @@ -919,8 +919,7 @@ namespace MediaBrowser.Providers.TV return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool + Url = url }); } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs index 72bd62d9f..e8ed05225 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs @@ -143,7 +143,6 @@ namespace MediaBrowser.Providers.TV Url = ServerTimeUrl, CancellationToken = cancellationToken, EnableHttpCompression = true, - ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool, BufferContent = false }).ConfigureAwait(false)) @@ -240,7 +239,6 @@ namespace MediaBrowser.Providers.TV Url = string.Format(UpdatesUrl, lastUpdateTime), CancellationToken = cancellationToken, EnableHttpCompression = true, - ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool, BufferContent = false }).ConfigureAwait(false)) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index e68b7ad1d..daa6e78f5 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -358,8 +358,7 @@ namespace MediaBrowser.Providers.TV return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index cdb9ac51e..50bc6bc74 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -347,8 +347,7 @@ namespace MediaBrowser.Providers.TV return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index af37c7632..4c5e57a94 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -27,7 +27,6 @@ namespace MediaBrowser.Providers.TV { public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder { - internal readonly SemaphoreSlim TvDbResourcePool = new SemaphoreSlim(2, 2); internal static TvdbSeriesProvider Current { get; private set; } private readonly IZipClient _zipClient; private readonly IHttpClient _httpClient; @@ -220,7 +219,6 @@ namespace MediaBrowser.Providers.TV using (var zipStream = await _httpClient.Get(new HttpRequestOptions { Url = url, - ResourcePool = TvDbResourcePool, CancellationToken = cancellationToken, BufferContent = false @@ -265,7 +263,6 @@ namespace MediaBrowser.Providers.TV using (var result = await _httpClient.Get(new HttpRequestOptions { Url = url, - ResourcePool = TvDbResourcePool, CancellationToken = cancellationToken, BufferContent = false @@ -520,7 +517,6 @@ namespace MediaBrowser.Providers.TV using (var stream = await _httpClient.Get(new HttpRequestOptions { Url = url, - ResourcePool = TvDbResourcePool, CancellationToken = cancellationToken, BufferContent = false @@ -1651,7 +1647,6 @@ namespace MediaBrowser.Providers.TV { CancellationToken = cancellationToken, Url = url, - ResourcePool = TvDbResourcePool, BufferContent = false }); } diff --git a/MediaBrowser.Server.Mac.sln b/MediaBrowser.Server.Mac.sln index 037cd488b..bc7cba21f 100644 --- a/MediaBrowser.Server.Mac.sln +++ b/MediaBrowser.Server.Mac.sln @@ -37,8 +37,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DvdLib", "DvdLib\DvdLib.csp EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.csproj", "{805844AB-E92F-45E6-9D99-4F6D48D129A5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.ImageMagick", "Emby.Drawing.ImageMagick\Emby.Drawing.ImageMagick.csproj", "{6CFEE013-6E7C-432B-AC37-CABF0880C69A}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.Net", "Emby.Drawing.Net\Emby.Drawing.Net.csproj", "{C97A239E-A96C-4D64-A844-CCF8CC30AECB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}"
@@ -400,26 +398,6 @@ Global {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Signed|Any CPU.Build.0 = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Signed|x86.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Signed|x86.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.AppStore|Any CPU.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.AppStore|x86.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.AppStore|x86.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Debug|x86.ActiveCfg = Debug|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Debug|x86.Build.0 = Debug|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release Mono|x86.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release|Any CPU.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release|x86.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release|x86.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|Any CPU.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|x86.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|x86.Build.0 = Release|Any CPU
{C97A239E-A96C-4D64-A844-CCF8CC30AECB}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{C97A239E-A96C-4D64-A844-CCF8CC30AECB}.AppStore|Any CPU.Build.0 = Release|Any CPU
{C97A239E-A96C-4D64-A844-CCF8CC30AECB}.AppStore|x86.ActiveCfg = Release|Any CPU
diff --git a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj index 0cf488bd0..8133efafb 100644 --- a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj +++ b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj @@ -162,9 +162,6 @@ <ItemGroup>
<None Include="Info.plist" />
<None Include="packages.config" />
- <None Include="ImageMagickSharp.dll.config">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
@@ -338,18 +335,6 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\gamegenres.html">
<Link>Resources\dashboard-ui\gamegenres.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\games.html">
- <Link>Resources\dashboard-ui\games.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\gamesrecommended.html">
- <Link>Resources\dashboard-ui\gamesrecommended.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\gamestudios.html">
- <Link>Resources\dashboard-ui\gamestudios.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\gamesystems.html">
- <Link>Resources\dashboard-ui\gamesystems.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\home.html">
<Link>Resources\dashboard-ui\home.html</Link>
</BundleResource>
@@ -953,84 +938,6 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\material-icons\style.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\material-icons\style.css</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\-l14jk06m6puhb-5mxqqnrjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\-l14jk06m6puhb-5mxqqnrjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\0ec6fl06luxeywpbsjvxcbjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\0ec6fl06luxeywpbsjvxcbjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\2tsd397wlxj96qwhynikxpeszw2xoq-xsnqo47m55da.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\2tsd397wlxj96qwhynikxpeszw2xoq-xsnqo47m55da.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\97uahxiqzroncbacei3awxjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\97uahxiqzroncbacei3awxjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\azmswpodyevhtrvuabjwvbtbgvql8ndjpwnre27mub0.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\azmswpodyevhtrvuabjwvbtbgvql8ndjpwnre27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\cwb0xya8bzo0ksthx0utua.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\cwb0xya8bzo0ksthx0utua.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\d-6iyplofoccackzxwxsoftxra8tvwticgirnjhmvjw.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\d-6iyplofoccackzxwxsoftxra8tvwticgirnjhmvjw.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\e7mevayvogmqfwwl61pkhbtbgvql8ndjpwnre27mub0.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\e7mevayvogmqfwwl61pkhbtbgvql8ndjpwnre27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\fcx7wwv8ozt71a3e1xoajveszw2xoq-xsnqo47m55da.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\fcx7wwv8ozt71a3e1xoajveszw2xoq-xsnqo47m55da.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\fl4y0qdoxyythegmxx8kcrjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\fl4y0qdoxyythegmxx8kcrjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\frnv30oaydlfrth2vnzzdhtbgvql8ndjpwnre27mub0.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\frnv30oaydlfrth2vnzzdhtbgvql8ndjpwnre27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\gwvjdern2amz39wrsoz7fxtbgvql8ndjpwnre27mub0.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\gwvjdern2amz39wrsoz7fxtbgvql8ndjpwnre27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\hgo13k-tfspn0qi1sfdufvtxra8tvwticgirnjhmvjw.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\hgo13k-tfspn0qi1sfdufvtxra8tvwticgirnjhmvjw.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\i3s1wsgsg9ycurv6puktorjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\i3s1wsgsg9ycurv6puktorjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\nydwbdd4giq26g5xybhsfbjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\nydwbdd4giq26g5xybhsfbjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\ooefwznlrtefzlymlvv1ubjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\ooefwznlrtefzlymlvv1ubjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\pru33qjshpzsmg3z6vywnrjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\pru33qjshpzsmg3z6vywnrjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotobold.woff">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotobold.woff</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotolight.woff">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotolight.woff</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotomedium.woff">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotomedium.woff</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotoregular.woff">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotoregular.woff</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotothin.woff">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotothin.woff</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\rxzjdnzeo3r5zsexge8uuvtxra8tvwticgirnjhmvjw.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\rxzjdnzeo3r5zsexge8uuvtxra8tvwticgirnjhmvjw.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\style.css">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\style.css</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\ty9dfvlaziwdqq2dhoyjphtbgvql8ndjpwnre27mub0.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\ty9dfvlaziwdqq2dhoyjphtbgvql8ndjpwnre27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\vvxugkzxbhtx_s_vctlpghtbgvql8ndjpwnre27mub0.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\vvxugkzxbhtx_s_vctlpghtbgvql8ndjpwnre27mub0.woff2</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fullscreen\fullscreen-doubleclick.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fullscreen\fullscreen-doubleclick.js</Link>
</BundleResource>
@@ -1061,9 +968,6 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\headroom\headroom.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\headroom\headroom.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\homescreensettings\homescreensettings.css">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\homescreensettings\homescreensettings.css</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\homescreensettings\homescreensettings.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\homescreensettings\homescreensettings.js</Link>
</BundleResource>
@@ -1331,6 +1235,21 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\scroller\smoothscroller.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\scroller\smoothscroller.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\search\searchfields.css">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\search\searchfields.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\search\searchfields.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\search\searchfields.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\search\searchfields.template.html">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\search\searchfields.template.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\search\searchresults.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\search\searchresults.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\search\searchresults.template.html">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\search\searchresults.template.html</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\serviceworker\notifications.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\serviceworker\notifications.js</Link>
</BundleResource>
@@ -2162,6 +2081,84 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\files\dummy.mp4">
<Link>Resources\dashboard-ui\files\dummy.mp4</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\-l14jk06m6puhb-5mxqqnrjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\-l14jk06m6puhb-5mxqqnrjtnkitppoi_ivcxxdnrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\0ec6fl06luxeywpbsjvxcbjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\0ec6fl06luxeywpbsjvxcbjtnkitppoi_ivcxxdnrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\2tsd397wlxj96qwhynikxpeszw2xoq-xsnqo47m55da.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\2tsd397wlxj96qwhynikxpeszw2xoq-xsnqo47m55da.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\97uahxiqzroncbacei3awxjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\97uahxiqzroncbacei3awxjtnkitppoi_ivcxxdnrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\azmswpodyevhtrvuabjwvbtbgvql8ndjpwnre27mub0.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\azmswpodyevhtrvuabjwvbtbgvql8ndjpwnre27mub0.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\cwb0xya8bzo0ksthx0utua.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\cwb0xya8bzo0ksthx0utua.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\d-6iyplofoccackzxwxsoftxra8tvwticgirnjhmvjw.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\d-6iyplofoccackzxwxsoftxra8tvwticgirnjhmvjw.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\e7mevayvogmqfwwl61pkhbtbgvql8ndjpwnre27mub0.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\e7mevayvogmqfwwl61pkhbtbgvql8ndjpwnre27mub0.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\fcx7wwv8ozt71a3e1xoajveszw2xoq-xsnqo47m55da.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\fcx7wwv8ozt71a3e1xoajveszw2xoq-xsnqo47m55da.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\fl4y0qdoxyythegmxx8kcrjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\fl4y0qdoxyythegmxx8kcrjtnkitppoi_ivcxxdnrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\frnv30oaydlfrth2vnzzdhtbgvql8ndjpwnre27mub0.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\frnv30oaydlfrth2vnzzdhtbgvql8ndjpwnre27mub0.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\gwvjdern2amz39wrsoz7fxtbgvql8ndjpwnre27mub0.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\gwvjdern2amz39wrsoz7fxtbgvql8ndjpwnre27mub0.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\hgo13k-tfspn0qi1sfdufvtxra8tvwticgirnjhmvjw.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\hgo13k-tfspn0qi1sfdufvtxra8tvwticgirnjhmvjw.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\i3s1wsgsg9ycurv6puktorjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\i3s1wsgsg9ycurv6puktorjtnkitppoi_ivcxxdnrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\nydwbdd4giq26g5xybhsfbjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\nydwbdd4giq26g5xybhsfbjtnkitppoi_ivcxxdnrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\ooefwznlrtefzlymlvv1ubjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\ooefwznlrtefzlymlvv1ubjtnkitppoi_ivcxxdnrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\pru33qjshpzsmg3z6vywnrjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\pru33qjshpzsmg3z6vywnrjtnkitppoi_ivcxxdnrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\robotobold.woff">
+ <Link>Resources\dashboard-ui\fonts\roboto\robotobold.woff</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\robotolight.woff">
+ <Link>Resources\dashboard-ui\fonts\roboto\robotolight.woff</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\robotomedium.woff">
+ <Link>Resources\dashboard-ui\fonts\roboto\robotomedium.woff</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\robotoregular.woff">
+ <Link>Resources\dashboard-ui\fonts\roboto\robotoregular.woff</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\robotothin.woff">
+ <Link>Resources\dashboard-ui\fonts\roboto\robotothin.woff</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\rxzjdnzeo3r5zsexge8uuvtxra8tvwticgirnjhmvjw.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\rxzjdnzeo3r5zsexge8uuvtxra8tvwticgirnjhmvjw.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\style.css">
+ <Link>Resources\dashboard-ui\fonts\roboto\style.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\ty9dfvlaziwdqq2dhoyjphtbgvql8ndjpwnre27mub0.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\ty9dfvlaziwdqq2dhoyjphtbgvql8ndjpwnre27mub0.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\vvxugkzxbhtx_s_vctlpghtbgvql8ndjpwnre27mub0.woff2">
+ <Link>Resources\dashboard-ui\fonts\roboto\vvxugkzxbhtx_s_vctlpghtbgvql8ndjpwnre27mub0.woff2</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\legacy\buttonenabled.js">
<Link>Resources\dashboard-ui\legacy\buttonenabled.js</Link>
</BundleResource>
@@ -2231,36 +2228,15 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\encodingsettings.js">
<Link>Resources\dashboard-ui\scripts\encodingsettings.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\episodes.js">
- <Link>Resources\dashboard-ui\scripts\episodes.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\forgotpassword.js">
<Link>Resources\dashboard-ui\scripts\forgotpassword.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\forgotpasswordpin.js">
<Link>Resources\dashboard-ui\scripts\forgotpasswordpin.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\gamegenrepage.js">
- <Link>Resources\dashboard-ui\scripts\gamegenrepage.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\gamespage.js">
- <Link>Resources\dashboard-ui\scripts\gamespage.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\gamesrecommendedpage.js">
- <Link>Resources\dashboard-ui\scripts\gamesrecommendedpage.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\gamestudiospage.js">
- <Link>Resources\dashboard-ui\scripts\gamestudiospage.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\gamesystemspage.js">
- <Link>Resources\dashboard-ui\scripts\gamesystemspage.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\homefavorites.js">
<Link>Resources\dashboard-ui\scripts\homefavorites.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\homeupcoming.js">
- <Link>Resources\dashboard-ui\scripts\homeupcoming.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\indexpage.js">
<Link>Resources\dashboard-ui\scripts\indexpage.js</Link>
</BundleResource>
@@ -2345,9 +2321,6 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\moviesrecommended.js">
<Link>Resources\dashboard-ui\scripts\moviesrecommended.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\moviestudios.js">
- <Link>Resources\dashboard-ui\scripts\moviestudios.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\movietrailers.js">
<Link>Resources\dashboard-ui\scripts\movietrailers.js</Link>
</BundleResource>
@@ -2357,12 +2330,12 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\musicartists.js">
<Link>Resources\dashboard-ui\scripts\musicartists.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\musicfolders.js">
- <Link>Resources\dashboard-ui\scripts\musicfolders.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\musicgenres.js">
<Link>Resources\dashboard-ui\scripts\musicgenres.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\musicplaylists.js">
+ <Link>Resources\dashboard-ui\scripts\musicplaylists.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\musicrecommended.js">
<Link>Resources\dashboard-ui\scripts\musicrecommended.js</Link>
</BundleResource>
@@ -2435,6 +2408,9 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\searchpage.js">
<Link>Resources\dashboard-ui\scripts\searchpage.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\searchtab.js">
+ <Link>Resources\dashboard-ui\scripts\searchtab.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\secondaryitems.js">
<Link>Resources\dashboard-ui\scripts\secondaryitems.js</Link>
</BundleResource>
diff --git a/MediaBrowser.Server.Mac/ImageMagickSharp.dll.config b/MediaBrowser.Server.Mac/ImageMagickSharp.dll.config deleted file mode 100644 index 0ad6d1e60..000000000 --- a/MediaBrowser.Server.Mac/ImageMagickSharp.dll.config +++ /dev/null @@ -1,3 +0,0 @@ -<configuration> - <dllmap dll="CORE_RL_Wand_.dll" target="libMagickWand-6.Q8.dylib" os="osx"/> -</configuration>
\ No newline at end of file diff --git a/MediaBrowser.Server.Mac/MacAppHost.cs b/MediaBrowser.Server.Mac/MacAppHost.cs index 304472529..4b29ba3a7 100644 --- a/MediaBrowser.Server.Mac/MacAppHost.cs +++ b/MediaBrowser.Server.Mac/MacAppHost.cs @@ -18,7 +18,7 @@ namespace MediaBrowser.Server.Mac { public class MacAppHost : ApplicationHost { - public MacAppHost(ServerApplicationPaths applicationPaths, ILogManager logManager, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, IMemoryStreamFactory memoryStreamFactory, MediaBrowser.Common.Net.INetworkManager networkManager, Action<string, string> certificateGenerator, Func<string> defaultUsernameFactory) : base(applicationPaths, logManager, options, fileSystem, powerManagement, releaseAssetFilename, environmentInfo, imageEncoder, systemEvents, memoryStreamFactory, networkManager, certificateGenerator, defaultUsernameFactory) + public MacAppHost(ServerApplicationPaths applicationPaths, ILogManager logManager, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, IMemoryStreamFactory memoryStreamFactory, MediaBrowser.Common.Net.INetworkManager networkManager, Action<string, string, string> certificateGenerator, Func<string> defaultUsernameFactory) : base(applicationPaths, logManager, options, fileSystem, powerManagement, releaseAssetFilename, environmentInfo, imageEncoder, systemEvents, memoryStreamFactory, networkManager, certificateGenerator, defaultUsernameFactory) { } diff --git a/MediaBrowser.Server.Mac/Main.cs b/MediaBrowser.Server.Mac/Main.cs index d703f7d0d..932ff6105 100644 --- a/MediaBrowser.Server.Mac/Main.cs +++ b/MediaBrowser.Server.Mac/Main.cs @@ -32,6 +32,7 @@ using Mono.Unix.Native; using MediaBrowser.Model.System; using MediaBrowser.Model.IO; using Emby.Server.Core.Logging; +using Emby.Drawing.Net; namespace MediaBrowser.Server.Mac { @@ -111,12 +112,7 @@ namespace MediaBrowser.Server.Mac _fileSystem = fileSystem; - var imageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, - logManager, - fileSystem, - options, - () => AppHost.HttpClient, - appPaths); + var imageEncoder = new GDIImageEncoder(fileSystem, logManager.GetLogger("GDI")); AppHost = new MacAppHost(appPaths, logManager, @@ -142,9 +138,9 @@ namespace MediaBrowser.Server.Mac Task.Run (() => StartServer(CancellationToken.None)); } - private static void GenerateCertificate(string certPath, string certHost) + private static void GenerateCertificate(string certPath, string certHost, string certPassword) { - CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, _logger); + CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, certPassword, _logger); } private static EnvironmentInfo GetEnvironmentInfo() diff --git a/MediaBrowser.Server.Startup.Common/ImageEncoderHelper.cs b/MediaBrowser.Server.Mono/ImageEncoderHelper.cs index ddbde2f66..ddbde2f66 100644 --- a/MediaBrowser.Server.Startup.Common/ImageEncoderHelper.cs +++ b/MediaBrowser.Server.Mono/ImageEncoderHelper.cs diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj index 17d9767f6..bcdfa858f 100644 --- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj +++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj @@ -117,6 +117,7 @@ <Compile Include="..\SharedVersion.cs"> <Link>Properties\SharedVersion.cs</Link> </Compile> + <Compile Include="ImageEncoderHelper.cs" /> <Compile Include="MonoAppHost.cs" /> <Compile Include="Native\MonoFileSystem.cs" /> <Compile Include="Native\PowerManagement.cs" /> diff --git a/MediaBrowser.Server.Mono/MonoAppHost.cs b/MediaBrowser.Server.Mono/MonoAppHost.cs index 54fd45019..09c409a2c 100644 --- a/MediaBrowser.Server.Mono/MonoAppHost.cs +++ b/MediaBrowser.Server.Mono/MonoAppHost.cs @@ -19,7 +19,7 @@ namespace MediaBrowser.Server.Mono { public class MonoAppHost : ApplicationHost { - public MonoAppHost(ServerApplicationPaths applicationPaths, ILogManager logManager, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, IMemoryStreamFactory memoryStreamFactory, MediaBrowser.Common.Net.INetworkManager networkManager, Action<string, string> certificateGenerator, Func<string> defaultUsernameFactory) : base(applicationPaths, logManager, options, fileSystem, powerManagement, releaseAssetFilename, environmentInfo, imageEncoder, systemEvents, memoryStreamFactory, networkManager, certificateGenerator, defaultUsernameFactory) + public MonoAppHost(ServerApplicationPaths applicationPaths, ILogManager logManager, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, IMemoryStreamFactory memoryStreamFactory, MediaBrowser.Common.Net.INetworkManager networkManager, Action<string, string, string> certificateGenerator, Func<string> defaultUsernameFactory) : base(applicationPaths, logManager, options, fileSystem, powerManagement, releaseAssetFilename, environmentInfo, imageEncoder, systemEvents, memoryStreamFactory, networkManager, certificateGenerator, defaultUsernameFactory) { } diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs index 66851f7e9..b79c8c1f7 100644 --- a/MediaBrowser.Server.Mono/Program.cs +++ b/MediaBrowser.Server.Mono/Program.cs @@ -159,9 +159,9 @@ namespace MediaBrowser.Server.Mono Task.WaitAll(task); } - private static void GenerateCertificate(string certPath, string certHost) + private static void GenerateCertificate(string certPath, string certHost, string certPassword) { - CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, _logger); + CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, certPassword, _logger); } private static MonoEnvironmentInfo GetEnvironmentInfo() @@ -258,7 +258,12 @@ namespace MediaBrowser.Server.Mono if (!Debugger.IsAttached) { - Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(exception)); + var message = LogHelper.GetLogMessage(exception).ToString(); + + if (message.IndexOf("InotifyWatcher", StringComparison.OrdinalIgnoreCase) == -1) + { + Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(exception)); + } } } diff --git a/MediaBrowser.Server.Mono/app.config b/MediaBrowser.Server.Mono/app.config index 07c113f3e..8f21d4a67 100644 --- a/MediaBrowser.Server.Mono/app.config +++ b/MediaBrowser.Server.Mono/app.config @@ -11,6 +11,8 @@ <add key="ReleaseProgramDataPath" value="ProgramData-Server" /> </appSettings> <runtime> + <legacyUnhandledExceptionPolicy enabled="1" /> + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="System.Data.SQLite" publicKeyToken="db937bc2d44ff139" culture="neutral" /> diff --git a/MediaBrowser.Server.Startup.Common/Cryptography/CertificateGenerator.cs b/MediaBrowser.Server.Startup.Common/Cryptography/CertificateGenerator.cs index 9e14b7713..4f5b3d004 100644 --- a/MediaBrowser.Server.Startup.Common/Cryptography/CertificateGenerator.cs +++ b/MediaBrowser.Server.Startup.Common/Cryptography/CertificateGenerator.cs @@ -12,6 +12,7 @@ namespace Emby.Common.Implementations.Security public static void CreateSelfSignCertificatePfx( string fileName, string hostname, + string password, ILogger logger) { if (string.IsNullOrWhiteSpace(fileName)) @@ -43,7 +44,7 @@ namespace Emby.Common.Implementations.Security cb.NotAfter = notAfter; cb.SubjectName = subject; cb.SubjectPublicKey = subjectKey; - + // signature cb.Hash = "SHA256"; byte[] rawcert = cb.Sign(issuerKey); @@ -59,6 +60,7 @@ namespace Emby.Common.Implementations.Security attributes.Add(PKCS9.localKeyId, list); p12.AddCertificate(new X509Certificate(rawcert), attributes); + p12.Password = password; p12.AddPkcs8ShroudedKeyBag(subjectKey, attributes); p12.SaveToFile(fileName); diff --git a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj index 5eb492166..5955d4c96 100644 --- a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj +++ b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj @@ -37,8 +37,8 @@ <Reference Include="Emby.Server.Core"> <HintPath>..\ThirdParty\emby\Emby.Server.Core.dll</HintPath> </Reference> - <Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> - <HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.1\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath> + <Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=1.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> + <HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.2\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="System" /> @@ -73,25 +73,12 @@ <Compile Include="Cryptography\X509Extension.cs" /> <Compile Include="Cryptography\X509Extensions.cs" /> <Compile Include="Cryptography\X520Attributes.cs" /> - <Compile Include="ImageEncoderHelper.cs" /> <Compile Include="IO\MemoryStreamProvider.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="SystemEvents.cs" /> <Compile Include="UpdateLevelHelper.cs" /> </ItemGroup> <ItemGroup> - <ProjectReference Include="..\Emby.Drawing.ImageMagick\Emby.Drawing.ImageMagick.csproj"> - <Project>{6cfee013-6e7c-432b-ac37-cabf0880c69a}</Project> - <Name>Emby.Drawing.ImageMagick</Name> - </ProjectReference> - <ProjectReference Include="..\Emby.Drawing.Net\Emby.Drawing.Net.csproj"> - <Project>{c97a239e-a96c-4d64-a844-ccf8cc30aecb}</Project> - <Name>Emby.Drawing.Net</Name> - </ProjectReference> - <ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj"> - <Project>{08fff49b-f175-4807-a2b5-73b0ebd9f716}</Project> - <Name>Emby.Drawing</Name> - </ProjectReference> <ProjectReference Include="..\Emby.Server.Implementations\Emby.Server.Implementations.csproj"> <Project>{e383961b-9356-4d5d-8233-9a1079d03055}</Project> <Name>Emby.Server.Implementations</Name> diff --git a/MediaBrowser.Server.Startup.Common/packages.config b/MediaBrowser.Server.Startup.Common/packages.config index d329f2cce..4b6266585 100644 --- a/MediaBrowser.Server.Startup.Common/packages.config +++ b/MediaBrowser.Server.Startup.Common/packages.config @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="Microsoft.IO.RecyclableMemoryStream" version="1.2.1" targetFramework="net46" /> + <package id="Microsoft.IO.RecyclableMemoryStream" version="1.2.2" targetFramework="net46" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.ServerApplication/ImageEncoderHelper.cs b/MediaBrowser.ServerApplication/ImageEncoderHelper.cs new file mode 100644 index 000000000..ddbde2f66 --- /dev/null +++ b/MediaBrowser.ServerApplication/ImageEncoderHelper.cs @@ -0,0 +1,48 @@ +using System; +using Emby.Drawing; +using Emby.Drawing.Net; +using Emby.Drawing.ImageMagick; +using Emby.Server.Core; +using Emby.Server.Implementations; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.Server.Startup.Common +{ + public class ImageEncoderHelper + { + public static IImageEncoder GetImageEncoder(ILogger logger, + ILogManager logManager, + IFileSystem fileSystem, + StartupOptions startupOptions, + Func<IHttpClient> httpClient, + IApplicationPaths appPaths) + { + if (!startupOptions.ContainsOption("-enablegdi")) + { + try + { + return new ImageMagickEncoder(logManager.GetLogger("ImageMagick"), appPaths, httpClient, fileSystem); + } + catch + { + logger.Error("Error loading ImageMagick. Will revert to GDI."); + } + } + + try + { + return new GDIImageEncoder(fileSystem, logManager.GetLogger("GDI")); + } + catch + { + logger.Error("Error loading GDI. Will revert to NullImageEncoder."); + } + + return new NullImageEncoder(); + } + } +} diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index 3643aab48..8e38c9a98 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -391,9 +391,9 @@ namespace MediaBrowser.ServerApplication } } - private static void GenerateCertificate(string certPath, string certHost) + private static void GenerateCertificate(string certPath, string certHost, string certPassword) { - CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, _logger); + CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, certPassword, _logger); } private static ServerNotifyIcon _serverNotifyIcon; diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index b968c2fb6..749468fe2 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -135,6 +135,7 @@ <Compile Include="BackgroundServiceInstaller.cs"> <SubType>Component</SubType> </Compile> + <Compile Include="ImageEncoderHelper.cs" /> <Compile Include="MainForm.cs"> <SubType>Form</SubType> </Compile> diff --git a/MediaBrowser.ServerApplication/Native/LoopUtil.cs b/MediaBrowser.ServerApplication/Native/LoopUtil.cs index 6160f853f..9a96f5518 100644 --- a/MediaBrowser.ServerApplication/Native/LoopUtil.cs +++ b/MediaBrowser.ServerApplication/Native/LoopUtil.cs @@ -145,16 +145,6 @@ namespace MediaBrowser.ServerApplication.Native { AppContainer app = new AppContainer(PI_app.appContainerName, PI_app.displayName, PI_app.workingDirectory, PI_app.appContainerSid); - var app_capabilities = LoopUtil.getCapabilites(PI_app.capabilities); - if (app_capabilities.Count > 0) - { - //var sid = new SecurityIdentifier(app_capabilities[0], 0); - - IntPtr arrayValue = IntPtr.Zero; - //var b = LoopUtil.ConvertStringSidToSid(app_capabilities[0].Sid, out arrayValue); - //string mysid; - //var b = LoopUtil.ConvertSidToStringSid(app_capabilities[0].Sid, out mysid); - } app.LoopUtil = CheckLoopback(PI_app.appContainerSid); Apps.Add(app); } @@ -209,42 +199,6 @@ namespace MediaBrowser.ServerApplication.Native util.SaveLoopbackState(); } - private static List<SID_AND_ATTRIBUTES> getCapabilites(INET_FIREWALL_AC_CAPABILITIES cap) - { - List<SID_AND_ATTRIBUTES> mycap = new List<SID_AND_ATTRIBUTES>(); - - IntPtr arrayValue = cap.capabilities; - - var structSize = Marshal.SizeOf(typeof(SID_AND_ATTRIBUTES)); - for (var i = 0; i < cap.count; i++) - { - var cur = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure(arrayValue, typeof(SID_AND_ATTRIBUTES)); - mycap.Add(cur); - arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize)); - } - - return mycap; - - } - - private static List<SID_AND_ATTRIBUTES> getContainerSID(INET_FIREWALL_AC_CAPABILITIES cap) - { - List<SID_AND_ATTRIBUTES> mycap = new List<SID_AND_ATTRIBUTES>(); - - IntPtr arrayValue = cap.capabilities; - - var structSize = Marshal.SizeOf(typeof(SID_AND_ATTRIBUTES)); - for (var i = 0; i < cap.count; i++) - { - var cur = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure(arrayValue, typeof(SID_AND_ATTRIBUTES)); - mycap.Add(cur); - arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize)); - } - - return mycap; - - } - private static List<SID_AND_ATTRIBUTES> PI_NetworkIsolationGetAppContainerConfig() { diff --git a/MediaBrowser.ServerApplication/WindowsAppHost.cs b/MediaBrowser.ServerApplication/WindowsAppHost.cs index cd293fddf..537c8b323 100644 --- a/MediaBrowser.ServerApplication/WindowsAppHost.cs +++ b/MediaBrowser.ServerApplication/WindowsAppHost.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.ServerApplication { public class WindowsAppHost : ApplicationHost { - public WindowsAppHost(ServerApplicationPaths applicationPaths, ILogManager logManager, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, IMemoryStreamFactory memoryStreamFactory, MediaBrowser.Common.Net.INetworkManager networkManager, Action<string, string> certificateGenerator, Func<string> defaultUsernameFactory) + public WindowsAppHost(ServerApplicationPaths applicationPaths, ILogManager logManager, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, IMemoryStreamFactory memoryStreamFactory, MediaBrowser.Common.Net.INetworkManager networkManager, Action<string, string, string> certificateGenerator, Func<string> defaultUsernameFactory) : base(applicationPaths, logManager, options, fileSystem, powerManagement, releaseAssetFilename, environmentInfo, imageEncoder, systemEvents, memoryStreamFactory, networkManager, certificateGenerator, defaultUsernameFactory) { } diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 8880441e2..72389044b 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -98,8 +98,8 @@ namespace MediaBrowser.WebDashboard.Api } path = GetDashboardResourcePath(path); - var parent = Path.GetDirectoryName(path); - + var parent = _fileSystem.GetDirectoryName(path); + return string.Equals(_basePath, parent, StringComparison.OrdinalIgnoreCase) || string.Equals(Path.Combine(_basePath, "voice"), parent, StringComparison.OrdinalIgnoreCase); } diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index a12976f82..dfd4694c3 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -358,18 +358,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; } - case "criticratingsummary": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - item.CriticRatingSummary = val; - } - - break; - } - case "language": { var val = reader.ReadElementContentAsString(); @@ -464,17 +452,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; } - case "mpaadescription": - { - var rating = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(rating)) - { - item.OfficialRatingDescription = rating; - } - break; - } - case "customrating": { var val = reader.ReadElementContentAsString(); diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index 02929f83d..623b109f7 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -19,7 +19,6 @@ using System.Text; using System.Threading; using System.Xml; using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.Xml; @@ -42,7 +41,6 @@ namespace MediaBrowser.XbmcMetadata.Savers "year", "sorttitle", "mpaa", - "mpaadescription", "aspectratio", "website", "collectionnumber", @@ -56,7 +54,6 @@ namespace MediaBrowser.XbmcMetadata.Savers "tag", "runtime", "actor", - "criticratingsummary", "criticrating", "fileinfo", "director", @@ -212,7 +209,7 @@ namespace MediaBrowser.XbmcMetadata.Savers private void SaveToFile(Stream stream, string path) { - FileSystem.CreateDirectory(Path.GetDirectoryName(path)); + FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path)); var file = FileSystem.GetFileInfo(path); @@ -557,11 +554,6 @@ namespace MediaBrowser.XbmcMetadata.Savers writer.WriteElementString("mpaa", item.OfficialRating); } - if (!string.IsNullOrEmpty(item.OfficialRatingDescription)) - { - writer.WriteElementString("mpaadescription", item.OfficialRatingDescription); - } - var hasAspectRatio = item as IHasAspectRatio; if (hasAspectRatio != null) { @@ -662,11 +654,6 @@ namespace MediaBrowser.XbmcMetadata.Savers writer.WriteElementString("criticrating", item.CriticRating.Value.ToString(UsCulture)); } - if (!string.IsNullOrEmpty(item.CriticRatingSummary)) - { - writer.WriteElementString("criticratingsummary", item.CriticRatingSummary); - } - var hasDisplayOrder = item as IHasDisplayOrder; if (hasDisplayOrder != null) diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 20681b317..c86e9a71c 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common</id> - <version>3.0.698</version> + <version>3.0.699</version> <title>Emby.Common</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 7b5f348c8..f68cf4e41 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <metadata> <id>MediaBrowser.Server.Core</id> - <version>3.0.698</version> + <version>3.0.699</version> <title>Emby.Server.Core</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains core components required to build plugins for Emby Server.</description> <copyright>Copyright © Emby 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.696" /> + <dependency id="MediaBrowser.Common" version="3.0.699" /> </dependencies> </metadata> <files> diff --git a/SocketHttpListener.Portable/Net/HttpConnection.cs b/SocketHttpListener.Portable/Net/HttpConnection.cs index 5fe47fc63..ac8ada486 100644 --- a/SocketHttpListener.Portable/Net/HttpConnection.cs +++ b/SocketHttpListener.Portable/Net/HttpConnection.cs @@ -217,7 +217,7 @@ namespace SocketHttpListener.Net { var supportsDirectSocketAccess = !context.Response.SendChunked && !isExpect100Continue && !secure; - o_stream = new ResponseStream(stream, context.Response, _memoryStreamFactory, _textEncoding, _fileSystem, sock, supportsDirectSocketAccess); + o_stream = new ResponseStream(stream, context.Response, _memoryStreamFactory, _textEncoding, _fileSystem, sock, supportsDirectSocketAccess, _logger); } else { diff --git a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs index d9f91c0cc..3cb6a0d75 100644 --- a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs +++ b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs @@ -480,6 +480,8 @@ namespace SocketHttpListener.Net headers.SetInternal("Set-Cookie", cookie.ToString()); } + headers.SetInternal("Status", status_code.ToString(CultureInfo.InvariantCulture)); + using (StreamWriter writer = new StreamWriter(ms, encoding, 256, true)) { writer.Write("HTTP/{0} {1} {2}\r\n", version, status_code, status_description); diff --git a/SocketHttpListener.Portable/Net/ResponseStream.cs b/SocketHttpListener.Portable/Net/ResponseStream.cs index 3c79f47c2..dea4049d5 100644 --- a/SocketHttpListener.Portable/Net/ResponseStream.cs +++ b/SocketHttpListener.Portable/Net/ResponseStream.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; using MediaBrowser.Model.Text; using SocketHttpListener.Primitives; @@ -26,8 +27,9 @@ namespace SocketHttpListener.Net private readonly IFileSystem _fileSystem; private readonly IAcceptSocket _socket; private readonly bool _supportsDirectSocketAccess; + private readonly ILogger _logger; - internal ResponseStream(Stream stream, HttpListenerResponse response, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IAcceptSocket socket, bool supportsDirectSocketAccess) + internal ResponseStream(Stream stream, HttpListenerResponse response, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IAcceptSocket socket, bool supportsDirectSocketAccess, ILogger logger) { this.response = response; _memoryStreamFactory = memoryStreamFactory; @@ -35,6 +37,7 @@ namespace SocketHttpListener.Net _fileSystem = fileSystem; _socket = socket; _supportsDirectSocketAccess = supportsDirectSocketAccess; + _logger = logger; this.stream = stream; } @@ -309,40 +312,38 @@ namespace SocketHttpListener.Net public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { - //if (_supportsDirectSocketAccess && offset == 0 && count == 0 && !response.SendChunked) + //if (_supportsDirectSocketAccess && offset == 0 && count == 0 && !response.SendChunked && response.ContentLength64 > 8192) //{ - // return TransmitFileOverSocket(path, offset, count, cancellationToken); + // return TransmitFileOverSocket(path, offset, count, fileShareMode, cancellationToken); //} return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken); } private readonly byte[] _emptyBuffer = new byte[] { }; - private async Task TransmitFileOverSocket(string path, long offset, long count, CancellationToken cancellationToken) + private Task TransmitFileOverSocket(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { MemoryStream ms = GetHeaders(response, _memoryStreamFactory, false); - var buffer = new byte[] {}; + byte[] buffer; if (ms != null) { - ms.Position = 0; - - byte[] msBuffer; - _memoryStreamFactory.TryGetBuffer(ms, out msBuffer); - buffer = msBuffer; + using (var msCopy = new MemoryStream()) + { + ms.CopyTo(msCopy); + buffer = msCopy.ToArray(); + } + } + else + { + return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken); } - await _socket.SendFile(path, buffer, _emptyBuffer, cancellationToken).ConfigureAwait(false); + _logger.Info("Socket sending file {0} {1}", path, response.ContentLength64); + return _socket.SendFile(path, buffer, _emptyBuffer, cancellationToken); } private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { - var chunked = response.SendChunked; - - if (!chunked) - { - await WriteAsync(_emptyBuffer, 0, 0, cancellationToken).ConfigureAwait(false); - } - using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, true)) { if (offset > 0) @@ -350,7 +351,7 @@ namespace SocketHttpListener.Net fs.Position = offset; } - var targetStream = chunked ? this : stream; + var targetStream = this; if (count > 0) { @@ -366,14 +367,23 @@ namespace SocketHttpListener.Net private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken) { var array = new byte[81920]; - int count; - while ((count = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0) + int bytesRead; + + while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0) { - var bytesToCopy = Math.Min(count, copyLength); + if (bytesRead == 0) + { + break; + } + + var bytesToWrite = Math.Min(bytesRead, copyLength); - await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy), cancellationToken).ConfigureAwait(false); + if (bytesToWrite > 0) + { + await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + } - copyLength -= bytesToCopy; + copyLength -= bytesToWrite; if (copyLength <= 0) { |
