import Player, { PartialRequired, PlayerPlugin } from '@nusaplayer/core' import type { ICONS_MAP } from './functions/icons' import type { Subtitle as SubtitleInstance } from './components/Subtitle' export type SubtitleSource = { default?: boolean name?: string src: string encoding?: string type?: string /** * seconds */ offset?: number } export type Subtitle = { source?: SubtitleSource[] fontSize?: number bottom?: string color?: string shadow?: string fontFamily?: string background?: boolean marginBottom?: string onChange?: (s: SubtitleSource) => void | Promise } export type ContextMenuItem = { name: string key?: string checked?: boolean disabled?: boolean hidden?: boolean separator?: boolean href?: string onClick?: (player: Player) => void | Promise } export type MenuBar< T = { name: string default?: boolean value?: any } > = { position?: 'top' | 'bottom' /** * Place this control next to the gear button instead of inside the settings panel. */ anchor?: 'settings' | 'start' key?: string name: string /** Button label when `icon` is omitted (e.g. current quality). */ label?: string icon?: string children?: T[] onChange?: (arg: T, elm: HTMLButtonElement, player: Player) => void onClick?: (elm: HTMLButtonElement, player: Player) => void } export type Setting = { name: string key?: string // children 可无 (用于移除) /** * selector 切换下个面板单选 1 | 2 | 3 * switcher 当前面板切换 true or false */ type?: 'selector' | 'switcher' | 'slider' | 'option' icon?: string children?: Setting[] onChange?: (a: T /* Setting | boolean */, b?: { index: number; player: Player }) => void | Promise default?: any value?: T min?: number max?: number step?: number } export type Thumbnails = { src: string[] | string number: number x?: number y?: number width?: number height?: number isVTT?: boolean } export type Highlight = { text: string time: number } export type UiConfig = { theme?: { primaryColor?: string watermark?: { /** img or svg */ src: string // make screenshot include watermark? // set positioning here [top, left, right, bottom] style?: Record attrs?: Record } progress?: { /** * default: 'auto' * auto: mobile->top pc->top */ position?: 'auto' | 'top' | 'center' backward?: number forward?: number /** * default: true * work only top */ mini?: boolean } controller?: { /** * default: 'always' */ display?: 'always' | 'played' /** * default: true */ header?: boolean | { back?: 'always' | 'fullscreen' } /** * default: true */ coverButton?: boolean /** * default: hover * */ displayBehavior?: 'hover' | 'delay' | 'none' /** * default: 'none' */ slideToSeek?: 'none' | 'always' | 'long-touch' /** * default: 'top' * auto: same as bottom (control bar) */ setting?: 'auto' | 'top' | 'bottom' } } /** * default: false */ autoFocus?: boolean /** * default: false */ screenshot?: boolean /** * default: true * 全屏(如果不可用将会降级为网页全屏) */ fullscreen?: boolean /** * default: true * 网页全屏按钮(系统全屏可用时额外显示) */ webFullscreen?: boolean /** * default: true */ pictureInPicture?: boolean /** * default: true * Whether or not the device should rotate to landscape mode when the video * enters fullscreen. Note that this behavior is based on an experimental * browser API, and may not work on all platforms. * Defaults to true. */ forceLandscapeOnFullscreen?: boolean /** * PC only - default: { focused: true } */ keyboard?: { focused?: boolean; global?: boolean } /** * default: ['2.0', '1.75', '1.25', '1.0', '0.75', '0.5'] */ speeds?: string[] subtitle?: Subtitle /** * default: ['loop', 'speed'] */ settings?: (Setting | 'loop')[] | false thumbnails?: Thumbnails highlight?: { color?: string source?: Highlight[] } menu?: MenuBar[] /** * Right-click menu. Default: play, mute, loop, copy URL. * `false` keeps the browser menu. An array appends extra items. */ contextmenu?: false | ContextMenuItem[] errorBuilder?: (error: ErrorPayload, target: HTMLDivElement, cb: (error: ErrorPayload) => void) => void icons?: { play?: string pause?: string volume?: [string, string] //on off fullscreen?: [string, string] webfullscreen?: [string, string] pip?: [string, string] setting?: string screenshot?: string playbackRate?: string loop?: string progressIndicator?: string loadingIndicator?: string next?: string previous?: string subtitle?: string quality?: string lang?: string audio?: string chromecast?: string airplay?: string danmaku?: string playlist?: string } } export type NoticePosition = | 'top' | 'bottom' | 'left' | 'right' | 'center' | 'top-left' | 'top-center' | 'top-right' | 'left-bottom' | 'bottom-right' export type ToastAction = { text: string variant?: 'primary' | 'ghost' | 'danger' /** Close the toast after click. Default true. */ close?: boolean onClick?: (ctx: { close: () => void }) => void | boolean | Promise } export type ToastOptions = { id?: string title?: string text?: string /** Raw HTML body. Prefer `text` unless you need markup. */ html?: string /** Custom node; replaces title/text/html layout. */ content?: string | HTMLElement type?: 'info' | 'success' | 'warning' | 'error' position?: NoticePosition /** Auto-hide ms. `false` keeps it until closed. Default 4000, or sticky if `actions` exist. */ duration?: number | false closable?: boolean icon?: string | false className?: string style?: Record actions?: ToastAction[] } export type ToastHandle = { id: string close: () => void update: (patch: Partial) => void } export type ToastApi = { (options: ToastOptions | string): ToastHandle close: (id?: string) => void closeAll: () => void } export type ErrorPayload = | Event | { message: string code?: number } export interface UIInterface extends PlayerPlugin { config: PartialRequired player: Player icons: typeof ICONS_MAP subtitle: SubtitleInstance $watermark?: HTMLImageElement notice: (text: string, position?: NoticePosition, id?: string) => void toast: ToastApi keyboard?: { register: (payload: Record void>) => void unregister: (keys: string[]) => void } setting?: { register: (payload: Setting | Setting[]) => void unregister: (key: string) => void updateLabel: (key: string, text: string) => void select: (key: string, value: boolean | number, shouldBeCallFn?: Boolean) => void } menu: { register: (menu: MenuBar | MenuBar[]) => void unregister: (key: string) => void select: (name: string, index: number, shouldBeCallFn?: Boolean) => void updateLabel: (key: string, text: string) => void } contextmenu?: { register: (item: ContextMenuItem | ContextMenuItem[]) => void unregister: (key: string) => void } toggleController: () => void changHighlightSource: (highlights: Highlight[]) => void changThumbnails: (src: Thumbnails) => void progressHoverCallback: ((rate?: number /** 0 ~ 1 */) => void)[] $root: HTMLDivElement $coverButton?: HTMLDivElement // 可配置无 $controllerBar?: HTMLDivElement $controllerBottom: HTMLDivElement $progress?: HTMLDivElement //live 无 $mask: HTMLDivElement $setting: HTMLDivElement $subtitle: HTMLDivElement }