Transcode revision, stack files, playback cleanup
This commit is contained in:
parent
0294957d14
commit
4ab6991968
5 changed files with 726 additions and 853 deletions
|
@ -1,30 +1,20 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#################################################################################################
|
|
||||||
# utils class
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
import xbmcaddon
|
|
||||||
import xbmcvfs
|
import xbmcvfs
|
||||||
|
|
||||||
from ClientInformation import ClientInformation
|
from ClientInformation import ClientInformation
|
||||||
import Utils as utils
|
import Utils as utils
|
||||||
|
|
||||||
###########################################################################
|
#################################################################################################
|
||||||
|
|
||||||
class PlayUtils():
|
class PlayUtils():
|
||||||
|
|
||||||
_shared_state = {}
|
|
||||||
|
|
||||||
clientInfo = ClientInformation()
|
clientInfo = ClientInformation()
|
||||||
|
|
||||||
addonName = clientInfo.getAddonName()
|
addonName = clientInfo.getAddonName()
|
||||||
WINDOW = xbmcgui.Window(10000)
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.__dict__ = self._shared_state
|
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
def logMsg(self, msg, lvl=1):
|
||||||
|
|
||||||
|
@ -33,59 +23,51 @@ class PlayUtils():
|
||||||
|
|
||||||
def getPlayUrl(self, server, id, result):
|
def getPlayUrl(self, server, id, result):
|
||||||
|
|
||||||
WINDOW = self.WINDOW
|
|
||||||
username = WINDOW.getProperty('currUser')
|
|
||||||
server = WINDOW.getProperty('server%s' % username)
|
|
||||||
|
|
||||||
if self.isDirectPlay(result,True):
|
if self.isDirectPlay(result,True):
|
||||||
# Try direct play
|
# Try direct play
|
||||||
playurl = self.directPlay(result)
|
playurl = self.directPlay(result)
|
||||||
if playurl:
|
if playurl:
|
||||||
self.logMsg("File is direct playing.", 1)
|
self.logMsg("File is direct playing.", 1)
|
||||||
WINDOW.setProperty("%splaymethod" % playurl.encode('utf-8'), "DirectPlay")
|
utils.window("%splaymethod" % playurl.encode('utf-8'), value="DirectPlay")
|
||||||
|
|
||||||
elif self.isDirectStream(result):
|
elif self.isDirectStream(result):
|
||||||
# Try direct stream
|
# Try direct stream
|
||||||
playurl = self.directStream(result, server, id)
|
playurl = self.directStream(result, server, id)
|
||||||
if playurl:
|
if playurl:
|
||||||
self.logMsg("File is direct streaming.", 1)
|
self.logMsg("File is direct streaming.", 1)
|
||||||
WINDOW.setProperty("%splaymethod" % playurl, "DirectStream")
|
utils.window("%splaymethod" % playurl, value="DirectStream")
|
||||||
|
|
||||||
elif self.isTranscoding(result):
|
elif self.isTranscoding(result):
|
||||||
# Try transcoding
|
# Try transcoding
|
||||||
playurl = self.transcoding(result, server, id)
|
playurl = self.transcoding(result, server, id)
|
||||||
if playurl:
|
if playurl:
|
||||||
self.logMsg("File is transcoding.", 1)
|
self.logMsg("File is transcoding.", 1)
|
||||||
WINDOW.setProperty("%splaymethod" % playurl, "Transcode")
|
utils.window("%splaymethod" % playurl, value="Transcode")
|
||||||
else:
|
|
||||||
# Error
|
else: # Error
|
||||||
return False
|
utils.window("playurlFalse", value="true")
|
||||||
|
return
|
||||||
|
|
||||||
return playurl.encode('utf-8')
|
return playurl.encode('utf-8')
|
||||||
|
|
||||||
def isDirectPlay(self, result, dialog=False):
|
|
||||||
|
def isDirectPlay(self, result, dialog = False):
|
||||||
# Requirements for Direct play:
|
# Requirements for Direct play:
|
||||||
# FileSystem, Accessible path
|
# FileSystem, Accessible path
|
||||||
|
if utils.settings('playFromStream') == "true":
|
||||||
playhttp = utils.settings('playFromStream')
|
# User forcing to play via HTTP instead of SMB
|
||||||
# User forcing to play via HTTP instead of SMB
|
|
||||||
if playhttp == "true":
|
|
||||||
self.logMsg("Can't direct play: Play from HTTP is enabled.", 1)
|
self.logMsg("Can't direct play: Play from HTTP is enabled.", 1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
canDirectPlay = result[u'MediaSources'][0][u'SupportsDirectPlay']
|
canDirectPlay = result['MediaSources'][0]['SupportsDirectPlay']
|
||||||
# Make sure it's supported by server
|
# Make sure it's supported by server
|
||||||
if not canDirectPlay:
|
if not canDirectPlay:
|
||||||
self.logMsg("Can't direct play: Server does not allow or support it.", 1)
|
self.logMsg("Can't direct play: Server does not allow or support it.", 1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if result['Path'].endswith('.strm'):
|
location = result['LocationType']
|
||||||
# Allow strm loading when direct playing
|
|
||||||
return True
|
|
||||||
|
|
||||||
location = result[u'LocationType']
|
|
||||||
# File needs to be "FileSystem"
|
# File needs to be "FileSystem"
|
||||||
if u'FileSystem' in location:
|
if 'FileSystem' in location:
|
||||||
# Verify if path is accessible
|
# Verify if path is accessible
|
||||||
if self.fileExists(result):
|
if self.fileExists(result):
|
||||||
return True
|
return True
|
||||||
|
@ -107,53 +89,49 @@ class PlayUtils():
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def directPlay(self, result):
|
def directPlay(self, result):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
try:
|
playurl = result['MediaSources'][0]['Path']
|
||||||
playurl = result[u'MediaSources'][0][u'Path']
|
except KeyError:
|
||||||
except:
|
playurl = result['Path']
|
||||||
playurl = result[u'Path']
|
|
||||||
except:
|
|
||||||
self.logMsg("Direct play failed. Trying Direct stream.", 1)
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
if u'VideoType' in result:
|
|
||||||
# Specific format modification
|
|
||||||
if u'Dvd' in result[u'VideoType']:
|
|
||||||
playurl = "%s/VIDEO_TS/VIDEO_TS.IFO" % playurl
|
|
||||||
elif u'BluRay' in result[u'VideoType']:
|
|
||||||
playurl = "%s/BDMV/index.bdmv" % playurl
|
|
||||||
|
|
||||||
# Network - SMB protocol
|
if 'VideoType' in result:
|
||||||
if "\\\\" in playurl:
|
# Specific format modification
|
||||||
smbuser = utils.settings('smbusername')
|
if 'Dvd' in result['VideoType']:
|
||||||
smbpass = utils.settings('smbpassword')
|
playurl = "%s/VIDEO_TS/VIDEO_TS.IFO" % playurl
|
||||||
# Network share
|
elif 'BluRay' in result['VideoType']:
|
||||||
if smbuser:
|
playurl = "%s/BDMV/index.bdmv" % playurl
|
||||||
playurl = playurl.replace("\\\\", "smb://%s:%s@" % (smbuser, smbpass))
|
|
||||||
else:
|
|
||||||
playurl = playurl.replace("\\\\", "smb://")
|
|
||||||
playurl = playurl.replace("\\", "/")
|
|
||||||
|
|
||||||
if "apple.com" in playurl:
|
# Network - SMB protocol
|
||||||
USER_AGENT = "QuickTime/7.7.4"
|
if "\\\\" in playurl:
|
||||||
playurl += "?|User-Agent=%s" % USER_AGENT
|
smbuser = utils.settings('smbusername')
|
||||||
|
smbpass = utils.settings('smbpassword')
|
||||||
|
# Network share
|
||||||
|
if smbuser:
|
||||||
|
playurl = playurl.replace("\\\\", "smb://%s:%s@" % (smbuser, smbpass))
|
||||||
|
else:
|
||||||
|
playurl = playurl.replace("\\\\", "smb://")
|
||||||
|
playurl = playurl.replace("\\", "/")
|
||||||
|
|
||||||
|
if "apple.com" in playurl:
|
||||||
|
USER_AGENT = "QuickTime/7.7.4"
|
||||||
|
playurl += "?|User-Agent=%s" % USER_AGENT
|
||||||
|
|
||||||
|
return playurl
|
||||||
|
|
||||||
return playurl
|
|
||||||
|
|
||||||
def isDirectStream(self, result):
|
def isDirectStream(self, result):
|
||||||
# Requirements for Direct stream:
|
# Requirements for Direct stream:
|
||||||
# FileSystem or Remote, BitRate, supported encoding
|
# FileSystem or Remote, BitRate, supported encoding
|
||||||
canDirectStream = result[u'MediaSources'][0][u'SupportsDirectStream']
|
canDirectStream = result['MediaSources'][0]['SupportsDirectStream']
|
||||||
# Make sure it's supported by server
|
# Make sure it's supported by server
|
||||||
if not canDirectStream:
|
if not canDirectStream:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
location = result[u'LocationType']
|
location = result['LocationType']
|
||||||
# File can be FileSystem or Remote, not Virtual
|
# File can be FileSystem or Remote, not Virtual
|
||||||
if u'Virtual' in location:
|
if 'Virtual' in location:
|
||||||
self.logMsg("File location is virtual. Can't proceed.", 1)
|
self.logMsg("File location is virtual. Can't proceed.", 1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -171,33 +149,29 @@ class PlayUtils():
|
||||||
playurl = self.directPlay(result)
|
playurl = self.directPlay(result)
|
||||||
return playurl
|
return playurl
|
||||||
|
|
||||||
try:
|
if "ThemeVideo" in type:
|
||||||
if "ThemeVideo" in type:
|
playurl = "%s/mediabrowser/Videos/%s/stream?static=true" % (server, id)
|
||||||
playurl ="%s/mediabrowser/Videos/%s/stream?static=true" % (server, id)
|
|
||||||
|
|
||||||
elif "Video" in type:
|
elif "Video" in type:
|
||||||
playurl = "%s/mediabrowser/Videos/%s/stream?static=true" % (server, id)
|
playurl = "%s/mediabrowser/Videos/%s/stream?static=true" % (server, id)
|
||||||
|
|
||||||
elif "Audio" in type:
|
elif "Audio" in type:
|
||||||
playurl = "%s/mediabrowser/Audio/%s/stream.mp3" % (server, id)
|
playurl = "%s/mediabrowser/Audio/%s/stream.mp3" % (server, id)
|
||||||
|
|
||||||
return playurl
|
return playurl
|
||||||
|
|
||||||
except:
|
|
||||||
self.logMsg("Direct stream failed. Trying transcoding.", 1)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def isTranscoding(self, result):
|
def isTranscoding(self, result):
|
||||||
# Last resort, no requirements
|
# Last resort, no requirements
|
||||||
# BitRate
|
# BitRate
|
||||||
canTranscode = result[u'MediaSources'][0][u'SupportsTranscoding']
|
canTranscode = result['MediaSources'][0]['SupportsTranscoding']
|
||||||
# Make sure it's supported by server
|
# Make sure it's supported by server
|
||||||
if not canTranscode:
|
if not canTranscode:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
location = result[u'LocationType']
|
location = result['LocationType']
|
||||||
# File can be FileSystem or Remote, not Virtual
|
# File can be FileSystem or Remote, not Virtual
|
||||||
if u'Virtual' in location:
|
if 'Virtual' in location:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -209,30 +183,27 @@ class PlayUtils():
|
||||||
playurl = self.directPlay(result)
|
playurl = self.directPlay(result)
|
||||||
return playurl
|
return playurl
|
||||||
|
|
||||||
try:
|
# Play transcoding
|
||||||
# Play transcoding
|
deviceId = self.clientInfo.getMachineId()
|
||||||
deviceId = self.clientInfo.getMachineId()
|
playurl = "%s/mediabrowser/Videos/%s/master.m3u8?mediaSourceId=%s" % (server, id, id)
|
||||||
playurl = "%s/mediabrowser/Videos/%s/master.m3u8?mediaSourceId=%s" % (server, id, id)
|
playurl = "%s&VideoCodec=h264&AudioCodec=ac3&MaxAudioChannels=6&deviceId=%s&VideoBitrate=%s" % (playurl, deviceId, self.getVideoBitRate()*1000)
|
||||||
playurl = "%s&VideoCodec=h264&AudioCodec=ac3&deviceId=%s&VideoBitrate=%s" % (playurl, deviceId, self.getVideoBitRate()*1000)
|
|
||||||
self.logMsg("Playurl: %s" % playurl)
|
|
||||||
|
|
||||||
return playurl
|
playurl = self.audioSubsPref(playurl, result.get('MediaSources'))
|
||||||
|
self.logMsg("Playurl: %s" % playurl, 1)
|
||||||
|
|
||||||
|
return playurl
|
||||||
|
|
||||||
except:
|
|
||||||
self.logMsg("Transcoding failed.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Works out if the network quality can play directly or if transcoding is needed
|
|
||||||
def isNetworkQualitySufficient(self, result):
|
def isNetworkQualitySufficient(self, result):
|
||||||
|
# Works out if the network quality can play directly or if transcoding is needed
|
||||||
settingsVideoBitRate = self.getVideoBitRate()
|
settingsVideoBitRate = self.getVideoBitRate()
|
||||||
settingsVideoBitRate = settingsVideoBitRate * 1000
|
settingsVideoBitRate = settingsVideoBitRate * 1000
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mediaSources = result[u'MediaSources']
|
mediaSources = result['MediaSources']
|
||||||
sourceBitRate = int(mediaSources[0][u'Bitrate'])
|
sourceBitRate = int(mediaSources[0]['Bitrate'])
|
||||||
except:
|
except KeyError:
|
||||||
self.logMsg("Bitrate value is missing.")
|
self.logMsg("Bitrate value is missing.", 1)
|
||||||
else:
|
else:
|
||||||
self.logMsg("The video quality selected is: %s, the video bitrate required to direct stream is: %s." % (settingsVideoBitRate, sourceBitRate), 1)
|
self.logMsg("The video quality selected is: %s, the video bitrate required to direct stream is: %s." % (settingsVideoBitRate, sourceBitRate), 1)
|
||||||
if settingsVideoBitRate < sourceBitRate:
|
if settingsVideoBitRate < sourceBitRate:
|
||||||
|
@ -243,58 +214,40 @@ class PlayUtils():
|
||||||
def getVideoBitRate(self):
|
def getVideoBitRate(self):
|
||||||
# get the addon video quality
|
# get the addon video quality
|
||||||
videoQuality = utils.settings('videoBitRate')
|
videoQuality = utils.settings('videoBitRate')
|
||||||
|
bitrate = {
|
||||||
|
|
||||||
if (videoQuality == "0"):
|
'0': 664,
|
||||||
return 664
|
'1': 996,
|
||||||
elif (videoQuality == "1"):
|
'2': 1320,
|
||||||
return 996
|
'3': 2000,
|
||||||
elif (videoQuality == "2"):
|
'4': 3200,
|
||||||
return 1320
|
'5': 4700,
|
||||||
elif (videoQuality == "3"):
|
'6': 6200,
|
||||||
return 2000
|
'7': 7700,
|
||||||
elif (videoQuality == "4"):
|
'8': 9200,
|
||||||
return 3200
|
'9': 10700,
|
||||||
elif (videoQuality == "5"):
|
'10': 12200,
|
||||||
return 4700
|
'11': 13700,
|
||||||
elif (videoQuality == "6"):
|
'12': 15200,
|
||||||
return 6200
|
'13': 16700,
|
||||||
elif (videoQuality == "7"):
|
'14': 18200,
|
||||||
return 7700
|
'15': 20000,
|
||||||
elif (videoQuality == "8"):
|
'16': 40000,
|
||||||
return 9200
|
'17': 100000,
|
||||||
elif (videoQuality == "9"):
|
'18': 1000000
|
||||||
return 10700
|
}
|
||||||
elif (videoQuality == "10"):
|
|
||||||
return 12200
|
# max bit rate supported by server (max signed 32bit integer)
|
||||||
elif (videoQuality == "11"):
|
return bitrate.get(videoQuality, 2147483)
|
||||||
return 13700
|
|
||||||
elif (videoQuality == "12"):
|
|
||||||
return 15200
|
|
||||||
elif (videoQuality == "13"):
|
|
||||||
return 16700
|
|
||||||
elif (videoQuality == "14"):
|
|
||||||
return 18200
|
|
||||||
elif (videoQuality == "15"):
|
|
||||||
return 20000
|
|
||||||
elif (videoQuality == "16"):
|
|
||||||
return 40000
|
|
||||||
elif (videoQuality == "17"):
|
|
||||||
return 100000
|
|
||||||
elif (videoQuality == "18"):
|
|
||||||
return 1000000
|
|
||||||
else:
|
|
||||||
return 2147483 # max bit rate supported by server (max signed 32bit integer)
|
|
||||||
|
|
||||||
def fileExists(self, result):
|
def fileExists(self, result):
|
||||||
|
|
||||||
if u'Path' not in result:
|
if 'Path' not in result:
|
||||||
# File has no path in server
|
# File has no path in server
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Convert Emby path to a path we can verify
|
# Convert Emby path to a path we can verify
|
||||||
path = self.directPlay(result)
|
path = self.directPlay(result)
|
||||||
if not path:
|
|
||||||
return False
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pathexists = xbmcvfs.exists(path)
|
pathexists = xbmcvfs.exists(path)
|
||||||
|
@ -313,3 +266,86 @@ class PlayUtils():
|
||||||
else:
|
else:
|
||||||
self.logMsg("Path is detected as follow: %s. Try direct streaming." % path, 2)
|
self.logMsg("Path is detected as follow: %s. Try direct streaming." % path, 2)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def audioSubsPref(self, url, mediaSources):
|
||||||
|
# For transcoding only
|
||||||
|
# Present the list of audio to select from
|
||||||
|
audioStreamsList = {}
|
||||||
|
audioStreams = []
|
||||||
|
audioStreamsChannelsList = {}
|
||||||
|
subtitleStreamsList = {}
|
||||||
|
subtitleStreams = ['No subtitles']
|
||||||
|
selectAudioIndex = ""
|
||||||
|
selectSubsIndex = ""
|
||||||
|
playurlprefs = "%s" % url
|
||||||
|
|
||||||
|
mediaStream = mediaSources[0].get('MediaStreams')
|
||||||
|
for stream in mediaStream:
|
||||||
|
# Since Emby returns all possible tracks together, have to sort them.
|
||||||
|
index = stream['Index']
|
||||||
|
type = stream['Type']
|
||||||
|
|
||||||
|
if 'Audio' in type:
|
||||||
|
codec = stream['Codec']
|
||||||
|
channelLayout = stream['ChannelLayout']
|
||||||
|
|
||||||
|
try:
|
||||||
|
track = "%s - %s - %s %s" % (index, stream['Language'], codec, channelLayout)
|
||||||
|
except:
|
||||||
|
track = "%s - %s %s" % (index, codec, channelLayout)
|
||||||
|
|
||||||
|
audioStreamsChannelsList[index] = stream['Channels']
|
||||||
|
audioStreamsList[track] = index
|
||||||
|
audioStreams.append(track)
|
||||||
|
|
||||||
|
elif 'Subtitle' in type:
|
||||||
|
try:
|
||||||
|
track = "%s - %s" % (index, stream['Language'])
|
||||||
|
except:
|
||||||
|
track = "%s - %s" % (index, stream['Codec'])
|
||||||
|
|
||||||
|
default = stream['IsDefault']
|
||||||
|
forced = stream['IsForced']
|
||||||
|
if default:
|
||||||
|
track = "%s - Default" % track
|
||||||
|
if forced:
|
||||||
|
track = "%s - Forced" % track
|
||||||
|
|
||||||
|
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]
|
||||||
|
selectAudioIndex = audioStreamsList[selected]
|
||||||
|
playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
|
||||||
|
else: # User backed out of selection
|
||||||
|
playurlprefs += "&AudioStreamIndex=%s" % mediaSources[0]['DefaultAudioStreamIndex']
|
||||||
|
else: # There's only one audiotrack.
|
||||||
|
selectAudioIndex = audioStreamsList[audioStreams[0]]
|
||||||
|
playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
|
||||||
|
|
||||||
|
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]
|
||||||
|
selectSubsIndex = subtitleStreamsList[selected]
|
||||||
|
playurlprefs += "&SubtitleStreamIndex=%s" % selectSubsIndex
|
||||||
|
else: # User backed out of selection
|
||||||
|
playurlprefs += "&SubtitleStreamIndex=%s" % mediaSources[0].get('DefaultSubtitleStreamIndex', "")
|
||||||
|
|
||||||
|
# Get number of channels for selected audio track
|
||||||
|
audioChannels = audioStreamsChannelsList.get(selectAudioIndex, 0)
|
||||||
|
if audioChannels > 2:
|
||||||
|
playurlprefs += "&AudioBitrate=384000"
|
||||||
|
else:
|
||||||
|
playurlprefs += "&AudioBitrate=192000"
|
||||||
|
|
||||||
|
return playurlprefs
|
|
@ -1,190 +1,151 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
#################################################################################################
|
||||||
|
|
||||||
import xbmc
|
|
||||||
import xbmcplugin
|
|
||||||
import xbmcgui
|
|
||||||
import xbmcaddon
|
|
||||||
import urllib
|
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
|
||||||
import json as json
|
import json as json
|
||||||
import inspect
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import xbmc
|
||||||
|
import xbmcaddon
|
||||||
|
import xbmcplugin
|
||||||
|
import xbmcgui
|
||||||
|
|
||||||
|
from API import API
|
||||||
from DownloadUtils import DownloadUtils
|
from DownloadUtils import DownloadUtils
|
||||||
from PlayUtils import PlayUtils
|
from PlayUtils import PlayUtils
|
||||||
from ReadKodiDB import ReadKodiDB
|
from ClientInformation import ClientInformation
|
||||||
from ReadEmbyDB import ReadEmbyDB
|
|
||||||
import Utils as utils
|
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():
|
class PlaybackUtils():
|
||||||
|
|
||||||
settings = None
|
clientInfo = ClientInformation()
|
||||||
|
doUtils = DownloadUtils()
|
||||||
|
api = API()
|
||||||
|
|
||||||
|
addon = xbmcaddon.Addon()
|
||||||
language = addon.getLocalizedString
|
language = addon.getLocalizedString
|
||||||
logLevel = 0
|
addonName = clientInfo.getAddonName()
|
||||||
downloadUtils = DownloadUtils()
|
|
||||||
|
|
||||||
WINDOW.clearProperty('playurlFalse')
|
username = utils.window('currUser')
|
||||||
|
userid = utils.window('userId%s' % username)
|
||||||
|
server = utils.window('server%s' % username)
|
||||||
|
|
||||||
def __init__(self, *args):
|
def logMsg(self, msg, lvl=1):
|
||||||
pass
|
|
||||||
|
|
||||||
def PLAY(self, result, setup="service"):
|
className = self.__class__.__name__
|
||||||
xbmc.log("PLAY Called")
|
utils.logMsg("%s %s" % (self.addonName, className), msg, int(lvl))
|
||||||
WINDOW = xbmcgui.Window(10000)
|
|
||||||
|
|
||||||
username = WINDOW.getProperty('currUser')
|
def PLAY(self, result, setup = "service"):
|
||||||
userid = WINDOW.getProperty('userId%s' % username)
|
|
||||||
server = WINDOW.getProperty('server%s' % username)
|
|
||||||
|
|
||||||
try:
|
self.logMsg("PLAY Called", 1)
|
||||||
id = result["Id"]
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
userData = result['UserData']
|
api = self.api
|
||||||
|
doUtils = self.doUtils
|
||||||
|
server = self.server
|
||||||
|
|
||||||
# BOOKMARK - RESUME POINT
|
listItem = xbmcgui.ListItem()
|
||||||
timeInfo = API().getTimeInfo(result)
|
id = result['Id']
|
||||||
jumpBackSec = int(utils.settings("resumeJumpBack"))
|
userdata = result['UserData']
|
||||||
|
|
||||||
|
# Get the playurl - direct play, direct stream or transcoding
|
||||||
|
playurl = PlayUtils().getPlayUrl(server, id, result)
|
||||||
|
|
||||||
|
if utils.window('playurlFalse') == "true":
|
||||||
|
# Playurl failed - set in PlayUtils.py
|
||||||
|
utils.window('playurlFalse', clear=True)
|
||||||
|
self.logMsg("Failed to retrieve the playback path/url or dialog was cancelled.", 1)
|
||||||
|
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listItem)
|
||||||
|
|
||||||
|
# Resume point for widget only
|
||||||
|
timeInfo = api.getTimeInfo(result)
|
||||||
|
jumpBackSec = int(utils.settings('resumeJumpBack'))
|
||||||
seekTime = round(float(timeInfo.get('ResumeTime')), 6)
|
seekTime = round(float(timeInfo.get('ResumeTime')), 6)
|
||||||
if seekTime > jumpBackSec:
|
if seekTime > jumpBackSec:
|
||||||
# To avoid negative bookmark
|
# To avoid negative bookmark
|
||||||
seekTime = seekTime - jumpBackSec
|
seekTime = seekTime - jumpBackSec
|
||||||
|
|
||||||
itemsToPlay = []
|
# Show the additional resume dialog if launched from a widget
|
||||||
|
if xbmc.getCondVisibility('Window.IsActive(home)') and seekTime:
|
||||||
|
# Dialog presentation
|
||||||
|
displayTime = str(datetime.timedelta(seconds=(int(seekTime))))
|
||||||
|
display_list = ["%s %s" % (self.language(30106), displayTime), self.language(30107)]
|
||||||
|
resume_result = xbmcgui.Dialog().select(self.language(30105), display_list)
|
||||||
|
|
||||||
|
if resume_result == 0:
|
||||||
|
# User selected to resume, append resume point to listitem
|
||||||
|
listItem.setProperty('StartOffset', str(seekTime))
|
||||||
|
|
||||||
|
elif resume_result > 0:
|
||||||
|
# User selected to start from beginning
|
||||||
|
seekTime = 0
|
||||||
|
|
||||||
|
elif resume_result < 0:
|
||||||
|
# User cancelled the dialog
|
||||||
|
self.logMsg("User cancelled resume dialog.", 1)
|
||||||
|
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listItem)
|
||||||
|
|
||||||
|
|
||||||
|
# In order, intros, original item requested and any additional parts
|
||||||
|
playstack = []
|
||||||
|
|
||||||
# Check for intros
|
# Check for intros
|
||||||
if seekTime == 0:
|
if utils.settings('disableCinema') == "false" 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
|
||||||
# We can add the option right here
|
|
||||||
url = "{server}/mediabrowser/Users/{UserId}/Items/%s/Intros?format=json&ImageTypeLimit=1&Fields=Etag" % id
|
url = "{server}/mediabrowser/Users/{UserId}/Items/%s/Intros?format=json&ImageTypeLimit=1&Fields=Etag" % id
|
||||||
intros = self.downloadUtils.downloadUrl(url)
|
intros = doUtils.downloadUrl(url)
|
||||||
if intros[u'TotalRecordCount'] == 0:
|
if intros['TotalRecordCount'] != 0:
|
||||||
pass
|
for intro in intros['Items']:
|
||||||
else:
|
introPlayurl = PlayUtils().getPlayUrl(server, intro['Id'], intro)
|
||||||
for intro in intros[u'Items']:
|
self.setProperties(introPlayurl, intro)
|
||||||
introId = intro[u'Id']
|
playstack.append(introPlayurl)
|
||||||
itemsToPlay.append(introId)
|
|
||||||
|
|
||||||
# Add original item
|
# Add original item
|
||||||
itemsToPlay.append(id)
|
playstack.append(playurl)
|
||||||
|
self.setProperties(playurl, result)
|
||||||
|
|
||||||
# For split movies
|
mainArt = API().getArtwork(result, "Primary")
|
||||||
if u'PartCount' in result:
|
listItem.setThumbnailImage(mainArt)
|
||||||
partcount = result[u'PartCount']
|
listItem.setIconImage(mainArt)
|
||||||
# Get additional parts/playurl
|
|
||||||
|
# Get additional parts/playurl
|
||||||
|
if result.get('PartCount'):
|
||||||
url = "{server}/mediabrowser/Videos/%s/AdditionalParts" % id
|
url = "{server}/mediabrowser/Videos/%s/AdditionalParts" % id
|
||||||
parts = self.downloadUtils.downloadUrl(url)
|
parts = doUtils.downloadUrl(url)
|
||||||
for part in parts[u'Items']:
|
for part in parts['Items']:
|
||||||
partId = part[u'Id']
|
additionalPlayurl = PlayUtils().getPlayUrl(server, part['Id'], part)
|
||||||
itemsToPlay.append(partId)
|
self.setProperties(additionalPlayurl, part)
|
||||||
|
playstack.append(additionalPlayurl)
|
||||||
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 len(playstack) > 1:
|
||||||
|
# Convert list in stack:// playurl
|
||||||
|
playMethod = utils.window('%splaymethod' % playurl)
|
||||||
|
playurl = "stack://%s" % " , ".join(playstack)
|
||||||
|
# Set new window properties for combined playurl
|
||||||
|
utils.window("%splaymethod" % playurl, value=playMethod)
|
||||||
|
self.setProperties(playurl, result)
|
||||||
|
|
||||||
#if the file is a virtual strm file, we need to override the path by reading it's contents
|
self.logMsg("Returned playurl: %s" % playurl, 1)
|
||||||
if playurl.endswith(".strm"):
|
listItem.setPath(playurl)
|
||||||
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 utils.window("%splaymethod" % playurl) != "Transcode":
|
||||||
|
|
||||||
if WINDOW.getProperty("%splaymethod" % playurl) != "Transcode":
|
|
||||||
# Only for direct play and direct stream
|
# Only for direct play and direct stream
|
||||||
# Append external subtitles to stream
|
# Append external subtitles to stream
|
||||||
subtitleList = self.externalSubs(id, playurl, server, result.get('MediaSources'))
|
subtitleList = self.externalSubs(id, playurl, server, result['MediaSources'])
|
||||||
listItem.setSubtitles(subtitleList)
|
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)
|
# Launch the playback - only set the listitem props if we're not using the setresolvedurl approach
|
||||||
positionurl = "%s/mediabrowser/Users/%s/PlayingItems/%s" % (server, userid, id)
|
if setup == "service" or xbmc.getCondVisibility('Window.IsActive(home)'):
|
||||||
deleteurl = "%s/mediabrowser/Items/%s" % (server, id)
|
# Sent via websocketclient.py or default.py but via widgets
|
||||||
|
|
||||||
# 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)
|
self.setListItemProps(server, id, listItem, result)
|
||||||
xbmc.Player().play(playurl,listItem)
|
xbmc.Player().play(playurl, listItem)
|
||||||
elif setup == "default":
|
elif setup == "default":
|
||||||
if xbmc.getCondVisibility("Window.IsActive(home)"):
|
# Sent via default.py
|
||||||
self.setListItemProps(server, id, listItem, result)
|
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listItem)
|
||||||
xbmc.Player().play(playurl,listItem)
|
|
||||||
else:
|
|
||||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listItem)
|
|
||||||
|
|
||||||
def externalSubs(self, id, playurl, server, mediaSources):
|
def externalSubs(self, id, playurl, server, mediaSources):
|
||||||
|
|
||||||
|
@ -214,100 +175,72 @@ class PlaybackUtils():
|
||||||
kodiindex += 1
|
kodiindex += 1
|
||||||
|
|
||||||
mapping = json.dumps(mapping)
|
mapping = json.dumps(mapping)
|
||||||
utils.window('%sIndexMapping' % playurl, mapping)
|
utils.window('%sIndexMapping' % playurl, value=mapping)
|
||||||
|
|
||||||
return externalsubs
|
return externalsubs
|
||||||
|
|
||||||
def audioSubsPref(self, url, mediaSources):
|
def setProperties(self, playurl, result):
|
||||||
|
# Set runtimeticks, type, refresh_id and item_id
|
||||||
|
id = result.get('Id')
|
||||||
|
type = result.get('Type', "")
|
||||||
|
|
||||||
WINDOW = xbmcgui.Window(10000)
|
utils.window("%sruntimeticks" % playurl, value=str(result.get('RunTimeTicks')))
|
||||||
# Present the list of audio to select from
|
utils.window("%stype" % playurl, value=type)
|
||||||
audioStreamsList = {}
|
utils.window("%sitem_id" % playurl, value=id)
|
||||||
audioStreams = []
|
|
||||||
selectAudioIndex = ""
|
|
||||||
subtitleStreamsList = {}
|
|
||||||
subtitleStreams = ['No subtitles']
|
|
||||||
selectSubsIndex = ""
|
|
||||||
playurlprefs = "%s" % url
|
|
||||||
|
|
||||||
mediaStream = mediaSources[0].get('MediaStreams')
|
if type == "Episode":
|
||||||
for stream in mediaStream:
|
utils.window("%srefresh_id" % playurl, value=result.get('SeriesId'))
|
||||||
index = stream['Index']
|
else:
|
||||||
# Since Emby returns all possible tracks together, have to sort them.
|
utils.window("%srefresh_id" % playurl, value=id)
|
||||||
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']:
|
def setArt(self, list, name, path):
|
||||||
try:
|
|
||||||
track = stream['Language']
|
|
||||||
subtitleStreamsList[track] = index
|
|
||||||
subtitleStreams.append(track)
|
|
||||||
except:
|
|
||||||
track = stream['Codec']
|
|
||||||
subtitleStreamsList[track] = index
|
|
||||||
subtitleStreams.append(track)
|
|
||||||
|
|
||||||
if len(audioStreams) > 1:
|
if name in ("thumb", "fanart_image", "small_poster", "tiny_poster", "medium_landscape", "medium_poster", "small_fanartimage", "medium_fanartimage", "fanart_noindicators"):
|
||||||
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)
|
list.setProperty(name, path)
|
||||||
else:
|
else:
|
||||||
list.setArt({name:path})
|
list.setArt({name:path})
|
||||||
|
|
||||||
return list
|
return list
|
||||||
|
|
||||||
def setListItemProps(self, server, id, listItem, result):
|
def setListItemProps(self, server, id, listItem, result):
|
||||||
# set up item and item info
|
# Set up item and item info
|
||||||
thumbID = id
|
api = self.api
|
||||||
eppNum = -1
|
|
||||||
seasonNum = -1
|
|
||||||
tvshowTitle = ""
|
|
||||||
|
|
||||||
if(result.get("Type") == "Episode"):
|
type = result.get('Type')
|
||||||
thumbID = result.get("SeriesId")
|
people = api.getPeople(result)
|
||||||
seasonNum = result.get("ParentIndexNumber")
|
studios = api.getStudios(result)
|
||||||
eppNum = result.get("IndexNumber")
|
|
||||||
tvshowTitle = result.get("SeriesName")
|
|
||||||
|
|
||||||
|
metadata = {
|
||||||
|
|
||||||
|
'title': result.get('Name', "Missing name"),
|
||||||
|
'year': result.get('ProductionYear'),
|
||||||
|
'plot': api.getOverview(result),
|
||||||
|
'director': people.get('Director'),
|
||||||
|
'writer': people.get('Writer'),
|
||||||
|
'mpaa': api.getMpaa(result),
|
||||||
|
'genre': api.getGenre(result),
|
||||||
|
'studio': " / ".join(studios),
|
||||||
|
'aired': api.getPremiereDate(result),
|
||||||
|
'rating': result.get('CommunityRating'),
|
||||||
|
'votes': result.get('VoteCount')
|
||||||
|
}
|
||||||
|
|
||||||
|
if "Episode" in type:
|
||||||
|
# Only for tv shows
|
||||||
|
thumbId = result.get('SeriesId')
|
||||||
|
season = result.get('ParentIndexNumber', -1)
|
||||||
|
episode = result.get('IndexNumber', -1)
|
||||||
|
show = result.get('SeriesName', "")
|
||||||
|
|
||||||
|
metadata['TVShowTitle'] = show
|
||||||
|
metadata['season'] = season
|
||||||
|
metadata['episode'] = episode
|
||||||
|
|
||||||
|
listItem.setProperty('IsPlayable', 'true')
|
||||||
|
listItem.setProperty('IsFolder', 'false')
|
||||||
|
listItem.setInfo('video', infoLabels=metadata)
|
||||||
|
|
||||||
|
# Set artwork for listitem
|
||||||
self.setArt(listItem,'poster', API().getArtwork(result, "Primary"))
|
self.setArt(listItem,'poster', API().getArtwork(result, "Primary"))
|
||||||
self.setArt(listItem,'tvshow.poster', API().getArtwork(result, "SeriesPrimary"))
|
self.setArt(listItem,'tvshow.poster', API().getArtwork(result, "SeriesPrimary"))
|
||||||
self.setArt(listItem,'clearart', API().getArtwork(result, "Art"))
|
self.setArt(listItem,'clearart', API().getArtwork(result, "Art"))
|
||||||
|
@ -318,105 +251,94 @@ class PlaybackUtils():
|
||||||
self.setArt(listItem,'fanart_image', API().getArtwork(result, "Backdrop"))
|
self.setArt(listItem,'fanart_image', API().getArtwork(result, "Backdrop"))
|
||||||
self.setArt(listItem,'landscape', API().getArtwork(result, "Thumb"))
|
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):
|
def seekToPosition(self, seekTo):
|
||||||
|
# Set a loop to wait for positive confirmation of playback
|
||||||
#Set a loop to wait for positive confirmation of playback
|
|
||||||
count = 0
|
count = 0
|
||||||
while not xbmc.Player().isPlaying():
|
while not xbmc.Player().isPlaying():
|
||||||
count = count + 1
|
count += 1
|
||||||
if count >= 10:
|
if count >= 10:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
xbmc.sleep(500)
|
xbmc.sleep(500)
|
||||||
|
|
||||||
#Jump to resume point
|
# Jump to seek position
|
||||||
jumpBackSec = int(addon.getSetting("resumeJumpBack"))
|
|
||||||
seekToTime = seekTo - jumpBackSec
|
|
||||||
count = 0
|
count = 0
|
||||||
while xbmc.Player().getTime() < (seekToTime - 5) and count < 11: # only try 10 times
|
while xbmc.Player().getTime() < (seekToTime - 5) and count < 11: # only try 10 times
|
||||||
count = count + 1
|
count += 1
|
||||||
#xbmc.Player().pause
|
xbmc.Player().seekTime(seekTo)
|
||||||
#xbmc.sleep(100)
|
|
||||||
xbmc.Player().seekTime(seekToTime)
|
|
||||||
xbmc.sleep(100)
|
xbmc.sleep(100)
|
||||||
#xbmc.Player().play()
|
|
||||||
|
|
||||||
def PLAYAllItems(self, items, startPositionTicks):
|
def PLAYAllItems(self, items, startPositionTicks):
|
||||||
utils.logMsg("PlayBackUtils", "== ENTER: PLAYAllItems ==")
|
|
||||||
utils.logMsg("PlayBackUtils", "Items : " + str(items))
|
|
||||||
WINDOW = xbmcgui.Window(10000)
|
|
||||||
|
|
||||||
username = WINDOW.getProperty('currUser')
|
self.logMsg("== ENTER: PLAYAllItems ==")
|
||||||
userid = WINDOW.getProperty('userId%s' % username)
|
self.logMsg("Items: %s" % items)
|
||||||
server = WINDOW.getProperty('server%s' % username)
|
|
||||||
|
|
||||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
doUtils = self.doUtils
|
||||||
|
|
||||||
|
playlist = xbmc.Playlist(xbmc.PLAYLIST_VIDEO)
|
||||||
playlist.clear()
|
playlist.clear()
|
||||||
started = False
|
started = False
|
||||||
|
|
||||||
for itemID in items:
|
for itemId in items:
|
||||||
|
self.logMsg("Adding Item to playlist: %s" % itemId, 1)
|
||||||
|
url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json" % itemId
|
||||||
|
result = doUtils.downloadUrl(url)
|
||||||
|
|
||||||
utils.logMsg("PlayBackUtils", "Adding Item to Playlist : " + itemID)
|
addition = self.addPlaylistItem(playlist, result)
|
||||||
item_url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json" % itemID
|
if not started and addition:
|
||||||
jsonData = self.downloadUtils.downloadUrl(item_url)
|
|
||||||
|
|
||||||
item_data = jsonData
|
|
||||||
added = self.addPlaylistItem(playlist, item_data, server, userid)
|
|
||||||
if(added and started == False):
|
|
||||||
started = True
|
started = True
|
||||||
utils.logMsg("PlayBackUtils", "Starting Playback Pre")
|
self.logMsg("Starting Playback Pre", 1)
|
||||||
xbmc.Player().play(playlist)
|
xbmc.Player().play(playlist)
|
||||||
|
|
||||||
if(started == False):
|
if not started:
|
||||||
utils.logMsg("PlayBackUtils", "Starting Playback Post")
|
self.logMsg("Starting Playback Post", 1)
|
||||||
xbmc.Player().play(playlist)
|
xbmc.Player().play(playlist)
|
||||||
|
|
||||||
#seek to position
|
# Seek to position
|
||||||
seekTime = 0
|
if startPositionTicks:
|
||||||
if(startPositionTicks != None):
|
seekTime = startPositionTicks / 10000000.0
|
||||||
seekTime = (startPositionTicks / 1000) / 10000
|
|
||||||
|
|
||||||
if seekTime > 0:
|
|
||||||
self.seekToPosition(seekTime)
|
self.seekToPosition(seekTime)
|
||||||
|
|
||||||
def PLAYAllEpisodes(self, items):
|
def AddToPlaylist(self, itemIds):
|
||||||
|
|
||||||
|
self.logMsg("PlayBackUtils", "== ENTER: PLAYAllItems ==")
|
||||||
|
|
||||||
|
doUtils = self.doUtils
|
||||||
|
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||||
|
|
||||||
|
for itemId in itemIds:
|
||||||
|
self.logMsg("Adding Item to Playlist: %s" % itemId)
|
||||||
|
url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json" % itemId
|
||||||
|
result = doUtils.downloadUrl(url)
|
||||||
|
|
||||||
|
self.addPlaylistItem(playlist, result)
|
||||||
|
|
||||||
|
return playlist
|
||||||
|
|
||||||
|
def addPlaylistItem(self, playlist, item):
|
||||||
|
|
||||||
|
id = item['Id']
|
||||||
|
server = self.server
|
||||||
|
|
||||||
|
playurl = PlayUtils().getPlayUrl(server, id, item)
|
||||||
|
|
||||||
|
if utils.window('playurlFalse') == "true":
|
||||||
|
# Playurl failed - set in PlayUtils.py
|
||||||
|
utils.window('playurlFalse', clear=True)
|
||||||
|
self.logMsg("Failed to retrieve the playback path/url or dialog was cancelled.", 1)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.logMsg("Playurl: %s" % playurl)
|
||||||
|
|
||||||
|
thumb = API().getArtwork(item, "Primary")
|
||||||
|
listItem = xbmcgui.ListItem(path=playurl, iconImage=thumb, thumbnailImage=thumb)
|
||||||
|
self.setListItemProps(server, id, listItem, item)
|
||||||
|
self.setProperties(playurl, item)
|
||||||
|
|
||||||
|
playlist.add(playurl, listItem)
|
||||||
|
|
||||||
|
# Not currently being used
|
||||||
|
'''def PLAYAllEpisodes(self, items):
|
||||||
WINDOW = xbmcgui.Window(10000)
|
WINDOW = xbmcgui.Window(10000)
|
||||||
|
|
||||||
username = WINDOW.getProperty('currUser')
|
username = WINDOW.getProperty('currUser')
|
||||||
|
@ -434,80 +356,4 @@ class PlaybackUtils():
|
||||||
item_data = jsonData
|
item_data = jsonData
|
||||||
self.addPlaylistItem(playlist, item_data, server, userid)
|
self.addPlaylistItem(playlist, item_data, server, userid)
|
||||||
|
|
||||||
xbmc.Player().play(playlist)
|
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
|
|
|
@ -1,27 +1,20 @@
|
||||||
import xbmcaddon
|
# -*- coding: utf-8 -*-
|
||||||
import xbmcplugin
|
|
||||||
|
#################################################################################################
|
||||||
|
|
||||||
|
import json as json
|
||||||
|
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
import os
|
|
||||||
import threading
|
|
||||||
import json
|
|
||||||
import inspect
|
|
||||||
|
|
||||||
import KodiMonitor
|
|
||||||
import Utils as utils
|
|
||||||
|
|
||||||
from DownloadUtils import DownloadUtils
|
from DownloadUtils import DownloadUtils
|
||||||
from WebSocketClient import WebSocketThread
|
from WebSocketClient import WebSocketThread
|
||||||
from PlayUtils import PlayUtils
|
|
||||||
from ClientInformation import ClientInformation
|
from ClientInformation import ClientInformation
|
||||||
from LibrarySync import LibrarySync
|
from LibrarySync import LibrarySync
|
||||||
from PlaybackUtils import PlaybackUtils
|
import Utils as utils
|
||||||
from ReadEmbyDB import ReadEmbyDB
|
|
||||||
from API import API
|
|
||||||
|
|
||||||
librarySync = LibrarySync()
|
#################################################################################################
|
||||||
|
|
||||||
# service class for playback monitoring
|
|
||||||
class Player( xbmc.Player ):
|
class Player( xbmc.Player ):
|
||||||
|
|
||||||
# Borg - multiple instances, shared state
|
# Borg - multiple instances, shared state
|
||||||
|
@ -31,135 +24,206 @@ class Player( xbmc.Player ):
|
||||||
doUtils = DownloadUtils()
|
doUtils = DownloadUtils()
|
||||||
clientInfo = ClientInformation()
|
clientInfo = ClientInformation()
|
||||||
ws = WebSocketThread()
|
ws = WebSocketThread()
|
||||||
|
librarySync = LibrarySync()
|
||||||
|
|
||||||
addonName = clientInfo.getAddonName()
|
addonName = clientInfo.getAddonName()
|
||||||
|
|
||||||
WINDOW = xbmcgui.Window(10000)
|
|
||||||
|
|
||||||
logLevel = 0
|
|
||||||
played_information = {}
|
played_information = {}
|
||||||
settings = None
|
|
||||||
playStats = {}
|
playStats = {}
|
||||||
|
currentFile = None
|
||||||
|
stackFiles = None
|
||||||
|
stackElapsed = 0
|
||||||
|
|
||||||
audioPref = "default"
|
def __init__(self, *args):
|
||||||
subsPref = "default"
|
|
||||||
|
|
||||||
def __init__( self, *args ):
|
|
||||||
|
|
||||||
self.__dict__ = self._shared_state
|
self.__dict__ = self._shared_state
|
||||||
self.logMsg("Starting playback monitor service", 1)
|
self.logMsg("Starting playback monitor.", 2)
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
def logMsg(self, msg, lvl=1):
|
||||||
|
|
||||||
self.className = self.__class__.__name__
|
self.className = self.__class__.__name__
|
||||||
utils.logMsg("%s %s" % (self.addonName, self.className), msg, int(lvl))
|
utils.logMsg("%s %s" % (self.addonName, self.className), msg, int(lvl))
|
||||||
|
|
||||||
def setAudioSubsPref(self, audio, subs):
|
def GetPlayStats(self):
|
||||||
self.audioPref = audio
|
return self.playStats
|
||||||
self.subsPref = subs
|
|
||||||
|
|
||||||
def hasData(self, data):
|
def currentStackItem(self, stackItems):
|
||||||
if(data == None or len(data) == 0 or data == "None"):
|
# Only for stacked items - stack://
|
||||||
return False
|
xbmcplayer = self.xbmcplayer
|
||||||
|
|
||||||
|
stack = stackItems.replace("stack://", "").split(" , ")
|
||||||
|
position = xbmcplayer.getTime()
|
||||||
|
totalRuntime = 0
|
||||||
|
|
||||||
|
for item in stack:
|
||||||
|
runtime = int(utils.window("%sruntimeticks" % item)) / 10000000
|
||||||
|
# Verify the position compared to the totalRuntime for stacked items processed in loop so far.
|
||||||
|
if position < (runtime + totalRuntime):
|
||||||
|
self.stackElapsed = totalRuntime
|
||||||
|
self.currentFile = item
|
||||||
|
return item
|
||||||
|
else:
|
||||||
|
totalRuntime += runtime
|
||||||
|
|
||||||
|
def onPlayBackStarted( self ):
|
||||||
|
# Will be called when xbmc starts playing a file
|
||||||
|
xbmcplayer = self.xbmcplayer
|
||||||
|
self.stopAll()
|
||||||
|
|
||||||
|
# Get current file - if stack://, get currently playing item
|
||||||
|
currentFile = xbmcplayer.getPlayingFile()
|
||||||
|
if "stack://" in currentFile:
|
||||||
|
self.stackFiles = currentFile
|
||||||
|
currentFile = self.currentStackItem(currentFile)
|
||||||
else:
|
else:
|
||||||
return True
|
self.stackFiles = None
|
||||||
|
self.currentFile = currentFile
|
||||||
|
self.stackElapsed = 0
|
||||||
|
|
||||||
def stopAll(self):
|
self.logMsg("ONPLAYBACK_STARTED: %s" % currentFile, 0)
|
||||||
|
|
||||||
WINDOW = xbmcgui.Window(10000)
|
# We may need to wait for info to be set in kodi monitor
|
||||||
|
itemId = utils.window("%sitem_id" % currentFile)
|
||||||
|
tryCount = 0
|
||||||
|
while not itemId:
|
||||||
|
|
||||||
if(len(self.played_information) == 0):
|
xbmc.sleep(500)
|
||||||
return
|
itemId = utils.window("%sitem_id" % currentFile)
|
||||||
|
if tryCount == 20: # try 20 times or about 10 seconds
|
||||||
|
break
|
||||||
|
else: tryCount += 1
|
||||||
|
|
||||||
self.logMsg("emby Service -> played_information : " + str(self.played_information))
|
else:
|
||||||
|
# Only proceed if an itemId was found.
|
||||||
|
runtime = utils.window("%sruntimeticks" % currentFile)
|
||||||
|
refresh_id = utils.window("%srefresh_id" % currentFile)
|
||||||
|
playMethod = utils.window("%splaymethod" % currentFile)
|
||||||
|
itemType = utils.window("%stype" % currentFile)
|
||||||
|
mapping = utils.window("%sIndexMapping" % currentFile)
|
||||||
|
seekTime = xbmc.Player().getTime()
|
||||||
|
|
||||||
for item_url in self.played_information:
|
self.logMsg("Mapping for subtitles index: %s" % mapping, 2)
|
||||||
data = self.played_information.get(item_url)
|
|
||||||
if (data is not None):
|
|
||||||
self.logMsg("emby Service -> item_url : " + item_url)
|
|
||||||
self.logMsg("emby Service -> item_data : " + str(data))
|
|
||||||
|
|
||||||
runtime = data.get("runtime")
|
# Get playback volume
|
||||||
currentPosition = data.get("currentPosition")
|
volume_query = '{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["volume","muted"]}, "id": 1}'
|
||||||
item_id = data.get("item_id")
|
result = xbmc.executeJSONRPC(volume_query)
|
||||||
refresh_id = data.get("refresh_id")
|
result = json.loads(result)
|
||||||
currentFile = data.get("currentfile")
|
volume = result.get('result').get('volume')
|
||||||
type = data.get("Type")
|
muted = result.get('result').get('muted')
|
||||||
playMethod = data.get('playmethod')
|
|
||||||
|
|
||||||
# Prevent websocket feedback
|
url = "{server}/mediabrowser/Sessions/Playing"
|
||||||
self.WINDOW.setProperty("played_itemId", item_id)
|
postdata = {
|
||||||
|
|
||||||
if(currentPosition != None and self.hasData(runtime)):
|
'QueueableMediaTypes': "Video",
|
||||||
runtimeTicks = int(runtime)
|
'CanSeek': True,
|
||||||
self.logMsg("emby Service -> runtimeticks:" + str(runtimeTicks))
|
'ItemId': itemId,
|
||||||
percentComplete = (currentPosition * 10000000) / runtimeTicks
|
'MediaSourceId': itemId,
|
||||||
markPlayedAt = float(90) / 100
|
'PlayMethod': playMethod,
|
||||||
|
'VolumeLevel': volume,
|
||||||
|
'PositionTicks': int(seekTime * 10000000) - int(self.stackElapsed * 10000000),
|
||||||
|
'IsMuted': muted
|
||||||
|
}
|
||||||
|
|
||||||
self.logMsg("emby Service -> Percent Complete:" + str(percentComplete) + " Mark Played At:" + str(markPlayedAt))
|
# Get the current audio track and subtitles
|
||||||
if percentComplete < markPlayedAt:
|
if playMethod == "Transcode":
|
||||||
# Do not mark as watched
|
# property set in PlayUtils.py
|
||||||
self.WINDOW.setProperty('played_skipWatched', 'true')
|
postdata['AudioStreamIndex'] = utils.window("%sAudioStreamIndex" % currentFile)
|
||||||
|
postdata['SubtitleStreamIndex'] = utils.window("%sSubtitleStreamIndex" % currentFile)
|
||||||
|
|
||||||
self.stopPlayback(data)
|
else:
|
||||||
|
track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties", "params": {"playerid": 1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}'
|
||||||
|
result = xbmc.executeJSONRPC(track_query)
|
||||||
|
result = json.loads(result)
|
||||||
|
|
||||||
offerDelete=False
|
# Audio tracks
|
||||||
if data.get("Type") == "Episode" and utils.settings("offerDeleteTV")=="true":
|
indexAudio = result.get('result', 0)
|
||||||
offerDelete = True
|
if indexAudio:
|
||||||
elif data.get("Type") == "Movie" and utils.settings("offerDeleteMovies")=="true":
|
indexAudio = indexAudio.get('currentaudiostream', {}).get('index', 0)
|
||||||
offerDelete = True
|
# Subtitles tracks
|
||||||
|
indexSubs = result.get('result', 0)
|
||||||
|
if indexSubs:
|
||||||
|
indexSubs = indexSubs.get('currentsubtitle', {}).get('index', 0)
|
||||||
|
# If subtitles are enabled
|
||||||
|
subsEnabled = result.get('result', "")
|
||||||
|
if subsEnabled:
|
||||||
|
subsEnabled = subsEnabled.get('subtitleenabled', "")
|
||||||
|
|
||||||
if percentComplete > .80 and offerDelete == True:
|
# Postdata for the audio and subs tracks
|
||||||
return_value = xbmcgui.Dialog().yesno("Offer Delete", "Delete\n" + data.get("currentfile").split("/")[-1] + "\non Emby Server? ")
|
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
|
||||||
if return_value:
|
postdata['AudioStreamIndex'] = indexAudio + 1
|
||||||
# Delete Kodi entry before Emby
|
|
||||||
listItem = [item_id]
|
|
||||||
LibrarySync().removefromDB(listItem, True)
|
|
||||||
|
|
||||||
# Stop transcoding
|
if subsEnabled and len(xbmc.Player().getAvailableSubtitleStreams()) > 0:
|
||||||
if playMethod == "Transcode":
|
|
||||||
self.logMsg("Transcoding for %s terminated." % item_id)
|
|
||||||
deviceId = self.clientInfo.getMachineId()
|
|
||||||
url = "{server}/mediabrowser/Videos/ActiveEncodings?DeviceId=%s" % deviceId
|
|
||||||
self.doUtils.downloadUrl(url, type="DELETE")
|
|
||||||
|
|
||||||
self.played_information.clear()
|
if mapping:
|
||||||
|
externalIndex = json.loads(mapping)
|
||||||
|
else: # Direct paths scenario
|
||||||
|
externalIndex = ""
|
||||||
|
|
||||||
def stopPlayback(self, data):
|
if externalIndex:
|
||||||
|
# If there's external subtitles added via PlaybackUtils
|
||||||
|
if externalIndex.get(str(indexSubs)):
|
||||||
|
# If the current subtitle is in the mapping
|
||||||
|
postdata['SubtitleStreamIndex'] = externalIndex[str(indexSubs)]
|
||||||
|
else:
|
||||||
|
# Internal subtitle currently selected
|
||||||
|
external = len(externalIndex)
|
||||||
|
postdata['SubtitleStreamIndex'] = indexSubs - external + audioTracks + 1
|
||||||
|
else:
|
||||||
|
# No external subtitles added via PlayUtils
|
||||||
|
postdata['SubtitleStreamIndex'] = indexSubs + audioTracks + 1
|
||||||
|
else:
|
||||||
|
postdata['SubtitleStreamIndex'] = ""
|
||||||
|
|
||||||
self.logMsg("stopPlayback called", 2)
|
# Post playback to server
|
||||||
|
self.logMsg("Sending POST play started.", 1)
|
||||||
|
self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
|
||||||
|
|
||||||
item_id = data.get("item_id")
|
# save data map for updates and position calls
|
||||||
currentPosition = data.get("currentPosition")
|
data = {
|
||||||
positionTicks = int(currentPosition * 10000000)
|
|
||||||
|
|
||||||
url = "{server}/mediabrowser/Sessions/Playing/Stopped"
|
'runtime': runtime,
|
||||||
|
'item_id': itemId,
|
||||||
|
'refresh_id': refresh_id,
|
||||||
|
'currentfile': currentFile,
|
||||||
|
'AudioStreamIndex': postdata['AudioStreamIndex'],
|
||||||
|
'SubtitleStreamIndex': postdata['SubtitleStreamIndex'],
|
||||||
|
'playmethod': playMethod,
|
||||||
|
'Type': itemType,
|
||||||
|
'currentPosition': int(seekTime) - int(self.stackElapsed)
|
||||||
|
}
|
||||||
|
|
||||||
postdata = {
|
self.played_information[currentFile] = data
|
||||||
'ItemId': item_id,
|
self.logMsg("ADDING_FILE: %s" % self.played_information, 1)
|
||||||
'MediaSourceId': item_id,
|
|
||||||
'PositionTicks': positionTicks
|
|
||||||
}
|
|
||||||
|
|
||||||
self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
|
# log some playback stats
|
||||||
|
'''if(itemType != None):
|
||||||
|
if(self.playStats.get(itemType) != None):
|
||||||
|
count = self.playStats.get(itemType) + 1
|
||||||
|
self.playStats[itemType] = count
|
||||||
|
else:
|
||||||
|
self.playStats[itemType] = 1
|
||||||
|
|
||||||
|
if(playMethod != None):
|
||||||
|
if(self.playStats.get(playMethod) != None):
|
||||||
|
count = self.playStats.get(playMethod) + 1
|
||||||
|
self.playStats[playMethod] = count
|
||||||
|
else:
|
||||||
|
self.playStats[playMethod] = 1'''
|
||||||
|
|
||||||
def reportPlayback(self):
|
def reportPlayback(self):
|
||||||
|
|
||||||
self.logMsg("reportPlayback Called", 2)
|
self.logMsg("reportPlayback Called", 2)
|
||||||
xbmcplayer = self.xbmcplayer
|
xbmcplayer = self.xbmcplayer
|
||||||
|
|
||||||
if not xbmcplayer.isPlaying():
|
# Get current file
|
||||||
self.logMsg("reportPlayback: Not playing anything so returning", 0)
|
currentFile = self.currentFile
|
||||||
return
|
|
||||||
|
|
||||||
currentFile = xbmcplayer.getPlayingFile()
|
|
||||||
data = self.played_information.get(currentFile)
|
data = self.played_information.get(currentFile)
|
||||||
|
|
||||||
# only report playback if emby has initiated the playback (item_id has value)
|
# only report playback if emby has initiated the playback (item_id has value)
|
||||||
if data is not None and data.get("item_id") is not None:
|
if data:
|
||||||
|
|
||||||
# Get playback information
|
# Get playback information
|
||||||
item_id = data.get("item_id")
|
itemId = data.get("item_id")
|
||||||
audioindex = data.get("AudioStreamIndex")
|
audioindex = data.get("AudioStreamIndex")
|
||||||
subtitleindex = data.get("SubtitleStreamIndex")
|
subtitleindex = data.get("SubtitleStreamIndex")
|
||||||
playTime = data.get("currentPosition")
|
playTime = data.get("currentPosition")
|
||||||
|
@ -173,14 +237,15 @@ class Player( xbmc.Player ):
|
||||||
volume_query = '{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["volume","muted"]}, "id": 1}'
|
volume_query = '{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["volume","muted"]}, "id": 1}'
|
||||||
result = xbmc.executeJSONRPC(volume_query)
|
result = xbmc.executeJSONRPC(volume_query)
|
||||||
result = json.loads(result)
|
result = json.loads(result)
|
||||||
volume = result.get(u'result').get(u'volume')
|
volume = result.get('result').get('volume')
|
||||||
muted = result.get(u'result').get(u'muted')
|
muted = result.get('result').get('muted')
|
||||||
|
|
||||||
postdata = {
|
postdata = {
|
||||||
|
|
||||||
'QueueableMediaTypes': "Video",
|
'QueueableMediaTypes': "Video",
|
||||||
'CanSeek': True,
|
'CanSeek': True,
|
||||||
'ItemId': item_id,
|
'ItemId': itemId,
|
||||||
'MediaSourceId': item_id,
|
'MediaSourceId': itemId,
|
||||||
'PlayMethod': playMethod,
|
'PlayMethod': playMethod,
|
||||||
'IsPaused': paused,
|
'IsPaused': paused,
|
||||||
'VolumeLevel': volume,
|
'VolumeLevel': volume,
|
||||||
|
@ -188,9 +253,14 @@ class Player( xbmc.Player ):
|
||||||
}
|
}
|
||||||
|
|
||||||
if playTime:
|
if playTime:
|
||||||
postdata['PositionTicks'] = int(playTime * 10000000)
|
postdata['PositionTicks'] = int(playTime * 10000000) - int(self.stackElapsed * 10000000)
|
||||||
|
|
||||||
if playMethod != "Transcode":
|
if playMethod == "Transcode":
|
||||||
|
|
||||||
|
data['AudioStreamIndex'] = audioindex
|
||||||
|
data['SubtitleStreamIndex'] = subtitleindex
|
||||||
|
|
||||||
|
else:
|
||||||
# Get current audio and subtitles track
|
# Get current audio and subtitles track
|
||||||
track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties", "params": {"playerid":1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}'
|
track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties", "params": {"playerid":1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}'
|
||||||
result = xbmc.executeJSONRPC(track_query)
|
result = xbmc.executeJSONRPC(track_query)
|
||||||
|
@ -249,208 +319,127 @@ class Player( xbmc.Player ):
|
||||||
postdata['SubtitleStreamIndex'] = indexSubs
|
postdata['SubtitleStreamIndex'] = indexSubs
|
||||||
data['SubtitleStreamIndex'] = indexSubs
|
data['SubtitleStreamIndex'] = indexSubs
|
||||||
|
|
||||||
else:
|
|
||||||
data['AudioStreamIndex'] = audioindex
|
|
||||||
data['SubtitleStreamIndex'] = subtitleindex
|
|
||||||
|
|
||||||
postdata = json.dumps(postdata)
|
postdata = json.dumps(postdata)
|
||||||
self.logMsg("Report: %s" % postdata, 2)
|
self.logMsg("Report: %s" % postdata, 2)
|
||||||
self.ws.sendProgressUpdate(postdata)
|
self.ws.sendProgressUpdate(postdata)
|
||||||
|
|
||||||
def onPlayBackPaused( self ):
|
def onPlayBackPaused( self ):
|
||||||
currentFile = xbmc.Player().getPlayingFile()
|
|
||||||
self.logMsg("PLAYBACK_PAUSED : " + currentFile,2)
|
currentFile = self.currentFile
|
||||||
if(self.played_information.get(currentFile) != None):
|
self.logMsg("PLAYBACK_PAUSED: %s" % currentFile, 2)
|
||||||
self.played_information[currentFile]["paused"] = "true"
|
|
||||||
|
if self.played_information.get(currentFile):
|
||||||
|
self.played_information[currentFile]['paused'] = "true"
|
||||||
|
|
||||||
self.reportPlayback()
|
self.reportPlayback()
|
||||||
|
|
||||||
def onPlayBackResumed( self ):
|
def onPlayBackResumed( self ):
|
||||||
currentFile = xbmc.Player().getPlayingFile()
|
|
||||||
self.logMsg("PLAYBACK_RESUMED : " + currentFile,2)
|
currentFile = self.currentFile
|
||||||
if(self.played_information.get(currentFile) != None):
|
self.logMsg("PLAYBACK_RESUMED: %s" % currentFile, 2)
|
||||||
self.played_information[currentFile]["paused"] = "false"
|
|
||||||
|
if self.played_information.get(currentFile):
|
||||||
|
self.played_information[currentFile]['paused'] = "false"
|
||||||
|
|
||||||
self.reportPlayback()
|
self.reportPlayback()
|
||||||
|
|
||||||
def onPlayBackSeek( self, time, seekOffset ):
|
def onPlayBackSeek( self, time, seekOffset ):
|
||||||
self.logMsg("PLAYBACK_SEEK",2)
|
|
||||||
# Make position when seeking a bit more accurate
|
|
||||||
try:
|
|
||||||
playTime = xbmc.Player().getTime()
|
|
||||||
currentFile = xbmc.Player().getPlayingFile()
|
|
||||||
if(self.played_information.get(currentFile) != None):
|
|
||||||
self.played_information[currentFile]["currentPosition"] = playTime
|
|
||||||
except: pass
|
|
||||||
self.reportPlayback()
|
|
||||||
|
|
||||||
def onPlayBackStarted( self ):
|
self.logMsg("PLAYBACK_SEEK", 2)
|
||||||
# Will be called when xbmc starts playing a file
|
|
||||||
WINDOW = xbmcgui.Window(10000)
|
|
||||||
xbmcplayer = self.xbmcplayer
|
xbmcplayer = self.xbmcplayer
|
||||||
self.stopAll()
|
# Make position when seeking a bit more accurate
|
||||||
|
position = xbmcplayer.getTime()
|
||||||
|
currentFile = self.currentFile
|
||||||
|
|
||||||
if xbmcplayer.isPlaying():
|
if self.played_information.get(currentFile):
|
||||||
|
self.played_information[currentFile]['currentPosition'] = position
|
||||||
|
|
||||||
currentFile = ""
|
self.reportPlayback()
|
||||||
try:
|
|
||||||
currentFile = xbmcplayer.getPlayingFile()
|
|
||||||
except: pass
|
|
||||||
self.logMsg("onPlayBackStarted: %s" % currentFile, 0)
|
|
||||||
|
|
||||||
# we may need to wait until the info is available
|
|
||||||
item_id = WINDOW.getProperty(currentFile + "item_id")
|
|
||||||
tryCount = 0
|
|
||||||
while(item_id == None or item_id == ""):
|
|
||||||
xbmc.sleep(500)
|
|
||||||
item_id = WINDOW.getProperty(currentFile + "item_id")
|
|
||||||
tryCount += 1
|
|
||||||
if(tryCount == 20): # try 20 times or about 10 seconds
|
|
||||||
return
|
|
||||||
xbmc.sleep(500)
|
|
||||||
|
|
||||||
# grab all the info about this item from the stored windows props
|
|
||||||
# only ever use the win props here, use the data map in all other places
|
|
||||||
runtime = WINDOW.getProperty(currentFile + "runtimeticks")
|
|
||||||
refresh_id = WINDOW.getProperty(currentFile + "refresh_id")
|
|
||||||
playMethod = WINDOW.getProperty(currentFile + "playmethod")
|
|
||||||
itemType = WINDOW.getProperty(currentFile + "type")
|
|
||||||
mapping = WINDOW.getProperty("%sIndexMapping" % currentFile)
|
|
||||||
|
|
||||||
self.logMsg("Mapping for index: %s" % mapping)
|
|
||||||
|
|
||||||
# Get playback volume
|
|
||||||
volume_query = '{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["volume","muted"]}, "id": 1}'
|
|
||||||
result = xbmc.executeJSONRPC(volume_query)
|
|
||||||
result = json.loads(result)
|
|
||||||
volume = result.get(u'result').get(u'volume')
|
|
||||||
muted = result.get(u'result').get(u'muted')
|
|
||||||
|
|
||||||
seekTime = xbmc.Player().getTime()
|
|
||||||
|
|
||||||
url = "{server}/mediabrowser/Sessions/Playing"
|
|
||||||
postdata = {
|
|
||||||
|
|
||||||
'QueueableMediaTypes': "Video",
|
|
||||||
'CanSeek': True,
|
|
||||||
'ItemId': item_id,
|
|
||||||
'MediaSourceId': item_id,
|
|
||||||
'PlayMethod': playMethod,
|
|
||||||
'VolumeLevel': volume,
|
|
||||||
'PositionTicks': int(seekTime * 10000000),
|
|
||||||
'IsMuted': muted
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get the current audio track and subtitles
|
|
||||||
if playMethod == "Transcode":
|
|
||||||
audioindex = WINDOW.getProperty(currentFile + "AudioStreamIndex")
|
|
||||||
subtitleindex = WINDOW.getProperty(currentFile + "SubtitleStreamIndex")
|
|
||||||
postdata['AudioStreamIndex'] = audioindex
|
|
||||||
postdata['SubtitleStreamIndex'] = subtitleindex
|
|
||||||
|
|
||||||
else:
|
|
||||||
track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties", "params": {"playerid": 1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}'
|
|
||||||
result = xbmc.executeJSONRPC(track_query)
|
|
||||||
result = json.loads(result)
|
|
||||||
|
|
||||||
# Audio tracks
|
|
||||||
indexAudio = result.get('result', 0)
|
|
||||||
if indexAudio:
|
|
||||||
indexAudio = indexAudio.get('currentaudiostream', {}).get('index', 0)
|
|
||||||
# Subtitles tracks
|
|
||||||
indexSubs = result.get('result', 0)
|
|
||||||
if indexSubs:
|
|
||||||
indexSubs = indexSubs.get('currentsubtitle', {}).get('index', 0)
|
|
||||||
# If subtitles are enabled
|
|
||||||
subsEnabled = result.get('result', "")
|
|
||||||
if subsEnabled:
|
|
||||||
subsEnabled = subsEnabled.get('subtitleenabled', "")
|
|
||||||
|
|
||||||
# Postdata for the audio and subs tracks
|
|
||||||
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
|
|
||||||
postdata['AudioStreamIndex'] = indexAudio + 1
|
|
||||||
|
|
||||||
if subsEnabled and len(xbmc.Player().getAvailableSubtitleStreams()) > 0:
|
|
||||||
|
|
||||||
if mapping:
|
|
||||||
externalIndex = json.loads(mapping)
|
|
||||||
else: # Direct paths scenario
|
|
||||||
externalIndex = ""
|
|
||||||
|
|
||||||
if externalIndex:
|
|
||||||
# If there's external subtitles added via PlaybackUtils
|
|
||||||
if externalIndex.get(str(indexSubs)):
|
|
||||||
# If the current subtitle is in the mapping
|
|
||||||
postdata['SubtitleStreamIndex'] = externalIndex[str(indexSubs)]
|
|
||||||
else:
|
|
||||||
# Internal subtitle currently selected
|
|
||||||
external = len(externalIndex)
|
|
||||||
postdata['SubtitleStreamIndex'] = indexSubs - external + audioTracks + 1
|
|
||||||
else:
|
|
||||||
# No external subtitles added via PlayUtils
|
|
||||||
postdata['SubtitleStreamIndex'] = indexSubs + audioTracks + 1
|
|
||||||
else:
|
|
||||||
postdata['SubtitleStreamIndex'] = ""
|
|
||||||
|
|
||||||
# Post playback to server
|
|
||||||
self.logMsg("Sending POST play started.", 1)
|
|
||||||
self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
|
|
||||||
|
|
||||||
|
|
||||||
# save data map for updates and position calls
|
|
||||||
data = {
|
|
||||||
'runtime': runtime,
|
|
||||||
'item_id': item_id,
|
|
||||||
'refresh_id': refresh_id,
|
|
||||||
'currentfile': currentFile,
|
|
||||||
'AudioStreamIndex': postdata['AudioStreamIndex'],
|
|
||||||
'SubtitleStreamIndex': postdata['SubtitleStreamIndex'],
|
|
||||||
'playmethod': playMethod,
|
|
||||||
'Type': itemType,
|
|
||||||
'currentPosition': int(seekTime)
|
|
||||||
}
|
|
||||||
self.played_information[currentFile] = data
|
|
||||||
self.logMsg("ADDING_FILE: %s" % self.played_information, 1)
|
|
||||||
|
|
||||||
# log some playback stats
|
|
||||||
if(itemType != None):
|
|
||||||
if(self.playStats.get(itemType) != None):
|
|
||||||
count = self.playStats.get(itemType) + 1
|
|
||||||
self.playStats[itemType] = count
|
|
||||||
else:
|
|
||||||
self.playStats[itemType] = 1
|
|
||||||
|
|
||||||
if(playMethod != None):
|
|
||||||
if(self.playStats.get(playMethod) != None):
|
|
||||||
count = self.playStats.get(playMethod) + 1
|
|
||||||
self.playStats[playMethod] = count
|
|
||||||
else:
|
|
||||||
self.playStats[playMethod] = 1
|
|
||||||
|
|
||||||
# reset in progress position
|
|
||||||
#self.reportPlayback()
|
|
||||||
|
|
||||||
def GetPlayStats(self):
|
|
||||||
return self.playStats
|
|
||||||
|
|
||||||
def onPlayBackEnded( self ):
|
|
||||||
# Will be called when xbmc stops playing a file
|
|
||||||
self.logMsg("onPlayBackEnded", 0)
|
|
||||||
|
|
||||||
#workaround when strm files are launched through the addon - mark watched when finished playing
|
|
||||||
#TODO --> mark watched when 95% is played of the file
|
|
||||||
WINDOW = xbmcgui.Window( 10000 )
|
|
||||||
if WINDOW.getProperty("virtualstrm") != "":
|
|
||||||
try:
|
|
||||||
id = WINDOW.getProperty("virtualstrm")
|
|
||||||
type = WINDOW.getProperty("virtualstrmtype")
|
|
||||||
watchedurl = "{server}/mediabrowser/Users/{UserId}/PlayedItems/%s" % id
|
|
||||||
self.doUtils.downloadUrl(watchedurl, postBody="", type="POST")
|
|
||||||
librarySync.updatePlayCount(id)
|
|
||||||
except: pass
|
|
||||||
WINDOW.clearProperty("virtualstrm")
|
|
||||||
|
|
||||||
self.stopAll()
|
|
||||||
|
|
||||||
def onPlayBackStopped( self ):
|
def onPlayBackStopped( self ):
|
||||||
# Will be called when user stops xbmc playing a file
|
# Will be called when user stops xbmc playing a file
|
||||||
self.logMsg("onPlayBackStopped", 0)
|
self.logMsg("ONPLAYBACK_STOPPED", 2)
|
||||||
self.stopAll()
|
self.stopAll()
|
||||||
|
|
||||||
|
def onPlayBackEnded( self ):
|
||||||
|
# Will be called when xbmc stops playing a file
|
||||||
|
self.logMsg("ONPLAYBACK_ENDED", 2)
|
||||||
|
self.stopAll()
|
||||||
|
|
||||||
|
def stopAll(self):
|
||||||
|
|
||||||
|
if not self.played_information:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.logMsg("Played_information: %s" % str(self.played_information), 1)
|
||||||
|
# Process each items
|
||||||
|
for item in self.played_information:
|
||||||
|
|
||||||
|
data = self.played_information.get(item)
|
||||||
|
if data:
|
||||||
|
|
||||||
|
self.logMsg("Item path: %s" % item, 1)
|
||||||
|
self.logMsg("Item data: %s" % str(data), 1)
|
||||||
|
|
||||||
|
runtime = data.get('runtime')
|
||||||
|
currentPosition = data.get('currentPosition')
|
||||||
|
itemId = data.get('item_id')
|
||||||
|
refresh_id = data.get('refresh_id')
|
||||||
|
currentFile = data.get('currentfile')
|
||||||
|
type = data.get('Type')
|
||||||
|
playMethod = data.get('playmethod')
|
||||||
|
|
||||||
|
if currentPosition and runtime:
|
||||||
|
self.logMsg("RuntimeTicks: %s" % runtime, 1)
|
||||||
|
percentComplete = (currentPosition * 10000000) / int(runtime)
|
||||||
|
markPlayedAt = float(utils.settings('markPlayed')) / 100
|
||||||
|
|
||||||
|
self.logMsg("Percent complete: %s Mark played at: %s" % (percentComplete, markPlayedAt))
|
||||||
|
if percentComplete < markPlayedAt:
|
||||||
|
# Do not mark as watched for Kodi Monitor
|
||||||
|
utils.window('played_skipWatched', value="true")
|
||||||
|
|
||||||
|
self.stopPlayback(data)
|
||||||
|
offerDelete = False
|
||||||
|
|
||||||
|
if type == "Episode" and utils.settings('offerDeleteTV') == "true":
|
||||||
|
offerDelete = True
|
||||||
|
|
||||||
|
elif type == "Movie" and utils.settings('offerDeleteMovies') == "true":
|
||||||
|
offerDelete = True
|
||||||
|
|
||||||
|
if percentComplete >= markPlayedAt and offerDelete:
|
||||||
|
# Item could be stacked, so only offer to delete the main item.
|
||||||
|
if not self.stackFiles or itemId == utils.window('%sitem_id' % self.stackFiles):
|
||||||
|
return_value = xbmcgui.Dialog().yesno("Offer Delete", "Delete %s" % data.get('currentfile').split("/")[-1], "on Emby Server?")
|
||||||
|
if return_value:
|
||||||
|
# Delete Kodi entry before Emby
|
||||||
|
listItem = [itemId]
|
||||||
|
LibrarySync().removefromDB(listItem, True)
|
||||||
|
|
||||||
|
# Stop transcoding
|
||||||
|
if playMethod == "Transcode":
|
||||||
|
self.logMsg("Transcoding for %s terminated." % itemId, 1)
|
||||||
|
deviceId = self.clientInfo.getMachineId()
|
||||||
|
url = "{server}/mediabrowser/Videos/ActiveEncodings?DeviceId=%s" % deviceId
|
||||||
|
self.doUtils.downloadUrl(url, type="DELETE")
|
||||||
|
|
||||||
|
self.played_information.clear()
|
||||||
|
|
||||||
|
def stopPlayback(self, data):
|
||||||
|
|
||||||
|
self.logMsg("stopPlayback called", 2)
|
||||||
|
|
||||||
|
itemId = data.get('item_id')
|
||||||
|
currentPosition = data.get('currentPosition')
|
||||||
|
positionTicks = int(currentPosition * 10000000)
|
||||||
|
|
||||||
|
url = "{server}/mediabrowser/Sessions/Playing/Stopped"
|
||||||
|
postdata = {
|
||||||
|
|
||||||
|
'ItemId': itemId,
|
||||||
|
'MediaSourceId': itemId,
|
||||||
|
'PositionTicks': positionTicks
|
||||||
|
}
|
||||||
|
|
||||||
|
self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
|
|
@ -35,8 +35,10 @@
|
||||||
<setting id="smbusername" type="text" label="30007" default="" visible="true" enable="true" />
|
<setting id="smbusername" type="text" label="30007" default="" visible="true" enable="true" />
|
||||||
<setting id="smbpassword" type="text" label="30008" default="" option="hidden" visible="true" enable="true" />
|
<setting id="smbpassword" type="text" label="30008" default="" option="hidden" visible="true" enable="true" />
|
||||||
<setting type="sep" />
|
<setting type="sep" />
|
||||||
|
<setting id="disableCinema" type="bool" label="Disable Emby cinema mode" default="false" visible="true" enable="true" />
|
||||||
|
<setting id="markPlayed" label="Mark watched at" type="slider" default="90" range="60,5,100" option="percent" visible="true" enable="true" />
|
||||||
<setting id="offerDelete" type="bool" label="30114" visible="true" enable="true" default="false" />
|
<setting id="offerDelete" type="bool" label="30114" visible="true" enable="true" default="false" />
|
||||||
<setting id="offerDeleteTV" type="bool" label=" 30115" visible="eq(-1,true)" enable="true" default="false" />
|
<setting id="offerDeleteTV" type="bool" label="30115" visible="eq(-1,true)" enable="true" default="false" />
|
||||||
<setting id="offerDeleteMovies" type="bool" label="30116" visible="eq(-2,true)" enable="true" default="false" />
|
<setting id="offerDeleteMovies" type="bool" label="30116" visible="eq(-2,true)" enable="true" default="false" />
|
||||||
<setting id="resumeJumpBack" type="slider" label="On Resume Jump Back Seconds" default="10" range="0,1,120" option="int" visible="true" enable="true" />
|
<setting id="resumeJumpBack" type="slider" label="On Resume Jump Back Seconds" default="10" range="0,1,120" option="int" visible="true" enable="true" />
|
||||||
<setting id="playFromStream" type="bool" label="30002" visible="true" enable="true" default="false" />
|
<setting id="playFromStream" type="bool" label="30002" visible="true" enable="true" default="false" />
|
||||||
|
|
|
@ -140,7 +140,7 @@ class Service():
|
||||||
# Update and report progress
|
# Update and report progress
|
||||||
playTime = xbmc.Player().getTime()
|
playTime = xbmc.Player().getTime()
|
||||||
totalTime = xbmc.Player().getTotalTime()
|
totalTime = xbmc.Player().getTotalTime()
|
||||||
currentFile = xbmc.Player().getPlayingFile()
|
currentFile = player.currentFile
|
||||||
|
|
||||||
# Update positionticks
|
# Update positionticks
|
||||||
if player.played_information.get(currentFile) is not None:
|
if player.played_information.get(currentFile) is not None:
|
||||||
|
|
Loading…
Reference in a new issue