mirror of
https://github.com/fabiodalez-dev/Monochrome-Android-APK.git
synced 2026-10-18 23:07:33 -05:00
v2.1.0: Fix audio playback (CORS/workbox), add console logger
Audio Fix: - Unregister service worker on Android — workbox CacheFirst on audio/video caused CORS failures on Tidal CDN streams - Patch workbox config: CacheFirst → NetworkOnly for audio/video - Shim @capgo/capacitor-media-session (our AudioForegroundService handles MediaSession natively) Console Logger: - fm-logger.js loaded synchronously in <head> (captures ALL output from the very first script execution) - Intercepts console.log/warn/error/info/debug + window.onerror + unhandledrejection + fetch errors (4xx/5xx/network) - Saves to Downloads/FabiodalezMusic/fm-console-log.txt every 30s and on app background - Max 3000 entries, timestamped, with error flag Search Simplification: - Removed wrapper-side search hooks (dropdown preview, highlight, monkey-patch, cache cleanup) — upstream search flow handles it - Kept upstream patches: debounce 700ms, limit=100, min 3 chars, cache normalization Upstream Sync: - Pulled ~60 new commits including Tidal API v2 migration for artists, artist banners, animated covers, mobile CSS, audio slider fix, international fonts Closes #4: haptic feedback removed.
This commit is contained in:
1 parent
3b5503bb45
commit
3aab74e451
4 files changed
+187
-7
No files matched your search
@@ -3,6 +3,26 @@
|
||||
// Injected into the Monochrome web app at build time.
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
// (fm-logger.js is loaded separately in <head> as a synchronous script
|
||||
// so it captures ALL console output from the very start, before any module.)
|
||||
|
||||
// ── UNREGISTER SERVICE WORKER ──
|
||||
// The upstream workbox SW uses CacheFirst for audio/video, but Tidal CDN
|
||||
// streams don't serve CORS headers → workbox can't read the response →
|
||||
// "no-response" error → audio won't play. The Android WebView already has
|
||||
// its own HTTP cache (configured in MainActivity), so we don't need SW at all.
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.getRegistrations().then((regs) => {
|
||||
regs.forEach((r) => r.unregister());
|
||||
}).catch(() => {});
|
||||
// Also clear the SW cache storage so stale entries don't linger
|
||||
if (typeof caches !== 'undefined') {
|
||||
caches.keys().then((names) => {
|
||||
names.forEach((name) => caches.delete(name));
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
// ── CLIPBOARD FALLBACK ──
|
||||
if (window.AndroidBridge) {
|
||||
const origClipboard = navigator.clipboard?.writeText?.bind(navigator.clipboard);
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
// fm-logger.js — Console logger to file (loaded FIRST, before any other scripts)
|
||||
// Saves all console output to Downloads/FabiodalezMusic/fm-console-log.txt
|
||||
(function () {
|
||||
var _logBuffer = [];
|
||||
var _MAX = 3000;
|
||||
var _origLog = console.log;
|
||||
var _origWarn = console.warn;
|
||||
var _origError = console.error;
|
||||
var _origInfo = console.info;
|
||||
var _origDebug = console.debug;
|
||||
var _hasErrors = false;
|
||||
|
||||
var _ts = function () {
|
||||
return new Date().toISOString().slice(11, 23);
|
||||
};
|
||||
|
||||
var _capture = function (level, args) {
|
||||
try {
|
||||
var parts = [];
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
try {
|
||||
if (typeof args[i] === 'object' && args[i] !== null) {
|
||||
if (args[i] instanceof Error) {
|
||||
parts.push(args[i].message + (args[i].stack ? '\n' + args[i].stack : ''));
|
||||
} else {
|
||||
parts.push(JSON.stringify(args[i], null, 0));
|
||||
}
|
||||
} else {
|
||||
parts.push(String(args[i]));
|
||||
}
|
||||
} catch (e) {
|
||||
parts.push('[unserializable]');
|
||||
}
|
||||
}
|
||||
var msg = _ts() + ' [' + level + '] ' + parts.join(' ');
|
||||
if (_logBuffer.length >= _MAX) _logBuffer.shift();
|
||||
_logBuffer.push(msg);
|
||||
if (level === 'ERR' || level === 'UNCAUGHT' || level === 'UNHANDLED') {
|
||||
_hasErrors = true;
|
||||
}
|
||||
} catch (e) {
|
||||
// logger itself must never throw
|
||||
}
|
||||
};
|
||||
|
||||
console.log = function () {
|
||||
_capture('LOG', arguments);
|
||||
return _origLog.apply(console, arguments);
|
||||
};
|
||||
console.warn = function () {
|
||||
_capture('WARN', arguments);
|
||||
return _origWarn.apply(console, arguments);
|
||||
};
|
||||
console.error = function () {
|
||||
_capture('ERR', arguments);
|
||||
return _origError.apply(console, arguments);
|
||||
};
|
||||
console.info = function () {
|
||||
_capture('INFO', arguments);
|
||||
return _origInfo.apply(console, arguments);
|
||||
};
|
||||
console.debug = function () {
|
||||
_capture('DBG', arguments);
|
||||
return _origDebug.apply(console, arguments);
|
||||
};
|
||||
|
||||
window.addEventListener('error', function (e) {
|
||||
_capture('UNCAUGHT', [
|
||||
e.message || 'unknown error',
|
||||
'at ' + (e.filename || '?') + ':' + (e.lineno || '?') + ':' + (e.colno || '?'),
|
||||
]);
|
||||
});
|
||||
|
||||
window.addEventListener('unhandledrejection', function (e) {
|
||||
var reason = e.reason;
|
||||
if (reason instanceof Error) {
|
||||
_capture('UNHANDLED', [reason.message, reason.stack || '']);
|
||||
} else {
|
||||
_capture('UNHANDLED', [reason]);
|
||||
}
|
||||
});
|
||||
|
||||
// Intercept fetch errors globally
|
||||
var _origFetch = window.fetch;
|
||||
if (_origFetch) {
|
||||
window.fetch = function () {
|
||||
var url = arguments[0];
|
||||
if (typeof url === 'object' && url.url) url = url.url;
|
||||
return _origFetch.apply(this, arguments).then(
|
||||
function (resp) {
|
||||
if (!resp.ok && resp.status >= 400) {
|
||||
_capture('FETCH', [resp.status + ' ' + resp.statusText + ' ' + url]);
|
||||
}
|
||||
return resp;
|
||||
},
|
||||
function (err) {
|
||||
_capture('FETCH_ERR', [String(url).substring(0, 200) + ' → ' + (err.message || err)]);
|
||||
throw err;
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
// Save function
|
||||
var _saveLog = function () {
|
||||
if (!_logBuffer.length) return;
|
||||
// Wait for AndroidDownload bridge to be available
|
||||
if (!window.AndroidDownload) {
|
||||
// Retry in 2s
|
||||
setTimeout(_saveLog, 2000);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var text =
|
||||
'=== Fabiodalez Music Console Log ===\n' +
|
||||
'Saved: ' + new Date().toISOString() + '\n' +
|
||||
'URL: ' + location.href + '\n' +
|
||||
'UA: ' + navigator.userAgent + '\n' +
|
||||
'Entries: ' + _logBuffer.length + '\n' +
|
||||
'Errors: ' + (_hasErrors ? 'YES' : 'none') + '\n' +
|
||||
'===================================\n\n' +
|
||||
_logBuffer.join('\n');
|
||||
var base64 = btoa(unescape(encodeURIComponent(text)));
|
||||
window.AndroidDownload.saveBase64(base64, 'fm-console-log.txt', 'text/plain');
|
||||
} catch (err) {
|
||||
_origError.call(console, '[fm-logger] save failed:', err);
|
||||
}
|
||||
};
|
||||
|
||||
// Save on visibility change (app minimized)
|
||||
document.addEventListener('visibilitychange', function () {
|
||||
if (document.visibilityState === 'hidden') _saveLog();
|
||||
});
|
||||
|
||||
// Save every 30 seconds
|
||||
setInterval(_saveLog, 30000);
|
||||
|
||||
// Expose manual save
|
||||
window._fmSaveLog = _saveLog;
|
||||
window._fmGetLog = function () {
|
||||
return _logBuffer.join('\n');
|
||||
};
|
||||
|
||||
_capture('INFO', ['fm-logger v2 initialized (early-load, fetch interceptor, max ' + _MAX + ')']);
|
||||
})();
|
||||
+20
-6
@@ -36,6 +36,7 @@ cleanup() {
|
||||
for f in \
|
||||
index.html \
|
||||
package.json \
|
||||
vite.config.ts \
|
||||
js/app.js \
|
||||
js/api.js \
|
||||
js/cache.js \
|
||||
@@ -46,7 +47,7 @@ cleanup() {
|
||||
fi
|
||||
done
|
||||
# Untracked files we generated during the build
|
||||
rm -f js/android-service.js package-lock.json
|
||||
rm -f js/android-service.js js/fm-logger.js package-lock.json
|
||||
rm -rf node_modules/@capgo/capacitor-media-session 2>/dev/null || true
|
||||
echo " ✓ Source restored to upstream."
|
||||
}
|
||||
@@ -118,11 +119,13 @@ echo " ✓ @capgo/capacitor-media-session shimmed (no-op → our AudioForegroun
|
||||
echo ""
|
||||
echo "▶ Patching for Android build..."
|
||||
|
||||
# 3a. Add script tag
|
||||
# 3a. Add script tags
|
||||
# Logger FIRST (synchronous, non-module) in <head> — captures ALL console output from start
|
||||
sed -i '' 's|</head>|<script src="./js/fm-logger.js"></script></head>|' index.html
|
||||
# Service JS LAST (module) in <body> — loads after DOM
|
||||
sed -i '' 's|</body>|<script type="module" src="./js/android-service.js"></script></body>|' index.html
|
||||
|
||||
# 3b. (upstream already has viewport-fit=cover — previous pinch-zoom-disable
|
||||
# sed was a noop and removed; rely on meta viewport as upstream ships it.)
|
||||
# 3b. (workbox audio/video CacheFirst → NetworkOnly is patched via Python below)
|
||||
|
||||
# 3c. Brand: "Monochrome" → "Fabiodalez" in sidebar logo
|
||||
sed -i '' 's|<span>Monochrome</span>|<span>Fabiodalez</span>|' index.html
|
||||
@@ -350,14 +353,25 @@ try:
|
||||
except Exception as e:
|
||||
print(" ! api.js: searchArtists patch failed: " + str(e))
|
||||
|
||||
# ── Workbox: CacheFirst → NetworkOnly for audio/video ──
|
||||
# Tidal CDN streams don't serve CORS headers → CacheFirst fails with
|
||||
# "no-response" in the service worker → audio won't play.
|
||||
patch(
|
||||
"vite.config.ts",
|
||||
"handler: 'CacheFirst',\n options: {\n cacheName: 'media',",
|
||||
"handler: 'NetworkOnly',\n options: {\n cacheName: 'media',",
|
||||
"vite.config.ts: workbox audio/video CacheFirst -> NetworkOnly",
|
||||
)
|
||||
|
||||
print(" ✓ Upstream JS optimizations applied.")
|
||||
PYEOF
|
||||
|
||||
echo " ✓ JS upstream optimizations applied (debounce, cache, limits)."
|
||||
|
||||
# ── 4. Copy android-service.js from android/ storage ──
|
||||
# ── 4. Copy wrapper JS files from android/ storage ──
|
||||
cp "$PROJECT_DIR/android/android-service.js" js/android-service.js
|
||||
echo " ✓ android-service.js copied."
|
||||
cp "$PROJECT_DIR/android/fm-logger.js" js/fm-logger.js
|
||||
echo " ✓ android-service.js + fm-logger.js copied."
|
||||
|
||||
# ── 5. Init Capacitor Android if needed ──
|
||||
if [ ! -d "$PROJECT_DIR/android" ]; then
|
||||
|
||||
+2
-1
@@ -31,9 +31,10 @@ cp "$SCRIPT_DIR/build-android.sh" "$TARGET/"
|
||||
cp "$SCRIPT_DIR/capacitor.config.ts" "$TARGET/"
|
||||
chmod +x "$TARGET/build-android.sh"
|
||||
|
||||
# Copy android-service.js
|
||||
# Copy wrapper JS files
|
||||
mkdir -p "$TARGET/android"
|
||||
cp "$SCRIPT_DIR/android/android-service.js" "$TARGET/android/"
|
||||
cp "$SCRIPT_DIR/android/fm-logger.js" "$TARGET/android/"
|
||||
|
||||
# Copy Java sources
|
||||
JAVA_DEST="$TARGET/android/app/src/main/java/com/monochrome/app"
|
||||
|
||||
Reference in new issue
Block a user