diff --git a/src/Backends/Emby/Action/Progress.php b/src/Backends/Emby/Action/Progress.php index c5c623ed..bfb90a65 100644 --- a/src/Backends/Emby/Action/Progress.php +++ b/src/Backends/Emby/Action/Progress.php @@ -83,7 +83,9 @@ class Progress ]; if ($context->backendName === $entity->via) { - $this->logger->info('Ignoring [{item.title}] for [{client}: {backend}]. Event generator.', [ + $this->logger->info( + 'Emby.Progress: Ignoring [{item.title}] for [{client}: {backend}]. Event generator.', + [ 'client' => $context->clientName, 'backend' => $context->backendName, ...$logContext, @@ -93,21 +95,27 @@ class Progress } if (null === ag($metadata, iState::COLUMN_ID, null)) { - $this->logger->warning('Ignoring [{item.title}] for [{client}: {backend}]. No metadata.', [ - 'client' => $context->clientName, - 'backend' => $context->backendName, - ...$logContext, - ]); + $this->logger->warning( + 'Emby.Progress: Ignoring [{item.title}] for [{client}: {backend}]. No metadata.', + [ + 'client' => $context->clientName, + 'backend' => $context->backendName, + ...$logContext, + ] + ); continue; } $senderDate = ag($entity->getExtra($entity->via), iState::COLUMN_EXTRA_DATE); if (null === $senderDate) { - $this->logger->warning('Ignoring [{item.title}] for [{client}: {backend}]. Sender date is not set.', [ - 'client' => $context->clientName, - 'backend' => $context->backendName, - ...$logContext, - ]); + $this->logger->warning( + 'Emby.Progress: Ignoring [{item.title}] for [{client}: {backend}]. Sender date is not set.', + [ + 'client' => $context->clientName, + 'backend' => $context->backendName, + ...$logContext, + ] + ); continue; } $senderDate = makeDate($senderDate)->getTimestamp(); @@ -115,7 +123,7 @@ class Progress $datetime = ag($entity->getExtra($context->backendName), iState::COLUMN_EXTRA_DATE, null); if (false === $ignoreDate && null !== $datetime && makeDate($datetime)->getTimestamp() > $senderDate) { $this->logger->warning( - 'Ignoring [{item.title}] for [{client}: {backend}]. Sender date is older than recorded backend date.', + 'Emby.Progress: Ignoring [{item.title}] for [{client}: {backend}]. Sender date is older than recorded backend date.', [ 'client' => $context->clientName, 'backend' => $context->backendName, @@ -138,7 +146,7 @@ class Progress if (false === $ignoreDate && makeDate($remoteItem->updated)->getTimestamp() > $senderDate) { $this->logger->info( - 'Ignoring [{item.title}] for [{client}: {backend}]. Sender date is older than backend remote item date.', + 'Emby.Progress: Ignoring [{item.title}] for [{client}: {backend}]. Sender date is older than backend remote item date.', [ 'client' => $context->clientName, 'backend' => $context->backendName, @@ -150,7 +158,7 @@ class Progress if ($remoteItem->isWatched()) { $this->logger->info( - 'Ignoring [{item.title}] for [{client}: {backend}]. The backend reported the item as watched.', + 'Emby.Progress: Ignoring [{item.title}] for [{client}: {backend}]. The backend reported the item as watched.', [ 'client' => $context->clientName, 'backend' => $context->backendName, @@ -194,13 +202,16 @@ class Progress $logContext['remote']['url'] = (string)$url; - $this->logger->debug('Updating [{client}: {backend}] {item.type} [{item.title}] watch progress.', [ - // -- convert time to ticks for emby to understand it. - 'time' => floor($entity->getPlayProgress() * 1_00_00), - 'client' => $context->clientName, - 'backend' => $context->backendName, - ...$logContext, - ]); + $this->logger->debug( + 'Emby.Progress: Updating [{client}: {backend}] {item.type} [{item.title}] watch progress.', + [ + // -- convert time to ticks for emby to understand it. + 'time' => floor($entity->getPlayProgress() * 1_00_00), + 'client' => $context->clientName, + 'backend' => $context->backendName, + ...$logContext, + ] + ); if (false === (bool)ag($context->options, Options::DRY_RUN, false)) { $queue->add( diff --git a/src/Backends/Emby/EmbyClient.php b/src/Backends/Emby/EmbyClient.php index be508987..ff2880db 100644 --- a/src/Backends/Emby/EmbyClient.php +++ b/src/Backends/Emby/EmbyClient.php @@ -8,6 +8,7 @@ use App\Backends\Common\Cache; use App\Backends\Common\ClientInterface as iClient; use App\Backends\Common\Context; use App\Backends\Common\GuidInterface as iGuid; +use App\Backends\Common\Response; use App\Backends\Emby\Action\Backup; use App\Backends\Emby\Action\Export; use App\Backends\Emby\Action\GetIdentifier; @@ -42,8 +43,9 @@ use SplFileObject; /** * Class EmbyClient * - * This class represents a client for the Emby backend. - * It implements the iClient interface. + * This class is responsible for facilitating communication with Emby Server backend. + * + * @implements iClient */ class EmbyClient implements iClient { @@ -200,10 +202,7 @@ class EmbyClient implements iClient } if (false === $response->isSuccessful()) { - throw new HttpException( - ag($response->extra, 'message', fn() => $response->error->format()), - ag($response->extra, 'http_code', 400), - ); + $this->throwError($response, HttpException::class, ag($response->extra, 'http_code', 400)); } return $response->response; @@ -229,7 +228,7 @@ class EmbyClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; @@ -252,7 +251,7 @@ class EmbyClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; @@ -279,7 +278,7 @@ class EmbyClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; @@ -302,7 +301,7 @@ class EmbyClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return []; @@ -326,7 +325,7 @@ class EmbyClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return []; @@ -349,7 +348,7 @@ class EmbyClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; @@ -367,7 +366,7 @@ class EmbyClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; @@ -385,7 +384,7 @@ class EmbyClient implements iClient ); if (false === $response->isSuccessful()) { - throw new RuntimeException(message: $response->error->format(), previous: $response->error->previous); + $this->throwError($response); } return $response->response; @@ -403,7 +402,7 @@ class EmbyClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; @@ -439,9 +438,7 @@ class EmbyClient implements iClient $this->logger->log($response->error->level(), $response->error->message, $response->error->context); } - throw new RuntimeException( - ag($response->extra, 'message', fn() => $response->error->format()) - ); + $this->throwError($response); } return $response->response; @@ -475,7 +472,7 @@ class EmbyClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; @@ -493,7 +490,7 @@ class EmbyClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; @@ -511,7 +508,7 @@ class EmbyClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; @@ -524,4 +521,21 @@ class EmbyClient implements iClient { return Container::get(EmbyManage::class)->manage(backend: $backend, opts: $opts); } + + /** + * Throws an exception with the specified message and previous exception. + * + * @template T + * @param Response $response The response object containing the error details. + * @param class-string $className The exception class name. + * @param int $code The exception code. + */ + private function throwError(Response $response, string $className = RuntimeException::class, int $code = 0): void + { + throw new $className( + message: ag($response->extra, 'message', fn() => $response->error->format()), + code: $code, + previous: $response->error->previous + ); + } } diff --git a/src/Backends/Plex/PlexClient.php b/src/Backends/Plex/PlexClient.php index 7f51e54b..6be1ad0e 100644 --- a/src/Backends/Plex/PlexClient.php +++ b/src/Backends/Plex/PlexClient.php @@ -8,6 +8,7 @@ use App\Backends\Common\Cache; use App\Backends\Common\ClientInterface as iClient; use App\Backends\Common\Context; use App\Backends\Common\GuidInterface as iGuid; +use App\Backends\Common\Response; use App\Backends\Plex\Action\Backup; use App\Backends\Plex\Action\Export; use App\Backends\Plex\Action\GetIdentifier; @@ -38,12 +39,16 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Log\LoggerInterface as iLogger; use RuntimeException; use SplFileObject; -use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; -use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; -use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +/** + * Class PlexClient + * + * This class is responsible for facilitating communication with Plex Server backend. + * + * @implements iClient + */ class PlexClient implements iClient { public const NAME = 'PlexBackend'; @@ -54,12 +59,18 @@ class PlexClient implements iClient public const TYPE_MOVIE = 'movie'; public const TYPE_EPISODE = 'episode'; + /** + * @var array Map plex types to iState types. + */ public const TYPE_MAPPER = [ PlexClient::TYPE_SHOW => iState::TYPE_SHOW, PlexClient::TYPE_MOVIE => iState::TYPE_MOVIE, PlexClient::TYPE_EPISODE => iState::TYPE_EPISODE, ]; + /** + * @var array List of supported agents. + */ public const SUPPORTED_AGENTS = [ 'com.plexapp.agents.imdb', 'com.plexapp.agents.tmdb', @@ -73,11 +84,31 @@ class PlexClient implements iClient 'tv.plex.agents.movie', 'tv.plex.agents.series', ]; + + /** + * @var mixed $context Backend context. + */ private Context $context; + /** + * @var iLogger The logger object. + */ private iLogger $logger; + /** + * @var iGuid GUID parser. + */ private iGuid $guid; + /** + * @var Cache The Cache store. + */ private Cache $cache; + /** + * Class constructor. + * + * @param iLogger $logger The logger instance. + * @param Cache $cache The cache instance. + * @param PlexGuid $guid The PlexGuid instance. + */ public function __construct(iLogger $logger, Cache $cache, PlexGuid $guid) { $this->cache = $cache; @@ -91,6 +122,9 @@ class PlexClient implements iClient $this->guid = $guid->withContext($this->context); } + /** + * @inheritdoc + */ public function withContext(Context $context): self { $cloned = clone $this; @@ -125,16 +159,25 @@ class PlexClient implements iClient return $cloned; } + /** + * @inheritdoc + */ public function getContext(): Context { return $this->context; } + /** + * @inheritdoc + */ public function getName(): string { return $this->context?->backendName ?? static::CLIENT_NAME; } + /** + * @inheritdoc + */ public function setLogger(iLogger $logger): self { $this->logger = $logger; @@ -142,6 +185,9 @@ class PlexClient implements iClient return $this; } + /** + * @inheritdoc + */ public function processRequest(ServerRequestInterface $request, array $opts = []): ServerRequestInterface { $response = Container::get(InspectRequest::class)(context: $this->context, request: $request); @@ -153,6 +199,9 @@ class PlexClient implements iClient return $response->isSuccessful() ? $response->response : $request; } + /** + * @inheritdoc + */ public function parseWebhook(ServerRequestInterface $request): iState { $response = Container::get(ParseWebhook::class)( @@ -166,15 +215,15 @@ class PlexClient implements iClient } if (false === $response->isSuccessful()) { - throw new HttpException( - ag($response->extra, 'message', fn() => $response->error->format()), - ag($response->extra, 'http_code', 400), - ); + $this->throwError($response, HttpException::class, ag($response->extra, 'http_code', 400)); } return $response->response; } + /** + * @inheritdoc + */ public function pull(iImport $mapper, iDate|null $after = null): array { $response = Container::get(Import::class)( @@ -192,12 +241,15 @@ class PlexClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; } + /** + * @inheritdoc + */ public function backup(iImport $mapper, SplFileObject|null $writer = null, array $opts = []): array { $response = Container::get(Backup::class)( @@ -212,12 +264,15 @@ class PlexClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; } + /** + * @inheritdoc + */ public function export(iImport $mapper, QueueRequests $queue, iDate|null $after = null): array { $response = Container::get(Export::class)( @@ -236,12 +291,15 @@ class PlexClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; } + /** + * @inheritdoc + */ public function push(array $entities, QueueRequests $queue, iDate|null $after = null): array { $response = Container::get(Push::class)( @@ -256,12 +314,15 @@ class PlexClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return []; } + /** + * @inheritdoc + */ public function progress(array $entities, QueueRequests $queue, iDate|null $after = null): array { $response = Container::get(Progress::class)( @@ -277,12 +338,15 @@ class PlexClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return []; } + /** + * @inheritdoc + */ public function search(string $query, int $limit = 25, array $opts = []): array { $response = Container::get(SearchQuery::class)( @@ -297,12 +361,15 @@ class PlexClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; } + /** + * @inheritdoc + */ public function searchId(string|int $id, array $opts = []): array { $response = Container::get(SearchId::class)(context: $this->context, id: $id, opts: $opts); @@ -312,23 +379,29 @@ class PlexClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; } + /** + * @inheritdoc + */ public function getMetadata(string|int $id, array $opts = []): array { $response = Container::get(GetMetaData::class)(context: $this->context, id: $id, opts: $opts); if (false === $response->isSuccessful()) { - throw new RuntimeException(message: $response->error->format(), previous: $response->error->previous); + $this->throwError($response); } return $response->response; } + /** + * @inheritdoc + */ public function getLibrary(string|int $id, array $opts = []): array { $response = Container::get(GetLibrary::class)(context: $this->context, guid: $this->guid, id: $id, opts: $opts); @@ -338,12 +411,15 @@ class PlexClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; } + /** + * @inheritdoc + */ public function getIdentifier(bool $forceRefresh = false): int|string|null { if (false === $forceRefresh && null !== $this->context->backendId) { @@ -359,6 +435,9 @@ class PlexClient implements iClient return $response->isSuccessful() ? $response->response : null; } + /** + * @inheritdoc + */ public function getUsersList(array $opts = []): array { $response = Container::get(GetUsersList::class)($this->context, $opts); @@ -368,14 +447,15 @@ class PlexClient implements iClient $this->logger->log($response->error->level(), $response->error->message, $response->error->context); } - throw new RuntimeException( - ag($response->extra, 'message', fn() => $response->error->format()) - ); + $this->throwError($response); } return $response->response; } + /** + * @inheritdoc + */ public function getUserToken(int|string $userId, string $username): string|bool { $response = Container::get(GetUserToken::class)($this->context, $userId, $username); @@ -385,14 +465,15 @@ class PlexClient implements iClient $this->logger->log($response->error->level(), $response->error->message, $response->error->context); } - throw new RuntimeException( - ag($response->extra, 'message', fn() => $response->error->format()) - ); + $this->throwError($response); } return $response->response; } + /** + * @inheritdoc + */ public function listLibraries(array $opts = []): array { $response = Container::get(GetLibrariesList::class)(context: $this->context, opts: $opts); @@ -402,12 +483,15 @@ class PlexClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; } + /** + * @inheritdoc + */ public function getInfo(array $opts = []): array { $response = Container::get(GetInfo::class)(context: $this->context, opts: $opts); @@ -417,12 +501,15 @@ class PlexClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; } + /** + * @inheritdoc + */ public function getVersion(array $opts = []): string { $response = Container::get(GetVersion::class)(context: $this->context, opts: $opts); @@ -432,23 +519,30 @@ class PlexClient implements iClient } if (false === $response->isSuccessful()) { - throw new RuntimeException(ag($response->extra, 'message', fn() => $response->error->format())); + $this->throwError($response); } return $response->response; } + /** + * @inheritdoc + */ public static function manage(array $backend, array $opts = []): array { return Container::get(PlexManage::class)->manage(backend: $backend, opts: $opts); } /** - * Discover Servers linked to plex token. + * Retrieves a list of Plex servers using the Plex.tv API. + * + * @param HttpClientInterface $http The HTTP client used to send the request. + * @param string $token The Plex authentication token. + * @param array $opts (Optional) options. + * + * @return array The list of Plex servers. + * @throws RuntimeException When an unexpected status code is returned or a network-related exception occurs. * - * @throws ServerExceptionInterface - * @throws RedirectionExceptionInterface - * @throws ClientExceptionInterface */ public static function discover(HttpClientInterface $http, string $token, array $opts = []): array { @@ -463,19 +557,24 @@ class PlexClient implements iClient if (200 !== $response->getStatusCode()) { throw new RuntimeException( - r('Request for servers list returned with unexpected [{status_code}] status code. {context}', [ - 'status_code' => $response->getStatusCode(), - 'context' => arrayToString(['payload' => $payload]), - ]) + r( + text: 'PlexClient: Request for servers list returned with unexpected [{status_code}] status code. {context}', + context: [ + 'status_code' => $response->getStatusCode(), + 'context' => arrayToString(['payload' => $payload]), + ] + ) ); } } catch (TransportExceptionInterface $e) { throw new RuntimeException( r( - 'Unexpected exception [{exception}] was thrown during request for servers list, likely network related error. [{error}]', - [ - 'exception' => $e::class, + text: 'PlexClient: Exception [{kind}] was thrown unhandled during request for plex servers list, likely network related error. [{error} @ {file}:{line}]', + context: [ + 'kind' => $e::class, 'error' => $e->getMessage(), + 'line' => $e->getLine(), + 'file' => after($e->getFile(), ROOT_PATH), ] ) ); @@ -486,7 +585,7 @@ class PlexClient implements iClient $list = []; if (false === $xml->Device) { - throw new RuntimeException('No devices found associated with the given token.'); + throw new RuntimeException('PlexClient: No backends were associated with the given token.'); } foreach ($xml->Device as $device) { @@ -535,4 +634,21 @@ class PlexClient implements iClient return $list; } + + /** + * Throws an exception with the specified message and previous exception. + * + * @template T + * @param Response $response The response object containing the error details. + * @param class-string $className The exception class name. + * @param int $code The exception code. + */ + private function throwError(Response $response, string $className = RuntimeException::class, int $code = 0): void + { + throw new $className( + message: ag($response->extra, 'message', fn() => $response->error->format()), + code: $code, + previous: $response->error->previous + ); + } } diff --git a/src/Backends/Plex/PlexGuid.php b/src/Backends/Plex/PlexGuid.php index a281d59b..6340aeec 100644 --- a/src/Backends/Plex/PlexGuid.php +++ b/src/Backends/Plex/PlexGuid.php @@ -12,6 +12,9 @@ use Throwable; final class PlexGuid implements iGuid { + /** + * @var array Map plex guids to our guids. + */ private const GUID_MAPPER = [ 'imdb' => Guid::GUID_IMDB, 'tmdb' => Guid::GUID_TMDB, @@ -22,6 +25,10 @@ final class PlexGuid implements iGuid 'youtube' => Guid::GUID_YOUTUBE, 'cmdb' => Guid::GUID_CMDB, ]; + + /** + * @var array List of legacy plex agents. + */ private const GUID_LEGACY = [ 'com.plexapp.agents.imdb', 'com.plexapp.agents.tmdb', @@ -33,11 +40,19 @@ final class PlexGuid implements iGuid 'com.plexapp.agents.youtube', 'com.plexapp.agents.cmdb', ]; + + /** + * @var array List of local plex agents. + */ private const GUID_LOCAL = [ 'plex', 'local', 'com.plexapp.agents.none', ]; + + /** + * @var array Map guids to their replacement. + */ private const GUID_LEGACY_REPLACER = [ 'com.plexapp.agents.themoviedb://' => 'com.plexapp.agents.tmdb://', 'com.plexapp.agents.xbmcnfotv://' => 'com.plexapp.agents.tvdb://', @@ -47,17 +62,24 @@ final class PlexGuid implements iGuid // -- otherwise fallback to tmdb. 'com.plexapp.agents.xbmcnfo://' => 'com.plexapp.agents.tmdb://', ]; + + /** + * @var Context|null Backend context. + */ private Context|null $context = null; /** - * Class to handle Plex external ids Parsing. + * Class constructor. * - * @param LoggerInterface $logger + * @param LoggerInterface $logger Logger instance. */ public function __construct(protected LoggerInterface $logger) { } + /** + * @inheritdoc + */ public function withContext(Context $context): self { $cloned = clone $this; @@ -66,16 +88,25 @@ final class PlexGuid implements iGuid return $cloned; } + /** + * @inheritdoc + */ public function parse(array $guids, array $context = []): array { return $this->ListExternalIds(guids: $guids, context: $context, log: false); } + /** + * @inheritdoc + */ public function get(array $guids, array $context = []): array { return $this->ListExternalIds(guids: $guids, context: $context, log: true); } + /** + * @inheritdoc + */ public function has(array $guids, array $context = []): bool { return count($this->ListExternalIds(guids: $guids, context: $context, log: false)) >= 1; @@ -94,12 +125,13 @@ final class PlexGuid implements iGuid } /** - * List Supported External Ids. + * List supported external ids. * - * @param array $guids - * @param array $context + * @param array $guids List of guids. + * @param array $context Context data. * @param bool $log Log errors. default true. - * @return array + * + * @return array List of external ids. */ private function ListExternalIds(array $guids, array $context = [], bool $log = true): array { @@ -128,7 +160,7 @@ final class PlexGuid implements iGuid if (false === str_contains($val, '://')) { if (true === $log) { $this->logger->info( - 'Unable to parse [{backend}] [{agent}] identifier.', + 'PlexGuid: Unable to parse [{backend}] [{agent}] identifier.', [ 'backend' => $this->context->backendName, 'agent' => $val, @@ -149,7 +181,7 @@ final class PlexGuid implements iGuid if (true === isIgnoredId($this->context->backendName, $type, $key, $value, $id)) { if (true === $log) { $this->logger->debug( - 'Ignoring [{backend}] external id [{source}] for {item.type} [{item.title}] as requested.', + 'PlexGuid: Ignoring [{backend}] external id [{source}] for {item.type} [{item.title}] as requested.', [ 'backend' => $this->context->backendName, 'source' => $val, @@ -168,7 +200,7 @@ final class PlexGuid implements iGuid if (null !== ($guid[self::GUID_MAPPER[$key]] ?? null)) { if (true === $log) { $this->logger->debug( - '[{backend}] reported multiple ids for same data source [{key}: {ids}] for {item.type} [{item.title}].', + 'PlexGuid: [{backend}] reported multiple ids for same data source [{key}: {ids}] for {item.type} [{item.title}].', [ 'backend' => $this->context->backendName, 'key' => $key, @@ -191,7 +223,7 @@ final class PlexGuid implements iGuid } catch (Throwable $e) { if (true === $log) { $this->logger->error( - message: 'Exception [{error.kind}] was thrown unhandled during [{client}: {backend}] parsing [{agent}] identifier. Error [{error.message} @ {error.file}:{error.line}].', + message: 'PlexGuid: Exception [{error.kind}] was thrown unhandled during [{client}: {backend}] parsing [{agent}] identifier. Error [{error.message} @ {error.file}:{error.line}].', context: [ 'backend' => $this->context->backendName, 'client' => $this->context->clientName, @@ -225,11 +257,11 @@ final class PlexGuid implements iGuid /** * Parse legacy plex agents. * - * @param string $guid - * @param array $context + * @param string $guid Guid to parse. + * @param array $context Context data. * @param bool $log Log errors. default true. * - * @return string + * @return string Parsed guid. * @see https://github.com/ZeroQI/Hama.bundle/issues/510 */ private function parseLegacyAgent(string $guid, array $context = [], bool $log = true): string @@ -299,7 +331,7 @@ final class PlexGuid implements iGuid } catch (Throwable $e) { if (true === $log) { $this->logger->error( - message: 'Exception [{error.kind}] was thrown unhandled during [{client}: {backend}] parsing legacy agent [{agent}] identifier. Error [{error.message} @ {error.file}:{error.line}].', + message: 'PlexGuid: Exception [{error.kind}] was thrown unhandled during [{client}: {backend}] parsing legacy agent [{agent}] identifier. Error [{error.message} @ {error.file}:{error.line}].', context: [ 'backend' => $this->context->backendName, 'client' => $this->context->clientName, diff --git a/src/Backends/Plex/PlexManage.php b/src/Backends/Plex/PlexManage.php index de6cfca4..a9d442c7 100644 --- a/src/Backends/Plex/PlexManage.php +++ b/src/Backends/Plex/PlexManage.php @@ -16,10 +16,28 @@ use Symfony\Component\Console\Question\Question; use Symfony\Contracts\HttpClient\HttpClientInterface as iHttp; use Throwable; +/** + * Class PlexManage + * + * This class is responsible for creating and managing Plex backends configuration. + * + * @implements ManageInterface. + */ class PlexManage implements ManageInterface { + /** + * @var QuestionHelper The QuestionHelper object used for asking questions. + */ private QuestionHelper $questionHelper; + /** + * Constructor for the class. + * + * @param iHttp $http The iHttp object used for HTTP requests. + * @param iOutput $output The iOutput object used for outputting data. + * @param iInput $input The iInput object used for retrieving user input. + * @param iLogger $logger The iLogger object used for logging. + */ public function __construct( private iHttp $http, private iOutput $output, @@ -29,6 +47,9 @@ class PlexManage implements ManageInterface $this->questionHelper = new QuestionHelper(); } + /** + * @inheritdoc + */ public function manage(array $backend, array $opts = []): array { // -- $backend.token diff --git a/src/Libs/BackendException.php b/src/Libs/BackendException.php new file mode 100644 index 00000000..9863cc71 --- /dev/null +++ b/src/Libs/BackendException.php @@ -0,0 +1,16 @@ + 'mem_total', + 'MemFree' => 'mem_free', + 'MemAvailable' => 'mem_available', + 'SwapTotal' => 'swap_total', + 'SwapFree' => 'swap_free', + ]; + + $result = []; + + if (!is_readable('/proc/meminfo')) { + return $result; + } + + if (false === ($lines = @file('/proc/meminfo', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES))) { + return $result; + } + + foreach ($lines as $line) { + if (empty($line)) { + continue; + } + + $line = explode(':', $line); + $key = trim($line[0]); + + if (false === array_key_exists($key, $keys)) { + continue; + } + + $val = trim(str_ireplace(' kB', '', $line[1])); + + $value = 1000 * (float)$val; + + $result[$keys[$key]] = $value; + } + + return $result; + } +}