Merge pull request #671 from arabcoders/dev
Fix Tautulli progress tracking
This commit is contained in:
@@ -18,13 +18,13 @@ use Monolog\Level;
|
||||
|
||||
return (function () {
|
||||
$inContainer = inContainer();
|
||||
$progressTimeCheck = fn (int $v, int $d): int => 0 === $v || $v >= 180 ? $v : $d;
|
||||
$progressTimeCheck = fn(int $v, int $d): int => 0 === $v || $v >= 180 ? $v : $d;
|
||||
|
||||
$config = [
|
||||
'name' => 'WatchState',
|
||||
'version' => '$(version_via_ci)',
|
||||
'tz' => env('WS_TZ', env('TZ', 'UTC')),
|
||||
'path' => fixPath(env('WS_DATA_PATH', fn () => $inContainer ? '/config' : __DIR__ . '/../var')),
|
||||
'path' => fixPath(env('WS_DATA_PATH', fn() => $inContainer ? '/config' : __DIR__ . '/../var')),
|
||||
'logs' => [
|
||||
'context' => (bool)env('WS_LOGS_CONTEXT', false),
|
||||
'prune' => [
|
||||
@@ -44,7 +44,7 @@ return (function () {
|
||||
'encode' => JSON_INVALID_UTF8_IGNORE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE,
|
||||
'headers' => [
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Application-Version' => fn () => getAppVersion(),
|
||||
'X-Application-Version' => fn() => getAppVersion(),
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
],
|
||||
],
|
||||
@@ -71,7 +71,7 @@ return (function () {
|
||||
'proxy' => (bool)env('WS_TRUST_PROXY', false),
|
||||
'header' => (string)env('WS_TRUST_HEADER', 'X-Forwarded-For'),
|
||||
'local' => (bool)env('WS_TRUST_LOCAL', false),
|
||||
'localnet' => [
|
||||
'local_net' => [
|
||||
'192.168.0.0/16', // RFC-1918 A-block.
|
||||
'127.0.0.1/32', // localhost IPv4
|
||||
'10.0.0.0/8', // RFC-1918 C-block.
|
||||
@@ -162,14 +162,14 @@ return (function () {
|
||||
|
||||
$config['profiler'] = [
|
||||
'save' => (bool)env('WS_PROFILER_SAVE', true),
|
||||
'path' => env('WS_PROFILER_PATH', fn () => ag($config, 'tmpDir') . '/profiler'),
|
||||
'path' => env('WS_PROFILER_PATH', fn() => ag($config, 'tmpDir') . '/profiler'),
|
||||
'collector' => env('WS_PROFILER_COLLECTOR', null),
|
||||
];
|
||||
|
||||
$config['cache'] = [
|
||||
'prefix' => env('WS_CACHE_PREFIX', null),
|
||||
'url' => env('WS_CACHE_URL', 'redis://127.0.0.1:6379'),
|
||||
'path' => env('WS_CACHE_PATH', fn () => ag($config, 'tmpDir') . '/cache'),
|
||||
'path' => env('WS_CACHE_PATH', fn() => ag($config, 'tmpDir') . '/cache'),
|
||||
];
|
||||
|
||||
$config['logger'] = [
|
||||
|
||||
@@ -67,7 +67,7 @@ return (function () {
|
||||
],
|
||||
[
|
||||
'key' => 'WS_TRUST_LOCAL',
|
||||
'description' => 'Bypass the authentication layer for local IP Addresses for WebUI.',
|
||||
'description' => 'Bypass the WebUI authentication layer for local IP addresses.',
|
||||
'type' => 'bool',
|
||||
'danger' => true,
|
||||
],
|
||||
@@ -276,20 +276,22 @@ return (function () {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$hash = password_hash($value, Config::get('password.algo'), Config::get('password.options', []));
|
||||
|
||||
if (false === $hash) {
|
||||
throw new ValidationException('Invalid password. Password hashing failed.');
|
||||
try {
|
||||
return $prefix . password_hash(
|
||||
$value,
|
||||
Config::get('password.algo'),
|
||||
Config::get('password.options', [])
|
||||
);
|
||||
} catch (ValueError $e) {
|
||||
throw new ValidationException('Invalid password. Password hashing failed.', $e);
|
||||
}
|
||||
|
||||
return $prefix . $hash;
|
||||
},
|
||||
'mask' => true,
|
||||
'protected' => true,
|
||||
],
|
||||
[
|
||||
[
|
||||
'key' => 'WS_SYSTEM_SECRET',
|
||||
'description' => 'The secret key which is used to sign sucessful auth requests.',
|
||||
'description' => 'The secret key which is used to sign successful auth requests.',
|
||||
'type' => 'string',
|
||||
'validate' => function (mixed $value): string {
|
||||
if (empty($value)) {
|
||||
|
||||
@@ -97,6 +97,8 @@ final class Webhooks
|
||||
$request = $client->processRequest($request);
|
||||
$attr = $request->getAttributes();
|
||||
|
||||
$debugTrace = true === (bool)ag($backend, 'options.' . Options::DEBUG_TRACE);
|
||||
|
||||
if (null !== ($userId = ag($backend, 'user', null)) && true === (bool)ag($backend, 'webhook.match.user')) {
|
||||
if (null === ($requestUser = ag($attr, 'user.id'))) {
|
||||
$message = "Request payload didn't contain a user id. Backend requires a user check.";
|
||||
@@ -109,7 +111,7 @@ final class Webhooks
|
||||
'req_user' => $requestUser ?? 'NOT SET',
|
||||
'config_user' => $userId,
|
||||
]);
|
||||
$this->write($request, Level::Info, $message);
|
||||
$this->write($request, $debugTrace ? Level::Notice : Level::Debug, $message);
|
||||
return api_error($message, Status::BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
@@ -126,7 +128,7 @@ final class Webhooks
|
||||
'req_uid' => $requestBackendId ?? 'NOT SET',
|
||||
'config_uid' => $uuid,
|
||||
]);
|
||||
$this->write($request, Level::Info, $message);
|
||||
$this->write($request, $debugTrace ? Level::Notice : Level::Debug, $message);
|
||||
return api_error($message, Status::BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
@@ -137,7 +139,6 @@ final class Webhooks
|
||||
}
|
||||
}
|
||||
|
||||
$debugTrace = true === (bool)ag($backend, 'options.' . Options::DEBUG_TRACE);
|
||||
$metadataOnly = true === (bool)ag($backend, 'options.' . Options::IMPORT_METADATA_ONLY);
|
||||
|
||||
if (true !== $metadataOnly && true !== (bool)ag($backend, 'import.enabled')) {
|
||||
|
||||
@@ -12,10 +12,10 @@ use App\Libs\Config;
|
||||
use App\Libs\DataUtil;
|
||||
use App\Libs\Enums\Http\Method;
|
||||
use App\Libs\Enums\Http\Status;
|
||||
use App\Libs\IpUtils;
|
||||
use App\Libs\Middlewares\AuthorizationMiddleware;
|
||||
use App\Libs\TokenUtil;
|
||||
use App\Libs\Traits\APITraits;
|
||||
use App\Libs\IpUtils;
|
||||
use Psr\Http\Message\ResponseInterface as iResponse;
|
||||
use Psr\Http\Message\ServerRequestInterface as iRequest;
|
||||
use Throwable;
|
||||
@@ -42,13 +42,14 @@ final class Auth
|
||||
return api_response(Status::NO_CONTENT);
|
||||
}
|
||||
|
||||
if (false === Config::get('trust.local', false)) {
|
||||
$localNet = Config::get('trust.local_net', []);
|
||||
if (true !== (bool)Config::get('trust.local', false) || count($localNet) < 1) {
|
||||
return api_response(Status::OK);
|
||||
}
|
||||
|
||||
$localAddress = getClientIp($request);
|
||||
|
||||
if (false === IpUtils::checkIp($localAddress, Config::get('trust.localnet', []))) {
|
||||
if (false === IpUtils::checkIp($localAddress, $localNet)) {
|
||||
return api_response(Status::OK);
|
||||
}
|
||||
|
||||
@@ -123,8 +124,8 @@ final class Auth
|
||||
|
||||
try {
|
||||
$payload = json_decode($payload, true, flags: JSON_THROW_ON_ERROR);
|
||||
$tokenUser = ag($payload, 'username', fn () => TokenUtil::generateSecret());
|
||||
$systemUser = Config::get('system.user', fn () => TokenUtil::generateSecret());
|
||||
$tokenUser = ag($payload, 'username', fn() => TokenUtil::generateSecret());
|
||||
$systemUser = Config::get('system.user', fn() => TokenUtil::generateSecret());
|
||||
|
||||
if (false === hash_equals($systemUser, $tokenUser)) {
|
||||
return api_error('Invalid token.', Status::UNAUTHORIZED);
|
||||
@@ -254,8 +255,8 @@ final class Auth
|
||||
return api_error('Invalid current password.', Status::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$repsonse = APIRequest(Method::POST, '/system/env/WS_SYSTEM_PASSWORD', ['value' => $new_password]);
|
||||
if (Status::OK !== $repsonse->status) {
|
||||
$response = APIRequest(Method::POST, '/system/env/WS_SYSTEM_PASSWORD', ['value' => $new_password]);
|
||||
if (Status::OK !== $response->status) {
|
||||
return api_error('Failed to set new password.', Status::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
|
||||
@@ -92,28 +92,26 @@ final class InspectRequest
|
||||
return $item;
|
||||
}
|
||||
|
||||
$item = ag_set($item, 'Account.id', (int)ag($item, 'Account.id', 0));
|
||||
$item = ag_set($item, 'Player.local', (bool)ag($item, 'Player.local', false));
|
||||
$item = ag_set($item, 'Metadata.index', (int)ag($item, 'Metadata.index', 0));
|
||||
$item = ag_set($item, 'Metadata.parentIndex', (int)ag($item, 'Metadata.parentIndex', 0));
|
||||
$item = ag_set($item, 'Metadata.audienceRating', (float)ag($item, 'Metadata.audienceRating', 0));
|
||||
$item = ag_set($item, 'Metadata.viewOffset', (int)ag($item, 'Metadata.viewOffset', 0));
|
||||
if ('' === ag($item, 'Metadata.lastViewedAt', '')) {
|
||||
$item = ag_set($item, 'Metadata.lastViewedAt', null);
|
||||
$item = ag_set($item, 'Metadata.viewCount', 0);
|
||||
} else {
|
||||
$item = ag_set(
|
||||
$item,
|
||||
'Metadata.lastViewedAt',
|
||||
makeDate(ag($item, 'Metadata.lastViewedAt'))->getTimestamp()
|
||||
);
|
||||
$item = ag_set($item, 'Metadata.viewCount', 1);
|
||||
$item = ag_sets($item, [
|
||||
'Account.id' => (int)ag($item, 'Account.id', 0),
|
||||
'Player.local' => (bool)ag($item, 'Player.local', false),
|
||||
'Metadata.index' => (int)ag($item, 'Metadata.index', 0),
|
||||
'Metadata.parentIndex' => (int)ag($item, 'Metadata.parentIndex', 0),
|
||||
'Metadata.audienceRating' => (float)ag($item, 'Metadata.audienceRating', 0),
|
||||
'Metadata.viewOffset' => (int)ag($item, 'Metadata.viewOffset', 0),
|
||||
'Metadata.year' => (int)ag($item, 'Metadata.year', 0),
|
||||
'Metadata.duration' => (int)ag($item, 'Metadata.duration', 0),
|
||||
'Metadata.addedAt' => makeDate(ag($item, 'Metadata.addedAt'))->getTimestamp(),
|
||||
'Metadata.updatedAt' => makeDate(ag($item, 'Metadata.updatedAt'))->getTimestamp(),
|
||||
'Metadata.lastViewedAt' => null,
|
||||
'Metadata.Guid' => [],
|
||||
'Metadata.viewCount' => 0,
|
||||
]);
|
||||
|
||||
$lastViewedAt = ag($item, 'Metadata.lastViewedAt', '');
|
||||
if (!empty($lastViewedAt)) {
|
||||
$item = ag_set($item, 'Metadata.lastViewedAt', makeDate($lastViewedAt)->getTimestamp());
|
||||
}
|
||||
$item = ag_set($item, 'Metadata.year', (int)ag($item, 'Metadata.year', 0));
|
||||
$item = ag_set($item, 'Metadata.duration', (int)ag($item, 'Metadata.duration', 0));
|
||||
$item = ag_set($item, 'Metadata.addedAt', makeDate(ag($item, 'Metadata.addedAt'))->getTimestamp());
|
||||
$item = ag_set($item, 'Metadata.updatedAt', makeDate(ag($item, 'Metadata.updatedAt'))->getTimestamp());
|
||||
$item = ag_set($item, 'Metadata.Guid', []);
|
||||
|
||||
if (null !== ($guids = ag($item, 'Metadata.Guids', null))) {
|
||||
foreach ($guids as $key => $val) {
|
||||
@@ -125,8 +123,7 @@ final class InspectRequest
|
||||
}
|
||||
|
||||
if ('tautulli.watched' === $event) {
|
||||
$item = ag_set($item, 'Metadata.viewCount', 1);
|
||||
$item = ag_set($item, 'Metadata.lastViewedAt', time());
|
||||
$item = ag_sets($item, ['Metadata.viewCount' => 1, 'Metadata.lastViewedAt' => time()]);
|
||||
}
|
||||
|
||||
return $item;
|
||||
|
||||
@@ -215,7 +215,7 @@ final class ParseWebhook
|
||||
}
|
||||
|
||||
$allowUpdate = (int)Config::get('progress.threshold', 0);
|
||||
$progCheck = $allowUpdate || 0 === $isPlayed;
|
||||
$progCheck = $allowUpdate || false === $isPlayed;
|
||||
|
||||
if ($progCheck && null !== ($progress = ag($item, 'viewOffset', null))) {
|
||||
// -- Plex reports play progress in milliseconds already no need to convert.
|
||||
|
||||
Reference in New Issue
Block a user