diff --git a/channels/channel.se/tv4se/chn_tv4se.py b/channels/channel.se/tv4se/chn_tv4se.py index 0668e1a0..5c36dbc3 100644 --- a/channels/channel.se/tv4se/chn_tv4se.py +++ b/channels/channel.se/tv4se/chn_tv4se.py @@ -49,18 +49,22 @@ def __init__(self, channel_info): else: raise Exception("Invalid channel code") - self._add_data_parser("https://client-gateway.tv4.a2d.tv/graphql?operationName=PageList&", - name="Main TV4 pages", json=True, requires_logon=True, - parser=["data", "pageList", "content"], - creator=self.create_api_typed_item) + self._add_data_parser( + "https://client-gateway.tv4.a2d.tv/graphql?operationName=PageList&", + name="Main TV4 pages", json=True, requires_logon=False, + preprocessor=self.check_query_errors, + parser=["data", "pageList", "content"], + creator=self.create_api_typed_item) self.mainListUri = "#mainlist" self._add_data_parser( "#mainlist", name="Main TV4 page", json=True, preprocessor=self.list_main_content) + # If logon is set to True, panels that are not available to the user, will not show. self._add_data_parser( "https://client-gateway.tv4.a2d.tv/graphql?operationName=Page&", name="Main TV4 pages", json=True, requires_logon=True, + preprocessor=self.check_query_errors, parser=["data", "page", "content", "panels"], creator=self.create_api_typed_item) @@ -71,21 +75,25 @@ def __init__(self, channel_info): parser=["data", "mediaIndex", "contentList", "items"], creator=self.create_api_typed_item) + # Requires logon to list all seasons. self._add_data_parser( "https://client-gateway.tv4.a2d.tv/graphql?operationName=ContentDetailsPage&", name="Seasons for show", json=True, requires_logon=True, + preprocessor=self.check_query_errors, parser=["data", "media", "allSeasonLinks"], creator=self.create_api_typed_item, postprocessor=self.check_for_seasons) self._add_data_parser( "https://client-gateway.tv4.a2d.tv/graphql?operationName=Panel&", - name="Panel results", json=True, requires_logon=True, + name="Panel results", json=True, requires_logon=False, + preprocessor=self.check_query_errors, parser=["data", "panel", "content", "items"], creator=self.create_api_typed_item) self._add_data_parser( "https://client-gateway.tv4.a2d.tv/graphql?operationName=SeasonEpisodes&", - name="Episodes for a season", json=True, requires_logon=True, + name="Episodes for a season", json=True, requires_logon=False, + preprocessor=self.check_query_errors, parser=["data", "season", "episodes", "items"], creator=self.create_api_typed_item) @@ -170,6 +178,17 @@ def log_on(self) -> bool: self.parentItem.HttpHeaders.update(self.httpHeaders) return bool(self.__access_token) + def check_query_errors(self, data: str) -> Tuple[str, List[MediaItem]]: + items = [] + + if "PERSISTED_QUERY_NOT_FOUND" in data: + Logger.warning("`PERSISTED_QUERY_NOT_FOUND` Error for TV4") + headers = self.parentItem.HttpHeaders + headers.update(self.httpHeaders) + data = UriHandler.open(self.parentItem.url, additional_headers=self.parentItem.HttpHeaders, no_cache=True) + + return data, items + def list_main_content(self, data: str) -> Tuple[str, List[MediaItem]]: items: List[MediaItem] = [] diff --git a/tests/channel_tests/test_chn_tv4se.py b/tests/channel_tests/test_chn_tv4se.py index fbeb5f7f..42983f95 100644 --- a/tests/channel_tests/test_chn_tv4se.py +++ b/tests/channel_tests/test_chn_tv4se.py @@ -16,96 +16,38 @@ def test_channel_exists(self): def test_main_list(self): items = self.channel.process_folder_list(None) - self.assertGreaterEqual(len(items), 7, "Incorrect number of items in mainlist") + self.assertGreaterEqual(len(items), 5, "Incorrect number of items in mainlist") - def test_tv_show_list(self): - # url = "https://www.tv4play.se/alla-program" - # Don't use the GraphQL for now. - url = "https://graphql.tv4play.se/graphql?query=query%7BprogramSearch(per_page%3A1000)%7B__typename%2Cprograms%7B__typename%2Cdescription%2CdisplayCategory%2Cid%2Cimage%2Cimages%7Bmain16x9%7D%2Cname%2Cnid%2Cgenres%7D%2CtotalHits%7D%7D" - self._test_folder_url(url, expected_results=100) + def test_program_list(self): + url = "https://client-gateway.tv4.a2d.tv/graphql?operationName=MediaIndex&variables=%7B%22input%22%3A%20%7B%22letterFilters%22%3A%20%5B%22A%22%2C%20%22B%22%2C%20%22C%22%2C%20%22D%22%2C%20%22E%22%2C%20%22F%22%2C%20%22G%22%2C%20%22H%22%2C%20%22I%22%2C%20%22J%22%2C%20%22K%22%2C%20%22L%22%2C%20%22M%22%2C%20%22N%22%2C%20%22O%22%2C%20%22P%22%2C%20%22Q%22%2C%20%22R%22%2C%20%22S%22%2C%20%22T%22%2C%20%22U%22%2C%20%22V%22%2C%20%22W%22%2C%20%22X%22%2C%20%22Y%22%2C%20%22Z%22%5D%2C%20%22limit%22%3A%20500%2C%20%22offset%22%3A%200%7D%7D&extensions=%7B%22persistedQuery%22%3A%20%7B%22version%22%3A%201%2C%20%22sha256Hash%22%3A%20%22423ba183684c9ea464c94e200696c8f6ec190fe9837f542a672623fa87ef0f4e%22%7D%7D" + self._test_folder_url(url, expected_results=1000) - def test_category_list(self): - url = "https://graphql.tv4play.se/graphql?query=query%7Btags%7D" - self._test_folder_url(url, expected_results=5) - - def test_category_tv_show_list(self): - url = "https://graphql.tv4play.se/graphql?query=query%7BprogramSearch%28tag%3A%22Humor%22%2Cper_page%3A1000%29%7B__typename%2Cprograms%7B__typename%2Cdescription%2CdisplayCategory%2Cid%2Cimage%2Cimages%7Bmain16x9%7D%2Cname%2Cnid%2Cgenres%2CvideoPanels%7Bid%2Cname%7D%7D%2CtotalHits%7D%7D" - self._test_folder_url(url, expected_results=20) + def test_lastest_news(self): + url = "https://client-gateway.tv4.a2d.tv/graphql?operationName=Panel&variables=%7B%22panelId%22%3A%20%225Rqb0w0SN16A6YHt5Mx8BU%22%2C%20%22limit%22%3A%20500%2C%20%22offset%22%3A%200%7D&extensions=%7B%22persistedQuery%22%3A%20%7B%22version%22%3A%201%2C%20%22sha256Hash%22%3A%20%223ef650feea500555e560903fee7fc06f8276d046ea880c5540282a5341b65985%22%7D%7D" + self._test_folder_url(url, expected_results=10) - @unittest.skip("Currenlty not available") - def test_recents(self): - url = "https://api.tv4play.se/play/video_assets?exclude_node_nids=&platform=tablet&per_page=32&is_live=true&product_groups=2&type=episode&per_page=100" - self._test_folder_url(url, expected_results=2) + def test_recent(self): + url = "https://client-gateway.tv4.a2d.tv/graphql?operationName=Panel&variables=%7B%22panelId%22%3A%20%221pDPvWRfhEg0wa5SvlP28N%22%2C%20%22limit%22%3A%20500%2C%20%22offset%22%3A%200%7D&extensions=%7B%22persistedQuery%22%3A%20%7B%22version%22%3A%201%2C%20%22sha256Hash%22%3A%20%223ef650feea500555e560903fee7fc06f8276d046ea880c5540282a5341b65985%22%7D%7D" + self._test_folder_url(url, expected_results=10) - @unittest.skip("Currenlty not available") def test_popular(self): - url = "https://api.tv4play.se/play/video_assets/most_viewed?type=episode&platform=tablet&is_live=false&per_page=100&start=0" - self._test_folder_url(url, expected_results=5) - - @unittest.skip("Currenlty not available") - def test_yesterday(self): - url = "https://api.tv4play.se/play/video_assets?exclude_node_nids=&platform=tablet" \ - "&is_live=false&product_groups=2&type=episode&per_page=100" \ - "&broadcast_from={:04d}{:02d}{:02d}" \ - "&broadcast_to={:04d}{:02d}{:02d}&" - today = datetime.datetime.now() - yesterday = today - datetime.timedelta(days=1) - url = url.format(yesterday.year, yesterday.month, yesterday.day, today.year, today.month, today.day) - items = self._test_folder_url(url, expected_results=25) + url = "https://client-gateway.tv4.a2d.tv/graphql?operationName=Panel&variables=%7B%22panelId%22%3A%20%223QnNaigt4Szgkyz8yMU9oF%22%2C%20%22limit%22%3A%20500%2C%20%22offset%22%3A%200%7D&extensions=%7B%22persistedQuery%22%3A%20%7B%22version%22%3A%201%2C%20%22sha256Hash%22%3A%20%223ef650feea500555e560903fee7fc06f8276d046ea880c5540282a5341b65985%22%7D%7D" + self._test_folder_url(url, expected_results=10) - # Check the date - self.assertEqual(yesterday.day, items[1]._MediaItem__timestamp.day) - self.assertEqual(yesterday.month, items[1]._MediaItem__timestamp.month) - self.assertEqual(yesterday.year, items[1]._MediaItem__timestamp.year) + def test_categories(self): + url = "https://client-gateway.tv4.a2d.tv/graphql?operationName=PageList&variables=%7B%22pageListId%22%3A%20%22categories%22%7D&extensions=%7B%22persistedQuery%22%3A%20%7B%22version%22%3A%201%2C%20%22sha256Hash%22%3A%20%2258da321b8e31df2b746f1d1f374151a450a4c24bda6415182fe81551c90e7d25%22%7D%7D" + self._test_folder_url(url, expected_results=10) - @unittest.skip("Currenlty not available") - def test_tv_show_videos(self): - url = "https://api.tv4play.se/play/video_assets?platform=tablet&per_page=100&is_live=false&type=episode&page=1&node_nids=nyheterna&start=0" + def test_season_content_listing(self): + url = "https://client-gateway.tv4.a2d.tv/graphql?operationName=SeasonEpisodes&variables=%7B%22seasonId%22%3A%20%224952da9781c046017460%22%2C%20%22input%22%3A%20%7B%22limit%22%3A%20100%2C%20%22offset%22%3A%200%7D%7D&extensions=%7B%22persistedQuery%22%3A%20%7B%22version%22%3A%201%2C%20%22sha256Hash%22%3A%20%229f069a1ce297d68a0b4a3d108142919fb6d12827f35fc71b03976a251e239796%22%7D%7D" self._test_folder_url(url, expected_results=5) - @unittest.skipIf("CI" in os.environ, "Skipping in CI due to Geo-Restrictions") - def test_playback(self): - url = "https://playback-api.b17g.net/media/13281470?service=tv4&device=browser&protocol=dash" - self._test_video_url(url) - - @unittest.skip("Currenlty not available") - def test_tv4_channel(self): - from resources.lib.helpers.channelimporter import ChannelIndex - chn = ChannelIndex.get_register().get_channel(self._channel, "tv4se") - self.assertIsNotNone(chn) - self.assertEqual("tv4se", chn.channelCode) - - @unittest.skip("Currenlty not available") - def test_sjuan_channel(self): - from resources.lib.helpers.channelimporter import ChannelIndex - chn = ChannelIndex.get_register().get_channel(self._channel, "tv7se") - self.assertIsNotNone(chn) - self.assertEqual("tv7se", chn.channelCode) - - @unittest.skip("Currenlty not available") - def test_tv12_channel(self): - from resources.lib.helpers.channelimporter import ChannelIndex - chn = ChannelIndex.get_register().get_channel(self._channel, "tv12se") - self.assertIsNotNone(chn) - self.assertEqual("tv12se", chn.channelCode) - - @unittest.skip("Currenlty not available") - def test_tv4_main_list(self): - url = "https://api.tv4play.se/play/programs?is_active=true&platform=tablet&per_page=1000&fl=nid,name,program_image,is_premium,updated_at,channel&start=0" - self._test_folder_url(url, expected_results=50) - - def test_tv4play_graph_video_list(self): - url = "https://graphql.tv4play.se/graphql?query=%7BvideoPanel%28id%3A%20%226xCXrYiuiC2lSqs6jfIZIM%22%29%7Bname%2CvideoList%28limit%3A%20100%29%7BtotalHits%2CvideoAssets%7Btitle%2Cid%2Cdescription%2Cseason%2Cepisode%2CdaysLeftInService%2CbroadcastDateTime%2Cimage%2Cfreemium%2CdrmProtected%2Clive%2Cduration%7D%7D%7D%7D" - self._test_folder_url(url, expected_results=10) - - def test_list_with_seasons_folders(self): - url = "https://graphql.tv4play.se/graphql?query=%7Bprogram%28nid%3A%22ambulansen%22%29%7Bname%2Cdescription%2CvideoPanels%7Bid%2Cname%2Csubheading%2CassetType%7D%7D%7D" - url = "https://graphql.tv4play.se/graphql?query=%7Bprogram%28nid%3A%22vad-blir-det-f%C3%B6r-mat%22%29%7Bname%2Cdescription%2CvideoPanels%7Bid%2Cname%2Csubheading%2CassetType%7D%7D%7D" - self._test_folder_url(url, expected_results=1) + @unittest.skip(reason="Requires Login") + def test_list_category_content(self): + url = "https://client-gateway.tv4.a2d.tv/graphql?operationName=Page&variables=%7B%22pageId%22%3A%20%22dokument%C3%A4rer%22%2C%20%22input%22%3A%20%7B%22limit%22%3A%20100%2C%20%22offset%22%3A%200%7D%7D&extensions=%7B%22persistedQuery%22%3A%20%7B%22version%22%3A%201%2C%20%22sha256Hash%22%3A%20%22a30fb04a7dbabeaf3b08f66134c6ac1f1e4980de1f21024fa755d752608e6ad9%22%7D%7D" + self._test_folder_url(url, expected_results=5) - def test_list_with_paging(self): - url = "https://graphql.tv4play.se/graphql?query=%7BvideoPanel%28id%3A%20%228e7562f0-65f6-11eb-a19d-0dff2f8593eb%22%29%7Bname%2CvideoList%28limit%3A%20100%2C+sortOrder%3A+%22broadcastDateTime%22%29%7BtotalHits%2CvideoAssets%7Btitle%2Cid%2Cdescription%2Cseason%2Cepisode%2CdaysLeftInService%2CbroadcastDateTime%2Cimage%2Cfreemium%2CdrmProtected%2Clive%2Cduration%7D%7D%7D%7D" - item = self._get_media_item(url, "test") - item.metaData["folder_id"] = "8e7562f0-65f6-11eb-a19d-0dff2f8593eb" - items = self.channel.process_folder_list(item) - self.assertGreaterEqual(len(items), 101) + @unittest.skip(reason="Requires Login") + def test_video_playback(self): + url = "https://playback2.a2d.tv/play/5deba52e58a8ebba316d?service=tv4play&device=browser&browser=GoogleChrome&protocol=hls%2Cdash&drm=widevine&capabilities=live-drm-adstitch-2%2Cexpired_assets" + self._test_video_url(url) \ No newline at end of file