Merge remote-tracking branch 'upstream/master' into security-path-traversal-fixes
# Conflicts: # Jellyfin.Api/Controllers/HlsSegmentController.cs # Jellyfin.Api/Controllers/PluginsController.cs
This commit is contained in:
@@ -447,6 +447,7 @@ public sealed partial class BaseItemRepository
|
||||
if (filter.IncludeInheritedTags.Length > 0)
|
||||
{
|
||||
var includeTags = filter.IncludeInheritedTags.Select(e => e.GetCleanValue()).ToArray();
|
||||
var personTypeName = _itemTypeLookup.BaseItemKindNames[BaseItemKind.Person];
|
||||
var allowedTagItemIds = context.ItemValuesMap
|
||||
.Where(f => f.ItemValue.Type == ItemValueType.Tags && includeTags.Contains(f.ItemValue.CleanValue))
|
||||
.Select(f => f.ItemId);
|
||||
@@ -455,7 +456,10 @@ public sealed partial class BaseItemRepository
|
||||
allowedTagItemIds.Contains(e.Id)
|
||||
|| (e.SeriesId.HasValue && allowedTagItemIds.Contains(e.SeriesId.Value))
|
||||
|| e.Parents!.Any(p => allowedTagItemIds.Contains(p.ParentItemId))
|
||||
|| (e.TopParentId.HasValue && allowedTagItemIds.Contains(e.TopParentId.Value)));
|
||||
|| (e.TopParentId.HasValue && allowedTagItemIds.Contains(e.TopParentId.Value))
|
||||
|
||||
// People don't carry the tags of the media they appear in and would never match
|
||||
|| e.Type == personTypeName);
|
||||
}
|
||||
|
||||
// Exclude alternate versions (have PrimaryVersionId set) and owned non-extra items.
|
||||
|
||||
@@ -557,8 +557,13 @@ public sealed partial class BaseItemRepository
|
||||
: baseQuery.Where(e => inProgressIds.Contains(e.Id));
|
||||
|
||||
// When several versions of the same item are in progress, keep only the most recently played one, use id as tiebreaker.
|
||||
// Only in-progress siblings can eliminate a candidate: a version without progress has a NULL max LastPlayedDate,
|
||||
// which is never greater and never ties. Restricting the sibling scan to the in-progress set keeps this bounded by
|
||||
// the user's Continue Watching count instead of forcing a full BaseItems scan (COALESCE keys are non-indexable) per row.
|
||||
baseQuery = baseQuery.Where(e => e.Type == seriesTypeName || !context.BaseItems
|
||||
.Where(s => s.Id != e.Id && (s.PrimaryVersionId ?? s.Id) == (e.PrimaryVersionId ?? e.Id))
|
||||
.Where(s => s.Id != e.Id
|
||||
&& inProgressIds.Contains(s.Id)
|
||||
&& (s.PrimaryVersionId ?? s.Id) == (e.PrimaryVersionId ?? e.Id))
|
||||
.Any(s =>
|
||||
inProgress.Where(su => su.ItemId == s.Id).Max(su => su.LastPlayedDate)
|
||||
> inProgress.Where(eu => eu.ItemId == e.Id).Max(eu => eu.LastPlayedDate)
|
||||
@@ -1084,6 +1089,7 @@ public sealed partial class BaseItemRepository
|
||||
{
|
||||
var includeTags = filter.IncludeInheritedTags.Select(e => e.GetCleanValue()).ToArray();
|
||||
var isPlaylistOnlyQuery = includeTypes.Length == 1 && includeTypes.FirstOrDefault() == BaseItemKind.Playlist;
|
||||
var personTypeName = _itemTypeLookup.BaseItemKindNames[BaseItemKind.Person];
|
||||
var allowedTagItemIds = context.ItemValuesMap
|
||||
.Where(f => f.ItemValue.Type == ItemValueType.Tags && includeTags.Contains(f.ItemValue.CleanValue))
|
||||
.Select(f => f.ItemId);
|
||||
@@ -1094,6 +1100,9 @@ public sealed partial class BaseItemRepository
|
||||
|| e.Parents!.Any(p => allowedTagItemIds.Contains(p.ParentItemId))
|
||||
|| (e.TopParentId.HasValue && allowedTagItemIds.Contains(e.TopParentId.Value))
|
||||
|
||||
// People don't carry the tags of the media they appear in and would never match
|
||||
|| e.Type == personTypeName
|
||||
|
||||
// A playlist should be accessible to its owner regardless of allowed tags
|
||||
|| (isPlaylistOnlyQuery && e.Data!.Contains($"OwnerUserId\":\"{filter.User!.Id:N}\"")));
|
||||
}
|
||||
|
||||
@@ -616,6 +616,12 @@ namespace Jellyfin.Server.Implementations.Users
|
||||
.SetProperty(f => f.LastActivityDate, date)
|
||||
.SetProperty(f => f.LastLoginDate, date))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// ExecuteUpdateAsync bypasses the change tracker, so keep the
|
||||
// returned entity in sync. Otherwise SessionManager.LogSessionActivity
|
||||
// saves this (stale) entity in full and reverts LastLoginDate.
|
||||
user.LastActivityDate = date;
|
||||
user.LastLoginDate = date;
|
||||
}
|
||||
|
||||
await dbContext.Users
|
||||
@@ -883,8 +889,20 @@ namespace Jellyfin.Server.Implementations.Users
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
dbContext.Remove(user.ProfileImage);
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
// Remove the tracked profile image loaded from the database instead of the
|
||||
// detached instance on the passed in user. That instance can carry a stale,
|
||||
// never-persisted (temporary) key, which makes EF Core throw when it is marked
|
||||
// for deletion, leaving the profile image impossible to clear or replace.
|
||||
var dbUser = await UserQuery(dbContext)
|
||||
.AsTracking()
|
||||
.FirstOrDefaultAsync(u => u.Id == user.Id)
|
||||
.ConfigureAwait(false);
|
||||
if (dbUser?.ProfileImage is not null)
|
||||
{
|
||||
dbContext.Remove(dbUser.ProfileImage);
|
||||
dbUser.ProfileImage = null;
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
user.ProfileImage = null;
|
||||
|
||||
Reference in New Issue
Block a user