From 90373e7ce263f6c2d2edfa8e18893f82c7dc9d74 Mon Sep 17 00:00:00 2001 From: abdulmohsen Date: Sat, 6 Jul 2024 18:34:13 +0300 Subject: [PATCH] Added support for using emby and jellyfin oauth tokens via config:add/manage. --- src/Backends/Jellyfin/JellyfinManage.php | 115 ++++++++++------------- src/Commands/Config/AddCommand.php | 2 +- src/Commands/Config/ManageCommand.php | 60 +++++------- src/Commands/Config/TestCommand.php | 2 +- 4 files changed, 74 insertions(+), 105 deletions(-) diff --git a/src/Backends/Jellyfin/JellyfinManage.php b/src/Backends/Jellyfin/JellyfinManage.php index c07afa34..f420fd51 100644 --- a/src/Backends/Jellyfin/JellyfinManage.php +++ b/src/Backends/Jellyfin/JellyfinManage.php @@ -60,10 +60,13 @@ class JellyfinManage implements ManageInterface $chosen = ag($backend, 'token'); $question = new Question( - r('Enter [{name}] API token. {default}' . PHP_EOL . '> ', [ - 'name' => ag($backend, 'name'), - 'default' => null !== $chosen ? "[Default: {$chosen}]" : '', - ]), + r( + 'Enter [{name}] API key or "username:password" for oauth token generation. {default}' . PHP_EOL . '> ', + [ + 'name' => ag($backend, 'name'), + 'default' => null !== $chosen ? "[Default: {$chosen}]" : '', + ] + ), $chosen ); @@ -86,7 +89,47 @@ class JellyfinManage implements ManageInterface }); $token = $this->questionHelper->ask($this->input, $this->output, $question); - $backend = ag_set($backend, 'token', $token); + + if (true === str_contains($token, ':')) { + [$username, $password] = explode(':', $token, 2); + if (empty($username) || empty($password)) { + $this->output->writeln('Invalid username or password was given.'); + goto re_goto_token; + } + + // we are probably dealing with a username:password, try to generate oAuth token. + $this->output->writeln( + 'Attempting to generate oAuth token from username and password. Please wait...' + ); + + $backend = ag_set($backend, 'token', 'oauth_token'); + + try { + $accessToken = makeBackend($backend)->generateAccessToken($username, $password); + } catch (Throwable $e) { + $this->output->writeln('Failed to generate oAuth token from username and password.'); + $this->output->writeln( + sprintf( + 'ERROR - %s: %s.' . PHP_EOL, + afterLast(get_class($e), '\\'), + $e->getMessage() + ) + ); + $backend = ag_set($backend, 'token', null); + goto re_goto_token; + } + + $backend = ag_set($backend, 'token', ag($accessToken, 'accesstoken')); + $backend = ag_set($backend, 'user', ag($accessToken, 'user')); + $backend = ag_set($backend, 'uuid', ag($accessToken, 'identifier')); + $backend = ag_set($backend, 'options.' . Options::IS_LIMITED_TOKEN, true); + } else { + $backend = ag_set($backend, 'token', $token); + } + + if (true === (bool)ag($backend, 'options.' . Options::IS_LIMITED_TOKEN, false)) { + return; + } $this->output->writeln(''); @@ -124,6 +167,8 @@ class JellyfinManage implements ManageInterface ] ) ); + $backend = ag_set($backend, 'uuid', $chosen); + return; } catch (Throwable $e) { $this->output->writeln('Failed to get the backend unique identifier.'); $this->output->writeln(r('ERROR - {kind}: {message}.' . PHP_EOL, [ @@ -135,66 +180,6 @@ class JellyfinManage implements ManageInterface goto re_goto_token; } - - $question = new Question( - r( - <<Enter [{name}] Unique identifier. {default} - ------------------ - The Server Unique identifier is randomly generated string on server setup. - ------------------ - If you select invalid or give incorrect server unique identifier, Some checks will - fail And you may not be able to sync your backend. - ------------------ - DO NOT CHANGE the default value unless you know what you are doing, or was told by devs. - HELP. PHP_EOL . '> ', - [ - 'name' => ag($backend, 'name'), - 'default' => "[Default: {$chosen}]", - ] - ), - $chosen - ); - - $question->setValidator(function ($answer) use ($custom) { - if (empty($answer)) { - throw new RuntimeException('Backend unique identifier cannot be empty.'); - } - - if (!is_string($answer) && !is_int($answer)) { - throw new RuntimeException( - r( - "Backend unique identifier is invalid. Expecting string or integer, but got '{type}' instead.", - [ - 'type' => get_debug_type($answer) - ] - ) - ); - } - - $backendUUid = makeBackend($custom, ag($custom, 'name'))->getIdentifier(true); - - if ('auto' === $answer && !empty($backendUUid)) { - return $backendUUid; - } - - if ($answer !== $backendUUid) { - throw new RuntimeException( - r( - 'Invalid backend unique identifier was given. Expecting "{uuid}", but got "{answer}" instead.', - [ - 'uuid' => $backendUUid, - 'answer' => $answer - ] - ) - ); - } - - return $answer; - }); - - $uuid = $this->questionHelper->ask($this->input, $this->output, $question); - $backend = ag_set($backend, 'uuid', $uuid); })(); $this->output->writeln(''); diff --git a/src/Commands/Config/AddCommand.php b/src/Commands/Config/AddCommand.php index b58ed14a..76113b1f 100644 --- a/src/Commands/Config/AddCommand.php +++ b/src/Commands/Config/AddCommand.php @@ -23,7 +23,7 @@ use Symfony\Component\Console\Question\Question; #[Cli(command: self::ROUTE)] final class AddCommand extends Command { - public const ROUTE = 'config:add'; + public const string ROUTE = 'config:add'; /** * Configures the command. diff --git a/src/Commands/Config/ManageCommand.php b/src/Commands/Config/ManageCommand.php index b9914173..9dbbaa9a 100644 --- a/src/Commands/Config/ManageCommand.php +++ b/src/Commands/Config/ManageCommand.php @@ -5,11 +5,11 @@ declare(strict_types=1); namespace App\Commands\Config; use App\Command; -use App\Commands\State\ImportCommand; use App\Commands\System\IndexCommand; use App\Libs\Attributes\Route\Cli; use App\Libs\Config; use App\Libs\ConfigFile; +use App\Libs\Exceptions\Backends\InvalidContextException; use App\Libs\Options; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Exception\ExceptionInterface; @@ -30,7 +30,7 @@ use Throwable; #[Cli(command: self::ROUTE)] final class ManageCommand extends Command { - public const ROUTE = 'config:manage'; + public const string ROUTE = 'config:manage'; public function __construct(private LoggerInterface $logger) { @@ -117,6 +117,8 @@ final class ManageCommand extends Command return self::FAILURE; } + go_start: + if (true === $add) { if (null !== $configFile->get("{$name}.type", null)) { $output->writeln( @@ -384,7 +386,24 @@ final class ManageCommand extends Command ); if (false === $helper->ask($input, $output, $question)) { - return $this->runCommand($input, $output, $u); + goto go_start; + } + + try { + $output->writeln('Validating backend context. Please wait...'); + $client = makeBackend($u, $name); + $client->setLogger($this->logger); + if (false === $client->validateContext($client->getContext())) { + $output->writeln('ERROR: Backend context is invalid.'); + goto go_start; + } + } catch (InvalidContextException $e) { + $output->writeln( + r('ERROR: Backend context validation has failed. {error}', [ + 'error' => $e->getMessage() + ]) + ); + goto go_start; } $configFile->set($name, $u)->persist(); @@ -411,41 +430,6 @@ final class ManageCommand extends Command if (true === $helper->ask($input, $output, $question)) { $this->getApplication()?->find(IndexCommand::ROUTE)->run(new ArrayInput([]), $output); } - - $importEnabled = (bool)ag($u, 'import.enabled'); - $metaEnabled = (bool)ag($u, 'options.' . Options::IMPORT_METADATA_ONLY); - - if (true === $importEnabled || true === $metaEnabled) { - $importType = $importEnabled ? 'play state & metadata' : 'metadata only.'; - - $helper = $this->getHelper('question'); - $text = - <<Would you like to import {type} from the backend now? [Y|N] [Default: No] - ----------------- - P.S: this could take few minutes to execute. - - TEXT; - - $text = r($text, ['type' => $importType]); - - $question = new ConfirmationQuestion($text . PHP_EOL . '> ', false); - - if (true === $helper->ask($input, $output, $question)) { - $output->writeln( - r('Importing {type} from {name}', [ - 'name' => $name, - 'type' => $importType - ]) - ); - - $cmd = $this->getApplication()?->find(ImportCommand::ROUTE); - $cmd->run(new ArrayInput(['--quiet', '--select-backend' => [$name]]), $output); - } - - $output->writeln('Import complete'); - } } return self::SUCCESS; diff --git a/src/Commands/Config/TestCommand.php b/src/Commands/Config/TestCommand.php index e84cf68b..35270887 100644 --- a/src/Commands/Config/TestCommand.php +++ b/src/Commands/Config/TestCommand.php @@ -144,7 +144,7 @@ final class TestCommand extends Command foreach ($backends as $backendName => $backend) { $backend = $this->getBackend($backendName); $context = $backend->getContext(); - $output->writeln(r("Running '{client}' functional tests on '{backend}'.", [ + $output->writeln(r("Running '{client}' client functional tests on '{backend}'.", [ 'client' => $context->clientName, 'backend' => $context->backendName, ]));