Skip to content

Commit

Permalink
feat(player): support new thumbnail src types
Browse files Browse the repository at this point in the history
  • Loading branch information
mihar-22 committed Dec 26, 2023
1 parent 72b8056 commit 698e575
Show file tree
Hide file tree
Showing 14 changed files with 425 additions and 237 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ All notable changes to this project will be documented in this file.
- new `crossOrigin` prop on poster component ([ecbf277](https://github.com/vidstack/player/commit/ecbf2778e813b357ea60f37c5d81f705979f1083))
- new `crossOrigin` prop on slider video component ([d699c7b](https://github.com/vidstack/player/commit/d699c7b9eb6a583a052f60e562853f7b727f7c5c))
- new `crossOrigin` prop on thumbnail components ([fa9ee2d](https://github.com/vidstack/player/commit/fa9ee2d26465b2c22e081f605849ccfca2a9f102))
- support new thumbnail src types ([842f7c4](https://github.com/vidstack/player/commit/842f7c422b44842bda48a5e9c5a39cb5572861e8))

#### Player (React)

Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/components/layouts/default/context.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as React from 'react';

import type { DefaultLayoutTranslations } from 'vidstack';
import type { DefaultLayoutTranslations, ThumbnailSrc } from 'vidstack';

import type { DefaultLayoutIcons } from './icons';

export const DefaultLayoutContext = React.createContext<DefaultLayoutContext>({} as any);

interface DefaultLayoutContext {
thumbnails?: string;
thumbnails: ThumbnailSrc | null;
menuContainer?: React.RefObject<HTMLElement | null>;
translations?: DefaultLayoutTranslations | null;
isSmallLayout: boolean;
Expand Down
10 changes: 6 additions & 4 deletions packages/react/src/components/layouts/default/shared-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
isTrackCaptionKind,
mediaContext,
type DefaultLayoutTranslations,
type ThumbnailSrc,
type TooltipPlacement,
} from 'vidstack';

Expand Down Expand Up @@ -64,10 +65,11 @@ export interface DefaultMediaLayoutProps<Slots = unknown> extends PrimitiveProps
*/
icons: DefaultLayoutIcons;
/**
* The absolute or relative URL to a [WebVTT](https://developer.mozilla.org/en-US/docs/Web/API/WebVTT_API)
* file resource.
* The thumbnails resource.
*
* @see {@link https://www.vidstack.io/docs/player/core-concepts/loading#thumbnails}
*/
thumbnails?: string;
thumbnails?: ThumbnailSrc | null;
/**
* Translation map from english to your desired language for words used throughout the layout.
*/
Expand Down Expand Up @@ -156,7 +158,7 @@ export function createDefaultMediaLayout({
{
className,
icons,
thumbnails,
thumbnails = null,
translations,
showMenuDelay,
showTooltipDelay = type === 'video' ? 500 : 700,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export interface DefaultVideoLayoutProps extends DefaultMediaLayoutProps<Default
* ```tsx
* <MediaPlayer src="video.mp4">
* <MediaProvider />
* <DefaultVideoLayout thumbnails="thumbnails.vtt" icons={defaultLayoutIcons} />
* <DefaultVideoLayout thumbnails="/thumbnails.vtt" icons={defaultLayoutIcons} />
* </MediaPlayer>
* ```
*/
Expand Down
97 changes: 29 additions & 68 deletions packages/react/src/hooks/use-thumbnails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,31 @@ import * as React from 'react';

import { scoped, signal } from 'maverick.js';
import { useReactScope, useSignal } from 'maverick.js/react';
import type { VTTCue } from 'media-captions';
import { findActiveCue, ThumbnailsLoader, type MediaCrossOrigin } from 'vidstack';

export interface ThumbnailData {
url: string;
cue: VTTCue;
x: number;
y: number;
width: number;
height: number;
}
import {
ThumbnailsLoader,
type MediaCrossOrigin,
type ThumbnailImage,
type ThumbnailSrc,
} from 'vidstack';

/**
* Fetches and parses a WebVTT file. The function will return the parsed thumbnail
* data such as the VTTCue, coordinates, dimensions, and url. It's safe to call this hook in
* multiple places with the same `src` argument as work is de-duped and cached.
* The function will return the resolved thumbnail images given a thumbnail resource. It's safe to
* call this hook in multiple places with the same `src` argument as work is de-duped and cached
* internally.
*
* @docs {@link https://www.vidstack.io/docs/player/api/hooks/use-thumbnails}
*/
export function useThumbnails(src: string, crossOrigin?: MediaCrossOrigin | null): ThumbnailData[] {
export function useThumbnails(
src: ThumbnailSrc | null,
crossOrigin?: MediaCrossOrigin | null,
): ThumbnailImage[] {
const scope = useReactScope(),
$src = React.useMemo(() => signal(src), []),
$crossOrigin = React.useMemo(() => signal($crossOrigin), []),
loader = React.useMemo(
() => scoped(() => ThumbnailsLoader.create($src, $crossOrigin), scope)!,
[],
),
$cues = useSignal(loader.$cues),
data = React.useMemo(() => {
const items: ThumbnailData[] = [],
baseURL = /^https?:/.test(src) || __SERVER__ ? src : location.href;

for (const cue of $cues) {
const [url, dataText = ''] = (cue.text || '').split('#'),
data = resolveThumbnailData(dataText);
items.push({
url: resolveThumbnailSrc(url, baseURL),
cue,
x: data.x ?? -1,
y: data.y ?? -1,
width: data.width ?? -1,
height: data.height ?? -1,
});
}

return items;
}, [$cues]);
);

if (__DEV__ && !scope) {
console.warn(
Expand All @@ -64,47 +42,30 @@ export function useThumbnails(src: string, crossOrigin?: MediaCrossOrigin | null
$crossOrigin.set(crossOrigin);
}, [crossOrigin]);

return data;
return useSignal(loader.$images);
}

/**
* Returns the active thumbnail based on the given time.
* Returns the active thumbnail image based on the given time.
*
* @param thumbnails - thumbnail data returned from called `useThumbnails("...")`.
* @param thumbnails - thumbnail images.
* @param time - the current time to determine which thumbnail is active.
*/
export function useActiveThumbnail(
thumbnails: ThumbnailData[],
thumbnails: ThumbnailImage[],
time: number,
): ThumbnailData | null {
const cues = React.useMemo(() => thumbnails.map((t) => t.cue), [thumbnails]);
): ThumbnailImage | null {
return React.useMemo(() => {
const cue = findActiveCue(cues, time);
return thumbnails.find((t) => t.cue === cue) || null;
}, [thumbnails, cues, time]);
}
let activeIndex = -1;

function resolveThumbnailSrc(src: string, baseURL: string) {
return /^https?:/.test(src) ? src : new URL(src, baseURL).href;
}

const propNames = {
x: 'x',
y: 'y',
w: 'width',
h: 'height',
};

function resolveThumbnailData(data: string) {
const [props, values] = data.split('='),
resolvedData: Record<string, number> = {},
dataValues = values?.split(',');

if (!props || !values) return {};

for (let i = 0; i < props.length; i++) {
resolvedData[propNames[props[i]]] = +dataValues[i];
}
for (let i = thumbnails.length - 1; i >= 0; i--) {
const image = thumbnails[i];
if (time >= image.startTime && (!image.endTime || time < image.endTime)) {
activeIndex = i;
break;
}
}

return resolvedData;
return thumbnails[activeIndex] || null;
}, [thumbnails, time]);
}
9 changes: 8 additions & 1 deletion packages/vidstack/mangle.json
Original file line number Diff line number Diff line change
Expand Up @@ -645,5 +645,12 @@
"_onChaptersChange": "Yk",
"_chaptersTrack": "Zk",
"_removeChapters": "_k",
"_watchAlt": "$k"
"_watchAlt": "$k",
"_crossOrigin": "al",
"_onFindActiveThumbnail": "il",
"_processStoryboard": "cl",
"_processVTTCues": "bl",
"_resolveBaseUrl": "dl",
"_resolveData": "fl",
"_resolveURL": "gl"
}
12 changes: 7 additions & 5 deletions packages/vidstack/src/components/layouts/default-layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import {
} from 'maverick.js';

import { PlayerQueryList } from '../../core';
import type { ThumbnailSrc } from '../ui/thumbnails/thumbnail-loader';

export class DefaultLayout extends Component<DefaultLayoutProps> {
static props: DefaultLayoutProps = {
when: '',
smallWhen: '',
thumbnails: '',
thumbnails: null,
customIcons: false,
translations: null,
menuGroup: 'bottom',
Expand Down Expand Up @@ -125,10 +126,11 @@ export interface DefaultLayoutProps {
*/
smallWhen: string;
/**
* The absolute or relative URL to a [WebVTT](https://developer.mozilla.org/en-US/docs/Web/API/WebVTT_API)
* file resource.
* The thumbnails resource.
*
* @see {@link https://www.vidstack.io/docs/wc/player/core-concepts/loading#thumbnails}
*/
thumbnails: string;
thumbnails: ThumbnailSrc | null;
/**
* Whether the default icons should _not_ be loaded. Set this to `true` when providing your own
* icons.
Expand Down Expand Up @@ -165,7 +167,7 @@ export interface DefaultLayoutProps {

export interface DefaultLayoutContext {
smQueryList: PlayerQueryList;
thumbnails: ReadSignal<string>;
thumbnails: ReadSignal<ThumbnailSrc | null>;
translations: ReadSignal<DefaultLayoutTranslations | null>;
noModal: ReadSignal<DefaultLayoutProps['noModal']>;
menuGroup: ReadSignal<DefaultLayoutProps['menuGroup']>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type { TextTrack } from '../../../../core/tracks/text/text-track';
import { isCueActive, observeActiveTextTrack } from '../../../../core/tracks/text/utils';
import { round } from '../../../../utils/number';
import { formatSpokenTime, formatTime } from '../../../../utils/time';
import type { ThumbnailSrc } from '../../thumbnails/thumbnail-loader';
import { menuContext, type MenuContext } from '../menu-context';
import type { RadioOption } from '../radio/radio';
import { RadioGroupController } from '../radio/radio-group-controller';
Expand All @@ -32,7 +33,7 @@ export class ChaptersRadioGroup extends Component<
ChaptersRadioGroupEvents
> {
static props: ChapterRadioGroupProps = {
thumbnails: '',
thumbnails: null,
};

private _media!: MediaContext;
Expand Down Expand Up @@ -188,10 +189,11 @@ export class ChaptersRadioGroup extends Component<

export interface ChapterRadioGroupProps {
/**
* The absolute or relative URL to a [WebVTT](https://developer.mozilla.org/en-US/docs/Web/API/WebVTT_API)
* file resource.
* The thumbnails resource.
*
* @see {@link https://www.vidstack.io/docs/player/core-concepts/loading#thumbnails}
*/
thumbnails: string;
thumbnails: ThumbnailSrc | null;
}

export interface ChaptersRadioGroupEvents {
Expand Down
Loading

0 comments on commit 698e575

Please sign in to comment.