From 431213552433fee651f43b8c028b193de99e78fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Chv=C3=ADla?= Date: Tue, 15 Sep 2020 00:12:29 +0200 Subject: [PATCH 1/4] playutils: Prettify audio/subs track selection dialog * Capitalize language in the track title * Capitalize channel layout in the track title (e.g. 'Stereo') * Remove track index from the title - users generally don't care what the # is * Use commercial names for the audio codecs (e.g. 'Dolby Digital' instead of 'ac3') * Always show codec name for subtitle tracks --- jellyfin_kodi/helper/playutils.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/jellyfin_kodi/helper/playutils.py b/jellyfin_kodi/helper/playutils.py index d6b5259d..628a2d49 100644 --- a/jellyfin_kodi/helper/playutils.py +++ b/jellyfin_kodi/helper/playutils.py @@ -526,6 +526,23 @@ class PlayUtils(object): return path + def get_commercial_codec_name(self, codec, profile): + NAMES = { + 'ac3': 'Dolby Digital', + 'eac3': 'Dolby Digital+', + 'truehd': 'Dolby TrueHD', + 'dts': 'DTS' + } + + if profile == 'DTS-HD MA': + return 'DTS-HD Master Audio' + if profile == 'DTS-HD HRA': + return 'DTS-HD High Resolution Audio' + if codec in NAMES: + return NAMES[codec] + + return codec.upper() + def get_audio_subs(self, source, audio=None, subtitle=None): ''' For transcoding only @@ -546,22 +563,24 @@ class PlayUtils(object): if stream_type == 'Audio': - codec = stream['Codec'] - channel = stream.get('ChannelLayout', "") + profile = stream['Profile'] if 'Profile' in stream else None + codec = self.get_commercial_codec_name(stream['Codec'], profile) + channel = stream.get('ChannelLayout', "").capitalize() if 'Language' in stream: - track = "%s - %s - %s %s" % (index, stream['Language'], codec, channel) + track = "%s - %s %s" % (stream['Language'].capitalize(), codec, channel) else: - track = "%s - %s %s" % (index, codec, channel) + track = "%s %s" % (codec, channel) audio_streams[track] = index elif stream_type == 'Subtitle': + codec = self.get_commercial_codec_name(stream['Codec'], None) if 'Language' in stream: - track = "%s - %s" % (index, stream['Language']) + track = "%s - %s" % (stream['Language'].capitalize(), codec) else: - track = "%s - %s" % (index, stream['Codec']) + track = "%s" % codec if stream['IsDefault']: track = "%s - Default" % track From e651b66ddc653a366068c2b0a69b49ed354e7f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Chv=C3=ADla?= Date: Tue, 15 Sep 2020 00:25:12 +0200 Subject: [PATCH 2/4] Add more transcoding options * Redo max. bitrate options, add more bitrates in the lower end * Remove SD/HD from the bitrate strings as the quality doesn't match up to what the backend selects (e.g. 1.3 Mbps definitely wasn't HD) * Add option to force transcode mpeg2 * Add option to choose between h264 & h265 as a preferred codec for transcoding * Add option to select preferred audio codec for transcoding/directstream * Add option to select audio bitrate when transcoding (previously hardcoded to 384kbps for >2.0ch & 192kbps for <=2.0ch) * Add option to select max audio channels (e.g. for server-side downmix) * Add option to hide embedded/internal & picture-based subtitles in the track selection dialog --- jellyfin_kodi/helper/playutils.py | 105 ++++++++++++------ .../resource.language.en_gb/strings.po | 32 +++++- resources/settings.xml | 16 ++- 3 files changed, 110 insertions(+), 43 deletions(-) diff --git a/jellyfin_kodi/helper/playutils.py b/jellyfin_kodi/helper/playutils.py index 628a2d49..1279270b 100644 --- a/jellyfin_kodi/helper/playutils.py +++ b/jellyfin_kodi/helper/playutils.py @@ -289,49 +289,88 @@ class PlayUtils(object): return self.info['Path'] - def get_bitrate(self): + def get_max_bitrate(self): ''' Get the video quality based on add-on settings. Max bit rate supported by server: 2147483 (max signed 32bit integer) ''' - bitrate = [664, 996, 1320, 2000, 3200, - 4700, 6200, 7700, 9200, 10700, - 12200, 13700, 15200, 16700, 18200, - 20000, 25000, 30000, 35000, 40000, - 100000, 1000000, 2147483] - return bitrate[int(settings('videoBitrate') or 22)] + bitrate = [500, 1000, 1500, 2000, 2500, 3000, 4000, 5000, 6000, + 7000, 8000, 9000, 10000, 12000, 14000, 16000, 18000, + 20000, 25000, 30000, 35000, 40000, 100000, 1000000, 2147483] + return bitrate[int(settings('maxBitrate') or 24)] * 1000 def get_resolution(self): return int(xbmc.getInfoLabel('System.ScreenWidth')), int(xbmc.getInfoLabel('System.ScreenHeight')) + def get_directplay_video_codec(self): + codecs = ['h264', 'hevc', 'h265', 'mpeg4', 'mpeg2video', 'vc1'] + + if settings('transcode_h265.bool'): + codecs.remove('hevc') + codecs.remove('h265') + + if settings('transcode_mpeg2.bool'): + codecs.remove('mpeg2video') + + return ','.join(codecs) + + def get_transcoding_video_codec(self): + codecs = ['h264', 'hevc', 'h265', 'mpeg4', 'mpeg2video', 'vc1'] + + if settings('transcode_h265.bool'): + codecs.remove('hevc') + codecs.remove('h265') + else: + if settings('videoPreferredCodec') == 'H265/HEVC': + codecs.insert(2, codecs.pop(codecs.index('h264'))) + + if settings('transcode_mpeg2.bool'): + codecs.remove('mpeg2video') + + return ','.join(codecs) + + def get_transcoding_audio_codec(self): + codecs = ['aac', 'mp3', 'ac3', 'opus', 'flac', 'vorbis'] + + preferred = settings('audioPreferredCodec').lower() + if preferred in codecs: + codecs.insert(0, codecs.pop(codecs.index(preferred))) + + return ','.join(codecs) + + def get_transcoding_audio_bitrate(self): + bitrate = [96, 128, 160, 192, 256, 320, 384] + return bitrate[int(settings('audioBitrate') or 6)] * 1000 + def get_device_profile(self): ''' Get device profile based on the add-on settings. ''' profile = { "Name": "Kodi", - "MaxStreamingBitrate": self.get_bitrate() * 1000, + "MaxStreamingBitrate": self.get_max_bitrate(), "MusicStreamingTranscodingBitrate": 1280000, "TimelineOffsetSeconds": 5, "TranscodingProfiles": [ + { + "Type": "Video", + "Container": "m3u8", + "AudioCodec": self.get_transcoding_audio_codec(), + "VideoCodec": self.get_transcoding_video_codec(), + "MaxAudioChannels": settings('audioMaxChannels') + }, { "Type": "Audio" }, { - "Container": "m3u8", - "Type": "Video", - "AudioCodec": "aac,mp3,ac3,opus,flac,vorbis", - "VideoCodec": "h264,mpeg4,mpeg2video", - "MaxAudioChannels": "6" - }, - { - "Container": "jpeg", - "Type": "Photo" + "Type": "Photo", + "Container": "jpeg" } ], "DirectPlayProfiles": [ { - "Type": "Video" + "Type": "Video", + "VideoCodec": self.get_directplay_video_codec() }, { "Type": "Audio" @@ -410,16 +449,6 @@ class PlayUtils(object): } ] } - if settings('transcode_h265.bool'): - profile['DirectPlayProfiles'][0]['VideoCodec'] = "h264,mpeg4,mpeg2video" - else: - profile['TranscodingProfiles'].insert(0, { - "Container": "m3u8", - "Type": "Video", - "AudioCodec": "aac,mp3,ac3,opus,flac,vorbis", - "VideoCodec": "h264,h265,hevc,mpeg4,mpeg2video", - "MaxAudioChannels": "6" - }) if settings('transcodeHi10P.bool'): profile['CodecProfiles'].append( @@ -556,6 +585,8 @@ class PlayUtils(object): subs_streams = collections.OrderedDict() streams = source['MediaStreams'] + allow_burned_subs = settings('allowBurnedSubs.bool') + for stream in streams: index = stream['Index'] @@ -575,6 +606,10 @@ class PlayUtils(object): audio_streams[track] = index elif stream_type == 'Subtitle': + downloadable = stream['IsTextSubtitleStream'] and stream['IsExternal'] and stream['SupportsExternalStream'] + if not downloadable and not allow_burned_subs: + continue + codec = self.get_commercial_codec_name(stream['Codec'], None) if 'Language' in stream: @@ -608,7 +643,7 @@ class PlayUtils(object): self.info['AudioStreamIndex'] = audio_selected prefs += "&AudioStreamIndex=%s" % audio_selected - prefs += "&AudioBitrate=384000" if streams[audio_selected].get('Channels', 0) > 2 else "&AudioBitrate=192000" + prefs += "&AudioBitrate=%d" % self.get_transcoding_audio_bitrate() if subtitle: @@ -618,10 +653,10 @@ class PlayUtils(object): if server_settings['EnableSubtitleExtraction'] and stream['SupportsExternalStream']: self.info['SubtitleUrl'] = self.get_subtitles(source, stream, index) - else: + self.info['SubtitleStreamIndex'] = index + elif allow_burned_subs: prefs += "&SubtitleStreamIndex=%s" % index - - self.info['SubtitleStreamIndex'] = index + self.info['SubtitleStreamIndex'] = index elif skip_dialog in (0, 2) and len(subs_streams): @@ -638,10 +673,10 @@ class PlayUtils(object): if server_settings['EnableSubtitleExtraction'] and stream['SupportsExternalStream']: self.info['SubtitleUrl'] = self.get_subtitles(source, stream, index) - else: + self.info['SubtitleStreamIndex'] = index + elif allow_burned_subs: prefs += "&SubtitleStreamIndex=%s" % index - - self.info['SubtitleStreamIndex'] = index + self.info['SubtitleStreamIndex'] = index return prefs diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index c4690526..1552c9cf 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -74,8 +74,28 @@ msgid "Enable enhanced artwork (i.e. cover art)" msgstr "Enable enhanced artwork (i.e. cover art)" msgctxt "#30160" -msgid "Video quality" -msgstr "Video quality" +msgid "Max stream bitrate" +msgstr "Max stream bitrate" + +msgctxt "#30161" +msgid "Preferred video codec" +msgstr "Preferred video codec" + +msgctxt "#30162" +msgid "Preferred audio codec" +msgstr "Preferred audio codec" + +msgctxt "#30163" +msgid "Audio bitrate" +msgstr "Audio bitrate" + +msgctxt "#30164" +msgid "Audio max channels" +msgstr "Audio max channels" + +msgctxt "#30165" +msgid "Allow burned subtitles" +msgstr "Allow burned subtitles" msgctxt "#30170" msgid "Recently Added TV Shows" @@ -277,6 +297,10 @@ msgctxt "#30522" msgid "Transcode H265/HEVC" msgstr "Transcode H265/HEVC" +msgctxt "#30523" +msgid "Transcode MPEG2" +msgstr "Transcode MPEG2" + msgctxt "#30527" msgid "Ignore specials in next episodes" msgstr "Ignore specials in next episodes" @@ -613,8 +637,8 @@ msgid "Enable external subtitles" msgstr "Enable external subtitles" msgctxt "#33115" -msgid "Adjust for remote connection" -msgstr "Adjust for remote connection" +msgid "Transcode options" +msgstr "Transcode options" msgctxt "#33116" msgid "Compress artwork (reduces quality)" diff --git a/resources/settings.xml b/resources/settings.xml index e141b9ac..ff515335 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -38,12 +38,20 @@ - - + + + + + + + + + + + - - + From fea6fc4cf1fb3006cd100f5f8d8a572b644394bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Chv=C3=ADla?= Date: Tue, 15 Sep 2020 01:09:02 +0200 Subject: [PATCH 3/4] playutils: Always honor manual transcoding bitrates * Override VideoBitrate option in transcoding url (previously calced in backend) * Use manual AudioBitrate when user has track selection dialog disabled * Fix bad loop-and-remove --- jellyfin_kodi/helper/playutils.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/jellyfin_kodi/helper/playutils.py b/jellyfin_kodi/helper/playutils.py index 1279270b..e64448f6 100644 --- a/jellyfin_kodi/helper/playutils.py +++ b/jellyfin_kodi/helper/playutils.py @@ -241,15 +241,22 @@ class PlayUtils(object): if self.item['MediaType'] == 'Video': base, params = source['TranscodingUrl'].split('?') + url_parsed = params.split('&') + manual_tracks = '' + + # manual bitrate + url_parsed = [p for p in url_parsed if 'AudioBitrate' not in p and 'VideoBitrate' not in p] if settings('skipDialogTranscode') != "3" and source.get('MediaStreams'): - url_parsed = params.split('&') + # manual tracks + url_parsed = [p for p in url_parsed if 'AudioStreamIndex' not in p and 'SubtitleStreamIndex' not in p] + manual_tracks = self.get_audio_subs(source, audio, subtitle) - for i in url_parsed: - if 'AudioStreamIndex' in i or 'AudioBitrate' in i or 'SubtitleStreamIndex' in i: # handle manually - url_parsed.remove(i) + audio_bitrate = self.get_transcoding_audio_bitrate() + video_bitrate = self.get_max_bitrate() - audio_bitrate - params = "%s%s" % ('&'.join(url_parsed), self.get_audio_subs(source, audio, subtitle)) + params = "%s%s" % ('&'.join(url_parsed), manual_tracks) + params += "&VideoBitrate=%s&AudioBitrate=%s" % (video_bitrate, audio_bitrate) video_type = 'live' if source['Protocol'] == 'LiveTV' else 'master' base = base.replace('stream' if 'stream' in base else 'master', video_type, 1) @@ -643,7 +650,6 @@ class PlayUtils(object): self.info['AudioStreamIndex'] = audio_selected prefs += "&AudioStreamIndex=%s" % audio_selected - prefs += "&AudioBitrate=%d" % self.get_transcoding_audio_bitrate() if subtitle: From 0fc908ec8510422b46e844b63300dea9f8547292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Chv=C3=ADla?= Date: Thu, 24 Sep 2020 17:38:28 +0200 Subject: [PATCH 4/4] settings: Add an option to transcode VC-1 --- jellyfin_kodi/helper/playutils.py | 6 ++++++ resources/language/resource.language.en_gb/strings.po | 4 ++++ resources/settings.xml | 1 + 3 files changed, 11 insertions(+) diff --git a/jellyfin_kodi/helper/playutils.py b/jellyfin_kodi/helper/playutils.py index e64448f6..05ccaa35 100644 --- a/jellyfin_kodi/helper/playutils.py +++ b/jellyfin_kodi/helper/playutils.py @@ -319,6 +319,9 @@ class PlayUtils(object): if settings('transcode_mpeg2.bool'): codecs.remove('mpeg2video') + if settings('transcode_vc1.bool'): + codecs.remove('vc1') + return ','.join(codecs) def get_transcoding_video_codec(self): @@ -334,6 +337,9 @@ class PlayUtils(object): if settings('transcode_mpeg2.bool'): codecs.remove('mpeg2video') + if settings('transcode_vc1.bool'): + codecs.remove('vc1') + return ','.join(codecs) def get_transcoding_audio_codec(self): diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index 1552c9cf..5a006ab2 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -301,6 +301,10 @@ msgctxt "#30523" msgid "Transcode MPEG2" msgstr "Transcode MPEG2" +msgctxt "#30524" +msgid "Transcode VC-1" +msgstr "Transcode VC-1" + msgctxt "#30527" msgid "Ignore specials in next episodes" msgstr "Ignore specials in next episodes" diff --git a/resources/settings.xml b/resources/settings.xml index ff515335..cac0aede 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -46,6 +46,7 @@ +