#!/usr/bin/python3
import sys
import os
import o11
import json
import datetime
import pytz
import base64
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_param = 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 = '/UFC_' + user + '.tokens'
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'

def find_wv_pssh_offsets(raw):
    offsets = []
    offset = 0
    while True:
        offset = raw.find(b'pssh', offset)
        if offset == -1:
            break
        size = int.from_bytes(raw[offset-4:offset], byteorder='big')
        pssh_offset = offset - 4
        offsets.append(raw[pssh_offset:pssh_offset+size])
        offset += size
    return offsets

def to_pssh(content):
    wv_offsets = find_wv_pssh_offsets(content)
    return [base64.b64encode(wv_offset).decode() for wv_offset in wv_offsets]

def init_to_pssh(init_url):
    headers = {'Accept': '*/*', 'User-Agent': user_agent}
    response = req.get(init_url, headers=headers)
    psshs = to_pssh(response.content)
    return psshs[-1] if psshs else None

def do_cdm_external(pssh_data, jwt_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 = {'authority': 'shield-drm.imggaming.com', 'accept': '*/*', 'authorization': 'Bearer ' + jwt_token, 'origin': 'https://ufcfightpass.com', 'referer': 'https://ufcfightpass.com/', 'user-agent': user_agent, 'x-drm-info': 'eyJzeXN0ZW0iOiJjb20ud2lkZXZpbmUuYWxwaGEifQ==', 'content-type': 'application/octet-stream'}
    licence = req.post('https://shield-drm.imggaming.com/api/v2/license', headers=lic_headers, data=challenge_data)
    cdm_obj.parse_license(session_id, licence.content)
    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_and_location(url):
    headers = {'Accept': '*/*', 'User-Agent': user_agent}
    response = req.get(url, headers=headers)
    location = response.url
    soup = BeautifulSoup(response.content, features="xml")
    init = soup.find('SegmentTemplate')['initialization']
    bandwidth = soup.find('Representation')['bandwidth']
    rep_id = soup.find('Representation')['id']
    base = soup.find('Representation').find('BaseURL').text
    loc_parts = location.split('/')
    loc_parts.pop()
    init_url = '/'.join(loc_parts) + '/' + base + '/' + init.replace('$Bandwidth$', bandwidth).replace('$RepresentationID$', rep_id)
    return init_to_pssh(init_url), location

def get_events(token):
    headers = {'authority': 'dce-frontoffice.imggaming.com', 'accept': 'application/json, text/plain, */*', 'app': 'dice', 'authorization': 'Bearer ' + token, 'content-type': 'application/json', 'origin': 'https://ufcfightpass.com', 'realm': 'dce.ufc', 'referer': 'https://ufcfightpass.com/', 'user-agent': user_agent, 'x-api-key': '857a1e5d-e35e-4fdf-805b-a87b6f8364bf', 'x-app-var': '6.0.1.109d795'}
    params = {'rpp': '15'}
    response = req.get('https://dce-frontoffice.imggaming.com/api/v2/event/live', params=params, headers=headers)
    return response.json().get('events', [])

def get_single(token, event_id):
    headers = {'Accept': 'application/json, text/plain, */*', 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json', 'Origin': 'https://ufcfightpass.com', 'Realm': 'dce.ufc', 'Referer': 'https://ufcfightpass.com/', 'User-Agent': user_agent, 'app': 'dice', 'x-api-key': '857a1e5d-e35e-4fdf-805b-a87b6f8364bf', 'x-app-var': '6.0.1.109d795'}
    params = {'displayGeoblockedLive': 'false', 'includePlaybackDetails': 'URL'}
    response = req.get('https://dce-frontoffice.imggaming.com/api/v4/event/' + event_id, params=params, headers=headers)
    data = response.json()
    url = data.get('playerUrlCallback', '')
    headers2 = {'authority': 'dge-streaming.imggaming.com', 'accept': '*/*', 'origin': 'https://ufcfightpass.com', 'referer': 'https://ufcfightpass.com/', 'user-agent': user_agent}
    response2 = req.get(url, params=params, headers=headers2)
    data2 = response2.json()
    if 'dashUrl' in data2:
        return data2['dashUrl'], data2.get('dash', {}).get('drm', {}).get('jwtToken', '')
    return data2.get('hlsUrl', ''), None

def refresh_token(auth_token, refresh_tok):
    headers = {'authority': 'dce-frontoffice.imggaming.com', 'accept': 'application/json, text/plain, */*', 'app': 'dice', 'authorization': 'Bearer ' + auth_token, 'content-type': 'application/json', 'origin': 'https://ufcfightpass.com', 'realm': 'dce.ufc', 'referer': 'https://ufcfightpass.com/', 'user-agent': user_agent, 'x-api-key': '857a1e5d-e35e-4fdf-805b-a87b6f8364bf', 'x-app-var': '6.0.1.109d795'}
    json_data = {'refreshToken': refresh_tok}
    response = req.post('https://dce-frontoffice.imggaming.com/api/v2/token/refresh', headers=headers, json=json_data)
    data = response.json()
    if data.get('status') == 429:
        return auth_token
    return data.get('authorisationToken', auth_token)

def init_login():
    headers = {'authority': 'dce-frontoffice.imggaming.com', 'accept': 'application/json, text/plain, */*', 'app': 'dice', 'content-type': 'application/json', 'origin': 'https://ufcfightpass.com', 'referer': 'https://ufcfightpass.com/', 'user-agent': user_agent, 'x-api-key': '857a1e5d-e35e-4fdf-805b-a87b6f8364bf', 'x-app-var': '6.0.1.109d795'}
    params = {'lk': 'language', 'pk': ['subTitleLanguage', 'audioLanguage', 'autoAdvance'], 'readLicences': 'true'}
    response = req.get('https://dce-frontoffice.imggaming.com/api/v1/init/', params=params, headers=headers)
    data = response.json()
    auth = data.get('authentication', {})
    return auth.get('authorisationToken', ''), auth.get('refreshToken', '')

def do_login_request(token, username, pwd):
    headers = {'authority': 'dce-frontoffice.imggaming.com', 'accept': 'application/json, text/plain, */*', 'app': 'dice', 'authorization': 'Bearer ' + token, 'cache-control': 'no-cache', 'content-type': 'application/json', 'origin': 'https://ufcfightpass.com', 'realm': 'dce.ufc', 'referer': 'https://ufcfightpass.com/', 'user-agent': user_agent, 'x-api-key': '857a1e5d-e35e-4fdf-805b-a87b6f8364bf', 'x-app-var': '6.0.1.109d795'}
    json_data = {'id': username, 'secret': pwd}
    response = req.post('https://dce-frontoffice.imggaming.com/api/v2/login', headers=headers, json=json_data)
    data = response.json()
    return data.get('authorisationToken', ''), data.get('refreshToken', '')

def login():
    print("logging in...", file=sys.stderr)
    init_token, init_refresh = init_login()
    token, refresh_tok = do_login_request(init_token, user, password)
    auth_data = {'token': token, 'refresh_token': refresh_tok}
    json.dump(auth_data, open(os.path.abspath(os.path.dirname(__file__)) + authFile, 'w'))
    print("logged in successfully", file=sys.stderr)

def get_auth():
    auth = json.load(open(os.path.abspath(os.path.dirname(__file__)) + authFile))
    token = refresh_token(auth['token'], auth['refresh_token'])
    return token

def do_action():
    if action == "login":
        login()
        sys.exit()
    try:
        token = get_auth()
    except:
        return "error"

    if action == "channels" or action == "events":
        output = {'Events' if action == "events" else 'Channels': []}
        for c in get_events(token):
            item = {'Name': c['title'].replace('/', ''), 'Mode': "live", 'SessionManifest': True, 'ManifestScript': 'eid=' + str(c['id']), 'CdmType': "widevine", 'UseCdm': True, 'Cdm': 'eid=' + str(c['id']), 'Video': 'best'}
            if action == "events":
                item['Autostart'] = True
                item['Start'] = int(datetime.datetime.now(pytz.UTC).timestamp())
                item['End'] = int((datetime.datetime.now(pytz.UTC) + datetime.timedelta(hours=4)).timestamp())
            output['Events' if action == "events" else 'Channels'].append(item)
        print(json.dumps(output, indent=2))
    elif action == "manifest":
        event_id = id.replace('eid=', '') if id.startswith('eid=') else id
        url, jwt_token = get_single(token, event_id)
        pssh_data, loc_url = get_pssh_and_location(url) if jwt_token else (None, url)
        output = {"Cdn": [], "ManifestUrl": loc_url if jwt_token else url, "Headers": {"Manifest": {'User-Agent': user_agent}, "Media": {'User-Agent': user_agent}}, "JwtToken": jwt_token}
        if pssh_data:
            output['Pssh'] = pssh_data
        print(json.dumps(output))
    elif action == "cdm" and cdm_param == "external":
        event_id = id.replace('eid=', '') if id.startswith('eid=') else id
        url, jwt_token = get_single(token, event_id)
        if jwt_token:
            pssh_data, loc_url = get_pssh_and_location(url)
            if pssh_data:
                for key in do_cdm_external(pssh_data, jwt_token):
                    print(key)

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