From d02efab94bbf2fdbd556dc7d4c553dfd1e9f489d Mon Sep 17 00:00:00 2001 From: "Abdulmhsen B. A. A" Date: Mon, 23 May 2022 08:41:59 +0300 Subject: [PATCH] adding initial code to support marking items as unplayed via state:import --- src/Libs/Entity/StateEntity.php | 18 +++ src/Libs/Entity/StateInterface.php | 22 +++- src/Libs/Servers/EmbyServer.php | 31 +++-- src/Libs/Servers/JellyfinServer.php | 181 +++++++--------------------- src/Libs/Servers/PlexServer.php | 38 +++--- src/Libs/helpers.php | 2 +- tests/Fixtures/EpisodeEntity.php | 2 + tests/Fixtures/MovieEntity.php | 2 + 8 files changed, 136 insertions(+), 160 deletions(-) diff --git a/src/Libs/Entity/StateEntity.php b/src/Libs/Entity/StateEntity.php index 52c1afdf..fd0ba828 100644 --- a/src/Libs/Entity/StateEntity.php +++ b/src/Libs/Entity/StateEntity.php @@ -263,6 +263,24 @@ final class StateEntity implements iFace return $this->tainted; } + public function getMetadata(string|null $via = null): array + { + if (null === $via) { + return $this->metadata; + } + + return $this->metadata[$via] ?? []; + } + + public function getExtra(string|null $via = null): array + { + if (null === $via) { + return $this->extra; + } + + return $this->extra[$via] ?? []; + } + private function isEqual(iFace $entity): bool { foreach (iFace::ENTITY_KEYS as $key) { diff --git a/src/Libs/Entity/StateInterface.php b/src/Libs/Entity/StateInterface.php index fd9690be..3b7bb307 100644 --- a/src/Libs/Entity/StateInterface.php +++ b/src/Libs/Entity/StateInterface.php @@ -24,12 +24,14 @@ interface StateInterface public const COLUMN_PARENT = 'parent'; public const COLUMN_GUIDS = 'guids'; public const COLUMN_META_DATA = 'metadata'; + public const COLUMN_META_DATA_ADDED_AT = 'added_at'; + public const COLUMN_META_DATA_PLAYED_AT = 'played_at'; public const COLUMN_META_DATA_EXTRA = 'extra'; public const COLUMN_META_DATA_EXTRA_TITLE = 'title'; + public const COLUMN_META_DATA_EXTRA_DATE = 'date'; public const COLUMN_EXTRA = 'extra'; public const COLUMN_EXTRA_EVENT = 'event'; public const COLUMN_EXTRA_DATE = 'received_at'; - public const COLUMN_META_DATA_EXTRA_DATE = 'date'; /** * List of table keys. @@ -239,4 +241,22 @@ interface StateInterface * @return bool */ public function isTainted(): bool; + + /** + * Get Metadata + * + * @param string|null $via if via is omitted, the entire "metadata" will be returned. + * + * @return array + */ + public function getMetadata(string|null $via = null): array; + + /** + * Get extra. + * + * @param string|null $via if via is omitted, the entire "extra" will be returned. + * + * @return array + */ + public function getExtra(string|null $via = null): array; } diff --git a/src/Libs/Servers/EmbyServer.php b/src/Libs/Servers/EmbyServer.php index 478895a6..6eb8cb48 100644 --- a/src/Libs/Servers/EmbyServer.php +++ b/src/Libs/Servers/EmbyServer.php @@ -120,13 +120,15 @@ class EmbyServer extends JellyfinServer } $isTainted = in_array($event, self::WEBHOOK_TAINTED_EVENTS); + $playedAt = null; if ('item.markplayed' === $event || 'playback.scrobble' === $event) { - $isWatched = 1; + $playedAt = time(); + $isPlayed = 1; } elseif ('item.markunplayed' === $event) { - $isWatched = 0; + $isPlayed = 0; } else { - $isWatched = (int)(bool)ag($json, ['Item.Played', 'Item.PlayedToCompletion'], false); + $isPlayed = (int)(bool)ag($json, ['Item.Played', 'Item.PlayedToCompletion'], false); } $providersId = ag($json, 'Item.ProviderIds', []); @@ -134,7 +136,7 @@ class EmbyServer extends JellyfinServer $row = [ iFace::COLUMN_TYPE => $type, iFace::COLUMN_UPDATED => time(), - iFace::COLUMN_WATCHED => $isWatched, + iFace::COLUMN_WATCHED => $isPlayed, iFace::COLUMN_VIA => $this->name, iFace::COLUMN_TITLE => ag($json, ['Item.Name', 'Item.OriginalTitle'], '??'), iFace::COLUMN_YEAR => (int)ag($json, 'Item.ProductionYear', 0000), @@ -146,7 +148,7 @@ class EmbyServer extends JellyfinServer $this->name => [ iFace::COLUMN_ID => (string)ag($json, 'Item.ItemId'), iFace::COLUMN_TYPE => $type, - iFace::COLUMN_WATCHED => (string)$isWatched, + iFace::COLUMN_WATCHED => (string)$isPlayed, iFace::COLUMN_VIA => $this->name, iFace::COLUMN_TITLE => ag($json, ['Item.Name', 'Item.OriginalTitle'], '??'), iFace::COLUMN_YEAR => (string)ag($json, 'Item.ProductionYear', 0000), @@ -165,6 +167,7 @@ class EmbyServer extends JellyfinServer $row[iFace::COLUMN_TITLE] = ag($json, 'Item.SeriesName', '??'); $row[iFace::COLUMN_SEASON] = ag($json, 'Item.ParentIndexNumber', 0); $row[iFace::COLUMN_EPISODE] = ag($json, 'Item.IndexNumber', 0); + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_TITLE] = ag($json, 'Item.SeriesName', '??'); $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_SEASON] = (string)$row[iFace::COLUMN_SEASON]; $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_EPISODE] = (string)$row[iFace::COLUMN_EPISODE]; $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_EXTRA][iFace::COLUMN_META_DATA_EXTRA_TITLE] = ag( @@ -178,9 +181,21 @@ class EmbyServer extends JellyfinServer } } - $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_EXTRA][iFace::COLUMN_META_DATA_EXTRA_DATE] = makeDate( - ag($json, ['Item.PremiereDate', 'Item.ProductionYear', 'Item.DateCreated'], 'now') - )->format('Y-m-d'); + if (null !== ($premiereDate = ag($json, 'Item.PremiereDate'))) { + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_EXTRA][iFace::COLUMN_META_DATA_EXTRA_DATE] = makeDate( + $premiereDate + )->format('Y-m-d'); + } + + if (null !== ($addedAt = ag($json, 'Item.DateCreated'))) { + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_ADDED_AT] = makeDate( + $addedAt + )->getTimestamp(); + } + + if (null !== $playedAt) { + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_PLAYED_AT] = $playedAt; + } $entity = Container::get(iFace::class)::fromArray($row)->setIsTainted($isTainted); diff --git a/src/Libs/Servers/JellyfinServer.php b/src/Libs/Servers/JellyfinServer.php index 05f68f66..3e01573a 100644 --- a/src/Libs/Servers/JellyfinServer.php +++ b/src/Libs/Servers/JellyfinServer.php @@ -270,7 +270,7 @@ class JellyfinServer implements ServerInterface 'ITEM_ID' => ag($json, 'ItemId', ''), 'SERVER_ID' => ag($json, 'ServerId', ''), 'SERVER_NAME' => ag($json, 'ServerName', ''), - 'SERVER_VERSION' => afterLast($userAgent, '/'), + 'SERVER_VERSION' => ag($json, 'ServerVersion', fn() => afterLast($userAgent, '/')), 'USER_ID' => ag($json, 'UserId', ''), 'USER_NAME' => ag($json, 'NotificationUsername', ''), 'WH_EVENT' => ag($json, 'NotificationType', 'not_set'), @@ -323,8 +323,8 @@ class JellyfinServer implements ServerInterface $row = [ iFace::COLUMN_TYPE => $type, - iFace::COLUMN_UPDATED => strtotime(ag($json, ['UtcTimestamp', 'Timestamp'], 'now')), - iFace::COLUMN_WATCHED => (int)(bool)ag($json, ['Played', 'PlayedToCompletion'], 0), + iFace::COLUMN_UPDATED => strtotime(ag($json, ['LastPlayedDate', 'UtcTimestamp', 'Timestamp'], 'now')), + iFace::COLUMN_WATCHED => (int)(bool)ag($json, 'Played', 0), iFace::COLUMN_VIA => $this->name, iFace::COLUMN_TITLE => ag($json, ['Name', 'OriginalTitle'], '??'), iFace::COLUMN_YEAR => (int)ag($json, 'Year', 0000), @@ -336,7 +336,7 @@ class JellyfinServer implements ServerInterface $this->name => [ iFace::COLUMN_ID => (string)ag($json, 'ItemId'), iFace::COLUMN_TYPE => $type, - iFace::COLUMN_WATCHED => (string)(int)(bool)ag($json, ['Played', 'PlayedToCompletion'], 0), + iFace::COLUMN_WATCHED => (string)(int)(bool)ag($json, 'Played', 0), iFace::COLUMN_VIA => $this->name, iFace::COLUMN_TITLE => ag($json, ['Name', 'OriginalTitle'], '??'), iFace::COLUMN_YEAR => (string)ag($json, 'Year', 0000), @@ -357,6 +357,7 @@ class JellyfinServer implements ServerInterface $row[iFace::COLUMN_TITLE] = $seriesName ?? '??'; $row[iFace::COLUMN_SEASON] = ag($json, 'SeasonNumber', 0); $row[iFace::COLUMN_EPISODE] = ag($json, 'EpisodeNumber', 0); + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_TITLE] = $seriesName ?? '??'; $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_SEASON] = (string)$row[iFace::COLUMN_SEASON]; $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_EPISODE] = (string)$row[iFace::COLUMN_EPISODE]; $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_EXTRA][iFace::COLUMN_META_DATA_EXTRA_TITLE] = ag( @@ -373,9 +374,23 @@ class JellyfinServer implements ServerInterface } } - $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_EXTRA][iFace::COLUMN_META_DATA_EXTRA_DATE] = makeDate( - ag($json, ['PremiereDate', 'ProductionYear', 'DateCreated'], 'now') - )->format('Y-m-d'); + if (null !== ($premiereDate = ag($json, 'PremiereDate'))) { + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_EXTRA][iFace::COLUMN_META_DATA_EXTRA_DATE] = makeDate( + $premiereDate + )->format('Y-m-d'); + } + + if (null !== ($addedAt = ag($json, 'DateCreated'))) { + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_ADDED_AT] = makeDate( + $addedAt + )->getTimestamp(); + } + + if (null !== ($lastPlayedAt = ag($json, 'LastPlayedDate'))) { + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_PLAYED_AT] = makeDate( + $lastPlayedAt + )->getTimestamp(); + } $entity = Container::get(iFace::class)::fromArray($row)->setIsTainted($isTainted); @@ -809,7 +824,7 @@ class JellyfinServer implements ServerInterface } if (false === (bool)ag($this->options, Options::IGNORE_DATE, false)) { - $date = ag($json, ['UserData.LastPlayedDate', 'DateCreated', 'PremiereDate'], null); + $date = ag($json, ['UserData.LastPlayedDate', 'DateCreated'], null); if (null === $date) { $this->logger->error( @@ -986,100 +1001,6 @@ class JellyfinServer implements ServerInterface ); } - public function cache(): array - { - return $this->getLibraries( - ok: function (string $cName, string $type) { - return function (ResponseInterface $response) use ($cName, $type) { - if (200 !== $response->getStatusCode()) { - $this->logger->error( - sprintf( - '%s: Request to \'%s\' responded with unexpected http status code \'%d\'.', - $this->name, - $cName, - $response->getStatusCode() - ) - ); - return; - } - - try { - $this->logger->info(sprintf('%s: Parsing \'%s\' response.', $this->name, $cName)); - - $it = Items::fromIterable( - httpClientChunks($this->http->stream($response)), - [ - 'pointer' => '/Items', - 'decoder' => new ErrorWrappingDecoder( - new ExtJsonDecoder(options: JSON_INVALID_UTF8_IGNORE) - ) - ] - ); - - foreach ($it as $entity) { - if ($entity instanceof DecodingError) { - $this->logger->warning( - sprintf( - '%s: Failed to decode one of \'%s\' items. %s', - $this->name, - $cName, - $entity->getErrorMessage() - ), - [ - 'payload' => $entity->getMalformedJson() - ] - ); - continue; - } - $this->processCache($entity, $type, $cName); - } - } catch (PathNotFoundException $e) { - $this->logger->error( - sprintf( - '%s: Failed to find items in \'%s\' response. %s', - $this->name, - $cName, - $e->getMessage() - ), - [ - 'file' => $e->getFile(), - 'line' => $e->getLine(), - 'kind' => get_class($e), - ], - ); - } catch (Throwable $e) { - $this->logger->error( - sprintf( - '%s: Failed to handle \'%s\' response. %s', - $this->name, - $cName, - $e->getMessage(), - ), - [ - 'file' => $e->getFile(), - 'line' => $e->getLine(), - 'kind' => get_class($e), - ], - ); - } - - $this->logger->info(sprintf('%s: Parsing \'%s\' response is complete.', $this->name, $cName)); - }; - }, - error: function (string $cName, string $type, UriInterface|string $url) { - return fn(Throwable $e) => $this->logger->error( - sprintf('%s: Error encountered in \'%s\' request. %s', $this->name, $cName, $e->getMessage()), - [ - 'url' => $url, - 'file' => $e->getFile(), - 'line' => $e->getLine(), - ] - ); - }, - includeParent: true - ); - } - /** * @throws InvalidArgumentException */ @@ -1365,7 +1286,7 @@ class JellyfinServer implements ServerInterface ]); } - $date = $item->UserData?->LastPlayedDate ?? $item->DateCreated ?? $item->PremiereDate ?? null; + $date = $item->UserData->LastPlayedDate ?? $item->DateCreated ?? null; if (null === $date) { $this->logger->warning( @@ -1433,30 +1354,6 @@ class JellyfinServer implements ServerInterface } } - protected function processCache(StdClass $item, string $type, string $library): void - { - try { - if ('show' === $type) { - $this->processShow($item, $library); - return; - } - - $date = $item->UserData?->LastPlayedDate ?? $item->DateCreated ?? $item->PremiereDate ?? null; - - if (null === $date) { - return; - } - - $this->createEntity($item, $type); - } catch (Throwable $e) { - $this->logger->error(sprintf('%s: %s', $this->name, $e->getMessage()), [ - 'file' => $e->getFile(), - 'line' => $e->getLine(), - 'kind' => get_class($e), - ]); - } - } - protected function processExport( ExportInterface $mapper, string $type, @@ -1486,7 +1383,7 @@ class JellyfinServer implements ServerInterface ); } - $date = $item->UserData?->LastPlayedDate ?? $item->DateCreated ?? $item->PremiereDate ?? null; + $date = $item->UserData?->LastPlayedDate ?? $item->DateCreated ?? null; if (null === $date) { $this->logger->notice( @@ -1700,12 +1597,16 @@ class JellyfinServer implements ServerInterface protected function createEntity(stdClass $item, string $type): StateEntity { - $date = strtotime($item->UserData?->LastPlayedDate ?? $item->DateCreated ?? $item->PremiereDate); + $date = $item->UserData->LastPlayedDate ?? $item->DateCreated ?? null; + + if (null === $date) { + throw new RuntimeException('No date was set on object.'); + } $row = [ iFace::COLUMN_TYPE => $type, - iFace::COLUMN_UPDATED => $date, - iFace::COLUMN_WATCHED => (int)(bool)($item->UserData?->Played ?? false), + iFace::COLUMN_UPDATED => makeDate($date)->getTimestamp(), + iFace::COLUMN_WATCHED => (int)(bool)($item->UserData->Played ?? false), iFace::COLUMN_VIA => $this->name, iFace::COLUMN_TITLE => $item->Name ?? $item->OriginalTitle ?? '??', iFace::COLUMN_YEAR => (int)($item->ProductionYear ?? 0000), @@ -1717,11 +1618,12 @@ class JellyfinServer implements ServerInterface $this->name => [ iFace::COLUMN_ID => (string)$item->Id, iFace::COLUMN_TYPE => $type, - iFace::COLUMN_WATCHED => (string)(int)(bool)($item->UserData?->Played ?? false), + iFace::COLUMN_WATCHED => (string)(int)(bool)($item->UserData->Played ?? false), iFace::COLUMN_VIA => $this->name, iFace::COLUMN_TITLE => $item->Name ?? $item->OriginalTitle ?? '??', iFace::COLUMN_YEAR => (string)($item->ProductionYear ?? 0000), iFace::COLUMN_GUIDS => array_change_key_case((array)($item->ProviderIds ?? []), CASE_LOWER), + iFace::COLUMN_META_DATA_ADDED_AT => (string)makeDate($item->DateCreated)->getTimestamp(), ], ], iFace::COLUMN_EXTRA => [], @@ -1731,6 +1633,7 @@ class JellyfinServer implements ServerInterface $row[iFace::COLUMN_TITLE] = $item->SeriesName ?? '??'; $row[iFace::COLUMN_SEASON] = $item->ParentIndexNumber ?? 0; $row[iFace::COLUMN_EPISODE] = $item->IndexNumber ?? 0; + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_TITLE] = $item->SeriesName ?? '??'; $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_SEASON] = (string)$row[iFace::COLUMN_SEASON]; $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_EPISODE] = (string)$row[iFace::COLUMN_EPISODE]; $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_EXTRA][iFace::COLUMN_META_DATA_EXTRA_TITLE] = $item->Name ?? $item->OriginalTitle ?? '??'; @@ -1741,9 +1644,17 @@ class JellyfinServer implements ServerInterface } } - $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_EXTRA][iFace::COLUMN_META_DATA_EXTRA_DATE] = makeDate( - $item->PremiereDate ?? $item->ProductionYear ?? 'now' - )->format('Y-m-d'); + if (null !== ($item->PremiereDate ?? null)) { + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_EXTRA][iFace::COLUMN_META_DATA_EXTRA_DATE] = makeDate( + $item->PremiereDate + )->format('Y-m-d'); + } + + if (null !== ($item->UserData->LastPlayedDate ?? null)) { + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_PLAYED_AT] = (string)makeDate( + $item->UserData->LastPlayedDate + )->getTimestamp(); + } return Container::get(iFace::class)::fromArray($row); } diff --git a/src/Libs/Servers/PlexServer.php b/src/Libs/Servers/PlexServer.php index da433e8d..6f34f262 100644 --- a/src/Libs/Servers/PlexServer.php +++ b/src/Libs/Servers/PlexServer.php @@ -385,6 +385,7 @@ class PlexServer implements ServerInterface iFace::COLUMN_TITLE => ag($item, ['title', 'originalTitle'], '??'), iFace::COLUMN_YEAR => (string)ag($item, ['grandParentYear', 'parentYear', 'year'], 0000), iFace::COLUMN_GUIDS => $this->parseGuids(ag($item, 'Guid', [])), + iFace::COLUMN_META_DATA_ADDED_AT => (string)ag($item, 'addedAt'), ], ], iFace::COLUMN_EXTRA => [ @@ -399,6 +400,7 @@ class PlexServer implements ServerInterface $row[iFace::COLUMN_TITLE] = ag($item, 'grandparentTitle', '??'); $row[iFace::COLUMN_SEASON] = ag($item, 'parentIndex', 0); $row[iFace::COLUMN_EPISODE] = ag($item, 'index', 0); + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_TITLE] = ag($item, 'grandparentTitle', '??'); $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_SEASON] = (string)$row[iFace::COLUMN_SEASON]; $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_EPISODE] = (string)$row[iFace::COLUMN_EPISODE]; $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_EXTRA][iFace::COLUMN_META_DATA_EXTRA_TITLE] = ag( @@ -413,11 +415,15 @@ class PlexServer implements ServerInterface } } - $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_EXTRA][iFace::COLUMN_META_DATA_EXTRA_DATE] = makeDate( - ag($item, 'originallyAvailableAt', 'now') - )->format( - 'Y-m-d' - ); + if (null !== ($premiereDate = ag($item, 'originallyAvailableAt'))) { + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_EXTRA][iFace::COLUMN_META_DATA_EXTRA_DATE] = makeDate( + $premiereDate + )->format('Y-m-d'); + } + + if (null !== ($lastPlayedAt = ag($item, 'lastViewedAt'))) { + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_PLAYED_AT] = (string)$lastPlayedAt; + } $entity = Container::get(iFace::class)::fromArray($row)->setIsTainted($isTainted); @@ -835,11 +841,7 @@ class PlexServer implements ServerInterface $isWatched = (int)(bool)ag($json, 'viewCount', 0); if (false === (bool)ag($this->options, Options::IGNORE_DATE, false)) { - $date = max( - (int)ag($json, 'lastViewedAt', 0), - (int)ag($json, 'updatedAt', 0), - (int)ag($json, 'addedAt', 0) - ); + $date = max((int)ag($json, 'lastViewedAt', 0), (int)ag($json, 'addedAt', 0)); if (0 === $date) { $this->logger->error( @@ -1298,7 +1300,7 @@ class PlexServer implements ServerInterface ]); } - $date = (int)($item->lastViewedAt ?? $item->updatedAt ?? $item->addedAt ?? 0); + $date = max((int)($item->lastViewedAt ?? 0), (int)($item->addedAt ?? 0)); if (0 === $date) { $this->logger->debug( @@ -1385,9 +1387,9 @@ class PlexServer implements ServerInterface ); } - $date = $item->lastViewedAt ?? $item->updatedAt ?? $item->addedAt ?? null; + $date = max((int)($item->lastViewedAt ?? 0), (int)($item->addedAt ?? 0)); - if (null === $date) { + if (0 === $date) { $this->logger->notice( sprintf('%s: Ignoring \'%s\'. Date is not set on backend object.', $this->name, $iName), [ @@ -1752,7 +1754,7 @@ class PlexServer implements ServerInterface $item->Guid[] = ['id' => $item->guid]; } - $date = (int)($item->lastViewedAt ?? $item->updatedAt ?? $item->addedAt ?? 0); + $date = max((int)($item->lastViewedAt ?? 0), (int)($item->addedAt ?? 0)); $row = [ iFace::COLUMN_TYPE => $type, @@ -1774,6 +1776,7 @@ class PlexServer implements ServerInterface iFace::COLUMN_TITLE => $item->title ?? $item->originalTitle ?? '??', iFace::COLUMN_YEAR => (string)($item->grandParentYear ?? $item->parentYear ?? $item->year ?? 0000), iFace::COLUMN_GUIDS => $this->parseGuids($item->Guid ?? []), + iFace::COLUMN_META_DATA_ADDED_AT => (string)$item->addedAt, ], ], iFace::COLUMN_EXTRA => [], @@ -1783,6 +1786,7 @@ class PlexServer implements ServerInterface $row[iFace::COLUMN_TITLE] = $item->grandparentTitle ?? '??'; $row[iFace::COLUMN_SEASON] = $item->parentIndex ?? 0; $row[iFace::COLUMN_EPISODE] = $item->index ?? 0; + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_TITLE] = $item->grandparentTitle ?? '??'; $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_SEASON] = (string)$row[iFace::COLUMN_SEASON]; $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_EPISODE] = (string)$row[iFace::COLUMN_EPISODE]; $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_EXTRA][iFace::COLUMN_META_DATA_EXTRA_TITLE] = $item->title ?? $item->originalTitle ?? '??'; @@ -1797,10 +1801,14 @@ class PlexServer implements ServerInterface if (null !== ($item->originallyAvailableAt ?? null)) { $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_EXTRA][iFace::COLUMN_META_DATA_EXTRA_DATE] = makeDate( - $item->originallyAvailableAt ?? 'now' + $item->originallyAvailableAt )->format('Y-m-d'); } + if (null !== ($item->lastViewedAt ?? null)) { + $row[iFace::COLUMN_META_DATA][$this->name][iFace::COLUMN_META_DATA_PLAYED_AT] = (string)$item->lastViewedAt; + } + return Container::get(iFace::class)::fromArray($row); } diff --git a/src/Libs/helpers.php b/src/Libs/helpers.php index 4b97c4b8..502fd842 100644 --- a/src/Libs/helpers.php +++ b/src/Libs/helpers.php @@ -258,7 +258,7 @@ if (!function_exists('saveWebhookPayload')) { Config::get('tmpDir') . '/webhooks/' . sprintf( 'webhook.%s.%s.%s.json', $name, - ag($state->extra, 'webhook.event', 'unknown'), + ag($state->getExtra(), 'event', 'unknown'), ag($request->getServerParams(), 'X_REQUEST_ID', time()) ), json_encode(value: $content, flags: JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) diff --git a/tests/Fixtures/EpisodeEntity.php b/tests/Fixtures/EpisodeEntity.php index d7261bb1..96f22c53 100644 --- a/tests/Fixtures/EpisodeEntity.php +++ b/tests/Fixtures/EpisodeEntity.php @@ -41,6 +41,8 @@ return [ iFace::COLUMN_META_DATA_EXTRA_DATE => '2020-01-03', iFace::COLUMN_META_DATA_EXTRA_TITLE => 'Episode Title', ], + iFace::COLUMN_META_DATA_ADDED_AT => 1, + iFace::COLUMN_META_DATA_PLAYED_AT => 2, ], ], iFace::COLUMN_EXTRA => [ diff --git a/tests/Fixtures/MovieEntity.php b/tests/Fixtures/MovieEntity.php index 9350a6b4..ace7b215 100644 --- a/tests/Fixtures/MovieEntity.php +++ b/tests/Fixtures/MovieEntity.php @@ -34,6 +34,8 @@ return [ iFace::COLUMN_META_DATA_EXTRA => [ iFace::COLUMN_META_DATA_EXTRA_DATE => '2020-01-03', ], + iFace::COLUMN_META_DATA_ADDED_AT => 1, + iFace::COLUMN_META_DATA_PLAYED_AT => 2, ], ], iFace::COLUMN_EXTRA => [