From ff80b6bca8b20ae42f2690851c00847134960ad5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:21:25 +0900 Subject: [PATCH 1/4] chore(deps): update dependency electron to v38.8.0 (#4285) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 38 +++++++++++++++++++------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index f0e14ecb6..ae4dd4a5c 100644 --- a/package.json +++ b/package.json @@ -154,7 +154,7 @@ "cross-env": "10.1.0", "del-cli": "6.0.0", "discord-api-types": "0.38.37", - "electron": "38.7.2", + "electron": "38.8.0", "electron-builder": "26.4.0", "electron-builder-squirrel-windows": "26.4.0", "electron-devtools-installer": "4.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9e73be16b..242b35fc7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,7 +44,7 @@ importers: version: 1.0.1(@types/node@24.3.0) '@electron/remote': specifier: 2.1.3 - version: 2.1.3(electron@38.7.2) + version: 2.1.3(electron@38.8.0) '@ffmpeg.wasm/core-mt': specifier: 0.12.0 version: 0.12.0 @@ -59,10 +59,10 @@ importers: version: 2.0.5 '@ghostery/adblocker-electron': specifier: 2.13.4 - version: 2.13.4(electron@38.7.2) + version: 2.13.4(electron@38.8.0) '@ghostery/adblocker-electron-preload': specifier: 2.13.4 - version: 2.13.4(electron@38.7.2) + version: 2.13.4(electron@38.8.0) '@hono/node-server': specifier: 1.19.9 version: 1.19.9(hono@4.11.7) @@ -119,7 +119,7 @@ importers: version: 14.0.0 custom-electron-prompt: specifier: 1.6.1 - version: 1.6.1(electron@38.7.2) + version: 1.6.1(electron@38.8.0) deepmerge-ts: specifier: 7.1.5 version: 7.1.5 @@ -302,8 +302,8 @@ importers: specifier: 0.38.37 version: 0.38.37 electron: - specifier: 38.7.2 - version: 38.7.2 + specifier: 38.8.0 + version: 38.8.0 electron-builder: specifier: 26.4.0 version: 26.4.0(electron-builder-squirrel-windows@26.4.0) @@ -2267,8 +2267,8 @@ packages: resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==} engines: {node: '>=8.0.0'} - electron@38.7.2: - resolution: {integrity: sha512-BcjR0IHqp3uv4ytVQwW2/9zAWo17Rjwrydn6RS+g+vqhpcPTzmBHDCHKaEcqheSl/7zzKPgFZdvT21BoSfrxRQ==} + electron@38.8.0: + resolution: {integrity: sha512-cMvNuIFdXXDr4pJd5xD+F8FDjQhin9vyJ5hGSb5cZRIUb1VqfvRyP2nLBzU2Iazj0z3L686AUOkykp6Xd8bT1w==} engines: {node: '>= 12.20.55'} hasBin: true @@ -5055,9 +5055,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@electron/remote@2.1.3(electron@38.7.2)': + '@electron/remote@2.1.3(electron@38.8.0)': dependencies: - electron: 38.7.2 + electron: 38.8.0 '@electron/universal@3.0.2': dependencies: @@ -5248,16 +5248,16 @@ snapshots: dependencies: '@ghostery/adblocker-extended-selectors': 2.13.4 - '@ghostery/adblocker-electron-preload@2.13.4(electron@38.7.2)': + '@ghostery/adblocker-electron-preload@2.13.4(electron@38.8.0)': dependencies: '@ghostery/adblocker-content': 2.13.4 - electron: 38.7.2 + electron: 38.8.0 - '@ghostery/adblocker-electron@2.13.4(electron@38.7.2)': + '@ghostery/adblocker-electron@2.13.4(electron@38.8.0)': dependencies: '@ghostery/adblocker': 2.13.4 - '@ghostery/adblocker-electron-preload': 2.13.4(electron@38.7.2) - electron: 38.7.2 + '@ghostery/adblocker-electron-preload': 2.13.4(electron@38.8.0) + electron: 38.8.0 tldts-experimental: 7.0.19 '@ghostery/adblocker-extended-selectors@2.13.4': {} @@ -5822,7 +5822,7 @@ snapshots: '@types/electron-localshortcut@3.1.3': dependencies: - electron: 38.7.2 + electron: 38.8.0 transitivePeerDependencies: - supports-color @@ -6603,9 +6603,9 @@ snapshots: csstype@3.1.3: {} - custom-electron-prompt@1.6.1(electron@38.7.2): + custom-electron-prompt@1.6.1(electron@38.8.0): optionalDependencies: - electron: 38.7.2 + electron: 38.8.0 data-uri-to-buffer@4.0.1: {} @@ -6931,7 +6931,7 @@ snapshots: transitivePeerDependencies: - supports-color - electron@38.7.2: + electron@38.8.0: dependencies: '@electron/get': 2.0.3 '@types/node': 22.17.2 From 2594d962728ade4c459685b8681efa69ef145614 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:21:34 +0900 Subject: [PATCH 2/4] fix(deps): update dependency solid-js to v1.9.11 (#4282) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 94 +++++++++++++++++++++++++------------------------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index ae4dd4a5c..263c6522a 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "socks": "2.8.7", "solid-element": "1.9.1", "solid-floating-ui": "0.3.1", - "solid-js": "1.9.10", + "solid-js": "1.9.11", "solid-styled-components": "0.28.5", "solid-transition-group": "0.3.0", "tiny-pinyin": "1.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 242b35fc7..d81d0b531 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -221,19 +221,19 @@ importers: version: 2.8.7 solid-element: specifier: 1.9.1 - version: 1.9.1(solid-js@1.9.10) + version: 1.9.1(solid-js@1.9.11) solid-floating-ui: specifier: 0.3.1 - version: 0.3.1(@floating-ui/dom@1.7.5)(solid-js@1.9.10) + version: 0.3.1(@floating-ui/dom@1.7.5)(solid-js@1.9.11) solid-js: - specifier: 1.9.10 - version: 1.9.10 + specifier: 1.9.11 + version: 1.9.11 solid-styled-components: specifier: 0.28.5 - version: 0.28.5(solid-js@1.9.10) + version: 0.28.5(solid-js@1.9.11) solid-transition-group: specifier: 0.3.0 - version: 0.3.0(solid-js@1.9.10) + version: 0.3.0(solid-js@1.9.11) tiny-pinyin: specifier: 1.3.2 version: 1.3.2 @@ -242,7 +242,7 @@ importers: version: 1.3.4 virtua: specifier: 0.48.5 - version: 0.48.5(solid-js@1.9.10) + version: 0.48.5(solid-js@1.9.11) vudio: specifier: 2.1.1 version: 2.1.1(patch_hash=0e06c2ed11c02bdc490c209fa80070e98517c2735c641f5738b6e15d7dc1959d) @@ -366,7 +366,7 @@ importers: version: 2.5.2 vite-plugin-solid: specifier: 2.11.10 - version: 2.11.10(rolldown-vite@7.3.1(@types/node@24.3.0)(jiti@2.6.1)(yaml@2.8.1))(solid-js@1.9.10) + version: 2.11.10(rolldown-vite@7.3.1(@types/node@24.3.0)(jiti@2.6.1)(yaml@2.8.1))(solid-js@1.9.11) ws: specifier: 8.19.0 version: 8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) @@ -4089,14 +4089,14 @@ packages: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} - seroval-plugins@1.3.2: - resolution: {integrity: sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ==} + seroval-plugins@1.5.0: + resolution: {integrity: sha512-EAHqADIQondwRZIdeW2I636zgsODzoBDwb3PT/+7TLDWyw1Dy/Xv7iGUIEXXav7usHDE9HVhOU61irI3EnyyHA==} engines: {node: '>=10'} peerDependencies: seroval: ^1.0 - seroval@1.3.2: - resolution: {integrity: sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==} + seroval@1.5.0: + resolution: {integrity: sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw==} engines: {node: '>=10'} serve-handler@6.1.6: @@ -4197,8 +4197,8 @@ packages: '@floating-ui/dom': ^1.5 solid-js: ^1.8 - solid-js@1.9.10: - resolution: {integrity: sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew==} + solid-js@1.9.11: + resolution: {integrity: sha512-WEJtcc5mkh/BnHA6Yrg4whlF8g6QwpmXXRg4P2ztPmcKeHHlH4+djYecBLhSpecZY2RRECXYUwIc/C2r3yzQ4Q==} solid-refresh@0.6.3: resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==} @@ -5746,18 +5746,18 @@ snapshots: '@skyra/jaro-winkler@1.1.1': {} - '@solid-primitives/refs@1.1.2(solid-js@1.9.10)': + '@solid-primitives/refs@1.1.2(solid-js@1.9.11)': dependencies: - '@solid-primitives/utils': 6.3.2(solid-js@1.9.10) - solid-js: 1.9.10 + '@solid-primitives/utils': 6.3.2(solid-js@1.9.11) + solid-js: 1.9.11 - '@solid-primitives/transition-group@1.1.2(solid-js@1.9.10)': + '@solid-primitives/transition-group@1.1.2(solid-js@1.9.11)': dependencies: - solid-js: 1.9.10 + solid-js: 1.9.11 - '@solid-primitives/utils@6.3.2(solid-js@1.9.10)': + '@solid-primitives/utils@6.3.2(solid-js@1.9.11)': dependencies: - solid-js: 1.9.10 + solid-js: 1.9.11 '@stylistic/eslint-plugin@5.7.1(eslint@9.39.2(jiti@2.6.1))': dependencies: @@ -6257,12 +6257,12 @@ snapshots: parse5: 7.3.0 validate-html-nesting: 1.2.3 - babel-preset-solid@1.9.9(@babel/core@7.28.3)(solid-js@1.9.10): + babel-preset-solid@1.9.9(@babel/core@7.28.3)(solid-js@1.9.11): dependencies: '@babel/core': 7.28.3 babel-plugin-jsx-dom-expressions: 0.40.1(@babel/core@7.28.3) optionalDependencies: - solid-js: 1.9.10 + solid-js: 1.9.11 balanced-match@1.0.2: {} @@ -8855,11 +8855,11 @@ snapshots: type-fest: 0.13.1 optional: true - seroval-plugins@1.3.2(seroval@1.3.2): + seroval-plugins@1.5.0(seroval@1.5.0): dependencies: - seroval: 1.3.2 + seroval: 1.5.0 - seroval@1.3.2: {} + seroval@1.5.0: {} serve-handler@6.1.6: dependencies: @@ -8985,42 +8985,42 @@ snapshots: ip-address: 10.0.1 smart-buffer: 4.2.0 - solid-element@1.9.1(solid-js@1.9.10): + solid-element@1.9.1(solid-js@1.9.11): dependencies: component-register: 0.8.7 - solid-js: 1.9.10 + solid-js: 1.9.11 - solid-floating-ui@0.3.1(@floating-ui/dom@1.7.5)(solid-js@1.9.10): + solid-floating-ui@0.3.1(@floating-ui/dom@1.7.5)(solid-js@1.9.11): dependencies: '@floating-ui/dom': 1.7.5 - solid-js: 1.9.10 + solid-js: 1.9.11 - solid-js@1.9.10: + solid-js@1.9.11: dependencies: csstype: 3.1.3 - seroval: 1.3.2 - seroval-plugins: 1.3.2(seroval@1.3.2) + seroval: 1.5.0 + seroval-plugins: 1.5.0(seroval@1.5.0) - solid-refresh@0.6.3(solid-js@1.9.10): + solid-refresh@0.6.3(solid-js@1.9.11): dependencies: '@babel/generator': 7.28.3 '@babel/helper-module-imports': 7.27.1 '@babel/types': 7.28.2 - solid-js: 1.9.10 + solid-js: 1.9.11 transitivePeerDependencies: - supports-color - solid-styled-components@0.28.5(solid-js@1.9.10): + solid-styled-components@0.28.5(solid-js@1.9.11): dependencies: csstype: 3.1.3 goober: 2.1.16(csstype@3.1.3) - solid-js: 1.9.10 + solid-js: 1.9.11 - solid-transition-group@0.3.0(solid-js@1.9.10): + solid-transition-group@0.3.0(solid-js@1.9.11): dependencies: - '@solid-primitives/refs': 1.1.2(solid-js@1.9.10) - '@solid-primitives/transition-group': 1.1.2(solid-js@1.9.10) - solid-js: 1.9.10 + '@solid-primitives/refs': 1.1.2(solid-js@1.9.11) + '@solid-primitives/transition-group': 1.1.2(solid-js@1.9.11) + solid-js: 1.9.11 source-map-js@1.2.1: {} @@ -9397,9 +9397,9 @@ snapshots: extsprintf: 1.4.1 optional: true - virtua@0.48.5(solid-js@1.9.10): + virtua@0.48.5(solid-js@1.9.11): optionalDependencies: - solid-js: 1.9.10 + solid-js: 1.9.11 vite-dev-rpc@1.1.0(rolldown-vite@7.3.1(@types/node@24.3.0)(jiti@2.6.1)(yaml@2.8.1)): dependencies: @@ -9430,14 +9430,14 @@ snapshots: dependencies: lib-esm: 0.4.2 - vite-plugin-solid@2.11.10(rolldown-vite@7.3.1(@types/node@24.3.0)(jiti@2.6.1)(yaml@2.8.1))(solid-js@1.9.10): + vite-plugin-solid@2.11.10(rolldown-vite@7.3.1(@types/node@24.3.0)(jiti@2.6.1)(yaml@2.8.1))(solid-js@1.9.11): dependencies: '@babel/core': 7.28.3 '@types/babel__core': 7.20.5 - babel-preset-solid: 1.9.9(@babel/core@7.28.3)(solid-js@1.9.10) + babel-preset-solid: 1.9.9(@babel/core@7.28.3)(solid-js@1.9.11) merge-anything: 5.1.7 - solid-js: 1.9.10 - solid-refresh: 0.6.3(solid-js@1.9.10) + solid-js: 1.9.11 + solid-refresh: 0.6.3(solid-js@1.9.11) vite: rolldown-vite@7.3.1(@types/node@24.3.0)(jiti@2.6.1)(yaml@2.8.1) vitefu: 1.1.1(rolldown-vite@7.3.1(@types/node@24.3.0)(jiti@2.6.1)(yaml@2.8.1)) transitivePeerDependencies: From 2180a2a8100cfa11696fcd55387dbe49d9069f2a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:21:41 +0900 Subject: [PATCH 3/4] chore(deps): update dependency @playwright/test to v1.58.0 (#4284) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 263c6522a..f02e93211 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "@electron-toolkit/tsconfig": "1.0.1", "@eslint/js": "9.39.2", "@malept/flatpak-bundler": "0.4.0", - "@playwright/test": "1.57.0", + "@playwright/test": "1.58.0", "@stylistic/eslint-plugin": "5.7.1", "@total-typescript/ts-reset": "0.6.1", "@types/electron-localshortcut": "3.1.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d81d0b531..583e21a24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -263,8 +263,8 @@ importers: specifier: 0.4.0 version: 0.4.0(patch_hash=c787371eeb2af011ea934e8818a0dad6d7dcb2df31bbb1686babc7231af0183c) '@playwright/test': - specifier: 1.57.0 - version: 1.57.0 + specifier: 1.58.0 + version: 1.58.0 '@stylistic/eslint-plugin': specifier: 5.7.1 version: 5.7.1(eslint@9.39.2(jiti@2.6.1)) @@ -1110,8 +1110,8 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@playwright/test@1.57.0': - resolution: {integrity: sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==} + '@playwright/test@1.58.0': + resolution: {integrity: sha512-fWza+Lpbj6SkQKCrU6si4iu+fD2dD3gxNHFhUPxsfXBPhnv3rRSQVd0NtBUT9Z/RhF/boCBcuUaMUSTRTopjZg==} engines: {node: '>=18'} hasBin: true @@ -3799,13 +3799,13 @@ packages: resolution: {integrity: sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==} hasBin: true - playwright-core@1.57.0: - resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==} + playwright-core@1.58.0: + resolution: {integrity: sha512-aaoB1RWrdNi3//rOeKuMiS65UCcgOVljU46At6eFcOFPFHWtd2weHRRow6z/n+Lec0Lvu0k9ZPKJSjPugikirw==} engines: {node: '>=18'} hasBin: true - playwright@1.57.0: - resolution: {integrity: sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==} + playwright@1.58.0: + resolution: {integrity: sha512-2SVA0sbPktiIY/MCOPX8e86ehA/e+tDNq+e5Y8qjKYti2Z/JG7xnronT/TXTIkKbYGWlCbuucZ6dziEgkoEjQQ==} engines: {node: '>=18'} hasBin: true @@ -5663,9 +5663,9 @@ snapshots: '@pkgr/core@0.2.9': {} - '@playwright/test@1.57.0': + '@playwright/test@1.58.0': dependencies: - playwright: 1.57.0 + playwright: 1.58.0 '@polka/url@1.0.0-next.29': {} @@ -8574,11 +8574,11 @@ snapshots: dependencies: pngjs: 6.0.0 - playwright-core@1.57.0: {} + playwright-core@1.58.0: {} - playwright@1.57.0: + playwright@1.58.0: dependencies: - playwright-core: 1.57.0 + playwright-core: 1.58.0 optionalDependencies: fsevents: 2.3.2 From 442dd51d3d0d1bcab04f6abd909b910e4692b801 Mon Sep 17 00:00:00 2001 From: Iris <244833241+its-iris@users.noreply.github.com> Date: Thu, 29 Jan 2026 09:24:00 +0100 Subject: [PATCH 4/4] refactor(visualizer): Removed restart requirement and refactored impls (#4200) --- src/plugins/visualizer/index.ts | 151 ++++++++++-------- .../visualizer/visualizers/butterchurn.ts | 51 +++--- .../visualizer/visualizers/visualizer.ts | 23 +-- src/plugins/visualizer/visualizers/vudio.ts | 30 ++-- src/plugins/visualizer/visualizers/wave.ts | 30 ++-- 5 files changed, 138 insertions(+), 147 deletions(-) diff --git a/src/plugins/visualizer/index.ts b/src/plugins/visualizer/index.ts index c3c7e43ba..cadb75f06 100644 --- a/src/plugins/visualizer/index.ts +++ b/src/plugins/visualizer/index.ts @@ -18,7 +18,6 @@ export type VisualizerPluginConfig = { type: 'butterchurn' | 'vudio' | 'wave'; butterchurn: { preset: string; - renderingFrequencyInMs: number; blendTimeInSeconds: number; }; vudio: { @@ -57,17 +56,23 @@ export type VisualizerPluginConfig = { }; }; +type RenderProps = { + visualizerInstance: Visualizer | null; + audioContext: AudioContext | null; + audioSource: MediaElementAudioSourceNode | null; + observer: ResizeObserver | null; +}; + export default createPlugin({ name: () => t('plugins.visualizer.name'), description: () => t('plugins.visualizer.description'), - restartNeeded: true, + restartNeeded: false, config: { enabled: false, type: 'butterchurn', // Config per visualizer butterchurn: { preset: 'martin [shadow harlequins shape code] - fata morgana', - renderingFrequencyInMs: 500, blendTimeInSeconds: 2.7, }, vudio: { @@ -148,81 +153,91 @@ export default createPlugin({ }, renderer: { - async onPlayerApiReady(_, { getConfig }) { - const config = await getConfig(); + props: { + visualizerInstance: null, + audioContext: null, + audioSource: null, + observer: null, + } as RenderProps, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let visualizerType: { new (...args: any[]): Visualizer } = vudio; + createVisualizer( + this: { props: RenderProps }, + config: VisualizerPluginConfig, + ) { + this.props.visualizerInstance?.destroy(); + this.props.visualizerInstance = null; + if (!this.props.audioContext || !this.props.audioSource) return; + if (!config.enabled) return; + + const video = document.querySelector< + HTMLVideoElement & { captureStream(): MediaStream } + >('video'); + if (!video) { + return; + } + + const visualizerContainer = + document.querySelector('#player'); + if (!visualizerContainer) { + return; + } + + let canvas = document.querySelector('#visualizer'); + if (!canvas) { + canvas = document.createElement('canvas'); + canvas.id = 'visualizer'; + visualizerContainer?.prepend(canvas); + } + + const gainNode = this.props.audioContext.createGain(); + gainNode.gain.value = 1.25; + this.props.audioSource.connect(gainNode); + + let visualizerType: { + new (...args: ConstructorParameters): Visualizer; + } = vudio; if (config.type === 'wave') { visualizerType = wave; } else if (config.type === 'butterchurn') { visualizerType = butterchurn; } + this.props.visualizerInstance = new visualizerType( + this.props.audioContext, + this.props.audioSource, + canvas, + gainNode, + video.captureStream(), + config, + ); + const resizeVisualizer = () => { + if (canvas && visualizerContainer) { + const { width, height } = + window.getComputedStyle(visualizerContainer); + canvas.width = Math.ceil(parseFloat(width)); + canvas.height = Math.ceil(parseFloat(height)); + } + this.props.visualizerInstance?.resize(canvas.width, canvas.height); + }; + resizeVisualizer(); + + this.props.observer?.disconnect(); + this.props.observer = new ResizeObserver(resizeVisualizer); + this.props.observer.observe(visualizerContainer); + }, + + onConfigChange(newConfig) { + this.createVisualizer(newConfig); + }, + + onPlayerApiReady(_, { getConfig }) { document.addEventListener( 'peard:audio-can-play', - (e) => { - const video = document.querySelector< - HTMLVideoElement & { captureStream(): MediaStream } - >('video'); - if (!video) { - return; - } - - const visualizerContainer = - document.querySelector('#player'); - if (!visualizerContainer) { - return; - } - - let canvas = document.querySelector('#visualizer'); - if (!canvas) { - canvas = document.createElement('canvas'); - canvas.id = 'visualizer'; - visualizerContainer?.prepend(canvas); - } - - const resizeCanvas = () => { - if (canvas) { - canvas.width = visualizerContainer.clientWidth; - canvas.height = visualizerContainer.clientHeight; - } - }; - - resizeCanvas(); - - 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(); + async (e) => { + this.props.audioContext = e.detail.audioContext; + this.props.audioSource = e.detail.audioSource; + this.createVisualizer(await getConfig()); }, { passive: true }, ); diff --git a/src/plugins/visualizer/visualizers/butterchurn.ts b/src/plugins/visualizer/visualizers/butterchurn.ts index 1a2f88658..1f9d257bc 100644 --- a/src/plugins/visualizer/visualizers/butterchurn.ts +++ b/src/plugins/visualizer/visualizers/butterchurn.ts @@ -5,54 +5,49 @@ import { Visualizer } from './visualizer'; import type { VisualizerPluginConfig } from '../index'; -class ButterchurnVisualizer extends Visualizer { - name = 'butterchurn'; - - visualizer: ReturnType; - private readonly renderingFrequencyInMs: number; +class ButterchurnVisualizer extends Visualizer { + private readonly visualizer: ReturnType; + private destroyed: boolean = false; + private animFrameHandle: number | null; constructor( audioContext: AudioContext, audioSource: MediaElementAudioSourceNode, - visualizerContainer: HTMLElement, canvas: HTMLCanvasElement, audioNode: GainNode, - stream: MediaStream, - options: VisualizerPluginConfig, + _stream: MediaStream, + config: VisualizerPluginConfig, ) { - super( - audioContext, - audioSource, - visualizerContainer, - canvas, - audioNode, - stream, - options, - ); + super(audioSource, audioNode); + + const preset = ButterchurnPresets[config.butterchurn.preset]; + const renderVisualizer = () => { + if (this.destroyed) return; + this.visualizer.render(); + this.animFrameHandle = requestAnimationFrame(renderVisualizer); + }; this.visualizer = Butterchurn.createVisualizer(audioContext, canvas, { width: canvas.width, height: canvas.height, }); - - const preset = ButterchurnPresets[options.butterchurn.preset]; - this.visualizer.loadPreset(preset, options.butterchurn.blendTimeInSeconds); - + this.visualizer.loadPreset(preset, config.butterchurn.blendTimeInSeconds); this.visualizer.connectAudio(audioNode); - this.renderingFrequencyInMs = options.butterchurn.renderingFrequencyInMs; + // Start animation request loop. Do not use setInterval! + this.animFrameHandle = requestAnimationFrame(renderVisualizer); } resize(width: number, height: number) { this.visualizer.setRendererSize(width, height); } - render() { - const renderVisualizer = () => { - requestAnimationFrame(renderVisualizer); - this.visualizer.render(); - }; - setTimeout(renderVisualizer, this.renderingFrequencyInMs); + destroy() { + if (this.animFrameHandle) cancelAnimationFrame(this.animFrameHandle); + this.destroyed = true; + try { + this.audioSource.disconnect(this.audioNode); + } catch {} } } diff --git a/src/plugins/visualizer/visualizers/visualizer.ts b/src/plugins/visualizer/visualizers/visualizer.ts index 12972dcaa..94ee2420b 100644 --- a/src/plugins/visualizer/visualizers/visualizer.ts +++ b/src/plugins/visualizer/visualizers/visualizer.ts @@ -1,22 +1,15 @@ -import type { VisualizerPluginConfig } from '../index'; - -export abstract class Visualizer { - /** - * The name must be the same as the file name. - */ - abstract name: string; - abstract visualizer: T; +export abstract class Visualizer { + protected audioNode: GainNode; + protected audioSource: MediaElementAudioSourceNode; protected constructor( - _audioContext: AudioContext, _audioSource: MediaElementAudioSourceNode, - _visualizerContainer: HTMLElement, - _canvas: HTMLCanvasElement, _audioNode: GainNode, - _stream: MediaStream, - _options: VisualizerPluginConfig, - ) {} + ) { + this.audioNode = _audioNode; + this.audioSource = _audioSource; + } abstract resize(width: number, height: number): void; - abstract render(): void; + abstract destroy(): void; } diff --git a/src/plugins/visualizer/visualizers/vudio.ts b/src/plugins/visualizer/visualizers/vudio.ts index 16f803fb9..2c4e34aee 100644 --- a/src/plugins/visualizer/visualizers/vudio.ts +++ b/src/plugins/visualizer/visualizers/vudio.ts @@ -4,35 +4,24 @@ import { Visualizer } from './visualizer'; import type { VisualizerPluginConfig } from '../index'; -class VudioVisualizer extends Visualizer { - name = 'vudio'; - - visualizer: Vudio; +class VudioVisualizer extends Visualizer { + private readonly visualizer: Vudio; constructor( - audioContext: AudioContext, + _audioContext: AudioContext, audioSource: MediaElementAudioSourceNode, - visualizerContainer: HTMLElement, canvas: HTMLCanvasElement, audioNode: GainNode, stream: MediaStream, - options: VisualizerPluginConfig, + config: VisualizerPluginConfig, ) { - super( - audioContext, - audioSource, - visualizerContainer, - canvas, - audioNode, - stream, - options, - ); + super(audioSource, audioNode); this.visualizer = new Vudio(stream, canvas, { width: canvas.width, height: canvas.height, // Visualizer config - ...options, + ...config, }); this.visualizer.dance(); @@ -45,7 +34,12 @@ class VudioVisualizer extends Visualizer { }); } - render() {} + destroy() { + this.visualizer.pause(); + try { + this.audioSource.disconnect(this.audioNode); + } catch {} + } } export default VudioVisualizer; diff --git a/src/plugins/visualizer/visualizers/wave.ts b/src/plugins/visualizer/visualizers/wave.ts index 3456e90cb..c95f38fe0 100644 --- a/src/plugins/visualizer/visualizers/wave.ts +++ b/src/plugins/visualizer/visualizers/wave.ts @@ -4,35 +4,24 @@ import { Visualizer } from './visualizer'; import type { VisualizerPluginConfig } from '../index'; -class WaveVisualizer extends Visualizer { - name = 'wave'; - - visualizer: Wave; +class WaveVisualizer extends Visualizer { + private readonly visualizer: Wave; constructor( audioContext: AudioContext, audioSource: MediaElementAudioSourceNode, - visualizerContainer: HTMLElement, canvas: HTMLCanvasElement, audioNode: GainNode, - stream: MediaStream, - options: VisualizerPluginConfig, + _stream: MediaStream, + config: VisualizerPluginConfig, ) { - super( - audioContext, - audioSource, - visualizerContainer, - canvas, - audioNode, - stream, - options, - ); + super(audioSource, audioNode); this.visualizer = new Wave( { context: audioContext, source: audioSource }, canvas, ); - for (const animation of options.wave.animations) { + for (const animation of config.wave.animations) { const TargetVisualizer = this.visualizer.animations[ animation.type as keyof typeof this.visualizer.animations @@ -46,7 +35,12 @@ class WaveVisualizer extends Visualizer { resize(_: number, __: number) {} - render() {} + destroy() { + this.visualizer.clearAnimations(); + try { + this.audioSource.disconnect(this.audioNode); + } catch {} + } } export default WaveVisualizer;