From f97d9cd76a66569f8dd962d2f9368597f4dbb506 Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Tue, 3 May 2022 04:25:15 +0200 Subject: [PATCH 1/8] Add support for content presented on homepage --- lib/srgssr.py | 115 +++++++++++++----- .../resource.language.de_de/strings.po | 4 + .../resource.language.en_gb/strings.po | 4 + .../resource.language.fr_fr/strings.po | 4 + .../resource.language.it_it/strings.po | 4 + 5 files changed, 98 insertions(+), 33 deletions(-) diff --git a/lib/srgssr.py b/lib/srgssr.py index 671d01f..710f46b 100644 --- a/lib/srgssr.py +++ b/lib/srgssr.py @@ -83,7 +83,10 @@ def __init__(self, plugin_handle, bu='srf', addon_id=ADDON_ID): self.host_url = f'https://www.{bu}.ch' if bu == 'swi': self.host_url = 'https://play.swissinfo.ch' + self.playtv_url = f'{self.host_url}/play/tv' self.apiv3_url = f'{self.host_url}/play/v3/api/{bu}/production/' + self.data_regex = \ + r'' self.data_uri = f'special://home/addons/{self.addon_id}/resources/data' self.media_uri = \ f'special://home/addons/{self.addon_id}/resources/media' @@ -142,10 +145,14 @@ def build_url(mode=None, name=None, url=None, page_hash=None, page=None): page -- an integer used to indicate the current page in the list of items """ - if mode: + try: mode = str(mode) - if page: + except Exception: + pass + try: page = str(page) + except Exception: + pass added = False queries = (url, mode, name, page_hash, page) query_names = ('url', 'mode', 'name', 'page_hash', 'page') @@ -271,6 +278,13 @@ def build_main_menu(self, identifiers=[]): 'mode': 27, 'displayItem': self.get_boolean_setting('Search'), 'icon': self.icon, + }, { + # Homepage + 'identifier': 'Homepage', + 'name': self.plugin_language(30060), + 'mode': 200, + 'displayItem': self.get_boolean_setting('Homepage'), + 'icon': self.icon, }, { # YouTube 'identifier': '%s_YouTube' % self.bu.upper(), @@ -319,7 +333,8 @@ def build_menu_apiv3(self, queries, mode=1000, page=1, page_hash=None, Keyword arguments: queries -- the query string or a list of several queries mode -- mode for the URL of the next folder - page -- current page + page -- current page; if page is set to 0, do not build + a next page button page_hash -- cursor for fetching the next items is_show -- indicates if the menu contains only shows whitelist_ids -- list of ids that should be displayed, if it is set @@ -349,8 +364,9 @@ def build_menu_apiv3(self, queries, mode=1000, page=1, page_hash=None, cursor = None if cursor: - data = json.loads(self.open_url(self.apiv3_url + queries + ( - '&' if '?' in queries else '?') + 'next=' + cursor)) + symb = '&' if '?' in queries else '?' + url = f'{self.apiv3_url}{queries}{symb}next={cursor}' + data = json.loads(self.open_url(url)) else: data = json.loads(self.open_url(self.apiv3_url + queries)) cursor = utils.try_get(data, 'next') or utils.try_get( @@ -370,6 +386,8 @@ def build_menu_apiv3(self, queries, mode=1000, page=1, page_hash=None, item, is_show=is_show, whitelist_ids=whitelist_ids) if cursor: + if page == 0 or page == '0': + return if page: url = self.build_url( mode=mode, name=queries, page=int(page)+1, @@ -443,37 +461,68 @@ def build_newest_favourite_menu(self, page=1): queries.append('videos-by-show-id?showId=' + sid) return self.build_menu_apiv3(queries) - def extract_id_list(self, url, editor_picks=False): + def build_homepage_menu(self): """ - Opens a webpage and extracts video ids (of the form "id": "") - from JavaScript snippets. + Builds the homepage menu. + """ + self.build_menu_from_page(self.playtv_url) - Keyword argmuents: - url -- the URL of the webpage - editor_picks -- if set, only extracts ids of editor picks - (default: False) + def build_menu_from_page(self, url): """ - self.log(f'extract_id_list, url = {url}') - response = self.open_url(url) - string_response = utils.str_or_none(response, default='') - if not string_response: - self.log(f'No video ids found on {url}') - return [] - readable_string_response = string_response.replace('"', '"') - id_regex = r'''(?x) - \"id\" - \s*:\s* - \" - (?P - %s - ) - \" - ''' % IDREGEX - if editor_picks: - id_regex += r'.+\"isEditorPick\"\s*:\s*true' - id_list = [m.group('id') for m in re.finditer( - id_regex, readable_string_response)] - return id_list + Builds a menu by extracting some content directly from a website. + + Keyword arguments: + url -- the url of the website + """ + html = self.open_url(url) + m = re.search(self.data_regex, html) + if not m: + self.log('build_menu_from_page: No data found in html') + return + content = m.groups()[0] + try: + js = json.loads(content) + except Exception: + self.log('build_menu_from_page: Invalid json') + return + data = utils.try_get( + js, ('initialData', 'pacPageConfigs', 'videoHomeSections'), + list, []) + if not data: + self.log('build_menu_from_page: Could not find any data in json') + for elem in data: + try: + id = elem['id'] + section_type = elem['sectionType'] + title = utils.try_get(elem, ('representation', 'title')) + page = 1 + # TODO: Are there more section types to consider? + # there is 'MediaSectionWithShow' + if section_type in ('MediaSection', 'ShowSection'): + if section_type == 'MediaSection' and not title and \ + utils.try_get( + elem, ('representation', 'name') + ) == 'HeroStage': + title = self.language(30053) + # NOTE: Accesing the next page fails for this type. + # So do not generate a next page button: + page = 0 + if not title: + continue + list_item = xbmcgui.ListItem(label=title) + list_item.setArt({ + 'thumb': self.icon, + 'fanart': self.fanart, + }) + if section_type == 'MediaSection': + name = f'media-section?sectionId={id}' + elif section_type == 'ShowSection': + name = f'show-section?sectionId={id}' + url = self.build_url(mode=1000, name=name, page=page) + xbmcplugin.addDirectoryItem( + self.handle, url, list_item, isFolder=True) + except Exception: + pass def build_episode_menu(self, video_id, include_segments=True, segment_option=False, audio=False): diff --git a/resources/language/resource.language.de_de/strings.po b/resources/language/resource.language.de_de/strings.po index 20cb149..cef7cc0 100644 --- a/resources/language/resource.language.de_de/strings.po +++ b/resources/language/resource.language.de_de/strings.po @@ -15,6 +15,10 @@ msgstr "" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +msgctxt "#30053" +msgid "Recommendations" +msgstr "Empfehlungen" + msgctxt "#30058" msgid "Today" msgstr "Heute" diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index 7e9af5f..f8db8a7 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -15,6 +15,10 @@ msgstr "" "Language: en\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +msgctxt "#30053" +msgid "Recommendations" +msgstr "" + msgctxt "#30058" msgid "Today" msgstr "" diff --git a/resources/language/resource.language.fr_fr/strings.po b/resources/language/resource.language.fr_fr/strings.po index 9fb5e83..b892378 100644 --- a/resources/language/resource.language.fr_fr/strings.po +++ b/resources/language/resource.language.fr_fr/strings.po @@ -15,6 +15,10 @@ msgstr "" "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +msgctxt "#30053" +msgid "Recommendations" +msgstr "Recommandations" + msgctxt "#30058" msgid "Today" msgstr "Aujourd'hui" diff --git a/resources/language/resource.language.it_it/strings.po b/resources/language/resource.language.it_it/strings.po index 78ef751..e3a098b 100644 --- a/resources/language/resource.language.it_it/strings.po +++ b/resources/language/resource.language.it_it/strings.po @@ -15,6 +15,10 @@ msgstr "" "Language: it\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +msgctxt "#30053" +msgid "Recommendations" +msgstr "Consigli" + msgctxt "#30058" msgid "Today" msgstr "Oggi" From fd5734694250085029002c36d006aeb97f96bd11 Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Tue, 3 May 2022 15:26:02 +0200 Subject: [PATCH 2/8] Make menu building from webpage more flexible --- lib/srgssr.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/srgssr.py b/lib/srgssr.py index 710f46b..7856378 100644 --- a/lib/srgssr.py +++ b/lib/srgssr.py @@ -465,14 +465,17 @@ def build_homepage_menu(self): """ Builds the homepage menu. """ - self.build_menu_from_page(self.playtv_url) + self.build_menu_from_page(self.playtv_url, + ('initialData', 'pacPageConfigs', 'videoHomeSections')) - def build_menu_from_page(self, url): + def build_menu_from_page(self, url, path): """ Builds a menu by extracting some content directly from a website. Keyword arguments: url -- the url of the website + path -- the path to the relevant data in the json (as tuple + or list of strings) """ html = self.open_url(url) m = re.search(self.data_regex, html) @@ -485,9 +488,7 @@ def build_menu_from_page(self, url): except Exception: self.log('build_menu_from_page: Invalid json') return - data = utils.try_get( - js, ('initialData', 'pacPageConfigs', 'videoHomeSections'), - list, []) + data = utils.try_get(js, path, list, []) if not data: self.log('build_menu_from_page: Could not find any data in json') for elem in data: From 84f3f84c2ad6cd88052b177f2f7c149beaa5a0e2 Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Tue, 3 May 2022 15:43:16 +0200 Subject: [PATCH 3/8] Add support for topics --- lib/srgssr.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/srgssr.py b/lib/srgssr.py index 7856378..eec686b 100644 --- a/lib/srgssr.py +++ b/lib/srgssr.py @@ -240,7 +240,7 @@ def build_main_menu(self, identifiers=[]): 'identifier': 'Topics', 'name': self.plugin_language(30058), 'mode': 13, - 'displayItem': False, # not (yet) supported + 'displayItem': self.get_boolean_setting('Topics'), 'icon': self.icon, }, { # Most searched TV shows @@ -465,8 +465,8 @@ def build_homepage_menu(self): """ Builds the homepage menu. """ - self.build_menu_from_page(self.playtv_url, - ('initialData', 'pacPageConfigs', 'videoHomeSections')) + self.build_menu_from_page(self.playtv_url, ( + 'initialData', 'pacPageConfigs', 'videoHomeSections')) def build_menu_from_page(self, url, path): """ @@ -702,7 +702,9 @@ def build_menu_by_urn(self, urn): self.build_menu_apiv3(f'videos-by-show-id?showId={id}') elif 'video' in urn: self.build_episode_menu(id) - # TODO: Add 'topic' + elif 'topic' in urn: + self.build_menu_from_page(self.playtv_url, ( + 'initialData', 'pacPageConfigs', 'topicSections', urn)) def build_entry(self, json_entry, is_folder=False, audio=False, fanart=None, urn=None, show_image_url=None, From 9ce941d6f0522632d42be81a6c62667c103a9cf0 Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Wed, 4 May 2022 04:48:48 +0200 Subject: [PATCH 4/8] Return if there is no data --- lib/srgssr.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/srgssr.py b/lib/srgssr.py index eec686b..6dedf05 100644 --- a/lib/srgssr.py +++ b/lib/srgssr.py @@ -491,6 +491,7 @@ def build_menu_from_page(self, url, path): data = utils.try_get(js, path, list, []) if not data: self.log('build_menu_from_page: Could not find any data in json') + return for elem in data: try: id = elem['id'] From 42c9112a972d68c64100bfb303150e0a63b0f213 Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Wed, 4 May 2022 06:00:53 +0200 Subject: [PATCH 5/8] Prevent upcoming live events from being played --- lib/srgssr.py | 15 ++++++++++++++- .../language/resource.language.de_de/strings.po | 12 ++++++++++++ .../language/resource.language.en_gb/strings.po | 12 ++++++++++++ .../language/resource.language.fr_fr/strings.po | 12 ++++++++++++ .../language/resource.language.it_it/strings.po | 12 ++++++++++++ 5 files changed, 62 insertions(+), 1 deletion(-) diff --git a/lib/srgssr.py b/lib/srgssr.py index 6dedf05..06910d5 100644 --- a/lib/srgssr.py +++ b/lib/srgssr.py @@ -688,8 +688,21 @@ def build_entry_apiv3(self, data, is_show=False, whitelist_ids=None): 'banner': show_image_url or image_url, }) url = self.build_url(mode=100, name=urn) + is_folder = True + + # Prevent upcoming live events from being played: + if 'swisstxt' in urn: + url = self.build_url(mode=500, name=urn) + is_folder = False + xbmcplugin.addDirectoryItem( - self.handle, url, list_item, isFolder=True) + self.handle, url, list_item, isFolder=is_folder) + + def playback_not_supported_dialog(self, urn): + heading = self.language(30500) + message = self.language(30501) + f' {urn} ' + self.language(30502) + dialog = xbmcgui.Dialog() + dialog.notification(heading, message) def build_menu_by_urn(self, urn): """ diff --git a/resources/language/resource.language.de_de/strings.po b/resources/language/resource.language.de_de/strings.po index cef7cc0..784df96 100644 --- a/resources/language/resource.language.de_de/strings.po +++ b/resources/language/resource.language.de_de/strings.po @@ -106,3 +106,15 @@ msgstr "Kürzlich gesuchte Audios" msgctxt "#30118" msgid "Recently searched shows" msgstr "Kürzlich gesuchte Sendungen" + +msgctxt "#30500" +msgid "Playback not supported" +msgstr "Wiedergabe wird nicht unterstützt" + +msgctxt "#30501" +msgid "Playback for item" +msgstr "Wiedergabe für das Medium" + +msgctxt "#30502" +msgid "not supported" +msgstr "wird nicht unterstützt" \ No newline at end of file diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index f8db8a7..6cbc454 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -106,3 +106,15 @@ msgstr "" msgctxt "#30118" msgid "Recently searched shows" msgstr "" + +msgctxt "#30500" +msgid "Playback not supported" +msgstr "" + +msgctxt "#30501" +msgid "Playback for item" +msgstr "" + +msgctxt "#30502" +msgid "not supported" +msgstr "" \ No newline at end of file diff --git a/resources/language/resource.language.fr_fr/strings.po b/resources/language/resource.language.fr_fr/strings.po index b892378..6b52719 100644 --- a/resources/language/resource.language.fr_fr/strings.po +++ b/resources/language/resource.language.fr_fr/strings.po @@ -106,3 +106,15 @@ msgstr "Audios récemment recherchées" msgctxt "#30118" msgid "Recently searched shows" msgstr "Émissions récemment recherchées" + +msgctxt "#30500" +msgid "Playback not supported" +msgstr "La lecture n'est pas prise en charge" + +msgctxt "#30501" +msgid "Playback for item" +msgstr "La lecture de l'élément" + +msgctxt "#30502" +msgid "not supported" +msgstr "n'est pas prise en charge" \ No newline at end of file diff --git a/resources/language/resource.language.it_it/strings.po b/resources/language/resource.language.it_it/strings.po index e3a098b..7f929aa 100644 --- a/resources/language/resource.language.it_it/strings.po +++ b/resources/language/resource.language.it_it/strings.po @@ -106,3 +106,15 @@ msgstr "Audios cercato di recente" msgctxt "#30118" msgid "Recently searched shows" msgstr "Show cercato di recente" + +msgctxt "#30500" +msgid "Playback not supported" +msgstr "Riproduzione non supportata" + +msgctxt "#30501" +msgid "Playback for item" +msgstr "Riproduzione per l'elemento" + +msgctxt "#30502" +msgid "not supported" +msgstr "non supportata" \ No newline at end of file From 55ad1b4dd2e6fe97dfa1a1a8256afd183cd8e075 Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Wed, 4 May 2022 06:11:43 +0200 Subject: [PATCH 6/8] Prevent broken next page urls to create a next page button --- lib/srgssr.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/srgssr.py b/lib/srgssr.py index 06910d5..9a5ee90 100644 --- a/lib/srgssr.py +++ b/lib/srgssr.py @@ -388,6 +388,14 @@ def build_menu_apiv3(self, queries, mode=1000, page=1, page_hash=None, if cursor: if page == 0 or page == '0': return + + # Next page urls containing the string 'urns=' do not work + # properly. So in this case prevent the next page button from + # being created. Note that might lead to not having a next + # page butten where there should be one. + if 'urns=' in cursor: + return + if page: url = self.build_url( mode=mode, name=queries, page=int(page)+1, From c1e004f802e7a816fd1e6f96a98a5e18853c992d Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Wed, 4 May 2022 06:16:35 +0200 Subject: [PATCH 7/8] Undo special handling of HeroStage elements Since we now exclude all the broken next page buttons from being created, it is not necessary anymore to prevent the next page button for the HeroStage from being built separately. --- lib/srgssr.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/srgssr.py b/lib/srgssr.py index 9a5ee90..8b85451 100644 --- a/lib/srgssr.py +++ b/lib/srgssr.py @@ -505,7 +505,6 @@ def build_menu_from_page(self, url, path): id = elem['id'] section_type = elem['sectionType'] title = utils.try_get(elem, ('representation', 'title')) - page = 1 # TODO: Are there more section types to consider? # there is 'MediaSectionWithShow' if section_type in ('MediaSection', 'ShowSection'): @@ -514,9 +513,6 @@ def build_menu_from_page(self, url, path): elem, ('representation', 'name') ) == 'HeroStage': title = self.language(30053) - # NOTE: Accesing the next page fails for this type. - # So do not generate a next page button: - page = 0 if not title: continue list_item = xbmcgui.ListItem(label=title) @@ -528,7 +524,7 @@ def build_menu_from_page(self, url, path): name = f'media-section?sectionId={id}' elif section_type == 'ShowSection': name = f'show-section?sectionId={id}' - url = self.build_url(mode=1000, name=name, page=page) + url = self.build_url(mode=1000, name=name, page=1) xbmcplugin.addDirectoryItem( self.handle, url, list_item, isFolder=True) except Exception: From 2c80f13f676d36ee264ee45338da8ded60e10700 Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Thu, 5 May 2022 06:25:42 +0200 Subject: [PATCH 8/8] Add support for query `media-section-with-show` --- lib/srgssr.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/srgssr.py b/lib/srgssr.py index 8b85451..58569f9 100644 --- a/lib/srgssr.py +++ b/lib/srgssr.py @@ -347,6 +347,7 @@ def build_menu_apiv3(self, queries, mode=1000, page=1, page_hash=None, data = json.loads(self.open_url(self.apiv3_url + query)) if data: data = utils.try_get(data, ['data', 'data'], list, []) or \ + utils.try_get(data, ['data', 'medias'], list, []) or \ utils.try_get(data, ['data', 'results'], list, []) or \ utils.try_get(data, 'data', list, []) for item in data: @@ -379,6 +380,7 @@ def build_menu_apiv3(self, queries, mode=1000, page=1, page_hash=None, return items = utils.try_get(data, 'data', list, []) or \ + utils.try_get(data, 'medias', list, []) or \ utils.try_get(data, 'results', list, []) or data for item in items: @@ -505,9 +507,8 @@ def build_menu_from_page(self, url, path): id = elem['id'] section_type = elem['sectionType'] title = utils.try_get(elem, ('representation', 'title')) - # TODO: Are there more section types to consider? - # there is 'MediaSectionWithShow' - if section_type in ('MediaSection', 'ShowSection'): + if section_type in ('MediaSection', 'ShowSection', + 'MediaSectionWithShow'): if section_type == 'MediaSection' and not title and \ utils.try_get( elem, ('representation', 'name') @@ -524,6 +525,8 @@ def build_menu_from_page(self, url, path): name = f'media-section?sectionId={id}' elif section_type == 'ShowSection': name = f'show-section?sectionId={id}' + elif section_type == 'MediaSectionWithShow': + name = f'media-section-with-show?sectionId={id}' url = self.build_url(mode=1000, name=name, page=1) xbmcplugin.addDirectoryItem( self.handle, url, list_item, isFolder=True)