stop a shared season handing over the rest of the series

Sharing one season let the guest see every season of that show. Sharing a whole
series or a single episode was fine, which is what made it look like a season
specific problem.

The cause is that the tag went upwards as well as downwards. BuildTagTreeTargets
added a season's parent series, so the guest's tag landed on the series itself,
and Jellyfin's GetInheritedTags is an item's own tags plus every ancestor's, with
AllowedTags matched against that. Tagging the series therefore made every other
season and episode under it inherit the tag and become visible. Sharing a series
looked correct because the whole show is meant to be visible, and sharing an
episode looked correct because nothing above it was ever tagged.

Tagging now only ever goes down: the shared item, and its descendants when it is a
folder. A guest sent one season gets that season and its episodes, and the series
page is not theirs to open, which is what sharing a season should mean.

Removal deliberately still reaches up to the parent series. Links created by
earlier builds put the tag there, and taking a tag off can only remove access, so
those get cleaned up when the link expires or is revoked instead of being stranded
on the series forever.
Cette révision appartient à :
Franciskid
2026-07-27 00:34:55 +02:00
Parent a811095784
révision ad17b17ab4
2 fichiers modifiés avec 48 ajouts et 19 suppressions
+35 -12
Voir le fichier
@@ -77,10 +77,10 @@ public sealed class ItemTagService
}
/// <summary>
/// Ensures the supplied tag is present on the item and, for a season or folder,
/// on the item's related tree (parent series for a season; all recursive
/// children for a folder such as a series or season) so a guest can browse the
/// whole shared branch instead of only the single node the link was created on.
/// Ensures the supplied tag is present on the item and, when the item is a folder
/// such as a series or a season, on everything underneath it, so a guest can
/// browse down through the shared branch instead of only seeing the single node
/// the link was created on.
/// </summary>
public async Task<bool> EnsureTagTreeAsync(BaseItem item, string tag, CancellationToken cancellationToken)
{
@@ -107,8 +107,8 @@ public sealed class ItemTagService
}
/// <summary>
/// Removes the supplied tag from the item and, for a season or folder, from the
/// item's related tree (mirrors <see cref="EnsureTagTreeAsync"/>).
/// Removes the supplied tag from the item and, when it is a folder, from
/// everything underneath it (mirrors <see cref="EnsureTagTreeAsync"/>).
/// </summary>
public async Task<bool> RemoveTagTreeAsync(BaseItem item, string tag, CancellationToken cancellationToken)
{
@@ -117,7 +117,7 @@ public sealed class ItemTagService
throw new ArgumentNullException(nameof(item));
}
var targets = BuildTagTreeTargets(item);
var targets = BuildTagRemovalTargets(item);
var changed = false;
foreach (var target in targets)
{
@@ -134,10 +134,38 @@ public sealed class ItemTagService
return changed;
}
/// <summary>
/// The items that carry a share's tag: the shared item itself and, when it is a
/// folder, everything under it.
/// </summary>
/// <remarks>
/// Never the parents. Jellyfin's <c>GetInheritedTags</c> is an item's own tags
/// plus every ancestor's, and the guest's AllowedTags policy is matched against
/// that, so tagging a season's parent series would hand the guest every other
/// season of that series as well.
/// </remarks>
private static List<BaseItem> BuildTagTreeTargets(BaseItem item)
{
var targets = new List<BaseItem> { item };
if (item is Folder folder)
{
targets.AddRange(folder.GetRecursiveChildren());
}
return targets;
}
/// <summary>
/// Removal deliberately reaches one level further than tagging does: up to a
/// season's parent series. Builds before this fix put the share's tag on that
/// series, and taking a tag off can only ever remove access, so cleaning those
/// up is safe where applying them was not.
/// </summary>
private static List<BaseItem> BuildTagRemovalTargets(BaseItem item)
{
var targets = BuildTagTreeTargets(item);
if (item is Season season)
{
var series = season.Series ?? season.GetParent() as Series;
@@ -147,11 +175,6 @@ public sealed class ItemTagService
}
}
if (item is Folder folder)
{
targets.AddRange(folder.GetRecursiveChildren());
}
return targets;
}