Overhaul Plex Companion
This commit is contained in:
parent
125daea6ef
commit
0f14019e5b
15 changed files with 355 additions and 268 deletions
|
@ -79,6 +79,7 @@ class PlexAPI():
|
||||||
self.deviceName = client.getDeviceName()
|
self.deviceName = client.getDeviceName()
|
||||||
self.plexversion = client.getVersion()
|
self.plexversion = client.getVersion()
|
||||||
self.platform = client.getPlatform()
|
self.platform = client.getPlatform()
|
||||||
|
self.user = utils.window('plex_username')
|
||||||
self.userId = utils.window('emby_currUser')
|
self.userId = utils.window('emby_currUser')
|
||||||
self.token = utils.window('emby_accessToken%s' % self.userId)
|
self.token = utils.window('emby_accessToken%s' % self.userId)
|
||||||
self.server = utils.window('emby_server%s' % self.userId)
|
self.server = utils.window('emby_server%s' % self.userId)
|
||||||
|
@ -782,6 +783,8 @@ class PlexAPI():
|
||||||
"""
|
"""
|
||||||
# Get addon infos
|
# Get addon infos
|
||||||
xargs = {
|
xargs = {
|
||||||
|
'Accept': '*/*',
|
||||||
|
'Connection': 'keep-alive',
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
# "Access-Control-Allow-Origin": "*",
|
# "Access-Control-Allow-Origin": "*",
|
||||||
'X-Plex-Language': 'en',
|
'X-Plex-Language': 'en',
|
||||||
|
@ -795,6 +798,9 @@ class PlexAPI():
|
||||||
'X-Plex-Version': self.plexversion,
|
'X-Plex-Version': self.plexversion,
|
||||||
'X-Plex-Client-Identifier': self.clientId,
|
'X-Plex-Client-Identifier': self.clientId,
|
||||||
'X-Plex-Provides': 'player',
|
'X-Plex-Provides': 'player',
|
||||||
|
'X-Plex-Username': self.user,
|
||||||
|
'X-Plex-Client-Capabilities': 'protocols=shoutcast,http-video;videoDecoders=h264{profile:high&resolution:1080&level:51};audioDecoders=mp3,aac,dts{bitrate:800000&channels:8},ac3{bitrate:800000&channels:8}',
|
||||||
|
'X-Plex-Client-Profile-Extra': 'add-transcode-target-audio-codec(type=videoProfile&context=streaming&protocol=*&audioCodec=dca,ac3)',
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.token:
|
if self.token:
|
||||||
|
@ -1374,6 +1380,7 @@ class API():
|
||||||
self.part = 0
|
self.part = 0
|
||||||
self.clientinfo = clientinfo.ClientInfo()
|
self.clientinfo = clientinfo.ClientInfo()
|
||||||
self.clientId = self.clientinfo.getDeviceId()
|
self.clientId = self.clientinfo.getDeviceId()
|
||||||
|
self.user = utils.window('plex_username')
|
||||||
self.userId = utils.window('emby_currUser')
|
self.userId = utils.window('emby_currUser')
|
||||||
self.server = utils.window('emby_server%s' % self.userId)
|
self.server = utils.window('emby_server%s' % self.userId)
|
||||||
self.token = utils.window('emby_accessToken%s' % self.userId)
|
self.token = utils.window('emby_accessToken%s' % self.userId)
|
||||||
|
@ -1591,6 +1598,9 @@ class API():
|
||||||
genre.append(child.attrib['tag'])
|
genre.append(child.attrib['tag'])
|
||||||
return genre
|
return genre
|
||||||
|
|
||||||
|
def getGuid(self):
|
||||||
|
return self.item.attrib.get('guid')
|
||||||
|
|
||||||
def getProvider(self, providername=None):
|
def getProvider(self, providername=None):
|
||||||
"""
|
"""
|
||||||
providername: e.g. 'imdb'
|
providername: e.g. 'imdb'
|
||||||
|
@ -2053,8 +2063,7 @@ class API():
|
||||||
allartworks['Primary'] = artwork
|
allartworks['Primary'] = artwork
|
||||||
return allartworks
|
return allartworks
|
||||||
|
|
||||||
def getTranscodeVideoPath(self, action, quality={}, subtitle={},
|
def getTranscodeVideoPath(self, action, quality={}):
|
||||||
audioboost=None, options={}):
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
To be called on a VIDEO level of PMS xml response!
|
To be called on a VIDEO level of PMS xml response!
|
||||||
|
@ -2070,38 +2079,18 @@ class API():
|
||||||
'maxVideoBitrate': e.g. '2000' (in kbits)
|
'maxVideoBitrate': e.g. '2000' (in kbits)
|
||||||
}
|
}
|
||||||
(one or several of these options)
|
(one or several of these options)
|
||||||
subtitle {'selected', 'dontBurnIn', 'size'}
|
|
||||||
audioboost e.g. 100
|
|
||||||
options dict() of PlexConnect-options as received from aTV
|
|
||||||
Output:
|
Output:
|
||||||
final URL to pull in PMS transcoder
|
final URL to pull in PMS transcoder
|
||||||
|
|
||||||
TODO: mediaIndex
|
TODO: mediaIndex
|
||||||
"""
|
"""
|
||||||
# Set Client capabilities
|
|
||||||
clientArgs = {
|
|
||||||
'X-Plex-Client-Capabilities':
|
|
||||||
'protocols='
|
|
||||||
'shoutcast,'
|
|
||||||
'http-video;'
|
|
||||||
'videoDecoders='
|
|
||||||
'h264{profile:high&resolution:1080&level:51};'
|
|
||||||
'audioDecoders='
|
|
||||||
'mp3,aac,dts{bitrate:800000&channels:8},'
|
|
||||||
'ac3{bitrate:800000&channels:8}',
|
|
||||||
'X-Plex-Client-Profile-Extra':
|
|
||||||
'add-transcode-target-audio-codec'
|
|
||||||
'(type=videoProfile&'
|
|
||||||
'context=streaming&'
|
|
||||||
'protocol=*&'
|
|
||||||
'audioCodec=dca,ac3)'
|
|
||||||
}
|
|
||||||
xargs = PlexAPI().getXArgsDeviceInfo()
|
|
||||||
|
|
||||||
|
xargs = PlexAPI().getXArgsDeviceInfo()
|
||||||
# For DirectPlay, path/key of PART is needed
|
# For DirectPlay, path/key of PART is needed
|
||||||
if action == "DirectPlay":
|
if action == "DirectPlay":
|
||||||
path = self.item[0][self.part].attrib['key']
|
path = self.item[0][self.part].attrib['key']
|
||||||
url = self.server + path
|
url = self.server + path
|
||||||
|
# e.g. Trailers already feature an '?'!
|
||||||
if '?' in url:
|
if '?' in url:
|
||||||
url += '&' + urlencode(xargs)
|
url += '&' + urlencode(xargs)
|
||||||
else:
|
else:
|
||||||
|
@ -2111,49 +2100,20 @@ class API():
|
||||||
# For Direct Streaming or Transcoding
|
# For Direct Streaming or Transcoding
|
||||||
# Path/key to VIDEO item of xml PMS response is needed, not part
|
# Path/key to VIDEO item of xml PMS response is needed, not part
|
||||||
path = self.item.attrib['key']
|
path = self.item.attrib['key']
|
||||||
# transcodePath = self.server + \
|
|
||||||
# '/video/:/transcode/universal/start.m3u8?'
|
|
||||||
transcodePath = self.server + \
|
transcodePath = self.server + \
|
||||||
'/video/:/transcode/universal/start.mkv?'
|
'/video/:/transcode/universal/start.m3u8?'
|
||||||
# args = {
|
|
||||||
# 'path': path,
|
|
||||||
# 'mediaIndex': 0, # Probably refering to XML reply sheme
|
|
||||||
# 'partIndex': self.part,
|
|
||||||
# 'protocol': 'hls', # seen in the wild: 'dash', 'http', 'hls'
|
|
||||||
# 'offset': 0, # Resume point
|
|
||||||
# 'fastSeek': 1
|
|
||||||
# }
|
|
||||||
args = {
|
args = {
|
||||||
'copyts': 1,
|
'copyts': 1,
|
||||||
'path': path,
|
'path': path,
|
||||||
'mediaIndex': 0, # Probably refering to XML reply sheme
|
'mediaIndex': 0, # Probably refering to XML reply sheme
|
||||||
'partIndex': self.part,
|
'partIndex': self.part,
|
||||||
'protocol': 'http', # seen in the wild: 'dash', 'http', 'hls'
|
'protocol': 'hls', # seen in the wild: 'dash', 'http', 'hls'
|
||||||
'session': self.clientId,
|
'session': self.clientId,
|
||||||
'offset': 0, # Resume point
|
# 'offset': 0, # Resume point
|
||||||
'fastSeek': 1
|
'fastSeek': 1
|
||||||
}
|
}
|
||||||
# All the settings
|
|
||||||
if subtitle:
|
|
||||||
argsUpdate = {
|
|
||||||
'subtitles': 'burn',
|
|
||||||
'subtitleSize': subtitle['size'], # E.g. 100
|
|
||||||
'skipSubtitles': subtitle['dontBurnIn'] # '1': shut off PMS
|
|
||||||
}
|
|
||||||
self.logMsg(
|
|
||||||
"Subtitle: selected %s, dontBurnIn %s, size %s"
|
|
||||||
% (subtitle['selected'], subtitle['dontBurnIn'],
|
|
||||||
subtitle['size']),
|
|
||||||
1
|
|
||||||
)
|
|
||||||
args.update(argsUpdate)
|
|
||||||
if audioboost:
|
|
||||||
argsUpdate = {
|
|
||||||
'audioBoost': audioboost
|
|
||||||
}
|
|
||||||
self.logMsg("audioboost: %s" % audioboost, 1)
|
|
||||||
args.update(argsUpdate)
|
|
||||||
|
|
||||||
|
# Currently not used!
|
||||||
if action == "DirectStream":
|
if action == "DirectStream":
|
||||||
argsUpdate = {
|
argsUpdate = {
|
||||||
'directPlay': '0',
|
'directPlay': '0',
|
||||||
|
@ -2170,7 +2130,6 @@ class API():
|
||||||
args.update(argsUpdate)
|
args.update(argsUpdate)
|
||||||
|
|
||||||
url = transcodePath + \
|
url = transcodePath + \
|
||||||
urlencode(clientArgs) + '&' + \
|
|
||||||
urlencode(xargs) + '&' + \
|
urlencode(xargs) + '&' + \
|
||||||
urlencode(args)
|
urlencode(args)
|
||||||
return url
|
return url
|
||||||
|
@ -2185,20 +2144,17 @@ class API():
|
||||||
except (TypeError, KeyError, IndexError):
|
except (TypeError, KeyError, IndexError):
|
||||||
return
|
return
|
||||||
|
|
||||||
itemid = self.getRatingKey()
|
|
||||||
kodiindex = 0
|
kodiindex = 0
|
||||||
for stream in mediastreams:
|
for stream in mediastreams:
|
||||||
index = stream.attrib['id']
|
index = stream.attrib['id']
|
||||||
# Since Emby returns all possible tracks together, have to pull only external subtitles.
|
# Since Emby returns all possible tracks together, have to pull only external subtitles.
|
||||||
|
key = stream.attrib.get('key')
|
||||||
# IsTextSubtitleStream if true, is available to download from emby.
|
# IsTextSubtitleStream if true, is available to download from emby.
|
||||||
if (stream.attrib.get('streamType', '') == "3" and
|
if stream.attrib.get('streamType') == "3" and key:
|
||||||
stream.attrib.get('format', '')):
|
|
||||||
|
|
||||||
# Direct stream
|
# Direct stream
|
||||||
# PLEX: TODO!!
|
url = ("%s%s" % (self.server, key))
|
||||||
url = ("%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
|
url = self.addPlexCredentialsToUrl(url)
|
||||||
% (self.server, itemid, itemid, index))
|
|
||||||
|
|
||||||
# map external subtitles for mapping
|
# map external subtitles for mapping
|
||||||
mapping[kodiindex] = index
|
mapping[kodiindex] = index
|
||||||
externalsubs.append(url)
|
externalsubs.append(url)
|
||||||
|
|
|
@ -83,10 +83,9 @@ class PlexCompanion(threading.Thread):
|
||||||
|
|
||||||
if not is_running:
|
if not is_running:
|
||||||
self.logMsg("PleXBMC Helper has started", 0)
|
self.logMsg("PleXBMC Helper has started", 0)
|
||||||
|
|
||||||
is_running = True
|
is_running = True
|
||||||
if message_count % 1 == 0:
|
|
||||||
subscribers.subMgr.notify()
|
subscribers.subMgr.notify()
|
||||||
settings['serverList'] = self.client.getServerList()
|
settings['serverList'] = self.client.getServerList()
|
||||||
except:
|
except:
|
||||||
self.logMsg("Error in loop, continuing anyway", 1)
|
self.logMsg("Error in loop, continuing anyway", 1)
|
||||||
|
|
|
@ -3,8 +3,10 @@ from urllib import urlencode
|
||||||
from ast import literal_eval
|
from ast import literal_eval
|
||||||
from urlparse import urlparse, parse_qs
|
from urlparse import urlparse, parse_qs
|
||||||
import re
|
import re
|
||||||
|
import json
|
||||||
|
|
||||||
from xbmcaddon import Addon
|
from xbmcaddon import Addon
|
||||||
|
import xbmc
|
||||||
|
|
||||||
import downloadutils
|
import downloadutils
|
||||||
from utils import logMsg, settings
|
from utils import logMsg, settings
|
||||||
|
@ -85,7 +87,9 @@ def LiteralEval(string):
|
||||||
def GetMethodFromPlexType(plexType):
|
def GetMethodFromPlexType(plexType):
|
||||||
methods = {
|
methods = {
|
||||||
'movie': 'add_update',
|
'movie': 'add_update',
|
||||||
'episode': 'add_updateEpisode'
|
'episode': 'add_updateEpisode',
|
||||||
|
'show': 'add_update',
|
||||||
|
'season': 'add_updateSeason'
|
||||||
}
|
}
|
||||||
return methods[plexType]
|
return methods[plexType]
|
||||||
|
|
||||||
|
@ -107,6 +111,15 @@ def EmbyItemtypes():
|
||||||
return ['Movie', 'Series', 'Season', 'Episode']
|
return ['Movie', 'Series', 'Season', 'Episode']
|
||||||
|
|
||||||
|
|
||||||
|
def SelectStreams(url, args):
|
||||||
|
"""
|
||||||
|
Does a PUT request to tell the PMS what audio and subtitle streams we have
|
||||||
|
chosen.
|
||||||
|
"""
|
||||||
|
downloadutils.DownloadUtils().downloadUrl(
|
||||||
|
url + '?' + urlencode(args), type='PUT')
|
||||||
|
|
||||||
|
|
||||||
def GetPlayQueue(playQueueID):
|
def GetPlayQueue(playQueueID):
|
||||||
"""
|
"""
|
||||||
Fetches the PMS playqueue with the playQueueID as an XML
|
Fetches the PMS playqueue with the playQueueID as an XML
|
||||||
|
@ -308,3 +321,12 @@ def GetPlexPlaylist(itemid, librarySectionUUID, mediatype='movie'):
|
||||||
logMsg("Error retrieving metadata for %s" % url, -1)
|
logMsg("Error retrieving metadata for %s" % url, -1)
|
||||||
return None
|
return None
|
||||||
return xml
|
return xml
|
||||||
|
|
||||||
|
|
||||||
|
def getPlexRepeat(kodiRepeat):
|
||||||
|
plexRepeat = {
|
||||||
|
'off': '0',
|
||||||
|
'one': '1',
|
||||||
|
'all': '2' # does this work?!?
|
||||||
|
}
|
||||||
|
return plexRepeat.get(kodiRepeat)
|
||||||
|
|
|
@ -32,7 +32,7 @@ requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||||
class DownloadUtils():
|
class DownloadUtils():
|
||||||
|
|
||||||
# Borg - multiple instances, shared state
|
# Borg - multiple instances, shared state
|
||||||
# _shared_state = {}
|
_shared_state = {}
|
||||||
clientInfo = clientinfo.ClientInfo()
|
clientInfo = clientinfo.ClientInfo()
|
||||||
|
|
||||||
# Requests session
|
# Requests session
|
||||||
|
@ -41,8 +41,7 @@ class DownloadUtils():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
# self.__dict__ = self._shared_state
|
self.__dict__ = self._shared_state
|
||||||
pass
|
|
||||||
|
|
||||||
def setUsername(self, username):
|
def setUsername(self, username):
|
||||||
# Reserved for userclient only
|
# Reserved for userclient only
|
||||||
|
@ -215,9 +214,12 @@ class DownloadUtils():
|
||||||
# For Plex Companion
|
# For Plex Companion
|
||||||
elif type == "POSTXML":
|
elif type == "POSTXML":
|
||||||
r = s.post(url, postBody, timeout=timeout, headers=header)
|
r = s.post(url, postBody, timeout=timeout, headers=header)
|
||||||
|
elif type == "PUT":
|
||||||
|
r = s.put(url, timeout=timeout, headers=header)
|
||||||
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# request session does not exists
|
# request session does not exists
|
||||||
|
self.logMsg("Request session does not exist: start one", 1)
|
||||||
# Get user information
|
# Get user information
|
||||||
self.userId = utils.window('emby_currUser')
|
self.userId = utils.window('emby_currUser')
|
||||||
self.server = utils.window('emby_server%s' % self.userId)
|
self.server = utils.window('emby_server%s' % self.userId)
|
||||||
|
@ -270,6 +272,13 @@ class DownloadUtils():
|
||||||
cert=cert,
|
cert=cert,
|
||||||
verify=verifyssl)
|
verify=verifyssl)
|
||||||
|
|
||||||
|
elif type == "PUT":
|
||||||
|
r = requests.put(url,
|
||||||
|
json=postBody,
|
||||||
|
headers=header,
|
||||||
|
timeout=timeout,
|
||||||
|
cert=cert,
|
||||||
|
verify=verifyssl)
|
||||||
# If user is not authenticated
|
# If user is not authenticated
|
||||||
elif not authenticate:
|
elif not authenticate:
|
||||||
|
|
||||||
|
@ -301,6 +310,12 @@ class DownloadUtils():
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
verify=verifyssl)
|
verify=verifyssl)
|
||||||
|
|
||||||
|
elif type == "PUT":
|
||||||
|
r = requests.put(url,
|
||||||
|
json=postBody,
|
||||||
|
headers=header,
|
||||||
|
timeout=timeout,
|
||||||
|
verify=verifyssl)
|
||||||
##### THE RESPONSE #####
|
##### THE RESPONSE #####
|
||||||
# self.logMsg(r.url, 2)
|
# self.logMsg(r.url, 2)
|
||||||
if r.status_code == 204:
|
if r.status_code == 204:
|
||||||
|
|
|
@ -6,7 +6,6 @@ import json
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import urlparse
|
import urlparse
|
||||||
import re
|
|
||||||
|
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcaddon
|
import xbmcaddon
|
||||||
|
@ -27,7 +26,6 @@ import api
|
||||||
|
|
||||||
import PlexAPI
|
import PlexAPI
|
||||||
import PlexFunctions
|
import PlexFunctions
|
||||||
import embydb_functions
|
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -81,11 +79,11 @@ def PassPlaylist(xml, resume=None):
|
||||||
"""
|
"""
|
||||||
# Set window properties to make them available later for other threads
|
# Set window properties to make them available later for other threads
|
||||||
windowArgs = [
|
windowArgs = [
|
||||||
|
# 'containerKey'
|
||||||
'playQueueID',
|
'playQueueID',
|
||||||
'playQueueVersion',
|
'playQueueVersion']
|
||||||
'playQueueShuffled']
|
|
||||||
for arg in windowArgs:
|
for arg in windowArgs:
|
||||||
utils.window(arg, xml.attrib.get(arg, ''))
|
utils.window(arg, value=xml.attrib.get(arg))
|
||||||
|
|
||||||
# Get resume point
|
# Get resume point
|
||||||
resume1 = PlexFunctions.ConvertPlexToKodiTime(utils.IntFromStr(
|
resume1 = PlexFunctions.ConvertPlexToKodiTime(utils.IntFromStr(
|
||||||
|
|
|
@ -63,7 +63,7 @@ class ThreadedGetMetadata(threading.Thread):
|
||||||
continue
|
continue
|
||||||
# Download Metadata
|
# Download Metadata
|
||||||
plexXML = PlexFunctions.GetPlexMetadata(updateItem['itemId'])
|
plexXML = PlexFunctions.GetPlexMetadata(updateItem['itemId'])
|
||||||
if not plexXML:
|
if plexXML is None:
|
||||||
# Did not receive a valid XML - skip that item for now
|
# Did not receive a valid XML - skip that item for now
|
||||||
queue.task_done()
|
queue.task_done()
|
||||||
continue
|
continue
|
||||||
|
@ -116,7 +116,6 @@ class ThreadedProcessMetadata(threading.Thread):
|
||||||
# grabs item from queue
|
# grabs item from queue
|
||||||
try:
|
try:
|
||||||
updateItem = queue.get(block=False)
|
updateItem = queue.get(block=False)
|
||||||
# Empty queue
|
|
||||||
except Queue.Empty:
|
except Queue.Empty:
|
||||||
continue
|
continue
|
||||||
# Do the work; lock to be sure we've only got 1 Thread
|
# Do the work; lock to be sure we've only got 1 Thread
|
||||||
|
@ -306,7 +305,7 @@ class LibrarySync(threading.Thread):
|
||||||
# Populate self.updatelist
|
# Populate self.updatelist
|
||||||
self.GetUpdatelist(items,
|
self.GetUpdatelist(items,
|
||||||
PlexFunctions.GetItemClassFromType(plexType),
|
PlexFunctions.GetItemClassFromType(plexType),
|
||||||
PlexFunctions.GetItemClassFromType(plexType),
|
PlexFunctions.GetMethodFromPlexType(plexType),
|
||||||
view['name'],
|
view['name'],
|
||||||
view['id'])
|
view['id'])
|
||||||
# Process self.updatelist
|
# Process self.updatelist
|
||||||
|
|
|
@ -72,8 +72,11 @@ class PlaybackUtils():
|
||||||
# Close DB
|
# Close DB
|
||||||
embyconn.close()
|
embyconn.close()
|
||||||
|
|
||||||
|
self.logMsg("Playlist size now: %s" % self.playlist.size(), 1)
|
||||||
# Kick off playback
|
# Kick off playback
|
||||||
if startPlayer:
|
if startPlayer:
|
||||||
|
self.logMsg("Starting up a new xbmc.Player() instance", 1)
|
||||||
|
self.logMsg("Starting position: %s" % self.startPos, 1)
|
||||||
Player = xbmc.Player()
|
Player = xbmc.Player()
|
||||||
Player.play(self.playlist, startpos=self.startPos)
|
Player.play(self.playlist, startpos=self.startPos)
|
||||||
if resume:
|
if resume:
|
||||||
|
@ -82,6 +85,7 @@ class PlaybackUtils():
|
||||||
except:
|
except:
|
||||||
self.logMsg("Error, could not resume", -1)
|
self.logMsg("Error, could not resume", -1)
|
||||||
else:
|
else:
|
||||||
|
self.logMsg("xbmc.Player() instance already up", 1)
|
||||||
# Delete the last playlist item because we have added it already
|
# Delete the last playlist item because we have added it already
|
||||||
filename = self.playlist[-1].getfilename()
|
filename = self.playlist[-1].getfilename()
|
||||||
self.playlist.remove(filename)
|
self.playlist.remove(filename)
|
||||||
|
@ -131,8 +135,8 @@ class PlaybackUtils():
|
||||||
if playmethod == "Transcode":
|
if playmethod == "Transcode":
|
||||||
utils.window('emby_%s.playmethod' % playurl, clear=True)
|
utils.window('emby_%s.playmethod' % playurl, clear=True)
|
||||||
playurl = playutils.audioSubsPref(
|
playurl = playutils.audioSubsPref(
|
||||||
playurl, listitem, part=counter)
|
listitem, playurl, part=counter)
|
||||||
utils.window('emby_%s.playmethod' % playurl, value="Transcode")
|
utils.window('emby_%s.playmethod' % playurl, "Transcode")
|
||||||
|
|
||||||
self.setProperties(playurl, listitem)
|
self.setProperties(playurl, listitem)
|
||||||
# Update the playurl to the PMS xml response (hence no loop)
|
# Update the playurl to the PMS xml response (hence no loop)
|
||||||
|
@ -161,12 +165,15 @@ class PlaybackUtils():
|
||||||
utils.window('%s.itemid' % embyitem, value=itemid)
|
utils.window('%s.itemid' % embyitem, value=itemid)
|
||||||
|
|
||||||
# We need to keep track of playQueueItemIDs for Plex Companion
|
# We need to keep track of playQueueItemIDs for Plex Companion
|
||||||
playQueueItemID = self.API.GetPlayQueueItemID()
|
|
||||||
utils.window(
|
utils.window(
|
||||||
'plex_%s.playQueueItemID' % playurl, playQueueItemID)
|
'plex_%s.playQueueItemID'
|
||||||
|
% playurl, self.API.GetPlayQueueItemID())
|
||||||
utils.window(
|
utils.window(
|
||||||
'plex_%s.playlistPosition'
|
'plex_%s.playlistPosition'
|
||||||
% playurl, str(self.currentPosition))
|
% playurl, str(self.currentPosition))
|
||||||
|
utils.window(
|
||||||
|
'plex_%s.guid'
|
||||||
|
% playurl, self.API.getGuid())
|
||||||
|
|
||||||
if itemtype == "episode":
|
if itemtype == "episode":
|
||||||
utils.window('%s.refreshid' % embyitem,
|
utils.window('%s.refreshid' % embyitem,
|
||||||
|
|
|
@ -38,6 +38,14 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
self.logMsg("Starting playback monitor.", 2)
|
self.logMsg("Starting playback monitor.", 2)
|
||||||
|
|
||||||
|
# Should we start notification or is this done by Plex Companion?
|
||||||
|
self.doNotify = False if (utils.settings('plexCompanion') == 'true') \
|
||||||
|
else True
|
||||||
|
if self.doNotify:
|
||||||
|
self.logMsg("PMS notifications not done by Plex Companion", 2)
|
||||||
|
else:
|
||||||
|
self.logMsg("PMS notifications done by Plex Companion", 2)
|
||||||
|
|
||||||
def GetPlayStats(self):
|
def GetPlayStats(self):
|
||||||
return self.playStats
|
return self.playStats
|
||||||
|
|
||||||
|
@ -68,7 +76,8 @@ class Player(xbmc.Player):
|
||||||
if currentFile:
|
if currentFile:
|
||||||
|
|
||||||
self.currentFile = currentFile
|
self.currentFile = currentFile
|
||||||
|
# Save currentFile for cleanup later
|
||||||
|
utils.window('plex_lastPlayedFiled', value=currentFile)
|
||||||
# We may need to wait for info to be set in kodi monitor
|
# We may need to wait for info to be set in kodi monitor
|
||||||
itemId = utils.window("emby_%s.itemid" % currentFile)
|
itemId = utils.window("emby_%s.itemid" % currentFile)
|
||||||
tryCount = 0
|
tryCount = 0
|
||||||
|
@ -93,6 +102,11 @@ class Player(xbmc.Player):
|
||||||
itemType = utils.window("%s.type" % embyitem)
|
itemType = utils.window("%s.type" % embyitem)
|
||||||
utils.window('emby_skipWatched%s' % itemId, value="true")
|
utils.window('emby_skipWatched%s' % itemId, value="true")
|
||||||
|
|
||||||
|
# Suspend library sync thread while movie is playing
|
||||||
|
self.logMsg("Playing itemtype is: %s" % itemType, 1)
|
||||||
|
if itemType in ['movie', 'audio']:
|
||||||
|
self.logMsg("Suspending library sync while playing", 1)
|
||||||
|
utils.window('suspend_LibraryThread', value='true')
|
||||||
|
|
||||||
if (utils.window('emby_customPlaylist') == "true" and
|
if (utils.window('emby_customPlaylist') == "true" and
|
||||||
utils.window('emby_customPlaylist.seektime')):
|
utils.window('emby_customPlaylist.seektime')):
|
||||||
|
@ -214,10 +228,18 @@ class Player(xbmc.Player):
|
||||||
runtime = xbmcplayer.getTotalTime()
|
runtime = xbmcplayer.getTotalTime()
|
||||||
self.logMsg("Runtime is missing, Kodi runtime: %s" % runtime, 1)
|
self.logMsg("Runtime is missing, Kodi runtime: %s" % runtime, 1)
|
||||||
|
|
||||||
|
playQueueVersion = utils.window(
|
||||||
|
'playQueueVersion')
|
||||||
|
playQueueID = utils.window(
|
||||||
|
'playQueueID')
|
||||||
|
playQueueItemID = utils.window(
|
||||||
|
'plex_%s.playQueueItemID' % currentFile)
|
||||||
# Save data map for updates and position calls
|
# Save data map for updates and position calls
|
||||||
data = {
|
data = {
|
||||||
|
'playQueueVersion': playQueueVersion,
|
||||||
'runtime': runtime,
|
'playQueueID': playQueueID,
|
||||||
|
'playQueueItemID': playQueueItemID,
|
||||||
|
'runtime': runtime * 1000,
|
||||||
'item_id': itemId,
|
'item_id': itemId,
|
||||||
'refresh_id': refresh_id,
|
'refresh_id': refresh_id,
|
||||||
'currentfile': currentFile,
|
'currentfile': currentFile,
|
||||||
|
@ -247,7 +269,10 @@ class Player(xbmc.Player):
|
||||||
self.playStats[playMethod] = 1'''
|
self.playStats[playMethod] = 1'''
|
||||||
|
|
||||||
def reportPlayback(self):
|
def reportPlayback(self):
|
||||||
|
# Don't use if Plex Companion is enabled
|
||||||
|
if not self.doNotify:
|
||||||
|
return
|
||||||
|
|
||||||
self.logMsg("reportPlayback Called", 2)
|
self.logMsg("reportPlayback Called", 2)
|
||||||
xbmcplayer = self.xbmcplayer
|
xbmcplayer = self.xbmcplayer
|
||||||
|
|
||||||
|
@ -311,6 +336,12 @@ class Player(xbmc.Player):
|
||||||
'duration': int(duration) * 1000
|
'duration': int(duration) * 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# For PMS playQueues/playlists only
|
||||||
|
if data.get('playQueueID'):
|
||||||
|
postdata['containerKey'] = '/playQueues/' + data.get('playQueueID')
|
||||||
|
postdata['playQueueVersion'] = data.get('playQueueVersion')
|
||||||
|
postdata['playQueueItemID'] = data.get('playQueueItemID')
|
||||||
|
|
||||||
if playMethod == "Transcode":
|
if playMethod == "Transcode":
|
||||||
# Track can't be changed, keep reporting the same index
|
# Track can't be changed, keep reporting the same index
|
||||||
postdata['AudioStreamIndex'] = audioindex
|
postdata['AudioStreamIndex'] = audioindex
|
||||||
|
@ -422,12 +453,14 @@ class Player(xbmc.Player):
|
||||||
utils.window('emby_customPlaylist.seektime', clear=True)
|
utils.window('emby_customPlaylist.seektime', clear=True)
|
||||||
utils.window('emby_playbackProps', clear=True)
|
utils.window('emby_playbackProps', clear=True)
|
||||||
self.logMsg("Clear playlist properties.", 1)
|
self.logMsg("Clear playlist properties.", 1)
|
||||||
|
utils.window('suspend_LibraryThread', clear=True)
|
||||||
self.stopAll()
|
self.stopAll()
|
||||||
|
|
||||||
def onPlayBackEnded( self ):
|
def onPlayBackEnded( self ):
|
||||||
# Will be called when xbmc stops playing a file
|
# Will be called when xbmc stops playing a file
|
||||||
self.logMsg("ONPLAYBACK_ENDED", 2)
|
self.logMsg("ONPLAYBACK_ENDED", 2)
|
||||||
utils.window('emby_customPlaylist.seektime', clear=True)
|
utils.window('emby_customPlaylist.seektime', clear=True)
|
||||||
|
utils.window('suspend_LibraryThread', clear=True)
|
||||||
self.stopAll()
|
self.stopAll()
|
||||||
|
|
||||||
def stopAll(self):
|
def stopAll(self):
|
||||||
|
@ -457,7 +490,7 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
if currentPosition and runtime:
|
if currentPosition and runtime:
|
||||||
try:
|
try:
|
||||||
percentComplete = (currentPosition * 10000) / int(runtime)
|
percentComplete = currentPosition / int(runtime)
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
# Runtime is 0.
|
# Runtime is 0.
|
||||||
percentComplete = 0
|
percentComplete = 0
|
||||||
|
@ -474,9 +507,8 @@ class Player(xbmc.Player):
|
||||||
# Stop transcoding
|
# Stop transcoding
|
||||||
if playMethod == "Transcode":
|
if playMethod == "Transcode":
|
||||||
self.logMsg("Transcoding for %s terminated." % itemid, 1)
|
self.logMsg("Transcoding for %s terminated." % itemid, 1)
|
||||||
deviceId = self.clientInfo.getDeviceId()
|
url = "{server}/video/:/transcode/universal/stop?session=%s" % self.clientInfo.getDeviceId()
|
||||||
url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId
|
doUtils.downloadUrl(url, type="GET")
|
||||||
doUtils.downloadUrl(url, type="DELETE")
|
|
||||||
|
|
||||||
# Send the delete action to the server.
|
# Send the delete action to the server.
|
||||||
offerDelete = False
|
offerDelete = False
|
||||||
|
@ -490,6 +522,8 @@ class Player(xbmc.Player):
|
||||||
# Delete could be disabled, even if the subsetting is enabled.
|
# Delete could be disabled, even if the subsetting is enabled.
|
||||||
offerDelete = False
|
offerDelete = False
|
||||||
|
|
||||||
|
# Plex: never delete
|
||||||
|
offerDelete = False
|
||||||
if percentComplete >= markPlayedAt and offerDelete:
|
if percentComplete >= markPlayedAt and offerDelete:
|
||||||
if utils.settings('skipConfirmDelete') != "true":
|
if utils.settings('skipConfirmDelete') != "true":
|
||||||
resp = xbmcgui.Dialog().yesno(
|
resp = xbmcgui.Dialog().yesno(
|
||||||
|
@ -502,7 +536,22 @@ class Player(xbmc.Player):
|
||||||
url = "{server}/emby/Items/%s?format=json" % itemid
|
url = "{server}/emby/Items/%s?format=json" % itemid
|
||||||
self.logMsg("Deleting request: %s" % itemid)
|
self.logMsg("Deleting request: %s" % itemid)
|
||||||
doUtils.downloadUrl(url, type="DELETE")
|
doUtils.downloadUrl(url, type="DELETE")
|
||||||
|
|
||||||
|
# Clean the WINDOW properties
|
||||||
|
# for filename in self.played_info:
|
||||||
|
# cleanup = (
|
||||||
|
# 'emby_%s.itemid' % filename,
|
||||||
|
# 'emby_%s.runtime' % filename,
|
||||||
|
# 'emby_%s.refreshid' % filename,
|
||||||
|
# 'emby_%s.playmethod' % filename,
|
||||||
|
# 'emby_%s.type' % filename,
|
||||||
|
# 'plex_%s.playQueueItemID' % filename,
|
||||||
|
# 'plex_%s.playlistPosition' % filename,
|
||||||
|
# 'plex_%s.guid' % filename
|
||||||
|
# )
|
||||||
|
# for item in cleanup:
|
||||||
|
# utils.window(item, clear=True)
|
||||||
|
|
||||||
self.played_info.clear()
|
self.played_info.clear()
|
||||||
|
|
||||||
def stopPlayback(self, data):
|
def stopPlayback(self, data):
|
||||||
|
@ -517,7 +566,7 @@ class Player(xbmc.Player):
|
||||||
'ratingKey': itemId,
|
'ratingKey': itemId,
|
||||||
'state': 'stopped', # 'stopped', 'paused', 'buffering', 'playing'
|
'state': 'stopped', # 'stopped', 'paused', 'buffering', 'playing'
|
||||||
'time': int(playTime) * 1000,
|
'time': int(playTime) * 1000,
|
||||||
'duration': int(duration) * 1000
|
'duration': int(duration)
|
||||||
}
|
}
|
||||||
url = url + urlencode(args)
|
url = url + urlencode(args)
|
||||||
self.doUtils.downloadUrl(url, type="GET")
|
self.doUtils.downloadUrl(url, type="GET")
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
from urllib import urlencode
|
||||||
|
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
import xbmcvfs
|
import xbmcvfs
|
||||||
|
@ -10,6 +12,7 @@ import clientinfo
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
import PlexAPI
|
import PlexAPI
|
||||||
|
import PlexFunctions
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -45,16 +48,19 @@ class PlayUtils():
|
||||||
# Set playmethod property
|
# Set playmethod property
|
||||||
utils.window('emby_%s.playmethod' % playurl, "DirectPlay")
|
utils.window('emby_%s.playmethod' % playurl, "DirectPlay")
|
||||||
|
|
||||||
elif self.isDirectStream():
|
# Currently no direct streaming possible - needs investigation
|
||||||
self.logMsg("File is direct streaming.", 1)
|
# elif self.isDirectStream():
|
||||||
playurl = self.API.getTranscodeVideoPath('DirectStream')
|
# self.logMsg("File is direct streaming.", 1)
|
||||||
# Set playmethod property
|
# playurl = self.API.getTranscodeVideoPath('DirectStream')
|
||||||
utils.window('emby_%s.playmethod' % playurl, "DirectStream")
|
# # Set playmethod property
|
||||||
|
# utils.window('emby_%s.playmethod' % playurl, "DirectStream")
|
||||||
|
|
||||||
elif self.isTranscoding():
|
elif self.isTranscoding():
|
||||||
self.logMsg("File is transcoding.", 1)
|
self.logMsg("File is transcoding.", 1)
|
||||||
quality = {
|
quality = {
|
||||||
'maxVideoBitrate': self.getBitrate()
|
'maxVideoBitrate': self.getBitrate(),
|
||||||
|
'videoResolution': self.getResolution(),
|
||||||
|
'videoQuality': '100'
|
||||||
}
|
}
|
||||||
playurl = self.API.getTranscodeVideoPath('Transcode',
|
playurl = self.API.getTranscodeVideoPath('Transcode',
|
||||||
quality=quality)
|
quality=quality)
|
||||||
|
@ -89,13 +95,9 @@ class PlayUtils():
|
||||||
self.logMsg("Can't direct play, user enabled play from HTTP.", 1)
|
self.logMsg("Can't direct play, user enabled play from HTTP.", 1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not self.h265enabled():
|
if self.h265enabled():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Found with e.g. trailers
|
|
||||||
# if self.API.getDataFromPartOrMedia('optimizedForStreaming') == '1':
|
|
||||||
# return False
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def directPlay(self):
|
def directPlay(self):
|
||||||
|
@ -150,22 +152,23 @@ class PlayUtils():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def h265enabled(self):
|
def h265enabled(self):
|
||||||
|
"""
|
||||||
|
Returns True if we need to transcode
|
||||||
|
"""
|
||||||
videoCodec = self.API.getVideoCodec()
|
videoCodec = self.API.getVideoCodec()
|
||||||
codec = videoCodec['videocodec']
|
codec = videoCodec['videocodec']
|
||||||
resolution = videoCodec['resolution']
|
resolution = videoCodec['resolution']
|
||||||
# 720p
|
h265 = self.getH265()
|
||||||
if ((utils.settings('transcode720H265') == "true") and
|
if not ('h265' in codec or 'hevc' in codec) or (h265 is None):
|
||||||
("h265" in codec) and
|
|
||||||
(resolution in "720 1080")):
|
|
||||||
self.logMsg("Option to transcode 720P/h265 enabled.", 0)
|
|
||||||
return False
|
return False
|
||||||
# 1080p
|
|
||||||
if ((utils.settings('transcodeH265') == "true") and
|
if resolution >= h265:
|
||||||
("h265" in codec) and
|
self.logMsg("Option to transcode h265 enabled. Resolution media: "
|
||||||
(resolution == "1080")):
|
"%s, transcoding limit resolution: %s"
|
||||||
self.logMsg("Option to transcode 1080P/h265 enabled.", 0)
|
% (resolution, h265), 1)
|
||||||
return False
|
return True
|
||||||
return True
|
|
||||||
|
return False
|
||||||
|
|
||||||
def isDirectStream(self):
|
def isDirectStream(self):
|
||||||
if not self.h265enabled():
|
if not self.h265enabled():
|
||||||
|
@ -247,88 +250,106 @@ class PlayUtils():
|
||||||
return playurl
|
return playurl
|
||||||
|
|
||||||
def getBitrate(self):
|
def getBitrate(self):
|
||||||
|
|
||||||
# get the addon video quality
|
# get the addon video quality
|
||||||
videoQuality = utils.settings('videoBitrate')
|
videoQuality = utils.settings('transcoderVideoQualities')
|
||||||
bitrate = {
|
bitrate = {
|
||||||
|
'0': 320,
|
||||||
'0': 664,
|
'1': 720,
|
||||||
'1': 996,
|
'2': 1500,
|
||||||
'2': 1320,
|
|
||||||
'3': 2000,
|
'3': 2000,
|
||||||
'4': 3200,
|
'4': 3000,
|
||||||
'5': 4700,
|
'5': 4000,
|
||||||
'6': 6200,
|
'6': 8000,
|
||||||
'7': 7700,
|
'7': 10000,
|
||||||
'8': 9200,
|
'8': 12000,
|
||||||
'9': 10700,
|
'9': 20000,
|
||||||
'10': 12200,
|
'10': 40000,
|
||||||
'11': 13700,
|
|
||||||
'12': 15200,
|
|
||||||
'13': 16700,
|
|
||||||
'14': 18200,
|
|
||||||
'15': 20000,
|
|
||||||
'16': 40000,
|
|
||||||
'17': 100000,
|
|
||||||
'18': 1000000
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# max bit rate supported by server (max signed 32bit integer)
|
# max bit rate supported by server (max signed 32bit integer)
|
||||||
return bitrate.get(videoQuality, 2147483)
|
return bitrate.get(videoQuality, 2147483)
|
||||||
|
|
||||||
def audioSubsPref(self, url, listitem, part=None):
|
def getH265(self):
|
||||||
|
chosen = utils.settings('transcodeH265')
|
||||||
|
H265 = {
|
||||||
|
'0': None,
|
||||||
|
'1': 480,
|
||||||
|
'2': 720,
|
||||||
|
'3': 1080
|
||||||
|
}
|
||||||
|
return H265.get(chosen, None)
|
||||||
|
|
||||||
|
def getResolution(self):
|
||||||
|
chosen = utils.settings('transcoderVideoQualities')
|
||||||
|
res = {
|
||||||
|
'0': '420x420',
|
||||||
|
'1': '576x320',
|
||||||
|
'2': '720x480',
|
||||||
|
'3': '1024x768',
|
||||||
|
'4': '1280x720',
|
||||||
|
'5': '1280x720',
|
||||||
|
'6': '1920x1080',
|
||||||
|
'7': '1920x1080',
|
||||||
|
'8': '1920x1080',
|
||||||
|
'9': '1920x1080',
|
||||||
|
'10': '1920x1080',
|
||||||
|
}
|
||||||
|
return res[chosen]
|
||||||
|
|
||||||
|
def audioSubsPref(self, listitem, url, part=None):
|
||||||
# For transcoding only
|
# For transcoding only
|
||||||
# Present the list of audio to select from
|
# Present the list of audio to select from
|
||||||
audioStreamsList = {}
|
audioStreamsList = []
|
||||||
audioStreams = []
|
audioStreams = []
|
||||||
audioStreamsChannelsList = {}
|
# audioStreamsChannelsList = []
|
||||||
subtitleStreamsList = {}
|
subtitleStreamsList = []
|
||||||
subtitleStreams = ['No subtitles']
|
subtitleStreams = ['1 No subtitles']
|
||||||
downloadableStreams = []
|
downloadableStreams = []
|
||||||
selectAudioIndex = ""
|
# selectAudioIndex = ""
|
||||||
selectSubsIndex = ""
|
selectSubsIndex = ""
|
||||||
playurlprefs = "%s" % url
|
playurlprefs = {}
|
||||||
|
|
||||||
# Set part where we're at
|
# Set part where we're at
|
||||||
self.API.setPartNumber(part)
|
self.API.setPartNumber(part)
|
||||||
item = self.item
|
|
||||||
try:
|
try:
|
||||||
mediasources = item[0]
|
mediastreams = self.item[0][part]
|
||||||
mediastreams = mediasources[part]
|
|
||||||
except (TypeError, KeyError, IndexError):
|
except (TypeError, KeyError, IndexError):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
audioNum = 0
|
||||||
|
# Remember 'no subtitles'
|
||||||
|
subNum = 1
|
||||||
for stream in mediastreams:
|
for stream in mediastreams:
|
||||||
# Since Emby returns all possible tracks together, have to sort them.
|
# Since Emby returns all possible tracks together, have to sort them.
|
||||||
index = stream['index']
|
index = stream.attrib['id']
|
||||||
type = stream['streamType']
|
type = stream.attrib['streamType']
|
||||||
|
|
||||||
# Audio
|
# Audio
|
||||||
if type == "2":
|
if type == "2":
|
||||||
codec = stream['codec']
|
codec = stream.attrib['codec']
|
||||||
channelLayout = stream.get('audioChannelLayout', "")
|
channelLayout = stream.attrib.get('audioChannelLayout', "")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
track = "%s - %s - %s %s" % (index, stream['language'], codec, channelLayout)
|
track = "%s %s - %s %s" % (audioNum+1, stream.attrib['language'], codec, channelLayout)
|
||||||
except:
|
except:
|
||||||
track = "%s - %s %s" % (index, codec, channelLayout)
|
track = "%s 'unknown' - %s %s" % (audioNum+1, codec, channelLayout)
|
||||||
|
|
||||||
audioStreamsChannelsList[index] = stream['channels']
|
#audioStreamsChannelsList[audioNum] = stream.attrib['channels']
|
||||||
audioStreamsList[track] = index
|
audioStreamsList.append(index)
|
||||||
audioStreams.append(track)
|
audioStreams.append(track)
|
||||||
|
audioNum += 1
|
||||||
|
|
||||||
# Subtitles
|
# Subtitles
|
||||||
elif type == "3":
|
elif type == "3":
|
||||||
'''if stream['IsExternal']:
|
'''if stream['IsExternal']:
|
||||||
continue'''
|
continue'''
|
||||||
try:
|
try:
|
||||||
track = "%s - %s" % (index, stream['language'])
|
track = "%s %s" % (subNum+1, stream.attrib['language'])
|
||||||
except:
|
except:
|
||||||
track = "%s - %s" % (index, stream['codec'])
|
track = "%s 'unknown' (%s)" % (subNum+1, stream.attrib['codec'])
|
||||||
|
|
||||||
default = stream.get('default', None)
|
default = stream.attrib.get('default')
|
||||||
forced = stream.get('forced', None)
|
forced = stream.attrib.get('forced')
|
||||||
downloadable = True if stream.get('format', None) else False
|
downloadable = stream.attrib.get('key')
|
||||||
|
|
||||||
if default:
|
if default:
|
||||||
track = "%s - Default" % track
|
track = "%s - Default" % track
|
||||||
|
@ -337,53 +358,58 @@ class PlayUtils():
|
||||||
if downloadable:
|
if downloadable:
|
||||||
downloadableStreams.append(index)
|
downloadableStreams.append(index)
|
||||||
|
|
||||||
subtitleStreamsList[track] = index
|
subtitleStreamsList.append(index)
|
||||||
subtitleStreams.append(track)
|
subtitleStreams.append(track)
|
||||||
|
subNum += 1
|
||||||
|
|
||||||
|
if audioNum > 1:
|
||||||
if len(audioStreams) > 1:
|
|
||||||
resp = xbmcgui.Dialog().select("Choose the audio stream", audioStreams)
|
resp = xbmcgui.Dialog().select("Choose the audio stream", audioStreams)
|
||||||
if resp > -1:
|
if resp > -1:
|
||||||
# User selected audio
|
# User selected audio
|
||||||
selected = audioStreams[resp]
|
playurlprefs['audioStreamID'] = audioStreamsList[resp]
|
||||||
selectAudioIndex = audioStreamsList[selected]
|
else: # User backed out of selection - let PMS decide
|
||||||
playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
|
pass
|
||||||
else: # User backed out of selection
|
|
||||||
playurlprefs += "&AudioStreamIndex=%s" % mediasources['DefaultAudioStreamIndex']
|
|
||||||
else: # There's only one audiotrack.
|
else: # There's only one audiotrack.
|
||||||
selectAudioIndex = audioStreamsList[audioStreams[0]]
|
playurlprefs['audioStreamID'] = audioStreamsList[0]
|
||||||
playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
|
|
||||||
|
|
||||||
if len(subtitleStreams) > 1:
|
# Add audio boost
|
||||||
|
playurlprefs['audioBoost'] = utils.settings('audioBoost')
|
||||||
|
|
||||||
|
if subNum > 1:
|
||||||
resp = xbmcgui.Dialog().select("Choose the subtitle stream", subtitleStreams)
|
resp = xbmcgui.Dialog().select("Choose the subtitle stream", subtitleStreams)
|
||||||
if resp == 0:
|
if resp == 0:
|
||||||
# User selected no subtitles
|
# User selected no subtitles
|
||||||
pass
|
playurlprefs["skipSubtitles"] = 1
|
||||||
elif resp > -1:
|
elif resp > -1:
|
||||||
# User selected subtitles
|
# User selected subtitles
|
||||||
selected = subtitleStreams[resp]
|
selectSubsIndex = subtitleStreamsList[resp-1]
|
||||||
selectSubsIndex = subtitleStreamsList[selected]
|
|
||||||
|
|
||||||
# Load subtitles in the listitem if downloadable
|
# Load subtitles in the listitem if downloadable
|
||||||
if selectSubsIndex in downloadableStreams:
|
if selectSubsIndex in downloadableStreams:
|
||||||
|
|
||||||
itemid = item['Id']
|
url = "%s/library/streams/%s" \
|
||||||
url = [("%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
|
% (self.server, selectSubsIndex)
|
||||||
% (self.server, itemid, itemid, selectSubsIndex))]
|
url = self.API.addPlexCredentialsToUrl(url)
|
||||||
self.logMsg("Set up subtitles: %s %s" % (selectSubsIndex, url), 1)
|
self.logMsg("Downloadable sub: %s: %s" % (selectSubsIndex, url), 1)
|
||||||
listitem.setSubtitles(url)
|
listitem.setSubtitles([url])
|
||||||
else:
|
else:
|
||||||
# Burn subtitles
|
self.logMsg('Need to burn in subtitle %s' % selectSubsIndex, 1)
|
||||||
playurlprefs += "&SubtitleStreamIndex=%s" % selectSubsIndex
|
playurlprefs["subtitleStreamID"] = selectSubsIndex
|
||||||
|
playurlprefs["subtitleSize"] = utils.settings('subtitleSize')
|
||||||
|
|
||||||
else: # User backed out of selection
|
else: # User backed out of selection
|
||||||
playurlprefs += "&SubtitleStreamIndex=%s" % mediasources.get('DefaultSubtitleStreamIndex', "")
|
pass
|
||||||
|
|
||||||
|
# Tell the PMS what we want with a PUT request
|
||||||
|
# url = self.server + '/library/parts/' + self.item[0][part].attrib['id']
|
||||||
|
# PlexFunctions.SelectStreams(url, playurlprefs)
|
||||||
|
url += '&' + urlencode(playurlprefs)
|
||||||
|
|
||||||
# Get number of channels for selected audio track
|
# Get number of channels for selected audio track
|
||||||
audioChannels = audioStreamsChannelsList.get(selectAudioIndex, 0)
|
# audioChannels = audioStreamsChannelsList.get(selectAudioIndex, 0)
|
||||||
if audioChannels > 2:
|
# if audioChannels > 2:
|
||||||
playurlprefs += "&AudioBitrate=384000"
|
# playurlprefs += "&AudioBitrate=384000"
|
||||||
else:
|
# else:
|
||||||
playurlprefs += "&AudioBitrate=192000"
|
# playurlprefs += "&AudioBitrate=192000"
|
||||||
|
|
||||||
return playurlprefs
|
return url
|
||||||
|
|
|
@ -115,7 +115,7 @@ def parseJSONRPC(jsonraw):
|
||||||
return parsed.get('result', {})
|
return parsed.get('result', {})
|
||||||
|
|
||||||
def getXMLHeader():
|
def getXMLHeader():
|
||||||
return '<?xml version="1.0" encoding="utf-8"?>'+"\r\n"
|
return '<?xml version="1.0" encoding="utf-8" ?>'+"\r\n"
|
||||||
|
|
||||||
def getOKMsg():
|
def getOKMsg():
|
||||||
return getXMLHeader() + '<Response code="200" status="OK" />'
|
return getXMLHeader() + '<Response code="200" status="OK" />'
|
||||||
|
@ -176,7 +176,10 @@ def getPhotoPlayerId(players = False):
|
||||||
return players.get(xbmc_photo(), {}).get('playerid', None)
|
return players.get(xbmc_photo(), {}).get('playerid', None)
|
||||||
|
|
||||||
def getVolume():
|
def getVolume():
|
||||||
return str(jsonrpc('Application.GetProperties', { "properties": [ "volume" ] }).get('volume', 100))
|
answ = jsonrpc('Application.GetProperties', { "properties": [ "volume", 'muted' ] })
|
||||||
|
vol = str(answ.get('volume', 100))
|
||||||
|
mute = ("0", "1")[answ.get('muted', False)]
|
||||||
|
return (vol, mute)
|
||||||
|
|
||||||
def timeToMillis(time):
|
def timeToMillis(time):
|
||||||
return (time['hours']*3600 + time['minutes']*60 + time['seconds'])*1000 + time['milliseconds']
|
return (time['hours']*3600 + time['minutes']*60 + time['seconds'])*1000 + time['milliseconds']
|
||||||
|
|
|
@ -111,13 +111,6 @@ class MyHandler(BaseHTTPRequestHandler):
|
||||||
printDebug("adjusting the volume to %s%%" % volume)
|
printDebug("adjusting the volume to %s%%" % volume)
|
||||||
jsonrpc("Application.SetVolume", {"volume": volume})
|
jsonrpc("Application.SetVolume", {"volume": volume})
|
||||||
elif "/playMedia" in request_path:
|
elif "/playMedia" in request_path:
|
||||||
playQueueVersion = int(params.get('playQueueVersion', 1))
|
|
||||||
if playQueueVersion < subMgr.playQueueVersion:
|
|
||||||
# playQueue was updated; ignore this command for now
|
|
||||||
return
|
|
||||||
if playQueueVersion > subMgr.playQueueVersion:
|
|
||||||
# TODO: we should probably update something else now :-)
|
|
||||||
subMgr.playQueueVersion = playQueueVersion
|
|
||||||
s.response(getOKMsg(), getPlexHeaders())
|
s.response(getOKMsg(), getPlexHeaders())
|
||||||
offset = params.get('viewOffset', params.get('offset', "0"))
|
offset = params.get('viewOffset', params.get('offset', "0"))
|
||||||
protocol = params.get('protocol', "http")
|
protocol = params.get('protocol', "http")
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
import xbmcgui
|
|
||||||
from xml.dom.minidom import parseString
|
from xml.dom.minidom import parseString
|
||||||
from functions import *
|
from functions import *
|
||||||
from settings import settings
|
from settings import settings
|
||||||
from httppersist import requests
|
from httppersist import requests
|
||||||
|
|
||||||
|
from xbmc import Player
|
||||||
|
import xbmcgui
|
||||||
import downloadutils
|
import downloadutils
|
||||||
|
from utils import window
|
||||||
|
import PlexFunctions as pf
|
||||||
|
|
||||||
class SubscriptionManager:
|
class SubscriptionManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -13,19 +17,18 @@ class SubscriptionManager:
|
||||||
self.info = {}
|
self.info = {}
|
||||||
self.lastkey = ""
|
self.lastkey = ""
|
||||||
self.containerKey = ""
|
self.containerKey = ""
|
||||||
self.playQueueID = ''
|
|
||||||
self.playQueueVersion = 1
|
|
||||||
self.lastratingkey = ""
|
self.lastratingkey = ""
|
||||||
self.volume = 0
|
self.volume = 0
|
||||||
self.guid = ""
|
self.mute = '0'
|
||||||
self.server = ""
|
self.server = ""
|
||||||
self.protocol = "http"
|
self.protocol = "http"
|
||||||
self.port = ""
|
self.port = ""
|
||||||
self.playerprops = {}
|
self.playerprops = {}
|
||||||
self.download = downloadutils.DownloadUtils()
|
self.download = downloadutils.DownloadUtils()
|
||||||
|
self.xbmcplayer = Player()
|
||||||
|
|
||||||
def getVolume(self):
|
def getVolume(self):
|
||||||
self.volume = getVolume()
|
self.volume, self.mute = getVolume()
|
||||||
|
|
||||||
def msg(self, players):
|
def msg(self, players):
|
||||||
msg = getXMLHeader()
|
msg = getXMLHeader()
|
||||||
|
@ -42,7 +45,6 @@ class SubscriptionManager:
|
||||||
else:
|
else:
|
||||||
self.mainlocation = "navigation"
|
self.mainlocation = "navigation"
|
||||||
msg += ' location="%s">' % self.mainlocation
|
msg += ' location="%s">' % self.mainlocation
|
||||||
|
|
||||||
msg += self.getTimelineXML(getAudioPlayerId(players), plex_audio())
|
msg += self.getTimelineXML(getAudioPlayerId(players), plex_audio())
|
||||||
msg += self.getTimelineXML(getPhotoPlayerId(players), plex_photo())
|
msg += self.getTimelineXML(getPhotoPlayerId(players), plex_photo())
|
||||||
msg += self.getTimelineXML(getVideoPlayerId(players), plex_video())
|
msg += self.getTimelineXML(getVideoPlayerId(players), plex_video())
|
||||||
|
@ -59,48 +61,63 @@ class SubscriptionManager:
|
||||||
else:
|
else:
|
||||||
state = "stopped"
|
state = "stopped"
|
||||||
time = 0
|
time = 0
|
||||||
ret = "\r\n"+'<Timeline location="%s" state="%s" time="%s" type="%s"' % (self.mainlocation, state, time, ptype)
|
ret = "\r\n"+' <Timeline state="%s" time="%s" type="%s"' % (state, time, ptype)
|
||||||
if playerid is not None:
|
if playerid is None:
|
||||||
WINDOW = xbmcgui.Window(10000)
|
ret += ' seekRange="0-0"'
|
||||||
|
ret += ' />'
|
||||||
# pbmc_server = str(WINDOW.getProperty('plexbmc.nowplaying.server'))
|
return ret
|
||||||
# userId = str(WINDOW.getProperty('emby_currUser'))
|
|
||||||
# pbmc_server = str(WINDOW.getProperty('emby_server%s' % userId))
|
WINDOW = xbmcgui.Window(10000)
|
||||||
pbmc_server = None
|
|
||||||
keyid = None
|
|
||||||
count = 0
|
|
||||||
while not keyid:
|
|
||||||
if count > 10:
|
|
||||||
break
|
|
||||||
keyid = str(WINDOW.getProperty('Plex_currently_playing_itemid'))
|
|
||||||
xbmc.sleep(1000)
|
|
||||||
count += 1
|
|
||||||
if keyid:
|
|
||||||
self.lastkey = "/library/metadata/%s"%keyid
|
|
||||||
self.lastratingkey = keyid
|
|
||||||
ret += ' containerKey="%s"' % (self.containerKey)
|
|
||||||
ret += ' key="%s"' % (self.lastkey)
|
|
||||||
ret += ' ratingKey="%s"' % (self.lastratingkey)
|
|
||||||
if pbmc_server:
|
|
||||||
(self.server, self.port) = pbmc_server.split(':')
|
|
||||||
serv = getServerByHost(self.server)
|
|
||||||
if self.playQueueID:
|
|
||||||
ret += ' playQueueID="%s"' % self.playQueueID
|
|
||||||
ret += ' playQueueVersion="%s"' % self.playQueueVersion
|
|
||||||
ret += ' duration="%s"' % info['duration']
|
|
||||||
ret += ' seekRange="0-%s"' % info['duration']
|
|
||||||
ret += ' controllable="%s"' % self.controllable()
|
|
||||||
ret += ' machineIdentifier="%s"' % serv.get('uuid', "")
|
|
||||||
ret += ' protocol="%s"' % serv.get('protocol', "http")
|
|
||||||
ret += ' address="%s"' % serv.get('server', self.server)
|
|
||||||
ret += ' port="%s"' % serv.get('port', self.port)
|
|
||||||
ret += ' guid="%s"' % info['guid']
|
|
||||||
ret += ' volume="%s"' % info['volume']
|
|
||||||
ret += ' shuffle="%s"' % info['shuffle']
|
|
||||||
|
|
||||||
ret += '/>'
|
# pbmc_server = str(WINDOW.getProperty('plexbmc.nowplaying.server'))
|
||||||
|
# userId = str(WINDOW.getProperty('emby_currUser'))
|
||||||
|
# pbmc_server = str(WINDOW.getProperty('emby_server%s' % userId))
|
||||||
|
pbmc_server = None
|
||||||
|
keyid = None
|
||||||
|
count = 0
|
||||||
|
while not keyid:
|
||||||
|
if count > 10:
|
||||||
|
break
|
||||||
|
keyid = WINDOW.getProperty('Plex_currently_playing_itemid')
|
||||||
|
xbmc.sleep(1000)
|
||||||
|
count += 1
|
||||||
|
if keyid:
|
||||||
|
self.lastkey = "/library/metadata/%s"%keyid
|
||||||
|
self.lastratingkey = keyid
|
||||||
|
ret += ' location="%s"' % (self.mainlocation)
|
||||||
|
ret += ' key="%s"' % (self.lastkey)
|
||||||
|
ret += ' ratingKey="%s"' % (self.lastratingkey)
|
||||||
|
if pbmc_server:
|
||||||
|
(self.server, self.port) = pbmc_server.split(':')
|
||||||
|
serv = getServerByHost(self.server)
|
||||||
|
if info.get('playQueueID'):
|
||||||
|
ret += ' playQueueID="%s"' % info.get('playQueueID')
|
||||||
|
ret += ' playQueueVersion="%s"' % info.get('playQueueVersion')
|
||||||
|
ret += ' playQueueItemID="%s"' % (info.get('playQueueItemID'))
|
||||||
|
ret += ' containerKey="/playQueues/%s"' \
|
||||||
|
% (info.get('playQueueID'))
|
||||||
|
elif keyid:
|
||||||
|
ret += ' containerKey="%s"' % (self.containerKey)
|
||||||
|
|
||||||
|
ret += ' duration="%s"' % info['duration']
|
||||||
|
ret += ' seekRange="0-%s"' % info['duration']
|
||||||
|
ret += ' controllable="%s"' % self.controllable()
|
||||||
|
ret += ' machineIdentifier="%s"' % serv.get('uuid', "")
|
||||||
|
ret += ' protocol="%s"' % serv.get('protocol', "http")
|
||||||
|
ret += ' address="%s"' % serv.get('server', self.server)
|
||||||
|
ret += ' port="%s"' % serv.get('port', self.port)
|
||||||
|
ret += ' guid="%s"' % info['guid']
|
||||||
|
ret += ' volume="%s"' % info['volume']
|
||||||
|
ret += ' shuffle="%s"' % info['shuffle']
|
||||||
|
ret += ' mute="%s"' % self.mute
|
||||||
|
ret += ' repeat="%s"' % info['repeat']
|
||||||
|
# Might need an update in the future
|
||||||
|
ret += ' subtitleStreamID="-1"'
|
||||||
|
ret += ' audioStreamID="-1"'
|
||||||
|
|
||||||
|
ret += ' />'
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def updateCommandID(self, uuid, commandID):
|
def updateCommandID(self, uuid, commandID):
|
||||||
if commandID and self.subscribers.get(uuid, False):
|
if commandID and self.subscribers.get(uuid, False):
|
||||||
self.subscribers[uuid].commandID = int(commandID)
|
self.subscribers[uuid].commandID = int(commandID)
|
||||||
|
@ -126,14 +143,15 @@ class SubscriptionManager:
|
||||||
info = self.playerprops[p.get('playerid')]
|
info = self.playerprops[p.get('playerid')]
|
||||||
params = {}
|
params = {}
|
||||||
params['containerKey'] = (self.containerKey or "/library/metadata/900000")
|
params['containerKey'] = (self.containerKey or "/library/metadata/900000")
|
||||||
if self.playQueueID:
|
if info.get('playQueueID'):
|
||||||
params['playQueueID'] = self.playQueueID
|
params['containerKey'] = '/playQueues/' + info['playQueueID']
|
||||||
|
params['playQueueVersion'] = info['playQueueVersion']
|
||||||
|
params['playQueueItemID'] = info['playQueueItemID']
|
||||||
params['key'] = (self.lastkey or "/library/metadata/900000")
|
params['key'] = (self.lastkey or "/library/metadata/900000")
|
||||||
params['ratingKey'] = (self.lastratingkey or "900000")
|
params['ratingKey'] = (self.lastratingkey or "900000")
|
||||||
params['state'] = info['state']
|
params['state'] = info['state']
|
||||||
params['time'] = info['time']
|
params['time'] = info['time']
|
||||||
params['duration'] = info['duration']
|
params['duration'] = info['duration']
|
||||||
params['playQueueVersion'] = self.playQueueVersion
|
|
||||||
serv = getServerByHost(self.server)
|
serv = getServerByHost(self.server)
|
||||||
url = serv.get('protocol', 'http') + '://' \
|
url = serv.get('protocol', 'http') + '://' \
|
||||||
+ serv.get('server', 'localhost') + ':' \
|
+ serv.get('server', 'localhost') + ':' \
|
||||||
|
@ -171,12 +189,20 @@ class SubscriptionManager:
|
||||||
info = {}
|
info = {}
|
||||||
try:
|
try:
|
||||||
# get info from the player
|
# get info from the player
|
||||||
props = jsonrpc("Player.GetProperties", {"playerid": playerid, "properties": ["time", "totaltime", "speed", "shuffled"]})
|
props = jsonrpc("Player.GetProperties", {"playerid": playerid, "properties": ["time", "totaltime", "speed", "shuffled", "repeat"]})
|
||||||
printDebug(jsonrpc("Player.GetItem", {"playerid": playerid, "properties": ["file", "showlink", "episode", "season"]}))
|
printDebug(jsonrpc("Player.GetItem", {"playerid": playerid, "properties": ["file", "showlink", "episode", "season"]}))
|
||||||
info['time'] = timeToMillis(props['time'])
|
info['time'] = timeToMillis(props['time'])
|
||||||
info['duration'] = timeToMillis(props['totaltime'])
|
info['duration'] = timeToMillis(props['totaltime'])
|
||||||
info['state'] = ("paused", "playing")[int(props['speed'])]
|
info['state'] = ("paused", "playing")[int(props['speed'])]
|
||||||
info['shuffle'] = ("0","1")[props.get('shuffled', False)]
|
info['shuffle'] = ("0","1")[props.get('shuffled', False)]
|
||||||
|
info['repeat'] = pf.getPlexRepeat(props.get('repeat'))
|
||||||
|
# New PMS playQueue attributes
|
||||||
|
cf = self.xbmcplayer.getPlayingFile()
|
||||||
|
info['playQueueID'] = window('playQueueID')
|
||||||
|
info['playQueueVersion'] = window('playQueueVersion')
|
||||||
|
info['playQueueItemID'] = window('plex_%s.playQueueItemID' % cf)
|
||||||
|
info['guid'] = window('plex_%s.guid' % cf)
|
||||||
|
|
||||||
except:
|
except:
|
||||||
info['time'] = 0
|
info['time'] = 0
|
||||||
info['duration'] = 0
|
info['duration'] = 0
|
||||||
|
@ -184,7 +210,7 @@ class SubscriptionManager:
|
||||||
info['shuffle'] = False
|
info['shuffle'] = False
|
||||||
# get the volume from the application
|
# get the volume from the application
|
||||||
info['volume'] = self.volume
|
info['volume'] = self.volume
|
||||||
info['guid'] = self.guid
|
info['mute'] = self.mute
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
@ -216,21 +242,11 @@ class Subscriber:
|
||||||
printDebug("sending xml to subscriber %s: %s" % (self.tostr(), msg))
|
printDebug("sending xml to subscriber %s: %s" % (self.tostr(), msg))
|
||||||
url = self.protocol + '://' + self.host + ':' + self.port \
|
url = self.protocol + '://' + self.host + ':' + self.port \
|
||||||
+ "/:/timeline"
|
+ "/:/timeline"
|
||||||
# Override some headers
|
|
||||||
headerOptions = {
|
|
||||||
'Content-Range': 'bytes 0-/-1',
|
|
||||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',
|
|
||||||
'Accept': '*/*',
|
|
||||||
'X-Plex-Username': 'croneter',
|
|
||||||
'Connection': 'keep-alive',
|
|
||||||
'X-Plex-Client-Capabilities': 'protocols=shoutcast,http-video;videoDecoders=h264{profile:high&resolution:1080&level:51};audioDecoders=mp3,aac,dts{bitrate:800000&channels:8},ac3{bitrate:800000&channels:8}',
|
|
||||||
'X-Plex-Client-Profile-Extra': 'add-transcode-target-audio-codec(type=videoProfile&context=streaming&protocol=*&audioCodec=dca,ac3)'
|
|
||||||
}
|
|
||||||
response = self.download.downloadUrl(
|
response = self.download.downloadUrl(
|
||||||
url,
|
url,
|
||||||
postBody=msg,
|
postBody=msg,
|
||||||
type="POSTXML",
|
type="POSTXML")
|
||||||
headerOptions=headerOptions)
|
|
||||||
# if not requests.post(self.host, self.port, "/:/timeline", msg, getPlexHeaders(), self.protocol):
|
# if not requests.post(self.host, self.port, "/:/timeline", msg, getPlexHeaders(), self.protocol):
|
||||||
# subMgr.removeSubscriber(self.uuid)
|
# subMgr.removeSubscriber(self.uuid)
|
||||||
if response in [False, None, 401]:
|
if response in [False, None, 401]:
|
||||||
|
|
|
@ -224,6 +224,7 @@ class UserClient(threading.Thread):
|
||||||
if authenticated == False:
|
if authenticated == False:
|
||||||
url = "%s/clients" % (self.currServer)
|
url = "%s/clients" % (self.currServer)
|
||||||
utils.window('emby_currUser', value=userId)
|
utils.window('emby_currUser', value=userId)
|
||||||
|
utils.window('plex_username', value=username)
|
||||||
utils.window('emby_accessToken%s' % userId, value=self.currToken)
|
utils.window('emby_accessToken%s' % userId, value=self.currToken)
|
||||||
result = doUtils.downloadUrl(url)
|
result = doUtils.downloadUrl(url)
|
||||||
|
|
||||||
|
@ -234,6 +235,7 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
# Set to windows property
|
# Set to windows property
|
||||||
utils.window('emby_currUser', value=userId)
|
utils.window('emby_currUser', value=userId)
|
||||||
|
utils.window('plex_username', value=username)
|
||||||
utils.window('emby_accessToken%s' % userId, value=self.currToken)
|
utils.window('emby_accessToken%s' % userId, value=self.currToken)
|
||||||
utils.window('emby_server%s' % userId, value=self.currServer)
|
utils.window('emby_server%s' % userId, value=self.currServer)
|
||||||
utils.window('emby_server_%s' % userId, value=self.getServer(prefix=False))
|
utils.window('emby_server_%s' % userId, value=self.getServer(prefix=False))
|
||||||
|
|
|
@ -60,9 +60,11 @@
|
||||||
<setting id="resumeJumpBack" type="slider" label="30521" default="10" range="0,1,120" option="int" />
|
<setting id="resumeJumpBack" type="slider" label="30521" default="10" range="0,1,120" option="int" />
|
||||||
<setting type="sep" />
|
<setting type="sep" />
|
||||||
<setting id="playFromStream" type="bool" label="30002" default="false" />
|
<setting id="playFromStream" type="bool" label="30002" default="false" />
|
||||||
<setting id="videoBitrate" type="enum" label="30160" values="664 Kbps SD|996 Kbps HD|1.3 Mbps HD|2.0 Mbps HD|3.2 Mbps HD|4.7 Mbps HD|6.2 Mbps HD|7.7 Mbps HD|9.2 Mbps HD|10.7 Mbps HD|12.2 Mbps HD|13.7 Mbps HD|15.2 Mbps HD|16.7 Mbps HD|18.2 Mbps HD|20.0 Mbps HD|40.0 Mbps HD|100.0 Mbps HD [default]|1000.0 Mbps HD" visible="eq(-1,true)" default="17" />
|
<setting id="transcoderVideoQualities" type="enum" label="30160" values="420x420, 320Kbps|576x320, 720Kbps|720x480, 1.5Mbps|1024x768, 2Mbps|1280x720, 3Mbps|1280x720, 4Mbps|1920x1080, 8Mbps|1920x1080, 10Mbps|1920x1080, 12Mbps|1920x1080, 20Mbps|1920x1080, 40Mbps" visible="eq(-1,true)" default="11" />
|
||||||
<setting id="transcodeH265" type="enum" label="30522" default="0" values="Disabled|480p(and higher)|720p(and higher)|1080p" />
|
<setting id="audioBoost" type="slider" label="Boost audio when transcoding" default="0" range="0,10,100" option="int"/>
|
||||||
<setting id="markPlayed" type="number" visible="false" default="90" />
|
<setting id="transcodeH265" type="enum" label="30522" default="0" values="Disabled (default)|480p (and higher)|720p (and higher)|1080p" />
|
||||||
|
<setting id="subtitleSize" label="Subtitle size when transcoding" type="slider" option="int" range="0,30,300" default="100" />
|
||||||
|
<setting id="markPlayed" type="number" visible="false" default="95" />
|
||||||
<setting id="failedCount" type="number" visible="false" default="0" />
|
<setting id="failedCount" type="number" visible="false" default="0" />
|
||||||
<setting id="networkCreds" type="text" visible="false" default="" />
|
<setting id="networkCreds" type="text" visible="false" default="" />
|
||||||
</category>
|
</category>
|
||||||
|
|
|
@ -81,7 +81,7 @@ class Service():
|
||||||
"emby_syncRunning", "emby_dbCheck", "emby_kodiScan",
|
"emby_syncRunning", "emby_dbCheck", "emby_kodiScan",
|
||||||
"emby_shouldStop", "emby_currUser", "emby_dbScan", "emby_sessionId",
|
"emby_shouldStop", "emby_currUser", "emby_dbScan", "emby_sessionId",
|
||||||
"emby_initialScan", "emby_customplaylist", "emby_playbackProps",
|
"emby_initialScan", "emby_customplaylist", "emby_playbackProps",
|
||||||
"plex_runLibScan"
|
"plex_runLibScan", "plex_username"
|
||||||
]
|
]
|
||||||
for prop in properties:
|
for prop in properties:
|
||||||
window(prop, clear=True)
|
window(prop, clear=True)
|
||||||
|
|
Loading…
Add table
Reference in a new issue