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
5 changes: 3 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,9 @@ mpv host's installed copy, WSL): `docs/notes/local-pc.md`.
- **SPLICER only processes `.mjs` and `.js`.** Code moved into a `.ts` file silently stops
being spliced. Stay on `.mjs` plus JSDoc.
- **A change to `native-host/faststream-mpv-host.mjs` or `install.ps1` raises the host's
version**: `HostVersion` there, `RequiredHostVersion` in `MpvBackend.mjs`, and the record
in `tests/unit/mpvHostVersion.test.mjs`, which fails until they agree.
version**: `HostVersion` there and the record in `tests/unit/mpvHostVersion.test.mjs`,
which fails until they agree; `RequiredHostVersion` in `MpvBackend.mjs` too only when the
extension needs the new host (a change with no new behaviour leaves it: no reinstall).
- **A file with `// @ts-check` keeps it**, and is listed in
`tests/unit/typeChecked.test.mjs`. `types/messages.d.ts` gets a message only after its
real payload was read.
Expand Down
18 changes: 16 additions & 2 deletions chrome/player/players/vm/VMPlayer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import {RequestUtils} from '../../utils/RequestUtils.mjs';
import HLSPlayer from '../hls/HLSPlayer.mjs';

export default class VMPlayer extends HLSPlayer {
/**
* @param {import('../../FastStreamClient.mjs').FastStreamClient} client - The player client the player talks through.
* @param {*} options - The player's options, passed unchanged to the base player.
*/
constructor(client, options) {
super(client, options);
}
Expand Down Expand Up @@ -37,6 +41,9 @@ export default class VMPlayer extends HLSPlayer {
return 'https://player.vimeo.com/video/' + encodeURIComponent(path[1]) + (path[2] ? '/config' : '') + query;
}

/**
* @param {import('../../VideoSource.mjs').VideoSource} source - The Vimeo source to play.
*/
async setSource(source) {
try {
const requestUrl = VMPlayer.vimeoRequestUrl(source.url);
Expand All @@ -48,13 +55,13 @@ export default class VMPlayer extends HLSPlayer {
const isEmbed = !new URL(requestUrl).pathname.endsWith('/config');
const hc = [];
if (Array.isArray(source.headers)) {
source.headers.forEach((h) => {
source.headers.forEach(/** @type {(h: {name: string, value: string}) => void} */ ((h) => {
hc.push({
operation: 'set',
header: h.name,
value: h.value,
});
});
}));
} else {
for (const key in source.headers) {
if (Object.hasOwn(source.headers, key)) {
Expand Down Expand Up @@ -114,6 +121,9 @@ export default class VMPlayer extends HLSPlayer {
return this.source;
}

/**
* @param {string} html - The HTML of the Vimeo player page.
*/
extractVimeoHlsUrlFromIframePlayer(html) {
const config = this.extractJsonConfig(html, 'window.playerConfig =');

Expand All @@ -124,6 +134,10 @@ export default class VMPlayer extends HLSPlayer {
return config?.request?.files?.hls;
}

/**
* @param {string} html - The HTML to search for the JSON config.
* @param {string} prefix - The text that marks where the JSON starts.
*/
extractJsonConfig(html, prefix) {
const startIndex = html.indexOf(prefix);
if (startIndex === -1) return null;
Expand Down
15 changes: 11 additions & 4 deletions chrome/player/ui/VolumeControls.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ if (!EnvUtils.isWebAudioSupported()) {
const VolumeSaveDelayMs = 500;

export class VolumeControls extends EventEmitter {
/** @param {import('../FastStreamClient.mjs').FastStreamClient} client */
constructor(client) {
super();
this.client = client;
Expand Down Expand Up @@ -61,6 +62,7 @@ export class VolumeControls extends EventEmitter {
this.loadVolumeState();
}

/** @param {number} volume */
setVolume(volume, dontSave = false) {
if (volume < 0) {
volume = 0;
Expand Down Expand Up @@ -95,13 +97,15 @@ export class VolumeControls extends EventEmitter {
}
}

/** @param {MouseEvent} event */
onVolumeBarMouseDown(event) {
// Only the left button drags. A right-click's context menu takes the mouseup, and the
// volume then followed the mouse until the next click.
if (event.button !== 0) {
return;
}

/** @param {number} volumeBarX */
const shiftVolume = (volumeBarX) => {
const totalWidth = /** @type {HTMLElement} */ (DOMElements.volumeControlBar).clientWidth;

Expand All @@ -120,13 +124,14 @@ export class VolumeControls extends EventEmitter {
}
};

/** @param {MouseEvent|TouchEvent} event */
const onVolumeBarMouseMove = (event) => {
// No button held: it was let go where this drag never heard of it.
if (event.type === 'mousemove' && event.buttons === 0) {
if (event.type === 'mousemove' && /** @type {MouseEvent} */ (event).buttons === 0) {
stopDrag();
return;
}
const currentX = event.clientX - WebUtils.getOffsetLeft(/** @type {HTMLElement} */ (DOMElements.volumeContainer)) - 10;
const currentX = /** @type {MouseEvent} */ (event).clientX - WebUtils.getOffsetLeft(/** @type {HTMLElement} */ (DOMElements.volumeContainer)) - 10;
shiftVolume(currentX);
};

Expand All @@ -138,10 +143,11 @@ export class VolumeControls extends EventEmitter {
document.removeEventListener('mouseup', onVolumeBarMouseUp);
};

/** @param {MouseEvent|TouchEvent} event */
const onVolumeBarMouseUp = (event) => {
stopDrag();

const currentX = event.clientX - WebUtils.getOffsetLeft(/** @type {HTMLElement} */ (DOMElements.volumeContainer)) - 10;
const currentX = /** @type {MouseEvent} */ (event).clientX - WebUtils.getOffsetLeft(/** @type {HTMLElement} */ (DOMElements.volumeContainer)) - 10;

if (!isNaN(currentX)) {
shiftVolume(currentX);
Expand All @@ -159,6 +165,7 @@ export class VolumeControls extends EventEmitter {
event.stopPropagation();
}

/** @param {number} volume */
updateVolumeBar(volume) {
const currentVolumeTag = /** @type {HTMLElement} */ (DOMElements.currentVolume);
const muteButtonTag = /** @type {HTMLElement} */ (DOMElements.muteBtn);
Expand Down Expand Up @@ -192,7 +199,7 @@ export class VolumeControls extends EventEmitter {
const state = await Utils.loadAndParseOptions('volumeState', {
volume: 1,
});
this.setVolume(state.volume, true);
this.setVolume(/** @type {{volume: number}} */ (state).volume, true);
}

/** Saves a volume change still waiting for its delay. */
Expand Down
6 changes: 3 additions & 3 deletions chrome/player/ui/VpnPrompt.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {DOMElements} from './DOMElements.mjs';
*/
export class VpnPrompt {
/**
* @param {Object} client - The FastStreamClient.
* @param {import('../FastStreamClient.mjs').FastStreamClient} client - The FastStreamClient.
*/
constructor(client) {
this.client = client;
Expand All @@ -42,10 +42,10 @@ export class VpnPrompt {
async check(source) {
this.hide();
this.source = source;
if (!EnvUtils.isExtension() || !source?.url) return;
if (!EnvUtils.isExtension() || !/** @type {{url: string}} */ (source)?.url) return;
let status;
try {
status = await chrome.runtime.sendMessage({type: MessageTypes.VPN_STATUS, url: source.url});
status = await chrome.runtime.sendMessage({type: MessageTypes.VPN_STATUS, url: /** @type {{url: string}} */ (source).url});
} catch (e) {
return;
}
Expand Down
3 changes: 3 additions & 0 deletions chrome/player/ui/audio/AbstractAudioModule.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import {EventEmitter} from '../../modules/eventemitter.mjs';
import {VirtualAudioNode} from './VirtualAudioNode.mjs';

export class AbstractAudioModule extends EventEmitter {
/**
* @param {string} name
*/
constructor(name) {
super();
this.audioContext = null;
Expand Down
2 changes: 1 addition & 1 deletion chrome/player/utils/BlobManager.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
export class BlobManager {
/**
* Creates a blob from data and MIME type.
* @param {Array} data - The data to store in the blob.
* @param {Array<BlobPart>} data - The data to store in the blob.
* @param {string} mimeType - The MIME type of the blob.
* @return {Blob} The created blob.
*/
Expand Down
10 changes: 6 additions & 4 deletions docs/notes/mpv.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ to it, and the result carries `hostOutdated`. That shows as "!" on the toolbar b
in MPV mode with what to run in the tooltip (`tab.mpvHostOutdated`, persisted like
`mpvError`, whose reason comes first), in the player's "Sent to mpv" message, and after
"Test mpv connection". This replaced `mpv-host-changed.yml`'s e-mail. **Every change to
the host file or to `install.ps1` raises both numbers by one**:
`tests/unit/mpvHostVersion.test.mjs` records both files' SHA-256 beside the version and
fails until `HostVersion`, `RequiredHostVersion` and the record agree (the failure prints
the new hashes). The installer counts because `update-local.ps1` tells an installed host
the host file or to `install.ps1` raises `HostVersion` by one**, and `RequiredHostVersion`
with it only when the extension needs the new host (since 2026-10-23: a change that alters
no behaviour, such as the host's types, has every installed host go on working, and nobody
is asked to reinstall): `tests/unit/mpvHostVersion.test.mjs` records both files' SHA-256
beside the version, fails until `HostVersion` and the record agree (the failure prints the
new hashes), and checks `RequiredHostVersion <= HostVersion`. The installer counts because `update-local.ps1` tells an installed host
from the repository's by the host file alone; a raised `HostVersion` changes that file. The
extension changes with it, so such a push releases, Firefox updates the extension, and
the next hand-off on the owner's PC shows the "!" until `update-local.cmd` (or
Expand Down
8 changes: 4 additions & 4 deletions docs/notes/type-checking.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ is lowered with `pnpm run typecheck:strict -- --update`, so a typed file cannot
unnoticed. `tests/unit/checkImplicitAny.test.mjs` covers the rules (each broken once, and
caught), and an untyped parameter planted in a file at 0 failed the check with its line.

The mpv host (`native-host/faststream-mpv-host.mjs`, 76 untyped places) is left out on
purpose: any change to it raises `HostVersion` and `RequiredHostVersion`, and the add-on
then has every user reinstall the helper, for comments that change no behaviour. It is
typed the next time it changes for a real reason; until then the ratchet holds its count.
The mpv host (`native-host/faststream-mpv-host.mjs`) was typed on 2026-10-23 (76 to 1
untyped places) with `HostVersion` 20 and `RequiredHostVersion` left at 19: a change
that alters no behaviour raises only the host's own version, so installed helpers go on
working and nobody is asked to reinstall for comments (the owner's choice).

### The libraries' own types (2026-10-22)

Expand Down
Loading
Loading