Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
b844d03
feat(settings): add endpoint and UI for switching media server
0xSysR3ll Feb 20, 2026
328c870
feat(auth, settings): enforce admin permissions
0xSysR3ll Feb 20, 2026
dea776c
fix(settings): enable reinitialization for Tautulli settings form
0xSysR3ll Feb 20, 2026
15980fd
fix(settings): use proper error messages
0xSysR3ll Feb 20, 2026
e9d9cf0
fix(settings): allow jellyfin/emby unlinking before migration
0xSysR3ll Feb 20, 2026
654c673
fix: missing translations
0xSysR3ll Feb 20, 2026
d561bf5
fix(settings): remove unnecessary user ID condition for plex and jell…
0xSysR3ll Feb 22, 2026
5548f85
fix(MediaSubscriber): remove optional chaining for status checks
0xSysR3ll Feb 22, 2026
5f8fab2
feat(settings): add switching logic to support Jellyfin and Emby tran…
0xSysR3ll Feb 22, 2026
d5f1b78
fix(settings): remove condition for user plexId in update query
0xSysR3ll Feb 22, 2026
ebffa1e
fix(userSettings): improve account linking logic to prevent conflicts…
0xSysR3ll Feb 22, 2026
f90c986
fix(auth): update token storage logic for Plex when using Jellyfin or…
0xSysR3ll Feb 22, 2026
9944f71
feat(settings): log out all users after successful switch
0xSysR3ll Feb 22, 2026
3753d67
feat(userList): add badges for linked Plex and Jellyfin/Emby users
0xSysR3ll Feb 22, 2026
e44d511
feat(settings): add a proper modal for switching
0xSysR3ll Feb 22, 2026
16d4810
fix(settings): update deprecation messages and improve media server s…
0xSysR3ll Feb 22, 2026
be2ffbb
feat(settings): make code more dry
0xSysR3ll Feb 22, 2026
c500015
fix(settings): refine media server switch logic and update user instr…
0xSysR3ll Feb 22, 2026
07cd6fa
fix(settings): wrong link for users page
0xSysR3ll Feb 22, 2026
f5f01b9
fix(settings): correct SQL syntax for jellyfinUserId condition
0xSysR3ll Feb 22, 2026
4704eb2
fix: quote columns
0xSysR3ll Feb 22, 2026
101425e
fix(settings): prefer typeorm over raw sql queries
0xSysR3ll Feb 24, 2026
bdee83b
refactor(settings): remove success message after media server switch
0xSysR3ll Feb 24, 2026
23d31b2
fix(settings): revalidate user after media server switch
0xSysR3ll Feb 25, 2026
cb56089
fix(settings): handle email comparison for Jellyfin users
0xSysR3ll Feb 25, 2026
490762f
fix(settings): ensure jobs restart after media server switch
0xSysR3ll Feb 25, 2026
3603f68
refactor(auth): remove unused Plex user fields
0xSysR3ll Mar 9, 2026
9458562
fix(settings): don't ovewrite local users
0xSysR3ll Mar 9, 2026
4b59de2
refactor(settings): handle Plex settings update process
0xSysR3ll Mar 30, 2026
68df6f0
fix(settings): unify handling of Jellyfin and Emby usernames
0xSysR3ll Mar 30, 2026
507f612
fix: lint
0xSysR3ll Mar 30, 2026
9ccbc59
refactor(settings): remove unnecessary admin query
0xSysR3ll Mar 30, 2026
dfad869
fix: clarify media server switch sign-in instructions
0xSysR3ll Mar 30, 2026
5a33195
feat(settings): add missing i18n labels support
0xSysR3ll Mar 30, 2026
a6444c5
fix(settings): toast notifications import
0xSysR3ll May 11, 2026
13faf8c
feat(settings): let admins link other user's account
0xSysR3ll May 20, 2026
b251afa
fix(settings): enforce required fields in API
0xSysR3ll May 20, 2026
4af40d9
fix: missing i18n translations
0xSysR3ll May 20, 2026
adc8fcb
fix: missing jellyfinConfigured in settings response
0xSysR3ll May 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions seerr-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,10 @@ components:
format: uuid
description: Instance Plex OAuth client identifier
example: 6919275e-142a-48d8-be6b-93594cbd4626
jellyfinConfigured:
type: boolean
description: Whether a Jellyfin/Emby connection is configured.
example: true
MovieResult:
type: object
required:
Expand Down Expand Up @@ -2298,6 +2302,47 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/JellyfinSettings'
/settings/switch-media-server:
post:
summary: Switch media server
tags:
- settings
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
targetServerType:
type: string
enum: [jellyfin, emby, plex]
description: Target media server type. Required when switching from Plex (jellyfin or emby) or from Jellyfin/Emby (plex, jellyfin, or emby).
Comment thread
0xSysR3ll marked this conversation as resolved.
required:
- targetServerType
responses:
'200':
description: Media server cleared
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: 'Media server cleared. Restart or reload to configure a new server.'
'400':
description: No media server is configured
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: 'No media server is configured.'
'500':
description: Failed to switch media server
/settings/jellyfin/library:
get:
summary: Get Jellyfin libraries
Expand Down Expand Up @@ -5155,6 +5200,44 @@ paths:
description: Unlink request invalid
'404':
description: User does not exist
/user/{userId}/settings/linked-accounts/jellyfin/map:
post:
summary: Map a Jellyfin/Emby account to a user
description: Uses the configured Jellyfin/Emby connection to verify the selected server user, then maps that identity to the Seerr user without authenticating as the Jellyfin/Emby user or storing a user auth token. Requires administrator permissions.
tags:
- users
parameters:
- in: path
name: userId
required: true
schema:
type: number
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
jellyfinUserId:
type: string
description: Jellyfin/Emby user ID selected from the configured server user list.
example: '0123456789abcdef0123456789abcdef'
required:
- jellyfinUserId
responses:
'204':
description: Mapping account succeeded
'400':
description: Mapping request invalid or Jellyfin/Emby connection is not configured
'403':
description: User does not have permission to map this account
'404':
description: Seerr user or Jellyfin/Emby user does not exist
'422':
description: Account already linked to a user
'500':
description: Unable to map account
/user/{userId}/settings/notifications:
get:
summary: Get notification settings for a user
Expand Down
1 change: 1 addition & 0 deletions server/interfaces/api/settingsInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface PublicSettingsResponse {
jellyfinHost?: string;
jellyfinExternalHost?: string;
jellyfinServerName?: string;
jellyfinConfigured: boolean;
jellyfinForgotPasswordUrl?: string;
initialized: boolean;
applicationTitle: string;
Expand Down
2 changes: 2 additions & 0 deletions server/lib/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ interface FullPublicSettings extends PublicSettings {
jellyfinExternalHost?: string;
jellyfinForgotPasswordUrl?: string;
jellyfinServerName?: string;
jellyfinConfigured: boolean;
partialRequestsEnabled: boolean;
enableSpecialEpisodes: boolean;
cacheImages: boolean;
Expand Down Expand Up @@ -712,6 +713,7 @@ class Settings {
mediaServerLogin: this.data.main.mediaServerLogin,
jellyfinExternalHost: this.data.jellyfin.externalHostname,
jellyfinForgotPasswordUrl: this.data.jellyfin.jellyfinForgotPasswordUrl,
jellyfinConfigured: !!this.data.jellyfin.ip,
movie4kEnabled: this.data.radarr.some(
(radarr) => radarr.is4k && radarr.isDefault
),
Expand Down
2 changes: 1 addition & 1 deletion server/middleware/deprecation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const deprecatedRoute = ({
}: DeprecationOptions) => {
return (req: Request, res: Response, next: NextFunction) => {
logger.warn(
`Deprecated API endpoint accessed: ${oldPath} use ${newPath} instead`,
`Deprecated API endpoint accessed: ${oldPath} => use ${newPath} instead`,
{
label: 'API Deprecation',
ip: req.ip,
Expand Down
49 changes: 46 additions & 3 deletions server/routes/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,47 @@ authRoutes.post('/plex', async (req, res, next) => {
});
}

const mediaServerType = settings.main.mediaServerType;

if (
mediaServerType === MediaServerType.JELLYFIN ||
mediaServerType === MediaServerType.EMBY
) {
if (!req.user) {
return next({
status: 401,
message: 'Authentication required.',
});
}
if (!req.user.hasPermission(Permission.ADMIN)) {
return next({
status: 403,
message: 'Admin permissions required.',
});
}
try {
const plextv = new PlexTvAPI(body.authToken);
const account = await plextv.getUser();
const admin = await userRepository.findOneOrFail({
where: { id: 1 },
});
admin.plexToken = body.authToken;
admin.plexId = account.id;
admin.plexUsername = account.username;
await userRepository.save(admin);
return res.status(200).json({ email: admin.email });
Comment thread
0xSysR3ll marked this conversation as resolved.
} catch (e) {
logger.error('Failed to store Plex token for settings', {
label: 'API',
errorMessage: (e as Error).message,
});
return next({
status: 500,
message: 'Unable to validate Plex token.',
});
}
}
Comment thread
0xSysR3ll marked this conversation as resolved.

if (
settings.main.mediaServerType != MediaServerType.NOT_CONFIGURED &&
(settings.main.mediaServerLogin === false ||
Expand Down Expand Up @@ -410,9 +451,7 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
settings.jellyfin.apiKey = apiKey;
await settings.save();
startJobs();
}
// User already exists, let's update their information
else if (account.User.Id === user?.jellyfinUserId) {
} else if (account.User.Id === user?.jellyfinUserId) {
logger.info(
`Found matching ${
settings.main.mediaServerType === MediaServerType.JELLYFIN
Expand All @@ -431,6 +470,10 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
);
user.avatar = getUserAvatarUrl(user);
user.jellyfinUsername = account.User.Name;
user.userType =
settings.main.mediaServerType === MediaServerType.JELLYFIN
? UserType.JELLYFIN
: UserType.EMBY;

if (user.username === account.User.Name) {
user.username = '';
Expand Down
Loading
Loading