Skip to content
Closed
Show file tree
Hide file tree
Changes from 8 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
1 change: 1 addition & 0 deletions apps/sandbox/app/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const PRESETS = [
'mux-audio',
'simple-hls-video',
'dash-video',
'vimeo-video',
'audio',
'background-video',
] as const;
4 changes: 2 additions & 2 deletions apps/sandbox/app/shared/html/sandbox-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ export type HtmlSandboxState = {
preload: PreloadValue;
};

export function createHtmlSandboxState(audioOnly?: boolean): HtmlSandboxState {
export function createHtmlSandboxState(audioOnly?: boolean, vimeoOnly?: boolean): HtmlSandboxState {
return {
skin: getInitialSkin(),
source: getInitialSource(audioOnly),
source: getInitialSource(audioOnly, vimeoOnly),
styling: getInitialStyling(),
autoplay: getInitialAutoplay(),
muted: getInitialMuted(),
Expand Down
4 changes: 2 additions & 2 deletions apps/sandbox/app/shared/react/use-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { getInitialSource, onSourceChange } from '@app/shared/sandbox-listener';
import type { SourceId } from '@app/shared/sources';
import { useEffect, useState } from 'react';

export function useSource(audioOnly?: boolean): SourceId {
const [source, setSource] = useState(() => getInitialSource(audioOnly));
export function useSource(audioOnly?: boolean, vimeoOnly?: boolean): SourceId {
const [source, setSource] = useState(() => getInitialSource(audioOnly, vimeoOnly));
useEffect(() => onSourceChange(setSource), []);
return source;
}
8 changes: 6 additions & 2 deletions apps/sandbox/app/shared/sandbox-listener.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SKINS } from '@app/constants';
import type { Skin } from '@app/types';
import { DEFAULT_AUDIO_SOURCE, SOURCES, type SourceId } from './sources';
import { DEFAULT_AUDIO_SOURCE, DEFAULT_VIMEO_SOURCE, SOURCES, type SourceId } from './sources';

export const PRELOAD_VALUES = ['none', 'metadata', 'auto'] as const;
export type PreloadValue = (typeof PRELOAD_VALUES)[number];
Expand Down Expand Up @@ -55,13 +55,17 @@ export function onSkinChange(callback: (skin: Skin) => void): () => void {
};
}

export function getInitialSource(audioOnly?: boolean): SourceId {
export function getInitialSource(audioOnly?: boolean, vimeoOnly?: boolean): SourceId {
const stored = currentSource;

if (audioOnly && SOURCES[stored].type !== 'mp4') {
return DEFAULT_AUDIO_SOURCE;
}

if (vimeoOnly && SOURCES[stored].type !== 'vimeo') {
return DEFAULT_VIMEO_SOURCE;
}

return stored;
}

Expand Down
16 changes: 15 additions & 1 deletion apps/sandbox/app/shared/sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,28 @@ export const SOURCES = {
url: 'https://dash.akamaized.net/envivio/EnvivioDash3/manifest.mpd',
type: 'dash',
},
'vimeo-1': {
label: 'The World In HDR 4K',
url: 'https://vimeo.com/648359100',
type: 'vimeo',
},
'vimeo-2': {
label: 'Caminandes 1: Llama Drama',
url: 'https://vimeo.com/638371504',
type: 'vimeo',
},
} as const;

export type SourceId = keyof typeof SOURCES;

export const SOURCE_IDS = Object.keys(SOURCES) as SourceId[];
export const NON_DASH_SOURCE_IDS = SOURCE_IDS.filter((id) => SOURCES[id].type !== 'dash');
export const NON_DASH_SOURCE_IDS = SOURCE_IDS.filter(
(id) => SOURCES[id].type !== 'dash' && SOURCES[id].type !== 'vimeo'
);
export const MP4_SOURCE_IDS = SOURCE_IDS.filter((id) => SOURCES[id].type === 'mp4');
export const DASH_SOURCE_IDS = SOURCE_IDS.filter((id) => SOURCES[id].type === 'dash');
export const VIMEO_SOURCE_IDS = SOURCE_IDS.filter((id) => SOURCES[id].type === 'vimeo');
Comment thread
cursor[bot] marked this conversation as resolved.
export const DEFAULT_VIMEO_SOURCE: SourceId = 'vimeo-1';
export const DEFAULT_SOURCE: SourceId = 'hls-1';
export const DEFAULT_AUDIO_SOURCE: SourceId = 'mp4-1';
export const DEFAULT_DASH_SOURCE: SourceId = 'dash-1';
Expand Down
23 changes: 21 additions & 2 deletions apps/sandbox/app/shell/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import {
DEFAULT_AUDIO_SOURCE,
DEFAULT_DASH_SOURCE,
DEFAULT_SOURCE,
DEFAULT_VIMEO_SOURCE,
MP4_SOURCE_IDS,
NON_DASH_SOURCE_IDS,
SOURCES,
VIMEO_SOURCE_IDS,
} from '@app/shared/sources';
import type { Platform, Preset, Styling } from '@app/types';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
Expand Down Expand Up @@ -106,11 +108,21 @@ export function App() {
}
}, [preset, source, setSource]);

// Constrain source away from DASH for non-DASH presets
// Constrain source to Vimeo when switching to vimeo-video
useEffect(() => {
if (preset === 'vimeo-video' && SOURCES[source].type !== 'vimeo') {
setSource(DEFAULT_VIMEO_SOURCE);
}
}, [preset, source, setSource]);

// Constrain source away from DASH and Vimeo for non-matching presets
useEffect(() => {
if (preset !== 'dash-video' && SOURCES[source].type === 'dash') {
setSource(DEFAULT_SOURCE);
}
if (preset !== 'vimeo-video' && SOURCES[source].type === 'vimeo') {
setSource(DEFAULT_SOURCE);
}
}, [preset, source, setSource]);

// CDN and background video do not have a Tailwind skin variant.
Expand All @@ -121,7 +133,13 @@ export function App() {
}, [platform, preset, styling]);

const availableSources =
preset === 'audio' ? MP4_SOURCE_IDS : preset === 'dash-video' ? DASH_SOURCE_IDS : NON_DASH_SOURCE_IDS;
preset === 'audio'
? MP4_SOURCE_IDS
: preset === 'dash-video'
? DASH_SOURCE_IDS
: preset === 'vimeo-video'
? VIMEO_SOURCE_IDS
: NON_DASH_SOURCE_IDS;

const handleSourceChange = useCallback((value: string) => setSource(value as SourceId), [setSource]);

Expand Down Expand Up @@ -151,6 +169,7 @@ export function App() {
isSimpleHlsVideo={preset === 'simple-hls-video'}
isMuxVideo={preset === 'mux-video'}
isMuxAudio={preset === 'mux-audio'}
isVimeoVideo={preset === 'vimeo-video'}
platforms={PLATFORMS}
stylings={STYLINGS}
presets={PRESETS}
Expand Down
3 changes: 3 additions & 0 deletions apps/sandbox/app/shell/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type NavbarProps = {
isSimpleHlsVideo: boolean;
isMuxVideo: boolean;
isMuxAudio: boolean;
isVimeoVideo: boolean;
platforms: readonly Platform[];
stylings: readonly Styling[];
presets: readonly Preset[];
Expand All @@ -50,6 +51,7 @@ const PRESET_LABELS: Record<Preset, string> = {
'mux-audio': 'Mux Audio',
'simple-hls-video': 'Simple HLS Video',
'dash-video': 'DASH Video',
'vimeo-video': 'Vimeo Video',
audio: 'Audio',
'background-video': 'Background Video',
};
Expand Down Expand Up @@ -78,6 +80,7 @@ export function Navbar({
isSimpleHlsVideo,
isMuxVideo,
isMuxAudio,
isVimeoVideo,
platforms,
stylings,
presets,
Expand Down
14 changes: 14 additions & 0 deletions apps/sandbox/templates/html-vimeo-video/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sandbox — HTML Vimeo Video</title>
<link rel="preconnect" href="https://rsms.me/" />
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
</head>
<body class="font-sans p-2">
<div id="root" class="flex justify-center items-center min-h-screen"></div>
<script type="module" src="./main.ts"></script>
</body>
</html>
67 changes: 67 additions & 0 deletions apps/sandbox/templates/html-vimeo-video/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import '@app/styles.css';
import '@videojs/html/video/player';
import '@videojs/html/media/vimeo-video';
import { createHtmlSandboxState, createLatestLoader } from '@app/shared/html/sandbox-state';
import { loadVideoSkinTag } from '@app/shared/html/skins';
import {
onAutoplayChange,
onLoopChange,
onMutedChange,
onSkinChange,
onSourceChange,
} from '@app/shared/sandbox-listener';
import { SOURCES } from '@app/shared/sources';

const html = String.raw;

const state = createHtmlSandboxState(false, true);
const loadLatest = createLatestLoader();

async function render() {
const tag = await loadLatest(() => loadVideoSkinTag(state.skin, state.styling));
if (!tag) return;

const autoplay = state.autoplay ? 'autoplay' : '';
const muted = state.muted ? 'muted' : '';
const loop = state.loop ? 'loop' : '';

document.getElementById('root')!.innerHTML = html`
<video-player>
<${tag} class="aspect-video max-w-4xl mx-auto">
<vimeo-video
src="${SOURCES[state.source].url}"
${autoplay}
${muted}
${loop}
></vimeo-video>
</${tag}>
</video-player>
`;
}

render();

onSkinChange((skin) => {
state.skin = skin;
render();
});

onSourceChange((source) => {
state.source = source;
render();
});

onAutoplayChange((autoplay) => {
state.autoplay = autoplay;
render();
});

onMutedChange((muted) => {
state.muted = muted;
render();
});

onLoopChange((loop) => {
state.loop = loop;
render();
});
14 changes: 14 additions & 0 deletions apps/sandbox/templates/react-vimeo-video/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sandbox — React Vimeo Video</title>
<link rel="preconnect" href="https://rsms.me/" />
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
</head>
<body class="font-sans p-2">
<div id="root" class="flex justify-center items-center min-h-screen"></div>
<script type="module" src="./main.tsx"></script>
</body>
</html>
36 changes: 36 additions & 0 deletions apps/sandbox/templates/react-vimeo-video/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import '@app/styles.css';
import { VideoProvider } from '@app/shared/react/providers';
import { VideoSkinComponent } from '@app/shared/react/skins';
import { useAutoplay } from '@app/shared/react/use-autoplay';
import { useLoop } from '@app/shared/react/use-loop';
import { useMuted } from '@app/shared/react/use-muted';
import { useSkin } from '@app/shared/react/use-skin';
import { useSource } from '@app/shared/react/use-source';
import { SOURCES } from '@app/shared/sources';
import type { Styling } from '@app/types';
import { VimeoVideo } from '@videojs/react/media/vimeo-video';
import { useMemo } from 'react';
import { createRoot } from 'react-dom/client';

function readStyling(): Styling {
return new URLSearchParams(location.search).get('styling') === 'tailwind' ? 'tailwind' : 'css';
}

function App() {
const skin = useSkin();
const source = useSource(false, true);
const styling = useMemo(readStyling, []);
const autoplay = useAutoplay();
const muted = useMuted();
const loop = useLoop();

return (
<VideoProvider>
<VideoSkinComponent skin={skin} styling={styling} className="aspect-video max-w-4xl mx-auto">
<VimeoVideo src={SOURCES[source].url} autoplay={autoplay} muted={muted} loop={loop} controls={false} />
</VideoSkinComponent>
</VideoProvider>
);
}

createRoot(document.getElementById('root')!).render(<App />);
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@videojs/spf": "workspace:*",
"@videojs/store": "workspace:*",
"@videojs/utils": "workspace:*",
"@vimeo/player": "^2.26.0",
"dashjs": "^5.0.0",
"hls.js": "^1.6.7",
"mux-embed": "^5.17.10"
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/core/media/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ export interface MediaFullscreenCapability {
exitFullscreen(): Promise<unknown>;
}

export interface MediaPictureInPictureEvents {
enterpictureinpicture: EventLike;
leavepictureinpicture: EventLike;
}

export interface MediaPictureInPictureCapability {
readonly isPictureInPicture: boolean;
requestPictureInPicture(): Promise<unknown>;
Expand Down Expand Up @@ -255,6 +260,7 @@ export interface VideoEvents
MediaPlaybackRateEvents,
MediaBufferEvents,
MediaErrorEvents,
MediaPictureInPictureEvents,
TextTrackListEvents {}

export interface Video
Expand Down
Loading