184 lines
5.9 KiB
Python
184 lines
5.9 KiB
Python
#!/usr/bin/python3
|
|
import sys
|
|
import os
|
|
import base64
|
|
import json
|
|
import requests
|
|
import jwt
|
|
import logging
|
|
from logging.handlers import RotatingFileHandler # Import RotatingFileHandler
|
|
from typing import Any, Dict, Optional
|
|
|
|
# Utility function for parameter parsing
|
|
def parse_params(params: list[str], name: str, default: str = "") -> str:
|
|
return next((p.split('=')[1] for p in params if p.startswith(f"{name}=")), default)
|
|
|
|
# Fetch parameters from sys.argv
|
|
user = parse_params(sys.argv, 'user')
|
|
password = parse_params(sys.argv, 'password')
|
|
id = parse_params(sys.argv, 'id')
|
|
action = parse_params(sys.argv, 'action')
|
|
pssh = parse_params(sys.argv, 'pssh')
|
|
|
|
# Dynamically set the log filename based on the 'id' parameter
|
|
|
|
log_filename = f"{id}.log" if id else f"default.log"
|
|
# Setup logging to a dynamically named file
|
|
logging.basicConfig(
|
|
level=logging.DEBUG, # Capture all log messages
|
|
format='%(asctime)s - %(levelname)s - %(message)s',
|
|
handlers=[
|
|
RotatingFileHandler(log_filename, maxBytes=5*1024*1024, backupCount=3),
|
|
logging.StreamHandler()
|
|
]
|
|
)
|
|
|
|
# Constants for URLs
|
|
CHANNELS_API_URL = 'https://cbd46b77.cdn.cms.movetv.com/cms/publish3/domain/summary/ums/1.json'
|
|
PLAYBACK_API_URL = 'https://cbd46b77.cdn.cms.movetv.com/playermetadata/sling/v1/api/channels/{id}/schedule/now/playback_info.qvt'
|
|
|
|
# API headers
|
|
api_headers = {"ccdodlad-api-key": "bluechip_studio_696223755_api_and_extension_unlimited"}
|
|
|
|
# CDM process
|
|
def do_cdm(pssh: str, id: str) -> Optional[Dict[str, Any]]:
|
|
try:
|
|
# Use f-string for URL formatting
|
|
sling_key_api = f"id={id}&pssh={pssh}"
|
|
|
|
# Make the request
|
|
response = requests.get(sling_key_api)
|
|
response.raise_for_status() # Raise an error for bad HTTP status codes
|
|
|
|
try:
|
|
key_data = response.json() # Attempt to parse JSON
|
|
except ValueError:
|
|
logging.error(f"Invalid JSON response: {response.content}")
|
|
return None
|
|
|
|
# Check if 'status' is present in the response and handle 'false' status
|
|
if key_data.get('status') == 'false':
|
|
logging.error("Failed to Grab Key: Status is false")
|
|
return None
|
|
|
|
# Return the 'keys' field from the response if present
|
|
keys = key_data.get('keys', None)
|
|
if keys is None or not keys: # Check if keys is None or an empty list
|
|
logging.error("Keys are empty or not present")
|
|
return None
|
|
|
|
list = "\n".join(keys)
|
|
|
|
print(list)
|
|
|
|
except requests.exceptions.RequestException as e:
|
|
logging.error(f"RequestException: {e}")
|
|
except Exception as e:
|
|
logging.error(f"Unexpected error in do_cdm: {e}")
|
|
|
|
return None
|
|
|
|
# Function to process channels and fetch playback manifest
|
|
def process_channel(id: str) -> Optional[str]:
|
|
try:
|
|
response = requests.get(PLAYBACK_API_URL.format(id=id))
|
|
response.raise_for_status()
|
|
|
|
data = response.json()
|
|
dash_manifest_url = data.get('playback_info', {}).get('dash_manifest_url')
|
|
|
|
if dash_manifest_url:
|
|
return dash_manifest_url
|
|
else:
|
|
logging.error("dash_manifest_url not found in the response")
|
|
return None
|
|
|
|
except requests.exceptions.RequestException as e:
|
|
logging.error(f"API request error: {e}")
|
|
except KeyError as e:
|
|
logging.error(f"KeyError: {e}")
|
|
except Exception as e:
|
|
logging.error(f"Unexpected error: {e}")
|
|
|
|
return None
|
|
|
|
# Function to handle the channels action
|
|
def handle_channels() -> None:
|
|
output = {"Channels": []}
|
|
|
|
try:
|
|
response = requests.get(CHANNELS_API_URL).json()
|
|
channels = response.get('channels', [])
|
|
|
|
for channel_data in channels:
|
|
channelid = str(channel_data['channel_guid'])
|
|
|
|
channel_info = {
|
|
"Name": channel_data['metadata']['channel_name'],
|
|
"Mode": "live",
|
|
"SessionManifest": True,
|
|
"ManifestScript": f'id={channelid}',
|
|
"CdmType": "widevine",
|
|
"UseCdm": True,
|
|
"Cdm": f'id={channelid}',
|
|
"Video": "best",
|
|
"OnDemand": True,
|
|
"SpeedUp": True,
|
|
"CdmMode": "external"
|
|
}
|
|
|
|
output['Channels'].append(channel_info)
|
|
|
|
print(json.dumps(output, indent=2))
|
|
|
|
except (KeyError, ValueError, requests.RequestException) as e:
|
|
logging.error(f"Error while handling channels: {e}")
|
|
|
|
# Function to handle the manifest action
|
|
def handle_manifest(id: str) -> None:
|
|
try:
|
|
manifest_url = process_channel(id)
|
|
output = {
|
|
"ManifestUrl": manifest_url,
|
|
"Headers": {
|
|
"Manifest": {
|
|
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'
|
|
},
|
|
"Media": {
|
|
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'
|
|
}
|
|
},
|
|
"Heartbeat": {
|
|
"Url": '',
|
|
"Params": '',
|
|
"PeriodMs": 5 * 60 * 1000
|
|
}
|
|
}
|
|
print(json.dumps(output, indent=4))
|
|
|
|
except Exception as e:
|
|
logging.error(f"Error while handling manifest: {e}")
|
|
|
|
|
|
def fix_base64(encoded_str):
|
|
# Add necessary padding if needed
|
|
missing_padding = len(encoded_str) % 4
|
|
if missing_padding:
|
|
encoded_str += '=' * (4 - missing_padding) # Add '=' padding
|
|
|
|
return encoded_str
|
|
|
|
# Main control flow
|
|
logging.error("Running with action : " + action)
|
|
if action == "cdm":
|
|
logging.error("Requesting keys")
|
|
do_cdm(pssh, id)
|
|
|
|
elif action == "channels":
|
|
logging.error("Reloading channels")
|
|
handle_channels()
|
|
|
|
elif action == "manifest":
|
|
logging.error("Requesting manifest")
|
|
handle_manifest(id)
|