summaryrefslogtreecommitdiff
path: root/plugin.video.dazn/resources/lib
diff options
context:
space:
mode:
Diffstat (limited to 'plugin.video.dazn/resources/lib')
-rw-r--r--plugin.video.dazn/resources/lib/__init__.py0
-rw-r--r--plugin.video.dazn/resources/lib/client.py203
-rw-r--r--plugin.video.dazn/resources/lib/common.py157
-rw-r--r--plugin.video.dazn/resources/lib/context.py59
-rw-r--r--plugin.video.dazn/resources/lib/items.py82
-rw-r--r--plugin.video.dazn/resources/lib/parser.py81
-rw-r--r--plugin.video.dazn/resources/lib/playback.py18
-rw-r--r--plugin.video.dazn/resources/lib/rails.py13
-rw-r--r--plugin.video.dazn/resources/lib/resources.py47
-rw-r--r--plugin.video.dazn/resources/lib/simple_requests/__init__.py6
-rw-r--r--plugin.video.dazn/resources/lib/simple_requests/api.py194
-rw-r--r--plugin.video.dazn/resources/lib/simple_requests/constants/__init__.py1
-rw-r--r--plugin.video.dazn/resources/lib/simple_requests/constants/codes.py5
-rw-r--r--plugin.video.dazn/resources/lib/tiles.py85
14 files changed, 951 insertions, 0 deletions
diff --git a/plugin.video.dazn/resources/lib/__init__.py b/plugin.video.dazn/resources/lib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugin.video.dazn/resources/lib/__init__.py
diff --git a/plugin.video.dazn/resources/lib/client.py b/plugin.video.dazn/resources/lib/client.py
new file mode 100644
index 0000000..b09521e
--- /dev/null
+++ b/plugin.video.dazn/resources/lib/client.py
@@ -0,0 +1,203 @@
+# -*- coding: utf-8 -*-
+
+import simple_requests as requests
+
+class Client:
+
+ def __init__(self, plugin):
+ self.plugin = plugin
+
+ self.DEVICE_ID = self.plugin.get_setting('device_id')
+ self.TOKEN = self.plugin.get_setting('token')
+ self.COUNTRY = self.plugin.get_setting('country')
+ self.LANGUAGE = self.plugin.get_setting('language')
+ self.POST_DATA = {}
+ self.ERRORS = 0
+
+ self.HEADERS = {
+ 'Content-Type': 'application/json',
+ 'Referer': self.plugin.api_base
+ }
+
+ self.PARAMS = {
+ '$format': 'json'
+ }
+
+ self.STARTUP = self.plugin.api_base + 'v2/Startup'
+ self.RAIL = self.plugin.api_base + 'v2/Rail'
+ self.RAILS = self.plugin.api_base + 'v3/Rails'
+ self.EPG = self.plugin.api_base + 'v1/Epg'
+ self.EVENT = self.plugin.api_base + 'v2/Event'
+ self.PLAYBACK = self.plugin.api_base + 'v1/Playback'
+ self.SIGNIN = self.plugin.api_base + 'v3/SignIn'
+ self.SIGNOUT = self.plugin.api_base + 'v1/SignOut'
+ self.REFRESH = self.plugin.api_base + 'v3/RefreshAccessToken'
+ self.PROFILE = self.plugin.api_base + 'v1/UserProfile'
+
+ def content_data(self, url):
+ data = self.request(url)
+ if data.get('odata.error', None):
+ self.errorHandler(data)
+ return data
+
+ def rails(self, id_, params=''):
+ self.PARAMS['groupId'] = id_
+ self.PARAMS['params'] = params
+ return self.content_data(self.RAILS)
+
+ def rail(self, id_, params=''):
+ self.PARAMS['id'] = id_
+ self.PARAMS['params'] = params
+ return self.content_data(self.RAIL)
+
+ def epg(self, params):
+ self.PARAMS['date'] = params
+ return self.content_data(self.EPG)
+
+ def event(self, id_):
+ self.PARAMS['Id'] = id_
+ return self.content_data(self.EVENT)
+
+ def playback_data(self, id_):
+ self.POST_DATA = {
+ 'AssetId': id_,
+ 'Format': 'MPEG-DASH',
+ 'PlayerId': 'DAZN-' + self.DEVICE_ID,
+ 'Secure': 'true',
+ 'PlayReadyInitiator': 'false',
+ 'BadManifests': [],
+ 'LanguageCode': self.LANGUAGE,
+ }
+ return self.request(self.PLAYBACK)
+
+ def playback(self, id_):
+ data = self.playback_data(id_)
+ if data.get('odata.error', None):
+ self.errorHandler(data)
+ if self.TOKEN:
+ data = self.playback_data(id_)
+ return data
+
+ def userProfile(self):
+ data = self.request(self.PROFILE)
+ if data.get('odata.error', None):
+ self.errorHandler(data)
+ else:
+ self.plugin.set_setting('viewer_id', data['ViewerId'])
+
+ def setToken(self, auth, result):
+ self.plugin.log('[{0}] signin: {1}'.format(self.plugin.addon_id, result))
+ if auth and result == 'SignedIn':
+ self.TOKEN = auth['Token']
+ else:
+ if result == 'HardOffer':
+ self.plugin.dialog_ok(30161)
+ self.signOut()
+ self.plugin.set_setting('token', self.TOKEN)
+
+ def signIn(self):
+ credentials = self.plugin.get_credentials()
+ if credentials:
+ self.POST_DATA = {
+ 'Email': credentials['email'],
+ 'Password': credentials['password'],
+ 'DeviceId': self.DEVICE_ID,
+ 'Platform': 'web'
+ }
+ data = self.request(self.SIGNIN)
+ if data.get('odata.error', None):
+ self.errorHandler(data)
+ else:
+ self.setToken(data['AuthToken'], data.get('Result', 'SignInError'))
+ self.userProfile()
+ else:
+ self.plugin.dialog_ok(30004)
+
+ def signOut(self):
+ self.POST_DATA = {
+ 'DeviceId': self.DEVICE_ID
+ }
+ r = self.request(self.SIGNOUT)
+ self.TOKEN = ''
+ self.plugin.set_setting('token', self.TOKEN)
+
+ def refreshToken(self):
+ self.POST_DATA = {
+ 'DeviceId': self.DEVICE_ID
+ }
+ data = self.request(self.REFRESH)
+ if data.get('odata.error', None):
+ self.signOut()
+ self.errorHandler(data)
+ else:
+ self.setToken(data['AuthToken'], data.get('Result', 'RefreshAccessTokenError'))
+
+ def startUp(self, device_id=''):
+ kodi_language = self.plugin.get_language()
+ if device_id != '':
+ self.DEVICE_ID = device_id
+ self.POST_DATA = {
+ 'LandingPageKey': 'generic',
+ 'Languages': '{0}, {1}'.format(kodi_language, self.LANGUAGE),
+ 'Platform': 'web',
+ 'Manufacturer': '',
+ 'PromoCode': ''
+ }
+ data = self.request(self.STARTUP)
+ region = data.get('Region', {})
+ if region.get('isAllowed', False):
+ self.COUNTRY = region['Country']
+ self.LANGUAGE = region['Language']
+ languages = data['SupportedLanguages']
+ for i in languages:
+ if i == kodi_language:
+ self.LANGUAGE = i
+ break
+ self.plugin.set_setting('language', self.LANGUAGE)
+ self.plugin.set_setting('country', self.COUNTRY)
+ if self.TOKEN:
+ self.refreshToken()
+ else:
+ self.signIn()
+ else:
+ self.TOKEN = ''
+ self.plugin.dialog_ok(30101)
+
+ def request(self, url):
+ self.HEADERS['Authorization'] = 'Bearer ' + self.TOKEN
+ self.PARAMS['LanguageCode'] = self.LANGUAGE
+ self.PARAMS['Country'] = self.COUNTRY
+
+ if self.POST_DATA:
+ r = requests.post(url, headers=self.HEADERS, data=self.POST_DATA, params=self.PARAMS)
+ self.POST_DATA = {}
+ else:
+ r = requests.get(url, headers=self.HEADERS, params=self.PARAMS)
+
+ if r.headers.get('content-type', '').startswith('application/json'):
+ return r.json()
+ else:
+ if not r.status_code == 204:
+ self.plugin.log('[{0}] error: {1} ({2}, {3})'.format(self.plugin.addon_id, url, str(r.status_code), r.headers.get('content-type', '')))
+ return {}
+
+ def errorHandler(self, data):
+ self.ERRORS += 1
+ msg = data['odata.error']['message']['value']
+ code = str(data['odata.error']['code'])
+ self.plugin.log('[{0}] version: {1} country: {2} language: {3}'.format(self.plugin.addon_id, self.plugin.addon_version, self.COUNTRY, self.LANGUAGE))
+ self.plugin.log('[{0}] error: {1} ({2})'.format(self.plugin.addon_id, msg, code))
+ if code == '10000' and self.ERRORS < 3:
+ self.refreshToken()
+ elif (code == '401' or code == '10033') and self.ERRORS < 3:
+ self.signIn()
+ elif code == '7':
+ self.plugin.dialog_ok(30107)
+ elif code == '3001':
+ self.startUp()
+ elif code == '10006':
+ self.plugin.dialog_ok(30101)
+ elif code == '10008':
+ self.plugin.dialog_ok(30108)
+ elif code == '10049':
+ self.plugin.dialog_ok(30151)
diff --git a/plugin.video.dazn/resources/lib/common.py b/plugin.video.dazn/resources/lib/common.py
new file mode 100644
index 0000000..d171d9a
--- /dev/null
+++ b/plugin.video.dazn/resources/lib/common.py
@@ -0,0 +1,157 @@
+# -*- coding: utf-8 -*-
+
+import datetime
+import hashlib
+import time
+import urllib
+import uuid
+import xbmc
+import xbmcaddon
+import xbmcgui
+from inputstreamhelper import Helper
+from resources import resources
+
+class Common:
+
+ def __init__(self, addon_handle=None, addon_url=None):
+ self.api_base = 'https://isl.dazn.com/misl/'
+ self.time_format = '%Y-%m-%dT%H:%M:%SZ'
+ self.date_format = '%Y-%m-%d'
+
+ addon = self.get_addon()
+ self.addon_handle = addon_handle
+ self.addon_url = addon_url
+ self.addon_id = addon.getAddonInfo('id')
+ self.addon_name = addon.getAddonInfo('name')
+ self.addon_version = addon.getAddonInfo('version')
+ self.addon_icon = addon.getAddonInfo('icon')
+ self.addon_fanart = addon.getAddonInfo('fanart')
+ self.content = addon.getSetting('content')
+ self.view_id = addon.getSetting('view_id')
+ self.force_view = addon.getSetting('force_view') == 'true'
+
+ def utfenc(self, text):
+ result = text
+ if isinstance(text, unicode):
+ result = text.encode('utf-8')
+ return result
+
+ def get_addon(self):
+ return xbmcaddon.Addon()
+
+ def get_dialog(self):
+ return xbmcgui.Dialog()
+
+ def set_setting(self, key, value):
+ return self.get_addon().setSetting(key, value)
+
+ def get_setting(self, key):
+ return self.get_addon().getSetting(key)
+
+ def get_string(self, id_):
+ return self.utfenc(self.get_addon().getLocalizedString(id_))
+
+ def dialog_ok(self, id_):
+ self.get_dialog().ok(self.addon_name, self.get_string(id_))
+
+ def get_resource(self, string):
+ result = self.utfenc(string)
+ id_ = resources(string)
+ if id_ != 0:
+ result = self.get_string(id_)
+ return result
+
+ def get_credentials(self):
+ email = self.get_dialog().input(self.addon_name + self.get_string(30002), type=xbmcgui.INPUT_ALPHANUM)
+ if '@' in email:
+ password = self.get_dialog().input(self.addon_name + self.get_string(30003), type=xbmcgui.INPUT_ALPHANUM, option=xbmcgui.ALPHANUM_HIDE_INPUT)
+ if len(password) > 4:
+ return {
+ 'email': email,
+ 'password': password
+ }
+ return None
+
+ def log(self, msg):
+ xbmc.log(str(msg), xbmc.LOGDEBUG)
+
+ def build_url(self, query):
+ return self.addon_url + '?' + urllib.urlencode(query)
+
+ def get_language(self):
+ language = xbmc.getLanguage().split(' (')[0]
+ return xbmc.convertLanguage(language, xbmc.ISO_639_1)
+
+ def time_now(self):
+ return datetime.datetime.now().strftime(self.time_format)
+
+ def time_stamp(self, str_date):
+ return datetime.datetime.fromtimestamp(time.mktime(time.strptime(str_date, self.time_format)))
+
+ def timedelta_total_seconds(self, timedelta):
+ return (
+ timedelta.microseconds + 0.0 +
+ (timedelta.seconds + timedelta.days * 24 * 3600) * 10 ** 6) / 10 ** 6
+
+ def utc2local(self, date_string):
+ if str(date_string).startswith('2'):
+ utc = datetime.datetime(*(time.strptime(date_string, self.time_format)[0:6]))
+ epoch = time.mktime(utc.timetuple())
+ offset = datetime.datetime.fromtimestamp(epoch) - datetime.datetime.utcfromtimestamp(epoch)
+ return (utc + offset).strftime(self.time_format)
+
+ def uniq_id(self):
+ device_id = ''
+ mac_addr = xbmc.getInfoLabel('Network.MacAddress')
+ if not ":" in mac_addr:
+ mac_addr = xbmc.getInfoLabel('Network.MacAddress')
+ # hack response busy
+ i = 0
+ while not ":" in mac_addr and i < 3:
+ i += 1
+ time.sleep(1)
+ mac_addr = xbmc.getInfoLabel('Network.MacAddress')
+ if ":" in mac_addr:
+ device_id = str(uuid.UUID(hashlib.md5(str(mac_addr.decode("utf-8"))).hexdigest()))
+ else:
+ log("[{0}] error: failed to get device id ({1})".format(self.addon_id, str(mac_addr)))
+ self.dialog_ok(30051)
+ self.set_setting('device_id', device_id)
+ return device_id
+
+ def open_is_settings(self):
+ xbmcaddon.Addon(id='inputstream.adaptive').openSettings()
+
+ def start_is_helper(self):
+ helper = Helper(protocol='mpd', drm='widevine')
+ return helper.check_inputstream()
+
+ def days(self, title, now, start):
+ today = datetime.date.today()
+ if start and not title == 'Live':
+ if now[:10] == start[:10]:
+ return 'Today'
+ elif str(today + datetime.timedelta(days=1)) == start[:10]:
+ return 'Tomorrow'
+ else:
+ for i in range(2,8):
+ if str(today + datetime.timedelta(days=i)) == start[:10]:
+ return (today + datetime.timedelta(days=i)).strftime('%A')
+ return title
+
+ def epg_date(self, date):
+ return datetime.datetime.fromtimestamp(time.mktime(time.strptime(date, self.date_format)))
+
+ def get_prev_day(self, date):
+ return (date - datetime.timedelta(days=1))
+
+ def get_next_day(self, date):
+ return (date + datetime.timedelta(days=1))
+
+ def get_date(self):
+ date = 'today'
+ dlg = self.get_dialog().numeric(1, self.get_string(30230))
+ if dlg:
+ spl = dlg.split('/')
+ date = '%s-%s-%s' % (spl[2], spl[1], spl[0])
+ return date
diff --git a/plugin.video.dazn/resources/lib/context.py b/plugin.video.dazn/resources/lib/context.py
new file mode 100644
index 0000000..d84cbb1
--- /dev/null
+++ b/plugin.video.dazn/resources/lib/context.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+
+class Context:
+
+ def __init__(self, plugin):
+ self.cm = []
+ self.plugin = plugin
+
+ def epg_date(self):
+ d = {
+ 'mode': 'epg',
+ 'id': 'date'
+ }
+ self.cm.append((self.plugin.get_string(30230), 'ActivateWindow(Videos, {0})'.format(self.plugin.build_url(d))))
+ return self.cm
+
+ def highlights(self, item, mode):
+ d = {
+ 'mode': mode,
+ 'title': self.plugin.utfenc(item['title']),
+ 'id': item.get('id', ''),
+ 'params': item.get('params','')
+ }
+ self.cm.append((self.plugin.get_string(30231), 'ActivateWindow(Videos, {0})'.format(self.plugin.build_url(d))))
+ return self.cm
+
+ def related(self, cm_items):
+ for i in cm_items:
+ d = {
+ 'mode': 'play_context',
+ 'title': self.plugin.utfenc(i['title']),
+ 'id': i.get('id', ''),
+ 'params': i.get('params','')
+ }
+ self.cm.append((self.plugin.get_string(30213), 'XBMC.RunPlugin({0})'.format(self.plugin.build_url(d))))
+ return self.cm
+
+ def goto(self, item):
+ if item.get('sport', None):
+ i = item['sport']
+ d = {
+ 'mode': 'rails',
+ 'title': self.plugin.utfenc(i['Title']),
+ 'id': 'sport',
+ 'params': i['Id']
+ }
+ self.cm.append((self.plugin.get_string(30214), 'ActivateWindow(Videos, {0})'.format(self.plugin.build_url(d))))
+
+ if item.get('competition', None):
+ i = item['competition']
+ d = {
+ 'mode': 'rails',
+ 'title': self.plugin.utfenc(i['Title']),
+ 'id': 'competition',
+ 'params': i['Id']
+ }
+ self.cm.append((self.plugin.get_string(30215), 'ActivateWindow(Videos, {0})'.format(self.plugin.build_url(d))))
+
+ return self.cm
diff --git a/plugin.video.dazn/resources/lib/items.py b/plugin.video.dazn/resources/lib/items.py
new file mode 100644
index 0000000..9e08127
--- /dev/null
+++ b/plugin.video.dazn/resources/lib/items.py
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+
+import xbmc
+import xbmcgui
+import xbmcplugin
+
+class Items:
+
+ def __init__(self, plugin):
+ self.cache = True
+ self.video = False
+ self.plugin = plugin
+
+ def list_items(self, focus=False, upd=False):
+ if self.video:
+ xbmcplugin.setContent(self.plugin.addon_handle, self.plugin.content)
+ xbmcplugin.endOfDirectory(self.plugin.addon_handle, cacheToDisc=self.cache, updateListing=upd)
+
+ if self.plugin.force_view:
+ xbmc.executebuiltin('Container.SetViewMode({0})'.format(self.plugin.view_id))
+
+ if focus:
+ try:
+ wnd = xbmcgui.Window(xbmcgui.getCurrentWindowId())
+ wnd.getControl(wnd.getFocusId()).selectItem(focus)
+ except:
+ pass
+
+ def add_item(self, item):
+ data = {
+ 'mode': item['mode'],
+ 'title': item['title'],
+ 'id': item.get('id', ''),
+ 'params': item.get('params','')
+ }
+
+ art = {
+ 'thumb': item.get('thumb', self.plugin.addon_icon),
+ 'poster': item.get('thumb', self.plugin.addon_icon),
+ 'fanart': item.get('fanart', self.plugin.addon_fanart)
+ }
+
+ labels = {
+ 'title': item['title'],
+ 'plot': item.get('plot', item['title']),
+ 'premiered': item.get('date', ''),
+ 'episode': item.get('episode', 0)
+ }
+
+ listitem = xbmcgui.ListItem(item['title'])
+ listitem.setArt(art)
+ listitem.setInfo(type='Video', infoLabels=labels)
+
+ if 'play' in item['mode']:
+ self.cache = False
+ self.video = True
+ folder = False
+ listitem.addStreamInfo('video', {'duration':item.get('duration', 0)})
+ listitem.setProperty('IsPlayable', 'true')
+ else:
+ folder = True
+
+ if item.get('cm', None):
+ listitem.addContextMenuItems( item['cm'] )
+
+ xbmcplugin.addDirectoryItem(self.plugin.addon_handle, self.plugin.build_url(data), listitem, folder)
+
+ def play_item(self, item, name, context):
+ path = item.ManifestUrl
+ listitem = xbmcgui.ListItem()
+ listitem.setContentLookup(False)
+ listitem.setMimeType('application/dash+xml')
+ listitem.setProperty('inputstreamaddon', 'inputstream.adaptive')
+ listitem.setProperty('inputstream.adaptive.manifest_type', 'mpd')
+ listitem.setProperty('inputstream.adaptive.license_type', 'com.widevine.alpha')
+ listitem.setProperty('inputstream.adaptive.license_key', item.LaUrl+'&_widevineChallenge=B{SSM}|||JBlicense')
+ if context:
+ listitem.setInfo('video', {'Title': name})
+ xbmc.Player().play(path, listitem)
+ else:
+ listitem.setPath(path)
+ xbmcplugin.setResolvedUrl(self.plugin.addon_handle, True, listitem)
diff --git a/plugin.video.dazn/resources/lib/parser.py b/plugin.video.dazn/resources/lib/parser.py
new file mode 100644
index 0000000..55d70c7
--- /dev/null
+++ b/plugin.video.dazn/resources/lib/parser.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+
+from items import Items
+from rails import Rails
+from tiles import Tiles
+from playback import Playback
+from context import Context
+
+class Parser:
+
+ def __init__(self, plugin):
+ self.plugin = plugin
+ self.items = Items(self.plugin)
+
+ def rails_items(self, data, id_):
+ if id_ == 'home':
+ epg = {
+ 'mode': 'epg',
+ 'title': self.plugin.get_string(30212),
+ 'plot': 'Schedule',
+ 'params': 'today',
+ }
+ epg['cm'] = Context(self.plugin).highlights(epg, mode='epg_highlights')
+ self.items.add_item(epg)
+ for i in data.get('Rails', []):
+ item = Rails(self.plugin, i).item
+ if item.get('id', '') == 'CatchUp':
+ item['cm'] = Context(self.plugin).highlights(item, mode='rail_highlights')
+ self.items.add_item(item)
+ self.items.list_items()
+
+ def rail_items(self, data, mode, list=True):
+ highlights = True if 'highlights' in mode else False
+ focus = data.get('StartPosition', False)
+ for i in data.get('Tiles', []):
+ context = Context(self.plugin)
+ item = Tiles(self.plugin, i).item
+ if highlights:
+ if item['type'] == 'Highlights':
+ item['cm'] = context.goto(item)
+ self.items.add_item(item)
+ elif item.get('related', []):
+ for i in item['related']:
+ if i.get('Videos', []):
+ _item = Tiles(self.plugin, i).item
+ _item['cm'] = context.goto(_item)
+ self.items.add_item(_item)
+ else:
+ if item.get('related', []):
+ cm_items = []
+ for i in item['related']:
+ if i.get('Videos', []):
+ cm_items.append(Tiles(self.plugin, i).item)
+ context.related(cm_items)
+ item['cm'] = context.goto(item)
+ self.items.add_item(item)
+ if list:
+ self.items.list_items(focus)
+
+ def epg_items(self, data, params, mode):
+ update = False if params == 'today' else True
+ if data.get('Date'):
+ date = self.plugin.epg_date(data['Date'])
+ cm = Context(self.plugin).epg_date()
+
+ def date_item(day):
+ return {
+ 'mode': mode,
+ 'title': '{0} ({1})'.format(self.plugin.get_resource(day.strftime('%A')), day.strftime(self.plugin.date_format)),
+ 'plot': '{0} ({1})'.format(self.plugin.get_resource(date.strftime('%A')), date.strftime(self.plugin.date_format)),
+ 'params': day,
+ 'cm': cm
+ }
+
+ self.items.add_item(date_item(self.plugin.get_prev_day(date)))
+ self.rail_items(data, mode, list=False)
+ self.items.add_item(date_item(self.plugin.get_next_day(date)))
+ self.items.list_items(upd=update)
+
+ def playback(self, data, name=False, context=False):
+ self.items.play_item(Playback(data), name, context)
diff --git a/plugin.video.dazn/resources/lib/playback.py b/plugin.video.dazn/resources/lib/playback.py
new file mode 100644
index 0000000..a7ae0c4
--- /dev/null
+++ b/plugin.video.dazn/resources/lib/playback.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+
+import simple_requests as requests
+
+class Playback:
+
+ def __init__(self, data):
+ self.ManifestUrl = ''
+ self.LaUrl = ''
+ self.parse_data(data.get('PlaybackDetails', []))
+
+ def parse_data(self, data):
+ for i in data:
+ r = requests.head(i['ManifestUrl'])
+ if r.status_code == 200:
+ self.ManifestUrl = i['ManifestUrl']
+ self.LaUrl = i['LaUrl']
+ break
diff --git a/plugin.video.dazn/resources/lib/rails.py b/plugin.video.dazn/resources/lib/rails.py
new file mode 100644
index 0000000..427af78
--- /dev/null
+++ b/plugin.video.dazn/resources/lib/rails.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+
+class Rails:
+
+ def __init__(self, plugin, i):
+ self.item = {}
+ self.plugin = plugin
+ self.item['mode'] = 'rail'
+ self.item['title'] = self.plugin.get_resource(i['Id'])
+ self.item['id'] = i['Id']
+ self.item['plot'] = i['Id']
+ if i.get('Params', ''):
+ self.item['params'] = i['Params']
diff --git a/plugin.video.dazn/resources/lib/resources.py b/plugin.video.dazn/resources/lib/resources.py
new file mode 100644
index 0000000..35fd423
--- /dev/null
+++ b/plugin.video.dazn/resources/lib/resources.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+
+def resources(string):
+ if string == 'CatchUp':
+ return 30201
+ elif string == 'ComingUp':
+ return 30202
+ elif string == 'UpComing':
+ return 30202
+ elif string == 'Editorial':
+ return 30203
+ elif string == 'Feature':
+ return 30204
+ elif string == 'Live':
+ return 30205
+ elif string == 'MostPopular':
+ return 30206
+ elif string == 'Personal':
+ return 30207
+ elif string == 'Scheduled':
+ return 30208
+ elif string == 'Sport':
+ return 30209
+ elif string == 'Competition':
+ return 30210
+ elif string == 'Competitor':
+ return 30211
+ elif string == 'Today':
+ return 30221
+ elif string == 'Tomorrow':
+ return 30222
+ elif string == 'Monday':
+ return 30223
+ elif string == 'Tuesday':
+ return 30224
+ elif string == 'Wednesday':
+ return 30225
+ elif string == 'Thursday':
+ return 30226
+ elif string == 'Friday':
+ return 30227
+ elif string == 'Saturday':
+ return 30228
+ elif string == 'Sunday':
+ return 30229
+ else:
+ return 0
diff --git a/plugin.video.dazn/resources/lib/simple_requests/__init__.py b/plugin.video.dazn/resources/lib/simple_requests/__init__.py
new file mode 100644
index 0000000..fa1fd74
--- /dev/null
+++ b/plugin.video.dazn/resources/lib/simple_requests/__init__.py
@@ -0,0 +1,6 @@
+__author__ = 'bromix'
+
+__ALL__ = ['get', 'post', 'put', 'delete', 'head', 'codes']
+
+from .constants import codes
+from api import get, post, put, delete, head
diff --git a/plugin.video.dazn/resources/lib/simple_requests/api.py b/plugin.video.dazn/resources/lib/simple_requests/api.py
new file mode 100644
index 0000000..35f6148
--- /dev/null
+++ b/plugin.video.dazn/resources/lib/simple_requests/api.py
@@ -0,0 +1,194 @@
+__author__ = 'bromix'
+
+import urllib
+import urllib2
+from StringIO import StringIO
+import gzip
+
+import json as real_json
+
+
+class ErrorHandler(urllib2.HTTPDefaultErrorHandler):
+ def http_error_default(self, req, fp, code, msg, hdrs):
+ infourl = urllib.addinfourl(fp, hdrs, req.get_full_url())
+ infourl.status = code
+ infourl.code = code
+ return infourl
+
+
+class NoRedirectHandler(urllib2.HTTPRedirectHandler):
+ def http_error_302(self, req, fp, code, msg, headers):
+ infourl = urllib.addinfourl(fp, headers, req.get_full_url())
+ infourl.status = code
+ infourl.code = code
+ return infourl
+
+ http_error_300 = http_error_302
+ http_error_301 = http_error_302
+ http_error_303 = http_error_302
+ http_error_307 = http_error_302
+
+
+class Response():
+ def __init__(self):
+ self.headers = {}
+ self.code = -1
+ self.text = u''
+ self.status_code = -1
+
+ def read(self):
+ return self.text
+
+ def json(self):
+ return real_json.loads(self.text)
+
+ pass
+
+
+def _request(method, url,
+ params=None,
+ data=None,
+ headers=None,
+ cookies=None,
+ files=None,
+ auth=None,
+ timeout=None,
+ allow_redirects=True,
+ proxies=None,
+ hooks=None,
+ stream=None,
+ verify=None,
+ cert=None,
+ json=None):
+ if not headers:
+ headers = {}
+ pass
+
+ url = urllib.quote(url, safe="%/:=&?~#+!$,;'@()*[]")
+
+ handlers = []
+
+ import sys
+ # starting with python 2.7.9 urllib verifies every https request
+ if False == verify and sys.version_info >= (2, 7, 9):
+ import ssl
+
+ ssl_context = ssl.create_default_context()
+ ssl_context.check_hostname = False
+ ssl_context.verify_mode = ssl.CERT_NONE
+ handlers.append(urllib2.HTTPSHandler(context=ssl_context))
+ pass
+
+ # handlers.append(urllib2.HTTPCookieProcessor())
+ # handlers.append(ErrorHandler)
+ if not allow_redirects:
+ handlers.append(NoRedirectHandler)
+ pass
+ opener = urllib2.build_opener(*handlers)
+
+ query = ''
+ if params:
+ for key in params:
+ value = params[key]
+ if isinstance(value, str):
+ value = value.decode('utf-8')
+ pass
+ params[key] = value.encode('utf-8')
+ pass
+ query = urllib.urlencode(params)
+ pass
+ if query:
+ url += '?%s' % query
+ pass
+ request = urllib2.Request(url)
+ if headers:
+ for key in headers:
+ request.add_header(key, str(unicode(headers[key]).encode('utf-8')))
+ pass
+ pass
+ if data or json:
+ if headers.get('Content-Type', '').startswith('application/x-www-form-urlencoded') and data:
+ # transform a string into a map of values
+ if isinstance(data, basestring):
+ _data = data.split('&')
+ data = {}
+ for item in _data:
+ name, value = item.split('=')
+ data[name] = value
+ pass
+ pass
+
+ # encode each value
+ for key in data:
+ data[key] = data[key].encode('utf-8')
+ pass
+
+ # urlencode
+ request.data = urllib.urlencode(data)
+ pass
+ elif headers.get('Content-Type', '').startswith('application/json') and data:
+ request.data = real_json.dumps(data).encode('utf-8')
+ pass
+ elif json:
+ request.data = real_json.dumps(json).encode('utf-8')
+ pass
+ else:
+ if not isinstance(data, basestring):
+ data = str(data)
+ pass
+
+ if isinstance(data, str):
+ data = data.encode('utf-8')
+ pass
+ request.data = data
+ pass
+ pass
+ elif method.upper() in ['POST', 'PUT']:
+ request.data = "null"
+ pass
+ request.get_method = lambda: method
+ result = Response()
+ response = None
+ try:
+ response = opener.open(request)
+ except urllib2.HTTPError as e:
+ # HTTPError implements addinfourl, so we can use the exception to construct a response
+ if isinstance(e, urllib2.addinfourl):
+ response = e
+ pass
+ pass
+
+ # process response
+ result.headers.update(response.headers)
+ result.status_code = response.getcode()
+ if response.headers.get('Content-Encoding', '').startswith('gzip'):
+ buf = StringIO(response.read())
+ f = gzip.GzipFile(fileobj=buf)
+ result.text = f.read()
+ pass
+ else:
+ result.text = response.read()
+
+ return result
+
+
+def get(url, **kwargs):
+ kwargs.setdefault('allow_redirects', True)
+ return _request('GET', url, **kwargs)
+
+
+def post(url, data=None, json=None, **kwargs):
+ kwargs.setdefault('allow_redirects', True)
+ return _request('POST', url, data=data, json=json, **kwargs)
+
+
+def put(url, data=None, json=None, **kwargs):
+ return _request('PUT', url, data=data, json=json, **kwargs)
+
+
+def delete(url, **kwargs):
+ return _request('DELETE', url, **kwargs)
+
+
+def head(url, **kwargs):
+ return _request('HEAD', url, **kwargs) \ No newline at end of file
diff --git a/plugin.video.dazn/resources/lib/simple_requests/constants/__init__.py b/plugin.video.dazn/resources/lib/simple_requests/constants/__init__.py
new file mode 100644
index 0000000..50f773a
--- /dev/null
+++ b/plugin.video.dazn/resources/lib/simple_requests/constants/__init__.py
@@ -0,0 +1 @@
+__author__ = 'bromix'
diff --git a/plugin.video.dazn/resources/lib/simple_requests/constants/codes.py b/plugin.video.dazn/resources/lib/simple_requests/constants/codes.py
new file mode 100644
index 0000000..5536a63
--- /dev/null
+++ b/plugin.video.dazn/resources/lib/simple_requests/constants/codes.py
@@ -0,0 +1,5 @@
+__author__ = 'bromix'
+
+ok = 200
+
+unauthorized = 401 \ No newline at end of file
diff --git a/plugin.video.dazn/resources/lib/tiles.py b/plugin.video.dazn/resources/lib/tiles.py
new file mode 100644
index 0000000..b3e0060
--- /dev/null
+++ b/plugin.video.dazn/resources/lib/tiles.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+
+class Tiles:
+
+ def __init__(self, plugin, i):
+ self.item = {}
+ self.plugin = plugin
+ self.title = self.plugin.utfenc(i['Title'])
+ self.subtitle = i.get('SubTitle', '')
+ self.description = self.plugin.utfenc(i['Description'])
+ self.start = self.plugin.utc2local(i.get('Start', ''))
+ self.end = self.plugin.utc2local(i.get('End', ''))
+ self.now = self.plugin.time_now()
+ self.sport = i.get('Sport', [])
+ self.competition = i.get('Competition', [])
+ self.type = i.get('Type', '')
+ self.nav = i.get('NavigateTo', '')
+ self.related = i.get('Related', [])
+ if self.nav:
+ self.mode = 'rails'
+ self.id = i['NavigateTo']
+ self.params = i['NavParams']
+ else:
+ self.mode = 'play'
+ self.id = i['AssetId']
+ self.params = i['EventId']
+ self.update_item(i)
+
+ def add_duration(self, i):
+ if 'UpComing' in self.type:
+ self.end = self.start
+ self.start = self.now
+ elif 'Live' in self.type:
+ self.start = self.now
+ if self.start and self.end:
+ self.item['duration'] = self.plugin.timedelta_total_seconds(self.plugin.time_stamp(self.end)-self.plugin.time_stamp(self.start))
+
+ def add_thumb(self, i):
+ url = self.plugin.api_base+'v2/image?id={0}&Quality=95&Width={1}&Height={2}&ResizeAction=fill&VerticalAlignment=top&Format={3}'
+ image = i.get('Image', '')
+ if image:
+ self.item['thumb'] = url.format(image['Id'], '720', '404', image['ImageMimeType'])
+ self.item['fanart'] = url.format(image['Id'], '1280', '720', image['ImageMimeType'])
+ background = i.get('BackgroundImage', '')
+ if background:
+ self.item['fanart'] = url.format(background['Id'], '1280', '720', background['ImageMimeType'])
+
+ def update_item(self, i):
+ self.item['mode'] = self.mode
+ self.item['title'] = self.title
+ self.item['plot'] = self.description
+ self.item['id'] = self.id
+ self.item['type'] = self.plugin.get_resource(self.type)
+
+ if self.params:
+ self.item['params'] = self.params
+
+ if 'Epg' in i.get('Id', ''):
+ if self.competition:
+ competition = self.plugin.utfenc(self.competition['Title'])
+ if self.sport:
+ sport = self.plugin.utfenc(self.sport['Title'])
+ time_ = self.start[11:][:5]
+ if self.type == 'Live':
+ self.item['title'] = '[COLOR red]{0}[/COLOR] [COLOR dimgray]{1}[/COLOR] {2} [COLOR dimgray]{3}[/COLOR]'.format(time_, sport, self.title, competition)
+ else:
+ self.item['title'] = '{0} [COLOR dimgray]{1}[/COLOR] {2} [COLOR dimgray]{3}[/COLOR]'.format(time_, sport, self.title, competition)
+
+ elif (self.type == 'UpComing' or 'Scheduled' in i.get('Id', '')) or (self.type == 'Highlights'):
+ if self.type == 'UpComing':
+ day = self.plugin.get_resource(self.plugin.days(self.type, self.now, self.start))
+ sub_title = '{0} {1}'.format(day, self.start[11:][:5])
+ else:
+ sub_title = self.plugin.get_resource(self.type)
+ self.item['title'] = '{0} ({1})'.format(self.title, sub_title)
+
+ if self.start:
+ self.item['date'] = self.start[:10]
+
+ self.item['related'] = self.related
+ self.item['sport'] = self.sport
+ self.item['competition'] = self.competition
+
+ self.add_thumb(i)
+ self.add_duration(i)