Skip to content
Draft
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
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