jellyfin added version constraint check for play progress sync.

This commit is contained in:
Abdulmhsen B. A. A
2023-12-20 16:54:07 +03:00
parent 8be548b445
commit 89730d9f14
5 changed files with 134 additions and 38 deletions

View File

@@ -114,16 +114,19 @@ class Progress
];
if ($context->backendName === $entity->via) {
$this->logger->info('Ignoring [{item.title}] for [{backend}]. Event originated from this backend.', [
'backend' => $context->backendName,
...$logContext,
]);
$this->logger->info(
'Jellyfin.Progress: Ignoring [{item.title}] for [{backend}]. Event originated from this backend.',
[
'backend' => $context->backendName,
...$logContext,
]
);
continue;
}
if (null === ag($metadata, iState::COLUMN_ID, null)) {
$this->logger->warning(
'Ignoring [{item.title}] for [{backend}]. No metadata was found.',
'Jellyfin.Progress: Ignoring [{item.title}] for [{backend}]. No metadata was found.',
[
'backend' => $context->backendName,
...$logContext,
@@ -134,10 +137,13 @@ class Progress
$senderDate = ag($entity->getExtra($entity->via), iState::COLUMN_EXTRA_DATE);
if (null === $senderDate) {
$this->logger->warning('Ignoring [{item.title}] for [{backend}]. Sender did not set a date.', [
'backend' => $context->backendName,
...$logContext,
]);
$this->logger->warning(
'Jellyfin.Progress: Ignoring [{item.title}] for [{backend}]. Sender did not set a date.',
[
'backend' => $context->backendName,
...$logContext,
]
);
continue;
}
$senderDate = makeDate($senderDate)->getTimestamp();
@@ -145,7 +151,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 [{backend}]. Sender date is older than backend date.',
'Jellyfin.Progress: Ignoring [{item.title}] for [{backend}]. Sender date is older than backend date.',
[
'backend' => $context->backendName,
...$logContext,
@@ -167,7 +173,7 @@ class Progress
if (false === $ignoreDate && makeDate($remoteItem->updated)->getTimestamp() > $senderDate) {
$this->logger->info(
'Ignoring [{item.title}] for [{backend}]. Sender date is older than backend item date.',
'Jellyfin.Progress: Ignoring [{item.title}] for [{backend}]. Sender date is older than backend item date.',
[
'backend' => $context->backendName,
...$logContext,
@@ -178,7 +184,7 @@ class Progress
if ($remoteItem->isWatched()) {
$this->logger->info(
'Ignoring [{item.title}] for [{backend}]. The backend reported the item as watched.',
'Jellyfin.Progress: Ignoring [{item.title}] for [{backend}]. The backend reported the item as watched.',
[
'backend' => $context->backendName,
...$logContext,
@@ -213,29 +219,37 @@ class Progress
try {
$url = $context->backendUrl->withPath(
r('/Users/{user_id}/PlayingItems/{item_id}', [
r('/Users/{user_id}/Items/{item_id}/UserData', [
'user_id' => $context->backendUser,
'item_id' => $logContext['remote']['id'],
])
)->withQuery(
http_build_query([
'positionTicks' => (string)floor($entity->getPlayProgress() * 1_00_00),
])
);
$logContext['remote']['url'] = (string)$url;
$this->logger->debug('Updating [{backend}] {item.type} [{item.title}] watch progress.', [
'backend' => $context->backendName,
...$logContext,
]);
$this->logger->debug(
'Jellyfin.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(
$this->http->request(
'DELETE',
'POST',
(string)$url,
array_replace_recursive($context->backendHeaders, [
'headers' => [
'Content-Type' => 'application/json',
],
'json' => [
'PlaybackPositionTicks' => (string)floor($entity->getPlayProgress() * 1_00_00),
],
'user_data' => [
'id' => $key,
'context' => $logContext + [

View File

@@ -7,7 +7,9 @@ namespace App\Backends\Jellyfin;
use App\Backends\Common\Cache;
use App\Backends\Common\ClientInterface as iClient;
use App\Backends\Common\Context;
use App\Backends\Common\Error;
use App\Backends\Common\GuidInterface as iGuid;
use App\Backends\Common\Levels;
use App\Backends\Common\Response;
use App\Backends\Jellyfin\Action\Backup;
use App\Backends\Jellyfin\Action\Export;
@@ -29,6 +31,7 @@ use App\Libs\Config;
use App\Libs\Container;
use App\Libs\Entity\StateInterface as iState;
use App\Libs\Exceptions\Backends\RuntimeException;
use App\Libs\Exceptions\Backends\UnexpectedVersionException;
use App\Libs\Exceptions\HttpException;
use App\Libs\Mappers\ImportInterface as iImport;
use App\Libs\Options;
@@ -332,6 +335,27 @@ class JellyfinClient implements iClient
*/
public function progress(array $entities, QueueRequests $queue, iDate|null $after = null): array
{
$version = $this->getVersion();
if (false === version_compare($version, '10.8.14', '>=')) {
$this->throwError(
response: new Response(
status: false,
error: new Error(
message: 'Jellyfin play progress support works on Jellyfin version {version.required} and above. You are currently running {version.current}.',
context: [
'version' => [
'current' => $version,
'required' => '10.8.14',
],
],
level: Levels::ERROR,
)
),
className: UnexpectedVersionException::class
);
}
$response = Container::get(Progress::class)(
context: $this->context,
guid: $this->guid,

View File

@@ -476,6 +476,7 @@ final class ListCommand extends Command
'via' => $entity->via ?? '??',
'date' => makeDate($entity->updated)->format('Y-m-d H:i:s T'),
'played' => $entity->isWatched() ? 'Yes' : 'No',
'progress' => $entity->hasPlayProgress() ? $entity->getPlayProgress() : 'None',
'event' => ag($entity->extra[$entity->via] ?? [], iState::COLUMN_EXTRA_EVENT, '-'),
];
}

View File

@@ -8,6 +8,7 @@ use App\Command;
use App\Libs\Config;
use App\Libs\Database\DatabaseInterface as iDB;
use App\Libs\Entity\StateInterface as iState;
use App\Libs\Exceptions\Backends\UnexpectedVersionException;
use App\Libs\Options;
use App\Libs\QueueRequests;
use App\Libs\Routable;
@@ -195,24 +196,66 @@ class ProgressCommand extends Command
}
foreach ($list as $name => &$backend) {
$opts = ag($backend, 'options', []);
try {
$opts = ag($backend, 'options', []);
if ($input->getOption('ignore-date')) {
$opts[Options::IGNORE_DATE] = true;
if ($input->getOption('ignore-date')) {
$opts[Options::IGNORE_DATE] = true;
}
if ($input->getOption('dry-run')) {
$opts[Options::DRY_RUN] = true;
}
if ($input->getOption('trace')) {
$opts[Options::DEBUG_TRACE] = true;
}
$backend['options'] = $opts;
$backend['class'] = $this->getBackend(name: $name, config: $backend);
$backend['class']->progress(entities: $entities, queue: $this->queue);
} /** @noinspection PhpRedundantCatchClauseInspection */
catch (UnexpectedVersionException $e) {
$this->logger->notice(
'SYSTEM: Sync play progress is not supported for [{backend}]. Error [{error.message} @ {error.file}:{error.line}].',
[
'backend' => $name,
'error' => [
'kind' => $e::class,
'line' => $e->getLine(),
'message' => $e->getMessage(),
'file' => after($e->getFile(), ROOT_PATH),
],
'exception' => [
'file' => $e->getFile(),
'line' => $e->getLine(),
'kind' => get_class($e),
'message' => $e->getMessage(),
'trace' => $e->getTrace(),
],
]
);
} catch (Throwable $e) {
$this->logger->error(
message: 'SYSTEM: Exception [{error.kind}] was thrown unhandled during [{backend}] request to sync progress. Error [{error.message} @ {error.file}:{error.line}].',
context: [
'error' => [
'kind' => $e::class,
'line' => $e->getLine(),
'message' => $e->getMessage(),
'file' => after($e->getFile(), ROOT_PATH),
],
'exception' => [
'file' => $e->getFile(),
'line' => $e->getLine(),
'kind' => get_class($e),
'message' => $e->getMessage(),
'trace' => $e->getTrace(),
],
]
);
}
if ($input->getOption('dry-run')) {
$opts[Options::DRY_RUN] = true;
}
if ($input->getOption('trace')) {
$opts[Options::DEBUG_TRACE] = true;
}
$backend['options'] = $opts;
$backend['class'] = $this->getBackend(name: $name, config: $backend);
$backend['class']->progress(entities: $entities, queue: $this->queue);
}
unset($backend);

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace App\Libs\Exceptions\Backends;
/**
* Class UnexpectedVersionException
*
* This exception is thrown when backend version is unexpected.
*/
class UnexpectedVersionException extends BackendException
{
}