Summary
DASH_EXTENSIONS and HLS_EXTENSIONS only allow .mpd/.m3u8 to be followed by end-of-string or ?, so a manifest URL carrying a media fragment (#t=) fails detection. VIDEO_EXTENSIONS explicitly allows #t=, so behaviour is inconsistent between progressive files and manifests:
// src/patterns.ts
export const VIDEO_EXTENSIONS = /\.(mp4|og[gv]|webm|mov|m4v)(#t=[,\d+]+)?($|\?)/i; // handles #t=
export const HLS_EXTENSIONS = /\.(m3u8)($|\?)/i; // does not
export const DASH_EXTENSIONS = /\.(mpd)($|\?)/i; // does not
This matters because #t= on a manifest is a legitimate, supported way to start a stream at an offset:
- dash.js parses the URL fragment via
URIFragmentModel and uses t to compute the start time, so attachSource('movie.mpd#t=258') starts at 4:18.
- Native HLS in Safari:
hls-video-element assigns the URL straight to nativeEl.src, where the browser honours the media fragment.
Impact
Because index.ts passes the html entry as playerFallback, a non-matching URL does not render nothing — it silently falls through to HtmlPlayer and produces a plain <video> pointed at the manifest. In any browser without native DASH support that is an unplayable source, so the symptom looks like a broken stream or a broken CDN rather than a URL-detection problem.
Reproduction
<ReactPlayer src="https://example.com/movie.mpd" /> // -> <dash-video src="..."> OK
<ReactPlayer src="https://example.com/movie.mpd#t=258" /> // -> <video src="..."> dash.js never loads
Rendered output, confirmed in jsdom on 3.3.3 and 3.4.0:
PLAIN .mpd -> <dash-video src="https://example.com/movie.mpd">
WITH .mpd#t=258 -> <video src="https://example.com/movie.mpd#t=258">
Related: #t= with a decimal never matches either
(#t=[,\d+]+)? has no . in its character class, so fractional times fail even for progressive files. (The + inside the class is a literal, which also looks unintended.)
src |
matched player |
a.mpd |
dash |
a.mpd#t=258 |
none |
a.m3u8 |
hls |
a.m3u8#t=258 |
none |
a.mp4#t=258 |
html |
a.mp4#t=10,20 |
html |
a.mp4#t=10.5 |
none |
a.mp3#t=5 |
none |
Fractional start positions are common when resuming playback from a stored position, and AUDIO_EXTENSIONS has no fragment handling at all.
Suggested fix
Treat # like ? — a fragment simply terminates the extension — instead of special-casing one fragment shape:
export const AUDIO_EXTENSIONS = /\.(m4a|m4b|mp4a|mpga|mp2|mp2a|mp3|m2a|m3a|wav|weba|aac|oga|spx)($|[?#])/i;
export const VIDEO_EXTENSIONS = /\.(mp4|og[gv]|webm|mov|m4v)($|[?#])/i;
export const HLS_EXTENSIONS = /\.(m3u8)($|[?#])/i;
export const DASH_EXTENSIONS = /\.(mpd)($|[?#])/i;
This collapses the special-cased #t= group into a general terminator and fixes decimals, commas, posix: times and any other fragment form in one go.
Workaround
Re-registering the built-in entries with a fragment-tolerant canPlay, since custom players are consulted first:
import ReactPlayer from 'react-player'
import Players from 'react-player/players'
for (const [key, pattern] of Object.entries({ hls: /\.m3u8($|[?#])/i, dash: /\.mpd($|[?#])/i })) {
const player = Players.find((entry) => entry.key === key)
if (player) ReactPlayer.addCustomPlayer({ ...player, canPlay: (src) => pattern.test(src) })
}
Environment
- react-player
3.3.3 and 3.4.0 — dist/patterns.js is byte-identical between them, and src/patterns.ts on master is unchanged too
- dash.js 5.2.0, dash-video-element 0.2.0, hls-video-element 1.5.11
Summary
DASH_EXTENSIONSandHLS_EXTENSIONSonly allow.mpd/.m3u8to be followed by end-of-string or?, so a manifest URL carrying a media fragment (#t=) fails detection.VIDEO_EXTENSIONSexplicitly allows#t=, so behaviour is inconsistent between progressive files and manifests:This matters because
#t=on a manifest is a legitimate, supported way to start a stream at an offset:URIFragmentModeland usestto compute the start time, soattachSource('movie.mpd#t=258')starts at 4:18.hls-video-elementassigns the URL straight tonativeEl.src, where the browser honours the media fragment.Impact
Because
index.tspasses thehtmlentry asplayerFallback, a non-matching URL does not render nothing — it silently falls through toHtmlPlayerand produces a plain<video>pointed at the manifest. In any browser without native DASH support that is an unplayable source, so the symptom looks like a broken stream or a broken CDN rather than a URL-detection problem.Reproduction
Rendered output, confirmed in jsdom on 3.3.3 and 3.4.0:
Related:
#t=with a decimal never matches either(#t=[,\d+]+)?has no.in its character class, so fractional times fail even for progressive files. (The+inside the class is a literal, which also looks unintended.)srca.mpddasha.mpd#t=258a.m3u8hlsa.m3u8#t=258a.mp4#t=258htmla.mp4#t=10,20htmla.mp4#t=10.5a.mp3#t=5Fractional start positions are common when resuming playback from a stored position, and
AUDIO_EXTENSIONShas no fragment handling at all.Suggested fix
Treat
#like?— a fragment simply terminates the extension — instead of special-casing one fragment shape:This collapses the special-cased
#t=group into a general terminator and fixes decimals, commas,posix:times and any other fragment form in one go.Workaround
Re-registering the built-in entries with a fragment-tolerant
canPlay, since custom players are consulted first:Environment
3.3.3and3.4.0—dist/patterns.jsis byte-identical between them, andsrc/patterns.tsonmasteris unchanged too