PlexKodiConnect/resources/lib/playutils.py

426 lines
15 KiB
Python
Raw Normal View History

2016-04-01 04:41:06 +11:00
# -*- coding: utf-8 -*-
#################################################################################################
import xbmc
import xbmcgui
import xbmcvfs
import clientinfo
2016-06-17 07:24:07 +10:00
from utils import Logging, window, settings, language as lang
2016-04-01 04:41:06 +11:00
#################################################################################################
class PlayUtils():
def __init__(self, item):
2016-06-17 07:24:07 +10:00
global log
log = Logging(self.__class__.__name__).log
2016-04-01 04:41:06 +11:00
self.item = item
self.clientInfo = clientinfo.ClientInfo()
self.addonName = self.clientInfo.getAddonName()
2016-06-17 07:24:07 +10:00
self.userid = window('emby_currUser')
self.server = window('emby_server%s' % self.userid)
2016-04-01 04:41:06 +11:00
def getPlayUrl(self):
playurl = None
2016-06-17 07:24:07 +10:00
if (self.item.get('Type') in ("Recording", "TvChannel") and self.item.get('MediaSources')
and self.item['MediaSources'][0]['Protocol'] == "Http"):
2016-04-01 04:41:06 +11:00
# Play LiveTV or recordings
2016-06-17 07:24:07 +10:00
log("File protocol is http (livetv).", 1)
playurl = "%s/emby/Videos/%s/stream.ts?audioCodec=copy&videoCodec=copy" % (self.server, self.item['Id'])
2016-04-01 04:41:06 +11:00
window('emby_%s.playmethod' % playurl, value="Transcode")
2016-04-01 04:46:51 +11:00
elif self.item.get('MediaSources') and self.item['MediaSources'][0]['Protocol'] == "Http":
2016-04-01 04:41:06 +11:00
# Only play as http, used for channels, or online hosting of content
2016-06-17 07:24:07 +10:00
log("File protocol is http.", 1)
2016-04-01 04:41:06 +11:00
playurl = self.httpPlay()
window('emby_%s.playmethod' % playurl, value="DirectStream")
elif self.isDirectPlay():
2016-06-17 07:24:07 +10:00
log("File is direct playing.", 1)
2016-04-01 04:41:06 +11:00
playurl = self.directPlay()
playurl = playurl.encode('utf-8')
# Set playmethod property
window('emby_%s.playmethod' % playurl, value="DirectPlay")
elif self.isDirectStream():
2016-06-17 07:24:07 +10:00
log("File is direct streaming.", 1)
2016-04-01 04:41:06 +11:00
playurl = self.directStream()
# Set playmethod property
window('emby_%s.playmethod' % playurl, value="DirectStream")
elif self.isTranscoding():
2016-06-17 07:24:07 +10:00
log("File is transcoding.", 1)
2016-04-01 04:41:06 +11:00
playurl = self.transcoding()
# Set playmethod property
window('emby_%s.playmethod' % playurl, value="Transcode")
return playurl
def httpPlay(self):
# Audio, Video, Photo
2016-04-01 04:46:51 +11:00
itemid = self.item['Id']
mediatype = self.item['MediaType']
2016-04-01 04:41:06 +11:00
if mediatype == "Audio":
2016-04-01 04:49:55 +11:00
playurl = "%s/emby/Audio/%s/stream" % (self.server, itemid)
2016-04-01 04:41:06 +11:00
else:
2016-04-01 04:49:55 +11:00
playurl = "%s/emby/Videos/%s/stream?static=true" % (self.server, itemid)
2016-04-01 04:41:06 +11:00
return playurl
def isDirectPlay(self):
dialog = xbmcgui.Dialog()
# Requirement: Filesystem, Accessible path
if settings('playFromStream') == "true":
# User forcing to play via HTTP
2016-06-17 07:24:07 +10:00
log("Can't direct play, play from HTTP enabled.", 1)
2016-04-01 04:41:06 +11:00
return False
2016-04-01 04:46:51 +11:00
videotrack = self.item['MediaSources'][0]['Name']
2016-04-01 04:41:06 +11:00
transcodeH265 = settings('transcodeH265')
videoprofiles = [x['Profile'] for x in self.item['MediaSources'][0]['MediaStreams'] if 'Profile' in x]
2016-06-17 07:24:07 +10:00
transcodeHi10P = settings('transcodeHi10P')
if transcodeHi10P == "true" and "H264" in videotrack and "High 10" in videoprofiles:
return False
2016-04-01 04:41:06 +11:00
if transcodeH265 in ("1", "2", "3") and ("HEVC" in videotrack or "H265" in videotrack):
# Avoid H265/HEVC depending on the resolution
resolution = int(videotrack.split("P", 1)[0])
res = {
'1': 480,
'2': 720,
'3': 1080
}
2016-06-17 07:24:07 +10:00
log("Resolution is: %sP, transcode for resolution: %sP+"
2016-04-01 04:41:06 +11:00
% (resolution, res[transcodeH265]), 1)
if res[transcodeH265] <= resolution:
return False
2016-04-01 04:46:51 +11:00
canDirectPlay = self.item['MediaSources'][0]['SupportsDirectPlay']
2016-04-01 04:41:06 +11:00
# Make sure direct play is supported by the server
if not canDirectPlay:
2016-06-17 07:24:07 +10:00
log("Can't direct play, server doesn't allow/support it.", 1)
2016-04-01 04:41:06 +11:00
return False
2016-04-01 04:46:51 +11:00
location = self.item['LocationType']
2016-04-01 04:41:06 +11:00
if location == "FileSystem":
# Verify the path
if not self.fileExists():
2016-06-17 07:24:07 +10:00
log("Unable to direct play.", 1)
2016-04-01 04:41:06 +11:00
try:
count = int(settings('failCount'))
except ValueError:
count = 0
2016-06-17 07:24:07 +10:00
log("Direct play failed: %s times." % count, 1)
2016-04-01 04:41:06 +11:00
if count < 2:
# Let the user know that direct play failed
settings('failCount', value=str(count+1))
dialog.notification(
2016-06-21 11:57:29 +10:00
heading=lang(29999),
2016-04-01 04:41:06 +11:00
message=lang(33011),
icon="special://home/addons/plugin.video.emby/icon.png",
sound=False)
elif settings('playFromStream') != "true":
# Permanently set direct stream as true
settings('playFromStream', value="true")
settings('failCount', value="0")
dialog.notification(
2016-06-21 11:57:29 +10:00
heading=lang(29999),
2016-04-01 04:41:06 +11:00
message=lang(33012),
icon="special://home/addons/plugin.video.emby/icon.png",
sound=False)
return False
return True
def directPlay(self):
try:
2016-04-01 04:46:51 +11:00
playurl = self.item['MediaSources'][0]['Path']
2016-04-01 04:41:06 +11:00
except (IndexError, KeyError):
2016-04-01 04:46:51 +11:00
playurl = self.item['Path']
2016-04-01 04:41:06 +11:00
2016-04-01 04:46:51 +11:00
if self.item.get('VideoType'):
2016-04-01 04:41:06 +11:00
# Specific format modification
2016-04-01 04:49:55 +11:00
if self.item['VideoType'] == "Dvd":
2016-04-01 04:41:06 +11:00
playurl = "%s/VIDEO_TS/VIDEO_TS.IFO" % playurl
2016-04-01 04:49:55 +11:00
elif self.item['VideoType'] == "BluRay":
2016-04-01 04:41:06 +11:00
playurl = "%s/BDMV/index.bdmv" % playurl
# Assign network protocol
if playurl.startswith('\\\\'):
playurl = playurl.replace("\\\\", "smb://")
playurl = playurl.replace("\\", "/")
if "apple.com" in playurl:
USER_AGENT = "QuickTime/7.7.4"
playurl += "?|User-Agent=%s" % USER_AGENT
return playurl
def fileExists(self):
if 'Path' not in self.item:
# File has no path defined in server
return False
# Convert path to direct play
path = self.directPlay()
2016-06-17 07:24:07 +10:00
log("Verifying path: %s" % path, 1)
2016-04-01 04:41:06 +11:00
if xbmcvfs.exists(path):
2016-06-17 07:24:07 +10:00
log("Path exists.", 1)
2016-04-01 04:41:06 +11:00
return True
elif ":" not in path:
2016-06-17 07:24:07 +10:00
log("Can't verify path, assumed linux. Still try to direct play.", 1)
2016-04-01 04:41:06 +11:00
return True
else:
2016-06-17 07:24:07 +10:00
log("Failed to find file.", 1)
2016-04-01 04:41:06 +11:00
return False
def isDirectStream(self):
2016-04-01 04:46:51 +11:00
videotrack = self.item['MediaSources'][0]['Name']
2016-06-19 04:56:56 +10:00
transcodeH265 = settings('transcodeH265')
videoprofiles = [x['Profile'] for x in self.item['MediaSources'][0]['MediaStreams'] if 'Profile' in x]
2016-06-19 04:56:56 +10:00
transcodeHi10P = settings('transcodeHi10P')
if transcodeHi10P == "true" and "H264" in videotrack and "High 10" in videoprofiles:
return False
2016-04-01 04:41:06 +11:00
if transcodeH265 in ("1", "2", "3") and ("HEVC" in videotrack or "H265" in videotrack):
# Avoid H265/HEVC depending on the resolution
resolution = int(videotrack.split("P", 1)[0])
res = {
'1': 480,
'2': 720,
'3': 1080
}
2016-06-17 07:24:07 +10:00
log("Resolution is: %sP, transcode for resolution: %sP+"
2016-04-01 04:41:06 +11:00
% (resolution, res[transcodeH265]), 1)
if res[transcodeH265] <= resolution:
return False
# Requirement: BitRate, supported encoding
2016-04-01 04:46:51 +11:00
canDirectStream = self.item['MediaSources'][0]['SupportsDirectStream']
2016-04-01 04:41:06 +11:00
# Make sure the server supports it
if not canDirectStream:
return False
# Verify the bitrate
if not self.isNetworkSufficient():
2016-06-17 07:24:07 +10:00
log("The network speed is insufficient to direct stream file.", 1)
2016-04-01 04:41:06 +11:00
return False
return True
def directStream(self):
2016-04-01 04:46:51 +11:00
if 'Path' in self.item and self.item['Path'].endswith('.strm'):
2016-04-01 04:41:06 +11:00
# Allow strm loading when direct streaming
playurl = self.directPlay()
2016-04-01 04:52:36 +11:00
elif self.item['Type'] == "Audio":
playurl = "%s/emby/Audio/%s/stream.mp3" % (self.server, self.item['Id'])
2016-04-01 04:41:06 +11:00
else:
2016-06-28 14:55:14 +10:00
playurl = "%s/emby/Videos/%s/stream?static=true" % (self.server, self.item['Id'])
2016-04-01 04:41:06 +11:00
return playurl
def isNetworkSufficient(self):
settings = self.getBitrate()*1000
try:
sourceBitrate = int(self.item['MediaSources'][0]['Bitrate'])
except (KeyError, TypeError):
2016-06-17 07:24:07 +10:00
log("Bitrate value is missing.", 1)
2016-04-01 04:41:06 +11:00
else:
2016-06-17 07:24:07 +10:00
log("The add-on settings bitrate is: %s, the video bitrate required is: %s"
2016-04-01 04:41:06 +11:00
% (settings, sourceBitrate), 1)
if settings < sourceBitrate:
return False
return True
def isTranscoding(self):
# Make sure the server supports it
2016-04-01 04:52:36 +11:00
if not self.item['MediaSources'][0]['SupportsTranscoding']:
2016-04-01 04:41:06 +11:00
return False
return True
def transcoding(self):
2016-04-01 04:46:51 +11:00
if 'Path' in self.item and self.item['Path'].endswith('.strm'):
2016-04-01 04:41:06 +11:00
# Allow strm loading when transcoding
playurl = self.directPlay()
else:
2016-04-01 04:46:51 +11:00
itemid = self.item['Id']
2016-04-01 04:41:06 +11:00
deviceId = self.clientInfo.getDeviceId()
playurl = (
"%s/emby/Videos/%s/master.m3u8?MediaSourceId=%s"
% (self.server, itemid, itemid)
)
playurl = (
"%s&VideoCodec=h264&AudioCodec=ac3&MaxAudioChannels=6&deviceId=%s&VideoBitrate=%s"
% (playurl, deviceId, self.getBitrate()*1000))
return playurl
def getBitrate(self):
# get the addon video quality
bitrate = {
'0': 664,
'1': 996,
'2': 1320,
'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
}
# max bit rate supported by server (max signed 32bit integer)
2016-06-19 04:56:56 +10:00
return bitrate.get(settings('videoBitrate'), 2147483)
2016-04-01 04:41:06 +11:00
def audioSubsPref(self, url, listitem):
dialog = xbmcgui.Dialog()
# For transcoding only
# Present the list of audio to select from
audioStreamsList = {}
audioStreams = []
audioStreamsChannelsList = {}
subtitleStreamsList = {}
subtitleStreams = ['No subtitles']
downloadableStreams = []
selectAudioIndex = ""
selectSubsIndex = ""
playurlprefs = "%s" % url
try:
2016-04-01 04:46:51 +11:00
mediasources = self.item['MediaSources'][0]
2016-04-01 04:41:06 +11:00
mediastreams = mediasources['MediaStreams']
except (TypeError, KeyError, IndexError):
return
for stream in mediastreams:
# Since Emby returns all possible tracks together, have to sort them.
index = stream['Index']
2016-04-01 04:52:36 +11:00
if 'Audio' in stream['Type']:
2016-04-01 04:41:06 +11:00
codec = stream['Codec']
channelLayout = stream.get('ChannelLayout', "")
try:
track = "%s - %s - %s %s" % (index, stream['Language'], codec, channelLayout)
except:
track = "%s - %s %s" % (index, codec, channelLayout)
audioStreamsChannelsList[index] = stream['Channels']
audioStreamsList[track] = index
audioStreams.append(track)
2016-04-01 04:52:36 +11:00
elif 'Subtitle' in stream['Type']:
2016-04-01 04:41:06 +11:00
try:
track = "%s - %s" % (index, stream['Language'])
except:
track = "%s - %s" % (index, stream['Codec'])
default = stream['IsDefault']
forced = stream['IsForced']
downloadable = stream['IsTextSubtitleStream']
if default:
track = "%s - Default" % track
if forced:
track = "%s - Forced" % track
if downloadable:
downloadableStreams.append(index)
subtitleStreamsList[track] = index
subtitleStreams.append(track)
if len(audioStreams) > 1:
resp = dialog.select(lang(33013), 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']
else: # There's only one audiotrack.
selectAudioIndex = audioStreamsList[audioStreams[0]]
playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
if len(subtitleStreams) > 1:
resp = dialog.select(lang(33014), subtitleStreams)
if resp == 0:
# User selected no subtitles
pass
elif resp > -1:
# User selected subtitles
selected = subtitleStreams[resp]
selectSubsIndex = subtitleStreamsList[selected]
# Load subtitles in the listitem if downloadable
if selectSubsIndex in downloadableStreams:
2016-04-01 04:46:51 +11:00
itemid = self.item['Id']
2016-04-01 04:41:06 +11:00
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)
else:
# Burn subtitles
playurlprefs += "&SubtitleStreamIndex=%s" % selectSubsIndex
else: # User backed out of selection
playurlprefs += "&SubtitleStreamIndex=%s" % mediasources.get('DefaultSubtitleStreamIndex', "")
# Get number of channels for selected audio track
audioChannels = audioStreamsChannelsList.get(selectAudioIndex, 0)
if audioChannels > 2:
playurlprefs += "&AudioBitrate=384000"
else:
playurlprefs += "&AudioBitrate=192000"
return playurlprefs