fix: strip ?si= from Spotify URL, improve spotdl error logging
spotdl doesn't handle tracking params in URLs. Also separate stdout/stderr in Python script and always try parsing stdout before checking exit code. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
3c85deab40
commit
9b197e119a
2 files changed
+31
-15
No files matched your search
@@ -379,16 +379,22 @@ app.get('/api/health', (req, res) => {
|
||||
async function _resolveSpotifyViaSpotdl(url) {
|
||||
const { execFile } = require('child_process');
|
||||
const scriptPath = path.join(__dirname, 'spotify_resolve.py');
|
||||
// Strip ?si= and other tracking params — spotdl doesn't handle them
|
||||
const cleanUrl = url.split('?')[0];
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile('python3', [scriptPath, url], { timeout: 90000, maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
|
||||
if (err) return reject(new Error(`spotdl: ${stderr?.trim() || err.message}`));
|
||||
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));
|
||||
// Always try to parse stdout first — script outputs JSON even on error
|
||||
const out = stdout?.trim();
|
||||
if (out) {
|
||||
try {
|
||||
const data = JSON.parse(stdout.trim());
|
||||
if (data.error) return reject(new Error(data.error));
|
||||
resolve(data);
|
||||
} catch {
|
||||
reject(new Error(`spotdl: ungültige Ausgabe — ${stdout.slice(0, 200)}`));
|
||||
const data = JSON.parse(out);
|
||||
if (data.error) return reject(new Error(`spotdl: ${data.error}`));
|
||||
return resolve(data);
|
||||
} catch { /* not JSON, fall through */ }
|
||||
}
|
||||
if (err) return reject(new Error(`spotdl exited ${err.code}: ${stderr?.trim() || out || err.message}`));
|
||||
reject(new Error(`spotdl: keine Ausgabe`));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"""
|
||||
Resolves a Spotify URL to track metadata using spotdl (TzurSoffer fork).
|
||||
Usage: python3 spotify_resolve.py <spotify_url>
|
||||
Outputs JSON to stdout.
|
||||
Outputs JSON to stdout. Errors go to stderr for clean separation.
|
||||
"""
|
||||
import sys
|
||||
import json
|
||||
@@ -10,36 +10,47 @@ import os
|
||||
import tempfile
|
||||
import subprocess
|
||||
|
||||
def eprint(*args):
|
||||
print(*args, file=sys.stderr)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(json.dumps({"error": "No URL provided"}))
|
||||
eprint("No URL provided")
|
||||
sys.exit(1)
|
||||
|
||||
url = sys.argv[1]
|
||||
tmp = tempfile.mktemp(suffix=".spotdl")
|
||||
|
||||
try:
|
||||
eprint(f"[spotdl] running: spotdl save {url}")
|
||||
result = subprocess.run(
|
||||
["spotdl", "save", url, "--save-file", tmp, "--log-level", "ERROR"],
|
||||
capture_output=True, text=True, timeout=60
|
||||
)
|
||||
eprint(f"[spotdl] exit={result.returncode}")
|
||||
if result.stdout.strip():
|
||||
eprint(f"[spotdl] stdout: {result.stdout.strip()[:300]}")
|
||||
if result.stderr.strip():
|
||||
eprint(f"[spotdl] stderr: {result.stderr.strip()[:300]}")
|
||||
|
||||
if result.returncode != 0:
|
||||
err = result.stderr.strip() or result.stdout.strip() or "spotdl failed"
|
||||
print(json.dumps({"error": err}))
|
||||
err = result.stderr.strip() or result.stdout.strip() or "spotdl exited non-zero"
|
||||
eprint(f"[spotdl] FAILED: {err}")
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.exists(tmp):
|
||||
print(json.dumps({"error": "spotdl produced no output file"}))
|
||||
eprint("[spotdl] no output file produced")
|
||||
sys.exit(1)
|
||||
|
||||
with open(tmp, "r", encoding="utf-8") as f:
|
||||
songs = json.load(f)
|
||||
|
||||
if not songs:
|
||||
print(json.dumps({"error": "No tracks found"}))
|
||||
eprint("[spotdl] empty track list")
|
||||
sys.exit(1)
|
||||
|
||||
# Transform spotdl Song format to NovaSonic format
|
||||
eprint(f"[spotdl] got {len(songs)} tracks")
|
||||
|
||||
def fmt_track(s):
|
||||
artists = s.get("artists") or []
|
||||
artist_str = ", ".join(artists) if artists else s.get("artist", "")
|
||||
@@ -60,7 +71,6 @@ def main():
|
||||
artists0 = first.get("artists") or []
|
||||
artist0 = ", ".join(artists0) if artists0 else first.get("artist", "")
|
||||
|
||||
# Determine type from URL
|
||||
if "/track/" in url:
|
||||
out = {
|
||||
"type": "track",
|
||||
|
||||
Reference in new issue
Block a user