Enable AudioContext and proxy for all users, not just extension users

- Set crossorigin="anonymous" as a static HTML attribute on audio/video elements
  so all users can use createMediaElementSource (required for Web Audio API)
- Remove window.__tidalOriginExtension guards from AudioContext init/changeSource:
  binaural DSP, EQ, M/S processing, and audio graph now active for everyone
- Route direct (non-DASH) stream URLs through getProxyUrl in player.js so
  non-extension users get CORS headers from the proxy for lossless/MP4 audio
- Proxy preloader src as well so browser cache warms with the proxied URL
- Add idempotency guard in getProxyUrl to prevent double-proxying when Shaka
  retries segment requests (proxied URL still contains "tidal.com" in params)
- Extension users: still bypass proxy and get CDN URLs directly (unchanged)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
uimaxbai 2026-04-22 21:07:39 +01:00
parent 9138ec5289
commit 7b8a6e6cab
4 changed files with 41 additions and 54 deletions

View file

@ -115,14 +115,8 @@
</head> </head>
<body> <body>
<audio id="audio-player" style="display: none"></audio> <audio id="audio-player" crossorigin="anonymous" style="display: none"></audio>
<video id="video-player" style="display: none"></video> <video id="video-player" crossorigin="anonymous" style="display: none"></video>
<script>
if (window.__tidalOriginExtension) {
document.getElementById('audio-player').crossOrigin = 'anonymous';
document.getElementById('video-player').crossOrigin = 'anonymous';
}
</script>
<div id="context-menu"> <div id="context-menu">
<ul> <ul>
<li data-action="shuffle-play-card" data-type-filter="album,playlist,mix,user-playlist"> <li data-action="shuffle-play-card" data-type-filter="album,playlist,mix,user-playlist">

View file

@ -475,7 +475,7 @@ class AudioContextManager {
this.audio = audioElement; this.audio = audioElement;
if (isIos && !window.__tidalOriginExtension) { if (isIos) {
console.log('[AudioContext] Skipping Web Audio initialization on iOS for lock screen compatibility'); console.log('[AudioContext] Skipping Web Audio initialization on iOS for lock screen compatibility');
return; return;
} }
@ -490,24 +490,22 @@ class AudioContextManager {
this.audioContext = new AudioContext(); this.audioContext = new AudioContext();
} }
if (window.__tidalOriginExtension) { if (!this.sources.has(audioElement)) {
if (!this.sources.has(audioElement)) { const src = this.audioContext.createMediaElementSource(audioElement);
const src = this.audioContext.createMediaElementSource(audioElement); this.sources.set(audioElement, src);
this.sources.set(audioElement, src);
}
this.source = this.sources.get(audioElement);
try {
this.audioContext.destination.channelCount = Math.min(this.audioContext.destination.maxChannelCount, 8);
this.audioContext.destination.channelCountMode = 'explicit';
this.audioContext.destination.channelInterpretation = 'discrete';
} catch {
// Some browsers may not support changing destination channel count
}
this.binauralDsp = new BinauralDSP(this.audioContext);
void this._loadBinauralSettings();
} }
this.source = this.sources.get(audioElement);
try {
this.audioContext.destination.channelCount = Math.min(this.audioContext.destination.maxChannelCount, 8);
this.audioContext.destination.channelCountMode = 'explicit';
this.audioContext.destination.channelInterpretation = 'discrete';
} catch {
// Some browsers may not support changing destination channel count
}
this.binauralDsp = new BinauralDSP(this.audioContext);
void this._loadBinauralSettings();
this.analyser = this.audioContext.createAnalyser(); this.analyser = this.audioContext.createAnalyser();
this.analyser.fftSize = 1024; this.analyser.fftSize = 1024;
@ -528,16 +526,14 @@ class AudioContextManager {
this.monoMergerNode = this.audioContext.createChannelMerger(2); this.monoMergerNode = this.audioContext.createChannelMerger(2);
if (window.__tidalOriginExtension) { this._connectGraph();
this._connectGraph();
}
// Auto-recover from unexpected suspensions (e.g. background throttling) // Auto-recover from unexpected suspensions (e.g. background throttling)
this.audioContext.addEventListener('statechange', () => { this.audioContext.addEventListener('statechange', () => {
if (this.audioContext.state === 'interrupted' || this.audioContext.state === 'suspended') { if (this.audioContext.state === 'interrupted' || this.audioContext.state === 'suspended') {
console.log(`[AudioContext] State changed to ${this.audioContext.state}, attempting resume`); console.log(`[AudioContext] State changed to ${this.audioContext.state}, attempting resume`);
setTimeout(() => { setTimeout(() => {
if (this.audioContext && this.audioContext.state !== 'running' && (window.__tidalOriginExtension ? this.source : true)) { if (this.audioContext && this.audioContext.state !== 'running') {
this.audioContext.resume().catch((e) => { this.audioContext.resume().catch((e) => {
console.warn('[AudioContext] Auto-resume failed:', e); console.warn('[AudioContext] Auto-resume failed:', e);
}); });
@ -559,31 +555,27 @@ class AudioContextManager {
} }
if (this.audio === audioElement) return; if (this.audio === audioElement) return;
if (window.__tidalOriginExtension) { try {
try { if (this.source) {
if (this.source) { try {
try { this.source.disconnect();
this.source.disconnect(); } catch {
} catch { // node may already be disconnected
// node may already be disconnected
}
} }
this.audio = audioElement;
if (!this.sources.has(audioElement)) {
this.sources.set(audioElement, this.audioContext.createMediaElementSource(audioElement));
}
this.source = this.sources.get(audioElement);
if (this.isInitialized) {
this._connectGraph();
}
} catch (e) {
console.warn('changeSource failed:', e);
} }
} else {
this.audio = audioElement; this.audio = audioElement;
if (!this.sources.has(audioElement)) {
this.sources.set(audioElement, this.audioContext.createMediaElementSource(audioElement));
}
this.source = this.sources.get(audioElement);
if (this.isInitialized) {
this._connectGraph();
}
} catch (e) {
console.warn('changeSource failed:', e);
} }
} }

View file

@ -663,7 +663,7 @@ export class Player {
const preloader = new Audio(); const preloader = new Audio();
preloader.preload = 'auto'; preloader.preload = 'auto';
preloader.muted = true; preloader.muted = true;
preloader.src = streamUrl; preloader.src = getProxyUrl(streamUrl);
streamInfo.preloader = preloader; // Hold reference streamInfo.preloader = preloader; // Hold reference
} }
} }
@ -1282,7 +1282,7 @@ export class Player {
} catch {} } catch {}
this.shakaInitialized = false; this.shakaInitialized = false;
} }
activeElement.src = streamUrl; activeElement.src = getProxyUrl(streamUrl);
this.applyAudioEffects(); this.applyAudioEffects();
this.updateAdaptiveQualityBadge(); this.updateAdaptiveQualityBadge();

View file

@ -1,4 +1,5 @@
export const getProxyUrl = (url) => { export const getProxyUrl = (url) => {
if (window.__tidalOriginExtension) return url; if (window.__tidalOriginExtension) return url;
if (url.startsWith('https://audio-proxy.binimum.org/')) return url;
return `https://audio-proxy.binimum.org/proxy-audio?url=${url}`; return `https://audio-proxy.binimum.org/proxy-audio?url=${url}`;
}; };