diff --git a/docs/configuration.md b/docs/configuration.md index 7faf6fc1..d15eb500 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -15,18 +15,16 @@ The `Web UI` column is set to yes if an environment variable can alternatively b ### General -| NAME | DEFAULT VALUE | INFO | Web UI | -|:--------------------------------------------|:---------------:|:-------------------------------------------------------------------------------|:------:| -| `TMDB_API_KEY` | - | **Required** (get key [here](https://www.themoviedb.org/settings/api)) | yes | -| `APPLICATION_URL` | - | Public base url of the application (e.g. `htttp://localhost`) | yes | -| `TMDB_ENABLE_IMAGE_CACHING` | `0` | More info [here](features/tmdb-data.md#image-cache) | | -| `PLEX_IDENTIFIER` | - | Required for Plex Authentication. Generate with e.g. `openssl rand -base64 32` | | -| `PLEX_APP_NAME` | `Movary` | Required for Plex Authentication | | -| `ENABLE_REGISTRATION` | `0` | Enables public user registration | | -| `MIN_RUNTIME_IN_SECONDS_FOR_JOB_PROCESSING` | `15` | Minimum time between background jobs processing | | -| `TIMEZONE` | `Europe/Berlin` | Supported timezones [here](https://www.php.net/manual/en/timezones.php) | | -| `DEFAULT_LOGIN_EMAIL` | - | Email address to always autofill on login page | | -| `DEFAULT_LOGIN_PASSWORD` | - | Password to always autofill on login page | | +| NAME | DEFAULT VALUE | INFO | Web UI | +|:--------------------------------------------|:---------------:|:------------------------------------------------------------------------|:------:| +| `TMDB_API_KEY` | - | **Required** (get key [here](https://www.themoviedb.org/settings/api)) | yes | +| `APPLICATION_URL` | - | Public base url of the application (e.g. `htttp://localhost`) | yes | +| `TMDB_ENABLE_IMAGE_CACHING` | `0` | More info [here](features/tmdb-data.md#image-cache) | | +| `ENABLE_REGISTRATION` | `0` | Enables public user registration | | +| `MIN_RUNTIME_IN_SECONDS_FOR_JOB_PROCESSING` | `15` | Minimum time between background jobs processing | | +| `TIMEZONE` | `Europe/Berlin` | Supported timezones [here](https://www.php.net/manual/en/timezones.php) | | +| `DEFAULT_LOGIN_EMAIL` | - | Email address to always autofill on login page | | +| `DEFAULT_LOGIN_PASSWORD` | - | Password to always autofill on login page | | ### Database @@ -45,13 +43,14 @@ Required to run the application ### Third party integrations -Required for some third party integrations. Only necessary if the relevant third party integrations should be enabled. +Required for some third party integrations. Only necessary if the relevant third party integrations should be enabled. -| NAME | DEFAULT VALUE | INFO | Web UI | -|:--------------------------------------------|:-------------:|:-------------------------------------------------------------------------------|:------:| -| `PLEX_IDENTIFIER` | - | Required for Plex authentication. Generate with e.g. `openssl rand -base64 32` | | -| `PLEX_APP_NAME` | `Movary` | Required for Plex authentication | | -| `JELLYFIN_DEVICE_ID` | - | Required for Jellyfin authentication | | +| NAME | DEFAULT VALUE | INFO | Web UI | +|:---------------------|:-------------:|:-------------------------------------------------------------------------------|:------:| +| `PLEX_IDENTIFIER` | - | Required for Plex Authentication. Generate with e.g. `openssl rand -base64 32` | | +| `PLEX_APP_NAME` | `Movary` | Used for Plex Authentication | | +| `JELLYFIN_DEVICE_ID` | - | Required for Jellyfin Authentication | | +| `JELLYFIN_APP_NAME` | `Movary` | Used for Jellyfin Authentication | | ### Email diff --git a/public/js/settings-integration-jellyfin.js b/public/js/settings-integration-jellyfin.js index 4f2f6c63..7e2a720f 100644 --- a/public/js/settings-integration-jellyfin.js +++ b/public/js/settings-integration-jellyfin.js @@ -1,11 +1,11 @@ function disableElement(el) { el.classList.add('disabled'); - el.disabled = true; + el.disabled = true; } function enableElement(el) { el.classList.remove('disabled'); - el.disabled = false; + el.disabled = false; } function insertAuthenticationForm() { @@ -19,7 +19,7 @@ function insertAuthenticationForm() { jellyfinUsernameInput.type = 'text'; jellyfinUsernameInput.classList.add('form-control', 'disabled'); jellyfinUsernameInput.id = 'JellyfinUsernameInput'; - + jellyfinPasswordInput.type = 'password'; jellyfinPasswordInput.classList.add('form-control', 'disabled'); jellyfinPasswordInput.id = 'JellyfinPasswordInput'; @@ -135,40 +135,46 @@ async function updateScrobbleOptions() { }); } -async function saveServerUrl() { +async function saveJellyfinServerUrl() { + document.getElementById('alertJellyfinServerUrlLoadingSpinner').classList.remove('d-none') + removeAlert('alertJellyfinServerUrlDiv'); + + const jellyfinServerUrl = document.getElementById('jellyfinServerUrlInput').value; + await fetch('/settings/jellyfin/server-url-save', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ - 'JellyfinServerUrl': document.getElementById('jellyfinServerUrlInput').value + 'JellyfinServerUrl': jellyfinServerUrl }) }).then(response => { - if(!response.ok) { - addAlert('alertJellyfinImportDiv', 'Could not save server URL', 'danger'); + document.getElementById('alertJellyfinServerUrlLoadingSpinner').classList.add('d-none') + + if (!response.ok) { + addAlert('alertJellyfinServerUrlDiv', 'Could not save server url', 'danger'); return; } - addAlert('alertJellyfinImportDiv', 'Succesfully saved the server URL!', 'success'); - - if(document.getElementById('jellyfinServerUrlInput').value.length > 0) { - enableElement(document.getElementById('verifyServerUrlBtn')); - enableElement(document.getElementById('JellyfinUsernameInput')); - enableElement(document.getElementById('JellyfinPasswordInput')); - enableElement(document.getElementById('jellyfinAuthenticationBtn')); + + document.getElementById('alertJellyfinServerUrlLoadingSpinner').classList.add('d-none') + addAlert('alertJellyfinServerUrlDiv', 'Server url was updated', 'success'); + if (jellyfinServerUrl !== '') { + document.getElementById('authenticateWithJellyfinButton').disabled = false } else { - disableElement(document.getElementById('verifyServerUrlBtn')); - disableElement(document.getElementById('JellyfinUsernameInput')); - disableElement(document.getElementById('JellyfinPasswordInput')); - disableElement(document.getElementById('jellyfinAuthenticationBtn')); + document.getElementById('authenticateWithJellyfinButton').disabled = true } }).catch(function (error) { console.log(error); - addAlert('alertJellyfinImportDiv', 'Could not save server URL', 'danger'); + document.getElementById('alertJellyfinServerUrlLoadingSpinner').classList.add('d-none') + addAlert('alertJellyfinServerUrlDiv', 'Could not save server url', 'danger'); }); } -async function verifyServerUrl() { +async function verifyJellyfinServerUrl() { + document.getElementById('alertJellyfinServerUrlLoadingSpinner').classList.remove('d-none') + removeAlert('alertJellyfinServerUrlDiv'); + await fetch('/settings/jellyfin/server-url-verify', { method: 'POST', headers: { @@ -178,46 +184,42 @@ async function verifyServerUrl() { 'JellyfinServerUrl': document.getElementById('jellyfinServerUrlInput').value }) }).then(response => { - if(!response.ok) { - addAlert('alertJellyfinImportDiv', 'Could not verify server URL', 'danger'); + document.getElementById('alertJellyfinServerUrlLoadingSpinner').classList.add('d-none') + if (!response.ok) { + addAlert('alertJellyfinServerUrlDiv', 'Could not verify server url', 'danger'); + return; } - addAlert('alertJellyfinImportDiv', 'Succesfully verified the server URL!', 'success'); + addAlert('alertJellyfinServerUrlDiv', 'Server url was verified', 'success'); }).catch(function (error) { console.log(error) - addAlert('alertJellyfinImportDiv', 'Could not verify server URL', 'danger'); + document.getElementById('alertJellyfinServerUrlLoadingSpinner').classList.add('d-none') + addAlert('alertJellyfinServerUrlDiv', 'Could not verify server url', 'danger'); }); } async function authenticateJellyfinAccount() { + console.log(document.getElementById('jellyfinUsernameInput').value) await fetch('/settings/jellyfin/authenticate', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ - 'username': document.getElementById('JellyfinUsernameInput').value, - 'password': document.getElementById('JellyfinPasswordInput').value, + 'username': document.getElementById('jellyfinUsernameInput').value, + 'password': document.getElementById('jellyfinPasswordInput').value, }) }).then(response => { - if(!response.ok) { - addAlert('alertJellyfinImportDiv', 'Could not authenticate with Jellyfin', 'danger'); + if (!response.ok) { + addAlert('alertJellyfinAuthenticationModalDiv', 'Could not authenticate with Jellyfin', 'danger', 0); + return; } - addAlert('alertJellyfinImportDiv', 'Succesfully authenticated with Jellyfin!', 'success'); - - document.getElementById('jellyfinModalBody').getElementsByClassName('input-group')[0].remove(); - document.getElementById('jellyfinAuthenticationBtn').remove(); - - let removeAuthenticationBtn = document.createElement('button'); - removeAuthenticationBtn.classList.add('btn', 'btn-danger', 'mb-3'); - removeAuthenticationBtn.addEventListener('click', removeJellyfinAuthentication); - removeAuthenticationBtn.innerText = 'Remove Jellyfin authentication'; - removeAuthenticationBtn.id = 'jellyfinRemoveAuthenticationBtn'; - document.getElementById('jellyfinModalBody').append(removeAuthenticationBtn); + + location.reload(); }).catch(function (error) { console.error(error) - addAlert('alertJellyfinImportDiv', 'Could not authenticate with Jellyfin', 'danger'); + addAlert('alertJellyfinAuthenticationModalDiv', 'Could not authenticate with Jellyfin', 'danger', 0); }); } @@ -228,17 +230,15 @@ async function removeJellyfinAuthentication() { 'Content-Type': 'application/json', } }).then(response => { - if(!response.ok) { + if (!response.ok) { addAlert('alertJellyfinImportDiv', 'The authentication could not be removed', 'danger'); - return; - } else { - addAlert('alertJellyfinImportDiv', 'Authentication succesfully removed!', 'success'); - document.getElementById('jellyfinRemoveAuthenticationBtn').remove(); - insertAuthenticationForm(); - + + return } + + location.reload(); }).catch(function (error) { console.error(error) addAlert('alertJellyfinImportDiv', 'The authentication could not be removed', 'danger'); }); -} \ No newline at end of file +} diff --git a/public/js/settings-integration-plex.js b/public/js/settings-integration-plex.js index 25cd2482..edc34cc4 100644 --- a/public/js/settings-integration-plex.js +++ b/public/js/settings-integration-plex.js @@ -248,11 +248,3 @@ async function importPlexWatchlist() { addAlert('alertPlexWatchlistImportDiv', 'Watchlist import scheduled', 'success') } - -async function authenticateWithJellyfin() { - // TODO -} - -async function removeJellyfinAuthentication() { - // TODO -} \ No newline at end of file diff --git a/src/Api/Jellyfin/JellyfinApi.php b/src/Api/Jellyfin/JellyfinApi.php index 20df5a02..8ea197a6 100644 --- a/src/Api/Jellyfin/JellyfinApi.php +++ b/src/Api/Jellyfin/JellyfinApi.php @@ -6,7 +6,9 @@ use Movary\Api\Jellyfin\Dto\JellyfinAuthenticationData; use Movary\Api\Jellyfin\Dto\JellyfinUser; use Movary\Api\Jellyfin\Dto\JellyfinUserId; +use Movary\Domain\User\UserApi; use Movary\Service\ServerSettings; +use Movary\ValueObject\RelativeUrl; use Psr\Log\LoggerInterface; class JellyfinApi @@ -14,16 +16,22 @@ class JellyfinApi public function __construct( private readonly JellyfinClient $jellyfinClient, private readonly ServerSettings $serverSettings, - private readonly LoggerInterface $logger + private readonly UserApi $userApi, + private readonly LoggerInterface $logger, ) { } - public function deleteJellyfinAccessToken() : void + public function deleteJellyfinAccessToken(int $userId) : void { + $accessToken = $this->userApi->findJellyfinAccessToken($userId); + $jellyfinServerUrl = $this->userApi->findJellyfinServerUrl($userId); + + $url = $jellyfinServerUrl->appendRelativeUrl(RelativeUrl::create('/Users/')); + $query = [ - 'id' => $this->serverSettings->getJellyfinDeviceId() + 'id' => $this->serverSettings->requireJellyfinDeviceId() ]; - $this->jellyfinClient->delete('/Devices', $query); + $this->jellyfinClient->delete($url, $query, $accessToken); $this->logger->info('Jellyfin access token has been invalidated'); } @@ -32,27 +40,38 @@ public function fetchJellyfinServerInfo() : ?array return $this->jellyfinClient->get('/system/info/public'); } - public function fetchJellyfinUser(JellyfinUserId $jellyfinUserId) : ?JellyfinUser + public function fetchJellyfinUser(int $userId) : ?JellyfinUser { - $userInformation = $this->jellyfinClient->get('/Users/'.$jellyfinUserId); - if($userInformation === null) { + $jellyfinUserId = $this->userApi->fetchJellyfinUserId($userId); + $jellyfinServerUrl = $this->userApi->findJellyfinServerUrl($userId); + + $url = $jellyfinServerUrl->appendRelativeUrl(RelativeUrl::create('/Users/' . $jellyfinUserId)); + + $userInformation = $this->jellyfinClient->get($url); + + if ($userInformation === null) { return null; } + return JellyfinUser::create(JellyfinUserId::create($userInformation['Id']), $userInformation['Name']); } - public function createJellyfinAuthentication(string $username, string $password) : ?JellyfinAuthenticationData + public function createJellyfinAuthentication(int $userId, string $username, string $password) : ?JellyfinAuthenticationData { + $jellyfinServerUrl = $this->userApi->findJellyfinServerUrl($userId); + + $url = $jellyfinServerUrl->appendRelativeUrl(RelativeUrl::create('/Users/authenticatebyname')); + $data = [ 'Username' => $username, 'Pw' => $password ]; - $response = $this->jellyfinClient->post('/Users/authenticatebyname', data: $data); + $response = $this->jellyfinClient->post($url, data: $data); if ($response === null) { return null; } - $this->logger->info('Jellyfin account has been authenticated'); + $this->logger->info('Jellyfin account has been authenticated for user: ' . $userId); return JellyfinAuthenticationData::create( JellyfinAccessToken::create((string)$response['AccessToken']), diff --git a/src/Api/Jellyfin/JellyfinClient.php b/src/Api/Jellyfin/JellyfinClient.php index fa545b26..84585c67 100644 --- a/src/Api/Jellyfin/JellyfinClient.php +++ b/src/Api/Jellyfin/JellyfinClient.php @@ -6,129 +6,62 @@ use GuzzleHttp\Exception\ClientException; use Movary\Api\Jellyfin\Dto\JellyfinAccessToken; use Movary\Api\Jellyfin\Exception\JellyfinInvalidAuthentication; -use Movary\Api\Jellyfin\Exception\JellyfinInvalidServerUrl; use Movary\Api\Jellyfin\Exception\JellyfinNotFoundError; -use Movary\Domain\User\Service\Authentication; -use Movary\Domain\User\UserApi; use Movary\Service\ServerSettings; use Movary\Util\Json; use Movary\ValueObject\Url; -use Psr\Log\LoggerInterface; use RuntimeException; class JellyfinClient { - private const APP_NAME = 'Movary'; - - private const DEFAULTHEADERS = [ - 'Accept' => 'application/json', - ]; - - private array $authorizationString; - - private ?JellyfinAccessToken $jellyfinAccessToken; - - private ?Url $jellyfinServerUrl; - public function __construct( private readonly HttpClient $httpClient, - private readonly Authentication $authenticationService, - private readonly UserApi $userApi, private readonly ServerSettings $serverSettings, - private readonly LoggerInterface $logger ) { - $this->jellyfinAccessToken = $this->userApi->findJellyfinAccessToken($this->authenticationService->getCurrentUserId()); - $this->jellyfinServerUrl = $this->userApi->findJellyfinServerUrl($this->authenticationService->getCurrentUserId()); - $this->authorizationString = ['X-Emby-Authorization' => 'MediaBrowser Client ="' . self::APP_NAME . '", Device ="' . php_uname('s') . '", Version="' . $this->serverSettings->getApplicationVersion() . '", DeviceId="' . $this->serverSettings->getJellyfinDeviceId() . '"']; - if ($this->jellyfinAccessToken !== null) { - $this->authorizationString['X-Emby-Authorization'] .= ', Token="' . $this->jellyfinAccessToken . '"'; - } } - public function delete(string $relativeUrl, ?array $query = []) : ?string + public function delete(Url $jellyfinServerUrl, ?array $query = [], ?JellyfinAccessToken $jellyfinAccessToken = null) : void { - if ($this->jellyfinServerUrl === null) { - $this->logger->error('No Jellyfin server URL set'); - return null; - } - if($this->serverSettings->getJellyfinDeviceId() === null) { - $this->logger->error('No Jellyfin Device ID set'); - return null; - } - - $headers = array_merge(self::DEFAULTHEADERS, $this->authorizationString); - $options = [ - 'headers' => $headers, - 'query' => $query + 'query' => $query, + 'headers' => $this->generateHeaders($jellyfinAccessToken) ]; - $url = $this->jellyfinServerUrl . $relativeUrl; - try { - $response = $this->httpClient->request('DELETE', $url, $options); + $this->httpClient->request('GET', (string)$jellyfinServerUrl, $options); } catch (ClientException $e) { - throw $this->convertException($e, Url::createFromString($url)); + throw $this->convertException($e, $jellyfinServerUrl); } - - /** @psalm-suppress PossiblyUndefinedVariable */ - return (string)$response->getBody(); } - public function get(string $relativeUrl, ?array $query = []) : ?array + public function get(Url $jellyfinServerUrl, ?array $query = [], ?JellyfinAccessToken $jellyfinAccessToken = null) : ?array { - if ($this->jellyfinServerUrl === null) { - $this->logger->error('No Jellyfin server URL set'); - return null; - } - if($this->serverSettings->getJellyfinDeviceId() === null) { - $this->logger->error('No Jellyfin Device ID set'); - return null; - } - - $headers = array_merge(self::DEFAULTHEADERS, $this->authorizationString); - $options = [ 'query' => $query, - 'headers' => $headers + 'headers' => $this->generateHeaders($jellyfinAccessToken) ]; - $url = $this->jellyfinServerUrl . $relativeUrl; - try { - $response = $this->httpClient->request('GET', $url, $options); + $response = $this->httpClient->request('GET', (string)$jellyfinServerUrl, $options); } catch (ClientException $e) { - throw $this->convertException($e, Url::createFromString($url)); + throw $this->convertException($e, $jellyfinServerUrl); } return Json::decode((string)$response->getBody()); } - public function post(string $relativeUrl, ?array $query = [], ?array $data = []) : ?array + public function post(Url $jellyfinServerUrl, ?array $query = [], ?array $data = [], ?JellyfinAccessToken $jellyfinAccessToken = null) : ?array { - if ($this->jellyfinServerUrl === null) { - $this->logger->error('No Jellyfin server URL set'); - return null; - } - if($this->serverSettings->getJellyfinDeviceId() === null) { - $this->logger->error('No Jellyfin Device ID set'); - return null; - } - - $headers = array_merge(self::DEFAULTHEADERS, $this->authorizationString); - $options = [ 'json' => $data, 'query' => $query, - 'headers' => $headers + 'headers' => $this->generateHeaders($jellyfinAccessToken) ]; - $url = $this->jellyfinServerUrl . $relativeUrl; - try { - $response = $this->httpClient->request('POST', $url, $options); + $response = $this->httpClient->request('POST', (string)$jellyfinServerUrl, $options); } catch (ClientException $e) { - throw $this->convertException($e, Url::createFromString($url)); + throw $this->convertException($e, $jellyfinServerUrl); } /** @psalm-suppress PossiblyUndefinedVariable */ @@ -144,4 +77,21 @@ private function convertException(\Exception $e, Url $url) : \Exception default => $e }; } + + private function generateHeaders(?JellyfinAccessToken $jellyfinAccessToken = null) : array + { + $appName = $this->serverSettings->getJellyfinAppName(); + $appVersion = $this->serverSettings->getApplicationVersion() ?? 'dev'; + $deviceId = $this->serverSettings->requireJellyfinDeviceId(); + + $authorizationString = 'MediaBrowser Client ="' . $appName . '", Device ="' . php_uname('s') . '", Version="' . $appVersion . '", DeviceId="' . $deviceId . '"'; + if ($jellyfinAccessToken !== null) { + $authorizationString .= ', Token="' . $jellyfinAccessToken . '"'; + } + + return [ + 'X-Emby-Authorization' => $authorizationString, + 'Accept' => 'application/json', + ]; + } } diff --git a/src/Api/Plex/PlexTvClient.php b/src/Api/Plex/PlexTvClient.php index 105c612c..d077ee53 100644 --- a/src/Api/Plex/PlexTvClient.php +++ b/src/Api/Plex/PlexTvClient.php @@ -57,7 +57,7 @@ private function generateDefaultFormData() : array return [ 'X-Plex-Client-Identifier' => $plexIdentifier, - 'X-Plex-Product' => $this->serverSettings->getAppName(), + 'X-Plex-Product' => $this->serverSettings->getPlexAppName(), 'X-Plex-Product-Version' => $plexIdentifier, 'X-Plex-Platform' => php_uname('s'), 'X-Plex-Platform-Version' => php_uname('v'), diff --git a/src/HttpController/JellyfinController.php b/src/HttpController/JellyfinController.php index 399703b0..b8fa9641 100644 --- a/src/HttpController/JellyfinController.php +++ b/src/HttpController/JellyfinController.php @@ -46,7 +46,7 @@ public function authenticateJellyfinAccount(Request $request) : Response return Response::createBadRequest(); } - $jellyfinAuthentication = $this->jellyfinApi->createJellyfinAuthentication($username, $password); + $jellyfinAuthentication = $this->jellyfinApi->createJellyfinAuthentication($userId, $username, $password); if ($jellyfinAuthentication === null) { return Response::createUnauthorized(); } @@ -102,8 +102,10 @@ public function removeJellyfinAuthentication() : Response return Response::createSeeOther('/'); } - $this->jellyfinApi->deleteJellyfinAccessToken(); - $this->userApi->deleteJellyfinAuthentication($this->authenticationService->getCurrentUserId()); + $userId = $this->authenticationService->getCurrentUserId(); + + $this->jellyfinApi->deleteJellyfinAccessToken($userId); + $this->userApi->deleteJellyfinAuthentication($userId); $this->logger->info('Jellyfin authentication has been removed'); @@ -142,8 +144,12 @@ public function verifyJellyfinServerUrl() : Response return Response::createSeeOther('/'); } + $jellyfinServerInfo = $this->jellyfinApi->fetchJellyfinServerInfo(); + var_dump($jellyfinServerInfo); + + return Response::createOk(); + try { - $jellyfinServerInfo = $this->jellyfinApi->fetchJellyfinServerInfo(); if ($jellyfinServerInfo === null) { return Response::createBadRequest(); } elseif ($jellyfinServerInfo['ProductName'] === 'Jellyfin Server') { diff --git a/src/HttpController/SettingsController.php b/src/HttpController/SettingsController.php index 83f8ea8b..1cc2ce02 100644 --- a/src/HttpController/SettingsController.php +++ b/src/HttpController/SettingsController.php @@ -3,8 +3,6 @@ namespace Movary\HttpController; use Movary\Api\Github\GithubApi; -use Movary\Api\Jellyfin\Exception\JellyfinInvalidAuthentication; -use Movary\Api\Jellyfin\Exception\JellyfinNotFoundError; use Movary\Api\Jellyfin\JellyfinApi; use Movary\Api\Plex\PlexApi; use Movary\Api\Tmdb\Cache\TmdbIsoCountryCache; @@ -277,14 +275,7 @@ public function renderJellyfinPage() : Response $jellyfinIsAuthenticated = $this->userApi->findJellyfinAccessToken($user->getId()) === null ? false : true; if ($jellyfinIsAuthenticated === true) { - $jellyfinUserId = $this->userApi->fetchJellyfinUserId($user->getId()); - - try { - $this->jellyfinApi->fetchJellyfinUser($jellyfinUserId); - } catch (JellyfinInvalidAuthentication|JellyfinNotFoundError) { - $jellyfinIsAuthenticated = false; - $this->userApi->deleteJellyfinAuthentication($user->getId()); - } + $jellyfinUsername = ''; //todo } if ($applicationUrl !== null && $webhookId !== null) { @@ -298,6 +289,7 @@ public function renderJellyfinPage() : Response 'jellyfinWebhookUrl' => $webhookUrl ?? '-', 'jellyfinServerUrl' => $jellyfinServerUrl, 'jellyfinIsAuthenticated' => $jellyfinIsAuthenticated, + 'jellyfinUsername' => $jellyfinUsername ?? null, 'scrobbleWatches' => $user->hasJellyfinScrobbleWatchesEnabled(), ]), ); diff --git a/src/Service/ServerSettings.php b/src/Service/ServerSettings.php index d45d31dc..b77680e7 100644 --- a/src/Service/ServerSettings.php +++ b/src/Service/ServerSettings.php @@ -12,6 +12,8 @@ class ServerSettings private const PLEX_APP_NAME = 'PLEX_APP_NAME'; + private const JELLYFIN_APP_NAME = 'JELLYFIN_APP_NAME'; + private const PLEX_IDENTIFIER = 'PLEX_IDENTIFIER'; private const APPLICATION_URL = 'APPLICATION_URL'; @@ -42,11 +44,6 @@ public function __construct( ) { } - public function getAppName() : string - { - return $this->getByKey(self::PLEX_APP_NAME) ?? 'Movary'; - } - public function getApplicationUrl() : ?string { return $this->getByKey(self::APPLICATION_URL); @@ -62,14 +59,19 @@ public function getFromAddress() : ?string return $this->getByKey(self::SMTP_FROM_ADDRESS); } - public function getPlexIdentifier() : ?string + public function getJellyfinAppName() : string { - return $this->getByKey(self::PLEX_IDENTIFIER); + return $this->getByKey(self::JELLYFIN_APP_NAME) ?? 'Movary'; + } + + public function getPlexAppName() : string + { + return $this->getByKey(self::PLEX_APP_NAME) ?? 'Movary'; } - public function getJellyfinDeviceId() : ?string + public function getPlexIdentifier() : ?string { - return $this->getByKey(self::JELLYFIN_DEVICE_ID); + return $this->getByKey(self::PLEX_IDENTIFIER); } public function getSmtpEncryption() : ?string @@ -167,6 +169,16 @@ public function requireApplicationUrl() : string return $value; } + public function requireJellyfinDeviceId() : ?string + { + $value = $this->getByKey(self::JELLYFIN_DEVICE_ID, true); + if ($value === null) { + throw ConfigNotSetException::create(self::JELLYFIN_DEVICE_ID); + } + + return $value; + } + public function requirePlexIdentifier() : string { $value = $this->getByKey(self::PLEX_IDENTIFIER, true); diff --git a/templates/page/settings-integration-jellyfin.html.twig b/templates/page/settings-integration-jellyfin.html.twig index 139d179e..acae18c9 100644 --- a/templates/page/settings-integration-jellyfin.html.twig +++ b/templates/page/settings-integration-jellyfin.html.twig @@ -83,17 +83,110 @@
-
Jellyfin Authentication
+
Jellyfin authentication

For personal data access you have to authenticate against Jellyfin.

-
- +
+ +
+
-
+
+

Username: {{ jellyfinUsername }}

+
+ +
+ Server Url + + +
+ +
+ Loading... +
+
+ + + + + + +