# Gnu General Public License - see LICENSE.TXT from __future__ import division, absolute_import, print_function, unicode_literals import socket import json import time from datetime import datetime import xbmcaddon import xbmcgui import xbmc from .kodi_utils import HomeWindow from .jellyfin import API from .lazylogger import LazyLogger from .utils import datetime_from_string, translate_string, save_user_details, load_user_details, get_current_datetime, get_saved_users log = LazyLogger(__name__) __addon__ = xbmcaddon.Addon() __addon_name__ = __addon__.getAddonInfo('name') def check_connection_speed(): log.debug("check_connection_speed") settings = xbmcaddon.Addon() speed_test_data_size = int(settings.getSetting("speed_test_data_size")) test_data_size = speed_test_data_size * 1000000 user_details = load_user_details() api = API( settings.getSetting('server_address'), user_details.get('user_id'), user_details.get('token') ) progress_dialog = xbmcgui.DialogProgress() message = 'Testing with {0} MB of data'.format(speed_test_data_size) progress_dialog.create("JellyCon connection speed test", message) start_time = time.time() log.debug("Starting Connection Speed Test") response = api.speedtest(test_data_size) last_percentage_done = 0 total_data_read = 0 if response.status_code == 200: for data in response.iter_content(chunk_size=10240): total_data_read += len(data) percentage_done = int(float(total_data_read) / float(test_data_size) * 100.0) if last_percentage_done != percentage_done: progress_dialog.update(percentage_done) last_percentage_done = percentage_done else: log.error("HTTP response error: {0} {1}".format(response.status_code, response.content)) error_message = "HTTP response error: %s\n%s" % (response.status_code, response.content) xbmcgui.Dialog().ok("Speed Test Error", error_message) return -1 total_data_read_kbits = (total_data_read * 8) / 1000 total_time = time.time() - start_time speed = int(total_data_read_kbits / total_time) log.debug("Finished Connection Speed Test, speed: {0} total_data: {1}, total_time: {2}".format(speed, total_data_read, total_time)) progress_dialog.close() del progress_dialog heading = "Speed Test Result : {0:,} Kbs".format(speed) message = "Do you want to set this speed as your max stream bitrate for playback?\n" message += "{0:,} MB over {1} sec".format(int((total_data_read / 1000000)), total_time) response = xbmcgui.Dialog().yesno(heading, message) if response: settings.setSetting("max_stream_bitrate", str(speed)) return speed def get_server_details(): log.debug("Getting Server Details from Network") servers = [] message = b"who is JellyfinServer?" multi_group = ("", 7359) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(4.0) sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) log.debug("MutliGroup: {0}".format(multi_group)) log.debug("Sending UDP Data: {0}".format(message)) progress = xbmcgui.DialogProgress() progress.create('{} : {}'.format(__addon_name__, translate_string(30373))) progress.update(0, translate_string(30374)) xbmc.sleep(1000) server_count = 0 try: sock.sendto(message, multi_group) while True: try: server_count += 1 progress.update(server_count * 10, '{}: {}'.format(translate_string(30375), server_count)) xbmc.sleep(1000) data, addr = sock.recvfrom(1024) servers.append(json.loads(data)) except: break except Exception as e: log.error("UPD Discovery Error: {0}".format(e)) progress.close() log.debug("Found Servers: {0}".format(servers)) return servers def check_server(force=False, change_user=False, notify=False): log.debug("checkServer Called") settings = xbmcaddon.Addon() something_changed = False # Initialize api object api = API() if force is False: # if not forcing use server details from settings api.server = settings.getSetting('server_address') # if the server is not set then try to detect it if not api.server: # scan for local server server_info = get_server_details() addon = xbmcaddon.Addon() server_icon = addon.getAddonInfo('icon') server_list = [] for server in server_info: server_item = xbmcgui.ListItem(server.get("Name", translate_string(30063))) sub_line = server.get("Address") server_item.setLabel2(sub_line) server_item.setProperty("address", server.get("Address")) art = {"Thumb": server_icon} server_item.setArt(art) server_list.append(server_item) if len(server_list) > 0: return_index = xbmcgui.Dialog().select('{} : {}'.format(__addon_name__, translate_string(30166)), server_list, useDetails=True) if return_index != -1: api.server = server_info[return_index]["Address"] if not api.server: return_index = xbmcgui.Dialog().yesno(__addon_name__, '{}\n{}'.format(translate_string(30282), translate_string(30370))) if not return_index: xbmc.executebuiltin("ActivateWindow(Home)") return while True: kb = xbmc.Keyboard() kb.setHeading(translate_string(30372)) if api.server: kb.setDefault(api.server) else: kb.setDefault("http://") kb.doModal() if kb.isConfirmed(): api.server = kb.getText() else: xbmc.executebuiltin("ActivateWindow(Home)") return progress = xbmcgui.DialogProgress() progress.create('{} : {}'.format(__addon_name__, translate_string(30376))) progress.update(0, translate_string(30377)) result = api.get('/System/Info/Public') progress.close() if result: xbmcgui.Dialog().ok('{} : {}'.format(__addon_name__, translate_string(30167)), api.server) break else: return_index = xbmcgui.Dialog().yesno('{} : {}'.format(__addon_name__, translate_string(30135)), api.server, translate_string(30371)) if not return_index: xbmc.executebuiltin("ActivateWindow(Home)") return log.debug("Selected server: {0}".format(api.server)) settings.setSetting("server_address", api.server) something_changed = True # do we need to change the user current_username = settings.getSetting('username') user_details = load_user_details() home_window = HomeWindow() home_window.set_property('user_name', current_username) # if asked or we have no current user then show user selection screen if something_changed or change_user or len(current_username) == 0 or not user_details: # stop playback when switching users xbmc.Player().stop() # Check if quick connect is active on the server, initiate connection quick = api.get('/QuickConnect/Initiate') code = quick.get('Code') secret = quick.get('Secret') users, user_selection = user_select(api, current_username, code) if user_selection > -1: # The user made a selection in the dialog something_changed = True selected_user = users[user_selection] quick_connect = selected_user.getProperty("quickconnect") == "true" count = 0 if quick_connect: # Try to authenticate to server with secret code 10 times while count < 10: log.debug('Checking for quick connect auth: attempt {}'.format(count)) check = api.get('/QuickConnect/Connect?secret={}'.format(secret)) if check.get('Authenticated'): break count += 1 xbmc.sleep(1000) auth = api.post('/Users/AuthenticateWithQuickConnect', {'secret': secret}) # If authentication was successful, save the username if auth: selected_user_name = auth['User'].get('Name') else: # Login failed, we don't want to change anything something_changed = False log.info("There was an error logging in with quick connect") else: selected_user_name = selected_user.getLabel() secured = selected_user.getProperty("secure") == "true" manual = selected_user.getProperty("manual") == "true" # If using a manual login, ask for username if manual: kb = xbmc.Keyboard() kb.setHeading(translate_string(30005)) if current_username: kb.setDefault(current_username) kb.doModal() if kb.isConfirmed(): selected_user_name = kb.getText() log.debug("Manual entered username: {0}".format(selected_user_name)) else: return home_window.set_property('user_name', selected_user_name) settings.setSetting('username', selected_user_name) user_details = load_user_details() if not user_details: # Ask for password if user has one password = '' if secured and not user_details.get('token'): kb = xbmc.Keyboard() kb.setHeading(translate_string(30006)) kb.setHiddenInput(True) kb.doModal() if kb.isConfirmed(): password = kb.getText() auth_payload = {'username': selected_user_name, 'pw': password} auth = api.authenticate(auth_payload) if not auth: # Login failed, we don't want to change anything something_changed = False log.info('There was an error logging in with user {}'.format(selected_user_name)) xbmcgui.Dialog().ok(__addon_name__, translate_string(30446)) if something_changed: home_window = HomeWindow() home_window.clear_property("jellycon_widget_reload") if auth: token = auth.get('AccessToken') user_id = auth.get('User').get('Id') else: token = user_details.get('token') user_id = user_details.get('user_id') save_user_details(selected_user_name, user_id, token) xbmc.executebuiltin("ActivateWindow(Home)") if "estuary_jellycon" in xbmc.getSkinDir(): xbmc.executebuiltin("SetFocus(9000, 0, absolute)") xbmc.executebuiltin("ReloadSkin()") def user_select(api, current_username, code): ''' Display user selection screen ''' # Retrieve list of public users from server public = api.get('/Users/Public') # Get list of saved users saved_users = get_saved_users() # Combine public and saved users for user in saved_users: name = user.get('Name') # Check if saved user is in public list if name not in [x.get('Name', '') for x in public]: # If saved user is not already in list, add it public.append(user) # Build user display selected_id = -1 users = [] # If quick connect is active, make it the first entry if code: user_item = xbmcgui.ListItem(code) user_image = "DefaultUser.png" art = {"Thumb": user_image} user_item.setArt(art) user_item.setLabel2(translate_string(30443)) user_item.setProperty('quickconnect', "true") users.append(user_item) for user in public: user_item = create_user_listitem(api.server, user) if user_item: users.append(user_item) name = user.get("Name") # Highlight currently logged in user if current_username == name: selected_id = len(users) - 1 if current_username: selection_title = translate_string(30180) + " (" + current_username + ")" else: selection_title = translate_string(30180) # Add manual login item user_item = xbmcgui.ListItem(translate_string(30365)) art = {"Thumb": "DefaultUser.png"} user_item.setArt(art) user_item.setLabel2(translate_string(30366)) user_item.setProperty("secure", "true") user_item.setProperty("manual", "true") users.append(user_item) user_selection = xbmcgui.Dialog().select( selection_title, users, preselect=selected_id, autoclose=60000, useDetails=True) return (users, user_selection) def create_user_listitem(server, user): ''' Create a user listitem for the user selection screen ''' config = user.get("Configuration") now = get_current_datetime() if config is not None: name = user.get("Name") time_ago = "" last_active = user.get("LastActivityDate") # Calculate how long it's been since the user was last active if last_active: last_active_date = datetime_from_string(last_active) ago = now - last_active_date # Check days if ago.days > 0: time_ago += ' {}d'.format(ago.days) # Check minutes if ago.seconds > 60: hours = 0 # Check hours if ago.seconds > 3600: hours = int(ago.seconds/3600) time_ago += ' {}h'.format(hours) minutes = int((ago.seconds - (hours * 3600)) / 60) time_ago += ' {}m'.format(minutes) time_ago = time_ago.strip() if not time_ago: time_ago = "Active: now" else: time_ago = "Active: {} ago".format(time_ago) user_item = xbmcgui.ListItem(name) # If the user doesn't have a profile image, user the default if 'PrimaryImageTag' not in user: user_image = "DefaultUser.png" else: user_id = user.get('Id') tag = user.get('PrimaryImageTag') user_image = '{}/Users/{}/Images/Primary?Format=original&tag={}'.format( server, user_id, tag ) art = {"Thumb": user_image} user_item.setArt(art) sub_line = time_ago if user.get("HasPassword", False) is True: user_item.setProperty("secure", "true") else: user_item.setProperty("secure", "false") user_item.setProperty("manual", "false") user_item.setLabel2(sub_line) return user_item return None