def _get_new_xvrttoken(self, login_json, token_variant=None): """Get new X-VRT-Token from VRT NU website""" if token_variant == 'roaming': xvrttoken = self._get_roaming_xvrttoken() else: login_token = login_json.get('sessionInfo', {}).get('login_token') if not login_token: return None from json import dumps login_cookie = 'glt_{api_key}={token}'.format(api_key=self._API_KEY, token=login_token) payload = dict( uid=login_json.get('UID'), uidsig=login_json.get('UIDSignature'), ts=login_json.get('signatureTimestamp'), email=from_unicode(get_setting('username')), ) data = dumps(payload).encode() headers = {'Content-Type': 'application/json', 'Cookie': login_cookie} log(2, 'URL post: {url}', url=unquote(self._TOKEN_GATEWAY_URL)) req = Request(self._TOKEN_GATEWAY_URL, data=data, headers=headers) try: # Python 3 setcookie_header = urlopen(req).info().get('Set-Cookie') except AttributeError: # Python 2 setcookie_header = urlopen(req).info().getheader('Set-Cookie') xvrttoken = TokenResolver._create_token_dictionary(setcookie_header) if xvrttoken is None: return None self._set_cached_token(xvrttoken, token_variant) notification(message=localize(30952)) # Login succeeded. return xvrttoken.get('X-VRT-Token')
def _get_xvrttoken(self, login_json=None): """Get a one year valid X-VRT-Token""" from json import dumps if not login_json: login_json = self._get_login_json() login_token = login_json.get('sessionInfo', {}).get('login_token') if not login_token: return None login_cookie = 'glt_{api_key}={token}'.format(api_key=self._API_KEY, token=login_token) payload = dict(uid=login_json.get('UID'), uidsig=login_json.get('UIDSignature'), ts=login_json.get('signatureTimestamp'), email=from_unicode(get_setting('username'))) data = dumps(payload).encode() headers = {'Content-Type': 'application/json', 'Cookie': login_cookie} response = open_url(self._TOKEN_GATEWAY_URL, data=data, headers=headers) if response is None: return None setcookie_header = response.info().get('Set-Cookie') xvrttoken = TokenResolver._create_token_dictionary(setcookie_header) if xvrttoken is None: return None notification(message=localize(30952)) # Login succeeded. return xvrttoken
def watchlater(self, episode_id, title): """Watch an episode later""" succeeded = self.update_watchlater(episode_id=episode_id, title=title, watch_later=True) if succeeded: notification(message=localize(30403, title=title)) container_refresh()
def unfollow(self, program_name, title, program_id=None, move_down=False): """Unfollow your favorite program""" succeeded = self.update(program_name, title, program_id, False) if succeeded: notification(message=localize(30412, title=title)) # If the current item is selected and we need to move down before removing if move_down: input_down() container_refresh()
def watchlater(self, uuid, title, url): ''' Watch an episode later ''' succeeded = self.update(uuid=uuid, title=title, url=url, watch_later=True) if succeeded: notification(message=localize(30403, title=title)) container_refresh()
def watchlater(self, asset_id, title, url): """Watch an episode later""" succeeded = self.update(asset_id=asset_id, title=title, url=url, watch_later=True) if succeeded: notification(message=localize(30403, title=title)) container_refresh()
def unwatchlater(self, asset_id, title, url, move_down=False): """Unwatch an episode later""" succeeded = self.update(asset_id=asset_id, title=title, url=url, watch_later=False) if succeeded: notification(message=localize(30404, title=title)) # If the current item is selected and we need to move down before removing if move_down: input_down() container_refresh()
def update_online(self, asset_id, title, url, payload): """Update resumepoint online""" from json import dumps try: get_url_json('https://video-user-data.vrt.be/resume_points/{asset_id}'.format(asset_id=asset_id), headers=self.resumepoint_headers(url), data=dumps(payload).encode()) except HTTPError as exc: log_error('Failed to (un)watch episode {title} at VRT NU ({error})', title=title, error=exc) notification(message=localize(30977, title=title)) return False return True
def resumepoints_headers(): """Generate http headers for VRT NU Resumepoint API""" from tokenresolver import TokenResolver vrtlogin_at = TokenResolver().get_token('vrtlogin-at') headers = {} if vrtlogin_at: headers = { 'Authorization': 'Bearer ' + vrtlogin_at, 'Content-Type': 'application/json', } else: log_error('Failed to get access token from VRT NU') notification(message=localize(30975)) return headers
def delete_tokens(): """Delete all cached tokens""" # Remove old tokens # FIXME: Deprecate and simplify this part in a future version _, files = listdir(addon_profile()) token_files = [item for item in files if item.endswith('.tkn')] # Empty userdata/tokens/ directory if exists(get_tokens_path()): _, files = listdir(get_tokens_path()) token_files += ['tokens/' + item for item in files] if token_files: for item in token_files: delete(addon_profile() + item) notification(message=localize(30985))
def delete_tokens(): ''' Delete all cached tokens ''' # Remove old tokens # FIXME: Deprecate and simplify this part in a future version dirs, files = listdir(get_userdata_path()) # pylint: disable=unused-variable token_files = [item for item in files if item.endswith('.tkn')] # Empty userdata/tokens/ directory if exists(get_tokens_path()): dirs, files = listdir(get_tokens_path()) # pylint: disable=unused-variable token_files += ['tokens/' + item for item in files] if token_files: for item in token_files: delete(get_userdata_path() + item) notification(message=localize(30985))
def login(username, password): resp = requests.post( "https://api.jio.com/v3/dip/user/unpw/verify", headers={"x-api-key": "l7xx938b6684ee9e4bbe8831a9a682b8e19f"}, json={ "identifier": username if '@' in username else "+91" + username, "password": password, "rememberUser": "******", "upgradeAuth": "Y", "returnSessionDetails": "T", "deviceInfo": { "consumptionDeviceName": "Jio", "info": { "type": "android", "platform": { "name": "vbox86p", "version": "8.0.0" }, "androidId": "6fcadeb7b4b10d77" } } }) if resp.status_code == 200 and resp.json()['ssoToken']: data = resp.json() _CREDS = urlencode({ "ssotoken": data['ssoToken'], "userId": data['sessionAttributes']['user']['uid'], "uniqueId": data['sessionAttributes']['user']['unique'], "crmid": data['sessionAttributes']['user']['subscriberId'] }) with open( xbmc.translatePath( 'special://home/addons/plugin.video.jiotv/resources/extra/channels.json' ), 'r') as f: raw = json.load(f) with open(ADDONDATA + 'channels.json', 'w+') as f: for cid, itm in raw.items(): if int(cid) in _STAR_CHANNELS.keys(): raw[cid]['url'] = itm['url'] + \ "?hdnea={token}|User-Agent=Hotstar%3Bin.startv.hotstar%2F8.2.4+%28Linux%3BAndroid+8.0.0%29+ExoPlayerLib%2F2.9.5&"+_CREDS else: raw[cid]['url'] = itm[ 'url'] + "?{token}|appkey=NzNiMDhlYzQyNjJm&lbcookie=1&devicetype=phone&deviceId=6fcadeb7b4b10d77&srno=200206173037&usergroup=tvYR7NSNn7rymo3F&versionCode=226&channelid=100&os=android&User-Agent=plaYtv%2F5.4.0+%28Linux%3BAndroid+8.0.0%29+ExoPlayerLib%2F2.3.0&" + _CREDS json.dump(raw, f, indent=2) else: kodiutils.notification('Login Failed', 'Invalid credentials')
def update(self, program, title, value=True): """Set a program as favorite, and update local copy""" # Survive any recent updates self.refresh(ttl=5) if value is self.is_favorite(program): # Already followed/unfollowed, nothing to do return True from tokenresolver import TokenResolver xvrttoken = TokenResolver().get_xvrttoken(token_variant='user') if xvrttoken is None: log_error('Failed to get favorites token from VRT NU') notification(message=localize(30975)) return False headers = { 'authorization': 'Bearer ' + xvrttoken, 'content-type': 'application/json', 'Referer': 'https://www.vrt.be/vrtnu', } from json import dumps from utils import program_to_url payload = dict(isFavorite=value, programUrl=program_to_url(program, 'short'), title=title) data = dumps(payload).encode('utf-8') program_id = program_to_id(program) try: get_url_json( 'https://video-user-data.vrt.be/favorites/{program_id}'.format( program_id=program_id), headers=headers, data=data) except HTTPError as exc: log_error( "Failed to (un)follow program '{program}' at VRT NU ({error})", program=program, error=exc) notification(message=localize(30976, program=program)) return False # NOTE: Updates to favorites take a longer time to take effect, so we keep our own cache and use it self._data[program_id] = dict(value=payload) update_cache('favorites.json', dumps(self._data)) invalidate_caches('my-offline-*.json', 'my-recent-*.json') return True
def login(username, password): resp = requests.post( "https://api.jio.com/v3/dip/user/unpw/verify", headers={"x-api-key": "l7xx938b6684ee9e4bbe8831a9a682b8e19f"}, json={ "identifier": username if '@' in username else "+91" + username, "password": password, "rememberUser": "******", "upgradeAuth": "Y", "returnSessionDetails": "T", "deviceInfo": { "consumptionDeviceName": "Jio", "info": { "type": "android", "platform": { "name": "vbox86p", "version": "8.0.0" }, "androidId": "6fcadeb7b4b10d77" } } }) if resp.status_code == 200 and resp.json()['ssoToken']: data = resp.json() _CREDS = { "ssotoken": data['ssoToken'], "userId": data['sessionAttributes']['user']['uid'], "uniqueId": data['sessionAttributes']['user']['unique'], "crmid": data['sessionAttributes']['user']['subscriberId'] } headers = { "User-Agent": "JioTV Kodi", "os": "Kodi", "deviceId": str(uuid.uuid4()), "versionCode": "226", "devicetype": "Kodi", "srno": datetime.today().strftime("%y%m%d%H%M%S"), # "200206173037", "appkey": "NzNiMDhlYzQyNjJm", "channelid": str(random.randint(100, 200)), "usergroup": "tvYR7NSNn7rymo3F", "lbcookie": "1" } headers.update(_CREDS) with open(TOKEN_FILE_PATH, 'w+') as f: json.dump(headers, f, indent=4) else: kodiutils.notification('Login Failed', 'Invalid credentials')
def watchlater_headers(url=None): """Generate http headers for VRT NU watchLater API""" from tokenresolver import TokenResolver xvrttoken = TokenResolver().get_token('X-VRT-Token', variant='user') headers = {} if xvrttoken: url = 'https://www.vrt.be' + url if url else 'https://www.vrt.be/vrtnu' headers = { 'Authorization': 'Bearer ' + xvrttoken, 'Content-Type': 'application/json', 'Referer': url, } else: log_error('Failed to get usertoken from VRT NU') notification(message=localize(30975)) return headers
def resumepoint_headers(url=None): """Generate http headers for VRT NU Resumepoints API""" from tokenresolver import TokenResolver xvrttoken = TokenResolver().get_xvrttoken(token_variant='user') headers = {} if xvrttoken: url = 'https://www.vrt.be' + url if url else 'https://www.vrt.be/vrtnu' headers = { 'authorization': 'Bearer ' + xvrttoken, 'content-type': 'application/json', 'Referer': url, } else: log_error('Failed to get usertoken from VRT NU') notification(message=localize(30975)) return headers
def check_login(): username = kodiutils.get_setting('username') password = kodiutils.get_setting('password') if os.path.isfile(ADDONDATA + 'headers.json'): return True elif username and password and not os.path.isfile(ADDONDATA + 'headers.json'): login(username, password) return True else: kodiutils.notification( 'Login Error', 'You need to login with Jio Username and password to use this plugin' ) kodiutils.show_settings() return False
def check_login(): username = kodiutils.get_setting('username') password = kodiutils.get_setting('password') # token is 5 days old ? if os.path.isfile(TOKEN_FILE_PATH) and time.time( ) < os.path.getmtime(TOKEN_FILE_PATH) + 432000000: return True elif username and password: login(username, password) return True else: kodiutils.notification( 'Login Error', 'You need to login with Jio Username and password to use this add-on' ) kodiutils.show_settings() return False
def update(self, program, title, value=True): ''' Set a program as favorite, and update local copy ''' self.refresh(ttl=60) if value is self.is_favorite(program): # Already followed/unfollowed, nothing to do return True from tokenresolver import TokenResolver xvrttoken = TokenResolver().get_xvrttoken(token_variant='user') if xvrttoken is None: log_error('Failed to get favorites token from VRT NU') notification(message=localize(30975)) return False headers = { 'authorization': 'Bearer ' + xvrttoken, 'content-type': 'application/json', 'Referer': 'https://www.vrt.be/vrtnu', } from statichelper import program_to_url payload = dict(isFavorite=value, programUrl=program_to_url(program, 'short'), title=title) from json import dumps data = dumps(payload).encode('utf-8') program_uuid = self.program_to_uuid(program) log(2, 'URL post: https://video-user-data.vrt.be/favorites/{uuid}', uuid=program_uuid) req = Request('https://video-user-data.vrt.be/favorites/%s' % program_uuid, data=data, headers=headers) result = urlopen(req) if result.getcode() != 200: log_error("Failed to (un)follow program '{program}' at VRT NU", program=program) notification(message=localize(30976, program=program)) return False # NOTE: Updates to favorites take a longer time to take effect, so we keep our own cache and use it self._favorites[program_uuid] = dict(value=payload) update_cache('favorites.json', self._favorites) invalidate_caches('my-offline-*.json', 'my-recent-*.json') return True
def update(self, uuid, title, url, watch_later=None, position=None, total=None): ''' Set program resumepoint or watchLater status and update local copy ''' # The video has no assetPath, so we cannot update resumepoints if uuid is None: return True if position is not None and position >= total - 30: watch_later = False self.refresh(ttl=0) if watch_later is not None and position is None and total is None and watch_later is self.is_watchlater( uuid): # watchLater status is not changed, nothing to do return True if watch_later is None and position == self.get_position( uuid) and total == self.get_total(uuid): # resumepoint is not changed, nothing to do return True # Collect header info for POST Request from tokenresolver import TokenResolver xvrttoken = TokenResolver().get_xvrttoken(token_variant='user') if xvrttoken is None: log_error('Failed to get usertoken from VRT NU') notification(message=localize(30975) + title) return False headers = { 'authorization': 'Bearer ' + xvrttoken, 'content-type': 'application/json', 'Referer': 'https://www.vrt.be' + url, } if uuid in self._resumepoints: # Update existing resumepoint values payload = self._resumepoints[uuid]['value'] payload['url'] = url else: # Create new resumepoint values payload = dict(position=0, total=100, url=url, watchLater=False) if position is not None: payload['position'] = position if total is not None: payload['total'] = total removes = [] if position is not None or total is not None: removes.append('continue-*.json') if watch_later is not None: # Add watchLater status to payload payload['watchLater'] = watch_later removes.append('watchlater-*.json') from json import dumps data = dumps(payload).encode() log(2, 'URL post: https://video-user-data.vrt.be/resume_points/{uuid}', uuid=uuid) log(2, 'URL post data:: {data}', data=data) try: req = Request('https://video-user-data.vrt.be/resume_points/%s' % uuid, data=data, headers=headers) urlopen(req) except HTTPError as exc: log_error('Failed to (un)watch episode at VRT NU ({error})', error=exc) notification(message=localize(30977)) return False # NOTE: Updates to resumepoints take a longer time to take effect, so we keep our own cache and use it self._resumepoints[uuid] = dict(value=payload) update_cache('resume_points.json', self._resumepoints) invalidate_caches(*removes) return True
def update_resumepoint(self, video_id, asset_str, title, position=None, total=None, path=None, episode_id=None, episode_title=None): """Set episode resumepoint and update local copy""" if video_id is None: return True menu_caches = [] self.refresh_resumepoints(ttl=5) # Add existing position and total if None if video_id in self._resumepoints and position is None and total is None: position = self.get_position(video_id) total = self.get_total(video_id) # Update if (self.still_watching(position, total) or (path and path.startswith('plugin://plugin.video.vrt.nu/play/upnext'))): # Normally, VRT NU resumepoints are deleted when an episode is (un)watched and Kodi GUI automatically sets # the (un)watched status when Kodi Player exits. This mechanism doesn't work with "Up Next" episodes because # these episodes are not initiated from a ListItem in Kodi GUI. # For "Up Next" episodes, we should never delete the VRT NU resumepoints to make sure the watched status # can be forced in Kodi GUI using the playcount infolabel. log(3, "[Resumepoints] Update resumepoint '{video_id}' {position}/{total}", video_id=video_id, position=position, total=total) if position == self.get_position( video_id) and total == self.get_total(video_id): # Resumepoint is not changed, nothing to do return True menu_caches.append('continue-*.json') # Update online gdpr = '{asset_str} gekeken tot {at} seconden.'.format( asset_str=asset_str, at=position) payload = dict( at=position, total=total, gdpr=gdpr, ) from json import dumps try: resumepoint_json = get_url_json( '{api}/{video_id}'.format(api=self.RESUMEPOINTS_URL, video_id=video_id), headers=self.resumepoints_headers(), data=dumps(payload).encode()) except HTTPError as exc: log_error( 'Failed to update resumepoint of {title} at VRT NU ({error})', title=title, error=exc) notification(message=localize(30977, title=title)) return False # Update local for idx, item in enumerate(self._resumepoints.get('items')): if item.get('mediaId') == video_id: self._resumepoints.get('items')[idx] = resumepoint_json break update_cache(self.RESUMEPOINTS_CACHE_FILE, dumps(self._resumepoints)) if menu_caches: invalidate_caches(*menu_caches) else: # Delete log(3, "[Resumepoints] Delete resumepoint '{asset_str}' {position}/{total}", asset_str=asset_str, position=position, total=total) # Delete watchlater self.update_watchlater(episode_id, episode_title, watch_later=False) # Do nothing if there is no resumepoint for this video_id from json import dumps if video_id not in dumps(self._resumepoints): log(3, "[Resumepoints] '{video_id}' not present, nothing to delete", video_id=video_id) return True # Add menu caches menu_caches.append('continue-*.json') # Delete online try: result = open_url('{api}/{video_id}'.format( api=self.RESUMEPOINTS_URL, video_id=video_id), headers=self.resumepoints_headers(), method='DELETE', raise_errors='all') log(3, "[Resumepoints] '{video_id}' online deleted: {code}", video_id=video_id, code=result.getcode()) except HTTPError as exc: log_error( "Failed to remove resumepoint of '{video_id}': {error}", video_id=video_id, error=exc) return False # Delete local representation and cache for item in self._resumepoints.get('items'): if item.get('mediaId') == video_id: self._resumepoints.get('items').remove(item) break update_cache(self.RESUMEPOINTS_CACHE_FILE, dumps(self._resumepoints)) if menu_caches: invalidate_caches(*menu_caches) return True
def follow(self, program, title): ''' Follow your favorite program ''' succeeded = self.update(program, title, True) if succeeded: notification(message=localize(30411, title=title)) container_refresh()
def favorites_refresh(): ''' The API interface to refresh the favorites cache ''' from favorites import Favorites Favorites().refresh(ttl=0) notification(message=localize(30982))
def resumepoints_refresh(): ''' The API interface to refresh the resumepoints cache ''' from resumepoints import ResumePoints ResumePoints().refresh(ttl=0) notification(message=localize(30983))
def follow(self, program_name, title, program_id=None): """Follow your favorite program""" succeeded = self.update(program_name, title, program_id, True) if succeeded: notification(message=localize(30411, title=title)) container_refresh()