From 06b78be199e23f6e897a3de88eb6f5504fda1591 Mon Sep 17 00:00:00 2001 From: Honey Date: Mon, 11 May 2026 21:26:51 +0200 Subject: [PATCH] fix: full stderr logging per-line, scan embed page for GQL hash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Log each Python stderr line separately (no 500-char truncation) so we can see exactly what's happening in the bundle scan - Try embed page (open.spotify.com/embed/...) first for GQL hashes — focused app, fewer bundles, GQL chunks directly referenced - Suppress noisy bundle-fetch errors to keep logs readable Co-Authored-By: Claude Sonnet 4.6 --- backend/novasonic-server.js | 2 +- backend/spotify_resolve.py | 30 ++++++++++++++++++------------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/backend/novasonic-server.js b/backend/novasonic-server.js index 79d084f..f7c82a4 100644 --- a/backend/novasonic-server.js +++ b/backend/novasonic-server.js @@ -359,7 +359,7 @@ async function _resolveSpotifyViaSpotdl(url) { const cleanUrl = url.split('?')[0]; return new Promise((resolve, reject) => { execFile('python3', [scriptPath, cleanUrl], { timeout: 90000, maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => { - if (stderr?.trim()) console.log('[spotdl-py]', stderr.trim().slice(0, 500)); + if (stderr?.trim()) stderr.trim().split('\n').forEach(l => console.log('[py]', l)); // Always try to parse stdout first — script outputs JSON even on error const out = stdout?.trim(); if (out) { diff --git a/backend/spotify_resolve.py b/backend/spotify_resolve.py index 84cfdcf..2b40302 100644 --- a/backend/spotify_resolve.py +++ b/backend/spotify_resolve.py @@ -67,8 +67,8 @@ def _scan_bundles(session, js_urls, need_totp, need_ops): if h: gql_hashes[op] = h eprint(f'[spotify] GQL hash {op}: {h[:16]}...') - except Exception as e: - eprint(f'[spotify] bundle error ({u[-40:]}): {e}') + except Exception: + pass # skip failed bundles silently return totp_secret, totp_version, gql_hashes def get_token_and_hashes(session, sp_type, sp_id): @@ -90,17 +90,23 @@ def get_token_and_hashes(session, sp_type, sp_id): if not totp_secret: raise Exception('TOTP secret not found') - # Step 2: resource page — find GQL hash (lazy-loaded playlist/album/track chunk) + # Step 2: try embed page — focused app, GQL chunks often directly referenced if GQL_OP not in gql_hashes: - eprint(f'[spotify] fetching {sp_type} page for GQL hash...') - try: - page_url = f'https://open.spotify.com/{sp_type}/{sp_id}' - page_html = session.get(page_url, headers=page_headers, timeout=15).text - extra_js = _collect_js_urls(page_html, seen) - _, _, extra_hashes = _scan_bundles(session, extra_js[:25], False, [GQL_OP]) - gql_hashes.update(extra_hashes) - except Exception as e: - eprint(f'[spotify] page scan error: {e}') + for extra_url in [ + f'https://open.spotify.com/embed/{sp_type}/{sp_id}?utm_source=generator', + f'https://open.spotify.com/{sp_type}/{sp_id}', + ]: + eprint(f'[spotify] scanning {extra_url[:60]} for GQL hash...') + try: + page_html = session.get(extra_url, headers=page_headers, timeout=15).text + extra_js = _collect_js_urls(page_html, seen) + eprint(f'[spotify] found {len(extra_js)} new JS URLs on that page') + _, _, extra_hashes = _scan_bundles(session, extra_js[:30], False, [GQL_OP]) + gql_hashes.update(extra_hashes) + except Exception as e: + eprint(f'[spotify] page scan error: {e}') + if GQL_OP in gql_hashes: + break st = session.get('https://open.spotify.com/api/server-time', headers={ 'Accept': 'application/json', 'Referer': 'https://open.spotify.com/',