import type { AviationStoreReader } from '../store'; import type { PlaybackEngine } from '../specs/PlaybackEngine.nitro'; import type { AdsController } from '../specs/AdsController.nitro'; import type { PlaybackOutputHandle } from '../ports/PlaybackOutput'; import type { RemoteCommand, RemoteCommandEvent } from '../specs/types.nitro'; export interface PluginPlayer { readonly id: string; readonly engine: PlaybackEngine; /** Whether a plugin with the given name is registered (setup may still be running). */ hasPlugin(name: string): boolean; getPluginStatus(name: string): PluginStatus; setAdsController(controller: AdsController): void; clearAdsController(): void; getAdsController(): AdsController | undefined; /** Registers an alternate playback output. See AviationPlayer.registerOutput. */ registerOutput(name: string): PlaybackOutputHandle; overrideRemoteCommand( command: RemoteCommand, handler: (event: RemoteCommandEvent) => void ): void; } export interface PluginContext { readonly player: PluginPlayer; readonly engine: PlaybackEngine; /** Read-only view: snapshots and subscriptions, never mutators. */ readonly store: AviationStoreReader; } export type PluginStatus = 'active' | 'failed' | 'absent'; /** * Player-scoped plugin contract. * * Plugins install in array order and tear down in reverse. A `setup()` throw * fails the whole player only when {@link MediaPlugin.critical} is set; * otherwise the failure is contained to the plugin. * * Plugins receive the player they are attached to. They must not discover a * global engine or mutate process-wide Aviation state. */ export interface MediaPlugin { readonly name: string; /** * When true, a setup failure aborts player setup. Default false: the * failure is logged, best-effort teardown runs, and the player continues * without this plugin. */ readonly critical?: boolean; setup(context: PluginContext): void | Promise; teardown(context: PluginContext): void | Promise; }