SlingTV-Stream-Extractor/slingtv.py

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)