#!/usr/bin/python3
import sys
import os
import o11
import base64
import json
import datetime
import pytz
import jwt
import uuid
from pywidevine.cdm import Cdm
from pywidevine.device import Device
from pywidevine.pssh import PSSH
from bs4 import BeautifulSoup

WVD_PATH = './WVD.wvd'

user = o11.parse_params(sys.argv, 'user')
password = o11.parse_params(sys.argv, 'password')

id = o11.parse_params(sys.argv, 'id')
action = o11.parse_params(sys.argv, 'action')

bind = o11.parse_params(sys.argv, 'bind')
proxy = o11.parse_params(sys.argv, 'proxy')
doh = o11.parse_params(sys.argv, 'doh')
worker = o11.parse_params(sys.argv, 'worker')

cdm = o11.parse_params(sys.argv, 'cdm')
challenge = o11.parse_params(sys.argv, 'challenge')

o11Session = o11.session(bind=bind, proxy=proxy, worker=worker)
req = o11Session.get_session()
if doh != "":
    o11.dns(doh)

if challenge == "cert":
    challenge = "CAQ="

authFile = '/Videoland_' + user + '.tokens'
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'

def do_cdm_external(pssh_data, lic_token):
    pssh_obj = PSSH(pssh_data)
    device_obj = Device.load(WVD_PATH)
    cdm_obj = Cdm.from_device(device_obj)
    session_id = cdm_obj.open()
    challenge_data = cdm_obj.get_license_challenge(session_id, pssh_obj)
    lic_headers = {'Origin': 'https://v2.videoland.com', 'Referer': 'https://v2.videoland.com/', 'User-Agent': user_agent, 'x-dt-auth-token': lic_token, 'Content-Type': 'application/octet-stream'}
    licence = req.post('https://lic.drmtoday.com/license-proxy-widevine/cenc/', headers=lic_headers, data=challenge_data)
    data = licence.json()
    cdm_obj.parse_license(session_id, base64.b64decode(data['license']))
    keys = [f"{key.kid.hex}:{key.key.hex()}" for key in cdm_obj.get_keys(session_id) if key.type != 'SIGNING']
    cdm_obj.close(session_id)
    return keys

def get_pssh_from_mpd(url):
    headers = {'User-Agent': user_agent}
    response = req.get(url, headers=headers)
    for cp in BeautifulSoup(response.content, features="xml").findAll('ContentProtection'):
        if cp.get('schemeIdUri') == 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed':
            pssh_tag = cp.find('cenc:pssh')
            if pssh_tag:
                return pssh_tag.text
    return None

def get_single(token, channel_id):
    headers = {'authorization': 'Bearer ' + token, 'origin': 'https://v2.videoland.com', 'referer': 'https://v2.videoland.com/', 'user-agent': user_agent, 'x-client-release': '5.85.12', 'x-customer-name': 'rtlnl'}
    response = req.get(f'https://layout.videoland.bedrock.tech/front/v1/rtlnl/m6group_web/main/token-web-4/live/{channel_id}/layout', params={'nbPages': '2'}, headers=headers)
    data = response.json()
    for b in data.get('blocks', []):
        if b.get('featureId') == 'live_for_player':
            for a in b['content']['items'][0]['itemContent']['video']['assets']:
                if a.get('format') == 'dashcenc' and a.get('drm', {}).get('type') == 'software':
                    return a['path'], a['drm']['config']['contentId']
    return None, None

def get_lic_token(token, content_id):
    decoded = jwt.decode(token, options={"verify_signature": False})
    headers = {'authorization': 'Bearer ' + token, 'origin': 'https://v2.videoland.com', 'referer': 'https://v2.videoland.com/', 'user-agent': user_agent, 'x-client-release': '5.85.12', 'x-customer-name': 'rtlnl'}
    response = req.get(f'https://drm.videoland.bedrock.tech/v1/customers/rtlnl/platforms/m6group_web/services/videoland_root/users/{decoded["gigya"]}/live/{content_id}/upfront-token', headers=headers)
    return response.json().get('token', '')

def get_channels():
    headers = {'referer': 'https://www.videoland.com/', 'user-agent': user_agent}
    response = req.get('https://v2.videoland.com/videoland/live-videoland-f_206', headers=headers, cookies={'autologin': 'true'})
    r_str = response.content.decode().split('root.__dehydratedState = "')[-1].split('";')[0].replace('\\"', '"')
    data = json.loads(r_str)
    r = []
    for k, s in data['service']['servicesById'].items():
        r += s.get('channels', [])
    return r

def get_gmid():
    headers = {'origin': 'https://www.videoland.com', 'referer': 'https://www.videoland.com/', 'user-agent': user_agent}
    params = {'apiKey': '4_hRanGnYDFjdiZQfh-ghhhg', 'pageURL': 'https://www.videoland.com/inloggen', 'sdk': 'js_latest', 'sdkBuild': '15791', 'format': 'json'}
    response = req.get('https://gigya-merge.videoland.com/accounts.webSdkBootstrap', params=params, headers=headers)
    return response.cookies.get_dict()

def do_login_request(username, pwd):
    cookies = get_gmid()
    headers = {'content-type': 'application/x-www-form-urlencoded', 'origin': 'https://www.videoland.com', 'referer': 'https://www.videoland.com/', 'user-agent': user_agent}
    data = {'loginID': username, 'password': pwd, 'sessionExpiration': '-2', 'targetEnv': 'jssdk', 'include': 'profile,data', 'includeUserInfo': 'true', 'lang': 'en', 'APIKey': '4_hRanGnYDFjdiZQfh-ghhhg', 'sdk': 'js_latest', 'authMode': 'cookie', 'pageURL': 'https://www.videoland.com/inloggen', 'sdkBuild': '15791', 'format': 'json'}
    response = req.post('https://gigya-merge.videoland.com/accounts.login', cookies=cookies, headers=headers, data=data)
    resp_data = response.json()
    return cookies['gmid'], resp_data['sessionInfo']['login_token']

def get_account_info(gmid, login_token):
    headers = {'content-type': 'application/x-www-form-urlencoded', 'origin': 'https://v2.videoland.com', 'referer': 'https://v2.videoland.com/', 'user-agent': user_agent}
    data = {'include': 'profile,data', 'lang': 'nl', 'APIKey': '4_hRanGnYDFjdiZQfh-ghhhg', 'sdk': 'js_latest', 'login_token': login_token, 'authMode': 'cookie', 'pageURL': 'https://v2.videoland.com/welcome', 'sdkBuild': '15791', 'format': 'json'}
    response = req.post('https://gigya-merge.videoland.com/accounts.getAccountInfo', cookies={'gmid': gmid}, headers=headers, data=data)
    d = response.json()
    return d['UID'], d['UIDSignature'], d['signatureTimestamp']

def get_jwt(uid, signature, signature_timestamp, device_id, profile_id=None):
    headers = {'X-Auth-gigya-uid': uid, 'X-Auth-gigya-signature': signature, 'X-Auth-gigya-signature-timestamp': signature_timestamp, 'x-auth-device-id': device_id, 'X-Client-Release': '5.85.12', 'X-Customer-Name': 'rtlnl', 'User-Agent': user_agent, 'Referer': 'https://v2.videoland.com/'}
    if profile_id:
        headers['X-Auth-profile-id'] = profile_id
    response = req.get('https://front-auth.videoland.bedrock.tech/v2/platforms/m6group_web/getJwt', headers=headers)
    return response.json().get('token', '')

def get_profile_id(token):
    decoded = jwt.decode(token, options={"verify_signature": False})
    headers = {'authorization': 'Bearer ' + token, 'origin': 'https://v2.videoland.com', 'referer': 'https://v2.videoland.com/', 'user-agent': user_agent, 'x-client-release': '5.85.12', 'x-customer-name': 'rtlnl'}
    response = req.get(f'https://users.videoland.bedrock.tech/v2/platforms/m6group_web/users/{decoded["gigya"]}/profiles', headers=headers)
    return response.json()[0]['uid']

def get_token(gmid, login_token, device_id):
    uid, signature, signature_timestamp = get_account_info(gmid, login_token)
    token = get_jwt(uid, signature, signature_timestamp, device_id)
    profile_id = get_profile_id(token)
    return get_jwt(uid, signature, signature_timestamp, device_id, profile_id)

def login():
    print("logging in...", file=sys.stderr)
    device_id = '_luid_' + str(uuid.uuid4())
    gmid, login_token = do_login_request(user, password)
    auth_data = {'gmid': gmid, 'loginToken': login_token, 'deviceId': device_id}
    json.dump(auth_data, open(os.path.abspath(os.path.dirname(__file__)) + authFile, 'w'))
    print("logged in successfully", file=sys.stderr)

def do_action():
    if action == "login":
        login()
        sys.exit()
    try:
        auth = json.load(open(os.path.abspath(os.path.dirname(__file__)) + authFile))
        token = get_token(auth['gmid'], auth['loginToken'], auth['deviceId'])
    except:
        return "error"

    if action == "channels":
        output = {'Channels': []}
        for c in get_channels():
            output['Channels'].append({'Name': c['title'], 'Mode': "live", 'SessionManifest': True, 'ManifestScript': 'id=' + c['code_url'], 'CdmType': "widevine", 'UseCdm': True, 'Cdm': 'id=' + c['code_url'], 'Video': 'best'})
        print(json.dumps(output, indent=2))
    elif action == "events":
        output = {'Events': []}
        for c in get_channels():
            output['Events'].append({'Name': c['title'], 'Mode': "live", 'SessionManifest': True, 'ManifestScript': 'id=' + c['code_url'], 'CdmType': "widevine", 'UseCdm': True, 'Cdm': 'id=' + c['code_url'], 'Video': 'best', 'Autostart': True, 'Start': int(datetime.datetime.now(pytz.UTC).timestamp()), 'End': int((datetime.datetime.now(pytz.UTC) + datetime.timedelta(hours=4)).timestamp())})
        print(json.dumps(output, indent=2))
    elif action == "manifest":
        channel_id = id.replace('id=', '') if id.startswith('id=') else id
        url, content_id = get_single(token, channel_id)
        pssh_data = get_pssh_from_mpd(url)
        lic_token = get_lic_token(token, content_id)
        output = {"Cdn": [], "ManifestUrl": url, "Headers": {"Manifest": {'User-Agent': user_agent}, "Media": {'User-Agent': user_agent}}, "LicenseToken": lic_token}
        if pssh_data:
            output['Pssh'] = pssh_data
        print(json.dumps(output))
    elif action == "cdm" and cdm == "external":
        channel_id = id.replace('id=', '') if id.startswith('id=') else id
        url, content_id = get_single(token, channel_id)
        pssh_data = get_pssh_from_mpd(url)
        if pssh_data:
            lic_token = get_lic_token(token, content_id)
            for key in do_cdm_external(pssh_data, lic_token):
                print(key)

if do_action() == "error":
    login()
    do_action()
