diff --git a/default.py b/default.py
index 467a1f2b..948d82eb 100644
--- a/default.py
+++ b/default.py
@@ -71,7 +71,8 @@ class Main:
'delete': entrypoint.deleteItem,
'browseplex': entrypoint.BrowsePlexContent,
'ondeck': entrypoint.getOnDeck,
- 'chooseServer': entrypoint.chooseServer
+ 'chooseServer': entrypoint.chooseServer,
+ 'watchlater': entrypoint.watchlater
}
if "/extrafanart" in sys.argv[0]:
diff --git a/resources/language/English/strings.xml b/resources/language/English/strings.xml
index ad91dfff..ba964ae4 100644
--- a/resources/language/English/strings.xml
+++ b/resources/language/English/strings.xml
@@ -414,6 +414,7 @@
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
+ Watch later
diff --git a/resources/language/German/strings.xml b/resources/language/German/strings.xml
index 7a2ae659..599716e5 100644
--- a/resources/language/German/strings.xml
+++ b/resources/language/German/strings.xml
@@ -354,6 +354,7 @@
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
+ Später ansehen
Alle Plex Bilder in Kodi zwischenzuspeichern kann sehr lange dauern. Möchten Sie wirklich fortfahren?
diff --git a/resources/lib/PlexAPI.py b/resources/lib/PlexAPI.py
index 23568753..7b285466 100644
--- a/resources/lib/PlexAPI.py
+++ b/resources/lib/PlexAPI.py
@@ -916,12 +916,15 @@ class PlexAPI():
username = answer.attrib.get('title', '')
token = answer.attrib.get('authenticationToken', '')
- userid = answer.attrib.get('id', '')
# Write to settings file
utils.settings('username', username)
- utils.settings('userid', userid)
utils.settings('accessToken', token)
+ utils.settings('userid',
+ answer.attrib.get('id', ''))
+ utils.settings('plex_restricteduser',
+ 'true' if answer.attrib.get('restricted', '0') == '1'
+ else 'false')
# Get final token to the PMS we've chosen
url = 'https://plex.tv/api/resources?includeHttps=1'
@@ -1476,8 +1479,11 @@ class API():
"""
res = self.item.attrib.get('audienceRating')
if res is None:
- res = self.item.attrib.get('rating', 0.0)
- res = float(res)
+ res = self.item.attrib.get('rating')
+ try:
+ res = float(res)
+ except (ValueError, TypeError):
+ res = 0.0
return res
def getYear(self):
@@ -1499,11 +1505,11 @@ class API():
try:
runtime = float(item['duration'])
- except KeyError:
+ except (KeyError, ValueError):
runtime = 0.0
try:
resume = float(item['viewOffset'])
- except KeyError:
+ except (KeyError, ValueError):
resume = 0.0
runtime = int(runtime * PlexToKodiTimefactor())
@@ -1837,16 +1843,22 @@ class API():
# Get background artwork URL
try:
background = item['art']
- background = "%s%s" % (self.server, background)
- background = self.addPlexCredentialsToUrl(background)
+ if background.startswith('http'):
+ pass
+ else:
+ background = "%s%s" % (self.server, background)
+ background = self.addPlexCredentialsToUrl(background)
except KeyError:
background = ""
allartworks['Backdrop'].append(background)
# Get primary "thumb" pictures:
try:
primary = item['thumb']
- primary = "%s%s" % (self.server, primary)
- primary = self.addPlexCredentialsToUrl(primary)
+ if primary.startswith('http'):
+ pass
+ else:
+ primary = "%s%s" % (self.server, primary)
+ primary = self.addPlexCredentialsToUrl(primary)
except KeyError:
primary = ""
allartworks['Primary'] = primary
@@ -2012,6 +2024,14 @@ class API():
self.logMsg('No extra artwork found')
return allartworks
+ def shouldStream(self):
+ """
+ Returns True if the item's 'optimizedForStreaming' is set, False other-
+ wise
+ """
+ return (True if self.item[0].attrib.get('optimizedForStreaming') == '1'
+ else False)
+
def getTranscodeVideoPath(self, action, quality={}):
"""
@@ -2033,7 +2053,6 @@ class API():
TODO: mediaIndex
"""
-
xargs = clientinfo.ClientInfo().getXArgsDeviceInfo()
# For DirectPlay, path/key of PART is needed
if action == "DirectStream":
@@ -2151,7 +2170,7 @@ class API():
'aired': self.getPremiereDate()
}
- if "episode" in self.getType():
+ if self.getType() == "episode":
# Only for tv shows
key, show, season, episode = self.getEpisodeDetails()
metadata['episode'] = episode
@@ -2204,6 +2223,7 @@ class API():
'album': 'music',
'song': 'music',
'track': 'music',
+ 'clip': 'clip'
}
typus = types[typus]
if utils.window('remapSMB') == 'true':
diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py
index c043cbba..840598a5 100644
--- a/resources/lib/entrypoint.py
+++ b/resources/lib/entrypoint.py
@@ -5,6 +5,7 @@
import json
import os
import sys
+import urllib
import xbmc
import xbmcaddon
@@ -235,6 +236,16 @@ def doPlayback(itemid, dbid):
Always to return with a "setResolvedUrl"
"""
+ if dbid == 'plexnode':
+ # Plex redirect, e.g. watch later. Need to get actual URLs
+ xml = downloadutils.DownloadUtils().downloadUrl(itemid,
+ authenticate=False)
+ if xml in (None, 401):
+ utils.logMsg(title, "Could not resolve url %s" % itemid, -1)
+ return xbmcplugin.setResolvedUrl(
+ int(sys.argv[1]), False, xbmcgui.ListItem())
+ return pbutils.PlaybackUtils(xml).play(None, dbid)
+
if utils.window('plex_authenticated') != "true":
utils.logMsg('doPlayback', 'Not yet authenticated for a PMS, abort '
'starting playback', -1)
@@ -249,12 +260,12 @@ def doPlayback(itemid, dbid):
return xbmcplugin.setResolvedUrl(
int(sys.argv[1]), False, xbmcgui.ListItem())
- item = PlexFunctions.GetPlexMetadata(itemid)
- if item is None or item == 401:
+ xml = PlexFunctions.GetPlexMetadata(itemid)
+ if xml in (None, 401):
return xbmcplugin.setResolvedUrl(
int(sys.argv[1]), False, xbmcgui.ListItem())
# Everything OK
- return pbutils.PlaybackUtils(item).play(itemid, dbid)
+ return pbutils.PlaybackUtils(xml).play(itemid, dbid)
# utils.logMsg(title, "doPlayback called with itemid=%s, dbid=%s"
# % (itemid, dbid), 1)
@@ -327,6 +338,9 @@ def doMainListing():
elif path and not xbmc.getCondVisibility("Window.IsActive(VideoLibrary) | Window.IsActive(Pictures) | Window.IsActive(MusicLibrary)"):
addDirectoryItem(label, path)
+ # Plex Watch later
+ addDirectoryItem(string(39211),
+ "plugin://plugin.video.plexkodiconnect/?mode=watchlater")
# Plex user switch
addDirectoryItem(string(39200) + utils.window('plex_username'),
"plugin://plugin.video.plexkodiconnect/"
@@ -1582,3 +1596,47 @@ def getOnDeck(viewid, mediatype, tagname, limit):
break
xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
+
+
+def watchlater():
+ """
+ Listing for plex.tv Watch Later section (if signed in to plex.tv)
+ """
+ if utils.window('plex_token') == '':
+ utils.logMsg(title, 'No watch later - not signed in to plex.tv', -1)
+ return xbmcplugin.endOfDirectory(int(sys.argv[1]), False)
+ if utils.settings('plex_restricteduser') == 'true':
+ utils.logMsg(title, 'No watch later - restricted user', -1)
+ return xbmcplugin.endOfDirectory(int(sys.argv[1]), False)
+
+ xml = downloadutils.DownloadUtils().downloadUrl(
+ 'https://plex.tv/pms/playlists/queue/all',
+ authenticate=False,
+ headerOptions={'X-Plex-Token': utils.window('plex_token')})
+ if xml in (None, 401):
+ utils.logMsg(title,
+ 'Could not download watch later list from plex.tv', -1)
+ return xbmcplugin.endOfDirectory(int(sys.argv[1]), False)
+
+ utils.logMsg(title, 'Displaying watch later plex.tv items', 1)
+ xbmcplugin.setContent(int(sys.argv[1]), 'movies')
+ url = "plugin://plugin.video.plexkodiconnect.movies/"
+ params = {
+ 'mode': "play",
+ 'dbid': 'plexnode'
+ }
+ for item in xml:
+ API = PlexAPI.API(item)
+ listitem = API.CreateListItemFromPlexItem()
+ API.AddStreamInfo(listitem)
+ pbutils.PlaybackUtils(item).setArtwork(listitem)
+ params['id'] = item.attrib.get('key')
+ xbmcplugin.addDirectoryItem(
+ handle=int(sys.argv[1]),
+ url="%s?%s" % (url, urllib.urlencode(params)),
+ listitem=listitem)
+
+ xbmcplugin.endOfDirectory(
+ handle=int(sys.argv[1]),
+ cacheToDisc=True if utils.settings('enableTextureCache') == 'true'
+ else False)
diff --git a/resources/lib/playbackutils.py b/resources/lib/playbackutils.py
index a076fad4..eb023e45 100644
--- a/resources/lib/playbackutils.py
+++ b/resources/lib/playbackutils.py
@@ -16,6 +16,7 @@ import playutils as putils
import playlist
import read_embyserver as embyserver
import utils
+import downloadutils
import PlexAPI
import PlexFunctions as PF
@@ -60,8 +61,24 @@ class PlaybackUtils():
if not playurl:
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
- if dbid in (None, '999999999'):
- # Item is not in Kodi database or is a trailer
+ if dbid in (None, '999999999', 'plexnode'):
+ # Item is not in Kodi database, is a trailer or plex redirect
+ # e.g. plex.tv watch later
+ API.CreateListItemFromPlexItem(listitem)
+ self.setArtwork(listitem)
+ if dbid == 'plexnode':
+ # Need to get yet another xml to get final url
+ window('emby_%s.playmethod' % playurl, clear=True)
+ xml = downloadutils.DownloadUtils().downloadUrl(
+ '{server}%s' % item[0][0][0].attrib.get('key'))
+ if xml in (None, 401):
+ log('Could not download %s'
+ % item[0][0][0].attrib.get('key'), -1)
+ return xbmcplugin.setResolvedUrl(
+ int(sys.argv[1]), False, listitem)
+ playurl = xml[0].attrib.get('key').encode('utf-8')
+ window('emby_%s.playmethod' % playurl, value='DirectStream')
+
playmethod = window('emby_%s.playmethod' % playurl)
if playmethod == "Transcode":
window('emby_%s.playmethod' % playurl, clear=True)
@@ -69,8 +86,6 @@ class PlaybackUtils():
listitem, playurl.decode('utf-8')).encode('utf-8')
window('emby_%s.playmethod' % playurl, "Transcode")
listitem.setPath(playurl)
- self.setArtwork(listitem)
- API.CreateListItemFromPlexItem(listitem)
self.setProperties(playurl, listitem)
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
diff --git a/resources/lib/playutils.py b/resources/lib/playutils.py
index 3ecf58e3..f5e5dd34 100644
--- a/resources/lib/playutils.py
+++ b/resources/lib/playutils.py
@@ -90,6 +90,10 @@ class PlayUtils():
"""
Returns the path/playurl if successful, False otherwise
"""
+ # True for e.g. plex.tv watch later
+ if self.API.shouldStream() is True:
+ self.logMsg("Plex item optimized for direct streaming", 1)
+ return False
# set to either 'Direct Stream=1' or 'Transcode=2'
if utils.settings('playType') != "0":
diff --git a/resources/settings.xml b/resources/settings.xml
index 6d8d18ff..964fca42 100644
--- a/resources/settings.xml
+++ b/resources/settings.xml
@@ -120,6 +120,9 @@
+
+
+