| 
					
				 | 
			
			
				@@ -1007,15 +1007,12 @@ namespace Emby.Server.Implementations.Data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var parts = value.Split('|', StringSplitOptions.RemoveEmptyEntries); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            foreach (var part in parts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            foreach (var part in value.SpanSplit('|')) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var idParts = part.Split('='); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (idParts.Length == 2) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var providerDelimiterIndex = part.IndexOf('='); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (providerDelimiterIndex != -1 && providerDelimiterIndex == part.LastIndexOf('=')) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    item.SetProviderId(idParts[0], idParts[1]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    item.SetProviderId(part.Slice(0, providerDelimiterIndex), part.Slice(providerDelimiterIndex + 1)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1057,9 +1054,8 @@ namespace Emby.Server.Implementations.Data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var parts = value.Split('|' , StringSplitOptions.RemoveEmptyEntries); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             var list = new List<ItemImageInfo>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            foreach (var part in parts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            foreach (var part in value.SpanSplit('|')) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 var image = ItemImageInfoFromValueString(part); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1094,41 +1090,89 @@ namespace Emby.Server.Implementations.Data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 .Append(hash.Replace('*', '/').Replace('|', '\\')); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public ItemImageInfo ItemImageInfoFromValueString(string value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private ItemImageInfo ItemImageInfoFromValueString(ReadOnlySpan<char> value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var parts = value.Split('*', StringSplitOptions.None); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var nextSegment = value.IndexOf('*'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (nextSegment == -1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (parts.Length < 3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ReadOnlySpan<char> path = value[..nextSegment]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            value = value[(nextSegment + 1)..]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            nextSegment = value.IndexOf('*'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (nextSegment == -1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var image = new ItemImageInfo(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ReadOnlySpan<char> dateModified = value[..nextSegment]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            value = value[(nextSegment + 1)..]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            nextSegment = value.IndexOf('*'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (nextSegment == -1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ReadOnlySpan<char> imageType = value[..nextSegment]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            image.Path = RestorePath(parts[0]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var image = new ItemImageInfo 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Path = RestorePath(path.ToString()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (long.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (long.TryParse(dateModified, NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 image.DateModified = new DateTime(ticks, DateTimeKind.Utc); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (Enum.TryParse(parts[2], true, out ImageType type)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (Enum.TryParse(imageType.ToString(), true, out ImageType type)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 image.Type = type; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (parts.Length >= 5) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // Optional parameters: width*height*blurhash 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (nextSegment + 1 < value.Length - 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (int.TryParse(parts[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out var width) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    && int.TryParse(parts[4], NumberStyles.Integer, CultureInfo.InvariantCulture, out var height)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                value = value[(nextSegment + 1)..]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                nextSegment = value.IndexOf('*'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ReadOnlySpan<char> widthSpan = value[..nextSegment]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                value = value[(nextSegment + 1)..]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                nextSegment = value.IndexOf('*'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (nextSegment == -1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return image; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ReadOnlySpan<char> heightSpan = value[..nextSegment]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (int.TryParse(widthSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var width) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    && int.TryParse(heightSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var height)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     image.Width = width; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     image.Height = height; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (parts.Length >= 6) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                nextSegment += 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (nextSegment < value.Length - 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    image.BlurHash = parts[5].Replace('/', '*').Replace('\\', '|'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    value = value[nextSegment..]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    var length = value.Length; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    Span<char> blurHashSpan = stackalloc char[length]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    for (int i = 0; i < length; i++) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        var c = value[i]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        blurHashSpan[i] = c switch 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            '/' => '*', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            '\\' => '|', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            _ => c 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    image.BlurHash = new string(blurHashSpan); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -2118,27 +2162,6 @@ namespace Emby.Server.Implementations.Data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         private readonly ItemFields[] _allFields = Enum.GetValues<ItemFields>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        private string[] GetColumnNamesFromField(ItemFields field) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            switch (field) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case ItemFields.Settings: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return new[] { "IsLocked", "PreferredMetadataCountryCode", "PreferredMetadataLanguage", "LockedFields" }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case ItemFields.ServiceName: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return new[] { "ExternalServiceId" }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case ItemFields.SortName: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return new[] { "ForcedSortName" }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case ItemFields.Taglines: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return new[] { "Tagline" }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case ItemFields.Tags: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return new[] { "Tags" }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case ItemFields.IsHD: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return Array.Empty<string>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return new[] { field.ToString() }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         private bool HasField(InternalItemsQuery query, ItemFields name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             switch (name) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -2327,9 +2350,32 @@ namespace Emby.Server.Implementations.Data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if (!HasField(query, field)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    foreach (var fieldToRemove in GetColumnNamesFromField(field)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    switch (field) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        list.Remove(fieldToRemove); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        case ItemFields.Settings: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            list.Remove("IsLocked"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            list.Remove("PreferredMetadataCountryCode"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            list.Remove("PreferredMetadataLanguage"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            list.Remove("LockedFields"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        case ItemFields.ServiceName: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            list.Remove("ExternalServiceId"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        case ItemFields.SortName: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            list.Remove("ForcedSortName"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        case ItemFields.Taglines: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            list.Remove("Tagline"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        case ItemFields.Tags: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            list.Remove("Tags"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        case ItemFields.IsHD: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            // do nothing 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            list.Remove(field.ToString()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -2575,10 +2621,21 @@ namespace Emby.Server.Implementations.Data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 query.Limit = query.Limit.Value + 4; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var commandText = "select " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count(distinct PresentationUniqueKey)" })) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            + GetFromText() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            + GetJoinUserDataText(query); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var commandText = "select "; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (EnableGroupByPresentationUniqueKey(query)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                commandText += "count (distinct PresentationUniqueKey)"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else if (query.GroupBySeriesPresentationUniqueKey) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                commandText += "count (distinct SeriesPresentationUniqueKey)"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                commandText += "count (guid)"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            commandText += GetFromText() + GetJoinUserDataText(query); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             var whereClauses = GetWhereClauses(query, null); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if (whereClauses.Count != 0) 
			 |