diff --git a/resources/language/English/strings.xml b/resources/language/English/strings.xml
index f7a65d32..02c28b05 100644
--- a/resources/language/English/strings.xml
+++ b/resources/language/English/strings.xml
@@ -145,14 +145,14 @@
Recently Added TV Shows
- On Deck TV Shows
+ In Progress TV Shows
All Music
Channels
Recently Added Movies
Recently Added Episodes
Recently Added Albums
- On Deck Movies
- On Deck Episodes
+ In Progress Movies
+ In Progress Episodes
Next Episodes
Favorite Movies
Favorite Shows
@@ -277,7 +277,7 @@
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
+ Maximum items to request from the server at once (restart!)
Playback
[COLOR yellow]Enter network credentials[/COLOR]
Enable Plex Trailers (Plexpass is needed)
@@ -379,6 +379,7 @@
Reseting PMS connections, please wait
Failed to reset PMS and plex.tv connects. Try to restart Kodi.
[COLOR yellow]Log-in to plex.tv[/COLOR]
+ Not yet connected to Plex Server
@@ -408,6 +409,6 @@
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. Please post your Kodi logs to the Plex forum.
+ 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.
diff --git a/resources/language/German/strings.xml b/resources/language/German/strings.xml
index 5025cf0c..65446c10 100644
--- a/resources/language/German/strings.xml
+++ b/resources/language/German/strings.xml
@@ -173,14 +173,14 @@
Addresse :
Zuletzt hinzugefügte Serien
- Aktuell Serien
+ Begonnene Serien
Alles an Musik
Kanäle
Zuletzt hinzugefügte Filme
Zuletzt hinzugefügte Episoden
Zuletzt hinzugefügte Alben
- Aktuelle Filme
- Aktuelle Episoden
+ Begonnene Filme
+ Begonnene Episoden
Nächste Episoden
Favorisierte Filme
Favorisierte Serien
@@ -252,6 +252,8 @@
Unterdrücke Server-Verbindungsmeldungen beim Starten
Benutze lokale Pfade anstelle von Addon-Umleitungen beim Abspielen
+ Max. Anzahl gleichzeitig nachgefragter PMS Einträge (Neustart!)
+
Plex Media Server Authorisierung ist zu häufig fehlgeschlagen. In den Einstellungen können die Anzahl erfolgloser Versuche zurückgesetzt werden.
@@ -310,8 +312,7 @@
PMS Verbindungen werden zurückgesetzt
PMS und plex.tv Verbindungen konnten nicht zurückgesetzt werden. Bitte versuchen Sie, Kodi neu zu starten, um das Problem zu beheben.
[COLOR yellow]Bei plex.tv einloggen[/COLOR]
-
-
+ Noch nicht mit Plex Server verbunden
Alle Plex Bilder in Kodi zwischenzuspeichern kann sehr lange dauern. Möchten Sie wirklich fortfahren?
@@ -340,6 +341,6 @@
Plex Playlisten/Nodes aktualisiert
Plex Playlisten/Nodes Aktualisierung fehlgeschlagen
Plex Bibliotheken aktualisiert
- Einige Plex Einträge mussten übersprungen werden, da sie nicht verarbeitet werden konnten. Bitte teilen Sie Ihr Kodi log im Plex Forum.
+ Einige Plex Einträge mussten übersprungen werden, da sie nicht verarbeitet werden konnten. Kodi ist nun möglicherweise instabil!! Bitte teilen Sie Ihr Kodi log im Plex Forum.
diff --git a/resources/lib/PlexAPI.py b/resources/lib/PlexAPI.py
index 71a5bc67..11734fb2 100644
--- a/resources/lib/PlexAPI.py
+++ b/resources/lib/PlexAPI.py
@@ -1056,8 +1056,6 @@ class PlexAPI():
self.logMsg("No URL for user avatar.", 1)
return False
for user in users:
- self.logMsg('type user: %s, type username: %s'
- % (type(user['title']), type(username)))
if username in user['title']:
url = user['thumb']
self.logMsg("Avatar url for user %s is: %s" % (username, url), 1)
@@ -2092,7 +2090,7 @@ class API():
allartworks['Backdrop'].append(background)
if not allartworks['Primary']:
- primary = item['parentThumb']
+ primary = item.get('parentThumb')
if primary:
primary = "%s%s" % (self.server, primary)
primary = self.addPlexCredentialsToUrl(primary)
diff --git a/resources/lib/PlexCompanion.py b/resources/lib/PlexCompanion.py
index bf46a9a1..93275547 100644
--- a/resources/lib/PlexCompanion.py
+++ b/resources/lib/PlexCompanion.py
@@ -84,12 +84,12 @@ class PlexCompanion(threading.Thread):
self.logMsg("Client is still registered", 1)
else:
self.logMsg("Client is no longer registered", 1)
- self.logMsg("PlexBMC Helper still running on port %s"
+ self.logMsg("Plex Companion still running on port %s"
% self.port, 1)
message_count = 0
if not is_running:
- self.logMsg("PleXBMC Helper has started", 0)
+ self.logMsg("Plex Companion has started", 0)
is_running = True
subscribers.subMgr.notify()
@@ -106,4 +106,4 @@ class PlexCompanion(threading.Thread):
finally:
httpd.socket.close()
requests.dumpConnections()
- self.logMsg("----===## STOP PlexBMC Helper ##===----", 0)
+ self.logMsg("----===## STOP Plex Companion ##===----", 0)
diff --git a/resources/lib/PlexFunctions.py b/resources/lib/PlexFunctions.py
index e8c4ace3..fb96485c 100644
--- a/resources/lib/PlexFunctions.py
+++ b/resources/lib/PlexFunctions.py
@@ -3,7 +3,7 @@ from urllib import urlencode
from ast import literal_eval
from urlparse import urlparse, parse_qs
import re
-import time
+from copy import deepcopy
from xbmcaddon import Addon
@@ -185,7 +185,7 @@ def GetPlexMetadata(key):
return xml
-def GetAllPlexChildren(key):
+def GetAllPlexChildren(key, containerSize=None):
"""
Returns a list (raw xml API dump) of all Plex children for the key.
(e.g. /library/metadata/194853/children pointing to a season)
@@ -193,18 +193,11 @@ def GetAllPlexChildren(key):
Input:
key Key to a Plex item, e.g. 12345
"""
- xml = downloadutils.DownloadUtils().downloadUrl(
- "{server}/library/metadata/%s/children" % key)
- try:
- xml.attrib
- except AttributeError:
- logMsg(
- title, "Error retrieving all children for Plex item %s" % key, -1)
- xml = None
- return xml
+ url = "{server}/library/metadata/%s/children?" % key
+ return DownloadChunks(url, containerSize)
-def GetPlexSectionResults(viewId, args=None):
+def GetPlexSectionResults(viewId, args=None, containerSize=None):
"""
Returns a list (XML API dump) of all Plex items in the Plex
section with key = viewId.
@@ -214,26 +207,76 @@ def GetPlexSectionResults(viewId, args=None):
Returns None if something went wrong
"""
- result = []
- url = "{server}/library/sections/%s/all" % viewId
+ url = "{server}/library/sections/%s/all?" % viewId
if args:
- url += "?" + urlencode(args)
-
- result = downloadutils.DownloadUtils().downloadUrl(url)
-
- try:
- result.tag
- # Nope, not an XML, abort
- except AttributeError:
- logMsg(title,
- "Error retrieving all items for Plex section %s"
- % viewId, -1)
- result = None
-
- return result
+ url += urlencode(args) + '&'
+ return DownloadChunks(url, containerSize)
-def GetAllPlexLeaves(viewId, lastViewedAt=None, updatedAt=None):
+def DownloadChunks(url, containerSize):
+ """
+ Downloads PMS url in chunks of containerSize (int).
+ If containerSize is None: ONE xml is fetched directly
+
+ url MUST end with '?' (if no other url encoded args are present) or '&'
+
+ Returns a stitched-together xml or None.
+ """
+ if containerSize is None:
+ # Get rid of '?' or '&' at the end of url
+ xml = downloadutils.DownloadUtils().downloadUrl(url[:-1])
+ try:
+ xml.attrib
+ except AttributeError:
+ # Nope, not an XML, abort
+ logMsg(title, "Error getting url %s" % url[:-1], -1)
+ return None
+ else:
+ return xml
+
+ xml = None
+ pos = 0
+ errorCounter = 0
+ while errorCounter < 10:
+ args = {
+ 'X-Plex-Container-Size': containerSize,
+ 'X-Plex-Container-Start': pos
+ }
+ xmlpart = downloadutils.DownloadUtils().downloadUrl(
+ url + urlencode(args))
+ # If something went wrong - skip in the hope that it works next time
+ try:
+ xmlpart.attrib
+ except AttributeError:
+ logMsg(title, 'Error while downloading chunks: %s'
+ % (url + urlencode(args)), -1)
+ pos += containerSize
+ errorCounter += 1
+ continue
+
+ # Very first run: starting xml (to retain data in xml's root!)
+ if xml is None:
+ xml = deepcopy(xmlpart)
+ if len(xmlpart) < containerSize:
+ break
+ else:
+ pos += containerSize
+ continue
+ # Build answer xml - containing the entire library
+ for child in xmlpart:
+ xml.append(child)
+ # Done as soon as we don't receive a full complement of items
+ if len(xmlpart) < containerSize:
+ break
+ pos += containerSize
+ if errorCounter == 10:
+ logMsg(title, 'Fatal error while downloading chunks for %s' % url, -1)
+ return None
+ return xml
+
+
+def GetAllPlexLeaves(viewId, lastViewedAt=None, updatedAt=None,
+ containerSize=None):
"""
Returns a list (raw XML API dump) of all Plex subitems for the key.
(e.g. /library/sections/2/allLeaves pointing to all TV shows)
@@ -244,6 +287,7 @@ def GetAllPlexLeaves(viewId, lastViewedAt=None, updatedAt=None):
since that point of time until now.
updatedAt Unix timestamp; only retrieves PMS items updated
by the PMS since that point of time until now.
+ containerSize Number of items simultaneously fetched from PMS
If lastViewedAt and updatedAt=None, ALL PMS items are returned.
@@ -260,19 +304,11 @@ def GetAllPlexLeaves(viewId, lastViewedAt=None, updatedAt=None):
if updatedAt:
args.append('updatedAt>=%s' % updatedAt)
if args:
- url += '?' + '&'.join(args)
+ url += '?' + '&'.join(args) + '&'
+ else:
+ url += '?'
- xml = downloadutils.DownloadUtils().downloadUrl(url)
-
- try:
- xml.attrib
- # Nope, not an XML, abort
- except AttributeError:
- logMsg(title,
- "Error retrieving all leaves for Plex section %s"
- % viewId, -1)
- xml = None
- return xml
+ return DownloadChunks(url, containerSize)
def GetPlexCollections(mediatype):
diff --git a/resources/lib/artwork.py b/resources/lib/artwork.py
index 75988e63..68adf648 100644
--- a/resources/lib/artwork.py
+++ b/resources/lib/artwork.py
@@ -421,7 +421,7 @@ class Artwork():
cursor.execute(query, (imageUrl, kodiId, mediaType, imageType))
# Cache fanart and poster in Kodi texture cache
- if cacheimage and imageType in ("fanart", "poster"):
+ if cacheimage and imageType in ("fanart", "poster", "thumb"):
self.CacheTexture(imageUrl)
def deleteArtwork(self, kodiid, mediatype, cursor):
diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py
index bf44f303..c927dd79 100644
--- a/resources/lib/entrypoint.py
+++ b/resources/lib/entrypoint.py
@@ -159,6 +159,18 @@ def doPlayback(itemid, dbid):
"""
Called only for a SINGLE element, not playQueues
"""
+ if utils.window('plex_authenticated') != "true":
+ utils.logMsg('doPlayback', 'Not yet authenticated for a PMS, abort '
+ 'starting playback', -1)
+ string = xbmcaddon.Addon().getLocalizedString
+ # Not yet connected to a PMS server
+ xbmcgui.Dialog().notification(
+ heading=addonName,
+ message=string(39210),
+ icon=xbmcgui.NOTIFICATION_ERROR,
+ time=7000,
+ sound=True)
+ return False
item = PlexFunctions.GetPlexMetadata(itemid)
if item is None:
diff --git a/resources/lib/itemtypes.py b/resources/lib/itemtypes.py
index 77ce3015..e75913b8 100644
--- a/resources/lib/itemtypes.py
+++ b/resources/lib/itemtypes.py
@@ -43,6 +43,8 @@ class Items(object):
self.artwork = artwork.Artwork()
self.emby = embyserver.Read_EmbyServer()
+ self.userid = utils.window('currUserId')
+ self.server = utils.window('pms_server')
def __enter__(self):
"""
@@ -1445,8 +1447,21 @@ class TVShows(Items):
people = API.getPeopleList()
kodi_db.addPeople(episodeid, people, "episode")
# Process artwork
- allartworks = API.getAllArtwork()
- artwork.addArtwork(allartworks, episodeid, "episode", kodicursor)
+ # Wide "screenshot" of particular episode
+ poster = item.attrib.get('thumb')
+ if poster:
+ poster = API.addPlexCredentialsToUrl(
+ "%s%s" % (self.server, poster))
+ artwork.addOrUpdateArt(
+ poster, episodeid, "episode", "thumb", kodicursor)
+ # poster of TV show itself
+ # poster = item.attrib.get('grandparentThumb')
+ # if poster:
+ # poster = API.addPlexCredentialsToUrl(
+ # "%s%s" % (self.server, poster))
+ # artwork.addOrUpdateArt(
+ # poster, episodeid, "episode", "poster", kodicursor)
+
# Process stream details
streams = API.getMediaStreams()
kodi_db.addStreams(fileid, streams, runtime)
@@ -1609,8 +1624,6 @@ class Music(Items):
self.enableimportsongrating = utils.settings('enableImportSongRating') == "true"
self.enableexportsongrating = utils.settings('enableExportSongRating') == "true"
self.enableupdatesongrating = utils.settings('enableUpdateSongRating') == "true"
- self.userid = utils.window('currUserId')
- self.server = utils.window('pms_server')
def __enter__(self):
"""
diff --git a/resources/lib/librarysync.py b/resources/lib/librarysync.py
index 5e030133..e1c3b1e1 100644
--- a/resources/lib/librarysync.py
+++ b/resources/lib/librarysync.py
@@ -237,6 +237,7 @@ class LibrarySync(Thread):
else False
self.enableBackgroundSync = True if utils.settings(
'enableBackgroundSync') == "true" else False
+ self.limitindex = int(utils.settings('limitindex'))
# Time offset between Kodi and PMS in seconds (=Koditime - PMStime)
self.timeoffset = 0
@@ -340,7 +341,8 @@ class LibrarySync(Thread):
xbmc.sleep(2000)
# Get all PMS items to find the item we changed
items = PlexFunctions.GetAllPlexLeaves(libraryId,
- lastViewedAt=timestamp)
+ lastViewedAt=timestamp,
+ containerSize=self.limitindex)
# Toggle watched state back
PlexFunctions.scrobble(plexId, 'unwatched')
# Get server timestamp for this change
@@ -482,7 +484,9 @@ class LibrarySync(Thread):
self.updatelist = []
# Get items per view
items = PlexFunctions.GetAllPlexLeaves(
- view['id'], updatedAt=self.getPMSfromKodiTime(lastSync))
+ view['id'],
+ updatedAt=self.getPMSfromKodiTime(lastSync),
+ containerSize=self.limitindex)
# Just skip if something went wrong
if not items:
continue
@@ -516,7 +520,9 @@ class LibrarySync(Thread):
songupdate = False
for view in self.views:
items = PlexFunctions.GetAllPlexLeaves(
- view['id'], lastViewedAt=self.getPMSfromKodiTime(lastSync))
+ view['id'],
+ lastViewedAt=self.getPMSfromKodiTime(lastSync),
+ containerSize=self.limitindex)
for item in items:
itemId = item.attrib.get('ratingKey')
# Skipping items 'title=All episodes' without a 'ratingKey'
@@ -1070,7 +1076,8 @@ class LibrarySync(Thread):
# Get items per view
viewId = view['id']
viewName = view['name']
- all_plexmovies = PlexFunctions.GetPlexSectionResults(viewId)
+ all_plexmovies = PlexFunctions.GetPlexSectionResults(
+ viewId, args=None, containerSize=self.limitindex)
if not all_plexmovies:
self.logMsg("Couldnt get section items, aborting for view.", 1)
continue
@@ -1107,7 +1114,8 @@ class LibrarySync(Thread):
"""
xml = PlexFunctions.GetAllPlexLeaves(viewId,
lastViewedAt=lastViewedAt,
- updatedAt=updatedAt)
+ updatedAt=updatedAt,
+ containerSize=self.limitindex)
# Return if there are no items in PMS reply - it's faster
try:
xml[0].attrib
@@ -1202,7 +1210,8 @@ class LibrarySync(Thread):
# Get items per view
viewId = view['id']
viewName = view['name']
- allPlexTvShows = PlexFunctions.GetPlexSectionResults(viewId)
+ allPlexTvShows = PlexFunctions.GetPlexSectionResults(
+ viewId, containerSize=self.limitindex)
if not allPlexTvShows:
self.logMsg(
"Error downloading show view xml for view %s" % viewId, -1)
@@ -1228,7 +1237,8 @@ class LibrarySync(Thread):
if self.threadStopped():
return False
# Grab all seasons to tvshow from PMS
- seasons = PlexFunctions.GetAllPlexChildren(tvShowId)
+ seasons = PlexFunctions.GetAllPlexChildren(
+ tvShowId, containerSize=self.limitindex)
if not seasons:
self.logMsg(
"Error downloading season xml for show %s" % tvShowId, -1)
@@ -1252,7 +1262,8 @@ class LibrarySync(Thread):
if self.threadStopped():
return False
# Grab all episodes to tvshow from PMS
- episodes = PlexFunctions.GetAllPlexLeaves(view['id'])
+ episodes = PlexFunctions.GetAllPlexLeaves(
+ view['id'], containerSize=self.limitindex)
if not episodes:
self.logMsg(
"Error downloading episod xml for view %s"
@@ -1265,7 +1276,7 @@ class LibrarySync(Thread):
None,
None)
self.logMsg("Analyzed all episodes of TV show with Plex Id %s"
- % tvShowId, 1)
+ % view['id'], 1)
# Process self.updatelist
self.GetAndProcessXMLs(itemType)
@@ -1350,7 +1361,7 @@ class LibrarySync(Thread):
viewId = view['id']
viewName = view['name']
itemsXML = PlexFunctions.GetPlexSectionResults(
- viewId, args=urlArgs)
+ viewId, args=urlArgs, containerSize=self.limitindex)
if not itemsXML:
self.logMsg("Error downloading xml for view %s"
% viewId, -1)
diff --git a/resources/lib/plexbmchelper/subscribers.py b/resources/lib/plexbmchelper/subscribers.py
index c5e63ab2..b11d762c 100644
--- a/resources/lib/plexbmchelper/subscribers.py
+++ b/resources/lib/plexbmchelper/subscribers.py
@@ -155,7 +155,7 @@ class SubscriptionManager:
serv = getServerByHost(self.server)
url = serv.get('protocol', 'http') + '://' \
+ serv.get('server', 'localhost') + ':' \
- + serv.get('port', 32400) + "/:/timeline"
+ + serv.get('port', '32400') + "/:/timeline"
self.download.downloadUrl(url, type="GET", parameters=params)
# requests.getwithparams(serv.get('server', 'localhost'), serv.get('port', 32400), "/:/timeline", params, getPlexHeaders(), serv.get('protocol', 'http'))
printDebug("params: %s" % params)
diff --git a/resources/lib/userclient.py b/resources/lib/userclient.py
index 72084b36..1f2579b6 100644
--- a/resources/lib/userclient.py
+++ b/resources/lib/userclient.py
@@ -179,9 +179,6 @@ class UserClient(threading.Thread):
if authenticated is False:
self.logMsg('Testing validity of current token', 0)
- window('currUserId', value=userId)
- window('plex_username', value=username)
- window('pms_token', value=self.currToken)
res = PlexAPI.PlexAPI().CheckConnection(
self.currServer, self.currToken)
if res is False:
@@ -205,6 +202,7 @@ class UserClient(threading.Thread):
window('pms_server', value=self.currServer)
window('plex_machineIdentifier', value=self.machineIdentifier)
window('plex_servername', value=self.servername)
+ window('plex_authenticated', value='true')
# Set DownloadUtils values
doUtils.setUsername(username)
@@ -331,6 +329,7 @@ class UserClient(threading.Thread):
settings = utils.settings
window = utils.window
+ window('plex_authenticated', clear=True)
window('pms_token', clear=True)
window('plex_token', clear=True)
window('pms_server', clear=True)
@@ -410,7 +409,7 @@ class UserClient(threading.Thread):
self.auth = True
# Minimize CPU load
- xbmc.sleep(500)
+ xbmc.sleep(100)
self.doUtils.stopSession()
log("##===---- UserClient Stopped ----===##", 0)
diff --git a/resources/settings.xml b/resources/settings.xml
index 28ac97b1..5c2ac6f8 100644
--- a/resources/settings.xml
+++ b/resources/settings.xml
@@ -49,7 +49,7 @@
-
+
diff --git a/service.py b/service.py
index 70518944..1f007bd2 100644
--- a/service.py
+++ b/service.py
@@ -78,7 +78,8 @@ class Service():
"emby_shouldStop", "currUserId", "emby_dbScan", "emby_sessionId",
"emby_initialScan", "emby_customplaylist", "emby_playbackProps",
"plex_runLibScan", "plex_username", "pms_token", "plex_token",
- "pms_server", "plex_machineIdentifier", "plex_servername"
+ "pms_server", "plex_machineIdentifier", "plex_servername",
+ "plex_authenticated"
]
for prop in properties:
window(prop, clear=True)