diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index 37b6e154..e48a9645 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -1158,12 +1158,12 @@ msgstr "" # Server selection dialog: button text to sign in or sign out of plex.tv msgctxt "#30600" -msgid "Toggle plex.tv sign-in" +msgid "Sign-in to plex.tv" msgstr "" # Server selection dialog: button text to add server manually msgctxt "#30601" -msgid "Manually add server" +msgid "Manually add PMS" msgstr "" # Button text, e.g. to cancel a dialog diff --git a/resources/lib/command_pipeline.py b/resources/lib/command_pipeline.py index efb441e0..39bf0aea 100644 --- a/resources/lib/command_pipeline.py +++ b/resources/lib/command_pipeline.py @@ -56,6 +56,7 @@ class Monitor_Window(Thread): except: log.error('Failed to execute function %s with params %s' % (function, params)) + raise def run(self): thread_stopped = self.thread_stopped diff --git a/resources/lib/connect/connectionmanager.py b/resources/lib/connect/connectionmanager.py index c027c33f..625805d4 100644 --- a/resources/lib/connect/connectionmanager.py +++ b/resources/lib/connect/connectionmanager.py @@ -2,13 +2,21 @@ ############################################################################### from logging import getLogger from hashlib import md5 -import json import requests +from struct import pack import socket import time from datetime import datetime +import xml.etree.ElementTree as etree +from Queue import Queue +from threading import Thread + +from xbmc import sleep import credentials as cred +from utils import tryDecode +from PlexFunctions import PMSHttpsEnabled + ############################################################################### @@ -35,6 +43,11 @@ CONNECTIONMODE = { 'Manual': 2 } +# multicast to PMS +IP_PLEXGDM = '239.0.0.250' +PORT_PLEXGDM = 32414 +MSG_PLEXGDM = 'M-SEARCH * HTTP/1.0' + ############################################################################### @@ -52,15 +65,16 @@ def getServerAddress(server, mode): class ConnectionManager(object): - - default_timeout = 20 + default_timeout = 30 apiClients = [] - minServerVersion = "3.0.5930" + minServerVersion = "1.7.0.0" connectUser = None + # Token for plex.tv + plexToken = None def __init__(self, appName, appVersion, deviceName, deviceId, capabilities=None, devicePixelRatio=None): - log.info("Begin ConnectionManager constructor") + log.debug("Instantiating") self.credentialProvider = cred.Credentials() self.appName = appName @@ -144,8 +158,7 @@ class ConnectionManager(object): if server is None or systemInfo is None: return - server['Name'] = systemInfo['ServerName'] - server['Id'] = systemInfo['Id'] + server['Id'] = systemInfo.attrib['machineIdentifier'] if systemInfo.get('LocalAddress'): server['LocalAddress'] = systemInfo['LocalAddress'] @@ -155,16 +168,11 @@ class ConnectionManager(object): server['WakeOnLanInfos'] = [{'MacAddress': systemInfo['MacAddress']}] def _getHeaders(self, request): - headers = request.setdefault('headers', {}) - - if request.get('dataType') == "json": - headers['Accept'] = "application/json" - request.pop('dataType') - - headers['X-Application'] = self._addAppInfoToConnectRequest() - headers['Content-type'] = request.get('contentType', - 'application/x-www-form-urlencoded; charset=UTF-8') + headers['Accept'] = '*/*' + headers['Content-type'] = request.get( + 'contentType', + "application/x-www-form-urlencoded") def requestUrl(self, request): @@ -192,9 +200,10 @@ class ConnectionManager(object): else: try: - return r.json() - except ValueError: - r.content # Read response to release connection + return etree.fromstring(r.content) + except etree.ParseError: + # Read response to release connection + r.content return def _requests(self, action, **kwargs): @@ -212,79 +221,68 @@ class ConnectionManager(object): def getConnectUrl(self, handler): return "https://connect.emby.media/service/%s" % handler - def _findServers(self, foundServers): - + @staticmethod + def _findServers(foundServers): servers = [] - - for foundServer in foundServers: - - server = self._convertEndpointAddressToManualAddress(foundServer) - - info = { - 'Id': foundServer['Id'], - 'LocalAddress': server or foundServer['Address'], - 'Name': foundServer['Name'] - } - info['LastCONNECTIONMODE'] = CONNECTIONMODE['Manual'] if info.get('ManualAddress') else CONNECTIONMODE['Local'] - - servers.append(info) - else: - return servers - - def _convertEndpointAddressToManualAddress(self, info): - - if info.get('Address') and info.get('EndpointAddress'): - address = info['EndpointAddress'].split(':')[0] - - # Determine the port, if any - parts = info['Address'].split(':') - if len(parts) > 1: - portString = parts[len(parts)-1] - - try: - address += ":%s" % int(portString) - return self._normalizeAddress(address) - except ValueError: + for server in foundServers: + if '200 OK' not in server['data']: + continue + ip = server['from'][0] + info = {'LastCONNECTIONMODE': CONNECTIONMODE['Local']} + for line in server['data'].split('\n'): + if line.startswith('Name:'): + info['Name'] = tryDecode(line.split(':')[1].strip()) + elif line.startswith('Port:'): + info['Port'] = line.split(':')[1].strip() + elif line.startswith('Resource-Identifier:'): + info['Id'] = line.split(':')[1].strip() + elif line.startswith('Updated-At:'): pass - - return None + elif line.startswith('Version:'): + pass + # Need to check whether we need HTTPS or only HTTP + https = PMSHttpsEnabled('%s:%s' % (ip, info['Port'])) + if https is None: + # Error contacting url. Skip for now + continue + elif https is True: + info['LocalAddress'] = 'https://%s:%s' % (ip, info['Port']) + else: + info['LocalAddress'] = 'http://%s:%s' % (ip, info['Port']) + servers.append(info) + return servers def _serverDiscovery(self): - - MULTI_GROUP = ("", 7359) - MESSAGE = "who is EmbyServer?" - - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.settimeout(1.0) # This controls the socket.timeout exception + """ + PlexGDM + """ + # setup socket for discovery -> multicast message + GDM = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + GDM.settimeout(2.0) - sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 20) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) - sock.setsockopt(socket.IPPROTO_IP, socket.SO_REUSEADDR, 1) - - log.debug("MultiGroup : %s" % str(MULTI_GROUP)) - log.debug("Sending UDP Data: %s" % MESSAGE) + # Set the time-to-live for messages to 2 for local network + ttl = pack('b', 2) + GDM.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl) servers = [] - try: - sock.sendto(MESSAGE, MULTI_GROUP) - except Exception as error: - log.error(error) - return servers - - while True: - try: - data, addr = sock.recvfrom(1024) # buffer size - servers.append(json.loads(data)) - - except socket.timeout: - log.info("Found Servers: %s" % servers) - return servers - - except Exception as e: - log.error("Error trying to find servers: %s" % e) - return servers + # Send data to the multicast group + GDM.sendto(MSG_PLEXGDM, (IP_PLEXGDM, PORT_PLEXGDM)) + # Look for responses from all recipients + while True: + try: + data, server = GDM.recvfrom(1024) + servers.append({'from': server, 'data': data}) + except socket.timeout: + break + except: + # Probably error: (101, 'Network is unreachable') + log.error('Could not find Plex servers using GDM') + import traceback + log.error("Traceback:\n%s" % traceback.format_exc()) + finally: + GDM.close() + return servers def _normalizeAddress(self, address): # Attempt to correct bad input @@ -345,15 +343,11 @@ class ConnectionManager(object): self.credentialProvider.getCredentials(credentials) def _tryConnect(self, url, timeout=None, options={}): - - url = self.getEmbyServerUrl(url, "system/info/public") - log.info("tryConnect url: %s" % url) - + url = '%s/identity' % url + log.debug("tryConnect url: %s" % url) return self.requestUrl({ - 'type': "GET", 'url': url, - 'dataType': "json", 'timeout': timeout, 'ssl': options.get('ssl') }) @@ -361,6 +355,61 @@ class ConnectionManager(object): def _addAppInfoToConnectRequest(self): return "%s/%s" % (self.appName, self.appVersion) + def __get_PMS_servers_from_plex_tv(self): + """ + Retrieves Plex Media Servers from plex.tv/pms/resources + """ + servers = [] + xml = self.requestUrl({ + 'url': 'https://plex.tv/api/resources?includeHttps=1', + 'type': 'GET', + 'headers': {'X-Plex-Token': self.plexToken}, + 'timeout': 5.0, + 'ssl': True}) + try: + xml.attrib + except AttributeError: + log.error('Could not get list of PMS from plex.tv') + return servers + + maxAgeSeconds = 2*60*60*24 + for device in xml.findall('Device'): + if 'server' not in device.attrib.get('provides'): + # No PMS - skip + continue + cons = device.find('Connection') + if cons is None: + # no valid connection - skip + continue + # check MyPlex data age - skip if >2 days + server = {'Name': device.attrib.get('name')} + infoAge = time.time() - int(device.attrib.get('lastSeenAt')) + if infoAge > maxAgeSeconds: + log.info("Server %s not seen for 2 days - skipping." + % server['Name']) + continue + server['Id'] = device.attrib['clientIdentifier'] + server['ConnectServerId'] = device.attrib['clientIdentifier'] + # server['AccessToken'] = device.attrib['accessToken'] + server['ExchangeToken'] = device.attrib['accessToken'] + # One's own Plex home? + server['UserLinkType'] = 'Guest' if device.attrib['owned'] == '0' \ + else 'LinkedUser' + # Foreign PMS' user name + server['UserId'] = device.attrib.get('sourceTitle') + for con in cons: + if con.attrib['local'] == '1': + # Local LAN address; there might be several!! + server['LocalAddress'] = con.attrib['uri'] + else: + server['RemoteAddress'] = con.attrib['uri'] + + # Additional stuff, not yet implemented + server['local'] = device.attrib.get('publicAddressMatches') + + servers.append(server) + return servers + def _getConnectServers(self, credentials): log.info("Begin getConnectServers") @@ -375,7 +424,6 @@ class ConnectionManager(object): 'type': "GET", 'url': url, - 'dataType': "json", 'headers': { 'X-Connect-UserToken': credentials['ConnectAccessToken'] } @@ -396,18 +444,16 @@ class ConnectionManager(object): return servers def getAvailableServers(self): - log.info("Begin getAvailableServers") - # Clone the array credentials = self.credentialProvider.getCredentials() - - connectServers = self._getConnectServers(credentials) - foundServers = self._findServers(self._serverDiscovery()) - servers = list(credentials['Servers']) + + if self.plexToken: + connectServers = self.__get_PMS_servers_from_plex_tv() + self._mergeServers(servers, connectServers) + foundServers = self._findServers(self._serverDiscovery()) self._mergeServers(servers, foundServers) - self._mergeServers(servers, connectServers) servers = self._filterServers(servers, connectServers) @@ -550,8 +596,9 @@ class ConnectionManager(object): return self._testNextCONNECTIONMODE(tests, index+1, server, options) else: - if self._compareVersions(self._getMinServerVersion(), result['Version']) == 1: - log.warn("minServerVersion requirement not met. Server version: %s" % result['Version']) + if self._compareVersions(self._getMinServerVersion(), + result.attrib['version']) == 1: + log.warn("minServerVersion requirement not met. Server version: %s" % result.attrib['version']) return { 'State': CONNECTIONSTATE['ServerUpdateNeeded'], 'Servers': [server] @@ -616,7 +663,6 @@ class ConnectionManager(object): 'type': "GET", 'url': self.getEmbyServerUrl(url, "System/Info"), 'ssl': options.get('ssl'), - 'dataType': "json", 'headers': { 'X-MediaBrowser-Token': server['AccessToken'] } @@ -631,7 +677,6 @@ class ConnectionManager(object): 'type': "GET", 'url': self.getEmbyServerUrl(url, "users/%s" % server['UserId']), 'ssl': options.get('ssl'), - 'dataType': "json", 'headers': { 'X-MediaBrowser-Token': server['AccessToken'] } @@ -658,7 +703,6 @@ class ConnectionManager(object): 'nameOrEmail': username, 'password': md5 }, - 'dataType': "json" } try: result = self.requestUrl(request) @@ -695,7 +739,6 @@ class ConnectionManager(object): 'type': "GET", 'url': url, - 'dataType': "json", 'headers': { 'X-Connect-UserToken': accessToken } @@ -718,7 +761,6 @@ class ConnectionManager(object): 'url': url, 'type': "GET", - 'dataType': "json", 'ssl': options.get('ssl'), 'params': { 'ConnectUserId': credentials['ConnectUserId'] diff --git a/resources/lib/connectmanager.py b/resources/lib/connectmanager.py index 1503468d..b5f674f8 100644 --- a/resources/lib/connectmanager.py +++ b/resources/lib/connectmanager.py @@ -1,14 +1,20 @@ # -*- coding: utf-8 -*- ############################################################################### from logging import getLogger +from copy import deepcopy +from os import makedirs + +from xbmc import getIPAddress # from connect.connectionmanager import ConnectionManager from downloadutils import DownloadUtils from dialogs.serverconnect import ServerConnect from dialogs.servermanual import ServerManual from connect.plex_tv import plex_tv_sign_in_with_pin +import connect.connectionmanager as connectionmanager from userclient import UserClient -from utils import window, settings, tryEncode, language as lang, dialog +from utils import window, settings, tryEncode, language as lang, dialog, \ + exists_dir from PlexFunctions import GetMachineIdentifier, get_pms_settings, \ check_connection import variables as v @@ -18,6 +24,7 @@ import state log = getLogger("PLEX."+__name__) +STATE = connectionmanager.CONNECTIONSTATE XML_PATH = (tryEncode(v.ADDON_PATH), "default", "1080i") ############################################################################### @@ -55,6 +62,7 @@ def get_plex_login_from_settings(): class ConnectManager(object): # Borg __shared_state = {} + state = {} def __init__(self): # Borg @@ -68,19 +76,36 @@ class ConnectManager(object): plexdict = get_plex_login_from_settings() self.myplexlogin = plexdict['myplexlogin'] == 'true' self.plexLogin = plexdict['plexLogin'] - self.plexToken = plexdict['plexToken'] self.plexid = plexdict['plexid'] # Token for the PMS, not plex.tv + self.__connect = connectionmanager.ConnectionManager( + appName="Kodi", + appVersion=v.ADDON_VERSION, + deviceName=v.DEVICENAME, + deviceId=window('plex_client_Id')) + self.pms_token = settings('accessToken') + self.plexToken = plexdict['plexToken'] + self.__connect.plexToken = self.plexToken if self.plexToken: log.debug('Found a plex.tv token in the settings') + if not exists_dir(v.ADDON_PATH_DATA): + makedirs(v.ADDON_PATH_DATA) + self.__connect.setFilePath(v.ADDON_PATH_DATA) + + if state.CONNECT_STATE: + self.state = state.CONNECT_STATE + else: + self.state = self.__connect.connect() + log.debug("Started with: %s", self.state) + state.CONNECT_STATE = deepcopy(self.state) def update_state(self): self.state = self.__connect.connect({'updateDateLastAccessed': False}) return self.get_state() - def get_sate(self): - window('emby_state.json', value=self.state) + def get_state(self): + state.CONNECT_STATE = deepcopy(self.state) return self.state def get_server(self, server, options={}): @@ -98,13 +123,14 @@ class ConnectManager(object): """ Will return selected server or raise RuntimeError """ + status = self.__connect.connect({'enableAutoLogin': False}) dia = ServerConnect("script-plex-connect-server.xml", *XML_PATH) kwargs = { - 'connect_manager': None, # self._connect + 'connect_manager': self.__connect, 'username': state.PLEX_USERNAME, 'user_image': state.PLEX_USER_IMAGE, - # 'servers': state.get('Servers') or [], - # 'emby_connect': False if user else True + 'servers': status.get('Servers') or [], + 'plex_connect': False if status.get('ConnectUser') else True } dia.set_args(**kwargs) dia.doModal() @@ -113,12 +139,9 @@ class ConnectManager(object): log.debug("Server selected") return dia.get_server() - elif dia._is_connect_login(): + elif dia.is_connect_login(): log.debug("Login to plex.tv") - try: - self._login_connect() - except RuntimeError: - pass + self.plex_tv_signin() return self.select_servers() elif dia.is_manual_server(): @@ -134,7 +157,7 @@ class ConnectManager(object): def manual_server(self): # Return server or raise error dia = ServerManual("script-plex-connect-server-manual.xml", *XML_PATH) - dia._set_connect_manager(self.__connect) + dia.set_connect_manager(self.__connect) dia.doModal() if dia._is_connected(): @@ -142,19 +165,6 @@ class ConnectManager(object): else: raise RuntimeError("Server is not connected") - def _login_connect(self): - # Return connect user or raise error - dia = LoginConnect("script-emby-connect-login.xml", *XML_PATH) - dia._set_connect_manager(self.__connect) - dia.doModal() - - self.update_state() - - if dia.is_logged_in(): - return dia.get_user() - else: - raise RuntimeError("Connect user is not logged in") - def login(self, server=None): # Return user or raise error server = server or self.state['Servers'][0] @@ -233,7 +243,7 @@ class ConnectManager(object): # Update the token in data.txt self.__connect.credentialProvider.getCredentials(credentials) - def _get_connect_servers(self): + def get_connect_servers(self): connect_servers = [] servers = self.__connect.getAvailableServers() @@ -580,8 +590,7 @@ class ConnectManager(object): """ Returns a list of servers from GDM and possibly plex.tv """ - self.discoverPMS(xbmc.getIPAddress(), - plexToken=self.plexToken) + self.discoverPMS(getIPAddress(), plexToken=self.plexToken) serverlist = self.plx.returnServerList(self.plx.g_PMS) log.debug('PMS serverlist: %s' % serverlist) return serverlist diff --git a/resources/lib/dialogs/loginconnect.py b/resources/lib/dialogs/loginconnect.py deleted file mode 100644 index db7c39cc..00000000 --- a/resources/lib/dialogs/loginconnect.py +++ /dev/null @@ -1,136 +0,0 @@ -# -*- coding: utf-8 -*- - -################################################################################################## - -import logging -import os - -import xbmcgui -import xbmcaddon - -from utils import language as lang - -################################################################################################## - -log = logging.getLogger("EMBY."+__name__) -addon = xbmcaddon.Addon('plugin.video.emby') - -ACTION_PARENT_DIR = 9 -ACTION_PREVIOUS_MENU = 10 -ACTION_BACK = 92 -SIGN_IN = 200 -CANCEL = 201 -ERROR_TOGGLE = 202 -ERROR_MSG = 203 -ERROR = { - 'Invalid': 1, - 'Empty': 2 -} - -################################################################################################## - - -class LoginConnect(xbmcgui.WindowXMLDialog): - - _user = None - error = None - - - def __init__(self, *args, **kwargs): - - xbmcgui.WindowXMLDialog.__init__(self, *args, **kwargs) - - def set_connect_manager(self, connect_manager): - self.connect_manager = connect_manager - - def is_logged_in(self): - return True if self._user else False - - def get_user(self): - return self._user - - - def onInit(self): - - self.user_field = self._add_editcontrol(725, 385, 40, 500) - self.setFocus(self.user_field) - self.password_field = self._add_editcontrol(725, 470, 40, 500, password=1) - self.signin_button = self.getControl(SIGN_IN) - self.remind_button = self.getControl(CANCEL) - self.error_toggle = self.getControl(ERROR_TOGGLE) - self.error_msg = self.getControl(ERROR_MSG) - - self.user_field.controlUp(self.remind_button) - self.user_field.controlDown(self.password_field) - self.password_field.controlUp(self.user_field) - self.password_field.controlDown(self.signin_button) - self.signin_button.controlUp(self.password_field) - self.remind_button.controlDown(self.user_field) - - def onClick(self, control): - - if control == SIGN_IN: - # Sign in to emby connect - self._disable_error() - - user = self.user_field.getText() - password = self.password_field.getText() - - if not user or not password: - # Display error - self._error(ERROR['Empty'], lang(30608)) - log.error("Username or password cannot be null") - - elif self._login(user, password): - self.close() - - elif control == CANCEL: - # Remind me later - self.close() - - def onAction(self, action): - - if (self.error == ERROR['Empty'] - and self.user_field.getText() and self.password_field.getText()): - self._disable_error() - - if action in (ACTION_BACK, ACTION_PARENT_DIR, ACTION_PREVIOUS_MENU): - self.close() - - def _add_editcontrol(self, x, y, height, width, password=0): - - media = os.path.join(addon.getAddonInfo('path'), 'resources', 'skins', 'default', 'media') - control = xbmcgui.ControlEdit(0, 0, 0, 0, - label="User", - font="font10", - textColor="ff525252", - focusTexture=os.path.join(media, "button-focus.png"), - noFocusTexture=os.path.join(media, "button-focus.png"), - isPassword=password) - control.setPosition(x, y) - control.setHeight(height) - control.setWidth(width) - - self.addControl(control) - return control - - def _login(self, username, password): - - result = self.connect_manager.loginToConnect(username, password) - if result is False: - self._error(ERROR['Invalid'], lang(33009)) - return False - else: - self._user = result - return True - - def _error(self, state, message): - - self.error = state - self.error_msg.setLabel(message) - self.error_toggle.setVisibleCondition('True') - - def _disable_error(self): - - self.error = None - self.error_toggle.setVisibleCondition('False') diff --git a/resources/lib/dialogs/serverconnect.py b/resources/lib/dialogs/serverconnect.py index 4d7bc040..a25f83bd 100644 --- a/resources/lib/dialogs/serverconnect.py +++ b/resources/lib/dialogs/serverconnect.py @@ -7,12 +7,14 @@ from logging import getLogger import xbmc import xbmcgui +import connect.connectionmanager as connectionmanager from utils import language as lang ############################################################################### log = getLogger("PLEX."+__name__) +CONN_STATE = connectionmanager.CONNECTIONSTATE ACTION_PARENT_DIR = 9 ACTION_PREVIOUS_MENU = 10 ACTION_BACK = 92 @@ -25,7 +27,7 @@ CANCEL = 201 MESSAGE_BOX = 202 MESSAGE = 203 BUSY = 204 -EMBY_CONNECT = 205 +PLEX_CONNECT = 205 MANUAL_SERVER = 206 ############################################################################### @@ -42,7 +44,7 @@ class ServerConnect(xbmcgui.WindowXMLDialog): _manual_server = False def set_args(self, **kwargs): - # connect_manager, username, user_image, servers, emby_connect + # connect_manager, username, user_image, servers, plex_connect for key, value in kwargs.iteritems(): setattr(self, key, value) @@ -74,8 +76,8 @@ class ServerConnect(xbmcgui.WindowXMLDialog): if self.user_image is not None: self.getControl(USER_IMAGE).setImage(self.user_image) - if not self.emby_connect: # Change connect user - self.getControl(EMBY_CONNECT).setLabel("[UPPERCASE][B]"+'plex.tv user change'+"[/B][/UPPERCASE]") + if not self.plex_connect: # Change connect user + self.getControl(PLEX_CONNECT).setLabel("[UPPERCASE][B]"+'plex.tv user change'+"[/B][/UPPERCASE]") if self.servers: self.setFocus(self.list_) @@ -107,7 +109,7 @@ class ServerConnect(xbmcgui.WindowXMLDialog): def onClick(self, control): - if control == EMBY_CONNECT: + if control == PLEX_CONNECT: self.connect_manager.clearData() self._connect_login = True self.close() diff --git a/resources/lib/state.py b/resources/lib/state.py index 73f8da15..c16af79e 100644 --- a/resources/lib/state.py +++ b/resources/lib/state.py @@ -38,3 +38,6 @@ PLEX_USER_ID = None # Token passed along, e.g. if playback initiated by Plex Companion. Might be # another user playing something! Token identifies user PLEX_TRANSIENT_TOKEN = None + +# Used by connectmanager.py +CONNECT_STATE = {} diff --git a/resources/lib/variables.py b/resources/lib/variables.py index 85766cee..bddcb50a 100644 --- a/resources/lib/variables.py +++ b/resources/lib/variables.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import xbmc from xbmcaddon import Addon +from os.path import join # Paths are in unicode, otherwise Windows will throw fits # For any file operations with KODI function, use encoded strings! @@ -32,6 +33,7 @@ KODILANGUAGE = xbmc.getLanguage(xbmc.ISO_639_1) KODIVERSION = int(xbmc.getInfoLabel("System.BuildVersion")[:2]) KODILONGVERSION = xbmc.getInfoLabel('System.BuildVersion') KODI_PROFILE = tryDecode(xbmc.translatePath("special://profile")) +ADDON_PATH_DATA = join(KODI_PROFILE, 'addon_data', ADDON_ID, '') if xbmc.getCondVisibility('system.platform.osx'): PLATFORM = "MacOSX" diff --git a/resources/skins/default/1080i/script-emby-connect-login.xml b/resources/skins/default/1080i/script-emby-connect-login.xml deleted file mode 100644 index cb69387c..00000000 --- a/resources/skins/default/1080i/script-emby-connect-login.xml +++ /dev/null @@ -1,179 +0,0 @@ - - - 200 - 0 - dialogeffect - - - Background fade - 100% - 100% - emby-bg-fade.png - - - - 600 - 35% - 15% - - Background box - white.png - 600 - 700 - - - - 705 - False - - Error box - white.png - 100% - 50 - - - - Error message - white - font10 - center - center - 50 - - - - - Emby logo - logo-white.png - 160 - 49 - 30 - 25 - - - - 500 - 50 - - Sign in emby connect - - white - font12 - top - 115 - - - - 190 - - Username email - - ffa6a6a6 - font10 - top - - - - separator - 102% - 0.5 - 66 - -10 - emby-separator.png - - - - - Password - 275 - - Password label - - ffa6a6a6 - font10 - top - - - - separator - 102% - 0.5 - 66 - -10 - emby-separator.png - - - - - Buttons - 385 - - Sign in - box.png - box.png - - font10 - ffa6a6a6 - white - center - 100% - 50 - 201 - - - - Cancel - box.png - box.png - - font10 - ffa6a6a6 - white - center - 100% - 50 - 55 - 200 - - - - - Disclaimer - 510 - - Disclaimer label - - font10 - ff464646 - true - top - 340 - 100% - - - - - Scan me - - font12 - ff0b8628 - top - 200 - 120 - 230 - - - - qrcode - qrcode_disclaimer.png - 140 - 140 - 10 - 360 - - - - - - - \ No newline at end of file