jellyfin added version constraint check for play progress sync.
This commit is contained in:
@@ -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 + [
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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, '-'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
14
src/Libs/Exceptions/Backends/UnexpectedVersionException.php
Normal file
14
src/Libs/Exceptions/Backends/UnexpectedVersionException.php
Normal 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
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user