summaryrefslogtreecommitdiff
path: root/plugin.video.vrt.nu/resources/lib/vrtplayer
diff options
context:
space:
mode:
authorpietje666 <martijn.moreel@gmail.com>2017-05-05 10:57:20 +0200
committerMartijn Kaijser <martijn@xbmc.org>2017-05-05 09:57:20 +0100
commita0610f87083df8e16c8c5886ca0a0942ffd52562 (patch)
tree485e2534b8b866740890f4e23d24dc43b9e8ef14 /plugin.video.vrt.nu/resources/lib/vrtplayer
parent650a2595e8041a0ed4aa7f297981d383e391ea0c (diff)
[plugin.video.vrt.nu] 0.0.1 (#1189)
[plugin.video.vrt.nu] 0.0.1
Diffstat (limited to 'plugin.video.vrt.nu/resources/lib/vrtplayer')
-rw-r--r--plugin.video.vrt.nu/resources/lib/vrtplayer/__init__.py0
-rw-r--r--plugin.video.vrt.nu/resources/lib/vrtplayer/actions.py9
-rw-r--r--plugin.video.vrt.nu/resources/lib/vrtplayer/metadatacollector.py59
-rw-r--r--plugin.video.vrt.nu/resources/lib/vrtplayer/metadatacreator.py30
-rw-r--r--plugin.video.vrt.nu/resources/lib/vrtplayer/statichelper.py13
-rw-r--r--plugin.video.vrt.nu/resources/lib/vrtplayer/urltostreamservice.py83
-rw-r--r--plugin.video.vrt.nu/resources/lib/vrtplayer/vrtplayer.py216
7 files changed, 410 insertions, 0 deletions
diff --git a/plugin.video.vrt.nu/resources/lib/vrtplayer/__init__.py b/plugin.video.vrt.nu/resources/lib/vrtplayer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugin.video.vrt.nu/resources/lib/vrtplayer/__init__.py
diff --git a/plugin.video.vrt.nu/resources/lib/vrtplayer/actions.py b/plugin.video.vrt.nu/resources/lib/vrtplayer/actions.py
new file mode 100644
index 0000000..7e50659
--- /dev/null
+++ b/plugin.video.vrt.nu/resources/lib/vrtplayer/actions.py
@@ -0,0 +1,9 @@
+LISTING_AZ = 'listingaz'
+LISTING_CATEGORIES = 'listingcategories'
+LISTING_LIVE = 'listinglive'
+
+GET_EPISODES = 'getepisodes'
+GET_CATEGORY_EPISODES = 'getcategoryepisodes'
+
+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
new file mode 100644
index 0000000..7dff20d
--- /dev/null
+++ b/plugin.video.vrt.nu/resources/lib/vrtplayer/metadatacollector.py
@@ -0,0 +1,59 @@
+import re
+from resources.lib.vrtplayer import metadatacreator
+from resources.lib.vrtplayer import statichelper
+
+
+class MetadataCollector:
+
+ def __init__(self):
+ pass
+
+ def get_single_layout_episode_metadata(self, soup):
+ metadata_creator = metadatacreator.MetadataCreator()
+ metadata_creator.duration = self.__get_episode_duration(soup)
+ metadata_creator.plot = self.get_plot(soup)
+ return metadata_creator.get_video_dictionary()
+
+ def get_multiple_layout_episode_metadata(self, soup):
+ metadata_creator = metadatacreator.MetadataCreator()
+ metadata_creator.duration = self.__get_multiple_layout_episode_duration(soup)
+ return metadata_creator.get_video_dictionary()
+
+ @staticmethod
+ def __get_episode_duration( soup):
+ duration = None
+ duration_item = soup.find(class_="content__duration")
+ if duration_item is not None:
+ minutes = re.findall("\d+", duration_item.text)
+ if len(minutes) != 0:
+ duration = statichelper.minutes_string_to_seconds_int(minutes[0])
+ return duration
+
+ @staticmethod
+ def get_az_metadata(tile):
+ metadata_creator = metadatacreator.MetadataCreator()
+ description = ""
+ description_item = tile.find(class_="tile__description")
+ if description_item is not None:
+ p_item = description_item.find("p")
+ if p_item is not None:
+ description = p_item.text.strip()
+ metadata_creator.plot = description
+ return metadata_creator.get_video_dictionary()
+
+ @staticmethod
+ def get_plot(soup):
+ description = ""
+ description_item = soup.find(class_="content__shortdescription")
+ if description_item is not None:
+ description = description_item.text
+ return description
+
+ @staticmethod
+ def __get_multiple_layout_episode_duration(soup):
+ seconds = None
+ minutes_element = soup.find("abbr", {"title": "minuten"})
+ if minutes_element is not None and minutes_element.parent is not None:
+ minutes = minutes_element.parent.next_element
+ seconds = statichelper.minutes_string_to_seconds_int(minutes)
+ return seconds
diff --git a/plugin.video.vrt.nu/resources/lib/vrtplayer/metadatacreator.py b/plugin.video.vrt.nu/resources/lib/vrtplayer/metadatacreator.py
new file mode 100644
index 0000000..7a05913
--- /dev/null
+++ b/plugin.video.vrt.nu/resources/lib/vrtplayer/metadatacreator.py
@@ -0,0 +1,30 @@
+class MetadataCreator:
+
+ def __init__(self):
+ self._duration = None
+ self._plot = None
+
+ @property
+ def duration(self):
+ return self._duration
+
+ @duration.setter
+ def duration(self, value):
+ self._duration = value
+
+ @property
+ def plot(self):
+ return self._plot
+
+ @plot.setter
+ def plot(self, value):
+ self._plot = value.strip()
+
+ 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
+
diff --git a/plugin.video.vrt.nu/resources/lib/vrtplayer/statichelper.py b/plugin.video.vrt.nu/resources/lib/vrtplayer/statichelper.py
new file mode 100644
index 0000000..e654804
--- /dev/null
+++ b/plugin.video.vrt.nu/resources/lib/vrtplayer/statichelper.py
@@ -0,0 +1,13 @@
+def minutes_string_to_seconds_int(minutes):
+ try:
+ return int(minutes) * 60
+ except ValueError:
+ return None
+
+
+def replace_newlines_and_strip(text):
+ return text.replace("\n", "").strip()
+
+
+def replace_double_slashes_with_https(url):
+ return url.replace("//", "https://")
diff --git a/plugin.video.vrt.nu/resources/lib/vrtplayer/urltostreamservice.py b/plugin.video.vrt.nu/resources/lib/vrtplayer/urltostreamservice.py
new file mode 100644
index 0000000..1d3cecc
--- /dev/null
+++ b/plugin.video.vrt.nu/resources/lib/vrtplayer/urltostreamservice.py
@@ -0,0 +1,83 @@
+import xbmcgui
+import requests
+import xbmc
+import json
+import cookielib
+import urlparse
+import os
+from resources.lib.helperobjects import helperobjects
+
+
+class UrlToStreamService:
+
+ _API_KEY ="3_qhEcPa5JGFROVwu5SWKqJ4mVOIkwlFNMSKwzPDAh8QZOtHqu6L4nD5Q7lk0eXOOG"
+ _BASE_GET_STREAM_URL_PATH = "https://mediazone.vrt.be/api/v1/vrtvideo/assets/"
+
+ def __init__(self, vrt_base, vrtnu_base_url, addon):
+ self._vrt_base = vrt_base
+ self._vrtnu_base_url = vrtnu_base_url
+ self._addon = addon
+ self._session = requests.session()
+
+ def get_stream_from_url(self, url):
+ cred = helperobjects.Credentials(self._addon)
+ if not cred.are_filled_in():
+ self._addon.openSettings()
+ cred.reload()
+ url = urlparse.urljoin(self._vrt_base, url)
+ r = self._session.post("https://accounts.eu1.gigya.com/accounts.login",
+ {'loginID': cred.username, 'password': cred.password, 'APIKey': self._API_KEY,
+ 'targetEnv': 'jssdk',
+ 'includeSSOToken': 'true',
+ 'authMode': 'cookie'})
+
+ logon_json = r.json()
+ if logon_json['errorCode'] == 0:
+ uid = logon_json['UID']
+ sig = logon_json['UIDSignature']
+ ts = logon_json['signatureTimestamp']
+
+ headers = {'Content-Type': 'application/json', 'Referer': self._vrtnu_base_url}
+ data = '{"uid": "%s", ' \
+ '"uidsig": "%s", ' \
+ '"ts": "%s", ' \
+ '"email": "%s"}' % (uid, sig, ts, cred.username)
+
+ response = self._session.post("https://token.vrt.be", data=data, headers=headers)
+ securevideo_url = "{0}.securevideo.json".format(self.__cut_slash_if_present(url))
+ securevideo_response = self._session.get(securevideo_url, cookies=response.cookies)
+ json = securevideo_response.json()
+
+ mzid = list(json
+ .values())[0]['mzid']
+ final_url = urlparse.urljoin(self._BASE_GET_STREAM_URL_PATH, mzid)
+
+ stream_response = self._session.get(final_url)
+ hls = self.__get_hls(stream_response.json()['targetUrls']).replace("https", "http")
+ subtitle = None
+ if self._addon.getSetting("showsubtitles") == "true":
+ subtitle = self.__get_subtitle(stream_response.json()['subtitleUrls'])
+ return helperobjects.StreamURLS(hls, subtitle)
+ else:
+ xbmcgui.Dialog().ok(self._addon.getAddonInfo('name'),
+ self._addon.getLocalizedString(32051),
+ self._addon.getLocalizedString(32052))
+
+ @staticmethod
+ def __get_hls(dictionary):
+ for item in dictionary:
+ if item['type'] == 'HLS':
+ return item['url']
+
+ @staticmethod
+ def __get_subtitle(dictionary):
+ for item in dictionary:
+ if item['type'] == 'CLOSED':
+ return item['url']
+
+ @staticmethod
+ def __cut_slash_if_present(url):
+ if url.endswith('/'):
+ return url[:-1]
+ else:
+ return url
diff --git a/plugin.video.vrt.nu/resources/lib/vrtplayer/vrtplayer.py b/plugin.video.vrt.nu/resources/lib/vrtplayer/vrtplayer.py
new file mode 100644
index 0000000..899089d
--- /dev/null
+++ b/plugin.video.vrt.nu/resources/lib/vrtplayer/vrtplayer.py
@@ -0,0 +1,216 @@
+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 = "http://live.stream.vrt.be/vrt_video1_live/smil:vrt_video1_live.smil/playlist.m3u8"
+ _CANVAS_LIVESTREAM_ = "http://live.stream.vrt.be/vrt_video2_live/smil:vrt_video2_live.smil/playlist.m3u8"
+ _KETNET_VRT = "http://live.stream.vrt.be/vrt_events3_live/smil:vrt_events3_live.smil/playlist.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
+ self.metadata_collector = metadatacollector.MetadataCollector()
+ self._addon = xbmcaddon.Addon()
+ self._addon_path = self._addon.getAddonInfo("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_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
+
+ 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
+
+ def get_video_category_episodes(self, path):
+ category = path.split('/')[-2]
+ joined_url = self._VRTNU_SEARCH_URL + category
+ response = requests.get(joined_url)
+ programs = response.json()
+ listing = []
+ for program in programs:
+ title = program["title"]
+ plot = BeautifulSoup(program["description"], "html.parser").text
+ thumbnail = statichelper.replace_double_slashes_with_https(program["thumbnail"])
+
+ metadata_creator = metadatacreator.MetadataCreator()
+ metadata_creator.plot = plot
+ video_dictionary = metadata_creator.get_video_dictionary()
+ #cut vrtbase url off since it will be added again when searching for episodes (with a-z we dont have the
+ # 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)
+ 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},
+ 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):
+ 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))
+
+ xbmcplugin.addDirectoryItems(self._handle, listing, len(listing))
+ xbmcplugin.endOfDirectory(self._handle)
+
+ def get_multiple_videos(self, soup):
+ items = []
+ episode_list = soup.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
+
+ 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')
+
+ video_dictionary = self.metadata_collector.get_single_layout_episode_metadata(soup)
+
+ 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)
+
+ def __get_media(self, file_name):
+ return os.path.join(self._addon_path, 'resources', 'media', file_name)
+
+ @staticmethod
+ 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)
+ 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
+
+