From 51dd276673894bfa461e6649de7c3d810c9a6801 Mon Sep 17 00:00:00 2001 From: JVT038 <47184046+JVT038@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:13:20 +0200 Subject: [PATCH 1/5] Move webhook handling to the API endpoints. --- settings/routes.php | 14 +++---- src/HttpController/Api/EmbyController.php | 38 +++++++++++++++++ src/HttpController/Api/JellyfinController.php | 37 +++++++++++++++++ src/HttpController/Api/PlexController.php | 41 +++++++++++++++++++ src/HttpController/Web/EmbyController.php | 18 -------- src/HttpController/Web/JellyfinController.php | 18 -------- src/Service/WebhookUrlBuilder.php | 2 +- 7 files changed, 124 insertions(+), 44 deletions(-) create mode 100644 src/HttpController/Api/EmbyController.php create mode 100644 src/HttpController/Api/JellyfinController.php create mode 100644 src/HttpController/Api/PlexController.php diff --git a/settings/routes.php b/settings/routes.php index 66af0c4d..164cdea2 100644 --- a/settings/routes.php +++ b/settings/routes.php @@ -33,13 +33,6 @@ function addWebRoutes(RouterService $routerService, FastRoute\RouteCollector $ro ]); $routes->add('GET', '/docs/api', [Web\OpenApiController::class, 'renderPage']); - ##################### - # Webhook listeners # - ##################### - $routes->add('POST', '/plex/{id:.+}', [Web\PlexController::class, 'handlePlexWebhook']); - $routes->add('POST', '/jellyfin/{id:.+}', [Web\JellyfinController::class, 'handleJellyfinWebhook']); - $routes->add('POST', '/emby/{id:.+}', [Web\EmbyController::class, 'handleEmbyWebhook']); - ############# # Job Queue # ############# @@ -210,5 +203,12 @@ function addApiRoutes(RouterService $routerService, FastRoute\RouteCollector $ro $routes->add('GET', '/movies/search', [Api\MovieSearchController::class, 'search'], [Api\Middleware\IsAuthenticated::class]); + ##################### + # Webhook listeners # + ##################### + $routes->add('POST', '/plex/{id:.+}', [Api\PlexController::class, 'handlePlexWebhook']); + $routes->add('POST', '/jellyfin/{id:.+}', [Api\JellyfinController::class, 'handleJellyfinWebhook']); + $routes->add('POST', '/emby/{id:.+}', [Api\EmbyController::class, 'handleEmbyWebhook']); + $routerService->addRoutesToRouteCollector($routeCollector, $routes); } diff --git a/src/HttpController/Api/EmbyController.php b/src/HttpController/Api/EmbyController.php new file mode 100644 index 00000000..f440a0c8 --- /dev/null +++ b/src/HttpController/Api/EmbyController.php @@ -0,0 +1,38 @@ +getRouteParameters()['id']; + + $userId = $this->userApi->findUserIdByEmbyWebhookId($webhookId); + if ($userId === null) { + return Response::createNotFound(); + } + + $requestPayload = $request->getPostParameters()['data']; + + $this->logger->debug('Emby: Webhook triggered with payload: ' . $requestPayload); + + $this->embyScrobbler->processEmbyWebhook($userId, Json::decode($requestPayload)); + + return Response::createOk(); + } +} \ No newline at end of file diff --git a/src/HttpController/Api/JellyfinController.php b/src/HttpController/Api/JellyfinController.php new file mode 100644 index 00000000..5619fef5 --- /dev/null +++ b/src/HttpController/Api/JellyfinController.php @@ -0,0 +1,37 @@ +getRouteParameters()['id']; + + $userId = $this->userApi->findUserIdByJellyfinWebhookId($webhookId); + if ($userId === null) { + return Response::createNotFound(); + } + + $requestPayload = $request->getBody(); + + $this->logger->debug('Jellyfin: Webhook triggered with payload: ' . $requestPayload); + + $this->jellyfinScrobbler->processJellyfinWebhook($userId, Json::decode($requestPayload)); + + return Response::createOk(); + } +} \ No newline at end of file diff --git a/src/HttpController/Api/PlexController.php b/src/HttpController/Api/PlexController.php new file mode 100644 index 00000000..5c496b4b --- /dev/null +++ b/src/HttpController/Api/PlexController.php @@ -0,0 +1,41 @@ +getRouteParameters()['id']; + + $userId = $this->userApi->findUserIdByPlexWebhookId($webhookId); + if ($userId === null) { + return Response::createNotFound(); + } + + $requestPayload = $request->getPostParameters()['payload'] ?? null; + if ($requestPayload === null) { + return Response::createOk(); + } + + $this->logger->debug('Plex: Webhook triggered with payload: ' . $requestPayload); + + $this->plexScrobbler->processPlexWebhook($userId, Json::decode((string)$requestPayload)); + + return Response::createOk(); + } +} \ No newline at end of file diff --git a/src/HttpController/Web/EmbyController.php b/src/HttpController/Web/EmbyController.php index 7abd4547..e4bfb27f 100644 --- a/src/HttpController/Web/EmbyController.php +++ b/src/HttpController/Web/EmbyController.php @@ -29,24 +29,6 @@ public function deleteEmbyWebhookUrl() : Response return Response::createOk(); } - public function handleEmbyWebhook(Request $request) : Response - { - $webhookId = $request->getRouteParameters()['id']; - - $userId = $this->userApi->findUserIdByEmbyWebhookId($webhookId); - if ($userId === null) { - return Response::createNotFound(); - } - - $requestPayload = $request->getPostParameters()['data']; - - $this->logger->debug('Emby: Webhook triggered with payload: ' . $requestPayload); - - $this->embyScrobbler->processEmbyWebhook($userId, Json::decode($requestPayload)); - - return Response::createOk(); - } - public function regenerateEmbyWebhookUrl() : Response { $webhookId = $this->userApi->regenerateEmbyWebhookId($this->authenticationService->getCurrentUserId()); diff --git a/src/HttpController/Web/JellyfinController.php b/src/HttpController/Web/JellyfinController.php index dde92211..7719d645 100644 --- a/src/HttpController/Web/JellyfinController.php +++ b/src/HttpController/Web/JellyfinController.php @@ -66,24 +66,6 @@ public function deleteJellyfinWebhookUrl() : Response return Response::createOk(); } - public function handleJellyfinWebhook(Request $request) : Response - { - $webhookId = $request->getRouteParameters()['id']; - - $userId = $this->userApi->findUserIdByJellyfinWebhookId($webhookId); - if ($userId === null) { - return Response::createNotFound(); - } - - $requestPayload = $request->getBody(); - - $this->logger->debug('Jellyfin: Webhook triggered with payload: ' . $requestPayload); - - $this->jellyfinScrobbler->processJellyfinWebhook($userId, Json::decode($requestPayload)); - - return Response::createOk(); - } - public function regenerateJellyfinWebhookUrl() : Response { $webhookId = $this->userApi->regenerateJellyfinWebhookId($this->authenticationService->getCurrentUserId()); diff --git a/src/Service/WebhookUrlBuilder.php b/src/Service/WebhookUrlBuilder.php index 8762f59f..472ad412 100644 --- a/src/Service/WebhookUrlBuilder.php +++ b/src/Service/WebhookUrlBuilder.php @@ -31,6 +31,6 @@ private function buildUrl(string $webhookType, string $webhookId) : ?string return null; } - return rtrim($applicationUrl, '/') . '/' . $webhookType . '/' . $webhookId; + return rtrim($applicationUrl, '/') . '/api/' . $webhookType . '/' . $webhookId; } } From a37ecb27f72e12e3b429da4244481e5e4d633e3c Mon Sep 17 00:00:00 2001 From: JVT038 <47184046+JVT038@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:26:14 +0200 Subject: [PATCH 2/5] Added webhooks to openapi specification --- docs/openapi.json | 81 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/docs/openapi.json b/docs/openapi.json index c3cced6a..18f0072c 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -964,6 +964,87 @@ } ] } + }, + "/plex/{uuid}": { + "post": { + "tags": [ + "Webhooks" + ], + "description": "Endpoint to scrobble your Plex watches to Movary.", + "parameters": [ + { + "name": "UUID", + "in": "query", + "description": "An UUID that is generated by the user in the Plex settings.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "404": { + "$ref": "#/components/responses/404" + } + } + } + }, + "/jellyfin/{uuid}": { + "post": { + "tags": [ + "Webhooks" + ], + "description": "Endpoint to scrobble your Jellyfin watches to Movary.", + "parameters": [ + { + "name": "UUID", + "in": "query", + "description": "An UUID that is generated by the user in the Jellyfin settings.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "404": { + "$ref": "#/components/responses/404" + } + } + } + }, + "/emby/{uuid}": { + "post": { + "tags": [ + "Webhooks" + ], + "description": "Endpoint to scrobble your Emby watches to Movary.", + "parameters": [ + { + "name": "UUID", + "in": "query", + "description": "An UUID that is generated by the user in the Emby settings.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "404": { + "$ref": "#/components/responses/404" + } + } + } } }, "components": { From 605951445708e8107ef9fac6f44d37c0f51f68fa Mon Sep 17 00:00:00 2001 From: Lee Peuker Date: Sat, 30 Sep 2023 19:31:18 +0200 Subject: [PATCH 3/5] Only deprecate webhook web routes and update api webhook route --- settings/routes.php | 18 +++++++------ src/HttpController/Api/EmbyController.php | 2 +- src/HttpController/Api/JellyfinController.php | 2 +- src/HttpController/Api/PlexController.php | 2 +- src/HttpController/Web/EmbyController.php | 23 +++++++++++++++++ src/HttpController/Web/JellyfinController.php | 25 +++++++++++++++++++ src/Service/WebhookUrlBuilder.php | 2 +- tests/rest/web/plex-scrobble.http | 2 +- 8 files changed, 64 insertions(+), 12 deletions(-) diff --git a/settings/routes.php b/settings/routes.php index 164cdea2..ddd020ec 100644 --- a/settings/routes.php +++ b/settings/routes.php @@ -33,7 +33,14 @@ function addWebRoutes(RouterService $routerService, FastRoute\RouteCollector $ro ]); $routes->add('GET', '/docs/api', [Web\OpenApiController::class, 'renderPage']); - ############# + ##################### + # Webhook listeners # !!! Deprecated use new api routes + ##################### + $routes->add('POST', '/plex/{id:.+}', [Web\PlexController::class, 'handlePlexWebhook']); + $routes->add('POST', '/jellyfin/{id:.+}', [Web\JellyfinController::class, 'handleJellyfinWebhook']); + $routes->add('POST', '/emby/{id:.+}', [Web\EmbyController::class, 'handleEmbyWebhook']); + + ############ # Job Queue # ############# $routes->add('GET', '/jobs', [Web\JobController::class, 'getJobs'], [Web\Middleware\UserIsAuthenticated::class]); @@ -203,12 +210,9 @@ function addApiRoutes(RouterService $routerService, FastRoute\RouteCollector $ro $routes->add('GET', '/movies/search', [Api\MovieSearchController::class, 'search'], [Api\Middleware\IsAuthenticated::class]); - ##################### - # Webhook listeners # - ##################### - $routes->add('POST', '/plex/{id:.+}', [Api\PlexController::class, 'handlePlexWebhook']); - $routes->add('POST', '/jellyfin/{id:.+}', [Api\JellyfinController::class, 'handleJellyfinWebhook']); - $routes->add('POST', '/emby/{id:.+}', [Api\EmbyController::class, 'handleEmbyWebhook']); + $routes->add('POST', '/webhook/plex/{id:.+}', [Api\PlexController::class, 'handlePlexWebhook']); + $routes->add('POST', '/webhook/jellyfin/{id:.+}', [Api\JellyfinController::class, 'handleJellyfinWebhook']); + $routes->add('POST', '/webhook/emby/{id:.+}', [Api\EmbyController::class, 'handleEmbyWebhook']); $routerService->addRoutesToRouteCollector($routeCollector, $routes); } diff --git a/src/HttpController/Api/EmbyController.php b/src/HttpController/Api/EmbyController.php index f440a0c8..6d2e3290 100644 --- a/src/HttpController/Api/EmbyController.php +++ b/src/HttpController/Api/EmbyController.php @@ -35,4 +35,4 @@ public function handleEmbyWebhook(Request $request) : Response return Response::createOk(); } -} \ No newline at end of file +} diff --git a/src/HttpController/Api/JellyfinController.php b/src/HttpController/Api/JellyfinController.php index 5619fef5..de2dc427 100644 --- a/src/HttpController/Api/JellyfinController.php +++ b/src/HttpController/Api/JellyfinController.php @@ -34,4 +34,4 @@ public function handleJellyfinWebhook(Request $request) : Response return Response::createOk(); } -} \ No newline at end of file +} diff --git a/src/HttpController/Api/PlexController.php b/src/HttpController/Api/PlexController.php index 5c496b4b..aecb0842 100644 --- a/src/HttpController/Api/PlexController.php +++ b/src/HttpController/Api/PlexController.php @@ -38,4 +38,4 @@ public function handlePlexWebhook(Request $request) : Response return Response::createOk(); } -} \ No newline at end of file +} diff --git a/src/HttpController/Web/EmbyController.php b/src/HttpController/Web/EmbyController.php index e4bfb27f..8be4b581 100644 --- a/src/HttpController/Web/EmbyController.php +++ b/src/HttpController/Web/EmbyController.php @@ -29,6 +29,29 @@ public function deleteEmbyWebhookUrl() : Response return Response::createOk(); } + /** + * @deprecated + * @see \Movary\HttpController\Api\EmbyController::handleEmbyWebhook() + */ + public function handleEmbyWebhook(Request $request) : Response + { + $webhookId = $request->getRouteParameters()['id']; + + $userId = $this->userApi->findUserIdByEmbyWebhookId($webhookId); + if ($userId === null) { + return Response::createNotFound(); + } + + $requestPayload = $request->getPostParameters()['data']; + + $this->logger->debug('Emby: Webhook triggered with payload: ' . $requestPayload); + $this->logger->warning('This emby webhook url is deprecated and will stop to work soon, regenerate the url'); + + $this->embyScrobbler->processEmbyWebhook($userId, Json::decode($requestPayload)); + + return Response::createOk(); + } + public function regenerateEmbyWebhookUrl() : Response { $webhookId = $this->userApi->regenerateEmbyWebhookId($this->authenticationService->getCurrentUserId()); diff --git a/src/HttpController/Web/JellyfinController.php b/src/HttpController/Web/JellyfinController.php index 7719d645..f9f079af 100644 --- a/src/HttpController/Web/JellyfinController.php +++ b/src/HttpController/Web/JellyfinController.php @@ -66,6 +66,31 @@ public function deleteJellyfinWebhookUrl() : Response return Response::createOk(); } + + /** + * @deprecated + * @see \Movary\HttpController\Api\JellyfinController::handleJellyfinWebhook() + */ + public function handleJellyfinWebhook(Request $request) : Response + { + $webhookId = $request->getRouteParameters()['id']; + + $userId = $this->userApi->findUserIdByJellyfinWebhookId($webhookId); + if ($userId === null) { + return Response::createNotFound(); + } + + $requestPayload = $request->getBody(); + + $this->logger->debug('Jellyfin: Webhook triggered with payload: ' . $requestPayload); + $this->logger->warning('This jellyfin webhook url is deprecated and will stop to work soon, regenerate the url'); + + $this->jellyfinScrobbler->processJellyfinWebhook($userId, Json::decode($requestPayload)); + + return Response::createOk(); + } + + public function regenerateJellyfinWebhookUrl() : Response { $webhookId = $this->userApi->regenerateJellyfinWebhookId($this->authenticationService->getCurrentUserId()); diff --git a/src/Service/WebhookUrlBuilder.php b/src/Service/WebhookUrlBuilder.php index 472ad412..26e9d339 100644 --- a/src/Service/WebhookUrlBuilder.php +++ b/src/Service/WebhookUrlBuilder.php @@ -31,6 +31,6 @@ private function buildUrl(string $webhookType, string $webhookId) : ?string return null; } - return rtrim($applicationUrl, '/') . '/api/' . $webhookType . '/' . $webhookId; + return rtrim($applicationUrl, '/') . '/api/webhook/' . $webhookType . '/' . $webhookId; } } diff --git a/tests/rest/web/plex-scrobble.http b/tests/rest/web/plex-scrobble.http index 1ec782cb..b40b453d 100644 --- a/tests/rest/web/plex-scrobble.http +++ b/tests/rest/web/plex-scrobble.http @@ -1,4 +1,4 @@ -POST http://127.0.0.1/plex/16914287-329d-4f3b-ba57-4a092f13058d +POST http://127.0.0.1/api/webhook/plex/14f14d6b-695f-41b9-a856-5ba401bcc040 Accept: */* Cache-Control: no-cache Content-Type: application/x-www-form-urlencoded From eead7addf1edc66f5c3ffde2565ff7dcdceaea47 Mon Sep 17 00:00:00 2001 From: Lee Peuker Date: Sat, 30 Sep 2023 19:32:50 +0200 Subject: [PATCH 4/5] Add deprecation to plex webhook --- src/HttpController/Web/JellyfinController.php | 1 - src/HttpController/Web/PlexController.php | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/HttpController/Web/JellyfinController.php b/src/HttpController/Web/JellyfinController.php index f9f079af..bc8a519f 100644 --- a/src/HttpController/Web/JellyfinController.php +++ b/src/HttpController/Web/JellyfinController.php @@ -90,7 +90,6 @@ public function handleJellyfinWebhook(Request $request) : Response return Response::createOk(); } - public function regenerateJellyfinWebhookUrl() : Response { $webhookId = $this->userApi->regenerateJellyfinWebhookId($this->authenticationService->getCurrentUserId()); diff --git a/src/HttpController/Web/PlexController.php b/src/HttpController/Web/PlexController.php index 001db998..6bbe36df 100644 --- a/src/HttpController/Web/PlexController.php +++ b/src/HttpController/Web/PlexController.php @@ -53,6 +53,10 @@ public function generatePlexAuthenticationUrl() : Response return Response::createJson(Json::encode(['authenticationUrl' => $plexAuthenticationUrl])); } + /** + * @deprecated + * @see \Movary\HttpController\Api\PlexController::handlePlexWebhook() + */ public function handlePlexWebhook(Request $request) : Response { $webhookId = $request->getRouteParameters()['id']; @@ -68,6 +72,7 @@ public function handlePlexWebhook(Request $request) : Response } $this->logger->debug('Plex: Webhook triggered with payload: ' . $requestPayload); + $this->logger->warning('This plex webhook url is deprecated and will stop to work soon, regenerate the url'); $this->plexScrobbler->processPlexWebhook($userId, Json::decode((string)$requestPayload)); From bfdd819e095eb1c99f838b6d721f594a7cf137c5 Mon Sep 17 00:00:00 2001 From: Lee Peuker Date: Sat, 30 Sep 2023 19:36:23 +0200 Subject: [PATCH 5/5] Update docs --- docs/openapi.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/openapi.json b/docs/openapi.json index 18f0072c..904273e7 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -965,7 +965,7 @@ ] } }, - "/plex/{uuid}": { + "/webhook/plex/{uuid}": { "post": { "tags": [ "Webhooks" @@ -992,7 +992,7 @@ } } }, - "/jellyfin/{uuid}": { + "/webhook/jellyfin/{uuid}": { "post": { "tags": [ "Webhooks" @@ -1019,7 +1019,7 @@ } } }, - "/emby/{uuid}": { + "/webhook/emby/{uuid}": { "post": { "tags": [ "Webhooks"