import json, re, urllib.request, sys UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36' def get(url, raw=False): req = urllib.request.Request(url, headers={'User-Agent': UA, 'Accept-Language': 'en-US,en;q=0.9'}) with urllib.request.urlopen(req, timeout=30) as r: data = r.read().decode('utf-8', 'replace') return data if raw else json.loads(data) # SoundCloud addresses a set by the slug in its URL, so these cannot be renamed # from here — renaming the sets on SoundCloud is what would change them. Every # downstream file is keyed by the label instead, which is why no slug reaches # sc_tracks.json, reference.txt or the harness. SETS = { 'chosic-playlist': 'set-0', 'chosic-playlist-1': 'set-1', 'chosic-playlist-2': 'set-2', 'chosic-playlist-3': 'set-3', 'chosic-playlist-4': 'set-4', 'chosic-playlist-5': 'set-5', } page = get(f'https://soundcloud.com/dietrich/sets/{next(iter(SETS))}', raw=True) scripts = re.findall(r']+src="(https://a-v2\.sndcdn\.com/assets/[^"]+\.js)"', page) print('asset scripts:', len(scripts)) client_id = None for src in reversed(scripts): try: js = get(src, raw=True) except Exception as e: continue m = re.search(r'client_id\s*[:=]\s*"([a-zA-Z0-9]{20,})"', js) if m: client_id = m.group(1) print('client_id found in', src.rsplit('/', 1)[-1]) break if not client_id: print('no client_id found'); sys.exit(1) out = {} for slug, label in SETS.items(): url = f'https://api-v2.soundcloud.com/resolve?url=https://soundcloud.com/dietrich/sets/{slug}&client_id={client_id}' try: pl = get(url) except Exception as e: print(label, 'FAILED', e) continue tracks = pl.get('tracks', []) ids = [t['id'] for t in tracks] titled = [t for t in tracks if t.get('title')] # Tracks past the first ~5 come back as id-only stubs; fetch them in batches. missing = [t['id'] for t in tracks if not t.get('title')] for i in range(0, len(missing), 50): batch = ','.join(str(x) for x in missing[i:i + 50]) try: more = get(f'https://api-v2.soundcloud.com/tracks?ids={batch}&client_id={client_id}') titled.extend(more) except Exception as e: print(' batch failed', e) out[label] = titled print(f'{label}: {pl.get("track_count")} tracks, resolved {len(titled)}') with open('sc_tracks.json', 'w') as f: json.dump({k: [{'title': t.get('title'), 'user': (t.get('user') or {}).get('username'), 'publisher': t.get('publisher_metadata')} for t in v] for k, v in out.items()}, f, indent=1) print('\nwrote sc_tracks.json') for slug, v in out.items(): print(f'\n{slug}:') for t in v[:8]: pm = t.get('publisher_metadata') or {} print(' ', pm.get('artist') or (t.get('user') or {}).get('username'), '::', t.get('title'))