From 983fd168bb6601c74294f13858cc7f8445bd622c Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 19 Feb 2017 15:57:31 +0100 Subject: [PATCH 01/55] Fix ratings for movies - Fixes #71 --- resources/lib/itemtypes.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/resources/lib/itemtypes.py b/resources/lib/itemtypes.py index 35809efe..5c419afd 100644 --- a/resources/lib/itemtypes.py +++ b/resources/lib/itemtypes.py @@ -316,14 +316,14 @@ class Movies(Items): # Update the movie entry if v.KODIVERSION >= 17: # update new ratings Kodi 17 - ratingid = self.kodi_db.get_ratingid(movieid, - v.KODI_TYPE_MOVIE) + rating_id = self.kodi_db.get_ratingid(movieid, + v.KODI_TYPE_MOVIE) self.kodi_db.update_ratings(movieid, v.KODI_TYPE_MOVIE, "default", rating, votecount, - ratingid) + rating_id) # update new uniqueid Kodi 17 uniqueid = self.kodi_db.get_uniqueid(movieid, v.KODI_TYPE_MOVIE) @@ -342,10 +342,10 @@ class Movies(Items): WHERE idMovie = ? ''' kodicursor.execute(query, (title, plot, shortplot, tagline, - votecount, rating, writer, year, imdb, sorttitle, runtime, - mpaa, genre, director, title, studio, trailer, country, - playurl, pathid, fileid, year, userdata['UserRating'], - movieid)) + votecount, rating_id, writer, year, imdb, sorttitle, + runtime, mpaa, genre, director, title, studio, trailer, + country, playurl, pathid, fileid, year, + userdata['UserRating'], movieid)) else: query = ''' UPDATE movie @@ -365,7 +365,8 @@ class Movies(Items): log.info("ADD movie itemid: %s - Title: %s" % (itemid, title)) if v.KODIVERSION >= 17: # add new ratings Kodi 17 - self.kodi_db.add_ratings(self.kodi_db.create_entry_rating(), + rating_id = self.kodi_db.create_entry_rating() + self.kodi_db.add_ratings(rating_id, movieid, v.KODI_TYPE_MOVIE, "default", @@ -385,9 +386,9 @@ class Movies(Items): ?, ?, ?, ?, ?, ?, ?) ''' kodicursor.execute(query, (movieid, fileid, title, plot, - shortplot, tagline, votecount, rating, writer, year, imdb, - sorttitle, runtime, mpaa, genre, director, title, studio, - trailer, country, playurl, pathid, year, + shortplot, tagline, votecount, rating_id, writer, year, + imdb, sorttitle, runtime, mpaa, genre, director, title, + studio, trailer, country, playurl, pathid, year, userdata['UserRating'])) else: query = ''' From 2079b3c554c13fb23fcfe58bdac63be6cab4a205 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 19 Feb 2017 16:18:34 +0100 Subject: [PATCH 02/55] Always run only one instance of PKC --- service.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/service.py b/service.py index 02b658ad..131d6189 100644 --- a/service.py +++ b/service.py @@ -334,14 +334,23 @@ class Service(): downloadutils.DownloadUtils().stopSession() except: pass - + window('plex_service_started', clear=True) log.warn("======== STOP %s ========" % v.ADDON_NAME) +# Safety net - Kody starts PKC twice upon first installation! +if window('plex_service_started') == 'true': + exit = True +else: + window('plex_service_started', value='true') + exit = False + # Delay option delay = int(settings('startupDelay')) log.warn("Delaying Plex startup by: %s sec..." % delay) -if delay and Monitor().waitForAbort(delay): +if exit: + log.error('PKC service.py already started - exiting this instance') +elif delay and Monitor().waitForAbort(delay): # Start the service log.warn("Abort requested while waiting. PKC not started.") else: From b4a44c9d7d0b9c290e493f41b534dc4dd1b7661c Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 19 Feb 2017 16:20:00 +0100 Subject: [PATCH 03/55] Only 1 colon : when logging --- resources/lib/loghandler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/loghandler.py b/resources/lib/loghandler.py index 51c22aff..c4e34188 100644 --- a/resources/lib/loghandler.py +++ b/resources/lib/loghandler.py @@ -63,7 +63,7 @@ class MyFormatter(logging.Formatter): # Replace the original format with one customized by logging level if record.levelno in (logging.DEBUG, logging.ERROR): - self._fmt = '%(name)s -> %(levelname)s:: %(message)s' + self._fmt = '%(name)s -> %(levelname)s: %(message)s' # Call the original formatter class to do the grunt work result = logging.Formatter.format(self, record) From 861736d8d3dfc33e8804ef0dbcfd6105e926f64c Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 19 Feb 2017 17:07:42 +0100 Subject: [PATCH 04/55] Fixes to Plex Companion - Fixes #224 --- resources/lib/PlexCompanion.py | 3 +- resources/lib/plexbmchelper/plexgdm.py | 125 +++++++++++++------------ 2 files changed, 68 insertions(+), 60 deletions(-) diff --git a/resources/lib/PlexCompanion.py b/resources/lib/PlexCompanion.py index 6d996151..03780c49 100644 --- a/resources/lib/PlexCompanion.py +++ b/resources/lib/PlexCompanion.py @@ -35,7 +35,7 @@ class PlexCompanion(Thread): # Start GDM for server/client discovery self.client = plexgdm.plexgdm() self.client.clientDetails(self.settings) - log.debug("Registration string is: %s " + log.debug("Registration string is:\n%s" % self.client.getClientDetails()) # kodi player instance self.player = player.Player() @@ -188,6 +188,7 @@ class PlexCompanion(Thread): log.debug("Client is no longer registered. " "Plex Companion still running on port %s" % self.settings['myport']) + client.register_as_client() # Get and set servers if message_count % 30 == 0: subscriptionManager.serverlist = client.getServerList() diff --git a/resources/lib/plexbmchelper/plexgdm.py b/resources/lib/plexbmchelper/plexgdm.py index 94fc492e..488dbf54 100644 --- a/resources/lib/plexbmchelper/plexgdm.py +++ b/resources/lib/plexbmchelper/plexgdm.py @@ -57,23 +57,22 @@ class plexgdm: self._discovery_is_running = False self._registration_is_running = False - self.discovery_complete = False self.client_registered = False self.download = downloadutils.DownloadUtils().downloadUrl def clientDetails(self, options): self.client_data = ( - "Content-Type: plex/media-player\r\n" - "Resource-Identifier: %s\r\n" - "Name: %s\r\n" - "Port: %s\r\n" - "Product: %s\r\n" - "Version: %s\r\n" - "Protocol: plex\r\n" - "Protocol-Version: 1\r\n" + "Content-Type: plex/media-player\n" + "Resource-Identifier: %s\n" + "Name: %s\n" + "Port: %s\n" + "Product: %s\n" + "Version: %s\n" + "Protocol: plex\n" + "Protocol-Version: 1\n" "Protocol-Capabilities: timeline,playback,navigation," - "playqueues\r\n" - "Device-Class: HTPC" + "playqueues\n" + "Device-Class: HTPC\n" ) % ( options['uuid'], options['client_name'], @@ -86,10 +85,25 @@ class plexgdm: def getClientDetails(self): return self.client_data + def register_as_client(self): + """ + Registers PKC's Plex Companion to the PMS + """ + try: + log.debug("Sending registration data: HELLO %s\n%s" + % (self.client_header, self.client_data)) + self.update_sock.sendto("HELLO %s\n%s" + % (self.client_header, self.client_data), + self.client_register_group) + log.debug('(Re-)registering PKC Plex Companion successful') + except: + log.error("Unable to send registration message") + def client_update(self): - update_sock = socket.socket(socket.AF_INET, - socket.SOCK_DGRAM, - socket.IPPROTO_UDP) + self.update_sock = socket.socket(socket.AF_INET, + socket.SOCK_DGRAM, + socket.IPPROTO_UDP) + update_sock = self.update_sock # Set socket reuse, may not work on all OSs. try: @@ -129,16 +143,9 @@ class plexgdm: self._multicast_address) + socket.inet_aton('0.0.0.0')) update_sock.setblocking(0) - log.debug("Sending registration data: HELLO %s\r\n%s" - % (self.client_header, self.client_data)) # Send initial client registration - try: - update_sock.sendto("HELLO %s\r\n%s" - % (self.client_header, self.client_data), - self.client_register_group) - except: - log.error("Unable to send registration message") + self.register_as_client() # Now, listen format client discovery reguests and respond. while self._registration_is_running: @@ -153,7 +160,7 @@ class plexgdm: log.debug("Detected client discovery request from %s. " " Replying" % str(addr)) try: - update_sock.sendto("HTTP/1.0 200 OK\r\n%s" + update_sock.sendto("HTTP/1.0 200 OK\n%s" % self.client_data, addr) except: @@ -165,10 +172,10 @@ class plexgdm: log.info("Client Update loop stopped") # When we are finished, then send a final goodbye message to # deregister cleanly. - log.debug("Sending registration data: BYE %s\r\n%s" + log.debug("Sending registration data: BYE %s\n%s" % (self.client_header, self.client_data)) try: - update_sock.sendto("BYE %s\r\n%s" + update_sock.sendto("BYE %s\n%s" % (self.client_header, self.client_data), self.client_register_group) except: @@ -176,41 +183,41 @@ class plexgdm: self.client_registered = False def check_client_registration(self): + if not self.client_registered: + log.debug('Client has not been marked as registered') + return False + if not self.server_list: + log.info("Server list is empty. Unable to check") + return False + for server in self.server_list: + if server['uuid'] == window('plex_machineIdentifier'): + media_server = server['server'] + media_port = server['port'] + scheme = server['protocol'] + break + else: + log.info("Did not find our server!") + return False - if self.client_registered and self.discovery_complete: - if not self.server_list: - log.info("Server list is empty. Unable to check") - return False - try: - for server in self.server_list: - if server['uuid'] == window('plex_machineIdentifier'): - media_server = server['server'] - media_port = server['port'] - scheme = server['protocol'] - break - else: - log.info("Did not find our server!") - return False - - log.debug("Checking server [%s] on port [%s]" - % (media_server, media_port)) - client_result = self.download( - '%s://%s:%s/clients' % (scheme, media_server, media_port)) - registered = False - for client in client_result: - if (client.attrib.get('machineIdentifier') == - self.client_id): - registered = True - if registered: - log.debug("Client registration successful. " - "Client data is: %s" % client_result) - return True - else: - log.info("Client registration not found. " - "Client data is: %s" % client_result) - except: - log.error("Unable to check status") - pass + log.debug("Checking server [%s] on port [%s]" + % (media_server, media_port)) + xml = self.download( + '%s://%s:%s/clients' % (scheme, media_server, media_port)) + try: + xml[0].attrib + except (TypeError, IndexError, AttributeError): + log.error('Could not download clients for %s' % media_server) + return False + registered = False + for client in xml: + if (client.attrib.get('machineIdentifier') == + self.client_id): + registered = True + if registered: + return True + else: + log.info("Client registration not found. " + "Client data is: %s" % xml) return False def getServerList(self): From 7f72ea76e6149040a117a04f0be9261430900dff Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 19 Feb 2017 17:09:54 +0100 Subject: [PATCH 05/55] Version bump --- addon.xml | 2 +- changelog.txt | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/addon.xml b/addon.xml index ffa9119f..a6d82382 100644 --- a/addon.xml +++ b/addon.xml @@ -1,7 +1,7 @@ diff --git a/changelog.txt b/changelog.txt index 7f14294e..a2171b48 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,8 @@ +version 1.5.15 (beta only) +- Fix ratings for movies +- Fixes to Plex Companion +- Always run only one instance of PKC + version 1.5.14 (beta only) - Krypton: Fix ratings for episodes and TV shows - Plex Companion: Fix KeyError for Plex Web From 5136424b37c1623b3e11378e4e571908cb8f42f5 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Mon, 20 Feb 2017 20:14:32 +0100 Subject: [PATCH 06/55] Version bump --- addon.xml | 2 +- changelog.txt | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/addon.xml b/addon.xml index a6d82382..7779b9ee 100644 --- a/addon.xml +++ b/addon.xml @@ -1,7 +1,7 @@ diff --git a/changelog.txt b/changelog.txt index a2171b48..9d601f6f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,22 @@ +version 1.6.0 +A DATABASE RESET IS ABSOLUTELY NECESSARY if you're not using beta PKC +Make previous version available for everyone. The highlights: +- New Spanish translation, thanks @bartolomesoriano +- New Czech translation, thanks @Pavuucek +- Plex Companion is completely rewired and should now handly anything you throw at it +- Early compatibility with Kodi 18 Leia +- New playback startup mechanism for plugin paths +- Code rebranding from Emby to Plex, including a plex.db database :-) +- Fixes to Kodi ratings +- Fix playstate and PMS item changes not working/not propagating anymore (caused by a change Plex made with the websocket interface) +- Improvements to the way PKC behaves if the PMS goes offline +- New setting to always transcode if the video bitrate is above a certain threshold (will not work with direct paths) +- Be smarter when deciding when to transcode +- Only sign the user out if the PMS says so +- Cache missing artwork on PKC startup +- Lots of code refactoring and code optimizations +- Tons of fixes + version 1.5.15 (beta only) - Fix ratings for movies - Fixes to Plex Companion From 36d57c24528129e00702d45312a95a6307bc4fc3 Mon Sep 17 00:00:00 2001 From: AllanMar Date: Tue, 21 Feb 2017 20:22:59 -0400 Subject: [PATCH 07/55] Fix Plex Web Issues Causes issues with Plex web due to Access-Control-Allow-Headers missing header. --- resources/lib/plexbmchelper/listener.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/plexbmchelper/listener.py b/resources/lib/plexbmchelper/listener.py index a3728b52..c3901ba7 100644 --- a/resources/lib/plexbmchelper/listener.py +++ b/resources/lib/plexbmchelper/listener.py @@ -58,7 +58,7 @@ class MyHandler(BaseHTTPRequestHandler): 'x-plex-version, x-plex-platform-version, x-plex-username, ' 'x-plex-client-identifier, x-plex-target-client-identifier, ' 'x-plex-device-name, x-plex-platform, x-plex-product, accept, ' - 'x-plex-device') + 'x-plex-device, x-plex-device-screen-resolution') self.end_headers() self.wfile.close() From 0b5c138709baa2a11e0b7a8092381b42a4ad03ed Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 26 Feb 2017 15:16:03 +0100 Subject: [PATCH 08/55] Better error handling for Plex Companion playqueues - Partially solves #236 --- resources/lib/playbackutils.py | 5 ++- resources/lib/playlist_func.py | 60 +++++++++++++++++++--------------- resources/lib/playqueue.py | 7 ++-- 3 files changed, 41 insertions(+), 31 deletions(-) diff --git a/resources/lib/playbackutils.py b/resources/lib/playbackutils.py index d9cd867e..14e8894f 100644 --- a/resources/lib/playbackutils.py +++ b/resources/lib/playbackutils.py @@ -138,7 +138,10 @@ class PlaybackUtils(): plex_lib_UUID, mediatype=api.getType(), trailers=trailers) - get_playlist_details_from_xml(playqueue, xml=xml) + try: + get_playlist_details_from_xml(playqueue, xml=xml) + except KeyError: + return if (not homeScreen and not seektime and sizePlaylist < 2 and window('plex_customplaylist') != "true" and diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index f5afa8b6..1485d9b5 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -209,15 +209,14 @@ def update_playlist_from_PMS(playlist, playlist_id=None, xml=None): """ if xml is None: xml = get_PMS_playlist(playlist, playlist_id) - try: - xml.attrib['%sVersion' % playlist.kind] - except: - log.error('Could not process Plex playlist') - return # Clear our existing playlist and the associated Kodi playlist playlist.clear() # Set new values - get_playlist_details_from_xml(playlist, xml) + try: + get_playlist_details_from_xml(playlist, xml) + except KeyError: + log.error('Could not update playlist from PMS') + return for plex_item in xml: playlist_item = add_to_Kodi_playlist(playlist, plex_item) if playlist_item is not None: @@ -231,19 +230,23 @@ def init_Plex_playlist(playlist, plex_id=None, kodi_item=None): WILL ALSO UPDATE OUR PLAYLISTS """ log.debug('Initializing the playlist %s on the Plex side' % playlist) - if plex_id: - item = playlist_item_from_plex(plex_id) - else: - item = playlist_item_from_kodi(kodi_item) - params = { - 'next': 0, - 'type': playlist.type, - 'uri': item.uri - } - xml = DU().downloadUrl(url="{server}/%ss" % playlist.kind, - action_type="POST", - parameters=params) - get_playlist_details_from_xml(playlist, xml) + try: + if plex_id: + item = playlist_item_from_plex(plex_id) + else: + item = playlist_item_from_kodi(kodi_item) + params = { + 'next': 0, + 'type': playlist.type, + 'uri': item.uri + } + xml = DU().downloadUrl(url="{server}/%ss" % playlist.kind, + action_type="POST", + parameters=params) + get_playlist_details_from_xml(playlist, xml) + except KeyError: + log.error('Could not init Plex playlist') + return item.ID = xml[-1].attrib['%sItemID' % playlist.kind] playlist.items.append(item) log.debug('Initialized the playlist on the Plex side: %s' % playlist) @@ -305,7 +308,11 @@ def add_item_to_PMS_playlist(playlist, pos, plex_id=None, kodi_item=None): log.debug('Adding new item plex_id: %s, kodi_item: %s on the Plex side at ' 'position %s for %s' % (plex_id, kodi_item, pos, playlist)) if plex_id: - item = playlist_item_from_plex(plex_id) + try: + item = playlist_item_from_plex(plex_id) + except KeyError: + log.error('Could not add new item to the PMS playlist') + return else: item = playlist_item_from_kodi(kodi_item) url = "{server}/%ss/%s?uri=%s" % (playlist.kind, playlist.ID, item.uri) @@ -418,11 +425,9 @@ def refresh_playlist_from_PMS(playlist): """ xml = get_PMS_playlist(playlist) try: - xml.attrib['%sVersion' % playlist.kind] - except: - log.error('Could not download Plex playlist.') - return - get_playlist_details_from_xml(playlist, xml) + get_playlist_details_from_xml(playlist, xml) + except KeyError: + log.error('Could not refresh playlist from PMS') def delete_playlist_item_from_PMS(playlist, pos): @@ -469,8 +474,9 @@ def get_kodi_playqueues(): try: queues = queues['result'] except KeyError: - raise KeyError('Could not get Kodi playqueues. JSON Result was: %s' - % queues) + log.error('Could not get Kodi playqueues. JSON Result was: %s' + % queues) + queues = [] return queues diff --git a/resources/lib/playqueue.py b/resources/lib/playqueue.py index a07ec325..0e8939f1 100644 --- a/resources/lib/playqueue.py +++ b/resources/lib/playqueue.py @@ -85,11 +85,12 @@ class Playqueue(Thread): '%s, repeat %s' % (playqueue_id, offset, repeat)) with lock: xml = PL.get_PMS_playlist(playqueue, playqueue_id) - if xml is None: + playqueue.clear() + try: + PL.get_playlist_details_from_xml(playqueue, xml) + except KeyError: log.error('Could not get playqueue ID %s' % playqueue_id) return - playqueue.clear() - PL.get_playlist_details_from_xml(playqueue, xml) PlaybackUtils(xml, playqueue).play_all() playqueue.repeat = 0 if not repeat else int(repeat) window('plex_customplaylist', value="true") From 82e7530816924f024de98ece58cad4c5973c8891 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 26 Feb 2017 15:21:55 +0100 Subject: [PATCH 09/55] Fix TypeError - Partially fixes #236 --- resources/lib/initialsetup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index 7bae1bde..4e9ad834 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -6,13 +6,12 @@ import logging import xbmc import xbmcgui -from utils import settings, window, language as lang +from utils import settings, window, language as lang, tryEncode import downloadutils from userclient import UserClient from PlexAPI import PlexAPI from PlexFunctions import GetMachineIdentifier, get_PMS_settings -import variables as v ############################################################################### @@ -257,7 +256,8 @@ class InitialSetup(): log.warn('Not authorized even though we are signed ' ' in to plex.tv correctly') self.dialog.ok(lang(29999), '%s %s' - % lang(39214) + server['name']) + % (lang(39214), + tryEncode(server['name']))) return else: return From 16acca16b9ff0ad5ed3861d7562b12e6562e4ca8 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 26 Feb 2017 16:01:35 +0100 Subject: [PATCH 10/55] Fix UnicodeDecodeError for non-ASCII filenames - Fixes #229 --- resources/lib/playlist_func.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index 1485d9b5..6714c900 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -3,7 +3,7 @@ from urllib import quote import plexdb_functions as plexdb from downloadutils import DownloadUtils as DU -from utils import JSONRPC, tryEncode +from utils import JSONRPC, tryEncode, tryDecode from PlexAPI import API ############################################################################### @@ -36,7 +36,7 @@ class Playlist_Object_Baseclase(object): answ += "items: %s, " % self.items for key in self.__dict__: if key not in ("ID", 'items'): - answ += '%s: %s, ' % (key, getattr(self, key)) + answ += '%s: %s, ' % (key, tryDecode(str(getattr(self, key)))) return answ[:-2] + ">" def clear(self): @@ -80,7 +80,7 @@ class Playlist_Item(object): def __repr__(self): answ = "<%s: " % (self.__class__.__name__) for key in self.__dict__: - answ += '%s: %s, ' % (key, getattr(self, key)) + answ += '%s: %s, ' % (key, tryDecode(str(getattr(self, key)))) return answ[:-2] + ">" From e03af767ca962e93b5e20daad8a88f7c7113b741 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 26 Feb 2017 16:12:39 +0100 Subject: [PATCH 11/55] Remove `\r` for Plex Companion replies --- resources/lib/plexbmchelper/functions.py | 2 +- resources/lib/plexbmchelper/listener.py | 4 ++-- resources/lib/plexbmchelper/subscribers.py | 4 ++-- resources/lib/websocket.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/lib/plexbmchelper/functions.py b/resources/lib/plexbmchelper/functions.py index 5885253d..cdda52fe 100644 --- a/resources/lib/plexbmchelper/functions.py +++ b/resources/lib/plexbmchelper/functions.py @@ -57,7 +57,7 @@ def plex_type(xbmc_type): def getXMLHeader(): - return '\r\n' + return '\n' def getOKMsg(): diff --git a/resources/lib/plexbmchelper/listener.py b/resources/lib/plexbmchelper/listener.py index a3728b52..316226d7 100644 --- a/resources/lib/plexbmchelper/listener.py +++ b/resources/lib/plexbmchelper/listener.py @@ -101,10 +101,10 @@ class MyHandler(BaseHTTPRequestHandler): params.get('commandID', False)) if request_path == "version": self.response( - "PlexKodiConnect Plex Companion: Running\r\nVersion: %s" + "PlexKodiConnect Plex Companion: Running\nVersion: %s" % settings['version']) elif request_path == "verify": - self.response("XBMC JSON connection test:\r\n" + + self.response("XBMC JSON connection test:\n" + js.jsonrpc("ping")) elif "resources" == request_path: resp = ('%s' diff --git a/resources/lib/plexbmchelper/subscribers.py b/resources/lib/plexbmchelper/subscribers.py index 41cb52da..86cde736 100644 --- a/resources/lib/plexbmchelper/subscribers.py +++ b/resources/lib/plexbmchelper/subscribers.py @@ -71,7 +71,7 @@ class SubscriptionManager: msg += self.getTimelineXML(self.js.getAudioPlayerId(players), plex_audio()) msg += self.getTimelineXML(self.js.getPhotoPlayerId(players), plex_photo()) msg += self.getTimelineXML(self.js.getVideoPlayerId(players), plex_video()) - msg += "\r\n" + msg += "\n" return msg def getTimelineXML(self, playerid, ptype): @@ -84,7 +84,7 @@ class SubscriptionManager: else: state = "stopped" time = 0 - ret = "\r\n"+' Date: Sun, 26 Feb 2017 16:14:46 +0100 Subject: [PATCH 12/55] Newline in logging xml --- resources/lib/plexbmchelper/subscribers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/plexbmchelper/subscribers.py b/resources/lib/plexbmchelper/subscribers.py index 86cde736..463807fe 100644 --- a/resources/lib/plexbmchelper/subscribers.py +++ b/resources/lib/plexbmchelper/subscribers.py @@ -312,7 +312,7 @@ class Subscriber: else: self.navlocationsent = True msg = re.sub(r"INSERTCOMMANDID", str(self.commandID), msg) - log.debug("sending xml to subscriber %s: %s" % (self.tostr(), msg)) + log.debug("sending xml to subscriber %s:\n%s" % (self.tostr(), msg)) url = self.protocol + '://' + self.host + ':' + self.port \ + "/:/timeline" t = threading.Thread(target=self.threadedSend, args=(url, msg)) From f8bfb981eff210150f9ac01e737700813e57006a Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 26 Feb 2017 16:29:19 +0100 Subject: [PATCH 13/55] Better error handling for Plex Companion - Partially fixes #233 --- resources/lib/playbackutils.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/resources/lib/playbackutils.py b/resources/lib/playbackutils.py index 14e8894f..fabbda83 100644 --- a/resources/lib/playbackutils.py +++ b/resources/lib/playbackutils.py @@ -290,16 +290,19 @@ class PlaybackUtils(): self.currentPosition = 0 for item in self.xml: api = API(item) + successful = True if api.getType() == v.PLEX_TYPE_CLIP: self.add_trailer(item) else: with Get_Plex_DB() as plex_db: db_item = plex_db.getItem_byId(api.getRatingKey()) if db_item is not None: - if add_item_to_kodi_playlist(self.playqueue, - self.currentPosition, - kodi_id=db_item[0], - kodi_type=db_item[4]) is True: + successful = add_item_to_kodi_playlist( + self.playqueue, + self.currentPosition, + kodi_id=db_item[0], + kodi_type=db_item[4]) + if successful is True: self.currentPosition += 1 if len(item[0]) > 1: self.add_part(item, @@ -309,8 +312,9 @@ class PlaybackUtils(): else: # Item not in Kodi DB self.add_trailer(item) - self.playqueue.items[self.currentPosition - 1].ID = item.get( - '%sItemID' % self.playqueue.kind) + if successful is True: + self.playqueue.items[self.currentPosition - 1].ID = item.get( + '%sItemID' % self.playqueue.kind) def add_trailer(self, item): # Playurl needs to point back so we can get metadata! From fdd07b77e38649e5d5562d8683e5788447fdfaca Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 26 Feb 2017 16:40:04 +0100 Subject: [PATCH 14/55] Fix ValueError for Watch Later - Fixes #235 --- resources/lib/PlexAPI.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/lib/PlexAPI.py b/resources/lib/PlexAPI.py index 37350607..e78621d5 100644 --- a/resources/lib/PlexAPI.py +++ b/resources/lib/PlexAPI.py @@ -1256,26 +1256,26 @@ class API(): favorite = False try: playcount = int(item['viewCount']) - except KeyError: + except (KeyError, ValueError): playcount = None played = True if playcount else False try: lastPlayedDate = DateToKodi(int(item['lastViewedAt'])) - except KeyError: + except (KeyError, ValueError): lastPlayedDate = None try: userrating = int(float(item['userRating'])) - except KeyError: + except (KeyError, ValueError): userrating = 0 try: rating = float(item['audienceRating']) - except KeyError: + except (KeyError, ValueError): try: rating = float(item['rating']) - except KeyError: + except (KeyError, ValueError): rating = 0.0 resume, runtime = self.getRuntime() From 39d3e8acc9a379b075a83b89aab0a31c01d3072d Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 26 Feb 2017 17:17:39 +0100 Subject: [PATCH 15/55] Fix WebSocketException: Invalid header --- resources/lib/websocket.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/lib/websocket.py b/resources/lib/websocket.py index 03791381..6d488e57 100644 --- a/resources/lib/websocket.py +++ b/resources/lib/websocket.py @@ -485,7 +485,7 @@ class WebSocket(object): headers.append("") headers.append("") - header_str = "\n".join(headers) + header_str = "\r\n".join(headers) self._send(header_str) if traceEnabled: log.debug("--- request header ---") @@ -530,7 +530,7 @@ class WebSocket(object): while True: line = self._recv_line() - if line == "\n": + if line == "\r\n": break line = line.strip() if traceEnabled: From 84f7aba5d1c3a74326e7af35955e92eb1450a66a Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 26 Feb 2017 18:04:54 +0100 Subject: [PATCH 16/55] Try to skip new PMS items we've already processed --- resources/lib/librarysync.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/resources/lib/librarysync.py b/resources/lib/librarysync.py index 6c95b56d..8de3a96a 100644 --- a/resources/lib/librarysync.py +++ b/resources/lib/librarysync.py @@ -321,6 +321,11 @@ class LibrarySync(Thread): def __init__(self, callback=None): self.mgr = callback + # Dict of items we just processed in order to prevent a reprocessing + # caused by websocket + self.just_processed = {} + # How long do we wait until we start re-processing? (in seconds) + self.ignore_just_processed = 10*60 self.itemsToProcess = [] self.sessionKeys = [] self.fanartqueue = Queue.Queue() @@ -532,6 +537,9 @@ class LibrarySync(Thread): # True: we're syncing only the delta, e.g. different checksum self.compare = not repair + # Empty our list of item's we've just processed in the past + self.just_processed = {} + self.new_items_only = True # This will also update playstates and userratings! log.info('Running fullsync for NEW PMS items with repair=%s' % repair) @@ -884,6 +892,7 @@ class LibrarySync(Thread): self.allPlexElementsId APPENDED(!!) dict = {itemid: checksum} """ + now = getUnixTimestamp() if self.new_items_only is True: # Only process Plex items that Kodi does not already have in lib for item in xml: @@ -903,6 +912,7 @@ class LibrarySync(Thread): 'title': item.attrib.get('title', 'Missing Title'), 'mediaType': item.attrib.get('type') }) + self.just_processed[itemId] = now return if self.compare: @@ -928,6 +938,7 @@ class LibrarySync(Thread): 'title': item.attrib.get('title', 'Missing Title'), 'mediaType': item.attrib.get('type') }) + self.just_processed[itemId] = now else: # Initial or repair sync: get all Plex movies for item in xml: @@ -946,6 +957,7 @@ class LibrarySync(Thread): 'title': item.attrib.get('title', 'Missing Title'), 'mediaType': item.attrib.get('type') }) + self.just_processed[itemId] = now def GetAndProcessXMLs(self, itemType): """ @@ -1450,6 +1462,8 @@ class LibrarySync(Thread): continue else: successful = self.process_newitems(item) + if successful: + self.just_processed[str(item['ratingKey'])] = now if successful and settings('FanartTV') == 'true': plex_type = v.PLEX_TYPE_FROM_WEBSOCKET[item['type']] if plex_type in (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW): @@ -1534,6 +1548,7 @@ class LibrarySync(Thread): PMS is messing with the library items, e.g. new or changed. Put in our "processing queue" for later """ + now = getUnixTimestamp() for item in data: if 'tv.plex' in item.get('identifier', ''): # Ommit Plex DVR messages - the Plex IDs are not corresponding @@ -1548,6 +1563,14 @@ class LibrarySync(Thread): if plex_id == '0': log.error('Received malformed PMS message: %s' % item) continue + try: + if (now - self.just_processed[plex_id] < + self.ignore_just_processed and state != 9): + log.debug('We just processed %s: ignoring' % plex_id) + continue + except KeyError: + # Item has NOT just been processed + pass # Have we already added this element? for existingItem in self.itemsToProcess: if existingItem['ratingKey'] == plex_id: From d021bd29b25491c83ffe60c0d2d46a87a7501ac8 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 26 Feb 2017 18:07:56 +0100 Subject: [PATCH 17/55] Version bump --- addon.xml | 2 +- changelog.txt | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/addon.xml b/addon.xml index 7779b9ee..2a2fac92 100644 --- a/addon.xml +++ b/addon.xml @@ -1,7 +1,7 @@ diff --git a/changelog.txt b/changelog.txt index 9d601f6f..96348315 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,10 @@ +version 1.6.1 +- Fix UnicodeDecodeError for non-ASCII filenames +- Better error handling for Plex Companion +- Fix ValueError for Watch Later +- Try to skip new PMS items we've already processed +- Fix TypeError + version 1.6.0 A DATABASE RESET IS ABSOLUTELY NECESSARY if you're not using beta PKC Make previous version available for everyone. The highlights: From 9c222748421e36697eae000d3ace64e37fa0f338 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 26 Feb 2017 18:12:43 +0100 Subject: [PATCH 18/55] New Danish translation --- addon.xml | 2 + changelog.txt | 1 + resources/language/Danish/strings.xml | 516 ++++++++++++++++++++++++++ 3 files changed, 519 insertions(+) create mode 100644 resources/language/Danish/strings.xml diff --git a/addon.xml b/addon.xml index 2a2fac92..e2a72166 100644 --- a/addon.xml +++ b/addon.xml @@ -27,12 +27,14 @@ Úplná integrace Plexu do Kodi Komplette Integration von Plex in Kodi Native Integration of Plex into Kodi + Indbygget Integration af Plex i Kodi Connect Kodi to your Plex Media Server. This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases (as this plugin directly changes them). Use at your own risk! Connect Kodi to your Plex Media Server. This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases (as this plugin directly changes them). Use at your own risk! Connect Kodi to your Plex Media Server. This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases (as this plugin directly changes them). Use at your own risk! Připojte Kodi ke svému Plex Media Serveru. Tento doplněk předpokládá, že spravujete veškerá svá videa pomocí Plexu (nikoliv pomocí Kodi). Můžete přijít o data uložená ve video a hudební databázi Kodi (tento doplněk je přímo mění). Používejte na vlastní nebezpečí! Verbindet Kodi mit deinem Plex Media Server. Dieses Addon geht davon aus, dass du all deine Videos mit Plex verwaltest (und keine direkt mit Kodi). Du wirst möglicherweise Daten verlieren, die bereits in der Kodi Video- und/oder Musik-Datenbank gespeichert sind (da dieses Addon beide Datenbanken direkt verändert). Verwende auf eigene Gefahr! Connect Kodi to your Plex Media Server. This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases (as this plugin directly changes them). Use at your own risk! + Tilslut Kodi til din Plex Media Server. Dette plugin forudsætter, at du styre alle dine videoer med Plex (og ikke med Kodi). Du kan miste data som allerede er gemt i Kodi video og musik-databaser (dette plugin ændrer direkte i dem). Brug på egen risiko! all GPL v2.0 https://forums.plex.tv diff --git a/changelog.txt b/changelog.txt index 96348315..806ed41e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,5 @@ version 1.6.1 +- New Danish translation, thanks @Osberg - Fix UnicodeDecodeError for non-ASCII filenames - Better error handling for Plex Companion - Fix ValueError for Watch Later diff --git a/resources/language/Danish/strings.xml b/resources/language/Danish/strings.xml new file mode 100644 index 00000000..d524d260 --- /dev/null +++ b/resources/language/Danish/strings.xml @@ -0,0 +1,516 @@ + + + + PlexKodiConnect + Server Address (IP) + Preferred playback method + Log level + Username: + Password: + Network Username: + Network Password: + Transcode: + Enable Performance Profiling + Local caching system + OK + Never show + + Connection + Network + Device Name + Unauthorized for PMS + + Advanced + Username + + Display message if PMS goes offline + + Port Number + I own this Plex Media Server + Number of recent Movies to show: + Number of recent TV episodes to show: + Number of recent Music Albums to show: + Mark watched at start of playback: + Set Season poster for episodes + + Genre Filter ... + Play All from Here + Refresh + Delete + Add Movie to CouchPotato + + Incorrect Username/Password + Username not found + + Deleting + Waiting for server to delete + + Server Default + Title + Year + Premiere Date + Date Created + Critic Rating + Community Rating + Play Count + Budget + + + Sort By + + None + Action + Adventure + Animation + Crime + Comedy + Documentary + Drama + Fantasy + Foreign + History + Horror + Music + Musical + Mystery + Romance + Science Fiction + Short + Suspense + Thriller + Western + + Genre Filter + Confirm file deletion + Delete this item? This action will delete media and associated data files. + + Mark Watched + Mark Unwatched + Add to Favorites + Remove from Favorites + Sort By ... + Sort Order Descending + Sort Order Ascending + Show People + + + Resume + Resume from + Start from beginning + + Interface + Include Stream Info + Include People + Include Overview + Offer delete after playback + For Episodes + For Movies + Background Art Refresh Rate (seconds) + Add Resume Percent + Add Episode Number + Show Load Progress + Loading Content + Retrieving Data + Done + Processing Item : + Play Error + This item is not playable + Local path detected + Your MB3 Server contains local paths. Please change server paths to UNC or change XBMB3C setting 'Play from Stream' to true. Path: + Warning + Debug logging enabled. + This will affect performance. + Error + Monitoring service is not running + If you have just installed please restart Kodi + Search + + Enable Theme Music (Requires Restart) + - Loop Theme Music + Enable Background Image (Requires Restart) + Services + + Always transcode if video bitrate is above + + Skin does not support setting views + Select item action (Requires Restart) + + Sort NextUp by Show Title + Enable Enhanced Images (eg CoverArt) + Metadata + Artwork + Video Quality if Transcoding necessary + + Enable Suggested Loader (Requires Restart) + Add Season Number + Flatten Seasons + + Direct Play - HTTP + Direct Play + Transcoding + Server Detection Succeeded + Found server + Address : + + + Recently Added TV Shows + In Progress TV Shows + All Music + Channels + Recently Added + Recently Added Episodes + Recently Added Albums + In Progress Movies + In Progress Episodes + Next Episodes + Favorite Movies + Favorite Shows + Favorite Episodes + Frequent Played Albums + Upcoming TV + BoxSets + Trailers + Music Videos + Photos + Unwatched Movies + Movie Genres + Movie Studios + Movie Actors + Unwatched Episodes + TV Genres + TV Networks + TV Actors + Playlists + Search + Set Views + + Select User + Profiling enabled. + Please remember to turn off when finished testing. + Error in ArtworkRotationThread + Unable to connect to server + Error in LoadMenuOptionsThread + + Enable Playlists Loader (Requires Restart) + + Songs + Albums + Album Artists + Artists + Music Genres + + Enable Theme Videos (Requires Restart) + - Loop Theme Videos + + AutoPlay remaining episodes in a season + Compress Artwork + Latest + In Progress + NextUp + User Views + Report Metrics + Use Kodi Sorting + Runtime + + Random + Recently releases + Random Items + Recommended + + Extras + Sync Theme Music + Sync Extra Fanart + Sync Movie BoxSets + + [COLOR yellow]Reset local Kodi database[/COLOR] + Enable watched/resume status sync + DB Sync Indication: + Play Count Sync Indication: + Enable HTTPS + Force Transcoding Codecs + + Enable Netflix style next up notification + - The number of seconds before the end to show the notification + Show Emby Info dialog on play/select action + Enable server connection message on startup + + Recently added Home Videos + Recently added Photos + Favourite Home Videos + Favourite Photos + Favourite Albums + + Recently added Music videos + In progress Music videos + Unwatched Music videos + + + Active + Clear Settings + Movies + BoxSets + Trailers + Series + Seasons + Episodes + Music Artists + Music Albums + Music Videos + Music Tracks + Channels + + + Plex options + Clear like for this item + Like this item + Dislike this item + Add to Plex favorites + Remove from Plex favorites + Set custom song rating + Plex addon settings + Delete item from server + Refresh this item + Set custom song rating (0-5) + Force transcode + Enable Plex context menu in Kodi + Could not delete the Plex item. Is item deletion enabled on the Plex Media Server? + Start playback via PMS + Settings for the Plex Server + + + Verify Host SSL Certificate (more secure) + Client SSL certificate + Use alternate address + Alternate Server Address + Use alternate device Name + [COLOR yellow]Reset login attempts[/COLOR] + Sync Options + Show syncing progress + Sync empty TV Shows + Enable Music Library + Direct stream music library + Playback Mode + Force artwork caching + Limit artwork cache threads (recommended for rpi) + Enable fast startup (requires server plugin) + Maximum items to request from the server at once + Playback + [COLOR yellow]Enter network credentials[/COLOR] + Enable Plex Trailers (Plexpass is needed) + Ask to play trailers + Skip Plex delete confirmation for the context menu (use at your own risk) + Jump back on resume (in seconds) + Force transcode h265/HEVC + Music metadata options (not compatible with direct stream) + Import music song rating directly from files + Convert music song rating to Emby rating + Allow rating in song files to be updated + Ignore specials in next episodes + Permanent users to add to the session + Startup delay (in seconds) + Enable server restart message + Enable new content notification + Duration of the video library pop up (in seconds) + Duration of the music library pop up (in seconds) + Server messages + [COLOR yellow]Generate a new unique device Id (e.g. when cloning Kodi)[/COLOR] + Users must log in every time Kodi restarts + RESTART KODI IF YOU MAKE ANY CHANGES + Complete Re-Sync necessary + Download additional art from FanArtTV + Download movie set/collection art from FanArtTV + Don't ask to pick a certain stream/quality + Always pick best quality for trailers + Kodi runs on a low-power device (e.g. Raspberry Pi) + Artwork + Force transcode pictures + + + Welcome + Error connecting + Server is unreachable + Server is online + items added to playlist + items queued to playlist + Server is restarting + Access is enabled + Enter password for user: + Invalid username or password + Failed to authenticate too many times. Reset in the settings. + Unable to direct play + Direct play failed 3 times. Enabled play from HTTP. + Choose the audio stream + Choose the subtitles stream + Delete file from your Emby server? + Play trailers? + Gathering movies from: + Gathering boxsets + Gathering music videos from: + Gathering tv shows from: + Gathering: + Detected the database needs to be recreated for this version of Emby for Kodi. Proceed? + Emby for Kodi may not work correctly until the database is reset. + Cancelling the database syncing process. The current Kodi version is unsupported. + completed in: + Comparing movies from: + Comparing boxsets + Comparing music videos from: + Comparing tv shows from: + Comparing episodes from: + Comparing: + Failed to generate a new device Id. See your logs for more information. + Kodi will now restart to apply the changes. + Delete file(s) from Plex Server? This will also delete the file(s) from disk! + + + - Number of trailers to play before a movie + Boost audio when transcoding + Burnt-in subtitle size + Limit download sync threads (rec. for rpi: 1) + Enable Plex Companion (restart Kodi!) + Plex Companion Port (change only if needed) + Activate Plex Companion debug log + Activate Plex Companion GDM debug log + Plex Companion: Allows flinging media to Kodi through Plex + Could not login to plex.tv. Please try signing in again. + Problems connecting to plex.tv. Network or internet issue? + Could not find any Plex server in the network. Aborting... + Choose your Plex server + Not yet authorized for Plex server + Please sign in to plex.tv. + Problems connecting to server. Pick another server? + Disable Plex music library? (It is HIGHLY recommended to use Plex music only with direct paths for large music libraries. Kodi might crash otherwise) + Would you now like to go to the plugin's settings to fine-tune PKC? You will need to RESTART Kodi! + + [COLOR yellow]Repair local database (force update all content)[/COLOR] + [COLOR red]Partial or full reset of Database and PKC[/COLOR] + [COLOR yellow]Cache all images to Kodi texture cache now[/COLOR] + [COLOR yellow]Sync Emby Theme Media to Kodi[/COLOR] + local + Failed to authenticate. Did you login to plex.tv? + + + Automatically log into plex.tv on startup + Enable constant background sync + Playback Mode + CAUTION! If you choose "Native" mode , you might loose access to certain Plex features such as: Plex trailers and transcoding options. ALL Plex shares need to use direct paths (e.g. smb://myNAS/mymovie.mkv or \\myNAS/mymovie.mkv)! + Network credentials + Add network credentials to allow Kodi access to your content? Note: Skipping this step may generate a message during the initial scan of your content if Kodi can't locate your content. + Kodi can't locate file: + Please verify the path. You may need to verify your network credentials in the add-on settings or use different Plex paths. Stop syncing? + Transform Plex UNC library paths \\myNas\mymovie.mkv automatically to smb paths, smb://myNas/mymovie.mkv? (recommended) + Replace Plex UNC paths \\myNas with smb://myNas + + Replace Plex paths /volume1/media or \\myserver\media with custom SMB paths smb://NAS/mystuff + Original Plex MOVIE path to replace: + Replace Plex MOVIE with: + Original Plex TV SHOWS path to replace: + Replace Plex TV SHOWS with: + Original Plex MUSIC path to replace: + Replace Plex MUSIC with: + Go a step further and completely replace all original Plex library paths (/volume1/media) with custom SMB paths (smb://NAS/MyStuff)? + Please enter your custom smb paths in the settings under "Sync Options" and then restart Kodi + Original Plex PHOTO path to replace: + Replace Plex PHOTO with: + On Deck: Append show title to episode + On Deck: Append season- and episode-number SxxExx + Nothing works? Try a full reset! + [COLOR yellow]Choose Plex Server from a list[/COLOR] + Wait before sync new/changed PMS item [s] + Background Sync + Do a full library sync every x minutes + remote + Searching for Plex Server + Used by Sync and when attempting to Direct Play + Customize Paths + Extend Plex TV Series "On Deck" view to all shows + Recently Added: Append show title to episode + Recently Added: Append season- and episode-number SxxExx + Would you like to download additional artwork from FanArtTV in the background? + Sync when screensaver is deactivated + Force Transcode Hi10P + Recently Added: Also show already watched episodes + Recently Added: Also show already watched movies (Refresh Plex playlist/nodes!) + Your current Plex Media Server: + [COLOR yellow]Manually enter Plex Media Server address[/COLOR] + Current address: + Current port: + Current plex.tv status: + Is your Kodi installed on a low-powered device like a Raspberry Pi? If yes, then we will reduce the strain on Kodi to prevent it from crashing. + Appearance Tweaks + TV Shows + Always use default Plex subtitle if possible + If you use several Plex libraries of one kind, e.g. "Kids Movies" and "Parents Movies", be sure to check the Wiki: https://goo.gl/JFtQV9 + Number of PMS items to show in widgets (e.g. "On Deck") + Plex Companion Update Port (change only if needed) + Plex Companion could not open the GDM port. Please change it in the PKC settings. + + + Log-out Plex Home User + Settings + Network credentials + Refresh Plex playlists/nodes + Perform manual library sync + Unable to run the sync, the add-on is not connected to a Plex server. + Plex might lock your account if you fail to log in too many times. Proceed anyway? + Resetting PMS connections, please wait + Failed to reset PKC. Try to restart Kodi. + [COLOR yellow]Toggle plex.tv login (sign in or sign out)[/COLOR] + Not yet connected to Plex Server + Watch later + is offline + Even though we signed in to plex.tv, we could not authorize for PMS + Enter your Plex Media Server's IP or URL, Examples are: + + Does your Plex Media Server support SSL connections? (https instead of http)? + Error contacting PMS + Abort (Yes) or save address anyway (No)? + connected + plex.tv toggle successful + [COLOR yellow]Look for missing fanart on FanartTV now[/COLOR] + Only look for missing fanart or refresh all fanart? The scan will take quite a while and happen in the background. + Refresh all + Missing only + + + + Running the image cache process can take some time. It will happen in the background. Are you sure you want continue? + Reset all existing cache data first? + + + : Enter plex.tv username. Or nothing to cancel. + Enter password for plex.tv user + Could not sign in user + Problems trying to contact plex.tv. Try again later + Go to https://plex.tv/pin and enter the code: + Could not sign in to plex.tv. Try again later + : Select User + Enter PIN for user + Could not log in user + Please try again. + unknown + or press No to not sign in. + + + Library sync thread has crashed. You should restart Kodi now. Please report this on the forum + Detected Kodi database needs to be recreated for this version. This might take a while. Proceed? + may not work correctly until the database is reset. + Cancelling the database syncing process. Current Kodi version is unsupported. Please verify your logs for more info. + Startup syncing process failed repeatedly. Try restarting Kodi. Stopping Sync for now. + Plex playlists/nodes refreshed + Plex playlists/nodes refresh failed + Full library sync finished + Sync had to skip some items because they could not be processed. Kodi may be instable now!! Please post your Kodi logs to the Plex forum. + The Plex Server did not like you asking for so much data at once and returned ERRORS. Try lowering the number of sync download threads in the settings. Skipped some items for now. + ERROR in library sync + + + On Deck + Collections + + + Are you sure you want to reset your local Kodi database? A re-sync of the Plex data will take time afterwards. + Could not stop the database from running. Please try again later. + Remove all cached artwork? (recommended!) + Reset all PlexKodiConnect Addon settings? (this is usually NOT recommended and unnecessary!) + \ No newline at end of file From 7276a195bf438740ba666b7e443fd2fe02c6a293 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 26 Feb 2017 18:25:41 +0100 Subject: [PATCH 19/55] Fix KeyError --- resources/lib/artwork.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/artwork.py b/resources/lib/artwork.py index 786a6d5b..1336687f 100644 --- a/resources/lib/artwork.py +++ b/resources/lib/artwork.py @@ -99,7 +99,7 @@ def setKodiWebServerDetails(): result = loads(result) try: xbmc_username = result['result']['value'] - except TypeError: + except (TypeError, KeyError): pass web_pass = { "jsonrpc": "2.0", From 8a8c9e1bd70d1b3ba9b8f96c7219f1634c10de54 Mon Sep 17 00:00:00 2001 From: croneter Date: Mon, 27 Feb 2017 19:45:48 +0100 Subject: [PATCH 20/55] Update CONTRIBUTING.md --- CONTRIBUTING.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5b2c3be1..30b34c9e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,9 @@ Thanks a ton for contributing to PlexKodiConnect! +## Feature requests + +* Please only use feathub: http://feathub.com/croneter/PlexKodiConnect ## Programming @@ -12,4 +15,4 @@ Thanks a ton for contributing to PlexKodiConnect! ## Translations -* Please [only use crowdin.com](https://crowdin.com/project/plexkodiconnect/invite) to help with translations. Don't use Github pull requests. \ No newline at end of file +* Please [only use crowdin.com](https://crowdin.com/project/plexkodiconnect/invite) to help with translations. Don't use Github pull requests. From fb041e5ac380bca3f4f1efcf4a3abd698ab53441 Mon Sep 17 00:00:00 2001 From: croneter Date: Mon, 27 Feb 2017 19:50:06 +0100 Subject: [PATCH 21/55] Update CONTRIBUTING.md --- CONTRIBUTING.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 30b34c9e..ff27ea78 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,11 @@ Thanks a ton for contributing to PlexKodiConnect! ## Feature requests -* Please only use feathub: http://feathub.com/croneter/PlexKodiConnect +* Are you missing a certain functionality? Then visit feathub: http://feathub.com/croneter/PlexKodiConnect + +## Issues + +* Something not working like it's supposed to? Then go here: https://github.com/croneter/PlexKodiConnect/wiki/How-to-Report-A-Bug ## Programming From 31bc8b12707bd437f59587712dd75e4e2c101a81 Mon Sep 17 00:00:00 2001 From: croneter Date: Mon, 27 Feb 2017 19:51:19 +0100 Subject: [PATCH 22/55] Update CONTRIBUTING.md --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ff27ea78..5b844553 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,11 +4,11 @@ Thanks a ton for contributing to PlexKodiConnect! ## Feature requests -* Are you missing a certain functionality? Then visit feathub: http://feathub.com/croneter/PlexKodiConnect +* Are you missing a certain functionality? Then [visit feathub.com](http://feathub.com/croneter/PlexKodiConnect) ## Issues -* Something not working like it's supposed to? Then go here: https://github.com/croneter/PlexKodiConnect/wiki/How-to-Report-A-Bug +* Something not working like it's supposed to? Then [open a new issue report](https://github.com/croneter/PlexKodiConnect/wiki/How-to-Report-A-Bug) ## Programming From bdef2b783752e61762fc71deece239d34d13dc4c Mon Sep 17 00:00:00 2001 From: croneter Date: Mon, 27 Feb 2017 19:52:22 +0100 Subject: [PATCH 23/55] Update CONTRIBUTING.md --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5b844553..05038189 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,13 +10,13 @@ Thanks a ton for contributing to PlexKodiConnect! * Something not working like it's supposed to? Then [open a new issue report](https://github.com/croneter/PlexKodiConnect/wiki/How-to-Report-A-Bug) +## Translations + +* Want to help translate PlexKodiConnect? Then go [visit crowdin.com](https://crowdin.com/project/plexkodiconnect/invite) + ## Programming * Please make pull requests towards the **develop** branch, not the master branch. Hence please fork the **develop** branch and not the master branch * Thanks if you can follow the Python style guide [PEP8](https://www.python.org/dev/peps/pep-0008/) to keep things neat and clean * Thanks if you add some comments to make your code more readable ;-) - -## Translations - -* Please [only use crowdin.com](https://crowdin.com/project/plexkodiconnect/invite) to help with translations. Don't use Github pull requests. From 733864446973f9986bc6481c7fcf86b9003f3155 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Mon, 27 Feb 2017 19:59:30 +0100 Subject: [PATCH 24/55] Update readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 80780efa..24cb627c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +##Status + +[![GitHub issues](https://img.shields.io/github/issues/croneter/PlexKodiConnect.svg?maxAge=60&style=flat-square)](https://github.com/croneter/PlexKodiConnect/issues) + + # PlexKodiConnect (PKC) **Combine the best frontend media player Kodi with the best multimedia backend server Plex** From 0b9b61b5908bae6f9771c33b133822913c521ca1 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Mon, 27 Feb 2017 20:04:10 +0100 Subject: [PATCH 25/55] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 24cb627c..02d1ad32 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ##Status -[![GitHub issues](https://img.shields.io/github/issues/croneter/PlexKodiConnect.svg?maxAge=60&style=flat-square)](https://github.com/croneter/PlexKodiConnect/issues) +[![GitHub issues](https://img.shields.io/github/issues-raw/badges/shields.svg?maxAge=60&style=flat-square)](https://github.com/croneter/PlexKodiConnect/issues) # PlexKodiConnect (PKC) From e0bbb9a9b45f06e251325040d68de362cc4bcd16 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Mon, 27 Feb 2017 20:05:05 +0100 Subject: [PATCH 26/55] Revert "Update readme" This reverts commit 0b9b61b5908bae6f9771c33b133822913c521ca1. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 02d1ad32..24cb627c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ##Status -[![GitHub issues](https://img.shields.io/github/issues-raw/badges/shields.svg?maxAge=60&style=flat-square)](https://github.com/croneter/PlexKodiConnect/issues) +[![GitHub issues](https://img.shields.io/github/issues/croneter/PlexKodiConnect.svg?maxAge=60&style=flat-square)](https://github.com/croneter/PlexKodiConnect/issues) # PlexKodiConnect (PKC) From 9e1669605d640679f9397f9bf5478cc3aecec2bb Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Mon, 27 Feb 2017 20:13:15 +0100 Subject: [PATCH 27/55] Update readme --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 24cb627c..3aaa192e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ ##Status [![GitHub issues](https://img.shields.io/github/issues/croneter/PlexKodiConnect.svg?maxAge=60&style=flat-square)](https://github.com/croneter/PlexKodiConnect/issues) +[![GitHub pull requests](https://img.shields.io/github/issues-pr/croneter/PlexKodiConnect.svg?maxAge=60&style=flat-square)](https://github.com/croneter/PlexKodiConnect/pulls) # PlexKodiConnect (PKC) @@ -24,7 +25,7 @@ Please help translate PlexKodiConnect into your language: [visit crowdin.com](ht * [**What is currently supported?**](#what-is-currently-supported) * [**Known Larger Issues**](#known-larger-issues) * [**Issues being worked on**](#issues-being-worked-on) -* [**Pipeline for future development**](#what-could-be-in-the-pipeline-for-future-development) +* [**Requests for new features**](#Requests-for-new-features) * [**Checkout the PKC Wiki**](#checkout-the-pkc-wiki) * [**Credits**](#credits) @@ -118,12 +119,9 @@ However, some changes to individual items are instantly detected, e.g. if you ma Have a look at the [Github Issues Page](https://github.com/croneter/PlexKodiConnect/issues). Before you open your own issue, please read [How to report a bug](https://github.com/croneter/PlexKodiConnect/wiki/How-to-Report-A-Bug). -### What could be in the pipeline for future development? +### Requests for new features -- Plex channels -- Movie extras (trailers already work) -- Playlists -- Music Videos +[![Feature Requests](http://feathub.com/croneter/PlexKodiConnect?format=svg)](http://feathub.com/croneter/PlexKodiConnect) ### Checkout the PKC Wiki The [Wiki can be found here](https://github.com/croneter/PlexKodiConnect/wiki) and will hopefully answer all your questions. You can even edit the wiki yourself! From 85ef84bb85fc52f5ba2b749624aa7bfc34115375 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Mon, 27 Feb 2017 20:33:48 +0100 Subject: [PATCH 28/55] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3aaa192e..0c4dc8e2 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Please help translate PlexKodiConnect into your language: [visit crowdin.com](ht * [**What is currently supported?**](#what-is-currently-supported) * [**Known Larger Issues**](#known-larger-issues) * [**Issues being worked on**](#issues-being-worked-on) -* [**Requests for new features**](#Requests-for-new-features) +* [**Requests for new features**](#requests for new features) * [**Checkout the PKC Wiki**](#checkout-the-pkc-wiki) * [**Credits**](#credits) From 1ac6bae335de244c69fafc772d3c13d8448716b8 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Mon, 27 Feb 2017 20:34:26 +0100 Subject: [PATCH 29/55] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c4dc8e2..3c9c4f2b 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Please help translate PlexKodiConnect into your language: [visit crowdin.com](ht * [**What is currently supported?**](#what-is-currently-supported) * [**Known Larger Issues**](#known-larger-issues) * [**Issues being worked on**](#issues-being-worked-on) -* [**Requests for new features**](#requests for new features) +* [**Requests for new features**](#requests-for-new-features) * [**Checkout the PKC Wiki**](#checkout-the-pkc-wiki) * [**Credits**](#credits) From be48743b1bc70cdf8ae781f3ab71e0feddf278c9 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Wed, 1 Mar 2017 18:51:18 +0100 Subject: [PATCH 30/55] Fix TypeError on manually entering PMS port - Fixes #242 --- resources/lib/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/lib/utils.py b/resources/lib/utils.py index 34b34c1e..bdfe6e9d 100644 --- a/resources/lib/utils.py +++ b/resources/lib/utils.py @@ -133,8 +133,7 @@ def dialog(typus, *args, **kwargs): '{ipaddress}': xbmcgui.INPUT_IPADDRESS, '{password}': xbmcgui.INPUT_PASSWORD } - for key, value in types.iteritems(): - kwargs['type'] = kwargs['type'].replace(key, value) + kwargs['type'] = types[kwargs['type']] if "heading" in kwargs: kwargs['heading'] = kwargs['heading'].replace("{plex}", language(29999)) From c200bde4ce8e7fa99ced424813abd91c9a4348ed Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Wed, 1 Mar 2017 19:13:28 +0100 Subject: [PATCH 31/55] Version bump --- README.md | 1 + addon.xml | 2 +- changelog.txt | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c9c4f2b..fdcf2ec3 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ PKC currently provides the following features: + German + Czech, thanks @Pavuucek + Spanish, thanks @bartolomesoriano + + Danish, thanks @FIGHT + More coming up: [you can help!](https://crowdin.com/project/plexkodiconnect/invite) - [Plex Watch Later / Plex It!](https://support.plex.tv/hc/en-us/sections/200211783-Plex-It-) - [Plex Companion](https://support.plex.tv/hc/en-us/sections/200276908-Plex-Companion): fling Plex media (or anything else) from other Plex devices to PlexKodiConnect diff --git a/addon.xml b/addon.xml index e2a72166..b1d8da57 100644 --- a/addon.xml +++ b/addon.xml @@ -1,7 +1,7 @@ diff --git a/changelog.txt b/changelog.txt index 806ed41e..7c361ab4 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,8 @@ +version 1.6.2 +- Fix TypeError on manually entering PMS port +- Fix KeyError +- Update readme + version 1.6.1 - New Danish translation, thanks @Osberg - Fix UnicodeDecodeError for non-ASCII filenames From 812c4114a2ab608522b800284bf55836e787c378 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Wed, 1 Mar 2017 19:18:58 +0100 Subject: [PATCH 32/55] Update addon.xml --- addon.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addon.xml b/addon.xml index b1d8da57..0e45d456 100644 --- a/addon.xml +++ b/addon.xml @@ -28,13 +28,15 @@ Komplette Integration von Plex in Kodi Native Integration of Plex into Kodi Indbygget Integration af Plex i Kodi + Directe integratie van Plex in Kodi Connect Kodi to your Plex Media Server. This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases (as this plugin directly changes them). Use at your own risk! Connect Kodi to your Plex Media Server. This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases (as this plugin directly changes them). Use at your own risk! Connect Kodi to your Plex Media Server. This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases (as this plugin directly changes them). Use at your own risk! Připojte Kodi ke svému Plex Media Serveru. Tento doplněk předpokládá, že spravujete veškerá svá videa pomocí Plexu (nikoliv pomocí Kodi). Můžete přijít o data uložená ve video a hudební databázi Kodi (tento doplněk je přímo mění). Používejte na vlastní nebezpečí! Verbindet Kodi mit deinem Plex Media Server. Dieses Addon geht davon aus, dass du all deine Videos mit Plex verwaltest (und keine direkt mit Kodi). Du wirst möglicherweise Daten verlieren, die bereits in der Kodi Video- und/oder Musik-Datenbank gespeichert sind (da dieses Addon beide Datenbanken direkt verändert). Verwende auf eigene Gefahr! Connect Kodi to your Plex Media Server. This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases (as this plugin directly changes them). Use at your own risk! - Tilslut Kodi til din Plex Media Server. Dette plugin forudsætter, at du styre alle dine videoer med Plex (og ikke med Kodi). Du kan miste data som allerede er gemt i Kodi video og musik-databaser (dette plugin ændrer direkte i dem). Brug på egen risiko! + Tilslut Kodi til din Plex Media Server. Dette plugin forudsætter, at du administrere alle dine videoer med Plex (og ikke med Kodi). Du kan miste data som allerede er gemt i Kodi video og musik-databaser (dette plugin ændrer direkte i dem). Brug på eget ansvar! + Verbind Kodi met je Plex Media Server. Deze plugin gaat ervan uit dat je al je video's met Plex (en niet met Kodi) beheerd. Je kunt gegevens reeds opgeslagen in de databases voor video en muziek van Kodi (deze plugin wijzigt deze gegevens direct) verliezen. Gebruik op eigen risico! all GPL v2.0 https://forums.plex.tv From 658817ef7d9a9dba3228ef6ccb27e6ac56a86f6d Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Wed, 1 Mar 2017 19:23:06 +0100 Subject: [PATCH 33/55] Update Danish translation --- resources/language/Danish/strings.xml | 862 +++++++++++++------------- 1 file changed, 431 insertions(+), 431 deletions(-) diff --git a/resources/language/Danish/strings.xml b/resources/language/Danish/strings.xml index d524d260..ddb27394 100644 --- a/resources/language/Danish/strings.xml +++ b/resources/language/Danish/strings.xml @@ -2,515 +2,515 @@ PlexKodiConnect - Server Address (IP) - Preferred playback method - Log level - Username: - Password: - Network Username: - Network Password: - Transcode: - Enable Performance Profiling - Local caching system + Server Adresse (IP) + Foretrukken afspilnings metode + Logføringsniveau + Brugernavn: + Adgangskode: + Netværks Brugernavn: + Netværks Adgangskode: + Transkode: + Aktivér Ydelse Profilering + Lokal caching-system OK - Never show + Vis Aldrig - Connection - Network - Device Name - Unauthorized for PMS + Forbindelser + Netværk + Enhedens navn + Uautoriseret for PMS - Advanced - Username + Avanceret + Brugernavn - Display message if PMS goes offline + Vise besked hvis PMS går offline - Port Number - I own this Plex Media Server - Number of recent Movies to show: - Number of recent TV episodes to show: - Number of recent Music Albums to show: - Mark watched at start of playback: - Set Season poster for episodes + Portnummer + Jeg ejer denne Plex Media Server + Seneste af antal viste film: + Seneste af antal viste Tv episoder: + Seneste af antal viste Musik albums: + Mark set på start afspilning: + Sæt sæson plakat for episoder - Genre Filter ... - Play All from Here - Refresh - Delete - Add Movie to CouchPotato + Genre Filter... + Afspil alle herfra + Opdater + Slet + Tilføj film til CouchPotato - Incorrect Username/Password - Username not found + Forkert brugernavn/adgangskode + Brugernavn blev ikke fundet - Deleting - Waiting for server to delete + Sletter + Venter på at serveren sletter - Server Default - Title - Year - Premiere Date - Date Created - Critic Rating - Community Rating - Play Count + Server standard + Titel + År + Premiere dato + Oprettelsesdato + Kritiker Rating + Fællesskabs Rating + Antal afspilninger Budget - Sort By + Sortér efter - None + Ingen Action - Adventure + Eventyr Animation - Crime - Comedy - Documentary + Krimi + Komedie + Dokumentar Drama - Fantasy - Foreign - History - Horror - Music + Fantasi + Udenlandske + Historie + Gyser + Musik Musical - Mystery - Romance + Mystik + Romantik Science Fiction - Short - Suspense + Kort + Spænding Thriller Western - Genre Filter - Confirm file deletion - Delete this item? This action will delete media and associated data files. + Genre Filter... + Bekræft sletning + Vil du slette dette element? Denne handling vil slette medier og tilknyttede datafiler. - Mark Watched - Mark Unwatched - Add to Favorites - Remove from Favorites - Sort By ... - Sort Order Descending - Sort Order Ascending - Show People + Mark som set + Mark som ikke set + Føj til favoritter + Fjern fra Favoritter + Sorter efter... + Sorter faldende + Sorter stigende + Vis personer - Resume - Resume from - Start from beginning + Fortsæt + Genoptage fra + Start fra begyndelsen Interface - Include Stream Info - Include People - Include Overview - Offer delete after playback - For Episodes - For Movies - Background Art Refresh Rate (seconds) - Add Resume Percent - Add Episode Number - Show Load Progress - Loading Content - Retrieving Data - Done - Processing Item : - Play Error - This item is not playable - Local path detected - Your MB3 Server contains local paths. Please change server paths to UNC or change XBMB3C setting 'Play from Stream' to true. Path: - Warning - Debug logging enabled. - This will affect performance. - Error - Monitoring service is not running - If you have just installed please restart Kodi - Search + Inkludere Stream Info + Inkludere mennesker + Inkludere oversigt + Tilbud sletning efter afspilning + Serie episoder + Sortér film + Baggrund kunst opdateringshastigheden (sekunder) + Tilføj genoptagelses procent + Tilføj episode nummer + Vis læse fremskridt + Indlæser indhold + Henter data + Udført + Behandling af emnet: + Afspilnings fejl + Emnet kan ikke afspilles + Lokale sti opdaget + MB3 serveren indeholder lokale stier. Venligst ændre server stier til UNC- eller Skift XBMB3C indstilling 'afspil fra Stream' til true. Sti: + Advarsel + Debug logføring aktiveret. + Dette vil påvirke ydeevnen. + Fejl + Overvågning service kører ikke + Hvis du lige har installeret genstart Kodi + Søg - Enable Theme Music (Requires Restart) - - Loop Theme Music - Enable Background Image (Requires Restart) + Aktiver temamusik (kræver genstart) + -Loop temamusik + Aktivere baggrundsbillede (kræver genstart) Services - Always transcode if video bitrate is above + Altid transcode hvis video bitrate er over - Skin does not support setting views - Select item action (Requires Restart) + Skin understøtter ikke visning indstillinger + Vælg handling (kræver genstart) - Sort NextUp by Show Title - Enable Enhanced Images (eg CoverArt) + Sorter NextUp efter titel visning + Aktiver forbedret billeder (fx CoverArt) Metadata - Artwork - Video Quality if Transcoding necessary + Illustrationer + Video kvalitet hvis Transcoding nødvendigt - Enable Suggested Loader (Requires Restart) - Add Season Number - Flatten Seasons + Aktivere baggrundsbillede (kræver genstart) + Tilføje sæson nummer + Flade sæsoner - Direct Play - HTTP - Direct Play + Direkte afspilning - HTTP + Direkte afspilning Transcoding - Server Detection Succeeded - Found server - Address : + Server søgning lykkedes + Server fundet + Adresse: - Recently Added TV Shows - In Progress TV Shows - All Music - Channels - Recently Added - Recently Added Episodes - Recently Added Albums - In Progress Movies - In Progress Episodes - Next Episodes - Favorite Movies - Favorite Shows - Favorite Episodes - Frequent Played Albums - Upcoming TV + Senest tilføjet TV shows + Igangværende TV Shows + Alt musik + Kanaler + Senest tilføjet + Senest tilføjet episoder + Senest tilføjet Albums + Igangværende film + Igangværende episoder + Næste afsnit + Favorit film + Favorit serie + Favorit episoder + Hyppigt afspillede Albums + Kommende TV BoxSets - Trailers - Music Videos - Photos - Unwatched Movies - Movie Genres - Movie Studios - Movie Actors - Unwatched Episodes - TV Genres - TV Networks - TV Actors - Playlists - Search - Set Views + Trailer + Musikvideoer + Billeder + Usete film + Film genrer + Film Studios + Skuespillere + Usete episoder + TV genrer + TV Netværk + TV Skuespillere + Afspilningsliste + Søg + Visning - Select User - Profiling enabled. - Please remember to turn off when finished testing. - Error in ArtworkRotationThread - Unable to connect to server - Error in LoadMenuOptionsThread + Vælg bruger + Profilering aktiveret. + Husk at slukke, når testen er færdig + Fejl i ArtworkRotationThread + Kan ikke forbinde til server + Fejl i LoadMenuOptionsThread - Enable Playlists Loader (Requires Restart) + Aktivere baggrundsbillede (kræver genstart) - Songs + Sange Albums - Album Artists - Artists - Music Genres + Album kunstner + Kunstner: + Musikgenrer - Enable Theme Videos (Requires Restart) - - Loop Theme Videos + Aktiverer tema videoer (kræver genstart) + -Loop tema videoer - AutoPlay remaining episodes in a season - Compress Artwork - Latest - In Progress + Auto afspil de resterende episoder af sæsonen + Komprimere billeder + Nyeste + Igangværende NextUp - User Views - Report Metrics - Use Kodi Sorting - Runtime + Visning + Tekstmålinger + Brug Kodi sortering + Spiltid - Random - Recently releases - Random Items - Recommended + Tilfældig + Seneste udgivelse + Tilfældige emner + Anbefalet - Extras - Sync Theme Music - Sync Extra Fanart - Sync Movie BoxSets + Ekstra + Sync temamusik + Synkronisere ekstra Fanart + Sync film BoxSets - [COLOR yellow]Reset local Kodi database[/COLOR] - Enable watched/resume status sync - DB Sync Indication: - Play Count Sync Indication: - Enable HTTPS - Force Transcoding Codecs + [Farve gul] Nulstille lokale Kodi database[/COLOR] + Vælg Set / Genoptag status sync + DB Sync angivelse: + Afspil Sync angivelse: + Aktivere HTTPS + Vælg Transcoding Codecs - Enable Netflix style next up notification - - The number of seconds before the end to show the notification - Show Emby Info dialog on play/select action - Enable server connection message on startup + Aktivere Netflix style notifikationer + -Antallet af sekunder før slutningen til at vise notifiaktioner + Vis dialogboks for Emby Info på play/Vælg handling + Aktivere server forbindelse besked ved opstart - Recently added Home Videos - Recently added Photos - Favourite Home Videos - Favourite Photos - Favourite Albums + Senest tilføjet hjemmevideoer + Senest tilføjet billeder + Favorit hjemmevideoer + Favorit fotos + Favorit Albums - Recently added Music videos - In progress Music videos - Unwatched Music videos + Senest tilføjet musik videoer + Igangværende musikvideoer + Usete musikvideoer - Active - Clear Settings - Movies + Aktiv + Nulstil indstillinger + Film BoxSets - Trailers - Series - Seasons - Episodes - Music Artists - Music Albums - Music Videos - Music Tracks - Channels + Trailer + Serier + Sæson + Episoder + Musik kunstnere + Musik Albums + Musikvideoer + Musiknumre + Kanaler - Plex options - Clear like for this item - Like this item - Dislike this item - Add to Plex favorites - Remove from Plex favorites - Set custom song rating - Plex addon settings - Delete item from server - Refresh this item - Set custom song rating (0-5) - Force transcode - Enable Plex context menu in Kodi - Could not delete the Plex item. Is item deletion enabled on the Plex Media Server? - Start playback via PMS - Settings for the Plex Server + Plex indstillinger + Slet Like for dette emne + Like dette emne + Dislike dette emne + Tilføj til Plex favoritter + Fjern fra Plex favoritter + Angive brugerdefinerede sange rating + Plex addon indstillinger + Slet element fra server + Opdater siden + Angive brugerdefinerede sange rating (0-5) + Vælg transcode + Aktivere Plex kontekstmenu i Kodi + Kunne ikke slette Plex emnet. Er sletning aktiveret på Plex Media Server? + Start afspilning via PMS + Indstillinger for Plex-Server - Verify Host SSL Certificate (more secure) - Client SSL certificate - Use alternate address - Alternate Server Address - Use alternate device Name - [COLOR yellow]Reset login attempts[/COLOR] - Sync Options - Show syncing progress - Sync empty TV Shows - Enable Music Library - Direct stream music library - Playback Mode - Force artwork caching - Limit artwork cache threads (recommended for rpi) - Enable fast startup (requires server plugin) - Maximum items to request from the server at once - Playback - [COLOR yellow]Enter network credentials[/COLOR] - Enable Plex Trailers (Plexpass is needed) - Ask to play trailers - Skip Plex delete confirmation for the context menu (use at your own risk) - Jump back on resume (in seconds) - Force transcode h265/HEVC - Music metadata options (not compatible with direct stream) - Import music song rating directly from files - Convert music song rating to Emby rating - Allow rating in song files to be updated - Ignore specials in next episodes - Permanent users to add to the session - Startup delay (in seconds) - Enable server restart message - Enable new content notification - Duration of the video library pop up (in seconds) - Duration of the music library pop up (in seconds) - Server messages - [COLOR yellow]Generate a new unique device Id (e.g. when cloning Kodi)[/COLOR] - Users must log in every time Kodi restarts - RESTART KODI IF YOU MAKE ANY CHANGES - Complete Re-Sync necessary - Download additional art from FanArtTV - Download movie set/collection art from FanArtTV - Don't ask to pick a certain stream/quality - Always pick best quality for trailers - Kodi runs on a low-power device (e.g. Raspberry Pi) - Artwork - Force transcode pictures + Bekræfte vært SSL certifikat (mere sikker) + Klient SSL certifikat + Brug alternativ adresse + Alternative server adresse + Brug alternative enhed navn + [Farve gul] Nulstille login attempts[/COLOR] + Indstillinger for synkronisering + Vis synkronisering fremskridt + Sync tomme TV shows + Aktivere musikbibliotek + Direkte stream fra musikbibliotek + Afspilningstilstand + Tvinge illustrationer cachelagring + Begrænse illustrationen cache tråde (anbefales til rpi) + Aktiver hurtig opstart (kræver server plugin) + Maksimale elementer til at anmode serveren på én gang + Afspilning + [Farve gul] Indtast netværk info[/COLOR] + Aktiverer Plex trailere (Plexpass er nødvendig) + Bede om at afspille trailere + Spring Plex slet bekræftelse over i kontekstmenuen (brug på eget ansvar) + Spring tid tilbage (i sekunder) + Transcode h265/HEVC + Musik metadata mulighed (ikke kompatibel med direkte stream) + Importere musik sang rating direkte fra filer + Konvertere musik sang rating til Emby rating + Tillad rating i sang filer skal opdateres + Ignorere ekstra i næste episoder + Faste brugere at føje til samlingen + Start forsinkelse (i sekunder) + Aktivere server genstart besked + Aktivere nyt indhold notifikationer + Varigheden af den video bibliotek pop op (i sekunder) + Varigheden af den musikbibliotek pop op (i sekunder) + Server beskeder + [Farve gul] Generere en ny unik enheds Id (f.eks. Når kloning Kodi)[/COLOR] + Brugerne skal logge ind hver gang Kodi genstarter + GENSTART KODI, HVIS DU FORETAGER ÆNDRINGER + Komplet re-synkronisere nødvendigt + Hent yderligere info fra FanArtTV + Download film sæt/samling info fra FanArtTV + Spørg ikke at vælge en bestemt stream/kvalitet + Altid vælge bedste kvalitet til anhængere + Kodi kører på en low-power enhed (f.eks. Raspberry Pi) + Illustrationer + Transcode billeder - Welcome - Error connecting - Server is unreachable - Server is online - items added to playlist - items queued to playlist - Server is restarting - Access is enabled - Enter password for user: - Invalid username or password - Failed to authenticate too many times. Reset in the settings. - Unable to direct play - Direct play failed 3 times. Enabled play from HTTP. - Choose the audio stream - Choose the subtitles stream - Delete file from your Emby server? - Play trailers? - Gathering movies from: - Gathering boxsets - Gathering music videos from: - Gathering tv shows from: - Gathering: - Detected the database needs to be recreated for this version of Emby for Kodi. Proceed? - Emby for Kodi may not work correctly until the database is reset. - Cancelling the database syncing process. The current Kodi version is unsupported. - completed in: - Comparing movies from: - Comparing boxsets - Comparing music videos from: - Comparing tv shows from: - Comparing episodes from: - Comparing: - Failed to generate a new device Id. See your logs for more information. - Kodi will now restart to apply the changes. - Delete file(s) from Plex Server? This will also delete the file(s) from disk! + Velkommen! + Fejl forbindelse + Serveren er ikke tilgængelig + Serveren er online + Emner tilføjet til afspilningslisten + Emner sat i kø til afspilningslisten + Server genstarter! + Adgang er aktiveret + Angiv adgangskode for brugeren: + Forkert brugernavn eller password + Kunne ikke godkendes for mange gange. Nulstil i indstillingerne. + Ikke muligt at køre direkte afspilning + Direkte afspilning fejlede 3 gange. Aktiveret afspilning fra HTTP. + Vælg lyd-stream + Vælg undertekster stream + Slette filen fra din Emby server? + afspil trailere? + Indsamling film fra: + Indsamling boxsets + Indsamling musikvideoer fra: + Indsamling TV Shows fra: + Indsamling: + Registreret database skal genskabes i denne version af Emby for Kodi. Vil du fortsætte? + Emby for Kodi fungerer muligvis ikke korrekt, før databasen er nulstillet. + Annullering af databasen synkronisering proces. Den nuværende Kodi version understøttes ikke. + udfyldes: + Sammenligne film fra: + Sammenligne boxsets + Sammenligne musikvideoer fra: + Sammenligne TV Shows fra: + Sammenligne episoder fra: + Sammenligne: + Det lykkedes ikke at generere en nyt enheds Id. Se dine logfiler for flere oplysninger. + Kodi vil nu genstarte for at gemme ændringer. + Slette fil(er) fra Plex Server? Dette vil også slette filen/filerne fra disken! - - Number of trailers to play before a movie - Boost audio when transcoding - Burnt-in subtitle size - Limit download sync threads (rec. for rpi: 1) - Enable Plex Companion (restart Kodi!) - Plex Companion Port (change only if needed) - Activate Plex Companion debug log - Activate Plex Companion GDM debug log - Plex Companion: Allows flinging media to Kodi through Plex - Could not login to plex.tv. Please try signing in again. - Problems connecting to plex.tv. Network or internet issue? - Could not find any Plex server in the network. Aborting... - Choose your Plex server - Not yet authorized for Plex server - Please sign in to plex.tv. - Problems connecting to server. Pick another server? - Disable Plex music library? (It is HIGHLY recommended to use Plex music only with direct paths for large music libraries. Kodi might crash otherwise) - Would you now like to go to the plugin's settings to fine-tune PKC? You will need to RESTART Kodi! + -Antallet af trailere før afspilning af en film + Øge lyd når transcoding + Undertekst størrelse + Begrænse download sync tråde (rec. for rpi: 1) + Aktivere Plex ledsager (genstart Kodi!) + Plex ledsager Port (ændring kun hvis nødvendigt) + Aktivere Plex ledsager fejlfindingslogfil + Aktivere Plex ledsager GDM fejlfindingslogfil + Plex ledsager: Tillad skift af medier til Kodi gennem Plex + Kunne ikke logge ind på plex.tv. Prøv at logge igen. + Problemer med at forbinde til plex.tv. Netværk eller internet problem? + Kunne ikke finde nogen Plex serveren i netværket. Afbryder... + Vælg din Plex server + Endnu ikke godkendt til Plex server + Venligst log på plex.tv. + Problemer med at forbinde til serveren. Vælg en anden server? + Deaktivere Plex musikbibliotek? (Det anbefales at bruge Plex musik med direkte adgang til store musikbiblioteker. Kodi kan gå ned, ellers) + Ønsker du at finjustere PKC nu, gå til plugins for dette. Du skal genstarte Kodi! - [COLOR yellow]Repair local database (force update all content)[/COLOR] - [COLOR red]Partial or full reset of Database and PKC[/COLOR] - [COLOR yellow]Cache all images to Kodi texture cache now[/COLOR] - [COLOR yellow]Sync Emby Theme Media to Kodi[/COLOR] - local - Failed to authenticate. Did you login to plex.tv? + [Farve gul] Reparere lokale database (opdatere alt indhold)[/COLOR] + [Farve rød] Fuld eller delvis nulstilling af Database og PKC[/COLOR] + [Farve gul] Cachelagre alle billeder til Kodi tekstur cache now[/COLOR] + [Farve gul] Sync Emby tema medier til Kodi[/COLOR] + lokal + Kunne ikke godkendes. Loggede du ind på plex.tv? - Automatically log into plex.tv on startup - Enable constant background sync - Playback Mode - CAUTION! If you choose "Native" mode , you might loose access to certain Plex features such as: Plex trailers and transcoding options. ALL Plex shares need to use direct paths (e.g. smb://myNAS/mymovie.mkv or \\myNAS/mymovie.mkv)! - Network credentials - Add network credentials to allow Kodi access to your content? Note: Skipping this step may generate a message during the initial scan of your content if Kodi can't locate your content. - Kodi can't locate file: - Please verify the path. You may need to verify your network credentials in the add-on settings or use different Plex paths. Stop syncing? - Transform Plex UNC library paths \\myNas\mymovie.mkv automatically to smb paths, smb://myNas/mymovie.mkv? (recommended) - Replace Plex UNC paths \\myNas with smb://myNas + Automatisk log ind på plex.tv ved start + Aktivere konstant baggrund sync + Afspilningstilstand + FORSIGTIG! Hvis du vælger "Native" mode, kan du miste adgang til visse Plex funktioner såsom: Plex trailere og transcode muligheder. ALLE Plex arkiver skal bruge direkte stier (f.eks. smb://myNAS/mymovie.mkv eller \\myNAS/mymovie.mkv)! + Legitimationsoplysninger til netværket + Tilføje legitimationsoplysninger til netværket for at tillade Kodi adgang til dit indhold? Bemærk: Springes dette trin over kan det generere en meddelelse under den første scanning af dit indhold hvis Kodi ikke kan finde dit indhold. + Kodi kan ikke finde filen: + Kontroller venligst stien. Du kan være nødvendigt at verificere dine legitimationsoplysninger til netværket i indstillingerne for tilføjelse eller bruge forskellige Plex stier. Stop synkronisering? + Omdanne Plex UNC bibliotek stier \\myNas\mymovie.mkv automatisk til SMB-kurver, smb://myNas/mymovie.mkv? (anbefales) + Erstatte Plex UNC stier \\myNas med smb://myNas - Replace Plex paths /volume1/media or \\myserver\media with custom SMB paths smb://NAS/mystuff - Original Plex MOVIE path to replace: - Replace Plex MOVIE with: - Original Plex TV SHOWS path to replace: - Replace Plex TV SHOWS with: - Original Plex MUSIC path to replace: - Replace Plex MUSIC with: - Go a step further and completely replace all original Plex library paths (/volume1/media) with custom SMB paths (smb://NAS/MyStuff)? - Please enter your custom smb paths in the settings under "Sync Options" and then restart Kodi - Original Plex PHOTO path to replace: - Replace Plex PHOTO with: - On Deck: Append show title to episode - On Deck: Append season- and episode-number SxxExx - Nothing works? Try a full reset! - [COLOR yellow]Choose Plex Server from a list[/COLOR] - Wait before sync new/changed PMS item [s] - Background Sync - Do a full library sync every x minutes - remote - Searching for Plex Server - Used by Sync and when attempting to Direct Play - Customize Paths - Extend Plex TV Series "On Deck" view to all shows - Recently Added: Append show title to episode - Recently Added: Append season- and episode-number SxxExx - Would you like to download additional artwork from FanArtTV in the background? - Sync when screensaver is deactivated - Force Transcode Hi10P - Recently Added: Also show already watched episodes - Recently Added: Also show already watched movies (Refresh Plex playlist/nodes!) - Your current Plex Media Server: - [COLOR yellow]Manually enter Plex Media Server address[/COLOR] - Current address: - Current port: - Current plex.tv status: - Is your Kodi installed on a low-powered device like a Raspberry Pi? If yes, then we will reduce the strain on Kodi to prevent it from crashing. - Appearance Tweaks - TV Shows - Always use default Plex subtitle if possible - If you use several Plex libraries of one kind, e.g. "Kids Movies" and "Parents Movies", be sure to check the Wiki: https://goo.gl/JFtQV9 - Number of PMS items to show in widgets (e.g. "On Deck") - Plex Companion Update Port (change only if needed) - Plex Companion could not open the GDM port. Please change it in the PKC settings. + Erstat Plex stier /volume1/media eller \\myserver\media med brugerdefinerede SMB stier smb://NAS/mystuff + Oprindelige Plex film sti erstattes: + Erstatte Plex film med: + Oprindelige Plex TV shows sti erstattes: + Erstatte Plex TV SHOWS med: + Oprindelige Plex Musik sti erstattes: + Erstatte Plex musik med: + Gå et skridt videre og erstat alle oprindelige Plex bibliotek stier (/ volume1/media) med brugerdefinerede SMB-stier (smb://NAS/MyStuff)? + Indtast venligst din brugerdefinerede smb stier i indstillingerne under "synkroniseringsindstillinger" og derefter genstarte Kodi + Oprindelige Plex Foto sti erstattes: + Erstatte Plex foto med: + Senest Tilføjet: Vis titel til episode + Senest Tilføjet: Føj sæson - og episode-nummer SxxExx + Intet virker? Prøv en fuld reset! + [Farve gul] Vælg Plex Server fra listen[/COLOR] + Vente før sync af nye/ændrede PMS element [s] + Baggrundssynkronisering + Kør en fuld bibliotek synkronisering hvert x minut + Fjern adgang + Søg efter Plex Server + Brugt af Sync og når du forsøger at direkte spille + Tilpasse stier + Udvide Plex TV serie "Senest Tilføjet" Til vis alle shows + Senest tilføjet: Føj Vis titel til episode + Senest Tilføjet: Føj sæson - og episode-nummer SxxExx + Ønsker du at downloade ekstra illustrationer fra FanArtTV i baggrunden? + Sync når screensaver er deaktiveret + Transcode Hi10P + Senest tilføjet: Vis allerede set episoder + Senest tilføjet: Vis allerede set film (Opdater Plex spilleliste/noder!) + Din nuværende Plex Media Server: + [Farve gul] Manuelt indtast Plex Media Server address[/COLOR] + Nuværende adresse: + Nuværende port: + Nuværende plex.tv status: + Er din Kodi installeret på en low-power enhed som en Raspberry Pi? Hvis ja, vil vi reducere belastning på Kodi til at forhindre, at det bryder sammen. + Udseende Tweaks + TV-udsendelser + Brug altid standard Plex undertekst hvis muligt + Hvis du bruger flere Plex biblioteker af en slags, fx "Kids film" og "Forældre film", skal du kontrollere Wiki: https://goo.gl/JFtQV9 + Antallet af PMS elementer at vise i widgets (fx "Igangværende") + Plex ledsager opdaterede Port (ændring kun hvis nødvendigt) + Plex ledsager kunne ikke åbne GDM port. Du kan ændre den i PKC indstillinger. - Log-out Plex Home User - Settings - Network credentials - Refresh Plex playlists/nodes - Perform manual library sync - Unable to run the sync, the add-on is not connected to a Plex server. - Plex might lock your account if you fail to log in too many times. Proceed anyway? - Resetting PMS connections, please wait - Failed to reset PKC. Try to restart Kodi. - [COLOR yellow]Toggle plex.tv login (sign in or sign out)[/COLOR] - Not yet connected to Plex Server - Watch later - is offline - Even though we signed in to plex.tv, we could not authorize for PMS - Enter your Plex Media Server's IP or URL, Examples are: + Log ud Plex hjemme bruger + Indstillinger + Legitimationsoplysninger til netværket + Opdater Plex afspilningslister/noder + Udføre manuel bibliotek synkronisering + Ude af stand til at køre synkroniseringen, add-on er ikke forbundet med en Plex server. + Plex kan låse din konto, hvis dit login forsøg fejler for mange gange. Vil du fortsætte alligevel? + Nulstille PMS forbindelser, vent + Undladt at nulstille PKC. Prøv at genstarte Kodi. + [Farve gul] Skifte plex.tv login (logge på eller logge ud)[/COLOR] + Endnu ikke forbundet med Plex Server + Se senere + Offline + Selvom vi logget på plex.tv, kunne vi ikke give tilladelse til PMS + Indtast din Plex Media Server IP eller URL, eksempler er: - Does your Plex Media Server support SSL connections? (https instead of http)? - Error contacting PMS - Abort (Yes) or save address anyway (No)? - connected - plex.tv toggle successful - [COLOR yellow]Look for missing fanart on FanartTV now[/COLOR] - Only look for missing fanart or refresh all fanart? The scan will take quite a while and happen in the background. - Refresh all - Missing only + Understøtter din Plex Media Server SSL-forbindelser? (https i stedet for http)? + Fejl ved oprettelse af forbindelse til PMS + Afbryde (ja) eller gemme adresse alligevel (Nej)? + Tilsluttet + Plex.tv skift vellykket + [Farve gul] Se efter for manglende fanart på FanartTV nu![/COLOR] + Søg kun efter manglende fanart eller Opdater alle fanart? Scanningen vil tage lang tid og sker i baggrunden. + Opdater alle + Mangler kun - Running the image cache process can take some time. It will happen in the background. Are you sure you want continue? - Reset all existing cache data first? + Kører image cache-processen kan tage lidt tid. Det sker i baggrunden. Er du sikker på du vil fortsætte? + Nulstille alle eksisterende cache data først? - : Enter plex.tv username. Or nothing to cancel. - Enter password for plex.tv user - Could not sign in user - Problems trying to contact plex.tv. Try again later - Go to https://plex.tv/pin and enter the code: - Could not sign in to plex.tv. Try again later - : Select User - Enter PIN for user - Could not log in user - Please try again. - unknown - or press No to not sign in. + : Indtast plex.tv Brugernavn. Eller intet at annullere. + Angiv adgangskode for brugeren: + Kunne ikke logge brugeren på + Problemer med at kontakte plex.tv. Prøv igen senere + Gå til https://plex.tv/pin og Indtast koden: + Kunne ikke logge på plex.tv, forsøg venligst igen senere + : Vælg bruger + Indtast PIN-kode for bruger + Kunne ikke logge brugeren på + Forsøg venligst igen. + Ukendt + eller tryk på Nej for ikke at logge på. - Library sync thread has crashed. You should restart Kodi now. Please report this on the forum - Detected Kodi database needs to be recreated for this version. This might take a while. Proceed? - may not work correctly until the database is reset. - Cancelling the database syncing process. Current Kodi version is unsupported. Please verify your logs for more info. - Startup syncing process failed repeatedly. Try restarting Kodi. Stopping Sync for now. - Plex playlists/nodes refreshed - Plex playlists/nodes refresh failed - Full library sync finished - Sync had to skip some items because they could not be processed. Kodi may be instable now!! Please post your Kodi logs to the Plex forum. - The Plex Server did not like you asking for so much data at once and returned ERRORS. Try lowering the number of sync download threads in the settings. Skipped some items for now. - ERROR in library sync + Biblioteket sync tråd er gået ned. Du skal genstarte Kodi nu. Bedes du rapportere det på forum + Registreret database skal genskabes i denne version af Emby for Kodi. Vil du fortsætte? + kan ikke fungerer korrekt indtil databasen bliver nulstillet. + Annullering af databasen synkronisering proces. Nuværende Kodi version understøttes ikke. Kontroller dine logfiler for mere info. + Start synkronisering processen mislykkedes gentagne gange. Prøv at genstarte Kodi. Stop synkronisering for nu. + Plex afspilningslister/noder opdateret + Plex afspilningslister/noder opdatering mislykkedes + Fuld bibliotek synkronisering færdig! + Sync måtte springe nogle emner, fordi de ikke kunne behandles. Kodi kan være ustabilt nu!! Bedes du sende dine Kodi logfiler til Plex forumet. + Plex-serveren kunne ikke lide du beder om så meget data på én gang og returnerede fejl. Prøv at sænke antallet af sync download tråde i indstillingerne. Spring nogle emner over for nu. + FEJL i bibliotek synkronisering - On Deck - Collections + Igangværende + Samlinger - Are you sure you want to reset your local Kodi database? A re-sync of the Plex data will take time afterwards. - Could not stop the database from running. Please try again later. - Remove all cached artwork? (recommended!) - Reset all PlexKodiConnect Addon settings? (this is usually NOT recommended and unnecessary!) + Vil du nulstille dine lokale Kodi database? Synkroniser Plex data igen vil tage tid bagefter. + Kunne ikke stoppe den kørende database. Prøv igen senere. + Fjern alle cachelagrede illustrationer? (anbefales!) + Nulstil alle indstillinger for PlexKodiConnect Addon? (dette er normalt ikke anbefalet og unødvendigt!) \ No newline at end of file From 020efe4696e7bb156a8b076b2a2fa6500a8af570 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Wed, 1 Mar 2017 19:23:44 +0100 Subject: [PATCH 34/55] Update changelog --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index 7c361ab4..36d57b17 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,7 @@ version 1.6.2 - Fix TypeError on manually entering PMS port - Fix KeyError +- Update Danish translation - Update readme version 1.6.1 From 4f2cae708acc84e27d3e860af46355231b515db2 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Wed, 1 Mar 2017 19:29:11 +0100 Subject: [PATCH 35/55] Update readme --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index 36d57b17..d9c70b6b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,5 @@ version 1.6.2 +- Fix Plex Web Issue, thanks @AllanMar - Fix TypeError on manually entering PMS port - Fix KeyError - Update Danish translation From b734d0be8ef13f654d7642b30f254f741f6a1e58 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sat, 4 Mar 2017 17:54:24 +0100 Subject: [PATCH 36/55] Amazon Alexa support! --- default.py | 3 +- resources/lib/PlexCompanion.py | 21 +- resources/lib/clientinfo.py | 2 +- resources/lib/companion.py | 193 +++++++++++++++ resources/lib/entrypoint.py | 23 +- resources/lib/plexbmchelper/listener.py | 139 ++--------- resources/lib/variables.py | 13 + resources/lib/websocket_client.py | 303 ++++++++++++++---------- service.py | 14 +- 9 files changed, 456 insertions(+), 255 deletions(-) create mode 100644 resources/lib/companion.py diff --git a/default.py b/default.py index 3c733577..bfc9b517 100644 --- a/default.py +++ b/default.py @@ -161,8 +161,7 @@ class Main(): modes[mode](itemid, params=argv[2]) elif mode == 'Plex_Node': modes[mode](params.get('id'), - params.get('viewOffset'), - params.get('plex_type')) + params.get('viewOffset')) else: modes[mode]() else: diff --git a/resources/lib/PlexCompanion.py b/resources/lib/PlexCompanion.py index 03780c49..7712f31c 100644 --- a/resources/lib/PlexCompanion.py +++ b/resources/lib/PlexCompanion.py @@ -6,14 +6,17 @@ from socket import SHUT_RDWR from xbmc import sleep -from utils import settings, ThreadMethodsAdditionalSuspend, ThreadMethods +from utils import settings, ThreadMethodsAdditionalSuspend, ThreadMethods, \ + window from plexbmchelper import listener, plexgdm, subscribers, functions, \ httppersist, plexsettings from PlexFunctions import ParseContainerKey, GetPlexMetadata from PlexAPI import API import player from entrypoint import Plex_Node -from variables import KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE +from variables import KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE, \ + PLEX_TO_KODI_TIMEFACTOR + ############################################################################### @@ -83,19 +86,28 @@ class PlexCompanion(Thread): thread = Thread(target=Plex_Node, args=('{server}%s' % data.get('key'), data.get('offset'), - data.get('type'), True),) thread.setDaemon(True) thread.start() elif task['action'] == 'playlist': # Get the playqueue ID try: - _, ID, query = ParseContainerKey(data['containerKey']) + typus, ID, query = ParseContainerKey(data['containerKey']) except Exception as e: log.error('Exception while processing: %s' % e) import traceback log.error("Traceback:\n%s" % traceback.format_exc()) return + if typus == 'library/metadata': + # e.g. Alexa + thread = Thread(target=Plex_Node, + args=('{server}%s' % data.get('key'), + data.get('offset'), + True, + False),) + thread.setDaemon(True) + thread.start() + return try: playqueue = self.mgr.playqueue.get_playqueue_from_type( KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[data['type']]) @@ -126,6 +138,7 @@ class PlexCompanion(Thread): jsonClass, requestMgr, self.player, self.mgr) queue = Queue.Queue(maxsize=100) + self.queue = queue if settings('plexCompanion') == 'true': # Start up httpd diff --git a/resources/lib/clientinfo.py b/resources/lib/clientinfo.py index 8568930c..91cb6b91 100644 --- a/resources/lib/clientinfo.py +++ b/resources/lib/clientinfo.py @@ -39,7 +39,7 @@ def getXArgsDeviceInfo(options=None): 'X-Plex-Product': v.ADDON_NAME, 'X-Plex-Version': v.ADDON_VERSION, 'X-Plex-Client-Identifier': getDeviceId(), - 'X-Plex-Provides': 'client,controller,player', + 'X-Plex-Provides': 'client,controller,player,pubsub-player', } if window('pms_token'): xargs['X-Plex-Token'] = window('pms_token') diff --git a/resources/lib/companion.py b/resources/lib/companion.py new file mode 100644 index 00000000..5791faf1 --- /dev/null +++ b/resources/lib/companion.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- +import logging +from urlparse import urlparse +from re import compile as re_compile + +from utils import JSONRPC +import plexdb_functions as plexdb +from variables import ALEXA_TO_COMPANION + +############################################################################### + +log = logging.getLogger("PLEX."+__name__) + +REGEX_PLAYQUEUES = re_compile(r'''/playQueues/(\d+)$''') + +############################################################################### + + +def getPlayers(): + info = JSONRPC("Player.GetActivePlayers").execute()['result'] or [] + log.debug('players: %s' % JSONRPC("Player.GetActivePlayers").execute()) + ret = {} + for player in info: + player['playerid'] = int(player['playerid']) + ret[player['type']] = player + return ret + + +def getPlayerIds(): + ret = [] + for player in getPlayers().values(): + ret.append(player['playerid']) + return ret + + +def getPlaylistId(typus): + """ + typus: one of the Kodi types, e.g. audio or video + + Returns None if nothing was found + """ + for playlist in getPlaylists(): + if playlist.get('type') == typus: + return playlist.get('playlistid') + + +def getPlaylists(): + """ + Returns a list, e.g. + [ + {u'playlistid': 0, u'type': u'audio'}, + {u'playlistid': 1, u'type': u'video'}, + {u'playlistid': 2, u'type': u'picture'} + ] + """ + return JSONRPC('Playlist.GetPlaylists').execute() + + +def millisToTime(t): + millis = int(t) + seconds = millis / 1000 + minutes = seconds / 60 + hours = minutes / 60 + seconds = seconds % 60 + minutes = minutes % 60 + millis = millis % 1000 + return {'hours': hours, + 'minutes': minutes, + 'seconds': seconds, + 'milliseconds': millis} + + +def skipTo(self, plexId, typus): + # playlistId = self.getPlaylistId(tryDecode(xbmc_type(typus))) + # playerId = self. + with plexdb.Get_Plex_DB() as plex_db: + plexdb_item = plex_db.getItem_byId(plexId) + try: + dbid = plexdb_item[0] + mediatype = plexdb_item[4] + except TypeError: + log.info('Couldnt find item %s in Kodi db' % plexId) + return + log.debug('plexid: %s, kodi id: %s, type: %s' + % (plexId, dbid, mediatype)) + + +def convert_alexa_to_companion(dictionary): + for key in dictionary: + if key in ALEXA_TO_COMPANION: + dictionary[ALEXA_TO_COMPANION[key]] = dictionary[key] + del dictionary[key] + + +def process_command(request_path, params, queue=None): + """ + queue: Queue() of PlexCompanion.py + """ + if params.get('deviceName') == 'Alexa': + convert_alexa_to_companion(params) + log.debug('Received request_path: %s, params: %s' % (request_path, params)) + if "/playMedia" in request_path: + try: + containerKey = urlparse(params.get('containerKey')).path + except: + containerKey = '' + try: + playQueueID = REGEX_PLAYQUEUES.findall(containerKey)[0] + except IndexError: + playQueueID = '' + # We need to tell service.py + queue.put({ + 'action': 'playlist', + 'data': params + }) + return { + 'lastkey': params['key'], + 'containerKey': containerKey, + 'playQueueID': playQueueID, + } + + elif request_path == "player/playback/setParameters": + if 'volume' in params: + volume = int(params['volume']) + log.debug("Adjusting the volume to %s" % volume) + JSONRPC('Application.SetVolume').execute({"volume": volume}) + + elif request_path == "player/playback/play": + for playerid in getPlayerIds(): + JSONRPC("Player.PlayPause").execute({"playerid": playerid, + "play": True}) + + elif request_path == "player/playback/pause": + for playerid in getPlayerIds(): + JSONRPC("Player.PlayPause").execute({"playerid": playerid, + "play": False}) + + elif request_path == "player/playback/stop": + for playerid in getPlayerIds(): + JSONRPC("Player.Stop").execute({"playerid": playerid}) + + elif request_path == "player/playback/seekTo": + for playerid in getPlayerIds(): + JSONRPC("Player.Seek").execute( + {"playerid": playerid, + "value": millisToTime(params.get('offset', 0))}) + + elif request_path == "player/playback/stepForward": + for playerid in getPlayerIds(): + JSONRPC("Player.Seek").execute({"playerid": playerid, + "value": "smallforward"}) + + elif request_path == "player/playback/stepBack": + for playerid in getPlayerIds(): + JSONRPC("Player.Seek").execute({"playerid": playerid, + "value": "smallbackward"}) + + elif request_path == "player/playback/skipNext": + for playerid in getPlayerIds(): + JSONRPC("Player.GoTo").execute({"playerid": playerid, + "to": "next"}) + + elif request_path == "player/playback/skipPrevious": + for playerid in getPlayerIds(): + JSONRPC("Player.GoTo").execute({"playerid": playerid, + "to": "previous"}) + + elif request_path == "player/playback/skipTo": + skipTo(params.get('key').rsplit('/', 1)[1], params.get('type')) + + elif request_path == "player/navigation/moveUp": + JSONRPC("Input.Up").execute() + + elif request_path == "player/navigation/moveDown": + JSONRPC("Input.Down").execute() + + elif request_path == "player/navigation/moveLeft": + JSONRPC("Input.Left").execute() + + elif request_path == "player/navigation/moveRight": + JSONRPC("Input.Right").execute() + + elif request_path == "player/navigation/select": + JSONRPC("Input.Select").execute() + + elif request_path == "player/navigation/home": + JSONRPC("Input.Home").execute() + + elif request_path == "player/navigation/back": + JSONRPC("Input.Back").execute() + + else: + log.error('Unknown request path: %s' % request_path) diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py index cd4f70dc..2aa33cd3 100644 --- a/resources/lib/entrypoint.py +++ b/resources/lib/entrypoint.py @@ -14,6 +14,7 @@ from utils import window, settings, language as lang, dialog, tryDecode,\ tryEncode, CatchExceptions, JSONRPC import downloadutils import playbackutils as pbutils +import plexdb_functions as plexdb from PlexFunctions import GetPlexMetadata, GetPlexSectionResults, \ GetMachineIdentifier @@ -96,7 +97,7 @@ def togglePlexTV(): sound=False) -def Plex_Node(url, viewOffset, plex_type, playdirectly=False): +def Plex_Node(url, viewOffset, playdirectly=False, node=True): """ Called only for a SINGLE element for Plex.tv watch later @@ -120,11 +121,25 @@ def Plex_Node(url, viewOffset, plex_type, playdirectly=False): else: window('plex_customplaylist.seektime', value=str(viewOffset)) log.info('Set resume point to %s' % str(viewOffset)) - typus = v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[plex_type] + api = API(xml[0]) + typus = v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()] + if node is True: + plex_id = None + kodi_id = 'plexnode' + else: + plex_id = api.getRatingKey() + kodi_id = None + with plexdb.Get_Plex_DB() as plex_db: + plexdb_item = plex_db.getItem_byId(plex_id) + try: + kodi_id = plexdb_item[0] + except TypeError: + log.info('Couldnt find item %s in Kodi db' + % api.getRatingKey()) playqueue = Playqueue().get_playqueue_from_type(typus) result = pbutils.PlaybackUtils(xml, playqueue).play( - None, - kodi_id='plexnode', + plex_id, + kodi_id=kodi_id, plex_lib_UUID=xml.attrib.get('librarySectionUUID')) if result.listitem: listitem = convert_PKC_to_listitem(result.listitem) diff --git a/resources/lib/plexbmchelper/listener.py b/resources/lib/plexbmchelper/listener.py index de63b8cc..f240ea1c 100644 --- a/resources/lib/plexbmchelper/listener.py +++ b/resources/lib/plexbmchelper/listener.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- import logging -import re +from re import sub from SocketServer import ThreadingMixIn from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from urlparse import urlparse, parse_qs from xbmc import sleep +from companion import process_command +from utils import window from functions import * @@ -19,7 +21,6 @@ log = logging.getLogger("PLEX."+__name__) class MyHandler(BaseHTTPRequestHandler): protocol_version = 'HTTP/1.1' - regex = re.compile(r'''/playQueues/(\d+)$''') def __init__(self, *args, **kwargs): BaseHTTPRequestHandler.__init__(self, *args, **kwargs) @@ -83,11 +84,10 @@ class MyHandler(BaseHTTPRequestHandler): subMgr = self.server.subscriptionManager js = self.server.jsonClass settings = self.server.settings - queue = self.server.queue try: request_path = self.path[1:] - request_path = re.sub(r"\?.*", "", request_path) + request_path = sub(r"\?.*", "", request_path) url = urlparse(self.path) paramarrays = parse_qs(url.query) params = {} @@ -145,9 +145,9 @@ class MyHandler(BaseHTTPRequestHandler): sleep(950) commandID = params.get('commandID', 0) self.response( - re.sub(r"INSERTCOMMANDID", - str(commandID), - subMgr.msg(js.getPlayers())), + sub(r"INSERTCOMMANDID", + str(commandID), + subMgr.msg(js.getPlayers())), { 'X-Plex-Client-Identifier': settings['uuid'], 'Access-Control-Expose-Headers': @@ -160,121 +160,18 @@ class MyHandler(BaseHTTPRequestHandler): uuid = self.headers.get('X-Plex-Client-Identifier', False) \ or self.client_address[0] subMgr.removeSubscriber(uuid) - elif request_path == "player/playback/setParameters": - self.response(getOKMsg(), js.getPlexHeaders()) - if 'volume' in params: - volume = int(params['volume']) - log.debug("adjusting the volume to %s%%" % volume) - js.jsonrpc("Application.SetVolume", - {"volume": volume}) - elif "/playMedia" in request_path: - self.response(getOKMsg(), js.getPlexHeaders()) - offset = params.get('viewOffset', params.get('offset', "0")) - protocol = params.get('protocol', "http") - address = params.get('address', self.client_address[0]) - server = self.getServerByHost(address) - port = params.get('port', server.get('port', '32400')) - try: - containerKey = urlparse(params.get('containerKey')).path - except: - containerKey = '' - try: - playQueueID = self.regex.findall(containerKey)[0] - except IndexError: - playQueueID = '' - # We need to tell service.py - queue.put({ - 'action': 'playlist', - 'data': params - }) - subMgr.lastkey = params['key'] - subMgr.containerKey = containerKey - subMgr.playQueueID = playQueueID - subMgr.server = server.get('server', 'localhost') - subMgr.port = port - subMgr.protocol = protocol - subMgr.notify() - elif request_path == "player/playback/play": - self.response(getOKMsg(), js.getPlexHeaders()) - for playerid in js.getPlayerIds(): - js.jsonrpc("Player.PlayPause", - {"playerid": playerid, "play": True}) - subMgr.notify() - elif request_path == "player/playback/pause": - self.response(getOKMsg(), js.getPlexHeaders()) - for playerid in js.getPlayerIds(): - js.jsonrpc("Player.PlayPause", - {"playerid": playerid, "play": False}) - subMgr.notify() - elif request_path == "player/playback/stop": - self.response(getOKMsg(), js.getPlexHeaders()) - for playerid in js.getPlayerIds(): - js.jsonrpc("Player.Stop", {"playerid": playerid}) - subMgr.notify() - elif request_path == "player/playback/seekTo": - self.response(getOKMsg(), js.getPlexHeaders()) - for playerid in js.getPlayerIds(): - js.jsonrpc("Player.Seek", - {"playerid": playerid, - "value": millisToTime( - params.get('offset', 0))}) - subMgr.notify() - elif request_path == "player/playback/stepForward": - self.response(getOKMsg(), js.getPlexHeaders()) - for playerid in js.getPlayerIds(): - js.jsonrpc("Player.Seek", - {"playerid": playerid, - "value": "smallforward"}) - subMgr.notify() - elif request_path == "player/playback/stepBack": - self.response(getOKMsg(), js.getPlexHeaders()) - for playerid in js.getPlayerIds(): - js.jsonrpc("Player.Seek", - {"playerid": playerid, - "value": "smallbackward"}) - subMgr.notify() - elif request_path == "player/playback/skipNext": - self.response(getOKMsg(), js.getPlexHeaders()) - for playerid in js.getPlayerIds(): - js.jsonrpc("Player.GoTo", - {"playerid": playerid, - "to": "next"}) - subMgr.notify() - elif request_path == "player/playback/skipPrevious": - self.response(getOKMsg(), js.getPlexHeaders()) - for playerid in js.getPlayerIds(): - js.jsonrpc("Player.GoTo", - {"playerid": playerid, - "to": "previous"}) - subMgr.notify() - elif request_path == "player/playback/skipTo": - js.skipTo(params.get('key').rsplit('/', 1)[1], - params.get('type')) - subMgr.notify() - elif request_path == "player/navigation/moveUp": - self.response(getOKMsg(), js.getPlexHeaders()) - js.jsonrpc("Input.Up") - elif request_path == "player/navigation/moveDown": - self.response(getOKMsg(), js.getPlexHeaders()) - js.jsonrpc("Input.Down") - elif request_path == "player/navigation/moveLeft": - self.response(getOKMsg(), js.getPlexHeaders()) - js.jsonrpc("Input.Left") - elif request_path == "player/navigation/moveRight": - self.response(getOKMsg(), js.getPlexHeaders()) - js.jsonrpc("Input.Right") - elif request_path == "player/navigation/select": - self.response(getOKMsg(), js.getPlexHeaders()) - js.jsonrpc("Input.Select") - elif request_path == "player/navigation/home": - self.response(getOKMsg(), js.getPlexHeaders()) - js.jsonrpc("Input.Home") - elif request_path == "player/navigation/back": - self.response(getOKMsg(), js.getPlexHeaders()) - js.jsonrpc("Input.Back") else: - log.error('Unknown request path: %s' % request_path) - + # Throw it to companion.py + answ = process_command(request_path, params, self.server.queue) + self.response(getOKMsg(), js.getPlexHeaders()) + subMgr.notify() + if answ is not None: + subMgr.lastkey = answ['lastkey'] + subMgr.containerKey = answ['containerKey'] + subMgr.playQueueID = answ['playQueueID'] + subMgr.protocol, subMgr.server, subMgr.port = \ + window('pms_server').split(':', 2) + subMgr.server = subMgr.server.replace('/', '') except: log.error('Error encountered. Traceback:') import traceback diff --git a/resources/lib/variables.py b/resources/lib/variables.py index ff9a1126..28b34278 100644 --- a/resources/lib/variables.py +++ b/resources/lib/variables.py @@ -248,3 +248,16 @@ KODI_SUPPORTED_IMAGES = ( '.pcx', '.tga' ) + + +# Translation table from Alexa websocket commands to Plex Companion +ALEXA_TO_COMPANION = { + 'queryKey': 'key', + 'queryOffset': 'offset', + 'queryMachineIdentifier': 'machineIdentifier', + 'queryProtocol': 'protocol', + 'queryAddress': 'address', + 'queryPort': 'port', + 'queryContainerKey': 'containerKey', + 'queryToken': 'token', +} diff --git a/resources/lib/websocket_client.py b/resources/lib/websocket_client.py index 312080b5..1d19d021 100644 --- a/resources/lib/websocket_client.py +++ b/resources/lib/websocket_client.py @@ -4,6 +4,7 @@ import logging import websocket from json import loads +import xml.etree.ElementTree as etree from threading import Thread from Queue import Queue from ssl import CERT_NONE @@ -12,6 +13,7 @@ from xbmc import sleep from utils import window, settings, ThreadMethodsAdditionalSuspend, \ ThreadMethods +from companion import process_command ############################################################################### @@ -29,10 +31,151 @@ class WebSocket(Thread): if callback is not None: self.mgr = callback self.ws = None - # Communication with librarysync - self.queue = Queue() Thread.__init__(self) + def process(self, opcode, message): + raise NotImplementedError + + def receive(self, ws): + # Not connected yet + if ws is None: + raise websocket.WebSocketConnectionClosedException + + frame = ws.recv_frame() + + if not frame: + raise websocket.WebSocketException("Not a valid frame %s" % frame) + elif frame.opcode in self.opcode_data: + return frame.opcode, frame.data + elif frame.opcode == websocket.ABNF.OPCODE_CLOSE: + ws.send_close() + return frame.opcode, None + elif frame.opcode == websocket.ABNF.OPCODE_PING: + ws.pong("Hi!") + return None, None + + def getUri(self): + raise NotImplementedError + + def run(self): + log.info("----===## Starting %s ##===----" % self.__class__.__name__) + + counter = 0 + handshake_counter = 0 + threadStopped = self.threadStopped + threadSuspended = self.threadSuspended + while not threadStopped(): + # In the event the server goes offline + while threadSuspended(): + # Set in service.py + if self.ws is not None: + try: + self.ws.shutdown() + except: + pass + self.ws = None + if threadStopped(): + # Abort was requested while waiting. We should exit + log.info("##===---- %s Stopped ----===##" + % self.__class__.__name__) + return + sleep(1000) + try: + self.process(*self.receive(self.ws)) + except websocket.WebSocketTimeoutException: + # No worries if read timed out + pass + except websocket.WebSocketConnectionClosedException: + log.info("Connection closed, (re)connecting") + uri, sslopt = self.getUri() + try: + # Low timeout - let's us shut this thread down! + self.ws = websocket.create_connection( + uri, + timeout=1, + sslopt=sslopt, + enable_multithread=True) + except IOError: + # Server is probably offline + log.info("Error connecting") + self.ws = None + counter += 1 + if counter > 3: + counter = 0 + self.IOError_response() + sleep(1000) + except websocket.WebSocketTimeoutException: + log.info("timeout while connecting, trying again") + self.ws = None + sleep(1000) + except websocket.WebSocketException as e: + log.info('WebSocketException: %s' % e) + if 'Handshake Status 401' in e.args: + handshake_counter += 1 + if handshake_counter >= 5: + log.info('Error in handshake detected. Stopping ' + '%s now' % self.__class__.__name__) + break + self.ws = None + sleep(1000) + except Exception as e: + log.error("Unknown exception encountered in connecting: %s" + % e) + import traceback + log.error("Traceback:\n%s" % traceback.format_exc()) + self.ws = None + sleep(1000) + else: + counter = 0 + handshake_counter = 0 + except Exception as e: + log.error("Unknown exception encountered: %s" % e) + import traceback + log.error("Traceback:\n%s" % traceback.format_exc()) + try: + self.ws.shutdown() + except: + pass + self.ws = None + log.info("##===---- %s Stopped ----===##" % self.__class__.__name__) + + def stopThread(self): + """ + Overwrite this method from ThreadMethods to close websockets + """ + log.info("Stopping %s thread." % self.__class__.__name__) + self._threadStopped = True + try: + self.ws.shutdown() + except: + pass + + +class PMS_Websocket(WebSocket): + """ + Websocket connection with the PMS for Plex Companion + """ + # Communication with librarysync + queue = Queue() + + def getUri(self): + server = window('pms_server') + # Need to use plex.tv token, if any. NOT user token + token = window('plex_token') + # Get the appropriate prefix for the websocket + if server.startswith('https'): + server = "wss%s" % server[5:] + else: + server = "ws%s" % server[4:] + uri = "%s/:/websockets/notifications" % server + if token: + uri += '?X-Plex-Token=%s' % token + sslopt = {} + if settings('sslverify') == "false": + sslopt["cert_reqs"] = CERT_NONE + log.debug("Uri: %s, sslopt: %s" % (uri, sslopt)) + return uri, sslopt + def process(self, opcode, message): if opcode not in self.opcode_data: return False @@ -62,131 +205,49 @@ class WebSocket(Thread): self.queue.put(message) return True - def receive(self, ws): - # Not connected yet - if ws is None: - raise websocket.WebSocketConnectionClosedException + def IOError_response(self): + log.warn("Repeatedly could not connect to PMS, " + "declaring the connection dead") + window('plex_online', value='false') - frame = ws.recv_frame() - - if not frame: - raise websocket.WebSocketException("Not a valid frame %s" % frame) - elif frame.opcode in self.opcode_data: - return frame.opcode, frame.data - elif frame.opcode == websocket.ABNF.OPCODE_CLOSE: - ws.send_close() - return frame.opcode, None - elif frame.opcode == websocket.ABNF.OPCODE_PING: - ws.pong("Hi!") - return None, None +class Alexia_Websocket(WebSocket): + """ + Websocket connection to talk to Amazon Alexia + """ def getUri(self): - server = window('pms_server') - # Need to use plex.tv token, if any. NOT user token - token = window('plex_token') - # Get the appropriate prefix for the websocket - if server.startswith('https'): - server = "wss%s" % server[5:] - else: - server = "ws%s" % server[4:] - uri = "%s/:/websockets/notifications" % server - if token: - uri += '?X-Plex-Token=%s' % token + self.plex_client_Id = window('plex_client_Id') + uri = ('wss://pubsub.plex.tv/sub/websockets/%s/%s?X-Plex-Token=%s' + % (window('currUserId'), + self.plex_client_Id, + window('plex_token'))) sslopt = {} - if settings('sslverify') == "false": - sslopt["cert_reqs"] = CERT_NONE log.debug("Uri: %s, sslopt: %s" % (uri, sslopt)) return uri, sslopt - def run(self): - log.info("----===## Starting WebSocketClient ##===----") - - counter = 0 - handshake_counter = 0 - threadStopped = self.threadStopped - threadSuspended = self.threadSuspended - while not threadStopped(): - # In the event the server goes offline - while threadSuspended(): - # Set in service.py - if self.ws is not None: - try: - self.ws.shutdown() - except: - pass - self.ws = None - if threadStopped(): - # Abort was requested while waiting. We should exit - log.info("##===---- WebSocketClient Stopped ----===##") - return - sleep(1000) - try: - self.process(*self.receive(self.ws)) - except websocket.WebSocketTimeoutException: - # No worries if read timed out - pass - except websocket.WebSocketConnectionClosedException: - log.info("Connection closed, (re)connecting") - uri, sslopt = self.getUri() - try: - # Low timeout - let's us shut this thread down! - self.ws = websocket.create_connection( - uri, - timeout=1, - sslopt=sslopt, - enable_multithread=True) - except IOError: - # Server is probably offline - log.info("Error connecting") - self.ws = None - counter += 1 - if counter > 3: - log.warn("Repeatedly could not connect to PMS, " - "declaring the connection dead") - window('plex_online', value='false') - counter = 0 - sleep(1000) - except websocket.WebSocketTimeoutException: - log.info("timeout while connecting, trying again") - self.ws = None - sleep(1000) - except websocket.WebSocketException as e: - log.info('WebSocketException: %s' % e) - if 'Handshake Status 401' in e.args: - handshake_counter += 1 - if handshake_counter >= 5: - log.info('Error in handshake detected. Stopping ' - 'WebSocketClient now') - break - self.ws = None - sleep(1000) - except Exception as e: - log.error("Unknown exception encountered in connecting: %s" - % e) - import traceback - log.error("Traceback:\n%s" % traceback.format_exc()) - self.ws = None - sleep(1000) - else: - counter = 0 - handshake_counter = 0 - except Exception as e: - log.error("Unknown exception encountered: %s" % e) - try: - self.ws.shutdown() - except: - pass - self.ws = None - - log.info("##===---- WebSocketClient Stopped ----===##") - - def stopThread(self): - """ - Overwrite this method from ThreadMethods to close websockets - """ - log.info("Stopping websocket client thread.") - self._threadStopped = True + def process(self, opcode, message): + if opcode not in self.opcode_data: + return False + log.debug('Received the following message from Alexia:') + log.debug(message) try: - self.ws.shutdown() + message = etree.fromstring(message) + except Exception as ex: + log.error('Error decoding message from Alexa: %s' % ex) + return False + try: + if message.attrib['command'] == 'processRemoteControlCommand': + message = message[0] + else: + log.error('Unknown Alexa message received') + return False except: - pass + log.error('Could not parse Alexia message') + return False + process_command(message.attrib['path'][1:], + message.attrib, + queue=self.mgr.plexCompanion.queue) + return True + + def IOError_response(self): + pass diff --git a/service.py b/service.py index 131d6189..b9afc73d 100644 --- a/service.py +++ b/service.py @@ -36,7 +36,7 @@ import initialsetup from kodimonitor import KodiMonitor from librarysync import LibrarySync import videonodes -from websocket_client import WebSocket +from websocket_client import PMS_Websocket, Alexia_Websocket import downloadutils from playqueue import Playqueue @@ -70,6 +70,7 @@ class Service(): user_running = False ws_running = False + alexia_running = False library_running = False plexCompanion_running = False playqueue_running = False @@ -148,7 +149,8 @@ class Service(): # Initialize important threads, handing over self for callback purposes self.user = UserClient(self) - self.ws = WebSocket(self) + self.ws = PMS_Websocket(self) + self.alexia = Alexia_Websocket(self) self.library = LibrarySync(self) self.plexCompanion = PlexCompanion(self) self.playqueue = Playqueue(self) @@ -201,6 +203,10 @@ class Service(): if not self.ws_running: self.ws_running = True self.ws.start() + # Start the Alexia thread + if not self.alexia_running: + self.alexia_running = True + self.alexia.start() # Start the syncing thread if not self.library_running: self.library_running = True @@ -326,6 +332,10 @@ class Service(): self.ws.stopThread() except: log.warn('Websocket client already shut down') + try: + self.alexia.stopThread() + except: + log.warn('Websocket client already shut down') try: self.user.stopThread() except: From cdb576517e124c4dcc7e43f90cb17327960c588d Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 14:02:58 +0100 Subject: [PATCH 37/55] Fix UnicodeEncodeError - Fixes #244 --- resources/lib/playlist_func.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index 6714c900..63cd994d 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -36,7 +36,7 @@ class Playlist_Object_Baseclase(object): answ += "items: %s, " % self.items for key in self.__dict__: if key not in ("ID", 'items'): - answ += '%s: %s, ' % (key, tryDecode(str(getattr(self, key)))) + answ += '%s: %s, ' % (key, tryEncode(str(getattr(self, key)))) return answ[:-2] + ">" def clear(self): @@ -80,7 +80,7 @@ class Playlist_Item(object): def __repr__(self): answ = "<%s: " % (self.__class__.__name__) for key in self.__dict__: - answ += '%s: %s, ' % (key, tryDecode(str(getattr(self, key)))) + answ += '%s: %s, ' % (key, tryEncode(str(getattr(self, key)))) return answ[:-2] + ">" From d5e19c532835155a214ece24952279d168dd70da Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 15:06:54 +0100 Subject: [PATCH 38/55] Cleanup playlist/playqueue string/unicode - Fixes #244 --- resources/lib/playlist_func.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index 63cd994d..dc0268c7 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -36,7 +36,11 @@ class Playlist_Object_Baseclase(object): answ += "items: %s, " % self.items for key in self.__dict__: if key not in ("ID", 'items'): - answ += '%s: %s, ' % (key, tryEncode(str(getattr(self, key)))) + if type(getattr(self, key)) in (str, unicode): + answ += '%s: %s, ' % (key, tryEncode(getattr(self, key))) + else: + # e.g. int + answ += '%s: %s, ' % (key, str(getattr(self, key))) return answ[:-2] + ">" def clear(self): @@ -73,14 +77,18 @@ class Playlist_Item(object): plex_UUID = None # Plex librarySectionUUID kodi_id = None # Kodi unique kodi id (unique only within type!) kodi_type = None # Kodi type: 'movie' - file = None # Path to the item's file - uri = None # Weird Plex uri path involving plex_UUID + file = None # Path to the item's file. STRING!! + uri = None # Weird Plex uri path involving plex_UUID. STRING! guid = None # Weird Plex guid def __repr__(self): answ = "<%s: " % (self.__class__.__name__) for key in self.__dict__: - answ += '%s: %s, ' % (key, tryEncode(str(getattr(self, key)))) + if type(getattr(self, key)) in (str, unicode): + answ += '%s: %s, ' % (key, tryEncode(getattr(self, key))) + else: + # e.g. int + answ += '%s: %s, ' % (key, str(getattr(self, key))) return answ[:-2] + ">" @@ -258,6 +266,8 @@ def add_listitem_to_playlist(playlist, pos, listitem, kodi_id=None, Adds a listitem to both the Kodi and Plex playlist at position pos [int]. If file is not None, file will overrule kodi_id! + + file: str!! """ log.debug('add_listitem_to_playlist at position %s. Playlist before add: ' '%s' % (pos, playlist)) @@ -285,6 +295,8 @@ def add_item_to_playlist(playlist, pos, kodi_id=None, kodi_type=None, plex_id=None, file=None): """ Adds an item to BOTH the Kodi and Plex playlist at position pos [int] + + file: str! """ log.debug('add_item_to_playlist. Playlist before adding: %s' % playlist) kodi_item = {'id': kodi_id, 'type': kodi_type, 'file': file} @@ -349,6 +361,8 @@ def add_item_to_kodi_playlist(playlist, pos, kodi_id=None, kodi_type=None, Adds an item to the KODI playlist only. WILL ALSO UPDATE OUR PLAYLISTS Returns False if unsuccessful + + file: str! """ log.debug('Adding new item kodi_id: %s, kodi_type: %s, file: %s to Kodi ' 'only at position %s for %s' @@ -496,7 +510,7 @@ def add_to_Kodi_playlist(playlist, xml_video_element): if item.kodi_id: params['item'] = {'%sid' % item.kodi_type: item.kodi_id} else: - params['item'] = {'file': tryEncode(item.file)} + params['item'] = {'file': item.file} reply = JSONRPC('Playlist.Add').execute(params) if reply.get('error') is not None: log.error('Could not add item %s to Kodi playlist. Error: %s' @@ -512,6 +526,8 @@ def add_listitem_to_Kodi_playlist(playlist, pos, listitem, file, Adds an xbmc listitem to the Kodi playlist.xml_video_element WILL NOT UPDATE THE PLEX SIDE, BUT WILL UPDATE OUR PLAYLISTS + + file: string! """ log.debug('Insert listitem at position %s for Kodi only for %s' % (pos, playlist)) From fa45e8dee8692fada098f3788dd355f96836c334 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 15:08:24 +0100 Subject: [PATCH 39/55] Version bump --- addon.xml | 2 +- changelog.txt | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/addon.xml b/addon.xml index 0e45d456..678e5bcc 100644 --- a/addon.xml +++ b/addon.xml @@ -1,7 +1,7 @@ diff --git a/changelog.txt b/changelog.txt index d9c70b6b..955b0a9a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +version 1.6.3 +- Fix UnicodeEncodeError for non ASCII filenames in playback_starter +- Cleanup playlist/playqueue string/unicode + version 1.6.2 - Fix Plex Web Issue, thanks @AllanMar - Fix TypeError on manually entering PMS port From 25afc0c70273261f39a73d0476aa6a03539c99e6 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 15:30:06 +0100 Subject: [PATCH 40/55] Set default companion name to PlexKodiConnect --- resources/lib/plexbmchelper/plexsettings.py | 2 +- resources/lib/variables.py | 19 ++++++++++++------- resources/settings.xml | 3 +-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/resources/lib/plexbmchelper/plexsettings.py b/resources/lib/plexbmchelper/plexsettings.py index cd289f03..3e93b01a 100644 --- a/resources/lib/plexbmchelper/plexsettings.py +++ b/resources/lib/plexbmchelper/plexsettings.py @@ -26,7 +26,7 @@ def getSettings(): options['gdm_debug'] = settings('companionGDMDebugging') options['gdm_debug'] = True if options['gdm_debug'] == 'true' else False - options['client_name'] = settings('deviceName') + options['client_name'] = v.DEVICENAME # XBMC web server options options['webserver_enabled'] = (getGUI('webserver') == "true") diff --git a/resources/lib/variables.py b/resources/lib/variables.py index 28b34278..1ed3fa9c 100644 --- a/resources/lib/variables.py +++ b/resources/lib/variables.py @@ -46,13 +46,18 @@ elif xbmc.getCondVisibility('system.platform.android'): else: PLATFORM = "Unknown" -if _ADDON.getSetting('deviceNameOpt') == "false": - # Use Kodi's deviceName - DEVICENAME = tryDecode(xbmc.getInfoLabel('System.FriendlyName')) -else: - DEVICENAME = tryDecode(_ADDON.getSetting('deviceName')) - DEVICENAME = DEVICENAME.replace("\"", "_") - DEVICENAME = DEVICENAME.replace("/", "_") +DEVICENAME = tryDecode(_ADDON.getSetting('deviceName')) +DEVICENAME = DEVICENAME.replace(":", "") +DEVICENAME = DEVICENAME.replace("/", "-") +DEVICENAME = DEVICENAME.replace("\\", "-") +DEVICENAME = DEVICENAME.replace("<", "") +DEVICENAME = DEVICENAME.replace(">", "") +DEVICENAME = DEVICENAME.replace("*", "") +DEVICENAME = DEVICENAME.replace("?", "") +DEVICENAME = DEVICENAME.replace('|', "") +DEVICENAME = DEVICENAME.replace('(', "") +DEVICENAME = DEVICENAME.replace(')', "") +DEVICENAME = DEVICENAME.strip() # Database paths _DB_VIDEO_VERSION = { diff --git a/resources/settings.xml b/resources/settings.xml index fa8348c7..b2e39d42 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -36,8 +36,7 @@ - - + From 57408c513732ebcdc80900b0ff8918baf02a259e Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 15:38:13 +0100 Subject: [PATCH 41/55] Suspend Alexa thread if Plex user is restricted - Alexa currently only works for the master user for Plex home --- resources/lib/websocket_client.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/resources/lib/websocket_client.py b/resources/lib/websocket_client.py index 1d19d021..120ef9b2 100644 --- a/resources/lib/websocket_client.py +++ b/resources/lib/websocket_client.py @@ -251,3 +251,11 @@ class Alexia_Websocket(WebSocket): def IOError_response(self): pass + + def threadSuspended(self): + """ + Overwrite to ignore library sync stuff and allow to check for + plex_restricteduser + """ + return (self._threadSuspended or + window('plex_restricteduser') == 'true') From df6d753c18b1ec6c60a314446651f7c67db68250 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 15:44:01 +0100 Subject: [PATCH 42/55] Only try to connect Alexa if we got a Plex token --- resources/lib/websocket_client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/lib/websocket_client.py b/resources/lib/websocket_client.py index 120ef9b2..4ec703fc 100644 --- a/resources/lib/websocket_client.py +++ b/resources/lib/websocket_client.py @@ -258,4 +258,5 @@ class Alexia_Websocket(WebSocket): plex_restricteduser """ return (self._threadSuspended or - window('plex_restricteduser') == 'true') + window('plex_restricteduser') == 'true' or + not window('plex_token')) From 715513cdb3a7f0ce5bc9547284272bff3f8e3f0c Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 15:48:08 +0100 Subject: [PATCH 43/55] Fix spelling --- resources/lib/websocket_client.py | 8 ++++---- service.py | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/resources/lib/websocket_client.py b/resources/lib/websocket_client.py index 4ec703fc..7ded4456 100644 --- a/resources/lib/websocket_client.py +++ b/resources/lib/websocket_client.py @@ -211,9 +211,9 @@ class PMS_Websocket(WebSocket): window('plex_online', value='false') -class Alexia_Websocket(WebSocket): +class Alexa_Websocket(WebSocket): """ - Websocket connection to talk to Amazon Alexia + Websocket connection to talk to Amazon Alexa """ def getUri(self): self.plex_client_Id = window('plex_client_Id') @@ -228,7 +228,7 @@ class Alexia_Websocket(WebSocket): def process(self, opcode, message): if opcode not in self.opcode_data: return False - log.debug('Received the following message from Alexia:') + log.debug('Received the following message from Alexa:') log.debug(message) try: message = etree.fromstring(message) @@ -242,7 +242,7 @@ class Alexia_Websocket(WebSocket): log.error('Unknown Alexa message received') return False except: - log.error('Could not parse Alexia message') + log.error('Could not parse Alexa message') return False process_command(message.attrib['path'][1:], message.attrib, diff --git a/service.py b/service.py index b9afc73d..6b23706f 100644 --- a/service.py +++ b/service.py @@ -36,7 +36,7 @@ import initialsetup from kodimonitor import KodiMonitor from librarysync import LibrarySync import videonodes -from websocket_client import PMS_Websocket, Alexia_Websocket +from websocket_client import PMS_Websocket, Alexa_Websocket import downloadutils from playqueue import Playqueue @@ -70,7 +70,7 @@ class Service(): user_running = False ws_running = False - alexia_running = False + alexa_running = False library_running = False plexCompanion_running = False playqueue_running = False @@ -150,7 +150,7 @@ class Service(): # Initialize important threads, handing over self for callback purposes self.user = UserClient(self) self.ws = PMS_Websocket(self) - self.alexia = Alexia_Websocket(self) + self.alexa = Alexa_Websocket(self) self.library = LibrarySync(self) self.plexCompanion = PlexCompanion(self) self.playqueue = Playqueue(self) @@ -203,10 +203,10 @@ class Service(): if not self.ws_running: self.ws_running = True self.ws.start() - # Start the Alexia thread - if not self.alexia_running: - self.alexia_running = True - self.alexia.start() + # Start the Alexa thread + if not self.alexa_running: + self.alexa_running = True + self.alexa.start() # Start the syncing thread if not self.library_running: self.library_running = True @@ -333,7 +333,7 @@ class Service(): except: log.warn('Websocket client already shut down') try: - self.alexia.stopThread() + self.alexa.stopThread() except: log.warn('Websocket client already shut down') try: From 501a7ddbd99e2fe026b4fa7fcef1e448ea54fcd8 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 15:59:55 +0100 Subject: [PATCH 44/55] New setting for Alexa opting out --- resources/language/English/strings.xml | 3 +++ resources/language/German/strings.xml | 3 +++ resources/settings.xml | 14 +++++++------- service.py | 3 ++- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/resources/language/English/strings.xml b/resources/language/English/strings.xml index 72a653b9..ce15f7fd 100644 --- a/resources/language/English/strings.xml +++ b/resources/language/English/strings.xml @@ -513,4 +513,7 @@ Could not stop the database from running. Please try again later. Remove all cached artwork? (recommended!) Reset all PlexKodiConnect Addon settings? (this is usually NOT recommended and unnecessary!) + + Amazon Alexa (Voice Recognition) + Alexa aktivieren diff --git a/resources/language/German/strings.xml b/resources/language/German/strings.xml index 499ca4f1..3c5a5515 100644 --- a/resources/language/German/strings.xml +++ b/resources/language/German/strings.xml @@ -513,4 +513,7 @@ Kodi Datenbank konnte nicht gestoppt werden. Bitte später erneut versuchen. Alle zwischengespeicherten Bilder löschen? (empfohlen!) Alle PlexKodiConnect Einstellungen zurücksetzen? (normalerweise NICHT empfohlen und nicht nötig!) + + Amazon Alexa (Spracherkennung) + Alexa aktivieren \ No newline at end of file diff --git a/resources/settings.xml b/resources/settings.xml index b2e39d42..fd50830b 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -21,13 +21,9 @@ - - - - @@ -37,9 +33,13 @@ - - - + + + + + + + diff --git a/service.py b/service.py index 6b23706f..696a87b9 100644 --- a/service.py +++ b/service.py @@ -204,7 +204,8 @@ class Service(): self.ws_running = True self.ws.start() # Start the Alexa thread - if not self.alexa_running: + if (not self.alexa_running and + settings('enable_alexa') == 'true'): self.alexa_running = True self.alexa.start() # Start the syncing thread From 4d555a931425e3d17d41a6c913af804af920149f Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 16:30:39 +0100 Subject: [PATCH 45/55] Fix possible IndexError --- resources/lib/playutils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/lib/playutils.py b/resources/lib/playutils.py index af6d6b8e..7c4e1af4 100644 --- a/resources/lib/playutils.py +++ b/resources/lib/playutils.py @@ -9,6 +9,7 @@ import xbmcgui import xbmcvfs from utils import window, settings, tryEncode, language as lang +import variables as v import PlexAPI @@ -160,11 +161,11 @@ class PlayUtils(): - video bitrate above specified settings bitrate if the corresponding file settings are set to 'true' """ - videoCodec = self.API.getVideoCodec() - log.info("videoCodec: %s" % videoCodec) - if self.API.getType() in ('clip', 'track'): + if self.API.getType() in (v.PLEX_TYPE_CLIP, v.PLEX_TYPE_SONG): log.info('Plex clip or music track, not transcoding') return False + videoCodec = self.API.getVideoCodec() + log.info("videoCodec: %s" % videoCodec) if window('plex_forcetranscode') == 'true': log.info('User chose to force-transcode') return True From 768a25d1a30972f488d576e4a4abc7f4f7ce0308 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 16:38:06 +0100 Subject: [PATCH 46/55] Simplify code --- resources/lib/companion.py | 6 +----- resources/lib/plexbmchelper/listener.py | 7 ------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/resources/lib/companion.py b/resources/lib/companion.py index 5791faf1..9a01eaa7 100644 --- a/resources/lib/companion.py +++ b/resources/lib/companion.py @@ -113,11 +113,7 @@ def process_command(request_path, params, queue=None): 'action': 'playlist', 'data': params }) - return { - 'lastkey': params['key'], - 'containerKey': containerKey, - 'playQueueID': playQueueID, - } + return elif request_path == "player/playback/setParameters": if 'volume' in params: diff --git a/resources/lib/plexbmchelper/listener.py b/resources/lib/plexbmchelper/listener.py index f240ea1c..159dcb4e 100644 --- a/resources/lib/plexbmchelper/listener.py +++ b/resources/lib/plexbmchelper/listener.py @@ -165,13 +165,6 @@ class MyHandler(BaseHTTPRequestHandler): answ = process_command(request_path, params, self.server.queue) self.response(getOKMsg(), js.getPlexHeaders()) subMgr.notify() - if answ is not None: - subMgr.lastkey = answ['lastkey'] - subMgr.containerKey = answ['containerKey'] - subMgr.playQueueID = answ['playQueueID'] - subMgr.protocol, subMgr.server, subMgr.port = \ - window('pms_server').split(':', 2) - subMgr.server = subMgr.server.replace('/', '') except: log.error('Error encountered. Traceback:') import traceback From 799356c1fbd5d30edcc09182f9209541757fae29 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 16:38:29 +0100 Subject: [PATCH 47/55] Simplify code --- resources/lib/plexbmchelper/listener.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/plexbmchelper/listener.py b/resources/lib/plexbmchelper/listener.py index 159dcb4e..a3294705 100644 --- a/resources/lib/plexbmchelper/listener.py +++ b/resources/lib/plexbmchelper/listener.py @@ -162,7 +162,7 @@ class MyHandler(BaseHTTPRequestHandler): subMgr.removeSubscriber(uuid) else: # Throw it to companion.py - answ = process_command(request_path, params, self.server.queue) + process_command(request_path, params, self.server.queue) self.response(getOKMsg(), js.getPlexHeaders()) subMgr.notify() except: From d2c52c105014e5ddd0a0b81891b6bba55c512f00 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 16:41:56 +0100 Subject: [PATCH 48/55] Simplify code --- resources/lib/companion.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/resources/lib/companion.py b/resources/lib/companion.py index 9a01eaa7..46878cf3 100644 --- a/resources/lib/companion.py +++ b/resources/lib/companion.py @@ -100,20 +100,11 @@ def process_command(request_path, params, queue=None): convert_alexa_to_companion(params) log.debug('Received request_path: %s, params: %s' % (request_path, params)) if "/playMedia" in request_path: - try: - containerKey = urlparse(params.get('containerKey')).path - except: - containerKey = '' - try: - playQueueID = REGEX_PLAYQUEUES.findall(containerKey)[0] - except IndexError: - playQueueID = '' # We need to tell service.py queue.put({ 'action': 'playlist', 'data': params }) - return elif request_path == "player/playback/setParameters": if 'volume' in params: From 260579f32d45899710befe0fe49f7001e486dd1d Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 16:43:06 +0100 Subject: [PATCH 49/55] Increase Plex Companion sleep --- resources/lib/PlexCompanion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/PlexCompanion.py b/resources/lib/PlexCompanion.py index 7712f31c..1a987756 100644 --- a/resources/lib/PlexCompanion.py +++ b/resources/lib/PlexCompanion.py @@ -223,7 +223,7 @@ class PlexCompanion(Thread): queue.task_done() # Don't sleep continue - sleep(20) + sleep(50) client.stop_all() if httpd: From 3d525f7772f7ecadc592fcb53fdb76bfdb5abcd2 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 16:51:13 +0100 Subject: [PATCH 50/55] Treat unsuccessful download better --- resources/lib/PlexCompanion.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/resources/lib/PlexCompanion.py b/resources/lib/PlexCompanion.py index 1a987756..169db6c3 100644 --- a/resources/lib/PlexCompanion.py +++ b/resources/lib/PlexCompanion.py @@ -14,8 +14,7 @@ from PlexFunctions import ParseContainerKey, GetPlexMetadata from PlexAPI import API import player from entrypoint import Plex_Node -from variables import KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE, \ - PLEX_TO_KODI_TIMEFACTOR +import variables as v ############################################################################### @@ -110,14 +109,19 @@ class PlexCompanion(Thread): return try: playqueue = self.mgr.playqueue.get_playqueue_from_type( - KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[data['type']]) + v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[data['type']]) except KeyError: # E.g. Plex web does not supply the media type # Still need to figure out the type (video vs. music vs. pix) xml = GetPlexMetadata(data['key']) + try: + xml[0].attrib + except (AttributeError, IndexError, TypeError): + log.error('Could not download Plex metadata') + return api = API(xml[0]) playqueue = self.mgr.playqueue.get_playqueue_from_type( - KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()]) + v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()]) self.mgr.playqueue.update_playqueue_from_PMS( playqueue, ID, From f1dc7639ab8dbeb997f0abab6793a2f0662e2083 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 17:51:58 +0100 Subject: [PATCH 51/55] Enable Alexa for Plex music --- resources/lib/PlexCompanion.py | 35 +++++++++++++++++++++++----------- resources/lib/companion.py | 3 ++- resources/lib/playlist_func.py | 5 +++++ resources/lib/playqueue.py | 29 +++++++++++++++++++++++++++- 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/resources/lib/PlexCompanion.py b/resources/lib/PlexCompanion.py index 169db6c3..fd79d8bf 100644 --- a/resources/lib/PlexCompanion.py +++ b/resources/lib/PlexCompanion.py @@ -79,7 +79,29 @@ class PlexCompanion(Thread): log.debug('Processing: %s' % task) data = task['data'] - if (task['action'] == 'playlist' and + if task['action'] == 'alexa': + # e.g. Alexa + xml = GetPlexMetadata(data['key']) + try: + xml[0].attrib + except (AttributeError, IndexError, TypeError): + log.error('Could not download Plex metadata') + return + api = API(xml[0]) + if api.getType() == v.PLEX_TYPE_ALBUM: + log.debug('Plex music album detected') + self.mgr.playqueue.init_playqueue_from_plex_children( + api.getRatingKey()) + else: + thread = Thread(target=Plex_Node, + args=('{server}%s' % data.get('key'), + data.get('offset'), + True, + False),) + thread.setDaemon(True) + thread.start() + + elif (task['action'] == 'playlist' and data.get('address') == 'node.plexapp.com'): # E.g. watch later initiated by Companion thread = Thread(target=Plex_Node, @@ -88,6 +110,7 @@ class PlexCompanion(Thread): True),) thread.setDaemon(True) thread.start() + elif task['action'] == 'playlist': # Get the playqueue ID try: @@ -97,16 +120,6 @@ class PlexCompanion(Thread): import traceback log.error("Traceback:\n%s" % traceback.format_exc()) return - if typus == 'library/metadata': - # e.g. Alexa - thread = Thread(target=Plex_Node, - args=('{server}%s' % data.get('key'), - data.get('offset'), - True, - False),) - thread.setDaemon(True) - thread.start() - return try: playqueue = self.mgr.playqueue.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[data['type']]) diff --git a/resources/lib/companion.py b/resources/lib/companion.py index 46878cf3..2a0007c3 100644 --- a/resources/lib/companion.py +++ b/resources/lib/companion.py @@ -101,8 +101,9 @@ def process_command(request_path, params, queue=None): log.debug('Received request_path: %s, params: %s' % (request_path, params)) if "/playMedia" in request_path: # We need to tell service.py + action = 'alexa' if params.get('deviceName') == 'Alexa' else 'playlist' queue.put({ - 'action': 'playlist', + 'action': action, 'data': params }) diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index dc0268c7..5ab80b6c 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -118,6 +118,7 @@ def playlist_item_from_kodi(kodi_item): # TO BE VERIFIED - PLEX DOESN'T LIKE PLAYLIST ADDS IN THIS MANNER item.uri = ('library://%s/item/library%%2Fmetadata%%2F%s' % (item.plex_UUID, item.plex_id)) + log.debug('Made playlist item from Kodi: %s' % item) return item @@ -136,6 +137,10 @@ def playlist_item_from_plex(plex_id): item.kodi_type = plex_dbitem[4] except: raise KeyError('Could not find plex_id %s in database' % plex_id) + item.plex_UUID = plex_id + item.uri = ('library://%s/item/library%%2Fmetadata%%2F%s' % + (item.plex_UUID, plex_id)) + log.debug('Made playlist item from plex: %s' % item) return item diff --git a/resources/lib/playqueue.py b/resources/lib/playqueue.py index 0e8939f1..c5a513a8 100644 --- a/resources/lib/playqueue.py +++ b/resources/lib/playqueue.py @@ -7,8 +7,10 @@ from xbmc import sleep, Player, PlayList, PLAYLIST_MUSIC, PLAYLIST_VIDEO from utils import window, ThreadMethods, ThreadMethodsAdditionalSuspend import playlist_func as PL -from PlexFunctions import ConvertPlexToKodiTime +from PlexFunctions import ConvertPlexToKodiTime, GetAllPlexChildren +from PlexAPI import API from playbackutils import PlaybackUtils +import variables as v ############################################################################### log = logging.getLogger("PLEX."+__name__) @@ -69,6 +71,31 @@ class Playqueue(Thread): raise ValueError('Wrong playlist type passed in: %s' % typus) return playqueue + def init_playqueue_from_plex_children(self, plex_id): + """ + Init a new playqueue e.g. from an album. Alexa does this + """ + xml = GetAllPlexChildren(plex_id) + try: + xml[0].attrib + except (TypeError, IndexError, AttributeError): + log.error('Could not download the PMS xml for %s' % plex_id) + return + playqueue = self.get_playqueue_from_type( + v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[xml[0].attrib['type']]) + playqueue.clear() + for i, child in enumerate(xml): + api = API(child) + PL.add_item_to_playlist(playqueue, i, plex_id=api.getRatingKey()) + log.debug('Firing up Kodi player') + thread = Thread(target=Player().play, + args=(playqueue.kodi_pl, + None, + False, + 0)) # starting position + thread.setDaemon(True) + thread.start() + def update_playqueue_from_PMS(self, playqueue, playqueue_id=None, From 8d2b312fcfcbe46df5af29da387b72ea42f13558 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 17:59:04 +0100 Subject: [PATCH 52/55] Remove obsolete import --- resources/lib/companion.py | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/lib/companion.py b/resources/lib/companion.py index 2a0007c3..bec94e1e 100644 --- a/resources/lib/companion.py +++ b/resources/lib/companion.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- import logging -from urlparse import urlparse from re import compile as re_compile from utils import JSONRPC From a8b7bf7871caf0231a960d0a3744f8af23df044d Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 17:59:56 +0100 Subject: [PATCH 53/55] Fix starting more playqueue instances --- resources/lib/playqueue.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/lib/playqueue.py b/resources/lib/playqueue.py index c5a513a8..49262bcb 100644 --- a/resources/lib/playqueue.py +++ b/resources/lib/playqueue.py @@ -33,6 +33,8 @@ class Playqueue(Thread): def __init__(self, callback=None): self.__dict__ = self.__shared_state if self.playqueues is not None: + log.debug('Playqueue thread has already been initialized') + Thread.__init__(self) return self.mgr = callback From 38d5d64f2c16b913d1b59c8459e4865a02a6a9e4 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 18:28:30 +0100 Subject: [PATCH 54/55] Enable skipping for Plex Companion --- resources/lib/companion.py | 39 ++++++++++++++++++++++---------------- resources/lib/playqueue.py | 8 +------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/resources/lib/companion.py b/resources/lib/companion.py index bec94e1e..652e5c48 100644 --- a/resources/lib/companion.py +++ b/resources/lib/companion.py @@ -2,9 +2,12 @@ import logging from re import compile as re_compile +from xbmc import Player + from utils import JSONRPC -import plexdb_functions as plexdb from variables import ALEXA_TO_COMPANION +from playqueue import Playqueue +from PlexFunctions import GetPlexKeyNumber ############################################################################### @@ -17,7 +20,6 @@ REGEX_PLAYQUEUES = re_compile(r'''/playQueues/(\d+)$''') def getPlayers(): info = JSONRPC("Player.GetActivePlayers").execute()['result'] or [] - log.debug('players: %s' % JSONRPC("Player.GetActivePlayers").execute()) ret = {} for player in info: player['playerid'] = int(player['playerid']) @@ -69,19 +71,24 @@ def millisToTime(t): 'milliseconds': millis} -def skipTo(self, plexId, typus): - # playlistId = self.getPlaylistId(tryDecode(xbmc_type(typus))) - # playerId = self. - with plexdb.Get_Plex_DB() as plex_db: - plexdb_item = plex_db.getItem_byId(plexId) - try: - dbid = plexdb_item[0] - mediatype = plexdb_item[4] - except TypeError: - log.info('Couldnt find item %s in Kodi db' % plexId) - return - log.debug('plexid: %s, kodi id: %s, type: %s' - % (plexId, dbid, mediatype)) +def skipTo(params): + # Does not seem to be implemented yet + playQueueItemID = params.get('playQueueItemID', 'not available') + library, plex_id = GetPlexKeyNumber(params.get('key')) + log.debug('Skipping to playQueueItemID %s, plex_id %s' + % (playQueueItemID, plex_id)) + found = True + playqueues = Playqueue() + for (player, ID) in getPlayers().iteritems(): + playqueue = playqueues.get_playqueue_from_type(player) + for i, item in enumerate(playqueue.items): + if item.ID == playQueueItemID or item.plex_id == plex_id: + break + else: + log.debug('Item not found to skip to') + found = False + if found: + Player().play(playqueue.kodi_pl, None, False, i) def convert_alexa_to_companion(dictionary): @@ -153,7 +160,7 @@ def process_command(request_path, params, queue=None): "to": "previous"}) elif request_path == "player/playback/skipTo": - skipTo(params.get('key').rsplit('/', 1)[1], params.get('type')) + skipTo(params) elif request_path == "player/navigation/moveUp": JSONRPC("Input.Up").execute() diff --git a/resources/lib/playqueue.py b/resources/lib/playqueue.py index 49262bcb..7adfca1a 100644 --- a/resources/lib/playqueue.py +++ b/resources/lib/playqueue.py @@ -90,13 +90,7 @@ class Playqueue(Thread): api = API(child) PL.add_item_to_playlist(playqueue, i, plex_id=api.getRatingKey()) log.debug('Firing up Kodi player') - thread = Thread(target=Player().play, - args=(playqueue.kodi_pl, - None, - False, - 0)) # starting position - thread.setDaemon(True) - thread.start() + Player().play(playqueue.kodi_pl, None, False, 0) def update_playqueue_from_PMS(self, playqueue, From 0e3964172c1685b5b2532bada2181968dcfc09b9 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sun, 5 Mar 2017 18:31:00 +0100 Subject: [PATCH 55/55] Version bump --- addon.xml | 2 +- changelog.txt | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/addon.xml b/addon.xml index 678e5bcc..7c699048 100644 --- a/addon.xml +++ b/addon.xml @@ -1,7 +1,7 @@ diff --git a/changelog.txt b/changelog.txt index 955b0a9a..f9eb03a3 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,8 @@ +version 1.6.4 (beta only) +- Amazon Alexa support! Be mindful to check the Alexa forum thread first; there are still many issues completely unrelated to PKC +- Enable skipping for Plex Companion +- Set default companion name to PlexKodiConnect + version 1.6.3 - Fix UnicodeEncodeError for non ASCII filenames in playback_starter - Cleanup playlist/playqueue string/unicode