From 9e2f3f06393e71ba5d1c0ba3c9512b64a3ce3ad7 Mon Sep 17 00:00:00 2001 From: notfakie <103784113+notfakie@users.noreply.github.com> Date: Wed, 27 Apr 2022 08:06:39 +1200 Subject: [PATCH] feat: implement import users from Jellyfin button --- overseerr-api.yml | 43 +++ server/api/jellyfin.ts | 17 ++ server/routes/settings/index.ts | 28 ++ server/routes/user/index.ts | 81 ++++++ .../UserList/JellyfinImportModal.tsx | 251 ++++++++++++++++++ src/components/UserList/index.tsx | 41 ++- src/i18n/locale/ca.json | 2 +- src/i18n/locale/da.json | 2 +- src/i18n/locale/de.json | 2 +- src/i18n/locale/el.json | 2 +- src/i18n/locale/en.json | 2 +- src/i18n/locale/es.json | 2 +- src/i18n/locale/fr.json | 2 +- src/i18n/locale/hu.json | 2 +- src/i18n/locale/it.json | 2 +- src/i18n/locale/ja.json | 2 +- src/i18n/locale/nb_NO.json | 2 +- src/i18n/locale/nl.json | 2 +- src/i18n/locale/pl.json | 2 +- src/i18n/locale/pt_BR.json | 2 +- src/i18n/locale/pt_PT.json | 2 +- src/i18n/locale/ru.json | 2 +- src/i18n/locale/sq.json | 2 +- src/i18n/locale/sv.json | 2 +- src/i18n/locale/zh_Hans.json | 2 +- src/i18n/locale/zh_Hant.json | 2 +- 26 files changed, 468 insertions(+), 33 deletions(-) create mode 100644 src/components/UserList/JellyfinImportModal.tsx diff --git a/overseerr-api.yml b/overseerr-api.yml index 3ab87446f..c8797c820 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -1984,6 +1984,20 @@ paths: type: array items: $ref: '#/components/schemas/JellyfinLibrary' + /settings/jellyfin/users: + get: + summary: Get Jellyfin Users + description: Returns a list of Jellyfin Users in a JSON array. + tags: + - settings + - users + responses: + '200': + description: Jellyfin users returned + content: + application/json: + schema: + type: array /settings/jellyfin/sync: get: summary: Get status of full Jellyfin library sync @@ -3528,6 +3542,35 @@ paths: type: array items: $ref: '#/components/schemas/User' + /user/import-from-jellyfin: + post: + summary: Import all users from Jellyfin + description: | + Fetches and imports users from the Jellyfin server. + + Requires the `MANAGE_USERS` permission. + tags: + - users + requestBody: + required: false + content: + application/json: + schema: + type: object + properties: + jellyfinIds: + type: array + items: + type: string + responses: + '201': + description: A list of the newly created users + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' /user/registerPushSubscription: post: summary: Register a web push /user/registerPushSubscription diff --git a/server/api/jellyfin.ts b/server/api/jellyfin.ts index 7ced4f8d0..f6f6cb9db 100644 --- a/server/api/jellyfin.ts +++ b/server/api/jellyfin.ts @@ -15,6 +15,10 @@ export interface JellyfinLoginResponse { AccessToken: string; } +export interface JellyfinUserListResponse { + users: Array; +} + export interface JellyfinLibrary { type: 'show' | 'movie'; key: string; @@ -134,6 +138,19 @@ class JellyfinAPI { } } + public async getUsers(): Promise { + try { + const account = await this.axios.get(`/Users`); + return { users: account.data }; + } catch (e) { + logger.error( + `Something went wrong while getting the account from the Jellyfin server: ${e.message}`, + { label: 'Jellyfin API' } + ); + throw new Error('Invalid auth token'); + } + } + public async getUser(): Promise { try { const account = await this.axios.get( diff --git a/server/routes/settings/index.ts b/server/routes/settings/index.ts index 0379c3995..8a5ef9a28 100644 --- a/server/routes/settings/index.ts +++ b/server/routes/settings/index.ts @@ -301,6 +301,34 @@ settingsRoutes.get('/jellyfin/library', async (req, res) => { return res.status(200).json(settings.jellyfin.libraries); }); +settingsRoutes.get('/jellyfin/users', async (req, res) => { + const settings = getSettings(); + + const userRepository = getRepository(User); + const admin = await userRepository.findOneOrFail({ + select: ['id', 'jellyfinAuthToken', 'jellyfinDeviceId', 'jellyfinUserId'], + order: { id: 'ASC' }, + }); + const jellyfinClient = new JellyfinAPI( + settings.jellyfin.hostname ?? '', + admin.jellyfinAuthToken ?? '', + admin.jellyfinDeviceId ?? '' + ); + + jellyfinClient.setUserId(admin.jellyfinUserId ?? ''); + const resp = await jellyfinClient.getUsers(); + const users = resp.users.map((user) => ({ + username: user.Name, + id: user.Id, + thumb: user.PrimaryImageTag + ? `${settings.jellyfin.hostname}/Users/${user.Id}/Images/Primary/?tag=${user.PrimaryImageTag}&quality=90` + : '/os_logo_square.png', + email: user.Name, + })); + + return res.status(200).json(users); +}); + settingsRoutes.get('/jellyfin/sync', (_req, res) => { return res.status(200).json(jobJellyfinFullSync.status()); }); diff --git a/server/routes/user/index.ts b/server/routes/user/index.ts index a4e8861e5..b8ada46b0 100644 --- a/server/routes/user/index.ts +++ b/server/routes/user/index.ts @@ -2,6 +2,7 @@ import { Router } from 'express'; import gravatarUrl from 'gravatar-url'; import { findIndex, sortBy } from 'lodash'; import { getRepository, In, Not } from 'typeorm'; +import JellyfinAPI from '../../api/jellyfin'; import PlexTvAPI from '../../api/plextv'; import TautulliAPI from '../../api/tautulli'; import { MediaType } from '../../constants/media'; @@ -465,6 +466,86 @@ router.post( } ); +router.post( + '/import-from-jellyfin', + isAuthenticated(Permission.MANAGE_USERS), + async (req, res, next) => { + try { + const settings = getSettings(); + const userRepository = getRepository(User); + const body = req.body as { jellyfinUserIds: string[] }; + + // taken from auth.ts + const admin = await userRepository.findOneOrFail({ + select: [ + 'id', + 'jellyfinAuthToken', + 'jellyfinDeviceId', + 'jellyfinUserId', + ], + order: { id: 'ASC' }, + }); + const jellyfinClient = new JellyfinAPI( + settings.jellyfin.hostname ?? '', + admin.jellyfinAuthToken ?? '', + admin.jellyfinDeviceId ?? '' + ); + jellyfinClient.setUserId(admin.jellyfinUserId ?? ''); + + const jellyfinUsersResponse = await jellyfinClient.getUsers(); + const createdUsers: User[] = []; + for (const account of jellyfinUsersResponse.users) { + if (account.Name) { + const user = await userRepository + .createQueryBuilder('user') + .where('user.jellyfinUserId = :id', { id: account.Id }) + .orWhere('user.email = :email', { + email: account.Name, + }) + .getOne(); + + const avatar = account.PrimaryImageTag + ? `${settings.jellyfin.hostname}/Users/${account.Id}/Images/Primary/?tag=${account.PrimaryImageTag}&quality=90` + : '/os_logo_square.png'; + + if (user) { + // Update the user's avatar with their Jellyfin thumbnail, in case it changed + user.avatar = avatar; + user.email = account.Name; + user.jellyfinUsername = account.Name; + + // In case the user was previously a local account + if (user.userType === UserType.LOCAL) { + user.userType = UserType.JELLYFIN; + user.jellyfinUserId = account.Id; + } + await userRepository.save(user); + } else if (!body || body.jellyfinUserIds.includes(account.Id)) { + logger.error('CREATED USER', { + label: 'API', + }); + + const newUser = new User({ + jellyfinUsername: account.Name, + jellyfinUserId: account.Id, + email: account.Name, + permissions: settings.main.defaultPermissions, + avatar, + userType: UserType.JELLYFIN, + }); + await userRepository.save(newUser); + createdUsers.push(newUser); + } + } + } + + return res.status(201).json(User.filterMany(createdUsers)); + } catch (e) { + next({ status: 500, message: e.message }); + } + } +); + router.get<{ id: string }, QuotaResponse>( '/:id/quota', async (req, res, next) => { diff --git a/src/components/UserList/JellyfinImportModal.tsx b/src/components/UserList/JellyfinImportModal.tsx new file mode 100644 index 000000000..295938b2f --- /dev/null +++ b/src/components/UserList/JellyfinImportModal.tsx @@ -0,0 +1,251 @@ +import { InboxInIcon } from '@heroicons/react/solid'; +import axios from 'axios'; +import React, { useState } from 'react'; +import { defineMessages, useIntl } from 'react-intl'; +import { useToasts } from 'react-toast-notifications'; +import useSWR from 'swr'; +import useSettings from '../../hooks/useSettings'; +import globalMessages from '../../i18n/globalMessages'; +import Alert from '../Common/Alert'; +import Modal from '../Common/Modal'; + +interface JellyfinImportProps { + onCancel?: () => void; + onComplete?: () => void; +} + +const messages = defineMessages({ + importfromJellyfin: 'Import Jellyfin Users', + importfromJellyfinerror: + 'Something went wrong while importing Jellyfin users.', + importedfromJellyfin: + '{userCount} Jellyfin {userCount, plural, one {user} other {users}} imported successfully!', + user: 'User', + noJellyfinuserstoimport: 'There are no Jellyfin users to import.', + newJellyfinsigninenabled: + 'The Enable New Jellyfin Sign-In setting is currently enabled. Jellyfin users with library access do not need to be imported in order to sign in.', +}); + +const JellyfinImportModal: React.FC = ({ + onCancel, + onComplete, +}) => { + const intl = useIntl(); + const settings = useSettings(); + const { addToast } = useToasts(); + const [isImporting, setImporting] = useState(false); + const [selectedUsers, setSelectedUsers] = useState([]); + const { data, error } = useSWR< + { + id: string; + title: string; + username: string; + email: string; + thumb: string; + }[] + >(`/api/v1/settings/jellyfin/users`, { + revalidateOnMount: true, + }); + + const importUsers = async () => { + setImporting(true); + + try { + const { data: createdUsers } = await axios.post( + '/api/v1/user/import-from-jellyfin', + { jellyfinUserIds: selectedUsers } + ); + + if (!createdUsers.length) { + throw new Error('No users were imported from Jellyfin.'); + } + + addToast( + intl.formatMessage(messages.importedfromJellyfin, { + userCount: createdUsers.length, + strong: function strong(msg) { + return {msg}; + }, + }), + { + autoDismiss: true, + appearance: 'success', + } + ); + + if (onComplete) { + onComplete(); + } + } catch (e) { + addToast(intl.formatMessage(messages.importfromJellyfinerror), { + autoDismiss: true, + appearance: 'error', + }); + } finally { + setImporting(false); + } + }; + + const isSelectedUser = (JellyfinId: string): boolean => + selectedUsers.includes(JellyfinId); + + const isAllUsers = (): boolean => selectedUsers.length === data?.length; + + const toggleUser = (JellyfinId: string): void => { + if (selectedUsers.includes(JellyfinId)) { + setSelectedUsers((users) => users.filter((user) => user !== JellyfinId)); + } else { + setSelectedUsers((users) => [...users, JellyfinId]); + } + }; + + const toggleAllUsers = (): void => { + if (data && selectedUsers.length >= 0 && !isAllUsers()) { + setSelectedUsers(data.map((user) => user.id)); + } else { + setSelectedUsers([]); + } + }; + + return ( + } + onOk={() => { + importUsers(); + }} + okDisabled={isImporting || !selectedUsers.length} + okText={intl.formatMessage( + isImporting ? globalMessages.importing : globalMessages.import + )} + onCancel={onCancel} + > + {data?.length ? ( + <> + {settings.currentSettings.newPlexLogin && ( + {msg} + ); + }, + })} + type="info" + /> + )} +
+
+
+
+ + + + + + + + + {data?.map((user) => ( + + + + + ))} + +
+ toggleAllUsers()} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === 'Space') { + toggleAllUsers(); + } + }} + className="relative inline-flex h-5 w-10 flex-shrink-0 cursor-pointer items-center justify-center pt-2 focus:outline-none" + > + + + + + {intl.formatMessage(messages.user)} +
+ toggleUser(user.id)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === 'Space') { + toggleUser(user.id); + } + }} + className="relative inline-flex h-5 w-10 flex-shrink-0 cursor-pointer items-center justify-center pt-2 focus:outline-none" + > + + + + +
+ +
+
+ {user.username} +
+ {/* {user.username && + user.username.toLowerCase() !== + user.email && ( +
+ {user.email} +
+ )} */} +
+
+
+
+
+
+
+ + ) : ( + + )} +
+ ); +}; + +export default JellyfinImportModal; diff --git a/src/components/UserList/index.tsx b/src/components/UserList/index.tsx index 6e383e0c7..444185a82 100644 --- a/src/components/UserList/index.tsx +++ b/src/components/UserList/index.tsx @@ -34,12 +34,13 @@ import SensitiveInput from '../Common/SensitiveInput'; import Table from '../Common/Table'; import Transition from '../Transition'; import BulkEditModal from './BulkEditModal'; +import JellyfinImportModal from './JellyfinImportModal'; import PlexImportModal from './PlexImportModal'; const messages = defineMessages({ users: 'Users', userlist: 'User List', - importfromplex: 'Import Plex Users', + importfromplex: 'Import {mediaServerName} Users', user: 'User', totalrequests: 'Requests', accounttype: 'Type', @@ -464,13 +465,23 @@ const UserList: React.FC = () => { leaveTo="opacity-0" show={showImportModal} > - setShowImportModal(false)} - onComplete={() => { - setShowImportModal(false); - revalidate(); - }} - /> + {settings.currentSettings.mediaServerType === MediaServerType.PLEX ? ( + setShowImportModal(false)} + onComplete={() => { + setShowImportModal(false); + revalidate(); + }} + /> + ) : ( + setShowImportModal(false)} + onComplete={() => { + setShowImportModal(false); + revalidate(); + }} + /> + )}
@@ -489,13 +500,17 @@ const UserList: React.FC = () => { className="flex-grow lg:mr-2" buttonType="primary" onClick={() => setShowImportModal(true)} - disabled={ - settings.currentSettings.mediaServerType !== - MediaServerType.PLEX - } > - {intl.formatMessage(messages.importfromplex)} + + {intl.formatMessage(messages.importfromplex, { + mediaServerName: + settings.currentSettings.mediaServerType === + MediaServerType.PLEX + ? 'Plex' + : 'Jellyfin', + })} +
diff --git a/src/i18n/locale/ca.json b/src/i18n/locale/ca.json index 736f8e969..3e373e7bc 100644 --- a/src/i18n/locale/ca.json +++ b/src/i18n/locale/ca.json @@ -686,7 +686,7 @@ "components.UserList.nouserstoimport": "No hi ha usuaris nous de Plex a importar.", "components.UserList.localuser": "Usuari local", "components.UserList.importfromplexerror": "S'ha produït un error en importar usuaris de Plex.", - "components.UserList.importfromplex": "Importeu usuaris de Plex", + "components.UserList.importfromplex": "Importeu usuaris de {mediaServerName}", "components.UserList.importedfromplex": "{userCount} {userCount, plural, one {usuari} other {usuaris}} de Plex importat correctament!", "components.TvDetails.watchtrailer": "Veure el tràiler", "components.TvDetails.viewfullcrew": "Mostreu equip complet", diff --git a/src/i18n/locale/da.json b/src/i18n/locale/da.json index ab232761c..54a5fdc64 100644 --- a/src/i18n/locale/da.json +++ b/src/i18n/locale/da.json @@ -850,7 +850,7 @@ "components.UserList.nouserstoimport": "Ingen nye brugere som kan importeres fra Plex.", "components.UserList.edituser": "Redigér Brugertilladelser", "components.UserList.email": "Email Adresse", - "components.UserList.importfromplex": "Importér Brugere fra Plex", + "components.UserList.importfromplex": "Importér Brugere fra {mediaServerName}", "components.UserList.owner": "Ejer", "components.UserList.password": "Kodeord", "components.UserList.passwordinfodescription": "Konfigurér en applikations-URL og aktivér emailnotifikationer for at tillade automatisk kodeordsgenerering.", diff --git a/src/i18n/locale/de.json b/src/i18n/locale/de.json index a459c8b08..4166152a8 100644 --- a/src/i18n/locale/de.json +++ b/src/i18n/locale/de.json @@ -223,7 +223,7 @@ "components.Settings.SettingsAbout.Releases.latestversion": "Neuste", "components.Settings.SettingsAbout.Releases.currentversion": "Aktuell", "components.UserList.importfromplexerror": "Beim Importieren von Plex-Benutzern ist etwas schief gelaufen.", - "components.UserList.importfromplex": "Plex-Benutzer importieren", + "components.UserList.importfromplex": "{mediaServerName}-Benutzer importieren", "components.TvDetails.viewfullcrew": "Komplette Crew anzeigen", "components.TvDetails.TvCrew.fullseriescrew": "Komplette Serien-Crew", "components.PersonDetails.crewmember": "Crew", diff --git a/src/i18n/locale/el.json b/src/i18n/locale/el.json index acbc9d5a4..0ce7062d9 100644 --- a/src/i18n/locale/el.json +++ b/src/i18n/locale/el.json @@ -602,7 +602,7 @@ "components.UserList.localuser": "Τοπικός χρήστης", "components.UserList.localLoginDisabled": "Η ρύθμιση Ενεργοποίηση τοπικής σύνδεσης είναι προς το παρόν απενεργοποιημένη.", "components.UserList.importfromplexerror": "Κάτι πήγε στραβά κατά την εισαγωγή χρηστών από το Plex.", - "components.UserList.importfromplex": "Εισαγωγή χρηστών από το Plex", + "components.UserList.importfromplex": "Εισαγωγή χρηστών από το {mediaServerName}", "components.UserList.importedfromplex": "{userCount, plural, one {# νέου χρήστη} other {#νέοι χρήστες}} εισήχθησαν απο το Plex επιτυχώς!", "components.UserList.email": "Διεύθυνση ηλεκτρονικού ταχυδρομείου", "components.UserList.edituser": "Επεξεργασία δικαιωμάτων χρήστη", diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 5fe79373b..d49dc25b0 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -860,7 +860,7 @@ "components.UserList.edituser": "Edit User Permissions", "components.UserList.email": "Email Address", "components.UserList.importedfromplex": "{userCount} Plex {userCount, plural, one {user} other {users}} imported successfully!", - "components.UserList.importfromplex": "Import Plex Users", + "components.UserList.importfromplex": "Import {mediaServerName} Users", "components.UserList.importfromplexerror": "Something went wrong while importing Plex users.", "components.UserList.localLoginDisabled": "The Enable Local Sign-In setting is currently disabled.", "components.UserList.localuser": "Local User", diff --git a/src/i18n/locale/es.json b/src/i18n/locale/es.json index cbe540fcd..45e247c78 100644 --- a/src/i18n/locale/es.json +++ b/src/i18n/locale/es.json @@ -223,7 +223,7 @@ "components.Settings.SettingsAbout.Releases.currentversion": "Actual", "components.MovieDetails.studio": "{studioCount, plural, one {Estudio} other {Estudios}}", "components.UserList.importfromplexerror": "Algo salió mal importando usuarios de Plex.", - "components.UserList.importfromplex": "Importar Usuarios de Plex", + "components.UserList.importfromplex": "Importar Usuarios de {mediaServerName}", "components.UserList.importedfromplex": "¡{userCount, plural, one {# nuevo usuario} other {# nuevos usuarios}} importado/s de Plex con éxito!", "components.TvDetails.viewfullcrew": "Ver Equipo Completo", "components.TvDetails.firstAirDate": "Primera fecha de emisión", diff --git a/src/i18n/locale/fr.json b/src/i18n/locale/fr.json index 4dcd2543b..a89c8dfdc 100644 --- a/src/i18n/locale/fr.json +++ b/src/i18n/locale/fr.json @@ -223,7 +223,7 @@ "components.Settings.SettingsAbout.Releases.latestversion": "Dernière version", "components.Settings.SettingsAbout.Releases.currentversion": "Actuelle", "components.UserList.importfromplexerror": "Une erreur s'est produite durant l'importation des utilisateurs de Plex.", - "components.UserList.importfromplex": "Importer les utilisateurs de Plex", + "components.UserList.importfromplex": "Importer les utilisateurs de {mediaServerName}", "components.UserList.importedfromplex": "{userCount} {userCount, plural, one {utilisateur} other {utilisateurs}} importé(s) depuis Plex avec succès !", "components.TvDetails.viewfullcrew": "Voir l'équipe complète de la série", "components.TvDetails.TvCrew.fullseriescrew": "Équipe complète de la série", diff --git a/src/i18n/locale/hu.json b/src/i18n/locale/hu.json index 79373f513..2cf2f2772 100644 --- a/src/i18n/locale/hu.json +++ b/src/i18n/locale/hu.json @@ -165,7 +165,7 @@ "components.UserList.password": "Jelszó", "components.UserList.localuser": "Helyi felhasználó", "components.UserList.importfromplexerror": "Hiba történt a felhasználók Plex-ről történő importálása közben.", - "components.UserList.importfromplex": "Felhasználók importálása Plex-ről", + "components.UserList.importfromplex": "Felhasználók importálása {mediaServerName}-ről", "components.UserList.importedfromplex": "{userCount, plural, =0 {Nem lett új} one {# új} other {# új}} felhasználó importálva Plex-ről!", "components.UserList.email": "E-mail-cím", "components.UserList.deleteuser": "Felhasználó törlése", diff --git a/src/i18n/locale/it.json b/src/i18n/locale/it.json index 1c8ea601e..fa9aa35a4 100644 --- a/src/i18n/locale/it.json +++ b/src/i18n/locale/it.json @@ -223,7 +223,7 @@ "components.Settings.SettingsAbout.Releases.latestversion": "Versione più recente", "components.Settings.SettingsAbout.Releases.currentversion": "Versione attuale", "components.UserList.importfromplexerror": "Qualcosa è andato storto nell'importare gli utenti Plex.", - "components.UserList.importfromplex": "Importa utenti Plex", + "components.UserList.importfromplex": "Importa utenti {mediaServerName}", "components.UserList.importedfromplex": "{userCount} {userCount, plural, one {utente} other {utenti}} Plex {userCount, plural, one {importato} other {importati}} correttamente!", "components.TvDetails.viewfullcrew": "Vedi troupe completa", "components.TvDetails.TvCrew.fullseriescrew": "Troupe completa serie", diff --git a/src/i18n/locale/ja.json b/src/i18n/locale/ja.json index efb23ca1f..72a762a04 100644 --- a/src/i18n/locale/ja.json +++ b/src/i18n/locale/ja.json @@ -231,7 +231,7 @@ "components.TvDetails.watchtrailer": "予告編を見る", "components.MovieDetails.watchtrailer": "予告編を見る", "components.UserList.importfromplexerror": "Plexからユーザーをインポート中に問題が発生しました。", - "components.UserList.importfromplex": "Plexからユーザーをインポート", + "components.UserList.importfromplex": "{mediaServerName}からユーザーをインポート", "components.UserList.importedfromplex": "Plex から新ユーザー {userCount} 名をインポートしました。", "components.TvDetails.viewfullcrew": "フルクルーを表示", "components.TvDetails.firstAirDate": "初放送日", diff --git a/src/i18n/locale/nb_NO.json b/src/i18n/locale/nb_NO.json index 56e821068..37d0b763a 100644 --- a/src/i18n/locale/nb_NO.json +++ b/src/i18n/locale/nb_NO.json @@ -194,7 +194,7 @@ "components.UserList.userssaved": "Brukertillatelsene ble lagret!", "components.UserList.users": "Brukere", "components.UserList.importfromplexerror": "Noe gikk galt ved importering av brukere fra Plex.", - "components.UserList.importfromplex": "Importer brukere fra Plex", + "components.UserList.importfromplex": "Importer brukere fra {mediaServerName}", "components.UserList.importedfromplex": "{userCount} {userCount, plural, one {ny bruker} other {nye brukere}} ble importert fra Plex!", "components.Settings.menuUsers": "Brukere", "components.Settings.SettingsUsers.users": "Brukere", diff --git a/src/i18n/locale/nl.json b/src/i18n/locale/nl.json index a011baa73..a4dfe0db8 100644 --- a/src/i18n/locale/nl.json +++ b/src/i18n/locale/nl.json @@ -214,7 +214,7 @@ "components.UserList.userdeleteerror": "Er ging iets mis bij het verwijderen van de gebruiker.", "components.UserList.userdeleted": "Gebruiker succesvol verwijderd!", "components.UserList.importfromplexerror": "Er is iets misgegaan bij het importeren van Plex-gebruikers.", - "components.UserList.importfromplex": "Plex-gebruikers importeren", + "components.UserList.importfromplex": "{mediaServerName}-gebruikers importeren", "components.UserList.deleteuser": "Gebruiker verwijderen", "components.UserList.deleteconfirm": "Weet je zeker dat je deze gebruiker wilt verwijderen? Al hun bestaande aanvraaggegevens zullen worden verwijderd.", "components.TvDetails.watchtrailer": "Trailer bekijken", diff --git a/src/i18n/locale/pl.json b/src/i18n/locale/pl.json index 0a6bd3db9..5c77cee69 100644 --- a/src/i18n/locale/pl.json +++ b/src/i18n/locale/pl.json @@ -962,7 +962,7 @@ "components.UserProfile.UserSettings.UserNotificationSettings.sendSilently": "Wyślij po cichu", "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingsfailed": "Nie udało się zapisać ustawień powiadomień telegram.", "components.UserProfile.UserSettings.UserNotificationSettings.discordIdTip": "Wielocyfrowy numer ID powiązany z Twoim kontem użytkownika", - "components.UserList.importfromplex": "Importuj użytkowników Plex", + "components.UserList.importfromplex": "Importuj użytkowników {mediaServerName}", "i18n.available": "Dostępny", "components.UserList.sortDisplayName": "Wyświetlana nazwa", "components.UserList.totalrequests": "Prośby", diff --git a/src/i18n/locale/pt_BR.json b/src/i18n/locale/pt_BR.json index edbea94e0..acf11f544 100644 --- a/src/i18n/locale/pt_BR.json +++ b/src/i18n/locale/pt_BR.json @@ -228,7 +228,7 @@ "components.MovieDetails.viewfullcrew": "Ver Equipe Técnica Completa", "components.MovieDetails.MovieCrew.fullcrew": "Equipe Técnica Completa", "components.UserList.importfromplexerror": "Algo deu errado ao importar usuários do Plex.", - "components.UserList.importfromplex": "Importar Usuários do Plex", + "components.UserList.importfromplex": "Importar Usuários do {mediaServerName}", "components.UserList.importedfromplex": "{userCount} {userCount, plural, one {usuário Plex importado} other {usuários Plex importados}} com sucesso!", "components.Settings.Notifications.NotificationsSlack.agentenabled": "Habilitar Agente", "components.RequestList.RequestItem.failedretry": "Algo deu errado ao retentar fazer a solicitação.", diff --git a/src/i18n/locale/pt_PT.json b/src/i18n/locale/pt_PT.json index 9baec1fd9..6e2c0ee24 100644 --- a/src/i18n/locale/pt_PT.json +++ b/src/i18n/locale/pt_PT.json @@ -199,7 +199,7 @@ "components.UserList.passwordinfodescription": "Configurar um URL de aplicação e ativar as notificações por e-mail para permitir a geração automática de palavra-passe.", "components.UserList.localuser": "Utilizador Local", "components.UserList.importfromplexerror": "Ocorreu um erro ao importar utilizadores do Plex.", - "components.UserList.importfromplex": "Importar Utilizadores do Plex", + "components.UserList.importfromplex": "Importar Utilizadores do {mediaServerName}", "components.UserList.importedfromplex": "{userCount, plural, one {# novo utilizador} other {# novos utilizadores}} importados do Plex com sucesso!", "components.UserList.email": "Endereço de E-mail", "components.UserList.deleteuser": "Apagar Utilizador", diff --git a/src/i18n/locale/ru.json b/src/i18n/locale/ru.json index ca387bff8..bcec3c673 100644 --- a/src/i18n/locale/ru.json +++ b/src/i18n/locale/ru.json @@ -793,7 +793,7 @@ "components.UserList.usercreatedfailed": "Что-то пошло не так при создании пользователя.", "components.UserList.passwordinfodescription": "Настройте URL-адрес приложения и включите уведомления по электронной почте, чтобы обеспечить возможность автоматической генерации пароля.", "components.UserList.importfromplexerror": "Что-то пошло не так при импорте пользователей из Plex.", - "components.UserList.importfromplex": "Импортировать пользователей из Plex", + "components.UserList.importfromplex": "Импортировать пользователей из {mediaServerName}", "components.UserList.importedfromplex": "{userCount, plural, one {# новый пользователь} other {# новых пользователя(ей)}} успешно импортированы из Plex!", "components.UserList.edituser": "Изменить разрешения пользователя", "components.UserList.displayName": "Отображаемое имя", diff --git a/src/i18n/locale/sq.json b/src/i18n/locale/sq.json index 171522097..94a200cf3 100644 --- a/src/i18n/locale/sq.json +++ b/src/i18n/locale/sq.json @@ -1006,7 +1006,7 @@ "components.UserProfile.UserSettings.UserPermissions.toastSettingsFailure": "Diçka shkoi keq duke ruajtur cilësimet.", "components.Settings.webAppUrlTip": "Në mënyrë opsionale drejto përdoruesit në aplikacionin web në serverin tënd në vend të atij web", "components.TvDetails.episodeRuntimeMinutes": "{runtime} minuta", - "components.UserList.importfromplex": "Importoni përdoruesit Plex", + "components.UserList.importfromplex": "Importoni përdoruesit {mediaServerName}", "components.UserList.importfromplexerror": "Diçka shkoi keq duke importuar përdoruesit Plex.", "components.TvDetails.firstAirDate": "Data e parë e transmetimit", "components.UserList.email": "Adresa email", diff --git a/src/i18n/locale/sv.json b/src/i18n/locale/sv.json index 1b47b0dc9..8c0a84596 100644 --- a/src/i18n/locale/sv.json +++ b/src/i18n/locale/sv.json @@ -222,7 +222,7 @@ "components.Settings.SettingsAbout.Releases.releasedataMissing": "Versionsdata är för närvarande inte tillgänglig.", "components.Settings.SettingsAbout.Releases.latestversion": "Senaste Versionen", "components.Settings.SettingsAbout.Releases.currentversion": "Aktuell", - "components.UserList.importfromplex": "Importera Plexanvändare", + "components.UserList.importfromplex": "Importera {mediaServerName}användare", "components.UserList.importfromplexerror": "Något gick fel när Plexanvändare importerades.", "components.TvDetails.watchtrailer": "Kolla Trailer", "components.Settings.Notifications.allowselfsigned": "Tillåt Självsignerade Certifikat", diff --git a/src/i18n/locale/zh_Hans.json b/src/i18n/locale/zh_Hans.json index c8d3bc858..f52a756ca 100644 --- a/src/i18n/locale/zh_Hans.json +++ b/src/i18n/locale/zh_Hans.json @@ -24,7 +24,7 @@ "components.UserList.localuser": "本地用户", "components.UserList.localLoginDisabled": "允许本地登录的设置目前被禁用。", "components.UserList.importfromplexerror": "导入 Plex 用户时出错。", - "components.UserList.importfromplex": "导入 Plex 用户", + "components.UserList.importfromplex": "导入 {mediaServerName} 用户", "components.UserList.importedfromplex": "{userCount} Plex {userCount, plural, one {user} other {users}} 成功导入!", "components.UserList.email": "电子邮件地址", "components.UserList.edituser": "编辑用户权限", diff --git a/src/i18n/locale/zh_Hant.json b/src/i18n/locale/zh_Hant.json index 9872982c8..acb11f5d6 100644 --- a/src/i18n/locale/zh_Hant.json +++ b/src/i18n/locale/zh_Hant.json @@ -52,7 +52,7 @@ "components.Settings.radarrsettings": "Radarr 設定", "components.Settings.menuPlexSettings": "Plex", "components.UserList.importfromplexerror": "匯入 Plex 使用者時出了點問題。", - "components.UserList.importfromplex": "匯入 Plex 使用者", + "components.UserList.importfromplex": "匯入 {mediaServerName} 使用者", "components.UserList.importedfromplex": "匯入 {userCount} 個 Plex 使用者成功!", "components.UserList.localuser": "本地使用者", "components.UserList.creating": "創建中…",