summaryrefslogtreecommitdiff
path: root/plugin.video.mlbbasesloaded/mlbtv_stream_api.py
blob: 34c97bdb9d8c6ba91fedd62266fffadc535d5d43 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import requests
import mlbtv_session
import mlb_exceptions
from xbmcswift2 import Plugin, xbmcgui, xbmcaddon, xbmc
import datetime
import time
from globals import *
import sys
import re

plugin = Plugin()
session = mlbtv_session.MlbTvSession()
settings = xbmcaddon.Addon(id='plugin.video.mlbbasesloaded')

def get_stream(home_team, away_team):
    # from grid_ce.json get calendar_event_id (event_id) and id (content_id)
    # and ['game_media']['homebase']['media']
    url = 'http://gdx.mlb.com/components/game/mlb/{0}/grid_ce.json'.format(datetime.datetime.today().strftime(u'year_%Y/month_%m/day_%d'))
    streams = requests.get(url).json()

    stream_to_goto = None
    for stream in streams['data']['games']['game']:
        if stream['home_name_abbrev'] == home_team and stream['away_name_abbrev'] == away_team:
            stream_to_goto = stream
            break

    if stream_to_goto is None:
        plugin.notify("Can't find stream for game between {0} and {1}".format(away_team, home_team))
        return

    event_id = stream_to_goto['calendar_event_id']
    try:
        # Searching for type = 'mlbtv_home' or 'mlbtv_away' will allow choosing
        # which stream to take. Maybe use broadcast rankings to determine?
        content_id = stream_to_goto['game_media']['homebase']['media'][0]['id']
    except KeyError:
        raise mlb_exceptions.StreamNotFoundException()

    cookies = session.get_cookies()
    identity_point_id = cookies['ipid']
    fingerprint = cookies['fprt']

    try:
        session_key = get_session_key(identity_point_id, fingerprint, event_id, content_id)

        if not session_key or session_key == 'blackout':
            raise mlb_exceptions.StreamNotFoundException()

        url = get_url(identity_point_id, fingerprint, content_id, session_key, event_id)
        return url
    except mlb_exceptions.SignOnRestrictionException:
        msg = "You've made too many requests to MLB.tv. Please wait some time and try again."
        dialog = xbmcgui.Dialog()
        ok = dialog.ok('Too many usage attempts', msg)
        sys.exit()


def get_url(identity_point_id, fingerprint, content_id, session_key, event_id):
    url = 'https://mlb-ws-mf.media.mlb.com/pubajaxws/bamrest/MediaService2_0/op-findUserVerifiedEvent/v-2.3'
    params = {
        'identityPointId': identity_point_id,
        'fingerprint': fingerprint,
        'contentId': content_id,
        'eventId': event_id,
        'playbackScenario': 'HTTP_CLOUD_WIRED_60',  # TODO ?
        'subject': 'LIVE_EVENT_COVERAGE',
        'platform': 'PS4',
        'sessionKey': session_key,
        'format': 'json'
    }

    headers = {
        'Accept': '*/*',
        'Accept-Encoding': 'deflate',
        'Accept-Language': 'en-US,en;q=0.8',
        'Connection': 'keep-alive',
        'User-Agent': UA_PS4
    }

    s = requests.Session()
    s.cookies = session.get_cookies()
    r = s.get(url, params=params, headers=headers).json()
    xbmc.log("API call {0}\n{1}\n{2}\n{3}".format(url, params, headers, session.get_cookies()))
    if r['status_code'] != 1:
        xbmc.log("{0}".format(r))
        if r['status_code'] == -3500:
            raise mlb_exceptions.SignOnRestrictionException()
        raise mlb_exceptions.StreamNotFoundException()
    else:
        # Check if blacked out
        if r['user_verified_event'][0]['user_verified_content'][0]['user_verified_media_item'][0]['blackout_status'] != 'SuccessStatus':
            raise mlb_exceptions.StreamNotFoundException()

        xbmc.log("get_url cookies response {0}".format(s.cookies))
        session.save_cookies(s.cookies)

        # Update session_key
        settings.setSetting(id='session_key', value=r['session_key'])

        base_url = r['user_verified_event'][0]['user_verified_content'][0]['user_verified_media_item'][0]['url']
        best_quality = _best_quality_for_stream(base_url)

        media_auth = s.cookies['mediaAuth']
        url = "{0}|User-Agent={1}&Cookie=mediaAuth={2}".format(base_url, UA_PS4, media_auth)
        url = url.replace('master_wired60.m3u8', "{0}K/{0}_complete.m3u8".format(best_quality))
        return url

def _best_quality_for_stream(base_stream):
    raw_result = requests.get(base_stream).text
    lines = raw_result.split('\n')
    lines_with_stream_quality = [line for line in lines if 'complete.m3u8' in line]
    # Extract quality from these strings
    # Converts e.g. [u'1800K', u'800K', u'1200K', u'2500K', u'3500K', u'5000K'] -> [1800, 800, 1200, 2500, 3500, 5000]
    stream_qualities = [int(re.search(r'(\d.+?)K', line).group(1)) for line in lines_with_stream_quality]
    return max(stream_qualities)

def get_session_key(identity_point_id, fingerprint, event_id, content_id):
    session_key = str(settings.getSetting(id="session_key"))
    if session_key:
        return session_key

    url = 'https://mlb-ws-mf.media.mlb.com/pubajaxws/bamrest/MediaService2_0/op-findUserVerifiedEvent/v-2.3'
    params = {
        'identityPointId': identity_point_id,
        'fingerprint': fingerprint,
        'eventId': event_id,
        'subject': 'LIVE_EVENT_COVERAGE',
        'platform': 'WIN8',
        '_': str(int(round(time.time()*1000))),
        'format': 'json',
        'frameworkURL': 'https://mlb-ws-mf.media.mlb.com&frameworkEndPoint=/pubajaxws/bamrest/MediaService2_0/op-findUserVerifiedEvent/v-2.3'
    }

    headers = {
        'Accept': '*/*',
        'Accept-Encoding': 'deflate',
        'Accept-Language': 'en-US,en;q=0.8',
        'Connection': 'keep-alive',
        'User-Agent': UA_PC,
        'Origin': 'http://m.mlb.com',
        'Referer': 'http://m.mlb.com/tv/e{0}/v{1}/?&media_type=video&clickOrigin=Media Grid&team=mlb&forwardUrl=http://m.mlb.com/tv/e{0}/v{1}/?&media_type=video&clickOrigin=Media%20Grid&team=mlb&template=mp5default&flowId=registration.dynaindex&mediaTypeTemplate=video'.format(event_id, content_id)
    }

    xbmc.log("API call {0}\n{1}\n{2}\n{3}".format(url, params, headers, session.get_cookies()))
    s = requests.Session()
    s.cookies = session.get_cookies()
    r = s.get(url, params=params, headers=headers).json()
    if 'session_key' not in r or not r['session_key']:
        xbmc.log("Couldn't find session key: {0}".format(r))
        if r['status_code'] == -3500:
            raise mlb_exceptions.SignOnRestrictionException()
        else:
            return ''
    else:
        xbmc.log("get_session_key cookies response {0}".format(s.cookies))
        session.save_cookies(s.cookies)
        session_key = r['session_key']
        xbmc.log("Session key: {0}".format(session_key))
        settings.setSetting(id='session_key', value=session_key)
        return session_key