aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBond-009 <bond.009@outlook.com>2026-03-13 20:23:23 +0100
committerGitHub <noreply@github.com>2026-03-13 20:23:23 +0100
commit7b7800435a0aba4c5dfa5b7e628ef8558036aa6f (patch)
treec0458f409cde72f2e4b9e3b070f858d68ca78bef
parent136ec00f3e63ac53f7ccb8005b023fa9e1108389 (diff)
parent946c6b9981145d73a6cd64fc6fbcbd6d5b6961ae (diff)
Merge pull request #16398 from Bond-009/invalidfilters
Return BadRequest when an invalid set of filters is given
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs2
-rw-r--r--Jellyfin.Api/Controllers/ArtistsController.cs68
-rw-r--r--Jellyfin.Api/Controllers/ChannelsController.cs70
-rw-r--r--Jellyfin.Api/Controllers/ItemsController.cs34
-rw-r--r--MediaBrowser.Controller/Entities/InternalItemsQuery.cs71
-rw-r--r--tests/Jellyfin.Controller.Tests/Entities/InternalItemsQueryTests.cs26
6 files changed, 104 insertions, 167 deletions
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index f7f5c387e..eee87c4d8 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -2289,7 +2289,7 @@ namespace Emby.Server.Implementations.Library
if (item is null)
{
- return new List<Folder>();
+ return [];
}
return GetCollectionFoldersInternal(item, allUserRootChildren);
diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs
index 642790f94..99b0fde06 100644
--- a/Jellyfin.Api/Controllers/ArtistsController.cs
+++ b/Jellyfin.Api/Controllers/ArtistsController.cs
@@ -187,39 +187,7 @@ public class ArtistsController : BaseJellyfinApiController
}).Where(i => i is not null).Select(i => i!.Id).ToArray();
}
- foreach (var filter in filters)
- {
- switch (filter)
- {
- case ItemFilter.Dislikes:
- query.IsLiked = false;
- break;
- case ItemFilter.IsFavorite:
- query.IsFavorite = true;
- break;
- case ItemFilter.IsFavoriteOrLikes:
- query.IsFavoriteOrLiked = true;
- break;
- case ItemFilter.IsFolder:
- query.IsFolder = true;
- break;
- case ItemFilter.IsNotFolder:
- query.IsFolder = false;
- break;
- case ItemFilter.IsPlayed:
- query.IsPlayed = true;
- break;
- case ItemFilter.IsResumable:
- query.IsResumable = true;
- break;
- case ItemFilter.IsUnplayed:
- query.IsPlayed = false;
- break;
- case ItemFilter.Likes:
- query.IsLiked = true;
- break;
- }
- }
+ query.ApplyFilters(filters);
var result = _libraryManager.GetArtists(query);
@@ -390,39 +358,7 @@ public class ArtistsController : BaseJellyfinApiController
}).Where(i => i is not null).Select(i => i!.Id).ToArray();
}
- foreach (var filter in filters)
- {
- switch (filter)
- {
- case ItemFilter.Dislikes:
- query.IsLiked = false;
- break;
- case ItemFilter.IsFavorite:
- query.IsFavorite = true;
- break;
- case ItemFilter.IsFavoriteOrLikes:
- query.IsFavoriteOrLiked = true;
- break;
- case ItemFilter.IsFolder:
- query.IsFolder = true;
- break;
- case ItemFilter.IsNotFolder:
- query.IsFolder = false;
- break;
- case ItemFilter.IsPlayed:
- query.IsPlayed = true;
- break;
- case ItemFilter.IsResumable:
- query.IsResumable = true;
- break;
- case ItemFilter.IsUnplayed:
- query.IsPlayed = false;
- break;
- case ItemFilter.Likes:
- query.IsLiked = true;
- break;
- }
- }
+ query.ApplyFilters(filters);
var result = _libraryManager.GetAlbumArtists(query);
diff --git a/Jellyfin.Api/Controllers/ChannelsController.cs b/Jellyfin.Api/Controllers/ChannelsController.cs
index 880b3a82d..0d85b3a0d 100644
--- a/Jellyfin.Api/Controllers/ChannelsController.cs
+++ b/Jellyfin.Api/Controllers/ChannelsController.cs
@@ -136,45 +136,13 @@ public class ChannelsController : BaseJellyfinApiController
{
Limit = limit,
StartIndex = startIndex,
- ChannelIds = new[] { channelId },
+ ChannelIds = [channelId],
ParentId = folderId ?? Guid.Empty,
OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder),
DtoOptions = new DtoOptions { Fields = fields }
};
- foreach (var filter in filters)
- {
- switch (filter)
- {
- case ItemFilter.IsFolder:
- query.IsFolder = true;
- break;
- case ItemFilter.IsNotFolder:
- query.IsFolder = false;
- break;
- case ItemFilter.IsUnplayed:
- query.IsPlayed = false;
- break;
- case ItemFilter.IsPlayed:
- query.IsPlayed = true;
- break;
- case ItemFilter.IsFavorite:
- query.IsFavorite = true;
- break;
- case ItemFilter.IsResumable:
- query.IsResumable = true;
- break;
- case ItemFilter.Likes:
- query.IsLiked = true;
- break;
- case ItemFilter.Dislikes:
- query.IsLiked = false;
- break;
- case ItemFilter.IsFavoriteOrLikes:
- query.IsFavoriteOrLiked = true;
- break;
- }
- }
+ query.ApplyFilters(filters);
return await _channelManager.GetChannelItems(query, CancellationToken.None).ConfigureAwait(false);
}
@@ -215,39 +183,7 @@ public class ChannelsController : BaseJellyfinApiController
DtoOptions = new DtoOptions { Fields = fields }
};
- foreach (var filter in filters)
- {
- switch (filter)
- {
- case ItemFilter.IsFolder:
- query.IsFolder = true;
- break;
- case ItemFilter.IsNotFolder:
- query.IsFolder = false;
- break;
- case ItemFilter.IsUnplayed:
- query.IsPlayed = false;
- break;
- case ItemFilter.IsPlayed:
- query.IsPlayed = true;
- break;
- case ItemFilter.IsFavorite:
- query.IsFavorite = true;
- break;
- case ItemFilter.IsResumable:
- query.IsResumable = true;
- break;
- case ItemFilter.Likes:
- query.IsLiked = true;
- break;
- case ItemFilter.Dislikes:
- query.IsLiked = false;
- break;
- case ItemFilter.IsFavoriteOrLikes:
- query.IsFavoriteOrLiked = true;
- break;
- }
- }
+ query.ApplyFilters(filters);
return await _channelManager.GetLatestChannelItems(query, CancellationToken.None).ConfigureAwait(false);
}
diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs
index 9674ecd09..091a0c8c7 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -386,39 +386,7 @@ public class ItemsController : BaseJellyfinApiController
query.CollapseBoxSetItems = false;
}
- foreach (var filter in filters)
- {
- switch (filter)
- {
- case ItemFilter.Dislikes:
- query.IsLiked = false;
- break;
- case ItemFilter.IsFavorite:
- query.IsFavorite = true;
- break;
- case ItemFilter.IsFavoriteOrLikes:
- query.IsFavoriteOrLiked = true;
- break;
- case ItemFilter.IsFolder:
- query.IsFolder = true;
- break;
- case ItemFilter.IsNotFolder:
- query.IsFolder = false;
- break;
- case ItemFilter.IsPlayed:
- query.IsPlayed = true;
- break;
- case ItemFilter.IsResumable:
- query.IsResumable = true;
- break;
- case ItemFilter.IsUnplayed:
- query.IsPlayed = false;
- break;
- case ItemFilter.Likes:
- query.IsLiked = true;
- break;
- }
- }
+ query.ApplyFilters(filters);
// Filter by Series Status
if (seriesStatus.Length != 0)
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index 076a59292..ecbeefbb9 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -10,6 +10,7 @@ using Jellyfin.Database.Implementations.Entities;
using Jellyfin.Database.Implementations.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Querying;
namespace MediaBrowser.Controller.Entities
{
@@ -388,5 +389,75 @@ namespace MediaBrowser.Controller.Entities
User = user;
}
+
+ public void ApplyFilters(ItemFilter[] filters)
+ {
+ static void ThrowConflictingFilters()
+ => throw new ArgumentException("Conflicting filters", nameof(filters));
+
+ foreach (var filter in filters)
+ {
+ switch (filter)
+ {
+ case ItemFilter.IsFolder:
+ if (filters.Contains(ItemFilter.IsNotFolder))
+ {
+ ThrowConflictingFilters();
+ }
+
+ IsFolder = true;
+ break;
+ case ItemFilter.IsNotFolder:
+ if (filters.Contains(ItemFilter.IsFolder))
+ {
+ ThrowConflictingFilters();
+ }
+
+ IsFolder = false;
+ break;
+ case ItemFilter.IsUnplayed:
+ if (filters.Contains(ItemFilter.IsPlayed))
+ {
+ ThrowConflictingFilters();
+ }
+
+ IsPlayed = false;
+ break;
+ case ItemFilter.IsPlayed:
+ if (filters.Contains(ItemFilter.IsUnplayed))
+ {
+ ThrowConflictingFilters();
+ }
+
+ IsPlayed = true;
+ break;
+ case ItemFilter.IsFavorite:
+ IsFavorite = true;
+ break;
+ case ItemFilter.IsResumable:
+ IsResumable = true;
+ break;
+ case ItemFilter.Likes:
+ if (filters.Contains(ItemFilter.Dislikes))
+ {
+ ThrowConflictingFilters();
+ }
+
+ IsLiked = true;
+ break;
+ case ItemFilter.Dislikes:
+ if (filters.Contains(ItemFilter.Likes))
+ {
+ ThrowConflictingFilters();
+ }
+
+ IsLiked = false;
+ break;
+ case ItemFilter.IsFavoriteOrLikes:
+ IsFavoriteOrLiked = true;
+ break;
+ }
+ }
+ }
}
}
diff --git a/tests/Jellyfin.Controller.Tests/Entities/InternalItemsQueryTests.cs b/tests/Jellyfin.Controller.Tests/Entities/InternalItemsQueryTests.cs
new file mode 100644
index 000000000..7093b2500
--- /dev/null
+++ b/tests/Jellyfin.Controller.Tests/Entities/InternalItemsQueryTests.cs
@@ -0,0 +1,26 @@
+using System;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Querying;
+using Xunit;
+
+namespace Jellyfin.Controller.Tests.Entities;
+
+public class InternalItemsQueryTests
+{
+ public static TheoryData<ItemFilter[]> ApplyFilters_Invalid()
+ {
+ var data = new TheoryData<ItemFilter[]>();
+ data.Add([ItemFilter.IsFolder, ItemFilter.IsNotFolder]);
+ data.Add([ItemFilter.IsPlayed, ItemFilter.IsUnplayed]);
+ data.Add([ItemFilter.Likes, ItemFilter.Dislikes]);
+ return data;
+ }
+
+ [Theory]
+ [MemberData(nameof(ApplyFilters_Invalid))]
+ public void ApplyFilters_Invalid_ThrowsArgumentException(ItemFilter[] filters)
+ {
+ var query = new InternalItemsQuery();
+ Assert.Throws<ArgumentException>(() => query.ApplyFilters(filters));
+ }
+}