summaryrefslogtreecommitdiff
path: root/plugin.video.fsgo/addon.py
diff options
context:
space:
mode:
authoremilsvennesson <emilsvennesson@users.noreply.github.com>2017-02-14 16:25:55 +0100
committeremilsvennesson <emilsvennesson@users.noreply.github.com>2017-02-14 18:20:41 +0100
commit1e4934006b7f76d8efc980c008f72a3c225ddf06 (patch)
tree515ad297bb2381972e85f9571f48aa9f47ae05cc /plugin.video.fsgo/addon.py
parent68038b17e695f6b37e80d2c6510d340d55955758 (diff)
[plugin.video.fsgo] 1.1.6
Diffstat (limited to 'plugin.video.fsgo/addon.py')
-rw-r--r--plugin.video.fsgo/addon.py424
1 files changed, 424 insertions, 0 deletions
diff --git a/plugin.video.fsgo/addon.py b/plugin.video.fsgo/addon.py
new file mode 100644
index 0000000..68f32d0
--- /dev/null
+++ b/plugin.video.fsgo/addon.py
@@ -0,0 +1,424 @@
+# -*- coding: utf-8 -*-
+"""
+A Kodi plugin for FOX Sports GO
+"""
+import sys
+import os
+import urllib
+import urlparse
+import json
+from datetime import datetime
+
+from resources.lib.fsgo import fsgolib
+
+import xbmc
+import xbmcaddon
+import xbmcvfs
+import xbmcgui
+import xbmcplugin
+
+addon = xbmcaddon.Addon()
+addon_path = xbmc.translatePath(addon.getAddonInfo('path'))
+addon_profile = xbmc.translatePath(addon.getAddonInfo('profile'))
+language = addon.getLocalizedString
+logging_prefix = '[%s-%s]' % (addon.getAddonInfo('id'), addon.getAddonInfo('version'))
+
+if not xbmcvfs.exists(addon_profile):
+ xbmcvfs.mkdir(addon_profile)
+
+_url = sys.argv[0] # get the plugin url in plugin:// notation
+_handle = int(sys.argv[1]) # get the plugin handle as an integer number
+if addon.getSetting('verify_ssl') == 'false':
+ verify_ssl = False
+else:
+ verify_ssl = True
+
+fsgo = fsgolib(addon_profile, debug=True, verify_ssl=verify_ssl)
+
+
+def addon_log(string):
+ msg = '%s: %s' % (logging_prefix, string)
+ xbmc.log(msg=msg, level=xbmc.LOGDEBUG)
+
+
+def play(channel_id, airing_id=None):
+ stream_url = fsgo.get_stream_url(channel_id, airing_id)
+ if stream_url:
+ bitrate = select_bitrate(stream_url['bitrates'].keys())
+ if bitrate:
+ play_url = stream_url['bitrates'][bitrate]
+ playitem = xbmcgui.ListItem(path=play_url)
+ playitem.setProperty('IsPlayable', 'true')
+ xbmcplugin.setResolvedUrl(_handle, True, listitem=playitem)
+ else:
+ dialog('ok', language(30020), message=language(30021))
+
+
+def main_menu():
+ addon_log('Hello World!') # print add-on version
+ items = [language(30023), language(30015), language(30026), language(30036), language(30030)]
+ for item in items:
+ if item == language(30023):
+ params = {
+ 'action': 'list_events_by_date',
+ 'schedule_type': 'all',
+ 'filter_date': 'today'
+ }
+ elif item == language(30015):
+ params = {'action': 'list_upcoming_days'}
+ elif item == language(30026):
+ params = {
+ 'action': 'list_events',
+ 'schedule_type': 'featured'
+ }
+ elif item == language(30036):
+ params = {'action': 'search'}
+ else: # auth details
+ item = '[B]%s[/B]' % item
+ params = {'action': 'show_auth_details'}
+
+ add_item(item, params)
+ xbmcplugin.endOfDirectory(_handle)
+
+
+def coloring(text, meaning):
+ """Return the text wrapped in appropriate color markup."""
+ if meaning == 'channel':
+ color = 'FF0FE8F0'
+ elif meaning == 'live':
+ color = 'FF03F12F'
+ elif meaning == 'upcoming':
+ color = 'FFF16C00'
+ elif meaning == 'replay':
+ color = 'FFE71A2B'
+ colored_text = '[COLOR=%s]%s[/COLOR]' % (color, text)
+ return colored_text
+
+
+def list_events(schedule_type, filter_date=False, search_query=None, search_filter=None):
+ items = []
+ now = datetime.now()
+ date_today = now.date()
+
+ schedule = fsgo.get_schedule(schedule_type, filter_date=filter_date, deportes=addon.getSetting('show_deportes'),
+ search_query=search_query, search_filter=search_filter)
+
+ for event in schedule:
+ if addon.getSetting('hide_replays') == 'true' and event['airings'][0]['replay']:
+ continue
+
+ channel_id = event['airings'][0]['channel_id']
+ airing_id = event['airings'][0]['airing_id']
+ channel_name = event['airings'][0]['channel_name']
+ airing_date_obj = fsgo.parse_datetime(event['airings'][0]['airing_date'], localize=True)
+ airing_date = airing_date_obj.date()
+
+ try:
+ sport_tag = event['sport_tag']
+ except KeyError:
+ sport_tag = None
+
+ if addon.getSetting('time_notation') == '0': # 12 hour clock
+ time = airing_date_obj.strftime('%I:%M %p')
+ else:
+ time = airing_date_obj.strftime('%H:%M')
+
+ if airing_date == date_today:
+ start_time = '%s %s' % (language(30023), time)
+ else:
+ start_time = '%s %s' % (airing_date_obj.strftime('%Y-%m-%d'), time)
+
+ if event['airings'][0]['is_live']:
+ params = {
+ 'action': 'play_event',
+ 'channel_id': channel_id,
+ 'airing_id': airing_id
+ }
+ playable = True
+ date_color = 'live'
+ else:
+ message = '%s [B]%s[/B].' % (language(30024), start_time)
+ params = {
+ 'action': 'dialog',
+ 'dialog_type': 'ok',
+ 'heading': language(30025),
+ 'message': message
+ }
+ playable = False
+ date_color = 'upcoming'
+
+ info = {
+ 'title': event['title'],
+ 'plot': event['title'],
+ 'genre': sport_tag
+ }
+
+ fav_params = {
+ 'action': 'channel_to_favourites',
+ 'channel_name': channel_name,
+ 'channel_id': channel_id
+ }
+
+ context_menu = {
+ 'title': language(30038),
+ 'function': 'RunPlugin',
+ '_url': _url + '?' + urllib.urlencode(fav_params)
+ }
+
+ try:
+ event_images = event['urls']
+ highest_res = 0
+ for image in event_images:
+ image_res = int(image['size'].split('_')[2])
+ if image_res > highest_res:
+ art = {
+ 'thumb': image['src'],
+ 'fanart': image['src'],
+ 'cover': image['src']
+ }
+ highest_res = image_res
+ except KeyError:
+ art = None
+
+ list_title = '[B]%s[/B] %s: %s' % (coloring(start_time, date_color), coloring(channel_name, 'channel'), event['title'])
+ if event['airings'][0]['replay']:
+ list_title = '%s [B]%s[/B]' % (list_title, coloring('(R)', 'replay'))
+
+ items = add_item(list_title, params, items=items, playable=playable, set_art=art,
+ set_info=info, context_menu=context_menu)
+ xbmcplugin.addDirectoryItems(_handle, items, len(items))
+ xbmcplugin.endOfDirectory(_handle)
+
+
+def show_auth_details():
+ auth_details = fsgo.refresh_session()['user']['registration']
+ tv_provider = auth_details['auth_provider']
+ entitlements = ', '.join(sorted(auth_details['entitlements']))
+ expiration_date_obj = fsgo.parse_datetime(auth_details['expires_on'], localize=True)
+ if addon.getSetting('time_notation') == '0': # 12 hour clock
+ expiration_date = expiration_date_obj.strftime('%Y-%m-%d %I:%M %p')
+ else:
+ expiration_date = expiration_date_obj.strftime('%Y-%m-%d %H:%M')
+
+ tv_provider_msg = '[B]%s:[/B] %s' % (language(30031), tv_provider)
+ entitlements_msg = '[B]%s:[/B] %s' % (language(30032), entitlements)
+ expiration_date_msg = '%s [B]%s[/B].' % (language(30033), expiration_date)
+ message = '%s[CR]%s[CR][CR]%s' % (tv_provider_msg, entitlements_msg, expiration_date_msg)
+ log_out = dialog('yesno', language(30030), message=message, nolabel=language(30027), yeslabel=language(30034))
+
+ if log_out:
+ confirm_log_out = dialog('yesno', language(30034), message=language(30035))
+ if confirm_log_out:
+ fsgo.reset_credentials()
+
+
+def list_upcoming_days():
+ event_dates = fsgo.get_event_dates()
+ now = datetime.now()
+ date_today = now.date()
+
+ for date in event_dates:
+ if date > date_today:
+ title = date.strftime('%Y-%m-%d')
+ params = {
+ 'action': 'list_events_by_date',
+ 'schedule_type': 'all',
+ 'filter_date': date
+ }
+
+ add_item(title, params)
+ xbmcplugin.endOfDirectory(_handle)
+
+
+def ask_bitrate(bitrates):
+ """Presents a dialog for user to select from a list of bitrates.
+ Returns the value of the selected bitrate."""
+ options = []
+ for bitrate in bitrates:
+ options.append(bitrate + ' Kbps')
+ selected_bitrate = dialog('select', language(30016), options=options)
+ if selected_bitrate is not None:
+ return bitrates[selected_bitrate]
+ else:
+ return None
+
+
+def select_bitrate(manifest_bitrates=None):
+ """Returns a bitrate while honoring the user's preference."""
+ bitrate_setting = int(addon.getSetting('preferred_bitrate'))
+ if bitrate_setting == 0:
+ preferred_bitrate = 'highest'
+ elif bitrate_setting == 1:
+ preferred_bitrate = 'limit'
+ else:
+ preferred_bitrate = 'ask'
+
+ manifest_bitrates.sort(key=int, reverse=True)
+ if preferred_bitrate == 'highest':
+ return manifest_bitrates[0]
+ elif preferred_bitrate == 'limit':
+ allowed_bitrates = []
+ max_bitrate_allowed = int(addon.getSetting('max_bitrate_allowed'))
+ for bitrate in manifest_bitrates:
+ if max_bitrate_allowed >= int(bitrate):
+ allowed_bitrates.append(str(bitrate))
+ if allowed_bitrates:
+ return allowed_bitrates[0]
+ else:
+ addon_log('No bitrate in stream matched the maximum bitrate allowed.')
+ return None
+ else:
+ return ask_bitrate(manifest_bitrates)
+
+
+def dialog(dialog_type, heading, message=None, options=None, nolabel=None, yeslabel=None):
+ dialog = xbmcgui.Dialog()
+ if dialog_type == 'ok':
+ dialog.ok(heading, message)
+ elif dialog_type == 'yesno':
+ return dialog.yesno(heading, message, nolabel=nolabel, yeslabel=yeslabel)
+ elif dialog_type == 'select':
+ ret = dialog.select(heading, options)
+ if ret > -1:
+ return ret
+ else:
+ return None
+
+
+def get_user_input(heading):
+ keyboard = xbmc.Keyboard('', heading)
+ keyboard.doModal()
+ if keyboard.isConfirmed():
+ query = keyboard.getText()
+ addon_log('User input string: %s' % query)
+ else:
+ query = None
+
+ if query and len(query) > 0:
+ return query
+ else:
+ return None
+
+
+def search():
+ search_query = get_user_input(language(30037))
+ if search_query:
+ options = [language(30040), language(30041)]
+ ret = dialog('select', language(30036), options=options)
+ if ret is not None:
+ if ret == 1:
+ search_filter = 'events'
+ else:
+ search_filter = None
+ list_events('search', search_query=search_query, search_filter=search_filter)
+ else:
+ addon_log('No search query provided.')
+
+
+def add_item(title, params, items=False, folder=True, playable=False, set_info=False, set_art=False,
+ watched=False, set_content=False, context_menu=None):
+ listitem = xbmcgui.ListItem(label=title)
+ if playable:
+ listitem.setProperty('IsPlayable', 'true')
+ folder = False
+ if set_art:
+ listitem.setArt(set_art)
+ else:
+ listitem.setArt({'icon': os.path.join(addon_path, 'resources', 'art', 'icon.png')})
+ listitem.setArt({'fanart': os.path.join(addon_path, 'resources', 'art', 'fanart.jpg')})
+ if set_info:
+ listitem.setInfo('video', set_info)
+ if not watched:
+ listitem.addStreamInfo('video', {'duration': 0})
+ if set_content:
+ xbmcplugin.setContent(_handle, set_content)
+ if context_menu:
+ run = '%s(%s)' % (context_menu['function'], context_menu['_url'])
+ listitem.addContextMenuItems([(context_menu['title'], run)])
+
+ listitem.setContentLookup(False) # allows sending custom headers/cookies to ffmpeg
+ recursive_url = _url + '?' + urllib.urlencode(params)
+
+ if items is False:
+ xbmcplugin.addDirectoryItem(_handle, recursive_url, listitem, folder)
+ else:
+ items.append((recursive_url, listitem, folder))
+ return items
+
+
+def channel_to_favourites(channel_name, channel_id):
+ params = {
+ 'action': 'play_channel',
+ 'channel_id': channel_id,
+ }
+
+ cmd = {
+ 'jsonrpc': '2.0',
+ 'method': 'Favourites.AddFavourite',
+ 'params': {
+ 'title': channel_name,
+ 'type': 'media',
+ 'path': _url + '?' + urllib.urlencode(params)
+ },
+ 'id': '1'
+ }
+
+ debug_dict = json.loads(xbmc.executeJSONRPC(json.dumps(cmd)))
+ addon_log('channel_to_favourites response: %s' % debug_dict)
+
+
+def authenticate(reg_code=None):
+ try:
+ fsgo.login(reg_code)
+ except fsgo.LoginFailure as error:
+ if error.value == 'NoRegCodeSupplied' or error.value == 'device-not-registered':
+ reg_code = fsgo.get_reg_code()
+ info_message = '%s [B]%s[/B] [CR][CR]%s' % (language(30010), reg_code, language(30011))
+ ok = dialog('yesno', language(30009), message=info_message, nolabel=language(30028),
+ yeslabel=language(30027))
+ if ok:
+ authenticate(reg_code)
+ else:
+ sys.exit(0)
+ elif error.value == 'InvalidAuthN':
+ try_again = dialog('yesno', language(30012), message=language(30013), nolabel=language(30028),
+ yeslabel=language(30029))
+ if try_again:
+ authenticate()
+ else:
+ sys.exit(0)
+ else:
+ dialog('ok', language(30020), error.value)
+ sys.exit(0)
+
+
+def router(paramstring):
+ """Router function that calls other functions depending on the provided paramstring."""
+ params = dict(urlparse.parse_qsl(paramstring))
+ if params:
+ if params['action'] == 'play_event':
+ play(params['channel_id'], params['airing_id'])
+ if params['action'] == 'play_channel':
+ play(params['channel_id'])
+ elif params['action'] == 'list_events':
+ list_events(params['schedule_type'])
+ elif params['action'] == 'list_events_by_date':
+ list_events(params['schedule_type'], params['filter_date'])
+ elif params['action'] == 'list_upcoming_days':
+ list_upcoming_days()
+ elif params['action'] == 'show_auth_details':
+ show_auth_details()
+ elif params['action'] == 'search':
+ search()
+ elif params['action'] == 'dialog':
+ dialog(params['dialog_type'], params['heading'], params['message'])
+ elif params['action'] == 'channel_to_favourites':
+ channel_to_favourites(params['channel_name'], params['channel_id'])
+ else:
+ main_menu()
+
+
+def run():
+ if not fsgo.valid_session:
+ authenticate()
+ router(sys.argv[2][1:]) # trim the leading '?' from the plugin call paramstring