diff options
author | emilsvennesson <emilsvennesson@users.noreply.github.com> | 2017-02-14 16:25:55 +0100 |
---|---|---|
committer | emilsvennesson <emilsvennesson@users.noreply.github.com> | 2017-02-14 18:20:41 +0100 |
commit | 1e4934006b7f76d8efc980c008f72a3c225ddf06 (patch) | |
tree | 515ad297bb2381972e85f9571f48aa9f47ae05cc /plugin.video.fsgo/addon.py | |
parent | 68038b17e695f6b37e80d2c6510d340d55955758 (diff) |
[plugin.video.fsgo] 1.1.6
Diffstat (limited to 'plugin.video.fsgo/addon.py')
-rw-r--r-- | plugin.video.fsgo/addon.py | 424 |
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 |