Overhaul playbackutils. Again

This commit is contained in:
tomkat83 2016-02-08 19:40:58 +01:00
parent 0eed5a9155
commit bea9e48bd9
4 changed files with 224 additions and 169 deletions

View file

@ -48,6 +48,14 @@ def GetKodiTypeFromPlex(plexItemType):
return classes[plexItemType.lower()] return classes[plexItemType.lower()]
def GetKodiItemTypeFromPlex(plexItemType):
classes = {
'movie': 'movie',
'episode': 'episode',
}
return classes[plexItemType.lower()]
def GetPlexKeyNumber(plexKey): def GetPlexKeyNumber(plexKey):
""" """
Deconstructs e.g. '/library/metadata/xxxx' to the tuple Deconstructs e.g. '/library/metadata/xxxx' to the tuple

View file

@ -100,34 +100,40 @@ def doPlayback(itemid, dbid):
""" """
Called only for a SINGLE element, not playQueues Called only for a SINGLE element, not playQueues
""" """
utils.logMsg(title, "doPlayback called with itemid=%s, dbid=%s"
% (itemid, dbid), 1)
item = PlexFunctions.GetPlexMetadata(itemid)
API = PlexAPI.API(item[0])
# If resume != 0, then we don't need to build a playlist for trailers etc.
# No idea how we could otherwise get resume timing out of Kodi
resume, runtime = API.getRuntime()
if resume == 0:
uuid = item.attrib.get('librarySectionUUID', None)
if uuid:
if utils.settings('askCinema') == "true":
trailers = xbmcgui.Dialog().yesno(addonName, "Play trailers?")
else:
trailers = True
if trailers:
playQueue = PlexFunctions.GetPlexPlaylist(
API.getRatingKey(), uuid, mediatype=API.getType())
if playQueue is not None:
return PassPlaylist(playQueue)
else:
utils.logMsg(title, "Error: no valid playQueue", -1)
else:
# E.g trailers being directly played
utils.logMsg(title, "No librarySectionUUID found.", 1)
# Play only 1 item, not playQueue item = PlexFunctions.GetPlexMetadata(itemid)
pbutils.PlaybackUtils(item).StartPlay(resume=resume, if item is None:
resumeId=None) return False
pbutils.PlaybackUtils(item).play(itemid, dbid)
# utils.logMsg(title, "doPlayback called with itemid=%s, dbid=%s"
# % (itemid, dbid), 1)
# item = PlexFunctions.GetPlexMetadata(itemid)
# API = PlexAPI.API(item[0])
# # If resume != 0, then we don't need to build a playlist for trailers etc.
# # No idea how we could otherwise get resume timing out of Kodi
# resume, runtime = API.getRuntime()
# if resume == 0:
# uuid = item.attrib.get('librarySectionUUID', None)
# if uuid:
# if utils.settings('askCinema') == "true":
# trailers = xbmcgui.Dialog().yesno(addonName, "Play trailers?")
# else:
# trailers = True
# if trailers:
# playQueue = PlexFunctions.GetPlexPlaylist(
# API.getRatingKey(), uuid, mediatype=API.getType())
# if playQueue is not None:
# return PassPlaylist(playQueue)
# else:
# utils.logMsg(title, "Error: no valid playQueue", -1)
# else:
# # E.g trailers being directly played
# utils.logMsg(title, "No librarySectionUUID found.", 1)
# # Play only 1 item, not playQueue
# pbutils.PlaybackUtils(item).StartPlay(resume=resume,
# resumeId=None)
##### DO RESET AUTH ##### ##### DO RESET AUTH #####

View file

@ -4,12 +4,12 @@
import json import json
import sys import sys
from urllib import urlencode
import xbmc import xbmc
import xbmcgui import xbmcgui
import xbmcplugin import xbmcplugin
import api
import artwork import artwork
import clientinfo import clientinfo
import downloadutils import downloadutils
@ -18,20 +18,23 @@ import playlist
import read_embyserver as embyserver import read_embyserver as embyserver
import utils import utils
import PlexAPI
import PlexFunctions as PF
################################################################################################# #################################################################################################
@utils.logging
class PlaybackUtils(): class PlaybackUtils():
def __init__(self, item): def __init__(self, item):
self.item = item self.item = item
self.API = api.API(self.item) self.API = PlexAPI.API(item)
self.clientInfo = clientinfo.ClientInfo() self.clientInfo = clientinfo.ClientInfo()
self.addonName = self.clientInfo.getAddonName() self.addonName = self.clientInfo.getAddonName()
self.doUtils = downloadutils.DownloadUtils()
self.userid = utils.window('emby_currUser') self.userid = utils.window('emby_currUser')
self.server = utils.window('emby_server%s' % self.userid) self.server = utils.window('emby_server%s' % self.userid)
@ -40,29 +43,33 @@ class PlaybackUtils():
self.emby = embyserver.Read_EmbyServer() self.emby = embyserver.Read_EmbyServer()
self.pl = playlist.Playlist() self.pl = playlist.Playlist()
def logMsg(self, msg, lvl=1):
self.className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
def play(self, itemid, dbid=None): def play(self, itemid, dbid=None):
self.logMsg("Play called.", 1) self.logMsg("Play called.", 1)
doUtils = self.doUtils
item = self.item item = self.item
# Hack to get only existing entry in PMS response for THIS instance of
# playbackutils :-)
self.API = PlexAPI.API(item[0])
API = self.API API = self.API
listitem = xbmcgui.ListItem() listitem = xbmcgui.ListItem()
playutils = putils.PlayUtils(item) playutils = putils.PlayUtils(item[0])
playurl = playutils.getPlayUrl() playurl = playutils.getPlayUrl()
if not playurl: if not playurl:
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem) return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
if dbid is None: if dbid is None or dbid == '999999999':
# Item is not in Kodi database # Item is not in Kodi database
playmethod = utils.window('emby_%s.playmethod' % playurl)
if playmethod == "Transcode":
utils.window('emby_%s.playmethod' % playurl, clear=True)
playurl = playutils.audioSubsPref(
listitem, playurl)
utils.window('emby_%s.playmethod' % playurl, "Transcode")
listitem.setPath(playurl) listitem.setPath(playurl)
self.setArtwork(listitem)
self.setListItem(listitem)
self.setProperties(playurl, listitem) self.setProperties(playurl, listitem)
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem) return xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
@ -72,20 +79,19 @@ class PlaybackUtils():
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
startPos = max(playlist.getposition(), 0) # Can return -1 startPos = max(playlist.getposition(), 0) # Can return -1
sizePlaylist = playlist.size() sizePlaylist = playlist.size()
currentPosition = startPos self.currentPosition = startPos
propertiesPlayback = utils.window('emby_playbackProps') == "true" propertiesPlayback = utils.window('emby_playbackProps') == "true"
introsPlaylist = False introsPlaylist = False
dummyPlaylist = False dummyPlaylist = False
self.logMsg("Playlist start position: %s" % startPos, 1) self.logMsg("Playlist start position: %s" % startPos, 1)
self.logMsg("Playlist plugin position: %s" % currentPosition, 1) self.logMsg("Playlist plugin position: %s" % self.currentPosition, 1)
self.logMsg("Playlist size: %s" % sizePlaylist, 1) self.logMsg("Playlist size: %s" % sizePlaylist, 1)
############### RESUME POINT ################ ############### RESUME POINT ################
userdata = API.getUserData() seektime, runtime = API.getRuntime()
seektime = API.adjustResume(userdata['Resume'])
# We need to ensure we add the intro and additional parts only once. # We need to ensure we add the intro and additional parts only once.
# Otherwise we get a loop. # Otherwise we get a loop.
@ -103,41 +109,21 @@ class PlaybackUtils():
# Remove the original item from playlist # Remove the original item from playlist
self.pl.removefromPlaylist(startPos+1) self.pl.removefromPlaylist(startPos+1)
# Readd the original item to playlist - via jsonrpc so we have full metadata # Readd the original item to playlist - via jsonrpc so we have full metadata
self.pl.insertintoPlaylist(currentPosition+1, dbid, item['Type'].lower()) self.pl.insertintoPlaylist(
currentPosition += 1 self.currentPosition+1,
dbid,
PF.GetKodiTypeFromPlex(API.getType()))
self.currentPosition += 1
############### -- CHECK FOR INTROS ################ ############### -- CHECK FOR INTROS ################
if utils.settings('enableCinema') == "true" and not seektime: if utils.settings('enableCinema') == "true" and not seektime:
# if we have any play them when the movie/show is not being resumed # if we have any play them when the movie/show is not being resumed
url = "{server}/emby/Users/{UserId}/Items/%s/Intros?format=json" % itemid xml = PF.GetPlexPlaylist(
intros = doUtils.downloadUrl(url) itemid,
item.attrib.get('librarySectionUUID'),
if intros['TotalRecordCount'] != 0: mediatype=API.getType())
getTrailers = True introsPlaylist = self.AddTrailers(xml)
if utils.settings('askCinema') == "true":
resp = xbmcgui.Dialog().yesno("Emby Cinema Mode", "Play trailers?")
if not resp:
# User selected to not play trailers
getTrailers = False
self.logMsg("Skip trailers.", 1)
if getTrailers:
for intro in intros['Items']:
# The server randomly returns intros, process them.
introListItem = xbmcgui.ListItem()
introPlayurl = putils.PlayUtils(intro).getPlayUrl()
self.logMsg("Adding Intro: %s" % introPlayurl, 1)
# Set listitem and properties for intros
pbutils = PlaybackUtils(intro)
pbutils.setProperties(introPlayurl, introListItem)
self.pl.insertintoPlaylist(currentPosition, url=introPlayurl)
introsPlaylist = True
currentPosition += 1
############### -- ADD MAIN ITEM ONLY FOR HOMESCREEN ############### ############### -- ADD MAIN ITEM ONLY FOR HOMESCREEN ###############
@ -145,32 +131,34 @@ class PlaybackUtils():
# Extend our current playlist with the actual item to play # Extend our current playlist with the actual item to play
# only if there's no playlist first # only if there's no playlist first
self.logMsg("Adding main item to playlist.", 1) self.logMsg("Adding main item to playlist.", 1)
self.pl.addtoPlaylist(dbid, item['Type'].lower()) self.pl.addtoPlaylist(
dbid,
PF.GetKodiItemTypeFromPlex(API.getType()))
# Ensure that additional parts are played after the main item # Ensure that additional parts are played after the main item
currentPosition += 1 self.currentPosition += 1
############### -- CHECK FOR ADDITIONAL PARTS ################ ############### -- CHECK FOR ADDITIONAL PARTS ################
if item.get('PartCount'): if len(item[0][0]) > 1:
# Only add to the playlist after intros have played # Only add to the playlist after intros have played
partcount = item['PartCount'] for counter, part in enumerate(item[0][0]):
url = "{server}/emby/Videos/%s/AdditionalParts?format=json" % itemid
parts = doUtils.downloadUrl(url)
for part in parts['Items']:
additionalListItem = xbmcgui.ListItem()
additionalPlayurl = putils.PlayUtils(part).getPlayUrl()
self.logMsg("Adding additional part: %s" % partcount, 1)
# Set listitem and properties for each additional parts # Set listitem and properties for each additional parts
pbutils = PlaybackUtils(part) API.setPartNumber(counter)
pbutils.setProperties(additionalPlayurl, additionalListItem) additionalListItem = xbmcgui.ListItem()
pbutils.setArtwork(additionalListItem) additionalPlayurl = playutils.getPlayUrl(
partNumber=counter)
self.logMsg("Adding additional part: %s" % counter, 1)
playlist.add(additionalPlayurl, additionalListItem, index=currentPosition) self.setProperties(additionalPlayurl, additionalListItem)
self.setArtwork(additionalListItem)
# NEW to Plex
self.setListItem(additionalListItem)
playlist.add(additionalPlayurl, additionalListItem, index=self.currentPosition)
self.pl.verifyPlaylist() self.pl.verifyPlaylist()
currentPosition += 1 self.currentPosition += 1
API.setPartNumber = 0
if dummyPlaylist: if dummyPlaylist:
# Added a dummy file to the playlist, # Added a dummy file to the playlist,
@ -189,6 +177,7 @@ class PlaybackUtils():
# For transcoding only, ask for audio/subs pref # For transcoding only, ask for audio/subs pref
if utils.window('emby_%s.playmethod' % playurl) == "Transcode": if utils.window('emby_%s.playmethod' % playurl) == "Transcode":
utils.window('emby_%s.playmethod' % playurl, clear=True)
playurl = playutils.audioSubsPref(playurl, listitem) playurl = playutils.audioSubsPref(playurl, listitem)
utils.window('emby_%s.playmethod' % playurl, value="Transcode") utils.window('emby_%s.playmethod' % playurl, value="Transcode")
@ -212,32 +201,74 @@ class PlaybackUtils():
self.logMsg("Play as a regular item.", 1) self.logMsg("Play as a regular item.", 1)
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
def AddTrailers(self, xml):
"""
Adds trailers to a movie, if applicable. Returns True if trailers were
added
"""
# Failure when downloading trailer playQueue
if xml is None:
return False
# Failure when getting trailers, e.g. when no plex pass
if xml.attrib.get('size') == '1':
return False
if utils.settings('askCinema') == "true":
resp = xbmcgui.Dialog().yesno(self.addonName, "Play trailers?")
if not resp:
# User selected to not play trailers
self.logMsg("Skip trailers.", 1)
return False
# Playurl needs to point back so we can get metadata!
path = "plugin://plugin.video.plexkodiconnect.movies/"
params = {
'mode': "play",
'dbid': 999999999
}
for counter, intro in enumerate(xml):
# Don't process the last item - it's the original movie
if counter == len(xml)-1:
break
# The server randomly returns intros, process them.
# introListItem = xbmcgui.ListItem()
# introPlayurl = putils.PlayUtils(intro).getPlayUrl()
introAPI = PlexAPI.API(intro)
params['id'] = introAPI.getRatingKey()
params['filename'] = introAPI.getKey()
introPlayurl = path + '?' + urlencode(params)
self.logMsg("Adding Intro: %s" % introPlayurl, 1)
self.pl.insertintoPlaylist(self.currentPosition, url=introPlayurl)
self.currentPosition += 1
return True
def setProperties(self, playurl, listitem): def setProperties(self, playurl, listitem):
# Set all properties necessary for plugin path playback # Set all properties necessary for plugin path playback
item = self.item itemid = self.API.getRatingKey()
itemid = item['Id'] itemtype = self.API.getType()
itemtype = item['Type'] resume, runtime = self.API.getRuntime()
embyitem = "emby_%s" % playurl embyitem = "emby_%s" % playurl
utils.window('%s.runtime' % embyitem, value=str(item.get('RunTimeTicks'))) utils.window('%s.runtime' % embyitem, value=str(runtime))
utils.window('%s.type' % embyitem, value=itemtype) utils.window('%s.type' % embyitem, value=itemtype)
utils.window('%s.itemid' % embyitem, value=itemid) utils.window('%s.itemid' % embyitem, value=itemid)
if itemtype == "Episode": # We need to keep track of playQueueItemIDs for Plex Companion
utils.window('%s.refreshid' % embyitem, value=item.get('SeriesId')) utils.window(
'plex_%s.playQueueItemID'
% playurl, self.API.GetPlayQueueItemID())
utils.window(
'plex_%s.guid'
% playurl, self.API.getGuid())
if itemtype == "episode":
utils.window('%s.refreshid' % embyitem,
value=self.API.getParentRatingKey())
else: else:
utils.window('%s.refreshid' % embyitem, value=itemid) utils.window('%s.refreshid' % embyitem, value=itemid)
# Append external subtitles to stream
playmethod = utils.window('%s.playmethod' % embyitem)
# Only for direct play and direct stream
subtitles = self.externalSubs(playurl)
if playmethod != "Transcode":
# Direct play automatically appends external
listitem.setSubtitles(subtitles)
self.setArtwork(listitem)
def externalSubs(self, playurl): def externalSubs(self, playurl):
externalsubs = [] externalsubs = []
@ -274,23 +305,33 @@ class PlaybackUtils():
return externalsubs return externalsubs
def setArtwork(self, listItem): def setArtwork(self, listItem):
# Set up item and item info allartwork = self.API.getAllArtwork(parentInfo=True)
item = self.item self.logMsg('allartwork: %s' % allartwork, 2)
artwork = self.artwork
allartwork = artwork.getAllArtwork(item, parentInfo=True) # arttypes = {
# Set artwork for listitem
# 'poster': "Primary",
# 'tvshow.poster': "Primary",
# 'clearart': "Art",
# 'tvshow.clearart': "Art",
# 'clearlogo': "Logo",
# 'tvshow.clearlogo': "Logo",
# 'discart': "Disc",
# 'fanart_image': "Backdrop",
# 'landscape': "Thumb"
# }
arttypes = { arttypes = {
'poster': "Primary", 'poster': "Primary",
'tvshow.poster': "Primary", 'tvshow.poster': "Primary",
'clearart': "Art", 'clearart': "Art",
'tvshow.clearart': "Art", 'tvshow.clearart': "Art",
'clearart': "Primary",
'tvshow.clearart': "Primary",
'clearlogo': "Logo", 'clearlogo': "Logo",
'tvshow.clearlogo': "Logo", 'tvshow.clearlogo': "Logo",
'discart': "Disc", 'discart': "Disc",
'fanart_image': "Backdrop", 'fanart_image': "Backdrop",
'landscape': "Thumb" 'landscape': "Backdrop"
} }
for arttype in arttypes: for arttype in arttypes:
@ -315,37 +356,40 @@ class PlaybackUtils():
def setListItem(self, listItem): def setListItem(self, listItem):
item = self.item
type = item['Type']
API = self.API API = self.API
mediaType = API.getType()
people = API.getPeople() people = API.getPeople()
studios = API.getStudios()
userdata = API.getUserData()
title, sorttitle = API.getTitle()
metadata = { metadata = {
'genre': API.joinList(API.getGenres()),
'title': item.get('Name', "Missing name"), 'year': API.getYear(),
'year': item.get('ProductionYear'), 'rating': API.getAudienceRating(),
'plot': API.getOverview(), 'playcount': userdata['PlayCount'],
'director': people.get('Director'), 'cast': people['Cast'],
'writer': people.get('Writer'), 'director': API.joinList(people.get('Director')),
'plot': API.getPlot(),
'title': title,
'sorttitle': sorttitle,
'duration': userdata['Runtime'],
'studio': API.joinList(API.getStudios()),
'tagline': API.getTagline(),
'writer': API.joinList(people.get('Writer')),
'premiered': API.getPremiereDate(),
'dateadded': API.getDateCreated(),
'lastplayed': userdata['LastPlayedDate'],
'mpaa': API.getMpaa(), 'mpaa': API.getMpaa(),
'genre': " / ".join(item['Genres']), 'aired': API.getPremiereDate()
'studio': " / ".join(studios),
'aired': API.getPremiereDate(),
'rating': item.get('CommunityRating'),
'votes': item.get('VoteCount')
} }
if "Episode" in type: if "episode" in mediaType:
# Only for tv shows # Only for tv shows
thumbId = item.get('SeriesId') key, show, season, episode = API.getEpisodeDetails()
season = item.get('ParentIndexNumber', -1)
episode = item.get('IndexNumber', -1)
show = item.get('SeriesName', "")
metadata['TVShowTitle'] = show
metadata['season'] = season
metadata['episode'] = episode metadata['episode'] = episode
metadata['season'] = season
metadata['tvshowtitle'] = show
listItem.setProperty('IsPlayable', 'true') listItem.setProperty('IsPlayable', 'true')
listItem.setProperty('IsFolder', 'false') listItem.setProperty('IsFolder', 'false')

View file

@ -32,14 +32,13 @@ class PlayUtils():
self.server = utils.window('emby_server%s' % self.userid) self.server = utils.window('emby_server%s' % self.userid)
self.machineIdentifier = utils.window('plex_machineIdentifier') self.machineIdentifier = utils.window('plex_machineIdentifier')
def getPlayUrl(self): def getPlayUrl(self, partNumber=None):
""" """
Returns a list of playurls, one per part in item Returns the playurl for the part with number partNumber
(movie might consist of several files)
""" """
playurls = []
for partNumber, part in enumerate(self.item[0]):
playurl = None
self.API.setPartNumber(partNumber) self.API.setPartNumber(partNumber)
playurl = None
if self.isDirectPlay(): if self.isDirectPlay():
self.logMsg("File is direct playing.", 1) self.logMsg("File is direct playing.", 1)
@ -67,10 +66,8 @@ class PlayUtils():
# Set playmethod property # Set playmethod property
utils.window('emby_%s.playmethod' % playurl, value="Transcode") utils.window('emby_%s.playmethod' % playurl, value="Transcode")
playurls.append(playurl) self.logMsg("The playurl is: %s" % playurl, 1)
return playurl
self.logMsg("The playurls are: %s" % playurls, 1)
return playurls
def httpPlay(self): def httpPlay(self):
# Audio, Video, Photo # Audio, Video, Photo