Esempio n. 1
0
class Account:
    addon = xbmcaddon.Addon()
    username = ''
    password = ''
    session_key = ''
    icon = os.path.join(addon.getAddonInfo('path'), 'icon.png')
    verify = False

    def __init__(self):
        self.username = self.addon.getSetting('username')
        self.password = self.addon.getSetting('password')
        self.session_key = self.addon.getSetting('session_key')
        self.util = Util()

    def login(self):
        # Check if username and password are provided
        if self.username == '':
            dialog = xbmcgui.Dialog()
            username = dialog.input('Please enter your username',
                                    type=xbmcgui.INPUT_ALPHANUM)
            self.addon.setSetting(id='username', value=username)

        if self.password == '':
            dialog = xbmcgui.Dialog()
            password = dialog.input('Please enter your password',
                                    type=xbmcgui.INPUT_ALPHANUM,
                                    option=xbmcgui.ALPHANUM_HIDE_INPUT)
            self.addon.setSetting(id='password', value=password)

        if self.username == '' or self.password == '':
            sys.exit()
        else:
            url = 'https://secure.mlb.com/pubajaxws/services/IdentityPointService'
            headers = {
                "SOAPAction":
                "http://services.bamnetworks.com/registration/identityPoint/identify",
                "Content-type": "text/xml; charset=utf-8",
                "User-Agent":
                "Dalvik/2.1.0 (Linux; U; Android 6.0.1; Hub Build/MHC19J)",
                "Connection": "Keep-Alive"
            }

            payload = "<?xml version='1.0' encoding='UTF-8'?>"
            payload += '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
            payload += '<SOAP-ENV:Body><tns:identityPoint_identify_request xmlns:tns="http://services.bamnetworks.com/registration/types/1.4">'
            payload += '<tns:identification type="email-password"><tns:id xsi:nil="true"/>'
            payload += '<tns:fingerprint xsi:nil="true"/>'
            payload += '<tns:email>'
            payload += '<tns:id xsi:nil="true"/>'
            payload += '<tns:address>' + self.username + '</tns:address>'
            payload += '</tns:email>'
            payload += '<tns:password>' + self.password + '</tns:password>'
            payload += '<tns:mobilePhone xsi:nil="true"/>'
            payload += '<tns:profileProperty xsi:nil="true"/>'
            payload += '</tns:identification>'
            payload += '</tns:identityPoint_identify_request>'
            payload += '</SOAP-ENV:Body>'
            payload += '</SOAP-ENV:Envelope>'

            r = requests.post(url,
                              headers=headers,
                              data=payload,
                              verify=self.verify)
            """
            Bad username => <status><code>-1000</code><message> [Invalid credentials for identification] [com.bamnetworks.registration.types.exception.IdentificationException: Account doesn't exits]</message><exceptionClass>com.bamnetworks.registration.types.exception.IdentificationException</exceptionClass><detail type="identityPoint" field="exists" message="false" messageKey="identityPoint.exists" /><detail type="identityPoint" field="email-password" message="identification error on identity point of type email-password" messageKey="identityPoint.email-password" /></status>
            Bad password => <status><code>-1000</code><message> [Invalid credentials for identification] [com.bamnetworks.registration.types.exception.IdentificationException: Invalid Password]</message><exceptionClass>com.bamnetworks.registration.types.exception.IdentificationException</exceptionClass><detail type="identityPoint" field="exists" message="true" messageKey="identityPoint.exists" /><detail type="identityPoint" field="email-password" message="identification error on identity point of type email-password" messageKey="identityPoint.email-password" /></status>
            Good => <status><code>1</code><message>OK</message></status>
            """
            if self.util.find(r.text, '<code>', '</code>') != '1':
                title = self.util.find(r.text, '<message> [', '] [')
                msg = self.util.find(
                    r.text,
                    'com.bamnetworks.registration.types.exception.IdentificationException: ',
                    ']</message>')
                dialog = xbmcgui.Dialog()
                dialog.ok(title, msg)
                sys.exit()
            else:
                self.util.save_cookies(r.cookies)

    def feature_service(self):
        if self.util.check_cookies():
            self.login()
        cookies = requests.utils.dict_from_cookiejar(self.util.load_cookies())
        url = 'https://secure.mlb.com/pubajaxws/services/FeatureService'
        headers = {
            "SOAPAction":
            "http://services.bamnetworks.com/registration/feature/findEntitledFeatures",
            "Content-type": "text/xml; charset=utf-8",
            "User-Agent":
            "Dalvik/2.1.0 (Linux; U; Android 6.0.1; Hub Build/MHC19J)",
            "Connection": "Keep-Alive"
        }

        payload = "<?xml version='1.0' encoding='UTF-8'?>"
        payload += '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">'
        payload += '<soapenv:Header />'
        payload += '<soapenv:Body>'
        payload += '<feature_findEntitled_request xmlns="http://services.bamnetworks.com/registration/types/1.6">'

        if 'ipid' in cookies and 'fprt' in cookies and self.session_key != '':
            payload += "<identification type='fingerprint'>"
            payload += '<id>' + cookies['ipid'] + '</id>'
            payload += '<fingerprint>' + cookies['fprt'] + '</fingerprint>'
            payload += "<signOnRestriction type='mobileApp'>"
            payload += '<location>ANDROID_21d994bd-ebb1-4253-bcab-3550e7882294</location>'
            payload += '<sessionKey>' + self.session_key + '</sessionKey>'
        else:
            payload += "<identification type='email-password'>"
            payload += '<email><address>' + self.username + '</address></email>'
            payload += '<password>' + self.password + '</password>'
            payload += '<signOnRestriction type="mobileApp">'
            payload += '<location>ANDROID_21d994bd-ebb1-4253-bcab-3550e7882294</location>'

        payload += '</signOnRestriction>'
        payload += '</identification>'
        payload += '<featureContextName>MLBTV2017.INAPPPURCHASE</featureContextName>'
        payload += '</feature_findEntitled_request>'
        payload += '</soapenv:Body>'
        payload += '</soapenv:Envelope>'

        r = requests.post(url,
                          headers=headers,
                          data=payload,
                          verify=self.verify)
        if self.util.find(r.text, '<code>', '</code>') != '1':
            title = self.util.find(r.text, '<message> [', '] [')
            msg = self.util.find(
                r.text,
                'com.bamnetworks.registration.types.exception.IdentificationException: ',
                ']</message>')
            dialog = xbmcgui.Dialog()
            dialog.ok(title, msg)
            sys.exit()
        else:
            self.session_key = self.util.find(r.text, '<sessionKey>',
                                              '</sessionKey>')
            self.addon.setSetting("session_key", self.session_key)
            self.util.save_cookies(r.cookies)

    def logout(self):
        self.util.delete_cookies()

    def media_entitlement(self):
        # check_cookies()
        self.feature_service()
        cookies = requests.utils.dict_from_cookiejar(self.util.load_cookies())
        url = 'https://media-entitlement.mlb.com/jwt'
        url += '?ipid=' + cookies['ipid']
        url += '&fingerprint=' + cookies['fprt']
        url += '&os=Android'
        url += '&appname=AtBat'
        headers = {
            'x-api-key': 'arBv5yTc359fDsqKdhYC41NZnIFZqEkY5Wyyn9uA',
            'Cache-Control': 'no-cache',
            'Connection': 'Keep-Alive',
            'User-Agent': 'okhttp/3.9.0'
        }

        r = requests.get(url,
                         headers=headers,
                         cookies=self.util.load_cookies(),
                         verify=self.verify)

        return r.text

    def access_token(self):
        url = 'https://edge.bamgrid.com/token'

        headers = {
            'Origin': 'https://www.mlb.com',
            'x-bamsdk-version': '3.0',
            'authorization':
            'Bearer bWxidHYmYnJvd3NlciYxLjAuMA.VfmGMGituFykTR89LFD-Gr5G-lwJ9QbHfXXNBMkuM9M',
            'content-type': 'application/x-www-form-urlencoded',
            'x-bamsdk-platform': 'windows',
            'accept': 'application/json',
            'User-Agent':
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36',
            'Referer':
            'https://www.mlb.com/tv/g529459/v8539eb7d-e8de-4b8d-84aa-d5026e632f36',
            'Accept-Encoding': 'gzip, deflate, br',
            'Accept-Language': 'en-US,en;q=0.9'
        }

        payload = 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange'
        payload += '&subject_token=' + self.media_entitlement()
        payload += '&subject_token_type=urn:ietf:params:oauth:token-type:jwt'
        payload += '&platform=browser'

        r = requests.post(url,
                          headers=headers,
                          data=payload,
                          cookies=self.util.load_cookies(),
                          verify=self.verify)
        access_token = r.json()['access_token']
        # refresh_token = r.json()['refresh_token']
        return access_token

    def get_playback_url(self, content_id):
        auth = self.access_token()
        url = 'https://search-api-mlbtv.mlb.com/svc/search/v2/graphql/persisted/query/core/Airings?variables=%7B%22contentId%22%3A%22' + content_id + '%22%7D'

        headers = {
            'Accept':
            'application/json',
            'Authorization':
            'Bearer ' + auth,
            'X-BAMSDK-Version':
            '3.0',
            'X-BAMSDK-Platform':
            'windows',
            'User-Agent':
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        }

        r = requests.get(url,
                         headers=headers,
                         cookies=self.util.load_cookies(),
                         verify=self.verify)
        if r.status_code != 200:
            dialog = xbmcgui.Dialog()
            title = "Error Occured"
            msg = ""
            for item in r.json()['errors']:
                msg += item['code'] + '\n'
            dialog.notification(title, msg, self.icon, 5000, False)
            sys.exit()

        return r.json()['data']['Airings'][0]['playbackUrls'][0]['href']

    def get_stream(self, content_id):
        auth = self.access_token()
        #url = 'https://edge.svcs.mlb.com/media/' + media_id + '/scenarios/browser~csai'
        #url = 'https://playback.svcs.mlb.com/events/ed5cc8a5-6fe5-4d36-b479-5097d77abdb6/media/' + media_id + '/scenarios/browser~csai'
        url = self.get_playback_url(content_id)
        url = url.replace('{scenario}', 'browser~csai')

        headers = {
            'Accept':
            'application/vnd.media-service+json; version=2',
            'Authorization':
            auth,
            'X-BAMSDK-Version':
            '3.0',
            'X-BAMSDK-Platform':
            'windows',
            'User-Agent':
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        }

        r = requests.get(url,
                         headers=headers,
                         cookies=self.util.load_cookies(),
                         verify=self.verify)
        if r.status_code != 200:
            dialog = xbmcgui.Dialog()
            title = "Error Occured"
            msg = ""
            for item in r.json()['errors']:
                msg += item['code'] + '\n'
            dialog.notification(title, msg, self.icon, 5000, False)
            sys.exit()

        if 'slide' in r.json()['stream']:
            stream_url = r.json()['stream']['slide']
        else:
            stream_url = r.json()['stream']['complete']

        if QUALITY == 'Always Ask':
            stream_url = self.get_stream_quality(stream_url)
        headers = 'User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        headers += '&Authorization=' + auth
        headers += '&Cookie='
        cookies = requests.utils.dict_from_cookiejar(self.util.load_cookies())
        for key, value in cookies.iteritems():
            headers += key + '=' + value + '; '

        #CDN
        akc_url = 'hlslive-aksc'
        l3c_url = 'hlslive-l3c'
        if CDN == 'Akamai' and akc_url not in stream_url:
            stream_url = stream_url.replace(l3c_url, akc_url)
        elif CDN == 'Level 3' and l3c_url not in stream_url:
            stream_url = stream_url.replace(akc_url, l3c_url)

        return stream_url, headers

    def get_stream_quality(self, stream_url):
        #Check if inputstream adaptive is on, if so warn user and return master m3u8
        if xbmc.getCondVisibility('System.HasAddon(inputstream.adaptive)'):
            dialog = xbmcgui.Dialog()
            title = 'Playback Conflict'
            msg = 'Always Ask stream quality will not work when inputstream adaptive is enabled. Either disable inputstream adaptive or switch stream quality to Best Available.'
            dialog.ok(title, msg)
            return stream_url

        stream_title = []
        stream_urls = []
        headers = {
            'User-Agent':
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        }

        r = requests.get(stream_url, headers=headers, verify=False)
        master = r.text

        line = re.compile("(.+?)\n").findall(master)

        for temp_url in line:
            if '#EXT' not in temp_url:
                match = re.search(r'(\d.+?)K', temp_url, re.IGNORECASE)
                bandwidth = match.group()
                if 0 < len(bandwidth) < 6:
                    bandwidth = bandwidth.replace('K', ' kbps')
                    stream_title.append(bandwidth)
                    stream_urls.append(temp_url)

        stream_title.sort(key=self.util.natural_sort_key, reverse=True)
        stream_urls.sort(key=self.util.natural_sort_key, reverse=True)
        dialog = xbmcgui.Dialog()
        ret = dialog.select('Choose Stream Quality', stream_title)
        if ret >= 0:
            if 'http' not in stream_urls[ret]:
                stream_url = stream_url.replace(
                    stream_url.rsplit('/', 1)[-1], stream_urls[ret])
            else:
                stream_url = stream_urls[ret]
        else:
            sys.exit()

        return stream_url
Esempio n. 2
0
class Account:
    addon = xbmcaddon.Addon()
    username = ''
    password = ''
    session_key = ''
    icon = os.path.join(addon.getAddonInfo('path'), 'icon.png')
    verify = True

    def __init__(self):
        self.username = self.addon.getSetting('username')
        self.password = self.addon.getSetting('password')
        self.session_key = self.addon.getSetting('session_key')
        self.did = self.device_id()
        self.util = Util()

    def device_id(self):
        if self.addon.getSetting('device_id') == '':
            self.addon.setSetting('device_id', str(uuid.uuid4()))

        return self.addon.getSetting('device_id')

    def login(self):
        # Check if username and password are provided
        if self.username == '':
            dialog = xbmcgui.Dialog()
            self.username = dialog.input('Please enter your username',
                                         type=xbmcgui.INPUT_ALPHANUM)
            self.addon.setSetting(id='username', value=self.username)

        if self.password == '':
            dialog = xbmcgui.Dialog()
            self.password = dialog.input('Please enter your password',
                                         type=xbmcgui.INPUT_ALPHANUM,
                                         option=xbmcgui.ALPHANUM_HIDE_INPUT)
            self.addon.setSetting(id='password', value=self.password)

        if self.username == '' or self.password == '':
            sys.exit()
        else:

            headers = {
                'User-Agent': 'okhttp/3.12.1',
                'Content-Type': 'application/x-www-form-urlencoded'
            }

            url = 'https://ids.mlb.com/oauth2/aus1m088yK07noBfh356/v1/token'
            payload = (
                'grant_type=password&username=%s&password=%s&scope=openid offline_access'
                '&client_id=0oa3e1nutA1HLzAKG356') % (self.username,
                                                      self.password)

            r = requests.post(url,
                              headers=headers,
                              data=payload,
                              verify=self.verify)
            login_token = r.json()['access_token']

            self.addon.setSetting('login_token', login_token)
            self.addon.setSetting('last_login', str(time.time()))

    def logout(self):
        self.util.delete_cookies()
        self.addon.setSetting('login_token', '')
        self.addon.setSetting('last_login', '')
        self.addon.setSetting('username', '')
        self.addon.setSetting('password', '')

    def media_entitlement(self):
        if self.addon.getSetting('last_login') == '' or \
                (time.time() - float(self.addon.getSetting('last_login')) >= 86400):
            self.login()

        url = 'https://media-entitlement.mlb.com/api/v3/jwt?os=Android&appname=AtBat&did=' + self.device_id(
        )
        headers = {
            'User-Agent': 'okhttp/3.12.1',
            'Authorization': 'Bearer ' + self.addon.getSetting('login_token')
        }

        r = requests.get(url, headers=headers, verify=self.verify)

        return r.text

    def access_token(self):
        url = 'https://us.edge.bamgrid.com/token'
        headers = {
            'Accept': 'application/json',
            'Authorization':
            'Bearer bWxidHYmYW5kcm9pZCYxLjAuMA.6LZMbH2r--rbXcgEabaDdIslpo4RyZrlVfWZhsAgXIk',
            'Content-Type': 'application/x-www-form-urlencoded'
        }
        payload = 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange&subject_token=%s' \
                  '&subject_token_type=urn:ietf:params:oauth:token-type:jwt&platform=android-tv' \
                  % self.media_entitlement()

        r = requests.post(url,
                          headers=headers,
                          data=payload,
                          verify=self.verify)
        access_token = r.json()['access_token']
        # refresh_token = r.json()['refresh_token']

        return access_token

    def get_playback_url(self, content_id):
        auth = self.access_token()
        url = 'https://search-api-mlbtv.mlb.com/svc/search/v2/graphql/persisted/query/core/Airings' \
              '?variables=%7B%22contentId%22%3A%22' + content_id + '%22%7D'

        headers = {
            'Accept':
            'application/json',
            'Authorization':
            'Bearer ' + auth,
            'X-BAMSDK-Version':
            'v4.3.0',
            'X-BAMSDK-Platform':
            'android-tv',
            'User-Agent':
            'BAMSDK/v4.3.0 (mlbaseball-7993996e 8.1.0; v2.0/v4.3.0; android; tv)'
        }

        r = requests.get(url,
                         headers=headers,
                         cookies=self.util.load_cookies(),
                         verify=self.verify)
        if r.status_code != 200:
            dialog = xbmcgui.Dialog()
            title = "Error Occured"
            msg = ""
            for item in r.json()['errors']:
                msg += item['code'] + '\n'
            dialog.notification(title, msg, self.icon, 5000, False)
            sys.exit()

        return auth, r.json()['data']['Airings'][0]['playbackUrls'][0]['href']

    def get_stream(self, content_id):
        auth, url = self.get_playback_url(content_id)
        url = url.replace('{scenario}', 'browser~csai')

        headers = {
            'Accept':
            'application/vnd.media-service+json; version=2',
            'Authorization':
            auth,
            'X-BAMSDK-Version':
            '3.0',
            'X-BAMSDK-Platform':
            'windows',
            'User-Agent':
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        }

        r = requests.get(url,
                         headers=headers,
                         cookies=self.util.load_cookies(),
                         verify=self.verify)
        if r.status_code != 200:
            dialog = xbmcgui.Dialog()
            title = "Error Occured"
            msg = ""
            for item in r.json()['errors']:
                msg += item['code'] + '\n'
            dialog.notification(title, msg, self.icon, 5000, False)
            sys.exit()

        if 'slide' in r.json()['stream']:
            stream_url = r.json()['stream']['slide']
        else:
            stream_url = r.json()['stream']['complete']

        if QUALITY == 'Always Ask':
            stream_url = self.get_stream_quality(stream_url)
        headers = 'User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        headers += '&Authorization=' + auth
        headers += '&Cookie='
        cookies = requests.utils.dict_from_cookiejar(self.util.load_cookies())
        for key, value in cookies.iteritems():
            headers += key + '=' + value + '; '

        #CDN
        akc_url = 'hlslive-aksc'
        l3c_url = 'hlslive-l3c'
        if CDN == 'Akamai' and akc_url not in stream_url:
            stream_url = stream_url.replace(l3c_url, akc_url)
        elif CDN == 'Level 3' and l3c_url not in stream_url:
            stream_url = stream_url.replace(akc_url, l3c_url)

        return stream_url, headers

    def get_stream_quality(self, stream_url):
        #Check if inputstream adaptive is on, if so warn user and return master m3u8
        if xbmc.getCondVisibility('System.HasAddon(inputstream.adaptive)'):
            dialog = xbmcgui.Dialog()
            title = 'Playback Conflict'
            msg = 'Always Ask stream quality will not work when inputstream adaptive is enabled. Either disable inputstream adaptive or switch stream quality to Best Available.'
            dialog.ok(title, msg)
            return stream_url

        stream_title = []
        stream_urls = []
        headers = {
            'User-Agent':
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        }

        r = requests.get(stream_url, headers=headers, verify=False)
        master = r.text

        line = re.compile("(.+?)\n").findall(master)

        for temp_url in line:
            if '#EXT' not in temp_url:
                match = re.search(r'(\d.+?)K', temp_url, re.IGNORECASE)
                bandwidth = match.group()
                if 0 < len(bandwidth) < 6:
                    bandwidth = bandwidth.replace('K', ' kbps')
                    stream_title.append(bandwidth)
                    stream_urls.append(temp_url)

        stream_title.sort(key=self.util.natural_sort_key, reverse=True)
        stream_urls.sort(key=self.util.natural_sort_key, reverse=True)
        dialog = xbmcgui.Dialog()
        ret = dialog.select('Choose Stream Quality', stream_title)
        if ret >= 0:
            if 'http' not in stream_urls[ret]:
                stream_url = stream_url.replace(
                    stream_url.rsplit('/', 1)[-1], stream_urls[ret])
            else:
                stream_url = stream_urls[ret]
        else:
            sys.exit()

        return stream_url
class Account:
    addon = xbmcaddon.Addon()
    username = ''
    password = ''
    session_key = ''
    icon = os.path.join(addon.getAddonInfo('path'), 'icon.png')
    verify = True

    def __init__(self):
        self.username = self.addon.getSetting('username')
        self.password = self.addon.getSetting('password')
        self.session_key = self.addon.getSetting('session_key')
        self.did = self.device_id()
        self.util = Util()

    def device_id(self):
        if self.addon.getSetting('device_id') == '':
            self.addon.setSetting('device_id', str(uuid.uuid4()))

        return self.addon.getSetting('device_id')

    def login(self):
        # Check if username and password are provided
        if self.username == '':
            dialog = xbmcgui.Dialog()
            self.username = dialog.input('Please enter your username', type=xbmcgui.INPUT_ALPHANUM)
            self.addon.setSetting(id='username', value=self.username)

        if self.password == '':
            dialog = xbmcgui.Dialog()
            self.password = dialog.input('Please enter your password', type=xbmcgui.INPUT_ALPHANUM,
                                    option=xbmcgui.ALPHANUM_HIDE_INPUT)
            self.addon.setSetting(id='password', value=self.password)

        if self.username == '' or self.password == '':
            sys.exit()
        else:

            headers = {'User-Agent': 'okhttp/3.12.1',
                       'Content-Type': 'application/x-www-form-urlencoded'
                       }

            url = 'https://ids.mlb.com/oauth2/aus1m088yK07noBfh356/v1/token'
            payload = ('grant_type=password&username=%s&password=%s&scope=openid offline_access'
                       '&client_id=0oa3e1nutA1HLzAKG356') % (self.username, self.password)

            r = requests.post(url, headers=headers, data=payload, verify=self.verify)
            login_token = r.json()['access_token']

            self.addon.setSetting('login_token', login_token)
            self.addon.setSetting('last_login', str(time.time()))

    def logout(self):
        self.util.delete_cookies()
        self.addon.setSetting('login_token', '')
        self.addon.setSetting('last_login', '')
        self.addon.setSetting('username', '')
        self.addon.setSetting('password', '')

    def media_entitlement(self):
        if self.addon.getSetting('last_login') == '' or \
                (time.time() - float(self.addon.getSetting('last_login')) >= 86400):
            self.login()

        url = 'https://media-entitlement.mlb.com/api/v3/jwt?os=Android&appname=AtBat&did=' + self.device_id()
        headers = {'User-Agent': 'okhttp/3.12.1',
                   'Authorization': 'Bearer ' + self.addon.getSetting('login_token')
                   }

        r = requests.get(url, headers=headers, verify=self.verify)

        return r.text

    def access_token(self):
        url = 'https://us.edge.bamgrid.com/token'
        headers = {'Accept': 'application/json',
                   'Authorization': 'Bearer bWxidHYmYW5kcm9pZCYxLjAuMA.6LZMbH2r--rbXcgEabaDdIslpo4RyZrlVfWZhsAgXIk',
                   'Content-Type': 'application/x-www-form-urlencoded'
                   }
        payload = 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange&subject_token=%s' \
                  '&subject_token_type=urn:ietf:params:oauth:token-type:jwt&platform=android-tv' \
                  % self.media_entitlement()

        r = requests.post(url, headers=headers, data=payload, verify=self.verify)
        access_token = r.json()['access_token']
        # refresh_token = r.json()['refresh_token']

        return access_token

    def get_playback_url(self, content_id):
        auth = self.access_token()
        url = 'https://search-api-mlbtv.mlb.com/svc/search/v2/graphql/persisted/query/core/Airings' \
              '?variables=%7B%22contentId%22%3A%22' + content_id + '%22%7D'

        headers = {
            'Accept': 'application/json',
            'Authorization': 'Bearer ' + auth,
            'X-BAMSDK-Version': 'v4.3.0',
            'X-BAMSDK-Platform': 'android-tv',
            'User-Agent': 'BAMSDK/v4.3.0 (mlbaseball-7993996e 8.1.0; v2.0/v4.3.0; android; tv)'
        }

        r = requests.get(url, headers=headers, cookies=self.util.load_cookies(), verify=self.verify)
        if r.status_code != 200:
            dialog = xbmcgui.Dialog()
            title = "Error Occured"
            msg = ""
            for item in r.json()['errors']:
                msg += item['code'] + '\n'
            dialog.notification(title, msg, self.icon, 5000, False)
            sys.exit()

        return auth, r.json()['data']['Airings'][0]['playbackUrls'][0]['href']

    def get_stream(self, content_id):
        auth, url = self.get_playback_url(content_id)
        url = url.replace('{scenario}','browser~csai')

        headers = {
            'Accept': 'application/vnd.media-service+json; version=2',
            'Authorization': auth,
            'X-BAMSDK-Version': '3.0',
            'X-BAMSDK-Platform': 'windows',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        }

        r = requests.get(url, headers=headers, cookies=self.util.load_cookies(), verify=self.verify)
        if r.status_code != 200:
            dialog = xbmcgui.Dialog()
            title = "Error Occured"
            msg = ""
            for item in r.json()['errors']:
                msg += item['code'] + '\n'
            dialog.notification(title, msg, self.icon, 5000, False)
            sys.exit()

        if 'slide' in r.json()['stream']:
            stream_url = r.json()['stream']['slide']
        else:
            stream_url = r.json()['stream']['complete']

        if QUALITY == 'Always Ask':
            stream_url = self.get_stream_quality(stream_url)
        headers = 'User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        headers += '&Authorization=' + auth
        headers += '&Cookie='
        cookies = requests.utils.dict_from_cookiejar(self.util.load_cookies())
        for key, value in cookies.iteritems():
            headers += key + '=' + value + '; '
            
        #CDN
        akc_url = 'hlslive-aksc'
        l3c_url = 'hlslive-l3c'
        if CDN == 'Akamai' and akc_url not in stream_url:
            stream_url = stream_url.replace(l3c_url, akc_url)
        elif CDN == 'Level 3' and l3c_url not in stream_url:
            stream_url = stream_url.replace(akc_url, l3c_url)
        
        return stream_url, headers

    def get_stream_quality(self, stream_url):
        #Check if inputstream adaptive is on, if so warn user and return master m3u8
        if xbmc.getCondVisibility('System.HasAddon(inputstream.adaptive)'):
            dialog = xbmcgui.Dialog()
            title = 'Playback Conflict'
            msg = 'Always Ask stream quality will not work when inputstream adaptive is enabled. Either disable inputstream adaptive or switch stream quality to Best Available.'
            dialog.ok(title, msg)
            return stream_url

        stream_title = []
        stream_urls = []
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'}

        r = requests.get(stream_url, headers=headers, verify=False)
        master = r.text

        line = re.compile("(.+?)\n").findall(master)

        for temp_url in line:
            if '#EXT' not in temp_url:
                match = re.search(r'(\d.+?)K', temp_url, re.IGNORECASE)
                bandwidth = match.group()
                if 0 < len(bandwidth) < 6:
                    bandwidth = bandwidth.replace('K', ' kbps')
                    stream_title.append(bandwidth)
                    stream_urls.append(temp_url)

        stream_title.sort(key=self.util.natural_sort_key, reverse=True)
        stream_urls.sort(key=self.util.natural_sort_key, reverse=True)
        dialog = xbmcgui.Dialog()
        ret = dialog.select('Choose Stream Quality', stream_title)
        if ret >= 0:
            if 'http' not in stream_urls[ret]:
                stream_url = stream_url.replace(stream_url.rsplit('/', 1)[-1], stream_urls[ret])
            else:
                stream_url = stream_urls[ret]
        else:
            sys.exit()

        return stream_url
Esempio n. 4
0
class Account:
    addon = xbmcaddon.Addon()
    username = ''
    password = ''
    session_key = ''
    icon = os.path.join(addon.getAddonInfo('path'), 'icon.png')
    verify = False

    def __init__(self):
        self.username = self.addon.getSetting('username')
        self.password = self.addon.getSetting('password')
        self.session_key = self.addon.getSetting('session_key')
        self.util = Util()

    def login(self):
        # Check if username and password are provided
        if self.username == '':
            dialog = xbmcgui.Dialog()
            username = dialog.input('Please enter your username', type=xbmcgui.INPUT_ALPHANUM)
            self.addon.setSetting(id='username', value=username)

        if self.password == '':
            dialog = xbmcgui.Dialog()
            password = dialog.input('Please enter your password', type=xbmcgui.INPUT_ALPHANUM, option=xbmcgui.ALPHANUM_HIDE_INPUT)
            self.addon.setSetting(id='password', value=password)

        if self.username == '' or self.password == '':
            sys.exit()
        else:
            url = 'https://secure.mlb.com/pubajaxws/services/IdentityPointService'
            headers = {
                "SOAPAction": "http://services.bamnetworks.com/registration/identityPoint/identify",
                "Content-type": "text/xml; charset=utf-8",
                "User-Agent": "Dalvik/2.1.0 (Linux; U; Android 6.0.1; Hub Build/MHC19J)",
                "Connection": "Keep-Alive"
            }

            payload = "<?xml version='1.0' encoding='UTF-8'?>"
            payload += '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
            payload += '<SOAP-ENV:Body><tns:identityPoint_identify_request xmlns:tns="http://services.bamnetworks.com/registration/types/1.4">'
            payload += '<tns:identification type="email-password"><tns:id xsi:nil="true"/>'
            payload += '<tns:fingerprint xsi:nil="true"/>'
            payload += '<tns:email>'
            payload += '<tns:id xsi:nil="true"/>'
            payload += '<tns:address>' + self.username + '</tns:address>'
            payload += '</tns:email>'
            payload += '<tns:password>' + self.password + '</tns:password>'
            payload += '<tns:mobilePhone xsi:nil="true"/>'
            payload += '<tns:profileProperty xsi:nil="true"/>'
            payload += '</tns:identification>'
            payload += '</tns:identityPoint_identify_request>'
            payload += '</SOAP-ENV:Body>'
            payload += '</SOAP-ENV:Envelope>'

            r = requests.post(url, headers=headers, data=payload, verify=self.verify)

            """
            Bad username => <status><code>-1000</code><message> [Invalid credentials for identification] [com.bamnetworks.registration.types.exception.IdentificationException: Account doesn't exits]</message><exceptionClass>com.bamnetworks.registration.types.exception.IdentificationException</exceptionClass><detail type="identityPoint" field="exists" message="false" messageKey="identityPoint.exists" /><detail type="identityPoint" field="email-password" message="identification error on identity point of type email-password" messageKey="identityPoint.email-password" /></status>
            Bad password => <status><code>-1000</code><message> [Invalid credentials for identification] [com.bamnetworks.registration.types.exception.IdentificationException: Invalid Password]</message><exceptionClass>com.bamnetworks.registration.types.exception.IdentificationException</exceptionClass><detail type="identityPoint" field="exists" message="true" messageKey="identityPoint.exists" /><detail type="identityPoint" field="email-password" message="identification error on identity point of type email-password" messageKey="identityPoint.email-password" /></status>
            Good => <status><code>1</code><message>OK</message></status>
            """
            if self.util.find(r.text, '<code>', '</code>') != '1':
                title = self.util.find(r.text, '<message> [', '] [')
                msg = self.util.find(r.text, 'com.bamnetworks.registration.types.exception.IdentificationException: ', ']</message>')
                dialog = xbmcgui.Dialog()
                dialog.ok(title, msg)
                sys.exit()
            else:
                self.util.save_cookies(r.cookies)

    def feature_service(self):
        if self.util.check_cookies():
            self.login()
        cookies = requests.utils.dict_from_cookiejar(self.util.load_cookies())
        url = 'https://secure.mlb.com/pubajaxws/services/FeatureService'
        headers = {
            "SOAPAction": "http://services.bamnetworks.com/registration/feature/findEntitledFeatures",
            "Content-type": "text/xml; charset=utf-8",
            "User-Agent": "Dalvik/2.1.0 (Linux; U; Android 6.0.1; Hub Build/MHC19J)",
            "Connection": "Keep-Alive"
        }

        payload = "<?xml version='1.0' encoding='UTF-8'?>"
        payload += '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">'
        payload += '<soapenv:Header />'
        payload += '<soapenv:Body>'
        payload += '<feature_findEntitled_request xmlns="http://services.bamnetworks.com/registration/types/1.6">'

        if 'ipid' in cookies and 'fprt' in cookies and self.session_key != '':
            payload += "<identification type='fingerprint'>"
            payload += '<id>' + cookies['ipid'] + '</id>'
            payload += '<fingerprint>' + cookies['fprt'] + '</fingerprint>'
            payload += "<signOnRestriction type='mobileApp'>"
            payload += '<location>ANDROID_21d994bd-ebb1-4253-bcab-3550e7882294</location>'
            payload += '<sessionKey>' + self.session_key + '</sessionKey>'
        else:
            payload += "<identification type='email-password'>"
            payload += '<email><address>' + self.username + '</address></email>'
            payload += '<password>' + self.password + '</password>'
            payload += '<signOnRestriction type="mobileApp">'
            payload += '<location>ANDROID_21d994bd-ebb1-4253-bcab-3550e7882294</location>'

        payload += '</signOnRestriction>'
        payload += '</identification>'
        payload += '<featureContextName>MLBTV2017.INAPPPURCHASE</featureContextName>'
        payload += '</feature_findEntitled_request>'
        payload += '</soapenv:Body>'
        payload += '</soapenv:Envelope>'

        r = requests.post(url, headers=headers, data=payload, verify=self.verify)
        if self.util.find(r.text, '<code>', '</code>') != '1':
            title = self.util.find(r.text, '<message> [', '] [')
            msg = self.util.find(r.text, 'com.bamnetworks.registration.types.exception.IdentificationException: ', ']</message>')
            dialog = xbmcgui.Dialog()
            dialog.ok(title, msg)
            sys.exit()
        else:
            self.session_key = self.util.find(r.text, '<sessionKey>', '</sessionKey>')
            self.addon.setSetting("session_key", self.session_key)
            self.util.save_cookies(r.cookies)

    def logout(self):
        self.util.delete_cookies()

    def media_entitlement(self):
        # check_cookies()
        self.feature_service()
        cookies = requests.utils.dict_from_cookiejar(self.util.load_cookies())
        url = 'https://media-entitlement.mlb.com/jwt'
        url += '?ipid=' + cookies['ipid']
        url += '&fingerprint=' + cookies['fprt']
        url += '&os=Android'
        url += '&appname=AtBat'
        headers = {
            'x-api-key': 'arBv5yTc359fDsqKdhYC41NZnIFZqEkY5Wyyn9uA',
            'Cache-Control': 'no-cache',
            'Connection': 'Keep-Alive',
            'User-Agent': 'okhttp/3.9.0'
        }

        r = requests.get(url, headers=headers, cookies=self.util.load_cookies(), verify=self.verify)

        return r.text

    def access_token(self):
        url = 'https://edge.bamgrid.com/token'

        headers = {
            'Origin': 'https://www.mlb.com',
            'x-bamsdk-version': '3.0',
            'authorization': 'Bearer bWxidHYmYnJvd3NlciYxLjAuMA.VfmGMGituFykTR89LFD-Gr5G-lwJ9QbHfXXNBMkuM9M',
            'content-type': 'application/x-www-form-urlencoded',
            'x-bamsdk-platform': 'windows',
            'accept': 'application/json',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36',
            'Referer': 'https://www.mlb.com/tv/g529459/v8539eb7d-e8de-4b8d-84aa-d5026e632f36',
            'Accept-Encoding': 'gzip, deflate, br',
            'Accept-Language': 'en-US,en;q=0.9'
        }

        payload = 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange'
        payload += '&subject_token=' + self.media_entitlement()
        payload += '&subject_token_type=urn:ietf:params:oauth:token-type:jwt'
        payload += '&platform=browser'

        r = requests.post(url, headers=headers, data=payload, cookies=self.util.load_cookies(), verify=self.verify)
        access_token = r.json()['access_token']
        # refresh_toekn = r.json()['refresh_token']
        return access_token

    def get_playback_url(self, content_id):
        auth = self.access_token()
        url ='https://search-api-mlbtv.mlb.com/svc/search/v2/graphql/persisted/query/core/Airings?variables=%7B%22contentId%22%3A%22' + content_id + '%22%7D'

        headers = {
            'Accept': 'application/json',
            'Authorization': 'Bearer ' + auth,
            'X-BAMSDK-Version': '3.0',
            'X-BAMSDK-Platform': 'windows',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        }

        r = requests.get(url, headers=headers, cookies=self.util.load_cookies(), verify=self.verify)
        if r.status_code != 200:
            dialog = xbmcgui.Dialog()
            title = "Error Occured"
            msg = ""
            for item in r.json()['errors']:
                msg += item['code'] + '\n'
            dialog.notification(title, msg, self.icon, 5000, False)
            sys.exit()

        return r.json()['data']['Airings'][0]['playbackUrls'][0]['href']

    def get_stream(self, content_id):
        auth = self.access_token()
        #url = 'https://edge.svcs.mlb.com/media/' + media_id + '/scenarios/browser~csai'
        #url = 'https://playback.svcs.mlb.com/events/ed5cc8a5-6fe5-4d36-b479-5097d77abdb6/media/' + media_id + '/scenarios/browser~csai'
        url = self.get_playback_url(content_id)
        url = url.replace('{scenario}','browser~csai')

        headers = {
            'Accept': 'application/vnd.media-service+json; version=2',
            'Authorization': auth,
            'X-BAMSDK-Version': '3.0',
            'X-BAMSDK-Platform': 'windows',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        }

        r = requests.get(url, headers=headers, cookies=self.util.load_cookies(), verify=self.verify)
        if r.status_code != 200:
            dialog = xbmcgui.Dialog()
            title = "Error Occured"
            msg = ""
            for item in r.json()['errors']:
                msg += item['code'] + '\n'
            dialog.notification(title, msg, self.icon, 5000, False)
            sys.exit()

        if 'slide' in r.json()['stream']:
            stream_url = r.json()['stream']['slide']
        else:
            stream_url = r.json()['stream']['complete']

        if QUALITY == 'Always Ask':
            stream_url = self.get_stream_quality(stream_url)
        headers = '|User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        headers += '&Authorization=' + auth
        headers += '&Cookie='
        cookies = requests.utils.dict_from_cookiejar(self.util.load_cookies())
        for key, value in cookies.iteritems():
            headers += key + '=' + value + '; '
            
        #CDN
        akc_url = 'hlslive-aksc'
        l3c_url = 'hlslive-l3c'
        if CDN == 'Akamai' and akc_url not in stream_url:
            stream_url = stream_url.replace(l3c_url, akc_url)
        elif CDN == 'Level 3' and l3c_url not in stream_url:
            stream_url = stream_url.replace(akc_url, l3c_url)
        
        return stream_url, headers

    def get_stream_quality(self, stream_url):
        stream_title = []
        stream_urls = []
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'}

        r = requests.get(stream_url, headers=headers, verify=False)
        master = r.text

        line = re.compile("(.+?)\n").findall(master)

        for temp_url in line:
            if '#EXT' not in temp_url:
                match = re.search(r'(\d.+?)K', temp_url, re.IGNORECASE)
                bandwidth = match.group()
                if 0 < len(bandwidth) < 6:
                    bandwidth = bandwidth.replace('K', ' kbps')
                    stream_title.append(bandwidth)
                    stream_urls.append(temp_url)

        stream_title.sort(key=self.util.natural_sort_key, reverse=True)
        stream_urls.sort(key=self.util.natural_sort_key, reverse=True)
        dialog = xbmcgui.Dialog()
        ret = dialog.select('Choose Stream Quality', stream_title)
        if ret >= 0:
            if 'http' not in stream_urls[ret]:
                stream_url = stream_url.replace(stream_url.rsplit('/', 1)[-1], stream_urls[ret])
            else:
                stream_url = stream_urls[ret]
        else:
            sys.exit()

        return stream_url
Esempio n. 5
0
class Account:
    addon = xbmcaddon.Addon()
    username = ''
    password = ''
    session_key = ''
    icon = os.path.join(addon.getAddonInfo('path'), 'icon.png')
    verify = False

    def __init__(self):
        self.username = self.addon.getSetting('username')
        self.password = self.addon.getSetting('password')
        self.session_key = self.addon.getSetting('session_key')
        self.util = Util()

    def login(self):
        # Check if username and password are provided
        if self.username == '':
            dialog = xbmcgui.Dialog()
            username = dialog.input('Please enter your username', type=xbmcgui.INPUT_ALPHANUM)
            self.addon.setSetting(id='username', value=username)

        if self.password == '':
            dialog = xbmcgui.Dialog()
            password = dialog.input('Please enter your password', type=xbmcgui.INPUT_ALPHANUM, option=xbmcgui.ALPHANUM_HIDE_INPUT)
            self.addon.setSetting(id='password', value=password)

        if self.username == '' or self.password == '':
            sys.exit()
        else:
            url = 'https://secure.mlb.com/pubajaxws/services/IdentityPointService'
            headers = {
                "SOAPAction": "http://services.bamnetworks.com/registration/identityPoint/identify",
                "Content-type": "text/xml; charset=utf-8",
                "User-Agent": "Dalvik/2.1.0 (Linux; U; Android 6.0.1; Hub Build/MHC19J)",
                "Connection": "Keep-Alive"
            }

            payload = "<?xml version='1.0' encoding='UTF-8'?>"
            payload += '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
            payload += '<SOAP-ENV:Body><tns:identityPoint_identify_request xmlns:tns="http://services.bamnetworks.com/registration/types/1.4">'
            payload += '<tns:identification type="email-password"><tns:id xsi:nil="true"/>'
            payload += '<tns:fingerprint xsi:nil="true"/>'
            payload += '<tns:email>'
            payload += '<tns:id xsi:nil="true"/>'
            payload += '<tns:address>' + self.username + '</tns:address>'
            payload += '</tns:email>'
            payload += '<tns:password>' + self.password + '</tns:password>'
            payload += '<tns:mobilePhone xsi:nil="true"/>'
            payload += '<tns:profileProperty xsi:nil="true"/>'
            payload += '</tns:identification>'
            payload += '</tns:identityPoint_identify_request>'
            payload += '</SOAP-ENV:Body>'
            payload += '</SOAP-ENV:Envelope>'

            r = requests.post(url, headers=headers, data=payload, verify=self.verify)

            """
            Bad username => <status><code>-1000</code><message> [Invalid credentials for identification] [com.bamnetworks.registration.types.exception.IdentificationException: Account doesn't exits]</message><exceptionClass>com.bamnetworks.registration.types.exception.IdentificationException</exceptionClass><detail type="identityPoint" field="exists" message="false" messageKey="identityPoint.exists" /><detail type="identityPoint" field="email-password" message="identification error on identity point of type email-password" messageKey="identityPoint.email-password" /></status>
            Bad password => <status><code>-1000</code><message> [Invalid credentials for identification] [com.bamnetworks.registration.types.exception.IdentificationException: Invalid Password]</message><exceptionClass>com.bamnetworks.registration.types.exception.IdentificationException</exceptionClass><detail type="identityPoint" field="exists" message="true" messageKey="identityPoint.exists" /><detail type="identityPoint" field="email-password" message="identification error on identity point of type email-password" messageKey="identityPoint.email-password" /></status>
            Good => <status><code>1</code><message>OK</message></status>
            """
            if self.util.find(r.text, '<code>', '</code>') != '1':
                title = self.util.find(r.text, '<message> [', '] [')
                msg = self.util.find(r.text, 'com.bamnetworks.registration.types.exception.IdentificationException: ', ']</message>')
                dialog = xbmcgui.Dialog()
                dialog.ok(title, msg)
                sys.exit()
            else:
                self.util.save_cookies(r.cookies)

    def feature_service(self):
        if self.util.check_cookies():
            self.login()
        cookies = requests.utils.dict_from_cookiejar(self.util.load_cookies())
        url = 'https://secure.mlb.com/pubajaxws/services/FeatureService'
        headers = {
            "SOAPAction": "http://services.bamnetworks.com/registration/feature/findEntitledFeatures",
            "Content-type": "text/xml; charset=utf-8",
            "User-Agent": "Dalvik/2.1.0 (Linux; U; Android 6.0.1; Hub Build/MHC19J)",
            "Connection": "Keep-Alive"
        }

        payload = "<?xml version='1.0' encoding='UTF-8'?>"
        payload += '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">'
        payload += '<soapenv:Header />'
        payload += '<soapenv:Body>'
        payload += '<feature_findEntitled_request xmlns="http://services.bamnetworks.com/registration/types/1.6">'

        if 'ipid' in cookies and 'fprt' in cookies and self.session_key != '':
            payload += "<identification type='fingerprint'>"
            payload += '<id>' + cookies['ipid'] + '</id>'
            payload += '<fingerprint>' + cookies['fprt'] + '</fingerprint>'
            payload += "<signOnRestriction type='mobileApp'>"
            payload += '<location>ANDROID_21d994bd-ebb1-4253-bcab-3550e7882294</location>'
            payload += '<sessionKey>' + self.session_key + '</sessionKey>'
        else:
            payload += "<identification type='email-password'>"
            payload += '<email><address>' + self.username + '</address></email>'
            payload += '<password>' + self.password + '</password>'
            payload += '<signOnRestriction type="mobileApp">'
            payload += '<location>ANDROID_21d994bd-ebb1-4253-bcab-3550e7882294</location>'

        payload += '</signOnRestriction>'
        payload += '</identification>'
        payload += '<featureContextName>MLBTV2017.INAPPPURCHASE</featureContextName>'
        payload += '</feature_findEntitled_request>'
        payload += '</soapenv:Body>'
        payload += '</soapenv:Envelope>'

        r = requests.post(url, headers=headers, data=payload, verify=self.verify)
        if self.util.find(r.text, '<code>', '</code>') != '1':
            title = self.util.find(r.text, '<message> [', '] [')
            msg = self.util.find(r.text, 'com.bamnetworks.registration.types.exception.IdentificationException: ', ']</message>')
            dialog = xbmcgui.Dialog()
            dialog.ok(title, msg)
            sys.exit()
        else:
            self.session_key = self.util.find(r.text, '<sessionKey>', '</sessionKey>')
            self.addon.setSetting("session_key", self.session_key)
            self.util.save_cookies(r.cookies)

    def logout(self):
        self.util.delete_cookies()

    def media_entitlement(self):
        # check_cookies()
        self.feature_service()
        cookies = requests.utils.dict_from_cookiejar(self.util.load_cookies())
        url = 'https://media-entitlement.mlb.com/jwt'
        url += '?ipid=' + cookies['ipid']
        url += '&fingerprint=' + cookies['fprt']
        url += '&os=Android'
        url += '&appname=AtBat'
        headers = {
            'x-api-key': 'arBv5yTc359fDsqKdhYC41NZnIFZqEkY5Wyyn9uA',
            'Cache-Control': 'no-cache',
            'Connection': 'Keep-Alive',
            'User-Agent': 'okhttp/3.9.0'
        }

        r = requests.get(url, headers=headers, cookies=self.util.load_cookies(), verify=self.verify)

        return r.text

    def access_token(self):
        url = 'https://edge.bamgrid.com/token'

        headers = {
            'Origin': 'https://www.mlb.com',
            'x-bamsdk-version': '3.0',
            'authorization': 'Bearer bWxidHYmYnJvd3NlciYxLjAuMA.VfmGMGituFykTR89LFD-Gr5G-lwJ9QbHfXXNBMkuM9M',
            'content-type': 'application/x-www-form-urlencoded',
            'x-bamsdk-platform': 'windows',
            'accept': 'application/json',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36',
            'Referer': 'https://www.mlb.com/tv/g529459/v8539eb7d-e8de-4b8d-84aa-d5026e632f36',
            'Accept-Encoding': 'gzip, deflate, br',
            'Accept-Language': 'en-US,en;q=0.9'
        }

        payload = 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange'
        payload += '&subject_token=' + self.media_entitlement()
        payload += '&subject_token_type=urn:ietf:params:oauth:token-type:jwt'
        payload += '&platform=browser'

        r = requests.post(url, headers=headers, data=payload, cookies=self.util.load_cookies(), verify=self.verify)
        access_token = r.json()['access_token']
        # refresh_token = r.json()['refresh_token']
        return access_token

    def get_playback_url(self, content_id):