From 9856b3d47ecb0c5c4d396f6bf1edc19c6614bd4f Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 27 Dec 2024 12:49:36 +0100 Subject: [PATCH 1/5] Refactor play_utils.py --- resources/lib/play_utils.py | 226 +++++++++++++++++------------------- 1 file changed, 106 insertions(+), 120 deletions(-) diff --git a/resources/lib/play_utils.py b/resources/lib/play_utils.py index e70e1b47..1d68e20f 100644 --- a/resources/lib/play_utils.py +++ b/resources/lib/play_utils.py @@ -233,7 +233,7 @@ def play_file(play_info): add_to_playlist(play_info) return - # if this is a list of items them add them all to the play list + # If this is a list of items, add them all to the play list if isinstance(item_id, list): return play_list_of_items(item_id) @@ -275,7 +275,7 @@ def play_file(play_info): result = api.get(url) log.debug("PlayAllFiles items: {0}".format(result)) - # process each item + # Process each item items = result["Items"] if items is None: items = [] @@ -311,13 +311,13 @@ def play_file(play_info): result = api.get(url) log.debug("PlayAllFiles items: {0}".format(result)) - # process each item + # Process each item items = result["Items"] if items is None: items = [] return play_all_files(items) - # if this is a program from live tv epg then play the actual channel + # If this is a program from live tv epg then play the actual channel if result.get("Type") == "Program": channel_id = result.get("ChannelId") url = "/Users/{}/Items/{}?format=json".format(api.user_id, channel_id) @@ -338,7 +338,7 @@ def play_file(play_info): action_menu.doModal() return - # get playback info from the server using the device profile + # Get playback info from the server using the device profile playback_info = get_item_playback_info(item_id, force_transcode) if playback_info is None: log.debug("playback_info was None, could not get MediaSources so can not play!") @@ -352,7 +352,7 @@ def play_file(play_info): play_session_id = playback_info.get("PlaySessionId") - # select the media source to use + # Select the media source to use media_sources = playback_info.get('MediaSources') selected_media_source = None @@ -391,7 +391,7 @@ def play_file(play_info): seek_time = 0 auto_resume = int(auto_resume) - # process user data for resume points + # Process user data for resume points if auto_resume != -1: seek_time = (auto_resume / 1000) / 10000 @@ -433,13 +433,13 @@ def play_file(play_info): elif playback_type == "1": playback_type_string = "DirectStream" - # add the playback type into the overview + # Add the playback type into the overview if result.get("Overview", None) is not None: result["Overview"] = playback_type_string + "\n" + result.get("Overview") else: result["Overview"] = playback_type_string - # add title decoration is needed + # Add title decoration is needed item_title = result.get("Name", translate_string(30280)) # extract item info from result @@ -459,15 +459,17 @@ def play_file(play_info): gui_item = add_gui_item(item_id, item_details, display_options, False) list_item = gui_item[1] - if playback_type == "2": # if transcoding then prompt for audio and subtitle + # If transcoding/remuxing, prompt for audio and subtitle stream selection + if playback_type == "2": playurl = audio_subs_pref(playurl, list_item, selected_media_source, item_id, audio_stream_index, subtitle_stream_index) log.debug("New playurl for transcoding: {0}".format(playurl)) - elif playback_type == "1": # for direct stream add any streamable subtitles - external_subs(selected_media_source, list_item, item_id) + elif playback_type == "1": # For direct stream add any streamable subtitles + subtitle_index = settings.getSetting("direct_stream_sub_select") + external_subs(selected_media_source, list_item, item_id, int(subtitle_index)) - # add playurl and data to the monitor + # Add playurl and data to the monitor data = {} data["item_id"] = item_id data["source_id"] = source_id @@ -838,7 +840,7 @@ def audio_subs_pref(url, list_item, media_source, item_id, audio_stream_index, s subtitle_streams_list[track] = index subtitle_streams.append(track) - # set audio index + # Set audio index if select_audio_index is not None: playurlprefs += "&AudioStreamIndex=%s" % select_audio_index @@ -852,12 +854,12 @@ def audio_subs_pref(url, list_item, media_source, item_id, audio_stream_index, s else: # User backed out of selection playurlprefs += "&AudioStreamIndex=%s" % default_audio - # set subtitle index + # Set subtitle index if select_subs_index is not None: # Load subtitles in the listitem if downloadable if select_subs_index in downloadable_streams: - subtitle_url = "%s/Videos/%s/%s/Subtitles/%s/Stream.srt" - subtitle_url = subtitle_url % (settings.getSetting('server_address'), item_id, source_id, select_subs_index) + delivery_url = downloadable_streams[select_subs_index]['DeliveryUrl'] + subtitle_url = settings.getSetting('server_address') + delivery_url log.debug("Streaming subtitles url: {0} {1}".format(select_subs_index, subtitle_url)) list_item.setSubtitles([subtitle_url]) else: @@ -876,8 +878,8 @@ def audio_subs_pref(url, list_item, media_source, item_id, audio_stream_index, s # Load subtitles in the listitem if downloadable if select_subs_index in downloadable_streams: - subtitle_url = "%s/Videos/%s/%s/Subtitles/%s/Stream.srt" - subtitle_url = subtitle_url % (settings.getSetting('server_address'), item_id, source_id, select_subs_index) + delivery_url = downloadable_streams[select_subs_index]['DeliveryUrl'] + subtitle_url = settings.getSetting('server_address') + delivery_url log.debug("Streaming subtitles url: {0} {1}".format(select_subs_index, subtitle_url)) list_item.setSubtitles([subtitle_url]) else: @@ -892,8 +894,8 @@ def audio_subs_pref(url, list_item, media_source, item_id, audio_stream_index, s return new_url -# direct stream, set any available subtitle streams -def external_subs(media_source, list_item, item_id): +# Add any available, streamable subtitle +def external_subs(media_source, list_item, item_id, subtitle_index): media_streams = media_source.get('MediaStreams') if media_streams is None: @@ -926,7 +928,7 @@ def external_subs(media_source, list_item, item_id): title = stream['Title'] ''' Starting in 10.8, the server no longer provides language - specific download points. We have to download the file + specific download points. We have to download the file and name it with the language code ourselves so Kodi will parse it correctly ''' @@ -943,16 +945,16 @@ def external_subs(media_source, list_item, item_id): if len(externalsubs) == 0: return - direct_stream_sub_select = settings.getSetting("direct_stream_sub_select") - - if direct_stream_sub_select == "0" or (len(externalsubs) == 1 and not direct_stream_sub_select == "2"): + if subtitle_index is None: list_item.setSubtitles(externalsubs) - else: + elif len(externalsubs) < subtitle_index: resp = xbmcgui.Dialog().select(translate_string(30292), sub_names) if resp > -1: selected_sub = externalsubs[resp] log.debug("External Subtitle Selected: {0}".format(selected_sub)) list_item.setSubtitles([selected_sub]) + else: + list_item.setSubtitles(externalsubs[subtitle_index]) def send_progress(): @@ -1207,9 +1209,9 @@ def get_jellyfin_playing_item(): return play_data.get("item_id") def get_play_url(media_source, play_session_id, channel_id=None): - log.debug("get_play_url - media_source: {0}", media_source) + log.debug("get_play_url - media_source: {0}".format(media_source)) - # check if strm file Container + # Check if strm file Container if media_source.get('Container') == 'strm': log.debug("Detected STRM Container") playurl, listitem_props = get_strm_details(media_source) @@ -1219,7 +1221,7 @@ def get_play_url(media_source, play_session_id, channel_id=None): else: return playurl, "0", listitem_props - # get all the options + # Get usere options server = settings.getSetting('server_address') allow_direct_file_play = settings.getSetting('allow_direct_file_play') == 'true' @@ -1230,13 +1232,13 @@ def get_play_url(media_source, play_session_id, channel_id=None): playurl = None playback_type = None - # check if file can be directly played + # Check if file can be directly played via local files if allow_direct_file_play and can_direct_play: direct_path = media_source["Path"] direct_path = direct_path.replace("\\", "/") direct_path = direct_path.strip() - # handle DVD structure + # Handle DVD structure container = media_source["Container"] if container == "dvd": direct_path = direct_path + "/VIDEO_TS/VIDEO_TS.IFO" @@ -1252,11 +1254,11 @@ def get_play_url(media_source, play_session_id, channel_id=None): playurl = direct_path playback_type = "0" - # check if file can be direct streamed/played + # Check if file can be direct streamed/played if (can_direct_stream or can_direct_play) and playurl is None: item_id = media_source.get('Id') if channel_id: - # live tv has to be transcoded by the server + # Live tv has to be transcoded by the server playurl = None else: url_root = '{}/Videos/{}/stream'.format(server, item_id) @@ -1269,48 +1271,9 @@ def get_play_url(media_source, play_session_id, channel_id=None): playurl = '{}?{}'.format(url_root, play_param_string) playback_type = "1" - # check is file can be transcoded + # Check if file can be remuxed/transcoded if can_transcode and playurl is None: - item_id = media_source.get('Id') - device_id = get_device_id() - - user_details = load_user_details() - user_token = user_details.get('token') - bitrate = get_bitrate(settings.getSetting("force_max_stream_bitrate")) - playback_max_width = settings.getSetting("playback_max_width") - audio_codec = settings.getSetting("audio_codec") - audio_playback_bitrate = settings.getSetting("audio_playback_bitrate") - audio_bitrate = int(audio_playback_bitrate) * 1000 - audio_max_channels = settings.getSetting("audio_max_channels") - playback_video_force_8 = settings.getSetting("playback_video_force_8") == "true" - - transcode_params = { - "MediaSourceId": item_id, - "DeviceId": device_id, - "PlaySessionId": play_session_id, - "api_key": user_token, - "SegmentContainer": "ts", - "VideoCodec": "h264", - "VideoBitrate": bitrate, - "MaxWidth": playback_max_width, - "AudioCodec": audio_codec, - "TranscodingMaxAudioChannels": audio_max_channels, - "AudioBitrate": audio_bitrate - } - if playback_video_force_8: - transcode_params.update({"MaxVideoBitDepth": "8"}) - - # We need to include the channel ID if this is a live stream - if channel_id: - if media_source.get('LiveStreamId'): - transcode_params['LiveStreamId'] = media_source.get('LiveStreamId') - transcode_path = urlencode(transcode_params) - playurl = '{}/Videos/{}/master.m3u8?{}'.format( - server, channel_id, transcode_path) - else: - transcode_path = urlencode(transcode_params) - playurl = '{}/Videos/{}/master.m3u8?{}'.format( - server, item_id, transcode_path) + playurl = '{}{}'.format(server, media_source['TranscodingUrl']) playback_type = "2" @@ -1535,6 +1498,7 @@ def screensaver_deactivated(self): def get_item_playback_info(item_id, force_transcode): + url = "" filtered_codecs = [] if settings.getSetting("force_transcode_h265") == "true": filtered_codecs.append("hevc") @@ -1548,51 +1512,25 @@ def get_item_playback_info(item_id, force_transcode): if settings.getSetting("force_transcode_av1") == "true": filtered_codecs.append("av1") - if not force_transcode: - bitrate = get_bitrate(settings.getSetting("max_stream_bitrate")) - else: - bitrate = get_bitrate(settings.getSetting("force_max_stream_bitrate")) - - audio_codec = settings.getSetting("audio_codec") - audio_playback_bitrate = settings.getSetting("audio_playback_bitrate") - audio_max_channels = settings.getSetting("audio_max_channels") - - audio_bitrate = int(audio_playback_bitrate) * 1000 - profile = { - "Name": "Kodi", - "MaxStaticBitrate": bitrate, - "MaxStreamingBitrate": bitrate, - "MusicStreamingTranscodingBitrate": audio_bitrate, - "TimelineOffsetSeconds": 5, + "Name": "Jellycon", + "SupportedMediaTypes": "Audio,Photo,Video", + "TimelineOffsetSeconds": 0, "TranscodingProfiles": [ { - "Type": "Audio" - }, - { - "Container": "ts", - "Protocol": "hls", + "Container": "mp4", "Type": "Video", - "AudioCodec": audio_codec, - "VideoCodec": "h264", - "MaxAudioChannels": audio_max_channels - }, - { - "Container": "jpeg", - "Type": "Photo" - } - ], - "DirectPlayProfiles": [ - { - "Type": "Video" + "Protocol": "hls" }, { "Type": "Audio" }, { - "Type": "Photo" + "Type": "Photo", + "Container": "jpeg" } ], + "DirectPlayProfiles": [], "ResponseProfiles": [], "ContainerProfiles": [], "CodecProfiles": [], @@ -1664,12 +1602,50 @@ def get_item_playback_info(item_id, force_transcode): ] } - if len(filtered_codecs) > 0: - profile['DirectPlayProfiles'][0]['VideoCodec'] = "-%s" % ",".join(filtered_codecs) + # If we are not force transcoding, add direct play profiles + if not force_transcode: + bitrate = get_bitrate(settings.getSetting("max_stream_bitrate")) + profile['MaxStaticBitrate'] = bitrate + profile['MaxStreamingBitrate'] = bitrate + profile['DirectPlayProfiles'] = [ + { + "Type": "Video", + }, + { + "Type": "Audio" + }, + { + "Type": "Photo" + } + ] + + # Forced codecs always need to be respected, therefore explicitly set them as unsupported + if len(filtered_codecs) > 0: + profile['DirectPlayProfiles'][0]["VideoCodec"] = "-%s" % ",".join(filtered_codecs) + + url = "/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s" % (item_id, bitrate) + # Replace transcoding profile with more restrictive one, respecting user configuration + else: + bitrate = get_bitrate(settings.getSetting("max_stream_bforce_max_stream_bitrateitrate")) + profile['MaxStaticBitrate'] = bitrate + profile['MaxStreamingBitrate'] = bitrate + profile['EnableDirectPlay'] = False + profile['EnableDirectStream'] = False - if force_transcode: - profile['DirectPlayProfiles'] = [] + audio_codec = settings.getSetting("audio_codec") + audio_max_channels = settings.getSetting("audio_max_channels") + profile['TranscodingProfiles'][0] = { + "Type": "Video", + "Container": "mp4", + "Protocol": "hls", + "AudioCodec": audio_codec, + "VideoCodec": "h264", + "MaxAudioChannels" : audio_max_channels, + } + + url = "/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s&EnableDirectPlay=false&EnableDirectStream=false" % (item_id, bitrate) + # This enforces SDR video if settings.getSetting("playback_video_force_8") == "true": profile['CodecProfiles'].append( { @@ -1677,8 +1653,8 @@ def get_item_playback_info(item_id, force_transcode): "Codec": "h264", "Conditions": [ { - "Condition": "LessThanEqual", "Property": "VideoBitDepth", + "Condition": "LessThanEqual", "Value": "8", "IsRequired": False } @@ -1691,9 +1667,24 @@ def get_item_playback_info(item_id, force_transcode): "Codec": "h265,hevc", "Conditions": [ { - "Condition": "EqualsAny", "Property": "VideoProfile", - "Value": "main" + "Condition": "EqualsAny", + "Value": "main", + "IsRequired": False + } + ] + } + ) + profile['CodecProfiles'].append( + { + "Type": "Video", + "Codec": "av1", + "Conditions": [ + { + "Property": "VideoBitDepth", + "Condition": "LessThanEqual", + "Value": "8", + "IsRequired": False } ] } @@ -1705,11 +1696,6 @@ def get_item_playback_info(item_id, force_transcode): 'AutoOpenLiveStream': True } - if force_transcode: - url = "/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s&EnableDirectPlay=false&EnableDirectStream=false" % (item_id, bitrate) - else: - url = "/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s" % (item_id, bitrate) - log.debug("PlaybackInfo : {0}".format(url)) log.debug("PlaybackInfo : {0}".format(profile)) play_info_result = api.post(url, playback_info) From 6ec4c048742e424dd95a3330c0ca90c07472bd15 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 27 Dec 2024 15:15:29 +0100 Subject: [PATCH 2/5] Enforce transcoding width --- resources/lib/play_utils.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/resources/lib/play_utils.py b/resources/lib/play_utils.py index 1d68e20f..9f6d1bad 100644 --- a/resources/lib/play_utils.py +++ b/resources/lib/play_utils.py @@ -792,7 +792,6 @@ def audio_subs_pref(url, list_item, media_source, item_id, audio_stream_index, s playurlprefs = "" default_audio = media_source.get('DefaultAudioStreamIndex', 1) default_sub = media_source.get('DefaultSubtitleStreamIndex', "") - source_id = media_source["Id"] media_streams = media_source['MediaStreams'] @@ -1626,7 +1625,7 @@ def get_item_playback_info(item_id, force_transcode): url = "/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s" % (item_id, bitrate) # Replace transcoding profile with more restrictive one, respecting user configuration else: - bitrate = get_bitrate(settings.getSetting("max_stream_bforce_max_stream_bitrateitrate")) + bitrate = get_bitrate(settings.getSetting("force_max_stream_bitrate")) profile['MaxStaticBitrate'] = bitrate profile['MaxStreamingBitrate'] = bitrate profile['EnableDirectPlay'] = False @@ -1635,14 +1634,26 @@ def get_item_playback_info(item_id, force_transcode): audio_codec = settings.getSetting("audio_codec") audio_max_channels = settings.getSetting("audio_max_channels") profile['TranscodingProfiles'][0] = { - "Type": "Video", - "Container": "mp4", - "Protocol": "hls", - "AudioCodec": audio_codec, - "VideoCodec": "h264", - "MaxAudioChannels" : audio_max_channels, - } - + "Type": "Video", + "Container": "mp4", + "Protocol": "hls", + "VideoCodec": "h264", + "AudioCodec": audio_codec, + "MaxAudioChannels" : audio_max_channels, + } + + playback_max_width = settings.getSetting("playback_max_width") + if playback_max_width is not None: + profile['TranscodingProfiles'][0]['Conditions'] = [ + { + "Property": "Width ", + "Condition": "LessThanEqual", + "Value": playback_max_width, + "IsRequired": False + } + ] + + url = "/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s&EnableDirectPlay=false&EnableDirectStream=false" % (item_id, bitrate) # This enforces SDR video From f70dbc058829dc167ebe050cba6a7fa4af307942 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 27 Dec 2024 15:15:40 +0100 Subject: [PATCH 3/5] Fix bitrate selection on transcode --- resources/lib/dialogs.py | 8 ++- resources/lib/functions.py | 8 +-- .../skins/default/720p/BitrateDialog.xml | 58 +++++++++---------- 3 files changed, 38 insertions(+), 36 deletions(-) diff --git a/resources/lib/dialogs.py b/resources/lib/dialogs.py index bf35a945..85d6afd5 100644 --- a/resources/lib/dialogs.py +++ b/resources/lib/dialogs.py @@ -16,7 +16,10 @@ class BitrateDialog(xbmcgui.WindowXMLDialog): slider_control = None bitrate_label = None - initial_bitrate_value = 0 + initial_bitrate_value = 500 + max_bitrate_value = 15000 + min_bitrate_value = 500 + step_size = 100 selected_transcode_value = 0 def __init__(self, *args, **kwargs): @@ -25,10 +28,11 @@ def __init__(self, *args, **kwargs): def onInit(self): log.debug("ActionMenu: onInit") + log.debug("ActionMenu: max: {} - min: {}, init: {}".format(self.max_bitrate_value, self.min_bitrate_value, self.initial_bitrate_value)) self.action_exitkeys_id = [10, 13] self.slider_control = self.getControl(3000) - self.slider_control.setInt(self.initial_bitrate_value, 400, 100, 15000) + self.slider_control.setInt(self.initial_bitrate_value, self.min_bitrate_value, self.step_size, self.max_bitrate_value) self.bitrate_label = self.getControl(3030) bitrate_label_string = str(self.slider_control.getInt()) + " Kbs" diff --git a/resources/lib/functions.py b/resources/lib/functions.py index a6301044..a266452f 100644 --- a/resources/lib/functions.py +++ b/resources/lib/functions.py @@ -17,7 +17,7 @@ from .jellyfin import api from .utils import ( - translate_string, get_version, load_user_details, get_art_url, + get_bitrate, translate_string, get_version, load_user_details, get_art_url, get_default_filters, translate_path, kodi_version, get_jellyfin_url ) from .kodi_utils import HomeWindow @@ -536,12 +536,12 @@ def show_menu(params): elif selected_action == "transcode": params['force_transcode'] = 'true' - max_bitrate = settings.getSetting("force_max_stream_bitrate") - initial_bitrate_value = int(max_bitrate) + max_bitrate = int(get_bitrate(settings.getSetting("force_max_stream_bitrate")) / 1000) bitrate_dialog = BitrateDialog( "BitrateDialog.xml", PLUGINPATH, "default", "720p" ) - bitrate_dialog.initial_bitrate_value = initial_bitrate_value + bitrate_dialog.max_bitrate_value = max_bitrate + bitrate_dialog.initial_bitrate_value = max_bitrate bitrate_dialog.doModal() selected_transcode_value = bitrate_dialog.selected_transcode_value del bitrate_dialog diff --git a/resources/skins/default/720p/BitrateDialog.xml b/resources/skins/default/720p/BitrateDialog.xml index bc758fd2..c025865c 100644 --- a/resources/skins/default/720p/BitrateDialog.xml +++ b/resources/skins/default/720p/BitrateDialog.xml @@ -8,7 +8,6 @@ 280 - 0 0 @@ -17,36 +16,36 @@ bg.png - - 120 - 20 - 5 - 45 - - 99FFFFFF - font14 - left - + + 120 + 20 + 5 + 45 + + 99FFFFFF + font14 + left + - - 150 - 120 - 5 - 45 - - 99FFFFFF - font14 - left - + + 150 + 120 + 5 + 45 + + 99FFFFFF + font14 + left + - - 20 - 55 - 340 - 30 - 3011 - true - + + 20 + 55 + 340 + 30 + 3011 + true + white.png @@ -60,6 +59,5 @@ font14 center - From feb16da71e7f6d0c601b49c9522dd8836c75b9b1 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 28 Dec 2024 10:23:43 +0100 Subject: [PATCH 4/5] Remove non-existing fields --- resources/lib/play_utils.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/resources/lib/play_utils.py b/resources/lib/play_utils.py index 9f6d1bad..1cbee52c 100644 --- a/resources/lib/play_utils.py +++ b/resources/lib/play_utils.py @@ -405,8 +405,8 @@ def play_file(play_info): if user_data.get("PlaybackPositionTicks") != 0: reasonable_ticks = int(user_data.get("PlaybackPositionTicks")) / 1000 - seek_time = round(reasonable_ticks / 10000,0) - display_time = (datetime.datetime(1,1,1) + datetime.timedelta(seconds=seek_time)).strftime('%H:%M:%S') + seek_time = round(reasonable_ticks / 10000, 0) + display_time = (datetime.datetime(1, 1, 1) + datetime.timedelta(seconds=seek_time)).strftime('%H:%M:%S') resume_dialog = ResumeDialog("ResumeDialog.xml", addon_path, "default", "720p") resume_dialog.setResumeTime("Resume from " + display_time) @@ -1428,11 +1428,10 @@ def onNotification(self, sender, method, data): ): return - signal = method.split('.', 1)[-1] if signal not in ( "jellycon_play_action", "jellycon_play_youtube_trailer_action", - "set_view", "plugin.video.jellycon_play_action"): + "set_view", "plugin.video.jellycon_play_action"): return data_json = json.loads(data) @@ -1617,7 +1616,7 @@ def get_item_playback_info(item_id, force_transcode): "Type": "Photo" } ] - + # Forced codecs always need to be respected, therefore explicitly set them as unsupported if len(filtered_codecs) > 0: profile['DirectPlayProfiles'][0]["VideoCodec"] = "-%s" % ",".join(filtered_codecs) @@ -1628,8 +1627,6 @@ def get_item_playback_info(item_id, force_transcode): bitrate = get_bitrate(settings.getSetting("force_max_stream_bitrate")) profile['MaxStaticBitrate'] = bitrate profile['MaxStreamingBitrate'] = bitrate - profile['EnableDirectPlay'] = False - profile['EnableDirectStream'] = False audio_codec = settings.getSetting("audio_codec") audio_max_channels = settings.getSetting("audio_max_channels") @@ -1639,7 +1636,7 @@ def get_item_playback_info(item_id, force_transcode): "Protocol": "hls", "VideoCodec": "h264", "AudioCodec": audio_codec, - "MaxAudioChannels" : audio_max_channels, + "MaxAudioChannels": audio_max_channels, } playback_max_width = settings.getSetting("playback_max_width") @@ -1653,7 +1650,6 @@ def get_item_playback_info(item_id, force_transcode): } ] - url = "/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s&EnableDirectPlay=false&EnableDirectStream=false" % (item_id, bitrate) # This enforces SDR video From 7ed82a22c05d3e5065e29455661a4c0750ed63e1 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Wed, 20 Aug 2025 15:04:04 +0200 Subject: [PATCH 5/5] Apply review suggestions --- resources/lib/play_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/lib/play_utils.py b/resources/lib/play_utils.py index 1cbee52c..b67f5149 100644 --- a/resources/lib/play_utils.py +++ b/resources/lib/play_utils.py @@ -857,7 +857,7 @@ def audio_subs_pref(url, list_item, media_source, item_id, audio_stream_index, s if select_subs_index is not None: # Load subtitles in the listitem if downloadable if select_subs_index in downloadable_streams: - delivery_url = downloadable_streams[select_subs_index]['DeliveryUrl'] + delivery_url = media_streams[select_subs_index]['DeliveryUrl'] subtitle_url = settings.getSetting('server_address') + delivery_url log.debug("Streaming subtitles url: {0} {1}".format(select_subs_index, subtitle_url)) list_item.setSubtitles([subtitle_url]) @@ -877,7 +877,7 @@ def audio_subs_pref(url, list_item, media_source, item_id, audio_stream_index, s # Load subtitles in the listitem if downloadable if select_subs_index in downloadable_streams: - delivery_url = downloadable_streams[select_subs_index]['DeliveryUrl'] + delivery_url = media_streams[select_subs_index]['DeliveryUrl'] subtitle_url = settings.getSetting('server_address') + delivery_url log.debug("Streaming subtitles url: {0} {1}".format(select_subs_index, subtitle_url)) list_item.setSubtitles([subtitle_url])