aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jellyfin.Data/Entities/BaseItem.cs4
-rw-r--r--Jellyfin.Server.Implementations/Item/BaseItemManager.cs255
2 files changed, 103 insertions, 156 deletions
diff --git a/Jellyfin.Data/Entities/BaseItem.cs b/Jellyfin.Data/Entities/BaseItem.cs
index 4ce652342..0e67a7ca4 100644
--- a/Jellyfin.Data/Entities/BaseItem.cs
+++ b/Jellyfin.Data/Entities/BaseItem.cs
@@ -14,7 +14,7 @@ public class BaseItem
public required string Type { get; set; }
- public IReadOnlyList<byte>? Data { get; set; }
+ public string? Data { get; set; }
public Guid? ParentId { get; set; }
@@ -94,7 +94,7 @@ public class BaseItem
public string? UnratedType { get; set; }
- public string? TopParentId { get; set; }
+ public Guid? TopParentId { get; set; }
public string? TrailerTypes { get; set; }
diff --git a/Jellyfin.Server.Implementations/Item/BaseItemManager.cs b/Jellyfin.Server.Implementations/Item/BaseItemManager.cs
index 5116b13d4..8f3c9636e 100644
--- a/Jellyfin.Server.Implementations/Item/BaseItemManager.cs
+++ b/Jellyfin.Server.Implementations/Item/BaseItemManager.cs
@@ -22,6 +22,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying;
using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using BaseItemDto = MediaBrowser.Controller.Entities.BaseItem;
using BaseItemEntity = Jellyfin.Data.Entities.BaseItem;
@@ -281,16 +282,9 @@ public class BaseItemManager : IItemRepository
}
}
- if (query.SimilarTo is not null && query.MinSimilarityScore > 0)
- {
- // TODO support similarty score via CTE
- baseQuery = baseQuery.Where(e => e.Sim == query.IsSeries);
- whereClauses.Add("SimilarityScore > " + (query.MinSimilarityScore - 1).ToString(CultureInfo.InvariantCulture));
- }
-
if (!string.IsNullOrEmpty(query.SearchTerm))
{
- whereClauses.Add("SearchScore > 0");
+ baseQuery = baseQuery.Where(e => e.CleanName!.Contains(query.SearchTerm, StringComparison.InvariantCultureIgnoreCase) || (e.OriginalTitle != null && e.OriginalTitle.Contains(query.SearchTerm, StringComparison.InvariantCultureIgnoreCase)));
}
if (query.IsFolder.HasValue)
@@ -917,44 +911,13 @@ public class BaseItemManager : IItemRepository
{
var includedItemByNameTypes = GetItemByNameTypesInQuery(query);
var enableItemsByName = (query.IncludeItemsByName ?? false) && includedItemByNameTypes.Count > 0;
-
- if (queryTopParentIds.Length == 1)
+ if (enableItemsByName && includedItemByNameTypes.Count > 0)
{
- if (enableItemsByName && includedItemByNameTypes.Count == 1)
- {
- whereClauses.Add("(TopParentId=@TopParentId or Type=@IncludedItemByNameType)");
- statement?.TryBind("@IncludedItemByNameType", includedItemByNameTypes[0]);
- }
- else if (enableItemsByName && includedItemByNameTypes.Count > 1)
- {
- var itemByNameTypeVal = string.Join(',', includedItemByNameTypes.Select(i => "'" + i + "'"));
- whereClauses.Add("(TopParentId=@TopParentId or Type in (" + itemByNameTypeVal + "))");
- }
- else
- {
- whereClauses.Add("(TopParentId=@TopParentId)");
- }
-
- statement?.TryBind("@TopParentId", queryTopParentIds[0].ToString("N", CultureInfo.InvariantCulture));
+ baseQuery = baseQuery.Where(e => includedItemByNameTypes.Contains(e.Type) || queryTopParentIds.Any(w => w.Equals(e.TopParentId!.Value)));
}
- else if (queryTopParentIds.Length > 1)
+ else
{
- var val = string.Join(',', queryTopParentIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
-
- if (enableItemsByName && includedItemByNameTypes.Count == 1)
- {
- whereClauses.Add("(Type=@IncludedItemByNameType or TopParentId in (" + val + "))");
- statement?.TryBind("@IncludedItemByNameType", includedItemByNameTypes[0]);
- }
- else if (enableItemsByName && includedItemByNameTypes.Count > 1)
- {
- var itemByNameTypeVal = string.Join(',', includedItemByNameTypes.Select(i => "'" + i + "'"));
- whereClauses.Add("(Type in (" + itemByNameTypeVal + ") or TopParentId in (" + val + "))");
- }
- else
- {
- whereClauses.Add("TopParentId in (" + val + ")");
- }
+ baseQuery = baseQuery.Where(e => queryTopParentIds.Any(w => w.Equals(e.TopParentId!.Value)));
}
}
@@ -965,124 +928,83 @@ public class BaseItemManager : IItemRepository
if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
{
- var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey";
- whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause));
- statement?.TryBind("@AncestorWithPresentationUniqueKey", query.AncestorWithPresentationUniqueKey);
+ baseQuery = baseQuery
+ .Where(e => context.BaseItems.Where(f => f.PresentationUniqueKey == query.AncestorWithPresentationUniqueKey).Any(f => f.AncestorIds!.Any(w => w.ItemId.Equals(f.Id))));
}
if (!string.IsNullOrWhiteSpace(query.SeriesPresentationUniqueKey))
{
- whereClauses.Add("SeriesPresentationUniqueKey=@SeriesPresentationUniqueKey");
- statement?.TryBind("@SeriesPresentationUniqueKey", query.SeriesPresentationUniqueKey);
+ baseQuery = baseQuery
+ .Where(e => e.SeriesPresentationUniqueKey == query.SeriesPresentationUniqueKey);
}
if (query.ExcludeInheritedTags.Length > 0)
{
- var paramName = "@ExcludeInheritedTags";
- if (statement is null)
- {
- int index = 0;
- string excludedTags = string.Join(',', query.ExcludeInheritedTags.Select(_ => paramName + index++));
- whereClauses.Add("((select CleanValue from ItemValues where ItemId=Guid and Type=6 and cleanvalue in (" + excludedTags + ")) is null)");
- }
- else
- {
- for (int index = 0; index < query.ExcludeInheritedTags.Length; index++)
- {
- statement.TryBind(paramName + index, GetCleanValue(query.ExcludeInheritedTags[index]));
- }
- }
+ baseQuery = baseQuery
+ .Where(e => !e.ItemValues!.Where(e => e.Type == 6)
+ .Any(f => query.ExcludeInheritedTags.Contains(f.CleanValue)));
}
if (query.IncludeInheritedTags.Length > 0)
{
- var paramName = "@IncludeInheritedTags";
- if (statement is null)
+ // Episodes do not store inherit tags from their parents in the database, and the tag may be still required by the client.
+ // In addtion to the tags for the episodes themselves, we need to manually query its parent (the season)'s tags as well.
+ if (includeTypes.Length == 1 && includeTypes.FirstOrDefault() is BaseItemKind.Episode)
{
- int index = 0;
- string includedTags = string.Join(',', query.IncludeInheritedTags.Select(_ => paramName + index++));
- // Episodes do not store inherit tags from their parents in the database, and the tag may be still required by the client.
- // In addtion to the tags for the episodes themselves, we need to manually query its parent (the season)'s tags as well.
- if (includeTypes.Length == 1 && includeTypes.FirstOrDefault() is BaseItemKind.Episode)
- {
- whereClauses.Add($"""
- ((select CleanValue from ItemValues where ItemId=Guid and Type=6 and CleanValue in ({includedTags})) is not null
- OR (select CleanValue from ItemValues where ItemId=ParentId and Type=6 and CleanValue in ({includedTags})) is not null)
- """);
- }
+ baseQuery = baseQuery
+ .Where(e => e.ItemValues!.Where(e => e.Type == 6)
+ .Any(f => query.IncludeInheritedTags.Contains(f.CleanValue))
+ ||
+ (e.ParentId.HasValue && context.ItemValues.Where(w => w.ItemId.Equals(e.ParentId.Value))!.Where(e => e.Type == 6)
+ .Any(f => query.IncludeInheritedTags.Contains(f.CleanValue))));
+ }
- // A playlist should be accessible to its owner regardless of allowed tags.
- else if (includeTypes.Length == 1 && includeTypes.FirstOrDefault() is BaseItemKind.Playlist)
- {
- whereClauses.Add($"""
- ((select CleanValue from ItemValues where ItemId=Guid and Type=6 and CleanValue in ({includedTags})) is not null
- OR data like @PlaylistOwnerUserId)
- """);
- }
- else
- {
- whereClauses.Add("((select CleanValue from ItemValues where ItemId=Guid and Type=6 and cleanvalue in (" + includedTags + ")) is not null)");
- }
+ // A playlist should be accessible to its owner regardless of allowed tags.
+ else if (includeTypes.Length == 1 && includeTypes.FirstOrDefault() is BaseItemKind.Playlist)
+ {
+ baseQuery = baseQuery
+ .Where(e => e.ItemValues!.Where(e => e.Type == 6)
+ .Any(f => query.IncludeInheritedTags.Contains(f.CleanValue)) || e.Data!.Contains($"OwnerUserId\":\"{query.User!.Id:N}\""));
+ // d ^^ this is stupid it hate this.
}
else
{
- for (int index = 0; index < query.IncludeInheritedTags.Length; index++)
- {
- statement.TryBind(paramName + index, GetCleanValue(query.IncludeInheritedTags[index]));
- }
-
- if (query.User is not null)
- {
- statement.TryBind("@PlaylistOwnerUserId", $"""%"OwnerUserId":"{query.User.Id.ToString("N")}"%""");
- }
+ baseQuery = baseQuery
+ .Where(e => e.ItemValues!.Where(e => e.Type == 6)
+ .Any(f => query.IncludeInheritedTags.Contains(f.CleanValue)));
}
}
if (query.SeriesStatuses.Length > 0)
{
- var statuses = new List<string>();
-
- foreach (var seriesStatus in query.SeriesStatuses)
- {
- statuses.Add("data like '%" + seriesStatus + "%'");
- }
-
- whereClauses.Add("(" + string.Join(" OR ", statuses) + ")");
+ baseQuery = baseQuery
+ .Where(e => query.SeriesStatuses.Any(f => e.Data!.Contains(f.ToString(), StringComparison.InvariantCultureIgnoreCase)));
}
if (query.BoxSetLibraryFolders.Length > 0)
{
- var folderIdQueries = new List<string>();
-
- foreach (var folderId in query.BoxSetLibraryFolders)
- {
- folderIdQueries.Add("data like '%" + folderId.ToString("N", CultureInfo.InvariantCulture) + "%'");
- }
-
- whereClauses.Add("(" + string.Join(" OR ", folderIdQueries) + ")");
+ baseQuery = baseQuery
+ .Where(e => query.BoxSetLibraryFolders.Any(f => e.Data!.Contains(f.ToString("N", CultureInfo.InvariantCulture), StringComparison.InvariantCultureIgnoreCase)));
}
if (query.VideoTypes.Length > 0)
{
- var videoTypes = new List<string>();
-
- foreach (var videoType in query.VideoTypes)
- {
- videoTypes.Add("data like '%\"VideoType\":\"" + videoType + "\"%'");
- }
-
- whereClauses.Add("(" + string.Join(" OR ", videoTypes) + ")");
+ var videoTypeBs = query.VideoTypes.Select(e => $"\"VideoType\":\"" + e + "\"");
+ baseQuery = baseQuery
+ .Where(e => videoTypeBs.Any(f => e.Data!.Contains(f, StringComparison.InvariantCultureIgnoreCase)));
}
if (query.Is3D.HasValue)
{
if (query.Is3D.Value)
{
- whereClauses.Add("data like '%Video3DFormat%'");
+ baseQuery = baseQuery
+ .Where(e => e.Data!.Contains("Video3DFormat", StringComparison.InvariantCultureIgnoreCase));
}
else
{
- whereClauses.Add("data not like '%Video3DFormat%'");
+ baseQuery = baseQuery
+ .Where(e => !e.Data!.Contains("Video3DFormat", StringComparison.InvariantCultureIgnoreCase));
}
}
@@ -1090,11 +1012,13 @@ public class BaseItemManager : IItemRepository
{
if (query.IsPlaceHolder.Value)
{
- whereClauses.Add("data like '%\"IsPlaceHolder\":true%'");
+ baseQuery = baseQuery
+ .Where(e => e.Data!.Contains("IsPlaceHolder\":true", StringComparison.InvariantCultureIgnoreCase));
}
else
{
- whereClauses.Add("(data is null or data not like '%\"IsPlaceHolder\":true%')");
+ baseQuery = baseQuery
+ .Where(e => !e.Data!.Contains("IsPlaceHolder\":true", StringComparison.InvariantCultureIgnoreCase));
}
}
@@ -1102,47 +1026,27 @@ public class BaseItemManager : IItemRepository
{
if (query.HasSpecialFeature.Value)
{
- whereClauses.Add("ExtraIds not null");
- }
- else
- {
- whereClauses.Add("ExtraIds is null");
- }
- }
-
- if (query.HasTrailer.HasValue)
- {
- if (query.HasTrailer.Value)
- {
- whereClauses.Add("ExtraIds not null");
- }
- else
- {
- whereClauses.Add("ExtraIds is null");
- }
- }
-
- if (query.HasThemeSong.HasValue)
- {
- if (query.HasThemeSong.Value)
- {
- whereClauses.Add("ExtraIds not null");
+ baseQuery = baseQuery
+ .Where(e => e.ExtraIds != null);
}
else
{
- whereClauses.Add("ExtraIds is null");
+ baseQuery = baseQuery
+ .Where(e => e.ExtraIds == null);
}
}
- if (query.HasThemeVideo.HasValue)
+ if (query.HasTrailer.HasValue || query.HasThemeSong.HasValue || query.HasThemeVideo.HasValue)
{
- if (query.HasThemeVideo.Value)
+ if (query.HasTrailer.GetValueOrDefault() || query.HasThemeSong.GetValueOrDefault() || query.HasThemeVideo.GetValueOrDefault())
{
- whereClauses.Add("ExtraIds not null");
+ baseQuery = baseQuery
+ .Where(e => e.ExtraIds != null);
}
else
{
- whereClauses.Add("ExtraIds is null");
+ baseQuery = baseQuery
+ .Where(e => e.ExtraIds == null);
}
}
}
@@ -1867,4 +1771,47 @@ public class BaseItemManager : IItemRepository
return image;
}
+
+ private List<string> GetItemByNameTypesInQuery(InternalItemsQuery query)
+ {
+ var list = new List<string>();
+
+ if (IsTypeInQuery(BaseItemKind.Person, query))
+ {
+ list.Add(typeof(Person).FullName!);
+ }
+
+ if (IsTypeInQuery(BaseItemKind.Genre, query))
+ {
+ list.Add(typeof(Genre).FullName!);
+ }
+
+ if (IsTypeInQuery(BaseItemKind.MusicGenre, query))
+ {
+ list.Add(typeof(MusicGenre).FullName!);
+ }
+
+ if (IsTypeInQuery(BaseItemKind.MusicArtist, query))
+ {
+ list.Add(typeof(MusicArtist).FullName!);
+ }
+
+ if (IsTypeInQuery(BaseItemKind.Studio, query))
+ {
+ list.Add(typeof(Studio).FullName!);
+ }
+
+ return list;
+ }
+
+ private bool IsTypeInQuery(BaseItemKind type, InternalItemsQuery query)
+ {
+ if (query.ExcludeItemTypes.Contains(type))
+ {
+ return false;
+ }
+
+ return query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(type);
+ }
+
}