mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 18:41:47 +00:00
feat: apply rollup 🚀 (#20)
Co-authored-by: Su-Yong <simssy2205@gmail.com>
This commit is contained in:
@ -1,8 +1,9 @@
|
||||
// Used for caching
|
||||
import path from 'node:path';
|
||||
import { promises } from 'node:fs';
|
||||
import fs, { promises } from 'node:fs';
|
||||
|
||||
import { ElectronBlocker } from '@cliqz/adblocker-electron';
|
||||
import { app } from 'electron';
|
||||
|
||||
const SOURCES = [
|
||||
'https://raw.githubusercontent.com/kbinani/adblock-youtube-ads/master/signed.txt',
|
||||
@ -23,10 +24,19 @@ export const loadAdBlockerEngine = (
|
||||
disableDefaultLists: boolean | string[] = false,
|
||||
) => {
|
||||
// Only use cache if no additional blocklists are passed
|
||||
let cacheDirectory: string;
|
||||
if (app.isPackaged) {
|
||||
cacheDirectory = path.join(app.getPath('userData'), 'cache');
|
||||
} else {
|
||||
cacheDirectory = path.resolve(__dirname, 'cache');
|
||||
}
|
||||
if (!fs.existsSync(cacheDirectory)) {
|
||||
fs.mkdirSync(cacheDirectory);
|
||||
}
|
||||
const cachingOptions
|
||||
= cache && additionalBlockLists.length === 0
|
||||
? {
|
||||
path: path.resolve(__dirname, 'ad-blocker-engine.bin'),
|
||||
path: path.join(cacheDirectory, 'adblocker-engine.bin'),
|
||||
read: promises.readFile,
|
||||
write: promises.writeFile,
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
export default () => {
|
||||
require('@cliqz/adblocker-electron-preload');
|
||||
const path = require.resolve('@cliqz/adblocker-electron-preload'); // prevent require hoisting
|
||||
require(path);
|
||||
};
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { getAverageColor } from 'fast-average-color-node';
|
||||
import { BrowserWindow } from 'electron';
|
||||
|
||||
import style from './style.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
import registerCallback from '../../providers/song-info';
|
||||
|
||||
|
||||
export default (win: BrowserWindow) => {
|
||||
injectCSS(win.webContents, path.join(__dirname, 'style.css'));
|
||||
injectCSS(win.webContents, style);
|
||||
|
||||
registerCallback((songInfo) => {
|
||||
const songTitle = songInfo.title;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { BrowserWindow } from 'electron';
|
||||
|
||||
import style from './style.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
|
||||
export default (win: BrowserWindow) => {
|
||||
injectCSS(win.webContents, path.join(__dirname, 'style.css'));
|
||||
injectCSS(win.webContents, style);
|
||||
};
|
||||
|
||||
@ -5,7 +5,9 @@ import { ipcRenderer } from 'electron';
|
||||
|
||||
import configProvider from './config';
|
||||
|
||||
import { ElementFromFile, templatePath } from '../utils';
|
||||
import CaptionsSettingsButtonHTML from './templates/captions-settings-template.html';
|
||||
|
||||
import { ElementFromHtml } from '../utils';
|
||||
import { YoutubePlayer } from '../../types/youtube-player';
|
||||
|
||||
import type { ConfigType } from '../../config/dynamic';
|
||||
@ -27,9 +29,7 @@ let config: ConfigType<'captions-selector'>;
|
||||
|
||||
const $ = <Element extends HTMLElement>(selector: string): Element => document.querySelector(selector)!;
|
||||
|
||||
const captionsSettingsButton = ElementFromFile(
|
||||
templatePath(__dirname, 'captions-settings-template.html'),
|
||||
);
|
||||
const captionsSettingsButton = ElementFromHtml(CaptionsSettingsButtonHTML);
|
||||
|
||||
export default async () => {
|
||||
// RENDERER
|
||||
|
||||
@ -24,6 +24,8 @@ import { cropMaxWidth, getFolder, presets, sendFeedback as sendFeedback_, setBad
|
||||
|
||||
import config from './config';
|
||||
|
||||
import style from './style.css';
|
||||
|
||||
import { fetchFromGenius } from '../lyrics-genius/back';
|
||||
import { isEnabled } from '../../config/plugins';
|
||||
import { cleanupName, getImage, SongInfo } from '../../providers/song-info';
|
||||
@ -32,6 +34,7 @@ import { cache } from '../../providers/decorators';
|
||||
|
||||
import type { GetPlayerResponse } from '../../types/get-player-response';
|
||||
|
||||
|
||||
type CustomSongInfo = SongInfo & { trackId?: string };
|
||||
|
||||
const ffmpeg = createFFmpeg({
|
||||
@ -68,7 +71,7 @@ const sendError = (error: Error, source?: string) => {
|
||||
|
||||
export default async (win_: BrowserWindow) => {
|
||||
win = win_;
|
||||
injectCSS(win.webContents, join(__dirname, 'style.css'));
|
||||
injectCSS(win.webContents, style);
|
||||
|
||||
const cookie = (await win.webContents.session.cookies.get({ url: 'https://music.youtube.com' })).map((it) =>
|
||||
it.name + '=' + it.value + ';'
|
||||
@ -104,6 +107,7 @@ export async function downloadSong(
|
||||
increasePlaylistProgress,
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
console.log('maybe?????', error);
|
||||
sendError(error as Error, resolvedName || url);
|
||||
}
|
||||
}
|
||||
@ -316,6 +320,7 @@ async function iterableStreamToMP3(
|
||||
ffmpeg.FS('unlink', `${safeVideoName}.mp3`);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.log('maybe?', error);
|
||||
sendError(error as Error, safeVideoName);
|
||||
} finally {
|
||||
releaseFFmpegMutex();
|
||||
@ -368,6 +373,7 @@ async function writeID3(buffer: Buffer, metadata: CustomSongInfo, sendFeedback:
|
||||
|
||||
return NodeID3.write(tags, buffer);
|
||||
} catch (error: unknown) {
|
||||
console.log('fallback', error);
|
||||
sendError(error as Error, `${metadata.artist} - ${metadata.title}`);
|
||||
return null;
|
||||
}
|
||||
@ -482,6 +488,7 @@ export async function downloadPlaylist(givenUrl?: string | URL) {
|
||||
counter++;
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.log('also?', error);
|
||||
sendError(error as Error);
|
||||
} finally {
|
||||
win.setProgressBar(-1); // Close progress bar
|
||||
@ -507,6 +514,7 @@ async function ffmpegWriteTags(filePath: string, metadata: CustomSongInfo, prese
|
||||
filePath,
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
console.log('ffmpeg?', error);
|
||||
sendError(error as Error);
|
||||
} finally {
|
||||
releaseFFmpegMutex();
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
import downloadHTML from './templates/download.html';
|
||||
|
||||
import defaultConfig from '../../config/defaults';
|
||||
import { getSongMenu } from '../../providers/dom-elements';
|
||||
import { ElementFromFile, templatePath } from '../utils';
|
||||
import { ElementFromHtml } from '../utils';
|
||||
import { getSongInfo } from '../../providers/song-info-front';
|
||||
|
||||
let menu: Element | null = null;
|
||||
let progress: Element | null = null;
|
||||
const downloadButton = ElementFromFile(
|
||||
templatePath(__dirname, 'download.html'),
|
||||
);
|
||||
const downloadButton = ElementFromHtml(downloadHTML);
|
||||
|
||||
let doneFirstLoad = false;
|
||||
|
||||
|
||||
@ -4,11 +4,13 @@ import { register } from 'electron-localshortcut';
|
||||
|
||||
import { BrowserWindow, Menu, MenuItem, ipcMain } from 'electron';
|
||||
|
||||
import titlebarStyle from './titlebar.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
|
||||
// Tracks menu visibility
|
||||
export default (win: BrowserWindow) => {
|
||||
injectCSS(win.webContents, path.join(__dirname, 'titlebar.css'));
|
||||
injectCSS(win.webContents, titlebarStyle);
|
||||
|
||||
win.once('ready-to-show', () => {
|
||||
register(win, '`', () => {
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { ipcRenderer, Menu } from 'electron';
|
||||
|
||||
import { createPanel } from './menu/panel';
|
||||
|
||||
import { ElementFromFile } from '../utils';
|
||||
import logo from '../../assets/youtube-music.svg';
|
||||
import { isEnabled } from '../../config/plugins';
|
||||
|
||||
function $<E extends Element = Element>(selector: string) {
|
||||
@ -18,7 +16,6 @@ export default () => {
|
||||
const navBar = document.querySelector<HTMLDivElement>('#nav-bar-background');
|
||||
if (isMacOS) titleBar.style.setProperty('--offset-left', '70px');
|
||||
|
||||
const logo = ElementFromFile(path.join(__dirname, '..' , '..' , 'assets', 'youtube-music.svg'));
|
||||
logo.classList.add('title-bar-icon');
|
||||
|
||||
if (!isMacOS) titleBar.appendChild(logo);
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { join } from 'node:path';
|
||||
|
||||
import { BrowserWindow, ipcMain, net } from 'electron';
|
||||
import is from 'electron-is';
|
||||
import { convert } from 'html-to-text';
|
||||
|
||||
import style from './style.css';
|
||||
import { GetGeniusLyric } from './types';
|
||||
|
||||
import { cleanupName, SongInfo } from '../../providers/song-info';
|
||||
@ -22,7 +21,7 @@ export default (win: BrowserWindow, options: LyricGeniusType) => {
|
||||
revRomanized = true;
|
||||
}
|
||||
|
||||
injectCSS(win.webContents, join(__dirname, 'style.css'));
|
||||
injectCSS(win.webContents, style);
|
||||
|
||||
ipcMain.handle('search-genius-lyrics', async (_, extractedSongInfo: SongInfo) => {
|
||||
const metadata = extractedSongInfo;
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { BrowserWindow } from 'electron';
|
||||
|
||||
import style from './style.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
|
||||
export function handle(win: BrowserWindow) {
|
||||
injectCSS(win.webContents, path.join(__dirname, 'style.css'), () => {
|
||||
injectCSS(win.webContents, style, () => {
|
||||
win.webContents.send('navigation-css-ready');
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
import { ElementFromFile, templatePath } from '../utils';
|
||||
import forwardHTML from './templates/forward.html';
|
||||
import backHTML from './templates/back.html';
|
||||
|
||||
import { ElementFromHtml } from '../utils';
|
||||
|
||||
export function run() {
|
||||
ipcRenderer.on('navigation-css-ready', () => {
|
||||
const forwardButton = ElementFromFile(
|
||||
templatePath(__dirname, 'forward.html'),
|
||||
);
|
||||
const backButton = ElementFromFile(templatePath(__dirname, 'back.html'));
|
||||
const forwardButton = ElementFromHtml(forwardHTML);
|
||||
const backButton = ElementFromHtml(backHTML);
|
||||
const menu = document.querySelector('#right-content');
|
||||
|
||||
if (menu) {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { BrowserWindow } from 'electron';
|
||||
|
||||
import style from './style.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
|
||||
export default (win: BrowserWindow) => {
|
||||
injectCSS(win.webContents, path.join(__dirname, 'style.css'));
|
||||
injectCSS(win.webContents, style);
|
||||
};
|
||||
|
||||
@ -154,7 +154,7 @@ const getXml = (songInfo: SongInfo, iconSrc: string) => {
|
||||
|
||||
const iconLocation = app.isPackaged
|
||||
? path.resolve(app.getPath('userData'), 'icons')
|
||||
: path.resolve(__dirname, '..', '..', 'assets/media-icons-black');
|
||||
: path.resolve(__dirname, 'assets', 'media-icons-black');
|
||||
|
||||
const display = (kind: keyof typeof icons) => {
|
||||
if (config.get('toastStyle') === ToastStyles.legacy) {
|
||||
|
||||
@ -88,7 +88,7 @@ export const saveTempIcon = () => {
|
||||
continue;
|
||||
}
|
||||
|
||||
const iconPath = path.resolve(__dirname, '../../assets/media-icons-black', `${kind}.png`);
|
||||
const iconPath = path.resolve(__dirname, 'assets', 'media-icons-black', `${kind}.png`);
|
||||
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
||||
fs.copyFile(iconPath, destinationPath, () => {
|
||||
});
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { app, BrowserWindow, ipcMain } from 'electron';
|
||||
|
||||
import { setOptions as setPluginOptions } from '../../config/plugins';
|
||||
import style from './style.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
import { setOptions as setPluginOptions } from '../../config/plugins';
|
||||
|
||||
import type { ConfigType } from '../../config/dynamic';
|
||||
|
||||
@ -102,7 +102,7 @@ export default (_win: BrowserWindow, _options: PiPOptions) => {
|
||||
options ??= _options;
|
||||
win ??= _win;
|
||||
setLocalOptions({ isInPiP });
|
||||
injectCSS(win.webContents, path.join(__dirname, 'style.css'));
|
||||
injectCSS(win.webContents, style);
|
||||
ipcMain.on('picture-in-picture', () => {
|
||||
togglePiP();
|
||||
});
|
||||
|
||||
@ -2,9 +2,11 @@ import { ipcRenderer } from 'electron';
|
||||
import { toKeyEvent } from 'keyboardevent-from-electron-accelerator';
|
||||
import keyEventAreEqual from 'keyboardevents-areequal';
|
||||
|
||||
import pipHTML from './templates/picture-in-picture.html';
|
||||
|
||||
import { getSongMenu } from '../../providers/dom-elements';
|
||||
|
||||
import { ElementFromFile, templatePath } from '../utils';
|
||||
import { ElementFromHtml } from '../utils';
|
||||
|
||||
import type { ConfigType } from '../../config/dynamic';
|
||||
|
||||
@ -16,9 +18,7 @@ function $<E extends Element = Element>(selector: string) {
|
||||
|
||||
let useNativePiP = false;
|
||||
let menu: Element | null = null;
|
||||
const pipButton = ElementFromFile(
|
||||
templatePath(__dirname, 'picture-in-picture.html'),
|
||||
);
|
||||
const pipButton = ElementFromHtml(pipHTML);
|
||||
|
||||
// Will also clone
|
||||
function replaceButton(query: string, button: Element) {
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import sliderHTML from './templates/slider.html';
|
||||
|
||||
import { getSongMenu } from '../../providers/dom-elements';
|
||||
import { ElementFromFile, templatePath } from '../utils';
|
||||
import { ElementFromHtml } from '../utils';
|
||||
import { singleton } from '../../providers/decorators';
|
||||
|
||||
|
||||
@ -7,7 +9,7 @@ function $<E extends Element = Element>(selector: string) {
|
||||
return document.querySelector<E>(selector);
|
||||
}
|
||||
|
||||
const slider = ElementFromFile(templatePath(__dirname, 'slider.html'));
|
||||
const slider = ElementFromHtml(sliderHTML);
|
||||
|
||||
const roundToTwo = (n: number) => Math.round(n * 1e2) / 1e2;
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { globalShortcut, BrowserWindow } from 'electron';
|
||||
|
||||
import volumeHudStyle from './volume-hud.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
|
||||
import type { ConfigType } from '../../config/dynamic';
|
||||
@ -16,7 +16,7 @@ export const enabled = () => isEnabled;
|
||||
|
||||
export default (win: BrowserWindow, options: ConfigType<'precise-volume'>) => {
|
||||
isEnabled = true;
|
||||
injectCSS(win.webContents, path.join(__dirname, 'volume-hud.css'));
|
||||
injectCSS(win.webContents, volumeHudStyle);
|
||||
|
||||
if (options.globalShortcuts?.volumeUp) {
|
||||
globalShortcut.register((options.globalShortcuts.volumeUp), () => win.webContents.send('changeVolume', true));
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
import { ElementFromFile, templatePath } from '../utils';
|
||||
import qualitySettingsTemplate from './templates/qualitySettingsTemplate.html';
|
||||
|
||||
import { ElementFromHtml } from '../utils';
|
||||
import { YoutubePlayer } from '../../types/youtube-player';
|
||||
|
||||
function $(selector: string): HTMLElement | null {
|
||||
return document.querySelector(selector);
|
||||
}
|
||||
|
||||
const qualitySettingsButton = ElementFromFile(
|
||||
templatePath(__dirname, 'qualitySettingsTemplate.html'),
|
||||
);
|
||||
const qualitySettingsButton = ElementFromHtml(qualitySettingsTemplate);
|
||||
|
||||
function setup(event: CustomEvent<YoutubePlayer>) {
|
||||
const api = event.detail;
|
||||
|
||||
@ -49,18 +49,32 @@ export const fileExists = (
|
||||
});
|
||||
};
|
||||
|
||||
const cssToInject = new Map();
|
||||
export const injectCSS = (webContents: Electron.WebContents, filepath: unknown, cb: (() => void) | undefined = undefined) => {
|
||||
if (cssToInject.size === 0) {
|
||||
const cssToInject = new Map<string, (() => void) | undefined>();
|
||||
const cssToInjectFile = new Map<string, (() => void) | undefined>();
|
||||
export const injectCSS = (webContents: Electron.WebContents, css: string, cb: (() => void) | undefined = undefined) => {
|
||||
if (cssToInject.size === 0 && cssToInjectFile.size === 0) {
|
||||
setupCssInjection(webContents);
|
||||
}
|
||||
|
||||
cssToInject.set(filepath, cb);
|
||||
cssToInject.set(css, cb);
|
||||
};
|
||||
|
||||
export const injectCSSAsFile = (webContents: Electron.WebContents, filepath: string, cb: (() => void) | undefined = undefined) => {
|
||||
if (cssToInject.size === 0 && cssToInjectFile.size === 0) {
|
||||
setupCssInjection(webContents);
|
||||
}
|
||||
|
||||
cssToInjectFile.set(filepath, cb);
|
||||
};
|
||||
|
||||
const setupCssInjection = (webContents: Electron.WebContents) => {
|
||||
webContents.on('did-finish-load', () => {
|
||||
cssToInject.forEach(async (callback: () => void | undefined, filepath: fs.PathOrFileDescriptor) => {
|
||||
cssToInject.forEach(async (callback, css) => {
|
||||
await webContents.insertCSS(css);
|
||||
callback?.();
|
||||
});
|
||||
|
||||
cssToInjectFile.forEach(async (callback, filepath) => {
|
||||
await webContents.insertCSS(fs.readFileSync(filepath, 'utf8'));
|
||||
callback?.();
|
||||
});
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { BrowserWindow } from 'electron';
|
||||
|
||||
import forceHideStyle from './force-hide.css';
|
||||
import buttonSwitcherStyle from './button-switcher.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
|
||||
import type { ConfigType } from '../../config/dynamic';
|
||||
|
||||
export default (win: BrowserWindow, options: ConfigType<'video-toggle'>) => {
|
||||
if (options.forceHide) {
|
||||
injectCSS(win.webContents, path.join(__dirname, 'force-hide.css'));
|
||||
injectCSS(win.webContents, forceHideStyle);
|
||||
} else if (!options.mode || options.mode === 'custom') {
|
||||
injectCSS(win.webContents, path.join(__dirname, 'button-switcher.css'));
|
||||
injectCSS(win.webContents, buttonSwitcherStyle);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import { ElementFromFile, templatePath } from '../utils';
|
||||
import buttonTemplate from './templates/button_template.html';
|
||||
|
||||
import { ElementFromHtml } from '../utils';
|
||||
import { setOptions, isEnabled } from '../../config/plugins';
|
||||
|
||||
import { moveVolumeHud as preciseVolumeMoveVolumeHud } from '../precise-volume/front';
|
||||
@ -19,9 +21,7 @@ let player: HTMLElement & { videoMode_: boolean } | null;
|
||||
let video: HTMLVideoElement | null;
|
||||
let api: YoutubePlayer;
|
||||
|
||||
const switchButtonDiv = ElementFromFile(
|
||||
templatePath(__dirname, 'button_template.html'),
|
||||
);
|
||||
const switchButtonDiv = ElementFromHtml(buttonTemplate);
|
||||
|
||||
export default (_options: ConfigType<'video-toggle'>) => {
|
||||
if (_options.forceHide) {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { BrowserWindow } from 'electron';
|
||||
|
||||
import emptyPlayerStyle from './empty-player.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
|
||||
export default (win: BrowserWindow) => {
|
||||
injectCSS(win.webContents, path.join(__dirname, 'empty-player.css'));
|
||||
injectCSS(win.webContents, emptyPlayerStyle);
|
||||
};
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
import { ButterchurnVisualizer as butterchurn, WaveVisualizer as wave, VudioVisualizer as vudio } from './visualizers';
|
||||
import { Visualizer } from './visualizers/visualizer';
|
||||
|
||||
import vudio from './visualizers/vudio';
|
||||
import wave from './visualizers/wave';
|
||||
import butterchurn from './visualizers/butterchurn';
|
||||
|
||||
import defaultConfig from '../../config/defaults';
|
||||
|
||||
import type { ConfigType } from '../../config/dynamic';
|
||||
|
||||
@ -1,17 +1,11 @@
|
||||
import { readdirSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
import { BrowserWindow } from 'electron';
|
||||
|
||||
import { setMenuOptions } from '../../config/plugins';
|
||||
|
||||
import { MenuTemplate } from '../../menu';
|
||||
import { setMenuOptions } from '../../config/plugins';
|
||||
|
||||
import type { ConfigType } from '../../config/dynamic';
|
||||
|
||||
const visualizerTypes = readdirSync(path.join(__dirname, 'visualizers'))
|
||||
.map((filename) => path.parse(filename).name)
|
||||
.filter((filename) => filename !== 'visualizer');
|
||||
const visualizerTypes = ['butterchurn', 'vudio', 'wave']; // For bundling
|
||||
|
||||
export default (win: BrowserWindow, options: ConfigType<'visualizer'>): MenuTemplate => [
|
||||
{
|
||||
|
||||
@ -8,6 +8,8 @@ import { ConfigType } from '../../../config/dynamic';
|
||||
const presets = ButterchurnPresets.getPresets();
|
||||
|
||||
class ButterchurnVisualizer extends Visualizer<Butterchurn> {
|
||||
name = 'butterchurn';
|
||||
|
||||
visualizer: ReturnType<typeof Butterchurn.createVisualizer>;
|
||||
private readonly renderingFrequencyInMs: number;
|
||||
|
||||
|
||||
5
plugins/visualizer/visualizers/index.ts
Normal file
5
plugins/visualizer/visualizers/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import ButterchurnVisualizer from './butterchurn';
|
||||
import VudioVisualizer from './vudio';
|
||||
import WaveVisualizer from './wave';
|
||||
|
||||
export { ButterchurnVisualizer, VudioVisualizer, WaveVisualizer };
|
||||
@ -1,6 +1,10 @@
|
||||
import type { ConfigType } from '../../../config/dynamic';
|
||||
|
||||
export abstract class Visualizer<T> {
|
||||
/**
|
||||
* The name must be the same as the file name.
|
||||
*/
|
||||
abstract name: string;
|
||||
abstract visualizer: T;
|
||||
|
||||
protected constructor(
|
||||
|
||||
@ -5,6 +5,8 @@ import { Visualizer } from './visualizer';
|
||||
import type { ConfigType } from '../../../config/dynamic';
|
||||
|
||||
class VudioVisualizer extends Visualizer<Vudio> {
|
||||
name = 'vudio';
|
||||
|
||||
visualizer: Vudio;
|
||||
|
||||
constructor(
|
||||
|
||||
@ -5,6 +5,8 @@ import { Visualizer } from './visualizer';
|
||||
import type { ConfigType } from '../../../config/dynamic';
|
||||
|
||||
class WaveVisualizer extends Visualizer<Wave> {
|
||||
name = 'wave';
|
||||
|
||||
visualizer: Wave;
|
||||
|
||||
constructor(
|
||||
|
||||
Reference in New Issue
Block a user