2018-07-13 02:46:02 +10:00
|
|
|
#!/usr/bin/env python
|
2015-12-25 06:51:47 +11:00
|
|
|
# -*- coding: utf-8 -*-
|
2018-07-13 02:46:02 +10:00
|
|
|
from __future__ import absolute_import, division, unicode_literals
|
2017-12-10 00:35:08 +11:00
|
|
|
from logging import getLogger
|
2018-01-07 01:19:12 +11:00
|
|
|
from Queue import Queue
|
2018-01-08 01:44:20 +11:00
|
|
|
import xml.etree.ElementTree as etree
|
2015-12-25 06:51:47 +11:00
|
|
|
|
2018-02-11 03:59:20 +11:00
|
|
|
from xbmc import executebuiltin, translatePath
|
2015-12-25 06:51:47 +11:00
|
|
|
|
2018-06-22 03:24:37 +10:00
|
|
|
from . import utils
|
2018-06-24 02:25:18 +10:00
|
|
|
from . import path_ops
|
2018-06-22 03:24:37 +10:00
|
|
|
from . import migration
|
|
|
|
from .downloadutils import DownloadUtils as DU
|
|
|
|
from . import videonodes
|
|
|
|
from . import userclient
|
|
|
|
from . import clientinfo
|
|
|
|
from . import plex_functions as PF
|
|
|
|
from . import plex_tv
|
|
|
|
from . import json_rpc as js
|
|
|
|
from . import playqueue as PQ
|
|
|
|
from . import state
|
|
|
|
from . import variables as v
|
2015-12-27 22:14:06 +11:00
|
|
|
|
2016-02-20 06:03:06 +11:00
|
|
|
###############################################################################
|
2015-12-25 06:51:47 +11:00
|
|
|
|
2018-06-22 03:24:37 +10:00
|
|
|
LOG = getLogger('PLEX.initialsetup')
|
2016-08-31 00:13:13 +10:00
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
2018-06-24 02:25:18 +10:00
|
|
|
if not path_ops.exists(v.EXTERNAL_SUBTITLE_TEMP_PATH):
|
|
|
|
path_ops.makedirs(v.EXTERNAL_SUBTITLE_TEMP_PATH)
|
|
|
|
|
2015-12-25 06:51:47 +11:00
|
|
|
|
2018-02-10 03:48:25 +11:00
|
|
|
WINDOW_PROPERTIES = (
|
2018-04-09 15:21:47 +10:00
|
|
|
"plex_online", "plex_serverStatus", "plex_shouldStop", "plex_dbScan",
|
2018-04-16 02:54:24 +10:00
|
|
|
"plex_customplayqueue", "plex_playbackProps",
|
2018-04-09 15:21:47 +10:00
|
|
|
"pms_token", "plex_token", "pms_server", "plex_machineIdentifier",
|
|
|
|
"plex_servername", "plex_authenticated", "PlexUserImage", "useDirectPaths",
|
|
|
|
"countError", "countUnauthorized", "plex_restricteduser",
|
|
|
|
"plex_allows_mediaDeletion", "plex_command", "plex_result",
|
|
|
|
"plex_force_transcode_pix"
|
2018-02-10 03:48:25 +11:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def reload_pkc():
|
|
|
|
"""
|
|
|
|
Will reload state.py entirely and then initiate some values from the Kodi
|
|
|
|
settings file
|
|
|
|
"""
|
|
|
|
LOG.info('Start (re-)loading PKC settings')
|
|
|
|
# Reset state.py
|
|
|
|
reload(state)
|
|
|
|
# Reset window props
|
|
|
|
for prop in WINDOW_PROPERTIES:
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.window(prop, clear=True)
|
2018-02-10 03:48:25 +11:00
|
|
|
# Clear video nodes properties
|
2018-06-22 03:24:37 +10:00
|
|
|
videonodes.VideoNodes().clearProperties()
|
2018-02-10 03:48:25 +11:00
|
|
|
|
|
|
|
# Initializing
|
2018-06-22 03:24:37 +10:00
|
|
|
state.VERIFY_SSL_CERT = utils.settings('sslverify') == 'true'
|
|
|
|
state.SSL_CERT_PATH = utils.settings('sslcert') \
|
|
|
|
if utils.settings('sslcert') != 'None' else None
|
|
|
|
state.FULL_SYNC_INTERVALL = int(utils.settings('fullSyncInterval')) * 60
|
|
|
|
state.SYNC_THREAD_NUMBER = int(utils.settings('syncThreadNumber'))
|
|
|
|
state.SYNC_DIALOG = utils.settings('dbSyncIndicator') == 'true'
|
|
|
|
state.ENABLE_MUSIC = utils.settings('enableMusic') == 'true'
|
|
|
|
state.BACKGROUND_SYNC_DISABLED = utils.settings(
|
2018-03-10 22:58:11 +11:00
|
|
|
'enableBackgroundSync') == 'false'
|
2018-02-10 03:48:25 +11:00
|
|
|
state.BACKGROUNDSYNC_SAFTYMARGIN = int(
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('backgroundsync_saftyMargin'))
|
|
|
|
state.REPLACE_SMB_PATH = utils.settings('replaceSMB') == 'true'
|
|
|
|
state.REMAP_PATH = utils.settings('remapSMB') == 'true'
|
|
|
|
state.KODI_PLEX_TIME_OFFSET = float(utils.settings('kodiplextimeoffset'))
|
|
|
|
state.FETCH_PMS_ITEM_NUMBER = utils.settings('fetch_pms_item_number')
|
|
|
|
state.FORCE_RELOAD_SKIN = \
|
|
|
|
utils.settings('forceReloadSkinOnPlaybackStop') == 'true'
|
2018-02-10 03:48:25 +11:00
|
|
|
# Init some Queues()
|
|
|
|
state.COMMAND_PIPELINE_QUEUE = Queue()
|
|
|
|
state.COMPANION_QUEUE = Queue(maxsize=100)
|
|
|
|
state.WEBSOCKET_QUEUE = Queue()
|
|
|
|
set_replace_paths()
|
|
|
|
set_webserver()
|
|
|
|
# To detect Kodi profile switches
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.window('plex_kodiProfile',
|
|
|
|
value=utils.try_decode(translatePath("special://profile")))
|
|
|
|
clientinfo.getDeviceId()
|
2018-02-10 03:48:25 +11:00
|
|
|
# Initialize the PKC playqueues
|
|
|
|
PQ.init_playqueues()
|
|
|
|
LOG.info('Done (re-)loading PKC settings')
|
|
|
|
|
|
|
|
|
|
|
|
def set_replace_paths():
|
|
|
|
"""
|
|
|
|
Sets our values for direct paths correctly (including using lower-case
|
|
|
|
protocols like smb:// and NOT SMB://)
|
|
|
|
"""
|
|
|
|
for typus in v.REMAP_TYPE_FROM_PLEXTYPE.values():
|
|
|
|
for arg in ('Org', 'New'):
|
|
|
|
key = 'remapSMB%s%s' % (typus, arg)
|
2018-06-22 03:24:37 +10:00
|
|
|
value = utils.settings(key)
|
2018-02-10 03:48:25 +11:00
|
|
|
if '://' in value:
|
|
|
|
protocol = value.split('://', 1)[0]
|
|
|
|
value = value.replace(protocol, protocol.lower())
|
|
|
|
setattr(state, key, value)
|
|
|
|
|
|
|
|
|
|
|
|
def set_webserver():
|
|
|
|
"""
|
|
|
|
Set the Kodi webserver details - used to set the texture cache
|
|
|
|
"""
|
2018-04-01 18:45:22 +10:00
|
|
|
if js.get_setting('services.webserver') in (None, False):
|
2018-02-10 03:48:25 +11:00
|
|
|
# Enable the webserver, it is disabled
|
2018-04-01 18:45:22 +10:00
|
|
|
js.set_setting('services.webserver', True)
|
2018-02-10 03:48:25 +11:00
|
|
|
# Set standard port and username
|
|
|
|
# set_setting('services.webserverport', 8080)
|
|
|
|
# set_setting('services.webserverusername', 'kodi')
|
|
|
|
# Webserver already enabled
|
2018-04-01 18:45:22 +10:00
|
|
|
state.WEBSERVER_PORT = js.get_setting('services.webserverport')
|
|
|
|
state.WEBSERVER_USERNAME = js.get_setting('services.webserverusername')
|
|
|
|
state.WEBSERVER_PASSWORD = js.get_setting('services.webserverpassword')
|
2018-02-10 03:48:25 +11:00
|
|
|
|
|
|
|
|
2018-02-11 03:59:20 +11:00
|
|
|
def _write_pms_settings(url, token):
|
|
|
|
"""
|
|
|
|
Sets certain settings for server by asking for the PMS' settings
|
|
|
|
Call with url: scheme://ip:port
|
|
|
|
"""
|
|
|
|
xml = PF.get_PMS_settings(url, token)
|
|
|
|
try:
|
|
|
|
xml.attrib
|
|
|
|
except AttributeError:
|
|
|
|
LOG.error('Could not get PMS settings for %s', url)
|
|
|
|
return
|
|
|
|
for entry in xml:
|
|
|
|
if entry.attrib.get('id', '') == 'allowMediaDeletion':
|
2018-06-08 01:23:24 +10:00
|
|
|
value = 'true' if entry.get('value', '1') == '1' else 'false'
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('plex_allows_mediaDeletion', value=value)
|
|
|
|
utils.window('plex_allows_mediaDeletion', value=value)
|
2018-02-11 03:59:20 +11:00
|
|
|
|
|
|
|
|
|
|
|
class InitialSetup(object):
|
|
|
|
"""
|
|
|
|
Will load Plex PMS settings (e.g. address) and token
|
|
|
|
Will ask the user initial questions on first PKC boot
|
|
|
|
"""
|
2015-12-25 06:51:47 +11:00
|
|
|
def __init__(self):
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.debug('Entering initialsetup class')
|
2018-06-22 03:24:37 +10:00
|
|
|
self.server = userclient.UserClient().get_server()
|
|
|
|
self.serverid = utils.settings('plex_machineIdentifier')
|
2016-05-25 04:43:52 +10:00
|
|
|
# Get Plex credentials from settings file, if they exist
|
2018-02-11 03:59:20 +11:00
|
|
|
plexdict = PF.GetPlexLoginFromSettings()
|
2016-05-25 04:43:52 +10:00
|
|
|
self.myplexlogin = plexdict['myplexlogin'] == 'true'
|
2018-02-11 03:59:20 +11:00
|
|
|
self.plex_login = plexdict['plexLogin']
|
|
|
|
self.plex_token = plexdict['plexToken']
|
2016-05-25 04:43:52 +10:00
|
|
|
self.plexid = plexdict['plexid']
|
2016-10-24 04:38:21 +11:00
|
|
|
# Token for the PMS, not plex.tv
|
2018-06-22 03:24:37 +10:00
|
|
|
self.pms_token = utils.settings('accessToken')
|
2018-02-11 03:59:20 +11:00
|
|
|
if self.plex_token:
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.debug('Found a plex.tv token in the settings')
|
2016-05-25 04:43:52 +10:00
|
|
|
|
2018-02-11 03:59:20 +11:00
|
|
|
def plex_tv_sign_in(self):
|
2016-05-25 03:00:39 +10:00
|
|
|
"""
|
2016-05-25 04:43:52 +10:00
|
|
|
Signs (freshly) in to plex.tv (will be saved to file settings)
|
|
|
|
|
|
|
|
Returns True if successful, or False if not
|
2016-05-25 03:00:39 +10:00
|
|
|
"""
|
2018-09-11 04:53:46 +10:00
|
|
|
try:
|
|
|
|
user = plex_tv.sign_in_with_pin()
|
|
|
|
except:
|
|
|
|
utils.ERROR()
|
|
|
|
if user:
|
|
|
|
self.plex_login = user.username
|
|
|
|
self.plex_token = user.authToken
|
|
|
|
self.plexid = user.id
|
2016-05-25 04:43:52 +10:00
|
|
|
return True
|
|
|
|
return False
|
2016-05-25 03:00:39 +10:00
|
|
|
|
2018-02-11 03:59:20 +11:00
|
|
|
def check_plex_tv_sign_in(self):
|
2016-01-15 00:47:34 +11:00
|
|
|
"""
|
2016-05-25 04:43:52 +10:00
|
|
|
Checks existing connection to plex.tv. If not, triggers sign in
|
2016-05-30 00:52:00 +10:00
|
|
|
|
|
|
|
Returns True if signed in, False otherwise
|
2016-01-15 00:47:34 +11:00
|
|
|
"""
|
2016-05-30 00:52:00 +10:00
|
|
|
answer = True
|
2018-02-11 03:59:20 +11:00
|
|
|
chk = PF.check_connection('plex.tv', token=self.plex_token)
|
2016-05-25 04:43:52 +10:00
|
|
|
if chk in (401, 403):
|
|
|
|
# HTTP Error: unauthorized. Token is no longer valid
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.info('plex.tv connection returned HTTP %s', str(chk))
|
2016-05-25 04:43:52 +10:00
|
|
|
# Delete token in the settings
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('plexToken', value='')
|
|
|
|
utils.settings('plexLogin', value='')
|
2016-05-25 04:43:52 +10:00
|
|
|
# Could not login, please try again
|
2018-09-16 00:53:06 +10:00
|
|
|
utils.messageDialog(utils.lang(29999), utils.lang(39009))
|
2018-02-11 03:59:20 +11:00
|
|
|
answer = self.plex_tv_sign_in()
|
2016-05-25 04:43:52 +10:00
|
|
|
elif chk is False or chk >= 400:
|
|
|
|
# Problems connecting to plex.tv. Network or internet issue?
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.info('Problems connecting to plex.tv; connection returned '
|
|
|
|
'HTTP %s', str(chk))
|
2018-09-16 00:53:06 +10:00
|
|
|
utils.messageDialog(utils.lang(29999), utils.lang(39010))
|
2016-05-30 00:52:00 +10:00
|
|
|
answer = False
|
2016-05-25 04:43:52 +10:00
|
|
|
else:
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.info('plex.tv connection with token successful')
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('plex_status', value=utils.lang(39227))
|
2016-05-25 04:43:52 +10:00
|
|
|
# Refresh the info from Plex.tv
|
2018-01-08 01:20:25 +11:00
|
|
|
xml = DU().downloadUrl('https://plex.tv/users/account',
|
|
|
|
authenticate=False,
|
2018-02-11 03:59:20 +11:00
|
|
|
headerOptions={'X-Plex-Token': self.plex_token})
|
2016-05-25 04:43:52 +10:00
|
|
|
try:
|
2018-02-11 03:59:20 +11:00
|
|
|
self.plex_login = xml.attrib['title']
|
2016-05-25 04:43:52 +10:00
|
|
|
except (AttributeError, KeyError):
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.error('Failed to update Plex info from plex.tv')
|
2016-05-25 04:43:52 +10:00
|
|
|
else:
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('plexLogin', value=self.plex_login)
|
2016-05-25 04:43:52 +10:00
|
|
|
home = 'true' if xml.attrib.get('home') == '1' else 'false'
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('plexAvatar', value=xml.attrib.get('thumb'))
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.info('Updated Plex info from plex.tv')
|
2016-05-30 00:52:00 +10:00
|
|
|
return answer
|
2016-03-04 23:34:30 +11:00
|
|
|
|
2018-02-11 03:59:20 +11:00
|
|
|
def check_existing_pms(self):
|
2016-05-25 04:43:52 +10:00
|
|
|
"""
|
|
|
|
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
|
2015-12-27 22:14:06 +11:00
|
|
|
|
2016-05-25 04:43:52 +10:00
|
|
|
Will also set the PMS machineIdentifier in the file settings if it was
|
|
|
|
not set before
|
|
|
|
"""
|
|
|
|
answer = True
|
2018-02-11 03:59:20 +11:00
|
|
|
chk = PF.check_connection(self.server, verifySSL=False)
|
2016-05-25 04:43:52 +10:00
|
|
|
if chk is False:
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.warn('Could not reach PMS %s', self.server)
|
2016-05-25 04:43:52 +10:00
|
|
|
answer = False
|
|
|
|
if answer is True and not self.serverid:
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.info('No PMS machineIdentifier found for %s. Trying to '
|
|
|
|
'get the PMS unique ID', self.server)
|
2018-02-11 03:59:20 +11:00
|
|
|
self.serverid = PF.GetMachineIdentifier(self.server)
|
2016-05-25 04:43:52 +10:00
|
|
|
if self.serverid is None:
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.warn('Could not retrieve machineIdentifier')
|
2016-05-25 04:43:52 +10:00
|
|
|
answer = False
|
2016-03-10 01:37:27 +11:00
|
|
|
else:
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('plex_machineIdentifier', value=self.serverid)
|
2016-05-25 04:43:52 +10:00
|
|
|
elif answer is True:
|
2018-02-11 03:59:20 +11:00
|
|
|
temp_server_id = PF.GetMachineIdentifier(self.server)
|
|
|
|
if temp_server_id != self.serverid:
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.warn('The current PMS %s was expected to have a '
|
2016-08-31 00:13:13 +10:00
|
|
|
'unique machineIdentifier of %s. But we got '
|
2018-01-07 01:19:12 +11:00
|
|
|
'%s. Pick a new server to be sure',
|
2018-02-11 03:59:20 +11:00
|
|
|
self.server, self.serverid, temp_server_id)
|
2016-05-25 04:43:52 +10:00
|
|
|
answer = False
|
|
|
|
return answer
|
2016-03-11 02:02:46 +11:00
|
|
|
|
2018-02-11 03:59:20 +11:00
|
|
|
@staticmethod
|
|
|
|
def _check_pms_connectivity(server):
|
2016-05-30 00:52:00 +10:00
|
|
|
"""
|
2018-02-11 03:59:20 +11:00
|
|
|
Checks for server's connectivity. Returns check_connection result
|
2016-05-30 00:52:00 +10:00
|
|
|
"""
|
|
|
|
# Re-direct via plex if remote - will lead to the correct SSL
|
|
|
|
# certificate
|
2018-02-11 03:59:20 +11:00
|
|
|
if server['local']:
|
|
|
|
url = ('%s://%s:%s'
|
|
|
|
% (server['scheme'], server['ip'], server['port']))
|
2016-05-30 00:52:00 +10:00
|
|
|
# Deactive SSL verification if the server is local!
|
|
|
|
verifySSL = False
|
|
|
|
else:
|
|
|
|
url = server['baseURL']
|
2017-05-21 02:46:31 +10:00
|
|
|
verifySSL = True
|
2018-02-11 03:59:20 +11:00
|
|
|
chk = PF.check_connection(url,
|
|
|
|
token=server['token'],
|
|
|
|
verifySSL=verifySSL)
|
2016-05-30 00:52:00 +10:00
|
|
|
return chk
|
|
|
|
|
2018-02-11 03:59:20 +11:00
|
|
|
def pick_pms(self, showDialog=False):
|
2016-05-30 00:52:00 +10:00
|
|
|
"""
|
2018-02-11 03:59:20 +11:00
|
|
|
Searches for PMS in local Lan and optionally (if self.plex_token set)
|
2016-05-30 00:52:00 +10:00
|
|
|
also on plex.tv
|
|
|
|
showDialog=True: let the user pick one
|
|
|
|
showDialog=False: automatically pick PMS based on machineIdentifier
|
2015-12-27 22:14:06 +11:00
|
|
|
|
2016-05-25 04:43:52 +10:00
|
|
|
Returns the picked PMS' detail as a dict:
|
|
|
|
{
|
2018-02-11 03:59:20 +11:00
|
|
|
'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
|
2016-05-25 04:43:52 +10:00
|
|
|
}
|
|
|
|
or None if unsuccessful
|
|
|
|
"""
|
2016-05-30 00:52:00 +10:00
|
|
|
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:
|
2018-02-11 03:59:20 +11:00
|
|
|
server = self._user_pick_pms()
|
2016-05-30 00:52:00 +10:00
|
|
|
else:
|
2018-02-11 03:59:20 +11:00
|
|
|
server = self._auto_pick_pms()
|
2016-10-24 04:38:21 +11:00
|
|
|
if server is not None:
|
2018-02-11 03:59:20 +11:00
|
|
|
_write_pms_settings(server['baseURL'], server['token'])
|
2016-05-30 00:52:00 +10:00
|
|
|
return server
|
|
|
|
|
2018-02-11 03:59:20 +11:00
|
|
|
def _auto_pick_pms(self):
|
2016-05-30 00:52:00 +10:00
|
|
|
"""
|
|
|
|
Will try to pick PMS based on machineIdentifier saved in file settings
|
|
|
|
but only once
|
|
|
|
|
|
|
|
Returns server or None if unsuccessful
|
|
|
|
"""
|
2018-02-11 03:59:20 +11:00
|
|
|
https_updated = False
|
2016-05-30 00:52:00 +10:00
|
|
|
server = None
|
2016-01-30 06:07:21 +11:00
|
|
|
while True:
|
2018-02-11 03:59:20 +11:00
|
|
|
if https_updated is False:
|
|
|
|
serverlist = PF.discover_pms(self.plex_token)
|
2016-05-30 00:52:00 +10:00
|
|
|
for item in serverlist:
|
|
|
|
if item.get('machineIdentifier') == self.serverid:
|
|
|
|
server = item
|
|
|
|
if server is None:
|
2018-06-22 03:24:37 +10:00
|
|
|
name = utils.settings('plex_servername')
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.warn('The PMS you have used before with a unique '
|
2016-08-31 00:13:13 +10:00
|
|
|
'machineIdentifier of %s and name %s is '
|
2018-01-07 01:19:12 +11:00
|
|
|
'offline', self.serverid, name)
|
2016-05-30 00:52:00 +10:00
|
|
|
return
|
2018-02-11 03:59:20 +11:00
|
|
|
chk = self._check_pms_connectivity(server)
|
|
|
|
if chk == 504 and https_updated is False:
|
|
|
|
# switch HTTPS to HTTP or vice-versa
|
|
|
|
if server['scheme'] == 'https':
|
|
|
|
server['scheme'] = 'http'
|
|
|
|
else:
|
|
|
|
server['scheme'] = 'https'
|
|
|
|
https_updated = True
|
2016-05-30 00:52:00 +10:00
|
|
|
continue
|
|
|
|
# Problems connecting
|
|
|
|
elif chk >= 400 or chk is False:
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.warn('Problems connecting to server %s. chk is %s',
|
|
|
|
server['name'], chk)
|
2016-05-30 00:52:00 +10:00
|
|
|
return
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.info('We found a server to automatically connect to: %s',
|
|
|
|
server['name'])
|
2016-05-30 00:52:00 +10:00
|
|
|
return server
|
|
|
|
|
2018-02-11 03:59:20 +11:00
|
|
|
def _user_pick_pms(self):
|
2016-05-30 00:52:00 +10:00
|
|
|
"""
|
|
|
|
Lets user pick his/her PMS from a list
|
|
|
|
|
|
|
|
Returns server or None if unsuccessful
|
|
|
|
"""
|
2018-02-11 03:59:20 +11:00
|
|
|
https_updated = False
|
2018-02-11 04:07:10 +11:00
|
|
|
# Searching for PMS
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.dialog('notification',
|
|
|
|
heading='{plex}',
|
|
|
|
message=utils.lang(30001),
|
|
|
|
icon='{plex}',
|
|
|
|
time=5000)
|
2016-05-30 00:52:00 +10:00
|
|
|
while True:
|
2018-02-11 03:59:20 +11:00
|
|
|
if https_updated is False:
|
|
|
|
serverlist = PF.discover_pms(self.plex_token)
|
2016-03-08 03:11:54 +11:00
|
|
|
# Exit if no servers found
|
2018-02-11 03:59:20 +11:00
|
|
|
if not serverlist:
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.warn('No plex media servers found!')
|
2018-09-16 00:53:06 +10:00
|
|
|
utils.messageDialog(utils.lang(29999), utils.lang(39011))
|
2016-05-30 00:52:00 +10:00
|
|
|
return
|
2016-05-25 04:43:52 +10:00
|
|
|
# Get a nicer list
|
|
|
|
dialoglist = []
|
2016-03-08 03:11:54 +11:00
|
|
|
for server in serverlist:
|
2018-02-11 03:59:20 +11:00
|
|
|
if server['local']:
|
2016-05-30 00:52:00 +10:00
|
|
|
# server is in the same network as client.
|
|
|
|
# Add"local"
|
2018-06-22 03:24:37 +10:00
|
|
|
msg = utils.lang(39022)
|
2016-04-07 00:24:03 +10:00
|
|
|
else:
|
|
|
|
# Add 'remote'
|
2018-06-22 03:24:37 +10:00
|
|
|
msg = utils.lang(39054)
|
2016-04-07 00:24:03 +10:00
|
|
|
if server.get('ownername'):
|
|
|
|
# Display username if its not our PMS
|
|
|
|
dialoglist.append('%s (%s, %s)'
|
|
|
|
% (server['name'],
|
|
|
|
server['ownername'],
|
|
|
|
msg))
|
2016-03-08 03:11:54 +11:00
|
|
|
else:
|
2016-04-07 00:24:03 +10:00
|
|
|
dialoglist.append('%s (%s)'
|
2016-05-25 04:43:52 +10:00
|
|
|
% (server['name'], msg))
|
|
|
|
# Let user pick server from a list
|
2018-06-22 03:24:37 +10:00
|
|
|
resp = utils.dialog('select', utils.lang(39012), dialoglist)
|
2017-02-01 19:35:39 +11:00
|
|
|
if resp == -1:
|
|
|
|
# User cancelled
|
|
|
|
return
|
2016-05-30 00:52:00 +10:00
|
|
|
|
2015-12-27 22:14:06 +11:00
|
|
|
server = serverlist[resp]
|
2018-02-11 03:59:20 +11:00
|
|
|
chk = self._check_pms_connectivity(server)
|
|
|
|
if chk == 504 and https_updated is False:
|
2016-03-08 03:11:54 +11:00
|
|
|
# Not able to use HTTP, try HTTPs for now
|
|
|
|
serverlist[resp]['scheme'] = 'https'
|
2018-02-11 03:59:20 +11:00
|
|
|
https_updated = True
|
2016-03-08 03:11:54 +11:00
|
|
|
continue
|
2018-02-11 03:59:20 +11:00
|
|
|
https_updated = False
|
2015-12-27 22:14:06 +11:00
|
|
|
if chk == 401:
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.warn('Not yet authorized for Plex server %s',
|
|
|
|
server['name'])
|
2018-09-16 00:53:06 +10:00
|
|
|
# Not yet authorized for Plex server %s
|
|
|
|
utils.messageDialog(
|
|
|
|
utils.lang(29999),
|
|
|
|
'%s %s\n%s' % (utils.lang(39013),
|
|
|
|
server['name'].decode('utf-8'),
|
|
|
|
utils.lang(39014)))
|
2018-02-11 03:59:20 +11:00
|
|
|
if self.plex_tv_sign_in() is False:
|
2016-01-14 20:20:19 +11:00
|
|
|
# Exit while loop if user cancels
|
2016-05-30 00:52:00 +10:00
|
|
|
return
|
2015-12-27 22:14:06 +11:00
|
|
|
# Problems connecting
|
2016-01-15 00:47:34 +11:00
|
|
|
elif chk >= 400 or chk is False:
|
2016-03-04 23:34:30 +11:00
|
|
|
# Problems connecting to server. Pick another server?
|
2018-09-16 00:53:06 +10:00
|
|
|
if not utils.yesno_dialog(utils.lang(29999), utils.lang(39015)):
|
|
|
|
# Exit while loop if user chooses No
|
2016-05-30 00:52:00 +10:00
|
|
|
return
|
2015-12-27 22:14:06 +11:00
|
|
|
# Otherwise: connection worked!
|
|
|
|
else:
|
2016-05-30 00:52:00 +10:00
|
|
|
return server
|
2016-05-25 04:43:52 +10:00
|
|
|
|
2018-02-11 03:59:20 +11:00
|
|
|
@staticmethod
|
|
|
|
def write_pms_to_settings(server):
|
2016-05-25 04:43:52 +10:00
|
|
|
"""
|
2018-02-11 03:59:20 +11:00
|
|
|
Saves server to file settings
|
2016-05-25 04:43:52 +10:00
|
|
|
"""
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('plex_machineIdentifier', server['machineIdentifier'])
|
|
|
|
utils.settings('plex_servername', server['name'])
|
|
|
|
utils.settings('plex_serverowned',
|
|
|
|
'true' if server['owned'] else 'false')
|
2016-05-25 04:43:52 +10:00
|
|
|
# Careful to distinguish local from remote PMS
|
2018-02-11 03:59:20 +11:00
|
|
|
if server['local']:
|
2016-03-09 00:50:43 +11:00
|
|
|
scheme = server['scheme']
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('ipaddress', server['ip'])
|
|
|
|
utils.settings('port', server['port'])
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.debug("Setting SSL verify to false, because server is "
|
2016-08-31 00:13:13 +10:00
|
|
|
"local")
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('sslverify', 'false')
|
2016-03-09 00:50:43 +11:00
|
|
|
else:
|
|
|
|
baseURL = server['baseURL'].split(':')
|
|
|
|
scheme = baseURL[0]
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('ipaddress', baseURL[1].replace('//', ''))
|
|
|
|
utils.settings('port', baseURL[2])
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.debug("Setting SSL verify to true, because server is not "
|
2016-08-31 00:13:13 +10:00
|
|
|
"local")
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('sslverify', 'true')
|
2016-03-09 00:50:43 +11:00
|
|
|
|
|
|
|
if scheme == 'https':
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('https', 'true')
|
2015-12-25 06:51:47 +11:00
|
|
|
else:
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('https', 'false')
|
2016-05-25 04:43:52 +10:00
|
|
|
# And finally do some logging
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.debug("Writing to Kodi user settings file")
|
|
|
|
LOG.debug("PMS machineIdentifier: %s, ip: %s, port: %s, https: %s ",
|
|
|
|
server['machineIdentifier'], server['ip'], server['port'],
|
|
|
|
server['scheme'])
|
2016-05-25 04:43:52 +10:00
|
|
|
|
2016-05-30 00:52:00 +10:00
|
|
|
def setup(self):
|
2016-05-25 04:43:52 +10:00
|
|
|
"""
|
|
|
|
Initial setup. Run once upon startup.
|
2015-12-25 06:51:47 +11:00
|
|
|
|
2016-05-25 04:43:52 +10:00
|
|
|
Check server, user, direct paths, music, direct stream if not direct
|
|
|
|
path.
|
|
|
|
"""
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.info("Initial setup called.")
|
2018-01-08 01:16:53 +11:00
|
|
|
try:
|
2018-06-22 03:24:37 +10:00
|
|
|
with utils.XmlKodiSetting('advancedsettings.xml',
|
|
|
|
force_create=True,
|
|
|
|
top_element='advancedsettings') as xml:
|
2018-01-08 01:16:53 +11:00
|
|
|
# Get current Kodi video cache setting
|
|
|
|
cache = xml.get_setting(['cache', 'memorysize'])
|
|
|
|
# Disable foreground "Loading media information from files"
|
|
|
|
# (still used by Kodi, even though the Wiki says otherwise)
|
|
|
|
xml.set_setting(['musiclibrary', 'backgroundupdate'],
|
|
|
|
value='true')
|
|
|
|
# Disable cleaning of library - not compatible with PKC
|
|
|
|
xml.set_setting(['videolibrary', 'cleanonupdate'],
|
|
|
|
value='false')
|
2018-01-26 03:15:38 +11:00
|
|
|
# Set completely watched point same as plex (and not 92%)
|
|
|
|
xml.set_setting(['video', 'ignorepercentatend'], value='10')
|
|
|
|
xml.set_setting(['video', 'playcountminimumpercent'],
|
|
|
|
value='90')
|
2018-02-05 02:58:10 +11:00
|
|
|
xml.set_setting(['video', 'ignoresecondsatstart'],
|
|
|
|
value='60')
|
2018-01-08 01:16:53 +11:00
|
|
|
reboot = xml.write_xml
|
2018-01-08 01:44:20 +11:00
|
|
|
except etree.ParseError:
|
2018-01-08 01:16:53 +11:00
|
|
|
cache = None
|
|
|
|
reboot = False
|
2017-12-29 01:24:36 +11:00
|
|
|
# Kodi default cache if no setting is set
|
|
|
|
cache = str(cache.text) if cache is not None else '20971520'
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.info('Current Kodi video memory cache in bytes: %s', cache)
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('kodi_video_cache', value=cache)
|
2018-01-08 01:16:53 +11:00
|
|
|
|
2018-01-08 01:44:20 +11:00
|
|
|
# Hack to make PKC Kodi master lock compatible
|
|
|
|
try:
|
2018-06-22 03:24:37 +10:00
|
|
|
with utils.XmlKodiSetting('sources.xml',
|
|
|
|
force_create=True,
|
|
|
|
top_element='sources') as xml:
|
2018-01-08 01:44:20 +11:00
|
|
|
root = xml.set_setting(['video'])
|
|
|
|
count = 2
|
|
|
|
for source in root.findall('.//path'):
|
|
|
|
if source.text == "smb://":
|
|
|
|
count -= 1
|
|
|
|
if count == 0:
|
|
|
|
# sources already set
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
# Missing smb:// occurences, re-add.
|
|
|
|
for _ in range(0, count):
|
|
|
|
source = etree.SubElement(root, 'source')
|
2018-02-11 03:59:20 +11:00
|
|
|
etree.SubElement(
|
|
|
|
source,
|
|
|
|
'name').text = "PlexKodiConnect Masterlock Hack"
|
|
|
|
etree.SubElement(
|
|
|
|
source,
|
|
|
|
'path',
|
|
|
|
attrib={'pathversion': "1"}).text = "smb://"
|
2018-01-08 01:44:20 +11:00
|
|
|
etree.SubElement(source, 'allowsharing').text = "true"
|
|
|
|
if reboot is False:
|
|
|
|
reboot = xml.write_xml
|
|
|
|
except etree.ParseError:
|
|
|
|
pass
|
|
|
|
|
2017-05-30 01:29:29 +10:00
|
|
|
# Do we need to migrate stuff?
|
2018-06-22 03:24:37 +10:00
|
|
|
migration.check_migration()
|
2018-05-16 03:33:17 +10:00
|
|
|
# Reload the server IP cause we might've deleted it during migration
|
2018-06-22 03:24:37 +10:00
|
|
|
self.server = userclient.UserClient().get_server()
|
2016-05-25 04:43:52 +10:00
|
|
|
|
2018-04-03 00:29:56 +10:00
|
|
|
# Display a warning if Kodi puts ALL movies into the queue, basically
|
|
|
|
# breaking playback reporting for PKC
|
|
|
|
if js.settings_getsettingvalue('videoplayer.autoplaynextitem'):
|
|
|
|
LOG.warn('Kodi setting videoplayer.autoplaynextitem is enabled!')
|
2018-06-22 03:24:37 +10:00
|
|
|
if utils.settings('warned_setting_videoplayer.autoplaynextitem') == 'false':
|
2018-04-03 00:29:56 +10:00
|
|
|
# Only warn once
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('warned_setting_videoplayer.autoplaynextitem',
|
|
|
|
value='true')
|
2018-04-03 00:29:56 +10:00
|
|
|
# Warning: Kodi setting "Play next video automatically" is
|
|
|
|
# enabled. This could break PKC. Deactivate?
|
2018-09-16 00:53:06 +10:00
|
|
|
if utils.yesno_dialog(utils.lang(29999), utils.lang(30003)):
|
2018-04-03 00:29:56 +10:00
|
|
|
js.settings_setsettingvalue('videoplayer.autoplaynextitem',
|
|
|
|
False)
|
2018-04-03 20:43:59 +10:00
|
|
|
# Set any video library updates to happen in the background in order to
|
|
|
|
# hide "Compressing database"
|
|
|
|
js.settings_setsettingvalue('videolibrary.backgroundupdate', True)
|
2018-04-03 00:29:56 +10:00
|
|
|
|
2016-05-25 04:43:52 +10:00
|
|
|
# If a Plex server IP has already been set
|
|
|
|
# return only if the right machine identifier is found
|
|
|
|
if self.server:
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.info("PMS is already set: %s. Checking now...", self.server)
|
2018-02-11 03:59:20 +11:00
|
|
|
if self.check_existing_pms():
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.info("Using PMS %s with machineIdentifier %s",
|
|
|
|
self.server, self.serverid)
|
2018-02-11 03:59:20 +11:00
|
|
|
_write_pms_settings(self.server, self.pms_token)
|
2018-01-08 01:16:53 +11:00
|
|
|
if reboot is True:
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.reboot_kodi()
|
2016-05-25 04:43:52 +10:00
|
|
|
return
|
|
|
|
|
|
|
|
# If not already retrieved myplex info, optionally let user sign in
|
|
|
|
# to plex.tv. This DOES get called on very first install run
|
2018-02-11 03:59:20 +11:00
|
|
|
if not self.plex_token and self.myplexlogin:
|
|
|
|
self.plex_tv_sign_in()
|
2016-05-25 04:43:52 +10:00
|
|
|
|
2018-02-11 03:59:20 +11:00
|
|
|
server = self.pick_pms()
|
2016-05-30 00:52:00 +10:00
|
|
|
if server is not None:
|
|
|
|
# Write our chosen server to Kodi settings file
|
2018-02-11 03:59:20 +11:00
|
|
|
self.write_pms_to_settings(server)
|
2016-03-04 23:34:30 +11:00
|
|
|
|
2016-05-30 00:52:00 +10:00
|
|
|
# User already answered the installation questions
|
2018-06-22 03:24:37 +10:00
|
|
|
if utils.settings('InstallQuestionsAnswered') == 'true':
|
2018-01-08 01:16:53 +11:00
|
|
|
if reboot is True:
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.reboot_kodi()
|
2016-05-30 00:52:00 +10:00
|
|
|
return
|
2016-05-25 04:43:52 +10:00
|
|
|
|
|
|
|
# Additional settings where the user needs to choose
|
2016-03-16 06:26:45 +11:00
|
|
|
# Direct paths (\\NAS\mymovie.mkv) or addon (http)?
|
2018-02-11 03:59:20 +11:00
|
|
|
goto_settings = False
|
2018-09-16 00:53:06 +10:00
|
|
|
from .windows import optionsdialog
|
|
|
|
# Use Add-on Paths (default, easy) or Direct Paths? PKC will not work
|
|
|
|
# if your Direct Paths setup is wrong!
|
|
|
|
# Buttons: Add-on Paths // Direct Paths
|
|
|
|
if optionsdialog.show(utils.lang(29999), utils.lang(39080),
|
|
|
|
utils.lang(39081), utils.lang(39082)) == 1:
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.debug("User opted to use direct paths.")
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('useDirectPaths', value="1")
|
2017-05-17 23:42:12 +10:00
|
|
|
state.DIRECT_PATHS = True
|
2016-03-16 06:26:45 +11:00
|
|
|
# Are you on a system where you would like to replace paths
|
|
|
|
# \\NAS\mymovie.mkv with smb://NAS/mymovie.mkv? (e.g. Windows)
|
2018-09-16 00:53:06 +10:00
|
|
|
if utils.yesno_dialog(utils.lang(29999), utils.lang(39033)):
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.debug("User chose to replace paths with smb")
|
2016-03-16 22:26:31 +11:00
|
|
|
else:
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('replaceSMB', value="false")
|
2016-03-16 22:26:31 +11:00
|
|
|
|
|
|
|
# complete replace all original Plex library paths with custom SMB
|
2018-09-16 00:53:06 +10:00
|
|
|
if utils.yesno_dialog(utils.lang(29999), utils.lang(39043)):
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.debug("User chose custom smb paths")
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('remapSMB', value="true")
|
2016-03-16 22:26:31 +11:00
|
|
|
# Please enter your custom smb paths in the settings under
|
|
|
|
# "Sync Options" and then restart Kodi
|
2018-09-16 00:53:06 +10:00
|
|
|
utils.messageDialog(utils.lang(29999), utils.lang(39044))
|
2018-02-11 03:59:20 +11:00
|
|
|
goto_settings = True
|
2016-03-16 06:26:45 +11:00
|
|
|
|
|
|
|
# Go to network credentials?
|
2018-09-16 00:53:06 +10:00
|
|
|
if utils.yesno_dialog(utils.lang(39029), utils.lang(39030)):
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.debug("Presenting network credentials dialog.")
|
2018-07-17 00:06:20 +10:00
|
|
|
utils.passwords_xml()
|
2016-03-08 23:00:03 +11:00
|
|
|
# Disable Plex music?
|
2018-09-16 00:53:06 +10:00
|
|
|
if utils.yesno_dialog(utils.lang(29999), utils.lang(39016)):
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.debug("User opted to disable Plex music library.")
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('enableMusic', value="false")
|
2016-03-04 00:00:48 +11:00
|
|
|
|
2016-04-29 20:44:56 +10:00
|
|
|
# Download additional art from FanArtTV
|
2018-09-16 00:53:06 +10:00
|
|
|
if utils.yesno_dialog(utils.lang(29999), utils.lang(39061)):
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.debug("User opted to use FanArtTV")
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('FanartTV', value="true")
|
2017-05-31 22:13:45 +10:00
|
|
|
# Do you want to replace your custom user ratings with an indicator of
|
|
|
|
# how many versions of a media item you posses?
|
2018-09-16 00:53:06 +10:00
|
|
|
if utils.yesno_dialog(utils.lang(29999), utils.lang(39718)):
|
2018-01-07 01:19:12 +11:00
|
|
|
LOG.debug("User opted to replace user ratings with version number")
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('indicate_media_versions', value="true")
|
2016-04-29 20:44:56 +10:00
|
|
|
|
2016-12-21 02:13:19 +11:00
|
|
|
# If you use several Plex libraries of one kind, e.g. "Kids Movies" and
|
|
|
|
# "Parents Movies", be sure to check https://goo.gl/JFtQV9
|
2018-06-22 03:24:37 +10:00
|
|
|
# dialog.ok(heading=utils.lang(29999), line1=utils.lang(39076))
|
2017-05-31 22:13:45 +10:00
|
|
|
|
2017-05-30 00:33:44 +10:00
|
|
|
# Need to tell about our image source for collections: themoviedb.org
|
2018-06-22 03:24:37 +10:00
|
|
|
# dialog.ok(heading=utils.lang(29999), line1=utils.lang(39717))
|
2016-05-30 00:52:00 +10:00
|
|
|
# Make sure that we only ask these questions upon first installation
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.settings('InstallQuestionsAnswered', value='true')
|
2018-10-11 04:19:30 +11:00
|
|
|
|
2018-10-06 23:47:41 +10:00
|
|
|
# New installation - make sure we start with a clean slate
|
|
|
|
from . import kodidb_functions
|
2018-10-11 04:18:06 +11:00
|
|
|
kodidb_functions.wipe_dbs()
|
2016-05-30 00:52:00 +10:00
|
|
|
|
2018-02-11 03:59:20 +11:00
|
|
|
if goto_settings is False:
|
2016-04-22 20:34:35 +10:00
|
|
|
# Open Settings page now? You will need to restart!
|
2018-09-16 00:53:06 +10:00
|
|
|
goto_settings = utils.yesno_dialog(utils.lang(29999),
|
|
|
|
utils.lang(39017))
|
2018-02-11 03:59:20 +11:00
|
|
|
if goto_settings:
|
2017-05-17 18:09:50 +10:00
|
|
|
state.PMS_STATUS = 'Stop'
|
2018-06-22 03:24:37 +10:00
|
|
|
executebuiltin(
|
2018-09-16 21:35:07 +10:00
|
|
|
'Addon.OpenSettings(plugin.video.plexkodiconnect)')
|
2018-01-08 01:16:53 +11:00
|
|
|
elif reboot is True:
|
2018-06-22 03:24:37 +10:00
|
|
|
utils.reboot_kodi()
|