#!/usr/bin/python3
import sys
import os
import o11
import json
import datetime
import pytz
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 = '/MeoGo_' + user + '.tokens'
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'
device_id = '5381af54-1ad7-44e7-becd-7749bd06a9df'

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 = {'accept': '*/*', 'content-type': 'application/octet-stream', 'nv-authorizations': lic_token, 'origin': 'https://meogo.meo.pt', 'priority': 'u=1, i', 'referer': 'https://meogo.meo.pt/', 'user-agent': user_agent}
    licence = req.post('https://meop1jf4.anycast.nagra.com/MEOP1JF4/wvls/contentlicenseservice/v1/licenses', 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_from_mpd(url):
    headers = {'Origin': 'https://meogo.meo.pt', 'Referer': 'https://meogo.meo.pt/', '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_channels():
    headers = {'Origin': 'https://meogo.meo.pt', 'Referer': 'https://meogo.meo.pt/', 'User-Agent': user_agent}
    params = {'channelsNumber': '0', 'random': 'false', 'userAgent': 'PC_CHROME', 'onlyGAs': 'false'}
    response = req.get('https://meogouser.apps.meo.pt/Services/MeoGo/MeoGo.svc/GetContentsForChannels', params=params, headers=headers)
    return response.json().get('Result', [])

def get_single(token, channel_id):
    headers = {'authorization': 'bearer ' + token, 'content-type': 'application/json;charset=utf-8', 'origin': 'https://meogo.meo.pt', 'referer': 'https://meogo.meo.pt/', 'user-agent': user_agent}
    params = {'userAgent': 'PC_CHROME', 'outputTemplateName': 'SilverlightPlayerSettings', 'includeKeyId': 'true'}
    response = req.get(f'https://ott.online.meo.pt/media/v8/resolve/LiveChannel/{channel_id}/Feature', params=params, headers=headers)
    data = response.json()
    playlist = data.get('Playlist', [{}])[0]
    key_id = ''
    for ck in playlist.get('ContentKeys', []):
        if ck.get('ContentKeyType') == 'CENC':
            key_id = ck.get('KeyId', '')
    return playlist.get('MediaSourceUri', ''), key_id

def get_lic_token(token, channel_id, key_id):
    headers = {'authority': 'amsdrmtoken.online.meo.pt', 'authorization': 'bearer ' + token, 'content-type': 'application/json;charset=utf-8', 'origin': 'https://meogo.meo.pt', 'referer': 'https://meogo.meo.pt/', 'user-agent': user_agent}
    params = {'UserAgent': 'PC_CHROME', 'drmType': 'Widevine', 'assetType': 'LiveChannel'}
    response = req.get(f'https://cpts.online.meo.pt/sts/nagra/contentauthz/{channel_id}/{key_id}/{device_id}', params=params, headers=headers)
    return response.content.decode().replace('"', '')

def check_token(token):
    headers = {'authorization': 'bearer ' + token, 'content-type': 'application/json;charset=utf-8', 'origin': 'https://meogo.meo.pt', 'referer': 'https://meogo.meo.pt/', 'user-agent': user_agent}
    params = {'includePlayWarnings': 'true', 'userAgent': 'PC_CHROME', 'afterSubscription': 'false'}
    response = req.get('https://ott.online.meo.pt/media/v9/individualize/LiveChannel/RTP1_HD/Feature', params=params, headers=headers)
    response.raise_for_status()

def do_login_request(username, pwd):
    headers = {'Authorization': 'Basic eUQ5MWpZRXUxYjpDRDM1MlEydXVLMVBSamxKY1M2', 'User-agent': 'Mozilla/5.0 (Linux; Android 9; SM-G955F Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/120.0.6099.210 Mobile Safari/537.36', 'Client-Version': 'and-3.22.1', 'Content-Type': 'application/x-www-form-urlencoded', 'Accept-Encoding': 'gzip'}
    data = f'grant_type=password&username={username}&password={pwd}&idp=PTCLIENTE&redirect_uri=https://online.meo.pt/Android'
    response = req.post('https://login.telecom.pt/AuthorizeService/Token', headers=headers, data=data)
    return response.json().get('access_token', '')

def login():
    print("logging in...", file=sys.stderr)
    token = do_login_request(user, password)
    auth_data = {'token': token}
    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 = auth['token']
    check_token(token)
    return token

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

    if action == "channels":
        output = {'Channels': []}
        for c in get_channels():
            output['Channels'].append({'Name': c['Title'], 'Mode': "live", 'SessionManifest': True, 'ManifestScript': 'cid=' + c['FriendlyUrlName'], 'CdmType': "widevine", 'UseCdm': True, 'Cdm': 'cid=' + c['FriendlyUrlName'], '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': 'cid=' + c['FriendlyUrlName'], 'CdmType': "widevine", 'UseCdm': True, 'Cdm': 'cid=' + c['FriendlyUrlName'], '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('cid=', '') if id.startswith('cid=') else id
        url, key_id = get_single(token, channel_id)
        lic_token = get_lic_token(token, channel_id, key_id) if key_id else ''
        pssh_data = get_pssh_from_mpd(url) if url else None
        output = {"Cdn": [], "ManifestUrl": url, "Headers": {"Manifest": {'User-Agent': user_agent}, "Media": {'User-Agent': user_agent}}, "LicToken": lic_token, "KeyId": key_id}
        if pssh_data:
            output['Pssh'] = pssh_data
        print(json.dumps(output))
    elif action == "cdm" and cdm_param == "external":
        channel_id = id.replace('cid=', '') if id.startswith('cid=') else id
        url, key_id = get_single(token, channel_id)
        if url and key_id:
            lic_token = get_lic_token(token, channel_id, key_id)
            pssh_data = get_pssh_from_mpd(url)
            if pssh_data and lic_token:
                for key in do_cdm_external(pssh_data, lic_token):
                    print(key)

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