Direct playing movie works
This commit is contained in:
parent
681e57b350
commit
bc65b6082d
6 changed files with 239 additions and 129 deletions
|
@ -41,7 +41,6 @@ import xbmc
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
import time
|
import time
|
||||||
import urllib
|
|
||||||
import urllib2
|
import urllib2
|
||||||
import httplib
|
import httplib
|
||||||
import socket
|
import socket
|
||||||
|
@ -52,6 +51,7 @@ import Queue
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import json
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import xml.etree.cElementTree as etree
|
import xml.etree.cElementTree as etree
|
||||||
|
@ -87,6 +87,12 @@ 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.userId = utils.window('emby_currUser')
|
||||||
|
self.token = utils.window('emby_accessToken%s' % self.userId)
|
||||||
|
self.server = utils.window('emby_server%s' % self.userId)
|
||||||
|
self.plexLogin = utils.settings('plexLogin')
|
||||||
|
self.plexToken = utils.settings('plexToken')
|
||||||
|
self.machineIdentifier = utils.window('plex_machineIdentifier')
|
||||||
|
|
||||||
self.doUtils = downloadutils.DownloadUtils()
|
self.doUtils = downloadutils.DownloadUtils()
|
||||||
|
|
||||||
|
@ -167,6 +173,7 @@ class PlexAPI():
|
||||||
headerOptions={'X-Plex-Token': token}
|
headerOptions={'X-Plex-Token': token}
|
||||||
)
|
)
|
||||||
self.logMsg("Response was: %s" % r, 2)
|
self.logMsg("Response was: %s" % r, 2)
|
||||||
|
# List of exception returns, when connection failed
|
||||||
exceptionlist = [
|
exceptionlist = [
|
||||||
'',
|
'',
|
||||||
401
|
401
|
||||||
|
@ -596,22 +603,30 @@ class PlexAPI():
|
||||||
requests. An authentication option is NOT yet added.
|
requests. An authentication option is NOT yet added.
|
||||||
|
|
||||||
Inputs:
|
Inputs:
|
||||||
|
JSON=True will enforce a JSON answer
|
||||||
options: dictionary of options that will override the
|
options: dictionary of options that will override the
|
||||||
standard header options otherwise set.
|
standard header options otherwise set.
|
||||||
JSON=True will enforce a JSON answer, never mind any options
|
|
||||||
Output:
|
Output:
|
||||||
header dictionary
|
header dictionary
|
||||||
"""
|
"""
|
||||||
# Get addon infos
|
# Get addon infos
|
||||||
xargs = dict()
|
xargs = {
|
||||||
xargs['User-agent'] = self.addonName
|
'User-agent': self.addonName,
|
||||||
xargs['X-Plex-Device'] = self.deviceName
|
'X-Plex-Device': self.deviceName,
|
||||||
# xargs['X-Plex-Model'] = ''
|
'X-Plex-Platform': self.platform,
|
||||||
xargs['X-Plex-Platform'] = self.platform
|
'X-Plex-Client-Platform': self.platform,
|
||||||
xargs['X-Plex-Client-Platform'] = self.platform
|
'X-Plex-Product': self.addonName,
|
||||||
xargs['X-Plex-Product'] = self.addonName
|
'X-Plex-Version': self.plexversion,
|
||||||
xargs['X-Plex-Version'] = self.plexversion
|
'X-Plex-Client-Identifier': self.clientId,
|
||||||
xargs['X-Plex-Client-Identifier'] = self.clientId
|
'machineIdentifier': self.machineIdentifier,
|
||||||
|
'Accept': 'application/xml'
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
xargs['X-Plex-Token'] = self.token
|
||||||
|
except NameError:
|
||||||
|
# no token needed/saved yet
|
||||||
|
pass
|
||||||
if JSON:
|
if JSON:
|
||||||
xargs['Accept'] = 'application/json'
|
xargs['Accept'] = 'application/json'
|
||||||
if options:
|
if options:
|
||||||
|
@ -821,31 +836,6 @@ class PlexAPI():
|
||||||
dprint(__name__, 1, "====== MyPlex sign out XML finished ======")
|
dprint(__name__, 1, "====== MyPlex sign out XML finished ======")
|
||||||
dprint(__name__, 0, 'MyPlex Sign Out done')
|
dprint(__name__, 0, 'MyPlex Sign Out done')
|
||||||
|
|
||||||
def UserAccessRestricted(self, username):
|
|
||||||
"""
|
|
||||||
Returns True if the user's access is restricted (parental restrictions)
|
|
||||||
False otherwise.
|
|
||||||
|
|
||||||
Returns False also if access cannot be checked because plex.tv cannot
|
|
||||||
be reached.
|
|
||||||
"""
|
|
||||||
plexToken = utils.settings('plexToken')
|
|
||||||
users = self.MyPlexListHomeUsers(plexToken)
|
|
||||||
# If an error is encountered, set to False
|
|
||||||
if not users:
|
|
||||||
self.logMsg("Could not check user access restrictions.", 1)
|
|
||||||
self.logMsg("Setting restrictions to False.", 1)
|
|
||||||
return False
|
|
||||||
for user in users:
|
|
||||||
if username in user['title']:
|
|
||||||
restricted = user['restricted']
|
|
||||||
if restricted == '1':
|
|
||||||
restricted = True
|
|
||||||
else:
|
|
||||||
restricted = False
|
|
||||||
self.logMsg("Successfully checked user parental access for %s: restricted access is set to %s" % (username, restricted), 1)
|
|
||||||
return restricted
|
|
||||||
|
|
||||||
def GetUserArtworkURL(self, username):
|
def GetUserArtworkURL(self, username):
|
||||||
"""
|
"""
|
||||||
Returns the URL for the user's Avatar. Or False if something went
|
Returns the URL for the user's Avatar. Or False if something went
|
||||||
|
@ -877,8 +867,8 @@ class PlexAPI():
|
||||||
Will return empty strings if failed.
|
Will return empty strings if failed.
|
||||||
"""
|
"""
|
||||||
string = self.__language__
|
string = self.__language__
|
||||||
plexToken = utils.settings('plexToken')
|
plexLogin = self.plexLogin
|
||||||
plexLogin = utils.settings('plexLogin')
|
plexToken = self.plexToken
|
||||||
self.logMsg("Getting user list.", 1)
|
self.logMsg("Getting user list.", 1)
|
||||||
# Get list of Plex home users
|
# Get list of Plex home users
|
||||||
users = self.MyPlexListHomeUsers(plexToken)
|
users = self.MyPlexListHomeUsers(plexToken)
|
||||||
|
@ -1021,54 +1011,6 @@ class PlexAPI():
|
||||||
users.append(user.attrib)
|
users.append(user.attrib)
|
||||||
return users
|
return users
|
||||||
|
|
||||||
def getTranscodeVideoPath(self, path, AuthToken, options, action, quality, subtitle, audio, partIndex):
|
|
||||||
"""
|
|
||||||
Transcode Video support
|
|
||||||
|
|
||||||
parameters:
|
|
||||||
path
|
|
||||||
AuthToken
|
|
||||||
options - dict() of PlexConnect-options as received from aTV
|
|
||||||
action - transcoder action: Auto, Directplay, Transcode
|
|
||||||
quality - (resolution, quality, bitrate)
|
|
||||||
subtitle - {'selected', 'dontBurnIn', 'size'}
|
|
||||||
audio - {'boost'}
|
|
||||||
result:
|
|
||||||
final path to pull in PMS transcoder
|
|
||||||
"""
|
|
||||||
UDID = options['PlexConnectUDID']
|
|
||||||
|
|
||||||
transcodePath = '/video/:/transcode/universal/start.m3u8?'
|
|
||||||
|
|
||||||
vRes = quality[0]
|
|
||||||
vQ = quality[1]
|
|
||||||
mVB = quality[2]
|
|
||||||
dprint(__name__, 1, "Setting transcode quality Res:{0} Q:{1} {2}Mbps", vRes, vQ, mVB)
|
|
||||||
dprint(__name__, 1, "Subtitle: selected {0}, dontBurnIn {1}, size {2}", subtitle['selected'], subtitle['dontBurnIn'], subtitle['size'])
|
|
||||||
dprint(__name__, 1, "Audio: boost {0}", audio['boost'])
|
|
||||||
|
|
||||||
args = dict()
|
|
||||||
args['session'] = UDID
|
|
||||||
args['protocol'] = 'hls'
|
|
||||||
args['videoResolution'] = vRes
|
|
||||||
args['maxVideoBitrate'] = mVB
|
|
||||||
args['videoQuality'] = vQ
|
|
||||||
args['directStream'] = '0' if action=='Transcode' else '1'
|
|
||||||
# 'directPlay' - handled by the client in MEDIARUL()
|
|
||||||
args['subtitleSize'] = subtitle['size']
|
|
||||||
args['skipSubtitles'] = subtitle['dontBurnIn'] #'1' # shut off PMS subtitles. Todo: skip only for aTV native/SRT (or other supported)
|
|
||||||
args['audioBoost'] = audio['boost']
|
|
||||||
args['fastSeek'] = '1'
|
|
||||||
args['path'] = path
|
|
||||||
args['partIndex'] = partIndex
|
|
||||||
|
|
||||||
xargs = getXArgsDeviceInfo(options)
|
|
||||||
xargs['X-Plex-Client-Capabilities'] = "protocols=http-live-streaming,http-mp4-streaming,http-streaming-video,http-streaming-video-720p,http-mp4-video,http-mp4-video-720p;videoDecoders=h264{profile:high&resolution:1080&level:41};audioDecoders=mp3,aac{bitrate:160000}"
|
|
||||||
if not AuthToken=='':
|
|
||||||
xargs['X-Plex-Token'] = AuthToken
|
|
||||||
|
|
||||||
return transcodePath + urlencode(args) + '&' + urlencode(xargs)
|
|
||||||
|
|
||||||
def getDirectVideoPath(self, key, AuthToken):
|
def getDirectVideoPath(self, key, AuthToken):
|
||||||
"""
|
"""
|
||||||
Direct Video Play support
|
Direct Video Play support
|
||||||
|
@ -1309,7 +1251,7 @@ class PlexAPI():
|
||||||
'includePopularLeaves': 1,
|
'includePopularLeaves': 1,
|
||||||
'includeConcerts': 1
|
'includeConcerts': 1
|
||||||
}
|
}
|
||||||
url = url + '?' + urllib.urlencode(arguments)
|
url = url + '?' + urlencode(arguments)
|
||||||
headerOptions = {'Accept': 'application/xml'}
|
headerOptions = {'Accept': 'application/xml'}
|
||||||
xml = self.doUtils.downloadUrl(url, headerOptions=headerOptions)
|
xml = self.doUtils.downloadUrl(url, headerOptions=headerOptions)
|
||||||
if not xml:
|
if not xml:
|
||||||
|
@ -1324,6 +1266,7 @@ class API():
|
||||||
self.item = item
|
self.item = item
|
||||||
self.clientinfo = clientinfo.ClientInfo()
|
self.clientinfo = clientinfo.ClientInfo()
|
||||||
self.addonName = self.clientinfo.getAddonName()
|
self.addonName = self.clientinfo.getAddonName()
|
||||||
|
self.clientId = self.clientinfo.getDeviceId()
|
||||||
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)
|
||||||
|
@ -1771,7 +1714,7 @@ class API():
|
||||||
token = {'X-Plex-Token': self.token}
|
token = {'X-Plex-Token': self.token}
|
||||||
xargs = PlexAPI().getXArgsDeviceInfo(options=token)
|
xargs = PlexAPI().getXArgsDeviceInfo(options=token)
|
||||||
xargs.update(arguments)
|
xargs.update(arguments)
|
||||||
url = "%s?%s" % (url, urllib.urlencode(xargs))
|
url = "%s?%s" % (url, urlencode(xargs))
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def getMediaStreams(self):
|
def getMediaStreams(self):
|
||||||
|
@ -1939,5 +1882,141 @@ class API():
|
||||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||||
% (server, parentId, maxWidth, maxHeight, parentTag, customquery))
|
% (server, parentId, maxWidth, maxHeight, parentTag, customquery))
|
||||||
allartworks['Primary'] = artwork
|
allartworks['Primary'] = artwork
|
||||||
|
|
||||||
return allartworks
|
return allartworks
|
||||||
|
|
||||||
|
def getTranscodeVideoPath(self, action, quality={}, subtitle={}, audioboost=None, partIndex=None, options={}):
|
||||||
|
"""
|
||||||
|
Transcode Video support
|
||||||
|
|
||||||
|
Input:
|
||||||
|
action 'Transcode' OR any other string
|
||||||
|
quality: {'videoResolution': 'resolution',
|
||||||
|
'videoQuality': 'quality',
|
||||||
|
'maxVideoBitrate': 'bitrate'}
|
||||||
|
subtitle {'selected', 'dontBurnIn', 'size'}
|
||||||
|
audioboost Guess this takes an int as str
|
||||||
|
partIndex No idea
|
||||||
|
options dict() of PlexConnect-options as received from aTV
|
||||||
|
Output:
|
||||||
|
final path to pull in PMS transcoder
|
||||||
|
"""
|
||||||
|
# path to item
|
||||||
|
transcodePath = self.server + \
|
||||||
|
'/video/:/transcode/universal/start.m3u8?'
|
||||||
|
|
||||||
|
ID = self.getKey()
|
||||||
|
path = self.server + '/library/metadata/' + ID
|
||||||
|
args = {
|
||||||
|
'session': self.clientId,
|
||||||
|
'protocol': 'hls',
|
||||||
|
'fastSeek': '1',
|
||||||
|
'path': path,
|
||||||
|
'X-Plex-Client-Capabilities': "protocols=http-live-streaming,"
|
||||||
|
"http-streaming-video,"
|
||||||
|
"http-streaming-video-720p,"
|
||||||
|
"http-streaming-video-1080p,"
|
||||||
|
"http-mp4-streaming,"
|
||||||
|
"http-mp4-video,"
|
||||||
|
"http-mp4-video-720p,"
|
||||||
|
"http-mp4-video-1080p;"
|
||||||
|
"videoDecoders="
|
||||||
|
"h264{profile:high&resolution:1080&level:51};"
|
||||||
|
"audioDecoders="
|
||||||
|
"mp3,"
|
||||||
|
"aac{bitrate:160000},"
|
||||||
|
"ac3{channels:6},"
|
||||||
|
"dts{channels:6}"
|
||||||
|
# 'partIndex': partIndex What do we do with this?!?
|
||||||
|
}
|
||||||
|
# All the settings
|
||||||
|
if subtitle:
|
||||||
|
args_update = {
|
||||||
|
'subtitleSize': subtitle['size'],
|
||||||
|
'skipSubtitles': subtitle['dontBurnIn'] # '1': shut off PMS
|
||||||
|
}
|
||||||
|
args.update(args_update)
|
||||||
|
self.logMsg(
|
||||||
|
"Subtitle: selected %s, dontBurnIn %s, size %s"
|
||||||
|
% (subtitle['selected'], subtitle['dontBurnIn'],
|
||||||
|
subtitle['size']),
|
||||||
|
1
|
||||||
|
)
|
||||||
|
if audioboost:
|
||||||
|
args_update = {
|
||||||
|
'audioBoost': audioboost
|
||||||
|
}
|
||||||
|
args.update(args_update)
|
||||||
|
self.logMsg("audioboost: %s" % audioboost, 1)
|
||||||
|
if action == 'Transcode':
|
||||||
|
# Possible Plex settings:
|
||||||
|
# 'videoResolution': vRes,
|
||||||
|
# 'maxVideoBitrate': mVB,
|
||||||
|
# : vQ
|
||||||
|
self.logMsg("Setting transcode quality to: %s" % quality, 1)
|
||||||
|
args['directStream'] = '0'
|
||||||
|
args.update(quality)
|
||||||
|
else:
|
||||||
|
args['directStream'] = '1'
|
||||||
|
|
||||||
|
xargs = PlexAPI().getXArgsDeviceInfo(options=options)
|
||||||
|
return transcodePath + urlencode(args) + '&' + urlencode(xargs)
|
||||||
|
|
||||||
|
def adjustResume(self, resume_seconds):
|
||||||
|
|
||||||
|
resume = 0
|
||||||
|
if resume_seconds:
|
||||||
|
resume = round(float(resume_seconds), 6)
|
||||||
|
jumpback = int(utils.settings('resumeJumpBack'))
|
||||||
|
if resume > jumpback:
|
||||||
|
# To avoid negative bookmark
|
||||||
|
resume = resume - jumpback
|
||||||
|
|
||||||
|
return resume
|
||||||
|
|
||||||
|
def returnParts(self):
|
||||||
|
"""
|
||||||
|
TODO
|
||||||
|
"""
|
||||||
|
item = self.item
|
||||||
|
PartCount = 0
|
||||||
|
# Run through parts
|
||||||
|
for child in item[0][0]:
|
||||||
|
if child.tag == 'Part':
|
||||||
|
PartCount += PartCount
|
||||||
|
|
||||||
|
def externalSubs(self, playurl):
|
||||||
|
|
||||||
|
externalsubs = []
|
||||||
|
mapping = {}
|
||||||
|
|
||||||
|
item = self.item
|
||||||
|
itemid = self.getKey()
|
||||||
|
try:
|
||||||
|
mediastreams = item[0][0][0]
|
||||||
|
except (TypeError, KeyError, IndexError):
|
||||||
|
return
|
||||||
|
|
||||||
|
kodiindex = 0
|
||||||
|
for stream in mediastreams:
|
||||||
|
|
||||||
|
# index = stream['Index']
|
||||||
|
index = stream.attrib['id']
|
||||||
|
# Since Emby returns all possible tracks together, have to pull only external subtitles.
|
||||||
|
# IsTextSubtitleStream if true, is available to download from emby.
|
||||||
|
if (stream.attrib['streamType'] == "3" and
|
||||||
|
stream.attrib['format']):
|
||||||
|
|
||||||
|
# Direct stream
|
||||||
|
# PLEX: TODO!!
|
||||||
|
url = ("%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
|
||||||
|
% (self.server, itemid, itemid, index))
|
||||||
|
|
||||||
|
# map external subtitles for mapping
|
||||||
|
mapping[kodiindex] = index
|
||||||
|
externalsubs.append(url)
|
||||||
|
kodiindex += 1
|
||||||
|
|
||||||
|
mapping = json.dumps(mapping)
|
||||||
|
utils.window('emby_%s.indexMapping' % playurl, value=mapping)
|
||||||
|
|
||||||
|
return externalsubs
|
||||||
|
|
|
@ -25,13 +25,14 @@ import playbackutils as pbutils
|
||||||
import playutils
|
import playutils
|
||||||
import api
|
import api
|
||||||
|
|
||||||
|
import PlexAPI
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
|
||||||
def doPlayback(itemid, dbid):
|
def doPlayback(itemid, dbid):
|
||||||
|
|
||||||
emby = embyserver.Read_EmbyServer()
|
item = PlexAPI.PlexAPI().GetPlexMetadata(itemid) # Now xml, not json!
|
||||||
item = emby.getItem(itemid)
|
|
||||||
pbutils.PlaybackUtils(item).play(itemid, dbid)
|
pbutils.PlaybackUtils(item).play(itemid, dbid)
|
||||||
|
|
||||||
##### DO RESET AUTH #####
|
##### DO RESET AUTH #####
|
||||||
|
|
|
@ -360,7 +360,7 @@ class Movies(Items):
|
||||||
utils.window('emby_pathverified', value="true")
|
utils.window('emby_pathverified', value="true")
|
||||||
else:
|
else:
|
||||||
# Set plugin path and media flags using real filename
|
# Set plugin path and media flags using real filename
|
||||||
path = "plugin://plugin.video.plexkodiconnect.movies/"
|
path = "plugin://plugin.video.emby.movies/"
|
||||||
params = {
|
params = {
|
||||||
|
|
||||||
'filename': filename.encode('utf-8'),
|
'filename': filename.encode('utf-8'),
|
||||||
|
@ -704,7 +704,7 @@ class HomeVideos(Items):
|
||||||
utils.window('emby_pathverified', value="true")
|
utils.window('emby_pathverified', value="true")
|
||||||
else:
|
else:
|
||||||
# Set plugin path and media flags using real filename
|
# Set plugin path and media flags using real filename
|
||||||
path = "plugin://plugin.video.plexkodiconnect.movies/"
|
path = "plugin://plugin.video.emby.movies/"
|
||||||
params = {
|
params = {
|
||||||
|
|
||||||
'filename': filename.encode('utf-8'),
|
'filename': filename.encode('utf-8'),
|
||||||
|
|
|
@ -18,6 +18,8 @@ import playlist
|
||||||
import read_embyserver as embyserver
|
import read_embyserver as embyserver
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
|
import PlexAPI
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,7 +29,7 @@ class PlaybackUtils():
|
||||||
def __init__(self, item):
|
def __init__(self, item):
|
||||||
|
|
||||||
self.item = item
|
self.item = item
|
||||||
self.API = api.API(self.item)
|
self.API = PlexAPI.API(self.item)
|
||||||
|
|
||||||
self.clientInfo = clientinfo.ClientInfo()
|
self.clientInfo = clientinfo.ClientInfo()
|
||||||
self.addonName = self.clientInfo.getAddonName()
|
self.addonName = self.clientInfo.getAddonName()
|
||||||
|
@ -107,8 +109,9 @@ class PlaybackUtils():
|
||||||
currentPosition += 1
|
currentPosition += 1
|
||||||
|
|
||||||
############### -- CHECK FOR INTROS ################
|
############### -- CHECK FOR INTROS ################
|
||||||
|
# PLEX: todo. Seems like Plex returns a playlist WITH trailers
|
||||||
if utils.settings('enableCinema') == "true" and not seektime:
|
# if utils.settings('enableCinema') == "true" and not seektime:
|
||||||
|
if False:
|
||||||
# if we have any play them when the movie/show is not being resumed
|
# if we have any play them when the movie/show is not being resumed
|
||||||
url = "{server}/emby/Users/{UserId}/Items/%s/Intros?format=json" % itemid
|
url = "{server}/emby/Users/{UserId}/Items/%s/Intros?format=json" % itemid
|
||||||
intros = doUtils.downloadUrl(url)
|
intros = doUtils.downloadUrl(url)
|
||||||
|
@ -145,14 +148,17 @@ class PlaybackUtils():
|
||||||
# Extend our current playlist with the actual item to play
|
# Extend our current playlist with the actual item to play
|
||||||
# only if there's no playlist first
|
# only if there's no playlist first
|
||||||
self.logMsg("Adding main item to playlist.", 1)
|
self.logMsg("Adding main item to playlist.", 1)
|
||||||
self.pl.addtoPlaylist(dbid, item['Type'].lower())
|
# self.pl.addtoPlaylist(dbid, item['Type'].lower())
|
||||||
|
self.pl.addtoPlaylist(dbid, item[0].attrib['type'].lower())
|
||||||
|
|
||||||
# Ensure that additional parts are played after the main item
|
# Ensure that additional parts are played after the main item
|
||||||
currentPosition += 1
|
currentPosition += 1
|
||||||
|
|
||||||
############### -- CHECK FOR ADDITIONAL PARTS ################
|
############### -- CHECK FOR ADDITIONAL PARTS ################
|
||||||
|
|
||||||
if item.get('PartCount'):
|
# Plex: TODO. Guess parts are sent back like trailers.
|
||||||
|
# if item.get('PartCount'):
|
||||||
|
if False:
|
||||||
# Only add to the playlist after intros have played
|
# Only add to the playlist after intros have played
|
||||||
partcount = item['PartCount']
|
partcount = item['PartCount']
|
||||||
url = "{server}/emby/Videos/%s/AdditionalParts?format=json" % itemid
|
url = "{server}/emby/Videos/%s/AdditionalParts?format=json" % itemid
|
||||||
|
@ -215,11 +221,14 @@ class PlaybackUtils():
|
||||||
def setProperties(self, playurl, listitem):
|
def setProperties(self, playurl, listitem):
|
||||||
# Set all properties necessary for plugin path playback
|
# Set all properties necessary for plugin path playback
|
||||||
item = self.item
|
item = self.item
|
||||||
itemid = item['Id']
|
# itemid = item['Id']
|
||||||
itemtype = item['Type']
|
itemid = self.API.getKey()
|
||||||
|
# itemtype = item['Type']
|
||||||
|
itemtype = item[0].attrib['type']
|
||||||
|
resume, runtime = self.API.getRuntime()
|
||||||
|
|
||||||
embyitem = "emby_%s" % playurl
|
embyitem = "emby_%s" % playurl
|
||||||
utils.window('%s.runtime' % embyitem, value=str(item.get('RunTimeTicks')))
|
utils.window('%s.runtime' % embyitem, value=str(runtime))
|
||||||
utils.window('%s.type' % embyitem, value=itemtype)
|
utils.window('%s.type' % embyitem, value=itemtype)
|
||||||
utils.window('%s.itemid' % embyitem, value=itemid)
|
utils.window('%s.itemid' % embyitem, value=itemid)
|
||||||
|
|
||||||
|
@ -231,7 +240,8 @@ class PlaybackUtils():
|
||||||
# Append external subtitles to stream
|
# Append external subtitles to stream
|
||||||
playmethod = utils.window('%s.playmethod' % embyitem)
|
playmethod = utils.window('%s.playmethod' % embyitem)
|
||||||
# Only for direct play and direct stream
|
# Only for direct play and direct stream
|
||||||
subtitles = self.externalSubs(playurl)
|
# subtitles = self.externalSubs(playurl)
|
||||||
|
subtitles = self.API.externalSubs(playurl)
|
||||||
if playmethod in ("DirectStream", "Transcode"):
|
if playmethod in ("DirectStream", "Transcode"):
|
||||||
# Direct play automatically appends external
|
# Direct play automatically appends external
|
||||||
listitem.setSubtitles(subtitles)
|
listitem.setSubtitles(subtitles)
|
||||||
|
@ -278,7 +288,8 @@ class PlaybackUtils():
|
||||||
item = self.item
|
item = self.item
|
||||||
artwork = self.artwork
|
artwork = self.artwork
|
||||||
|
|
||||||
allartwork = artwork.getAllArtwork(item, parentInfo=True)
|
# allartwork = artwork.getAllArtwork(item, parentInfo=True)
|
||||||
|
allartwork = self.API.getAllArtwork(parentInfo=True)
|
||||||
# Set artwork for listitem
|
# Set artwork for listitem
|
||||||
arttypes = {
|
arttypes = {
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ import xbmcvfs
|
||||||
import clientinfo
|
import clientinfo
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
|
import PlexAPI
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,6 +26,9 @@ class PlayUtils():
|
||||||
|
|
||||||
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.machineIdentifier = utils.window('plex_machineIdentifier')
|
||||||
|
|
||||||
|
self.plx = PlexAPI.API(item)
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
def logMsg(self, msg, lvl=1):
|
||||||
|
|
||||||
|
@ -36,34 +41,39 @@ class PlayUtils():
|
||||||
item = self.item
|
item = self.item
|
||||||
playurl = None
|
playurl = None
|
||||||
|
|
||||||
if item['MediaSources'][0]['Protocol'] == "Http":
|
# if item['MediaSources'][0]['Protocol'] == "Http":
|
||||||
# Only play as http
|
# # Only play as http
|
||||||
self.logMsg("File protocol is http.", 1)
|
# self.logMsg("File protocol is http.", 1)
|
||||||
playurl = self.httpPlay()
|
# playurl = self.httpPlay()
|
||||||
utils.window('emby_%s.playmethod' % playurl, value="DirectStream")
|
# utils.window('emby_%s.playmethod' % playurl, value="DirectStream")
|
||||||
|
|
||||||
elif self.isDirectPlay():
|
# elif self.isDirectPlay():
|
||||||
|
|
||||||
self.logMsg("File is direct playing.", 1)
|
# self.logMsg("File is direct playing.", 1)
|
||||||
playurl = self.directPlay()
|
# playurl = self.directPlay()
|
||||||
playurl = playurl.encode('utf-8')
|
# playurl = playurl.encode('utf-8')
|
||||||
# Set playmethod property
|
# # Set playmethod property
|
||||||
utils.window('emby_%s.playmethod' % playurl, value="DirectPlay")
|
# utils.window('emby_%s.playmethod' % playurl, value="DirectPlay")
|
||||||
|
|
||||||
elif self.isDirectStream():
|
if self.isDirectStream():
|
||||||
|
|
||||||
self.logMsg("File is direct streaming.", 1)
|
self.logMsg("File is direct streaming.", 1)
|
||||||
playurl = self.directStream()
|
playurl = self.plx.getTranscodeVideoPath('direct')
|
||||||
# Set playmethod property
|
# Set playmethod property
|
||||||
utils.window('emby_%s.playmethod' % playurl, value="DirectStream")
|
utils.window('emby_%s.playmethod' % playurl, value="DirectStream")
|
||||||
|
|
||||||
elif self.isTranscoding():
|
elif self.isTranscoding():
|
||||||
|
|
||||||
self.logMsg("File is transcoding.", 1)
|
self.logMsg("File is transcoding.", 1)
|
||||||
playurl = self.transcoding()
|
quality = {
|
||||||
|
'bitrate': self.getBitrate()
|
||||||
|
}
|
||||||
|
playurl = self.plx.getTranscodeVideoPath(
|
||||||
|
'Transcode', quality=quality
|
||||||
|
)
|
||||||
# Set playmethod property
|
# Set playmethod property
|
||||||
utils.window('emby_%s.playmethod' % playurl, value="Transcode")
|
utils.window('emby_%s.playmethod' % playurl, value="Transcode")
|
||||||
|
self.logMsg("The playurl is: %s" % playurl, 1)
|
||||||
return playurl
|
return playurl
|
||||||
|
|
||||||
def httpPlay(self):
|
def httpPlay(self):
|
||||||
|
@ -192,13 +202,16 @@ class PlayUtils():
|
||||||
item = self.item
|
item = self.item
|
||||||
|
|
||||||
if (utils.settings('transcodeH265') == "true" and
|
if (utils.settings('transcodeH265') == "true" and
|
||||||
item['MediaSources'][0]['Name'].startswith("1080P/H265")):
|
item[0][0].attrib('videoCodec').startswith("h265") and
|
||||||
|
item[0][0].attrib('videoResolution').startswith("1080")):
|
||||||
# Avoid H265 1080p
|
# Avoid H265 1080p
|
||||||
self.logMsg("Option to transcode 1080P/H265 enabled.", 1)
|
self.logMsg("Option to transcode 1080P/H265 enabled.", 1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Requirement: BitRate, supported encoding
|
# Requirement: BitRate, supported encoding
|
||||||
canDirectStream = item['MediaSources'][0]['SupportsDirectStream']
|
# canDirectStream = item['MediaSources'][0]['SupportsDirectStream']
|
||||||
|
# Plex: always able?!?
|
||||||
|
canDirectStream = True
|
||||||
# Make sure the server supports it
|
# Make sure the server supports it
|
||||||
if not canDirectStream:
|
if not canDirectStream:
|
||||||
return False
|
return False
|
||||||
|
@ -215,16 +228,18 @@ class PlayUtils():
|
||||||
item = self.item
|
item = self.item
|
||||||
server = self.server
|
server = self.server
|
||||||
|
|
||||||
itemid = item['Id']
|
itemid = self.plx.getKey()
|
||||||
type = item['Type']
|
type = item[0].tag
|
||||||
|
|
||||||
if 'Path' in item and item['Path'].endswith('.strm'):
|
# if 'Path' in item and item['Path'].endswith('.strm'):
|
||||||
# Allow strm loading when direct streaming
|
# # Allow strm loading when direct streaming
|
||||||
playurl = self.directPlay()
|
# playurl = self.directPlay()
|
||||||
elif type == "Audio":
|
if type == "Audio":
|
||||||
playurl = "%s/emby/Audio/%s/stream.mp3" % (server, itemid)
|
playurl = "%s/emby/Audio/%s/stream.mp3" % (server, itemid)
|
||||||
else:
|
else:
|
||||||
playurl = "%s/emby/Videos/%s/stream?static=true" % (server, itemid)
|
playurl = "%s/emby/Videos/%s/stream?static=true" % (server, itemid)
|
||||||
|
playurl = "{server}/player/playback/playMedia?key=%2Flibrary%2Fmetadata%2F%s&offset=0&X-Plex-Client-Identifier={clientId}&machineIdentifier={SERVER ID}&address={SERVER IP}&port={SERVER PORT}&protocol=http&path=http%3A%2F%2F{SERVER IP}%3A{SERVER PORT}%2Flibrary%2Fmetadata%2F{MEDIA ID}" % (itemid)
|
||||||
|
playurl = self.plx.replaceURLtags()
|
||||||
|
|
||||||
return playurl
|
return playurl
|
||||||
|
|
||||||
|
@ -233,7 +248,7 @@ class PlayUtils():
|
||||||
settings = self.getBitrate()*1000
|
settings = self.getBitrate()*1000
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sourceBitrate = int(self.item['MediaSources'][0]['Bitrate'])
|
sourceBitrate = int(self.item[0][0].attrib['bitrate'])
|
||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
self.logMsg("Bitrate value is missing.", 1)
|
self.logMsg("Bitrate value is missing.", 1)
|
||||||
else:
|
else:
|
||||||
|
@ -245,7 +260,8 @@ class PlayUtils():
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def isTranscoding(self):
|
def isTranscoding(self):
|
||||||
|
# I hope Plex transcodes everything
|
||||||
|
return True
|
||||||
item = self.item
|
item = self.item
|
||||||
|
|
||||||
canTranscode = item['MediaSources'][0]['SupportsTranscoding']
|
canTranscode = item['MediaSources'][0]['SupportsTranscoding']
|
||||||
|
|
|
@ -234,12 +234,13 @@ class UserClient(threading.Thread):
|
||||||
self.currUserId = userId
|
self.currUserId = userId
|
||||||
self.currServer = self.getServer()
|
self.currServer = self.getServer()
|
||||||
self.currToken = self.getToken()
|
self.currToken = self.getToken()
|
||||||
|
self.machineIdentifier = self.getServerId()
|
||||||
self.ssl = self.getSSLverify()
|
self.ssl = self.getSSLverify()
|
||||||
self.sslcert = self.getSSL()
|
self.sslcert = self.getSSL()
|
||||||
|
|
||||||
# Test the validity of current token
|
# Test the validity of current token
|
||||||
if authenticated == False:
|
if authenticated == False:
|
||||||
url = "%s/emby/Users/%s?format=json" % (self.currServer, userId)
|
url = "%s/clients" % (self.currServer)
|
||||||
utils.window('emby_currUser', value=userId)
|
utils.window('emby_currUser', value=userId)
|
||||||
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)
|
||||||
|
@ -254,6 +255,7 @@ class UserClient(threading.Thread):
|
||||||
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))
|
||||||
|
utils.window('plex_machineIdentifier', value=self.machineIdentifier)
|
||||||
|
|
||||||
# Set DownloadUtils values
|
# Set DownloadUtils values
|
||||||
doUtils.setUsername(username)
|
doUtils.setUsername(username)
|
||||||
|
@ -279,6 +281,7 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
username = self.getUsername()
|
username = self.getUsername()
|
||||||
server = self.getServer()
|
server = self.getServer()
|
||||||
|
machineIdentifier = self.getServerId()
|
||||||
|
|
||||||
# If there's no settings.xml
|
# If there's no settings.xml
|
||||||
if not hasSettings:
|
if not hasSettings:
|
||||||
|
|
Loading…
Reference in a new issue