# -*- coding: utf-8 -*- from __future__ import division, absolute_import, print_function, unicode_literals ################################################################################################## from kodi_six import xbmc, xbmcaddon import client from database import get_credentials, save_credentials from dialogs import ServerConnect, UsersConnect, LoginManual, ServerManual from helper import settings, addon_id, event, api, window from jellyfin import Jellyfin from jellyfin.connection_manager import CONNECTION_STATE from jellyfin.exceptions import HTTPException from helper import LazyLogger ################################################################################################## LOG = LazyLogger(__name__) XML_PATH = (xbmcaddon.Addon(addon_id()).getAddonInfo('path'), "default", "1080i") ################################################################################################## class Connect(object): def __init__(self): self.info = client.get_info() def register(self, server_id=None, options={}): ''' Login into server. If server is None, then it will show the proper prompts to login, etc. If a server id is specified then only a login dialog will be shown for that server. ''' LOG.info("--[ server/%s ]", server_id or 'default') credentials = dict(get_credentials()) servers = credentials['Servers'] if server_id is None and credentials['Servers']: credentials['Servers'] = [credentials['Servers'][0]] elif credentials['Servers']: for server in credentials['Servers']: if server['Id'] == server_id: credentials['Servers'] = [server] server_select = True if server_id is None and not settings('SyncInstallRunDone.bool') else False new_credentials = self.register_client(credentials, options, server_id, server_select) for server in servers: if server['Id'] == new_credentials['Servers'][0]['Id']: server = new_credentials['Servers'][0] break else: servers = new_credentials['Servers'] credentials['Servers'] = servers save_credentials(credentials) try: Jellyfin(server_id).start(True) except ValueError as error: LOG.error(error) def get_ssl(self): ''' Returns boolean value. True: verify connection. ''' return settings('sslverify.bool') def get_client(self, server_id=None): ''' Get Jellyfin client. ''' client = Jellyfin(server_id) client.config.app("Kodi", self.info['Version'], self.info['DeviceName'], self.info['DeviceId']) client.config.data['http.user_agent'] = "Jellyfin-Kodi/%s" % self.info['Version'] client.config.data['auth.ssl'] = self.get_ssl() return client def register_client(self, credentials=None, options=None, server_id=None, server_selection=False): client = self.get_client(server_id) self.client = client self.connect_manager = client.auth if server_id is None: client.config.data['app.default'] = True try: state = client.authenticate(credentials or {}, options or {}) if state['State'] == CONNECTION_STATE['SignedIn']: client.callback_ws = event if server_id is None: # Only assign for default server client.callback = event self.get_user(client) settings('serverName', client.config.data['auth.server-name']) settings('server', client.config.data['auth.server']) event('ServerOnline', {'ServerId': server_id}) event('LoadServer', {'ServerId': server_id}) return state['Credentials'] elif (server_selection or state['State'] == CONNECTION_STATE['ServerSelection'] or state['State'] == CONNECTION_STATE['Unavailable'] and not settings('SyncInstallRunDone.bool')): self.select_servers(state) elif state['State'] == CONNECTION_STATE['ServerSignIn']: if 'ExchangeToken' not in state['Servers'][0]: self.login() elif state['State'] == CONNECTION_STATE['Unavailable']: raise HTTPException('ServerUnreachable', {}) return self.register_client(state['Credentials'], options, server_id, False) except RuntimeError as error: LOG.exception(error) xbmc.executebuiltin('Addon.OpenSettings(%s)' % addon_id()) raise Exception('User sign in interrupted') except HTTPException as error: if error.status == 'ServerUnreachable': event('ServerUnreachable', {'ServerId': server_id}) return client.get_credentials() def get_user(self, client): ''' Save user info. ''' self.user = client.jellyfin.get_user() settings('username', self.user['Name']) if 'PrimaryImageTag' in self.user: server_address = client.auth.get_server_info(client.auth.server_id)['address'] window('JellyfinUserImage', api.API(self.user, server_address).get_user_artwork(self.user['Id'])) def select_servers(self, state=None): state = state or self.connect_manager.connect({'enableAutoLogin': False}) user = {} dialog = ServerConnect("script-jellyfin-connect-server.xml", *XML_PATH) dialog.set_args(**{ 'connect_manager': self.connect_manager, 'username': user.get('DisplayName', ""), 'user_image': user.get('ImageUrl'), 'servers': state.get('Servers', []) }) dialog.doModal() if dialog.is_server_selected(): LOG.debug("Server selected: %s", dialog.get_server()) return elif dialog.is_manual_server(): LOG.debug("Adding manual server") try: self.manual_server() except RuntimeError: pass else: raise RuntimeError("No server selected") return self.select_servers() def setup_manual_server(self): ''' Setup manual servers ''' client = self.get_client() client.set_credentials(get_credentials()) manager = client.auth try: self.manual_server(manager) except RuntimeError: return credentials = client.get_credentials() save_credentials(credentials) def manual_server(self, manager=None): ''' Return server or raise error. ''' dialog = ServerManual("script-jellyfin-connect-server-manual.xml", *XML_PATH) dialog.set_args(**{'connect_manager': manager or self.connect_manager}) dialog.doModal() if dialog.is_connected(): return dialog.get_server() else: raise RuntimeError("Server is not connected") def login(self): users = self.connect_manager.get_public_users() server = self.connect_manager.get_server_info(self.connect_manager.server_id)['address'] if not users: try: return self.login_manual() except RuntimeError: raise RuntimeError("No user selected") dialog = UsersConnect("script-jellyfin-connect-users.xml", *XML_PATH) dialog.set_args(**{'server': server, 'users': users}) dialog.doModal() if dialog.is_user_selected(): user = dialog.get_user() username = user['Name'] if user['HasPassword']: LOG.debug("User has password, present manual login") try: return self.login_manual(username) except RuntimeError: pass else: return self.connect_manager.login(server, username) elif dialog.is_manual_login(): try: return self.login_manual() except RuntimeError: pass else: raise RuntimeError("No user selected") return self.login() def setup_login_manual(self): ''' Setup manual login by itself for default server. ''' client = self.get_client() client.set_credentials(get_credentials()) manager = client.auth try: self.login_manual(manager=manager) except RuntimeError: return credentials = client.get_credentials() save_credentials(credentials) def login_manual(self, user=None, manager=None): ''' Return manual login user authenticated or raise error. ''' dialog = LoginManual("script-jellyfin-connect-login-manual.xml", *XML_PATH) dialog.set_args(**{'connect_manager': manager or self.connect_manager, 'username': user or {}}) dialog.doModal() if dialog.is_logged_in(): return dialog.get_user() else: raise RuntimeError("User is not authenticated") def remove_server(self, server_id): ''' Stop client and remove server. ''' Jellyfin(server_id).close() credentials = get_credentials() for server in credentials['Servers']: if server['Id'] == server_id: credentials['Servers'].remove(server) break save_credentials(credentials) LOG.info("[ remove server ] %s", server_id)