Big transcoding overhaul

- Fixes #278
This commit is contained in:
tomkat83 2017-05-01 19:51:10 +02:00
parent 56400e54da
commit 72ed20e47f
6 changed files with 169 additions and 125 deletions

View file

@ -1851,7 +1851,6 @@ msgctxt "#39702"
msgid "Browse by folder" msgid "Browse by folder"
msgstr "" msgstr ""
# For use with addon.xml (PKC metadata for Kodi, e.g. description) # For use with addon.xml (PKC metadata for Kodi, e.g. description)
# Addon Summary # Addon Summary
msgctxt "#39703" msgctxt "#39703"
@ -1869,3 +1868,29 @@ msgstr ""
msgctxt "#39705" msgctxt "#39705"
msgid "Use at your own risk" msgid "Use at your own risk"
msgstr "" msgstr ""
# If user gets prompted to choose between several subtitles. Leave the number one at the beginning of the string!
msgctxt "#39706"
msgid "1 No subtitles"
msgstr ""
# If user gets prompted to choose between several audio/subtitle tracks and language is unknown
msgctxt "#39707"
msgid "unknown"
msgstr ""
# If user gets prompted to choose between several subtitles and Plex adds the "default" flag
msgctxt "#39708"
msgid "Default"
msgstr ""
# If user gets prompted to choose between several subtitles and Plex adds the "forced" flag
msgctxt "#39709"
msgid "Forced"
msgstr ""
# If user gets prompted to choose between several subtitles the subtitle cannot be downloaded (has no 'key' attribute from the PMS), the subtitle needs to be burned in
msgctxt "#39710"
msgid "burn-in"
msgstr ""

View file

@ -2282,6 +2282,7 @@ class API():
'hasMDE': 1, 'hasMDE': 1,
'location': 'lan', 'location': 'lan',
'mediaBufferSize': '16384', 'mediaBufferSize': '16384',
'subtitleSize': settings('subtitleSize')
# 'copyts': 1, # 'copyts': 1,
# 'offset': 0, # Resume point # 'offset': 0, # Resume point
} }
@ -2313,7 +2314,7 @@ class API():
if key: if key:
# We do know the language - temporarily download # We do know the language - temporarily download
if stream.attrib.get('languageCode') is not None: if stream.attrib.get('languageCode') is not None:
path = self.__download_external_subtitles( path = self.download_external_subtitles(
"{server}%s" % key, "{server}%s" % key,
"subtitle.%s.%s" % (stream.attrib['languageCode'], "subtitle.%s.%s" % (stream.attrib['languageCode'],
stream.attrib['codec'])) stream.attrib['codec']))
@ -2331,7 +2332,7 @@ class API():
return externalsubs return externalsubs
@staticmethod @staticmethod
def __download_external_subtitles(url, filename): def download_external_subtitles(url, filename):
""" """
One cannot pass the subtitle language for ListItems. Workaround; will One cannot pass the subtitle language for ListItems. Workaround; will
download the subtitle at url to the Kodi PKC directory in a temp dir download the subtitle at url to the Kodi PKC directory in a temp dir

View file

@ -106,7 +106,8 @@ class KodiMonitor(Monitor):
log.error("Could not find itemid in plex database for a " log.error("Could not find itemid in plex database for a "
"video library update") "video library update")
else: else:
# Stop from manually marking as watched unwatched, with actual playback. # Stop from manually marking as watched unwatched, with
# actual playback.
if window('plex_skipWatched%s' % itemid) == "true": if window('plex_skipWatched%s' % itemid) == "true":
# property is set in player.py # property is set in player.py
window('plex_skipWatched%s' % itemid, clear=True) window('plex_skipWatched%s' % itemid, clear=True)
@ -171,11 +172,11 @@ class KodiMonitor(Monitor):
# Try to get a Kodi ID # Try to get a Kodi ID
# If PKC was used - native paths, not direct paths # If PKC was used - native paths, not direct paths
plexid = window('plex_%s.itemid' % tryEncode(currentFile)) plex_id = window('plex_%s.itemid' % tryEncode(currentFile))
# Get rid of the '' if the window property was not set # Get rid of the '' if the window property was not set
plexid = None if not plexid else plexid plex_id = None if not plex_id else plex_id
kodiid = None kodiid = None
if plexid is None: if plex_id is None:
log.debug('Did not get Plex id from window properties') log.debug('Did not get Plex id from window properties')
try: try:
kodiid = data['item']['id'] kodiid = data['item']['id']
@ -183,30 +184,39 @@ class KodiMonitor(Monitor):
log.debug('Did not get a Kodi id from Kodi, darn') log.debug('Did not get a Kodi id from Kodi, darn')
# For direct paths, if we're not streaming something # For direct paths, if we're not streaming something
# When using Widgets, Kodi doesn't tell us shit so we need this hack # When using Widgets, Kodi doesn't tell us shit so we need this hack
if (kodiid is None and plexid is None and typus != 'song' if (kodiid is None and plex_id is None and typus != 'song'
and not currentFile.startswith('http')): and not currentFile.startswith('http')):
(kodiid, typus) = get_kodiid_from_filename(currentFile) (kodiid, typus) = get_kodiid_from_filename(currentFile)
if kodiid is None: if kodiid is None:
return return
if plexid is None: if plex_id is None:
# Get Plex' item id # Get Plex' item id
with plexdb.Get_Plex_DB() as plexcursor: with plexdb.Get_Plex_DB() as plexcursor:
plex_dbitem = plexcursor.getItem_byKodiId(kodiid, typus) plex_dbitem = plexcursor.getItem_byKodiId(kodiid, typus)
try: try:
plexid = plex_dbitem[0] plex_id = plex_dbitem[0]
except TypeError: except TypeError:
log.info("No Plex id returned for kodiid %s. Aborting playback" log.info("No Plex id returned for kodiid %s. Aborting playback"
" report" % kodiid) " report" % kodiid)
return return
log.debug("Found Plex id %s for Kodi id %s for type %s" log.debug("Found Plex id %s for Kodi id %s for type %s"
% (plexid, kodiid, typus)) % (plex_id, kodiid, typus))
# Switch subtitle tracks if applicable
subtitle = window('plex_%s.subtitle' % tryEncode(currentFile))
if window(tryEncode('plex_%s.playmethod' % currentFile)) \
== 'Transcode' and subtitle:
if window('plex_%s.subtitle' % currentFile) == 'None':
self.xbmcplayer.showSubtitles(False)
else:
self.xbmcplayer.setSubtitleStream(int(subtitle))
# Set some stuff if Kodi initiated playback # Set some stuff if Kodi initiated playback
if ((settings('useDirectPaths') == "1" and not typus == "song") if ((settings('useDirectPaths') == "1" and not typus == "song")
or or
(typus == "song" and settings('enableMusic') == "true")): (typus == "song" and settings('enableMusic') == "true")):
if self.StartDirectPath(plexid, if self.StartDirectPath(plex_id,
typus, typus,
tryEncode(currentFile)) is False: tryEncode(currentFile)) is False:
log.error('Could not initiate monitoring; aborting') log.error('Could not initiate monitoring; aborting')
@ -214,19 +224,19 @@ class KodiMonitor(Monitor):
# Save currentFile for cleanup later and to be able to access refs # Save currentFile for cleanup later and to be able to access refs
window('plex_lastPlayedFiled', value=currentFile) window('plex_lastPlayedFiled', value=currentFile)
window('plex_currently_playing_itemid', value=plexid) window('plex_currently_playing_itemid', value=plex_id)
window("plex_%s.itemid" % tryEncode(currentFile), value=plexid) window("plex_%s.itemid" % tryEncode(currentFile), value=plex_id)
log.info('Finish playback startup') log.info('Finish playback startup')
def StartDirectPath(self, plexid, type, currentFile): def StartDirectPath(self, plex_id, type, currentFile):
""" """
Set some additional stuff if playback was initiated by Kodi, not PKC Set some additional stuff if playback was initiated by Kodi, not PKC
""" """
xml = self.doUtils('{server}/library/metadata/%s' % plexid) xml = self.doUtils('{server}/library/metadata/%s' % plex_id)
try: try:
xml[0].attrib xml[0].attrib
except: except:
log.error('Did not receive a valid XML for plexid %s.' % plexid) log.error('Did not receive a valid XML for plex_id %s.' % plex_id)
return False return False
# Setup stuff, because playback was started by Kodi, not PKC # Setup stuff, because playback was started by Kodi, not PKC
api = API(xml[0]) api = API(xml[0])

View file

@ -75,10 +75,7 @@ class PlaybackUtils():
playmethod = window('plex_%s.playmethod' % playurl) playmethod = window('plex_%s.playmethod' % playurl)
if playmethod == "Transcode": if playmethod == "Transcode":
window('plex_%s.playmethod' % playurl, clear=True) playutils.audioSubsPref(listitem, tryDecode(playurl))
playurl = tryEncode(playutils.audioSubsPref(
listitem, tryDecode(playurl)))
window('plex_%s.playmethod' % playurl, "Transcode")
listitem.setPath(playurl) listitem.setPath(playurl)
api.set_playback_win_props(playurl, listitem) api.set_playback_win_props(playurl, listitem)
result.listitem = listitem result.listitem = listitem
@ -195,12 +192,7 @@ class PlaybackUtils():
# Would be using the direct path # Would be using the direct path
log.debug("Adding contextmenu item for direct paths") log.debug("Adding contextmenu item for direct paths")
if window('plex_%s.playmethod' % playurl) == "Transcode": if window('plex_%s.playmethod' % playurl) == "Transcode":
window('plex_%s.playmethod' % playurl, playutils.audioSubsPref(listitem, tryDecode(playurl))
clear=True)
playurl = tryEncode(playutils.audioSubsPref(
listitem, tryDecode(playurl)))
window('plex_%s.playmethod' % playurl,
value="Transcode")
api.CreateListItemFromPlexItem(listitem) api.CreateListItemFromPlexItem(listitem)
api.set_playback_win_props(playurl, listitem) api.set_playback_win_props(playurl, listitem)
api.set_listitem_artwork(listitem) api.set_listitem_artwork(listitem)
@ -246,10 +238,7 @@ class PlaybackUtils():
# For transcoding only, ask for audio/subs pref # For transcoding only, ask for audio/subs pref
if (window('plex_%s.playmethod' % playurl) == "Transcode" and if (window('plex_%s.playmethod' % playurl) == "Transcode" and
not contextmenu_play): not contextmenu_play):
window('plex_%s.playmethod' % playurl, clear=True) playutils.audioSubsPref(listitem, tryDecode(playurl))
playurl = tryEncode(playutils.audioSubsPref(
listitem, tryDecode(playurl)))
window('plex_%s.playmethod' % playurl, value="Transcode")
listitem.setPath(playurl) listitem.setPath(playurl)
api.set_playback_win_props(playurl, listitem) api.set_playback_win_props(playurl, listitem)

View file

@ -396,7 +396,8 @@ class Player(xbmc.Player):
'%s.type' % plex_item, '%s.type' % plex_item,
'%s.runtime' % plex_item, '%s.runtime' % plex_item,
'%s.playcount' % plex_item, '%s.playcount' % plex_item,
'%s.playlistPosition' % plex_item '%s.playlistPosition' % plex_item,
'%s.subtitle' % plex_item,
) )
for item in cleanup: for item in cleanup:
window(item, clear=True) window(item, clear=True)

View file

@ -3,11 +3,9 @@
############################################################################### ###############################################################################
import logging import logging
from urllib import urlencode from downloadutils import DownloadUtils
import xbmcgui from utils import window, settings, tryEncode, language as lang, dialog
from utils import window, settings, tryEncode, language as lang
import variables as v import variables as v
import PlexAPI import PlexAPI
@ -24,6 +22,7 @@ class PlayUtils():
self.item = item self.item = item
self.API = PlexAPI.API(item) self.API = PlexAPI.API(item)
self.doUtils = DownloadUtils().downloadUrl
self.userid = window('currUserId') self.userid = window('currUserId')
self.server = window('pms_server') self.server = window('pms_server')
@ -254,131 +253,150 @@ class PlayUtils():
return res[chosen] return res[chosen]
def audioSubsPref(self, listitem, url, part=None): def audioSubsPref(self, listitem, url, part=None):
dialog = xbmcgui.Dialog() """
# For transcoding only For transcoding only
# Present the list of audio to select from
audioStreamsList = []
audioStreams = []
# audioStreamsChannelsList = []
subtitleStreamsList = []
subtitleStreams = ['1 No subtitles']
downloadableStreams = []
# selectAudioIndex = ""
selectSubsIndex = ""
playurlprefs = {}
# Set part where we're at Called at the very beginning of play; used to change audio and subtitle
self.API.setPartNumber(part) stream by a PUT request to the PMS
"""
# Set media and part where we're at
if self.API.mediastream is None:
self.API.getMediastreamNumber()
if part is None: if part is None:
part = 0 part = 0
try: try:
mediastreams = self.item[0][part] mediastreams = self.item[self.API.mediastream][part]
except (TypeError, KeyError, IndexError): except (TypeError, IndexError):
return url log.error('Could not get media %s, part %s'
% (self.API.mediastream, part))
audioNum = 0 return
part_id = mediastreams.attrib['id']
audio_streams_list = []
audio_streams = []
subtitle_streams_list = []
# No subtitles as an option
subtitle_streams = [lang(39706)]
downloadable_streams = []
download_subs = []
# selectAudioIndex = ""
select_subs_index = ""
audio_numb = 0
# Remember 'no subtitles' # Remember 'no subtitles'
subNum = 1 sub_num = 1
defaultSub = None default_sub = None
for stream in mediastreams: for stream in mediastreams:
# Since Plex returns all possible tracks together, have to sort # Since Plex returns all possible tracks together, have to sort
# them. # them.
index = stream.attrib.get('id') index = stream.attrib.get('id')
type = stream.attrib.get('streamType') typus = stream.attrib.get('streamType')
# Audio # Audio
if type == "2": if typus == "2":
codec = stream.attrib.get('codec') codec = stream.attrib.get('codec')
channelLayout = stream.attrib.get('audioChannelLayout', "") channelLayout = stream.attrib.get('audioChannelLayout', "")
try: try:
track = "%s %s - %s %s" % (audioNum+1, track = "%s %s - %s %s" % (audio_numb+1,
stream.attrib['language'], stream.attrib['language'],
codec, codec,
channelLayout) channelLayout)
except: except:
track = "%s 'unknown' - %s %s" % (audioNum+1, track = "%s %s - %s %s" % (audio_numb+1,
lang(39707), # unknown
codec, codec,
channelLayout) channelLayout)
audioStreamsList.append(index) audio_streams_list.append(index)
audioStreams.append(tryEncode(track)) audio_streams.append(tryEncode(track))
audioNum += 1 audio_numb += 1
# Subtitles # Subtitles
elif type == "3": elif typus == "3":
try: try:
track = "%s %s" % (subNum+1, stream.attrib['language']) track = "%s %s" % (sub_num+1, stream.attrib['language'])
except: except KeyError:
track = "%s 'unknown' (%s)" % (subNum+1, track = "%s %s (%s)" % (sub_num+1,
lang(39707), # unknown
stream.attrib.get('codec')) stream.attrib.get('codec'))
default = stream.attrib.get('default') default = stream.attrib.get('default')
forced = stream.attrib.get('forced') forced = stream.attrib.get('forced')
downloadable = stream.attrib.get('key') downloadable = stream.attrib.get('key')
if default: if default:
track = "%s - Default" % track track = "%s - %s" % (track, lang(39708)) # Default
if forced: if forced:
track = "%s - Forced" % track track = "%s - %s" % (track, lang(39709)) # Forced
if downloadable: if downloadable:
downloadableStreams.append(index) # We do know the language - temporarily download
if 'language' in stream.attrib:
path = self.API.download_external_subtitles(
'{server}%s' % stream.attrib['key'],
"subtitle.%s.%s" % (stream.attrib['language'],
stream.attrib['codec']))
# We don't know the language - no need to download
else: else:
track = "%s (burn-in)" % track path = self.API.addPlexCredentialsToUrl(
"%s%s" % (self.server, stream.attrib['key']))
downloadable_streams.append(index)
download_subs.append(tryEncode(path))
else:
track = "%s (%s)" % (track, lang(39710)) # burn-in
if stream.attrib.get('selected') == '1' and downloadable: if stream.attrib.get('selected') == '1' and downloadable:
# Only show subs without asking user if they can be # Only show subs without asking user if they can be
# turned off # turned off
defaultSub = index default_sub = index
subtitleStreamsList.append(index) subtitle_streams_list.append(index)
subtitleStreams.append(tryEncode(track)) subtitle_streams.append(tryEncode(track))
subNum += 1 sub_num += 1
if audioNum > 1: if audio_numb > 1:
resp = dialog.select(lang(33013), audioStreams) resp = dialog('select', lang(33013), audio_streams)
if resp > -1: if resp > -1:
# User selected audio # User selected some audio track
playurlprefs['audioStreamID'] = audioStreamsList[resp] args = {
else: 'audioStreamID': audio_streams_list[resp],
# User backed out of selection - let PMS decide 'allParts': 1
pass }
else: self.doUtils('{server}/library/parts/%s' % part_id,
# There's only one audiotrack. action_type='PUT',
playurlprefs['audioStreamID'] = audioStreamsList[0] parameters=args)
selectSubsIndex = None if sub_num == 1:
if subNum > 1: # No subtitles
return
select_subs_index = None
if (settings('pickPlexSubtitles') == 'true' and if (settings('pickPlexSubtitles') == 'true' and
defaultSub is not None): default_sub is not None):
log.info('Using default Plex subtitle: %s' % defaultSub) log.info('Using default Plex subtitle: %s' % default_sub)
selectSubsIndex = defaultSub select_subs_index = default_sub
else: else:
resp = dialog.select(lang(33014), subtitleStreams) resp = dialog('select', lang(33014), subtitle_streams)
if resp > 0: if resp > 0:
selectSubsIndex = subtitleStreamsList[resp-1] select_subs_index = subtitle_streams_list[resp-1]
else: else:
# User selected no subtitles or backed out of dialog # User selected no subtitles or backed out of dialog
playurlprefs["skipSubtitles"] = 1 select_subs_index = ''
if selectSubsIndex is not None:
# Load subtitles in the listitem if downloadable log.debug('Adding external subtitles: %s' % download_subs)
if selectSubsIndex in downloadableStreams: # Enable Kodi to switch autonomously to downloadable subtitles
sub_url = self.API.addPlexHeadersToUrl( if download_subs:
"%s/library/streams/%s" listitem.setSubtitles(download_subs)
% (self.server, selectSubsIndex))
log.info("Downloadable sub: %s: %s" if select_subs_index in downloadable_streams:
% (selectSubsIndex, sub_url)) for i, stream in enumerate(downloadable_streams):
listitem.setSubtitles([tryEncode(sub_url)]) if stream == select_subs_index:
# Set the correct subtitle
window('plex_%s.subtitle' % tryEncode(url), value=str(i))
break
# Don't additionally burn in subtitles # Don't additionally burn in subtitles
playurlprefs["skipSubtitles"] = 1 select_subs_index = ''
else: else:
log.info('Need to burn in subtitle %s' % selectSubsIndex) window('plex_%s.subtitle' % tryEncode(url), value='None')
playurlprefs["subtitleStreamID"] = selectSubsIndex
playurlprefs["subtitleSize"] = settings('subtitleSize')
url += '&' + urlencode(playurlprefs) args = {
'subtitleStreamID': select_subs_index,
# Get number of channels for selected audio track 'allParts': 1
# audioChannels = audioStreamsChannelsList.get(selectAudioIndex, 0) }
# if audioChannels > 2: self.doUtils('{server}/library/parts/%s' % part_id,
# playurlprefs += "&AudioBitrate=384000" action_type='PUT',
# else: parameters=args)
# playurlprefs += "&AudioBitrate=192000"
return url