From 64d5ec12e260758c4a7b3e9df85212b51dab8444 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 8 Feb 2019 22:59:28 +0100 Subject: [PATCH 01/11] Use HashSets for increased perf --- .../Data/BaseSqliteRepository.cs | 10 +- .../Data/SqliteItemRepository.cs | 183 +++++++++--------- .../Library/LibraryManager.cs | 20 +- .../Library/UserManager.cs | 4 +- MediaBrowser.Api/BaseApiService.cs | 32 ++- MediaBrowser.Api/UserLibrary/ItemsService.cs | 9 +- 6 files changed, 128 insertions(+), 130 deletions(-) diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 0f432c36c0..a6be0da587 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -375,21 +375,15 @@ namespace Emby.Server.Implementations.Data } } - public class DummyToken : IDisposable - { - public void Dispose() - { - } - } - public static IDisposable Read(this ReaderWriterLockSlim obj) { //if (BaseSqliteRepository.ThreadSafeMode > 0) //{ // return new DummyToken(); //} - return new WriteLockToken(obj); + return new ReadLockToken(obj); } + public static IDisposable Write(this ReaderWriterLockSlim obj) { //if (BaseSqliteRepository.ThreadSafeMode > 0) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 3014e482df..a0abd6ea0d 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -1183,9 +1183,9 @@ namespace Emby.Server.Implementations.Data /// public BaseItem RetrieveItem(Guid id) { - if (id.Equals(Guid.Empty)) + if (id == Guid.Empty) { - throw new ArgumentNullException(nameof(id)); + throw new ArgumentException(nameof(id), "Guid can't be empty"); } CheckDisposed(); @@ -2079,14 +2079,14 @@ namespace Emby.Server.Implementations.Data return false; } - var sortingFields = query.OrderBy.Select(i => i.Item1); + var sortingFields = new HashSet(query.OrderBy.Select(i => i.Item1), StringComparer.OrdinalIgnoreCase); - return sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase) - || sortingFields.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase) - || sortingFields.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase) - || sortingFields.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase) - || sortingFields.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase) - || sortingFields.Contains(ItemSortBy.SeriesDatePlayed, StringComparer.OrdinalIgnoreCase) + return sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked) + || sortingFields.Contains(ItemSortBy.IsPlayed) + || sortingFields.Contains(ItemSortBy.IsUnplayed) + || sortingFields.Contains(ItemSortBy.PlayCount) + || sortingFields.Contains(ItemSortBy.DatePlayed) + || sortingFields.Contains(ItemSortBy.SeriesDatePlayed) || query.IsFavoriteOrLiked.HasValue || query.IsFavorite.HasValue || query.IsResumable.HasValue @@ -2151,18 +2151,26 @@ namespace Emby.Server.Implementations.Data } } - private bool HasProgramAttributes(InternalItemsQuery query) + private static readonly HashSet _programExcludeParentTypes = new HashSet(StringComparer.OrdinalIgnoreCase) { - var excludeParentTypes = new string[] - { - "Series", - "Season", - "MusicAlbum", - "MusicArtist", - "PhotoAlbum" - }; + "Series", + "Season", + "MusicAlbum", + "MusicArtist", + "PhotoAlbum" + }; - if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + private static readonly HashSet _programTypes = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "Program", + "TvChannel", + "LiveTvProgram", + "LiveTvTvChannel" + }; + + private bool HasProgramAttributes(InternalItemsQuery query) + { + if (_programExcludeParentTypes.Contains(query.ParentType)) { return false; } @@ -2172,29 +2180,18 @@ namespace Emby.Server.Implementations.Data return true; } - var types = new string[] - { - "Program", - "TvChannel", - "LiveTvProgram", - "LiveTvTvChannel" - }; - - return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase)); + return query.IncludeItemTypes.Any(x => _programTypes.Contains(x)); } - private bool HasServiceName(InternalItemsQuery query) + private static readonly HashSet _serviceTypes = new HashSet(StringComparer.OrdinalIgnoreCase) { - var excludeParentTypes = new string[] - { - "Series", - "Season", - "MusicAlbum", - "MusicArtist", - "PhotoAlbum" - }; + "TvChannel", + "LiveTvTvChannel" + }; - if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + private bool HasServiceName(InternalItemsQuery query) + { + if (_programExcludeParentTypes.Contains(query.ParentType)) { return false; } @@ -2204,27 +2201,18 @@ namespace Emby.Server.Implementations.Data return true; } - var types = new string[] - { - "TvChannel", - "LiveTvTvChannel" - }; - - return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase)); + return query.IncludeItemTypes.Any(x => _serviceTypes.Contains(x)); } - private bool HasStartDate(InternalItemsQuery query) + private static readonly HashSet _startDateTypes = new HashSet(StringComparer.OrdinalIgnoreCase) { - var excludeParentTypes = new string[] - { - "Series", - "Season", - "MusicAlbum", - "MusicArtist", - "PhotoAlbum" - }; + "Program", + "LiveTvProgram" + }; - if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + private bool HasStartDate(InternalItemsQuery query) + { + if (_programExcludeParentTypes.Contains(query.ParentType)) { return false; } @@ -2234,13 +2222,7 @@ namespace Emby.Server.Implementations.Data return true; } - var types = new string[] - { - "Program", - "LiveTvProgram" - }; - - return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase)); + return query.IncludeItemTypes.Any(x => _startDateTypes.Contains(x)); } private bool HasEpisodeAttributes(InternalItemsQuery query) @@ -2263,16 +2245,26 @@ namespace Emby.Server.Implementations.Data return query.IncludeItemTypes.Contains("Trailer", StringComparer.OrdinalIgnoreCase); } - private bool HasArtistFields(InternalItemsQuery query) + + private static readonly HashSet _artistExcludeParentTypes = new HashSet(StringComparer.OrdinalIgnoreCase) { - var excludeParentTypes = new string[] - { - "Series", - "Season", - "PhotoAlbum" - }; + "Series", + "Season", + "PhotoAlbum" + }; - if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + private static readonly HashSet _artistsTypes = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "Audio", + "MusicAlbum", + "MusicVideo", + "AudioBook", + "AudioPodcast" + }; + + private bool HasArtistFields(InternalItemsQuery query) + { + if (_artistExcludeParentTypes.Contains(query.ParentType)) { return false; } @@ -2282,18 +2274,18 @@ namespace Emby.Server.Implementations.Data return true; } - var types = new string[] - { - "Audio", - "MusicAlbum", - "MusicVideo", - "AudioBook", - "AudioPodcast" - }; - - return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase)); + return query.IncludeItemTypes.Any(x => _artistsTypes.Contains(x)); } + private static readonly HashSet _seriesTypes = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "Audio", + "MusicAlbum", + "MusicVideo", + "AudioBook", + "AudioPodcast" + }; + private bool HasSeriesFields(InternalItemsQuery query) { if (string.Equals(query.ParentType, "PhotoAlbum", StringComparison.OrdinalIgnoreCase)) @@ -2306,15 +2298,7 @@ namespace Emby.Server.Implementations.Data return true; } - var types = new string[] - { - "Book", - "AudioBook", - "Episode", - "Season" - }; - - return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase)); + return query.IncludeItemTypes.Any(x => _seriesTypes.Contains(x)); } private string[] GetFinalColumnsToSelect(InternalItemsQuery query, string[] startColumns) @@ -2325,7 +2309,7 @@ namespace Emby.Server.Implementations.Data { if (!HasField(query, field)) { - foreach (var fieldToRemove in GetColumnNamesFromField(field).ToList()) + foreach (var fieldToRemove in GetColumnNamesFromField(field)) { list.Remove(fieldToRemove); } @@ -2419,11 +2403,14 @@ namespace Emby.Server.Implementations.Data list.Add(builder.ToString()); - var excludeIds = query.ExcludeItemIds.ToList(); - excludeIds.Add(item.Id); - excludeIds.AddRange(item.ExtraIds); + var oldLen = query.ExcludeItemIds.Length; + var newLen = oldLen + item.ExtraIds.Length + 1; + var excludeIds = new Guid[newLen]; + query.ExcludeItemIds.CopyTo(excludeIds, 0); + excludeIds[oldLen] = item.Id; + item.ExtraIds.CopyTo(excludeIds, oldLen + 1); - query.ExcludeItemIds = excludeIds.ToArray(); + query.ExcludeItemIds = excludeIds; query.ExcludeProviderIds = item.ProviderIds; } @@ -2735,6 +2722,7 @@ namespace Emby.Server.Implementations.Data { continue; } + if (item.GetProviderId(providerId.Key) == providerId.Value) { if (newItem.SourceType == SourceType.Library) @@ -4952,7 +4940,12 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type return result; } - return new[] { value }.Where(IsValidType); + if (IsValidType(value)) + { + return new[] { value }; + } + + return Array.Empty(); } public void DeleteItem(Guid id, CancellationToken cancellationToken) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 064006ebd6..6218d3a086 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1225,9 +1225,9 @@ namespace Emby.Server.Implementations.Library /// id public BaseItem GetItemById(Guid id) { - if (id.Equals(Guid.Empty)) + if (id == Guid.Empty) { - throw new ArgumentNullException(nameof(id)); + throw new ArgumentException(nameof(id), "Guid can't be empty"); } if (LibraryItemsCache.TryGetValue(id, out BaseItem item)) @@ -1237,8 +1237,6 @@ namespace Emby.Server.Implementations.Library item = RetrieveItem(id); - //_logger.LogDebug("GetitemById {0}", id); - if (item != null) { RegisterItem(item); @@ -2005,9 +2003,7 @@ namespace Emby.Server.Implementations.Library .FirstOrDefault(); } - var options = collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions(); - - return options; + return collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions(); } public string GetContentType(BaseItem item) @@ -2017,11 +2013,13 @@ namespace Emby.Server.Implementations.Library { return configuredContentType; } + configuredContentType = GetConfiguredContentType(item, true); if (!string.IsNullOrEmpty(configuredContentType)) { return configuredContentType; } + return GetInheritedContentType(item); } @@ -2056,6 +2054,7 @@ namespace Emby.Server.Implementations.Library { return collectionFolder.CollectionType; } + return GetContentTypeOverride(item.ContainingFolderPath, inheritConfiguredPath); } @@ -2066,6 +2065,7 @@ namespace Emby.Server.Implementations.Library { return nameValuePair.Value; } + return null; } @@ -2108,9 +2108,9 @@ namespace Emby.Server.Implementations.Library string viewType, string sortName) { - var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views"); - - path = Path.Combine(path, _fileSystem.GetValidFilename(viewType)); + var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, + "views", + _fileSystem.GetValidFilename(viewType)); var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView)); diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 3ff84382f2..22a593d494 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -171,9 +171,9 @@ namespace Emby.Server.Implementations.Library /// public User GetUserById(Guid id) { - if (id.Equals(Guid.Empty)) + if (id == Guid.Empty) { - throw new ArgumentNullException(nameof(id)); + throw new ArgumentException(nameof(id), "Guid can't be empty"); } return Users.FirstOrDefault(u => u.Id == id); diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 451ee72dd9..a037357ede 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -9,6 +9,7 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Services; +using MediaBrowser.Model.Querying; using Microsoft.Extensions.Logging; namespace MediaBrowser.Api @@ -118,8 +119,7 @@ namespace MediaBrowser.Api { var options = new DtoOptions(); - var hasFields = request as IHasItemFields; - if (hasFields != null) + if (request is IHasItemFields hasFields) { options.Fields = hasFields.GetItemFields(); } @@ -133,9 +133,11 @@ namespace MediaBrowser.Api client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 || client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1) { - var list = options.Fields.ToList(); - list.Add(Model.Querying.ItemFields.RecursiveItemCount); - options.Fields = list.ToArray(); + int oldLen = options.Fields.Length; + var arr = new ItemFields[oldLen + 1]; + options.Fields.CopyTo(arr, 0); + arr[oldLen] = Model.Querying.ItemFields.RecursiveItemCount; + options.Fields = arr; } if (client.IndexOf("kodi", StringComparison.OrdinalIgnoreCase) != -1 || @@ -146,9 +148,12 @@ namespace MediaBrowser.Api client.IndexOf("samsung", StringComparison.OrdinalIgnoreCase) != -1 || client.IndexOf("androidtv", StringComparison.OrdinalIgnoreCase) != -1) { - var list = options.Fields.ToList(); - list.Add(Model.Querying.ItemFields.ChildCount); - options.Fields = list.ToArray(); + + int oldLen = options.Fields.Length; + var arr = new ItemFields[oldLen + 1]; + options.Fields.CopyTo(arr, 0); + arr[oldLen] = Model.Querying.ItemFields.ChildCount; + options.Fields = arr; } } @@ -167,7 +172,16 @@ namespace MediaBrowser.Api if (!string.IsNullOrWhiteSpace(hasDtoOptions.EnableImageTypes)) { - options.ImageTypes = (hasDtoOptions.EnableImageTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)).ToArray(); + if (string.IsNullOrEmpty(hasDtoOptions.EnableImageTypes)) + { + options.ImageTypes = Array.Empty(); + } + else + { + options.ImageTypes = hasDtoOptions.EnableImageTypes.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)) + .ToArray(); + } } } diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 3ae7da0073..46d7c250e8 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -155,7 +155,7 @@ namespace MediaBrowser.Api.UserLibrary /// The request. private QueryResult GetItems(GetItems request) { - var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null; + var user = request.UserId == Guid.Empty ? null : _userManager.GetUserById(request.UserId); var dtoOptions = GetDtoOptions(_authContext, request); @@ -190,11 +190,8 @@ namespace MediaBrowser.Api.UserLibrary /// private QueryResult GetQueryResult(GetItems request, DtoOptions dtoOptions, User user) { - if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase)) - { - request.ParentId = null; - } - else if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase) + || string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase)) { request.ParentId = null; } From 41fb1e510616e42490354b8912d2117b836ab822 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 9 Feb 2019 00:48:09 +0100 Subject: [PATCH 02/11] Tuple -> ValueTuple --- .../Data/BaseSqliteRepository.cs | 21 ++--------- .../Data/SqliteItemRepository.cs | 35 ++++++++----------- .../Library/LibraryManager.cs | 12 +++---- Jellyfin.Server/Jellyfin.Server.csproj | 2 +- .../UserLibrary/ArtistsService.cs | 2 +- .../UserLibrary/BaseItemsByNameService.cs | 4 +-- MediaBrowser.Api/UserLibrary/GenresService.cs | 2 +- .../UserLibrary/MusicGenresService.cs | 2 +- .../UserLibrary/PersonsService.cs | 6 ++-- .../UserLibrary/StudiosService.cs | 2 +- .../Library/ILibraryManager.cs | 12 +++---- .../Persistence/IItemRepository.cs | 12 +++---- 12 files changed, 45 insertions(+), 67 deletions(-) diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index a6be0da587..556b7a547d 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -224,7 +224,7 @@ namespace Emby.Server.Implementations.Data }); } - db.ExecuteAll(string.Join(";", queries.ToArray())); + db.ExecuteAll(string.Join(";", queries)); Logger.LogInformation("PRAGMA synchronous=" + db.Query("PRAGMA synchronous").SelectScalarString().First()); } @@ -232,23 +232,6 @@ namespace Emby.Server.Implementations.Data protected virtual int? CacheSize => null; - internal static void CheckOk(int rc) - { - string msg = ""; - - if (raw.SQLITE_OK != rc) - { - throw CreateException((ErrorCode)rc, msg); - } - } - - internal static Exception CreateException(ErrorCode rc, string msg) - { - var exp = new Exception(msg); - - return exp; - } - private bool _disposed; protected void CheckDisposed() { @@ -381,7 +364,7 @@ namespace Emby.Server.Implementations.Data //{ // return new DummyToken(); //} - return new ReadLockToken(obj); + return new WriteLockToken(obj); // TODO: fix segfault } public static IDisposable Write(this ReaderWriterLockSlim obj) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index a0abd6ea0d..6a346f31f6 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2301,7 +2301,7 @@ namespace Emby.Server.Implementations.Data return query.IncludeItemTypes.Any(x => _seriesTypes.Contains(x)); } - private string[] GetFinalColumnsToSelect(InternalItemsQuery query, string[] startColumns) + private List GetFinalColumnsToSelect(InternalItemsQuery query, IEnumerable startColumns) { var list = startColumns.ToList(); @@ -2431,7 +2431,7 @@ namespace Emby.Server.Implementations.Data list.Add(builder.ToString()); } - return list.ToArray(); + return list; } private void BindSearchParams(InternalItemsQuery query, IStatement statement) @@ -5208,32 +5208,32 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type } } - public QueryResult> GetAllArtists(InternalItemsQuery query) + public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query) { return GetItemValues(query, new[] { 0, 1 }, typeof(MusicArtist).FullName); } - public QueryResult> GetArtists(InternalItemsQuery query) + public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query) { return GetItemValues(query, new[] { 0 }, typeof(MusicArtist).FullName); } - public QueryResult> GetAlbumArtists(InternalItemsQuery query) + public QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query) { return GetItemValues(query, new[] { 1 }, typeof(MusicArtist).FullName); } - public QueryResult> GetStudios(InternalItemsQuery query) + public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query) { return GetItemValues(query, new[] { 3 }, typeof(Studio).FullName); } - public QueryResult> GetGenres(InternalItemsQuery query) + public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query) { return GetItemValues(query, new[] { 2 }, typeof(Genre).FullName); } - public QueryResult> GetMusicGenres(InternalItemsQuery query) + public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query) { return GetItemValues(query, new[] { 2 }, typeof(MusicGenre).FullName); } @@ -5310,7 +5310,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type } } - private QueryResult> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType) + private QueryResult<(BaseItem, ItemCounts)> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType) { if (query == null) { @@ -5328,7 +5328,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type var typeClause = itemValueTypes.Length == 1 ? ("Type=" + itemValueTypes[0].ToString(CultureInfo.InvariantCulture)) : - ("Type in (" + string.Join(",", itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray()) + ")"); + ("Type in (" + string.Join(",", itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture))) + ")"); InternalItemsQuery typeSubQuery = null; @@ -5356,11 +5356,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type whereClauses.Add("guid in (select ItemId from ItemValues where ItemValues.CleanValue=A.CleanName AND " + typeClause + ")"); - var typeWhereText = whereClauses.Count == 0 ? - string.Empty : - " where " + string.Join(" AND ", whereClauses); - - itemCountColumnQuery += typeWhereText; + itemCountColumnQuery += " where " + string.Join(" AND ", whereClauses); itemCountColumns = new Dictionary() { @@ -5393,7 +5389,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type IsSeries = query.IsSeries }; - columns = GetFinalColumnsToSelect(query, columns.ToArray()).ToList(); + columns = GetFinalColumnsToSelect(query, columns); var commandText = "select " + string.Join(",", columns) @@ -5485,8 +5481,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type { return connection.RunInTransaction(db => { - var list = new List>(); - var result = new QueryResult>(); + var list = new List<(BaseItem, ItemCounts)>(); + var result = new QueryResult<(BaseItem, ItemCounts)>(); var statements = PrepareAllSafe(db, statementTexts); @@ -5524,7 +5520,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type { var countStartColumn = columns.Count - 1; - list.Add(new Tuple(item, GetItemCounts(row, countStartColumn, typesToCount))); + list.Add((item, GetItemCounts(row, countStartColumn, typesToCount))); } } @@ -6191,6 +6187,5 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type return item; } - } } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 6218d3a086..e55f885504 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1331,7 +1331,7 @@ namespace Emby.Server.Implementations.Library return ItemRepository.GetItemIdsList(query); } - public QueryResult> GetStudios(InternalItemsQuery query) + public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query) { if (query.User != null) { @@ -1342,7 +1342,7 @@ namespace Emby.Server.Implementations.Library return ItemRepository.GetStudios(query); } - public QueryResult> GetGenres(InternalItemsQuery query) + public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query) { if (query.User != null) { @@ -1353,7 +1353,7 @@ namespace Emby.Server.Implementations.Library return ItemRepository.GetGenres(query); } - public QueryResult> GetMusicGenres(InternalItemsQuery query) + public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query) { if (query.User != null) { @@ -1364,7 +1364,7 @@ namespace Emby.Server.Implementations.Library return ItemRepository.GetMusicGenres(query); } - public QueryResult> GetAllArtists(InternalItemsQuery query) + public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query) { if (query.User != null) { @@ -1375,7 +1375,7 @@ namespace Emby.Server.Implementations.Library return ItemRepository.GetAllArtists(query); } - public QueryResult> GetArtists(InternalItemsQuery query) + public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query) { if (query.User != null) { @@ -1419,7 +1419,7 @@ namespace Emby.Server.Implementations.Library } } - public QueryResult> GetAlbumArtists(InternalItemsQuery query) + public QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query) { if (query.User != null) { diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index b1515df439..885709b0c2 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -3,7 +3,7 @@ jellyfin Exe - netcoreapp2.1 + netcoreapp3.0 false diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs index 651da1939d..7a8455ff26 100644 --- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs +++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs @@ -112,7 +112,7 @@ namespace MediaBrowser.Api.UserLibrary return ToOptimizedResult(result); } - protected override QueryResult> GetItems(GetItemsByName request, InternalItemsQuery query) + protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query) { if (request is GetAlbumArtists) { diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index 471b411279..e3c9ae58e7 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -209,9 +209,9 @@ namespace MediaBrowser.Api.UserLibrary }; } - protected virtual QueryResult> GetItems(GetItemsByName request, InternalItemsQuery query) + protected virtual QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query) { - return new QueryResult>(); + return new QueryResult<(BaseItem, ItemCounts)>(); } private void SetItemCounts(BaseItemDto dto, ItemCounts counts) diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs index baf570d50f..0c04d02dd8 100644 --- a/MediaBrowser.Api/UserLibrary/GenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GenresService.cs @@ -92,7 +92,7 @@ namespace MediaBrowser.Api.UserLibrary return ToOptimizedResult(result); } - protected override QueryResult> GetItems(GetItemsByName request, InternalItemsQuery query) + protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query) { var viewType = GetParentItemViewType(request); diff --git a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs index 4fcc3aa53f..94f5262b08 100644 --- a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs +++ b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs @@ -83,7 +83,7 @@ namespace MediaBrowser.Api.UserLibrary return ToOptimizedResult(result); } - protected override QueryResult> GetItems(GetItemsByName request, InternalItemsQuery query) + protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query) { return LibraryManager.GetMusicGenres(query); } diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs index d317f9f387..c264577788 100644 --- a/MediaBrowser.Api/UserLibrary/PersonsService.cs +++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs @@ -101,7 +101,7 @@ namespace MediaBrowser.Api.UserLibrary throw new NotImplementedException(); } - protected override QueryResult> GetItems(GetItemsByName request, InternalItemsQuery query) + protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query) { var items = LibraryManager.GetPeopleItems(new InternalPeopleQuery { @@ -109,10 +109,10 @@ namespace MediaBrowser.Api.UserLibrary NameContains = query.NameContains ?? query.SearchTerm }); - return new QueryResult> + return new QueryResult<(BaseItem, ItemCounts)> { TotalRecordCount = items.Count, - Items = items.Take(query.Limit ?? int.MaxValue).Select(i => new Tuple(i, new ItemCounts())).ToArray() + Items = items.Take(query.Limit ?? int.MaxValue).Select(i => (i as BaseItem, new ItemCounts())).ToArray() }; } diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs index 4e2483a564..890acc9311 100644 --- a/MediaBrowser.Api/UserLibrary/StudiosService.cs +++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs @@ -91,7 +91,7 @@ namespace MediaBrowser.Api.UserLibrary return ToOptimizedResult(result); } - protected override QueryResult> GetItems(GetItemsByName request, InternalItemsQuery query) + protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query) { return LibraryManager.GetStudios(query); } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 60c183d041..ab1f5a6b5e 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -520,12 +520,12 @@ namespace MediaBrowser.Controller.Library void UpdateMediaPath(string virtualFolderName, MediaPathInfo path); void RemoveMediaPath(string virtualFolderName, string path); - QueryResult> GetGenres(InternalItemsQuery query); - QueryResult> GetMusicGenres(InternalItemsQuery query); - QueryResult> GetStudios(InternalItemsQuery query); - QueryResult> GetArtists(InternalItemsQuery query); - QueryResult> GetAlbumArtists(InternalItemsQuery query); - QueryResult> GetAllArtists(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query); int GetCount(InternalItemsQuery query); diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 5156fce11e..3cb0b76395 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -141,12 +141,12 @@ namespace MediaBrowser.Controller.Persistence int GetCount(InternalItemsQuery query); - QueryResult> GetGenres(InternalItemsQuery query); - QueryResult> GetMusicGenres(InternalItemsQuery query); - QueryResult> GetStudios(InternalItemsQuery query); - QueryResult> GetArtists(InternalItemsQuery query); - QueryResult> GetAlbumArtists(InternalItemsQuery query); - QueryResult> GetAllArtists(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query); + QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query); List GetMusicGenreNames(); List GetStudioNames(); From 3e6819c718a45b44eb3f84439d118ad849ccffa8 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 9 Feb 2019 00:57:58 +0100 Subject: [PATCH 03/11] Don't clone lists --- .../Data/SqliteItemRepository.cs | 4 ++-- .../Library/LibraryManager.cs | 12 +++++------- MediaBrowser.Controller/Library/ILibraryManager.cs | 2 +- .../Persistence/IItemRepository.cs | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 6a346f31f6..c7bcb6fa3e 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -536,7 +536,7 @@ namespace Emby.Server.Implementations.Data throw new ArgumentNullException(nameof(item)); } - SaveItems(new List { item }, cancellationToken); + SaveItems(new [] { item }, cancellationToken); } public void SaveImages(BaseItem item) @@ -576,7 +576,7 @@ namespace Emby.Server.Implementations.Data /// or /// cancellationToken /// - public void SaveItems(List items, CancellationToken cancellationToken) + public void SaveItems(IEnumerable items, CancellationToken cancellationToken) { if (items == null) { diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index e55f885504..378693a43f 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1806,18 +1806,16 @@ namespace Emby.Server.Implementations.Library /// Task. public void CreateItems(IEnumerable items, BaseItem parent, CancellationToken cancellationToken) { - var list = items.ToList(); - - ItemRepository.SaveItems(list, cancellationToken); + ItemRepository.SaveItems(items, cancellationToken); - foreach (var item in list) + foreach (var item in items) { RegisterItem(item); } if (ItemAdded != null) { - foreach (var item in list) + foreach (var item in items) { // With the live tv guide this just creates too much noise if (item.SourceType != SourceType.Library) @@ -1851,7 +1849,7 @@ namespace Emby.Server.Implementations.Library /// /// Updates the item. /// - public void UpdateItems(List items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) + public void UpdateItems(IEnumerable items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) { foreach (var item in items) { @@ -1906,7 +1904,7 @@ namespace Emby.Server.Implementations.Library /// Task. public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) { - UpdateItems(new List { item }, parent, updateReason, cancellationToken); + UpdateItems(new [] { item }, parent, updateReason, cancellationToken); } /// diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index ab1f5a6b5e..511356aa4e 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -193,7 +193,7 @@ namespace MediaBrowser.Controller.Library /// /// Updates the item. /// - void UpdateItems(List items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); + void UpdateItems(IEnumerable items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); /// diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 3cb0b76395..47e0f34532 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -32,7 +32,7 @@ namespace MediaBrowser.Controller.Persistence /// /// The items. /// The cancellation token. - void SaveItems(List items, CancellationToken cancellationToken); + void SaveItems(IEnumerable items, CancellationToken cancellationToken); void SaveImages(BaseItem item); From 7722cb3ffab5b8330b34fb27608ba9ff35f6ce81 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 9 Feb 2019 01:09:22 +0100 Subject: [PATCH 04/11] Some Lists -> IEnumerable --- .../Data/SqliteItemRepository.cs | 12 ++++++------ MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs | 8 +++----- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index c7bcb6fa3e..49a510a22c 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -587,7 +587,7 @@ namespace Emby.Server.Implementations.Data CheckDisposed(); - var tuples = new List, BaseItem, string, List>>(); + var tuples = new List<(BaseItem, List, BaseItem, string, List)>(); foreach (var item in items) { var ancestorIds = item.SupportsAncestors ? @@ -599,7 +599,7 @@ namespace Emby.Server.Implementations.Data var userdataKey = item.GetUserDataKeys().FirstOrDefault(); var inheritedTags = item.GetInheritedTags(); - tuples.Add(new Tuple, BaseItem, string, List>(item, ancestorIds, topParent, userdataKey, inheritedTags)); + tuples.Add((item, ancestorIds, topParent, userdataKey, inheritedTags)); } using (WriteLock.Write()) @@ -615,7 +615,7 @@ namespace Emby.Server.Implementations.Data } } - private void SaveItemsInTranscation(IDatabaseConnection db, List, BaseItem, string, List>> tuples) + private void SaveItemsInTranscation(IDatabaseConnection db, IEnumerable<(BaseItem, List, BaseItem, string, List)> tuples) { var statements = PrepareAllSafe(db, new string[] { @@ -2094,9 +2094,9 @@ namespace Emby.Server.Implementations.Data || query.IsLiked.HasValue; } - private readonly List allFields = Enum.GetNames(typeof(ItemFields)) + private readonly ItemFields[] _allFields = Enum.GetNames(typeof(ItemFields)) .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) - .ToList(); + .ToArray(); private string[] GetColumnNamesFromField(ItemFields field) { @@ -2305,7 +2305,7 @@ namespace Emby.Server.Implementations.Data { var list = startColumns.ToList(); - foreach (var field in allFields) + foreach (var field in _allFields) { if (!HasField(query, field)) { diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index 7af50c329a..a26f59573c 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -396,14 +396,12 @@ namespace MediaBrowser.Api.UserLibrary public VideoType[] GetVideoTypes() { - var val = VideoTypes; - - if (string.IsNullOrEmpty(val)) + if (string.IsNullOrEmpty(VideoTypes)) { - return new VideoType[] { }; + return Array.Empty(); } - return val.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(v => (VideoType)Enum.Parse(typeof(VideoType), v, true)).ToArray(); + return VideoTypes.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(v => (VideoType)Enum.Parse(typeof(VideoType), v, true)).ToArray(); } /// From 9dba930a85bc606840918639f4cd0b80c1e1eb2b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 9 Feb 2019 01:25:14 +0100 Subject: [PATCH 05/11] Warn faster for slow requests --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 49a510a22c..7e106569b0 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2710,13 +2710,11 @@ namespace Emby.Server.Implementations.Data private void AddItem(List items, BaseItem newItem) { - var providerIds = newItem.ProviderIds.ToList(); - for (var i = 0; i < items.Count; i++) { var item = items[i]; - foreach (var providerId in providerIds) + foreach (var providerId in newItem.ProviderIds) { if (providerId.Key == MetadataProviders.TmdbCollection.ToString()) { @@ -2741,10 +2739,10 @@ namespace Emby.Server.Implementations.Data { var elapsed = (DateTime.UtcNow - startDate).TotalMilliseconds; - int slowThreshold = 1000; + int slowThreshold = 100; #if DEBUG - slowThreshold = 250; + slowThreshold = 10; #endif if (elapsed >= slowThreshold) From da9418c1b2348a68bd7a554462c8f355eacb6131 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 9 Feb 2019 01:39:59 +0100 Subject: [PATCH 06/11] Useless copy --- .../Data/SqliteItemRepository.cs | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 7e106569b0..6502e4aeda 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -966,7 +966,7 @@ namespace Emby.Server.Implementations.Data if (item.ExtraIds.Length > 0) { - saveItemStatement.TryBind("@ExtraIds", string.Join("|", item.ExtraIds.ToArray())); + saveItemStatement.TryBind("@ExtraIds", string.Join("|", item.ExtraIds)); } else { @@ -2792,7 +2792,7 @@ namespace Emby.Server.Implementations.Data var whereText = whereClauses.Count == 0 ? string.Empty : - " where " + string.Join(" AND ", whereClauses.ToArray()); + " where " + string.Join(" AND ", whereClauses); commandText += whereText + GetGroupBy(query) @@ -2916,25 +2916,31 @@ namespace Emby.Server.Implementations.Data private string GetOrderByText(InternalItemsQuery query) { - var orderBy = query.OrderBy.ToList(); - var enableOrderInversion = false; - - if (query.SimilarTo != null && orderBy.Count == 0) + if (string.IsNullOrEmpty(query.SearchTerm)) { - orderBy.Add(new ValueTuple("SimilarityScore", SortOrder.Descending)); - orderBy.Add(new ValueTuple(ItemSortBy.Random, SortOrder.Ascending)); - } + int oldLen = query.OrderBy.Length; - if (!string.IsNullOrEmpty(query.SearchTerm)) + if (query.SimilarTo != null && oldLen == 0) + { + var arr = new (string, SortOrder)[oldLen + 2]; + query.OrderBy.CopyTo(arr, 0); + arr[oldLen] = ("SimilarityScore", SortOrder.Descending); + arr[oldLen + 1] = (ItemSortBy.Random, SortOrder.Ascending); + query.OrderBy = arr; + } + } + else { - orderBy = new List<(string, SortOrder)>(); - orderBy.Add(new ValueTuple("SearchScore", SortOrder.Descending)); - orderBy.Add(new ValueTuple(ItemSortBy.SortName, SortOrder.Ascending)); + query.OrderBy = new [] + { + ("SearchScore", SortOrder.Descending), + (ItemSortBy.SortName, SortOrder.Ascending) + }; } - query.OrderBy = orderBy.ToArray(); + var orderBy = query.OrderBy; - if (orderBy.Count == 0) + if (orderBy.Length == 0) { return string.Empty; } @@ -2943,6 +2949,7 @@ namespace Emby.Server.Implementations.Data { var columnMap = MapOrderByField(i.Item1, query); var columnAscending = i.Item2 == SortOrder.Ascending; + const bool enableOrderInversion = false; if (columnMap.Item2 && enableOrderInversion) { columnAscending = !columnAscending; @@ -2954,7 +2961,7 @@ namespace Emby.Server.Implementations.Data })); } - private ValueTuple MapOrderByField(string name, InternalItemsQuery query) + private (string, bool) MapOrderByField(string name, InternalItemsQuery query) { if (string.Equals(name, ItemSortBy.AirTime, StringComparison.OrdinalIgnoreCase)) { @@ -3204,7 +3211,7 @@ namespace Emby.Server.Implementations.Data var whereText = whereClauses.Count == 0 ? string.Empty : - " where " + string.Join(" AND ", whereClauses.ToArray()); + " where " + string.Join(" AND ", whereClauses); commandText += whereText + GetGroupBy(query) @@ -4364,7 +4371,7 @@ namespace Emby.Server.Implementations.Data } else if (query.Years.Length > 1) { - var val = string.Join(",", query.Years.ToArray()); + var val = string.Join(",", query.Years); whereClauses.Add("ProductionYear in (" + val + ")"); } From 8d98885cdae15cc9865e0984e4270ee4a8d9d2db Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 9 Feb 2019 11:53:07 +0100 Subject: [PATCH 07/11] Less string allocations --- .../HttpServer/HttpResultFactory.cs | 33 +++++++++---------- .../HttpServer/StreamWriter.cs | 8 ++--- .../LiveTv/LiveTvManager.cs | 2 +- .../Serialization/JsonSerializer.cs | 21 ++++++++++++ Jellyfin.Server/Jellyfin.Server.csproj | 2 +- .../SocketSharp/WebSocketSharpRequest.cs | 30 +++++++++++++---- MediaBrowser.Api/UserLibrary/ItemsService.cs | 8 ++--- .../Serialization/IJsonSerializer.cs | 8 +++++ 8 files changed, 76 insertions(+), 36 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 7445fd3c28..e7e3308dc7 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -90,7 +90,7 @@ namespace Emby.Server.Implementations.HttpServer /// private IHasHeaders GetHttpResult(IRequest requestContext, Stream content, string contentType, bool addCachePrevention, IDictionary responseHeaders = null) { - var result = new StreamWriter(content, contentType, _logger); + var result = new StreamWriter(content, contentType); if (responseHeaders == null) { @@ -131,7 +131,7 @@ namespace Emby.Server.Implementations.HttpServer content = Array.Empty(); } - result = new StreamWriter(content, contentType, contentLength, _logger); + result = new StreamWriter(content, contentType, contentLength); } else { @@ -143,7 +143,7 @@ namespace Emby.Server.Implementations.HttpServer responseHeaders = new Dictionary(); } - if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string expires)) + if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string _)) { responseHeaders["Expires"] = "-1"; } @@ -175,7 +175,7 @@ namespace Emby.Server.Implementations.HttpServer bytes = Array.Empty(); } - result = new StreamWriter(bytes, contentType, contentLength, _logger); + result = new StreamWriter(bytes, contentType, contentLength); } else { @@ -187,7 +187,7 @@ namespace Emby.Server.Implementations.HttpServer responseHeaders = new Dictionary(); } - if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string expires)) + if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string _)) { responseHeaders["Expires"] = "-1"; } @@ -277,9 +277,9 @@ namespace Emby.Server.Implementations.HttpServer private object ToOptimizedResultInternal(IRequest request, T dto, IDictionary responseHeaders = null) { - var contentType = request.ResponseContentType; + var contentType = request.ResponseContentType?.Split(';')[0]; - switch (GetRealContentType(contentType)) + switch (contentType) { case "application/xml": case "text/xml": @@ -333,13 +333,13 @@ namespace Emby.Server.Implementations.HttpServer if (isHeadRequest) { - var result = new StreamWriter(Array.Empty(), contentType, contentLength, _logger); + var result = new StreamWriter(Array.Empty(), contentType, contentLength); AddResponseHeaders(result, responseHeaders); return result; } else { - var result = new StreamWriter(content, contentType, contentLength, _logger); + var result = new StreamWriter(content, contentType, contentLength); AddResponseHeaders(result, responseHeaders); return result; } @@ -348,13 +348,19 @@ namespace Emby.Server.Implementations.HttpServer private byte[] Compress(byte[] bytes, string compressionType) { if (string.Equals(compressionType, "br", StringComparison.OrdinalIgnoreCase)) + { return CompressBrotli(bytes); + } if (string.Equals(compressionType, "deflate", StringComparison.OrdinalIgnoreCase)) + { return Deflate(bytes); + } if (string.Equals(compressionType, "gzip", StringComparison.OrdinalIgnoreCase)) + { return GZip(bytes); + } throw new NotSupportedException(compressionType); } @@ -390,13 +396,6 @@ namespace Emby.Server.Implementations.HttpServer } } - public static string GetRealContentType(string contentType) - { - return contentType == null - ? null - : contentType.Split(';')[0].ToLowerInvariant().Trim(); - } - private static string SerializeToXmlString(object from) { using (var ms = new MemoryStream()) @@ -621,7 +620,7 @@ namespace Emby.Server.Implementations.HttpServer } } - var hasHeaders = new StreamWriter(stream, contentType, _logger) + var hasHeaders = new StreamWriter(stream, contentType) { OnComplete = options.OnComplete, OnError = options.OnError diff --git a/Emby.Server.Implementations/HttpServer/StreamWriter.cs b/Emby.Server.Implementations/HttpServer/StreamWriter.cs index 3269d44cf6..cb2e3580b2 100644 --- a/Emby.Server.Implementations/HttpServer/StreamWriter.cs +++ b/Emby.Server.Implementations/HttpServer/StreamWriter.cs @@ -14,8 +14,6 @@ namespace Emby.Server.Implementations.HttpServer /// public class StreamWriter : IAsyncStreamWriter, IHasHeaders { - private ILogger Logger { get; set; } - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); /// @@ -45,7 +43,7 @@ namespace Emby.Server.Implementations.HttpServer /// The source. /// Type of the content. /// The logger. - public StreamWriter(Stream source, string contentType, ILogger logger) + public StreamWriter(Stream source, string contentType) { if (string.IsNullOrEmpty(contentType)) { @@ -53,7 +51,6 @@ namespace Emby.Server.Implementations.HttpServer } SourceStream = source; - Logger = logger; Headers["Content-Type"] = contentType; @@ -69,7 +66,7 @@ namespace Emby.Server.Implementations.HttpServer /// The source. /// Type of the content. /// The logger. - public StreamWriter(byte[] source, string contentType, int contentLength, ILogger logger) + public StreamWriter(byte[] source, string contentType, int contentLength) { if (string.IsNullOrEmpty(contentType)) { @@ -77,7 +74,6 @@ namespace Emby.Server.Implementations.HttpServer } SourceBytes = source; - Logger = logger; Headers["Content-Type"] = contentType; diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index a363028760..f7ef16fb09 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -184,7 +184,7 @@ namespace Emby.Server.Implementations.LiveTv public QueryResult GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken) { - var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId); + var user = query.UserId == Guid.Empty ? null : _userManager.GetUserById(query.UserId); var topFolder = GetInternalLiveTvFolder(cancellationToken); diff --git a/Emby.Server.Implementations/Serialization/JsonSerializer.cs b/Emby.Server.Implementations/Serialization/JsonSerializer.cs index 44898d4986..8ae7fd90cc 100644 --- a/Emby.Server.Implementations/Serialization/JsonSerializer.cs +++ b/Emby.Server.Implementations/Serialization/JsonSerializer.cs @@ -41,6 +41,27 @@ namespace Emby.Server.Implementations.Serialization ServiceStack.Text.JsonSerializer.SerializeToStream(obj, obj.GetType(), stream); } + /// + /// Serializes to stream. + /// + /// The obj. + /// The stream. + /// obj + public void SerializeToStream(T obj, Stream stream) + { + if (obj == null) + { + throw new ArgumentNullException(nameof(obj)); + } + + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + ServiceStack.Text.JsonSerializer.SerializeToStream(obj, stream); + } + /// /// Serializes to file. /// diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 885709b0c2..c7e1461bde 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -3,7 +3,7 @@ jellyfin Exe - netcoreapp3.0 + netcoreapp2.2;netcoreapp3.0 false diff --git a/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs index ebeb18ea06..12ae8169d7 100644 --- a/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs +++ b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs @@ -99,7 +99,7 @@ namespace Jellyfin.Server.SocketSharp name = name.Trim(HttpTrimCharacters); // First, check for correctly formed multi-line value - // Second, check for absenece of CTL characters + // Second, check for absence of CTL characters int crlf = 0; for (int i = 0; i < name.Length; ++i) { @@ -216,8 +216,13 @@ namespace Jellyfin.Server.SocketSharp { foreach (var acceptsType in acceptContentTypes) { - var contentType = HttpResultFactory.GetRealContentType(acceptsType); - acceptsAnything = acceptsAnything || contentType == "*/*"; + var contentType = acceptsType?.Split(';')[0]; + acceptsAnything = contentType.IndexOf("*/*", StringComparison.Ordinal) != -1; + + if (acceptsAnything) + { + break; + } } if (acceptsAnything) @@ -226,7 +231,7 @@ namespace Jellyfin.Server.SocketSharp { return defaultContentType; } - else if (serverDefaultContentType != null) + else { return serverDefaultContentType; } @@ -269,11 +274,11 @@ namespace Jellyfin.Server.SocketSharp private static string GetQueryStringContentType(IRequest httpReq) { - var format = httpReq.QueryString["format"]; + ReadOnlySpan format = httpReq.QueryString["format"]; if (format == null) { const int formatMaxLength = 4; - var pi = httpReq.PathInfo; + ReadOnlySpan pi = httpReq.PathInfo; if (pi == null || pi.Length <= formatMaxLength) { return null; @@ -281,7 +286,7 @@ namespace Jellyfin.Server.SocketSharp if (pi[0] == '/') { - pi = pi.Substring(1); + pi = pi.Slice(1); } format = LeftPart(pi, '/'); @@ -315,6 +320,17 @@ namespace Jellyfin.Server.SocketSharp return pos == -1 ? strVal : strVal.Substring(0, pos); } + public static ReadOnlySpan LeftPart(ReadOnlySpan strVal, char needle) + { + if (strVal == null) + { + return null; + } + + var pos = strVal.IndexOf(needle); + return pos == -1 ? strVal : strVal.Slice(0, pos); + } + public static string HandlerFactoryPath; private string pathInfo; diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 46d7c250e8..96b0aa003e 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -90,7 +90,7 @@ namespace MediaBrowser.Api.UserLibrary var options = GetDtoOptions(_authContext, request); - var ancestorIds = new List(); + var ancestorIds = Array.Empty(); var excludeFolderIds = user.Configuration.LatestItemsExcludes; if (parentIdGuid.Equals(Guid.Empty) && excludeFolderIds.Length > 0) @@ -99,12 +99,12 @@ namespace MediaBrowser.Api.UserLibrary .Where(i => i is Folder) .Where(i => !excludeFolderIds.Contains(i.Id.ToString("N"))) .Select(i => i.Id) - .ToList(); + .ToArray(); } var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user) { - OrderBy = new[] { ItemSortBy.DatePlayed }.Select(i => new ValueTuple(i, SortOrder.Descending)).ToArray(), + OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending) }, IsResumable = true, StartIndex = request.StartIndex, Limit = request.Limit, @@ -115,7 +115,7 @@ namespace MediaBrowser.Api.UserLibrary IsVirtualItem = false, CollapseBoxSetItems = false, EnableTotalRecordCount = request.EnableTotalRecordCount, - AncestorIds = ancestorIds.ToArray(), + AncestorIds = ancestorIds, IncludeItemTypes = request.GetIncludeItemTypes(), ExcludeItemTypes = request.GetExcludeItemTypes(), SearchTerm = request.SearchTerm diff --git a/MediaBrowser.Model/Serialization/IJsonSerializer.cs b/MediaBrowser.Model/Serialization/IJsonSerializer.cs index ae0cf6f365..18f51f6525 100644 --- a/MediaBrowser.Model/Serialization/IJsonSerializer.cs +++ b/MediaBrowser.Model/Serialization/IJsonSerializer.cs @@ -14,6 +14,14 @@ namespace MediaBrowser.Model.Serialization /// obj void SerializeToStream(object obj, Stream stream); + /// + /// Serializes to stream. + /// + /// The obj. + /// The stream. + /// obj + void SerializeToStream(T obj, Stream stream); + /// /// Serializes to file. /// From 585b5201f1b3b34427b1b8f8e03258feff651516 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 9 Feb 2019 12:01:11 +0100 Subject: [PATCH 08/11] Last bits --- .../SocketSharp/WebSocketSharpRequest.cs | 37 +++++-------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs index 12ae8169d7..d862931542 100644 --- a/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs +++ b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs @@ -27,30 +27,6 @@ namespace Jellyfin.Server.SocketSharp //HandlerFactoryPath = GetHandlerPathIfAny(UrlPrefixes[0]); } - private static string GetHandlerPathIfAny(string listenerUrl) - { - if (listenerUrl == null) - { - return null; - } - - var pos = listenerUrl.IndexOf("://", StringComparison.OrdinalIgnoreCase); - if (pos == -1) - { - return null; - } - - var startHostUrl = listenerUrl.Substring(pos + "://".Length); - var endPos = startHostUrl.IndexOf('/'); - if (endPos == -1) - { - return null; - } - - var endHostUrl = startHostUrl.Substring(endPos + 1); - return string.IsNullOrEmpty(endHostUrl) ? null : endHostUrl.TrimEnd('/'); - } - public HttpListenerRequest HttpRequest => request; public object OriginalRequest => request; @@ -216,6 +192,8 @@ namespace Jellyfin.Server.SocketSharp { foreach (var acceptsType in acceptContentTypes) { + // TODO: @bond move to Span when Span.Split lands + // https://github.com/dotnet/corefx/issues/26528 var contentType = acceptsType?.Split(';')[0]; acceptsAnything = contentType.IndexOf("*/*", StringComparison.Ordinal) != -1; @@ -342,7 +320,7 @@ namespace Jellyfin.Server.SocketSharp { var mode = HandlerFactoryPath; - var pos = request.RawUrl.IndexOf("?", StringComparison.Ordinal); + var pos = request.RawUrl.IndexOf('?', StringComparison.Ordinal); if (pos != -1) { var path = request.RawUrl.Substring(0, pos); @@ -524,10 +502,13 @@ namespace Jellyfin.Server.SocketSharp public static string NormalizePathInfo(string pathInfo, string handlerPath) { - var trimmed = pathInfo.TrimStart('/'); - if (handlerPath != null && trimmed.StartsWith(handlerPath, StringComparison.OrdinalIgnoreCase)) + if (handlerPath != null) { - return trimmed.Substring(handlerPath.Length); + var trimmed = pathInfo.TrimStart('/'); + if (trimmed.StartsWith(handlerPath, StringComparison.OrdinalIgnoreCase)) + { + return trimmed.Substring(handlerPath.Length); + } } return pathInfo; From ea446fd4a3c41c1350a4faf34044bdf808d334fd Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 9 Feb 2019 16:06:37 +0100 Subject: [PATCH 09/11] Revert back to netcoreapp2.1 --- Jellyfin.Server/Jellyfin.Server.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index c7e1461bde..fcfeeb225e 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -3,7 +3,7 @@ jellyfin Exe - netcoreapp2.2;netcoreapp3.0 + netcoreapp2.2 false From 3f13851be54ce97df72fc27a15ee74c7600e38b1 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Tue, 12 Feb 2019 21:06:34 +0100 Subject: [PATCH 10/11] Address comments --- Emby.Server.Implementations/HttpServer/HttpResultFactory.cs | 3 ++- Jellyfin.Server/Jellyfin.Server.csproj | 2 +- Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index e7e3308dc7..85a08b05ac 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -277,7 +277,8 @@ namespace Emby.Server.Implementations.HttpServer private object ToOptimizedResultInternal(IRequest request, T dto, IDictionary responseHeaders = null) { - var contentType = request.ResponseContentType?.Split(';')[0]; + // TODO: @bond use Span and .Equals + var contentType = request.ResponseContentType?.Split(';')[0].Trim().ToLowerInvariant(); switch (contentType) { diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index fcfeeb225e..b1515df439 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -3,7 +3,7 @@ jellyfin Exe - netcoreapp2.2 + netcoreapp2.1 false diff --git a/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs index d862931542..c2b71e9ef7 100644 --- a/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs +++ b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs @@ -194,8 +194,8 @@ namespace Jellyfin.Server.SocketSharp { // TODO: @bond move to Span when Span.Split lands // https://github.com/dotnet/corefx/issues/26528 - var contentType = acceptsType?.Split(';')[0]; - acceptsAnything = contentType.IndexOf("*/*", StringComparison.Ordinal) != -1; + var contentType = acceptsType?.Split(';')[0].Trim(); + acceptsAnything = contentType.Equals("*/*", StringComparison.OrdinalIgnoreCase); if (acceptsAnything) { From 5835c4b21d5ab2666478f29c240d2e56316e1f67 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 13 Feb 2019 12:58:46 +0100 Subject: [PATCH 11/11] Remove comment --- Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 556b7a547d..fba81306b3 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -364,7 +364,7 @@ namespace Emby.Server.Implementations.Data //{ // return new DummyToken(); //} - return new WriteLockToken(obj); // TODO: fix segfault + return new WriteLockToken(obj); } public static IDisposable Write(this ReaderWriterLockSlim obj)