diff --git a/src/plugins/htmlVideoPlayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js index d3c9e6b7f8c..de9a51202a6 100644 --- a/src/plugins/htmlVideoPlayer/plugin.js +++ b/src/plugins/htmlVideoPlayer/plugin.js @@ -35,6 +35,7 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../components/ba import { PluginType } from '../../types/plugin.ts'; import Events from '../../utils/events.ts'; import { includesAny } from '../../utils/container.ts'; +import { isHls } from '../../utils/mediaSource.ts'; import debounce from 'lodash-es/debounce'; /** @@ -69,12 +70,12 @@ function tryRemoveElement(elem) { } } -function enableNativeTrackSupport(currentSrc, track) { +function enableNativeTrackSupport(mediaSource, track) { if (track?.DeliveryMethod === 'Embed') { return true; } - if (browser.firefox && (currentSrc || '').toLowerCase().includes('.m3u8')) { + if (browser.firefox && isHls(mediaSource)) { return false; } @@ -341,15 +342,13 @@ export class HtmlVideoPlayer { * @private */ updateVideoUrl(streamInfo) { - const isHls = streamInfo.url.toLowerCase().includes('.m3u8'); - const mediaSource = streamInfo.mediaSource; const item = streamInfo.item; // Huge hack alert. Safari doesn't seem to like if the segments aren't available right away when playback starts // This will start the transcoding process before actually feeding the video url into the player // Edit: Also seeing stalls from hls.js - if (mediaSource && item && !mediaSource.RunTimeTicks && isHls && streamInfo.playMethod === 'Transcode' && (browser.iOS || browser.osx)) { + if (mediaSource && item && !mediaSource.RunTimeTicks && isHls(mediaSource) && streamInfo.playMethod === 'Transcode' && (browser.iOS || browser.osx)) { const hlsPlaylistUrl = streamInfo.url.replace('master.m3u8', 'live.m3u8'); loading.show(); @@ -513,7 +512,7 @@ export class HtmlVideoPlayer { elem.crossOrigin = crossOrigin; } - if (enableHlsJsPlayer(options.mediaSource.RunTimeTicks, 'Video') && val.includes('.m3u8')) { + if (enableHlsJsPlayer(options.mediaSource.RunTimeTicks, 'Video') && isHls(options.mediaSource)) { return this.setSrcWithHlsJs(elem, options, val); } else if (options.playMethod !== 'Transcode' && options.mediaSource.Container === 'flv') { return this.setSrcWithFlvJs(elem, options, val); @@ -1558,7 +1557,7 @@ export class HtmlVideoPlayer { })[0]; this.setTrackForDisplay(this.#mediaElement, track, targetTextTrackIndex); - if (enableNativeTrackSupport(this.#currentSrc, track)) { + if (enableNativeTrackSupport(this._currentPlayOptions?.mediaSource, track)) { if (streamIndex !== -1) { this.setCueAppearance(); } diff --git a/src/scripts/browserDeviceProfile.js b/src/scripts/browserDeviceProfile.js index ff8bcfcca2b..c00e188d4bc 100644 --- a/src/scripts/browserDeviceProfile.js +++ b/src/scripts/browserDeviceProfile.js @@ -718,6 +718,15 @@ export default function (options) { enableFmp4Hls = false; } if (hlsInFmp4VideoCodecs.length && hlsInFmp4VideoAudioCodecs.length && enableFmp4Hls) { + // HACK: Since there is no filter for TS/MP4 in the API, specify HLS support in general and rely on retry after DirectPlay error + // FIXME: Need support for {Container: 'mp4', Protocol: 'hls'} or {Container: 'hls', SubContainer: 'mp4'} + profile.DirectPlayProfiles.push({ + Container: 'hls', + Type: 'Video', + VideoCodec: hlsInFmp4VideoCodecs.join(','), + AudioCodec: hlsInFmp4VideoAudioCodecs.join(',') + }); + profile.TranscodingProfiles.push({ Container: 'mp4', Type: 'Video', @@ -732,6 +741,15 @@ export default function (options) { } if (hlsInTsVideoCodecs.length && hlsInTsVideoAudioCodecs.length) { + // HACK: Since there is no filter for TS/MP4 in the API, specify HLS support in general and rely on retry after DirectPlay error + // FIXME: Need support for {Container: 'ts', Protocol: 'hls'} or {Container: 'hls', SubContainer: 'ts'} + profile.DirectPlayProfiles.push({ + Container: 'hls', + Type: 'Video', + VideoCodec: hlsInTsVideoCodecs.join(','), + AudioCodec: hlsInTsVideoAudioCodecs.join(',') + }); + profile.TranscodingProfiles.push({ Container: 'ts', Type: 'Video', diff --git a/src/utils/mediaSource.ts b/src/utils/mediaSource.ts new file mode 100644 index 00000000000..8238ef1d4db --- /dev/null +++ b/src/utils/mediaSource.ts @@ -0,0 +1,10 @@ +import type { MediaSourceInfo } from '@jellyfin/sdk/lib/generated-client'; + +/** + * Checks if the media source is an HLS stream. + * @param mediaSource The media source. + * @returns _true_ if the media source is an HLS stream, _false_ otherwise. + */ +export function isHls(mediaSource: MediaSourceInfo|null|undefined): boolean { + return (mediaSource?.TranscodingSubProtocol || mediaSource?.Container) === 'hls'; +}