Merge pull request #17579 from Shadowghost/fix-removal-notification
Fix missing ItemRemoved events and search fallback after access filtering
This commit is contained in:
@@ -412,6 +412,13 @@ namespace Emby.Server.Implementations.Library
|
||||
}
|
||||
|
||||
_persistenceService.DeleteItem([.. pathMaps.Select(f => f.Item.Id)]);
|
||||
|
||||
// Evict the deleted items from the cache and announce each removal.
|
||||
foreach (var (item, _, _) in pathMaps)
|
||||
{
|
||||
_cache.TryRemove(item.Id, out _);
|
||||
ReportItemRemoved(item, item.GetOwner() ?? item.GetParent());
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteItem(BaseItem item, DeleteOptions options, BaseItem parent, bool notifyParentItem)
|
||||
@@ -611,6 +618,12 @@ namespace Emby.Server.Implementations.Library
|
||||
folder.UserData = null;
|
||||
}
|
||||
|
||||
// Announce the descendants before the item itself.
|
||||
foreach (var child in children)
|
||||
{
|
||||
ReportItemRemoved(child, item);
|
||||
}
|
||||
|
||||
ReportItemRemoved(item, parent);
|
||||
}
|
||||
|
||||
|
||||
@@ -92,37 +92,33 @@ public class SearchManager : ISearchManager
|
||||
await Task.WhenAll(externalTask, internalTask).ConfigureAwait(false);
|
||||
|
||||
var externalResults = await externalTask.ConfigureAwait(false);
|
||||
var fromExternal = externalResults.Count > 0;
|
||||
IReadOnlyList<SearchResult> results;
|
||||
if (fromExternal)
|
||||
{
|
||||
results = externalResults;
|
||||
}
|
||||
else
|
||||
{
|
||||
results = await internalTask.ConfigureAwait(false);
|
||||
if (_internalProviders.Length > 0)
|
||||
{
|
||||
_logger.LogDebug("No results from external providers, using internal provider results");
|
||||
}
|
||||
}
|
||||
|
||||
// Internal providers apply user-access filtering inline in their queries. External
|
||||
// providers don't know about user permissions, so they may return IDs from hidden
|
||||
// libraries or items the user is otherwise blocked from. Run the post-filter only
|
||||
// when results came from externals to close that gap. The Items controller's second
|
||||
// roundtrip via folder.GetItems applies most of these again, but it does not restrict
|
||||
// by TopParentIds when ItemIds is set.
|
||||
if (fromExternal && results.Count > 0 && query.UserId.HasValue && !query.UserId.Value.IsEmpty())
|
||||
// libraries or items the user is otherwise blocked from. Filter them here to close
|
||||
// that gap. The Items controller's second roundtrip via folder.GetItems applies most
|
||||
// of these again, but it does not restrict by TopParentIds when ItemIds is set.
|
||||
if (externalResults.Count > 0 && query.UserId.HasValue && !query.UserId.Value.IsEmpty())
|
||||
{
|
||||
var user = _userManager.GetUserById(query.UserId.Value);
|
||||
if (user is not null)
|
||||
{
|
||||
results = await FilterByUserAccessAsync(results, user, query, cancellationToken).ConfigureAwait(false);
|
||||
externalResults = await FilterByUserAccessAsync(externalResults, user, query, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
if (externalResults.Count > 0)
|
||||
{
|
||||
return externalResults;
|
||||
}
|
||||
|
||||
var internalResults = await internalTask.ConfigureAwait(false);
|
||||
if (_internalProviders.Length > 0)
|
||||
{
|
||||
_logger.LogDebug("No results from external providers, using internal provider results");
|
||||
}
|
||||
|
||||
return internalResults;
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyList<SearchResult>> FilterByUserAccessAsync(
|
||||
|
||||
@@ -19,7 +19,9 @@ public class SearchProviderQuery
|
||||
public Guid? UserId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item types to include in the search.
|
||||
/// Gets the item types to include in the search. An empty array means every type is eligible.
|
||||
/// When this is non-empty it is the authoritative type filter and <see cref="ExcludeItemTypes"/>
|
||||
/// does not apply; excludes only take effect when no include types were requested.
|
||||
/// </summary>
|
||||
public BaseItemKind[] IncludeItemTypes { get; init; } = [];
|
||||
|
||||
@@ -29,7 +31,9 @@ public class SearchProviderQuery
|
||||
public BaseItemKind[] ExcludeItemTypes { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the media types to include in the search.
|
||||
/// Gets the media types to include in the search. This is an additional constraint rather than
|
||||
/// an alternative one: a provider must return only items that match both the requested media
|
||||
/// types and the requested item types, not the union of the two.
|
||||
/// </summary>
|
||||
public MediaType[] MediaTypes { get; init; } = [];
|
||||
|
||||
@@ -39,7 +43,9 @@ public class SearchProviderQuery
|
||||
public int? Limit { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent ID to scope the search.
|
||||
/// Gets the parent ID to scope the search. This scopes to the whole subtree, not just direct
|
||||
/// children - callers routinely pass a library folder id and expect items nested arbitrarily
|
||||
/// deep beneath it (an episode under a season under a series) to match.
|
||||
/// </summary>
|
||||
public Guid? ParentId { get; init; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user