SlingTV-Stream-Extractor/slingtv.py

196 lines
7.3 KiB
Python
Raw Permalink Normal View History

2023-08-09 07:04:55 +00:00
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')
2023-08-09 07:12:25 +00:00
print('Did you copy "persist:root" local storage variable from browser to user.txt?')
2023-08-09 07:04:55 +00:00
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)