def test_list_matches_live(self, mock_listitem): responses.add(responses.GET, config.HOME_URL, body=self.HOME_BOX_XML, status=200) escaped_box_url = re.escape(config.BOX_URL).replace('\\{', '{').replace( '\\}', '}') box_url = re.compile(escaped_box_url.format('.*')) responses.add(responses.GET, box_url, body=self.BOX_XML, status=200) responses.add(responses.GET, config.SCORE_URL, body=self.SCORE_XML, status=200) mock_listitem.side_effect = fakes.FakeListItem params = dict(parse_qsl(sys.argv[2][1:])) mock_plugin = fakes.FakePlugin() with mock.patch.dict('sys.modules', xbmcplugin=mock_plugin): import resources.lib.menu as menu menu.list_matches(params, live=True) for index, expected in enumerate(fakes.EXPECTED_LIVE_TITLES): url = mock_plugin.directory[index].get('url') url_query = dict(parse_qsl(urlparse(url)[4])) observed = url_query.get('title') self.assertEqual(expected, observed)
def test_make_seasons_list(self, mock_listitem): responses.add(responses.POST, config.TOKEN_URL, body=json.dumps({'token': 'abcdef'}), status=200) responses.add(responses.GET, config.SEASONS_URL, body=self.SEASONS_JSON, status=200) mock_listitem.side_effect = fakes.FakeListItem mock_plugin = fakes.FakePlugin() with mock.patch.dict('sys.modules', xbmcplugin=mock_plugin): import resources.lib.index as index index.make_seasons_list() expected_url = 'plugin://{addonid}/?{params}'.format( addonid='plugin.video.afl-video', params=unquote_plus( urlencode({ 'season': 'CD_S2020014', 'current_round': 'CD_R202001401', 'name': 'AFL Premiership 2020' }))) observed_url = mock_plugin.directory[0].get('url') expected = urlparse(expected_url) observed = urlparse(observed_url) for x in range(6): if x == 4: self.assertEqual(dict(parse_qsl(expected[x])), dict(parse_qsl(observed[x]))) else: self.assertEqual(expected[x], observed[x])
def router(paramstring): """ Router function that calls other functions depending on the provided paramstring :param paramstring: URL encoded plugin paramstring :type paramstring: str """ # Parse a URL-encoded paramstring to the dictionary of # {<parameter>: <value>} elements params = dict(parse_qsl(paramstring)) # Check the parameters passed to the plugin if params: if params['action'] == 'listing': # Display the list of videos in a provided category. list_videos(params['category'], int(params.get('page', '1'))) elif params['action'] == 'play': # Play a video from a provided URL. play_video(params['video']) else: # If the provided paramstring does not contain a supported action # we raise an exception. This helps to catch coding errors, # e.g. typos in action names. raise ValueError('Invalid paramstring: {0}!'.format(paramstring)) else: # If the plugin is called from Kodi UI without any parameters, # display the list of video categories list_categories()
def test_play_video(self, mock_listitem, mock_ticket): escaped_auth_url = re.escape( config.AUTH_URL).replace('\\{', '{').replace('\\}', '}') auth_url = re.compile(escaped_auth_url.format('.*', '.*', '.*')) responses.add(responses.GET, auth_url, body=self.AUTH_JSON, status=200) escaped_embed_url = re.escape( config.EMBED_TOKEN_URL).replace('\\{', '{').replace('\\}', '}') embed_url = re.compile(escaped_embed_url.format('.*')) responses.add(responses.GET, embed_url, body=self.EMBED_TOKEN_XML, status=200) escaped_stream_url = re.escape( config.STREAM_API_URL).replace('\\{', '{').replace('\\}', '}') escaped_stream_url = escaped_stream_url.replace('\\_', '_') stream_url = re.compile(escaped_stream_url.format(video_id='.*')) responses.add(responses.GET, stream_url, body=self.STREAM_API_JSON, status=200) responses.add(responses.GET, config.MEDIA_AUTH_URL.format(embed_code='123456'), body=self.VIDEOTOKEN_JSON) mock_ticket.return_value = 'foobar123456' mock_listitem.side_effect = fakes.FakeListItem params = dict(parse_qsl(sys.argv[2][1:])) mock_plugin = fakes.FakePlugin() with mock.patch.dict('sys.modules', xbmcplugin=mock_plugin): import resources.lib.play as play play.play_video(params) self.assertEqual(fakes.M3U8_URL.decode(), mock_plugin.resolved[2].getPath())
def router(paramstring): """ Router function that calls other functions depending on the provided paramstring :param paramstring: """ params = dict(parse_qsl(paramstring)) utils.log('Running with params: {0}'.format(params)) if params: if params['action'] == 'listcategories': if params['category'] == 'Live Matches': menu.list_matches(params, live=True) elif params['category'] == 'Settings': addon.openSettings() else: menu.list_videos(params) elif params['action'] in ['listvideos', 'listmatches']: play.play_video(params) elif params['action'] == 'clearticket': stream_auth.clear_ticket() elif params['action'] == 'open_ia_settings': try: import drmhelper if drmhelper.check_inputstream(drm=False): ia = drmhelper.get_addon() ia.openSettings() else: utils.dialog_message( "Can't open inputstream.adaptive settings") except Exception: utils.dialog_message( "Can't open inputstream.adaptive settings") else: menu.list_categories()
def get_filters(self, user_id=None): if not user_id: return {} try: monitor_db = database.MonitorDatabase() query = 'SELECT filter_all, filter_movies, filter_tv, filter_music, filter_photos FROM users ' \ 'WHERE user_id = ?' result = monitor_db.select_single(query, args=[user_id]) except Exception as e: logger.warn( "Tautulli Users :: Unable to execute database query for get_filters: %s." % e) result = {} filters_list = {} for k, v in result.items(): filters = {} for f in v.split('|'): if 'contentRating=' in f or 'label=' in f: filters.update(dict(parse_qsl(f))) filters['content_rating'] = tuple( f for f in filters.pop('contentRating', '').split(',') if f) filters['labels'] = tuple( f for f in filters.pop('label', '').split(',') if f) filters_list[k] = filters return filters_list
def collect_metadata(request, response): metadata = collections.OrderedDict() parsed = parse_url(request.url) hostname = get_endpoint_name(request.url) if hostname is None: address = parsed.netloc hostname = parsed.hostname else: address = hostname if parsed.port: address += ':{}'.format(parsed.port) # do not include querystring in url, as may have senstive info metadata['url'] = '{}://{}{}'.format(parsed.scheme, address, parsed.path) if parsed.query: metadata['url'] += '?' redacted = ('{}=<len {}>'.format(k, len(v)) for k, v in parse_qsl(parsed.query)) metadata['qs'] = '?' + '&'.join(redacted) metadata['qs_size'] = len(parsed.query) metadata['method'] = request.method metadata['host'] = hostname if parsed.netloc not in metadata['url']: metadata['netloc'] = parsed.netloc if response is not None: metadata['status_code'] = response.status_code if 'X-View-Name' in response.headers: metadata['view'] = response.headers['X-View-Name'] if 'Server' in response.headers: metadata['server'] = response.headers['Server'] duration = response.elapsed.total_seconds() * 1000 metadata['duration_ms'] = round(duration, 3) request_type = request.headers.get('content-type', None) if request_type is not None: metadata['request_type'] = request_type if metadata['method'] in ('POST', 'PUT', 'PATCH'): try: metadata['request_size'] = int( request.headers.get('content-length', 0)) except ValueError: pass if response is not None: response_type = response.headers.get('content-type', None) if response_type is not None: metadata['response_type'] = response_type try: metadata['response_size'] = int( response.headers.get('content-length', 0)) except ValueError: pass return metadata
def router(paramstring): """ Router function that calls other functions depending on the provided paramstring :param paramstring: """ params = dict(parse_qsl(paramstring)) if params: if params['action'] == 'listcategories': if params['category'] == 'settings': addon.openSettings() else: matches.make_matches_list(params) elif params['action'] == 'listmatches': play.play_video(params) elif params['action'] == 'clearticket': stream_auth.clear_ticket() elif params['action'] == 'sendreport': utils.user_report() elif params['action'] == 'open_ia_settings': try: import drmhelper if drmhelper.check_inputstream(drm=False): ia = drmhelper.get_addon() ia.openSettings() else: utils.dialog_message( "Can't open inputstream.adaptive settings") except Exception: utils.dialog_message( "Can't open inputstream.adaptive settings") else: categories.list_categories()
def test_play_video_live(self, mock_listitem, mock_ticket): params = dict(parse_qsl(sys.argv[2][1:])) escaped_bc_url = re.escape(config.BC_URL).replace('\\{', '{').replace( '\\}', '}') bc_url = re.compile(escaped_bc_url.format('.*', '.*')) responses.add(responses.GET, bc_url, body=self.BC_EDGE_JSON, status=200) responses.add(responses.GET, config.MEDIA_AUTH_URL.format(video_id='6112170884001'), body=self.VIDEO_TOKEN_JSON, status=200) responses.add(responses.GET, config.SIGN_URL.format( quote_plus('https://foo.bar/video.m3u8')), body=self.SIGN_JSON, status=200) mock_ticket.return_value = json.dumps({ 'pai': fakes.FAKE_UUID[0], 'bearer': 'abc123' }) mock_listitem.side_effect = fakes.FakeListItem mock_plugin = fakes.FakePlugin() with mock.patch.dict('sys.modules', xbmcplugin=mock_plugin): import resources.lib.play as play play.play_video(params) self.assertEqual('https://foo.bar/index.m3u8?signed', mock_plugin.resolved[2].getPath())
def test_list_videos_urls(self, mock_listitem): responses.add(responses.GET, config.VIDEO_URL, body=self.VIDEO_XML, status=200) mock_listitem.side_effect = fakes.FakeListItem params = dict(parse_qsl(sys.argv[2][1:])) mock_plugin = fakes.FakePlugin() with mock.patch.dict('sys.modules', xbmcplugin=mock_plugin): import resources.lib.menu as menu menu.list_videos(params) for index, expected in enumerate(fakes.EXPECTED_VIDEO_TITLES): url = mock_plugin.directory[index].get('url') url_query = dict(parse_qsl(urlparse(url)[4])) observed = url_query.get('title') self.assertEqual(expected, observed)
def set_callback_code(self): # get oauth code myid_signon_proceed = self.session.get( config.MYID_RESUME_AUTHORIZATION_URL.format( self.identify_path_token), params={'ctfr-proceed': 'true'}) query = urlparse(myid_signon_proceed.url).query self.callback_code = dict(parse_qsl(query)).get('code')
def get_url_query_param(url, param): """ 获取url参数值 :param url: :param param: :return: """ result = urlparse(url) return dict(parse_qsl(result.query)).get(param)
def parse_kodi_url(self, url): params = dict(parse_qsl(url)) for item in params.keys(): setattr(self, item, unquote_plus(params[item])) if getattr(self, 'captions', None) == 'True': self.captions = True if getattr(self, 'date', None): self.date = self.parse_datetime(self.date) if getattr(self, 'expire', None): self.expire = self.parse_datetime(self.expire)
def test_make_list(self, mock_listitem): mock_listitem.side_effect = fakes.FakeListItem mock_plugin = fakes.FakePlugin() with mock.patch.dict('sys.modules', xbmcplugin=mock_plugin): import resources.lib.index as index index.make_list() for ind, category in enumerate(config.CATEGORIES): expected_url = 'plugin://{addonid}/?{params}'.format( addonid='plugin.video.afl-video', params=unquote_plus(urlencode({'category': category}))) observed_url = mock_plugin.directory[ind].get('url') expected = urlparse(expected_url) observed = urlparse(observed_url) for x in range(6): if x == 4: self.assertEqual(dict(parse_qsl(expected[x])), dict(parse_qsl(observed[x]))) else: self.assertEqual(expected[x], observed[x])
def parse_kodi_url(self, url): params = dict(parse_qsl(url)) for item in params.keys(): setattr(self, item, unquote_plus(params[item])) if self.date: try: self.date = datetime.datetime.strptime(self.date, "%Y-%m-%d") except TypeError: self.date = datetime.datetime( *(time.strptime(self.date, "%Y-%m-%d")[0:6]))
def resolve_ref(uri): """ Finds and returns a schema pointed to by `uri` that has been registered in the register_schema function. """ parsed = urlparse(uri) if parsed.path in schema_paths: schema = schema_paths[parsed.path] if callable(schema): return schema(**dict(parse_qsl(parsed.query))) return schema raise jsonschema.RefResolutionError("%s could not be resolved" % uri)
def test_default_categories_paths(self, mock_listitem): mock_listitem.side_effect = fakes.FakeListItem responses.add(responses.GET, config.API_BASE_URL.format(path='/v2/navigation/mobile'), body=self.NAV_JSON) categories.make_category_list() for index, expected in enumerate(fakes.EXPECTED_CATEGORY_PATHS): url = self.mock_plugin.directory[index].get('url') url_query = dict(parse_qsl(urlparse(url)[4])) observed = url_query.get('category') self.assertEqual(expected, observed)
def resolve_ref(uri): """ Finds and returns a schema pointed to by `uri` that has been registered in the register_schema function. """ parsed = urlparse(uri) if parsed.path in schema_paths: schema = schema_paths[parsed.path] if callable(schema): return schema(**dict(parse_qsl(parsed.query))) return schema raise jsonschema.RefResolutionError("%s could not be resolved" % uri)
def test_make_collection_list_titles(self, mock_listitem): mock_listitem.side_effect = fakes.FakeListItem channel_path = '/channel/abc4kids' channel_url = config.API_BASE_URL.format( path='/v2{0}'.format(channel_path)) responses.add(responses.GET, channel_url, body=self.CHANNEL_JSON) collect.make_collect_list({'category': channel_path}) for index, expected in enumerate(fakes.EXPECTED_COLLECTION_TITLES): url = self.mock_plugin.directory[index].get('url') url_query = dict(parse_qsl(urlparse(url)[4])) observed = url_query.get('title') self.assertEqual(expected, observed)
def get_update_url(url, data): """ 获取更新后的url :param url: :param data: :return: """ result = urlparse(url) query_payload = dict(parse_qsl(result.query), **data) query_param = urlencode(query_payload) return urlunparse((result.scheme, result.netloc, result.path, result.params, query_param, result.fragment))
def test_make_search_history_list(self, mock_listitem): mock_listitem.side_effect = fakes.FakeListItem with mock.patch('resources.lib.search.get_search_history', return_value=fakes.EXPECTED_SEARCH_HISTORY[:]): expected_titles = ['New Search'] expected_titles.extend(fakes.EXPECTED_SEARCH_HISTORY) search.make_search_history_list() for index, expected in enumerate(expected_titles): url = self.mock_plugin.directory[index].get('url') url_query = dict(parse_qsl(urlparse(url)[4])) observed = url_query.get('name') self.assertEqual(expected, observed)
def test_make_search_list(self, mock_listitem): mock_listitem.side_effect = fakes.FakeListItem search_path = '/search?keyword=news' search_url = config.API_BASE_URL.format( path='/v2{0}'.format(search_path)) responses.add(responses.GET, search_url, body=self.SEARCH_JSON) search.make_search_list({'name': 'news'}) for index, expected in enumerate(fakes.EXPECTED_SEARCH_TITLES): url = self.mock_plugin.directory[index].get('url') url_query = dict(parse_qsl(urlparse(url)[4])) observed = url_query.get('title') self.assertEqual(expected, observed)
def set_spc_url_and_tpuid(self): """ Send ticket back and get 'sports pass confirmation' URL and 'TpUid' :return: """ yinz_resp = self.session.get(config.YINZCAM_AUTH_URL2) jsondata = json.loads(yinz_resp.text) self.token = jsondata.get('TpUid') self.spc_url = jsondata.get('Url') self.offer_id = dict(parse_qsl(urlsplit(self.spc_url)[3]))['offerId'] if not self.token or not self.spc_url: raise TelstraAuthException( 'Unable to get token/spc url from Netball API')
def test_default_programs_episode_titles(self, mock_listitem, mock_version): mock_listitem.side_effect = fakes.FakeListItem mock_version.return_value = 15 show_path = '/show/sesame-street' show_url = config.API_BASE_URL.format(path='/v2{0}{1}'.format( show_path, '?embed=seriesList,selectedSeries')) responses.add(responses.GET, show_url, body=self.SHOW_JSON) default.main() for index, expected in enumerate(fakes.EXPECTED_SHOW_TITLES): url = self.mock_plugin.directory[index].get('url') url_query = dict(parse_qsl(urlparse(url)[4])) observed = url_query.get('episode_title') self.assertEqual(expected, observed)
def test_list_categories(self, mock_listitem): mock_listitem.side_effect = fakes.FakeListItem mock_plugin = fakes.FakePlugin() with mock.patch.dict('sys.modules', xbmcplugin=mock_plugin): import resources.lib.categories as categories categories.list_categories() for index, category in enumerate(sorted(config.CATEGORIES.keys())): expected_url = 'plugin://{addonid}/?{params}'.format( addonid=config.ADDON_ID, params=unquote_plus( urlencode({ 'action': 'listcategories', 'category': config.CATEGORIES[category] }))) observed_url = mock_plugin.directory[index].get('url') expected = urlparse(expected_url) observed = urlparse(observed_url) for x in range(6): if x == 4: self.assertEqual(dict(parse_qsl(expected[x])), dict(parse_qsl(observed[x]))) else: self.assertEqual(expected[x], observed[x])
def test_make_search_list_no_results(self, mock_listitem): mock_listitem.side_effect = fakes.FakeListItem search_path = '/search?keyword=les%20norton' search_url = config.API_BASE_URL.format( path='/v2{0}'.format(search_path)) responses.add(responses.GET, search_url, body=self.SEARCH_NORESULTS_JSON) search.make_search_list({'name': 'les norton'}) expected = 'No results!' url = self.mock_plugin.directory[0].get('url') url_query = dict(parse_qsl(urlparse(url)[4])) observed = url_query.get('title') self.assertEqual(expected, observed) self.assertEqual('True', url_query.get('dummy'))
def login(self, username, password): '''Authenticate using XAuth variant of OAuth. :param str username: Username or email address for the relevant account :param str password: Password for the account ''' response = self.request(ACCESS_TOKEN, { 'x_auth_mode': 'client_auth', 'x_auth_username': username, 'x_auth_password': password }, returns_json=False) token = dict(parse_qsl(response['data'].decode())) self.token = oauth.Token(token['oauth_token'], token['oauth_token_secret']) self.oauth_client = oauth.Client(self.consumer, self.token)
def test_make_series_list_episode_count(self, mock_listitem): mock_listitem.side_effect = fakes.FakeListItem channel_path = '/channel/abc4kids' channel_url = config.API_BASE_URL.format( path='/v2{0}'.format(channel_path)) collection_path = '/collection/1962' collection_url = config.API_BASE_URL.format( path='/v2{0}'.format(collection_path)) responses.add(responses.GET, channel_url, body=self.CHANNEL_JSON) responses.add(responses.GET, collection_url, body=self.COLLECTION_JSON) series.make_series_list({'category': channel_path}) for index, expected in enumerate(fakes.EXPECTED_EPISODE_COUNTS): url = self.mock_plugin.directory[index].get('url') url_query = dict(parse_qsl(urlparse(url)[4])) observed_str = url_query.get('num_episodes') if observed_str: observed = int(observed_str) else: observed = observed_str self.assertEqual(expected, observed)
def test_default_collection_with_multi_series(self, mock_listitem, mock_version): mock_listitem.side_effect = fakes.FakeListItem mock_version.return_value = 15 series_path = '/show/gardening-australia' series_url = config.API_BASE_URL.format( path='/v2{0}'.format(series_path)) responses.add(responses.GET, series_url, body=self.SHOW_MULTISERIES_JSON) default.main() for index, expected in enumerate(fakes.EXPECTED_MULTISERIES_TITLES): url = self.mock_plugin.directory[index].get('url') url_query = dict(parse_qsl(urlparse(url)[4])) if url_query.get('type') == 'Program': o = classes.Program() o.parse_kodi_url(urlparse(url)[4]) elif url_query.get('type') == 'Series': o = classes.Series() o.parse_kodi_url(urlparse(url)[4]) observed = o.get_list_title() self.assertEqual(expected, observed)
def url(self): environ = self.environ # See https://www.python.org/dev/peps/pep-0333/#url-reconstruction host = environ.get("HTTP_HOST") if host is not None: host, _, port = host.partition(":") port = int(port) if port else None else: host = environ["SERVER_NAME"] port = int(environ["SERVER_PORT"]) path = environ.get("SCRIPT_NAME", "") + environ.get("PATH_INFO", "") return URL( host=host.decode("ascii"), port=port, path=path.lstrip("/").decode("ascii").split(u"/"), query=[ (k.decode("ascii"), v.decode("ascii")) for k, v in parse_qsl(environ.get("QUERY_STRING", "")) ], scheme=environ["wsgi.url_scheme"].decode("ascii"), )
def get_paid_token(self): """ Obtain a valid token from Telstra/Yinzcam, will be used to make requests for Ooyala embed tokens """ session = custom_session.Session(force_tlsv1=False) self.code_verifier = self.get_code_verifier() params = config.NRL_AUTH_PARAMS scope = base64.b64encode(os.urandom(16)).decode('utf-8').rstrip('=') params.update({ 'scope': scope, 'code_challenge': self.get_code_challenge(self.code_verifier) }) auth_resp = session.get(config.NRL_AUTH, params=params, allow_redirects=False) xsrf = auth_resp.cookies['XSRF-TOKEN'] session.headers.update({'x-xsrf-token': xsrf}) data = { 'emailAddress': '{0}'.format(self.username), 'password': '******'.format(self.password) } login_resp = session.post(config.NRL_LOGIN, json=data) login_resp_json = json.loads(login_resp.text) if not login_resp_json.get('success') == True: # noqa: E712 raise TelstraAuthException('Login failed for nrl.com: {0}'.format( login_resp_json.get('error'))) auth2_resp = session.get(config.NRL_AUTH_ACCEPT, params=params, allow_redirects=False) redirect_url = auth2_resp.headers.get('location') redirect_pieces = urlsplit(redirect_url) redirect_query = dict(parse_qsl(redirect_pieces.query)) code = redirect_query.get('code') token_form = {'code': code, 'code_verifier': self.code_verifier} token_form.update(config.TOKEN_DATA) session.headers = {} session.cookies.clear() token_resp = session.post(config.NRL_TOKEN, data=token_form) refresh_token = json.loads(token_resp.text).get('refresh_token') session.headers.update({ 'Content-Type': 'application/xml', 'Accept': 'application/json, text/plain, */*' }) ticket_signon = session.post( config.YINZCAM_AUTH_URL, data=config.NEW_LOGIN_DATA2.format(refresh_token)) ticket = json.loads(ticket_signon.text).get('Ticket') # check validity of subscription session.headers.update({'X-YinzCam-Ticket': ticket}) sub_status = session.get(config.STATUS_URL) status_json = json.loads(sub_status.text) if status_json.get('Valid') != 'true': raise TelstraAuthException('NRL.com login failed: {0}'.format( status_json.get('Reason'))) return ticket