Add files via upload

This commit is contained in:
adef17286-sudo 2025-10-09 01:07:23 +00:00 committed by GitHub
parent ebecfc57dc
commit 75da966da1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 275 additions and 0 deletions

89
NPO.py Normal file
View File

@ -0,0 +1,89 @@
import requests
import re
import json
import argparse
def get_stream_url(url):
if url.startswith("https://npo.nl/start/serie/") and url.endswith("/afspelen"):
try:
# Step 1: Get the JSON data
response = requests.get(url)
response.raise_for_status()
match = re.search(r'<script id="__NEXT_DATA__" type="application/json">(.*?)</script>', response.text, re.DOTALL)
if match:
json_data = match.group(1)
data = json.loads(json_data)
product_info = None
for item in data.get('props', {}).get('pageProps', {}).get('dehydratedState', {}).get('queries', []):
for episode_data in item.get('state', {}).get('data', []):
# Debug output to understand structure
if isinstance(episode_data, dict) and episode_data.get('slug') == url.split('/')[-2]:
product_info = {
'productId': episode_data.get('productId'),
'guid': episode_data.get('guid')
}
break
if product_info:
break
if product_info:
# Step 2: Get JWT
token_url = f"https://npo.nl/start/api/domain/player-token?productId={product_info['productId']}"
token_response = requests.get(token_url)
token_response.raise_for_status()
jwt = token_response.json().get('jwt')
if jwt:
# Step 3: Make POST request to get stream link
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36",
"Authorization": jwt,
"Content-Type": "application/json",
"Accept": "*/*",
"Referer": "https://npo.nl/"
}
body = {
"profileName": "dash",
"drmType": "widevine",
"referrerUrl": url,
"ster": {
"identifier": "npo-app-desktop",
"deviceType": 4,
"player": "web"
}
}
stream_response = requests.post("https://prod.npoplayer.nl/stream-link", headers=headers, json=body)
stream_response.raise_for_status()
# Step 4: Extract streams URL and drmToken
stream_data = stream_response.json().get('stream', {})
stream_url = stream_data.get('streamURL', "streamURL not found in response.")
drm_token = stream_data.get('drmToken', "drmToken not found in response.")
return (stream_url, drm_token) # Return both if needed
return "Product ID and GUID not found for the given slug."
return "JSON script not found in the response."
except requests.exceptions.RequestException as e:
return f"An error occurred: {str(e)}"
except json.JSONDecodeError:
return "Failed to decode JSON data."
return "Invalid URL. Please provide a URL that starts with 'https://npo.nl/start/serie/' and ends with '/afspelen'."
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Get the streaming URL from an NPO series page.")
parser.add_argument("url", type=str, help="The URL of the NPO series page.")
args = parser.parse_args()
stream_url_response = get_stream_url(args.url)
# Print the final result
if isinstance(stream_url_response, tuple):
print(f"Stream URL: {stream_url_response[0]}")
print(f"DRM Token: {stream_url_response[1]}")
else:
print(stream_url_response)

186
getpssh.py Normal file
View File

@ -0,0 +1,186 @@
#!/usr/bin/env python3
import subprocess
import re
import requests
from pathlib import Path
import os
import sys
import traceback
from pywidevine.cdm import Cdm
from pywidevine.device import Device
from pywidevine.pssh import PSSH
def run_npo_get_output(link):
try:
p = subprocess.run([sys.executable, 'NPO.py', link],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
text=True)
return p.stdout or "", p.stderr or "", p.returncode
except Exception as e:
print("[!] Failed to run NPO.py:", e)
return "", str(e), 1
def extract_mpd_url(text):
m = re.search(r'(https?://[^\s\'"]+?\.mpd(?:[^\s\'"]*)?)', text, re.IGNORECASE)
return m.group(1).strip() if m else None
def extract_drm_token(text):
match = re.search(r'DRM Token:\s*([A-Za-z0-9_\-\.=]+)', text)
if match:
return match.group(1).strip()
return None
def download_mpd(mpd_url, out_path="manifest.mpd"):
try:
r = requests.get(mpd_url, timeout=20)
r.raise_for_status()
Path(out_path).write_text(r.text, encoding='utf-8', errors='ignore')
return out_path
except requests.RequestException as e:
print("[!] Failed to download MPD:", e)
return None
def extract_pssh_blocks_from_mpd(path, max_len=200):
text = Path(path).read_text(encoding='utf-8', errors='ignore')
pattern = re.compile(r"<cenc:pssh>(.*?)</cenc:pssh>", re.DOTALL | re.IGNORECASE)
found = pattern.findall(text)
short = [f.strip() for f in found if len(f.strip()) <= max_len]
return short
def to_hex(val):
if isinstance(val, bytes):
return val.hex()
elif isinstance(val, str):
return val
else:
return str(val)
def kid_to_nodash_hex(kid_val):
if isinstance(kid_val, bytes):
return kid_val.hex()
if isinstance(kid_val, str):
return kid_val.replace("-", "").lower()
return str(kid_val)
def process_pssh_with_pywidevine(pssh_b64, provision_path, license_url):
try:
pssh = PSSH(pssh_b64)
device = Device.load(provision_path)
cdm = Cdm.from_device(device)
session_id = cdm.open()
challenge = cdm.get_license_challenge(session_id, pssh)
headers = {'Content-Type': 'application/octet-stream'}
resp = requests.post(license_url, data=challenge, headers=headers, timeout=30)
resp.raise_for_status()
cdm.parse_license(session_id, resp.content)
keys = cdm.get_keys(session_id)
if not keys:
print("[!] No keys returned.")
cdm.close(session_id)
return []
keypairs = []
for key in keys:
kid_raw = kid_to_nodash_hex(key.kid).lower()
key_hex = to_hex(key.key).lower()
kid_raw = kid_raw.replace("-", "")
output = f"{kid_raw}:{key_hex}"
if len(output) < 70:
print(output)
keypairs.append(output)
cdm.close(session_id)
return keypairs
except Exception:
traceback.print_exc()
return []
def main():
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <link>")
sys.exit(1)
link = sys.argv[1]
provision_path = "motorola_moto.wvd"
if not Path(provision_path).is_file():
print(f"[!] Provisioning file '{provision_path}' not found.")
sys.exit(1)
out, err, code = run_npo_get_output(link)
if code != 0:
print("[!] NPO.py exited with code", code)
sys.exit(1)
combined = out + "\n" + err
mpd_url = extract_mpd_url(combined)
if not mpd_url:
print("[!] Could not find .mpd URL in output.")
sys.exit(1)
drm_token = extract_drm_token(combined)
if not drm_token:
print("[!] Could not find DRM token.")
sys.exit(1)
license_url = f"https://npo-drm-gateway.samgcloud.nepworldwide.nl/authentication?custom_data={drm_token}"
mpd_file = download_mpd(mpd_url)
if not mpd_file:
sys.exit(1)
psshs = extract_pssh_blocks_from_mpd(mpd_file, max_len=200)
try:
os.remove(mpd_file)
except Exception:
pass
if not psshs:
print("[!] No suitable PSSH blocks found.")
sys.exit(1)
all_keypairs = []
for pssh in psshs:
p_clean = "".join(pssh.split())
keys = process_pssh_with_pywidevine(p_clean, provision_path, license_url)
if not keys:
sys.exit(1)
all_keypairs.extend(keys)
# Build command for N_m3u8dl-re
cmd = ["N_m3u8dl-re", mpd_url]
for keypair in all_keypairs:
cmd += ["--key", keypair]
# Append fixed options
cmd += ["-sv", "best", "-sa", "best", "-M", "mkv"]
print("\n[+] Running command:")
print(" ".join(cmd))
# Run the command
try:
subprocess.run(cmd, check=True)
except subprocess.CalledProcessError as e:
print("[!] N_m3u8dl-re failed:", e)
sys.exit(1)
if __name__ == "__main__":
main()