|
@@ -1,6 +1,7 @@
|
|
|
#pragma warning disable CS1591
|
|
|
|
|
|
using System;
|
|
|
+using System.Buffers.Text;
|
|
|
using System.Collections.Generic;
|
|
|
using System.Globalization;
|
|
|
using System.IO;
|
|
@@ -88,7 +89,7 @@ namespace Emby.Server.Implementations.Data
|
|
|
_imageProcessor = imageProcessor;
|
|
|
|
|
|
_typeMapper = new TypeMapper();
|
|
|
- _jsonOptions = JsonDefaults.GetOptions();
|
|
|
+ _jsonOptions = JsonDefaults.Options;
|
|
|
|
|
|
DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db");
|
|
|
}
|
|
@@ -502,7 +503,7 @@ namespace Emby.Server.Implementations.Data
|
|
|
using (var saveImagesStatement = base.PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id"))
|
|
|
{
|
|
|
saveImagesStatement.TryBind("@Id", item.Id.ToByteArray());
|
|
|
- saveImagesStatement.TryBind("@Images", SerializeImages(item));
|
|
|
+ saveImagesStatement.TryBind("@Images", SerializeImages(item.ImageInfos));
|
|
|
|
|
|
saveImagesStatement.MoveNext();
|
|
|
}
|
|
@@ -897,8 +898,8 @@ namespace Emby.Server.Implementations.Data
|
|
|
saveItemStatement.TryBind("@ExternalSeriesId", item.ExternalSeriesId);
|
|
|
saveItemStatement.TryBind("@Tagline", item.Tagline);
|
|
|
|
|
|
- saveItemStatement.TryBind("@ProviderIds", SerializeProviderIds(item));
|
|
|
- saveItemStatement.TryBind("@Images", SerializeImages(item));
|
|
|
+ saveItemStatement.TryBind("@ProviderIds", SerializeProviderIds(item.ProviderIds));
|
|
|
+ saveItemStatement.TryBind("@Images", SerializeImages(item.ImageInfos));
|
|
|
|
|
|
if (item.ProductionLocations.Length > 0)
|
|
|
{
|
|
@@ -968,10 +969,10 @@ namespace Emby.Server.Implementations.Data
|
|
|
saveItemStatement.MoveNext();
|
|
|
}
|
|
|
|
|
|
- private static string SerializeProviderIds(BaseItem item)
|
|
|
+ internal static string SerializeProviderIds(Dictionary<string, string> providerIds)
|
|
|
{
|
|
|
StringBuilder str = new StringBuilder();
|
|
|
- foreach (var i in item.ProviderIds)
|
|
|
+ foreach (var i in providerIds)
|
|
|
{
|
|
|
// Ideally we shouldn't need this IsNullOrWhiteSpace check,
|
|
|
// but we're seeing some cases of bad data slip through
|
|
@@ -995,35 +996,25 @@ namespace Emby.Server.Implementations.Data
|
|
|
return str.ToString();
|
|
|
}
|
|
|
|
|
|
- private static void DeserializeProviderIds(string value, BaseItem item)
|
|
|
+ internal static void DeserializeProviderIds(string value, IHasProviderIds item)
|
|
|
{
|
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (item.ProviderIds.Count > 0)
|
|
|
+ foreach (var part in value.SpanSplit('|'))
|
|
|
{
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- var parts = value.Split('|', StringSplitOptions.RemoveEmptyEntries);
|
|
|
-
|
|
|
- foreach (var part in parts)
|
|
|
- {
|
|
|
- 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).ToString(), part.Slice(providerDelimiterIndex + 1).ToString());
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private string SerializeImages(BaseItem item)
|
|
|
+ internal string SerializeImages(ItemImageInfo[] images)
|
|
|
{
|
|
|
- var images = item.ImageInfos;
|
|
|
-
|
|
|
if (images.Length == 0)
|
|
|
{
|
|
|
return null;
|
|
@@ -1045,21 +1036,15 @@ namespace Emby.Server.Implementations.Data
|
|
|
return str.ToString();
|
|
|
}
|
|
|
|
|
|
- private void DeserializeImages(string value, BaseItem item)
|
|
|
+ internal ItemImageInfo[] DeserializeImages(string value)
|
|
|
{
|
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
|
{
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (item.ImageInfos.Length > 0)
|
|
|
- {
|
|
|
- return;
|
|
|
+ return Array.Empty<ItemImageInfo>();
|
|
|
}
|
|
|
|
|
|
- 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);
|
|
|
|
|
@@ -1069,15 +1054,14 @@ namespace Emby.Server.Implementations.Data
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- item.ImageInfos = list.ToArray();
|
|
|
+ return list.ToArray();
|
|
|
}
|
|
|
|
|
|
- public void AppendItemImageInfo(StringBuilder bldr, ItemImageInfo image)
|
|
|
+ private void AppendItemImageInfo(StringBuilder bldr, ItemImageInfo image)
|
|
|
{
|
|
|
const char Delimiter = '*';
|
|
|
|
|
|
var path = image.Path ?? string.Empty;
|
|
|
- var hash = image.BlurHash ?? string.Empty;
|
|
|
|
|
|
bldr.Append(GetPathToSave(path))
|
|
|
.Append(Delimiter)
|
|
@@ -1087,48 +1071,105 @@ namespace Emby.Server.Implementations.Data
|
|
|
.Append(Delimiter)
|
|
|
.Append(image.Width)
|
|
|
.Append(Delimiter)
|
|
|
- .Append(image.Height)
|
|
|
- .Append(Delimiter)
|
|
|
- // Replace delimiters with other characters.
|
|
|
- // This can be removed when we migrate to a proper DB.
|
|
|
- .Append(hash.Replace('*', '/').Replace('|', '\\'));
|
|
|
+ .Append(image.Height);
|
|
|
+
|
|
|
+ var hash = image.BlurHash;
|
|
|
+ if (!string.IsNullOrEmpty(hash))
|
|
|
+ {
|
|
|
+ bldr.Append(Delimiter)
|
|
|
+ // Replace delimiters with other characters.
|
|
|
+ // This can be removed when we migrate to a proper DB.
|
|
|
+ .Append(hash.Replace('*', '/').Replace('|', '\\'));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- public ItemImageInfo ItemImageInfoFromValueString(string value)
|
|
|
+ internal 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)
|
|
|
+ {
|
|
|
+ nextSegment = value.Length;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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('*');
|
|
|
+ if (nextSegment == -1 || nextSegment == value.Length)
|
|
|
+ {
|
|
|
+ return image;
|
|
|
+ }
|
|
|
+
|
|
|
+ ReadOnlySpan<char> widthSpan = value[..nextSegment];
|
|
|
+
|
|
|
+ value = value[(nextSegment + 1)..];
|
|
|
+ nextSegment = value.IndexOf('*');
|
|
|
+ if (nextSegment == -1)
|
|
|
+ {
|
|
|
+ nextSegment = value.Length;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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)
|
|
|
+ if (nextSegment < value.Length - 1)
|
|
|
{
|
|
|
- image.BlurHash = parts[5].Replace('/', '*').Replace('\\', '|');
|
|
|
+ value = value[(nextSegment + 1)..];
|
|
|
+ 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);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1311,7 +1352,14 @@ namespace Emby.Server.Implementations.Data
|
|
|
|
|
|
if (!reader.IsDBNull(index))
|
|
|
{
|
|
|
- item.ChannelId = new Guid(reader.GetString(index));
|
|
|
+ if (!Utf8Parser.TryParse(reader[index].ToBlob(), out Guid value, out _, standardFormat: 'N'))
|
|
|
+ {
|
|
|
+ var str = reader.GetString(index);
|
|
|
+ Logger.LogWarning("{ChannelId} isn't in the expected format", str);
|
|
|
+ value = new Guid(str);
|
|
|
+ }
|
|
|
+
|
|
|
+ item.ChannelId = value;
|
|
|
}
|
|
|
|
|
|
index++;
|
|
@@ -1790,7 +1838,7 @@ namespace Emby.Server.Implementations.Data
|
|
|
index++;
|
|
|
}
|
|
|
|
|
|
- if (!reader.IsDBNull(index))
|
|
|
+ if (item.ProviderIds.Count == 0 && !reader.IsDBNull(index))
|
|
|
{
|
|
|
DeserializeProviderIds(reader.GetString(index), item);
|
|
|
}
|
|
@@ -1799,9 +1847,9 @@ namespace Emby.Server.Implementations.Data
|
|
|
|
|
|
if (query.DtoOptions.EnableImages)
|
|
|
{
|
|
|
- if (!reader.IsDBNull(index))
|
|
|
+ if (item.ImageInfos.Length == 0 && !reader.IsDBNull(index))
|
|
|
{
|
|
|
- DeserializeImages(reader.GetString(index), item);
|
|
|
+ item.ImageInfos = DeserializeImages(reader.GetString(index));
|
|
|
}
|
|
|
|
|
|
index++;
|
|
@@ -2116,30 +2164,7 @@ namespace Emby.Server.Implementations.Data
|
|
|
|| query.IsLiked.HasValue;
|
|
|
}
|
|
|
|
|
|
- private readonly ItemFields[] _allFields = Enum.GetNames(typeof(ItemFields))
|
|
|
- .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
|
|
|
- .ToArray();
|
|
|
-
|
|
|
- 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 readonly ItemFields[] _allFields = Enum.GetValues<ItemFields>();
|
|
|
|
|
|
private bool HasField(InternalItemsQuery query, ItemFields name)
|
|
|
{
|
|
@@ -2329,9 +2354,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;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -2578,9 +2626,9 @@ namespace Emby.Server.Implementations.Data
|
|
|
}
|
|
|
|
|
|
var commandText = "select "
|
|
|
- + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count(distinct PresentationUniqueKey)" }))
|
|
|
- + GetFromText()
|
|
|
- + GetJoinUserDataText(query);
|
|
|
+ + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count(distinct PresentationUniqueKey)" }))
|
|
|
+ + GetFromText()
|
|
|
+ + GetJoinUserDataText(query);
|
|
|
|
|
|
var whereClauses = GetWhereClauses(query, null);
|
|
|
if (whereClauses.Count != 0)
|
|
@@ -2721,87 +2769,22 @@ namespace Emby.Server.Implementations.Data
|
|
|
|
|
|
private string FixUnicodeChars(string buffer)
|
|
|
{
|
|
|
- if (buffer.IndexOf('\u2013', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u2013', '-'); // en dash
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u2014', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u2014', '-'); // em dash
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u2015', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u2015', '-'); // horizontal bar
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u2017', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u2017', '_'); // double low line
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u2018', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u2018', '\''); // left single quotation mark
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u2019', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u2019', '\''); // right single quotation mark
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u201a', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u201a', ','); // single low-9 quotation mark
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u201b', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u201b', '\''); // single high-reversed-9 quotation mark
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u201c', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u201c', '\"'); // left double quotation mark
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u201d', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u201d', '\"'); // right double quotation mark
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u201e', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u201e', '\"'); // double low-9 quotation mark
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u2026', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace("\u2026", "...", StringComparison.Ordinal); // horizontal ellipsis
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u2032', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u2032', '\''); // prime
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u2033', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u2033', '\"'); // double prime
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u0060', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u0060', '\''); // grave accent
|
|
|
- }
|
|
|
-
|
|
|
- if (buffer.IndexOf('\u00B4', StringComparison.Ordinal) > -1)
|
|
|
- {
|
|
|
- buffer = buffer.Replace('\u00B4', '\''); // acute accent
|
|
|
- }
|
|
|
-
|
|
|
- return buffer;
|
|
|
+ buffer = buffer.Replace('\u2013', '-'); // en dash
|
|
|
+ buffer = buffer.Replace('\u2014', '-'); // em dash
|
|
|
+ buffer = buffer.Replace('\u2015', '-'); // horizontal bar
|
|
|
+ buffer = buffer.Replace('\u2017', '_'); // double low line
|
|
|
+ buffer = buffer.Replace('\u2018', '\''); // left single quotation mark
|
|
|
+ buffer = buffer.Replace('\u2019', '\''); // right single quotation mark
|
|
|
+ buffer = buffer.Replace('\u201a', ','); // single low-9 quotation mark
|
|
|
+ buffer = buffer.Replace('\u201b', '\''); // single high-reversed-9 quotation mark
|
|
|
+ buffer = buffer.Replace('\u201c', '\"'); // left double quotation mark
|
|
|
+ buffer = buffer.Replace('\u201d', '\"'); // right double quotation mark
|
|
|
+ buffer = buffer.Replace('\u201e', '\"'); // double low-9 quotation mark
|
|
|
+ buffer = buffer.Replace("\u2026", "...", StringComparison.Ordinal); // horizontal ellipsis
|
|
|
+ buffer = buffer.Replace('\u2032', '\''); // prime
|
|
|
+ buffer = buffer.Replace('\u2033', '\"'); // double prime
|
|
|
+ buffer = buffer.Replace('\u0060', '\''); // grave accent
|
|
|
+ return buffer.Replace('\u00B4', '\''); // acute accent
|
|
|
}
|
|
|
|
|
|
private void AddItem(List<BaseItem> items, BaseItem newItem)
|
|
@@ -3584,11 +3567,11 @@ namespace Emby.Server.Implementations.Data
|
|
|
statement?.TryBind("@IsFolder", query.IsFolder);
|
|
|
}
|
|
|
|
|
|
- var includeTypes = query.IncludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
|
|
|
+ var includeTypes = query.IncludeItemTypes.Select(MapIncludeItemTypes).Where(x => x != null).ToArray();
|
|
|
// Only specify excluded types if no included types are specified
|
|
|
if (includeTypes.Length == 0)
|
|
|
{
|
|
|
- var excludeTypes = query.ExcludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
|
|
|
+ var excludeTypes = query.ExcludeItemTypes.Select(MapIncludeItemTypes).Where(x => x != null).ToArray();
|
|
|
if (excludeTypes.Length == 1)
|
|
|
{
|
|
|
whereClauses.Add("type<>@type");
|
|
@@ -4532,7 +4515,7 @@ namespace Emby.Server.Implementations.Data
|
|
|
whereClauses.Add(GetProviderIdClause(query.HasTvdbId.Value, "tvdb"));
|
|
|
}
|
|
|
|
|
|
- var includedItemByNameTypes = GetItemByNameTypesInQuery(query).SelectMany(MapIncludeItemTypes).ToList();
|
|
|
+ var includedItemByNameTypes = GetItemByNameTypesInQuery(query);
|
|
|
var enableItemsByName = (query.IncludeItemsByName ?? false) && includedItemByNameTypes.Count > 0;
|
|
|
|
|
|
var queryTopParentIds = query.TopParentIds;
|
|
@@ -4790,27 +4773,27 @@ namespace Emby.Server.Implementations.Data
|
|
|
|
|
|
if (IsTypeInQuery(nameof(Person), query))
|
|
|
{
|
|
|
- list.Add(nameof(Person));
|
|
|
+ list.Add(typeof(Person).FullName);
|
|
|
}
|
|
|
|
|
|
if (IsTypeInQuery(nameof(Genre), query))
|
|
|
{
|
|
|
- list.Add(nameof(Genre));
|
|
|
+ list.Add(typeof(Genre).FullName);
|
|
|
}
|
|
|
|
|
|
if (IsTypeInQuery(nameof(MusicGenre), query))
|
|
|
{
|
|
|
- list.Add(nameof(MusicGenre));
|
|
|
+ list.Add(typeof(MusicGenre).FullName);
|
|
|
}
|
|
|
|
|
|
if (IsTypeInQuery(nameof(MusicArtist), query))
|
|
|
{
|
|
|
- list.Add(nameof(MusicArtist));
|
|
|
+ list.Add(typeof(MusicArtist).FullName);
|
|
|
}
|
|
|
|
|
|
if (IsTypeInQuery(nameof(Studio), query))
|
|
|
{
|
|
|
- list.Add(nameof(Studio));
|
|
|
+ list.Add(typeof(Studio).FullName);
|
|
|
}
|
|
|
|
|
|
return list;
|
|
@@ -4915,15 +4898,10 @@ namespace Emby.Server.Implementations.Data
|
|
|
typeof(AggregateFolder)
|
|
|
};
|
|
|
|
|
|
- public void UpdateInheritedValues(CancellationToken cancellationToken)
|
|
|
- {
|
|
|
- UpdateInheritedTags(cancellationToken);
|
|
|
- }
|
|
|
-
|
|
|
- private void UpdateInheritedTags(CancellationToken cancellationToken)
|
|
|
+ public void UpdateInheritedValues()
|
|
|
{
|
|
|
string sql = string.Join(
|
|
|
- ";",
|
|
|
+ ';',
|
|
|
new string[]
|
|
|
{
|
|
|
"delete from itemvalues where type = 6",
|
|
@@ -4946,37 +4924,38 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private static Dictionary<string, string[]> GetTypeMapDictionary()
|
|
|
+ private static Dictionary<string, string> GetTypeMapDictionary()
|
|
|
{
|
|
|
- var dict = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
|
|
|
+ var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
|
|
foreach (var t in _knownTypes)
|
|
|
{
|
|
|
- dict[t.Name] = new[] { t.FullName };
|
|
|
+ dict[t.Name] = t.FullName ;
|
|
|
}
|
|
|
|
|
|
- dict["Program"] = new[] { typeof(LiveTvProgram).FullName };
|
|
|
- dict["TvChannel"] = new[] { typeof(LiveTvChannel).FullName };
|
|
|
+ dict["Program"] = typeof(LiveTvProgram).FullName;
|
|
|
+ dict["TvChannel"] = typeof(LiveTvChannel).FullName;
|
|
|
|
|
|
return dict;
|
|
|
}
|
|
|
|
|
|
// Not crazy about having this all the way down here, but at least it's in one place
|
|
|
- private readonly Dictionary<string, string[]> _types = GetTypeMapDictionary();
|
|
|
+ private readonly Dictionary<string, string> _types = GetTypeMapDictionary();
|
|
|
|
|
|
- private string[] MapIncludeItemTypes(string value)
|
|
|
+ private string MapIncludeItemTypes(string value)
|
|
|
{
|
|
|
- if (_types.TryGetValue(value, out string[] result))
|
|
|
+ if (_types.TryGetValue(value, out string result))
|
|
|
{
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
if (IsValidType(value))
|
|
|
{
|
|
|
- return new[] { value };
|
|
|
+ return value;
|
|
|
}
|
|
|
|
|
|
- return Array.Empty<string>();
|
|
|
+ Logger.LogWarning("Unknown item type: {ItemType}", value);
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
public void DeleteItem(Guid id)
|
|
@@ -5279,31 +5258,46 @@ AND Type = @InternalPersonType)");
|
|
|
|
|
|
public List<string> GetStudioNames()
|
|
|
{
|
|
|
- return GetItemValueNames(new[] { 3 }, new List<string>(), new List<string>());
|
|
|
+ return GetItemValueNames(new[] { 3 }, Array.Empty<string>(), Array.Empty<string>());
|
|
|
}
|
|
|
|
|
|
public List<string> GetAllArtistNames()
|
|
|
{
|
|
|
- return GetItemValueNames(new[] { 0, 1 }, new List<string>(), new List<string>());
|
|
|
+ return GetItemValueNames(new[] { 0, 1 }, Array.Empty<string>(), Array.Empty<string>());
|
|
|
}
|
|
|
|
|
|
public List<string> GetMusicGenreNames()
|
|
|
{
|
|
|
- return GetItemValueNames(new[] { 2 }, new List<string> { "Audio", "MusicVideo", "MusicAlbum", "MusicArtist" }, new List<string>());
|
|
|
+ return GetItemValueNames(
|
|
|
+ new[] { 2 },
|
|
|
+ new string[]
|
|
|
+ {
|
|
|
+ typeof(Audio).FullName,
|
|
|
+ typeof(MusicVideo).FullName,
|
|
|
+ typeof(MusicAlbum).FullName,
|
|
|
+ typeof(MusicArtist).FullName
|
|
|
+ },
|
|
|
+ Array.Empty<string>());
|
|
|
}
|
|
|
|
|
|
public List<string> GetGenreNames()
|
|
|
{
|
|
|
- return GetItemValueNames(new[] { 2 }, new List<string>(), new List<string> { "Audio", "MusicVideo", "MusicAlbum", "MusicArtist" });
|
|
|
+ return GetItemValueNames(
|
|
|
+ new[] { 2 },
|
|
|
+ Array.Empty<string>(),
|
|
|
+ new string[]
|
|
|
+ {
|
|
|
+ typeof(Audio).FullName,
|
|
|
+ typeof(MusicVideo).FullName,
|
|
|
+ typeof(MusicAlbum).FullName,
|
|
|
+ typeof(MusicArtist).FullName
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
- private List<string> GetItemValueNames(int[] itemValueTypes, List<string> withItemTypes, List<string> excludeItemTypes)
|
|
|
+ private List<string> GetItemValueNames(int[] itemValueTypes, IReadOnlyList<string> withItemTypes, IReadOnlyList<string> excludeItemTypes)
|
|
|
{
|
|
|
CheckDisposed();
|
|
|
|
|
|
- withItemTypes = withItemTypes.SelectMany(MapIncludeItemTypes).ToList();
|
|
|
- excludeItemTypes = excludeItemTypes.SelectMany(MapIncludeItemTypes).ToList();
|
|
|
-
|
|
|
var now = DateTime.UtcNow;
|
|
|
|
|
|
var typeClause = itemValueTypes.Length == 1 ?
|
|
@@ -5415,7 +5409,6 @@ AND Type = @InternalPersonType)");
|
|
|
ItemIds = query.ItemIds,
|
|
|
TopParentIds = query.TopParentIds,
|
|
|
ParentId = query.ParentId,
|
|
|
- IsPlayed = query.IsPlayed,
|
|
|
IsAiring = query.IsAiring,
|
|
|
IsMovie = query.IsMovie,
|
|
|
IsSports = query.IsSports,
|
|
@@ -5441,6 +5434,7 @@ AND Type = @InternalPersonType)");
|
|
|
|
|
|
var outerQuery = new InternalItemsQuery(query.User)
|
|
|
{
|
|
|
+ IsPlayed = query.IsPlayed,
|
|
|
IsFavorite = query.IsFavorite,
|
|
|
IsFavoriteOrLiked = query.IsFavoriteOrLiked,
|
|
|
IsLiked = query.IsLiked,
|
|
@@ -5809,7 +5803,10 @@ AND Type = @InternalPersonType)");
|
|
|
var endIndex = Math.Min(people.Count, startIndex + Limit);
|
|
|
for (var i = startIndex; i < endIndex; i++)
|
|
|
{
|
|
|
- insertText.AppendFormat("(@ItemId, @Name{0}, @Role{0}, @PersonType{0}, @SortOrder{0}, @ListOrder{0}),", i.ToString(CultureInfo.InvariantCulture));
|
|
|
+ insertText.AppendFormat(
|
|
|
+ CultureInfo.InvariantCulture,
|
|
|
+ "(@ItemId, @Name{0}, @Role{0}, @PersonType{0}, @SortOrder{0}, @ListOrder{0}),",
|
|
|
+ i.ToString(CultureInfo.InvariantCulture));
|
|
|
}
|
|
|
|
|
|
// Remove last comma
|
|
@@ -6261,7 +6258,7 @@ AND Type = @InternalPersonType)");
|
|
|
CheckDisposed();
|
|
|
if (id == Guid.Empty)
|
|
|
{
|
|
|
- throw new ArgumentException(nameof(id));
|
|
|
+ throw new ArgumentException("Guid can't be empty.", nameof(id));
|
|
|
}
|
|
|
|
|
|
if (attachments == null)
|