From 96a79aec83a9f1340ecb37e173cbd570793a2f8d Mon Sep 17 00:00:00 2001 From: Nirvana Date: Tue, 18 Nov 2025 15:25:40 +0100 Subject: [PATCH] Fix get_manifest --- .../providers/magenta2/concurrency.py | 84 +++++++++++++++++++ .../providers/magenta2/provider.py | 20 ++++- 2 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 lib/streaming_providers/providers/magenta2/concurrency.py diff --git a/lib/streaming_providers/providers/magenta2/concurrency.py b/lib/streaming_providers/providers/magenta2/concurrency.py new file mode 100644 index 0000000..9021eb6 --- /dev/null +++ b/lib/streaming_providers/providers/magenta2/concurrency.py @@ -0,0 +1,84 @@ +# streaming_providers/providers/magenta2/concurrency.py +import urllib.parse +from ...base.network import HTTPManager +from ...base.utils.logger import logger + + +def extract_and_release_lock(smil_content: str, http_manager: HTTPManager, + client_id: str, user_agent: str) -> bool: + """ + Extract concurrency lock from SMIL and immediately release it + Returns True if lock was found and released, False otherwise + + Args: + smil_content: SMIL XML content + http_manager: HTTP manager for making requests + client_id: The client ID used in SMIL request (will be formatted as player_{client_id}) + user_agent: Platform-specific user agent + """ + try: + import xml.etree.ElementTree as ET + + # Parse SMIL XML + root = ET.fromstring(smil_content) + + # Extract head metadata + head = root.find('{http://www.w3.org/2005/SMIL21/Language}head') + if head is None: + return False + + # Extract lock parameters + lock_params = {} + for meta in head.findall('{http://www.w3.org/2005/SMIL21/Language}meta'): + name = meta.get('name') + content = meta.get('content') + if name and content: + lock_params[name] = content + + # Check if we have all required lock parameters + required_params = ['concurrencyInstance', 'concurrencyServiceUrl', 'lockId', 'lockSequenceToken', 'lock'] + if not all(param in lock_params for param in required_params): + logger.debug("SMIL doesn't contain complete concurrency lock") + return False + + # Build release URL with the same client_id formatted as player_{client_id} + base_url = lock_params['concurrencyServiceUrl'].rstrip('/') + "/web/Concurrency/release" + formatted_client_id = f"player_{client_id}" + + params = { + 'schema': '1.0', + 'form': 'json', + '_clientId': formatted_client_id, # Use player_{smil_client_id} + '_id': lock_params['lockId'], + '_sequenceToken': urllib.parse.quote(lock_params['lockSequenceToken']), + '_encryptedLock': urllib.parse.quote(lock_params['lock']) + } + + param_string = '&'.join([f"{k}={v}" for k, v in params.items()]) + release_url = f"{base_url}?{param_string}" + + logger.debug(f"Releasing concurrency lock: {lock_params['lockId']} with client: {formatted_client_id}") + + headers = { + 'User-Agent': user_agent, # Use platform-specific user agent + 'Accept': 'application/json' + } + + # Release the lock immediately + response = http_manager.get( + release_url, + operation='concurrency_release', + headers=headers, + timeout=10 + ) + + if response.status_code == 200: + logger.info(f"✓ Concurrency lock released successfully with client: {formatted_client_id}") + return True + else: + logger.warning(f"Concurrency lock release failed: {response.status_code}") + return False + + except Exception as e: + logger.warning(f"Error releasing concurrency lock: {e}") + return False \ No newline at end of file diff --git a/lib/streaming_providers/providers/magenta2/provider.py b/lib/streaming_providers/providers/magenta2/provider.py index 757f92e..a45ea16 100644 --- a/lib/streaming_providers/providers/magenta2/provider.py +++ b/lib/streaming_providers/providers/magenta2/provider.py @@ -1187,8 +1187,10 @@ class Magenta2Provider(StreamingProvider): logger.error(f"Error getting SMIL data for channel {channel_id}: {e}") return None + # In provider.py - update _get_smil_content method + def _get_smil_content(self, channel_id: str) -> Optional[str]: - """Get SMIL content for a channel to extract releasePid""" + """Get SMIL content for a channel to extract releasePid and release concurrency lock""" try: # Reuse the same logic as get_manifest but return the raw SMIL content self._ensure_authenticated() @@ -1204,12 +1206,13 @@ class Magenta2Provider(StreamingProvider): if not account_pid: return None + # Use the same client_id as in the original SMIL request client_id = "a8198f31-b406-4177-8dee-f6216c356c75" smil_url = f"{selector_service}{account_pid}/media/{channel_id}?format=smil&formats=MPEG-DASH&tracking=true&clientId={client_id}" headers = { 'Authorization': f'Basic {self.persona_token}', - 'User-Agent': self.platform_config['user_agent'], + 'User-Agent': self.platform_config['user_agent'], # Platform user agent 'Accept': 'application/smil+xml, application/xml;q=0.9, */*;q=0.8' } @@ -1221,7 +1224,18 @@ class Magenta2Provider(StreamingProvider): ) if response.status_code == 200: - return response.text + smil_content = response.text + + # RELEASE CONCURRENCY LOCK IMMEDIATELY AFTER GETTING SMIL + from .concurrency import extract_and_release_lock + extract_and_release_lock( + smil_content, + self.http_manager, + client_id=client_id, # Use the same client_id as SMIL request + user_agent=self.platform_config['user_agent'] # Platform user agent + ) + + return smil_content else: logger.error(f"Failed to get SMIL content for DRM: {response.status_code}") return None