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';
+}