Просмотр исходного кода

Add iOS support for privacy mode

Toby Chui 1 месяц назад
Родитель
Сommit
0579d253af
2 измененных файлов с 27 добавлено и 1 удалено
  1. 6 0
      src/web/Musicify/index.html
  2. 21 1
      src/web/Musicify/musicify.js

+ 6 - 0
src/web/Musicify/index.html

@@ -1585,6 +1585,12 @@
                                 <span class="toggle-slider"></span>
                                 <span class="toggle-slider"></span>
                             </label>
                             </label>
                         </div>
                         </div>
+                        <div x-show="_isIOS()" style="margin-top:12px;padding:10px 12px;background:var(--bg3);border-radius:7px;border:1px solid var(--border);">
+                            <span style="font-size:12px;color:var(--text3);">
+                                <i class="ui info circle icon" style="color:var(--accent);"></i>
+                                On iPhone &amp; iPad the bars use a simulated animation rather than true audio analysis. This is intentional: analysing the live audio signal would route playback through Web Audio, which iOS stops when the screen locks. The simulated animation keeps background playback working while still hiding the album art.
+                            </span>
+                        </div>
                     </div>
                     </div>
 
 
                     <!-- Mobile Volume subsection -->
                     <!-- Mobile Volume subsection -->

+ 21 - 1
src/web/Musicify/musicify.js

@@ -130,6 +130,7 @@ function musicifyApp() {
         _analyser: null,
         _analyser: null,
         _vizData: null,
         _vizData: null,
         _vizAnimId: null,
         _vizAnimId: null,
+        _syntheticViz: false,          // iOS: animate the bars without Web Audio so screen-lock doesn't stop playback
 
 
         // ── Arozcast ─────────────────────────────────────────────────────────
         // ── Arozcast ─────────────────────────────────────────────────────────
         castMode: false,
         castMode: false,
@@ -1539,7 +1540,16 @@ function musicifyApp() {
         // done once per element (createMediaElementSource throws on a second call),
         // done once per element (createMediaElementSource throws on a second call),
         // so the analyser is created once and simply left connected afterwards.
         // so the analyser is created once and simply left connected afterwards.
         _ensureAnalyser() {
         _ensureAnalyser() {
-            if (this._analyser || !this._audio) return;
+            if (this._analyser || this._syntheticViz || !this._audio) return;
+            // iOS: routing the <audio> element through a Web Audio AudioContext
+            // (createMediaElementSource) hands playback to that context, which iOS
+            // suspends when the screen locks — stopping background audio. Skip Web
+            // Audio there and drive the bars with a synthetic animation instead, so
+            // playback behaves exactly like non-privacy mode.
+            if (this._isIOS()) {
+                this._syntheticViz = true;
+                return;
+            }
             try {
             try {
                 var AudioCtx = window.AudioContext || window.webkitAudioContext;
                 var AudioCtx = window.AudioContext || window.webkitAudioContext;
                 this._audioCtx = new AudioCtx();
                 this._audioCtx = new AudioCtx();
@@ -1582,6 +1592,16 @@ function musicifyApp() {
                     var idx = Math.min(binCount - 1, dist * step);
                     var idx = Math.min(binCount - 1, dist * step);
                     heights[i] = Math.max(0.06, this._vizData[idx] / 255);
                     heights[i] = Math.max(0.06, this._vizData[idx] / 255);
                 }
                 }
+            } else if (this._syntheticViz) {
+                // iOS fallback: procedurally animated, center-weighted bars that
+                // pulse while playing and settle when paused (no real FFT).
+                var t = performance.now() / 1000;
+                var active = this.isPlaying ? 1 : 0;
+                for (var k = 0; k < bars; k++) {
+                    var envelope = 1 - (Math.abs(k - mid) / mid) * 0.7; // taller in the middle
+                    var wave = 0.5 + 0.30 * Math.sin(t * 3.1 + k * 0.5) + 0.15 * Math.sin(t * 5.7 + k * 0.9);
+                    heights[k] = Math.max(0.06, Math.min(1, 0.08 + active * Math.max(0, wave) * envelope * 0.9));
+                }
             } else {
             } else {
                 for (var j = 0; j < bars; j++) heights[j] = 0.06;
                 for (var j = 0; j < bars; j++) heights[j] = 0.06;
             }
             }