Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/decisions/media/mux-data-monitor-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ date: 2026-09-01

## Decision

`MuxData` starts one `mux-embed` monitor per target and keeps it alive for the target's lifetime. A source change is reported to the live monitor as `videochange`, and an engine swap re-hooks engine telemetry on the live monitor. The monitor is destroyed and re-created only when the target changes or when an option baked into `monitor()` itself changes (SDK, beacon domain, debug, cookies).
`MuxDataExtension` starts one `mux-embed` monitor per target and keeps it alive for the target's lifetime. A source change is reported to the live monitor as `videochange`, and an engine swap re-hooks engine telemetry on the live monitor. The monitor is destroyed and re-created only when the target changes or when an option baked into `monitor()` itself changes (SDK, beacon domain, debug, cookies).

## Why

Expand Down
14 changes: 7 additions & 7 deletions packages/google-cast/src/google-cast-provider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { HTMLMediaTargetLike } from '@videojs/media/dom';
import { isCaptionOrSubtitleTrack } from '@videojs/utils/dom';

import type { GoogleCastProps } from './index';
import type { GoogleCastExtensionProps } from './index';
import { castFramework, ensureCastFramework, googleCastInstances } from './registry';
import { RemotePlayback, type RemotePlaybackHooks } from './remote-playback';
import {
Expand All @@ -20,14 +20,14 @@ import {

type RemotePlayerListener = (event?: cast.framework.RemotePlayerChangedEvent) => void;

type GoogleCastConfig = GoogleCastProps;
type GoogleCastConfig = GoogleCastExtensionProps;

/**
* Cast provider + lifecycle. Created by the {@link GoogleCast} component and installed as the host's `targetOverride`
* while a cast session is connected, so its getters/setters route through the cast receiver; when disconnected the host
* falls through to the attached target. Also owns the cast framework integration, the `RemotePlayback` instance exposed
* via {@link GoogleCastProvider#remote}, and dispatches media events on the attached target (forwarded by the host)
* while casting.
* Cast provider + lifecycle. Created by the {@link GoogleCastExtension} component and installed as the host's
* `targetOverride` while a cast session is connected, so its getters/setters route through the cast receiver; when
* disconnected the host falls through to the attached target. Also owns the cast framework integration, the
* `RemotePlayback` instance exposed via {@link GoogleCastProvider#remote}, and dispatches media events on the attached
* target (forwarded by the host) while casting.
*/
export class GoogleCastProvider {
target: HTMLMediaTargetLike | null = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { requiresCastFramework } from './utils';

type MediaHost = HTMLMediaElementHost<HTMLMediaTargetLike, any>;

export interface GoogleCastProps {
export interface GoogleCastExtensionProps {
/** Source URL loaded on the Cast receiver. Falls back to the host's `src` / `currentSrc`. */
src?: string | undefined;
/** MIME type of the Cast source. When unset, the receiver infers it from the URL. */
Expand All @@ -19,15 +19,15 @@ export interface GoogleCastProps {
customData?: Record<string, unknown> | null | undefined;
}

export const googleCastDefaultProps: GoogleCastProps = {
src: undefined,
contentType: undefined,
streamType: undefined,
receiver: undefined,
customData: undefined,
};
export class GoogleCastExtension implements GoogleCastExtensionProps, MediaComponent {
static defaultProps: GoogleCastExtensionProps = {
src: undefined,
contentType: undefined,
streamType: undefined,
receiver: undefined,
customData: undefined,
};

export class GoogleCast implements GoogleCastProps, MediaComponent {
#src: string | undefined;
#contentType: string | undefined;
#streamType: MediaStreamType | undefined;
Expand All @@ -37,7 +37,7 @@ export class GoogleCast implements GoogleCastProps, MediaComponent {
#provider: GoogleCastProvider | null = null;
#override: Partial<HTMLMediaTargetLike> | null = null;

constructor(props: GoogleCastProps = {}) {
constructor(props: GoogleCastExtensionProps = {}) {
Object.assign(this, props);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/google-cast/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './media';
export * from './google-cast';
8 changes: 4 additions & 4 deletions packages/google-cast/src/tests/google-cast-provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { addMediaComponent, type HTMLMediaTargetLike, HTMLVideoElementHost } fro
import { afterEach, beforeEach, describe, expect, it, vi } from 'vite-plus/test';

import { GoogleCastProvider } from '../google-cast-provider';
import { GoogleCast } from '../index';
import { GoogleCastExtension } from '../index';
import { ensureCastFramework } from '../registry';

vi.mock('../registry', async (importOriginal) => {
Expand Down Expand Up @@ -99,7 +99,7 @@ describe('GoogleCastProvider', () => {
});
});

describe('GoogleCast', () => {
describe('GoogleCastExtension', () => {
it('loads the cast framework when the host remote is read while attached', () => {
vi.stubGlobal('chrome', {});

Expand All @@ -108,7 +108,7 @@ describe('GoogleCast', () => {

host.attach(target as Parameters<HTMLVideoElementHost['attach']>[0]);

addMediaComponent(host, new GoogleCast());
addMediaComponent(host, new GoogleCastExtension());
expect(ensureCastFramework).not.toHaveBeenCalled();

// The component's override must expose `remote` as an accessor so host
Expand All @@ -123,7 +123,7 @@ describe('GoogleCast', () => {

const host = new HTMLVideoElementHost();

addMediaComponent(host, new GoogleCast());
addMediaComponent(host, new GoogleCastExtension());

void host.remote;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { addMediaComponent, HTMLVideoElementHost } from '@videojs/media/dom';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vite-plus/test';

import { GoogleCast } from '../index';
import { GoogleCastExtension } from '../index';

const mocks = vi.hoisted(() => {
class FakeRemote extends EventTarget {
Expand Down Expand Up @@ -47,7 +47,7 @@ function setup() {

host.attach(video);

const googleCast = new GoogleCast();
const googleCast = new GoogleCastExtension();

addMediaComponent(host, googleCast);

Expand Down Expand Up @@ -76,7 +76,7 @@ afterEach(() => {
vi.unstubAllGlobals();
});

describe('GoogleCast', () => {
describe('GoogleCastExtension', () => {
it('registers remote state listeners only once across media changes', () => {
const { googleCast, provider } = setup();
const nextHost = new HTMLVideoElementHost();
Expand Down
8 changes: 4 additions & 4 deletions packages/html/src/define/extensions/google-cast.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { GoogleCastElement } from '../../extensions/google-cast';
import { GoogleCastExtension } from '../../extensions/google-cast';
import { safeDefine } from '../../registration/safe-define';

export { GoogleCastElement };
export { GoogleCastExtension };

safeDefine(GoogleCastElement);
safeDefine(GoogleCastExtension);

declare global {
interface HTMLElementTagNameMap {
[GoogleCastElement.tagName]: GoogleCastElement;
[GoogleCastExtension.tagName]: GoogleCastExtension;
}
}
8 changes: 4 additions & 4 deletions packages/html/src/define/extensions/mux-data.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { MuxDataElement } from '../../extensions/mux-data';
import { MuxDataExtension } from '../../extensions/mux-data';
import { safeDefine } from '../../registration/safe-define';

export { MuxDataElement };
export { MuxDataExtension };

safeDefine(MuxDataElement);
safeDefine(MuxDataExtension);

declare global {
interface HTMLElementTagNameMap {
[MuxDataElement.tagName]: MuxDataElement;
[MuxDataExtension.tagName]: MuxDataExtension;
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the file name google-cast-extension.ts should be just extension.ts to follow the adapter pattern.
the folder name already indicates what extension it is

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed in 0e19841 to extension.ts; the folder names the extension. Nothing in the reference builder or workspace tooling keyed on the old file names, only the barrels and tests, which follow the file.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { PropertyDeclarationMap } from '@videojs/element';
import { GoogleCast, type GoogleCastProps } from '@videojs/google-cast';
import { GoogleCastExtension as GoogleCastExtensionBase, type GoogleCastExtensionProps } from '@videojs/google-cast';

import { MediaComponentElement } from '../media-component-element';

Expand All @@ -16,7 +16,7 @@ import { MediaComponentElement } from '../media-component-element';
* </video-player>
* ```;
*/
export class GoogleCastElement extends MediaComponentElement<GoogleCast> {
export class GoogleCastExtension extends MediaComponentElement<GoogleCastExtensionBase> {
static readonly tagName = 'google-cast';

static override properties = {
Expand All @@ -25,10 +25,10 @@ export class GoogleCastElement extends MediaComponentElement<GoogleCast> {
streamType: { type: String, attribute: 'stream-type' },
receiver: { type: String },
// `customData` takes an object, so it's a property-only prop.
} satisfies PropertyDeclarationMap<Exclude<keyof GoogleCastProps, 'customData'>>;
} satisfies PropertyDeclarationMap<Exclude<keyof GoogleCastExtensionProps, 'customData'>>;

protected createComponent(): GoogleCast {
return new GoogleCast();
protected createComponent(): GoogleCastExtensionBase {
return new GoogleCastExtensionBase();
}

/** Source URL loaded on the Cast receiver. Falls back to the media's `src` / `currentSrc`. */
Expand All @@ -50,11 +50,11 @@ export class GoogleCastElement extends MediaComponentElement<GoogleCast> {
}

/** Stream type used on the Cast receiver. Falls back to the media's `streamType`. */
get streamType(): GoogleCastProps['streamType'] {
get streamType(): GoogleCastExtensionProps['streamType'] {
return this.component.streamType;
}

set streamType(value: GoogleCastProps['streamType'] | null) {
set streamType(value: GoogleCastExtensionProps['streamType'] | null) {
this.component.streamType = value ?? undefined;
}

Expand All @@ -68,11 +68,11 @@ export class GoogleCastElement extends MediaComponentElement<GoogleCast> {
}

/** Custom data sent to the Cast receiver with the load request. */
get customData(): GoogleCastProps['customData'] {
get customData(): GoogleCastExtensionProps['customData'] {
return this.component.customData;
}

set customData(value: GoogleCastProps['customData']) {
set customData(value: GoogleCastExtensionProps['customData']) {
this.component.customData = value;
}
}
2 changes: 1 addition & 1 deletion packages/html/src/extensions/google-cast/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './google-cast-element';
export * from './extension';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the file name mux-data-extension.ts should be just extension.ts to follow the adapter pattern.
the folder name already indicates what extension it is

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed in 0e19841 to extension.ts; the folder names the extension. Nothing in the reference builder or workspace tooling keyed on the old file names, only the barrels and tests, which follow the file.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { PropertyDeclarationMap } from '@videojs/element';
import { MuxData, type MuxDataProps } from '@videojs/mux-data';
import { MuxDataExtension as MuxDataExtensionBase, type MuxDataExtensionProps } from '@videojs/mux-data';

import { MediaComponentElement } from '../media-component-element';

Expand All @@ -22,7 +22,7 @@ import { MediaComponentElement } from '../media-component-element';
* </video-player>
* ```;
*/
export class MuxDataElement extends MediaComponentElement<MuxData> {
export class MuxDataExtension extends MediaComponentElement<MuxDataExtensionBase> {
static readonly tagName = 'mux-data';

static override properties = {
Expand All @@ -34,10 +34,10 @@ export class MuxDataElement extends MediaComponentElement<MuxData> {
playerSoftwareVersion: { type: String, attribute: 'player-software-version' },
playerInitTime: { type: Number, attribute: 'player-init-time' },
// `metadata` and `MuxDataSdk` take objects, so they're property-only props.
} satisfies PropertyDeclarationMap<Exclude<keyof MuxDataProps, 'metadata' | 'MuxDataSdk'>>;
} satisfies PropertyDeclarationMap<Exclude<keyof MuxDataExtensionProps, 'metadata' | 'MuxDataSdk'>>;

protected createComponent(): MuxData {
return new MuxData();
protected createComponent(): MuxDataExtensionBase {
return new MuxDataExtensionBase();
}

/** Mux Data environment key for the beacons. Optional for Mux-hosted playback. */
Expand Down Expand Up @@ -104,20 +104,20 @@ export class MuxDataElement extends MediaComponentElement<MuxData> {
}

/** Custom view metadata forwarded to the Mux Data SDK. */
get metadata(): MuxDataProps['metadata'] {
get metadata(): MuxDataExtensionProps['metadata'] {
return this.component.metadata;
}

set metadata(value: MuxDataProps['metadata']) {
set metadata(value: MuxDataExtensionProps['metadata']) {
this.component.metadata = value;
}

/** Mux Data SDK used for monitoring. Set to `undefined` to disable monitoring. */
get MuxDataSdk(): MuxDataProps['MuxDataSdk'] {
get MuxDataSdk(): MuxDataExtensionProps['MuxDataSdk'] {
return this.component.MuxDataSdk;
}

set MuxDataSdk(value: MuxDataProps['MuxDataSdk']) {
set MuxDataSdk(value: MuxDataExtensionProps['MuxDataSdk']) {
this.component.MuxDataSdk = value;
}
}
2 changes: 1 addition & 1 deletion packages/html/src/extensions/mux-data/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './mux-data-element';
export * from './extension';
Loading
Loading