From fd6d35e1d0a3ceae6080fbf1fe08fb68f01a4395 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Tue, 22 Jan 2019 21:18:48 +0100 Subject: [PATCH 01/13] Remove unconditional caching, modified since header and use ETags --- .../HttpServer/FileWriter.cs | 2 +- .../HttpServer/HttpResultFactory.cs | 54 ++++++++----------- 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs index c32c91670a..7aedba9b31 100644 --- a/Emby.Server.Implementations/HttpServer/FileWriter.cs +++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs @@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.HttpServer private long RangeStart { get; set; } private long RangeEnd { get; set; } private long RangeLength { get; set; } - private long TotalContentLength { get; set; } + public long TotalContentLength { get; set; } public Action OnComplete { get; set; } public Action OnError { get; set; } diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 8b60d61d41..dee417b43f 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -5,6 +5,7 @@ using System.IO; using System.IO.Compression; using System.Net; using System.Runtime.Serialization; +using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using System.Xml; @@ -423,13 +424,11 @@ namespace Emby.Server.Implementations.HttpServer /// private object GetCachedResult(IRequest requestContext, IDictionary responseHeaders, Guid cacheKey, string cacheKeyString, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType) { - responseHeaders["ETag"] = string.Format("\"{0}\"", cacheKeyString); - bool noCache = (requestContext.Headers.Get("Cache-Control") ?? string.Empty).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1; if (!noCache) { - if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration)) + if (IsNotModified(requestContext, cacheKey)) { AddAgeHeader(responseHeaders, lastDateModified); AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration); @@ -442,7 +441,7 @@ namespace Emby.Server.Implementations.HttpServer } } - AddCachingHeaders(responseHeaders, cacheKeyString, lastDateModified, cacheDuration); + AddCachingHeaders(responseHeaders, cacheKeyString, cacheDuration); return null; } @@ -532,10 +531,11 @@ namespace Emby.Server.Implementations.HttpServer public async Task GetStaticResult(IRequest requestContext, StaticResultOptions options) { - var cacheKey = options.CacheKey; options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary(StringComparer.OrdinalIgnoreCase); - var contentType = options.ContentType; + var contentType = options.ContentType; + var etag = requestContext.Headers.Get("If-None-Match"); + var cacheKey = etag != null ? new Guid(etag) : Guid.Empty; if (!cacheKey.Equals(Guid.Empty)) { var key = cacheKey.ToString("N"); @@ -554,8 +554,6 @@ namespace Emby.Server.Implementations.HttpServer var factoryFn = options.ContentFactory; var responseHeaders = options.ResponseHeaders; - //var requestedCompressionType = GetCompressionType(requestContext); - var rangeHeader = requestContext.Headers.Get("Range"); if (!isHeadRequest && !string.IsNullOrEmpty(options.Path)) @@ -568,10 +566,21 @@ namespace Emby.Server.Implementations.HttpServer }; AddResponseHeaders(hasHeaders, options.ResponseHeaders); + // Generate an ETag based on identifying information - TODO read contents from filesystem instead? + var responseId = $"{hasHeaders.ContentType}{options.Path}{hasHeaders.TotalContentLength}"; + var hashedId = MD5.Create().ComputeHash(Encoding.Default.GetBytes(responseId)); + hasHeaders.Headers["ETag"] = new Guid(hashedId).ToString("N"); + return hasHeaders; } var stream = await factoryFn().ConfigureAwait(false); + // Generate an etag based on stream content + var streamHash = MD5.Create().ComputeHash(stream); + var newEtag = new Guid(streamHash).ToString("N"); + + // reset position so the response can re-use it -- TODO is this ok? + stream.Position = 0; var totalContentLength = options.ContentLength; if (!totalContentLength.HasValue) @@ -594,6 +603,7 @@ namespace Emby.Server.Implementations.HttpServer }; AddResponseHeaders(hasHeaders, options.ResponseHeaders); + hasHeaders.Headers["ETag"] = newEtag; return hasHeaders; } else @@ -618,6 +628,7 @@ namespace Emby.Server.Implementations.HttpServer }; AddResponseHeaders(hasHeaders, options.ResponseHeaders); + hasHeaders.Headers["ETag"] = newEtag; return hasHeaders; } } @@ -630,16 +641,8 @@ namespace Emby.Server.Implementations.HttpServer /// /// Adds the caching responseHeaders. /// - private void AddCachingHeaders(IDictionary responseHeaders, string cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration) + private void AddCachingHeaders(IDictionary responseHeaders, string cacheKey, TimeSpan? cacheDuration) { - // Don't specify both last modified and Etag, unless caching unconditionally. They are redundant - // https://developers.google.com/speed/docs/best-practices/caching#LeverageBrowserCaching - if (lastDateModified.HasValue && (string.IsNullOrEmpty(cacheKey) || cacheDuration.HasValue)) - { - AddAgeHeader(responseHeaders, lastDateModified); - responseHeaders["Last-Modified"] = lastDateModified.Value.ToString("r"); - } - if (cacheDuration.HasValue) { responseHeaders["Cache-Control"] = "public, max-age=" + Convert.ToInt32(cacheDuration.Value.TotalSeconds); @@ -692,28 +695,15 @@ namespace Emby.Server.Implementations.HttpServer /// The last date modified. /// Duration of the cache. /// true if [is not modified] [the specified cache key]; otherwise, false. - private bool IsNotModified(IRequest requestContext, Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration) + private bool IsNotModified(IRequest requestContext, Guid cacheKey) { - //var isNotModified = true; - - var ifModifiedSinceHeader = requestContext.Headers.Get("If-Modified-Since"); - - if (!string.IsNullOrEmpty(ifModifiedSinceHeader) - && DateTime.TryParse(ifModifiedSinceHeader, out DateTime ifModifiedSince) - && IsNotModified(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified)) - { - return true; - } - var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match"); bool hasCacheKey = !cacheKey.Equals(Guid.Empty); // Validate If-None-Match - if ((hasCacheKey && !string.IsNullOrEmpty(ifNoneMatchHeader))) + if (hasCacheKey && !string.IsNullOrEmpty(ifNoneMatchHeader)) { - ifNoneMatchHeader = (ifNoneMatchHeader ?? string.Empty).Trim('\"'); - if (Guid.TryParse(ifNoneMatchHeader, out var ifNoneMatch) && cacheKey.Equals(ifNoneMatch)) { From 94789860b1217a500e50d74ba5dbec26bb61a8d7 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Tue, 22 Jan 2019 22:37:26 +0100 Subject: [PATCH 02/13] Trim quotes from If-None-Match --- Emby.Server.Implementations/HttpServer/HttpResultFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index dee417b43f..370c76ec9a 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -535,7 +535,7 @@ namespace Emby.Server.Implementations.HttpServer var contentType = options.ContentType; var etag = requestContext.Headers.Get("If-None-Match"); - var cacheKey = etag != null ? new Guid(etag) : Guid.Empty; + var cacheKey = etag != null ? new Guid(etag.Trim("\"")) : Guid.Empty; if (!cacheKey.Equals(Guid.Empty)) { var key = cacheKey.ToString("N"); From df5e87409ae08199333da549be118653bc0c2221 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Tue, 22 Jan 2019 22:40:06 +0100 Subject: [PATCH 03/13] Fix trim input --- Emby.Server.Implementations/HttpServer/HttpResultFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 370c76ec9a..0ad4d8406b 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -535,7 +535,7 @@ namespace Emby.Server.Implementations.HttpServer var contentType = options.ContentType; var etag = requestContext.Headers.Get("If-None-Match"); - var cacheKey = etag != null ? new Guid(etag.Trim("\"")) : Guid.Empty; + var cacheKey = etag != null ? new Guid(etag.Trim('\"')) : Guid.Empty; if (!cacheKey.Equals(Guid.Empty)) { var key = cacheKey.ToString("N"); From 722120af74fadc1dfb43f1535797464a0ba63ba9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 21 Jan 2019 20:18:52 +0100 Subject: [PATCH 04/13] Remove conditions that are always true/false --- Emby.Dlna/Didl/DidlBuilder.cs | 8 +--- Emby.Drawing/SkiaEncoder.cs | 13 ++---- Emby.Naming/AudioBook/AudioBookResolver.cs | 5 ++- .../Channels/ChannelManager.cs | 15 +++---- .../Library/MediaSourceManager.cs | 9 +++-- .../Library/Resolvers/Audio/AudioResolver.cs | 40 ++++--------------- .../Library/Resolvers/Movies/MovieResolver.cs | 2 +- .../Library/Resolvers/TV/SeriesResolver.cs | 10 +---- .../Library/UserManager.cs | 33 +++++++-------- .../LiveTv/EmbyTV/EmbyTV.cs | 8 ++-- .../LiveTv/EmbyTV/EncodedRecorder.cs | 6 --- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 23 +++++------ 12 files changed, 56 insertions(+), 116 deletions(-) diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index d2f635e567..81c4d3d16c 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -932,13 +932,7 @@ namespace Emby.Dlna.Didl private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlWriter writer) { - ImageDownloadInfo imageInfo = null; - - // Finally, just use the image from the item - if (imageInfo == null) - { - imageInfo = GetImageInfo(item); - } + ImageDownloadInfo imageInfo = GetImageInfo(item);; if (imageInfo == null) { diff --git a/Emby.Drawing/SkiaEncoder.cs b/Emby.Drawing/SkiaEncoder.cs index 87e0eca21f..9883b3cca0 100644 --- a/Emby.Drawing/SkiaEncoder.cs +++ b/Emby.Drawing/SkiaEncoder.cs @@ -270,17 +270,10 @@ namespace Emby.Drawing // create the bitmap var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height, !requiresTransparencyHack); - if (bitmap != null) - { - // decode - codec.GetPixels(bitmap.Info, bitmap.GetPixels()); + // decode + codec.GetPixels(bitmap.Info, bitmap.GetPixels()); - origin = codec.EncodedOrigin; - } - else - { - origin = GetSKEncodedOrigin(orientation); - } + origin = codec.EncodedOrigin; return bitmap; } diff --git a/Emby.Naming/AudioBook/AudioBookResolver.cs b/Emby.Naming/AudioBook/AudioBookResolver.cs index f8f13bc5e4..67451a6396 100644 --- a/Emby.Naming/AudioBook/AudioBookResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookResolver.cs @@ -30,8 +30,11 @@ namespace Emby.Naming.AudioBook { throw new ArgumentNullException(nameof(path)); } - if (IsDirectory) + + if (IsDirectory) // TODO + { return null; + } var extension = Path.GetExtension(path) ?? string.Empty; // Check supported extensions diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 660547c85c..a306b01699 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -681,22 +681,17 @@ namespace Emby.Server.Implementations.Channels // Find the corresponding channel provider plugin var channelProvider = GetChannelProvider(channel); - var user = query.User; - - ChannelItemSortField? sortField = null; - var sortDescending = false; - - var parentItem = !query.ParentId.Equals(Guid.Empty) ? _libraryManager.GetItemById(query.ParentId) : channel; + var parentItem = query.ParentId == Guid.Empty ? channel : _libraryManager.GetItemById(query.ParentId); var itemsResult = await GetChannelItems(channelProvider, - user, + query.User, parentItem is Channel ? null : parentItem.ExternalId, - sortField, - sortDescending, + null, + false, cancellationToken) .ConfigureAwait(false); - if (query.ParentId.Equals(Guid.Empty)) + if (query.ParentId == Guid.Empty) { query.Parent = channel; } diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 0adc5553bc..1ed838893e 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -469,7 +469,7 @@ namespace Emby.Server.Implementations.Library } // TODO: Don't hardcode this - var isAudio = false; + const bool isAudio = false; try { @@ -480,9 +480,11 @@ namespace Emby.Server.Implementations.Library else { // hack - these two values were taken from LiveTVMediaSourceProvider - var cacheKey = request.OpenToken; + string cacheKey = request.OpenToken; - await new LiveStreamHelper(_mediaEncoder(), _logger, _jsonSerializer, _appPaths).AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken).ConfigureAwait(false); + await new LiveStreamHelper(_mediaEncoder(), _logger, _jsonSerializer, _appPaths) + .AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken) + .ConfigureAwait(false); } } catch (Exception ex) @@ -491,6 +493,7 @@ namespace Emby.Server.Implementations.Library AddMediaInfo(mediaSource, isAudio); } + // TODO: @bond Fix var json = _jsonSerializer.SerializeToString(mediaSource); _logger.LogInformation("Live stream opened: " + json); var clone = _jsonSerializer.DeserializeFromString(json); diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index baa665fce8..e39192d286 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -86,12 +86,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio .Where(i => !LibraryManager.IgnoreFile(i, args.Parent)) .ToList(); - if (isBooksCollectionType) - { - return FindAudio(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false); - } - - return null; + return FindAudio(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false); } if (LibraryManager.IsAudioFile(args.Path, libraryOptions)) @@ -145,36 +140,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio private T FindAudio(ItemResolveArgs args, string path, Folder parent, List fileSystemEntries, IDirectoryService directoryService, string collectionType, bool parseName) where T : MediaBrowser.Controller.Entities.Audio.Audio, new() { - var multiDiscFolders = new List(); - - var libraryOptions = args.GetLibraryOptions(); - var filesFromOtherItems = new List(); - // TODO: Allow GetMultiDiscMovie in here - var supportsMultiVersion = false; + const bool supportsMultiVersion = false; var result = ResolveMultipleAudio(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType, parseName) ?? new MultiItemResolverResult(); if (result.Items.Count == 1) { - var videoPath = result.Items[0].Path; - // If we were supporting this we'd be checking filesFromOtherItems - var hasOtherItems = false; - - if (!hasOtherItems) - { - var item = (T)result.Items[0]; - item.IsInMixedFolder = false; - item.Name = Path.GetFileName(item.ContainingFolderPath); - return item; - } - } - - if (result.Items.Count == 0 && multiDiscFolders.Count > 0) - { - //return GetMultiDiscAudio(multiDiscFolders, directoryService); + var item = (T)result.Items[0]; + item.IsInMixedFolder = false; + item.Name = Path.GetFileName(item.ContainingFolderPath); + return item; } return null; @@ -194,11 +172,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio { leftOver.Add(child); } - else if (IsIgnored(child.Name)) - { - - } - else + else if (!IsIgnored(child.Name)) { files.Add(child); } diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index e48213dbb1..472a3f105c 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -410,7 +410,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies } // TODO: Allow GetMultiDiscMovie in here - var supportsMultiVersion = true; + const bool supportsMultiVersion = true; var result = ResolveVideos(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType, parseName) ?? new MultiItemResolverResult(); diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 8ef2276898..5c95534ec1 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -153,16 +153,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions(); var episodeResolver = new Naming.TV.EpisodeResolver(namingOptions); - bool? isNamed = null; - bool? isOptimistic = null; - if (!isTvContentType) - { - isNamed = true; - isOptimistic = false; - } - - var episodeInfo = episodeResolver.Resolve(fullName, false, isNamed, isOptimistic, fillExtendedInfo: false); + var episodeInfo = episodeResolver.Resolve(fullName, false, true, false, fillExtendedInfo: false); if (episodeInfo != null && episodeInfo.EpisodeNumber.HasValue) { return true; diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index d5bc3d332e..6139659b70 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -322,17 +322,14 @@ namespace Emby.Server.Implementations.Library throw new SecurityException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name)); } - if (user != null) + if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint)) { - if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint)) - { - throw new SecurityException("Forbidden."); - } + throw new SecurityException("Forbidden."); + } - if (!user.IsParentalScheduleAllowed()) - { - throw new SecurityException("User is not allowed access at this time."); - } + if (!user.IsParentalScheduleAllowed()) + { + throw new SecurityException("User is not allowed access at this time."); } // Update LastActivityDate and LastLoginDate, then save @@ -463,26 +460,26 @@ namespace Emby.Server.Implementations.Library { user.Policy.InvalidLoginAttemptCount = newValue; - var maxCount = user.Policy.IsAdministrator ? - 3 : - 5; + var maxCount = user.Policy.IsAdministrator ? 3 : 5; + // TODO: Fix + /* var fireLockout = false; if (newValue >= maxCount) { - //_logger.LogDebug("Disabling user {0} due to {1} unsuccessful login attempts.", user.Name, newValue.ToString(CultureInfo.InvariantCulture)); - //user.Policy.IsDisabled = true; + _logger.LogDebug("Disabling user {0} due to {1} unsuccessful login attempts.", user.Name, newValue.ToString(CultureInfo.InvariantCulture)); + user.Policy.IsDisabled = true; - //fireLockout = true; - } + fireLockout = true; + }*/ UpdateUserPolicy(user, user.Policy, false); - if (fireLockout) + /* if (fireLockout) { UserLockedOut?.Invoke(this, new GenericEventArgs(user)); - } + }*/ } } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 64e5affd70..9e11494c99 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1133,8 +1133,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV IgnoreIndex = true }; - var isAudio = false; - await new LiveStreamHelper(_mediaEncoder, _logger, _jsonSerializer, _config.CommonApplicationPaths).AddMediaInfoWithProbe(stream, isAudio, false, cancellationToken).ConfigureAwait(false); + await new LiveStreamHelper(_mediaEncoder, _logger, _jsonSerializer, _config.CommonApplicationPaths) + .AddMediaInfoWithProbe(stream, false, false, cancellationToken).ConfigureAwait(false); return new List { @@ -1149,12 +1149,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public Task RecordLiveStream(string id, CancellationToken cancellationToken) { - return Task.FromResult(0); + return Task.CompletedTask; } public Task ResetTuner(string id, CancellationToken cancellationToken) { - return Task.FromResult(0); + return Task.CompletedTask; } async void _timerProvider_TimerFired(object sender, GenericEventArgs e) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index c09ee93482..c11a85027d 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -175,12 +175,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } var videoStream = mediaSource.VideoStream; - string videoDecoder = null; - - if (!string.IsNullOrEmpty(videoDecoder)) - { - inputModifier += " " + videoDecoder; - } if (mediaSource.ReadAtNativeFramerate) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index e8e4bc7238..77b09a83de 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -354,10 +354,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun int? height = null; bool isInterlaced = true; string videoCodec = null; - string audioCodec = null; int? videoBitrate = null; - int? audioBitrate = null; var isHd = channelInfo.IsHD ?? true; @@ -427,20 +425,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } } - if (channelInfo != null) + if (string.IsNullOrWhiteSpace(videoCodec)) { - if (string.IsNullOrWhiteSpace(videoCodec)) - { - videoCodec = channelInfo.VideoCodec; - } - audioCodec = channelInfo.AudioCodec; - - if (!videoBitrate.HasValue) - { - videoBitrate = isHd ? 15000000 : 2000000; - } - audioBitrate = isHd ? 448000 : 192000; + videoCodec = channelInfo.VideoCodec; } + string audioCodec = channelInfo.AudioCodec; + + if (!videoBitrate.HasValue) + { + videoBitrate = isHd ? 15000000 : 2000000; + } + int? audioBitrate = isHd ? 448000 : 192000; // normalize if (string.Equals(videoCodec, "mpeg2", StringComparison.OrdinalIgnoreCase)) From f6f0a8a481332f55a4af68ee776580cb506dd48c Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 6 Jan 2019 16:00:30 +0100 Subject: [PATCH 05/13] Use EF Core for Activity database --- .../Activity/ActivityManager.cs | 36 +- .../Activity/ActivityRepository.cs | 307 +----------------- .../ApplicationHost.cs | 6 +- .../Emby.Server.Implementations.csproj | 1 + .../Activity/IActivityManager.cs | 9 +- .../Activity/IActivityRepository.cs | 8 +- 6 files changed, 52 insertions(+), 315 deletions(-) diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs index 6febcc2f7b..a434edd279 100644 --- a/Emby.Server.Implementations/Activity/ActivityManager.cs +++ b/Emby.Server.Implementations/Activity/ActivityManager.cs @@ -1,9 +1,10 @@ using System; +using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Events; -using MediaBrowser.Model.Querying; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Activity @@ -26,20 +27,38 @@ namespace Emby.Server.Implementations.Activity _userManager = userManager; } - public void Create(ActivityLogEntry entry) + public async Task Create(ActivityLogEntry entry) { entry.Date = DateTime.UtcNow; - _repo.Create(entry); + await _repo.CreateAsync(entry); EntryCreated?.Invoke(this, new GenericEventArgs(entry)); } - public QueryResult GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit) + public IEnumerable GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit) { - var result = _repo.GetActivityLogEntries(minDate, hasUserId, startIndex, limit); + var result = _repo.GetActivityLogEntries(); - foreach (var item in result.Items.Where(i => !i.UserId.Equals(Guid.Empty))) + if (minDate.HasValue) + { + result = result.Where(x => x.Date >= minDate.Value); + } + if (hasUserId.HasValue) + { + result = result.Where(x => x.UserId != null && x.UserId != Guid.Empty); + } + if (startIndex.HasValue) + { + result = result.Where(x => x.Id >= startIndex.Value); + } + if (limit.HasValue) + { + result = result.Take(limit.Value); + } + + // Add images for each user + foreach (var item in result) { var user = _userManager.GetUserById(item.UserId); @@ -52,10 +71,5 @@ namespace Emby.Server.Implementations.Activity return result; } - - public QueryResult GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit) - { - return GetActivityLogEntries(minDate, null, startIndex, limit); - } } } diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index aeed8b6f1a..34d6bc1984 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -1,310 +1,37 @@ -using System; -using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; -using Emby.Server.Implementations.Data; -using MediaBrowser.Controller; +using System.Threading.Tasks; using MediaBrowser.Model.Activity; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Querying; -using Microsoft.Extensions.Logging; -using SQLitePCL.pretty; +using Microsoft.EntityFrameworkCore; namespace Emby.Server.Implementations.Activity { - public class ActivityRepository : BaseSqliteRepository, IActivityRepository + public class ActivityRepository : DbContext, IActivityRepository { - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - protected IFileSystem FileSystem { get; private set; } + protected string _dataDirPath; - public ActivityRepository(ILoggerFactory loggerFactory, IServerApplicationPaths appPaths, IFileSystem fileSystem) - : base(loggerFactory.CreateLogger(nameof(ActivityRepository))) + public DbSet ActivityLogs { get; set; } + + public ActivityRepository(string dataDirPath) { - DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db"); - FileSystem = fileSystem; + _dataDirPath = dataDirPath; } - public void Initialize() + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - try - { - InitializeInternal(); - } - catch (Exception ex) - { - Logger.LogError(ex, "Error loading database file. Will reset and retry."); + // Ensure the dir exists + if (!Directory.Exists(_dataDirPath)) Directory.CreateDirectory(_dataDirPath); - FileSystem.DeleteFile(DbFilePath); - - InitializeInternal(); - } + optionsBuilder.UseSqlite($"Filename={Path.Combine(_dataDirPath, "activitylog.sqlite.db")}"); } - private void InitializeInternal() + public async Task CreateAsync(ActivityLogEntry entry) { - using (var connection = CreateConnection()) - { - RunDefaultInitialization(connection); - - connection.RunQueries(new[] - { - "create table if not exists ActivityLog (Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)", - "drop index if exists idx_ActivityLogEntries" - }); - - TryMigrate(connection); - } + await ActivityLogs.AddAsync(entry); + await SaveChangesAsync(); } - private void TryMigrate(ManagedConnection connection) - { - try - { - if (TableExists(connection, "ActivityLogEntries")) - { - connection.RunQueries(new[] - { - "INSERT INTO ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) SELECT Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity FROM ActivityLogEntries", - "drop table if exists ActivityLogEntries" - }); - } - } - catch (Exception ex) - { - Logger.LogError(ex, "Error migrating activity log database"); - } - } - - private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog"; - - public void Create(ActivityLogEntry entry) - { - if (entry == null) - { - throw new ArgumentNullException(nameof(entry)); - } - - using (WriteLock.Write()) - using (var connection = CreateConnection()) - { - connection.RunInTransaction(db => - { - using (var statement = db.PrepareStatement("insert into ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)")) - { - statement.TryBind("@Name", entry.Name); - - statement.TryBind("@Overview", entry.Overview); - statement.TryBind("@ShortOverview", entry.ShortOverview); - statement.TryBind("@Type", entry.Type); - statement.TryBind("@ItemId", entry.ItemId); - - if (entry.UserId.Equals(Guid.Empty)) - { - statement.TryBindNull("@UserId"); - } - else - { - statement.TryBind("@UserId", entry.UserId.ToString("N")); - } - - statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue()); - statement.TryBind("@LogSeverity", entry.Severity.ToString()); - - statement.MoveNext(); - } - }, TransactionMode); - } - } - - public void Update(ActivityLogEntry entry) - { - if (entry == null) - { - throw new ArgumentNullException(nameof(entry)); - } - - using (WriteLock.Write()) - using (var connection = CreateConnection()) - { - connection.RunInTransaction(db => - { - using (var statement = db.PrepareStatement("Update ActivityLog set Name=@Name,Overview=@Overview,ShortOverview=@ShortOverview,Type=@Type,ItemId=@ItemId,UserId=@UserId,DateCreated=@DateCreated,LogSeverity=@LogSeverity where Id=@Id")) - { - statement.TryBind("@Id", entry.Id); - - statement.TryBind("@Name", entry.Name); - statement.TryBind("@Overview", entry.Overview); - statement.TryBind("@ShortOverview", entry.ShortOverview); - statement.TryBind("@Type", entry.Type); - statement.TryBind("@ItemId", entry.ItemId); - - if (entry.UserId.Equals(Guid.Empty)) - { - statement.TryBindNull("@UserId"); - } - else - { - statement.TryBind("@UserId", entry.UserId.ToString("N")); - } - - statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue()); - statement.TryBind("@LogSeverity", entry.Severity.ToString()); - - statement.MoveNext(); - } - }, TransactionMode); - } - } - - public QueryResult GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit) - { - using (WriteLock.Read()) - using (var connection = CreateConnection(true)) - { - var commandText = BaseActivitySelectText; - var whereClauses = new List(); - - if (minDate.HasValue) - { - whereClauses.Add("DateCreated>=@DateCreated"); - } - if (hasUserId.HasValue) - { - if (hasUserId.Value) - { - whereClauses.Add("UserId not null"); - } - else - { - whereClauses.Add("UserId is null"); - } - } - - var whereTextWithoutPaging = whereClauses.Count == 0 ? - string.Empty : - " where " + string.Join(" AND ", whereClauses.ToArray()); - - if (startIndex.HasValue && startIndex.Value > 0) - { - var pagingWhereText = whereClauses.Count == 0 ? - string.Empty : - " where " + string.Join(" AND ", whereClauses.ToArray()); - - whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM ActivityLog {0} ORDER BY DateCreated DESC LIMIT {1})", - pagingWhereText, - startIndex.Value.ToString(_usCulture))); - } - - var whereText = whereClauses.Count == 0 ? - string.Empty : - " where " + string.Join(" AND ", whereClauses.ToArray()); - - commandText += whereText; - - commandText += " ORDER BY DateCreated DESC"; - - if (limit.HasValue) - { - commandText += " LIMIT " + limit.Value.ToString(_usCulture); - } - - var statementTexts = new List(); - statementTexts.Add(commandText); - statementTexts.Add("select count (Id) from ActivityLog" + whereTextWithoutPaging); - - return connection.RunInTransaction(db => - { - var list = new List(); - var result = new QueryResult(); - - var statements = PrepareAllSafe(db, statementTexts).ToList(); - - using (var statement = statements[0]) - { - if (minDate.HasValue) - { - statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue()); - } - - foreach (var row in statement.ExecuteQuery()) - { - list.Add(GetEntry(row)); - } - } - - using (var statement = statements[1]) - { - if (minDate.HasValue) - { - statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue()); - } - - result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); - } - - result.Items = list.ToArray(); - return result; - - }, ReadTransactionMode); - } - } - - private static ActivityLogEntry GetEntry(IReadOnlyList reader) - { - var index = 0; - - var info = new ActivityLogEntry - { - Id = reader[index].ToInt64() - }; - - index++; - if (reader[index].SQLiteType != SQLiteType.Null) - { - info.Name = reader[index].ToString(); - } - - index++; - if (reader[index].SQLiteType != SQLiteType.Null) - { - info.Overview = reader[index].ToString(); - } - - index++; - if (reader[index].SQLiteType != SQLiteType.Null) - { - info.ShortOverview = reader[index].ToString(); - } - - index++; - if (reader[index].SQLiteType != SQLiteType.Null) - { - info.Type = reader[index].ToString(); - } - - index++; - if (reader[index].SQLiteType != SQLiteType.Null) - { - info.ItemId = reader[index].ToString(); - } - - index++; - if (reader[index].SQLiteType != SQLiteType.Null) - { - info.UserId = new Guid(reader[index].ToString()); - } - - index++; - info.Date = reader[index].ReadDateTime(); - - index++; - if (reader[index].SQLiteType != SQLiteType.Null) - { - info.Severity = (LogLevel)Enum.Parse(typeof(LogLevel), reader[index].ToString(), true); - } - - return info; - } + public IQueryable GetActivityLogEntries() + => ActivityLogs; } } diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c8c4da0f76..6d0e251ce2 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1148,11 +1148,7 @@ namespace Emby.Server.Implementations private IActivityRepository GetActivityLogRepository() { - var repo = new ActivityRepository(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager); - - repo.Initialize(); - - return repo; + return new ActivityRepository(ServerConfigurationManager.ApplicationPaths.DataPath); } /// diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 3aa617b021..f2a5b2e386 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -22,6 +22,7 @@ + diff --git a/MediaBrowser.Model/Activity/IActivityManager.cs b/MediaBrowser.Model/Activity/IActivityManager.cs index 897d93d790..3cfd94695d 100644 --- a/MediaBrowser.Model/Activity/IActivityManager.cs +++ b/MediaBrowser.Model/Activity/IActivityManager.cs @@ -1,6 +1,7 @@ using System; +using System.Collections.Generic; +using System.Threading.Tasks; using MediaBrowser.Model.Events; -using MediaBrowser.Model.Querying; namespace MediaBrowser.Model.Activity { @@ -8,10 +9,8 @@ namespace MediaBrowser.Model.Activity { event EventHandler> EntryCreated; - void Create(ActivityLogEntry entry); + Task Create(ActivityLogEntry entry); - QueryResult GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit); - - QueryResult GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? x, int? y); + IEnumerable GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? x, int? y); } } diff --git a/MediaBrowser.Model/Activity/IActivityRepository.cs b/MediaBrowser.Model/Activity/IActivityRepository.cs index f0e3b902c4..3ed6175ced 100644 --- a/MediaBrowser.Model/Activity/IActivityRepository.cs +++ b/MediaBrowser.Model/Activity/IActivityRepository.cs @@ -1,12 +1,12 @@ -using System; -using MediaBrowser.Model.Querying; +using System.Linq; +using System.Threading.Tasks; namespace MediaBrowser.Model.Activity { public interface IActivityRepository { - void Create(ActivityLogEntry entry); + Task CreateAsync(ActivityLogEntry entry); - QueryResult GetActivityLogEntries(DateTime? minDate, bool? z, int? startIndex, int? limit); + IQueryable GetActivityLogEntries(); } } From 8d9428ebdc463dcaa02cfa8daf91ab480f2ace6a Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 14 Jan 2019 18:28:29 +0100 Subject: [PATCH 06/13] Ensure DB exists --- .../Activity/ActivityRepository.cs | 5 ++++- Emby.Server.Implementations/ApplicationHost.cs | 16 ++++++++++------ Jellyfin.Server/Program.cs | 4 +--- MediaBrowser.Common/IApplicationHost.cs | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index 34d6bc1984..fb5f8d6efa 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -20,7 +20,10 @@ namespace Emby.Server.Implementations.Activity protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { // Ensure the dir exists - if (!Directory.Exists(_dataDirPath)) Directory.CreateDirectory(_dataDirPath); + if (!Directory.Exists(_dataDirPath)) + { + Directory.CreateDirectory(_dataDirPath); + } optionsBuilder.UseSqlite($"Filename={Path.Combine(_dataDirPath, "activitylog.sqlite.db")}"); } diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 6d0e251ce2..3562074fef 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -709,7 +709,7 @@ namespace Emby.Server.Implementations } } - public void Init() + public async Task Init() { HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; @@ -739,7 +739,7 @@ namespace Emby.Server.Implementations SetHttpLimit(); - RegisterResources(); + await RegisterResources(); FindParts(); } @@ -754,7 +754,7 @@ namespace Emby.Server.Implementations /// /// Registers resources that classes will depend on /// - protected void RegisterResources() + protected async Task RegisterResources() { RegisterSingleInstance(ConfigurationManager); RegisterSingleInstance(this); @@ -931,7 +931,7 @@ namespace Emby.Server.Implementations EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager); RegisterSingleInstance(EncodingManager); - var activityLogRepo = GetActivityLogRepository(); + var activityLogRepo = await GetActivityLogRepository(); RegisterSingleInstance(activityLogRepo); RegisterSingleInstance(new ActivityManager(LoggerFactory, activityLogRepo, UserManager)); @@ -1146,9 +1146,13 @@ namespace Emby.Server.Implementations return repo; } - private IActivityRepository GetActivityLogRepository() + private async Task GetActivityLogRepository() { - return new ActivityRepository(ServerConfigurationManager.ApplicationPaths.DataPath); + var repo = new ActivityRepository(ServerConfigurationManager.ApplicationPaths.DataPath); + + await repo.Database.EnsureCreatedAsync(); + + return repo; } /// diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 0510548b56..bdf8c4e94a 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -99,7 +99,7 @@ namespace Jellyfin.Server new SystemEvents(), new NetworkManager(_loggerFactory, environmentInfo))) { - appHost.Init(); + await appHost.Init(); appHost.ImageProcessor.ImageEncoder = GetImageEncoder(fileSystem, appPaths, appHost.LocalizationManager); @@ -108,7 +108,6 @@ namespace Jellyfin.Server await appHost.RunStartupTasks(); // TODO: read input for a stop command - try { // Block main thread until shutdown @@ -167,7 +166,6 @@ namespace Jellyfin.Server { Directory.CreateDirectory(programDataPath); } - string configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR"); if (string.IsNullOrEmpty(configDir)) { diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index 385127c54b..f32eec6ceb 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -131,7 +131,7 @@ namespace MediaBrowser.Common /// /// Inits this instance. /// - void Init(); + Task Init(); /// /// Creates the instance. From 48da8f429e2b997c7c67f68ac4e6b67a29b06867 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 16 Jan 2019 15:16:19 +0100 Subject: [PATCH 07/13] Return a true IEnumerable --- Emby.Server.Implementations/Activity/ActivityManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs index a434edd279..e504bc4d73 100644 --- a/Emby.Server.Implementations/Activity/ActivityManager.cs +++ b/Emby.Server.Implementations/Activity/ActivityManager.cs @@ -69,7 +69,7 @@ namespace Emby.Server.Implementations.Activity } } - return result; + return result.AsEnumerable(); } } } From 3cd31cadf891fb4aad7ca8d8594bf27423724eb8 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 16 Jan 2019 15:32:51 +0100 Subject: [PATCH 08/13] No need to check if the dir exists --- Emby.Server.Implementations/Activity/ActivityRepository.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index fb5f8d6efa..af0b20d921 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -20,10 +20,7 @@ namespace Emby.Server.Implementations.Activity protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { // Ensure the dir exists - if (!Directory.Exists(_dataDirPath)) - { - Directory.CreateDirectory(_dataDirPath); - } + Directory.CreateDirectory(_dataDirPath); optionsBuilder.UseSqlite($"Filename={Path.Combine(_dataDirPath, "activitylog.sqlite.db")}"); } From 905a253ff5ce2eeee9806b7a4581506468b2fb55 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 23 Jan 2019 19:09:34 +0100 Subject: [PATCH 09/13] Suffix async methods with Async --- .../Activity/ActivityLogEntryPoint.cs | 93 ++++++++++--------- .../Activity/ActivityManager.cs | 2 +- .../ApplicationHost.cs | 10 +- Jellyfin.Server/Program.cs | 2 +- MediaBrowser.Api/Library/LibraryService.cs | 2 +- MediaBrowser.Common/IApplicationHost.cs | 2 +- .../Activity/IActivityManager.cs | 2 +- 7 files changed, 57 insertions(+), 56 deletions(-) diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index a8e8f815a0..aef72c6972 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; @@ -92,18 +93,18 @@ namespace Emby.Server.Implementations.Activity _appHost.ApplicationUpdated += _appHost_ApplicationUpdated; } - void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs e) + async void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("CameraImageUploadedFrom"), e.Argument.Device.Name), Type = NotificationType.CameraImageUploaded.ToString() }); } - void _userManager_UserLockedOut(object sender, GenericEventArgs e) + async void _userManager_UserLockedOut(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("UserLockedOutWithName"), e.Argument.Name), Type = NotificationType.UserLockedOut.ToString(), @@ -111,9 +112,9 @@ namespace Emby.Server.Implementations.Activity }); } - void _subManager_SubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e) + async void _subManager_SubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, Notifications.Notifications.GetItemName(e.Item)), Type = "SubtitleDownloadFailure", @@ -122,7 +123,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e) + async void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e) { var item = e.MediaInfo; @@ -145,7 +146,7 @@ namespace Emby.Server.Implementations.Activity var user = e.Users.First(); - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName), Type = GetPlaybackStoppedNotificationType(item.MediaType), @@ -153,7 +154,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e) + async void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e) { var item = e.MediaInfo; @@ -176,7 +177,7 @@ namespace Emby.Server.Implementations.Activity var user = e.Users.First(); - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName), Type = GetPlaybackNotificationType(item.MediaType), @@ -237,7 +238,7 @@ namespace Emby.Server.Implementations.Activity return null; } - void _sessionManager_SessionEnded(object sender, SessionEventArgs e) + async void _sessionManager_SessionEnded(object sender, SessionEventArgs e) { string name; var session = e.SessionInfo; @@ -254,7 +255,7 @@ namespace Emby.Server.Implementations.Activity name = string.Format(_localization.GetLocalizedString("UserOfflineFromDevice"), session.UserName, session.DeviceName); } - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = name, Type = "SessionEnded", @@ -263,11 +264,11 @@ namespace Emby.Server.Implementations.Activity }); } - void _sessionManager_AuthenticationSucceeded(object sender, GenericEventArgs e) + async void _sessionManager_AuthenticationSucceeded(object sender, GenericEventArgs e) { var user = e.Argument.User; - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("AuthenticationSucceededWithUserName"), user.Name), Type = "AuthenticationSucceeded", @@ -276,9 +277,9 @@ namespace Emby.Server.Implementations.Activity }); } - void _sessionManager_AuthenticationFailed(object sender, GenericEventArgs e) + async void _sessionManager_AuthenticationFailed(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("FailedLoginAttemptWithUserName"), e.Argument.Username), Type = "AuthenticationFailed", @@ -287,9 +288,9 @@ namespace Emby.Server.Implementations.Activity }); } - void _appHost_ApplicationUpdated(object sender, GenericEventArgs e) + async void _appHost_ApplicationUpdated(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("MessageApplicationUpdatedTo"), e.Argument.versionStr), Type = NotificationType.ApplicationUpdateInstalled.ToString(), @@ -297,27 +298,27 @@ namespace Emby.Server.Implementations.Activity }); } - void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e) + async void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("MessageNamedServerConfigurationUpdatedWithValue"), e.Key), Type = "NamedConfigurationUpdated" }); } - void _config_ConfigurationUpdated(object sender, EventArgs e) + async void _config_ConfigurationUpdated(object sender, EventArgs e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = _localization.GetLocalizedString("MessageServerConfigurationUpdated"), Type = "ServerConfigurationUpdated" }); } - void _userManager_UserPolicyUpdated(object sender, GenericEventArgs e) + async void _userManager_UserPolicyUpdated(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("UserPolicyUpdatedWithName"), e.Argument.Name), Type = "UserPolicyUpdated", @@ -325,18 +326,18 @@ namespace Emby.Server.Implementations.Activity }); } - void _userManager_UserDeleted(object sender, GenericEventArgs e) + async void _userManager_UserDeleted(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("UserDeletedWithName"), e.Argument.Name), Type = "UserDeleted" }); } - void _userManager_UserPasswordChanged(object sender, GenericEventArgs e) + async void _userManager_UserPasswordChanged(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("UserPasswordChangedWithName"), e.Argument.Name), Type = "UserPasswordChanged", @@ -344,9 +345,9 @@ namespace Emby.Server.Implementations.Activity }); } - void _userManager_UserCreated(object sender, GenericEventArgs e) + async void _userManager_UserCreated(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("UserCreatedWithName"), e.Argument.Name), Type = "UserCreated", @@ -354,9 +355,9 @@ namespace Emby.Server.Implementations.Activity }); } - void _subManager_SubtitlesDownloaded(object sender, SubtitleDownloadEventArgs e) + async void _subManager_SubtitlesDownloaded(object sender, SubtitleDownloadEventArgs e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("SubtitlesDownloadedForItem"), Notifications.Notifications.GetItemName(e.Item)), Type = "SubtitlesDownloaded", @@ -365,7 +366,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _sessionManager_SessionStarted(object sender, SessionEventArgs e) + async void _sessionManager_SessionStarted(object sender, SessionEventArgs e) { string name; var session = e.SessionInfo; @@ -382,7 +383,7 @@ namespace Emby.Server.Implementations.Activity name = string.Format(_localization.GetLocalizedString("UserOnlineFromDevice"), session.UserName, session.DeviceName); } - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = name, Type = "SessionStarted", @@ -391,9 +392,9 @@ namespace Emby.Server.Implementations.Activity }); } - void _installationManager_PluginUpdated(object sender, GenericEventArgs> e) + async void _installationManager_PluginUpdated(object sender, GenericEventArgs> e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("PluginUpdatedWithName"), e.Argument.Item1.Name), Type = NotificationType.PluginUpdateInstalled.ToString(), @@ -402,18 +403,18 @@ namespace Emby.Server.Implementations.Activity }); } - void _installationManager_PluginUninstalled(object sender, GenericEventArgs e) + async void _installationManager_PluginUninstalled(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("PluginUninstalledWithName"), e.Argument.Name), Type = NotificationType.PluginUninstalled.ToString() }); } - void _installationManager_PluginInstalled(object sender, GenericEventArgs e) + async void _installationManager_PluginInstalled(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("PluginInstalledWithName"), e.Argument.name), Type = NotificationType.PluginInstalled.ToString(), @@ -421,11 +422,11 @@ namespace Emby.Server.Implementations.Activity }); } - void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e) + async void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e) { var installationInfo = e.InstallationInfo; - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("NameInstallFailed"), installationInfo.Name), Type = NotificationType.InstallationFailed.ToString(), @@ -434,7 +435,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e) + async void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e) { var result = e.Result; var task = e.Task; @@ -461,7 +462,7 @@ namespace Emby.Server.Implementations.Activity vals.Add(e.Result.LongErrorMessage); } - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name), Type = NotificationType.TaskFailed.ToString(), @@ -472,11 +473,11 @@ namespace Emby.Server.Implementations.Activity } } - private void CreateLogEntry(ActivityLogEntry entry) + private async Task CreateLogEntry(ActivityLogEntry entry) { try { - _activityManager.Create(entry); + await _activityManager.CreateAsync(entry); } catch { diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs index e504bc4d73..8fcacb0024 100644 --- a/Emby.Server.Implementations/Activity/ActivityManager.cs +++ b/Emby.Server.Implementations/Activity/ActivityManager.cs @@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Activity _userManager = userManager; } - public async Task Create(ActivityLogEntry entry) + public async Task CreateAsync(ActivityLogEntry entry) { entry.Date = DateTime.UtcNow; diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 3562074fef..f5a4f15817 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -709,7 +709,7 @@ namespace Emby.Server.Implementations } } - public async Task Init() + public async Task InitAsync() { HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; @@ -739,7 +739,7 @@ namespace Emby.Server.Implementations SetHttpLimit(); - await RegisterResources(); + await RegisterResourcesAsync(); FindParts(); } @@ -754,7 +754,7 @@ namespace Emby.Server.Implementations /// /// Registers resources that classes will depend on /// - protected async Task RegisterResources() + protected async Task RegisterResourcesAsync() { RegisterSingleInstance(ConfigurationManager); RegisterSingleInstance(this); @@ -931,7 +931,7 @@ namespace Emby.Server.Implementations EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager); RegisterSingleInstance(EncodingManager); - var activityLogRepo = await GetActivityLogRepository(); + var activityLogRepo = await GetActivityLogRepositoryAsync(); RegisterSingleInstance(activityLogRepo); RegisterSingleInstance(new ActivityManager(LoggerFactory, activityLogRepo, UserManager)); @@ -1146,7 +1146,7 @@ namespace Emby.Server.Implementations return repo; } - private async Task GetActivityLogRepository() + private async Task GetActivityLogRepositoryAsync() { var repo = new ActivityRepository(ServerConfigurationManager.ApplicationPaths.DataPath); diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index bdf8c4e94a..2f14d1c52d 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -99,7 +99,7 @@ namespace Jellyfin.Server new SystemEvents(), new NetworkManager(_loggerFactory, environmentInfo))) { - await appHost.Init(); + await appHost.InitAsync(); appHost.ImageProcessor.ImageEncoder = GetImageEncoder(fileSystem, appPaths, appHost.LocalizationManager); diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 12d807a7e2..d3009295f4 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -839,7 +839,7 @@ namespace MediaBrowser.Api.Library { try { - _activityManager.Create(new ActivityLogEntry + _activityManager.CreateAsync(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Name, item.Name), Type = "UserDownloadingContent", diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index f32eec6ceb..9dc625e357 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -131,7 +131,7 @@ namespace MediaBrowser.Common /// /// Inits this instance. /// - Task Init(); + Task InitAsync(); /// /// Creates the instance. diff --git a/MediaBrowser.Model/Activity/IActivityManager.cs b/MediaBrowser.Model/Activity/IActivityManager.cs index 3cfd94695d..bbd7b6f2d6 100644 --- a/MediaBrowser.Model/Activity/IActivityManager.cs +++ b/MediaBrowser.Model/Activity/IActivityManager.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.Model.Activity { event EventHandler> EntryCreated; - Task Create(ActivityLogEntry entry); + Task CreateAsync(ActivityLogEntry entry); IEnumerable GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? x, int? y); } From cc3b1e5cc8243745f18719ee633c599580aa7637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20B=C3=BCttner?= Date: Wed, 23 Jan 2019 21:14:10 +0100 Subject: [PATCH 10/13] Fix package build for fedora --- deployment/common.build.sh | 3 +-- deployment/fedora-package-x64/clean.sh | 12 +++++++++++- deployment/fedora-package-x64/package.sh | 18 ++++++++++-------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/deployment/common.build.sh b/deployment/common.build.sh index 7392fd4015..c191ec2a1b 100755 --- a/deployment/common.build.sh +++ b/deployment/common.build.sh @@ -23,8 +23,7 @@ get_version() ( local ROOT=${1-$DEFAULT_ROOT} grep "AssemblyVersion" ${ROOT}/SharedVersion.cs \ - | sed -E 's/\[assembly: ?AssemblyVersion\("([0-9\.]+)"\)\]/\1/' \ - | sed -E 's/.0$//' + | sed -E 's/\[assembly: ?AssemblyVersion\("([0-9\.]+)"\)\]/\1/' ) # Run a build diff --git a/deployment/fedora-package-x64/clean.sh b/deployment/fedora-package-x64/clean.sh index 3df2d7796e..d0f487648a 100755 --- a/deployment/fedora-package-x64/clean.sh +++ b/deployment/fedora-package-x64/clean.sh @@ -4,4 +4,14 @@ source ../common.build.sh VERSION=`get_version ../..` -clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION} +package_temporary_dir="`pwd`/pkg-dist-tmp" +pkg_src_dir="`pwd`/pkg-src" +image_name="jellyfin-rpmbuild" +docker_sudo="" +if ! $(id -Gn | grep -q 'docker') && [ ! $EUID -eq 0 ]; then + docker_sudo=sudo +fi + +$docker_sudo docker image rm $image_name --force +rm -rf "$package_temporary_dir" +rm -rf "$pkg_src_dir/jellyfin-${VERSION}.tar.gz" diff --git a/deployment/fedora-package-x64/package.sh b/deployment/fedora-package-x64/package.sh index 416c8213b9..390c2d9b70 100755 --- a/deployment/fedora-package-x64/package.sh +++ b/deployment/fedora-package-x64/package.sh @@ -18,10 +18,14 @@ output_dir="`pwd`/pkg-dist" pkg_src_dir="`pwd`/pkg-src" current_user="`whoami`" image_name="jellyfin-rpmbuild" +docker_sudo="" +if ! $(id -Gn | grep -q 'docker') && [ ! $EUID -eq 0 ]; then + docker_sudo=sudo +fi cleanup() { set +o errexit - docker image rm $image_name --force + $docker_sudo docker image rm $image_name --force rm -rf "$package_temporary_dir" rm -rf "$pkg_src_dir/jellyfin-${VERSION}.tar.gz" } @@ -30,7 +34,7 @@ GNU_TAR=1 mkdir -p "$package_temporary_dir" echo "Bundling all sources for RPM build." tar \ ---transform "s,^\.,jellyfin-${VERSION}" \ +--transform "s,^\.,jellyfin-${VERSION}," \ --exclude='.git*' \ --exclude='**/.git' \ --exclude='**/.hg' \ @@ -42,10 +46,8 @@ tar \ --exclude='**/.nuget' \ --exclude='*.deb' \ --exclude='*.rpm' \ --Jcvf \ -"$package_temporary_dir/jellyfin-${VERSION}.tar.xz" \ --C "../.." \ -./ || true && GNU_TAR=0 +-zcf "$pkg_src_dir/jellyfin-${VERSION}.tar.gz" \ +-C "../.." ./ || GNU_TAR=0 if [ $GNU_TAR -eq 0 ]; then echo "The installed tar binary did not support --transform. Using workaround." @@ -75,9 +77,9 @@ if [ $GNU_TAR -eq 0 ]; then tar -zcf "$pkg_src_dir/jellyfin-${VERSION}.tar.gz" -C "$package_temporary_dir" "jellyfin-${VERSION}" fi -docker build ../.. -t "$image_name" -f ./Dockerfile +$docker_sudo docker build ../.. -t "$image_name" -f ./Dockerfile mkdir -p "$output_dir" -docker run --rm -v "$package_temporary_dir:/temp" "$image_name" sh -c 'find /build/rpmbuild -maxdepth 3 -type f -name "jellyfin*.rpm" -exec mv {} /temp \;' +$docker_sudo docker run --rm -v "$package_temporary_dir:/temp" "$image_name" sh -c 'find /build/rpmbuild -maxdepth 3 -type f -name "jellyfin*.rpm" -exec mv {} /temp \;' chown -R "$current_user" "$package_temporary_dir" \ || sudo chown -R "$current_user" "$package_temporary_dir" mv "$package_temporary_dir"/*.rpm "$output_dir" From f2d0d1f646c16cf2099f6395721822bbfa555fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20B=C3=BCttner?= Date: Thu, 24 Jan 2019 15:24:04 +0100 Subject: [PATCH 11/13] Make the docker group check BSD compatible --- deployment/fedora-package-x64/clean.sh | 2 +- deployment/fedora-package-x64/package.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deployment/fedora-package-x64/clean.sh b/deployment/fedora-package-x64/clean.sh index d0f487648a..001f568dda 100755 --- a/deployment/fedora-package-x64/clean.sh +++ b/deployment/fedora-package-x64/clean.sh @@ -8,7 +8,7 @@ package_temporary_dir="`pwd`/pkg-dist-tmp" pkg_src_dir="`pwd`/pkg-src" image_name="jellyfin-rpmbuild" docker_sudo="" -if ! $(id -Gn | grep -q 'docker') && [ ! $EUID -eq 0 ]; then +if ! $(id -Gn | grep -q 'docker') && [ ! ${EUID:-1000} -eq 0 ] && [ ! $USER == "root" ]; then docker_sudo=sudo fi diff --git a/deployment/fedora-package-x64/package.sh b/deployment/fedora-package-x64/package.sh index 390c2d9b70..6942240066 100755 --- a/deployment/fedora-package-x64/package.sh +++ b/deployment/fedora-package-x64/package.sh @@ -19,7 +19,7 @@ pkg_src_dir="`pwd`/pkg-src" current_user="`whoami`" image_name="jellyfin-rpmbuild" docker_sudo="" -if ! $(id -Gn | grep -q 'docker') && [ ! $EUID -eq 0 ]; then +if ! $(id -Gn | grep -q 'docker') && [ ! ${EUID:-1000} -eq 0 ] && [ ! $USER == "root" ]; then docker_sudo=sudo fi From 6a3ed5d519b6670ddccbf8bb326ce51b424c9799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20B=C3=BCttner?= Date: Thu, 24 Jan 2019 16:24:00 +0100 Subject: [PATCH 12/13] Added macOS to the docker sudo exception --- deployment/fedora-package-x64/clean.sh | 3 ++- deployment/fedora-package-x64/package.sh | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/deployment/fedora-package-x64/clean.sh b/deployment/fedora-package-x64/clean.sh index 001f568dda..d7233208ff 100755 --- a/deployment/fedora-package-x64/clean.sh +++ b/deployment/fedora-package-x64/clean.sh @@ -8,7 +8,8 @@ package_temporary_dir="`pwd`/pkg-dist-tmp" pkg_src_dir="`pwd`/pkg-src" image_name="jellyfin-rpmbuild" docker_sudo="" -if ! $(id -Gn | grep -q 'docker') && [ ! ${EUID:-1000} -eq 0 ] && [ ! $USER == "root" ]; then +if ! $(id -Gn | grep -q 'docker') && [ ! ${EUID:-1000} -eq 0 ] && \ + [ ! $USER == "root" ] && ! $(echo "$OSTYPE" | grep -q "darwin"); then docker_sudo=sudo fi diff --git a/deployment/fedora-package-x64/package.sh b/deployment/fedora-package-x64/package.sh index 6942240066..d459cdb243 100755 --- a/deployment/fedora-package-x64/package.sh +++ b/deployment/fedora-package-x64/package.sh @@ -19,7 +19,8 @@ pkg_src_dir="`pwd`/pkg-src" current_user="`whoami`" image_name="jellyfin-rpmbuild" docker_sudo="" -if ! $(id -Gn | grep -q 'docker') && [ ! ${EUID:-1000} -eq 0 ] && [ ! $USER == "root" ]; then +if ! $(id -Gn | grep -q 'docker') && [ ! ${EUID:-1000} -eq 0 ] && \ + [ ! $USER == "root" ] && ! $(echo "$OSTYPE" | grep -q "darwin"); then docker_sudo=sudo fi From 8191efb90cb94727ee8a7626d2226d1d1451f047 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 23 Jan 2019 20:08:50 +0100 Subject: [PATCH 13/13] Fix some analyzer warnings Some changes: * Don't omit braces * Fix culture sensitive string compare * Define accessibility functions I restricted myself to these 5 files, for now :p --- Jellyfin.Server/Program.cs | 10 +- Jellyfin.Server/SocketSharp/RequestMono.cs | 168 +++++++++++++++--- .../SocketSharp/WebSocketSharpListener.cs | 10 +- .../SocketSharp/WebSocketSharpRequest.cs | 64 +++++-- 4 files changed, 202 insertions(+), 50 deletions(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 0510548b56..c196a3f236 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -224,7 +224,7 @@ namespace Jellyfin.Server .GetManifestResourceStream("Jellyfin.Server.Resources.Configuration.logging.json")) using (Stream fstr = File.Open(configPath, FileMode.CreateNew)) { - await rscstr.CopyToAsync(fstr); + await rscstr.CopyToAsync(fstr).ConfigureAwait(false); } } var configuration = new ConfigurationBuilder() @@ -334,11 +334,9 @@ namespace Jellyfin.Server } else { - commandLineArgsString = string.Join(" ", - Environment.GetCommandLineArgs() - .Skip(1) - .Select(NormalizeCommandLineArgument) - ); + commandLineArgsString = string.Join( + " ", + Environment.GetCommandLineArgs().Skip(1).Select(NormalizeCommandLineArgument)); } _logger.LogInformation("Executable: {0}", module); diff --git a/Jellyfin.Server/SocketSharp/RequestMono.cs b/Jellyfin.Server/SocketSharp/RequestMono.cs index 45cb323d28..017690062e 100644 --- a/Jellyfin.Server/SocketSharp/RequestMono.cs +++ b/Jellyfin.Server/SocketSharp/RequestMono.cs @@ -15,19 +15,27 @@ namespace Jellyfin.SocketSharp { int ap = header.IndexOf(attr); if (ap == -1) + { return null; + } ap += attr.Length; if (ap >= header.Length) + { return null; + } char ending = header[ap]; if (ending != '"') + { ending = ' '; + } int end = header.IndexOf(ending, ap + 1); if (end == -1) + { return ending == '"' ? null : header.Substring(ap); + } return header.Substring(ap + 1, end - ap - 1); } @@ -36,7 +44,9 @@ namespace Jellyfin.SocketSharp { string boundary = GetParameter(ContentType, "; boundary="); if (boundary == null) + { return; + } using (var requestStream = InputStream) { @@ -124,7 +134,9 @@ namespace Jellyfin.SocketSharp { string v = "\"" + value + "\""; if (v.Length > 20) + { v = v.Substring(0, 16) + "...\""; + } string msg = string.Format("A potentially dangerous Request.{0} value was " + "detected from the client ({1}={2}).", name, key, v); @@ -135,21 +147,23 @@ namespace Jellyfin.SocketSharp static void ValidateNameValueCollection(string name, QueryParamCollection coll) { if (coll == null) + { return; + } foreach (var pair in coll) { var key = pair.Name; var val = pair.Value; if (val != null && val.Length > 0 && IsInvalidString(val)) + { ThrowValidationException(name, key, val); + } } } internal static bool IsInvalidString(string val) - { - return IsInvalidString(val, out var validationFailureIndex); - } + => IsInvalidString(val, out var validationFailureIndex); internal static bool IsInvalidString(string val, out int validationFailureIndex) { @@ -157,7 +171,9 @@ namespace Jellyfin.SocketSharp int len = val.Length; if (len < 2) + { return false; + } char current = val[0]; for (int idx = 1; idx < len; idx++) @@ -195,10 +211,15 @@ namespace Jellyfin.SocketSharp bool IsContentType(string ct, bool starts_with) { - if (ct == null || ContentType == null) return false; + if (ct == null || ContentType == null) + { + return false; + } if (starts_with) + { return StrUtils.StartsWith(ContentType, ct, true); + } return string.Equals(ContentType, ct, StringComparison.OrdinalIgnoreCase); } @@ -231,7 +252,9 @@ namespace Jellyfin.SocketSharp break; } else + { value.Append((char)c); + } } if (c == -1) { @@ -240,22 +263,26 @@ namespace Jellyfin.SocketSharp } } else if (c == '&') + { AddRawKeyValue(form, key, value); + } else + { key.Append((char)c); + } } if (c == -1) + { AddRawKeyValue(form, key, value); + } } } } } - void AddRawKeyValue(WebROCollection form, StringBuilder key, StringBuilder value) + static void AddRawKeyValue(WebROCollection form, StringBuilder key, StringBuilder value) { - string decodedKey = WebUtility.UrlDecode(key.ToString()); - form.Add(decodedKey, - WebUtility.UrlDecode(value.ToString())); + form.Add(WebUtility.UrlDecode(key.ToString()), WebUtility.UrlDecode(value.ToString())); key.Length = 0; value.Length = 0; @@ -271,7 +298,9 @@ namespace Jellyfin.SocketSharp foreach (var pair in this) { if (result.Length > 0) + { result.Append('&'); + } var key = pair.Name; if (key != null && key.Length > 0) @@ -314,33 +343,52 @@ namespace Jellyfin.SocketSharp public override int Read(byte[] buffer, int dest_offset, int count) { if (buffer == null) + { throw new ArgumentNullException(nameof(buffer)); + } if (dest_offset < 0) + { throw new ArgumentOutOfRangeException(nameof(dest_offset), "< 0"); + } if (count < 0) + { throw new ArgumentOutOfRangeException(nameof(count), "< 0"); + } int len = buffer.Length; if (dest_offset > len) + { throw new ArgumentException("destination offset is beyond array size"); + } + // reordered to avoid possible integer overflow if (dest_offset > len - count) + { throw new ArgumentException("Reading would overrun buffer"); + } if (count > end - position) + { count = (int)(end - position); + } if (count <= 0) + { return 0; + } s.Position = position; int result = s.Read(buffer, dest_offset, count); if (result > 0) + { position += result; + } else + { position = end; + } return result; } @@ -348,14 +396,20 @@ namespace Jellyfin.SocketSharp public override int ReadByte() { if (position >= end) + { return -1; + } s.Position = position; int result = s.ReadByte(); if (result < 0) + { position = end; + } else + { position++; + } return result; } @@ -380,7 +434,9 @@ namespace Jellyfin.SocketSharp long virt = real - offset; if (virt < 0 || virt > Length) + { throw new ArgumentException(); + } position = s.Seek(real, SeekOrigin.Begin); return position; @@ -410,7 +466,9 @@ namespace Jellyfin.SocketSharp set { if (value > Length) - throw new ArgumentOutOfRangeException(); + { + throw new ArgumentOutOfRangeException(nameof(value)); + } position = Seek(value, SeekOrigin.Begin); } @@ -438,7 +496,7 @@ namespace Jellyfin.SocketSharp public static readonly CultureInfo InvariantCulture = CultureInfo.InvariantCulture; } - internal sealed class StrUtils + internal static class StrUtils { public static bool StartsWith(string str1, string str2, bool ignore_case) { @@ -455,11 +513,15 @@ namespace Jellyfin.SocketSharp { int l2 = str2.Length; if (l2 == 0) + { return true; + } int l1 = str1.Length; if (l2 > l1) + { return false; + } var comparison = ignore_case ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; return str1.IndexOf(str2, comparison) == str1.Length - str2.Length - 1; @@ -493,7 +555,7 @@ namespace Jellyfin.SocketSharp Encoding encoding; StringBuilder sb; - const byte HYPHEN = (byte)'-', LF = (byte)'\n', CR = (byte)'\r'; + const byte LF = (byte)'\n', CR = (byte)'\r'; // See RFC 2046 // In the case of multipart entities, in which one or more different @@ -520,7 +582,7 @@ namespace Jellyfin.SocketSharp sb = new StringBuilder(); } - string ReadLine() + private string ReadLine() { // CRLF or LF are ok as line endings. bool got_cr = false; @@ -543,58 +605,86 @@ namespace Jellyfin.SocketSharp } if (got_cr) + { sb.Length--; + } return sb.ToString(); } - static string GetContentDispositionAttribute(string l, string name) + private static string GetContentDispositionAttribute(string l, string name) { int idx = l.IndexOf(name + "=\""); if (idx < 0) + { return null; + } + int begin = idx + name.Length + "=\"".Length; int end = l.IndexOf('"', begin); if (end < 0) + { return null; + } + if (begin == end) - return ""; + { + return string.Empty; + } + return l.Substring(begin, end - begin); } - string GetContentDispositionAttributeWithEncoding(string l, string name) + private string GetContentDispositionAttributeWithEncoding(string l, string name) { int idx = l.IndexOf(name + "=\""); if (idx < 0) + { return null; + } + int begin = idx + name.Length + "=\"".Length; int end = l.IndexOf('"', begin); if (end < 0) + { return null; + } + if (begin == end) - return ""; + { + return string.Empty; + } string temp = l.Substring(begin, end - begin); byte[] source = new byte[temp.Length]; for (int i = temp.Length - 1; i >= 0; i--) + { source[i] = (byte)temp[i]; + } return encoding.GetString(source, 0, source.Length); } - bool ReadBoundary() + private bool ReadBoundary() { try { string line = ReadLine(); - while (line == "") + while (line == string.Empty) + { line = ReadLine(); + } + if (line[0] != '-' || line[1] != '-') + { return false; + } if (!StrUtils.EndsWith(line, boundary, false)) + { return true; + } } catch { @@ -603,25 +693,31 @@ namespace Jellyfin.SocketSharp return false; } - string ReadHeaders() + private string ReadHeaders() { string s = ReadLine(); - if (s == "") + if (s.Length == 0) + { return null; + } return s; } - bool CompareBytes(byte[] orig, byte[] other) + private static bool CompareBytes(byte[] orig, byte[] other) { for (int i = orig.Length - 1; i >= 0; i--) + { if (orig[i] != other[i]) + { return false; + } + } return true; } - long MoveToNextBoundary() + private long MoveToNextBoundary() { long retval = 0; bool got_cr = false; @@ -631,13 +727,18 @@ namespace Jellyfin.SocketSharp while (true) { if (c == -1) + { return -1; + } if (state == 0 && c == LF) { retval = data.Position - 1; if (got_cr) + { retval--; + } + state = 1; c = data.ReadByte(); } @@ -650,7 +751,9 @@ namespace Jellyfin.SocketSharp { c = data.ReadByte(); if (c == -1) + { return -1; + } if (c != '-') { @@ -662,7 +765,9 @@ namespace Jellyfin.SocketSharp int nread = data.Read(buffer, 0, buffer.Length); int bl = buffer.Length; if (nread != bl) + { return -1; + } if (!CompareBytes(boundary_bytes, buffer)) { @@ -673,6 +778,7 @@ namespace Jellyfin.SocketSharp data.Position++; got_cr = false; } + c = data.ReadByte(); continue; } @@ -690,12 +796,16 @@ namespace Jellyfin.SocketSharp data.Position++; got_cr = false; } + c = data.ReadByte(); continue; } data.Position = retval + 2; if (got_cr) + { data.Position++; + } + break; } else @@ -711,7 +821,9 @@ namespace Jellyfin.SocketSharp public Element ReadNextElement() { if (at_eof || ReadBoundary()) + { return null; + } var elem = new Element(); string header; @@ -734,19 +846,27 @@ namespace Jellyfin.SocketSharp elem.Start = start; long pos = MoveToNextBoundary(); if (pos == -1) + { return null; + } elem.Length = pos - start; return elem; } - static string StripPath(string path) + private static string StripPath(string path) { if (path == null || path.Length == 0) + { return path; + } - if (path.IndexOf(":\\") != 1 && !path.StartsWith("\\\\")) + if (path.IndexOf(":\\", StringComparison.Ordinal) != 1 + && !path.StartsWith("\\\\", StringComparison.Ordinal)) + { return path; + } + return path.Substring(path.LastIndexOf('\\') + 1); } } diff --git a/Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs index ef75644d75..c7f9f01b5d 100644 --- a/Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs +++ b/Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs @@ -83,15 +83,15 @@ namespace Jellyfin.SocketSharp private void ProcessContext(HttpListenerContext context) { - //InitTask(context, _disposeCancellationToken); - Task.Run(() => InitTask(context, _disposeCancellationToken)); + var _ = Task.Run(async () => await InitTask(context, _disposeCancellationToken)); } - private void LogRequest(ILogger logger, HttpListenerRequest request) + private static void LogRequest(ILogger logger, HttpListenerRequest request) { var url = request.Url.ToString(); - logger.LogInformation("{0} {1}. UserAgent: {2}", request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty); + logger.LogInformation("{0} {1}. UserAgent: {2}", + request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty); } private Task InitTask(HttpListenerContext context, CancellationToken cancellationToken) @@ -196,7 +196,7 @@ namespace Jellyfin.SocketSharp { try { - ctx.Response.StatusCode = 200; + ctx.Response.StatusCode = statusCode; ctx.Response.Close(); } catch (ObjectDisposedException) diff --git a/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs index e38468388b..97550e686c 100644 --- a/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs +++ b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs @@ -242,7 +242,6 @@ namespace Jellyfin.SocketSharp return request.ContentType.StartsWith(contentType, StringComparison.OrdinalIgnoreCase); } - public const string Xml = "application/xml"; private static string GetQueryStringContentType(IRequest httpReq) { var format = httpReq.QueryString["format"]; @@ -250,22 +249,40 @@ namespace Jellyfin.SocketSharp { const int formatMaxLength = 4; var pi = httpReq.PathInfo; - if (pi == null || pi.Length <= formatMaxLength) return null; - if (pi[0] == '/') pi = pi.Substring(1); + if (pi == null || pi.Length <= formatMaxLength) + { + return null; + } + if (pi[0] == '/') + { + pi = pi.Substring(1); + } format = LeftPart(pi, '/'); - if (format.Length > formatMaxLength) return null; + if (format.Length > formatMaxLength) + { + return null; + } } format = LeftPart(format, '.').ToLower(); - if (format.Contains("json")) return "application/json"; - if (format.Contains("xml")) return Xml; + if (format.Contains("json", StringComparison.OrdinalIgnoreCase)) + { + return "application/json"; + } + if (format.Contains("xml", StringComparison.OrdinalIgnoreCase)) + { + return "application/xml"; + } return null; } public static string LeftPart(string strVal, char needle) { - if (strVal == null) return null; + if (strVal == null) + { + return null; + } var pos = strVal.IndexOf(needle); return pos == -1 ? strVal @@ -283,14 +300,14 @@ namespace Jellyfin.SocketSharp { var mode = HandlerFactoryPath; - var pos = request.RawUrl.IndexOf("?"); + var pos = request.RawUrl.IndexOf("?", StringComparison.Ordinal); if (pos != -1) { var path = request.RawUrl.Substring(0, pos); this.pathInfo = GetPathInfo( path, mode, - mode ?? ""); + mode ?? string.Empty); } else { @@ -307,18 +324,27 @@ namespace Jellyfin.SocketSharp private static string GetPathInfo(string fullPath, string mode, string appPath) { var pathInfo = ResolvePathInfoFromMappedPath(fullPath, mode); - if (!string.IsNullOrEmpty(pathInfo)) return pathInfo; + if (!string.IsNullOrEmpty(pathInfo)) + { + return pathInfo; + } //Wildcard mode relies on this to work out the handlerPath pathInfo = ResolvePathInfoFromMappedPath(fullPath, appPath); - if (!string.IsNullOrEmpty(pathInfo)) return pathInfo; + if (!string.IsNullOrEmpty(pathInfo)) + { + return pathInfo; + } return fullPath; } private static string ResolvePathInfoFromMappedPath(string fullPath, string mappedPathRoot) { - if (mappedPathRoot == null) return null; + if (mappedPathRoot == null) + { + return null; + } var sbPathInfo = new StringBuilder(); var fullPathParts = fullPath.Split('/'); @@ -345,7 +371,10 @@ namespace Jellyfin.SocketSharp } } } - if (!pathRootFound) return null; + if (!pathRootFound) + { + return null; + } var path = sbPathInfo.ToString(); return path.Length > 1 ? path.TrimEnd('/') : "/"; @@ -400,7 +429,10 @@ namespace Jellyfin.SocketSharp public static Encoding GetEncoding(string contentTypeHeader) { var param = GetParameter(contentTypeHeader, "charset="); - if (param == null) return null; + if (param == null) + { + return null; + } try { return Encoding.GetEncoding(param); @@ -423,7 +455,9 @@ namespace Jellyfin.SocketSharp if (httpFiles == null) { if (files == null) - return httpFiles = new IHttpFile[0]; + { + return httpFiles = Array.Empty(); + } httpFiles = new IHttpFile[files.Count]; var i = 0;