'video/mp4', 'm4v' => 'video/mp4', 'webm' => 'video/webm', 'ogv' => 'video/ogg', 'ogg' => 'video/ogg', 'mov' => 'video/quicktime', 'mkv' => 'video/x-matroska', 'avi' => 'video/x-msvideo', 'wmv' => 'video/x-ms-wmv', 'flv' => 'video/x-flv', 'ts' => 'video/mp2t', '3gp' => 'video/3gpp', '3g2' => 'video/3gpp2', 'm3u8' => 'application/vnd.apple.mpegurl', 'mpd' => 'application/dash+xml', 'mp3' => 'audio/mpeg', 'm4a' => 'audio/mp4', 'aac' => 'audio/aac', 'wav' => 'audio/wav', 'flac' => 'audio/flac', 'oga' => 'audio/ogg', 'opus' => 'audio/ogg', ]; $ext = extension_from_url($url); return $map[$ext] ?? ''; } function classify_kind(string $url, string $mime, string $forced_mode): string { $forced = strtolower($forced_mode); if (in_array($forced, ['video', 'audio', 'hls', 'dash', 'iframe'], true)) { return $forced; } $ext = extension_from_url($url); $mime_lc = strtolower($mime); if ($ext === 'm3u8' || in_array($mime_lc, ['application/x-mpegurl', 'application/vnd.apple.mpegurl'], true)) { return 'hls'; } if ($ext === 'mpd' || $mime_lc === 'application/dash+xml') { return 'dash'; } if (starts_with($mime_lc, 'audio/')) { return 'audio'; } if (starts_with($mime_lc, 'video/')) { return 'video'; } if (in_array($ext, ['mp3', 'm4a', 'aac', 'wav', 'flac', 'oga', 'opus'], true)) { return 'audio'; } if (in_array($ext, ['mp4', 'm4v', 'webm', 'ogv', 'ogg', 'mov', 'mkv', 'avi', 'wmv', 'flv', 'ts', '3gp', '3g2'], true)) { return 'video'; } return 'unknown'; } function normalize_url(string $url): string { $url = trim(html_entity_decode($url, ENT_QUOTES | ENT_HTML5, 'UTF-8')); if ($url === '') { return ''; } if (starts_with($url, '//')) { $url = 'https:' . $url; } $scheme = parse_url($url, PHP_URL_SCHEME); if (!is_string($scheme) || $scheme === '') { return $url; } $scheme = strtolower($scheme); if (!in_array($scheme, ['http', 'https'], true)) { return ''; } return $url; } function append_query(string $base, array $params): string { $query = http_build_query($params, '', '&', PHP_QUERY_RFC3986); if ($query === '') { return $base; } return $base . (strpos($base, '?') === false ? '?' : '&') . $query; } function parse_youtube_time(string $value): int { $value = trim($value); if ($value === '') { return 0; } if (ctype_digit($value)) { return max(0, (int) $value); } $seconds = 0; if (preg_match_all('/(\d+)\s*([hms])/i', $value, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $amount = (int) $match[1]; $unit = strtolower($match[2]); if ($unit === 'h') { $seconds += $amount * 3600; } elseif ($unit === 'm') { $seconds += $amount * 60; } else { $seconds += $amount; } } } return max(0, $seconds); } function clean_token(string $value, string $pattern = '/[^A-Za-z0-9_-]/'): string { $clean = preg_replace($pattern, '', trim($value)); return is_string($clean) ? $clean : ''; } function twitch_parent_host(): string { $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost'; $host = strtolower(trim((string) $host)); $host = preg_replace('/:\d+$/', '', $host); $host = is_string($host) ? $host : 'localhost'; return $host !== '' ? $host : 'localhost'; } function provider_embed(string $url): ?array { $parts = @parse_url($url); if (!is_array($parts) || empty($parts['host'])) { return null; } $host = strtolower((string) $parts['host']); $path = isset($parts['path']) ? (string) $parts['path'] : ''; $segments = path_segments($url); $query = get_query_values($url); if (host_matches($host, 'youtu.be') || host_matches($host, 'youtube.com') || host_matches($host, 'youtube-nocookie.com')) { $id = ''; if (host_matches($host, 'youtu.be') && isset($segments[0])) { $id = $segments[0]; } if ($id === '' && isset($query['v'])) { $id = scalar_string($query['v']); } if ($id === '' && preg_match('#/(?:embed|shorts|live|v)/([^/?#]+)#', $path, $m)) { $id = $m[1]; } $id = clean_token($id); if ($id !== '') { $params = ['rel' => '0', 'modestbranding' => '1', 'playsinline' => '1']; $start = 0; if (isset($query['start'])) { $start = parse_youtube_time(scalar_string($query['start'])); } elseif (isset($query['t'])) { $start = parse_youtube_time(scalar_string($query['t'])); } if ($start > 0) { $params['start'] = (string) $start; } return [ 'provider' => 'youtube', 'embed' => append_query('https://www.youtube-nocookie.com/embed/' . rawurlencode($id), $params), ]; } if (isset($query['list'])) { $list = clean_token(scalar_string($query['list'])); if ($list !== '') { return [ 'provider' => 'youtube', 'embed' => append_query('https://www.youtube-nocookie.com/embed/videoseries', [ 'list' => $list, 'rel' => '0', 'modestbranding' => '1', 'playsinline' => '1', ]), ]; } } } if (host_matches($host, 'vimeo.com')) { $id = ''; if (preg_match('#/(?:video/)?(\d+)#', $path, $m)) { $id = $m[1]; } if ($id !== '') { return [ 'provider' => 'vimeo', 'embed' => append_query('https://player.vimeo.com/video/' . rawurlencode($id), [ 'title' => '0', 'byline' => '0', 'portrait' => '0', ]), ]; } } if (host_matches($host, 'dailymotion.com') || host_matches($host, 'dai.ly')) { $id = ''; if (host_matches($host, 'dai.ly') && isset($segments[0])) { $id = $segments[0]; } if ($id === '' && preg_match('#/(?:embed/)?video/([^/?#_]+)#', $path, $m)) { $id = $m[1]; } $id = clean_token($id); if ($id !== '') { return [ 'provider' => 'dailymotion', 'embed' => 'https://www.dailymotion.com/embed/video/' . rawurlencode($id), ]; } } if (host_matches($host, 'twitch.tv')) { $parent = twitch_parent_host(); if (preg_match('#/videos/(\d+)#', $path, $m)) { return [ 'provider' => 'twitch', 'embed' => append_query('https://player.twitch.tv/', [ 'video' => $m[1], 'parent' => $parent, 'autoplay' => 'false', ]), ]; } if (preg_match('#/([^/]+)/clip/([^/?#]+)#', $path, $m)) { return [ 'provider' => 'twitch', 'embed' => append_query('https://clips.twitch.tv/embed', [ 'clip' => clean_token($m[2]), 'parent' => $parent, 'autoplay' => 'false', ]), ]; } if (isset($segments[0]) && $segments[0] !== '') { return [ 'provider' => 'twitch', 'embed' => append_query('https://player.twitch.tv/', [ 'channel' => clean_token($segments[0]), 'parent' => $parent, 'autoplay' => 'false', ]), ]; } } if (host_matches($host, 'clips.twitch.tv') && isset($segments[0])) { return [ 'provider' => 'twitch', 'embed' => append_query('https://clips.twitch.tv/embed', [ 'clip' => clean_token($segments[0]), 'parent' => twitch_parent_host(), 'autoplay' => 'false', ]), ]; } if (host_matches($host, 'facebook.com') || host_matches($host, 'fb.watch')) { return [ 'provider' => 'facebook', 'embed' => append_query('https://www.facebook.com/plugins/video.php', [ 'href' => $url, 'show_text' => 'false', 'width' => '1280', ]), ]; } if (host_matches($host, 'tiktok.com') && preg_match('#/video/(\d+)#', $path, $m)) { return [ 'provider' => 'tiktok', 'embed' => 'https://www.tiktok.com/embed/v2/' . rawurlencode($m[1]), ]; } if (host_matches($host, 'instagram.com') && preg_match('#/(p|reel|tv)/([^/?#]+)#', $path, $m)) { return [ 'provider' => 'instagram', 'embed' => 'https://www.instagram.com/' . rawurlencode($m[1]) . '/' . rawurlencode($m[2]) . '/embed', ]; } if ((host_matches($host, 'twitter.com') || host_matches($host, 'x.com')) && preg_match('#/status/(\d+)#', $path, $m)) { return [ 'provider' => 'twitter', 'embed' => append_query('https://platform.twitter.com/embed/Tweet.html', ['id' => $m[1]]), ]; } if (host_matches($host, 'soundcloud.com')) { return [ 'provider' => 'soundcloud', 'embed' => append_query('https://w.soundcloud.com/player/', [ 'url' => $url, 'auto_play' => 'false', 'show_teaser' => 'true', ]), ]; } if (host_matches($host, 'open.spotify.com') && isset($segments[0], $segments[1])) { $type = clean_token($segments[0]); $id = clean_token($segments[1]); if (in_array($type, ['album', 'artist', 'episode', 'playlist', 'show', 'track'], true) && $id !== '') { return [ 'provider' => 'spotify', 'embed' => 'https://open.spotify.com/embed/' . rawurlencode($type) . '/' . rawurlencode($id), ]; } } if (host_matches($host, 'drive.google.com')) { $id = ''; if (preg_match('#/file/d/([^/]+)#', $path, $m)) { $id = $m[1]; } elseif (isset($query['id'])) { $id = scalar_string($query['id']); } $id = clean_token($id); if ($id !== '') { return [ 'provider' => 'google-drive', 'embed' => 'https://drive.google.com/file/d/' . rawurlencode($id) . '/preview', ]; } } if ((host_matches($host, 'wistia.com') || host_matches($host, 'wi.st')) && preg_match('#/(?:medias/)?([A-Za-z0-9]+)#', $path, $m)) { return [ 'provider' => 'wistia', 'embed' => 'https://fast.wistia.net/embed/iframe/' . rawurlencode($m[1]), ]; } if (host_matches($host, 'streamable.com') && isset($segments[0])) { return [ 'provider' => 'streamable', 'embed' => 'https://streamable.com/e/' . rawurlencode(clean_token($segments[0])), ]; } if (host_matches($host, 'loom.com') && isset($segments[0], $segments[1]) && $segments[0] === 'share') { return [ 'provider' => 'loom', 'embed' => 'https://www.loom.com/embed/' . rawurlencode(clean_token($segments[1])), ]; } return null; } function rewrite_direct_url(string $url): string { $host = strtolower((string) (parse_url($url, PHP_URL_HOST) ?? '')); $path = (string) (parse_url($url, PHP_URL_PATH) ?? ''); if (host_matches($host, 'dropbox.com')) { $parts = @parse_url($url); if (is_array($parts)) { $query = []; if (isset($parts['query'])) { parse_str((string) $parts['query'], $query); } unset($query['dl'], $query['raw']); $query['raw'] = '1'; $rebuilt = ($parts['scheme'] ?? 'https') . '://' . ($parts['host'] ?? 'www.dropbox.com'); $rebuilt .= $parts['path'] ?? ''; $rebuilt = append_query($rebuilt, $query); if (isset($parts['fragment'])) { $rebuilt .= '#' . $parts['fragment']; } return $rebuilt; } } if (host_matches($host, 'github.com') && preg_match('#^/([^/]+)/([^/]+)/blob/([^/]+)/(.+)$#', $path, $m)) { return 'https://raw.githubusercontent.com/' . rawurlencode($m[1]) . '/' . rawurlencode($m[2]) . '/' . rawurlencode($m[3]) . '/' . str_replace('%2F', '/', rawurlencode($m[4])); } return $url; } function source_from_raw(string $raw, string $forced_mode): ?array { $raw = trim($raw); if ($raw === '') { return null; } $mime = ''; if (strpos($raw, '|') !== false) { [$raw, $mime] = explode('|', $raw, 2); $mime = trim($mime); } $original_url = normalize_url($raw); if ($original_url === '') { return null; } $provider = provider_embed($original_url); $url = $provider ? $original_url : rewrite_direct_url($original_url); if ($mime === '') { $mime = guess_mime($url); } $kind = classify_kind($url, $mime, $forced_mode); if ($provider && strtolower($forced_mode) === 'auto') { $kind = 'iframe'; } if (strtolower($forced_mode) === 'iframe') { $kind = 'iframe'; } $ext = extension_from_url($url); $label = $provider['provider'] ?? ($ext !== '' ? $ext : ($kind === 'unknown' ? 'url' : $kind)); return [ 'url' => $url, 'originalUrl' => $original_url, 'embed' => $provider['embed'] ?? $url, 'provider' => $provider['provider'] ?? '', 'mime' => $mime, 'kind' => $kind, 'label' => $label, ]; } function query_bool(string $name, bool $default = false): bool { if (!array_key_exists($name, $_GET)) { return $default; } $value = strtolower(trim((string) $_GET[$name])); if (in_array($value, ['1', 'true', 'yes', 'on'], true)) { return true; } if (in_array($value, ['0', 'false', 'no', 'off'], true)) { return false; } return $default; } function media_type_attr(array $source): string { $mime = isset($source['mime']) ? trim((string) $source['mime']) : ''; return $mime !== '' ? ' type="' . h($mime) . '"' : ''; } function media_common_attrs(array $config): string { $attrs = []; if (($config['controls'] ?? true) !== false) { $attrs[] = 'controls'; } if (($config['autoplay'] ?? false) === true) { $attrs[] = 'autoplay'; } if (($config['muted'] ?? false) === true || ($config['autoplay'] ?? false) === true) { $attrs[] = 'muted'; } if (($config['loop'] ?? false) === true) { $attrs[] = 'loop'; } $attrs[] = 'preload="metadata"'; $crossorigin = trim((string) ($config['crossorigin'] ?? '')); if ($crossorigin !== '') { $attrs[] = 'crossorigin="' . h($crossorigin) . '"'; } return implode(' ', $attrs); } $title = trim((string) ($_GET['title'] ?? '')); $poster = normalize_url((string) ($_GET['poster'] ?? '')); $mode = strtolower(trim((string) ($_GET['mode'] ?? 'auto'))); if (!in_array($mode, ['auto', 'video', 'audio', 'hls', 'dash', 'iframe'], true)) { $mode = 'auto'; } $primary_raw = ''; foreach (['video', 'url', 'src', 'v', 'slug'] as $key) { if (isset($_GET[$key]) && trim((string) $_GET[$key]) !== '') { $primary_raw = trim((string) $_GET[$key]); break; } } $sources = []; if ($primary_raw !== '') { $source = source_from_raw($primary_raw, $mode); if ($source !== null) { $sources[] = $source; } } for ($i = 2; $i <= 10; $i++) { foreach (["video{$i}", "url{$i}", "src{$i}"] as $key) { if (isset($_GET[$key]) && trim((string) $_GET[$key]) !== '') { $source = source_from_raw(trim((string) $_GET[$key]), $mode); if ($source !== null) { $sources[] = $source; } break; } } } $crossorigin = strtolower(trim((string) ($_GET['crossorigin'] ?? ''))); if (!in_array($crossorigin, ['anonymous', 'use-credentials'], true)) { $crossorigin = ''; } $config = [ 'sources' => $sources, 'title' => $title, 'poster' => $poster, 'autoplay' => query_bool('autoplay', false), 'muted' => query_bool('muted', false), 'loop' => query_bool('loop', false), 'controls' => query_bool('controls', true), 'playsinline' => true, 'iframeFallback' => query_bool('iframe_fallback', true), 'crossorigin' => $crossorigin, 'withCredentials' => query_bool('with_credentials', false), ]; $primary_source = $sources[0] ?? null; $json_config = json_encode( $config, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ); if (!is_string($json_config)) { $json_config = '{"sources":[]}'; } header_remove('X-Frame-Options'); header_remove('Content-Security-Policy'); header_remove('X-Content-Security-Policy'); header('Content-Type: text/html; charset=utf-8'); header("Content-Security-Policy: default-src * data: blob: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; style-src * 'unsafe-inline'; img-src * data: blob:; media-src * data: blob:; frame-src *; connect-src *"); header('Referrer-Policy: no-referrer-when-downgrade'); header('Cross-Origin-Resource-Policy: cross-origin'); header('X-Content-Type-Options: nosniff'); ?> <?= $title !== '' ? h($title) . ' - Player' : 'Player' ?>
0): ?>
style="background-image:url('')">

No media URL supplied

Use ?video=https%3A%2F%2Fexample.com%2Fclip.mp4 or ?url=....

0): ?>