Merge pull request #695 from croneter/beta-version

Bump stable
This commit is contained in:
croneter 2019-02-03 21:04:52 +01:00 committed by GitHub
commit 85f73126ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 316 additions and 158 deletions

View file

@ -1,5 +1,5 @@
[![stable version](https://img.shields.io/badge/stable_version-2.6.3-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip) [![stable version](https://img.shields.io/badge/stable_version-2.6.4-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip)
[![beta version](https://img.shields.io/badge/beta_version-2.6.3-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) [![beta version](https://img.shields.io/badge/beta_version-2.6.4-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip)
[![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation) [![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation)
[![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq) [![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq)

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.6.3" provider-name="croneter"> <addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.6.4" provider-name="croneter">
<requires> <requires>
<import addon="xbmc.python" version="2.1.0"/> <import addon="xbmc.python" version="2.1.0"/>
<import addon="script.module.requests" version="2.9.1" /> <import addon="script.module.requests" version="2.9.1" />
@ -77,7 +77,16 @@
<summary lang="uk_UA">Нативна інтеграція Plex в Kodi</summary> <summary lang="uk_UA">Нативна інтеграція Plex в Kodi</summary>
<description lang="uk_UA">Підключає Kodi до серверу Plex. Цей плагін передбачає, що ви керуєте всіма своїми відео за допомогою Plex (і ніяк не Kodi). Ви можете втратити дані, які вже зберігаються у відео та музичних БД Kodi (оскільки цей плагін безпосередньо їх змінює). Використовуйте на свій страх і ризик!</description> <description lang="uk_UA">Підключає Kodi до серверу Plex. Цей плагін передбачає, що ви керуєте всіма своїми відео за допомогою Plex (і ніяк не Kodi). Ви можете втратити дані, які вже зберігаються у відео та музичних БД Kodi (оскільки цей плагін безпосередньо їх змінює). Використовуйте на свій страх і ризик!</description>
<disclaimer lang="uk_UA">Використовуйте на свій ризик</disclaimer> <disclaimer lang="uk_UA">Використовуйте на свій ризик</disclaimer>
<news>version 2.6.3: <news>version 2.6.4:
- Fix music items getting deleted on startup
- Never ignore SSL certificate errors for Kodi >= 18 - just like Kodi
- Fix playback not starting at the beginning
- Improve dialog to manually enter PMS IP and port
- Show logged in Plex home user in the settings and allow changing it
- Update German strings
- Implement Codacy suggestions
version 2.6.3:
- Fix PKC crashing on Xbox - Fix PKC crashing on Xbox
version 2.6.2: version 2.6.2:

View file

@ -1,3 +1,12 @@
version 2.6.4:
- Fix music items getting deleted on startup
- Never ignore SSL certificate errors for Kodi >= 18 - just like Kodi
- Fix playback not starting at the beginning
- Improve dialog to manually enter PMS IP and port
- Show logged in Plex home user in the settings and allow changing it
- Update German strings
- Implement Codacy suggestions
version 2.6.3: version 2.6.3:
- Fix PKC crashing on Xbox - Fix PKC crashing on Xbox

View file

@ -493,6 +493,13 @@ msgctxt "#30502"
msgid "Sync Plex artwork from the PMS (recommended)" msgid "Sync Plex artwork from the PMS (recommended)"
msgstr "Plex Bilder vom Plex Medienserver synchronisieren (empfohlen)" msgstr "Plex Bilder vom Plex Medienserver synchronisieren (empfohlen)"
# Message shown if SSL HTTPS certificate fails
msgctxt "#30503"
msgid "SSL certificate failed to validate. Please check {0} for solutions."
msgstr ""
"SSL-Zertifikat konnte nicht validiert werden. Bitte besuche {0} für "
"Lösungsvorschläge."
# PKC Settings, category name # PKC Settings, category name
msgctxt "#30506" msgctxt "#30506"
msgid "Sync Options" msgid "Sync Options"
@ -1193,6 +1200,16 @@ msgctxt "#39082"
msgid "Direct Paths" msgid "Direct Paths"
msgstr "Direct Paths" msgstr "Direct Paths"
# Dialog for manually entering PMS
msgctxt "#39083"
msgid "Enter PMS IP or URL"
msgstr "PMS IP oder URL eingeben"
# Dialog for manually entering PMS
msgctxt "#39084"
msgid "Enter PMS port"
msgstr "PMS Port eingeben"
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Plex Home Benutzer abmelden: " msgstr "Plex Home Benutzer abmelden: "
@ -1254,11 +1271,11 @@ msgstr "Plex Media Server IP oder URL eingeben. Zum Beispiel:"
msgctxt "#39217" msgctxt "#39217"
msgid "" msgid ""
"Does your Plex Media Server support SSL connections? (https instead of " "Use HTTPS (SSL) connections? With Kodi 18 or later, HTTPS will likely not "
"http)?" "work!"
msgstr "" msgstr ""
"Unterstützt der Plex Media Server sichere SSL Verbindungen (https anstelle " "HTTPS (SSL) Verbindungen nutzen? Dies funktioniert u.U. nicht mit Kodi 18 "
"von http)?" "oder späteren Versionen!"
msgctxt "#39218" msgctxt "#39218"
msgid "Error contacting PMS" msgid "Error contacting PMS"
@ -1308,11 +1325,10 @@ msgctxt "#39227"
msgid "Logged in to plex.tv" msgid "Logged in to plex.tv"
msgstr "Eingeloggt bei plex.tv" msgstr "Eingeloggt bei plex.tv"
# Message in the PKC settings to display the plex.tv username. Leave the colon # Message in the PKC settings to display the plex.tv username
# :
msgctxt "#39228" msgctxt "#39228"
msgid "Plex user:" msgid "Plex admin user"
msgstr "Plex Benutzer:" msgstr "Plex Admin Benutzer"
# Error message if user could not log in; the actual user name will be # Error message if user could not log in; the actual user name will be
# appended at the end of the string # appended at the end of the string
@ -1320,6 +1336,16 @@ msgctxt "#39229"
msgid "Login failed with plex.tv for user" msgid "Login failed with plex.tv for user"
msgstr "Login für plex.tv fehlgeschlagen für Benutzer" msgstr "Login für plex.tv fehlgeschlagen für Benutzer"
# Message in the PKC settings to display the plex.tv username
msgctxt "#39230"
msgid "Logged in Plex home user"
msgstr "Eingeloggter Plex Home Benutzer"
# Message in the PKC settings to change the logged in Plex home user
msgctxt "#39231"
msgid "Change logged in Plex home user"
msgstr "Eingeloggten Plex Home Benutzer wechseln"
msgctxt "#39250" msgctxt "#39250"
msgid "" msgid ""
"Running the image cache process can take some time. It will happen in the " "Running the image cache process can take some time. It will happen in the "

View file

@ -464,6 +464,11 @@ msgctxt "#30502"
msgid "Sync Plex artwork from the PMS (recommended)" msgid "Sync Plex artwork from the PMS (recommended)"
msgstr "" msgstr ""
# Message shown if SSL HTTPS certificate fails
msgctxt "#30503"
msgid "SSL certificate failed to validate. Please check {0} for solutions."
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#30506" msgctxt "#30506"
msgid "Sync Options" msgid "Sync Options"
@ -1081,6 +1086,16 @@ msgctxt "#39082"
msgid "Direct Paths" msgid "Direct Paths"
msgstr "" msgstr ""
# Dialog for manually entering PMS
msgctxt "#39083"
msgid "Enter PMS IP or URL"
msgstr ""
# Dialog for manually entering PMS
msgctxt "#39084"
msgid "Enter PMS port"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "" msgstr ""
@ -1133,7 +1148,7 @@ msgid "Enter your Plex Media Server's IP or URL, Examples are:"
msgstr "" msgstr ""
msgctxt "#39217" msgctxt "#39217"
msgid "Does your Plex Media Server support SSL connections? (https instead of http)?" msgid "Use HTTPS (SSL) connections? With Kodi 18 or later, HTTPS will likely not work!"
msgstr "" msgstr ""
msgctxt "#39218" msgctxt "#39218"
@ -1179,9 +1194,9 @@ msgctxt "#39227"
msgid "Logged in to plex.tv" msgid "Logged in to plex.tv"
msgstr "" msgstr ""
# Message in the PKC settings to display the plex.tv username. Leave the colon : # Message in the PKC settings to display the plex.tv username
msgctxt "#39228" msgctxt "#39228"
msgid "Plex user:" msgid "Plex admin user"
msgstr "" msgstr ""
# Error message if user could not log in; the actual user name will be appended at the end of the string # Error message if user could not log in; the actual user name will be appended at the end of the string
@ -1189,6 +1204,16 @@ msgctxt "#39229"
msgid "Login failed with plex.tv for user" msgid "Login failed with plex.tv for user"
msgstr "" msgstr ""
# Message in the PKC settings to display the plex.tv username
msgctxt "#39230"
msgid "Logged in Plex home user"
msgstr ""
# Message in the PKC settings to change the logged in Plex home user
msgctxt "#39231"
msgid "Change logged in Plex home user"
msgstr ""
msgctxt "#39250" msgctxt "#39250"
msgid "Running the image cache process can take some time. It will happen in the background. Are you sure you want continue?" msgid "Running the image cache process can take some time. It will happen in the background. Are you sure you want continue?"
msgstr "" msgstr ""

View file

@ -3,7 +3,7 @@
from __future__ import absolute_import, division, unicode_literals from __future__ import absolute_import, division, unicode_literals
from logging import getLogger from logging import getLogger
from .. import utils, json_rpc as js from .. import utils, json_rpc as js, variables as v
LOG = getLogger('PLEX.connection') LOG = getLogger('PLEX.connection')
@ -38,7 +38,9 @@ class Connection(object):
def load(self): def load(self):
LOG.debug('Loading connection settings') LOG.debug('Loading connection settings')
# Shall we verify SSL certificates? "None" will leave SSL enabled # Shall we verify SSL certificates? "None" will leave SSL enabled
self.verify_ssl_cert = None if utils.settings('sslverify') == 'true' \ # Ignore this setting for Kodi >= 18 as Kodi 18 is much stricter
# with checking SSL certs
self.verify_ssl_cert = None if v.KODIVERSION >= 18 or utils.settings('sslverify') == 'true' \
else False else False
# Do we have an ssl certificate for PKC we need to use? # Do we have an ssl certificate for PKC we need to use?
self.ssl_cert_path = utils.settings('sslcert') \ self.ssl_cert_path = utils.settings('sslcert') \
@ -61,7 +63,7 @@ class Connection(object):
self.server_name, self.machine_identifier, self.server) self.server_name, self.machine_identifier, self.server)
def load_entrypoint(self): def load_entrypoint(self):
self.verify_ssl_cert = None if utils.settings('sslverify') == 'true' \ self.verify_ssl_cert = None if v.KODIVERSION >= 18 or utils.settings('sslverify') == 'true' \
else False else False
self.ssl_cert_path = utils.settings('sslcert') \ self.ssl_cert_path = utils.settings('sslcert') \
if utils.settings('sslcert') != 'None' else None if utils.settings('sslcert') != 'None' else None

View file

@ -62,7 +62,7 @@ class ImageCachingThread(backgroundthread.KillableThread):
LOG.info("---===### Starting ImageCachingThread ###===---") LOG.info("---===### Starting ImageCachingThread ###===---")
try: try:
self._run() self._run()
except: except Exception:
utils.ERROR() utils.ERROR()
finally: finally:
LOG.info("---===### Stopped ImageCachingThread ###===---") LOG.info("---===### Stopped ImageCachingThread ###===---")

View file

@ -131,12 +131,12 @@ class Tasks(list):
class Task(object): class Task(object):
def __init__(self, priority=None): def __init__(self, priority=None):
self._priority = priority self.priority = priority
self._canceled = False self._canceled = False
self.finished = False self.finished = False
def __cmp__(self, other): def __cmp__(self, other):
return self._priority - other._priority return self.priority - other.priority
def start(self): def start(self):
BGThreader.addTask(self) BGThreader.addTask(self)
@ -182,7 +182,7 @@ class MutablePriorityQueue(Queue.PriorityQueue):
self.mutex.acquire() self.mutex.acquire()
try: try:
lowest = self.queue and min(self.queue) or None lowest = self.queue and min(self.queue) or None
except: except Exception:
lowest = None lowest = None
utils.ERROR() utils.ERROR()
finally: finally:
@ -203,7 +203,7 @@ class BackgroundWorker(object):
return return
try: try:
task._run() task._run()
except: except Exception:
utils.ERROR() utils.ERROR()
def abort(self): def abort(self):
@ -275,12 +275,12 @@ class BackgroundThreader:
self.name = name self.name = name
self._queue = MutablePriorityQueue() self._queue = MutablePriorityQueue()
self._abort = False self._abort = False
self._priority = -1 self.priority = -1
self.workers = [worker(self._queue, 'queue.{0}:worker.{1}'.format(self.name, x)) for x in range(worker_count)] self.workers = [worker(self._queue, 'queue.{0}:worker.{1}'.format(self.name, x)) for x in range(worker_count)]
def _nextPriority(self): def _nextPriority(self):
self._priority += 1 self.priority += 1
return self._priority return self.priority
def abort(self): def abort(self):
self._abort = True self._abort = True
@ -298,13 +298,13 @@ class BackgroundThreader:
w.shutdown() w.shutdown()
def addTask(self, task): def addTask(self, task):
task._priority = self._nextPriority() task.priority = self._nextPriority()
self._queue.put(task) self._queue.put(task)
self.startWorkers() self.startWorkers()
def addTasks(self, tasks): def addTasks(self, tasks):
for t in tasks: for t in tasks:
t._priority = self._nextPriority() t.priority = self._nextPriority()
self._queue.put(t) self._queue.put(t)
self.startWorkers() self.startWorkers()
@ -316,7 +316,7 @@ class BackgroundThreader:
p = lowest - len(tasks) p = lowest - len(tasks)
for t in tasks: for t in tasks:
t._priority = p t.priority = p
self._queue.put(t) self._queue.put(t)
p += 1 p += 1
@ -337,14 +337,14 @@ class BackgroundThreader:
if not lowest: if not lowest:
return None return None
return lowest._priority return lowest.priority
def moveToFront(self, qitem): def moveToFront(self, qitem):
lowest = self.getLowestPrority() lowest = self.getLowestPrority()
if lowest is None: if lowest is None:
return return
qitem._priority = lowest - 1 qitem.priority = lowest - 1
class ThreaderManager: class ThreaderManager:

View file

@ -3,6 +3,7 @@
from __future__ import absolute_import, division, unicode_literals from __future__ import absolute_import, division, unicode_literals
from logging import getLogger from logging import getLogger
import requests import requests
import requests.exceptions as exceptions
from . import utils, clientinfo, app from . import utils, clientinfo, app
@ -39,16 +40,12 @@ class DownloadUtils():
def __init__(self): def __init__(self):
self.__dict__ = self._shared_state self.__dict__ = self._shared_state
def setSSL(self, verifySSL=None, certificate=None): def setSSL(self):
""" """
verifySSL must be 'true' to enable certificate validation
certificate must be path to certificate or 'None' certificate must be path to certificate or 'None'
""" """
if verifySSL is None: verifySSL = app.CONN.verify_ssl_cert
verifySSL = app.CONN.verify_ssl_cert certificate = app.CONN.ssl_cert_path
if certificate is None:
certificate = app.CONN.ssl_cert_path
# Set the session's parameters # Set the session's parameters
self.s.verify = verifySSL self.s.verify = verifySSL
if certificate: if certificate:
@ -84,21 +81,23 @@ class DownloadUtils():
def stopSession(self): def stopSession(self):
try: try:
self.s.close() self.s.close()
except: except Exception:
LOG.info("Requests session already closed") LOG.info("Requests session already closed")
try: try:
del self.s del self.s
except: except AttributeError:
pass pass
LOG.info('Request session stopped') LOG.info('Request session stopped')
def getHeader(self, options=None): @staticmethod
def getHeader(options=None):
header = clientinfo.getXArgsDeviceInfo() header = clientinfo.getXArgsDeviceInfo()
if options is not None: if options is not None:
header.update(options) header.update(options)
return header return header
def _doDownload(self, s, action_type, **kwargs): @staticmethod
def _doDownload(s, action_type, **kwargs):
if action_type == "GET": if action_type == "GET":
r = s.get(**kwargs) r = s.get(**kwargs)
elif action_type == "POST": elif action_type == "POST":
@ -114,7 +113,7 @@ class DownloadUtils():
def downloadUrl(self, url, action_type="GET", postBody=None, def downloadUrl(self, url, action_type="GET", postBody=None,
parameters=None, authenticate=True, headerOptions=None, parameters=None, authenticate=True, headerOptions=None,
verifySSL=True, timeout=None, return_response=False, verifySSL=True, timeout=None, return_response=False,
headerOverride=None): headerOverride=None, reraise=False):
""" """
Override SSL check with verifySSL=False Override SSL check with verifySSL=False
@ -172,39 +171,55 @@ class DownloadUtils():
r = self._doDownload(s, action_type, **kwargs) r = self._doDownload(s, action_type, **kwargs)
# THE EXCEPTIONS # THE EXCEPTIONS
except requests.exceptions.SSLError as e: except exceptions.SSLError as e:
LOG.warn("Invalid SSL certificate for: %s", url) LOG.warn("Invalid SSL certificate for: %s", url)
LOG.warn(e) LOG.warn(e)
if reraise:
raise
except requests.exceptions.ConnectionError as e: except exceptions.ConnectionError as e:
# Connection error # Connection error
LOG.warn("Server unreachable at: %s", url) LOG.warn("Server unreachable at: %s", url)
LOG.warn(e) LOG.warn(e)
if reraise:
raise
except requests.exceptions.Timeout as e: except exceptions.Timeout as e:
LOG.warn("Server timeout at: %s", url) LOG.warn("Server timeout at: %s", url)
LOG.warn(e) LOG.warn(e)
if reraise:
raise
except requests.exceptions.HTTPError as e: except exceptions.HTTPError as e:
LOG.warn('HTTP Error at %s', url) LOG.warn('HTTP Error at %s', url)
LOG.warn(e) LOG.warn(e)
if reraise:
raise
except requests.exceptions.TooManyRedirects as e: except exceptions.TooManyRedirects as e:
LOG.warn("Too many redirects connecting to: %s", url) LOG.warn("Too many redirects connecting to: %s", url)
LOG.warn(e) LOG.warn(e)
if reraise:
raise
except requests.exceptions.RequestException as e: except exceptions.RequestException as e:
LOG.warn("Unknown error connecting to: %s", url) LOG.warn("Unknown error connecting to: %s", url)
LOG.warn(e) LOG.warn(e)
if reraise:
raise
except SystemExit: except SystemExit:
LOG.info('SystemExit detected, aborting download') LOG.info('SystemExit detected, aborting download')
self.stopSession() self.stopSession()
if reraise:
raise
except: except Exception:
LOG.warn('Unknown error while downloading. Traceback:') LOG.warn('Unknown error while downloading. Traceback:')
import traceback import traceback
LOG.warn(traceback.format_exc()) LOG.warn(traceback.format_exc())
if reraise:
raise
# THE RESPONSE ##### # THE RESPONSE #####
else: else:
@ -255,7 +270,7 @@ class DownloadUtils():
# xml response # xml response
r = utils.defused_etree.fromstring(r.content) r = utils.defused_etree.fromstring(r.content)
return r return r
except: except Exception:
r.encoding = 'utf-8' r.encoding = 'utf-8'
if r.text == '': if r.text == '':
# Answer does not contain a body # Answer does not contain a body
@ -264,7 +279,7 @@ class DownloadUtils():
# UNICODE - JSON object # UNICODE - JSON object
r = r.json() r = r.json()
return r return r
except: except Exception:
if '200 OK' in r.text: if '200 OK' in r.text:
# Received fucked up OK from PMS on playstate # Received fucked up OK from PMS on playstate
# update # update

View file

@ -9,7 +9,7 @@ from . import utils
from .utils import etree from .utils import etree
from . import path_ops from . import path_ops
from . import migration from . import migration
from .downloadutils import DownloadUtils as DU from .downloadutils import DownloadUtils as DU, exceptions
from . import plex_functions as PF from . import plex_functions as PF
from . import plex_tv from . import plex_tv
from . import json_rpc as js from . import json_rpc as js
@ -70,46 +70,81 @@ class InitialSetup(object):
utils.window('plex_allows_mediaDeletion', value=value) utils.window('plex_allows_mediaDeletion', value=value)
def enter_new_pms_address(self): def enter_new_pms_address(self):
LOG.info('Start getting manual PMS address and port')
# "Enter your Plex Media Server's IP or URL. Examples are:" # "Enter your Plex Media Server's IP or URL. Examples are:"
utils.messageDialog(utils.lang(29999), utils.messageDialog(utils.lang(29999),
'%s\n%s\n%s' % (utils.lang(39215), '%s\n%s\n%s' % (utils.lang(39215),
'192.168.1.2', '192.168.1.2',
'plex.myServer.org')) 'plex.myServer.org'))
address = utils.dialog('input', "Enter PMS IP or URL") # "Enter PMS IP or URL"
if address == '': address = utils.dialog('input', utils.lang(39083))
if not address:
return False return False
port = utils.dialog('input', "Enter PMS port", '32400', type='{numeric}') port = utils.dialog('input', utils.lang(39084), '32400', type='{numeric}')
if port == '': if not port:
return False return False
url = '%s:%s' % (address, port) url = '%s:%s' % (address, port)
# "Does your Plex Media Server support SSL connections? # "Use HTTPS (SSL) connections? With Kodi 18 or later, HTTPS will likely
# (https instead of http)" # not work!"
https = utils.yesno_dialog(utils.lang(29999), utils.lang(39217)) https = utils.yesno_dialog(utils.lang(29999), utils.lang(39217))
if https: if https:
url = 'https://%s' % url url = 'https://%s' % url
else: else:
url = 'http://%s' % url url = 'http://%s' % url
https = 'true' if https else 'false' https = 'true' if https else 'false'
machine_identifier = PF.GetMachineIdentifier(url) # Try to connect first
if machine_identifier is None: error = False
# "Error contacting url try:
# Abort (Yes) or save address anyway (No)" machine_identifier = PF.GetMachineIdentifier(url)
if utils.yesno_dialog(utils.lang(29999), except exceptions.SSLError:
'%s %s. %s' % (utils.lang(39218), LOG.error('SSL cert error contacting %s', url)
url, # "SSL certificate failed to validate. Please check {0}
utils.lang(39219))): # for solutions."
return False utils.messageDialog(utils.lang(29999),
else: utils.lang(30503).format('github.com/croneter/PlexKodiConnect/issues'))
utils.settings('plex_machineIdentifier', '') return
else: except Exception:
utils.settings('plex_machineIdentifier', machine_identifier) error = True
LOG.info('Set new PMS to https %s, address %s, port %s, machineId %s', if error or machine_identifier is None:
https, address, port, machine_identifier) LOG.error('Could not even get a machineIdentifier for %s', url)
utils.settings('https', value=https) # "Server is unreachable"
utils.settings('ipaddress', value=address) utils.messageDialog(utils.lang(29999), utils.lang(33002))
utils.settings('port', value=port) return
# Chances are this is a local PMS, so disable SSL certificate check # Let's use the main account's token, not managed user token
utils.settings('sslverify', value='false') token = utils.settings('plexToken')
xml = PF.pms_root(url, token)
if xml == 401:
LOG.error('Not yet authorized for %s', url)
# "User is unauthorized for server {0}",
# "Please sign in to plex.tv."
utils.messageDialog(utils.lang(29999),
'%s. %s' % (utils.lang(33010).format(address),
utils.lang(39014)))
return
try:
xml[0].attrib
except (IndexError, TypeError, AttributeError):
LOG.error('Could not get PMS root directory for %s', url)
# "Error contacting PMS"
utils.messageDialog(utils.lang(29999), utils.lang(39218))
return
pms = {
'baseURL': url,
'ip': address,
# Assume PMS is not local so we're not resetting verifyssl
'local': False,
'machineIdentifier': xml.get('machineIdentifier'),
'name': xml.get('friendlyName'),
# Assume that we own this PMS - no easy way to check
'owned': True,
'platform': xml.get('platform'),
'port': port,
# 'relay': True,
'scheme': 'https' if https else 'http',
'token': token,
'version': xml.get('version')
}
return pms
def plex_tv_sign_in(self): def plex_tv_sign_in(self):
""" """
@ -177,7 +212,8 @@ class InitialSetup(object):
not set before not set before
""" """
answer = True answer = True
chk = PF.check_connection(app.CONN.server, verifySSL=False) chk = PF.check_connection(app.CONN.server,
verifySSL=True if v.KODIVERSION >= 18 else False)
if chk is False: if chk is False:
LOG.warn('Could not reach PMS %s', app.CONN.server) LOG.warn('Could not reach PMS %s', app.CONN.server)
answer = False answer = False
@ -210,8 +246,8 @@ class InitialSetup(object):
if server['local']: if server['local']:
url = ('%s://%s:%s' url = ('%s://%s:%s'
% (server['scheme'], server['ip'], server['port'])) % (server['scheme'], server['ip'], server['port']))
# Deactive SSL verification if the server is local! # Deactive SSL verification if the server is local for Kodi 17
verifySSL = False verifySSL = True if v.KODIVERSION >= 18 else False
else: else:
url = server['baseURL'] url = server['baseURL']
verifySSL = True verifySSL = True

View file

@ -52,12 +52,9 @@ class MusicMixin(object):
db_item['kodi_type'], db_item['kodi_type'],
userdata['UserRating']) userdata['UserRating'])
if plex_type == v.PLEX_TYPE_SONG: if plex_type == v.PLEX_TYPE_SONG:
self.kodidb.set_resume(db_item['kodi_fileid'], self.kodidb.set_playcount(userdata['PlayCount'],
userdata['Resume'], userdata['LastPlayedDate'],
userdata['Runtime'], db_item['kodi_id'],)
userdata['PlayCount'],
userdata['LastPlayedDate'],
plex_type)
return True return True
def remove(self, plex_id, plex_type=None): def remove(self, plex_id, plex_type=None):

View file

@ -459,6 +459,15 @@ class KodiMusicDB(common.KodiDBBase):
WHERE idSong = ? WHERE idSong = ?
''', (args)) ''', (args))
@common.catch_operationalerrors
def set_playcount(self, *args):
self.cursor.execute('''
UPDATE song
SET iTimesPlayed = ?,
lastplayed = ?
WHERE idSong = ?
''', (args))
@common.catch_operationalerrors @common.catch_operationalerrors
def update_song_17(self, *args): def update_song_17(self, *args):
self.cursor.execute(''' self.cursor.execute('''

View file

@ -694,7 +694,7 @@ class KodiVideoDB(common.KodiDBBase):
SET tag_id = ? SET tag_id = ?
WHERE media_id = ? AND media_type = ? AND tag_id = ? WHERE media_id = ? AND media_type = ? AND tag_id = ?
''', (newtag, kodiid, mediatype, oldtag,)) ''', (newtag, kodiid, mediatype, oldtag,))
except: except Exception:
# The new tag we are going to apply already exists for this item # The new tag we are going to apply already exists for this item
# delete current tag instead # delete current tag instead
self.cursor.execute(''' self.cursor.execute('''

View file

@ -41,7 +41,7 @@ class FanartThread(backgroundthread.KillableThread):
def run(self): def run(self):
try: try:
self._run_internal() self._run_internal()
except: except Exception:
utils.ERROR(notify=True) utils.ERROR(notify=True)
def _run_internal(self): def _run_internal(self):

View file

@ -65,6 +65,10 @@ class FullSync(common.fullsync_mixin):
self.title = '' self.title = ''
self.section = None self.section = None
self.section_name = None self.section_name = None
self.section_type_text = None
self.context = None
self.get_children = None
self.successful = None
self.install_sync_done = utils.settings('SyncInstallRunDone') == 'true' self.install_sync_done = utils.settings('SyncInstallRunDone') == 'true'
self.threader = backgroundthread.ThreaderManager( self.threader = backgroundthread.ThreaderManager(
worker=backgroundthread.NonstoppingBackgroundWorker, worker=backgroundthread.NonstoppingBackgroundWorker,
@ -181,7 +185,7 @@ class FullSync(common.fullsync_mixin):
while True: while True:
# Check Plex DB to see what we need to add/update # Check Plex DB to see what we need to add/update
with PlexDB() as self.plexdb: with PlexDB() as self.plexdb:
for i, (last, xml_item) in enumerate(loop): for last, xml_item in loop:
if self.isCanceled(): if self.isCanceled():
return False return False
self.process_item(xml_item) self.process_item(xml_item)
@ -323,6 +327,11 @@ class FullSync(common.fullsync_mixin):
if self.successful: if self.successful:
# Set timestamp for next sync - neglecting playstates! # Set timestamp for next sync - neglecting playstates!
utils.settings('lastfullsync', value=str(int(self.current_sync))) utils.settings('lastfullsync', value=str(int(self.current_sync)))
# In order to not delete all your songs again
if app.SYNC.enable_music:
kinds.extend([
(v.PLEX_TYPE_SONG, v.PLEX_TYPE_ARTIST, itemtypes.Song, True),
])
# SYNC PLAYSTATE of ALL items (otherwise we won't pick up on items that # SYNC PLAYSTATE of ALL items (otherwise we won't pick up on items that
# were set to unwatched). Also mark all items on the PMS to be able # were set to unwatched). Also mark all items on the PMS to be able
# to delete the ones still in Kodi # to delete the ones still in Kodi
@ -428,7 +437,4 @@ class FullSync(common.fullsync_mixin):
def start(show_dialog, repair=False, callback=None): def start(show_dialog, repair=False, callback=None):
"""
"""
# FullSync(repair, callback, show_dialog).start()
FullSync(repair, callback, show_dialog).run() FullSync(repair, callback, show_dialog).run()

View file

@ -15,7 +15,8 @@ LOG = getLogger('PLEX.videonodes')
class VideoNodes(object): class VideoNodes(object):
def commonRoot(self, order, label, tagname, roottype=1): @staticmethod
def commonRoot(order, label, tagname, roottype=1):
if roottype == 0: if roottype == 0:
# Index # Index
@ -113,10 +114,7 @@ class VideoNodes(object):
label=tagname, label=tagname,
tagname=tagname, tagname=tagname,
roottype=0) roottype=0)
try: utils.indent(root)
utils.indent(root)
except:
pass
etree.ElementTree(root).write(nodeXML, encoding="UTF-8") etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
nodetypes = { nodetypes = {
@ -406,10 +404,7 @@ class VideoNodes(object):
rule = etree.SubElement(root, rule = etree.SubElement(root,
'rule', 'rule',
{'field': "inprogress", 'operator':"true"}) {'field': "inprogress", 'operator':"true"})
try: utils.indent(root)
utils.indent(root)
except:
pass
etree.ElementTree(root).write(path_ops.encode_path(nodeXML), etree.ElementTree(root).write(path_ops.encode_path(nodeXML),
encoding="UTF-8") encoding="UTF-8")
@ -464,13 +459,11 @@ class VideoNodes(object):
etree.SubElement(root, 'content').text = mediatype etree.SubElement(root, 'content').text = mediatype
try: utils.indent(root)
utils.indent(root)
except:
pass
etree.ElementTree(root).write(nodeXML, encoding="UTF-8") etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
def clearProperties(self): @staticmethod
def clearProperties():
LOG.info("Clearing nodes properties.") LOG.info("Clearing nodes properties.")
plexprops = utils.window('Plex.nodes.total') plexprops = utils.window('Plex.nodes.total')

View file

@ -424,6 +424,8 @@ def _conclude_playback(playqueue, pos):
else: else:
listitem.setProperty('StartOffset', str(item.offset)) listitem.setProperty('StartOffset', str(item.offset))
listitem.setProperty('resumetime', str(item.offset)) listitem.setProperty('resumetime', str(item.offset))
elif v.KODIVERSION >= 18:
listitem.setProperty('StartPercent', '0')
# Reset the resumable flag # Reset the resumable flag
transfer.send(listitem) transfer.send(listitem)
LOG.info('Done concluding playback') LOG.info('Done concluding playback')

View file

@ -495,12 +495,6 @@ class API(object):
""" """
pass pass
def votecount(self):
"""
Not yet implemented
"""
pass
def tagline(self): def tagline(self):
""" """
Returns a shorter tagline or None Returns a shorter tagline or None
@ -1517,7 +1511,7 @@ class API(object):
return return
try: try:
date = sub(r'(\d+)-(\d+)-(\d+)', r'\3.\2.\1', date) date = sub(r'(\d+)-(\d+)-(\d+)', r'\3.\2.\1', date)
except: except Exception:
date = None date = None
return date return date

View file

@ -282,7 +282,7 @@ class PlexCompanion(backgroundthread.KillableThread):
listener.MyHandler) listener.MyHandler)
httpd.timeout = 0.95 httpd.timeout = 0.95
break break
except: except Exception:
LOG.error("Unable to start PlexCompanion. Traceback:") LOG.error("Unable to start PlexCompanion. Traceback:")
import traceback import traceback
LOG.error(traceback.print_exc()) LOG.error(traceback.print_exc())
@ -330,7 +330,7 @@ class PlexCompanion(backgroundthread.KillableThread):
subscription_manager.notify() subscription_manager.notify()
if not httpd: if not httpd:
message_count = 0 message_count = 0
except: except Exception:
LOG.warn("Error in loop, continuing anyway. Traceback:") LOG.warn("Error in loop, continuing anyway. Traceback:")
import traceback import traceback
LOG.warn(traceback.format_exc()) LOG.warn(traceback.format_exc())

View file

@ -131,7 +131,11 @@ def check_connection(url, token=None, verifySSL=None):
if token is not None: if token is not None:
header_options = {'X-Plex-Token': token} header_options = {'X-Plex-Token': token}
if verifySSL is True: if verifySSL is True:
verifySSL = None if utils.settings('sslverify') == 'true' else False if v.KODIVERSION >= 18:
# Always verify with Kodi >= 18
verifySSL = True
else:
verifySSL = True if utils.settings('sslverify') == 'true' else False
if 'plex.tv' in url: if 'plex.tv' in url:
url = 'https://plex.tv/api/home/users' url = 'https://plex.tv/api/home/users'
LOG.debug("Checking connection to server %s with verifySSL=%s", LOG.debug("Checking connection to server %s with verifySSL=%s",
@ -424,7 +428,7 @@ def _poke_pms(pms, queue):
xml = DU().downloadUrl('%s/identity' % url, xml = DU().downloadUrl('%s/identity' % url,
authenticate=False, authenticate=False,
headerOptions={'X-Plex-Token': pms['token']}, headerOptions={'X-Plex-Token': pms['token']},
verifySSL=False, verifySSL=True if v.KODIVERSION >= 18 else False,
timeout=10) timeout=10)
try: try:
xml.attrib['machineIdentifier'] xml.attrib['machineIdentifier']
@ -804,14 +808,14 @@ def _pms_https_enabled(url):
""" """
res = DU().downloadUrl('https://%s/identity' % url, res = DU().downloadUrl('https://%s/identity' % url,
authenticate=False, authenticate=False,
verifySSL=False) verifySSL=True if v.KODIVERSION >= 18 else 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 = DU().downloadUrl('http://%s/identity' % url, res = DU().downloadUrl('http://%s/identity' % url,
authenticate=False, authenticate=False,
verifySSL=False) verifySSL=True if v.KODIVERSION >= 18 else False)
try: try:
res.attrib res.attrib
except AttributeError: except AttributeError:
@ -833,8 +837,9 @@ def GetMachineIdentifier(url):
""" """
xml = DU().downloadUrl('%s/identity' % url, xml = DU().downloadUrl('%s/identity' % url,
authenticate=False, authenticate=False,
verifySSL=False, verifySSL=True if v.KODIVERSION >= 18 else False,
timeout=10) timeout=10,
reraise=True)
try: try:
machineIdentifier = xml.attrib['machineIdentifier'] machineIdentifier = xml.attrib['machineIdentifier']
except (AttributeError, KeyError): except (AttributeError, KeyError):
@ -937,6 +942,17 @@ def delete_item_from_pms(plexid):
return False return False
def pms_root(url, token):
"""
Retrieve the PMS' most basic settings by retrieving <url>/
"""
return DU().downloadUrl(
url,
authenticate=False,
verifySSL=True if v.KODIVERSION >= 18 else False,
headerOptions={'X-Plex-Token': token} if token else None)
def get_PMS_settings(url, token): def get_PMS_settings(url, token):
""" """
Retrieve the PMS' settings via <url>/:/prefs Retrieve the PMS' settings via <url>/:/prefs
@ -946,7 +962,7 @@ def get_PMS_settings(url, token):
return DU().downloadUrl( return DU().downloadUrl(
'%s/:/prefs' % url, '%s/:/prefs' % url,
authenticate=False, authenticate=False,
verifySSL=False, verifySSL=True if v.KODIVERSION >= 18 else False,
headerOptions={'X-Plex-Token': token} if token else None) headerOptions={'X-Plex-Token': token} if token else None)

View file

@ -70,7 +70,7 @@ class RequestMgr:
# Close connection just in case # Close connection just in case
try: try:
conn.close() conn.close()
except: except Exception:
pass pass
return False return False

View file

@ -93,7 +93,7 @@ class MyHandler(BaseHTTPRequestHandler):
self.end_headers() self.end_headers()
self.wfile.write(body) self.wfile.write(body)
self.wfile.close() self.wfile.close()
except: except Exception:
pass pass
def answer_request(self, send_data): def answer_request(self, send_data):

View file

@ -95,7 +95,7 @@ class plexgdm:
% (self.client_header, self.client_data), % (self.client_header, self.client_data),
self.client_register_group) self.client_register_group)
log.debug('(Re-)registering PKC Plex Companion successful') log.debug('(Re-)registering PKC Plex Companion successful')
except: except Exception:
log.error("Unable to send registration message") log.error("Unable to send registration message")
def client_update(self): def client_update(self):
@ -109,14 +109,14 @@ class plexgdm:
update_sock.setsockopt(socket.SOL_SOCKET, update_sock.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR, socket.SO_REUSEADDR,
1) 1)
except: except Exception:
pass pass
# Attempt to bind to the socket to recieve and send data. If we cant # Attempt to bind to the socket to recieve and send data. If we cant
# do this, then we cannot send registration # do this, then we cannot send registration
try: try:
update_sock.bind(('0.0.0.0', self.client_update_port)) update_sock.bind(('0.0.0.0', self.client_update_port))
except: except Exception:
log.error("Unable to bind to port [%s] - Plex Companion will not " log.error("Unable to bind to port [%s] - Plex Companion will not "
"be registered. Change the Plex Companion update port!" "be registered. Change the Plex Companion update port!"
% self.client_update_port) % self.client_update_port)
@ -165,7 +165,7 @@ class plexgdm:
update_sock.sendto("HTTP/1.0 200 OK\n%s" update_sock.sendto("HTTP/1.0 200 OK\n%s"
% self.client_data, % self.client_data,
addr) addr)
except: except Exception:
log.error("Unable to send client update message") log.error("Unable to send client update message")
log.debug("Sending registration data HTTP/1.0 200 OK") log.debug("Sending registration data HTTP/1.0 200 OK")
@ -180,7 +180,7 @@ class plexgdm:
update_sock.sendto("BYE %s\n%s" update_sock.sendto("BYE %s\n%s"
% (self.client_header, self.client_data), % (self.client_header, self.client_data),
self.client_register_group) self.client_register_group)
except: except Exception:
log.error("Unable to send client update message") log.error("Unable to send client update message")
self.client_registered = False self.client_registered = False

View file

@ -269,14 +269,29 @@ class Service():
self.auth_running = False self.auth_running = False
def enter_new_pms_address(self): def enter_new_pms_address(self):
if self.setup.enter_new_pms_address(): server = self.setup.enter_new_pms_address()
app.CONN.load() if not server:
app.ACCOUNT.reset_session() return
app.ACCOUNT.set_unauthenticated() if not self.log_out():
self.server_has_been_online = False return False
self.welcome_msg = False # Save changes to to file
# Force a full sync self.setup.save_pms_settings(server['baseURL'], server['token'])
app.SYNC.run_lib_scan = 'full' self.setup.write_pms_to_settings(server)
if not v.KODIVERSION >= 18:
utils.settings('sslverify', value='false')
if not self.log_out():
return False
# Wipe Kodi and Plex database as well as playlists and video nodes
utils.wipe_database()
app.CONN.load()
app.ACCOUNT.reset_session()
app.ACCOUNT.set_unauthenticated()
self.server_has_been_online = False
self.welcome_msg = False
# Force a full sync
app.SYNC.run_lib_scan = 'full'
LOG.info("Choosing new PMS complete")
return True
def _do_auth(self): def _do_auth(self):
LOG.info('Authenticating user') LOG.info('Authenticating user')
@ -466,7 +481,7 @@ class Service():
PF.check_connection, PF.check_connection,
self.on_connection_check, self.on_connection_check,
server, server,
verifySSL=True) verifySSL=app.CONN.verify_ssl_cert)
backgroundthread.BGThreader.addTasksToFront([task]) backgroundthread.BGThreader.addTasksToFront([task])
continue continue
elif not app.ACCOUNT.authenticated: elif not app.ACCOUNT.authenticated:

View file

@ -146,7 +146,7 @@ class Sync(backgroundthread.KillableThread):
def run(self): def run(self):
try: try:
self._run_internal() self._run_internal()
except: except Exception:
app.SYNC.db_scan = False app.SYNC.db_scan = False
utils.window('plex_dbScan', clear=True) utils.window('plex_dbScan', clear=True)
utils.ERROR(txt='sync.py crashed', notify=True) utils.ERROR(txt='sync.py crashed', notify=True)

View file

@ -607,19 +607,22 @@ def indent(elem, level=0):
""" """
Prettifies xml trees. Pass the etree root in Prettifies xml trees. Pass the etree root in
""" """
i = "\n" + level * " " try:
if len(elem): i = "\n" + level * " "
if not elem.text or not elem.text.strip(): if len(elem):
elem.text = i + " " if not elem.text or not elem.text.strip():
if not elem.tail or not elem.tail.strip(): elem.text = i + " "
elem.tail = i if not elem.tail or not elem.tail.strip():
for elem in elem: elem.tail = i
indent(elem, level + 1) for elem in elem:
if not elem.tail or not elem.tail.strip(): indent(elem, level + 1)
elem.tail = i if not elem.tail or not elem.tail.strip():
else: elem.tail = i
if level and (not elem.tail or not elem.tail.strip()): else:
elem.tail = i if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
except Exception as err:
LOG.info('Indentation failed with: %s', err)
class XmlKodiSetting(object): class XmlKodiSetting(object):

View file

@ -705,7 +705,7 @@ class WebSocket(object):
""" """
try: try:
self.sock.shutdown(socket.SHUT_RDWR) self.sock.shutdown(socket.SHUT_RDWR)
except: except Exception:
pass pass
self._closeInternal() self._closeInternal()

View file

@ -237,7 +237,7 @@ class Alexa_Websocket(WebSocket):
LOG.error('%s: Unknown Alexa message received', LOG.error('%s: Unknown Alexa message received',
self.__class__.__name__) self.__class__.__name__)
return return
except: except Exception:
LOG.error('%s: Could not parse Alexa message', LOG.error('%s: Could not parse Alexa message',
self.__class__.__name__) self.__class__.__name__)
return return

View file

@ -21,8 +21,10 @@
<category label="Plex"> <category label="Plex">
<setting type="lsep" label="plex.tv"/> <setting type="lsep" label="plex.tv"/>
<setting id="plex_status" label="39071" type="text" default="Not logged in to plex.tv" enable="false" /><!-- Current plex.tv status: --> <setting id="plex_status" label="39071" type="text" default="Not logged in to plex.tv" enable="false" /><!-- Current plex.tv status: -->
<setting id="plexLogin" label="39228" type="text" default="" enable="false" /> <setting id="plexLogin" label="39228" type="text" default="" enable="false" /><!-- Plex admin user -->
<setting id="username" label="39230" type="text" default="" enable="false" /><!-- Logged in Plex home user -->
<setting id="myplexlogin" label="39025" type="bool" default="true" /> <!-- Log into plex.tv on startup --> <setting id="myplexlogin" label="39025" type="bool" default="true" /> <!-- Log into plex.tv on startup -->
<setting label="[COLOR yellow]$ADDON[plugin.video.plexkodiconnect 39231][/COLOR]" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=switchuser)" option="close" /><!-- Change logged in Plex home user -->
<setting label="[COLOR yellow]$ADDON[plugin.video.plexkodiconnect 39209][/COLOR]" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=togglePlexTV)" option="close" /><!-- Toggle plex.tv login (sign in or sign out) --> <setting label="[COLOR yellow]$ADDON[plugin.video.plexkodiconnect 39209][/COLOR]" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=togglePlexTV)" option="close" /><!-- Toggle plex.tv login (sign in or sign out) -->
<setting id="plexToken" label="plexToken" type="text" default="" visible="false" /> <setting id="plexToken" label="plexToken" type="text" default="" visible="false" />
<setting type="sep" text=""/> <setting type="sep" text=""/>
@ -47,7 +49,6 @@
<setting id="dbCreatedWithVersion" type="text" default="" visible="false"/> <setting id="dbCreatedWithVersion" type="text" default="" visible="false"/>
<setting id="plexid" type="text" default="" visible="false"/> <setting id="plexid" type="text" default="" visible="false"/>
<setting id="userid" type="text" default="" visible="false"/> <setting id="userid" type="text" default="" visible="false"/>
<setting id="username" type="text" default="" visible="false"/>
</category> </category>
<category label="30506"><!-- Sync Options --> <category label="30506"><!-- Sync Options -->