More documentations update.

This commit is contained in:
abdulmohsen
2023-12-15 21:11:56 +03:00
parent 32832857bd
commit 7d7240ff2f
7 changed files with 353 additions and 93 deletions

View File

@@ -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(

View File

@@ -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<T> $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
);
}
}

View File

@@ -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<T> $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
);
}
}

View File

@@ -12,6 +12,9 @@ use Throwable;
final class PlexGuid implements iGuid
{
/**
* @var array<string,string> 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<array-key,string> 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<array-key,string> List of local plex agents.
*/
private const GUID_LOCAL = [
'plex',
'local',
'com.plexapp.agents.none',
];
/**
* @var array<string,string> 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,

View File

@@ -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

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace App\Libs;
use ErrorException;
/**
* Class BackendException
*
* This class represents an exception that is thrown by the backend logic.
*/
class BackendException extends ErrorException
{
}

View File

@@ -1034,3 +1034,53 @@ if (false === function_exists('isValidURL')) {
);
}
}
if (false === function_exists('getSystemMemoryInfo')) {
/**
* Get system memory information.
*
* @return array{ MemTotal: float, MemFree: float, MemAvailable: float, SwapTotal: float, SwapFree: float }
*/
function getSystemMemoryInfo(): array
{
$keys = [
'MemTotal' => '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;
}
}