diff --git a/FAQ.md b/FAQ.md index 63ad45bb..0e459027 100644 --- a/FAQ.md +++ b/FAQ.md @@ -158,13 +158,16 @@ $ docker exec -ti console backend:users:list --with-tokens -- [BACKEND_NAME] ### How do i migrate invited friends i.e. (external user) data from from plex to emby/jellyfin? As this tool is designed to work with single user, You have to treat each invited friend as a separate user. what is -needed, you need to contact that friend of yours and ask him to give you a copy of his [X-Plex-Token](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/), +needed, you need to contact that friend of yours and ask him to give you a copy of +his [X-Plex-Token](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/), then create new container and add the backend with the token you got from your friend. -After that, add your other backends like emby/jellyfin using your regular API key. jellyfin/emby differentiate between users by using the userId which +After that, add your other backends like emby/jellyfin using your regular API key. jellyfin/emby differentiate between +users by using the userId which you should select at the start of the add process. -After that. run the `state:import -f -s [plex_server_name]` command to import the user watch state. After that, you can run the `state:export -fi -s [emby/jellyfin_server_name]` to export the +After that. run the `state:import -f -s [plex_server_name]` command to import the user watch state. After that, you can +run the `state:export -fi -s [emby/jellyfin_server_name]` to export the watch state to the new backend. You have to repeat these steps for each user you want to migrate their data off the plex server. @@ -256,8 +259,11 @@ $ mv /config/db/watchstate_v01-repaired.db /config/db/watchstate_v01.db * com.plexapp.agents.xbmcnfo://(id)?lang=en `(XBMC NFO Movies agent)` * com.plexapp.agents.xbmcnfotv://(id)?lang=en `(XBMC NFO TV agent)` * com.plexapp.agents.hama://(db)\d?-(id)?lang=en `(HAMA multi source db agent mainly for anime)` -* com.plexapp.agents.ytinforeader://(id)?lang=en [ytinforeader.bundle](https://github.com/arabcoders/plex-ytdlp-info-reader-agent) With [jp_scanner.py](https://github.com/arabcoders/plex-daily-scanner) as scanner. -* com.plexapp.agents.cmdb://(id)?lang=en [cmdb.bundle](https://github.com/arabcoders/cmdb.bundle) `(User custom metadata database)`. +* com.plexapp.agents.ytinforeader://(id) + ?lang=en [ytinforeader.bundle](https://github.com/arabcoders/plex-ytdlp-info-reader-agent) + With [jp_scanner.py](https://github.com/arabcoders/plex-daily-scanner) as scanner. +* com.plexapp.agents.cmdb://(id) + ?lang=en [cmdb.bundle](https://github.com/arabcoders/cmdb.bundle) `(User custom metadata database)`. --- @@ -269,8 +275,10 @@ $ mv /config/db/watchstate_v01-repaired.db /config/db/watchstate_v01.db * tvmaze://(id) * tvrage://(id) * anidb://(id) -* ytinforeader://(id) [jellyfin](https://github.com/arabcoders/jf-ytdlp-info-reader-plugin) & [Emby](https://github.com/arabcoders/emby-ytdlp-info-reader-plugin). `(A yt-dlp info reader plugin)`. -* cmdb://(id) [jellyfin](https://github.com/arabcoders/jf-custom-metadata-db) & [Emby](https://github.com/arabcoders/emby-custom-metadata-db). `(User custom metadata database)`. +* ytinforeader://( + id) [jellyfin](https://github.com/arabcoders/jf-ytdlp-info-reader-plugin) & [Emby](https://github.com/arabcoders/emby-ytdlp-info-reader-plugin). `(A yt-dlp info reader plugin)`. +* cmdb://( + id) [jellyfin](https://github.com/arabcoders/jf-custom-metadata-db) & [Emby](https://github.com/arabcoders/emby-custom-metadata-db). `(User custom metadata database)`. --- @@ -295,7 +303,7 @@ These environment variables relates to the tool itself, you can load them via th |-------------------------|---------|-------------------------------------------------------------------------|--------------------| | WS_DATA_PATH | string | Where to store main data. (config, db). | `${BASE_PATH}/var` | | WS_TMP_DIR | string | Where to store temp data. (logs, cache) | `${WS_DATA_PATH}` | -| WS_TZ | string | Set timezone. | `UTC` | +| WS_TZ | string | Set timezone. Fallback to to `TZ` variable if `WS_TZ` not set. | `UTC` | | WS_CRON_{TASK} | bool | Enable {task} task. Value casted to bool. | `false` | | WS_CRON_{TASK}_AT | string | When to run {task} task. Valid Cron Expression Expected. | `*/1 * * * *` | | WS_CRON_{TASK}_ARGS | string | Flags to pass to the {task} command. | `-v` | @@ -481,15 +489,19 @@ Those are some web hook limitations we discovered for the following media backen #### Emby * Emby does not send webhooks events for newly added items. - ~~[See feature request](https://emby.media/community/index.php?/topic/97889-new-content-notification-webhook/)~~ implemented in `4.7.9` still does not work as expected no metadata being sent when the item notification goes out. -* Emby webhook test event does not contain data. To test if your setup works, play something or do mark an item as played or unplayed you should see changes reflected in `docker exec -ti watchstate console db:list`. + ~~[See feature request](https://emby.media/community/index.php?/topic/97889-new-content-notification-webhook/)~~ + implemented in `4.7.9` still does not work as expected no metadata being sent when the item notification goes out. +* Emby webhook test event does not contain data. To test if your setup works, play something or do mark an item as + played or unplayed you should see changes reflected in `docker exec -ti watchstate console db:list`. #### Jellyfin -* If you don't select a user id, the plugin will send `itemAdd` event without user data, and will fail the check if you happen to enable `webhook.match.user` for jellyfin. +* If you don't select a user id, the plugin will send `itemAdd` event without user data, and will fail the check if you + happen to enable `webhook.match.user` for jellyfin. * Sometimes jellyfin will fire webhook `itemAdd` event without the item being matched. * Even if you select user id, sometimes `itemAdd` event will fire without user data. -* Items might be marked as unplayed if Libraries > Display - `Date added behavior for new content:` is set to `Use date scanned into library`. This happens if the media file has been replaced. +* Items might be marked as unplayed if Libraries > Display - `Date added behavior for new content:` is set + to `Use date scanned into library`. This happens if the media file has been replaced. --- @@ -497,7 +509,8 @@ Those are some web hook limitations we discovered for the following media backen As stated in webhook limitation section sometimes media backends don't make it easy to receive those events, as such, to complement webhooks, you should enable import/export tasks by settings their respective environment variables in -your `docker-compose.yaml` file. For more information run help on `system:env` command as well as `system:tasks` command. +your `docker-compose.yaml` file. For more information run help on `system:env` command as well as `system:tasks` +command. --- @@ -548,10 +561,13 @@ location and delete the empty directories. ### How to get WatchState working with YouTube content/library? Due to the nature on how people name their youtube files i had to pick something specific for it to work cross supported -media agents. Please visit [this link](https://github.com/arabcoders/jf-ytdlp-info-reader-plugin#usage) to know how to name your files. Please be aware these plugins and scanners `REQUIRE` -that you have a `yt-dlp` `.info.json` files named exactly as your media file. For example, if you have `20231030 my awesome youtube video [youtube-RandomString].mkv` +media agents. Please visit [this link](https://github.com/arabcoders/jf-ytdlp-info-reader-plugin#usage) to know how to +name your files. Please be aware these plugins and scanners `REQUIRE` +that you have a `yt-dlp` `.info.json` files named exactly as your media file. For example, if you +have `20231030 my awesome youtube video [youtube-RandomString].mkv` you should have `20231030 my awesome youtube video [youtube-RandomString].info.json` in the same directory. In the -future, I plan to make `.info.json` optional However at the moment the file is required for emby/jellyfin plugin to work. +future, I plan to make `.info.json` optional However at the moment the file is required for emby/jellyfin plugin to +work. #### Plex Setup @@ -561,11 +577,13 @@ future, I plan to make `.info.json` optional However at the moment the file is r #### Jellyfin Setup -* Download this plugin [jf-ytdlp-info-reader-plugin](https://github.com/arabcoders/jf-ytdlp-info-reader-plugin). Please refer to the link on how to install it. +* Download this plugin [jf-ytdlp-info-reader-plugin](https://github.com/arabcoders/jf-ytdlp-info-reader-plugin). Please + refer to the link on how to install it. #### Emby Setup -* Download this plugin [emby-ytdlp-info-reader-plugin](https://github.com/arabcoders/emby-ytdlp-info-reader-plugin). Please refer to the link on how to install it. +* Download this plugin [emby-ytdlp-info-reader-plugin](https://github.com/arabcoders/emby-ytdlp-info-reader-plugin). + Please refer to the link on how to install it. If your media is not matching correctly or not marking it as expected, it's most likely scanners issues as plex and jellyfin/emby reports the GUID differently, and we try our best to match them. So, please hop on discord with the @@ -618,10 +636,12 @@ If everything is working correctly you should see something like this previous j ### I keep receiving this warning in log `INFO: Ignoring [xxx] Episode range, and treating it as single episode. Backend says it covers [00-00]`? -We recently added guard clause to prevent backends from sending possibly invalid episode ranges, as such if you see this, +We recently added guard clause to prevent backends from sending possibly invalid episode ranges, as such if you see +this, this likely means your backend mis-identified episodes range. By default, we allow an episode to cover up to 4 episodes. -If this is not enough for your library content. fear not we have you covered you can increase the limit by running the following command: +If this is not enough for your library content. fear not we have you covered you can increase the limit by running the +following command: ```bash $ docker exec -ti watchstate console config:edit --key options.MAX_EPISODE_RANGE --set 10 -- [BACKEND_NAME] diff --git a/README.md b/README.md index dde8a524..4b69e9d4 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,21 @@ This tool primary goal is to sync your backends play state without relying on third party services, out of the box, this tool support `Jellyfin`, `Plex` and `Emby` media servers. -## Feature updates +## updates + +### 2024-03-08 + +This update include breaking changes to how we process commands, we have streamlined the command interface to accept +some consistent flags and options. Notably, we have added `-s, --select-backend` flag to all commands that accept it. +commands that were accepting comma separated list of backends now needs to be separate option call for example +`--select-backend home_plex --select-backend home_jellyfin` instead of `--select-backend home_plex,home_jellyfin`. + +All commands that was accepting backend name as argument now accepts `-s, --select-backend` flag. This change is to make +the +command interface more consistent and easier to use. + +We started working no a `Web API` which hopefully will lead to a `web frontend` to manage the tool. This is a long +term goal, and it's not expected to be ready soon. However, the `Web API` is expected within 3rd quarter of 2024. ### 2023-11-11 @@ -136,10 +150,10 @@ $ docker exec -ti watchstate console state:import -v ``` This command will pull your play state from all your backends. To import from specific backends use -the `[-s, --select-backends]` flag which accept comma seperated list of backend names. For example, +the `[-s, --select-backend]` flag. For example, ```bash -$ docker exec -ti watchstate console state:import -v --select-backends 'home_plex,home_jellyfin' +$ docker exec -ti watchstate console state:import -v -s home_plex -s home_jellyfin ``` > [!NOTE] @@ -176,11 +190,10 @@ $ docker exec -ti watchstate console state:export -v ``` This command will export your current play state to all of your export enabled backends. To export to -specific backends use the `[-s, --select-backends]` flag which accept comma seperated list of backend names. For -example, +specific backends use the `[-s, --select-backend]`flag. For example, ```bash -$ docker exec -ti watchstate console state:export -v --select-backends 'home_plex,home_jellyfin' +$ docker exec -ti watchstate console state:export -v -s home_plex -s home_jellyfin ``` > [!NOTE] diff --git a/config/config.php b/config/config.php index c88eac76..8eb30b07 100644 --- a/config/config.php +++ b/config/config.php @@ -21,7 +21,7 @@ return (function () { $config = [ 'name' => 'WatchState', 'version' => '$(version_via_ci)', - 'tz' => env('WS_TZ', 'UTC'), + 'tz' => env('WS_TZ', env('TZ', 'UTC')), 'path' => fixPath(env('WS_DATA_PATH', fn() => $inContainer ? '/config' : __DIR__ . '/../var')), 'logs' => [ 'context' => (bool)env('WS_LOGS_CONTEXT', false), diff --git a/src/Commands/Backend/Ignore/ListCommand.php b/src/Commands/Backend/Ignore/ListCommand.php index 364cba03..56ebaf9c 100644 --- a/src/Commands/Backend/Ignore/ListCommand.php +++ b/src/Commands/Backend/Ignore/ListCommand.php @@ -51,10 +51,15 @@ final class ListCommand extends Command protected function configure(): void { $this->setName(self::ROUTE) - ->addOption('type', null, InputOption::VALUE_REQUIRED, 'Filter based on type.') - ->addOption('backend', null, InputOption::VALUE_REQUIRED, 'Filter based on backend.') - ->addOption('db', null, InputOption::VALUE_REQUIRED, 'Filter based on db.') - ->addOption('id', null, InputOption::VALUE_REQUIRED, 'Filter based on id.') + ->addOption( + 'select-backend', + 's', + InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, + 'Filter based on backend.' + ) + ->addOption('type', 't', InputOption::VALUE_REQUIRED, 'Filter based on type.') + ->addOption('db', 'd', InputOption::VALUE_REQUIRED, 'Filter based on db.') + ->addOption('id', 'i', InputOption::VALUE_REQUIRED, 'Filter based on id.') ->setDescription('List Ignored external ids.') ->setHelp( r( @@ -69,11 +74,11 @@ final class ListCommand extends Command # List all ignore rules that relate to specific backend. - {cmd} {route} --backend backend_name + {cmd} {route} -s backend_name # Appending more filters to narrow down list - {cmd} {route} --backend backend_name --db tvdb + {cmd} {route} -s backend_name -d tvdb HELP, [ @@ -102,10 +107,10 @@ final class ListCommand extends Command $list = []; - $fBackend = $input->getOption('backend'); $fType = $input->getOption('type'); $fDb = $input->getOption('db'); $fId = $input->getOption('id'); + $backends = $input->getOption('select-backend'); $ids = Config::get('ignore', []); @@ -118,19 +123,40 @@ final class ListCommand extends Command $id = ag($urlParts, 'pass'); $scope = ag($urlParts, 'query'); - if (null !== $fBackend && $backend !== $fBackend) { + if (!empty($backends) && !in_array($backend, $backends)) { + if (true === str_contains($backend, ',')) { + throw new \RuntimeException( + 'The option [-s --select-backend] does not support comma separated values. it should be used multiple times.' + ); + } + $output->writeln(r('Skipping \'{rule}\' as requested by [-s, --select-backend].', [ + 'rule' => $guid, + 'backend' => $backend + ]), OutputInterface::VERBOSITY_DEBUG); continue; } if (null !== $fType && $type !== $fType) { + $output->writeln(r('Skipping \'{rule}\' as requested by [-t, --type].', [ + 'rule' => $guid, + 'type' => $type + ]), OutputInterface::VERBOSITY_DEBUG); continue; } if (null !== $fDb && $db !== $fDb) { + $output->writeln(r('Skipping \'{rule}\' as requested by [-d, --db].', [ + 'rule' => $guid, + 'db' => $db + ]), OutputInterface::VERBOSITY_DEBUG); continue; } if (null !== $fId && $id !== $fId) { + $output->writeln(r('Skipping \'{rule}\' as requested by [-i, --id].', [ + 'rule' => $guid, + 'id' => $id + ]), OutputInterface::VERBOSITY_DEBUG); continue; } diff --git a/src/Commands/Backend/Ignore/ManageCommand.php b/src/Commands/Backend/Ignore/ManageCommand.php index 03d903c7..c23a3952 100644 --- a/src/Commands/Backend/Ignore/ManageCommand.php +++ b/src/Commands/Backend/Ignore/ManageCommand.php @@ -33,7 +33,7 @@ final class ManageCommand extends Command protected function configure(): void { $this->setName(self::ROUTE) - ->setDescription('Add/Remove external id from ignore list.') + ->setDescription('Add or remove external id from ignore list.') ->addOption('remove', 'r', InputOption::VALUE_NONE, 'Remove id from ignore list.') ->addArgument('id', InputArgument::REQUIRED, 'id.') ->setHelp( diff --git a/src/Commands/Backend/InfoCommand.php b/src/Commands/Backend/InfoCommand.php index 10c0aca4..e493f388 100644 --- a/src/Commands/Backend/InfoCommand.php +++ b/src/Commands/Backend/InfoCommand.php @@ -8,7 +8,6 @@ use App\Command; use App\Libs\Attributes\Route\Cli; use App\Libs\Options; use RuntimeException; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -35,8 +34,7 @@ class InfoCommand extends Command $this->setName(self::ROUTE) ->setDescription('Get backend product info.') ->addOption('include-raw-response', null, InputOption::VALUE_NONE, 'Include unfiltered raw response.') - ->addOption('select-backend', 's', InputOption::VALUE_REQUIRED, 'Select backend.') - ->addArgument('backend', InputArgument::OPTIONAL, 'Backend name to restore.'); + ->addOption('select-backend', 's', InputOption::VALUE_REQUIRED, 'Select backend.'); } /** @@ -48,23 +46,14 @@ class InfoCommand extends Command */ protected function runCommand(InputInterface $input, OutputInterface $output): int { - if (null !== ($name = $input->getOption('select-backend'))) { - $name = explode(',', $name, 2)[0]; - } - - if (empty($name) && null !== ($name = $input->getArgument('backend'))) { - $name = $input->getArgument('backend'); - $output->writeln( - 'WARNING: The use of backend name as argument is deprecated and will be removed from future versions. Please use [-s, --select-backend] option instead.' - ); - } + $mode = $input->getOption('output'); + $name = $input->getOption('select-backend'); if (empty($name)) { $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); return self::FAILURE; } - $mode = $input->getOption('output'); $opts = []; if ($input->getOption('include-raw-response')) { diff --git a/src/Commands/Backend/Library/IgnoreCommand.php b/src/Commands/Backend/Library/IgnoreCommand.php index c1b2ad52..b147732e 100644 --- a/src/Commands/Backend/Library/IgnoreCommand.php +++ b/src/Commands/Backend/Library/IgnoreCommand.php @@ -11,7 +11,6 @@ use App\Libs\Config; use App\Libs\ConfigFile; use Psr\Log\LoggerInterface; use RuntimeException; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -40,7 +39,6 @@ final class IgnoreCommand extends Command $this->setName(self::ROUTE) ->setDescription('Manage Backend ignored libraries.') ->addOption('select-backend', 's', InputOption::VALUE_REQUIRED, 'Select backend.') - ->addArgument('backend', InputArgument::OPTIONAL, 'Backend name.') ->setHelp( r( <<Id column, once you have list of your ids, you can run the following command to update the backend ignorelist. - {cmd} {backend_edit} --key 'options.ignore' --set 'id1,id2,id3' -- backend_name - - You can also directly update the config file at [{configPath}]. + {cmd} {backend_edit} --key 'options.ignore' --set 'id1,id2,id3' -s backend_name The [options.ignore] key accept comma seperated list of ids. @@ -89,16 +85,7 @@ final class IgnoreCommand extends Command */ protected function runCommand(InputInterface $input, OutputInterface $output, null|array $rerun = null): int { - if (null !== ($name = $input->getOption('select-backend'))) { - $name = explode(',', $name, 2)[0]; - } - - if (empty($name) && null !== ($name = $input->getArgument('backend'))) { - $name = $input->getArgument('backend'); - $output->writeln( - 'WARNING: The use of backend name as argument is deprecated and will be removed from future versions. Please use [-s, --select-backend] option instead.' - ); - } + $name = $input->getOption('select-backend'); if (empty($name)) { $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); diff --git a/src/Commands/Backend/Library/ListCommand.php b/src/Commands/Backend/Library/ListCommand.php index e5b832bd..8ec7e78d 100644 --- a/src/Commands/Backend/Library/ListCommand.php +++ b/src/Commands/Backend/Library/ListCommand.php @@ -8,7 +8,6 @@ use App\Command; use App\Libs\Attributes\Route\Cli; use App\Libs\Options; use RuntimeException; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -32,7 +31,6 @@ final class ListCommand extends Command ->setDescription('Get Backend libraries list.') ->addOption('include-raw-response', null, InputOption::VALUE_NONE, 'Include unfiltered raw response.') ->addOption('select-backend', 's', InputOption::VALUE_REQUIRED, 'Select backend.') - ->addArgument('backend', InputArgument::OPTIONAL, 'Backend name.') ->setHelp( <<getOption('select-backend'))) { - $name = explode(',', $name, 2)[0]; - } - - if (empty($name) && null !== ($name = $input->getArgument('backend'))) { - $name = $input->getArgument('backend'); - $output->writeln( - 'WARNING: The use of backend name as argument is deprecated and will be removed from future versions. Please use [-s, --select-backend] option instead.' - ); - } + $mode = $input->getOption('output'); + $name = $input->getOption('select-backend'); if (empty($name)) { $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); return self::FAILURE; } - $mode = $input->getOption('output'); - $opts = $backendOpts = []; if ($input->getOption('include-raw-response')) { diff --git a/src/Commands/Backend/Library/MismatchCommand.php b/src/Commands/Backend/Library/MismatchCommand.php index 21e6958a..4aceff1d 100644 --- a/src/Commands/Backend/Library/MismatchCommand.php +++ b/src/Commands/Backend/Library/MismatchCommand.php @@ -11,7 +11,6 @@ use App\Libs\Options; use RuntimeException; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -86,7 +85,6 @@ final class MismatchCommand extends Command ->addOption('cutoff', null, InputOption::VALUE_REQUIRED, 'Increase title cutoff', self::CUTOFF) ->addOption('id', null, InputOption::VALUE_REQUIRED, 'backend Library id.') ->addOption('select-backend', 's', InputOption::VALUE_REQUIRED, 'Select backend.') - ->addArgument('backend', InputArgument::OPTIONAL, 'Backend name.') ->setHelp( r( <<getOption('select-backend'))) { - $name = explode(',', $name, 2)[0]; - } - - if (empty($name) && null !== ($name = $input->getArgument('backend'))) { - $name = $input->getArgument('backend'); - $output->writeln( - 'WARNING: The use of backend name as argument is deprecated and will be removed from future versions. Please use [-s, --select-backend] option instead.' - ); - } - - if (empty($name)) { - $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); - return self::FAILURE; - } - $mode = $input->getOption('output'); $showAll = $input->getOption('show-all'); $percentage = $input->getOption('percentage'); $id = $input->getOption('id'); $cutoff = (int)$input->getOption('cutoff'); + $name = $input->getOption('select-backend'); + if (empty($name)) { + $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); + return self::FAILURE; + } $backendOpts = $opts = $list = []; diff --git a/src/Commands/Backend/Library/UnmatchedCommand.php b/src/Commands/Backend/Library/UnmatchedCommand.php index 30990153..921ca0d5 100644 --- a/src/Commands/Backend/Library/UnmatchedCommand.php +++ b/src/Commands/Backend/Library/UnmatchedCommand.php @@ -9,7 +9,6 @@ use App\Libs\Attributes\Route\Cli; use App\Libs\Config; use App\Libs\Options; use RuntimeException; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -45,7 +44,6 @@ final class UnmatchedCommand extends Command ->addOption('cutoff', null, InputOption::VALUE_REQUIRED, 'Increase title cutoff', self::CUTOFF) ->addOption('id', null, InputOption::VALUE_REQUIRED, 'backend Library id.') ->addOption('select-backend', 's', InputOption::VALUE_REQUIRED, 'Select backend.') - ->addArgument('backend', InputArgument::OPTIONAL, 'Backend name.') ->setHelp( r( <<getOption('select-backend'))) { - $name = explode(',', $name, 2)[0]; - } - - if (empty($name) && null !== ($name = $input->getArgument('backend'))) { - $name = $input->getArgument('backend'); - $output->writeln( - 'WARNING: The use of backend name as argument is deprecated and will be removed from future versions. Please use [-s, --select-backend] option instead.' - ); - } + $mode = $input->getOption('output'); + $showAll = $input->getOption('show-all'); + $id = $input->getOption('id'); + $cutoff = (int)$input->getOption('cutoff'); + $name = $input->getOption('select-backend'); if (empty($name)) { $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); return self::FAILURE; } - $mode = $input->getOption('output'); - $showAll = $input->getOption('show-all'); - $id = $input->getOption('id'); - $cutoff = (int)$input->getOption('cutoff'); - $backendOpts = $opts = $list = []; if ($input->getOption('timeout')) { diff --git a/src/Commands/Backend/RestoreCommand.php b/src/Commands/Backend/RestoreCommand.php index 472df678..bd23bfa6 100644 --- a/src/Commands/Backend/RestoreCommand.php +++ b/src/Commands/Backend/RestoreCommand.php @@ -87,7 +87,7 @@ class RestoreCommand extends Command For example, - {cmd} {route} --execute -vv -s backend_name -- {backupDir}/backup_file.json + {cmd} {route} --execute -vv -s backend_name -- {backupDir}/backup_file.json ------- [ FAQ ] @@ -148,13 +148,13 @@ class RestoreCommand extends Command */ protected function process(InputInterface $input, OutputInterface $output): int { - if (null === ($name = $input->getOption('select-backend'))) { - $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backends].')); + $name = $input->getOption('select-backend'); + + if (empty($name)) { + $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); return self::FAILURE; } - $name = explode(',', $name, 2)[0]; - $file = $input->getArgument('file'); if (false === file_exists($file) || false === is_readable($file)) { diff --git a/src/Commands/Backend/Search/IdCommand.php b/src/Commands/Backend/Search/IdCommand.php index 4c617e8c..20a575ae 100644 --- a/src/Commands/Backend/Search/IdCommand.php +++ b/src/Commands/Backend/Search/IdCommand.php @@ -44,7 +44,7 @@ final class IdCommand extends Command [--output] flag to [json or yaml] and use the [--include-raw-response] flag. For example, - {cmd} {route} --output yaml --include-raw-response -- backend_name backend_item_id + {cmd} {route} --output yaml --include-raw-response -s backend_name backend_item_id HELP, [ @@ -67,14 +67,11 @@ final class IdCommand extends Command { $mode = $input->getOption('output'); $id = $input->getArgument('id'); + $name = $input->getOption('select-backend'); - if (null === ($name = $input->getOption('select-backend'))) { - $output->writeln( - r('ERROR: You must select a backend using [-s, --select-backends] option.') - ); + if (empty($name)) { + $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); return self::FAILURE; - } else { - $name = explode(',', $name)[0]; } $backendOpts = []; diff --git a/src/Commands/Backend/Search/QueryCommand.php b/src/Commands/Backend/Search/QueryCommand.php index 7337b162..005b535a 100644 --- a/src/Commands/Backend/Search/QueryCommand.php +++ b/src/Commands/Backend/Search/QueryCommand.php @@ -46,7 +46,7 @@ final class QueryCommand extends Command [--output] flag to [json or yaml] and use the [--include-raw-response] flag. For example, - {cmd} {route} --output yaml --include-raw-response -- backend_name 'search query word' + {cmd} {route} --output yaml --include-raw-response -s backend_name 'search query word' HELP, [ @@ -69,14 +69,11 @@ final class QueryCommand extends Command { $mode = $input->getOption('output'); $query = $input->getArgument('query'); + $name = $input->getOption('select-backend'); - if (null === ($name = $input->getOption('select-backend'))) { - $output->writeln( - r('ERROR: You must select a backend using [-s, --select-backends] option.') - ); + if (empty($name)) { + $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); return self::FAILURE; - } else { - $name = explode(',', $name)[0]; } $opts = $backendOpts = []; diff --git a/src/Commands/Backend/Users/ListCommand.php b/src/Commands/Backend/Users/ListCommand.php index 2cad35df..c3889ddb 100644 --- a/src/Commands/Backend/Users/ListCommand.php +++ b/src/Commands/Backend/Users/ListCommand.php @@ -9,7 +9,6 @@ use App\Command; use App\Libs\Attributes\Route\Cli; use App\Libs\Options; use RuntimeException; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -42,7 +41,6 @@ final class ListCommand extends Command ->addOption('use-token', 'u', InputOption::VALUE_REQUIRED, 'Use this given token.') ->addOption('include-raw-response', null, InputOption::VALUE_NONE, 'Include unfiltered raw response.') ->addOption('select-backend', 's', InputOption::VALUE_REQUIRED, 'Select backend') - ->addArgument('backend', InputArgument::OPTIONAL, 'Backend name.') ->setHelp( r( <<# How to get user tokens? - {cmd} {route} --with-tokens -s backend_name + {cmd} {route} --with-tokens -s backend_name Notice: If you have many plex users and request tokens for all of them you may get rate-limited by plex api, you shouldn't do this unless you have good reason. In most cases you don't need to, and can use @@ -68,7 +66,7 @@ final class ListCommand extends Command # How to see the raw response? - {cmd} {route} --output yaml --include-raw-response -s backend_name + {cmd} {route} --output yaml --include-raw-response -s backend_name # My Plex backend report only one user? @@ -76,7 +74,7 @@ final class ListCommand extends Command work for managed user we have to use the managed user token instead of the admin user token due to plex api limitation. To see list of your users you can do the following. - {cmd} {route} --use-token PLEX_TOKEN -s backend_name + {cmd} {route} --use-token PLEX_ADMIN_TOKEN -s backend_name HELP, [ @@ -100,24 +98,14 @@ final class ListCommand extends Command */ protected function runCommand(InputInterface $input, OutputInterface $output): int { - if (null !== ($name = $input->getOption('select-backend'))) { - $name = explode(',', $name, 2)[0]; - } - - if (empty($name) && null !== ($name = $input->getArgument('backend'))) { - $name = $input->getArgument('backend'); - $output->writeln( - 'WARNING: The use of backend name as argument is deprecated and will be removed from future versions. Please use [-s, --select-backend] option instead.' - ); - } + $mode = $input->getOption('output'); + $name = $input->getOption('select-backend'); if (empty($name)) { $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); return self::FAILURE; } - $mode = $input->getOption('output'); - $opts = $backendOpts = []; if ($input->getOption('with-tokens')) { diff --git a/src/Commands/Backend/Users/SessionsCommand.php b/src/Commands/Backend/Users/SessionsCommand.php index 422c8f49..3789c3e1 100644 --- a/src/Commands/Backend/Users/SessionsCommand.php +++ b/src/Commands/Backend/Users/SessionsCommand.php @@ -80,14 +80,12 @@ final class SessionsCommand extends Command protected function runCommand(InputInterface $input, OutputInterface $output): int { $mode = $input->getOption('output'); - - if (null === ($name = $input->getOption('select-backend'))) { - $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backends].')); + $name = $input->getOption('select-backend'); + if (empty($name)) { + $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); return self::FAILURE; } - $name = explode(',', $name, 2)[0]; - $opts = $backendOpts = []; if ($input->getOption('include-raw-response')) { diff --git a/src/Commands/Backend/VersionCommand.php b/src/Commands/Backend/VersionCommand.php index 23b23ad7..bb4e4749 100644 --- a/src/Commands/Backend/VersionCommand.php +++ b/src/Commands/Backend/VersionCommand.php @@ -7,7 +7,6 @@ namespace App\Commands\Backend; use App\Command; use App\Libs\Attributes\Route\Cli; use RuntimeException; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -29,8 +28,7 @@ class VersionCommand extends Command { $this->setName(self::ROUTE) ->setDescription('Get backend product version.') - ->addOption('select-backend', 's', InputOption::VALUE_REQUIRED, 'Select backend.') - ->addArgument('backend', InputArgument::OPTIONAL, 'Backend name to restore.'); + ->addOption('select-backend', 's', InputOption::VALUE_REQUIRED, 'Select backend.'); } /** @@ -43,17 +41,7 @@ class VersionCommand extends Command */ protected function runCommand(InputInterface $input, OutputInterface $output): int { - if (null !== ($name = $input->getOption('select-backend'))) { - $name = explode(',', $name, 2)[0]; - } - - if (empty($name) && null !== ($name = $input->getArgument('backend'))) { - $name = $input->getArgument('backend'); - $output->writeln( - 'WARNING: The use of backend name as argument is deprecated and will be removed from future versions. Please use [-s, --select-backend] option instead.' - ); - } - + $name = $input->getOption('select-backend'); if (empty($name)) { $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); return self::FAILURE; diff --git a/src/Commands/Config/AddCommand.php b/src/Commands/Config/AddCommand.php index 9a80a3de..8d022488 100644 --- a/src/Commands/Config/AddCommand.php +++ b/src/Commands/Config/AddCommand.php @@ -8,7 +8,6 @@ use App\Command; use App\Libs\Attributes\Route\Cli; use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -33,7 +32,6 @@ final class AddCommand extends Command { $this->setName(self::ROUTE) ->setDescription('Add new backend.') - ->addArgument('backend', InputArgument::OPTIONAL, 'Backend name', null) ->addOption('select-backend', 's', InputOption::VALUE_REQUIRED, 'Select backend.') ->setHelp( r( @@ -103,22 +101,11 @@ final class AddCommand extends Command $opts['--' . $option] = $val; } - if (null !== ($name = $input->getOption('select-backend'))) { - $name = explode(',', $name, 2)[0]; - } + $name = $input->getOption('select-backend'); - if (empty($name) && null !== ($name = $input->getArgument('backend'))) { - $name = $input->getArgument('backend'); - $output->writeln( - 'WARNING: The use of backend name as argument is deprecated and will be removed from future versions. Please use [-s, --select-backend] option instead.' - ); - } - - if (null !== $name) { - $opts['backend'] = strtolower($name); - } else { + if (empty($name)) { // -- $backend.token - $opts['backend'] = (function () use (&$opts, $input, $output) { + $name = (function () use (&$opts, $input, $output) { $chosen = ag($opts, 'backend'); $question = new Question( @@ -152,10 +139,10 @@ final class AddCommand extends Command return (new QuestionHelper())->ask($input, $output, $question); })(); - $output->writeln(''); - $opts['--select-backend'] = strtolower($opts['backend']); } + $opts['--select-backend'] = strtolower($name); + return $this->getApplication()?->find(ManageCommand::ROUTE)->run(new ArrayInput($opts), $output) ?? 1; } } diff --git a/src/Commands/Config/DeleteCommand.php b/src/Commands/Config/DeleteCommand.php index cf8456e7..5c842282 100644 --- a/src/Commands/Config/DeleteCommand.php +++ b/src/Commands/Config/DeleteCommand.php @@ -11,7 +11,6 @@ use App\Libs\ConfigFile; use App\Libs\Database\DatabaseInterface as iDB; use PDO; use Psr\Log\LoggerInterface; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -45,7 +44,6 @@ final class DeleteCommand extends Command $this->setName(self::ROUTE) ->setDescription('Delete Local backend data.') ->addOption('select-backend', 's', InputOption::VALUE_REQUIRED, 'Select backend.') - ->addArgument('backend', InputArgument::OPTIONAL, 'Backend name.') ->setHelp( r( <<getOption('select-backend'))) { - $name = explode(',', $name, 2)[0]; - } - - if (empty($name) && null !== ($name = $input->getArgument('backend'))) { - $name = $input->getArgument('backend'); - $output->writeln( - 'WARNING: The use of backend name as argument is deprecated and will be removed from future versions. Please use [-s, --select-backend] option instead.' - ); - } - + $name = $input->getOption('select-backend'); if (empty($name)) { $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); return self::FAILURE; diff --git a/src/Commands/Config/EditCommand.php b/src/Commands/Config/EditCommand.php index efb1f029..6fff16e4 100644 --- a/src/Commands/Config/EditCommand.php +++ b/src/Commands/Config/EditCommand.php @@ -11,7 +11,6 @@ use App\Libs\ConfigFile; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -45,14 +44,13 @@ final class EditCommand extends Command ->addOption('delete', 'd', InputOption::VALUE_NONE, 'Delete value.') ->addOption('regenerate-webhook-token', 'g', InputOption::VALUE_NONE, 'Re-generate backend webhook token.') ->addOption('select-backend', 's', InputOption::VALUE_REQUIRED, 'Select backend.') - ->addArgument('backend', InputArgument::OPTIONAL, 'Backend name') ->setHelp( r( <<edit backend config settings inline. - The [--key] accept string value. the list of officially supported keys are: + The [-k, --key] accept string value. the list of officially supported keys are: [{keyNames}] @@ -62,7 +60,7 @@ final class EditCommand extends Command # How to edit config setting? - {cmd} {route} --key key --set value -s backend_name + {cmd} {route} -k key -e value -s backend_name # How to change the re-generate webhook token? @@ -102,16 +100,7 @@ final class EditCommand extends Command */ protected function runCommand(InputInterface $input, OutputInterface $output, null|array $rerun = null): int { - if (null !== ($name = $input->getOption('select-backend'))) { - $name = explode(',', $name, 2)[0]; - } - - if (empty($name) && null !== ($name = $input->getArgument('backend'))) { - $name = $input->getArgument('backend'); - $output->writeln( - 'WARNING: The use of backend name as argument is deprecated and will be removed from future versions. Please use [-s, --select-backend] option instead.' - ); - } + $name = $input->getOption('select-backend'); if (empty($name)) { $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); diff --git a/src/Commands/Config/ManageCommand.php b/src/Commands/Config/ManageCommand.php index 5a22c741..47a04090 100644 --- a/src/Commands/Config/ManageCommand.php +++ b/src/Commands/Config/ManageCommand.php @@ -14,7 +14,6 @@ use App\Libs\Options; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Exception\ExceptionInterface; use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -44,7 +43,6 @@ final class ManageCommand extends Command ->setDescription('Manage backend settings.') ->addOption('add', 'a', InputOption::VALUE_NONE, 'Add Backend.') ->addOption('select-backend', 's', InputOption::VALUE_REQUIRED, 'Select backend.') - ->addArgument('backend', InputArgument::OPTIONAL, 'Backend name.') ->setHelp( r( <<getOption('select-backend'))) { - $name = explode(',', $name, 2)[0]; - } - - if (empty($name) && null !== ($name = $input->getArgument('backend'))) { - $name = $input->getArgument('backend'); - $output->writeln( - 'WARNING: The use of backend name as argument is deprecated and will be removed from future versions. Please use [-s, --select-backend] option instead.' - ); - } + $name = $input->getOption('select-backend'); if (empty($name)) { $output->writeln(r('ERROR: Backend not specified. Please use [-s, --select-backend].')); @@ -451,7 +440,7 @@ final class ManageCommand extends Command ); $cmd = $this->getApplication()?->find(ImportCommand::ROUTE); - $cmd->run(new ArrayInput(['--quiet', '--select-backends' => $name]), $output); + $cmd->run(new ArrayInput(['--quiet', '--select-backend' => $name]), $output); } $output->writeln('Import complete'); diff --git a/src/Commands/Config/ViewCommand.php b/src/Commands/Config/ViewCommand.php index 9cb00281..6fa63166 100644 --- a/src/Commands/Config/ViewCommand.php +++ b/src/Commands/Config/ViewCommand.php @@ -4,11 +4,10 @@ declare(strict_types=1); namespace App\Commands\Config; +use App\API\Backends\Index; use App\Command; use App\Libs\Attributes\Route\Cli; use App\Libs\Config; -use App\Libs\Exceptions\RuntimeException; -use App\Libs\Options; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Helper\Table; @@ -29,15 +28,6 @@ final class ViewCommand extends Command { public const ROUTE = 'config:view'; - /** - * @var array Keys to be hidden from general view. - */ - private array $hidden = [ - 'token', - 'webhook.token', - 'options.' . Options::ADMIN_TOKEN - ]; - /** * Configure the command. */ @@ -45,8 +35,13 @@ final class ViewCommand extends Command { $this->setName(self::ROUTE) ->setDescription('View Backends settings.') - ->addOption('select-backends', 's', InputOption::VALUE_REQUIRED, 'Select backends. comma , seperated.') - ->addOption('exclude', null, InputOption::VALUE_NONE, 'Inverse --select-backends logic.') + ->addOption( + 'select-backend', + 's', + InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, + 'Select backend.' + ) + ->addOption('exclude', null, InputOption::VALUE_NONE, 'Inverse --select-backend logic.') ->addOption('expose', 'x', InputOption::VALUE_NONE, 'Expose the secret tokens in the view.') ->addArgument( 'filter', @@ -66,11 +61,11 @@ final class ViewCommand extends Command # How to show one backend information? - The flag [-s, --select-backends] accept comma seperated list of backends name, Using the flag - in combination with [--exclude] flag will flip the logic to exclude the selected backends - rather than include them. + The flag [-s, --select-backend] is array option with can accept many backends names, + Using the flag in combination with [--exclude] flag will flip the logic to exclude + the selected backends rather than include them. - {cmd} {route} --select-backends my_backend + {cmd} {route} -s my_backend # How to show specific key? @@ -98,16 +93,14 @@ final class ViewCommand extends Command */ protected function runCommand(InputInterface $input, OutputInterface $output): int { - $selectBackends = (string)$input->getOption('select-backends'); - $list = []; - $selected = array_map('trim', explode(',', $selectBackends)); - $isCustom = !empty($selectBackends) && count($selected) >= 1; + $selected = $input->getOption('select-backend'); + $isCustom = count($selected) > 0; $filter = $input->getArgument('filter'); foreach (Config::get('servers', []) as $backendName => $backend) { if ($isCustom && $input->getOption('exclude') === in_array($backendName, $selected)) { - $output->writeln(r('Ignoring backend \'{backend}\' as requested by [-s, --select-backends].', [ + $output->writeln(r('Ignoring backend \'{backend}\' as requested by [-s, --select-backend].', [ 'backend' => $backendName ]), OutputInterface::VERBOSITY_VERY_VERBOSE); continue; @@ -117,9 +110,10 @@ final class ViewCommand extends Command } if (empty($list)) { - throw new RuntimeException( - $isCustom ? '[-s, --select-backends] did not return any backend.' : 'No backends were found.' - ); + $output->writeln(r('{error}', [ + 'error' => $isCustom ? '[-s, --select-backend] did not return any backend.' : 'No backends were found.' + ])); + return self::FAILURE; } $x = 0; @@ -189,7 +183,7 @@ final class ViewCommand extends Command private function filterData(array $backend, string|null $filter = null, bool $expose = false): string { if (null === $filter && true !== $expose) { - foreach ($this->hidden as $hideValue) { + foreach (Index::BLACK_LIST as $hideValue) { if (true === ag_exists($backend, $hideValue)) { $backend = ag_set($backend, $hideValue, '*HIDDEN*'); } diff --git a/src/Commands/State/BackupCommand.php b/src/Commands/State/BackupCommand.php index 1b1c5bc4..2084a0e6 100644 --- a/src/Commands/State/BackupCommand.php +++ b/src/Commands/State/BackupCommand.php @@ -59,8 +59,13 @@ class BackupCommand extends Command ) ->addOption('dry-run', null, InputOption::VALUE_NONE, 'No actions will be committed.') ->addOption('timeout', null, InputOption::VALUE_REQUIRED, 'Set request timeout in seconds.') - ->addOption('select-backends', 's', InputOption::VALUE_OPTIONAL, 'Select backends. comma , seperated.', '') - ->addOption('exclude', null, InputOption::VALUE_NONE, 'Inverse --select-backends logic.') + ->addOption( + 'select-backend', + 's', + InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, + 'Select backend.', + ) + ->addOption('exclude', null, InputOption::VALUE_NONE, 'Inverse --select-backend logic.') ->addOption( 'no-enhance', null, @@ -88,7 +93,7 @@ class BackupCommand extends Command Backups generated without [-k, --keep] flag are subject to be REMOVED during system:prune run. To keep permanent copy of your backups you can use the [-k, --keep] flag. For example: - {cmd} {route} --keep [--select-backends backend_name] + {cmd} {route} --keep [--select-backend backend_name] Backups generated with [-k, --keep] flag will not contain a date and will be named [backend_name.json] where automated backups will be named [backend_name.00000000{date}.json] @@ -112,11 +117,11 @@ class BackupCommand extends Command # I want different file name for my backup? Backup names are something tricky, however it's possible to choose the backup filename if the total number - of backed up backends are 1. So, in essence you have to combine two flags [-s, --select-backends] and [--file]. + of backed up backends are 1. So, in essence you have to combine two flags [-s, --select-backend] and [--file]. For example, to back up [backend_name] backend data to [/tmp/backend_name.json] do the following: - {cmd} {route} --select-backends backend_name --file /tmp/my_backend.json + {cmd} {route} --select-backend backend_name --file /tmp/my_backend.json HELP, [ @@ -152,9 +157,8 @@ class BackupCommand extends Command protected function process(InputInterface $input): int { $list = []; - $selectBackends = (string)$input->getOption('select-backends'); - $selected = explode(',', $selectBackends); - $isCustom = !empty($selectBackends) && count($selected) >= 1; + $selected = $input->getOption('select-backend'); + $isCustom = !empty($selected) && count($selected) > 0; $supported = Config::get('supported', []); $mapperOpts = []; @@ -211,7 +215,7 @@ class BackupCommand extends Command if (empty($list)) { $this->logger->warning( - $isCustom ? '[-s, --select-backends] flag did not match any backend.' : 'No backends were found.' + $isCustom ? '[-s, --select-backend] flag did not match any backend.' : 'No backends were found.' ); return self::FAILURE; } diff --git a/src/Commands/State/ExportCommand.php b/src/Commands/State/ExportCommand.php index a2adb70f..df257a43 100644 --- a/src/Commands/State/ExportCommand.php +++ b/src/Commands/State/ExportCommand.php @@ -67,8 +67,13 @@ class ExportCommand extends Command ->addOption('force-full', 'f', InputOption::VALUE_NONE, 'Force full export. Ignore last export date.') ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not commit changes to backends.') ->addOption('timeout', null, InputOption::VALUE_REQUIRED, 'Set request timeout in seconds.') - ->addOption('select-backends', 's', InputOption::VALUE_OPTIONAL, 'Select backends. comma , seperated.', '') - ->addOption('exclude', null, InputOption::VALUE_NONE, 'Inverse --select-backends logic.') + ->addOption( + 'select-backend', + 's', + InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, + 'Select backend.' + ) + ->addOption('exclude', null, InputOption::VALUE_NONE, 'Inverse --select-backend logic.') ->addOption('ignore-date', 'i', InputOption::VALUE_NONE, 'Ignore date comparison.') ->addOption('logfile', null, InputOption::VALUE_REQUIRED, 'Save console output to file.') ->setHelp( @@ -84,15 +89,15 @@ class ExportCommand extends Command # How to force export play state to a backend? - You have to use the following flags [-f, --force-full] and [-i, --ignore-date]. For example, + You have to use the following flags [-f, -f] and [-i, -i]. For example, - {cmd} {route} -fi --select-backends backend_name + {cmd} {route} -fi -s backend_name # how to see what will be changed without committing them? You have to use the [--dry-run]. For example, - {cmd} {route} -v --dry-run --select-backends backend_name + {cmd} {route} -v --dry-run -s backend_name # Ignoring [backend_name] [item_title]. [Movie|Episode] Is not imported yet. @@ -145,11 +150,9 @@ class ExportCommand extends Command $configFile = ConfigFile::open(Config::get('backends_file'), 'yaml'); $configFile->setLogger($this->logger); - $selectBackends = (string)$input->getOption('select-backends'); - $backends = []; - $selected = explode(',', $selectBackends); - $isCustom = !empty($selectBackends) && count($selected) >= 1; + $selected = $input->getOption('select-backend'); + $isCustom = !empty($selected) && count($selected) > 0; $supported = Config::get('supported', []); $export = $push = $entities = []; @@ -161,7 +164,7 @@ class ExportCommand extends Command $type = strtolower(ag($backend, 'type', 'unknown')); if ($isCustom && $input->getOption('exclude') === in_array($backendName, $selected)) { - $this->logger->info('{backend}: Ignoring backend as requested by [-s, --select-backends].', [ + $this->logger->info('{backend}: Ignoring backend as requested by [-s, --select-backend].', [ 'backend' => $backendName ]); continue; @@ -197,7 +200,7 @@ class ExportCommand extends Command if (empty($backends)) { $this->logger->warning( - $isCustom ? '[-s, --select-backends] flag did not match any backend.' : 'No backends were found.' + $isCustom ? '[-s, --select-backend] flag did not match any backend.' : 'No backends were found.' ); return self::FAILURE; } diff --git a/src/Commands/State/ImportCommand.php b/src/Commands/State/ImportCommand.php index 452adfad..cef88b4a 100644 --- a/src/Commands/State/ImportCommand.php +++ b/src/Commands/State/ImportCommand.php @@ -66,8 +66,13 @@ class ImportCommand extends Command ->addOption('force-full', 'f', InputOption::VALUE_NONE, 'Force full import. Ignore last sync date.') ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not commit any changes.') ->addOption('timeout', null, InputOption::VALUE_REQUIRED, 'Set request timeout in seconds.') - ->addOption('select-backends', 's', InputOption::VALUE_OPTIONAL, 'Select backends. comma , seperated.', '') - ->addOption('exclude', null, InputOption::VALUE_NONE, 'Inverse --select-backends logic.') + ->addOption( + 'select-backend', + 's', + InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, + 'Select backend.' + ) + ->addOption('exclude', null, InputOption::VALUE_NONE, 'Inverse --select-backend logic.') ->addOption( 'direct-mapper', null, @@ -106,7 +111,7 @@ class ImportCommand extends Command # How to import from specific backend? - {cmd} {route} --select-backends backend_name + {cmd} {route} -s backend_name # How to Import the metadata only? @@ -116,7 +121,7 @@ class ImportCommand extends Command If you want to permanently increase the timeout for specific backend, you can do the following - {cmd} {config_edit} --key options.client.timeout --set 600.0 -- backend_name + {cmd} {config_edit} -k options.client.timeout -e 600.0 -s backend_name 600.0 seconds is equal to 10 minutes before the timeout handler kicks in. Alternatively, you can also increase the timeout temporarily by using the [--timeout] flag. It will increase the timeout for all backends during this run. @@ -142,8 +147,8 @@ class ImportCommand extends Command By default commands only show log level WARNING and higher, to see more verbose output You can use the [-v|-vv|-vvv] flag to signal that you want more output. And you can enable - even more info by using [--trace] and [--context] flags. Be warned the output using all those flags - is quite excessive and shouldn't be used unless told by the team. + even more info by using [--debug] flag. Be warned the output is quite excessive + and shouldn't be used unless told by the team. {cmd} {route} -vvv --trace --context @@ -170,7 +175,7 @@ class ImportCommand extends Command or you could use the built-in unmatched checker. - {cmd} {unmatched_route} -- backend_name + {cmd} {unmatched_route} -s backend_name If you don't have any unmatched items, this likely means you are using unsupported external db ids. @@ -201,7 +206,7 @@ class ImportCommand extends Command Running this command will force full export your current database state to the selected backend. Once that done you can turn on import from the new backend. by editing the backend setting: - {cmd} config:manage backend_name + {cmd} config:manage -s backend_name HELP, [ @@ -246,9 +251,8 @@ class ImportCommand extends Command $list = []; - $selectBackends = (string)$input->getOption('select-backends'); - $selected = explode(',', $selectBackends); - $isCustom = !empty($selectBackends) && count($selected) >= 1; + $selected = $input->getOption('select-backend'); + $isCustom = !empty($selected) && count($selected) > 0; $supported = Config::get('supported', []); $mapperOpts = []; @@ -330,7 +334,7 @@ class ImportCommand extends Command if (empty($list)) { $this->logger->warning( - $isCustom ? '[-s, --select-backends] flag did not match any backend.' : 'No backends were found.' + $isCustom ? '[-s, --select-backend] flag did not match any backend.' : 'No backends were found.' ); return self::FAILURE; } diff --git a/src/Commands/System/EnvCommand.php b/src/Commands/System/EnvCommand.php index 7cb9f92a..95348fc0 100644 --- a/src/Commands/System/EnvCommand.php +++ b/src/Commands/System/EnvCommand.php @@ -20,6 +20,8 @@ final class EnvCommand extends Command { public const ROUTE = 'system:env'; + private const EXEMPT_KEYS = ['HTTP_PORT', 'TZ']; + /** * Configure the command. */ @@ -43,6 +45,8 @@ final class EnvCommand extends Command * the key SHOULD attempt to mirror the key path in default config, If not applicable or otherwise impossible it should then use an approximate path. + * The following keys are exempt from the rules: [{exempt_keys}]. + ------- [ FAQ ] ------- @@ -57,7 +61,6 @@ final class EnvCommand extends Command For example, to enable import task, do the following: ------------------------------- - version: '3.3' services: watchstate: image: ghcr.io/arabcoders/watchstate:latest @@ -80,7 +83,8 @@ final class EnvCommand extends Command HELP, [ - 'path' => after(Config::get('path') . '/config', ROOT_PATH) + 'path' => after(Config::get('path') . '/config', ROOT_PATH), + 'exempt_keys' => implode(', ', self::EXEMPT_KEYS), ] ) ); @@ -100,7 +104,7 @@ final class EnvCommand extends Command $keys = []; foreach (getenv() as $key => $val) { - if (false === str_starts_with($key, 'WS_') && $key !== 'HTTP_PORT') { + if (false === str_starts_with($key, 'WS_') && in_array($key, self::EXEMPT_KEYS)) { continue; } diff --git a/src/Commands/System/ReportCommand.php b/src/Commands/System/ReportCommand.php index f02cce9d..05b31df1 100644 --- a/src/Commands/System/ReportCommand.php +++ b/src/Commands/System/ReportCommand.php @@ -131,6 +131,7 @@ final class ReportCommand extends Command $this->getTasks($output); $output->writeln('[ Logs ]' . PHP_EOL); $this->getLogs($input, $output); + $this->printFooter($output); return self::SUCCESS; } @@ -407,4 +408,20 @@ final class ReportCommand extends Command $output->writeln($line); } } + + private function printFooter(iOutput $output) + { + $output->writeln('[ Notice ]'); + $output->writeln( + trim( + <<