Overhaul Plex Companion

This commit is contained in:
tomkat83 2016-02-07 12:38:50 +01:00
parent 125daea6ef
commit 0f14019e5b
15 changed files with 355 additions and 268 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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:

View file

@ -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(

View file

@ -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

View file

@ -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,

View file

@ -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")

View file

@ -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

View file

@ -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']

View file

@ -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")

View file

@ -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]:

View file

@ -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))

View file

@ -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>

View file

@ -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)