jellyfin-kodi/resources/lib/PlaybackUtils.py
angelblue05 1c4bf024fb Fix for intro
Not very pretty but this should prevent loop when enabling intros
2015-09-16 11:57:08 -05:00

513 lines
No EOL
20 KiB
Python

import xbmc
import xbmcplugin
import xbmcgui
import xbmcaddon
import urllib
import datetime
import time
import json as json
import inspect
import sys
from DownloadUtils import DownloadUtils
from PlayUtils import PlayUtils
from ReadKodiDB import ReadKodiDB
from ReadEmbyDB import ReadEmbyDB
import Utils as utils
from API import API
import os
import xbmcvfs
addon = xbmcaddon.Addon()
addondir = xbmc.translatePath(addon.getAddonInfo('profile'))
WINDOW = xbmcgui.Window( 10000 )
class PlaybackUtils():
settings = None
language = addon.getLocalizedString
logLevel = 0
downloadUtils = DownloadUtils()
WINDOW.clearProperty('playurlFalse')
def __init__(self, *args):
pass
def PLAY(self, result, setup="service"):
xbmc.log("PLAY Called")
WINDOW = xbmcgui.Window(10000)
username = WINDOW.getProperty('currUser')
userid = WINDOW.getProperty('userId%s' % username)
server = WINDOW.getProperty('server%s' % username)
try:
id = result["Id"]
except:
return
userData = result['UserData']
# BOOKMARK - RESUME POINT
timeInfo = API().getTimeInfo(result)
jumpBackSec = int(utils.settings("resumeJumpBack"))
seekTime = round(float(timeInfo.get('ResumeTime')), 6)
if seekTime > jumpBackSec:
# To avoid negative bookmark
seekTime = seekTime - jumpBackSec
itemsToPlay = []
# Check for intros
if seekTime == 0:
# if we have any play them when the movie/show is not being resumed
# We can add the option right here
url = "{server}/mediabrowser/Users/{UserId}/Items/%s/Intros?format=json&ImageTypeLimit=1&Fields=Etag" % id
intros = self.downloadUtils.downloadUrl(url)
if intros[u'TotalRecordCount'] == 0:
pass
else:
for intro in intros[u'Items']:
introId = intro[u'Id']
itemsToPlay.append(introId)
# Add original item
itemsToPlay.append(id)
# For split movies
if u'PartCount' in result:
partcount = result[u'PartCount']
# Get additional parts/playurl
url = "{server}/mediabrowser/Videos/%s/AdditionalParts" % id
parts = self.downloadUtils.downloadUrl(url)
for part in parts[u'Items']:
partId = part[u'Id']
itemsToPlay.append(partId)
if len(itemsToPlay) > 1:
# Let's play the playlist
playlist = self.AddToPlaylist(itemsToPlay)
if xbmc.getCondVisibility("Window.IsActive(home)"):
# Widget workaround
return xbmc.Player().play(playlist)
else:
# Can't pass a playlist to setResolvedUrl, so return False
# Wait for Kodi to process setResolved failure, then launch playlist
xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, xbmcgui.ListItem())
xbmc.sleep(10)
# Since we clear the original Kodi playlist, this is to prevent
# info dialog - can't play next item from showing up.
xbmc.executebuiltin('Dialog.Close(all,true)')
return xbmc.Player().play(playlist)
playurl = PlayUtils().getPlayUrl(server, id, result)
if playurl == False or WINDOW.getProperty('playurlFalse') == "true":
WINDOW.clearProperty('playurlFalse')
xbmc.log("Failed to retrieve the playback path/url.")
return
if WINDOW.getProperty("%splaymethod" % playurl) == "Transcode":
# Transcoding, we pull every track to set before playback starts
playurlprefs = self.audioSubsPref(playurl, result.get("MediaSources"))
if playurlprefs:
playurl = playurlprefs
else: # User cancelled dialog
return
thumbPath = API().getArtwork(result, "Primary")
#if the file is a virtual strm file, we need to override the path by reading it's contents
if playurl.endswith(".strm"):
xbmc.log("virtual strm file file detected, starting playback with 3th party addon...")
StrmTemp = "special://temp/temp.strm"
xbmcvfs.copy(playurl, StrmTemp)
playurl = open(xbmc.translatePath(StrmTemp), 'r').readline()
listItem = xbmcgui.ListItem(path=playurl, iconImage=thumbPath, thumbnailImage=thumbPath)
if WINDOW.getProperty("%splaymethod" % playurl) != "Transcode":
# Only for direct play and direct stream
# Append external subtitles to stream
subtitleList = self.externalSubs(id, playurl, server, result.get('MediaSources'))
listItem.setSubtitles(subtitleList)
#pass
# Can not play virtual items
if (result.get("LocationType") == "Virtual"):
xbmcgui.Dialog().ok(self.language(30128), self.language(30129))
watchedurl = "%s/mediabrowser/Users/%s/PlayedItems/%s" % (server, userid, id)
positionurl = "%s/mediabrowser/Users/%s/PlayingItems/%s" % (server, userid, id)
deleteurl = "%s/mediabrowser/Items/%s" % (server, id)
# set the current playing info
WINDOW.setProperty(playurl+"watchedurl", watchedurl)
WINDOW.setProperty(playurl+"positionurl", positionurl)
WINDOW.setProperty(playurl+"deleteurl", "")
WINDOW.setProperty(playurl+"deleteurl", deleteurl)
#show the additional resume dialog if launched from a widget
if xbmc.getCondVisibility("Window.IsActive(home)"):
if seekTime != 0:
displayTime = str(datetime.timedelta(seconds=(int(seekTime))))
display_list = [ self.language(30106) + ' ' + displayTime, self.language(30107)]
resumeScreen = xbmcgui.Dialog()
resume_result = resumeScreen.select(self.language(30105), display_list)
if resume_result == 0:
listItem.setProperty('StartOffset', str(seekTime))
elif resume_result < 0:
# User cancelled dialog
xbmc.log("Emby player -> User cancelled resume dialog.")
xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listItem)
return
if result.get("Type")=="Episode":
WINDOW.setProperty(playurl+"refresh_id", result.get("SeriesId"))
else:
WINDOW.setProperty(playurl+"refresh_id", id)
WINDOW.setProperty(playurl+"runtimeticks", str(result.get("RunTimeTicks")))
WINDOW.setProperty(playurl+"type", result.get("Type"))
WINDOW.setProperty(playurl+"item_id", id)
#launch the playback - only set the listitem props if we're not using the setresolvedurl approach
if setup == "service":
self.setListItemProps(server, id, listItem, result)
xbmc.Player().play(playurl,listItem)
elif setup == "default":
if xbmc.getCondVisibility("Window.IsActive(home)"):
self.setListItemProps(server, id, listItem, result)
xbmc.Player().play(playurl,listItem)
else:
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listItem)
def externalSubs(self, id, playurl, server, mediaSources):
externalsubs = []
mapping = {}
mediaStream = mediaSources[0].get('MediaStreams')
kodiindex = 0
for stream in mediaStream:
index = stream['Index']
# Since Emby returns all possible tracks together, have to pull only external subtitles.
# IsTextSubtitleStream if true, is available to download from emby.
if "Subtitle" in stream['Type'] and stream['IsExternal'] and stream['IsTextSubtitleStream']:
playmethod = utils.window("%splaymethod" % playurl)
if "DirectPlay" in playmethod:
# Direct play, get direct path
url = PlayUtils().directPlay(stream)
elif "DirectStream" in playmethod: # Direct stream
url = "%s/Videos/%s/%s/Subtitles/%s/Stream.srt" % (server, id, id, index)
# map external subtitles for mapping
mapping[kodiindex] = index
externalsubs.append(url)
kodiindex += 1
mapping = json.dumps(mapping)
utils.window('%sIndexMapping' % playurl, mapping)
return externalsubs
def audioSubsPref(self, url, mediaSources):
WINDOW = xbmcgui.Window(10000)
# Present the list of audio to select from
audioStreamsList = {}
audioStreams = []
selectAudioIndex = ""
subtitleStreamsList = {}
subtitleStreams = ['No subtitles']
selectSubsIndex = ""
playurlprefs = "%s" % url
mediaStream = mediaSources[0].get('MediaStreams')
for stream in mediaStream:
index = stream['Index']
# Since Emby returns all possible tracks together, have to sort them.
if 'Audio' in stream['Type']:
try:
track = stream['Language']
audioStreamsList[track] = index
audioStreams.append(track)
except:
track = stream['Codec']
audioStreamsList[track] = index
audioStreams.append(track)
elif 'Subtitle' in stream['Type']:
try:
track = stream['Language']
subtitleStreamsList[track] = index
subtitleStreams.append(track)
except:
track = stream['Codec']
subtitleStreamsList[track] = index
subtitleStreams.append(track)
if len(audioStreams) > 1:
resp = xbmcgui.Dialog().select("Choose the audio stream", audioStreams)
if resp > -1:
# User selected audio
selected = audioStreams[resp]
selected_audioIndex = audioStreamsList[selected]
playurlprefs += "&AudioStreamIndex=%s" % selected_audioIndex
selectAudioIndex = str(selected_audioIndex)
else: return False
else: # There's only one audiotrack.
audioIndex = audioStreamsList[audioStreams[0]]
playurlprefs += "&AudioStreamIndex=%s" % audioIndex
selectAudioIndex = str(audioIndex)
if len(subtitleStreams) > 1:
resp = xbmcgui.Dialog().select("Choose the subtitle stream", subtitleStreams)
if resp == 0:
# User selected no subtitles
pass
elif resp > -1:
# User selected subtitles
selected = subtitleStreams[resp]
selected_subsIndex = subtitleStreamsList[selected]
playurlprefs += "&SubtitleStreamIndex=%s" % selected_subsIndex
selectSubsIndex = str(selected_subsIndex)
else: return False
# Reset the method with the new playurl
WINDOW.setProperty("%splaymethod" % playurlprefs, "Transcode")
WINDOW.setProperty("%sAudioStreamIndex" % playurlprefs, selectAudioIndex)
WINDOW.setProperty("%sSubtitleStreamIndex" % playurlprefs, selectSubsIndex)
return playurlprefs
def setArt(self, list,name,path):
if name=='thumb' or name=='fanart_image' or name=='small_poster' or name=='tiny_poster' or name == "medium_landscape" or name=='medium_poster' or name=='small_fanartimage' or name=='medium_fanartimage' or name=='fanart_noindicators':
list.setProperty(name, path)
else:
list.setArt({name:path})
return list
def setListItemProps(self, server, id, listItem, result):
# set up item and item info
thumbID = id
eppNum = -1
seasonNum = -1
tvshowTitle = ""
if(result.get("Type") == "Episode"):
thumbID = result.get("SeriesId")
seasonNum = result.get("ParentIndexNumber")
eppNum = result.get("IndexNumber")
tvshowTitle = result.get("SeriesName")
self.setArt(listItem,'poster', API().getArtwork(result, "Primary"))
self.setArt(listItem,'tvshow.poster', API().getArtwork(result, "SeriesPrimary"))
self.setArt(listItem,'clearart', API().getArtwork(result, "Art"))
self.setArt(listItem,'tvshow.clearart', API().getArtwork(result, "Art"))
self.setArt(listItem,'clearlogo', API().getArtwork(result, "Logo"))
self.setArt(listItem,'tvshow.clearlogo', API().getArtwork(result, "Logo"))
self.setArt(listItem,'discart', API().getArtwork(result, "Disc"))
self.setArt(listItem,'fanart_image', API().getArtwork(result, "Backdrop"))
self.setArt(listItem,'landscape', API().getArtwork(result, "Thumb"))
listItem.setProperty('IsPlayable', 'true')
listItem.setProperty('IsFolder', 'false')
# Process Studios
studios = API().getStudios(result)
if studios == []:
studio = ""
else:
studio = studios[0]
listItem.setInfo('video', {'studio' : studio})
details = {
'title' : result.get("Name", "Missing Name"),
'plot' : result.get("Overview")
}
if(eppNum > -1):
details["episode"] = str(eppNum)
if(seasonNum > -1):
details["season"] = str(seasonNum)
if tvshowTitle != None:
details["TVShowTitle"] = tvshowTitle
listItem.setInfo( "Video", infoLabels=details )
people = API().getPeople(result)
# Process Genres
genre = API().getGenre(result)
listItem.setInfo('video', {'director' : people.get('Director')})
listItem.setInfo('video', {'writer' : people.get('Writer')})
listItem.setInfo('video', {'mpaa': result.get("OfficialRating")})
listItem.setInfo('video', {'genre': API().getGenre(result)})
def seekToPosition(self, seekTo):
#Set a loop to wait for positive confirmation of playback
count = 0
while not xbmc.Player().isPlaying():
count = count + 1
if count >= 10:
return
else:
xbmc.sleep(500)
#Jump to resume point
jumpBackSec = int(addon.getSetting("resumeJumpBack"))
seekToTime = seekTo - jumpBackSec
count = 0
while xbmc.Player().getTime() < (seekToTime - 5) and count < 11: # only try 10 times
count = count + 1
#xbmc.Player().pause
#xbmc.sleep(100)
xbmc.Player().seekTime(seekToTime)
xbmc.sleep(100)
#xbmc.Player().play()
def PLAYAllItems(self, items, startPositionTicks):
utils.logMsg("PlayBackUtils", "== ENTER: PLAYAllItems ==")
utils.logMsg("PlayBackUtils", "Items : " + str(items))
WINDOW = xbmcgui.Window(10000)
username = WINDOW.getProperty('currUser')
userid = WINDOW.getProperty('userId%s' % username)
server = WINDOW.getProperty('server%s' % username)
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
playlist.clear()
started = False
for itemID in items:
utils.logMsg("PlayBackUtils", "Adding Item to Playlist : " + itemID)
item_url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json" % itemID
jsonData = self.downloadUtils.downloadUrl(item_url)
item_data = jsonData
added = self.addPlaylistItem(playlist, item_data, server, userid)
if(added and started == False):
started = True
utils.logMsg("PlayBackUtils", "Starting Playback Pre")
xbmc.Player().play(playlist)
if(started == False):
utils.logMsg("PlayBackUtils", "Starting Playback Post")
xbmc.Player().play(playlist)
#seek to position
seekTime = 0
if(startPositionTicks != None):
seekTime = (startPositionTicks / 1000) / 10000
if seekTime > 0:
self.seekToPosition(seekTime)
def PLAYAllEpisodes(self, items):
WINDOW = xbmcgui.Window(10000)
username = WINDOW.getProperty('currUser')
userid = WINDOW.getProperty('userId%s' % username)
server = WINDOW.getProperty('server%s' % username)
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
playlist.clear()
for item in items:
item_url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json&ImageTypeLimit=1" % item["Id"]
jsonData = self.downloadUtils.downloadUrl(item_url)
item_data = jsonData
self.addPlaylistItem(playlist, item_data, server, userid)
xbmc.Player().play(playlist)
def AddToPlaylist(self, itemIds):
utils.logMsg("PlayBackUtils", "== ENTER: PLAYAllItems ==")
WINDOW = xbmcgui.Window(10000)
username = WINDOW.getProperty('currUser')
userid = WINDOW.getProperty('userId%s' % username)
server = WINDOW.getProperty('server%s' % username)
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
playlist.clear()
for itemID in itemIds:
utils.logMsg("PlayBackUtils", "Adding Item to Playlist : " + itemID)
item_url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json" % itemID
jsonData = self.downloadUtils.downloadUrl(item_url)
item_data = jsonData
self.addPlaylistItem(playlist, item_data, server, userid)
return playlist
def addPlaylistItem(self, playlist, item, server, userid):
id = item.get("Id")
playurl = PlayUtils().getPlayUrl(server, id, item)
utils.logMsg("PlayBackUtils", "Play URL: " + playurl)
thumbPath = API().getArtwork(item, "Primary")
listItem = xbmcgui.ListItem(path=playurl, iconImage=thumbPath, thumbnailImage=thumbPath)
self.setListItemProps(server, id, listItem, item)
WINDOW = xbmcgui.Window(10000)
username = WINDOW.getProperty('currUser')
userid = WINDOW.getProperty('userId%s' % username)
server = WINDOW.getProperty('server%s' % username)
# Can not play virtual items
if (item.get("LocationType") == "Virtual") or (item.get("IsPlaceHolder") == True):
xbmcgui.Dialog().ok(self.language(30128), self.language(30129))
return False
else:
watchedurl = "%s/mediabrowser/Users/%s/PlayedItems/%s" % (server, userid, id)
positionurl = "%s/mediabrowser/Users/%s/PlayingItems/%s" % (server, userid, id)
deleteurl = "%s/mediabrowser/Items/%s" % (server, id)
# set the current playing info
WINDOW = xbmcgui.Window( 10000 )
WINDOW.setProperty(playurl + "watchedurl", watchedurl)
WINDOW.setProperty(playurl + "positionurl", positionurl)
WINDOW.setProperty(playurl + "deleteurl", "")
if item.get("Type") == "Episode" and addon.getSetting("offerDeleteTV")=="true":
WINDOW.setProperty(playurl + "deleteurl", deleteurl)
if item.get("Type") == "Movie" and addon.getSetting("offerDeleteMovies")=="true":
WINDOW.setProperty(playurl + "deleteurl", deleteurl)
WINDOW.setProperty(playurl + "runtimeticks", str(item.get("RunTimeTicks")))
WINDOW.setProperty(playurl+"type", item.get("Type"))
WINDOW.setProperty(playurl + "item_id", id)
if (item.get("Type") == "Episode"):
WINDOW.setProperty(playurl + "refresh_id", item.get("SeriesId"))
else:
WINDOW.setProperty(playurl + "refresh_id", id)
utils.logMsg("PlayBackUtils", "PlayList Item Url : " + str(playurl))
playlist.add(playurl, listItem)
return True