Files
youtube-music/src/plugins/custom-output-device/renderer.ts
2025-09-06 04:51:31 +09:00

77 lines
2.1 KiB
TypeScript

import { createRenderer } from '@/utils';
import type { YoutubePlayer } from '@/types/youtube-player';
import type { RendererContext } from '@/types/contexts';
import type { CustomOutputPluginConfig } from './index';
const updateDeviceList = async (
context: RendererContext<CustomOutputPluginConfig>,
) => {
const newDevices: Record<string, string> = {};
const devices = await navigator.mediaDevices
.enumerateDevices()
.then((devices) =>
devices.filter((device) => device.kind === 'audiooutput'),
);
for (const device of devices) {
newDevices[device.deviceId] = device.label;
}
const options = await context.getConfig();
options.devices = newDevices;
context.setConfig(options);
};
const updateSinkId = async (
audioContext?: AudioContext & {
setSinkId?: (sinkId: string) => Promise<void>;
},
sinkId?: string,
) => {
if (!audioContext || !sinkId) return;
if (!('setSinkId' in audioContext)) return;
if (typeof audioContext.setSinkId === 'function') {
await audioContext.setSinkId(sinkId);
}
};
export const renderer = createRenderer<
{
options?: CustomOutputPluginConfig;
audioContext?: AudioContext;
audioCanPlayHandler: (event: CustomEvent<Compressor>) => Promise<void>;
},
CustomOutputPluginConfig
>({
async audioCanPlayHandler({ detail: { audioContext } }) {
this.audioContext = audioContext;
await updateSinkId(audioContext, this.options!.output);
},
async onPlayerApiReady(_: YoutubePlayer, context) {
this.options = await context.getConfig();
await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
navigator.mediaDevices.ondevicechange = async () =>
await updateDeviceList(context);
document.addEventListener('ytmd:audio-can-play', this.audioCanPlayHandler, {
once: true,
passive: true,
});
await updateDeviceList(context);
},
stop() {
document.removeEventListener(
'ytmd:audio-can-play',
this.audioCanPlayHandler,
);
navigator.mediaDevices.ondevicechange = null;
},
async onConfigChange(config) {
this.options = config;
await updateSinkId(this.audioContext, config.output);
},
});