commit
85f73126ff
29 changed files with 316 additions and 158 deletions
|
@ -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)
|
||||||
|
|
13
addon.xml
13
addon.xml
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 "
|
||||||
|
|
|
@ -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 ""
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 ###===---")
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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,15 +40,11 @@ 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
|
||||||
if certificate is None:
|
|
||||||
certificate = app.CONN.ssl_cert_path
|
certificate = app.CONN.ssl_cert_path
|
||||||
# Set the session's parameters
|
# Set the session's parameters
|
||||||
self.s.verify = verifySSL
|
self.s.verify = verifySSL
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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'
|
||||||
|
# Try to connect first
|
||||||
|
error = False
|
||||||
|
try:
|
||||||
machine_identifier = PF.GetMachineIdentifier(url)
|
machine_identifier = PF.GetMachineIdentifier(url)
|
||||||
if machine_identifier is None:
|
except exceptions.SSLError:
|
||||||
# "Error contacting url
|
LOG.error('SSL cert error contacting %s', url)
|
||||||
# Abort (Yes) or save address anyway (No)"
|
# "SSL certificate failed to validate. Please check {0}
|
||||||
if utils.yesno_dialog(utils.lang(29999),
|
# for solutions."
|
||||||
'%s %s. %s' % (utils.lang(39218),
|
utils.messageDialog(utils.lang(29999),
|
||||||
url,
|
utils.lang(30503).format('github.com/croneter/PlexKodiConnect/issues'))
|
||||||
utils.lang(39219))):
|
return
|
||||||
return False
|
except Exception:
|
||||||
else:
|
error = True
|
||||||
utils.settings('plex_machineIdentifier', '')
|
if error or machine_identifier is None:
|
||||||
else:
|
LOG.error('Could not even get a machineIdentifier for %s', url)
|
||||||
utils.settings('plex_machineIdentifier', machine_identifier)
|
# "Server is unreachable"
|
||||||
LOG.info('Set new PMS to https %s, address %s, port %s, machineId %s',
|
utils.messageDialog(utils.lang(29999), utils.lang(33002))
|
||||||
https, address, port, machine_identifier)
|
return
|
||||||
utils.settings('https', value=https)
|
# Let's use the main account's token, not managed user token
|
||||||
utils.settings('ipaddress', value=address)
|
token = utils.settings('plexToken')
|
||||||
utils.settings('port', value=port)
|
xml = PF.pms_root(url, token)
|
||||||
# Chances are this is a local PMS, so disable SSL certificate check
|
if xml == 401:
|
||||||
utils.settings('sslverify', value='false')
|
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
|
||||||
|
|
|
@ -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['Runtime'],
|
|
||||||
userdata['PlayCount'],
|
|
||||||
userdata['LastPlayedDate'],
|
userdata['LastPlayedDate'],
|
||||||
plex_type)
|
db_item['kodi_id'],)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def remove(self, plex_id, plex_type=None):
|
def remove(self, plex_id, plex_type=None):
|
||||||
|
|
|
@ -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('''
|
||||||
|
|
|
@ -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('''
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -269,7 +269,20 @@ 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()
|
||||||
|
if not server:
|
||||||
|
return
|
||||||
|
if not self.log_out():
|
||||||
|
return False
|
||||||
|
# Save changes to to file
|
||||||
|
self.setup.save_pms_settings(server['baseURL'], server['token'])
|
||||||
|
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.CONN.load()
|
||||||
app.ACCOUNT.reset_session()
|
app.ACCOUNT.reset_session()
|
||||||
app.ACCOUNT.set_unauthenticated()
|
app.ACCOUNT.set_unauthenticated()
|
||||||
|
@ -277,6 +290,8 @@ class Service():
|
||||||
self.welcome_msg = False
|
self.welcome_msg = False
|
||||||
# Force a full sync
|
# Force a full sync
|
||||||
app.SYNC.run_lib_scan = 'full'
|
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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -607,6 +607,7 @@ def indent(elem, level=0):
|
||||||
"""
|
"""
|
||||||
Prettifies xml trees. Pass the etree root in
|
Prettifies xml trees. Pass the etree root in
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
i = "\n" + level * " "
|
i = "\n" + level * " "
|
||||||
if len(elem):
|
if len(elem):
|
||||||
if not elem.text or not elem.text.strip():
|
if not elem.text or not elem.text.strip():
|
||||||
|
@ -620,6 +621,8 @@ def indent(elem, level=0):
|
||||||
else:
|
else:
|
||||||
if level and (not elem.tail or not elem.tail.strip()):
|
if level and (not elem.tail or not elem.tail.strip()):
|
||||||
elem.tail = i
|
elem.tail = i
|
||||||
|
except Exception as err:
|
||||||
|
LOG.info('Indentation failed with: %s', err)
|
||||||
|
|
||||||
|
|
||||||
class XmlKodiSetting(object):
|
class XmlKodiSetting(object):
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 -->
|
||||||
|
|
Loading…
Reference in a new issue