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.plexversion = client.getVersion()
|
||||
self.platform = client.getPlatform()
|
||||
self.user = utils.window('plex_username')
|
||||
self.userId = utils.window('emby_currUser')
|
||||
self.token = utils.window('emby_accessToken%s' % self.userId)
|
||||
self.server = utils.window('emby_server%s' % self.userId)
|
||||
|
@ -782,6 +783,8 @@ class PlexAPI():
|
|||
"""
|
||||
# Get addon infos
|
||||
xargs = {
|
||||
'Accept': '*/*',
|
||||
'Connection': 'keep-alive',
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
# "Access-Control-Allow-Origin": "*",
|
||||
'X-Plex-Language': 'en',
|
||||
|
@ -795,6 +798,9 @@ class PlexAPI():
|
|||
'X-Plex-Version': self.plexversion,
|
||||
'X-Plex-Client-Identifier': self.clientId,
|
||||
'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:
|
||||
|
@ -1374,6 +1380,7 @@ class API():
|
|||
self.part = 0
|
||||
self.clientinfo = clientinfo.ClientInfo()
|
||||
self.clientId = self.clientinfo.getDeviceId()
|
||||
self.user = utils.window('plex_username')
|
||||
self.userId = utils.window('emby_currUser')
|
||||
self.server = utils.window('emby_server%s' % self.userId)
|
||||
self.token = utils.window('emby_accessToken%s' % self.userId)
|
||||
|
@ -1591,6 +1598,9 @@ class API():
|
|||
genre.append(child.attrib['tag'])
|
||||
return genre
|
||||
|
||||
def getGuid(self):
|
||||
return self.item.attrib.get('guid')
|
||||
|
||||
def getProvider(self, providername=None):
|
||||
"""
|
||||
providername: e.g. 'imdb'
|
||||
|
@ -2053,8 +2063,7 @@ class API():
|
|||
allartworks['Primary'] = artwork
|
||||
return allartworks
|
||||
|
||||
def getTranscodeVideoPath(self, action, quality={}, subtitle={},
|
||||
audioboost=None, options={}):
|
||||
def getTranscodeVideoPath(self, action, quality={}):
|
||||
"""
|
||||
|
||||
To be called on a VIDEO level of PMS xml response!
|
||||
|
@ -2070,38 +2079,18 @@ class API():
|
|||
'maxVideoBitrate': e.g. '2000' (in kbits)
|
||||
}
|
||||
(one or several of these options)
|
||||
subtitle {'selected', 'dontBurnIn', 'size'}
|
||||
audioboost e.g. 100
|
||||
options dict() of PlexConnect-options as received from aTV
|
||||
Output:
|
||||
final URL to pull in PMS transcoder
|
||||
|
||||
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
|
||||
if action == "DirectPlay":
|
||||
path = self.item[0][self.part].attrib['key']
|
||||
url = self.server + path
|
||||
# e.g. Trailers already feature an '?'!
|
||||
if '?' in url:
|
||||
url += '&' + urlencode(xargs)
|
||||
else:
|
||||
|
@ -2111,49 +2100,20 @@ class API():
|
|||
# For Direct Streaming or Transcoding
|
||||
# Path/key to VIDEO item of xml PMS response is needed, not part
|
||||
path = self.item.attrib['key']
|
||||
# transcodePath = self.server + \
|
||||
# '/video/:/transcode/universal/start.m3u8?'
|
||||
transcodePath = self.server + \
|
||||
'/video/:/transcode/universal/start.mkv?'
|
||||
# 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
|
||||
# }
|
||||
'/video/:/transcode/universal/start.m3u8?'
|
||||
args = {
|
||||
'copyts': 1,
|
||||
'path': path,
|
||||
'mediaIndex': 0, # Probably refering to XML reply sheme
|
||||
'partIndex': self.part,
|
||||
'protocol': 'http', # seen in the wild: 'dash', 'http', 'hls'
|
||||
'protocol': 'hls', # seen in the wild: 'dash', 'http', 'hls'
|
||||
'session': self.clientId,
|
||||
'offset': 0, # Resume point
|
||||
# 'offset': 0, # Resume point
|
||||
'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":
|
||||
argsUpdate = {
|
||||
'directPlay': '0',
|
||||
|
@ -2170,7 +2130,6 @@ class API():
|
|||
args.update(argsUpdate)
|
||||
|
||||
url = transcodePath + \
|
||||
urlencode(clientArgs) + '&' + \
|
||||
urlencode(xargs) + '&' + \
|
||||
urlencode(args)
|
||||
return url
|
||||
|
@ -2185,20 +2144,17 @@ class API():
|
|||
except (TypeError, KeyError, IndexError):
|
||||
return
|
||||
|
||||
itemid = self.getRatingKey()
|
||||
kodiindex = 0
|
||||
for stream in mediastreams:
|
||||
index = stream.attrib['id']
|
||||
# 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.
|
||||
if (stream.attrib.get('streamType', '') == "3" and
|
||||
stream.attrib.get('format', '')):
|
||||
if stream.attrib.get('streamType') == "3" and key:
|
||||
|
||||
# Direct stream
|
||||
# PLEX: TODO!!
|
||||
url = ("%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
|
||||
% (self.server, itemid, itemid, index))
|
||||
|
||||
url = ("%s%s" % (self.server, key))
|
||||
url = self.addPlexCredentialsToUrl(url)
|
||||
# map external subtitles for mapping
|
||||
mapping[kodiindex] = index
|
||||
externalsubs.append(url)
|
||||
|
|
|
@ -83,10 +83,9 @@ class PlexCompanion(threading.Thread):
|
|||
|
||||
if not is_running:
|
||||
self.logMsg("PleXBMC Helper has started", 0)
|
||||
|
||||
is_running = True
|
||||
if message_count % 1 == 0:
|
||||
subscribers.subMgr.notify()
|
||||
|
||||
subscribers.subMgr.notify()
|
||||
settings['serverList'] = self.client.getServerList()
|
||||
except:
|
||||
self.logMsg("Error in loop, continuing anyway", 1)
|
||||
|
|
|
@ -3,8 +3,10 @@ from urllib import urlencode
|
|||
from ast import literal_eval
|
||||
from urlparse import urlparse, parse_qs
|
||||
import re
|
||||
import json
|
||||
|
||||
from xbmcaddon import Addon
|
||||
import xbmc
|
||||
|
||||
import downloadutils
|
||||
from utils import logMsg, settings
|
||||
|
@ -85,7 +87,9 @@ def LiteralEval(string):
|
|||
def GetMethodFromPlexType(plexType):
|
||||
methods = {
|
||||
'movie': 'add_update',
|
||||
'episode': 'add_updateEpisode'
|
||||
'episode': 'add_updateEpisode',
|
||||
'show': 'add_update',
|
||||
'season': 'add_updateSeason'
|
||||
}
|
||||
return methods[plexType]
|
||||
|
||||
|
@ -107,6 +111,15 @@ def EmbyItemtypes():
|
|||
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):
|
||||
"""
|
||||
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)
|
||||
return None
|
||||
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():
|
||||
|
||||
# Borg - multiple instances, shared state
|
||||
# _shared_state = {}
|
||||
_shared_state = {}
|
||||
clientInfo = clientinfo.ClientInfo()
|
||||
|
||||
# Requests session
|
||||
|
@ -41,8 +41,7 @@ class DownloadUtils():
|
|||
|
||||
def __init__(self):
|
||||
|
||||
# self.__dict__ = self._shared_state
|
||||
pass
|
||||
self.__dict__ = self._shared_state
|
||||
|
||||
def setUsername(self, username):
|
||||
# Reserved for userclient only
|
||||
|
@ -215,9 +214,12 @@ class DownloadUtils():
|
|||
# For Plex Companion
|
||||
elif type == "POSTXML":
|
||||
r = s.post(url, postBody, timeout=timeout, headers=header)
|
||||
elif type == "PUT":
|
||||
r = s.put(url, timeout=timeout, headers=header)
|
||||
|
||||
except AttributeError:
|
||||
# request session does not exists
|
||||
self.logMsg("Request session does not exist: start one", 1)
|
||||
# Get user information
|
||||
self.userId = utils.window('emby_currUser')
|
||||
self.server = utils.window('emby_server%s' % self.userId)
|
||||
|
@ -270,6 +272,13 @@ class DownloadUtils():
|
|||
cert=cert,
|
||||
verify=verifyssl)
|
||||
|
||||
elif type == "PUT":
|
||||
r = requests.put(url,
|
||||
json=postBody,
|
||||
headers=header,
|
||||
timeout=timeout,
|
||||
cert=cert,
|
||||
verify=verifyssl)
|
||||
# If user is not authenticated
|
||||
elif not authenticate:
|
||||
|
||||
|
@ -301,6 +310,12 @@ class DownloadUtils():
|
|||
timeout=timeout,
|
||||
verify=verifyssl)
|
||||
|
||||
elif type == "PUT":
|
||||
r = requests.put(url,
|
||||
json=postBody,
|
||||
headers=header,
|
||||
timeout=timeout,
|
||||
verify=verifyssl)
|
||||
##### THE RESPONSE #####
|
||||
# self.logMsg(r.url, 2)
|
||||
if r.status_code == 204:
|
||||
|
|
|
@ -6,7 +6,6 @@ import json
|
|||
import os
|
||||
import sys
|
||||
import urlparse
|
||||
import re
|
||||
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
|
@ -27,7 +26,6 @@ import api
|
|||
|
||||
import PlexAPI
|
||||
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
|
||||
windowArgs = [
|
||||
# 'containerKey'
|
||||
'playQueueID',
|
||||
'playQueueVersion',
|
||||
'playQueueShuffled']
|
||||
'playQueueVersion']
|
||||
for arg in windowArgs:
|
||||
utils.window(arg, xml.attrib.get(arg, ''))
|
||||
utils.window(arg, value=xml.attrib.get(arg))
|
||||
|
||||
# Get resume point
|
||||
resume1 = PlexFunctions.ConvertPlexToKodiTime(utils.IntFromStr(
|
||||
|
|
|
@ -63,7 +63,7 @@ class ThreadedGetMetadata(threading.Thread):
|
|||
continue
|
||||
# Download Metadata
|
||||
plexXML = PlexFunctions.GetPlexMetadata(updateItem['itemId'])
|
||||
if not plexXML:
|
||||
if plexXML is None:
|
||||
# Did not receive a valid XML - skip that item for now
|
||||
queue.task_done()
|
||||
continue
|
||||
|
@ -116,7 +116,6 @@ class ThreadedProcessMetadata(threading.Thread):
|
|||
# grabs item from queue
|
||||
try:
|
||||
updateItem = queue.get(block=False)
|
||||
# Empty queue
|
||||
except Queue.Empty:
|
||||
continue
|
||||
# Do the work; lock to be sure we've only got 1 Thread
|
||||
|
@ -306,7 +305,7 @@ class LibrarySync(threading.Thread):
|
|||
# Populate self.updatelist
|
||||
self.GetUpdatelist(items,
|
||||
PlexFunctions.GetItemClassFromType(plexType),
|
||||
PlexFunctions.GetItemClassFromType(plexType),
|
||||
PlexFunctions.GetMethodFromPlexType(plexType),
|
||||
view['name'],
|
||||
view['id'])
|
||||
# Process self.updatelist
|
||||
|
|
|
@ -72,8 +72,11 @@ class PlaybackUtils():
|
|||
# Close DB
|
||||
embyconn.close()
|
||||
|
||||
self.logMsg("Playlist size now: %s" % self.playlist.size(), 1)
|
||||
# Kick off playback
|
||||
if startPlayer:
|
||||
self.logMsg("Starting up a new xbmc.Player() instance", 1)
|
||||
self.logMsg("Starting position: %s" % self.startPos, 1)
|
||||
Player = xbmc.Player()
|
||||
Player.play(self.playlist, startpos=self.startPos)
|
||||
if resume:
|
||||
|
@ -82,6 +85,7 @@ class PlaybackUtils():
|
|||
except:
|
||||
self.logMsg("Error, could not resume", -1)
|
||||
else:
|
||||
self.logMsg("xbmc.Player() instance already up", 1)
|
||||
# Delete the last playlist item because we have added it already
|
||||
filename = self.playlist[-1].getfilename()
|
||||
self.playlist.remove(filename)
|
||||
|
@ -131,8 +135,8 @@ class PlaybackUtils():
|
|||
if playmethod == "Transcode":
|
||||
utils.window('emby_%s.playmethod' % playurl, clear=True)
|
||||
playurl = playutils.audioSubsPref(
|
||||
playurl, listitem, part=counter)
|
||||
utils.window('emby_%s.playmethod' % playurl, value="Transcode")
|
||||
listitem, playurl, part=counter)
|
||||
utils.window('emby_%s.playmethod' % playurl, "Transcode")
|
||||
|
||||
self.setProperties(playurl, listitem)
|
||||
# Update the playurl to the PMS xml response (hence no loop)
|
||||
|
@ -161,12 +165,15 @@ class PlaybackUtils():
|
|||
utils.window('%s.itemid' % embyitem, value=itemid)
|
||||
|
||||
# We need to keep track of playQueueItemIDs for Plex Companion
|
||||
playQueueItemID = self.API.GetPlayQueueItemID()
|
||||
utils.window(
|
||||
'plex_%s.playQueueItemID' % playurl, playQueueItemID)
|
||||
'plex_%s.playQueueItemID'
|
||||
% playurl, self.API.GetPlayQueueItemID())
|
||||
utils.window(
|
||||
'plex_%s.playlistPosition'
|
||||
% playurl, str(self.currentPosition))
|
||||
utils.window(
|
||||
'plex_%s.guid'
|
||||
% playurl, self.API.getGuid())
|
||||
|
||||
if itemtype == "episode":
|
||||
utils.window('%s.refreshid' % embyitem,
|
||||
|
|
|
@ -38,6 +38,14 @@ class Player(xbmc.Player):
|
|||
|
||||
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):
|
||||
return self.playStats
|
||||
|
||||
|
@ -68,7 +76,8 @@ class Player(xbmc.Player):
|
|||
if 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
|
||||
itemId = utils.window("emby_%s.itemid" % currentFile)
|
||||
tryCount = 0
|
||||
|
@ -93,6 +102,11 @@ class Player(xbmc.Player):
|
|||
itemType = utils.window("%s.type" % embyitem)
|
||||
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
|
||||
utils.window('emby_customPlaylist.seektime')):
|
||||
|
@ -214,10 +228,18 @@ class Player(xbmc.Player):
|
|||
runtime = xbmcplayer.getTotalTime()
|
||||
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
|
||||
data = {
|
||||
|
||||
'runtime': runtime,
|
||||
'playQueueVersion': playQueueVersion,
|
||||
'playQueueID': playQueueID,
|
||||
'playQueueItemID': playQueueItemID,
|
||||
'runtime': runtime * 1000,
|
||||
'item_id': itemId,
|
||||
'refresh_id': refresh_id,
|
||||
'currentfile': currentFile,
|
||||
|
@ -247,7 +269,10 @@ class Player(xbmc.Player):
|
|||
self.playStats[playMethod] = 1'''
|
||||
|
||||
def reportPlayback(self):
|
||||
|
||||
# Don't use if Plex Companion is enabled
|
||||
if not self.doNotify:
|
||||
return
|
||||
|
||||
self.logMsg("reportPlayback Called", 2)
|
||||
xbmcplayer = self.xbmcplayer
|
||||
|
||||
|
@ -311,6 +336,12 @@ class Player(xbmc.Player):
|
|||
'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":
|
||||
# Track can't be changed, keep reporting the same index
|
||||
postdata['AudioStreamIndex'] = audioindex
|
||||
|
@ -422,12 +453,14 @@ class Player(xbmc.Player):
|
|||
utils.window('emby_customPlaylist.seektime', clear=True)
|
||||
utils.window('emby_playbackProps', clear=True)
|
||||
self.logMsg("Clear playlist properties.", 1)
|
||||
utils.window('suspend_LibraryThread', clear=True)
|
||||
self.stopAll()
|
||||
|
||||
def onPlayBackEnded( self ):
|
||||
# Will be called when xbmc stops playing a file
|
||||
self.logMsg("ONPLAYBACK_ENDED", 2)
|
||||
utils.window('emby_customPlaylist.seektime', clear=True)
|
||||
utils.window('suspend_LibraryThread', clear=True)
|
||||
self.stopAll()
|
||||
|
||||
def stopAll(self):
|
||||
|
@ -457,7 +490,7 @@ class Player(xbmc.Player):
|
|||
|
||||
if currentPosition and runtime:
|
||||
try:
|
||||
percentComplete = (currentPosition * 10000) / int(runtime)
|
||||
percentComplete = currentPosition / int(runtime)
|
||||
except ZeroDivisionError:
|
||||
# Runtime is 0.
|
||||
percentComplete = 0
|
||||
|
@ -474,9 +507,8 @@ class Player(xbmc.Player):
|
|||
# Stop transcoding
|
||||
if playMethod == "Transcode":
|
||||
self.logMsg("Transcoding for %s terminated." % itemid, 1)
|
||||
deviceId = self.clientInfo.getDeviceId()
|
||||
url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId
|
||||
doUtils.downloadUrl(url, type="DELETE")
|
||||
url = "{server}/video/:/transcode/universal/stop?session=%s" % self.clientInfo.getDeviceId()
|
||||
doUtils.downloadUrl(url, type="GET")
|
||||
|
||||
# Send the delete action to the server.
|
||||
offerDelete = False
|
||||
|
@ -490,6 +522,8 @@ class Player(xbmc.Player):
|
|||
# Delete could be disabled, even if the subsetting is enabled.
|
||||
offerDelete = False
|
||||
|
||||
# Plex: never delete
|
||||
offerDelete = False
|
||||
if percentComplete >= markPlayedAt and offerDelete:
|
||||
if utils.settings('skipConfirmDelete') != "true":
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
|
@ -502,7 +536,22 @@ class Player(xbmc.Player):
|
|||
url = "{server}/emby/Items/%s?format=json" % itemid
|
||||
self.logMsg("Deleting request: %s" % itemid)
|
||||
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()
|
||||
|
||||
def stopPlayback(self, data):
|
||||
|
@ -517,7 +566,7 @@ class Player(xbmc.Player):
|
|||
'ratingKey': itemId,
|
||||
'state': 'stopped', # 'stopped', 'paused', 'buffering', 'playing'
|
||||
'time': int(playTime) * 1000,
|
||||
'duration': int(duration) * 1000
|
||||
'duration': int(duration)
|
||||
}
|
||||
url = url + urlencode(args)
|
||||
self.doUtils.downloadUrl(url, type="GET")
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#################################################################################################
|
||||
|
||||
from urllib import urlencode
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
import xbmcvfs
|
||||
|
@ -10,6 +12,7 @@ import clientinfo
|
|||
import utils
|
||||
|
||||
import PlexAPI
|
||||
import PlexFunctions
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
@ -45,16 +48,19 @@ class PlayUtils():
|
|||
# Set playmethod property
|
||||
utils.window('emby_%s.playmethod' % playurl, "DirectPlay")
|
||||
|
||||
elif self.isDirectStream():
|
||||
self.logMsg("File is direct streaming.", 1)
|
||||
playurl = self.API.getTranscodeVideoPath('DirectStream')
|
||||
# Set playmethod property
|
||||
utils.window('emby_%s.playmethod' % playurl, "DirectStream")
|
||||
# Currently no direct streaming possible - needs investigation
|
||||
# elif self.isDirectStream():
|
||||
# self.logMsg("File is direct streaming.", 1)
|
||||
# playurl = self.API.getTranscodeVideoPath('DirectStream')
|
||||
# # Set playmethod property
|
||||
# utils.window('emby_%s.playmethod' % playurl, "DirectStream")
|
||||
|
||||
elif self.isTranscoding():
|
||||
self.logMsg("File is transcoding.", 1)
|
||||
quality = {
|
||||
'maxVideoBitrate': self.getBitrate()
|
||||
'maxVideoBitrate': self.getBitrate(),
|
||||
'videoResolution': self.getResolution(),
|
||||
'videoQuality': '100'
|
||||
}
|
||||
playurl = self.API.getTranscodeVideoPath('Transcode',
|
||||
quality=quality)
|
||||
|
@ -89,13 +95,9 @@ class PlayUtils():
|
|||
self.logMsg("Can't direct play, user enabled play from HTTP.", 1)
|
||||
return False
|
||||
|
||||
if not self.h265enabled():
|
||||
if self.h265enabled():
|
||||
return False
|
||||
|
||||
# Found with e.g. trailers
|
||||
# if self.API.getDataFromPartOrMedia('optimizedForStreaming') == '1':
|
||||
# return False
|
||||
|
||||
return True
|
||||
|
||||
def directPlay(self):
|
||||
|
@ -150,22 +152,23 @@ class PlayUtils():
|
|||
return False
|
||||
|
||||
def h265enabled(self):
|
||||
"""
|
||||
Returns True if we need to transcode
|
||||
"""
|
||||
videoCodec = self.API.getVideoCodec()
|
||||
codec = videoCodec['videocodec']
|
||||
resolution = videoCodec['resolution']
|
||||
# 720p
|
||||
if ((utils.settings('transcode720H265') == "true") and
|
||||
("h265" in codec) and
|
||||
(resolution in "720 1080")):
|
||||
self.logMsg("Option to transcode 720P/h265 enabled.", 0)
|
||||
h265 = self.getH265()
|
||||
if not ('h265' in codec or 'hevc' in codec) or (h265 is None):
|
||||
return False
|
||||
# 1080p
|
||||
if ((utils.settings('transcodeH265') == "true") and
|
||||
("h265" in codec) and
|
||||
(resolution == "1080")):
|
||||
self.logMsg("Option to transcode 1080P/h265 enabled.", 0)
|
||||
return False
|
||||
return True
|
||||
|
||||
if resolution >= h265:
|
||||
self.logMsg("Option to transcode h265 enabled. Resolution media: "
|
||||
"%s, transcoding limit resolution: %s"
|
||||
% (resolution, h265), 1)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def isDirectStream(self):
|
||||
if not self.h265enabled():
|
||||
|
@ -247,88 +250,106 @@ class PlayUtils():
|
|||
return playurl
|
||||
|
||||
def getBitrate(self):
|
||||
|
||||
# get the addon video quality
|
||||
videoQuality = utils.settings('videoBitrate')
|
||||
videoQuality = utils.settings('transcoderVideoQualities')
|
||||
bitrate = {
|
||||
|
||||
'0': 664,
|
||||
'1': 996,
|
||||
'2': 1320,
|
||||
'0': 320,
|
||||
'1': 720,
|
||||
'2': 1500,
|
||||
'3': 2000,
|
||||
'4': 3200,
|
||||
'5': 4700,
|
||||
'6': 6200,
|
||||
'7': 7700,
|
||||
'8': 9200,
|
||||
'9': 10700,
|
||||
'10': 12200,
|
||||
'11': 13700,
|
||||
'12': 15200,
|
||||
'13': 16700,
|
||||
'14': 18200,
|
||||
'15': 20000,
|
||||
'16': 40000,
|
||||
'17': 100000,
|
||||
'18': 1000000
|
||||
'4': 3000,
|
||||
'5': 4000,
|
||||
'6': 8000,
|
||||
'7': 10000,
|
||||
'8': 12000,
|
||||
'9': 20000,
|
||||
'10': 40000,
|
||||
}
|
||||
|
||||
# max bit rate supported by server (max signed 32bit integer)
|
||||
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
|
||||
# Present the list of audio to select from
|
||||
audioStreamsList = {}
|
||||
audioStreamsList = []
|
||||
audioStreams = []
|
||||
audioStreamsChannelsList = {}
|
||||
subtitleStreamsList = {}
|
||||
subtitleStreams = ['No subtitles']
|
||||
# audioStreamsChannelsList = []
|
||||
subtitleStreamsList = []
|
||||
subtitleStreams = ['1 No subtitles']
|
||||
downloadableStreams = []
|
||||
selectAudioIndex = ""
|
||||
# selectAudioIndex = ""
|
||||
selectSubsIndex = ""
|
||||
playurlprefs = "%s" % url
|
||||
playurlprefs = {}
|
||||
|
||||
# Set part where we're at
|
||||
self.API.setPartNumber(part)
|
||||
item = self.item
|
||||
try:
|
||||
mediasources = item[0]
|
||||
mediastreams = mediasources[part]
|
||||
mediastreams = self.item[0][part]
|
||||
except (TypeError, KeyError, IndexError):
|
||||
return
|
||||
|
||||
audioNum = 0
|
||||
# Remember 'no subtitles'
|
||||
subNum = 1
|
||||
for stream in mediastreams:
|
||||
# Since Emby returns all possible tracks together, have to sort them.
|
||||
index = stream['index']
|
||||
type = stream['streamType']
|
||||
index = stream.attrib['id']
|
||||
type = stream.attrib['streamType']
|
||||
|
||||
# Audio
|
||||
if type == "2":
|
||||
codec = stream['codec']
|
||||
channelLayout = stream.get('audioChannelLayout', "")
|
||||
codec = stream.attrib['codec']
|
||||
channelLayout = stream.attrib.get('audioChannelLayout', "")
|
||||
|
||||
try:
|
||||
track = "%s - %s - %s %s" % (index, stream['language'], codec, channelLayout)
|
||||
track = "%s %s - %s %s" % (audioNum+1, stream.attrib['language'], codec, channelLayout)
|
||||
except:
|
||||
track = "%s - %s %s" % (index, codec, channelLayout)
|
||||
track = "%s 'unknown' - %s %s" % (audioNum+1, codec, channelLayout)
|
||||
|
||||
audioStreamsChannelsList[index] = stream['channels']
|
||||
audioStreamsList[track] = index
|
||||
#audioStreamsChannelsList[audioNum] = stream.attrib['channels']
|
||||
audioStreamsList.append(index)
|
||||
audioStreams.append(track)
|
||||
audioNum += 1
|
||||
|
||||
# Subtitles
|
||||
elif type == "3":
|
||||
'''if stream['IsExternal']:
|
||||
continue'''
|
||||
try:
|
||||
track = "%s - %s" % (index, stream['language'])
|
||||
track = "%s %s" % (subNum+1, stream.attrib['language'])
|
||||
except:
|
||||
track = "%s - %s" % (index, stream['codec'])
|
||||
track = "%s 'unknown' (%s)" % (subNum+1, stream.attrib['codec'])
|
||||
|
||||
default = stream.get('default', None)
|
||||
forced = stream.get('forced', None)
|
||||
downloadable = True if stream.get('format', None) else False
|
||||
default = stream.attrib.get('default')
|
||||
forced = stream.attrib.get('forced')
|
||||
downloadable = stream.attrib.get('key')
|
||||
|
||||
if default:
|
||||
track = "%s - Default" % track
|
||||
|
@ -337,53 +358,58 @@ class PlayUtils():
|
|||
if downloadable:
|
||||
downloadableStreams.append(index)
|
||||
|
||||
subtitleStreamsList[track] = index
|
||||
subtitleStreamsList.append(index)
|
||||
subtitleStreams.append(track)
|
||||
subNum += 1
|
||||
|
||||
|
||||
if len(audioStreams) > 1:
|
||||
if audioNum > 1:
|
||||
resp = xbmcgui.Dialog().select("Choose the audio stream", audioStreams)
|
||||
if resp > -1:
|
||||
# User selected audio
|
||||
selected = audioStreams[resp]
|
||||
selectAudioIndex = audioStreamsList[selected]
|
||||
playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
|
||||
else: # User backed out of selection
|
||||
playurlprefs += "&AudioStreamIndex=%s" % mediasources['DefaultAudioStreamIndex']
|
||||
playurlprefs['audioStreamID'] = audioStreamsList[resp]
|
||||
else: # User backed out of selection - let PMS decide
|
||||
pass
|
||||
else: # There's only one audiotrack.
|
||||
selectAudioIndex = audioStreamsList[audioStreams[0]]
|
||||
playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
|
||||
playurlprefs['audioStreamID'] = audioStreamsList[0]
|
||||
|
||||
if len(subtitleStreams) > 1:
|
||||
# Add audio boost
|
||||
playurlprefs['audioBoost'] = utils.settings('audioBoost')
|
||||
|
||||
if subNum > 1:
|
||||
resp = xbmcgui.Dialog().select("Choose the subtitle stream", subtitleStreams)
|
||||
if resp == 0:
|
||||
# User selected no subtitles
|
||||
pass
|
||||
playurlprefs["skipSubtitles"] = 1
|
||||
elif resp > -1:
|
||||
# User selected subtitles
|
||||
selected = subtitleStreams[resp]
|
||||
selectSubsIndex = subtitleStreamsList[selected]
|
||||
selectSubsIndex = subtitleStreamsList[resp-1]
|
||||
|
||||
# Load subtitles in the listitem if downloadable
|
||||
if selectSubsIndex in downloadableStreams:
|
||||
|
||||
itemid = item['Id']
|
||||
url = [("%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
|
||||
% (self.server, itemid, itemid, selectSubsIndex))]
|
||||
self.logMsg("Set up subtitles: %s %s" % (selectSubsIndex, url), 1)
|
||||
listitem.setSubtitles(url)
|
||||
url = "%s/library/streams/%s" \
|
||||
% (self.server, selectSubsIndex)
|
||||
url = self.API.addPlexCredentialsToUrl(url)
|
||||
self.logMsg("Downloadable sub: %s: %s" % (selectSubsIndex, url), 1)
|
||||
listitem.setSubtitles([url])
|
||||
else:
|
||||
# Burn subtitles
|
||||
playurlprefs += "&SubtitleStreamIndex=%s" % selectSubsIndex
|
||||
self.logMsg('Need to burn in subtitle %s' % selectSubsIndex, 1)
|
||||
playurlprefs["subtitleStreamID"] = selectSubsIndex
|
||||
playurlprefs["subtitleSize"] = utils.settings('subtitleSize')
|
||||
|
||||
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
|
||||
audioChannels = audioStreamsChannelsList.get(selectAudioIndex, 0)
|
||||
if audioChannels > 2:
|
||||
playurlprefs += "&AudioBitrate=384000"
|
||||
else:
|
||||
playurlprefs += "&AudioBitrate=192000"
|
||||
# audioChannels = audioStreamsChannelsList.get(selectAudioIndex, 0)
|
||||
# if audioChannels > 2:
|
||||
# playurlprefs += "&AudioBitrate=384000"
|
||||
# else:
|
||||
# playurlprefs += "&AudioBitrate=192000"
|
||||
|
||||
return playurlprefs
|
||||
return url
|
||||
|
|
|
@ -115,7 +115,7 @@ def parseJSONRPC(jsonraw):
|
|||
return parsed.get('result', {})
|
||||
|
||||
def getXMLHeader():
|
||||
return '<?xml version="1.0" encoding="utf-8"?>'+"\r\n"
|
||||
return '<?xml version="1.0" encoding="utf-8" ?>'+"\r\n"
|
||||
|
||||
def getOKMsg():
|
||||
return getXMLHeader() + '<Response code="200" status="OK" />'
|
||||
|
@ -176,7 +176,10 @@ def getPhotoPlayerId(players = False):
|
|||
return players.get(xbmc_photo(), {}).get('playerid', None)
|
||||
|
||||
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):
|
||||
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)
|
||||
jsonrpc("Application.SetVolume", {"volume": volume})
|
||||
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())
|
||||
offset = params.get('viewOffset', params.get('offset', "0"))
|
||||
protocol = params.get('protocol', "http")
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import re
|
||||
import threading
|
||||
import xbmcgui
|
||||
from xml.dom.minidom import parseString
|
||||
from functions import *
|
||||
from settings import settings
|
||||
from httppersist import requests
|
||||
|
||||
from xbmc import Player
|
||||
import xbmcgui
|
||||
import downloadutils
|
||||
from utils import window
|
||||
import PlexFunctions as pf
|
||||
|
||||
class SubscriptionManager:
|
||||
def __init__(self):
|
||||
|
@ -13,19 +17,18 @@ class SubscriptionManager:
|
|||
self.info = {}
|
||||
self.lastkey = ""
|
||||
self.containerKey = ""
|
||||
self.playQueueID = ''
|
||||
self.playQueueVersion = 1
|
||||
self.lastratingkey = ""
|
||||
self.volume = 0
|
||||
self.guid = ""
|
||||
self.mute = '0'
|
||||
self.server = ""
|
||||
self.protocol = "http"
|
||||
self.port = ""
|
||||
self.playerprops = {}
|
||||
self.download = downloadutils.DownloadUtils()
|
||||
|
||||
self.xbmcplayer = Player()
|
||||
|
||||
def getVolume(self):
|
||||
self.volume = getVolume()
|
||||
self.volume, self.mute = getVolume()
|
||||
|
||||
def msg(self, players):
|
||||
msg = getXMLHeader()
|
||||
|
@ -42,7 +45,6 @@ class SubscriptionManager:
|
|||
else:
|
||||
self.mainlocation = "navigation"
|
||||
msg += ' location="%s">' % self.mainlocation
|
||||
|
||||
msg += self.getTimelineXML(getAudioPlayerId(players), plex_audio())
|
||||
msg += self.getTimelineXML(getPhotoPlayerId(players), plex_photo())
|
||||
msg += self.getTimelineXML(getVideoPlayerId(players), plex_video())
|
||||
|
@ -59,48 +61,63 @@ class SubscriptionManager:
|
|||
else:
|
||||
state = "stopped"
|
||||
time = 0
|
||||
ret = "\r\n"+'<Timeline location="%s" state="%s" time="%s" type="%s"' % (self.mainlocation, state, time, ptype)
|
||||
if playerid is not None:
|
||||
WINDOW = xbmcgui.Window(10000)
|
||||
|
||||
# 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 = 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 = "\r\n"+' <Timeline state="%s" time="%s" type="%s"' % (state, time, ptype)
|
||||
if playerid is None:
|
||||
ret += ' seekRange="0-0"'
|
||||
ret += ' />'
|
||||
return ret
|
||||
|
||||
WINDOW = xbmcgui.Window(10000)
|
||||
|
||||
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
|
||||
|
||||
|
||||
def updateCommandID(self, uuid, commandID):
|
||||
if commandID and self.subscribers.get(uuid, False):
|
||||
self.subscribers[uuid].commandID = int(commandID)
|
||||
|
@ -126,14 +143,15 @@ class SubscriptionManager:
|
|||
info = self.playerprops[p.get('playerid')]
|
||||
params = {}
|
||||
params['containerKey'] = (self.containerKey or "/library/metadata/900000")
|
||||
if self.playQueueID:
|
||||
params['playQueueID'] = self.playQueueID
|
||||
if info.get('playQueueID'):
|
||||
params['containerKey'] = '/playQueues/' + info['playQueueID']
|
||||
params['playQueueVersion'] = info['playQueueVersion']
|
||||
params['playQueueItemID'] = info['playQueueItemID']
|
||||
params['key'] = (self.lastkey or "/library/metadata/900000")
|
||||
params['ratingKey'] = (self.lastratingkey or "900000")
|
||||
params['state'] = info['state']
|
||||
params['time'] = info['time']
|
||||
params['duration'] = info['duration']
|
||||
params['playQueueVersion'] = self.playQueueVersion
|
||||
serv = getServerByHost(self.server)
|
||||
url = serv.get('protocol', 'http') + '://' \
|
||||
+ serv.get('server', 'localhost') + ':' \
|
||||
|
@ -171,12 +189,20 @@ class SubscriptionManager:
|
|||
info = {}
|
||||
try:
|
||||
# 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"]}))
|
||||
info['time'] = timeToMillis(props['time'])
|
||||
info['duration'] = timeToMillis(props['totaltime'])
|
||||
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:
|
||||
info['time'] = 0
|
||||
info['duration'] = 0
|
||||
|
@ -184,7 +210,7 @@ class SubscriptionManager:
|
|||
info['shuffle'] = False
|
||||
# get the volume from the application
|
||||
info['volume'] = self.volume
|
||||
info['guid'] = self.guid
|
||||
info['mute'] = self.mute
|
||||
|
||||
return info
|
||||
|
||||
|
@ -216,21 +242,11 @@ class Subscriber:
|
|||
printDebug("sending xml to subscriber %s: %s" % (self.tostr(), msg))
|
||||
url = self.protocol + '://' + self.host + ':' + self.port \
|
||||
+ "/:/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(
|
||||
url,
|
||||
postBody=msg,
|
||||
type="POSTXML",
|
||||
headerOptions=headerOptions)
|
||||
type="POSTXML")
|
||||
# if not requests.post(self.host, self.port, "/:/timeline", msg, getPlexHeaders(), self.protocol):
|
||||
# subMgr.removeSubscriber(self.uuid)
|
||||
if response in [False, None, 401]:
|
||||
|
|
|
@ -224,6 +224,7 @@ class UserClient(threading.Thread):
|
|||
if authenticated == False:
|
||||
url = "%s/clients" % (self.currServer)
|
||||
utils.window('emby_currUser', value=userId)
|
||||
utils.window('plex_username', value=username)
|
||||
utils.window('emby_accessToken%s' % userId, value=self.currToken)
|
||||
result = doUtils.downloadUrl(url)
|
||||
|
||||
|
@ -234,6 +235,7 @@ class UserClient(threading.Thread):
|
|||
|
||||
# Set to windows property
|
||||
utils.window('emby_currUser', value=userId)
|
||||
utils.window('plex_username', value=username)
|
||||
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.getServer(prefix=False))
|
||||
|
|
|
@ -60,9 +60,11 @@
|
|||
<setting id="resumeJumpBack" type="slider" label="30521" default="10" range="0,1,120" option="int" />
|
||||
<setting type="sep" />
|
||||
<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="transcodeH265" type="enum" label="30522" default="0" values="Disabled|480p(and higher)|720p(and higher)|1080p" />
|
||||
<setting id="markPlayed" type="number" visible="false" default="90" />
|
||||
<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="audioBoost" type="slider" label="Boost audio when transcoding" default="0" range="0,10,100" option="int"/>
|
||||
<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="networkCreds" type="text" visible="false" default="" />
|
||||
</category>
|
||||
|
|
|
@ -81,7 +81,7 @@ class Service():
|
|||
"emby_syncRunning", "emby_dbCheck", "emby_kodiScan",
|
||||
"emby_shouldStop", "emby_currUser", "emby_dbScan", "emby_sessionId",
|
||||
"emby_initialScan", "emby_customplaylist", "emby_playbackProps",
|
||||
"plex_runLibScan"
|
||||
"plex_runLibScan", "plex_username"
|
||||
]
|
||||
for prop in properties:
|
||||
window(prop, clear=True)
|
||||
|
|
Loading…
Reference in a new issue