From d7a1a87009eed638d28d070e6a47c8a5bee38c2e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 1 Dec 2017 12:03:40 -0500 Subject: [PATCH 1/3] reduce work done by system info endpoints --- .../ApplicationHost.cs | 17 ++++++++++++++++- .../EntryPoints/StartupWizard.cs | 7 ++++++- .../Networking/NetworkManager.cs | 19 ++++++------------- .../Session/HttpSessionController.cs | 2 +- .../Session/SessionManager.cs | 4 +--- .../Session/WebSocketController.cs | 6 +++--- MediaBrowser.Api/System/SystemService.cs | 14 ++------------ .../IServerApplicationHost.cs | 2 ++ .../Session/ISessionController.cs | 5 +---- 9 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 9a159194e..abc6c3566 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -361,7 +361,7 @@ namespace Emby.Server.Implementations protected IAuthService AuthService { get; private set; } - protected readonly StartupOptions StartupOptions; + public StartupOptions StartupOptions { get; private set; } protected readonly string ReleaseAssetFilename; internal IPowerManagement PowerManagement { get; private set; } @@ -1950,6 +1950,21 @@ namespace Emby.Server.Implementations }; } + public async Task GetPublicSystemInfo(CancellationToken cancellationToken) + { + var localAddress = await GetLocalApiUrl(cancellationToken).ConfigureAwait(false); + + return new PublicSystemInfo + { + Version = ApplicationVersion.ToString(), + Id = SystemId, + OperatingSystem = EnvironmentInfo.OperatingSystem.ToString(), + WanAddress = ConnectManager.WanApiAddress, + ServerName = FriendlyName, + LocalAddress = localAddress + }; + } + public bool EnableHttps { get diff --git a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs index 8d1355795..746edf9e7 100644 --- a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs +++ b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs @@ -41,7 +41,12 @@ namespace Emby.Server.Implementations.EntryPoints } else if (_config.Configuration.IsStartupWizardCompleted) { - BrowserLauncher.OpenDashboardPage("index.html", _appHost); + var options = ((ApplicationHost)_appHost).StartupOptions; + + if (!options.ContainsOption("-service") && !options.ContainsOption("-nobrowser")) + { + BrowserLauncher.OpenDashboardPage("index.html", _appHost); + } } } diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index 30a3ff9e8..60da8a012 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -11,15 +11,12 @@ using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; -using System.Threading; namespace Emby.Server.Implementations.Networking { public class NetworkManager : INetworkManager { protected ILogger Logger { get; private set; } - private DateTime _lastRefresh; - private int NetworkCacheMinutes = 720; public event EventHandler NetworkChanged; @@ -33,7 +30,6 @@ namespace Emby.Server.Implementations.Networking } catch (Exception ex) { - NetworkCacheMinutes = 15; Logger.ErrorException("Error binding to NetworkAddressChanged event", ex); } @@ -43,7 +39,6 @@ namespace Emby.Server.Implementations.Networking } catch (Exception ex) { - NetworkCacheMinutes = 15; Logger.ErrorException("Error binding to NetworkChange_NetworkAvailabilityChanged event", ex); } } @@ -51,19 +46,21 @@ namespace Emby.Server.Implementations.Networking private void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) { Logger.Debug("NetworkAvailabilityChanged"); - _lastRefresh = DateTime.MinValue; OnNetworkChanged(); } private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e) { Logger.Debug("NetworkAddressChanged"); - _lastRefresh = DateTime.MinValue; OnNetworkChanged(); } private void OnNetworkChanged() { + lock (_localIpAddressSyncLock) + { + _localIpAddresses = null; + } if (NetworkChanged != null) { NetworkChanged(this, EventArgs.Empty); @@ -77,20 +74,16 @@ namespace Emby.Server.Implementations.Networking { lock (_localIpAddressSyncLock) { - var forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= NetworkCacheMinutes; - - if (_localIpAddresses == null || forceRefresh) + if (_localIpAddresses == null) { var addresses = GetLocalIpAddressesInternal().Result.Select(ToIpAddressInfo).ToList(); _localIpAddresses = addresses; - _lastRefresh = DateTime.UtcNow; return addresses; } + return _localIpAddresses; } - - return _localIpAddresses; } private async Task> GetLocalIpAddressesInternal() diff --git a/Emby.Server.Implementations/Session/HttpSessionController.cs b/Emby.Server.Implementations/Session/HttpSessionController.cs index e85254420..6725cd7af 100644 --- a/Emby.Server.Implementations/Session/HttpSessionController.cs +++ b/Emby.Server.Implementations/Session/HttpSessionController.cs @@ -151,7 +151,7 @@ namespace Emby.Server.Implementations.Session return SendMessage("LibraryChanged", info, cancellationToken); } - public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken) + public Task SendRestartRequiredNotification(CancellationToken cancellationToken) { return SendMessage("RestartRequired", cancellationToken); } diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index f49251da5..6b70f2cda 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1182,13 +1182,11 @@ namespace Emby.Server.Implementations.Session { var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList(); - var info = await _appHost.GetSystemInfo(cancellationToken).ConfigureAwait(false); - var tasks = sessions.Select(session => Task.Run(async () => { try { - await session.SessionController.SendRestartRequiredNotification(info, cancellationToken).ConfigureAwait(false); + await session.SessionController.SendRestartRequiredNotification(cancellationToken).ConfigureAwait(false); } catch (Exception ex) { diff --git a/Emby.Server.Implementations/Session/WebSocketController.cs b/Emby.Server.Implementations/Session/WebSocketController.cs index ee9ee8969..b13eb6116 100644 --- a/Emby.Server.Implementations/Session/WebSocketController.cs +++ b/Emby.Server.Implementations/Session/WebSocketController.cs @@ -145,12 +145,12 @@ namespace Emby.Server.Implementations.Session /// The information. /// The cancellation token. /// Task. - public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken) + public Task SendRestartRequiredNotification(CancellationToken cancellationToken) { - return SendMessagesInternal(new WebSocketMessage + return SendMessagesInternal(new WebSocketMessage { MessageType = "RestartRequired", - Data = info + Data = string.Empty }, cancellationToken); } diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs index 6de7dd98b..c0bbf70ea 100644 --- a/MediaBrowser.Api/System/SystemService.cs +++ b/MediaBrowser.Api/System/SystemService.cs @@ -172,19 +172,9 @@ namespace MediaBrowser.Api.System public async Task Get(GetPublicSystemInfo request) { - var result = await _appHost.GetSystemInfo(CancellationToken.None).ConfigureAwait(false); + var result = await _appHost.GetPublicSystemInfo(CancellationToken.None).ConfigureAwait(false); - var publicInfo = new PublicSystemInfo - { - Id = result.Id, - ServerName = result.ServerName, - Version = result.Version, - LocalAddress = result.LocalAddress, - WanAddress = result.WanAddress, - OperatingSystem = result.OperatingSystem - }; - - return ToOptimizedResult(publicInfo); + return ToOptimizedResult(result); } /// diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 380be068e..89ae85b50 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -22,6 +22,8 @@ namespace MediaBrowser.Controller /// SystemInfo. Task GetSystemInfo(CancellationToken cancellationToken); + Task GetPublicSystemInfo(CancellationToken cancellationToken); + /// /// Gets a value indicating whether [supports automatic run at startup]. /// diff --git a/MediaBrowser.Controller/Session/ISessionController.cs b/MediaBrowser.Controller/Session/ISessionController.cs index f8a6ed1fc..0d8c207b6 100644 --- a/MediaBrowser.Controller/Session/ISessionController.cs +++ b/MediaBrowser.Controller/Session/ISessionController.cs @@ -55,10 +55,7 @@ namespace MediaBrowser.Controller.Session /// /// Sends the restart required message. /// - /// The information. - /// The cancellation token. - /// Task. - Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken); + Task SendRestartRequiredNotification(CancellationToken cancellationToken); /// /// Sends the user data change info. From dec3b1bbb0f53e1dafcf0bd9238aaefbfd18f335 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 1 Dec 2017 12:04:32 -0500 Subject: [PATCH 2/3] improve image processing performance --- Emby.Dlna/PlayTo/PlayToController.cs | 2 +- .../ImageMagickEncoder.cs | 2 + Emby.Drawing.Skia/SkiaEncoder.cs | 2 + Emby.Drawing/ImageProcessor.cs | 104 ++++++++++++------ .../Social/SharingManager.cs | 4 +- 5 files changed, 80 insertions(+), 34 deletions(-) diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index 9b2c70394..b253cb26e 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -430,7 +430,7 @@ namespace Emby.Dlna.PlayTo return Task.FromResult(true); } - public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken) + public Task SendRestartRequiredNotification(CancellationToken cancellationToken) { return Task.FromResult(true); } diff --git a/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs index 38e2879ea..3b3f7cf0a 100644 --- a/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs +++ b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs @@ -173,6 +173,8 @@ namespace Emby.Drawing.ImageMagick originalImage.CurrentImage.CompressionQuality = quality; originalImage.CurrentImage.StripImage(); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(outputPath)); + originalImage.SaveImage(outputPath); } } diff --git a/Emby.Drawing.Skia/SkiaEncoder.cs b/Emby.Drawing.Skia/SkiaEncoder.cs index a89e1f2db..9b4f1fc58 100644 --- a/Emby.Drawing.Skia/SkiaEncoder.cs +++ b/Emby.Drawing.Skia/SkiaEncoder.cs @@ -528,6 +528,7 @@ namespace Emby.Drawing.Skia // If all we're doing is resizing then we can stop now if (!hasBackgroundColor && !hasForegroundColor && blur == 0 && !hasIndicator) { + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(outputPath)); using (var outputStream = new SKFileWStream(outputPath)) { resizedBitmap.Encode(outputStream, skiaOutputFormat, quality); @@ -580,6 +581,7 @@ namespace Emby.Drawing.Skia DrawIndicator(canvas, width, height, options); } + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(outputPath)); using (var outputStream = new SKFileWStream(outputPath)) { saveBitmap.Encode(outputStream, skiaOutputFormat, quality); diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index e28d22cf7..f91cb4675 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -220,7 +220,7 @@ namespace Emby.Drawing Type = originalImage.Type, Path = originalImagePath - }, requiresTransparency, item, options.ImageIndex, options.Enhancers).ConfigureAwait(false); + }, requiresTransparency, item, options.ImageIndex, options.Enhancers, CancellationToken.None).ConfigureAwait(false); originalImagePath = tuple.Item1; dateModified = tuple.Item2; @@ -256,31 +256,29 @@ namespace Emby.Drawing var outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency); var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.Blur, options.BackgroundColor, options.ForegroundLayer); + CheckDisposed(); + + var lockInfo = GetLock(cacheFilePath); + + await lockInfo.Lock.WaitAsync().ConfigureAwait(false); + try { - CheckDisposed(); - if (!_fileSystem.FileExists(cacheFilePath)) { - var tmpPath = Path.ChangeExtension(Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")), Path.GetExtension(cacheFilePath)); - _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(tmpPath)); - if (options.CropWhiteSpace && !SupportsTransparency(originalImagePath)) { options.CropWhiteSpace = false; } - var resultPath = _imageEncoder.EncodeImage(originalImagePath, dateModified, tmpPath, autoOrient, orientation, quality, options, outputFormat); + var resultPath = _imageEncoder.EncodeImage(originalImagePath, dateModified, cacheFilePath, autoOrient, orientation, quality, options, outputFormat); if (string.Equals(resultPath, originalImagePath, StringComparison.OrdinalIgnoreCase)) { return new Tuple(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); } - _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFilePath)); - CopyFile(tmpPath, cacheFilePath); - - return new Tuple(tmpPath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(tmpPath)); + return new Tuple(cacheFilePath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(cacheFilePath)); } return new Tuple(cacheFilePath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(cacheFilePath)); @@ -302,6 +300,10 @@ namespace Emby.Drawing // Just spit out the original file if all the options are default return new Tuple(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); } + finally + { + ReleaseLock(cacheFilePath, lockInfo); + } } private ImageFormat GetOutputFormat(ImageFormat[] clientSupportedFormats, bool requiresTransparency) @@ -667,7 +669,7 @@ namespace Emby.Drawing var inputImageSupportsTransparency = SupportsTransparency(imageInfo.Path); - var result = await GetEnhancedImage(imageInfo, inputImageSupportsTransparency, item, imageIndex, enhancers); + var result = await GetEnhancedImage(imageInfo, inputImageSupportsTransparency, item, imageIndex, enhancers, CancellationToken.None); return result.Item1; } @@ -676,7 +678,8 @@ namespace Emby.Drawing bool inputImageSupportsTransparency, IHasMetadata item, int imageIndex, - List enhancers) + List enhancers, + CancellationToken cancellationToken) { var originalImagePath = image.Path; var dateModified = image.DateModified; @@ -687,7 +690,7 @@ namespace Emby.Drawing var cacheGuid = GetImageCacheTag(item, image, enhancers); // Enhance if we have enhancers - var ehnancedImageInfo = await GetEnhancedImageInternal(originalImagePath, item, imageType, imageIndex, enhancers, cacheGuid).ConfigureAwait(false); + var ehnancedImageInfo = await GetEnhancedImageInternal(originalImagePath, item, imageType, imageIndex, enhancers, cacheGuid, cancellationToken).ConfigureAwait(false); var ehnancedImagePath = ehnancedImageInfo.Item1; @@ -727,7 +730,8 @@ namespace Emby.Drawing ImageType imageType, int imageIndex, List supportedEnhancers, - string cacheGuid) + string cacheGuid, + CancellationToken cancellationToken) { if (string.IsNullOrEmpty(originalImagePath)) { @@ -755,29 +759,28 @@ namespace Emby.Drawing var enhancedImagePath = GetCachePath(EnhancedImageCachePath, cacheGuid + cacheExtension); - // Check again in case of contention - if (_fileSystem.FileExists(enhancedImagePath)) - { - return new Tuple(enhancedImagePath, treatmentRequiresTransparency); - } + var lockInfo = GetLock(enhancedImagePath); - _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(enhancedImagePath)); - - var tmpPath = Path.Combine(_appPaths.TempDirectory, Path.ChangeExtension(Guid.NewGuid().ToString(), Path.GetExtension(enhancedImagePath))); - _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(tmpPath)); - - await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, tmpPath, item, imageType, imageIndex).ConfigureAwait(false); + await lockInfo.Lock.WaitAsync(cancellationToken).ConfigureAwait(false); try { - _fileSystem.CopyFile(tmpPath, enhancedImagePath, true); + // Check again in case of contention + if (_fileSystem.FileExists(enhancedImagePath)) + { + return new Tuple(enhancedImagePath, treatmentRequiresTransparency); + } + + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(enhancedImagePath)); + + await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, enhancedImagePath, item, imageType, imageIndex).ConfigureAwait(false); + + return new Tuple(enhancedImagePath, treatmentRequiresTransparency); } - catch + finally { - + ReleaseLock(enhancedImagePath, lockInfo); } - - return new Tuple(tmpPath, treatmentRequiresTransparency); } /// @@ -896,6 +899,45 @@ namespace Emby.Drawing return list; } + private Dictionary _locks = new Dictionary(); + private class LockInfo + { + public SemaphoreSlim Lock = new SemaphoreSlim(1, 1); + public int Count = 1; + } + private LockInfo GetLock(string key) + { + lock (_locks) + { + LockInfo info; + if (_locks.TryGetValue(key, out info)) + { + info.Count++; + } + else + { + info = new LockInfo(); + _locks[key] = info; + } + return info; + } + } + + private void ReleaseLock(string key, LockInfo info) + { + info.Lock.Release(); + + lock (_locks) + { + info.Count--; + if (info.Count <= 0) + { + _locks.Remove(key); + info.Lock.Dispose(); + } + } + } + private bool _disposed; public void Dispose() { diff --git a/Emby.Server.Implementations/Social/SharingManager.cs b/Emby.Server.Implementations/Social/SharingManager.cs index e94b9100a..23ce7492a 100644 --- a/Emby.Server.Implementations/Social/SharingManager.cs +++ b/Emby.Server.Implementations/Social/SharingManager.cs @@ -43,7 +43,7 @@ namespace Emby.Server.Implementations.Social throw new ResourceNotFoundException(); } - var externalUrl = (await _appHost.GetSystemInfo(CancellationToken.None).ConfigureAwait(false)).WanAddress; + var externalUrl = (await _appHost.GetPublicSystemInfo(CancellationToken.None).ConfigureAwait(false)).WanAddress; if (string.IsNullOrWhiteSpace(externalUrl)) { @@ -74,7 +74,7 @@ namespace Emby.Server.Implementations.Social { var info = _repository.GetShareInfo(id); - AddShareInfo(info, _appHost.GetSystemInfo(CancellationToken.None).Result.WanAddress); + AddShareInfo(info, _appHost.GetPublicSystemInfo(CancellationToken.None).Result.WanAddress); return info; } From 6320869212269ae34dfb9f1aad31fc0dcdd76790 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 1 Dec 2017 12:06:14 -0500 Subject: [PATCH 3/3] 3.2.40.6 --- SharedVersion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SharedVersion.cs b/SharedVersion.cs index 24c973d60..f5f637be0 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.40.5")] +[assembly: AssemblyVersion("3.2.40.6")]