Skip to content

Commit 5e85462

Browse files
authored
fix: allow extension less Mux m3u8 url as src (#1096)
this allows https://stream.mux.com/Sc89iWAyNkhJ3P1rQ02nrEdCFTnfT01CZ2KmaEcxXfB008 https://stream.mux.com/Sc89iWAyNkhJ3P1rQ02nrEdCFTnfT01CZ2KmaEcxXfB008?foo=bar instead of https://stream.mux.com/Sc89iWAyNkhJ3P1rQ02nrEdCFTnfT01CZ2KmaEcxXfB008.m3u8 this is needed for react-player to have a difference in the source to choose which provider. the one where they add the .m3u8 goes to hls-video, without the extension goes to mux-player.
1 parent f602519 commit 5e85462

File tree

2 files changed

+47
-10
lines changed

2 files changed

+47
-10
lines changed

packages/playback-core/src/util.ts

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,13 @@ export const toPlaybackIdParts = (playbackIdWithOptionalParams: string): [string
5959
return [idPart, queryPart];
6060
};
6161

62-
export const getType = (props: Partial<Pick<MuxMediaProps, 'type' | 'src'>>) => {
63-
const type = props.type;
62+
export const getType = (props: Partial<Pick<MuxMediaProps, 'type' | 'src' | 'customDomain'>>) => {
63+
const { type } = props;
6464
if (type) {
6565
const upperType = type.toUpperCase();
6666
return isKeyOf(upperType, MimeTypeShorthandMap) ? MimeTypeShorthandMap[upperType] : type;
6767
}
68-
69-
const { src } = props;
70-
if (!src) return '';
71-
72-
return inferMimeTypeFromURL(src);
68+
return inferMimeTypeFromURL(props);
7369
};
7470

7571
export const toStreamTypeFromPlaylistType = (playlistType: HlsPlaylistTypes) => {
@@ -82,23 +78,50 @@ export const toTargetLiveWindowFromPlaylistType = (playlistType: HlsPlaylistType
8278
return 0;
8379
};
8480

85-
export const inferMimeTypeFromURL = (url: string) => {
81+
export const inferMimeTypeFromURL = (props: Partial<Pick<MuxMediaProps, 'src' | 'customDomain'>>) => {
82+
const { src } = props;
83+
if (!src) return '';
84+
8685
let pathname = '';
8786
try {
88-
pathname = new URL(url).pathname;
87+
pathname = new URL(src).pathname;
8988
} catch (_e) {
9089
console.error('invalid url');
9190
}
9291

9392
const extDelimIdx = pathname.lastIndexOf('.');
94-
if (extDelimIdx < 0) return '';
93+
if (extDelimIdx < 0) {
94+
if (isExtensionLessMuxM3U8URL(props)) {
95+
return ExtensionMimeTypeMap.M3U8; // Treat extension-less Mux URLs as HLS
96+
}
97+
return '';
98+
}
9599

96100
const ext = pathname.slice(extDelimIdx + 1);
97101
const upperExt = ext.toUpperCase();
98102

99103
return isKeyOf(upperExt, ExtensionMimeTypeMap) ? ExtensionMimeTypeMap[upperExt] : '';
100104
};
101105

106+
const MUX_VIDEO_DOMAIN = 'mux.com';
107+
export const isExtensionLessMuxM3U8URL = ({
108+
src,
109+
customDomain = MUX_VIDEO_DOMAIN,
110+
}: Partial<Pick<MuxMediaProps, 'src' | 'customDomain'>>) => {
111+
let urlObj;
112+
try {
113+
urlObj = new URL(`${src}`);
114+
} catch {
115+
return false;
116+
}
117+
const validProtocol = urlObj.protocol === 'https:';
118+
const validHostname = urlObj.hostname === `stream.${customDomain}`.toLowerCase();
119+
const pathParts = urlObj.pathname.split('/');
120+
const validPathPartsLength = pathParts.length === 2;
121+
const validExtensionlessPath = !pathParts?.[1].includes('.');
122+
return validProtocol && validHostname && validPathPartsLength && validExtensionlessPath;
123+
};
124+
102125
export type MuxJWT = {
103126
sub: string;
104127
aud: 'v' | 't' | 'g' | 's' | 'd';

packages/playback-core/test/util.test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,20 @@ describe('Module: util', () => {
5252
const actual = getType({ src: `http://foo.com/bar.${extension}`, type: MimeTypeShorthandMap.HLS });
5353
assert.equal(actual, expected);
5454
});
55+
56+
it('should support Mux extensionless m3u8 URLs', () => {
57+
const expected = ExtensionMimeTypeMap.M3U8;
58+
// Mux extensionless m3u8 URLs should be treated as M3U8
59+
const actual = getType({ src: 'https://stream.mux.com/abc123' });
60+
assert.equal(actual, expected);
61+
});
62+
63+
it('should support Mux extensionless m3u8 URLs with custom domain', () => {
64+
const expected = ExtensionMimeTypeMap.M3U8;
65+
// Mux extensionless m3u8 URLs with custom domain should be treated as M3U8
66+
const actual = getType({ src: 'https://stream.abc.com/abc123', customDomain: 'abc.com' });
67+
assert.equal(actual, expected);
68+
});
5569
});
5670

5771
describe('parseJwt', () => {

0 commit comments

Comments
 (0)