Code refactoring

This commit is contained in:
croneter 2018-02-10 17:59:20 +01:00
parent 4fca4ecf63
commit 1151076660
8 changed files with 1086 additions and 1404 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,21 +1,31 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from logging import getLogger from logging import getLogger
from urllib import urlencode from urllib import urlencode, quote_plus
from ast import literal_eval from ast import literal_eval
from urlparse import urlparse, parse_qsl from urlparse import urlparse, parse_qsl
import re from re import compile as re_compile
from copy import deepcopy from copy import deepcopy
from time import time
from threading import Thread
import downloadutils from xbmc import sleep
from utils import settings
from downloadutils import DownloadUtils as DU
from utils import settings, tryEncode, tryDecode
from variables import PLEX_TO_KODI_TIMEFACTOR from variables import PLEX_TO_KODI_TIMEFACTOR
import plex_tv
############################################################################### ###############################################################################
LOG = getLogger("PLEX." + __name__)
log = getLogger("PLEX."+__name__)
CONTAINERSIZE = int(settings('limitindex')) CONTAINERSIZE = int(settings('limitindex'))
REGEX_PLEX_KEY = re.compile(r'''/(.+)/(\d+)$''') REGEX_PLEX_KEY = re_compile(r'''/(.+)/(\d+)$''')
# For discovery of PMS in the local LAN
PLEX_GDM_IP = '239.0.0.250' # multicast to PMS
PLEX_GDM_PORT = 32414
PLEX_GDM_MSG = 'M-SEARCH * HTTP/1.0'
############################################################################### ###############################################################################
@ -76,6 +86,374 @@ def GetMethodFromPlexType(plexType):
return methods[plexType] return methods[plexType]
def GetPlexLoginFromSettings():
"""
Returns a dict:
'plexLogin': settings('plexLogin'),
'plexToken': settings('plexToken'),
'plexhome': settings('plexhome'),
'plexid': settings('plexid'),
'myplexlogin': settings('myplexlogin'),
'plexAvatar': settings('plexAvatar'),
'plexHomeSize': settings('plexHomeSize')
Returns strings or unicode
Returns empty strings '' for a setting if not found.
myplexlogin is 'true' if user opted to log into plex.tv (the default)
plexhome is 'true' if plex home is used (the default)
"""
return {
'plexLogin': settings('plexLogin'),
'plexToken': settings('plexToken'),
'plexhome': settings('plexhome'),
'plexid': settings('plexid'),
'myplexlogin': settings('myplexlogin'),
'plexAvatar': settings('plexAvatar'),
'plexHomeSize': settings('plexHomeSize')
}
def check_connection(url, token=None, verifySSL=None):
"""
Checks connection to a Plex server, available at url. Can also be used
to check for connection with plex.tv.
Override SSL to skip the check by setting verifySSL=False
if 'None', SSL will be checked (standard requests setting)
if 'True', SSL settings from file settings are used (False/True)
Input:
url URL to Plex server (e.g. https://192.168.1.1:32400)
token appropriate token to access server. If None is passed,
the current token is used
Output:
False if server could not be reached or timeout occured
200 if connection was successfull
int or other HTML status codes as received from the server
"""
# Add '/clients' to URL because then an authentication is necessary
# If a plex.tv URL was passed, this does not work.
header_options = None
if token is not None:
header_options = {'X-Plex-Token': token}
if verifySSL is True:
verifySSL = None if settings('sslverify') == 'true' else False
if 'plex.tv' in url:
url = 'https://plex.tv/api/home/users'
else:
url = url + '/library/onDeck'
LOG.debug("Checking connection to server %s with verifySSL=%s",
url, verifySSL)
answer = DU().downloadUrl(url,
authenticate=False,
headerOptions=header_options,
verifySSL=verifySSL,
timeout=10)
if answer is None:
LOG.debug("Could not connect to %s", url)
return False
try:
# xml received?
answer.attrib
except AttributeError:
if answer is True:
# Maybe no xml but connection was successful nevertheless
answer = 200
else:
# Success - we downloaded an xml!
answer = 200
# We could connect but maybe were not authenticated. No worries
LOG.debug("Checking connection successfull. Answer: %s", answer)
return answer
def discover_pms(token=None):
"""
Optional parameter:
token token for plex.tv
Returns a list of available PMS to connect to, one entry is the dict:
{
'machineIdentifier' [str] unique identifier of the PMS
'name' [str] name of the PMS
'token' [str] token needed to access that PMS
'ownername' [str] name of the owner of this PMS or None if
the owner itself supplied tries to connect
'product' e.g. 'Plex Media Server' or None
'version' e.g. '1.11.2.4772-3e...' or None
'device': e.g. 'PC' or 'Windows' or None
'platform': e.g. 'Windows', 'Android' or None
'local' [bool] True if plex.tv supplied
'publicAddressMatches'='1'
or if found using Plex GDM in the local LAN
'owned' [bool] True if it's the owner's PMS
'relay' [bool] True if plex.tv supplied 'relay'='1'
'presence' [bool] True if plex.tv supplied 'presence'='1'
'httpsRequired' [bool] True if plex.tv supplied
'httpsRequired'='1'
'scheme' [str] either 'http' or 'https'
'ip': [str] IP of the PMS, e.g. '192.168.1.1'
'port': [str] Port of the PMS, e.g. '32400'
'baseURL': [str] <scheme>://<ip>:<port> of the PMS
}
"""
LOG.info('Start discovery of Plex Media Servers')
# Look first for local PMS in the LAN
local_pms_list = _plex_gdm()
LOG.debug('PMS found in the local LAN using Plex GDM: %s', local_pms_list)
# Get PMS from plex.tv
if token:
LOG.info('Checking with plex.tv for more PMS to connect to')
plex_pms_list = _pms_list_from_plex_tv(token)
LOG.debug('PMS found on plex.tv: %s', plex_pms_list)
else:
LOG.info('No plex token supplied, only checked LAN for available PMS')
plex_pms_list = []
# See if we found a PMS both locally and using plex.tv. If so, use local
# connection data
all_pms = []
for pms in local_pms_list:
for i, plex_pms in enumerate(plex_pms_list):
if pms['machineIdentifier'] == plex_pms['machineIdentifier']:
# Update with GDM data - potentially more reliable than plex.tv
LOG.debug('Found this PMS also in the LAN: %s', plex_pms)
plex_pms['ip'] = pms['ip']
plex_pms['port'] = pms['port']
plex_pms['local'] = True
# Use all the other data we know from plex.tv
pms = plex_pms
# Remove this particular pms since we already know it
plex_pms_list.pop(i)
break
https = _pms_https_enabled('%s:%s' % (pms['ip'], pms['port']))
if https is None:
# Error contacting url. Skip and ignore this PMS for now
continue
elif https is True:
pms['scheme'] = 'https'
pms['baseURL'] = 'https://%s:%s' % (pms['ip'], pms['port'])
else:
pms['scheme'] = 'http'
pms['baseURL'] = 'http://%s:%s' % (pms['ip'], pms['port'])
all_pms.append(pms)
# Now add the remaining PMS from plex.tv (where we already checked connect.)
for plex_pms in plex_pms_list:
all_pms.append(plex_pms)
LOG.debug('Found the following PMS in total: %s', all_pms)
return all_pms
def _plex_gdm():
"""
PlexGDM - looks for PMS in the local LAN and returns a list of the PMS found
"""
# Import here because we might not need to do gdm because we already
# connected to a PMS successfully in the past
import struct
import socket
# setup socket for discovery -> multicast message
gdm = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
gdm.settimeout(2.0)
# Set the time-to-live for messages to 2 for local network
ttl = struct.pack('b', 2)
gdm.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
return_data = []
try:
# Send data to the multicast group
gdm.sendto(PLEX_GDM_MSG, (PLEX_GDM_IP, PLEX_GDM_PORT))
# Look for responses from all recipients
while True:
try:
data, server = gdm.recvfrom(1024)
return_data.append({'from': server, 'data': data})
except socket.timeout:
break
except Exception as e:
# Probably error: (101, 'Network is unreachable')
LOG.error(e)
import traceback
LOG.error("Traceback:\n%s", traceback.format_exc())
finally:
gdm.close()
LOG.debug('Plex GDM returned the data: %s', return_data)
pms_list = []
for response in return_data:
# Check if we had a positive HTTP response
if '200 OK' not in response['data']:
continue
pms = {
'ip': response['from'][0],
'scheme': None,
'local': True, # Since we found it using GDM
'product': None,
'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'):
if 'Content-Type:' in line:
pms['product'] = tryDecode(line.split(':')[1].strip())
elif 'Host:' in line:
pms['baseURL'] = line.split(':')[1].strip()
elif 'Name:' in line:
pms['name'] = tryDecode(line.split(':')[1].strip())
elif 'Port:' in line:
pms['port'] = line.split(':')[1].strip()
elif 'Resource-Identifier:' in line:
pms['machineIdentifier'] = line.split(':')[1].strip()
elif 'Version:' in line:
pms['version'] = line.split(':')[1].strip()
pms_list.append(pms)
return pms_list
def _pms_list_from_plex_tv(token):
"""
get Plex media Server List from plex.tv/pms/resources
"""
xml = DU().downloadUrl('https://plex.tv/api/resources',
authenticate=False,
parameters={'includeHttps': 1},
headerOptions={'X-Plex-Token': token})
try:
xml.attrib
except AttributeError:
LOG.error('Could not get list of PMS from plex.tv')
return
from Queue import Queue
queue = Queue()
thread_queue = []
max_age_in_seconds = 2*60*60*24
for device in xml.findall('Device'):
if 'server' not in device.get('provides'):
# No PMS - skip
continue
if device.find('Connection') is None:
# no valid connection - skip
continue
# check MyPlex data age - skip if >2 days
info_age = time() - int(device.get('lastSeenAt'))
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
thread = Thread(target=_poke_pms, args=(pms, queue))
thread_queue.append(thread)
max_threads = 5
threads = []
# poke PMS, own thread for each PMS
while True:
# Remove finished threads
for thread in threads:
if not thread.isAlive():
threads.remove(thread)
if len(threads) < max_threads:
try:
thread = thread_queue.pop()
except IndexError:
# We have done our work
break
else:
thread.start()
threads.append(thread)
else:
sleep(50)
# wait for requests being answered
for thread in threads:
thread.join()
# declare new PMSs
pms_list = []
while not queue.empty():
pms = queue.get()
del pms['connections']
pms_list.append(pms)
queue.task_done()
return pms_list
def _poke_pms(pms, queue):
data = pms['connections'][0].attrib
if data['local'] == '1':
protocol = data['protocol']
address = data['address']
port = data['port']
url = '%s://%s:%s' % (protocol, address, port)
else:
url = data['uri']
if url.count(':') == 1:
url = '%s:%s' % (url, data['port'])
protocol, address, port = url.split(':', 2)
address = address.replace('/', '')
xml = DU().downloadUrl('%s/identity' % url,
authenticate=False,
headerOptions={'X-Plex-Token': pms['token']},
verifySSL=False,
timeout=10)
try:
xml.attrib['machineIdentifier']
except (AttributeError, KeyError):
# No connection, delete the one we just tested
del pms['connections'][0]
if pms['connections']:
# Still got connections left, try them
return _poke_pms(pms, queue)
return
else:
# Connection successful - correct pms?
if xml.get('machineIdentifier') == pms['machineIdentifier']:
# process later
pms['baseURL'] = url
pms['protocol'] = protocol
pms['ip'] = address
pms['port'] = port
queue.put(pms)
return
LOG.info('Found a pms at %s, but the expected machineIdentifier of '
'%s did not match the one we found: %s',
url, pms['uuid'], xml.get('machineIdentifier'))
def GetPlexMetadata(key): def GetPlexMetadata(key):
""" """
Returns raw API metadata for key as an etree XML. Returns raw API metadata for key as an etree XML.
@ -102,7 +480,7 @@ def GetPlexMetadata(key):
# 'includeConcerts': 1 # 'includeConcerts': 1
} }
url = url + '?' + urlencode(arguments) url = url + '?' + urlencode(arguments)
xml = downloadutils.DownloadUtils().downloadUrl(url) xml = DU().downloadUrl(url)
if xml == 401: if xml == 401:
# Either unauthorized (taken care of by doUtils) or PMS under strain # Either unauthorized (taken care of by doUtils) or PMS under strain
return 401 return 401
@ -111,7 +489,7 @@ def GetPlexMetadata(key):
xml.attrib xml.attrib
# Nope we did not receive a valid XML # Nope we did not receive a valid XML
except AttributeError: except AttributeError:
log.error("Error retrieving metadata for %s" % url) LOG.error("Error retrieving metadata for %s", url)
xml = None xml = None
return xml return xml
@ -153,22 +531,21 @@ def DownloadChunks(url):
""" """
xml = None xml = None
pos = 0 pos = 0
errorCounter = 0 error_counter = 0
while errorCounter < 10: while error_counter < 10:
args = { args = {
'X-Plex-Container-Size': CONTAINERSIZE, 'X-Plex-Container-Size': CONTAINERSIZE,
'X-Plex-Container-Start': pos 'X-Plex-Container-Start': pos
} }
xmlpart = downloadutils.DownloadUtils().downloadUrl( xmlpart = DU().downloadUrl(url + urlencode(args))
url + urlencode(args))
# If something went wrong - skip in the hope that it works next time # If something went wrong - skip in the hope that it works next time
try: try:
xmlpart.attrib xmlpart.attrib
except AttributeError: except AttributeError:
log.error('Error while downloading chunks: %s' LOG.error('Error while downloading chunks: %s',
% (url + urlencode(args))) url + urlencode(args))
pos += CONTAINERSIZE pos += CONTAINERSIZE
errorCounter += 1 error_counter += 1
continue continue
# Very first run: starting xml (to retain data in xml's root!) # Very first run: starting xml (to retain data in xml's root!)
@ -186,8 +563,8 @@ def DownloadChunks(url):
if len(xmlpart) < CONTAINERSIZE: if len(xmlpart) < CONTAINERSIZE:
break break
pos += CONTAINERSIZE pos += CONTAINERSIZE
if errorCounter == 10: if error_counter == 10:
log.error('Fatal error while downloading chunks for %s' % url) LOG.error('Fatal error while downloading chunks for %s', url)
return None return None
return xml return xml
@ -235,8 +612,7 @@ def get_plex_sections():
""" """
Returns all Plex sections (libraries) of the PMS as an etree xml Returns all Plex sections (libraries) of the PMS as an etree xml
""" """
return downloadutils.DownloadUtils().downloadUrl( return DU().downloadUrl('{server}/library/sections')
'{server}/library/sections')
def init_plex_playqueue(itemid, librarySectionUUID, mediatype='movie', def init_plex_playqueue(itemid, librarySectionUUID, mediatype='movie',
@ -255,17 +631,16 @@ def init_plex_playqueue(itemid, librarySectionUUID, mediatype='movie',
} }
if trailers is True: if trailers is True:
args['extrasPrefixCount'] = settings('trailerNumber') args['extrasPrefixCount'] = settings('trailerNumber')
xml = downloadutils.DownloadUtils().downloadUrl( xml = DU().downloadUrl(url + '?' + urlencode(args), action_type="POST")
url + '?' + urlencode(args), action_type="POST")
try: try:
xml[0].tag xml[0].tag
except (IndexError, TypeError, AttributeError): except (IndexError, TypeError, AttributeError):
log.error("Error retrieving metadata for %s" % url) LOG.error("Error retrieving metadata for %s", url)
return None return None
return xml return xml
def PMSHttpsEnabled(url): def _pms_https_enabled(url):
""" """
Returns True if the PMS can talk https, False otherwise. Returns True if the PMS can talk https, False otherwise.
None if error occured, e.g. the connection timed out None if error occured, e.g. the connection timed out
@ -277,21 +652,20 @@ def PMSHttpsEnabled(url):
Prefers HTTPS over HTTP Prefers HTTPS over HTTP
""" """
doUtils = downloadutils.DownloadUtils().downloadUrl res = DU().downloadUrl('https://%s/identity' % url,
res = doUtils('https://%s/identity' % url,
authenticate=False, authenticate=False,
verifySSL=False) verifySSL=False)
try: try:
res.attrib res.attrib
except AttributeError: except AttributeError:
# Might have SSL deactivated. Try with http # Might have SSL deactivated. Try with http
res = doUtils('http://%s/identity' % url, res = DU().downloadUrl('http://%s/identity' % url,
authenticate=False, authenticate=False,
verifySSL=False) verifySSL=False)
try: try:
res.attrib res.attrib
except AttributeError: except AttributeError:
log.error("Could not contact PMS %s" % url) LOG.error("Could not contact PMS %s", url)
return None return None
else: else:
# Received a valid XML. Server wants to talk HTTP # Received a valid XML. Server wants to talk HTTP
@ -307,17 +681,17 @@ def GetMachineIdentifier(url):
Returns None if something went wrong Returns None if something went wrong
""" """
xml = downloadutils.DownloadUtils().downloadUrl('%s/identity' % url, xml = DU().downloadUrl('%s/identity' % url,
authenticate=False, authenticate=False,
verifySSL=False, verifySSL=False,
timeout=10) timeout=10)
try: try:
machineIdentifier = xml.attrib['machineIdentifier'] machineIdentifier = xml.attrib['machineIdentifier']
except (AttributeError, KeyError): except (AttributeError, KeyError):
log.error('Could not get the PMS machineIdentifier for %s' % url) LOG.error('Could not get the PMS machineIdentifier for %s', url)
return None return None
log.debug('Found machineIdentifier %s for the PMS %s' LOG.debug('Found machineIdentifier %s for the PMS %s',
% (machineIdentifier, url)) machineIdentifier, url)
return machineIdentifier return machineIdentifier
@ -337,8 +711,7 @@ def GetPMSStatus(token):
or an empty dict. or an empty dict.
""" """
answer = {} answer = {}
xml = downloadutils.DownloadUtils().downloadUrl( xml = DU().downloadUrl('{server}/status/sessions',
'{server}/status/sessions',
headerOptions={'X-Plex-Token': token}) headerOptions={'X-Plex-Token': token})
try: try:
xml.attrib xml.attrib
@ -377,8 +750,8 @@ def scrobble(ratingKey, state):
url = "{server}/:/unscrobble?" + urlencode(args) url = "{server}/:/unscrobble?" + urlencode(args)
else: else:
return return
downloadutils.DownloadUtils().downloadUrl(url) DU().downloadUrl(url)
log.info("Toggled watched state for Plex item %s" % ratingKey) LOG.info("Toggled watched state for Plex item %s", ratingKey)
def delete_item_from_pms(plexid): def delete_item_from_pms(plexid):
@ -388,24 +761,76 @@ def delete_item_from_pms(plexid):
Returns True if successful, False otherwise Returns True if successful, False otherwise
""" """
if downloadutils.DownloadUtils().downloadUrl( if DU().downloadUrl('{server}/library/metadata/%s' % plexid,
'{server}/library/metadata/%s' % plexid,
action_type="DELETE") is True: action_type="DELETE") is True:
log.info('Successfully deleted Plex id %s from the PMS' % plexid) LOG.info('Successfully deleted Plex id %s from the PMS', plexid)
return True return True
else: LOG.error('Could not delete Plex id %s from the PMS', plexid)
log.error('Could not delete Plex id %s from the PMS' % plexid)
return False return False
def get_PMS_settings(url, token): def get_PMS_settings(url, token):
""" """
Retrieve the PMS' settings via <url>/:/ Retrieve the PMS' settings via <url>/:/prefs
Call with url: scheme://ip:port Call with url: scheme://ip:port
""" """
return downloadutils.DownloadUtils().downloadUrl( return DU().downloadUrl(
'%s/:/prefs' % url, '%s/:/prefs' % url,
authenticate=False, authenticate=False,
verifySSL=False, verifySSL=False,
headerOptions={'X-Plex-Token': token} if token else None) headerOptions={'X-Plex-Token': token} if token else None)
def GetUserArtworkURL(username):
"""
Returns the URL for the user's Avatar. Or False if something went
wrong.
"""
users = plex_tv.list_home_users(settings('plexToken'))
url = ''
# If an error is encountered, set to False
if not users:
LOG.info("Couldnt get user from plex.tv. No URL for user avatar")
return False
for user in users:
if username in user['title']:
url = user['thumb']
LOG.debug("Avatar url for user %s is: %s", username, url)
return url
def transcode_image_path(key, AuthToken, path, width, height):
"""
Transcode Image support
parameters:
key
AuthToken
path - source path of current XML: path[srcXML]
width
height
result:
final path to image file
"""
# external address - can we get a transcoding request for external images?
if key.startswith('http://') or key.startswith('https://'):
path = key
elif key.startswith('/'): # internal full path.
path = 'http://127.0.0.1:32400' + key
else: # internal path, add-on
path = 'http://127.0.0.1:32400' + path + '/' + key
path = tryEncode(path)
# This is bogus (note the extra path component) but ATV is stupid when it
# comes to caching images, it doesn't use querystrings. Fortunately PMS is
# lenient...
transcode_path = ('/photo/:/transcode/%sx%s/%s'
% (width, height, quote_plus(path)))
args = {
'width': width,
'height': height,
'url': path
}
if AuthToken:
args['X-Plex-Token'] = AuthToken
return transcode_path + '?' + urlencode(args)

View file

@ -40,7 +40,7 @@ def chooseServer():
import initialsetup import initialsetup
setup = initialsetup.InitialSetup() setup = initialsetup.InitialSetup()
server = setup.PickPMS(showDialog=True) server = setup.pick_pms(showDialog=True)
if server is None: if server is None:
log.error('We did not connect to a new PMS, aborting') log.error('We did not connect to a new PMS, aborting')
plex_command('SUSPEND_USER_CLIENT', 'False') plex_command('SUSPEND_USER_CLIENT', 'False')
@ -48,7 +48,7 @@ def chooseServer():
return return
log.info("User chose server %s" % server['name']) log.info("User chose server %s" % server['name'])
setup.WritePMStoSettings(server) setup.write_pms_to_settings(server)
if not __LogOut(): if not __LogOut():
return return
@ -87,7 +87,7 @@ def togglePlexTV():
else: else:
log.info('Login to plex.tv') log.info('Login to plex.tv')
import initialsetup import initialsetup
initialsetup.InitialSetup().PlexTVSignIn() initialsetup.InitialSetup().plex_tv_sign_in()
dialog('notification', dialog('notification',
lang(29999), lang(29999),
lang(39221), lang(39221),

View file

@ -4,17 +4,16 @@ from logging import getLogger
from Queue import Queue from Queue import Queue
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
import xbmc from xbmc import executebuiltin, translatePath
import xbmcgui
from utils import settings, window, language as lang, tryEncode, tryDecode, \ from utils import settings, window, language as lang, tryEncode, tryDecode, \
XmlKodiSetting, reboot_kodi XmlKodiSetting, reboot_kodi, dialog
from migration import check_migration from migration import check_migration
from downloadutils import DownloadUtils as DU from downloadutils import DownloadUtils as DU
from userclient import UserClient from userclient import UserClient
from clientinfo import getDeviceId from clientinfo import getDeviceId
from PlexAPI import PlexAPI import PlexFunctions as PF
from PlexFunctions import GetMachineIdentifier, get_PMS_settings import plex_tv
from json_rpc import get_setting, set_setting from json_rpc import get_setting, set_setting
import playqueue as PQ import playqueue as PQ
from videonodes import VideoNodes from videonodes import VideoNodes
@ -77,7 +76,7 @@ def reload_pkc():
set_webserver() set_webserver()
# To detect Kodi profile switches # To detect Kodi profile switches
window('plex_kodiProfile', window('plex_kodiProfile',
value=tryDecode(xbmc.translatePath("special://profile"))) value=tryDecode(translatePath("special://profile")))
getDeviceId() getDeviceId()
# Initialize the PKC playqueues # Initialize the PKC playqueues
PQ.init_playqueues() PQ.init_playqueues()
@ -115,188 +114,12 @@ def set_webserver():
state.WEBSERVER_PASSWORD = get_setting('services.webserverpassword') state.WEBSERVER_PASSWORD = get_setting('services.webserverpassword')
class InitialSetup(): def _write_pms_settings(url, token):
def __init__(self):
LOG.debug('Entering initialsetup class')
self.plx = PlexAPI()
self.dialog = xbmcgui.Dialog()
self.server = UserClient().getServer()
self.serverid = settings('plex_machineIdentifier')
# Get Plex credentials from settings file, if they exist
plexdict = self.plx.GetPlexLoginFromSettings()
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.pms_token = settings('accessToken')
if self.plexToken:
LOG.debug('Found a plex.tv token in the settings')
def PlexTVSignIn(self):
"""
Signs (freshly) in to plex.tv (will be saved to file settings)
Returns True if successful, or False if not
"""
result = self.plx.PlexTvSignInWithPin()
if result:
self.plexLogin = result['username']
self.plexToken = result['token']
self.plexid = result['plexid']
return True
return False
def CheckPlexTVSignIn(self):
"""
Checks existing connection to plex.tv. If not, triggers sign in
Returns True if signed in, False otherwise
"""
answer = True
chk = self.plx.CheckConnection('plex.tv', token=self.plexToken)
if chk in (401, 403):
# HTTP Error: unauthorized. Token is no longer valid
LOG.info('plex.tv connection returned HTTP %s', str(chk))
# Delete token in the settings
settings('plexToken', value='')
settings('plexLogin', value='')
# Could not login, please try again
self.dialog.ok(lang(29999), lang(39009))
answer = self.PlexTVSignIn()
elif chk is False or chk >= 400:
# Problems connecting to plex.tv. Network or internet issue?
LOG.info('Problems connecting to plex.tv; connection returned '
'HTTP %s', str(chk))
self.dialog.ok(lang(29999), lang(39010))
answer = False
else:
LOG.info('plex.tv connection with token successful')
settings('plex_status', value=lang(39227))
# Refresh the info from Plex.tv
xml = DU().downloadUrl('https://plex.tv/users/account',
authenticate=False,
headerOptions={'X-Plex-Token': self.plexToken})
try:
self.plexLogin = xml.attrib['title']
except (AttributeError, KeyError):
LOG.error('Failed to update Plex info from plex.tv')
else:
settings('plexLogin', value=self.plexLogin)
home = 'true' if xml.attrib.get('home') == '1' else 'false'
settings('plexhome', value=home)
settings('plexAvatar', value=xml.attrib.get('thumb'))
settings('plexHomeSize', value=xml.attrib.get('homeSize', '1'))
LOG.info('Updated Plex info from plex.tv')
return answer
def CheckPMS(self):
"""
Check the PMS that was set in file settings.
Will return False if we need to reconnect, because:
PMS could not be reached (no matter the authorization)
machineIdentifier did not match
Will also set the PMS machineIdentifier in the file settings if it was
not set before
"""
answer = True
chk = self.plx.CheckConnection(self.server, verifySSL=False)
if chk is False:
LOG.warn('Could not reach PMS %s', self.server)
answer = False
if answer is True and not self.serverid:
LOG.info('No PMS machineIdentifier found for %s. Trying to '
'get the PMS unique ID', self.server)
self.serverid = GetMachineIdentifier(self.server)
if self.serverid is None:
LOG.warn('Could not retrieve machineIdentifier')
answer = False
else:
settings('plex_machineIdentifier', value=self.serverid)
elif answer is True:
tempServerid = GetMachineIdentifier(self.server)
if tempServerid != self.serverid:
LOG.warn('The current PMS %s was expected to have a '
'unique machineIdentifier of %s. But we got '
'%s. Pick a new server to be sure',
self.server, self.serverid, tempServerid)
answer = False
return answer
def _getServerList(self):
"""
Returns a list of servers from GDM and possibly plex.tv
"""
self.plx.discoverPMS(xbmc.getIPAddress(),
plexToken=self.plexToken)
serverlist = self.plx.returnServerList(self.plx.g_PMS)
LOG.debug('PMS serverlist: %s', serverlist)
return serverlist
def _checkServerCon(self, server):
"""
Checks for server's connectivity. Returns CheckConnection result
"""
# Re-direct via plex if remote - will lead to the correct SSL
# certificate
if server['local'] == '1':
url = '%s://%s:%s' \
% (server['scheme'], server['ip'], server['port'])
# Deactive SSL verification if the server is local!
verifySSL = False
else:
url = server['baseURL']
verifySSL = True
chk = self.plx.CheckConnection(url,
token=server['accesstoken'],
verifySSL=verifySSL)
return chk
def PickPMS(self, showDialog=False):
"""
Searches for PMS in local Lan and optionally (if self.plexToken set)
also on plex.tv
showDialog=True: let the user pick one
showDialog=False: automatically pick PMS based on machineIdentifier
Returns the picked PMS' detail as a dict:
{
'name': friendlyName, the Plex server's name
'address': ip:port
'ip': ip, without http/https
'port': port
'scheme': 'http'/'https', nice for checking for secure connections
'local': '1'/'0', Is the server a local server?
'owned': '1'/'0', Is the server owned by the user?
'machineIdentifier': id, Plex server machine identifier
'accesstoken': token Access token to this server
'baseURL': baseURL scheme://ip:port
'ownername' Plex username of PMS owner
}
or None if unsuccessful
"""
server = None
# If no server is set, let user choose one
if not self.server or not self.serverid:
showDialog = True
if showDialog is True:
server = self._UserPickPMS()
else:
server = self._AutoPickPMS()
if server is not None:
self._write_PMS_settings(server['baseURL'], server['accesstoken'])
return server
def _write_PMS_settings(self, url, token):
""" """
Sets certain settings for server by asking for the PMS' settings Sets certain settings for server by asking for the PMS' settings
Call with url: scheme://ip:port Call with url: scheme://ip:port
""" """
xml = get_PMS_settings(url, token) xml = PF.get_PMS_settings(url, token)
try: try:
xml.attrib xml.attrib
except AttributeError: except AttributeError:
@ -309,19 +132,196 @@ class InitialSetup():
window('plex_allows_mediaDeletion', window('plex_allows_mediaDeletion',
value=entry.attrib.get('value', 'true')) value=entry.attrib.get('value', 'true'))
def _AutoPickPMS(self):
class InitialSetup(object):
"""
Will load Plex PMS settings (e.g. address) and token
Will ask the user initial questions on first PKC boot
"""
def __init__(self):
LOG.debug('Entering initialsetup class')
self.server = UserClient().getServer()
self.serverid = settings('plex_machineIdentifier')
# Get Plex credentials from settings file, if they exist
plexdict = PF.GetPlexLoginFromSettings()
self.myplexlogin = plexdict['myplexlogin'] == 'true'
self.plex_login = plexdict['plexLogin']
self.plex_token = plexdict['plexToken']
self.plexid = plexdict['plexid']
# Token for the PMS, not plex.tv
self.pms_token = settings('accessToken')
if self.plex_token:
LOG.debug('Found a plex.tv token in the settings')
def plex_tv_sign_in(self):
"""
Signs (freshly) in to plex.tv (will be saved to file settings)
Returns True if successful, or False if not
"""
result = plex_tv.sign_in_with_pin()
if result:
self.plex_login = result['username']
self.plex_token = result['token']
self.plexid = result['plexid']
return True
return False
def check_plex_tv_sign_in(self):
"""
Checks existing connection to plex.tv. If not, triggers sign in
Returns True if signed in, False otherwise
"""
answer = True
chk = PF.check_connection('plex.tv', token=self.plex_token)
if chk in (401, 403):
# HTTP Error: unauthorized. Token is no longer valid
LOG.info('plex.tv connection returned HTTP %s', str(chk))
# Delete token in the settings
settings('plexToken', value='')
settings('plexLogin', value='')
# Could not login, please try again
dialog('ok', lang(29999), lang(39009))
answer = self.plex_tv_sign_in()
elif chk is False or chk >= 400:
# Problems connecting to plex.tv. Network or internet issue?
LOG.info('Problems connecting to plex.tv; connection returned '
'HTTP %s', str(chk))
dialog('ok', lang(29999), lang(39010))
answer = False
else:
LOG.info('plex.tv connection with token successful')
settings('plex_status', value=lang(39227))
# Refresh the info from Plex.tv
xml = DU().downloadUrl('https://plex.tv/users/account',
authenticate=False,
headerOptions={'X-Plex-Token': self.plex_token})
try:
self.plex_login = xml.attrib['title']
except (AttributeError, KeyError):
LOG.error('Failed to update Plex info from plex.tv')
else:
settings('plexLogin', value=self.plex_login)
home = 'true' if xml.attrib.get('home') == '1' else 'false'
settings('plexhome', value=home)
settings('plexAvatar', value=xml.attrib.get('thumb'))
settings('plexHomeSize', value=xml.attrib.get('homeSize', '1'))
LOG.info('Updated Plex info from plex.tv')
return answer
def check_existing_pms(self):
"""
Check the PMS that was set in file settings.
Will return False if we need to reconnect, because:
PMS could not be reached (no matter the authorization)
machineIdentifier did not match
Will also set the PMS machineIdentifier in the file settings if it was
not set before
"""
answer = True
chk = PF.check_connection(self.server, verifySSL=False)
if chk is False:
LOG.warn('Could not reach PMS %s', self.server)
answer = False
if answer is True and not self.serverid:
LOG.info('No PMS machineIdentifier found for %s. Trying to '
'get the PMS unique ID', self.server)
self.serverid = PF.GetMachineIdentifier(self.server)
if self.serverid is None:
LOG.warn('Could not retrieve machineIdentifier')
answer = False
else:
settings('plex_machineIdentifier', value=self.serverid)
elif answer is True:
temp_server_id = PF.GetMachineIdentifier(self.server)
if temp_server_id != self.serverid:
LOG.warn('The current PMS %s was expected to have a '
'unique machineIdentifier of %s. But we got '
'%s. Pick a new server to be sure',
self.server, self.serverid, temp_server_id)
answer = False
return answer
@staticmethod
def _check_pms_connectivity(server):
"""
Checks for server's connectivity. Returns check_connection result
"""
# Re-direct via plex if remote - will lead to the correct SSL
# certificate
if server['local']:
url = ('%s://%s:%s'
% (server['scheme'], server['ip'], server['port']))
# Deactive SSL verification if the server is local!
verifySSL = False
else:
url = server['baseURL']
verifySSL = True
chk = PF.check_connection(url,
token=server['token'],
verifySSL=verifySSL)
return chk
def pick_pms(self, showDialog=False):
"""
Searches for PMS in local Lan and optionally (if self.plex_token set)
also on plex.tv
showDialog=True: let the user pick one
showDialog=False: automatically pick PMS based on machineIdentifier
Returns the picked PMS' detail as a dict:
{
'machineIdentifier' [str] unique identifier of the PMS
'name' [str] name of the PMS
'token' [str] token needed to access that PMS
'ownername' [str] name of the owner of this PMS or None if
the owner itself supplied tries to connect
'product' e.g. 'Plex Media Server' or None
'version' e.g. '1.11.2.4772-3e...' or None
'device': e.g. 'PC' or 'Windows' or None
'platform': e.g. 'Windows', 'Android' or None
'local' [bool] True if plex.tv supplied
'publicAddressMatches'='1'
or if found using Plex GDM in the local LAN
'owned' [bool] True if it's the owner's PMS
'relay' [bool] True if plex.tv supplied 'relay'='1'
'presence' [bool] True if plex.tv supplied 'presence'='1'
'httpsRequired' [bool] True if plex.tv supplied
'httpsRequired'='1'
'scheme' [str] either 'http' or 'https'
'ip': [str] IP of the PMS, e.g. '192.168.1.1'
'port': [str] Port of the PMS, e.g. '32400'
'baseURL': [str] <scheme>://<ip>:<port> of the PMS
}
or None if unsuccessful
"""
server = None
# If no server is set, let user choose one
if not self.server or not self.serverid:
showDialog = True
if showDialog is True:
server = self._user_pick_pms()
else:
server = self._auto_pick_pms()
if server is not None:
_write_pms_settings(server['baseURL'], server['token'])
return server
def _auto_pick_pms(self):
""" """
Will try to pick PMS based on machineIdentifier saved in file settings Will try to pick PMS based on machineIdentifier saved in file settings
but only once but only once
Returns server or None if unsuccessful Returns server or None if unsuccessful
""" """
httpsUpdated = False https_updated = False
checkedPlexTV = False checked_plex_tv = False
server = None server = None
while True: while True:
if httpsUpdated is False: if https_updated is False:
serverlist = self._getServerList() serverlist = PF.discover_pms(self.plex_token)
for item in serverlist: for item in serverlist:
if item.get('machineIdentifier') == self.serverid: if item.get('machineIdentifier') == self.serverid:
server = item server = item
@ -331,26 +331,30 @@ class InitialSetup():
'machineIdentifier of %s and name %s is ' 'machineIdentifier of %s and name %s is '
'offline', self.serverid, name) 'offline', self.serverid, name)
return return
chk = self._checkServerCon(server) chk = self._check_pms_connectivity(server)
if chk == 504 and httpsUpdated is False: if chk == 504 and https_updated is False:
# Not able to use HTTP, try HTTPs for now # switch HTTPS to HTTP or vice-versa
if server['scheme'] == 'https':
server['scheme'] = 'http'
else:
server['scheme'] = 'https' server['scheme'] = 'https'
httpsUpdated = True https_updated = True
continue continue
if chk == 401: if chk == 401:
LOG.warn('Not yet authorized for Plex server %s', LOG.warn('Not yet authorized for Plex server %s',
server['name']) server['name'])
if self.CheckPlexTVSignIn() is True: if self.check_plex_tv_sign_in() is True:
if checkedPlexTV is False: if checked_plex_tv is False:
# Try again # Try again
checkedPlexTV = True checked_plex_tv = True
httpsUpdated = False https_updated = False
continue continue
else: else:
LOG.warn('Not authorized even though we are signed ' LOG.warn('Not authorized even though we are signed '
' in to plex.tv correctly') ' in to plex.tv correctly')
self.dialog.ok(lang(29999), '%s %s' dialog('ok',
% (lang(39214), lang(29999),
'%s %s' % (lang(39214),
tryEncode(server['name']))) tryEncode(server['name'])))
return return
else: else:
@ -364,25 +368,25 @@ class InitialSetup():
server['name']) server['name'])
return server return server
def _UserPickPMS(self): def _user_pick_pms(self):
""" """
Lets user pick his/her PMS from a list Lets user pick his/her PMS from a list
Returns server or None if unsuccessful Returns server or None if unsuccessful
""" """
httpsUpdated = False https_updated = False
while True: while True:
if httpsUpdated is False: if https_updated is False:
serverlist = self._getServerList() serverlist = PF.discover_pms(self.plex_token)
# Exit if no servers found # Exit if no servers found
if len(serverlist) == 0: if not serverlist:
LOG.warn('No plex media servers found!') LOG.warn('No plex media servers found!')
self.dialog.ok(lang(29999), lang(39011)) dialog('ok', lang(29999), lang(39011))
return return
# Get a nicer list # Get a nicer list
dialoglist = [] dialoglist = []
for server in serverlist: for server in serverlist:
if server['local'] == '1': if server['local']:
# server is in the same network as client. # server is in the same network as client.
# Add"local" # Add"local"
msg = lang(39022) msg = lang(39022)
@ -399,34 +403,34 @@ class InitialSetup():
dialoglist.append('%s (%s)' dialoglist.append('%s (%s)'
% (server['name'], msg)) % (server['name'], msg))
# Let user pick server from a list # Let user pick server from a list
resp = self.dialog.select(lang(39012), dialoglist) resp = dialog('select', lang(39012), dialoglist)
if resp == -1: if resp == -1:
# User cancelled # User cancelled
return return
server = serverlist[resp] server = serverlist[resp]
chk = self._checkServerCon(server) chk = self._check_pms_connectivity(server)
if chk == 504 and httpsUpdated is False: if chk == 504 and https_updated is False:
# Not able to use HTTP, try HTTPs for now # Not able to use HTTP, try HTTPs for now
serverlist[resp]['scheme'] = 'https' serverlist[resp]['scheme'] = 'https'
httpsUpdated = True https_updated = True
continue continue
httpsUpdated = False https_updated = False
if chk == 401: if chk == 401:
LOG.warn('Not yet authorized for Plex server %s', LOG.warn('Not yet authorized for Plex server %s',
server['name']) server['name'])
# Please sign in to plex.tv # Please sign in to plex.tv
self.dialog.ok(lang(29999), dialog('ok',
lang(29999),
lang(39013) + server['name'], lang(39013) + server['name'],
lang(39014)) lang(39014))
if self.PlexTVSignIn() is False: if self.plex_tv_sign_in() is False:
# Exit while loop if user cancels # Exit while loop if user cancels
return return
# Problems connecting # Problems connecting
elif chk >= 400 or chk is False: elif chk >= 400 or chk is False:
# Problems connecting to server. Pick another server? # Problems connecting to server. Pick another server?
answ = self.dialog.yesno(lang(29999), answ = dialog('yesno', lang(29999), lang(39015))
lang(39015))
# Exit while loop if user chooses No # Exit while loop if user chooses No
if not answ: if not answ:
return return
@ -434,30 +438,16 @@ class InitialSetup():
else: else:
return server return server
def WritePMStoSettings(self, server): @staticmethod
def write_pms_to_settings(server):
""" """
Saves server to file settings. server is a dict of the form: Saves server to file settings
{
'name': friendlyName, the Plex server's name
'address': ip:port
'ip': ip, without http/https
'port': port
'scheme': 'http'/'https', nice for checking for secure connections
'local': '1'/'0', Is the server a local server?
'owned': '1'/'0', Is the server owned by the user?
'machineIdentifier': id, Plex server machine identifier
'accesstoken': token Access token to this server
'baseURL': baseURL scheme://ip:port
'ownername' Plex username of PMS owner
}
""" """
settings('plex_machineIdentifier', server['machineIdentifier']) settings('plex_machineIdentifier', server['machineIdentifier'])
settings('plex_servername', server['name']) settings('plex_servername', server['name'])
settings('plex_serverowned', settings('plex_serverowned', 'true' if server['owned'] else 'false')
'true' if server['owned'] == '1'
else 'false')
# Careful to distinguish local from remote PMS # Careful to distinguish local from remote PMS
if server['local'] == '1': if server['local']:
scheme = server['scheme'] scheme = server['scheme']
settings('ipaddress', server['ip']) settings('ipaddress', server['ip'])
settings('port', server['port']) settings('port', server['port'])
@ -491,7 +481,6 @@ class InitialSetup():
path. path.
""" """
LOG.info("Initial setup called.") LOG.info("Initial setup called.")
dialog = self.dialog
try: try:
with XmlKodiSetting('advancedsettings.xml', with XmlKodiSetting('advancedsettings.xml',
force_create=True, force_create=True,
@ -537,9 +526,11 @@ class InitialSetup():
# Missing smb:// occurences, re-add. # Missing smb:// occurences, re-add.
for _ in range(0, count): for _ in range(0, count):
source = etree.SubElement(root, 'source') source = etree.SubElement(root, 'source')
etree.SubElement(source, etree.SubElement(
source,
'name').text = "PlexKodiConnect Masterlock Hack" 'name').text = "PlexKodiConnect Masterlock Hack"
etree.SubElement(source, etree.SubElement(
source,
'path', 'path',
attrib={'pathversion': "1"}).text = "smb://" attrib={'pathversion': "1"}).text = "smb://"
etree.SubElement(source, 'allowsharing').text = "true" etree.SubElement(source, 'allowsharing').text = "true"
@ -555,23 +546,23 @@ class InitialSetup():
# return only if the right machine identifier is found # return only if the right machine identifier is found
if self.server: if self.server:
LOG.info("PMS is already set: %s. Checking now...", self.server) LOG.info("PMS is already set: %s. Checking now...", self.server)
if self.CheckPMS(): if self.check_existing_pms():
LOG.info("Using PMS %s with machineIdentifier %s", LOG.info("Using PMS %s with machineIdentifier %s",
self.server, self.serverid) self.server, self.serverid)
self._write_PMS_settings(self.server, self.pms_token) _write_pms_settings(self.server, self.pms_token)
if reboot is True: if reboot is True:
reboot_kodi() reboot_kodi()
return return
# If not already retrieved myplex info, optionally let user sign in # If not already retrieved myplex info, optionally let user sign in
# to plex.tv. This DOES get called on very first install run # to plex.tv. This DOES get called on very first install run
if not self.plexToken and self.myplexlogin: if not self.plex_token and self.myplexlogin:
self.PlexTVSignIn() self.plex_tv_sign_in()
server = self.PickPMS() server = self.pick_pms()
if server is not None: if server is not None:
# Write our chosen server to Kodi settings file # Write our chosen server to Kodi settings file
self.WritePMStoSettings(server) self.write_pms_to_settings(server)
# User already answered the installation questions # User already answered the installation questions
if settings('InstallQuestionsAnswered') == 'true': if settings('InstallQuestionsAnswered') == 'true':
@ -581,8 +572,9 @@ class InitialSetup():
# Additional settings where the user needs to choose # Additional settings where the user needs to choose
# Direct paths (\\NAS\mymovie.mkv) or addon (http)? # Direct paths (\\NAS\mymovie.mkv) or addon (http)?
goToSettings = False goto_settings = False
if dialog.yesno(lang(29999), if dialog('yesno',
lang(29999),
lang(39027), lang(39027),
lang(39028), lang(39028),
nolabel="Addon (Default)", nolabel="Addon (Default)",
@ -592,39 +584,40 @@ class InitialSetup():
state.DIRECT_PATHS = True state.DIRECT_PATHS = True
# Are you on a system where you would like to replace paths # Are you on a system where you would like to replace paths
# \\NAS\mymovie.mkv with smb://NAS/mymovie.mkv? (e.g. Windows) # \\NAS\mymovie.mkv with smb://NAS/mymovie.mkv? (e.g. Windows)
if dialog.yesno(heading=lang(29999), line1=lang(39033)): if dialog('yesno', heading=lang(29999), line1=lang(39033)):
LOG.debug("User chose to replace paths with smb") LOG.debug("User chose to replace paths with smb")
else: else:
settings('replaceSMB', value="false") settings('replaceSMB', value="false")
# complete replace all original Plex library paths with custom SMB # complete replace all original Plex library paths with custom SMB
if dialog.yesno(heading=lang(29999), line1=lang(39043)): if dialog('yesno', heading=lang(29999), line1=lang(39043)):
LOG.debug("User chose custom smb paths") LOG.debug("User chose custom smb paths")
settings('remapSMB', value="true") settings('remapSMB', value="true")
# Please enter your custom smb paths in the settings under # Please enter your custom smb paths in the settings under
# "Sync Options" and then restart Kodi # "Sync Options" and then restart Kodi
dialog.ok(heading=lang(29999), line1=lang(39044)) dialog('ok', heading=lang(29999), line1=lang(39044))
goToSettings = True goto_settings = True
# Go to network credentials? # Go to network credentials?
if dialog.yesno(heading=lang(29999), if dialog('yesno',
heading=lang(29999),
line1=lang(39029), line1=lang(39029),
line2=lang(39030)): line2=lang(39030)):
LOG.debug("Presenting network credentials dialog.") LOG.debug("Presenting network credentials dialog.")
from utils import passwordsXML from utils import passwordsXML
passwordsXML() passwordsXML()
# Disable Plex music? # Disable Plex music?
if dialog.yesno(heading=lang(29999), line1=lang(39016)): if dialog('yesno', heading=lang(29999), line1=lang(39016)):
LOG.debug("User opted to disable Plex music library.") LOG.debug("User opted to disable Plex music library.")
settings('enableMusic', value="false") settings('enableMusic', value="false")
# Download additional art from FanArtTV # Download additional art from FanArtTV
if dialog.yesno(heading=lang(29999), line1=lang(39061)): if dialog('yesno', heading=lang(29999), line1=lang(39061)):
LOG.debug("User opted to use FanArtTV") LOG.debug("User opted to use FanArtTV")
settings('FanartTV', value="true") settings('FanartTV', value="true")
# Do you want to replace your custom user ratings with an indicator of # Do you want to replace your custom user ratings with an indicator of
# how many versions of a media item you posses? # how many versions of a media item you posses?
if dialog.yesno(heading=lang(29999), line1=lang(39718)): if dialog('yesno', heading=lang(29999), line1=lang(39718)):
LOG.debug("User opted to replace user ratings with version number") LOG.debug("User opted to replace user ratings with version number")
settings('indicate_media_versions', value="true") settings('indicate_media_versions', value="true")
@ -637,12 +630,13 @@ class InitialSetup():
# Make sure that we only ask these questions upon first installation # Make sure that we only ask these questions upon first installation
settings('InstallQuestionsAnswered', value='true') settings('InstallQuestionsAnswered', value='true')
if goToSettings is False: if goto_settings is False:
# Open Settings page now? You will need to restart! # Open Settings page now? You will need to restart!
goToSettings = dialog.yesno(heading=lang(29999), line1=lang(39017)) goto_settings = dialog('yesno',
if goToSettings: heading=lang(29999),
line1=lang(39017))
if goto_settings:
state.PMS_STATUS = 'Stop' state.PMS_STATUS = 'Stop'
xbmc.executebuiltin( executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)')
'Addon.OpenSettings(plugin.video.plexkodiconnect)')
elif reboot is True: elif reboot is True:
reboot_kodi() reboot_kodi()

338
resources/lib/plex_tv.py Normal file
View file

@ -0,0 +1,338 @@
# -*- coding: utf-8 -*-
from logging import getLogger
from xbmc import sleep, executebuiltin
from downloadutils import DownloadUtils as DU
from utils import dialog, language as lang, settings, tryEncode
import variables as v
import state
###############################################################################
LOG = getLogger("PLEX." + __name__)
###############################################################################
def choose_home_user(token):
"""
Let's user choose from a list of Plex home users. Will switch to that
user accordingly.
Returns a dict:
{
'username': Unicode
'userid': '' Plex ID of the user
'token': '' User's token
'protected': True if PIN is needed, else False
}
Will return False if something went wrong (wrong PIN, no connection)
"""
# Get list of Plex home users
users = list_home_users(token)
if not users:
LOG.error("User download failed.")
return False
userlist = []
userlist_coded = []
for user in users:
username = user['title']
userlist.append(username)
# To take care of non-ASCII usernames
userlist_coded.append(tryEncode(username))
usernumber = len(userlist)
username = ''
usertoken = ''
trials = 0
while trials < 3:
if usernumber > 1:
# Select user
user_select = dialog('select', lang(29999) + lang(39306),
userlist_coded)
if user_select == -1:
LOG.info("No user selected.")
settings('username', value='')
executebuiltin('Addon.OpenSettings(%s)' % v.ADDON_ID)
return False
# Only 1 user received, choose that one
else:
user_select = 0
selected_user = userlist[user_select]
LOG.info("Selected user: %s", selected_user)
user = users[user_select]
# Ask for PIN, if protected:
pin = None
if user['protected'] == '1':
LOG.debug('Asking for users PIN')
pin = dialog('input',
lang(39307) + selected_user,
'',
type='{numeric}',
option='{hide}')
# User chose to cancel
# Plex bug: don't call url for protected user with empty PIN
if not pin:
trials += 1
continue
# Switch to this Plex Home user, if applicable
result = switch_home_user(user['id'],
pin,
token,
settings('plex_machineIdentifier'))
if result:
# Successfully retrieved username: break out of while loop
username = result['username']
usertoken = result['usertoken']
break
# Couldn't get user auth
else:
trials += 1
# Could not login user, please try again
if not dialog('yesno',
heading='{plex}',
line1=lang(39308) + selected_user,
line2=lang(39309)):
# User chose to cancel
break
if not username:
LOG.error('Failed signing in a user to plex.tv')
executebuiltin('Addon.OpenSettings(%s)' % v.ADDON_ID)
return False
return {
'username': username,
'userid': user['id'],
'protected': True if user['protected'] == '1' else False,
'token': usertoken
}
def switch_home_user(userid, pin, token, machineIdentifier):
"""
Retrieves Plex home token for a Plex home user.
Returns False if unsuccessful
Input:
userid id of the Plex home user
pin PIN of the Plex home user, if protected
token token for plex.tv
Output:
{
'username'
'usertoken' Might be empty strings if no token found
for the machineIdentifier that was chosen
}
settings('userid') and settings('username') with new plex token
"""
LOG.info('Switching to user %s', userid)
url = 'https://plex.tv/api/home/users/' + userid + '/switch'
if pin:
url += '?pin=' + pin
answer = DU().downloadUrl(url,
authenticate=False,
action_type="POST",
headerOptions={'X-Plex-Token': token})
try:
answer.attrib
except AttributeError:
LOG.error('Error: plex.tv switch HomeUser change failed')
return False
username = answer.attrib.get('title', '')
token = answer.attrib.get('authenticationToken', '')
# Write to settings file
settings('username', username)
settings('accessToken', token)
settings('userid', answer.attrib.get('id', ''))
settings('plex_restricteduser',
'true' if answer.attrib.get('restricted', '0') == '1'
else 'false')
state.RESTRICTED_USER = True if \
answer.attrib.get('restricted', '0') == '1' else False
# Get final token to the PMS we've chosen
url = 'https://plex.tv/api/resources?includeHttps=1'
xml = DU().downloadUrl(url,
authenticate=False,
headerOptions={'X-Plex-Token': token})
try:
xml.attrib
except AttributeError:
LOG.error('Answer from plex.tv not as excepted')
# Set to empty iterable list for loop
xml = []
found = 0
LOG.debug('Our machineIdentifier is %s', machineIdentifier)
for device in xml:
identifier = device.attrib.get('clientIdentifier')
LOG.debug('Found a Plex machineIdentifier: %s', identifier)
if identifier == machineIdentifier:
found += 1
token = device.attrib.get('accessToken')
result = {
'username': username,
}
if found == 0:
LOG.info('No tokens found for your server! Using empty string')
result['usertoken'] = ''
else:
result['usertoken'] = token
LOG.info('Plex.tv switch HomeUser change successfull for user %s',
username)
return result
def list_home_users(token):
"""
Returns a list for myPlex home users for the current plex.tv account.
Input:
token for plex.tv
Output:
List of users, where one entry is of the form:
"id": userId,
"admin": '1'/'0',
"guest": '1'/'0',
"restricted": '1'/'0',
"protected": '1'/'0',
"email": email,
"title": title,
"username": username,
"thumb": thumb_url
}
If any value is missing, None is returned instead (or "" from plex.tv)
If an error is encountered, False is returned
"""
xml = DU().downloadUrl('https://plex.tv/api/home/users/',
authenticate=False,
headerOptions={'X-Plex-Token': token})
try:
xml.attrib
except AttributeError:
LOG.error('Download of Plex home users failed.')
return False
users = []
for user in xml:
users.append(user.attrib)
return users
def sign_in_with_pin():
"""
Prompts user to sign in by visiting https://plex.tv/pin
Writes to Kodi settings file. Also returns:
{
'plexhome': 'true' if Plex Home, 'false' otherwise
'username':
'avatar': URL to user avator
'token':
'plexid': Plex user ID
'homesize': Number of Plex home users (defaults to '1')
}
Returns False if authentication did not work.
"""
code, identifier = get_pin()
if not code:
# Problems trying to contact plex.tv. Try again later
dialog('ok', heading='{plex}', line1=lang(39303))
return False
# Go to https://plex.tv/pin and enter the code:
# Or press No to cancel the sign in.
answer = dialog('yesno',
heading='{plex}',
line1=lang(39304) + "\n\n",
line2=code + "\n\n",
line3=lang(39311))
if not answer:
return False
count = 0
# Wait for approx 30 seconds (since the PIN is not visible anymore :-))
while count < 30:
xml = check_pin(identifier)
if xml is not False:
break
# Wait for 1 seconds
sleep(1000)
count += 1
if xml is False:
# Could not sign in to plex.tv Try again later
dialog('ok', heading='{plex}', line1=lang(39305))
return False
# Parse xml
userid = xml.attrib.get('id')
home = xml.get('home', '0')
if home == '1':
home = 'true'
else:
home = 'false'
username = xml.get('username', '')
avatar = xml.get('thumb', '')
token = xml.findtext('authentication-token')
home_size = xml.get('homeSize', '1')
result = {
'plexhome': home,
'username': username,
'avatar': avatar,
'token': token,
'plexid': userid,
'homesize': home_size
}
settings('plexLogin', username)
settings('plexToken', token)
settings('plexhome', home)
settings('plexid', userid)
settings('plexAvatar', avatar)
settings('plexHomeSize', home_size)
# Let Kodi log into plex.tv on startup from now on
settings('myplexlogin', 'true')
settings('plex_status', value=lang(39227))
return result
def get_pin():
"""
For plex.tv sign-in: returns 4-digit code and identifier as 2 str
"""
code = None
identifier = None
# Download
xml = DU().downloadUrl('https://plex.tv/pins.xml',
authenticate=False,
action_type="POST")
try:
xml.attrib
except AttributeError:
LOG.error("Error, no PIN from plex.tv provided")
return None, None
code = xml.find('code').text
identifier = xml.find('id').text
LOG.info('Successfully retrieved code and id from plex.tv')
return code, identifier
def check_pin(identifier):
"""
Checks with plex.tv whether user entered the correct PIN on plex.tv/pin
Returns False if not yet done so, or the XML response file as etree
"""
# Try to get a temporary token
xml = DU().downloadUrl('https://plex.tv/pins/%s.xml' % identifier,
authenticate=False)
try:
temp_token = xml.find('auth_token').text
except AttributeError:
LOG.error("Could not find token in plex.tv answer")
return False
if not temp_token:
return False
# Use temp token to get the final plex credentials
xml = DU().downloadUrl('https://plex.tv/users/account',
authenticate=False,
parameters={'X-Plex-Token': temp_token})
return xml

View file

@ -1,25 +1,21 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################### ###############################################################################
from logging import getLogger from logging import getLogger
from threading import Thread from threading import Thread
import xbmc from xbmc import sleep, executebuiltin, translatePath
import xbmcgui
import xbmcaddon import xbmcaddon
from xbmcvfs import exists from xbmcvfs import exists
from utils import window, settings, language as lang, thread_methods, dialog
from utils import window, settings, language as lang, thread_methods from downloadutils import DownloadUtils as DU
import downloadutils import plex_tv
import PlexFunctions as PF
import PlexAPI
from PlexFunctions import GetMachineIdentifier
import state import state
############################################################################### ###############################################################################
log = getLogger("PLEX."+__name__) LOG = getLogger("PLEX." + __name__)
############################################################################### ###############################################################################
@ -45,7 +41,7 @@ class UserClient(Thread):
self.userSettings = None self.userSettings = None
self.addon = xbmcaddon.Addon() self.addon = xbmcaddon.Addon()
self.doUtils = downloadutils.DownloadUtils() self.doUtils = DU()
Thread.__init__(self) Thread.__init__(self)
@ -55,10 +51,10 @@ class UserClient(Thread):
""" """
username = settings('username') username = settings('username')
if not username: if not username:
log.debug("No username saved, trying to get Plex username") LOG.debug("No username saved, trying to get Plex username")
username = settings('plexLogin') username = settings('plexLogin')
if not username: if not username:
log.debug("Also no Plex username found") LOG.debug("Also no Plex username found")
return "" return ""
return username return username
@ -73,7 +69,7 @@ class UserClient(Thread):
server = host + ":" + port server = host + ":" + port
if not host: if not host:
log.debug("No server information saved.") LOG.debug("No server information saved.")
return False return False
# If https is true # If https is true
@ -84,11 +80,11 @@ class UserClient(Thread):
server = "http://%s" % server server = "http://%s" % server
# User entered IP; we need to get the machineIdentifier # User entered IP; we need to get the machineIdentifier
if self.machineIdentifier == '' and prefix is True: if self.machineIdentifier == '' and prefix is True:
self.machineIdentifier = GetMachineIdentifier(server) self.machineIdentifier = PF.GetMachineIdentifier(server)
if self.machineIdentifier is None: if self.machineIdentifier is None:
self.machineIdentifier = '' self.machineIdentifier = ''
settings('plex_machineIdentifier', value=self.machineIdentifier) settings('plex_machineIdentifier', value=self.machineIdentifier)
log.debug('Returning active server: %s' % server) LOG.debug('Returning active server: %s', server)
return server return server
def getSSLverify(self): def getSSLverify(self):
@ -101,10 +97,10 @@ class UserClient(Thread):
else settings('sslcert') else settings('sslcert')
def setUserPref(self): def setUserPref(self):
log.debug('Setting user preferences') LOG.debug('Setting user preferences')
# Only try to get user avatar if there is a token # Only try to get user avatar if there is a token
if self.currToken: if self.currToken:
url = PlexAPI.PlexAPI().GetUserArtworkURL(self.currUser) url = PF.GetUserArtworkURL(self.currUser)
if url: if url:
window('PlexUserImage', value=url) window('PlexUserImage', value=url)
# Set resume point max # Set resume point max
@ -116,7 +112,7 @@ class UserClient(Thread):
return True return True
def loadCurrUser(self, username, userId, usertoken, authenticated=False): def loadCurrUser(self, username, userId, usertoken, authenticated=False):
log.debug('Loading current user') LOG.debug('Loading current user')
doUtils = self.doUtils doUtils = self.doUtils
self.currToken = usertoken self.currToken = usertoken
@ -127,18 +123,18 @@ class UserClient(Thread):
if authenticated is False: if authenticated is False:
if self.currServer is None: if self.currServer is None:
return False return False
log.debug('Testing validity of current token') LOG.debug('Testing validity of current token')
res = PlexAPI.PlexAPI().CheckConnection(self.currServer, res = PF.check_connection(self.currServer,
token=self.currToken, token=self.currToken,
verifySSL=self.ssl) verifySSL=self.ssl)
if res is False: if res is False:
# PMS probably offline # PMS probably offline
return False return False
elif res == 401: elif res == 401:
log.error('Token is no longer valid') LOG.error('Token is no longer valid')
return 401 return 401
elif res >= 400: elif res >= 400:
log.error('Answer from PMS is not as expected. Retrying') LOG.error('Answer from PMS is not as expected. Retrying')
return False return False
# Set to windows property # Set to windows property
@ -182,31 +178,29 @@ class UserClient(Thread):
return True return True
def authenticate(self): def authenticate(self):
log.debug('Authenticating user') LOG.debug('Authenticating user')
dialog = xbmcgui.Dialog()
# Give attempts at entering password / selecting user # Give attempts at entering password / selecting user
if self.retry >= 2: if self.retry >= 2:
log.error("Too many retries to login.") LOG.error("Too many retries to login.")
state.PMS_STATUS = 'Stop' state.PMS_STATUS = 'Stop'
dialog.ok(lang(33001), dialog('ok', lang(33001), lang(39023))
lang(39023)) executebuiltin(
xbmc.executebuiltin(
'Addon.OpenSettings(plugin.video.plexkodiconnect)') 'Addon.OpenSettings(plugin.video.plexkodiconnect)')
return False return False
# Get /profile/addon_data # Get /profile/addon_data
addondir = xbmc.translatePath(self.addon.getAddonInfo('profile')) addondir = translatePath(self.addon.getAddonInfo('profile'))
# If there's no settings.xml # If there's no settings.xml
if not exists("%ssettings.xml" % addondir): if not exists("%ssettings.xml" % addondir):
log.error("Error, no settings.xml found.") LOG.error("Error, no settings.xml found.")
self.auth = False self.auth = False
return False return False
server = self.getServer() server = self.getServer()
# If there is no server we can connect to # If there is no server we can connect to
if not server: if not server:
log.info("Missing server information.") LOG.info("Missing server information.")
self.auth = False self.auth = False
return False return False
@ -217,7 +211,7 @@ class UserClient(Thread):
enforceLogin = settings('enforceUserLogin') enforceLogin = settings('enforceUserLogin')
# Found a user in the settings, try to authenticate # Found a user in the settings, try to authenticate
if username and enforceLogin == 'false': if username and enforceLogin == 'false':
log.debug('Trying to authenticate with old settings') LOG.debug('Trying to authenticate with old settings')
answ = self.loadCurrUser(username, answ = self.loadCurrUser(username,
userId, userId,
usertoken, usertoken,
@ -226,21 +220,19 @@ class UserClient(Thread):
# SUCCESS: loaded a user from the settings # SUCCESS: loaded a user from the settings
return True return True
elif answ == 401: elif answ == 401:
log.error("User token no longer valid. Sign user out") LOG.error("User token no longer valid. Sign user out")
settings('username', value='') settings('username', value='')
settings('userid', value='') settings('userid', value='')
settings('accessToken', value='') settings('accessToken', value='')
else: else:
log.debug("Could not yet authenticate user") LOG.debug("Could not yet authenticate user")
return False return False
plx = PlexAPI.PlexAPI()
# Could not use settings - try to get Plex user list from plex.tv # Could not use settings - try to get Plex user list from plex.tv
plextoken = settings('plexToken') plextoken = settings('plexToken')
if plextoken: if plextoken:
log.info("Trying to connect to plex.tv to get a user list") LOG.info("Trying to connect to plex.tv to get a user list")
userInfo = plx.ChoosePlexHomeUser(plextoken) userInfo = plex_tv.choose_home_user(plextoken)
if userInfo is False: if userInfo is False:
# FAILURE: Something went wrong, try again # FAILURE: Something went wrong, try again
self.auth = True self.auth = True
@ -250,7 +242,7 @@ class UserClient(Thread):
userId = userInfo['userid'] userId = userInfo['userid']
usertoken = userInfo['token'] usertoken = userInfo['token']
else: else:
log.info("Trying to authenticate without a token") LOG.info("Trying to authenticate without a token")
username = '' username = ''
userId = '' userId = ''
usertoken = '' usertoken = ''
@ -258,14 +250,13 @@ class UserClient(Thread):
if self.loadCurrUser(username, userId, usertoken, authenticated=False): if self.loadCurrUser(username, userId, usertoken, authenticated=False):
# SUCCESS: loaded a user from the settings # SUCCESS: loaded a user from the settings
return True return True
else: # Something went wrong, try again
# FAILUR: Something went wrong, try again
self.auth = True self.auth = True
self.retry += 1 self.retry += 1
return False return False
def resetClient(self): def resetClient(self):
log.debug("Reset UserClient authentication.") LOG.debug("Reset UserClient authentication.")
self.doUtils.stopSession() self.doUtils.stopSession()
window('plex_authenticated', clear=True) window('plex_authenticated', clear=True)
@ -295,17 +286,17 @@ class UserClient(Thread):
self.retry = 0 self.retry = 0
def run(self): def run(self):
log.info("----===## Starting UserClient ##===----") LOG.info("----===## Starting UserClient ##===----")
thread_stopped = self.thread_stopped thread_stopped = self.thread_stopped
thread_suspended = self.thread_suspended thread_suspended = self.thread_suspended
while not thread_stopped(): while not thread_stopped():
while thread_suspended(): while thread_suspended():
if thread_stopped(): if thread_stopped():
break break
xbmc.sleep(1000) sleep(1000)
if state.PMS_STATUS == "Stop": if state.PMS_STATUS == "Stop":
xbmc.sleep(500) sleep(500)
continue continue
# Verify the connection status to server # Verify the connection status to server
@ -318,7 +309,7 @@ class UserClient(Thread):
state.PMS_STATUS = 'Auth' state.PMS_STATUS = 'Auth'
window('plex_serverStatus', value='Auth') window('plex_serverStatus', value='Auth')
self.resetClient() self.resetClient()
xbmc.sleep(3000) sleep(3000)
if self.auth and (self.currUser is None): if self.auth and (self.currUser is None):
# Try to authenticate user # Try to authenticate user
@ -328,9 +319,9 @@ class UserClient(Thread):
self.auth = False self.auth = False
if self.authenticate(): if self.authenticate():
# Successfully authenticated and loaded a user # Successfully authenticated and loaded a user
log.info("Successfully authenticated!") LOG.info("Successfully authenticated!")
log.info("Current user: %s" % self.currUser) LOG.info("Current user: %s", self.currUser)
log.info("Current userId: %s" % state.PLEX_USER_ID) LOG.info("Current userId: %s", state.PLEX_USER_ID)
self.retry = 0 self.retry = 0
state.SUSPEND_LIBRARY_THREAD = False state.SUSPEND_LIBRARY_THREAD = False
window('plex_serverStatus', clear=True) window('plex_serverStatus', clear=True)
@ -344,10 +335,10 @@ class UserClient(Thread):
# Or retried too many times # Or retried too many times
if server and state.PMS_STATUS != "Stop": if server and state.PMS_STATUS != "Stop":
# Only if there's information found to login # Only if there's information found to login
log.debug("Server found: %s" % server) LOG.debug("Server found: %s", server)
self.auth = True self.auth = True
# Minimize CPU load # Minimize CPU load
xbmc.sleep(100) sleep(100)
log.info("##===---- UserClient Stopped ----===##") LOG.info("##===---- UserClient Stopped ----===##")

View file

@ -129,22 +129,19 @@ def dialog(typus, *args, **kwargs):
""" """
Displays xbmcgui Dialog. Pass a string as typus: Displays xbmcgui Dialog. Pass a string as typus:
'yesno', 'ok', 'notification', 'input', 'select', 'numeric' 'yesno', 'ok', 'notification', 'input', 'select', 'numeric'
kwargs: kwargs:
heading='{plex}' title bar (here PlexKodiConnect) heading='{plex}' title bar (here PlexKodiConnect)
message=lang(30128), Actual dialog content. Don't use with OK message=lang(30128), Dialog content. Don't use with 'OK', 'yesno'
line1=str(), For 'OK' and 'yesno' dialogs use line1...line3! line1=str(), For 'OK' and 'yesno' dialogs use line1...line3!
time=5000, time=5000,
sound=True, sound=True,
nolabel=str(), For 'yesno' dialogs nolabel=str(), For 'yesno' dialogs
yeslabel=str(), For 'yesno' dialogs yeslabel=str(), For 'yesno' dialogs
Icons: Icons:
icon='{plex}' Display Plex standard icon icon='{plex}' Display Plex standard icon
icon='{info}' xbmcgui.NOTIFICATION_INFO icon='{info}' xbmcgui.NOTIFICATION_INFO
icon='{warning}' xbmcgui.NOTIFICATION_WARNING icon='{warning}' xbmcgui.NOTIFICATION_WARNING
icon='{error}' xbmcgui.NOTIFICATION_ERROR icon='{error}' xbmcgui.NOTIFICATION_ERROR
Input Types: Input Types:
type='{alphanum}' xbmcgui.INPUT_ALPHANUM (standard keyboard) type='{alphanum}' xbmcgui.INPUT_ALPHANUM (standard keyboard)
type='{numeric}' xbmcgui.INPUT_NUMERIC (format: #) type='{numeric}' xbmcgui.INPUT_NUMERIC (format: #)
@ -153,9 +150,12 @@ def dialog(typus, *args, **kwargs):
type='{ipaddress}' xbmcgui.INPUT_IPADDRESS (format: #.#.#.#) type='{ipaddress}' xbmcgui.INPUT_IPADDRESS (format: #.#.#.#)
type='{password}' xbmcgui.INPUT_PASSWORD type='{password}' xbmcgui.INPUT_PASSWORD
(return md5 hash of input, input is masked) (return md5 hash of input, input is masked)
Options:
option='{password}' xbmcgui.PASSWORD_VERIFY (verifies an existing
(default) md5 hashed password)
option='{hide}' xbmcgui.ALPHANUM_HIDE_INPUT (masks input)
""" """
d = xbmcgui.Dialog() if 'icon' in kwargs:
if "icon" in kwargs:
types = { types = {
'{plex}': 'special://home/addons/plugin.video.plexkodiconnect/icon.png', '{plex}': 'special://home/addons/plugin.video.plexkodiconnect/icon.png',
'{info}': xbmcgui.NOTIFICATION_INFO, '{info}': xbmcgui.NOTIFICATION_INFO,
@ -174,16 +174,23 @@ def dialog(typus, *args, **kwargs):
'{password}': xbmcgui.INPUT_PASSWORD '{password}': xbmcgui.INPUT_PASSWORD
} }
kwargs['type'] = types[kwargs['type']] kwargs['type'] = types[kwargs['type']]
if "heading" in kwargs: if 'option' in kwargs:
types = {
'{password}': xbmcgui.PASSWORD_VERIFY,
'{hide}': xbmcgui.ALPHANUM_HIDE_INPUT
}
kwargs['option'] = types[kwargs['option']]
if 'heading' in kwargs:
kwargs['heading'] = kwargs['heading'].replace("{plex}", kwargs['heading'] = kwargs['heading'].replace("{plex}",
language(29999)) language(29999))
dia = xbmcgui.Dialog()
types = { types = {
'yesno': d.yesno, 'yesno': dia.yesno,
'ok': d.ok, 'ok': dia.ok,
'notification': d.notification, 'notification': dia.notification,
'input': d.input, 'input': dia.input,
'select': d.select, 'select': dia.select,
'numeric': d.numeric 'numeric': dia.numeric
} }
return types[typus](*args, **kwargs) return types[typus](*args, **kwargs)

View file

@ -35,7 +35,7 @@ from kodimonitor import KodiMonitor, SpecialMonitor
from librarysync import LibrarySync from librarysync import LibrarySync
from websocket_client import PMS_Websocket, Alexa_Websocket from websocket_client import PMS_Websocket, Alexa_Websocket
import PlexAPI from PlexFunctions import check_connection
from PlexCompanion import PlexCompanion from PlexCompanion import PlexCompanion
from command_pipeline import Monitor_Window from command_pipeline import Monitor_Window
from playback_starter import Playback_Starter from playback_starter import Playback_Starter
@ -116,8 +116,6 @@ class Service():
if settings('enableTextureCache') == "true": if settings('enableTextureCache') == "true":
self.image_cache_thread = Image_Cache_Thread() self.image_cache_thread = Image_Cache_Thread()
plx = PlexAPI.PlexAPI()
welcome_msg = True welcome_msg = True
counter = 0 counter = 0
while not __stop_PKC(): while not __stop_PKC():
@ -208,7 +206,7 @@ class Service():
if server is False: if server is False:
# No server info set in add-on settings # No server info set in add-on settings
pass pass
elif plx.CheckConnection(server, verifySSL=True) is False: elif check_connection(server, verifySSL=True) is False:
# Server is offline or cannot be reached # Server is offline or cannot be reached
# Alert the user and suppress future warning # Alert the user and suppress future warning
if self.server_online: if self.server_online:
@ -228,9 +226,9 @@ class Service():
if counter > 20: if counter > 20:
counter = 0 counter = 0
setup = initialsetup.InitialSetup() setup = initialsetup.InitialSetup()
tmp = setup.PickPMS() tmp = setup.pick_pms()
if tmp is not None: if tmp is not None:
setup.WritePMStoSettings(tmp) setup.write_pms_to_settings(tmp)
else: else:
# Server is online # Server is online
counter = 0 counter = 0