The release and nightly workflows stamp a date-based version into
build.yaml (1.0.20260729.42). That build segment exceeds the 16-bit
limit AssemblyVersion and FileVersion require, so it would fail the
compile with CS7034 if it reached them.
Directory.Build.targets is imported after Directory.Build.props and is
not rewritten by CI, so pinning the assembly identity there keeps those
builds working. The manifest version is untouched - Jellyfin identifies
plugins by GUID plus manifest version, not assembly version. Verified by
building with a date-based version injected.
Previously the shared account's libraries were chosen independently of
its members, so a group could see a library that one of its members was
blocked from - joining a group became a way to gain access. That was
especially sharp with auto-created groups, where no admin is in the loop.
A shared account is now granted exactly the libraries every member can
already reach. If one member is blocked from a library, no group
containing them can see it. The account is therefore always a subset of
what each member could reach alone, which is what makes creating groups
at the login screen safe to leave on by default.
Details:
- "Enable all folders" is expanded to concrete library ids before
intersecting, since it cannot otherwise be compared with an explicit
list. Shared accounts are always given an explicit list, never the
all-folders permission, so newly added libraries do not silently widen
an existing group.
- Explicitly blocked folders are subtracted even for members who
otherwise have access to everything.
- Fails closed: an unresolvable member contributes no access rather than
being treated as unrestricted.
- Recomputed when membership changes, and re-applied to every group at
startup so narrowing a member's own access narrows their groups.
Drops the now-meaningless EnableAllFolders/EnabledFolders provisioning
inputs and the DynamicGroupsEnableAllFolders setting. Adds 8 tests
covering the intersection rules.
Replaces the plugin template with a working plugin that lets several
users share one viewing account while keeping their individual watched
lists accurate.
Three pieces:
- Auto-creating groups. Logging in as "alice+bob" with any named
member's own password provisions the shared account and signs you in.
Verified against 10.11.5: AuthenticateUser offers unmatched usernames
to every enabled provider and re-queries afterwards, which is the hook
this relies on. Gated on a real member password so knowing two
usernames is not enough to create an account.
- Multi-password authentication. IRequiresResolvedUser hands us the
resolved shared account; each member's live stored hash is checked via
ICryptoProvider.Verify. Deliberately avoids re-entering
UserManager.AuthenticateUser, which would trip every member's
failed-attempt counter whenever a different member's password matched.
- One-way played-state sync. Shared account to members only, filtered to
PlaybackFinished/TogglePlayed/Import so playback progress ticks are
ignored. No loop guard needed: member writes carry a non-shared id.
Membership is stored as user IDs rather than re-parsed from the username,
so shared accounts can be renamed freely. The +/name collision resolves
itself because Jellyfin only consults the plugin when no local user
matches the typed name.
Targets Jellyfin 10.11.x / net9.0. Adds Gitea CI (test, build, release),
a builder image, and 34 tests covering the auth and sync rules.