Skip to content

Commit

Permalink
Merge pull request #9 from ivankokan/dev
Browse files Browse the repository at this point in the history
New authentication for the 2020-21 NBA season
  • Loading branch information
ivankokan committed Dec 22, 2020
2 parents 3a27329 + 1a6b84a commit 49fffd7
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 77 deletions.
3 changes: 1 addition & 2 deletions config/config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"publish_endpoint": "https://watch.nba.com/service/publishpoint",
"login_endpoint": "https://watch.nba.com/nba/secure/login?",
"publish_endpoint": "https://nbaapi.neulion.com/api_nba/v1/publishpoint",
"game_data_endpoint": "http://watch.nba.com/game/%s?format=json",
"config_endpoint": "https://watch.nba.com/service/config?format=json&cameras=true",

Expand Down
2 changes: 1 addition & 1 deletion resources/language/English/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ msgid "Failed to get a video URL. Are you logged in?"
msgstr ""

msgctxt "#50021"
msgid "Cannot login: invalid username and password, or your account is locked."
msgid "Cannot login: invalid e-mail and password, or your account is locked."
msgstr ""

msgctxt "#50022"
Expand Down
2 changes: 1 addition & 1 deletion resources/language/Hebrew/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ msgid "Failed to get a video URL. Are you logged in?"
msgstr "נכשל בהשגת כתובת URL לוידאו. האם אתה מחובר?"

msgctxt "#50021"
msgid "Cannot login: invalid username and password, or your account is locked."
msgid "Cannot login: invalid e-mail and password, or your account is locked."
msgstr "לא מצליח להתחבר: חסר שם משתמש, סיסמה, או שהחשבון שלך ננעל."

msgctxt "#50022"
Expand Down
2 changes: 1 addition & 1 deletion resources/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<settings>
<!-- General -->
<category label="50000">
<setting id="username" type="text" label="50001" default="" />
<setting id="email" type="text" label="50001" default="" />
<setting id="password" type="text" label="50002" option="hidden" default="" />
<setting type="sep" />
<setting id="scores" type="bool" label="50004" default="false" />
Expand Down
65 changes: 41 additions & 24 deletions src/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,36 +84,53 @@ def get_date(default='', heading='Please enter date (YYYY/MM/DD)', hidden=False)
ret = datetime.date(int(temp[0]), int(temp[1]), int(temp[2]))
return ret

def login():
username = vars.settings.getSetting(id="username")
def authenticate():
email = vars.settings.getSetting(id="email")
password = vars.settings.getSetting(id="password")

if not username or not password:
if not email or not password:
littleErrorPopup(xbmcaddon.Addon().getLocalizedString(50024))
return ''
return False

try:
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
body = urllib.urlencode({
'username': username,
'password': password
headers = {
'Content-Type': 'application/json',
'X-Client-Platform': 'web',
}
body = json.dumps({
'email': email,
'password': password,
'rememberMe': True,
})

request = urllib2.Request(vars.config['login_endpoint'], body, headers)
request = urllib2.Request('https://identity.nba.com/api/v1/auth', body, headers)
response = urllib2.urlopen(request)
content = response.read()
except urllib2.HTTPError as e:
log("Login failed with code: %d and content: %s" % (e.getcode(), e.read()))
littleErrorPopup(xbmcaddon.Addon().getLocalizedString(50022))
return ''

# Check the response xml
xml = parseString(str(content))
if xml.getElementsByTagName("code")[0].firstChild.nodeValue == "loginlocked":
littleErrorPopup(xbmcaddon.Addon().getLocalizedString(50021))
return ''
else:
# logged in
vars.cookies = response.info().getheader('Set-Cookie').partition(';')[0]

return vars.cookies
content_json = json.loads(content)
vars.cookies = response.info()['Set-Cookie'].partition(';')[0]
except urllib2.HTTPError as err:
littleErrorPopup(err)
return False

try:
headers = {
'Cookie': vars.cookies,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
}
body = {
'format': 'json',
'accesstoken': 'true',
'ciamlogin': 'true',
}
body = urllib.urlencode(body)

request = urllib2.Request('https://watch.nba.com/secure/authenticate', body, headers)
response = urllib2.urlopen(request)
content = response.read()
content_json = json.loads(content)
vars.access_token = content_json['data']['accessToken']
except urllib2.HTTPError as err:
littleErrorPopup(err)
return False

return True
84 changes: 47 additions & 37 deletions src/games.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def get_game(video_id, video_type, video_ishomefeed, start_time, duration):
'type': 'game',
'extid': str(video_id),
'drmtoken': True,
'token': vars.access_token,
'deviceid': xbmc.getInfoLabel('Network.MacAddress'), # TODO
'gt': gt,
'gs': vars.params.get('game_state', 3),
Expand Down Expand Up @@ -151,6 +152,10 @@ def getHighlightGameUrl(video_id):
utils.log("highlight video url: %s" % url, xbmc.LOGDEBUG)
return url

def process_key(dictionary, key, processed_keys):
processed_keys.add(key)
return dictionary.get(key)

def addGamesLinks(date='', video_type="archive"):
try:
now_datetime_est = utils.nowEST()
Expand All @@ -167,20 +172,30 @@ def addGamesLinks(date='', video_type="archive"):
utils.log("daily games for day %d are %s" % (index, daily_games), xbmc.LOGDEBUG)

for game in daily_games:
h = game.get('h', '')
v = game.get('v', '')
game_id = game.get('id', '')
game_start_date_est = game.get('d', '')
vs = game.get('vs', '')
hs = game.get('hs', '')
name = game.get('name', '')
image = game.get('image', '')
seo_name = game.get("seoName", "")
has_condensed_video = game.get("video", {}).get("c", False)

has_away_feed = False
video_details = game.get('video', {})
has_away_feed = bool(video_details.get("af", {}))
processed_keys = set()

v = process_key(game, 'v', processed_keys)
h = process_key(game, 'h', processed_keys)
vr = process_key(game, 'vr', processed_keys)
hr = process_key(game, 'hr', processed_keys)
vs = process_key(game, 'vs', processed_keys)
hs = process_key(game, 'hs', processed_keys)

if v is None or h is None: # TODO
utils.log(json.dumps(game), xbmc.LOGDEBUG)
continue

game_id = process_key(game, 'id', processed_keys)
game_start_date_est = process_key(game, 'd', processed_keys)

name = process_key(game, 'name', processed_keys)
image = process_key(game, 'image', processed_keys)
seo_name = process_key(game, 'seoName', processed_keys)

video = process_key(game, 'video', processed_keys)
has_video = video is not None
has_condensed_video = has_video and bool(video.get('c'))
has_away_feed = has_video and bool(video.get('af'))

# Try to convert start date to datetime
try:
Expand All @@ -207,36 +222,31 @@ def addGamesLinks(date='', video_type="archive"):
playoff_status = "%d-%d" % (playoff_visitor_wins, playoff_home_wins)
playoff_game_number = playoff_home_wins + playoff_visitor_wins

if game_id != '':
if game_id is not None:
# Get pretty names for the team names
[visitor_name, host_name] = [vars.config['teams'].get(t.lower(), t) for t in [v, h]]
[unknown_teams.setdefault(t, []).append(game_start_datetime_est.strftime("%Y-%m-%d"))
for t in [v, h] if t.lower() not in vars.config['teams']]

has_video = "video" in game
future_video = game_start_datetime_est > now_datetime_est and \
game_start_datetime_est.date() == now_datetime_est.date()
live_video = game_start_datetime_est < now_datetime_est < game_end_datetime_est

# Create the title
if host_name and visitor_name:
name = game_start_datetime_est.strftime("%Y-%m-%d")
if video_type == "live":
name = utils.toLocalTimezone(game_start_datetime_est).strftime("%Y-%m-%d (at %I:%M %p)")
name = game_start_datetime_est.strftime("%Y-%m-%d")
if video_type == "live":
name = utils.toLocalTimezone(game_start_datetime_est).strftime("%Y-%m-%d (at %I:%M %p)")

name += " %s (%s) vs %s (%s)" % (visitor_name, vr, host_name, hr)

# Add the teams' names and the scores if needed
name += ' %s vs %s' % (visitor_name, host_name)
if playoff_game_number != 0:
name += ' (game %d)' % (playoff_game_number)
if vars.show_scores and not future_video:
name += ' %s:%s' % (str(vs), str(hs))
if playoff_game_number != 0:
name += ' (game %d)' % (playoff_game_number)
if vars.show_scores and not future_video:
name += ' %s:%s' % (vs, hs)

if playoff_status:
name += " (series: %s)" % playoff_status
if playoff_status:
name += " (series: %s)" % playoff_status

thumbnail_url = utils.generateCombinedThumbnail(v, h)
elif image:
thumbnail_url = "https://nbadsdmt.akamaized.net/media/nba/nba/thumbs/%s" % image
thumbnail_url = utils.generateCombinedThumbnail(v, h)

if video_type == "live":
if future_video:
Expand All @@ -253,7 +263,7 @@ def addGamesLinks(date='', video_type="archive"):
add_link = False


if add_link == True:
if add_link:
params = {
'video_id': game_id,
'video_type': video_type,
Expand Down Expand Up @@ -281,6 +291,9 @@ def addGamesLinks(date='', video_type="archive"):
# Add a directory item that contains home/away/condensed items
common.addListItem(name, url="", mode="gamechoosevideo", iconimage=thumbnail_url, isfolder=True, customparams=params)

remaining_keys = set(game.keys()).difference(processed_keys)
utils.log('Remaining keys: {}'.format(remaining_keys), xbmc.LOGDEBUG)

if unknown_teams:
utils.log("Unknown teams: %s" % str(unknown_teams), xbmc.LOGWARNING)

Expand All @@ -290,10 +303,7 @@ def addGamesLinks(date='', video_type="archive"):
pass

def play_game():
# Authenticate
if vars.cookies == '':
vars.cookies = common.login()
if not vars.cookies:
if not common.authenticate():
return

currentvideo_id = vars.params.get("video_id")
Expand Down
4 changes: 2 additions & 2 deletions src/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ def __init__(self):

def refreshLiveUrl(self):
if self.shared_data.get('playing.what') == 'nba_tv_live':
video = TV.get_live(force_login=True)
video = TV.get_live()
elif self.shared_data.get('playing.what') == 'nba_tv_episode':
start_timestamp = self.shared_data.get('playing.data.start_timestamp')
duration = self.shared_data.get('playing.data.duration')
video = TV.get_episode(start_timestamp, duration, force_login=True)
video = TV.get_episode(start_timestamp, duration)

if video is not None:
self.readExpiresFromUrl(video)
Expand Down
14 changes: 6 additions & 8 deletions src/tv.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,8 @@ def play_episode():
common.play(episode)

@staticmethod
def get_episode(start_timestamp, duration, force_login=False):
if not vars.cookies or force_login:
common.login()
if not vars.cookies:
def get_episode(start_timestamp, duration):
if not common.authenticate():
return None

url = vars.config['publish_endpoint']
Expand All @@ -106,6 +104,7 @@ def get_episode(start_timestamp, duration, force_login=False):
'type': 'channel',
'id': 1,
'drmtoken': True,
'token': vars.access_token,
'deviceid': xbmc.getInfoLabel('Network.MacAddress'), # TODO
'st': start_timestamp,
'dur': duration,
Expand Down Expand Up @@ -134,10 +133,8 @@ def get_episode(start_timestamp, duration, force_login=False):
return {'url': url, 'drm': drm}

@staticmethod
def get_live(force_login=False):
if not vars.cookies or force_login:
common.login()
if not vars.cookies:
def get_live():
if not common.authenticate():
return None

url = vars.config['publish_endpoint']
Expand All @@ -150,6 +147,7 @@ def get_live(force_login=False):
'type': 'channel',
'id': 1,
'drmtoken': True,
'token': vars.access_token,
'deviceid': xbmc.getInfoLabel('Network.MacAddress'), # TODO
'pcid': vars.player_id,
'format': 'xml',
Expand Down
4 changes: 3 additions & 1 deletion src/vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
cache = StorageServer.StorageServer("nbaleaguepass", 1)
cache.table_name = "nbaleaguepass"

cookies = ''
cookies = None
access_token = None

player_id = binascii.b2a_hex(os.urandom(16))
addon_dir = xbmc.translatePath(settings.getAddonInfo('path')).decode('utf-8')

Expand Down

0 comments on commit 49fffd7

Please sign in to comment.