feat: migrate to new plugin api

Co-authored-by: Su-Yong <simssy2205@gmail.com>
This commit is contained in:
JellyBrick
2023-11-11 18:02:22 +09:00
parent 739e7a448b
commit 794d00ce9e
124 changed files with 3363 additions and 2720 deletions

View File

@ -0,0 +1,132 @@
import emptyStyle from './empty-player.css?inline';
import { createPluginBuilder } from '../utils/builder';
type WaveColor = {
gradient: string[];
rotate?: number;
};
export type VisualizerPluginConfig = {
enabled: boolean;
type: 'butterchurn' | 'vudio' | 'wave';
butterchurn: {
preset: string;
renderingFrequencyInMs: number;
blendTimeInSeconds: number;
},
vudio: {
effect: string;
accuracy: number;
lighting: {
maxHeight: number;
maxSize: number;
lineWidth: number;
color: string;
shadowBlur: number;
shadowColor: string;
fadeSide: boolean;
prettify: boolean;
horizontalAlign: string;
verticalAlign: string;
dottify: boolean;
}
};
wave: {
animations: {
type: string;
config: {
bottom?: boolean;
top?: boolean;
count?: number;
cubeHeight?: number;
lineWidth?: number;
diameter?: number;
fillColor?: string | WaveColor;
lineColor?: string | WaveColor;
radius?: number;
frequencyBand?: string;
}
}[];
};
};
const builder = createPluginBuilder('visualizer', {
name: 'Visualizer',
restartNeeded: true,
config: {
enabled: false,
type: 'butterchurn',
// Config per visualizer
butterchurn: {
preset: 'martin [shadow harlequins shape code] - fata morgana',
renderingFrequencyInMs: 500,
blendTimeInSeconds: 2.7,
},
vudio: {
effect: 'lighting',
accuracy: 128,
lighting: {
maxHeight: 160,
maxSize: 12,
lineWidth: 1,
color: '#49f3f7',
shadowBlur: 2,
shadowColor: 'rgba(244,244,244,.5)',
fadeSide: true,
prettify: false,
horizontalAlign: 'center',
verticalAlign: 'middle',
dottify: true,
},
},
wave: {
animations: [
{
type: 'Cubes',
config: {
bottom: true,
count: 30,
cubeHeight: 5,
fillColor: { gradient: ['#FAD961', '#F76B1C'] },
lineColor: 'rgba(0,0,0,0)',
radius: 20,
},
},
{
type: 'Cubes',
config: {
top: true,
count: 12,
cubeHeight: 5,
fillColor: { gradient: ['#FAD961', '#F76B1C'] },
lineColor: 'rgba(0,0,0,0)',
radius: 10,
},
},
{
type: 'Circles',
config: {
lineColor: {
gradient: ['#FAD961', '#FAD961', '#F76B1C'],
rotate: 90,
},
lineWidth: 4,
diameter: 20,
count: 10,
frequencyBand: 'base',
},
},
],
},
} as VisualizerPluginConfig,
styles: [emptyStyle],
});
export default builder;
declare global {
interface PluginBuilderList {
[builder.id]: typeof builder;
}
}

View File

@ -1,9 +0,0 @@
import { BrowserWindow } from 'electron';
import emptyPlayerStyle from './empty-player.css';
import { injectCSS } from '../utils/main';
export default (win: BrowserWindow) => {
injectCSS(win.webContents, emptyPlayerStyle);
};

View File

@ -1,23 +1,21 @@
import { BrowserWindow } from 'electron';
import builder from './index';
import { MenuTemplate } from '../../menu';
import { setMenuOptions } from '../../config/plugins';
const visualizerTypes = ['butterchurn', 'vudio', 'wave'] as const; // For bundling
import type { ConfigType } from '../../config/dynamic';
export default builder.createMenu(async ({ getConfig, setConfig }) => {
const config = await getConfig();
const visualizerTypes = ['butterchurn', 'vudio', 'wave']; // For bundling
export default (win: BrowserWindow, options: ConfigType<'visualizer'>): MenuTemplate => [
{
label: 'Type',
submenu: visualizerTypes.map((visualizerType) => ({
label: visualizerType,
type: 'radio',
checked: options.type === visualizerType,
click() {
options.type = visualizerType;
setMenuOptions('visualizer', options);
},
})),
},
];
return [
{
label: 'Type',
submenu: visualizerTypes.map((visualizerType) => ({
label: visualizerType,
type: 'radio',
checked: config.type === visualizerType,
click() {
setConfig({ type: visualizerType });
},
})),
},
];
});

View File

@ -1,83 +1,82 @@
import { ButterchurnVisualizer as butterchurn, WaveVisualizer as wave, VudioVisualizer as vudio } from './visualizers';
import { Visualizer } from './visualizers/visualizer';
import defaultConfig from '../../config/defaults';
import builder from './index';
import type { ConfigType } from '../../config/dynamic';
export default builder.createRenderer(({ getConfig }) => {
return {
async onLoad() {
const config = await getConfig();
export default (options: ConfigType<'visualizer'>) => {
const optionsWithDefaults = {
...defaultConfig.plugins.visualizer,
...options,
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let visualizerType: { new(...args: any[]): Visualizer<unknown> } = vudio;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let visualizerType: { new(...args: any[]): Visualizer<unknown> } = vudio;
if (optionsWithDefaults.type === 'wave') {
visualizerType = wave;
} else if (optionsWithDefaults.type === 'butterchurn') {
visualizerType = butterchurn;
}
document.addEventListener(
'audioCanPlay',
(e) => {
const video = document.querySelector<HTMLVideoElement & { captureStream(): MediaStream; }>('video');
if (!video) {
return;
if (config.type === 'wave') {
visualizerType = wave;
} else if (config.type === 'butterchurn') {
visualizerType = butterchurn;
}
const visualizerContainer = document.querySelector<HTMLElement>('#player');
if (!visualizerContainer) {
return;
}
document.addEventListener(
'audioCanPlay',
(e) => {
const video = document.querySelector<HTMLVideoElement & { captureStream(): MediaStream; }>('video');
if (!video) {
return;
}
let canvas = document.querySelector<HTMLCanvasElement>('#visualizer');
if (!canvas) {
canvas = document.createElement('canvas');
canvas.id = 'visualizer';
visualizerContainer?.prepend(canvas);
}
const visualizerContainer = document.querySelector<HTMLElement>('#player');
if (!visualizerContainer) {
return;
}
const resizeCanvas = () => {
if (canvas) {
canvas.width = visualizerContainer.clientWidth;
canvas.height = visualizerContainer.clientHeight;
}
};
let canvas = document.querySelector<HTMLCanvasElement>('#visualizer');
if (!canvas) {
canvas = document.createElement('canvas');
canvas.id = 'visualizer';
visualizerContainer?.prepend(canvas);
}
resizeCanvas();
const resizeCanvas = () => {
if (canvas) {
canvas.width = visualizerContainer.clientWidth;
canvas.height = visualizerContainer.clientHeight;
}
};
const gainNode = e.detail.audioContext.createGain();
gainNode.gain.value = 1.25;
e.detail.audioSource.connect(gainNode);
resizeCanvas();
const visualizer = new visualizerType(
e.detail.audioContext,
e.detail.audioSource,
visualizerContainer,
canvas,
gainNode,
video.captureStream(),
optionsWithDefaults,
const gainNode = e.detail.audioContext.createGain();
gainNode.gain.value = 1.25;
e.detail.audioSource.connect(gainNode);
const visualizer = new visualizerType(
e.detail.audioContext,
e.detail.audioSource,
visualizerContainer,
canvas,
gainNode,
video.captureStream(),
config,
);
const resizeVisualizer = (width: number, height: number) => {
resizeCanvas();
visualizer.resize(width, height);
};
resizeVisualizer(canvas.width, canvas.height);
const visualizerContainerObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
resizeVisualizer(entry.contentRect.width, entry.contentRect.height);
}
});
visualizerContainerObserver.observe(visualizerContainer);
visualizer.render();
},
{ passive: true },
);
const resizeVisualizer = (width: number, height: number) => {
resizeCanvas();
visualizer.resize(width, height);
};
resizeVisualizer(canvas.width, canvas.height);
const visualizerContainerObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
resizeVisualizer(entry.contentRect.width, entry.contentRect.height);
}
});
visualizerContainerObserver.observe(visualizerContainer);
visualizer.render();
},
{ passive: true },
);
};
};
});

View File

@ -3,7 +3,7 @@ import ButterchurnPresets from 'butterchurn-presets';
import { Visualizer } from './visualizer';
import type { ConfigType } from '../../../config/dynamic';
import type { VisualizerPluginConfig } from '../index';
class ButterchurnVisualizer extends Visualizer<Butterchurn> {
name = 'butterchurn';
@ -18,7 +18,7 @@ class ButterchurnVisualizer extends Visualizer<Butterchurn> {
canvas: HTMLCanvasElement,
audioNode: GainNode,
stream: MediaStream,
options: ConfigType<'visualizer'>,
options: VisualizerPluginConfig,
) {
super(
audioContext,

View File

@ -1,4 +1,4 @@
import type { ConfigType } from '../../../config/dynamic';
import type { VisualizerPluginConfig } from '../index';
export abstract class Visualizer<T> {
/**
@ -14,7 +14,7 @@ export abstract class Visualizer<T> {
_canvas: HTMLCanvasElement,
_audioNode: GainNode,
_stream: MediaStream,
_options: ConfigType<'visualizer'>,
_options: VisualizerPluginConfig,
) {}
abstract resize(width: number, height: number): void;

View File

@ -2,7 +2,7 @@ import Vudio from 'vudio/umd/vudio';
import { Visualizer } from './visualizer';
import type { ConfigType } from '../../../config/dynamic';
import type { VisualizerPluginConfig } from '../index';
class VudioVisualizer extends Visualizer<Vudio> {
name = 'vudio';
@ -16,7 +16,7 @@ class VudioVisualizer extends Visualizer<Vudio> {
canvas: HTMLCanvasElement,
audioNode: GainNode,
stream: MediaStream,
options: ConfigType<'visualizer'>,
options: VisualizerPluginConfig,
) {
super(
audioContext,

View File

@ -2,8 +2,7 @@ import { Wave } from '@foobar404/wave';
import { Visualizer } from './visualizer';
import type { ConfigType } from '../../../config/dynamic';
import type { VisualizerPluginConfig } from '../index';
class WaveVisualizer extends Visualizer<Wave> {
name = 'wave';
@ -16,7 +15,7 @@ class WaveVisualizer extends Visualizer<Wave> {
canvas: HTMLCanvasElement,
audioNode: GainNode,
stream: MediaStream,
options: ConfigType<'visualizer'>,
options: VisualizerPluginConfig,
) {
super(
audioContext,