From 0b73a22d9f37ce3c331c9cba2e6d3dfa863e05dd Mon Sep 17 00:00:00 2001 From: pietje666 Date: Fri, 21 Jul 2017 00:42:20 +0200 Subject: [plugin.video.vrt.nu] 0.0.4 (#1331) --- .../language/resource.language.en_gb/strings.po | 11 +- .../resources/lib/helperobjects/helperobjects.py | 10 +- .../resources/lib/kodiwrappers/__init__.py | 0 .../resources/lib/kodiwrappers/kodiwrapper.py | 46 +++++ .../resources/lib/vrtplayer/actions.py | 5 +- .../resources/lib/vrtplayer/metadatacollector.py | 20 +- .../resources/lib/vrtplayer/metadatacreator.py | 20 +- .../resources/lib/vrtplayer/vrtplayer.py | 227 +++++++++------------ plugin.video.vrt.nu/resources/media/Sporza.png | Bin 0 -> 4707 bytes 9 files changed, 196 insertions(+), 143 deletions(-) create mode 100644 plugin.video.vrt.nu/resources/lib/kodiwrappers/__init__.py create mode 100644 plugin.video.vrt.nu/resources/lib/kodiwrappers/kodiwrapper.py create mode 100644 plugin.video.vrt.nu/resources/media/Sporza.png (limited to 'plugin.video.vrt.nu/resources') diff --git a/plugin.video.vrt.nu/resources/language/resource.language.en_gb/strings.po b/plugin.video.vrt.nu/resources/language/resource.language.en_gb/strings.po index 23e6df0..e6e9b85 100644 --- a/plugin.video.vrt.nu/resources/language/resource.language.en_gb/strings.po +++ b/plugin.video.vrt.nu/resources/language/resource.language.en_gb/strings.po @@ -53,8 +53,8 @@ msgid "Livestreams" msgstr "Livestreams" msgctxt "#32101" -msgid "één" -msgstr "één" +msgid "Eén" +msgstr "Eén" msgctxt "#32102" msgid "Canvas" @@ -62,4 +62,9 @@ msgstr "Canvas" msgctxt "#32103" msgid "Ketnet" -msgstr "Ketnet" \ No newline at end of file +msgstr "Ketnet" + +msgctxt "#32104" +msgid "Sporza" +msgstr "Sporza" + diff --git a/plugin.video.vrt.nu/resources/lib/helperobjects/helperobjects.py b/plugin.video.vrt.nu/resources/lib/helperobjects/helperobjects.py index 974a361..599d3ef 100644 --- a/plugin.video.vrt.nu/resources/lib/helperobjects/helperobjects.py +++ b/plugin.video.vrt.nu/resources/lib/helperobjects/helperobjects.py @@ -1,15 +1,12 @@ class TitleItem: - - - def __init__(self, title, url_dictionary, is_playable, logo, video_dictionary = None): + def __init__(self, title, url_dictionary, is_playable, thumbnail = None, video_dictionary=None): self.title = title self.url_dictionary = url_dictionary self.is_playable = is_playable - self.logo = logo + self.thumbnail = thumbnail self.video_dictionary = video_dictionary - class StreamURLS: def __init__(self, stream_url, subtitle_url): @@ -28,4 +25,5 @@ class Credentials: def reload(self): self.username = self.addon.getSetting("username") - self.password = self.addon.getSetting("password") \ No newline at end of file + self.password = self.addon.getSetting("password") + diff --git a/plugin.video.vrt.nu/resources/lib/kodiwrappers/__init__.py b/plugin.video.vrt.nu/resources/lib/kodiwrappers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugin.video.vrt.nu/resources/lib/kodiwrappers/kodiwrapper.py b/plugin.video.vrt.nu/resources/lib/kodiwrappers/kodiwrapper.py new file mode 100644 index 0000000..588da79 --- /dev/null +++ b/plugin.video.vrt.nu/resources/lib/kodiwrappers/kodiwrapper.py @@ -0,0 +1,46 @@ +import xbmc +import xbmcgui +import xbmcplugin +from urllib import urlencode +from resources.lib.vrtplayer import vrtplayer +from resources.lib.vrtplayer import urltostreamservice + +class KodiWrapper: + + def __init__(self, handle, url, addon): + self._handle = handle + self._url = url + self._addon = addon + + def show_listing(self, list_items): + listing = [] + for title_item in list_items: + list_item = xbmcgui.ListItem(label=title_item.title) + url = self._url + '?' + urlencode(title_item.url_dictionary) + list_item.setProperty('IsPlayable', str(title_item.is_playable)) + + if title_item.thumbnail is not None: + list_item.setArt({'thumb': title_item.thumbnail}) + + list_item.setInfo('video', title_item.video_dictionary) + + listing.append((url, list_item, not title_item.is_playable)) + xbmcplugin.addDirectoryItems(self._handle, listing, len(listing)) + xbmcplugin.addSortMethod(self._handle, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE) + xbmcplugin.endOfDirectory(self._handle) + + def play_video(self, path): + stream_service = urltostreamservice.UrlToStreamService(vrtplayer.VRTPlayer._VRT_BASE, + vrtplayer.VRTPlayer._VRTNU_BASE_URL, + self._addon) + stream = stream_service.get_stream_from_url(path) + if stream is not None: + play_item = xbmcgui.ListItem(path=stream.stream_url) + play_item.setMimeType('application/x-mpegURL') + if stream.subtitle_url is not None: + play_item.setSubtitles([stream.subtitle_url]) + xbmcplugin.setResolvedUrl(self._handle, True, listitem=play_item) + + def play_livestream(self, path): + play_item = xbmcgui.ListItem(path=path) + xbmcplugin.setResolvedUrl(self._handle, True, listitem=play_item) diff --git a/plugin.video.vrt.nu/resources/lib/vrtplayer/actions.py b/plugin.video.vrt.nu/resources/lib/vrtplayer/actions.py index 7e50659..e8cc597 100644 --- a/plugin.video.vrt.nu/resources/lib/vrtplayer/actions.py +++ b/plugin.video.vrt.nu/resources/lib/vrtplayer/actions.py @@ -2,8 +2,9 @@ LISTING_AZ = 'listingaz' LISTING_CATEGORIES = 'listingcategories' LISTING_LIVE = 'listinglive' -GET_EPISODES = 'getepisodes' -GET_CATEGORY_EPISODES = 'getcategoryepisodes' +LISTING_VIDEOS = 'listingvideos' +LISTING_CATEGORY_VIDEOS = 'listingcategoryvideos' +LISTING_EPISODES = 'listingepisodes' PLAY = 'play' PLAY_LIVE = "playlive" diff --git a/plugin.video.vrt.nu/resources/lib/vrtplayer/metadatacollector.py b/plugin.video.vrt.nu/resources/lib/vrtplayer/metadatacollector.py index 7dff20d..3fd10ce 100644 --- a/plugin.video.vrt.nu/resources/lib/vrtplayer/metadatacollector.py +++ b/plugin.video.vrt.nu/resources/lib/vrtplayer/metadatacollector.py @@ -1,4 +1,5 @@ import re +import time from resources.lib.vrtplayer import metadatacreator from resources.lib.vrtplayer import statichelper @@ -12,6 +13,7 @@ class MetadataCollector: metadata_creator = metadatacreator.MetadataCreator() metadata_creator.duration = self.__get_episode_duration(soup) metadata_creator.plot = self.get_plot(soup) + metadata_creator.datetime = self.get_broadcast_datetime(soup) return metadata_creator.get_video_dictionary() def get_multiple_layout_episode_metadata(self, soup): @@ -20,7 +22,7 @@ class MetadataCollector: return metadata_creator.get_video_dictionary() @staticmethod - def __get_episode_duration( soup): + def __get_episode_duration(soup): duration = None duration_item = soup.find(class_="content__duration") if duration_item is not None: @@ -49,6 +51,22 @@ class MetadataCollector: description = description_item.text return description + @staticmethod + def get_broadcast_datetime(soup): + broadcast_datetime = None + broadcast_date_element = soup.find(class_="content__broadcastdate") + + if broadcast_date_element is None: + return broadcast_datetime + + time_element = broadcast_date_element.find("time") + + if time_element is None: + return broadcast_datetime + + broadcast_datetime = time.strptime(time_element["datetime"][:18], "%Y-%m-%dT%H:%M:%S") + return broadcast_datetime + @staticmethod def __get_multiple_layout_episode_duration(soup): seconds = None diff --git a/plugin.video.vrt.nu/resources/lib/vrtplayer/metadatacreator.py b/plugin.video.vrt.nu/resources/lib/vrtplayer/metadatacreator.py index 7a05913..44a3c35 100644 --- a/plugin.video.vrt.nu/resources/lib/vrtplayer/metadatacreator.py +++ b/plugin.video.vrt.nu/resources/lib/vrtplayer/metadatacreator.py @@ -1,8 +1,12 @@ +import time + + class MetadataCreator: def __init__(self): self._duration = None self._plot = None + self._datetime = None @property def duration(self): @@ -20,11 +24,25 @@ class MetadataCreator: def plot(self, value): self._plot = value.strip() + @property + def datetime(self): + return self._datetime + + @datetime.setter + def datetime (self, value): + self._datetime = value + def get_video_dictionary(self): video_dictionary = dict() + if self.plot is not None: video_dictionary["plot"] = self.plot + if self.duration is not None: video_dictionary["duration"] = self.duration - return video_dictionary + if self.datetime is not None: + video_dictionary["date"] = time.strftime("%d.%m.%Y", self.datetime) + video_dictionary["shortdate"] = time.strftime("%d/%m", self.datetime) + + return video_dictionary diff --git a/plugin.video.vrt.nu/resources/lib/vrtplayer/vrtplayer.py b/plugin.video.vrt.nu/resources/lib/vrtplayer/vrtplayer.py index 565597d..6543fe0 100644 --- a/plugin.video.vrt.nu/resources/lib/vrtplayer/vrtplayer.py +++ b/plugin.video.vrt.nu/resources/lib/vrtplayer/vrtplayer.py @@ -1,84 +1,46 @@ -import sys -import xbmc import os -import xbmcgui -import xbmcplugin -import xbmcaddon import requests -import re -import time -from urlparse import parse_qsl from urlparse import urljoin -from urllib import urlencode from bs4 import BeautifulSoup from bs4 import SoupStrainer -from resources.lib.vrtplayer import urltostreamservice from resources.lib.helperobjects import helperobjects from resources.lib.vrtplayer import metadatacollector from resources.lib.vrtplayer import statichelper from resources.lib.vrtplayer import actions from resources.lib.vrtplayer import metadatacreator - class VRTPlayer: - _VRT_LIVESTREAM_URL = "https://live-w.lwc.vrtcdn.be/groupc/live/d05012c2-6a5d-49ff-a711-79b32684615b/live.isml/.m3u8" + #Url met de urls https://services.vrt.be/videoplayer/r/live.json + _EEN_LIVESTREAM = "https://live-w.lwc.vrtcdn.be/groupc/live/d05012c2-6a5d-49ff-a711-79b32684615b/live.isml/.m3u8" _CANVAS_LIVESTREAM_ = "https://live-w.lwc.vrtcdn.be/groupc/live/905b0602-9719-4d14-ae2a-a9b459630653/live.isml/.m3u8" - _KETNET_VRT = "https://live-w.lwc.vrtcdn.be/groupc/live/8b898c7d-adf7-4d44-ab82-b5bb3a069989/live.isml/.m3u8" + _KETNET_LIVESTREAM = "https://live-w.lwc.vrtcdn.be/groupc/live/8b898c7d-adf7-4d44-ab82-b5bb3a069989/live.isml/.m3u8" + _SPORZA_LIVESTREAM = "https://live-w.lwc.vrtcdn.be/groupa/live/bf2f7c79-1d77-4cdc-80e8-47ae024f30ba/live.isml/.m3u8" _VRT_BASE = "https://www.vrt.be/" _VRTNU_BASE_URL = urljoin(_VRT_BASE, "/vrtnu/") _VRTNU_SEARCH_URL = "https://search.vrt.be/suggest?facets[categories]=" - def __init__(self, handle, url): - self._handle = handle - self._url = url + def __init__(self, addon, addon_path): self.metadata_collector = metadatacollector.MetadataCollector() - self._addon = xbmcaddon.Addon() - self._addon_path = self._addon.getAddonInfo("path") + self._addon = addon + self._addon_path = addon_path - def show_listing(self, list_items): - listing = [] - for title_item in list_items: - list_item = xbmcgui.ListItem(label=title_item.title) - url = self._url + '?' + urlencode(title_item.url_dictionary) - list_item.setProperty('IsPlayable', str(title_item.is_playable)) - list_item.setArt({'thumb': title_item.logo}) - list_item.setInfo('video', title_item.video_dictionary) - listing.append((url, list_item, not title_item.is_playable)) - xbmcplugin.addDirectoryItems(self._handle, listing, len(listing)) - xbmcplugin.addSortMethod(self._handle, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE) - xbmcplugin.endOfDirectory(self._handle) + def get_main_menu_items(self): + return {helperobjects.TitleItem(self._addon.getLocalizedString(32091), {'action': actions.LISTING_AZ}, False, + None), + helperobjects.TitleItem(self._addon.getLocalizedString(32092), {'action': actions.LISTING_CATEGORIES}, + False, None), + helperobjects.TitleItem(self._addon.getLocalizedString(32100), {'action': actions.LISTING_LIVE}, False, + None)} def get_az_menu_items(self): - url = urljoin(self._VRTNU_BASE_URL, "./a-z/") - response = requests.get(url) - tiles = SoupStrainer('a', {"class": "tile"}) - soup = BeautifulSoup(response.content, "html.parser", parse_only=tiles) - listing = [] - for tile in soup.find_all(class_="tile"): - link_to_video = tile["href"] - video_dictionary = self.metadata_collector.get_az_metadata(tile) - thumbnail, title = self.__get_thumbnail_and_title(tile) - item = helperobjects.TitleItem(title, {'action': actions.GET_EPISODES, 'video': link_to_video}, False - , thumbnail, - video_dictionary) - listing.append(item) - return listing + joined_url = urljoin(self._VRTNU_BASE_URL, "./a-z/") + return self.__get_menu_items(joined_url, {"class": "tile"}, actions.LISTING_VIDEOS, self.metadata_collector.get_az_metadata) def get_category_menu_items(self): joined_url = urljoin(self._VRTNU_BASE_URL, "./categorieen/") - response = requests.get(joined_url) - tiles = SoupStrainer('a', {"class": "tile tile--category"}) - soup = BeautifulSoup(response.content, "html.parser", parse_only=tiles) - listing = [] - for tile in soup.find_all(class_="tile"): - link_to_video = tile["href"] - thumbnail, title = self.__get_thumbnail_and_title(tile) - item = helperobjects.TitleItem(title, {'action': actions.GET_CATEGORY_EPISODES, 'video': link_to_video}, - False, thumbnail) - listing.append(item) - return listing + return self.__get_menu_items(joined_url, {"class": "tile tile--category"}, actions.LISTING_CATEGORY_VIDEOS) def get_video_category_episodes(self, path): category = path.split('/')[-2] @@ -98,119 +60,124 @@ class VRTPlayer: # full url) link_to_video = statichelper.replace_double_slashes_with_https(program["targetUrl"]).replace(self._VRT_BASE, "") - item = helperobjects.TitleItem(title, {'action': actions.GET_EPISODES, 'video': link_to_video}, - False, thumbnail, video_dictionary) + item = helperobjects.TitleItem(title, {'action': actions.LISTING_VIDEOS, 'video': link_to_video}, + False, thumbnail, video_dictionary) listing.append(item) return listing - - - def get_main_menu_items(self): - return {helperobjects.TitleItem(self._addon.getLocalizedString(32091), {'action': actions.LISTING_AZ}, False, - None), - helperobjects.TitleItem(self._addon.getLocalizedString(32092), {'action': actions.LISTING_CATEGORIES}, - False, None), - helperobjects.TitleItem(self._addon.getLocalizedString(32100), {'action': actions.LISTING_LIVE}, False, - None)} def get_livestream_items(self): return {helperobjects.TitleItem(self._addon.getLocalizedString(32101), - {'action': actions.PLAY_LIVE, 'video': self._VRT_LIVESTREAM_URL}, + {'action': actions.PLAY_LIVE, 'video': self._EEN_LIVESTREAM}, True, self.__get_media("een.png")), helperobjects.TitleItem(self._addon.getLocalizedString(32102), {'action': actions.PLAY_LIVE, 'video': self._CANVAS_LIVESTREAM_}, True, self.__get_media("canvas.png")), helperobjects.TitleItem(self._addon.getLocalizedString(32103), - {'action': actions.PLAY_LIVE, 'video': self._KETNET_VRT}, - True, self.__get_media("ketnet.png"))} - - def get_video_episodes(self, path): + {'action': actions.PLAY_LIVE, 'video': self._KETNET_LIVESTREAM}, + True, self.__get_media("ketnet.png")), + helperobjects.TitleItem(self._addon.getLocalizedString(32104), + {'action': actions.PLAY_LIVE, 'video': self._SPORZA_LIVESTREAM}, + True, self.__get_media("sporza.png")) + } + + def get_videos(self, path): url = urljoin(self._VRT_BASE, path) #xbmc.log(url, xbmc.LOGWARNING) # go to url.relevant gets redirected and go on with this url relevant_path = requests.get(url) response = requests.get(relevant_path.url) soup = BeautifulSoup(response.content, "html.parser") - listing = [] - episodes = soup.find_all(class_="tile") - if len(episodes) != 0: - listing.extend(self.get_multiple_videos(soup)) - else: - li, url = self.get_single_video(relevant_path.url, soup) - listing.append((url, li, False)) + title_items = [] + episodes_list = soup.find(class_="episodeslist") + option_tags = [] - xbmcplugin.addDirectoryItems(self._handle, listing, len(listing)) - xbmcplugin.endOfDirectory(self._handle) + if episodes_list is not None: + option_tags.extend(episodes_list.find_all("option")) - def get_multiple_videos(self, soup): - items = [] - episode_list = soup.find("div", {"id": "episodelist__slider"}) + if len(option_tags) != 0: + title_items.extend(self.__get_episodes(option_tags)) + else: + episodes = soup.find_all(class_="tile") + if len(episodes) != 0: + title_items.extend(self.__get_multiple_videos(soup)) + else: + title_items.extend(self.__get_single_video(relevant_path.url, soup)) + return title_items + + def __get_episodes(self, option_tags): + title_items = [] + for option_tag in option_tags: + title = statichelper.replace_newlines_and_strip(option_tag.text) + path = option_tag['data-href'] + title_items.append(helperobjects.TitleItem(title, {"action" : actions.LISTING_VIDEOS, 'video':path}, False)) + return title_items + + + def __get_multiple_videos(self, tiles): + title_items = [] + episode_list = tiles.find("div", {"id": "episodelist__slider"}) for tile in episode_list.find_all(class_="tile"): - li = self.__get_item(tile, "true") - if li is not None: - link_to_video = tile["href"] - video_dictionary = self.metadata_collector.get_multiple_layout_episode_metadata(tile) - li.setInfo('video', video_dictionary) - url = '{0}?action=play&video={1}'.format(self._url, link_to_video) - items.append((url, li, False)) - return items + thumbnail = VRTPlayer.__format_image_url(tile) + found_element = tile.find(class_="tile__title") - def get_single_video(self, path, soup): - vrt_video = soup.find(class_="vrtvideo") - thumbnail = VRTPlayer.format_image_url(vrt_video) - li = xbmcgui.ListItem(soup.find(class_="content__title").text) - li.setProperty('IsPlayable', 'true') + if found_element is not None: + title = statichelper.replace_newlines_and_strip(found_element.contents[0]) + broadcast_date_tag = tile.find(class_="tile__broadcastdate--mobile") + if broadcast_date_tag is not None: + title = broadcast_date_tag.text + " " + title + + path = tile["href"] + video_dictionary = self.metadata_collector.get_multiple_layout_episode_metadata(tile) + title_items.append(helperobjects.TitleItem(title, {"action": "play", "video": path}, True, thumbnail, video_dictionary)) + return title_items + + def __get_single_video(self, path, soup): + title_items = [] video_dictionary = self.metadata_collector.get_single_layout_episode_metadata(soup) + list_item_title = soup.find(class_="content__title").text + + if "shortdate" in video_dictionary: + video_dictionary["shortdate"] + " " + list_item_title - li.setInfo('video', video_dictionary) - li.setArt({'thumb': thumbnail}) - url = '{0}?action=play&video={1}'.format(self._url, path) - return li, url - - def play_video(self, path): - stream_service = urltostreamservice.UrlToStreamService(self._VRT_BASE, - self._VRTNU_BASE_URL, - self._addon) - stream = stream_service.get_stream_from_url(path) - if stream is not None: - play_item = xbmcgui.ListItem(path=stream.stream_url) - play_item.setMimeType('application/x-mpegURL') - if stream.subtitle_url is not None: - play_item.setSubtitles([stream.subtitle_url]) - xbmcplugin.setResolvedUrl(self._handle, True, listitem=play_item) - - def play_livestream(self, path): - play_item = xbmcgui.ListItem(path=path) - xbmcplugin.setResolvedUrl(self._handle, True, listitem=play_item) + vrt_video = soup.find(class_="vrtvideo") + thumbnail = VRTPlayer.__format_image_url(vrt_video) + title_items.append(helperobjects.TitleItem(list_item_title, {"action": "play", "video": path}, True, thumbnail, video_dictionary)) + return title_items def __get_media(self, file_name): return os.path.join(self._addon_path, 'resources', 'media', file_name) + def __get_menu_items(self, url, soupstrainer_parser_selector, routing_action, video_dictionary_action=None): + response = requests.get(url) + tiles = SoupStrainer('a', soupstrainer_parser_selector) + soup = BeautifulSoup(response.content, "html.parser", parse_only=tiles) + listing = [] + for tile in soup.find_all(class_="tile"): + link_to_video = tile["href"] + thumbnail, title = self.__get_thumbnail_and_title(tile) + video_dictionary = None + if video_dictionary_action is not None: + video_dictionary = video_dictionary_action(tile) + + item = helperobjects.TitleItem(title, {'action': routing_action, 'video': link_to_video}, + False, thumbnail, video_dictionary) + listing.append(item) + return listing + @staticmethod - def format_image_url(element): + def __format_image_url(element): raw_thumbnail = element.find("img")['srcset'].split('1x,')[0] return statichelper.replace_double_slashes_with_https(raw_thumbnail) @staticmethod def __get_thumbnail_and_title(element): - thumbnail = VRTPlayer.format_image_url(element) + thumbnail = VRTPlayer.__format_image_url(element) found_element = element.find(class_="tile__title") title = "" if found_element is not None: title = statichelper.replace_newlines_and_strip(found_element.contents[0]) return thumbnail, title - @staticmethod - def __get_item(element, is_playable): - thumbnail = VRTPlayer.format_image_url(element) - found_element = element.find(class_="tile__title") - li = None - if found_element is not None: - stripped = statichelper.replace_newlines_and_strip(found_element.contents[0]) - li = xbmcgui.ListItem(stripped) - li.setProperty('IsPlayable', is_playable) - li.setArt({'thumb': thumbnail}) - return li - diff --git a/plugin.video.vrt.nu/resources/media/Sporza.png b/plugin.video.vrt.nu/resources/media/Sporza.png new file mode 100644 index 0000000..037b053 Binary files /dev/null and b/plugin.video.vrt.nu/resources/media/Sporza.png differ -- cgit v1.2.3