import type { CoreEvents } from './events/core-types' import type { RuntimeOption } from './option/runtime' import type { OptionInput } from './option/types' import type { PlayerProperties } from './player/properties' import type { PlayerTemplate } from './template/types' import validator from 'option-validator' import { version } from '../package.json' import publishBrowserEntry from './bootstrap/browser' import config from './config' import Contextmenu from './contextmenu' import Control from './control' import Events from './events' import Hotkey from './hotkey' import I18n from './i18n' import Icons from './icons' import Info from './info' import Layer from './layer' import { beginLifecycle, destroyInstance, finishLifecycle, getScope } from './lifecycle/instance' import Loading from './loading' import Mask from './mask' import Notice from './notice' import createDefaults from './option/defaults' import resolveRuntimeOption from './option/runtime' import Player from './player' import Plugins from './plugins' import scheme from './scheme' import Setting from './setting' import Storage from './storage' import style from './style' import Subtitle from './subtitle' import Template from './template' import * as utils from './utils' import Emitter from './utils/emitter' let id = 0 const instances: Artplayer[] = [] // eslint-disable-next-line ts/no-unsafe-declaration-merging -- Player installs these descriptors; emitting fields would shadow them. interface Artplayer extends PlayerProperties { constructor: typeof Artplayer } // eslint-disable-next-line ts/no-unsafe-declaration-merging -- The merged interface describes installed descriptors and the inherited constructor. class Artplayer extends Emitter { declare static STYLE: string declare static DEBUG: boolean declare static CONTEXTMENU: boolean declare static NOTICE_TIME: number declare static SETTING_WIDTH: number declare static SETTING_ITEM_WIDTH: number declare static SETTING_ITEM_HEIGHT: number declare static RESIZE_TIME: number declare static SCROLL_TIME: number declare static SCROLL_GAP: number declare static AUTO_PLAYBACK_MAX: number declare static AUTO_PLAYBACK_MIN: number declare static AUTO_PLAYBACK_TIMEOUT: number declare static RECONNECT_TIME_MAX: number declare static RECONNECT_SLEEP_TIME: number declare static CONTROL_HIDE_TIME: number declare static DBCLICK_TIME: number declare static DBCLICK_FULLSCREEN: boolean declare static MOBILE_DBCLICK_PLAY: boolean declare static MOBILE_CLICK_PLAY: boolean declare static AUTO_ORIENTATION_TIME: number declare static INFO_LOOP_TIME: number declare static FAST_FORWARD_VALUE: number declare static FAST_FORWARD_TIME: number declare static TOUCH_MOVE_RATIO: number declare static VOLUME_STEP: number declare static SEEK_STEP: number declare static PLAYBACK_RATE: number[] declare static ASPECT_RATIO: string[] declare static FLIP: string[] declare static FULLSCREEN_WEB_IN_BODY: boolean declare static LOG_VERSION: boolean declare static USE_RAF: boolean declare static REMOVE_SRC_WHEN_DESTROY: boolean declare id: number declare option: RuntimeOption declare isLock: boolean declare isReady: boolean declare isFocus: boolean declare isInput: boolean declare isRotate: boolean declare isDestroy: boolean declare template: PlayerTemplate declare events: Events declare storage: Storage declare icons: Icons declare i18n: I18n declare notice: Notice declare player: Player declare layers: Layer declare controls: Control declare contextmenu: Contextmenu declare subtitle: Subtitle declare info: Info declare loading: Loading declare hotkey: Hotkey declare mask: Mask declare setting: Setting declare plugins: Plugins constructor(option: OptionInput, readyCallback?: (this: Artplayer, art: Artplayer) => unknown) { super() if (!utils.isBrowser) { throw new Error('Artplayer can only be used in the browser environment') } this.id = ++id this.option = resolveRuntimeOption(option, Artplayer.option) this.isLock = false this.isReady = false this.isFocus = false this.isInput = false this.isRotate = false this.isDestroy = false beginLifecycle(this) try { this.template = new Template(this) as PlayerTemplate if (getScope(this).closed) return this.events = new Events(this) if (getScope(this).closed) return this.storage = new Storage() if (getScope(this).closed) return this.icons = new Icons(this) if (getScope(this).closed) return this.i18n = new I18n(this) if (getScope(this).closed) return this.notice = new Notice(this) if (getScope(this).closed) return this.player = new Player(this) if (getScope(this).closed) return this.layers = new Layer(this) if (getScope(this).closed) return this.controls = new Control(this) if (getScope(this).closed) return this.contextmenu = new Contextmenu(this) if (getScope(this).closed) return this.subtitle = new Subtitle(this) if (getScope(this).closed) return this.info = new Info(this) if (getScope(this).closed) return this.loading = new Loading(this) if (getScope(this).closed) return this.hotkey = new Hotkey(this) if (getScope(this).closed) return this.mask = new Mask(this) if (getScope(this).closed) return this.setting = new Setting(this) if (getScope(this).closed) return this.plugins = new Plugins(this) if (getScope(this).closed) return if (typeof readyCallback === 'function') { this.on('ready', () => readyCallback.call(this, this)) } if (Artplayer.DEBUG) { // eslint-disable-next-line no-console const log = (msg: string) => console.log(`[ART.${this.id}] -> ${msg}`) log(`Version@${Artplayer.version}`) for (let index = 0; index < config.events.length; index++) { this.on(`video:${config.events[index]}`, event => log(`Event@${event.type}`)) } } if (finishLifecycle(this)) instances.push(this) } catch (error) { try { destroyInstance(this, instances, true, Artplayer.REMOVE_SRC_WHEN_DESTROY, true) } catch (cleanupError) { console.warn('Failed to clean up ArtPlayer initialization:', cleanupError) } throw error } } static get instances() { return instances } static get version() { return version } static get config() { return config } static get utils() { return utils } static get scheme() { return scheme } static get Emitter() { return Emitter } static get validator() { return validator } static get kindOf() { return validator.kindOf } static get html() { return Template.html } static get option() { return createDefaults() } get proxy() { return this.events.proxy } get query() { return this.template.query } get video() { return this.template.$video } reset() { this.video.removeAttribute('src') this.video.load() } destroy(removeHtml = true) { destroyInstance(this, instances, removeHtml, Artplayer.REMOVE_SRC_WHEN_DESTROY) } } Artplayer.STYLE = style Artplayer.DEBUG = false Artplayer.CONTEXTMENU = true Artplayer.NOTICE_TIME = 2000 Artplayer.SETTING_WIDTH = 250 Artplayer.SETTING_ITEM_WIDTH = 200 Artplayer.SETTING_ITEM_HEIGHT = 35 Artplayer.RESIZE_TIME = 200 Artplayer.SCROLL_TIME = 200 Artplayer.SCROLL_GAP = 50 Artplayer.AUTO_PLAYBACK_MAX = 10 Artplayer.AUTO_PLAYBACK_MIN = 5 Artplayer.AUTO_PLAYBACK_TIMEOUT = 3000 Artplayer.RECONNECT_TIME_MAX = 5 Artplayer.RECONNECT_SLEEP_TIME = 1000 Artplayer.CONTROL_HIDE_TIME = 3000 Artplayer.DBCLICK_TIME = 300 Artplayer.DBCLICK_FULLSCREEN = true Artplayer.MOBILE_DBCLICK_PLAY = true Artplayer.MOBILE_CLICK_PLAY = false Artplayer.AUTO_ORIENTATION_TIME = 200 Artplayer.INFO_LOOP_TIME = 1000 Artplayer.FAST_FORWARD_VALUE = 3 Artplayer.FAST_FORWARD_TIME = 1000 Artplayer.TOUCH_MOVE_RATIO = 0.5 Artplayer.VOLUME_STEP = 0.1 Artplayer.SEEK_STEP = 5 Artplayer.PLAYBACK_RATE = [0.5, 0.75, 1, 1.25, 1.5, 2] Artplayer.ASPECT_RATIO = ['default', '4:3', '16:9'] Artplayer.FLIP = ['normal', 'horizontal', 'vertical'] Artplayer.FULLSCREEN_WEB_IN_BODY = true Artplayer.LOG_VERSION = true Artplayer.USE_RAF = false Artplayer.REMOVE_SRC_WHEN_DESTROY = true publishBrowserEntry(Artplayer, style) export default Artplayer