From 41ed4ab2af61f5c7f9118dfd01bbf44f8d26cbe8 Mon Sep 17 00:00:00 2001 From: mltobi Date: Mon, 5 Oct 2026 21:05:44 +0200 Subject: [PATCH] Fix magenta2 recording delete: use delete-recording-for-listing/{guid} endpoint --- .../providers/magenta2/constants.py | 4 ++ .../providers/magenta2/recordings_manager.py | 60 ++++++++++++++++--- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/lib/streaming_providers/providers/magenta2/constants.py b/lib/streaming_providers/providers/magenta2/constants.py index 1ec96cd..6561f90 100644 --- a/lib/streaming_providers/providers/magenta2/constants.py +++ b/lib/streaming_providers/providers/magenta2/constants.py @@ -373,6 +373,10 @@ VOD_PREFIX_MOVIE_SH = "GN_SH" PVR_GET_RECORDINGS_PATH = "/get-recordings" PVR_RECORDINGS_PATH = "/recordings" +# recordings are deleted with HTTP DELETE on this listing-GUID-based path (returns 202 Accepted), +# NOT via DELETE /recordings/{id} (which 404s for every identifier). +PVR_DELETE_RECORDING_FOR_LISTING_PATH = "/delete-recording-for-listing" + # Default and maximum page sizes for the nPVR get-recordings endpoint PVR_DEFAULT_PAGE_LIMIT = 500 PVR_MAX_PAGE_LIMIT = 500 diff --git a/lib/streaming_providers/providers/magenta2/recordings_manager.py b/lib/streaming_providers/providers/magenta2/recordings_manager.py index 3f5b0f9..54e5621 100644 --- a/lib/streaming_providers/providers/magenta2/recordings_manager.py +++ b/lib/streaming_providers/providers/magenta2/recordings_manager.py @@ -44,6 +44,7 @@ from ...base.utils.logger import logger from .constants import ( PVR_DEFAULT_PAGE_LIMIT, + PVR_DELETE_RECORDING_FOR_LISTING_PATH, PVR_GET_RECORDINGS_PATH, PVR_MAX_PAGE_LIMIT, PVR_RECORDINGS_PATH, @@ -144,23 +145,69 @@ class RecordingsManager(PvrHttpMixin): """ Permanently delete a recording on the nPVR backend. + The recording_id received from the PVR client is the MPX playback GUID + (content_id). Deletion is performed with HTTP DELETE on + ``/delete-recording-for-listing/{listingGuid}`` (confirmed to return + 202 Accepted) - the documented ``/recordings/{id}`` path 404s for every + known identifier. + Args: - recording_id: The recording's ``id`` field (not externalRecordingId). + recording_id: The recording's content_id (MPX GUID). Raises: - RuntimeError: When the API returns a non-200/204 status or the - ``auth_headers_callback`` is not configured. - KeyError: When the recording does not exist (404). + RuntimeError: When the recording cannot be resolved or the API + returns a non-success status. """ pvr_base_url = self._get_pvr_base_url() - url = f"{pvr_base_url}{PVR_RECORDINGS_PATH}/{recording_id}" + listing_guid = self._resolve_listing_guid(recording_id) + if not listing_guid: + raise RuntimeError( + f"{self._provider}: cannot delete recording '{recording_id}' - " + "no matching listing GUID found" + ) + + url = f"{pvr_base_url}{PVR_DELETE_RECORDING_FOR_LISTING_PATH}/{listing_guid}" status_code = self._delete(url) logger.info( f"{self._provider}: Deleted recording '{recording_id}' " - f"[HTTP {status_code}]" + f"(listing '{listing_guid}') [HTTP {status_code}]" ) + def _resolve_listing_guid(self, recording_id: str) -> str: + """ + Resolve the listing GUID for the recording the PVR client refers to. + + The client sends content_id (MPX playback GUID); the delete endpoint + needs the recording's ``listing.guid``. Fetch the raw recordings and + match on the playback GUID (or the internal/external recording ids). + """ + try: + pvr_base_url = self._get_pvr_base_url() + url = f"{pvr_base_url}{PVR_GET_RECORDINGS_PATH}" + params = { + "limit": PVR_MAX_PAGE_LIMIT, + "offset": 1, + "byRecordingStatus": "|".join(PVR_RECORDING_STATUSES_ALL), + } + data = self._get(url, params) or {} + for raw in data.get("recordings", []): + if not raw: + continue + internal_id = raw.get("id", "") + external_id = raw.get("externalRecordingId", "") + mpx_guid = PvrHelpers.extract_mpx_guid(raw.get("playbackUrl")) + if recording_id in (internal_id, external_id, mpx_guid): + listing = raw.get("listing") or {} + if isinstance(listing, dict): + return listing.get("guid") or "" + except Exception as exc: + logger.warning( + f"{self._provider}: could not resolve listing guid for " + f"'{recording_id}': {exc}" + ) + return "" + def get_recording_manifest(self, recording_id: str) -> Optional[str]: """ Return the playback URL for a recording. @@ -320,7 +367,6 @@ class RecordingsManager(PvrHttpMixin): content_id=effective_content_id, name=title, provider=self._provider, - # descriptions description=description, plot=plot, plot_outline=plot_outline,