From 09c2ad1b8048302e87daff8a5ddd93c75dc886f8 Mon Sep 17 00:00:00 2001 From: croneter Date: Sat, 2 Feb 2019 20:50:01 +0100 Subject: [PATCH 1/9] Fix playback not starting at the beginning --- resources/lib/playback.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/lib/playback.py b/resources/lib/playback.py index 40e9015b..0c2f5582 100644 --- a/resources/lib/playback.py +++ b/resources/lib/playback.py @@ -424,6 +424,8 @@ def _conclude_playback(playqueue, pos): else: listitem.setProperty('StartOffset', str(item.offset)) listitem.setProperty('resumetime', str(item.offset)) + elif v.KODIVERSION >= 18: + listitem.setProperty('StartPercent', '0') # Reset the resumable flag transfer.send(listitem) LOG.info('Done concluding playback') From 1071d75857173534a28646a4c8bc53ad840f4de1 Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 3 Feb 2019 10:53:19 +0100 Subject: [PATCH 2/9] Improve dialog to manually enter PMS IP and port --- .../language/resource.language.en_gb/strings.po | 12 +++++++++++- resources/lib/initialsetup.py | 16 ++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index a486189a..b98f74bd 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -1081,6 +1081,16 @@ msgctxt "#39082" msgid "Direct Paths" 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" msgid "Log-out Plex Home User " msgstr "" @@ -1133,7 +1143,7 @@ msgid "Enter your Plex Media Server's IP or URL, Examples are:" msgstr "" 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 "" msgctxt "#39218" diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index 43718a35..269fa7a6 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -70,20 +70,22 @@ class InitialSetup(object): utils.window('plex_allows_mediaDeletion', value=value) 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:" utils.messageDialog(utils.lang(29999), '%s\n%s\n%s' % (utils.lang(39215), '192.168.1.2', 'plex.myServer.org')) - address = utils.dialog('input', "Enter PMS IP or URL") - if address == '': + # "Enter PMS IP or URL" + address = utils.dialog('input', utils.lang(39083)) + if not address: return False - port = utils.dialog('input', "Enter PMS port", '32400', type='{numeric}') - if port == '': + port = utils.dialog('input', utils.lang(39084), '32400', type='{numeric}') + if not port: return False url = '%s:%s' % (address, port) - # "Does your Plex Media Server support SSL connections? - # (https instead of http)" + # "Use HTTPS (SSL) connections? With Kodi 18 or later, HTTPS will likely + # not work!" https = utils.yesno_dialog(utils.lang(29999), utils.lang(39217)) if https: url = 'https://%s' % url @@ -92,6 +94,7 @@ class InitialSetup(object): https = 'true' if https else 'false' machine_identifier = PF.GetMachineIdentifier(url) if machine_identifier is None: + LOG.error('Could not get machine identifier') # "Error contacting url # Abort (Yes) or save address anyway (No)" if utils.yesno_dialog(utils.lang(29999), @@ -100,6 +103,7 @@ class InitialSetup(object): utils.lang(39219))): return False else: + LOG.info('Saving manual address anyway') utils.settings('plex_machineIdentifier', '') else: utils.settings('plex_machineIdentifier', machine_identifier) From 01609e4d72cdd3034e971f5ebfb40d3f9561d730 Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 3 Feb 2019 16:44:37 +0100 Subject: [PATCH 3/9] Better inform the user if problems arise with manual PMS IP input --- .../resource.language.en_gb/strings.po | 5 ++ resources/lib/downloadutils.py | 31 ++++++-- resources/lib/initialsetup.py | 77 +++++++++++++------ resources/lib/plex_functions.py | 14 +++- resources/lib/service_entry.py | 31 ++++++-- 5 files changed, 119 insertions(+), 39 deletions(-) diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index b98f74bd..a4c04828 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -464,6 +464,11 @@ msgctxt "#30502" msgid "Sync Plex artwork from the PMS (recommended)" 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 msgctxt "#30506" msgid "Sync Options" diff --git a/resources/lib/downloadutils.py b/resources/lib/downloadutils.py index b4bc2562..77491c64 100644 --- a/resources/lib/downloadutils.py +++ b/resources/lib/downloadutils.py @@ -3,6 +3,7 @@ from __future__ import absolute_import, division, unicode_literals from logging import getLogger import requests +import requests.exceptions as exceptions from . import utils, clientinfo, app @@ -114,7 +115,7 @@ class DownloadUtils(): def downloadUrl(self, url, action_type="GET", postBody=None, parameters=None, authenticate=True, headerOptions=None, verifySSL=True, timeout=None, return_response=False, - headerOverride=None): + headerOverride=None, reraise=False): """ Override SSL check with verifySSL=False @@ -172,39 +173,55 @@ class DownloadUtils(): r = self._doDownload(s, action_type, **kwargs) # THE EXCEPTIONS - except requests.exceptions.SSLError as e: + except exceptions.SSLError as e: LOG.warn("Invalid SSL certificate for: %s", url) LOG.warn(e) + if reraise: + raise - except requests.exceptions.ConnectionError as e: + except exceptions.ConnectionError as e: # Connection error LOG.warn("Server unreachable at: %s", url) 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(e) + if reraise: + raise - except requests.exceptions.HTTPError as e: + except exceptions.HTTPError as e: LOG.warn('HTTP Error at %s', url) 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(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(e) + if reraise: + raise except SystemExit: LOG.info('SystemExit detected, aborting download') self.stopSession() + if reraise: + raise except: LOG.warn('Unknown error while downloading. Traceback:') import traceback LOG.warn(traceback.format_exc()) + if reraise: + raise # THE RESPONSE ##### else: diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index 269fa7a6..78fc590b 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -9,7 +9,7 @@ from . import utils from .utils import etree from . import path_ops 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_tv from . import json_rpc as js @@ -92,28 +92,59 @@ class InitialSetup(object): else: url = 'http://%s' % url https = 'true' if https else 'false' - machine_identifier = PF.GetMachineIdentifier(url) - if machine_identifier is None: - LOG.error('Could not get machine identifier') - # "Error contacting url - # Abort (Yes) or save address anyway (No)" - if utils.yesno_dialog(utils.lang(29999), - '%s %s. %s' % (utils.lang(39218), - url, - utils.lang(39219))): - return False - else: - LOG.info('Saving manual address anyway') - utils.settings('plex_machineIdentifier', '') - else: - utils.settings('plex_machineIdentifier', machine_identifier) - LOG.info('Set new PMS to https %s, address %s, port %s, machineId %s', - https, address, port, machine_identifier) - utils.settings('https', value=https) - utils.settings('ipaddress', value=address) - utils.settings('port', value=port) - # Chances are this is a local PMS, so disable SSL certificate check - utils.settings('sslverify', value='false') + # Try to connect first + error = False + try: + machine_identifier = PF.GetMachineIdentifier(url) + except exceptions.SSLError: + LOG.error('SSL cert error contacting %s', url) + # "SSL certificate failed to validate. Please check {0} + # for solutions." + utils.messageDialog(utils.lang(29999), + utils.lang(30503).format('github.com/croneter/PlexKodiConnect/issues')) + return + except Exception: + error = True + if error or machine_identifier is None: + LOG.error('Could not even get a machineIdentifier for %s', url) + # "Server is unreachable" + utils.messageDialog(utils.lang(29999), utils.lang(33002)) + return + # Let's use the main account's token, not managed user token + 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): """ diff --git a/resources/lib/plex_functions.py b/resources/lib/plex_functions.py index b006dc10..6d29609e 100644 --- a/resources/lib/plex_functions.py +++ b/resources/lib/plex_functions.py @@ -834,7 +834,8 @@ def GetMachineIdentifier(url): xml = DU().downloadUrl('%s/identity' % url, authenticate=False, verifySSL=False, - timeout=10) + timeout=10, + reraise=True) try: machineIdentifier = xml.attrib['machineIdentifier'] except (AttributeError, KeyError): @@ -937,6 +938,17 @@ def delete_item_from_pms(plexid): return False +def pms_root(url, token): + """ + Retrieve the PMS' most basic settings by retrieving / + """ + 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): """ Retrieve the PMS' settings via /:/prefs diff --git a/resources/lib/service_entry.py b/resources/lib/service_entry.py index f59ed943..63b4e554 100644 --- a/resources/lib/service_entry.py +++ b/resources/lib/service_entry.py @@ -269,14 +269,29 @@ class Service(): self.auth_running = False def enter_new_pms_address(self): - if self.setup.enter_new_pms_address(): - 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' + 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.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): LOG.info('Authenticating user') From 8f8ccd1daf7d4101c9b0905f68f06c8ac794bd13 Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 3 Feb 2019 17:20:36 +0100 Subject: [PATCH 4/9] Fix music items getting deleted on startup --- resources/lib/itemtypes/music.py | 9 +++------ resources/lib/kodi_db/music.py | 9 +++++++++ resources/lib/library_sync/full_sync.py | 5 +++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/resources/lib/itemtypes/music.py b/resources/lib/itemtypes/music.py index 20dc516c..f54fac45 100644 --- a/resources/lib/itemtypes/music.py +++ b/resources/lib/itemtypes/music.py @@ -52,12 +52,9 @@ class MusicMixin(object): db_item['kodi_type'], userdata['UserRating']) if plex_type == v.PLEX_TYPE_SONG: - self.kodidb.set_resume(db_item['kodi_fileid'], - userdata['Resume'], - userdata['Runtime'], - userdata['PlayCount'], - userdata['LastPlayedDate'], - plex_type) + self.kodidb.set_playcount(userdata['PlayCount'], + userdata['LastPlayedDate'], + db_item['kodi_id'],) return True def remove(self, plex_id, plex_type=None): diff --git a/resources/lib/kodi_db/music.py b/resources/lib/kodi_db/music.py index 793b7a25..37f5c4a9 100644 --- a/resources/lib/kodi_db/music.py +++ b/resources/lib/kodi_db/music.py @@ -459,6 +459,15 @@ class KodiMusicDB(common.KodiDBBase): WHERE idSong = ? ''', (args)) + @common.catch_operationalerrors + def set_playcount(self, *args): + self.cursor.execute(''' + UPDATE song + SET iTimesPlayed = ?, + lastplayed = ? + WHERE idSong = ? + ''', (args)) + @common.catch_operationalerrors def update_song_17(self, *args): self.cursor.execute(''' diff --git a/resources/lib/library_sync/full_sync.py b/resources/lib/library_sync/full_sync.py index ccda2b7e..3010eebb 100644 --- a/resources/lib/library_sync/full_sync.py +++ b/resources/lib/library_sync/full_sync.py @@ -323,6 +323,11 @@ class FullSync(common.fullsync_mixin): if self.successful: # Set timestamp for next sync - neglecting playstates! 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 # were set to unwatched). Also mark all items on the PMS to be able # to delete the ones still in Kodi From 662ed1bdfce89d48b4119cc98b873b8de97906fb Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 3 Feb 2019 19:12:35 +0100 Subject: [PATCH 5/9] Show logged in Plex home user in the settings and allow changing it --- .../language/resource.language.en_gb/strings.po | 14 ++++++++++++-- resources/settings.xml | 5 +++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index a486189a..b174cd0d 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -1179,9 +1179,9 @@ msgctxt "#39227" msgid "Logged in to plex.tv" 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" -msgid "Plex user:" +msgid "Plex admin user" msgstr "" # Error message if user could not log in; the actual user name will be appended at the end of the string @@ -1189,6 +1189,16 @@ msgctxt "#39229" msgid "Login failed with plex.tv for user" 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" msgid "Running the image cache process can take some time. It will happen in the background. Are you sure you want continue?" msgstr "" diff --git a/resources/settings.xml b/resources/settings.xml index 3debb66e..3968a174 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -21,8 +21,10 @@ - + + + @@ -47,7 +49,6 @@ - From 2c77bd28af4898873f182f7618fa473bf7cdb7e3 Mon Sep 17 00:00:00 2001 From: croneter Date: Sat, 2 Feb 2019 20:22:06 +0100 Subject: [PATCH 6/9] Implement Codacy suggestions --- resources/lib/artwork.py | 2 +- resources/lib/backgroundthread.py | 24 +++++++++--------- resources/lib/downloadutils.py | 16 ++++++------ resources/lib/kodi_db/video.py | 2 +- resources/lib/library_sync/fanart.py | 2 +- resources/lib/library_sync/full_sync.py | 9 ++++--- resources/lib/library_sync/videonodes.py | 21 ++++++---------- resources/lib/plex_api.py | 8 +----- resources/lib/plex_companion.py | 4 +-- resources/lib/plexbmchelper/httppersist.py | 2 +- resources/lib/plexbmchelper/listener.py | 2 +- resources/lib/plexbmchelper/plexgdm.py | 10 ++++---- resources/lib/sync.py | 2 +- resources/lib/utils.py | 29 ++++++++++++---------- resources/lib/websocket.py | 2 +- resources/lib/websocket_client.py | 2 +- 16 files changed, 65 insertions(+), 72 deletions(-) diff --git a/resources/lib/artwork.py b/resources/lib/artwork.py index 539a9c45..555cb70c 100644 --- a/resources/lib/artwork.py +++ b/resources/lib/artwork.py @@ -62,7 +62,7 @@ class ImageCachingThread(backgroundthread.KillableThread): LOG.info("---===### Starting ImageCachingThread ###===---") try: self._run() - except: + except Exception: utils.ERROR() finally: LOG.info("---===### Stopped ImageCachingThread ###===---") diff --git a/resources/lib/backgroundthread.py b/resources/lib/backgroundthread.py index 46d8e240..7e9ac465 100644 --- a/resources/lib/backgroundthread.py +++ b/resources/lib/backgroundthread.py @@ -131,12 +131,12 @@ class Tasks(list): class Task(object): def __init__(self, priority=None): - self._priority = priority + self.priority = priority self._canceled = False self.finished = False def __cmp__(self, other): - return self._priority - other._priority + return self.priority - other.priority def start(self): BGThreader.addTask(self) @@ -182,7 +182,7 @@ class MutablePriorityQueue(Queue.PriorityQueue): self.mutex.acquire() try: lowest = self.queue and min(self.queue) or None - except: + except Exception: lowest = None utils.ERROR() finally: @@ -203,7 +203,7 @@ class BackgroundWorker(object): return try: task._run() - except: + except Exception: utils.ERROR() def abort(self): @@ -275,12 +275,12 @@ class BackgroundThreader: self.name = name self._queue = MutablePriorityQueue() 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)] def _nextPriority(self): - self._priority += 1 - return self._priority + self.priority += 1 + return self.priority def abort(self): self._abort = True @@ -298,13 +298,13 @@ class BackgroundThreader: w.shutdown() def addTask(self, task): - task._priority = self._nextPriority() + task.priority = self._nextPriority() self._queue.put(task) self.startWorkers() def addTasks(self, tasks): for t in tasks: - t._priority = self._nextPriority() + t.priority = self._nextPriority() self._queue.put(t) self.startWorkers() @@ -316,7 +316,7 @@ class BackgroundThreader: p = lowest - len(tasks) for t in tasks: - t._priority = p + t.priority = p self._queue.put(t) p += 1 @@ -337,14 +337,14 @@ class BackgroundThreader: if not lowest: return None - return lowest._priority + return lowest.priority def moveToFront(self, qitem): lowest = self.getLowestPrority() if lowest is None: return - qitem._priority = lowest - 1 + qitem.priority = lowest - 1 class ThreaderManager: diff --git a/resources/lib/downloadutils.py b/resources/lib/downloadutils.py index b4bc2562..746c11a0 100644 --- a/resources/lib/downloadutils.py +++ b/resources/lib/downloadutils.py @@ -84,21 +84,23 @@ class DownloadUtils(): def stopSession(self): try: self.s.close() - except: + except Exception: LOG.info("Requests session already closed") try: del self.s - except: + except AttributeError: pass LOG.info('Request session stopped') - def getHeader(self, options=None): + @staticmethod + def getHeader(options=None): header = clientinfo.getXArgsDeviceInfo() if options is not None: header.update(options) return header - def _doDownload(self, s, action_type, **kwargs): + @staticmethod + def _doDownload(s, action_type, **kwargs): if action_type == "GET": r = s.get(**kwargs) elif action_type == "POST": @@ -201,7 +203,7 @@ class DownloadUtils(): LOG.info('SystemExit detected, aborting download') self.stopSession() - except: + except Exception: LOG.warn('Unknown error while downloading. Traceback:') import traceback LOG.warn(traceback.format_exc()) @@ -255,7 +257,7 @@ class DownloadUtils(): # xml response r = utils.defused_etree.fromstring(r.content) return r - except: + except Exception: r.encoding = 'utf-8' if r.text == '': # Answer does not contain a body @@ -264,7 +266,7 @@ class DownloadUtils(): # UNICODE - JSON object r = r.json() return r - except: + except Exception: if '200 OK' in r.text: # Received fucked up OK from PMS on playstate # update diff --git a/resources/lib/kodi_db/video.py b/resources/lib/kodi_db/video.py index 6954e19e..5eaf3eea 100644 --- a/resources/lib/kodi_db/video.py +++ b/resources/lib/kodi_db/video.py @@ -694,7 +694,7 @@ class KodiVideoDB(common.KodiDBBase): SET tag_id = ? WHERE media_id = ? AND media_type = ? AND tag_id = ? ''', (newtag, kodiid, mediatype, oldtag,)) - except: + except Exception: # The new tag we are going to apply already exists for this item # delete current tag instead self.cursor.execute(''' diff --git a/resources/lib/library_sync/fanart.py b/resources/lib/library_sync/fanart.py index 121d3786..c9f9ff9a 100644 --- a/resources/lib/library_sync/fanart.py +++ b/resources/lib/library_sync/fanart.py @@ -41,7 +41,7 @@ class FanartThread(backgroundthread.KillableThread): def run(self): try: self._run_internal() - except: + except Exception: utils.ERROR(notify=True) def _run_internal(self): diff --git a/resources/lib/library_sync/full_sync.py b/resources/lib/library_sync/full_sync.py index ccda2b7e..62cd3b89 100644 --- a/resources/lib/library_sync/full_sync.py +++ b/resources/lib/library_sync/full_sync.py @@ -65,6 +65,10 @@ class FullSync(common.fullsync_mixin): self.title = '' self.section = 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.threader = backgroundthread.ThreaderManager( worker=backgroundthread.NonstoppingBackgroundWorker, @@ -181,7 +185,7 @@ class FullSync(common.fullsync_mixin): while True: # Check Plex DB to see what we need to add/update with PlexDB() as self.plexdb: - for i, (last, xml_item) in enumerate(loop): + for last, xml_item in loop: if self.isCanceled(): return False self.process_item(xml_item) @@ -428,7 +432,4 @@ class FullSync(common.fullsync_mixin): def start(show_dialog, repair=False, callback=None): - """ - """ - # FullSync(repair, callback, show_dialog).start() FullSync(repair, callback, show_dialog).run() diff --git a/resources/lib/library_sync/videonodes.py b/resources/lib/library_sync/videonodes.py index 3eae6eb3..54c1e51f 100644 --- a/resources/lib/library_sync/videonodes.py +++ b/resources/lib/library_sync/videonodes.py @@ -15,7 +15,8 @@ LOG = getLogger('PLEX.videonodes') class VideoNodes(object): - def commonRoot(self, order, label, tagname, roottype=1): + @staticmethod + def commonRoot(order, label, tagname, roottype=1): if roottype == 0: # Index @@ -113,10 +114,7 @@ class VideoNodes(object): label=tagname, tagname=tagname, roottype=0) - try: - utils.indent(root) - except: - pass + utils.indent(root) etree.ElementTree(root).write(nodeXML, encoding="UTF-8") nodetypes = { @@ -406,10 +404,7 @@ class VideoNodes(object): rule = etree.SubElement(root, 'rule', {'field': "inprogress", 'operator':"true"}) - try: - utils.indent(root) - except: - pass + utils.indent(root) etree.ElementTree(root).write(path_ops.encode_path(nodeXML), encoding="UTF-8") @@ -464,13 +459,11 @@ class VideoNodes(object): etree.SubElement(root, 'content').text = mediatype - try: - utils.indent(root) - except: - pass + utils.indent(root) etree.ElementTree(root).write(nodeXML, encoding="UTF-8") - def clearProperties(self): + @staticmethod + def clearProperties(): LOG.info("Clearing nodes properties.") plexprops = utils.window('Plex.nodes.total') diff --git a/resources/lib/plex_api.py b/resources/lib/plex_api.py index f6749481..29418253 100644 --- a/resources/lib/plex_api.py +++ b/resources/lib/plex_api.py @@ -495,12 +495,6 @@ class API(object): """ pass - def votecount(self): - """ - Not yet implemented - """ - pass - def tagline(self): """ Returns a shorter tagline or None @@ -1517,7 +1511,7 @@ class API(object): return try: date = sub(r'(\d+)-(\d+)-(\d+)', r'\3.\2.\1', date) - except: + except Exception: date = None return date diff --git a/resources/lib/plex_companion.py b/resources/lib/plex_companion.py index 57f287f8..01b7f259 100644 --- a/resources/lib/plex_companion.py +++ b/resources/lib/plex_companion.py @@ -282,7 +282,7 @@ class PlexCompanion(backgroundthread.KillableThread): listener.MyHandler) httpd.timeout = 0.95 break - except: + except Exception: LOG.error("Unable to start PlexCompanion. Traceback:") import traceback LOG.error(traceback.print_exc()) @@ -330,7 +330,7 @@ class PlexCompanion(backgroundthread.KillableThread): subscription_manager.notify() if not httpd: message_count = 0 - except: + except Exception: LOG.warn("Error in loop, continuing anyway. Traceback:") import traceback LOG.warn(traceback.format_exc()) diff --git a/resources/lib/plexbmchelper/httppersist.py b/resources/lib/plexbmchelper/httppersist.py index 450cba3c..89a77ed8 100644 --- a/resources/lib/plexbmchelper/httppersist.py +++ b/resources/lib/plexbmchelper/httppersist.py @@ -70,7 +70,7 @@ class RequestMgr: # Close connection just in case try: conn.close() - except: + except Exception: pass return False diff --git a/resources/lib/plexbmchelper/listener.py b/resources/lib/plexbmchelper/listener.py index 807f96d4..7efd94bf 100644 --- a/resources/lib/plexbmchelper/listener.py +++ b/resources/lib/plexbmchelper/listener.py @@ -93,7 +93,7 @@ class MyHandler(BaseHTTPRequestHandler): self.end_headers() self.wfile.write(body) self.wfile.close() - except: + except Exception: pass def answer_request(self, send_data): diff --git a/resources/lib/plexbmchelper/plexgdm.py b/resources/lib/plexbmchelper/plexgdm.py index b28df861..077c46c2 100644 --- a/resources/lib/plexbmchelper/plexgdm.py +++ b/resources/lib/plexbmchelper/plexgdm.py @@ -95,7 +95,7 @@ class plexgdm: % (self.client_header, self.client_data), self.client_register_group) log.debug('(Re-)registering PKC Plex Companion successful') - except: + except Exception: log.error("Unable to send registration message") def client_update(self): @@ -109,14 +109,14 @@ class plexgdm: update_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - except: + except Exception: pass # Attempt to bind to the socket to recieve and send data. If we cant # do this, then we cannot send registration try: 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 " "be registered. Change the Plex Companion update port!" % self.client_update_port) @@ -165,7 +165,7 @@ class plexgdm: update_sock.sendto("HTTP/1.0 200 OK\n%s" % self.client_data, addr) - except: + except Exception: log.error("Unable to send client update message") log.debug("Sending registration data HTTP/1.0 200 OK") @@ -180,7 +180,7 @@ class plexgdm: update_sock.sendto("BYE %s\n%s" % (self.client_header, self.client_data), self.client_register_group) - except: + except Exception: log.error("Unable to send client update message") self.client_registered = False diff --git a/resources/lib/sync.py b/resources/lib/sync.py index 7f2f6afb..dacca413 100644 --- a/resources/lib/sync.py +++ b/resources/lib/sync.py @@ -146,7 +146,7 @@ class Sync(backgroundthread.KillableThread): def run(self): try: self._run_internal() - except: + except Exception: app.SYNC.db_scan = False utils.window('plex_dbScan', clear=True) utils.ERROR(txt='sync.py crashed', notify=True) diff --git a/resources/lib/utils.py b/resources/lib/utils.py index 56ac9f50..df021cde 100644 --- a/resources/lib/utils.py +++ b/resources/lib/utils.py @@ -607,19 +607,22 @@ def indent(elem, level=0): """ Prettifies xml trees. Pass the etree root in """ - i = "\n" + level * " " - if len(elem): - if not elem.text or not elem.text.strip(): - elem.text = i + " " - if not elem.tail or not elem.tail.strip(): - elem.tail = i - for elem in elem: - indent(elem, level + 1) - if not elem.tail or not elem.tail.strip(): - elem.tail = i - else: - if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i + try: + i = "\n" + level * " " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for elem in elem: + indent(elem, level + 1) + if not elem.tail or not elem.tail.strip(): + elem.tail = i + else: + 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): diff --git a/resources/lib/websocket.py b/resources/lib/websocket.py index 6b039429..7c4e3452 100644 --- a/resources/lib/websocket.py +++ b/resources/lib/websocket.py @@ -705,7 +705,7 @@ class WebSocket(object): """ try: self.sock.shutdown(socket.SHUT_RDWR) - except: + except Exception: pass self._closeInternal() diff --git a/resources/lib/websocket_client.py b/resources/lib/websocket_client.py index 09f919c1..2068d2f8 100644 --- a/resources/lib/websocket_client.py +++ b/resources/lib/websocket_client.py @@ -237,7 +237,7 @@ class Alexa_Websocket(WebSocket): LOG.error('%s: Unknown Alexa message received', self.__class__.__name__) return - except: + except Exception: LOG.error('%s: Could not parse Alexa message', self.__class__.__name__) return From f90b470f5bf4244d86e67f552f7d55f7d9aab0c3 Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 3 Feb 2019 12:38:28 +0100 Subject: [PATCH 7/9] Never ignore SSL certificate errors for Kodi >= 18 - just like Kodi --- resources/lib/app/connection.py | 8 +++++--- resources/lib/downloadutils.py | 10 +++------- resources/lib/initialsetup.py | 7 ++++--- resources/lib/plex_functions.py | 16 ++++++++++------ resources/lib/service_entry.py | 2 +- 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/resources/lib/app/connection.py b/resources/lib/app/connection.py index fa6586c9..2adcfb34 100644 --- a/resources/lib/app/connection.py +++ b/resources/lib/app/connection.py @@ -3,7 +3,7 @@ from __future__ import absolute_import, division, unicode_literals 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') @@ -38,7 +38,9 @@ class Connection(object): def load(self): LOG.debug('Loading connection settings') # 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 # Do we have an ssl certificate for PKC we need to use? self.ssl_cert_path = utils.settings('sslcert') \ @@ -61,7 +63,7 @@ class Connection(object): self.server_name, self.machine_identifier, self.server) 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 self.ssl_cert_path = utils.settings('sslcert') \ if utils.settings('sslcert') != 'None' else None diff --git a/resources/lib/downloadutils.py b/resources/lib/downloadutils.py index 4a558f6e..c419f8b1 100644 --- a/resources/lib/downloadutils.py +++ b/resources/lib/downloadutils.py @@ -40,16 +40,12 @@ class DownloadUtils(): def __init__(self): 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' """ - if verifySSL is None: - verifySSL = app.CONN.verify_ssl_cert - if certificate is None: - certificate = app.CONN.ssl_cert_path + verifySSL = app.CONN.verify_ssl_cert + certificate = app.CONN.ssl_cert_path # Set the session's parameters self.s.verify = verifySSL if certificate: diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index 78fc590b..16cea0cf 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -212,7 +212,8 @@ class InitialSetup(object): not set before """ 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: LOG.warn('Could not reach PMS %s', app.CONN.server) answer = False @@ -245,8 +246,8 @@ class InitialSetup(object): if server['local']: url = ('%s://%s:%s' % (server['scheme'], server['ip'], server['port'])) - # Deactive SSL verification if the server is local! - verifySSL = False + # Deactive SSL verification if the server is local for Kodi 17 + verifySSL = True if v.KODIVERSION >= 18 else False else: url = server['baseURL'] verifySSL = True diff --git a/resources/lib/plex_functions.py b/resources/lib/plex_functions.py index 6d29609e..580705d0 100644 --- a/resources/lib/plex_functions.py +++ b/resources/lib/plex_functions.py @@ -131,7 +131,11 @@ def check_connection(url, token=None, verifySSL=None): if token is not None: header_options = {'X-Plex-Token': token} 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: url = 'https://plex.tv/api/home/users' 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, authenticate=False, headerOptions={'X-Plex-Token': pms['token']}, - verifySSL=False, + verifySSL=True if v.KODIVERSION >= 18 else False, timeout=10) try: xml.attrib['machineIdentifier'] @@ -804,14 +808,14 @@ def _pms_https_enabled(url): """ res = DU().downloadUrl('https://%s/identity' % url, authenticate=False, - verifySSL=False) + verifySSL=True if v.KODIVERSION >= 18 else False) try: res.attrib except AttributeError: # Might have SSL deactivated. Try with http res = DU().downloadUrl('http://%s/identity' % url, authenticate=False, - verifySSL=False) + verifySSL=True if v.KODIVERSION >= 18 else False) try: res.attrib except AttributeError: @@ -833,7 +837,7 @@ def GetMachineIdentifier(url): """ xml = DU().downloadUrl('%s/identity' % url, authenticate=False, - verifySSL=False, + verifySSL=True if v.KODIVERSION >= 18 else False, timeout=10, reraise=True) try: @@ -958,7 +962,7 @@ def get_PMS_settings(url, token): return DU().downloadUrl( '%s/:/prefs' % url, authenticate=False, - verifySSL=False, + verifySSL=True if v.KODIVERSION >= 18 else False, headerOptions={'X-Plex-Token': token} if token else None) diff --git a/resources/lib/service_entry.py b/resources/lib/service_entry.py index 63b4e554..17ee3959 100644 --- a/resources/lib/service_entry.py +++ b/resources/lib/service_entry.py @@ -481,7 +481,7 @@ class Service(): PF.check_connection, self.on_connection_check, server, - verifySSL=True) + verifySSL=app.CONN.verify_ssl_cert) backgroundthread.BGThreader.addTasksToFront([task]) continue elif not app.ACCOUNT.authenticated: From b206d1a99f787b894772e5b38570816a22c2e643 Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 3 Feb 2019 20:50:21 +0100 Subject: [PATCH 8/9] Update German strings --- .../resource.language.de_DE/strings.po | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/resources/language/resource.language.de_DE/strings.po b/resources/language/resource.language.de_DE/strings.po index 0441a3b5..34b42e8b 100644 --- a/resources/language/resource.language.de_DE/strings.po +++ b/resources/language/resource.language.de_DE/strings.po @@ -493,6 +493,13 @@ msgctxt "#30502" msgid "Sync Plex artwork from the PMS (recommended)" 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 msgctxt "#30506" msgid "Sync Options" @@ -1193,6 +1200,16 @@ msgctxt "#39082" msgid "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" msgid "Log-out Plex Home User " msgstr "Plex Home Benutzer abmelden: " @@ -1254,11 +1271,11 @@ msgstr "Plex Media Server IP oder URL eingeben. Zum Beispiel:" msgctxt "#39217" msgid "" -"Does your Plex Media Server support SSL connections? (https instead of " -"http)?" +"Use HTTPS (SSL) connections? With Kodi 18 or later, HTTPS will likely not " +"work!" msgstr "" -"Unterstützt der Plex Media Server sichere SSL Verbindungen (https anstelle " -"von http)?" +"HTTPS (SSL) Verbindungen nutzen? Dies funktioniert u.U. nicht mit Kodi 18 " +"oder späteren Versionen!" msgctxt "#39218" msgid "Error contacting PMS" @@ -1308,11 +1325,10 @@ msgctxt "#39227" msgid "Logged in to 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" -msgid "Plex user:" -msgstr "Plex Benutzer:" +msgid "Plex admin user" +msgstr "Plex Admin Benutzer" # Error message if user could not log in; the actual user name will be # appended at the end of the string @@ -1320,6 +1336,16 @@ msgctxt "#39229" msgid "Login failed with plex.tv for user" 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" msgid "" "Running the image cache process can take some time. It will happen in the " From 995d5a5ba8088fdad9aa57bd4bfe1c5d070b180b Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 3 Feb 2019 20:53:32 +0100 Subject: [PATCH 9/9] Stable and beta version bump 2.6.4 --- README.md | 4 ++-- addon.xml | 13 +++++++++++-- changelog.txt | 9 +++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1f17b037..fbf17c56 100644 --- a/README.md +++ b/README.md @@ -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) -[![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) +[![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.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) [![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq) diff --git a/addon.xml b/addon.xml index 3a238aee..06f4b178 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - + @@ -77,7 +77,16 @@ Нативна інтеграція Plex в Kodi Підключає Kodi до серверу Plex. Цей плагін передбачає, що ви керуєте всіма своїми відео за допомогою Plex (і ніяк не Kodi). Ви можете втратити дані, які вже зберігаються у відео та музичних БД Kodi (оскільки цей плагін безпосередньо їх змінює). Використовуйте на свій страх і ризик! Використовуйте на свій ризик - version 2.6.3: + 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 version 2.6.2: diff --git a/changelog.txt b/changelog.txt index f5610858..1c060382 100644 --- a/changelog.txt +++ b/changelog.txt @@ -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: - Fix PKC crashing on Xbox