Merge pull request #1131 from croneter/fix-playback
Rewire the set-up of audio and subtitle streams, esp. before starting a transcoding session. Fixes playback not starting at all
This commit is contained in:
commit
5aceb223ee
4 changed files with 104 additions and 98 deletions
|
@ -291,8 +291,8 @@ class DownloadUtils():
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
r.encoding = 'utf-8'
|
r.encoding = 'utf-8'
|
||||||
LOG.warn('Unknown answer from PMS %s with status code %s. ',
|
LOG.warn('Unknown answer from PMS %s with status code %s: %s',
|
||||||
url, r.status_code)
|
url, r.status_code, r.text)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
|
|
@ -12,16 +12,10 @@ import xbmc
|
||||||
|
|
||||||
from .plex_api import API
|
from .plex_api import API
|
||||||
from .plex_db import PlexDB
|
from .plex_db import PlexDB
|
||||||
from . import plex_functions as PF
|
|
||||||
from . import utils
|
|
||||||
from .kodi_db import KodiVideoDB
|
from .kodi_db import KodiVideoDB
|
||||||
from . import playlist_func as PL
|
from . import plex_functions as PF, playlist_func as PL, playqueue as PQ
|
||||||
from . import playqueue as PQ
|
from . import json_rpc as js, variables as v, utils, transfer
|
||||||
from . import json_rpc as js
|
from . import playback_decision, app
|
||||||
from . import transfer
|
|
||||||
from .playback_decision import set_playurl, audio_subtitle_prefs
|
|
||||||
from . import variables as v
|
|
||||||
from . import app
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
LOG = getLogger('PLEX.playback')
|
LOG = getLogger('PLEX.playback')
|
||||||
|
@ -457,21 +451,20 @@ def _conclude_playback(playqueue, pos):
|
||||||
_ensure_resolve()
|
_ensure_resolve()
|
||||||
return
|
return
|
||||||
api.part = item.part or 0
|
api.part = item.part or 0
|
||||||
listitem = api.listitem(listitem=transfer.PKCListItem, resume=False)
|
playback_decision.set_pkc_playmethod(api, item)
|
||||||
set_playurl(api, item)
|
if not playback_decision.audio_subtitle_prefs(api, item):
|
||||||
if not item.file:
|
LOG.info('Did not set audio subtitle prefs, aborting silently')
|
||||||
LOG.debug('Did not get a playurl, aborting playback silently')
|
|
||||||
_ensure_resolve()
|
_ensure_resolve()
|
||||||
return
|
return
|
||||||
|
playback_decision.set_playurl(api, item)
|
||||||
|
if not item.file:
|
||||||
|
LOG.info('Did not get a playurl, aborting playback silently')
|
||||||
|
_ensure_resolve()
|
||||||
|
return
|
||||||
|
listitem = api.listitem(listitem=transfer.PKCListItem, resume=False)
|
||||||
listitem.setPath(item.file.encode('utf-8'))
|
listitem.setPath(item.file.encode('utf-8'))
|
||||||
if item.playmethod == v.PLAYBACK_METHOD_DIRECT_PLAY:
|
if item.playmethod != v.PLAYBACK_METHOD_DIRECT_PATH:
|
||||||
listitem.setSubtitles(api.cache_external_subs())
|
listitem.setSubtitles(api.cache_external_subs())
|
||||||
elif item.playmethod in (v.PLAYBACK_METHOD_DIRECT_STREAM,
|
|
||||||
v.PLAYBACK_METHOD_TRANSCODE):
|
|
||||||
audio_subtitle_prefs(api, listitem)
|
|
||||||
# Need to hit the PMS api again in order to get the selected
|
|
||||||
# burn-in subtitles set-up correctly
|
|
||||||
set_playurl(api, item)
|
|
||||||
transfer.send(listitem)
|
transfer.send(listitem)
|
||||||
LOG.debug('Done concluding playback')
|
LOG.debug('Done concluding playback')
|
||||||
|
|
||||||
|
|
|
@ -18,30 +18,36 @@ DIRECT_PLAY_OK = 1000
|
||||||
CONVERSION_OK = 1001 # PMS can either direct stream or transcode
|
CONVERSION_OK = 1001 # PMS can either direct stream or transcode
|
||||||
|
|
||||||
|
|
||||||
def set_playurl(api, item):
|
def set_pkc_playmethod(api, item):
|
||||||
item.playmethod = int(utils.settings('playType'))
|
item.playmethod = int(utils.settings('playType'))
|
||||||
LOG.info('User chose playback method %s in PKC settings',
|
LOG.info('User chose playback method %s in PKC settings',
|
||||||
v.EXPLICIT_PLAYBACK_METHOD[item.playmethod])
|
v.EXPLICIT_PLAYBACK_METHOD[item.playmethod])
|
||||||
_initial_best_playback_method(api, item)
|
_initial_best_playback_method(api, item)
|
||||||
LOG.info('PKC decided on playback method %s',
|
LOG.info('PKC decided on playback method %s',
|
||||||
v.EXPLICIT_PLAYBACK_METHOD[item.playmethod])
|
v.EXPLICIT_PLAYBACK_METHOD[item.playmethod])
|
||||||
|
|
||||||
|
|
||||||
|
def set_playurl(api, item):
|
||||||
|
try:
|
||||||
if item.playmethod == v.PLAYBACK_METHOD_DIRECT_PATH:
|
if item.playmethod == v.PLAYBACK_METHOD_DIRECT_PATH:
|
||||||
# No need to ask the PMS whether we can play - we circumvent
|
# No need to ask the PMS whether we can play - we circumvent
|
||||||
# the PMS entirely
|
# the PMS entirely
|
||||||
LOG.info('The playurl for %s is: %s',
|
|
||||||
v.EXPLICIT_PLAYBACK_METHOD[item.playmethod], item.file)
|
|
||||||
return
|
return
|
||||||
LOG.info('Lets ask the PMS next')
|
LOG.info('Lets ask the PMS next')
|
||||||
try:
|
try:
|
||||||
_pms_playback_decision(api, item)
|
_pms_playback_decision(api, item)
|
||||||
except (exceptions.RequestException, AttributeError, IndexError, SystemExit) as err:
|
except (exceptions.RequestException,
|
||||||
|
AttributeError,
|
||||||
|
IndexError,
|
||||||
|
SystemExit):
|
||||||
LOG.warn('Could not find suitable settings for playback, aborting')
|
LOG.warn('Could not find suitable settings for playback, aborting')
|
||||||
LOG.warn('Error received: %s', err)
|
utils.ERROR(notify=True)
|
||||||
item.playmethod = None
|
item.playmethod = None
|
||||||
item.file = None
|
item.file = None
|
||||||
else:
|
else:
|
||||||
item.file = api.transcode_video_path(item.playmethod,
|
item.file = api.transcode_video_path(item.playmethod,
|
||||||
quality=item.quality)
|
quality=item.quality)
|
||||||
|
finally:
|
||||||
LOG.info('The playurl for %s is: %s',
|
LOG.info('The playurl for %s is: %s',
|
||||||
v.EXPLICIT_PLAYBACK_METHOD[item.playmethod], item.file)
|
v.EXPLICIT_PLAYBACK_METHOD[item.playmethod], item.file)
|
||||||
|
|
||||||
|
@ -313,12 +319,16 @@ def _getH265():
|
||||||
return H265[utils.settings('transcodeH265')]
|
return H265[utils.settings('transcodeH265')]
|
||||||
|
|
||||||
|
|
||||||
def audio_subtitle_prefs(api, listitem):
|
def audio_subtitle_prefs(api, item):
|
||||||
"""
|
"""
|
||||||
For transcoding only
|
Sets the stage for transcoding, letting the user potentially choose both
|
||||||
|
audio and subtitle streams; subtitle streams to burn-into the video file.
|
||||||
|
|
||||||
Called at the very beginning of play; used to change audio and subtitle
|
Uses a PUT request to the PMS, simulating e.g. the user using Plex Web,
|
||||||
stream by a PUT request to the PMS
|
choosing a different stream in the video's metadata and THEN initiating
|
||||||
|
playback.
|
||||||
|
|
||||||
|
Returns None if user cancelled or we need to abort, True otherwise
|
||||||
"""
|
"""
|
||||||
# Set media and part where we're at
|
# Set media and part where we're at
|
||||||
if (api.mediastream is None and
|
if (api.mediastream is None and
|
||||||
|
@ -331,13 +341,21 @@ def audio_subtitle_prefs(api, listitem):
|
||||||
api.mediastream, api.part)
|
api.mediastream, api.part)
|
||||||
return
|
return
|
||||||
part_id = mediastreams.attrib['id']
|
part_id = mediastreams.attrib['id']
|
||||||
|
if item.playmethod != v.PLAYBACK_METHOD_TRANSCODE:
|
||||||
|
LOG.debug('Telling PMS we are not burning in any subtitles')
|
||||||
|
args = {
|
||||||
|
'subtitleStreamID': 0,
|
||||||
|
'allParts': 1
|
||||||
|
}
|
||||||
|
DU().downloadUrl('{server}/library/parts/%s' % part_id,
|
||||||
|
action_type='PUT',
|
||||||
|
parameters=args)
|
||||||
|
return True
|
||||||
audio_streams_list = []
|
audio_streams_list = []
|
||||||
audio_streams = []
|
audio_streams = []
|
||||||
subtitle_streams_list = []
|
subtitle_streams_list = []
|
||||||
# "Don't burn-in any subtitle"
|
# "Don't burn-in any subtitle"
|
||||||
subtitle_streams = ['1 %s' % utils.lang(39706)]
|
subtitle_streams = ['1 %s' % utils.lang(39706)]
|
||||||
downloadable_streams = []
|
|
||||||
download_subs = []
|
|
||||||
# selectAudioIndex = ""
|
# selectAudioIndex = ""
|
||||||
select_subs_index = ""
|
select_subs_index = ""
|
||||||
audio_numb = 0
|
audio_numb = 0
|
||||||
|
@ -369,19 +387,11 @@ def audio_subtitle_prefs(api, listitem):
|
||||||
|
|
||||||
# Subtitles
|
# Subtitles
|
||||||
elif typus == "3":
|
elif typus == "3":
|
||||||
downloadable = stream.get('key')
|
if stream.get('key'):
|
||||||
if downloadable:
|
# Subtitle can and will be downloaded - don't let user choose
|
||||||
# Download the subtitle to Kodi - the user will need to
|
# this subtitle to burn-in
|
||||||
# manually select the subtitle on the Kodi side
|
continue
|
||||||
# Hence do NOT show dialog for this sub
|
# Subtitle is available within the video file
|
||||||
path = api.download_external_subtitles(
|
|
||||||
'{{server}}{}'.format(stream.get('key')),
|
|
||||||
stream.get('displayTitle'),
|
|
||||||
stream.get('codec'))
|
|
||||||
if path:
|
|
||||||
downloadable_streams.append(index)
|
|
||||||
download_subs.append(path.encode('utf-8'))
|
|
||||||
else:
|
|
||||||
# Burn in the subtitle, if user chooses to do so
|
# Burn in the subtitle, if user chooses to do so
|
||||||
default = stream.get('default')
|
default = stream.get('default')
|
||||||
forced = stream.get('forced')
|
forced = stream.get('forced')
|
||||||
|
@ -403,8 +413,9 @@ def audio_subtitle_prefs(api, listitem):
|
||||||
|
|
||||||
if audio_numb > 1:
|
if audio_numb > 1:
|
||||||
resp = utils.dialog('select', utils.lang(33013), audio_streams)
|
resp = utils.dialog('select', utils.lang(33013), audio_streams)
|
||||||
if resp > -1:
|
if resp == -1:
|
||||||
# User selected some audio track
|
LOG.info('User aborted dialog to select audio stream')
|
||||||
|
return
|
||||||
args = {
|
args = {
|
||||||
'audioStreamID': audio_streams_list[resp],
|
'audioStreamID': audio_streams_list[resp],
|
||||||
'allParts': 1
|
'allParts': 1
|
||||||
|
@ -413,22 +424,21 @@ def audio_subtitle_prefs(api, listitem):
|
||||||
action_type='PUT',
|
action_type='PUT',
|
||||||
parameters=args)
|
parameters=args)
|
||||||
|
|
||||||
LOG.debug('Adding downloadable subtitles: %s', download_subs)
|
|
||||||
# Enable Kodi to switch autonomously to downloadable subtitles
|
|
||||||
if download_subs:
|
|
||||||
listitem.setSubtitles(download_subs)
|
|
||||||
select_subs_index = ''
|
select_subs_index = ''
|
||||||
if sub_num == 1:
|
if sub_num == 1:
|
||||||
# Note: we DO need to tell the PMS that we DONT want any sub
|
# Note: we DO need to tell the PMS that we DONT want any sub
|
||||||
# Otherwise, the PMS might pick-up the last one
|
# Otherwise, the PMS might pick-up the last one
|
||||||
LOG.debug('No subtitles to burn-in')
|
LOG.info('No subtitles to burn-in')
|
||||||
else:
|
else:
|
||||||
resp = utils.dialog('select', utils.lang(33014), subtitle_streams)
|
resp = utils.dialog('select', utils.lang(33014), subtitle_streams)
|
||||||
if resp < 1:
|
if resp == -1:
|
||||||
|
LOG.info('User aborted dialog to select subtitle stream')
|
||||||
|
return
|
||||||
|
elif resp == 0:
|
||||||
# User did not select a subtitle or backed out of the dialog
|
# User did not select a subtitle or backed out of the dialog
|
||||||
LOG.debug('User chose to not burn-in any subtitles')
|
LOG.info('User chose to not burn-in any subtitles')
|
||||||
else:
|
else:
|
||||||
LOG.debug('User chose to burn-in subtitle %s: %s',
|
LOG.info('User chose to burn-in subtitle %s: %s',
|
||||||
select_subs_index,
|
select_subs_index,
|
||||||
subtitle_streams[resp].decode('utf-8'))
|
subtitle_streams[resp].decode('utf-8'))
|
||||||
select_subs_index = subtitle_streams_list[resp - 1]
|
select_subs_index = subtitle_streams_list[resp - 1]
|
||||||
|
@ -440,3 +450,4 @@ def audio_subtitle_prefs(api, listitem):
|
||||||
DU().downloadUrl('{server}/library/parts/%s' % part_id,
|
DU().downloadUrl('{server}/library/parts/%s' % part_id,
|
||||||
action_type='PUT',
|
action_type='PUT',
|
||||||
parameters=args)
|
parameters=args)
|
||||||
|
return True
|
||||||
|
|
|
@ -1085,13 +1085,15 @@ def transcoding_arguments(path, media, part, playmethod, args=None):
|
||||||
'protocol': 'hls', # seen in the wild: 'http', 'dash', 'http', 'hls'
|
'protocol': 'hls', # seen in the wild: 'http', 'dash', 'http', 'hls'
|
||||||
'session': v.PKC_MACHINE_IDENTIFIER, # TODO: create new unique id
|
'session': v.PKC_MACHINE_IDENTIFIER, # TODO: create new unique id
|
||||||
'fastSeek': 1,
|
'fastSeek': 1,
|
||||||
# none, embedded, sidecar
|
|
||||||
# Essentially indicating what you want to do with subtitles and state
|
|
||||||
# you aren’t want it to burn them into the video (requires transcoding)
|
|
||||||
# 'subtitles': 'none',
|
|
||||||
'subtitleSize': utils.settings('subtitleSize'),
|
|
||||||
'copyts': 1
|
'copyts': 1
|
||||||
}
|
}
|
||||||
|
if playmethod != v.PLAYBACK_METHOD_TRANSCODE:
|
||||||
|
# Essentially indicating what you want to do with subtitles and state
|
||||||
|
# you aren’t want it to burn them into the video (requires transcoding)
|
||||||
|
# none, embedded, sidecar
|
||||||
|
args['subtitles'] = 'none'
|
||||||
|
else:
|
||||||
|
args['subtitleSize'] = utils.settings('subtitleSize')
|
||||||
if args:
|
if args:
|
||||||
arguments.update(args)
|
arguments.update(args)
|
||||||
return arguments
|
return arguments
|
||||||
|
|
Loading…
Reference in a new issue