132 lines
3.8 KiB
TypeScript
132 lines
3.8 KiB
TypeScript
import type { VideoWidgetNode } from '../schema';
|
|
import { pickUrlLike } from './urlLike';
|
|
|
|
/**
|
|
* goView video option shape varies across versions.
|
|
* Keep it permissive and normalize the common fields.
|
|
*/
|
|
export interface GoViewVideoOption {
|
|
dataset?: unknown;
|
|
src?: unknown;
|
|
url?: unknown;
|
|
|
|
loop?: boolean;
|
|
muted?: boolean;
|
|
autoplay?: boolean;
|
|
autoPlay?: boolean;
|
|
|
|
fit?: unknown;
|
|
objectFit?: unknown;
|
|
|
|
borderRadius?: number;
|
|
}
|
|
|
|
/**
|
|
* Back-compat alias (older code used "LegacyVideoOption").
|
|
*/
|
|
export type LegacyVideoOption = GoViewVideoOption;
|
|
|
|
function pickSrc(option: GoViewVideoOption): string {
|
|
// Prefer the whole option first (covers videoUrl/mp4/m3u8/flv/etc directly on the object).
|
|
return pickUrlLike(option) || pickUrlLike(option.dataset) || pickUrlLike(option.src) || pickUrlLike(option.url);
|
|
}
|
|
|
|
function asString(v: unknown): string {
|
|
if (typeof v === 'string') return v;
|
|
if (!v) return '';
|
|
return '';
|
|
}
|
|
|
|
function toMaybeBoolean(v: unknown): boolean | undefined {
|
|
if (typeof v === 'boolean') return v;
|
|
if (typeof v === 'number') return v !== 0;
|
|
if (typeof v === 'string') {
|
|
const s = v.trim().toLowerCase();
|
|
if (s === 'true' || s === '1') return true;
|
|
if (s === 'false' || s === '0') return false;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function toMaybeNumber(v: unknown): number | undefined {
|
|
if (typeof v === 'number' && Number.isFinite(v)) return v;
|
|
if (typeof v === 'string') {
|
|
const n = Number(v);
|
|
if (Number.isFinite(n)) return n;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function pickFromNested<T>(input: unknown, picker: (obj: Record<string, unknown>) => T | undefined, depth: number): T | undefined {
|
|
if (!input || typeof input !== 'object') return undefined;
|
|
const obj = input as Record<string, unknown>;
|
|
|
|
const direct = picker(obj);
|
|
if (direct !== undefined) return direct;
|
|
if (depth <= 0) return undefined;
|
|
|
|
for (const key of ['style', 'config', 'option', 'options', 'props', 'dataset', 'data']) {
|
|
const nested = pickFromNested(obj[key], picker, depth - 1);
|
|
if (nested !== undefined) return nested;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
function pickBorderRadius(option: GoViewVideoOption): number | undefined {
|
|
const direct = toMaybeNumber(option.borderRadius);
|
|
if (direct !== undefined) return direct;
|
|
|
|
return pickFromNested(option, (obj) => toMaybeNumber(obj.borderRadius ?? obj.radius ?? obj.r), 2);
|
|
}
|
|
|
|
function pickBooleanLike(option: GoViewVideoOption, keys: string[]): boolean | undefined {
|
|
return pickFromNested(
|
|
option,
|
|
(obj) => {
|
|
for (const key of keys) {
|
|
const v = toMaybeBoolean(obj[key]);
|
|
if (v !== undefined) return v;
|
|
}
|
|
return undefined;
|
|
},
|
|
2,
|
|
);
|
|
}
|
|
|
|
function pickFitFromNested(option: GoViewVideoOption): string {
|
|
const direct = asString(option.fit) || asString(option.objectFit);
|
|
if (direct) return direct;
|
|
|
|
const nested = pickFromNested(option, (obj) => asString(obj.fit) || asString(obj.objectFit), 2);
|
|
return nested ?? '';
|
|
}
|
|
|
|
function pickFit(option: GoViewVideoOption): VideoWidgetNode['props']['fit'] | undefined {
|
|
const raw = pickFitFromNested(option);
|
|
if (!raw) return undefined;
|
|
|
|
// normalize common variants
|
|
const v = raw.toLowerCase();
|
|
if (v === 'contain' || v === 'cover' || v === 'fill' || v === 'none' || v === 'scale-down') {
|
|
return v as VideoWidgetNode['props']['fit'];
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
export function convertGoViewVideoOptionToNodeProps(option: GoViewVideoOption): VideoWidgetNode['props'] {
|
|
return {
|
|
src: pickSrc(option),
|
|
loop: pickBooleanLike(option, ['loop', 'isLoop']),
|
|
muted: pickBooleanLike(option, ['muted', 'isMuted', 'mute']),
|
|
fit: pickFit(option),
|
|
borderRadius: pickBorderRadius(option),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Back-compat export.
|
|
*/
|
|
export const convertLegacyVideoOptionToNodeProps = convertGoViewVideoOptionToNodeProps;
|