196 lines
7.3 KiB
Python
196 lines
7.3 KiB
Python
import base64
|
|
import requests
|
|
import json
|
|
import jwt
|
|
import os
|
|
import m3u8
|
|
import signal
|
|
import sys
|
|
from pywidevine.cdm import Cdm
|
|
from pywidevine.device import Device
|
|
from pywidevine.pssh import PSSH
|
|
from pywidevine.license_protocol_pb2 import SignedMessage
|
|
|
|
#Config
|
|
WVD_PATH = './WVD.wvd'
|
|
|
|
def ascii_clear():
|
|
os.system('cls||clear')
|
|
print("""
|
|
:: .YY7^
|
|
:^:. ~: :5Y7. :PPPJ:
|
|
!5PPP5J. !PJ: !PP5: ?PPP5.
|
|
....... ^PPPPPPG? .PG? :PPP? !PPPP~
|
|
?JJJJJ7 .?PPPPP5^ !P5^ !PPP^ ?PPP5:
|
|
?JJJJY7 :~!!^ .!~. :55J: :PPPY^
|
|
?JJJJY7 ^^. :Y5J~
|
|
?JJJJY7 .:
|
|
.^~!777777!~^:. ?JJJJY7 ~?????7 ^?????7. .^!77??7!~^. :^!77??77~^. ^!!!!!~
|
|
^7JYYYYYYYYYYYYJJ7^ ?JJJJY7 7YJJJY? ~YJJJYJ~7JYYYYYJJYYYJ!. .~?JYYYJYYYYYJJ~7YYYYYY
|
|
!YYJJJJ??777?JJYYYJ^ ?JJJJY7 7YJJJJ? ~YJJJJJJYYJ???JJYJJJJYJ: ^?YJJJJYJJ???JJYYJJJJJJJ
|
|
^YJJJJJ^ .:^7?: ?JJJJY7 7YJJJJ? ~YJJJJJJ7^. .:7JJJJJY? ^JJJJJJJ!:. .:7JJJJJJJJ
|
|
:JJJJJJ?!^^:.. ?JJJJY7 7YJJJJ? ~YJJJJJ! 7JJJJJJ. .JJJJJJ?. ~JJJJJJJ
|
|
^JYYJJJYYJJJJ?7!^: ?JJJJY7 7YJJJJ? ~YJJJJJ. ~YJJJJJ. ~YJJJJY^ 7JJJJJJ
|
|
.^7?JJJYYYYYJJYYJ?^ ?JJJJY7 7YJJJJ? ~YJJJJJ. !YJJJJJ. ~YJJJJY^ 7YJJJJJ
|
|
.:^^~!7?JJJJJJY~ ?JJJJY7 7YJJJJ? ~YJJJJJ. !YJJJJJ. ^YJJJJJ7 :JJJJJJJ
|
|
^^. .!JJJJJ? ?JJJJY7 7YJJJJ? ~YJJJJJ. !YJJJJJ. 7YJJJJJ?^. .~JJJJJJJJ
|
|
.7YJJ7!~^:::::~?JJJJY! ?JJJJY7 7YJJJJ? ~YJJJJJ. !YJJJJJ. .7YJJJJJYJ?777?JYJJJJJJJJ
|
|
7JYYYYYYYJJJJJYJJJYJ! ?JJJJY7 7YJJJJ? ~YJJJJJ. !YJJJJJ. ~?YYJJJJJJYYYYY??JJJJJJ
|
|
.^!?JJJJYYYYJJJ?7~. ?JJJJJ7 !JJJJJ? ~JJJJJJ. ~JJJJJ?. .^7?JJJYYJJJ7^ 7YJJJJJ
|
|
..::^^^^::. ....... ....... ...... ...... .:^^^::. .JJJJJJ?
|
|
:7~: .~JJJJJJJ:
|
|
~JYYJ?7!!~~!!7JYJJJJY?:
|
|
.!?JJJYYYYYYYYYJJJJJ7^
|
|
:~!7?JJJJJJJ?7!^.
|
|
Stream Extractor
|
|
TAJLN 2023
|
|
|
|
""")
|
|
|
|
def signal_handler(sig, frame):
|
|
print('\nBye :)')
|
|
sys.exit(0)
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
|
|
def do_cdm(pssh, token):
|
|
channel_id = jwt.decode(token, options={"verify_signature": False})['channel_guid']
|
|
|
|
pssh = PSSH(pssh)
|
|
|
|
device = Device.load(WVD_PATH)
|
|
|
|
cdm = Cdm.from_device(device)
|
|
|
|
session_id = cdm.open()
|
|
|
|
challenge = cdm.get_license_challenge(session_id, pssh)
|
|
|
|
c_array = []
|
|
|
|
for c in challenge:
|
|
c_array.append(c)
|
|
|
|
headers = {
|
|
'Authorization': 'Bearer ' + token,
|
|
'Env': 'production',
|
|
'Channel-Id': channel_id,
|
|
'Content-Type': 'application/json',
|
|
}
|
|
|
|
data = {
|
|
"message": c_array
|
|
}
|
|
|
|
data = json.dumps(data)
|
|
|
|
licence = requests.post("https://p-drmwv.movetv.com/widevine/proxy", headers=headers, data=data)
|
|
licence.raise_for_status()
|
|
|
|
cdm.parse_license(session_id, licence.content)
|
|
|
|
for key in cdm.get_keys(session_id):
|
|
if key.type != 'SIGNING':
|
|
print(f"\nDRM key: {key.kid.hex}:{key.key.hex()}")
|
|
|
|
cdm.close(session_id)
|
|
|
|
def process_channel(channel, token):
|
|
|
|
subscriber_id = jwt.decode(token, options={"verify_signature": False})['prof']
|
|
|
|
headers = {
|
|
'authorization': 'Bearer ' + token,
|
|
}
|
|
|
|
json_data = {
|
|
'os_version': '10',
|
|
'device_name': 'browser',
|
|
'os_name': 'Windows',
|
|
'brand': 'sling',
|
|
'subscriber_id': subscriber_id,
|
|
'qvt': 'https://cbd46b77.cdn.cms.movetv.com/playermetadata/sling/v1/api/channels/' + channel + '/current/schedule.qvt',
|
|
'drm': 'widevine',
|
|
'account_status': 'active',
|
|
'advertiser_id': None,
|
|
'support_mode': 'false',
|
|
'ssai_vod': 'true',
|
|
'ssai_dvr': 'true',
|
|
}
|
|
|
|
response = json.loads(requests.post('https://p-streamauth.movetv.com/stream/auth', headers=headers, json=json_data).content)
|
|
|
|
temp_token = response['jwt']
|
|
m3u8_url = response['m3u8_url']
|
|
ssai_manifest = response['ssai_manifest'].split('?')[0]
|
|
|
|
m3u8_obj = m3u8.load(m3u8_url)
|
|
for key in m3u8_obj.session_keys:
|
|
pssh = key.uri.split(',')[-1]
|
|
|
|
print('\nm3u8 URL: ' + ssai_manifest)
|
|
do_cdm(pssh, temp_token)
|
|
|
|
def get_channels(token):
|
|
headers = {
|
|
'authorization': 'Bearer ' + token,
|
|
'client-config': 'rn-client-config',
|
|
'client-version': '4.32.20',
|
|
'content-type': 'application/json; charset=UTF-8',
|
|
'device-model': 'Chrome',
|
|
'dma': '501',
|
|
'features': 'use_ui_4=true,inplace_reno_invalidation=true,gzip_response=true,enable_extended_expiry=true,enable_home_channels=true,enable_iap=true,enable_trash_franchise_iview=false,browse-by-service-ribbon=true,subpack-hub-view=true,entitled_streaming_hub=false,add-premium-channels=false,enable_home_sports_scores=false,enable-basepack-ribbon=true',
|
|
'page_size': 'large',
|
|
'player-version': '7.6.2',
|
|
'response-config': 'ar_browser_1_1',
|
|
'sling-interaction-id': '596bd797-90f1-440f-bc02-e6f588dae8f6',
|
|
'timezone': 'America/Los_Angeles',
|
|
}
|
|
|
|
response = json.loads(requests.get('https://p-cmwnext.movetv.com/pres/grid_guide_a_z', headers=headers).content)
|
|
|
|
special_ribbons = response['special_ribbons']
|
|
|
|
tiles = special_ribbons[0]['tiles']
|
|
|
|
channels = []
|
|
|
|
for t in tiles:
|
|
r = {}
|
|
r['title'] = t['title']
|
|
r['code'] = t['href'].split('/')[-1]
|
|
channels.append(r)
|
|
|
|
return channels
|
|
|
|
def extract_token():
|
|
f = open("user.txt", "r")
|
|
|
|
user = json.loads(json.loads(f.read())['user'])
|
|
|
|
return user['userData']['jwt']
|
|
|
|
ascii_clear()
|
|
|
|
try:
|
|
token = extract_token()
|
|
except:
|
|
print('Failed getting token')
|
|
print('Did you copy "persist:root" local storage variable from browser to user.txt?')
|
|
quit()
|
|
|
|
input('Press enter to fetch channel list')
|
|
|
|
ascii_clear()
|
|
|
|
channels = get_channels(token)
|
|
i = 1
|
|
for c in channels:
|
|
print(str(i) + '. ' + c['title'])
|
|
i+=1
|
|
|
|
choice = int(input('\nChoose channel: ')) - 1
|
|
|
|
ascii_clear()
|
|
print(channels[choice]['title'])
|
|
process_channel(channels[choice]['code'], token) |