Revert "First attempt at server select dialog"
This reverts commit 59040f3b3e
.
This commit is contained in:
parent
59040f3b3e
commit
5cdda0e334
6 changed files with 72 additions and 599 deletions
|
@ -11,8 +11,9 @@ from threading import Thread
|
||||||
from xbmc import sleep
|
from xbmc import sleep
|
||||||
|
|
||||||
from .downloadutils import DownloadUtils as DU
|
from .downloadutils import DownloadUtils as DU
|
||||||
from .plexapi.base import PlexServer, Connection
|
from . import utils
|
||||||
from . import utils, plex_tv, variables as v
|
from . import plex_tv
|
||||||
|
from . import variables as v
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
LOG = getLogger('PLEX.plex_functions')
|
LOG = getLogger('PLEX.plex_functions')
|
||||||
|
@ -192,7 +193,7 @@ def discover_pms(token=None):
|
||||||
"""
|
"""
|
||||||
LOG.info('Start discovery of Plex Media Servers')
|
LOG.info('Start discovery of Plex Media Servers')
|
||||||
# Look first for local PMS in the LAN
|
# Look first for local PMS in the LAN
|
||||||
local_pms_list = plex_gdm()
|
local_pms_list = _plex_gdm()
|
||||||
LOG.debug('PMS found in the local LAN using Plex GDM: %s', local_pms_list)
|
LOG.debug('PMS found in the local LAN using Plex GDM: %s', local_pms_list)
|
||||||
# Get PMS from plex.tv
|
# Get PMS from plex.tv
|
||||||
if token:
|
if token:
|
||||||
|
@ -235,7 +236,7 @@ def _log_pms(pms_list):
|
||||||
LOG.debug('Found the following PMS: %s', log_list)
|
LOG.debug('Found the following PMS: %s', log_list)
|
||||||
|
|
||||||
|
|
||||||
def plex_gdm():
|
def _plex_gdm():
|
||||||
"""
|
"""
|
||||||
PlexGDM - looks for PMS in the local LAN and returns a list of the PMS found
|
PlexGDM - looks for PMS in the local LAN and returns a list of the PMS found
|
||||||
"""
|
"""
|
||||||
|
@ -277,54 +278,44 @@ def plex_gdm():
|
||||||
# Check if we had a positive HTTP response
|
# Check if we had a positive HTTP response
|
||||||
if '200 OK' not in response['data']:
|
if '200 OK' not in response['data']:
|
||||||
continue
|
continue
|
||||||
connection = Connection(local=True)
|
pms = {
|
||||||
# Local LAN IP from GDM
|
'ip': response['from'][0],
|
||||||
connection.address = response['from'][0]
|
'scheme': None,
|
||||||
pms = PlexServer()
|
'local': True, # Since we found it using GDM
|
||||||
pms.presence = True
|
'product': None,
|
||||||
pms.connections.append(connection)
|
'baseURL': None,
|
||||||
|
'name': None,
|
||||||
|
'version': None,
|
||||||
|
'token': None,
|
||||||
|
'ownername': None,
|
||||||
|
'device': None,
|
||||||
|
'platform': None,
|
||||||
|
'owned': None,
|
||||||
|
'relay': None,
|
||||||
|
'presence': True, # Since we're talking to the PMS
|
||||||
|
'httpsRequired': None,
|
||||||
|
}
|
||||||
for line in response['data'].split('\n'):
|
for line in response['data'].split('\n'):
|
||||||
try:
|
if 'Content-Type:' in line:
|
||||||
kind, info = line.split(':', 1)
|
pms['product'] = utils.try_decode(line.split(':')[1].strip())
|
||||||
except ValueError:
|
elif 'Host:' in line:
|
||||||
continue
|
pms['baseURL'] = line.split(':')[1].strip()
|
||||||
else:
|
elif 'Name:' in line:
|
||||||
kind, info = kind.strip(), info.strip()
|
pms['name'] = utils.try_decode(line.split(':')[1].strip())
|
||||||
if kind == 'Name':
|
elif 'Port:' in line:
|
||||||
pms.name = info
|
pms['port'] = line.split(':')[1].strip()
|
||||||
elif kind == 'Resource-Identifier':
|
elif 'Resource-Identifier:' in line:
|
||||||
pms.clientIdentifier = info
|
pms['machineIdentifier'] = line.split(':')[1].strip()
|
||||||
elif kind == 'Content-Type':
|
elif 'Version:' in line:
|
||||||
pms.product = info
|
pms['version'] = line.split(':')[1].strip()
|
||||||
elif kind == 'Version':
|
|
||||||
pms.productVersion = info
|
|
||||||
elif kind == 'Updated-At':
|
|
||||||
pms.lastSeenAt = int(info)
|
|
||||||
elif kind == 'Host':
|
|
||||||
# Example: "Host: <....sfe...>.plex.direct"
|
|
||||||
connection.uri = info
|
|
||||||
elif kind == 'Port':
|
|
||||||
connection.port = int(info)
|
|
||||||
# Assume https
|
|
||||||
connection.protocol = 'https'
|
|
||||||
if connection.uri != connection.address:
|
|
||||||
# The PMS might return both local IP and plex.direct address
|
|
||||||
alt_connection = deepcopy(connection)
|
|
||||||
alt_connection.uri = 'https://%s:%s' % (connection.address,
|
|
||||||
connection.port)
|
|
||||||
pms.connections.append(alt_connection)
|
|
||||||
connection.uri = 'https://%s:%s' % (connection.uri,
|
|
||||||
connection.port)
|
|
||||||
pms_list.append(pms)
|
pms_list.append(pms)
|
||||||
LOG.debug('Found PMS in the LAN: %s: %s', pms, pms.connections)
|
|
||||||
return pms_list
|
return pms_list
|
||||||
|
|
||||||
|
|
||||||
def pms_from_plex_tv(token):
|
def _pms_list_from_plex_tv(token):
|
||||||
"""
|
"""
|
||||||
get Plex media Server List from plex.tv/pms/resources
|
get Plex media Server List from plex.tv/pms/resources
|
||||||
"""
|
"""
|
||||||
pms_list = []
|
|
||||||
xml = DU().downloadUrl('https://plex.tv/api/resources',
|
xml = DU().downloadUrl('https://plex.tv/api/resources',
|
||||||
authenticate=False,
|
authenticate=False,
|
||||||
parameters={'includeHttps': 1},
|
parameters={'includeHttps': 1},
|
||||||
|
@ -333,20 +324,49 @@ def pms_from_plex_tv(token):
|
||||||
xml.attrib
|
xml.attrib
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
LOG.error('Could not get list of PMS from plex.tv')
|
LOG.error('Could not get list of PMS from plex.tv')
|
||||||
return pms_list
|
return []
|
||||||
|
|
||||||
|
from Queue import Queue
|
||||||
|
queue = Queue()
|
||||||
|
thread_queue = []
|
||||||
|
|
||||||
|
max_age_in_seconds = 2 * 60 * 60 * 24
|
||||||
for device in xml.findall('Device'):
|
for device in xml.findall('Device'):
|
||||||
if 'server' not in device.get('provides', ''):
|
if 'server' not in device.get('provides'):
|
||||||
# No PMS - skip
|
# No PMS - skip
|
||||||
continue
|
continue
|
||||||
if device.find('Connection') is None:
|
if device.find('Connection') is None:
|
||||||
# no valid connection - skip
|
# no valid connection - skip
|
||||||
continue
|
continue
|
||||||
pms = PlexServer(xml=device)
|
# check MyPlex data age - skip if >2 days
|
||||||
pms_list.append(pms)
|
info_age = time() - int(device.get('lastSeenAt'))
|
||||||
return pms_list
|
if info_age > max_age_in_seconds:
|
||||||
|
LOG.debug("Skip server %s not seen for 2 days", device.get('name'))
|
||||||
|
continue
|
||||||
|
pms = {
|
||||||
|
'machineIdentifier': device.get('clientIdentifier'),
|
||||||
|
'name': device.get('name'),
|
||||||
|
'token': device.get('accessToken'),
|
||||||
|
'ownername': device.get('sourceTitle'),
|
||||||
|
'product': device.get('product'), # e.g. 'Plex Media Server'
|
||||||
|
'version': device.get('productVersion'), # e.g. '1.11.2.4772-3e..'
|
||||||
|
'device': device.get('device'), # e.g. 'PC' or 'Windows'
|
||||||
|
'platform': device.get('platform'), # e.g. 'Windows', 'Android'
|
||||||
|
'local': device.get('publicAddressMatches') == '1',
|
||||||
|
'owned': device.get('owned') == '1',
|
||||||
|
'relay': device.get('relay') == '1',
|
||||||
|
'presence': device.get('presence') == '1',
|
||||||
|
'httpsRequired': device.get('httpsRequired') == '1',
|
||||||
|
'connections': []
|
||||||
|
}
|
||||||
|
# Try a local connection first, no matter what plex.tv tells us
|
||||||
|
for connection in device.findall('Connection'):
|
||||||
|
if connection.get('local') == '1':
|
||||||
|
pms['connections'].append(connection)
|
||||||
|
# Then try non-local
|
||||||
|
for connection in device.findall('Connection'):
|
||||||
|
if connection.get('local') != '1':
|
||||||
|
pms['connections'].append(connection)
|
||||||
# Spawn threads to ping each PMS simultaneously
|
# Spawn threads to ping each PMS simultaneously
|
||||||
thread = Thread(target=_poke_pms, args=(pms, queue))
|
thread = Thread(target=_poke_pms, args=(pms, queue))
|
||||||
thread_queue.append(thread)
|
thread_queue.append(thread)
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
# Dummy file to make this directory a package.
|
|
|
@ -1,126 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
"""
|
|
||||||
from __future__ import absolute_import, division, unicode_literals
|
|
||||||
from logging import getLogger
|
|
||||||
|
|
||||||
from ..utils import cast, to_list
|
|
||||||
|
|
||||||
LOG = getLogger('PLEX.' + __name__)
|
|
||||||
|
|
||||||
|
|
||||||
class Connection(object):
|
|
||||||
def __init__(self, xml=None, **kwargs):
|
|
||||||
self.protocol = None
|
|
||||||
self.address = None
|
|
||||||
self.port = None
|
|
||||||
self.uri = None
|
|
||||||
self.local = None
|
|
||||||
if xml:
|
|
||||||
self.load_from_xml(xml)
|
|
||||||
# Set all remaining attributes set on Class instantiation
|
|
||||||
for key, value in kwargs.items():
|
|
||||||
setattr(self, key, value)
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return '<Connection {self.uri}>'.format(self=self)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return self.__unicode__().encode('utf-8')
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return self.uri == other.uri
|
|
||||||
|
|
||||||
def __ne__(self, other):
|
|
||||||
return self.uri != other.uri
|
|
||||||
|
|
||||||
def load_from_xml(self, xml):
|
|
||||||
"""
|
|
||||||
Throw in an etree xml-element to load PMS settings from it
|
|
||||||
"""
|
|
||||||
if xml.tag != 'Connection':
|
|
||||||
raise RuntimeError('Did not receive Connection xml but %s'
|
|
||||||
% xml.tag)
|
|
||||||
self.protocol = cast(unicode, xml.get('protocol'))
|
|
||||||
self.address = cast(unicode, xml.get('address'))
|
|
||||||
self.port = cast(int, xml.get('port'))
|
|
||||||
self.uri = cast(unicode, xml.get('uri'))
|
|
||||||
self.local = cast(bool, xml.get('local'))
|
|
||||||
|
|
||||||
|
|
||||||
class PlexServer(object):
|
|
||||||
def __init__(self, xml=None, **kwargs):
|
|
||||||
# Information from plex.tv
|
|
||||||
self.name = None
|
|
||||||
self.clientIdentifier = None
|
|
||||||
self.provides = set()
|
|
||||||
self.owned = None
|
|
||||||
self.home = None
|
|
||||||
self.httpsRequired = None
|
|
||||||
self.synced = None
|
|
||||||
self.relay = None
|
|
||||||
self.publicAddressMatches = None
|
|
||||||
self.presence = None
|
|
||||||
self.accessToken = None
|
|
||||||
|
|
||||||
self.product = None # Usually "Plex Media Server"
|
|
||||||
self.ownerId = None # User id of the owner of this PMS
|
|
||||||
self.owner = None # User name of the (foreign!) owner
|
|
||||||
self.productVersion = None
|
|
||||||
self.platform = None
|
|
||||||
self.platformVersion = None
|
|
||||||
self.device = None
|
|
||||||
self.createdAt = None
|
|
||||||
self.lastSeenAt = None
|
|
||||||
|
|
||||||
# Connection info
|
|
||||||
self.connections = []
|
|
||||||
if xml:
|
|
||||||
self.load_from_xml(xml)
|
|
||||||
# Set all remaining attributes set on Class instantiation
|
|
||||||
for key, value in kwargs.items():
|
|
||||||
setattr(self, key, value)
|
|
||||||
|
|
||||||
def load_from_xml(self, xml):
|
|
||||||
"""
|
|
||||||
Throw in an etree xml-element to load PMS settings from it
|
|
||||||
"""
|
|
||||||
if xml.tag != 'Device':
|
|
||||||
raise RuntimeError('Did not receive Device xml but %s' % xml.tag)
|
|
||||||
self.name = cast(unicode, xml.get('name'))
|
|
||||||
self.clientIdentifier = cast(unicode, xml.get('clientIdentifier'))
|
|
||||||
self.provides = set(to_list(cast(unicode, xml.get('provides'))))
|
|
||||||
self.owned = cast(bool, xml.get('owned'))
|
|
||||||
self.home = cast(bool, xml.get('home'))
|
|
||||||
self.httpsRequired = cast(bool, xml.get('httpsRequired'))
|
|
||||||
self.synced = cast(bool, xml.get('synced'))
|
|
||||||
self.relay = cast(bool, xml.get('relay'))
|
|
||||||
self.publicAddressMatches = cast(bool,
|
|
||||||
xml.get('publicAddressMatches'))
|
|
||||||
self.presence = cast(bool, xml.get('presence'))
|
|
||||||
self.accessToken = cast(unicode, xml.get('accessToken'))
|
|
||||||
self.product = cast(unicode, xml.get('product'))
|
|
||||||
self.ownerId = cast(int, xml.get('ownerId'))
|
|
||||||
self.owner = cast(unicode, xml.get('sourceTitle'))
|
|
||||||
self.productVersion = cast(unicode, xml.get('productVersion'))
|
|
||||||
self.platform = cast(unicode, xml.get('platform'))
|
|
||||||
self.platformVersion = cast(unicode, xml.get('platformVersion'))
|
|
||||||
self.device = cast(unicode, xml.get('device'))
|
|
||||||
self.createdAt = cast(int, xml.get('createdAt'))
|
|
||||||
self.lastSeenAt = cast(int, xml.get('lastSeenAt'))
|
|
||||||
|
|
||||||
for connection in xml.findall('Connection'):
|
|
||||||
self.connections.append(Connection(xml=connection))
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return '<PlexServer {self.name}:{self.clientIdentifier}>'.format(self=self)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return self.__unicode__().encode('utf-8')
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return self.clientIdentifier == other.clientIdentifier
|
|
||||||
|
|
||||||
def __ne__(self, other):
|
|
||||||
return self.clientIdentifier != other.clientIdentifier
|
|
|
@ -249,46 +249,6 @@ def ERROR(txt='', hide_tb=False, notify=False):
|
||||||
return short
|
return short
|
||||||
|
|
||||||
|
|
||||||
def to_list(value, itemcast=None, delim=','):
|
|
||||||
"""
|
|
||||||
Returns a list of unicodes from the specified value [unicode].
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
value [unicode]: (comma) delimited string to convert to list.
|
|
||||||
itemcast (func): Function to cast each list item to (default unicode).
|
|
||||||
delim (str): string delimiter (optional; default ',').
|
|
||||||
"""
|
|
||||||
value = value or ''
|
|
||||||
itemcast = itemcast or unicode
|
|
||||||
return [itemcast(item) for item in value.split(delim) if item != '']
|
|
||||||
|
|
||||||
|
|
||||||
def cast(func, value):
|
|
||||||
"""
|
|
||||||
Cast the specified value to the specified type (returned by func).
|
|
||||||
Currently supports int, float, bool, unicode (if str supplied), str
|
|
||||||
Will return None if value=None
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
func (func): Calback function to used cast to type
|
|
||||||
value (any): value to be cast and returned.
|
|
||||||
"""
|
|
||||||
if value is None:
|
|
||||||
return value
|
|
||||||
if func == bool:
|
|
||||||
return bool(int(value))
|
|
||||||
elif func in (int, float):
|
|
||||||
try:
|
|
||||||
return func(value)
|
|
||||||
except ValueError:
|
|
||||||
return float('nan')
|
|
||||||
elif func == unicode:
|
|
||||||
return value.decode('utf-8')
|
|
||||||
elif func == str:
|
|
||||||
return value.encode('utf-8')
|
|
||||||
return func(value)
|
|
||||||
|
|
||||||
|
|
||||||
class AttributeDict(dict):
|
class AttributeDict(dict):
|
||||||
"""
|
"""
|
||||||
Turns an etree xml response's xml.attrib into an object with attributes
|
Turns an etree xml response's xml.attrib into an object with attributes
|
||||||
|
|
|
@ -1,224 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
:module: plexkodiconnect.userselect
|
|
||||||
:synopsis: This module shows a dialog to let one choose between different Plex
|
|
||||||
(home) users
|
|
||||||
"""
|
|
||||||
from __future__ import absolute_import, division, unicode_literals
|
|
||||||
from logging import getLogger
|
|
||||||
import xbmc
|
|
||||||
import xbmcgui
|
|
||||||
|
|
||||||
from . import kodigui
|
|
||||||
from .connection import plexapp
|
|
||||||
from .. import backgroundthread, utils, plex_functions as PF, variables as v
|
|
||||||
|
|
||||||
LOG = getLogger('PLEX.' + __name__)
|
|
||||||
|
|
||||||
|
|
||||||
class UserThumbTask(backgroundthread.Task):
|
|
||||||
def setup(self, users, callback):
|
|
||||||
self.users = users
|
|
||||||
self.callback = callback
|
|
||||||
return self
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
for user in self.users:
|
|
||||||
if self.isCanceled():
|
|
||||||
return
|
|
||||||
thumb, back = user.thumb, ''
|
|
||||||
self.callback(user, thumb, back)
|
|
||||||
|
|
||||||
|
|
||||||
class ServerListItem(kodigui.ManagedListItem):
|
|
||||||
def init(self):
|
|
||||||
self.dataSource.on('completed:reachability', self.onUpdate)
|
|
||||||
self.dataSource.on('started:reachability', self.onUpdate)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def safeSetProperty(self, key, value):
|
|
||||||
# For if we catch the item in the middle of being removed
|
|
||||||
try:
|
|
||||||
self.setProperty(key, value)
|
|
||||||
return True
|
|
||||||
except AttributeError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def safeSetLabel(self, value):
|
|
||||||
# For if we catch the item in the middle of being removed
|
|
||||||
try:
|
|
||||||
self.setLabel(value)
|
|
||||||
return True
|
|
||||||
except AttributeError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def onUpdate(self, **kwargs):
|
|
||||||
if not self.listItem: # ex. can happen on Kodi shutdown
|
|
||||||
return
|
|
||||||
|
|
||||||
if not self.dataSource.isSupported or not self.dataSource.isReachable():
|
|
||||||
if self.dataSource.pendingReachabilityRequests > 0:
|
|
||||||
self.safeSetProperty('status', 'refreshing.gif')
|
|
||||||
else:
|
|
||||||
self.safeSetProperty('status', 'unreachable.png')
|
|
||||||
else:
|
|
||||||
self.safeSetProperty('status', self.dataSource.isSecure and 'secure.png' or '')
|
|
||||||
|
|
||||||
self.safeSetProperty('current', plexapp.SERVERMANAGER.selectedServer == self.dataSource and '1' or '')
|
|
||||||
self.safeSetLabel(self.dataSource.name)
|
|
||||||
|
|
||||||
def onDestroy(self):
|
|
||||||
self.dataSource.off('completed:reachability', self.onUpdate)
|
|
||||||
self.dataSource.off('started:reachability', self.onUpdate)
|
|
||||||
|
|
||||||
|
|
||||||
class ServerSelectWindow(kodigui.BaseWindow):
|
|
||||||
xmlFile = 'script-plex-server_select.xml'
|
|
||||||
path = v.ADDON_PATH
|
|
||||||
theme = 'Main'
|
|
||||||
res = '1080i'
|
|
||||||
width = 1920
|
|
||||||
height = 1080
|
|
||||||
|
|
||||||
USER_LIST_ID = 101
|
|
||||||
PIN_ENTRY_GROUP_ID = 400
|
|
||||||
HOME_BUTTON_ID = 500
|
|
||||||
SERVER_LIST_ID = 260
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.tasks = None
|
|
||||||
self.server = None
|
|
||||||
self.aborted = False
|
|
||||||
self.serverList = None
|
|
||||||
kodigui.BaseWindow.__init__(self, *args, **kwargs)
|
|
||||||
|
|
||||||
def onFirstInit(self):
|
|
||||||
self.serverList = kodigui.ManagedControlList(self,
|
|
||||||
self.SERVER_LIST_ID,
|
|
||||||
10)
|
|
||||||
self.start()
|
|
||||||
|
|
||||||
def onAction(self, action):
|
|
||||||
try:
|
|
||||||
ID = action.getId()
|
|
||||||
if 57 < ID < 68:
|
|
||||||
if not xbmc.getCondVisibility('ControlGroup({0}).HasFocus(0)'.format(self.PIN_ENTRY_GROUP_ID)):
|
|
||||||
item = self.userList.getSelectedItem()
|
|
||||||
if not item.dataSource.isProtected:
|
|
||||||
return
|
|
||||||
self.setFocusId(self.PIN_ENTRY_GROUP_ID)
|
|
||||||
self.pinEntryClicked(ID + 142)
|
|
||||||
return
|
|
||||||
elif 142 <= ID <= 149: # JumpSMS action
|
|
||||||
if not xbmc.getCondVisibility('ControlGroup({0}).HasFocus(0)'.format(self.PIN_ENTRY_GROUP_ID)):
|
|
||||||
item = self.userList.getSelectedItem()
|
|
||||||
if not item.dataSource.isProtected:
|
|
||||||
return
|
|
||||||
self.setFocusId(self.PIN_ENTRY_GROUP_ID)
|
|
||||||
self.pinEntryClicked(ID + 60)
|
|
||||||
return
|
|
||||||
elif ID in (xbmcgui.ACTION_NAV_BACK, xbmcgui.ACTION_BACKSPACE):
|
|
||||||
if xbmc.getCondVisibility('ControlGroup({0}).HasFocus(0)'.format(self.PIN_ENTRY_GROUP_ID)):
|
|
||||||
self.pinEntryClicked(211)
|
|
||||||
return
|
|
||||||
except:
|
|
||||||
utils.ERROR()
|
|
||||||
|
|
||||||
kodigui.BaseWindow.onAction(self, action)
|
|
||||||
|
|
||||||
def onClick(self, controlID):
|
|
||||||
if controlID == self.USER_LIST_ID:
|
|
||||||
item = self.userList.getSelectedItem()
|
|
||||||
if item.dataSource.isProtected:
|
|
||||||
self.setFocusId(self.PIN_ENTRY_GROUP_ID)
|
|
||||||
else:
|
|
||||||
self.userSelected(item)
|
|
||||||
elif 200 < controlID < 212:
|
|
||||||
self.pinEntryClicked(controlID)
|
|
||||||
elif controlID == self.HOME_BUTTON_ID:
|
|
||||||
self.home_button_clicked()
|
|
||||||
|
|
||||||
def onFocus(self, controlID):
|
|
||||||
if controlID == self.USER_LIST_ID:
|
|
||||||
item = self.userList.getSelectedItem()
|
|
||||||
item.setProperty('editing.pin', '')
|
|
||||||
|
|
||||||
def showServers(self, from_refresh=False, mouse=False):
|
|
||||||
selection = None
|
|
||||||
if from_refresh:
|
|
||||||
mli = self.serverList.getSelectedItem()
|
|
||||||
if mli:
|
|
||||||
selection = mli.dataSource
|
|
||||||
else:
|
|
||||||
plexapp.refreshResources()
|
|
||||||
|
|
||||||
servers = sorted(
|
|
||||||
plexapp.SERVERMANAGER.getServers(),
|
|
||||||
key=lambda x: (x.owned and '0' or '1') + x.name.lower()
|
|
||||||
)
|
|
||||||
servers = PF.plex_gdm()
|
|
||||||
|
|
||||||
items = []
|
|
||||||
for s in servers:
|
|
||||||
item = ServerListItem(s.name,
|
|
||||||
not s.owned and s.owner or '',
|
|
||||||
data_source=s).init()
|
|
||||||
item.onUpdate()
|
|
||||||
item.setProperty(
|
|
||||||
'current',
|
|
||||||
plexapp.SERVERMANAGER.selectedServer == s and '1' or '')
|
|
||||||
items.append(item)
|
|
||||||
|
|
||||||
if len(items) > 1:
|
|
||||||
items[0].setProperty('first', '1')
|
|
||||||
items[-1].setProperty('last', '1')
|
|
||||||
elif items:
|
|
||||||
items[0].setProperty('only', '1')
|
|
||||||
|
|
||||||
self.serverList.replaceItems(items)
|
|
||||||
|
|
||||||
self.getControl(800).setHeight((min(len(items), 9) * 100) + 80)
|
|
||||||
|
|
||||||
if selection:
|
|
||||||
for mli in self.serverList:
|
|
||||||
if mli.dataSource == selection:
|
|
||||||
self.serverList.selectItem(mli.pos())
|
|
||||||
if not from_refresh and items and not mouse:
|
|
||||||
self.setFocusId(self.SERVER_LIST_ID)
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self.setProperty('busy', '1')
|
|
||||||
self.showServers()
|
|
||||||
self.setProperty('initialized', '1')
|
|
||||||
self.setProperty('busy', '')
|
|
||||||
|
|
||||||
def home_button_clicked(self):
|
|
||||||
"""
|
|
||||||
Action taken if user clicked the home button
|
|
||||||
"""
|
|
||||||
self.server = None
|
|
||||||
self.aborted = True
|
|
||||||
self.doClose()
|
|
||||||
|
|
||||||
def finished(self):
|
|
||||||
for task in self.tasks:
|
|
||||||
task.cancel()
|
|
||||||
|
|
||||||
|
|
||||||
def start():
|
|
||||||
"""
|
|
||||||
Hit this function to open a dialog to choose the Plex user
|
|
||||||
|
|
||||||
Returns
|
|
||||||
=======
|
|
||||||
tuple (server, aborted)
|
|
||||||
server : PlexServer
|
|
||||||
Or None if server switch failed or aborted by the server
|
|
||||||
aborted : bool
|
|
||||||
True if the server cancelled the dialog
|
|
||||||
"""
|
|
||||||
w = ServerSelectWindow.open()
|
|
||||||
server, aborted = w.server, w.aborted
|
|
||||||
del w
|
|
||||||
return server, aborted
|
|
|
@ -1,156 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<window>
|
|
||||||
<defaultcontrol>100</defaultcontrol>
|
|
||||||
<coordinates>
|
|
||||||
<system>1</system>
|
|
||||||
<posx>0</posx>
|
|
||||||
<posy>0</posy>
|
|
||||||
</coordinates>
|
|
||||||
<backgroundcolor>0xff111111</backgroundcolor>
|
|
||||||
<controls>
|
|
||||||
<!-- Background -->
|
|
||||||
<control type="group">
|
|
||||||
<control type="image">
|
|
||||||
<posx>0</posx>
|
|
||||||
<posy>0</posy>
|
|
||||||
<width>1920</width>
|
|
||||||
<height>1080</height>
|
|
||||||
<texture colordiffuse="80FFFFFF">script.plex/home/background-fallback.png</texture>
|
|
||||||
</control>
|
|
||||||
<control type="image">
|
|
||||||
<posx>0</posx>
|
|
||||||
<posy>0</posy>
|
|
||||||
<width>1920</width>
|
|
||||||
<height>1080</height>
|
|
||||||
<fadetime>1000</fadetime>
|
|
||||||
<texture background="true">$INFO[Window.Property(background)]</texture>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<!-- Top bar -->
|
|
||||||
<control type="group">
|
|
||||||
<defaultcontrol always="true">201</defaultcontrol>
|
|
||||||
<posx>0</posx>
|
|
||||||
<posy>0</posy>
|
|
||||||
<width>1920</width>
|
|
||||||
<height>135</height>
|
|
||||||
<control type="image">
|
|
||||||
<posx>0</posx>
|
|
||||||
<posy>0</posy>
|
|
||||||
<width>1920</width>
|
|
||||||
<height>135</height>
|
|
||||||
<texture>plugin.video.plexkodiconnect/white-square.png</texture>
|
|
||||||
<colordiffuse>19000000</colordiffuse>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="grouplist">
|
|
||||||
<posx>20</posx>
|
|
||||||
<posy>-5.5</posy>
|
|
||||||
<width>1040</width>
|
|
||||||
<height>170</height>
|
|
||||||
<align>left</align>
|
|
||||||
<itemgap>60</itemgap>
|
|
||||||
<orientation>horizontal</orientation>
|
|
||||||
<ondown>101</ondown>
|
|
||||||
<usecontrolcoords>true</usecontrolcoords>
|
|
||||||
<control type="group">
|
|
||||||
<posx>40</posx>
|
|
||||||
<posy>34.5</posy>
|
|
||||||
<width>124</width>
|
|
||||||
<height>66</height>
|
|
||||||
<control type="button" id="500">
|
|
||||||
<posx>0</posx>
|
|
||||||
<posy>0</posy>
|
|
||||||
<width>124</width>
|
|
||||||
<height>66</height>
|
|
||||||
<ondown>101</ondown>
|
|
||||||
<onright>101</onright>
|
|
||||||
<align>right</align>
|
|
||||||
<aligny>center</aligny>
|
|
||||||
<texturefocus>-</texturefocus>
|
|
||||||
<texturenofocus>-</texturenofocus>
|
|
||||||
<label> </label>
|
|
||||||
</control>
|
|
||||||
<control type="image">
|
|
||||||
<visible>!String.IsEmpty(Window.Property(dropdown))</visible>
|
|
||||||
<posx>0</posx>
|
|
||||||
<posy>0</posy>
|
|
||||||
<width>124</width>
|
|
||||||
<height>66</height>
|
|
||||||
<texture colordiffuse="FFCC7B19" border="10">plugin.video.plexkodiconnect/white-square-rounded.png</texture>
|
|
||||||
</control>
|
|
||||||
<control type="group">
|
|
||||||
<posx>27</posx>
|
|
||||||
<posy>13</posy>
|
|
||||||
<control type="image">
|
|
||||||
<visible>!Control.HasFocus(500) + String.IsEmpty(Window.Property(dropdown))</visible>
|
|
||||||
<posx>0</posx>
|
|
||||||
<posy>0</posy>
|
|
||||||
<width>90</width>
|
|
||||||
<height>90</height>
|
|
||||||
<texture>plugin.video.plexkodiconnect/home/type/home.png</texture>
|
|
||||||
</control>
|
|
||||||
<control type="image">
|
|
||||||
<visible>Control.HasFocus(500) | !String.IsEmpty(Window.Property(dropdown))</visible>
|
|
||||||
<posx>-40</posx>
|
|
||||||
<posy>-40</posy>
|
|
||||||
<width>170</width>
|
|
||||||
<height>170</height>
|
|
||||||
<texture>plugin.video.plexkodiconnect/home/type/home-selected.png</texture>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
<control type="label">
|
|
||||||
<posx>13</posx>
|
|
||||||
<posy>50</posy>
|
|
||||||
<width max="500">auto</width>
|
|
||||||
<height>66</height>
|
|
||||||
<font>font12</font>
|
|
||||||
<align>left</align>
|
|
||||||
<aligny>center</aligny>
|
|
||||||
<textcolor>FFFFFFFF</textcolor>
|
|
||||||
<label>[UPPERCASE]$ADDON[plugin.video.plexkodiconnect 33000][/UPPERCASE]</label>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="label">
|
|
||||||
<right>213</right>
|
|
||||||
<posy>35</posy>
|
|
||||||
<width>200</width>
|
|
||||||
<height>65</height>
|
|
||||||
<font>font12</font>
|
|
||||||
<align>right</align>
|
|
||||||
<aligny>center</aligny>
|
|
||||||
<textcolor>FFFFFFFF</textcolor>
|
|
||||||
<label>$INFO[System.Time]</label>
|
|
||||||
</control>
|
|
||||||
<control type="image">
|
|
||||||
<posx>153r</posx>
|
|
||||||
<posy>54</posy>
|
|
||||||
<width>93</width>
|
|
||||||
<height>30</height>
|
|
||||||
<texture>plugin.video.plexkodiconnect/home/plex.png</texture>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
|
|
||||||
<control type="group">
|
|
||||||
<visible>!String.IsEmpty(Window.Property(busy))</visible>
|
|
||||||
<control type="image">
|
|
||||||
<posx>840</posx>
|
|
||||||
<posy>465</posy>
|
|
||||||
<width>240</width>
|
|
||||||
<height>150</height>
|
|
||||||
<texture>script.plex/busy-back.png</texture>
|
|
||||||
<colordiffuse>A0FFFFFF</colordiffuse>
|
|
||||||
</control>
|
|
||||||
<control type="image">
|
|
||||||
<posx>915</posx>
|
|
||||||
<posy>521</posy>
|
|
||||||
<width>90</width>
|
|
||||||
<height>38</height>
|
|
||||||
<texture diffuse="script.plex/busy-diffuse.png">script.plex/busy.gif</texture>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
</controls>
|
|
||||||
</window>
|
|
Loading…
Reference in a new issue