Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 15 additions & 36 deletions src/patterns.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,20 @@
import { resolveAdapterType, resolveMimeType, type AdapterType } from '@videojs/react';

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)(#t=[,\d+]+)?($|\?)/i;
export const HLS_EXTENSIONS = /\.(m3u8)($|\?)/i;
export const DASH_EXTENSIONS = /\.(mpd)($|\?)/i;
// Match Mux m3u8 URLs without the extension so users can use hls.js with Mux by adding the `.m3u8` extension. https://regexr.com/7um5f
export const MATCH_URL_MUX = /stream\.mux\.com\/(?!\w+\.m3u8)(\w+)/;
export const MATCH_URL_YOUTUBE =
/(?:youtu\.be\/|youtube(?:-nocookie|education)?\.com\/(?:embed\/|v\/|watch\/|watch\?v=|watch\?.+&v=|shorts\/|live\/))((\w|-){11})|youtube\.com\/playlist\?list=|youtube\.com\/user\//;
export const MATCH_URL_VIMEO = /vimeo\.com\/(?!progressive_redirect).+/;
export const MATCH_URL_WISTIA = /(?:wistia\.(?:com|net)|wi\.st)\/(?:medias|embed)\/(?:iframe\/)?([^?]+)/;
export const MATCH_URL_SPOTIFY = /open\.spotify\.com\/(\w+)\/(\w+)/i;
export const MATCH_URL_TWITCH = /(?:www\.|go\.)?twitch\.tv\/([a-zA-Z0-9_]+|(videos?\/|\?video=)\d+)($|\?)/;
export const MATCH_URL_TIKTOK = /tiktok\.com\/(?:player\/v1\/|share\/video\/|@[^/]+\/video\/)([0-9]+)/;

const canPlayFile = (url: string, test: (u: string) => boolean) => {
if (Array.isArray(url)) {
for (const item of url) {
if (typeof item === 'string' && canPlayFile(item, test)) {
return true;
}
if (canPlayFile(item.src, test)) {
return true;
}
}
return false;
}
return test(url);
};
export type PlayerKey = Exclude<AdapterType, 'video' | 'audio'> | 'html';

export const canPlay = {
html: (url: string) => canPlayFile(url, (u: string) => AUDIO_EXTENSIONS.test(u) || VIDEO_EXTENSIONS.test(u)),
hls: (url: string) => canPlayFile(url, (u: string) => HLS_EXTENSIONS.test(u)),
dash: (url: string) => canPlayFile(url, (u: string) => DASH_EXTENSIONS.test(u)),
mux: (url: string) => MATCH_URL_MUX.test(url),
youtube: (url: string) => MATCH_URL_YOUTUBE.test(url),
vimeo: (url: string) => MATCH_URL_VIMEO.test(url) && !VIDEO_EXTENSIONS.test(url) && !HLS_EXTENSIONS.test(url),
wistia: (url: string) => MATCH_URL_WISTIA.test(url),
spotify: (url: string) => MATCH_URL_SPOTIFY.test(url),
twitch: (url: string) => MATCH_URL_TWITCH.test(url),
tiktok: (url: string) => MATCH_URL_TIKTOK.test(url),
/** The key of the built-in player that plays `url`, from its Video.js adapter type. */
const resolvePlayerKey = (url: string): PlayerKey | null => {
const type = resolveAdapterType(url);
// Mux stream URLs with the `.m3u8` extension resolve to `mux`, but are played with hls.js so
// users can opt into it by adding the extension.
if (type === 'mux' && resolveMimeType(url) === 'application/x-mpegurl') return 'hls';
if (type === 'video' || type === 'audio') return 'html';
// The extension patterns cover formats Video.js doesn't recognize (e.g. m4v, weba, oga).
if (!type && (AUDIO_EXTENSIONS.test(url) || VIDEO_EXTENSIONS.test(url))) return 'html';
return type;
};

export const canPlay = (key: PlayerKey) => (url: string) => resolvePlayerKey(url) === key;
20 changes: 10 additions & 10 deletions src/players.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,23 @@ const lazyMedia = <P extends object>(
const Players: PlayerEntry[] = [
{
key: 'hls',
canPlay: canPlay.hls,
canPlay: canPlay('hls'),
canEnablePIP: () => true,
player: lazyMedia(() =>
import(/* webpackChunkName: 'reactPlayerHls' */ '@videojs/react/media/hlsjs-video').then((m) => m.HlsJsVideo)
),
},
{
key: 'dash',
canPlay: canPlay.dash,
canPlay: canPlay('dash'),
canEnablePIP: () => true,
player: lazyMedia(() =>
import(/* webpackChunkName: 'reactPlayerDash' */ '@videojs/react/media/dash-video').then((m) => m.DashVideo)
),
},
{
key: 'mux',
canPlay: canPlay.mux,
canPlay: canPlay('mux'),
canEnablePIP: () => true,
player: lazyMedia(
() => import(/* webpackChunkName: 'reactPlayerMux' */ '@videojs/react/media/mux-video').then((m) => m.MuxVideo),
Expand All @@ -49,7 +49,7 @@ const Players: PlayerEntry[] = [
},
{
key: 'youtube',
canPlay: canPlay.youtube,
canPlay: canPlay('youtube'),
player: lazyMedia(() =>
import(/* webpackChunkName: 'reactPlayerYouTube' */ '@videojs/react/media/youtube-video').then(
(m) => m.YouTubeVideo
Expand All @@ -58,14 +58,14 @@ const Players: PlayerEntry[] = [
},
{
key: 'vimeo',
canPlay: canPlay.vimeo,
canPlay: canPlay('vimeo'),
player: lazyMedia(() =>
import(/* webpackChunkName: 'reactPlayerVimeo' */ '@videojs/react/media/vimeo-video').then((m) => m.VimeoVideo)
),
},
{
key: 'wistia',
canPlay: canPlay.wistia,
canPlay: canPlay('wistia'),
canEnablePIP: () => true,
player: lazyMedia(
() =>
Expand All @@ -77,7 +77,7 @@ const Players: PlayerEntry[] = [
},
{
key: 'spotify',
canPlay: canPlay.spotify,
canPlay: canPlay('spotify'),
canEnablePIP: () => false,
player: lazyMedia(() =>
import(/* webpackChunkName: 'reactPlayerSpotify' */ '@videojs/react/media/spotify-audio').then(
Expand All @@ -87,23 +87,23 @@ const Players: PlayerEntry[] = [
},
{
key: 'twitch',
canPlay: canPlay.twitch,
canPlay: canPlay('twitch'),
canEnablePIP: () => false,
player: lazyMedia(() =>
import(/* webpackChunkName: 'reactPlayerTwitch' */ '@videojs/react/media/twitch-video').then((m) => m.TwitchVideo)
),
},
{
key: 'tiktok',
canPlay: canPlay.tiktok,
canPlay: canPlay('tiktok'),
canEnablePIP: () => false,
player: lazyMedia(() =>
import(/* webpackChunkName: 'reactPlayerTiktok' */ '@videojs/react/media/tiktok-video').then((m) => m.TikTokVideo)
),
},
{
key: 'html',
canPlay: canPlay.html,
canPlay: canPlay('html'),
canEnablePIP: () => true,
player: HtmlPlayer,
},
Expand Down
Loading