From cbab61da91175a0f1278d127a210b60324fa3ff1 Mon Sep 17 00:00:00 2001 From: thororen <78185467+thororen1234@users.noreply.github.com> Date: Fri, 2 Oct 2026 08:58:51 -0400 Subject: [PATCH] BetterAudioPlayer: Use CORS Proxy Again --- .../betterAudioPlayer/index.tsx | 123 ++++++++++++------ 1 file changed, 83 insertions(+), 40 deletions(-) diff --git a/src/equicordplugins/betterAudioPlayer/index.tsx b/src/equicordplugins/betterAudioPlayer/index.tsx index 5784f4bf8..8361c3043 100644 --- a/src/equicordplugins/betterAudioPlayer/index.tsx +++ b/src/equicordplugins/betterAudioPlayer/index.tsx @@ -13,6 +13,8 @@ import definePlugin, { OptionType } from "@utils/types"; import { ColorUtils, React, showToast, Toasts } from "@webpack/common"; const cl = classNameFactory("vc-better-audio-player-"); +const CORS_PROXY = "https://cors.keiran0.workers.dev?url="; +const MAX_FILE_SIZE = 12e6; interface PlayerInstance { mediaRef: React.RefObject; @@ -20,6 +22,12 @@ interface PlayerInstance { state: { playing: boolean; }; } +interface AudioGraph { + ctx: AudioContext; + analyser: AnalyserNode; + blobUrl: string; +} + function validateColor(value: string, key: string, fallback: string) { if (/^\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}$/.test(value)) return; @@ -98,62 +106,101 @@ function drawSpectrograph(ctx: CanvasRenderingContext2D, w: number, h: number, f } } +async function fetchAudioBlob(src: string): Promise { + const url = new URL(src); + url.searchParams.set("t", Date.now().toString()); + + const response = await fetch(CORS_PROXY + encodeURIComponent(url.href)); + const contentLength = response.headers.get("content-length"); + if (contentLength && Number(contentLength) > MAX_FILE_SIZE) return null; + + const blob = await response.blob(); + return URL.createObjectURL(blob); +} + +async function createAudioGraph(audio: HTMLAudioElement): Promise { + const blobUrl = await fetchAudioBlob(audio.currentSrc || audio.src).catch(() => null); + if (!blobUrl) return null; + + const wasPlaying = !audio.paused; + const { currentTime } = audio; + audio.src = blobUrl; + audio.currentTime = currentTime; + if (wasPlaying) audio.play().catch(() => { }); + + const ctx = new AudioContext(); + const analyser = ctx.createAnalyser(); + analyser.fftSize = 2048; + ctx.createMediaElementSource(audio).connect(analyser); + analyser.connect(ctx.destination); + + return { ctx, analyser, blobUrl }; +} + function Visualizer({ mediaRef, playing }: { mediaRef: PlayerInstance["mediaRef"]; playing: boolean; }) { const canvasRef = React.useRef(null); - const graphRef = React.useRef<{ ctx: AudioContext; analyser: AnalyserNode; } | null>(null); + const graphRef = React.useRef | null>(null); React.useEffect(() => { const audio = mediaRef.current; const canvasCtx = canvasRef.current?.getContext("2d"); if (!playing || !audio || !canvasCtx) return; - if (!graphRef.current) { - const ctx = new AudioContext(); - const analyser = ctx.createAnalyser(); - analyser.fftSize = 2048; - ctx.createMediaElementSource(audio).connect(analyser); - analyser.connect(ctx.destination); - graphRef.current = { ctx, analyser }; - } + graphRef.current ??= createAudioGraph(audio); - const { ctx, analyser } = graphRef.current; - ctx.resume(); - - const dataArray = new Uint8Array(analyser.frequencyBinCount); - const frequencyData = new Uint8Array(analyser.frequencyBinCount); let frame = 0; + let stopped = false; + let graphCtx: AudioContext | null = null; - const draw = () => { - frame = requestAnimationFrame(draw); + graphRef.current.then(graph => { + if (stopped || !graph) return; - analyser.getByteTimeDomainData(dataArray); - analyser.getByteFrequencyData(frequencyData); + const { ctx, analyser } = graph; + graphCtx = ctx; + ctx.resume(); - const { canvas } = canvasCtx; - const width = canvas.clientWidth; - const height = canvas.clientHeight; - const dpr = window.devicePixelRatio; - const pixelWidth = Math.round(width * dpr); - const pixelHeight = Math.round(height * dpr); - if (canvas.width !== pixelWidth || canvas.height !== pixelHeight) { - canvas.width = pixelWidth; - canvas.height = pixelHeight; - canvasCtx.setTransform(dpr, 0, 0, dpr, 0, 0); - } + const dataArray = new Uint8Array(analyser.frequencyBinCount); + const frequencyData = new Uint8Array(analyser.frequencyBinCount); - canvasCtx.clearRect(0, 0, width, height); - if (settings.store.oscilloscope) drawOscilloscope(canvasCtx, width, height, dataArray, dataArray.length); - if (settings.store.spectrograph) drawSpectrograph(canvasCtx, width, height, frequencyData, frequencyData.length); - }; - draw(); + const draw = () => { + frame = requestAnimationFrame(draw); + + analyser.getByteTimeDomainData(dataArray); + analyser.getByteFrequencyData(frequencyData); + + const { canvas } = canvasCtx; + const width = canvas.clientWidth; + const height = canvas.clientHeight; + const dpr = window.devicePixelRatio; + const pixelWidth = Math.round(width * dpr); + const pixelHeight = Math.round(height * dpr); + if (canvas.width !== pixelWidth || canvas.height !== pixelHeight) { + canvas.width = pixelWidth; + canvas.height = pixelHeight; + canvasCtx.setTransform(dpr, 0, 0, dpr, 0, 0); + } + + canvasCtx.clearRect(0, 0, width, height); + if (settings.store.oscilloscope) drawOscilloscope(canvasCtx, width, height, dataArray, dataArray.length); + if (settings.store.spectrograph) drawSpectrograph(canvasCtx, width, height, frequencyData, frequencyData.length); + }; + draw(); + }); return () => { + stopped = true; cancelAnimationFrame(frame); - ctx.suspend(); + graphCtx?.suspend(); }; }, [playing]); - React.useEffect(() => () => void graphRef.current?.ctx.close(), []); + React.useEffect(() => () => { + graphRef.current?.then(graph => { + if (!graph) return; + graph.ctx.close(); + URL.revokeObjectURL(graph.blobUrl); + }); + }, []); return (