Commit 05c8ab30 by PLN (Algolia)

feat(probe): five probes that found what four guesses could not

Every SoundCloud blocker this week fell to looking at the page instead of
reasoning about it, so the probes are kept rather than thrown away — each one
encodes a question worth re-asking when SoundCloud changes its uploader again.

  * `probe_upload_form.py` — enumerate every input/select/textarea/button in the
    upload frame with its label. This produced the whole selector table the
    uploader is written against: #title, #trackPermalink, #artist, #primaryGenre,
    #tags, textarea#description, input#fileInput, and the Upload button.
  * `probe_upload_privacy.py` — read the radio groups properly. Answer: they are
    geo (worldwide/exclusiveRegions/blockedRegions) and licensing
    (all-rights-reserved/commons). NOT privacy.
  * `probe_upload_legacy.py` — does an older, non-v2 uploader still exist with a
    Public/Private choice? Written when I wrongly believed the v2 form had none.
  * `probe_upload_watch.py` — open the real form and poll for state changes, so
    PLN can click a control and have the script report exactly which element it
    is. Built after concluding, twice, that a control was absent.
  * `probe_upload_sharing.py` — drive the MUI Select and VERIFY the trigger
    changed. Confirms Public -> Private end to end.

The lesson the set exists to encode: I reported "the v2 uploader is public-only"
after sweeping every input, select and radio in the frame and finding nothing.
The control was a MUI Select, whose options are portal-rendered only once the
trigger is clicked — so an unmounted menu and an absent feature are
indistinguishable to a DOM sweep. PLN pasted the real markup
(`li[role=option][data-value="Private"]`) and it drove fine first try. A
component library's Select is not `<select>`, and "I searched and found nothing"
is a statement about the search.
parent 32e7a008
"""Enumerate EVERY control on SoundCloud's upload form, so the driver is written
against what is there rather than what I imagine.
Attaches a 1-second probe and dumps inputs, selects, radios, switches, buttons
and their labels from the nested `/n/upload` frame. Stops before clicking
Upload, so no track is created — SoundCloud will push the probe's bytes to S3 on
its own, which is why the probe is a sine and not a record.
Three earlier guesses at `POST /tracks` all failed, and the last two blind spots
here were a frame we never looked in and a modal we never dismissed. So: look.
"""
import json
import sys
from pathlib import Path
sys.path.insert(0, "/home/pln/Work/Sound/tidal-ears/src")
from tidal_ears import consent # noqa: E402
from tidal_ears.scbrowser import Browser # noqa: E402
PROBE = Path(sys.argv[1])
OUT = Path(sys.argv[2]) if len(sys.argv) > 2 else None
DUMP_JS = """() => {
const out = [];
const label = (el) => {
if (el.getAttribute('aria-label')) return el.getAttribute('aria-label');
if (el.id) {
const l = document.querySelector(`label[for="${el.id}"]`);
if (l) return l.innerText.trim().slice(0, 50);
}
const l = el.closest('label');
if (l) return l.innerText.trim().slice(0, 50);
return '';
};
for (const el of document.querySelectorAll(
'input, select, textarea, [role=switch], [role=radio], [role=checkbox], [contenteditable=true]')) {
const r = el.getBoundingClientRect();
out.push({
tag: el.tagName.toLowerCase(),
type: el.type || el.getAttribute('role') || '',
name: el.name || '',
id: el.id || '',
value: (el.value || '').slice(0, 40),
checked: el.checked ?? el.getAttribute('aria-checked') ?? null,
label: label(el),
visible: r.width > 0 && r.height > 0,
});
}
const btns = [];
for (const el of document.querySelectorAll('button, [role=button], a[href]')) {
const r = el.getBoundingClientRect();
if (!(r.width > 0 && r.height > 0)) continue;
btns.push({
text: (el.innerText || el.getAttribute('aria-label') || '').trim().slice(0, 60),
cls: (el.className || '').toString().slice(0, 45),
disabled: el.disabled ?? null,
});
}
return {controls: out, buttons: btns,
text: (document.body.innerText || '').replace(/\\n{2,}/g, '\\n').slice(0, 1500)};
}"""
with Browser() as b:
b.open()
consent.ensure(b.page, policy="reject")
b.require_login()
b.page.goto("https://soundcloud.com/upload", wait_until="domcontentloaded")
frame = b.upload_frame()
if frame is None:
sys.exit("no frame carries a file input")
print(f"form frame: {frame.url[:70]}\n")
frame.locator('input[type="file"]').first.set_input_files(str(PROBE))
b.page.wait_for_timeout(15000)
d = frame.evaluate(DUMP_JS)
print("--- controls ---")
for c in d["controls"]:
if not c["visible"] and c["type"] != "file":
continue
print(f" {c['tag']:<15} type={c['type']:<14} name={c['name']:<16} "
f"id={c['id'][:18]:<18} checked={str(c['checked'])[:5]:<5} "
f"{c['label'][:34]!r}")
print("\n--- buttons ---")
for x in d["buttons"]:
print(f" {str(x['disabled'])[:5]:<5} {x['text']!r}")
print("\n--- frame text ---")
for line in d["text"].splitlines():
if line.strip():
print(" |", line.strip()[:96])
if OUT:
OUT.write_text(json.dumps(d, indent=1) + "\n")
print(f"\nwrote {OUT}")
"""Does SoundCloud's LEGACY uploader still exist, and does it offer Private?
The v2 form is public-only: its radios are geo (worldwide/exclusiveRegions/
blockedRegions) and licensing (all-rights-reserved/commons), and the word
"private" appears nowhere. PLN asked for a private album upload, and
upload-public-then-PUT-private leaves a window where 14 tracks with
unadjudicated sample rights are world-readable.
The form frame is served as /n/upload?...&v2_layout=true, so the obvious
question is whether v2_layout=false still renders the older form that had a
Public/Private choice. Uploads nothing.
"""
import sys
from pathlib import Path
sys.path.insert(0, "/home/pln/Work/Sound/tidal-ears/src")
from tidal_ears import consent # noqa: E402
from tidal_ears.scbrowser import Browser # noqa: E402
PROBE = Path(sys.argv[1])
CANDIDATES = [
"https://soundcloud.com/n/upload?v2_layout=false",
"https://soundcloud.com/n/upload",
"https://soundcloud.com/upload?v2_layout=false",
]
SCAN = """() => {
const t = document.body.innerText || '';
return {
priv: /privat/i.test(t),
pub: /public/i.test(t),
lines: t.split('\\n').filter(l => /privat|public|who can|visib/i.test(l))
.map(l => l.trim().slice(0, 80)).slice(0, 10),
radios: [...document.querySelectorAll('input[type=radio]')]
.map(r => r.value || '(no value)'),
selects: [...document.querySelectorAll('select')].map(s =>
[...s.options].map(o => o.value).join('|').slice(0, 60)),
files: document.querySelectorAll('input[type=file]').length,
};
}"""
with Browser() as b:
b.open()
consent.ensure(b.page, policy="reject")
b.require_login()
for url in CANDIDATES:
print(f"\n=== {url}")
try:
b.page.goto(url, wait_until="domcontentloaded", timeout=45000)
except Exception as e:
print(" goto failed:", str(e).splitlines()[0][:70])
continue
b.page.wait_for_timeout(5000)
print(" landed on:", b.page.url[:90])
# The form may be in this document or nested one deeper.
target = None
for f in b.page.frames:
try:
if f.locator('input[type="file"]').count():
target = f
break
except Exception:
pass
if target is None:
print(" no file input in any frame")
continue
print(" file input frame:", target.url[:80])
target.locator('input[type="file"]').first.set_input_files(str(PROBE))
b.page.wait_for_timeout(13000)
d = target.evaluate(SCAN)
print(f" mentions private: {d['priv']} radios: {d['radios']}")
if d["selects"]:
print(" selects:", d["selects"])
for line in d["lines"]:
print(" |", line)
if d["priv"]:
print(" *** PRIVATE AVAILABLE HERE ***")
break
"""What privacy settings can the new SoundCloud uploader actually set?
PLN asked for a PRIVATE album upload. The form's visible text offers only
"Public" and "Schedule public release (ARTIST PRO)", but the DOM carries five
radios whose labels did not extract — so before assuming private is unavailable
(and before uploading 15 files publicly by accident), read the radios properly.
Uploads nothing: stops before the Upload button.
"""
import sys
from pathlib import Path
sys.path.insert(0, "/home/pln/Work/Sound/tidal-ears/src")
from tidal_ears import consent # noqa: E402
from tidal_ears.scbrowser import Browser # noqa: E402
PROBE = Path(sys.argv[1])
RADIO_JS = """() => {
const out = [];
document.querySelectorAll('input[type=radio]').forEach((el, i) => {
// Walk up until an ancestor has text of its own, which is where these
// designs park the label.
let node = el, text = '', depth = 0;
while (node && depth < 6) {
const t = (node.innerText || '').replace(/\\s+/g, ' ').trim();
if (t.length > text.length) text = t;
if (text.length > 20) break;
node = node.parentElement; depth++;
}
const r = el.getBoundingClientRect();
out.push({i, name: el.name || '(none)', value: el.value || '',
checked: el.checked, disabled: el.disabled,
visible: r.width > 0 && r.height > 0,
text: text.slice(0, 110)});
});
return out;
}"""
with Browser() as b:
b.open()
consent.ensure(b.page, policy="reject")
b.require_login()
b.page.goto("https://soundcloud.com/upload", wait_until="domcontentloaded")
frame = b.upload_frame()
if frame is None:
sys.exit("no upload frame")
frame.locator('input[type="file"]').first.set_input_files(str(PROBE))
b.page.wait_for_timeout(15000)
print("--- radios ---")
for r in frame.evaluate(RADIO_JS):
print(f" [{r['i']}] name={r['name']:<8} value={r['value']!r:<12} "
f"checked={str(r['checked']):<5} vis={str(r['visible']):<5} "
f"{r['text']!r}")
# Also: does the word "private" appear anywhere in this frame at all?
hits = frame.evaluate("""() => {
const t = document.body.innerText || '';
return t.split('\\n').filter(l => /privat|only you|secret|unlisted/i.test(l))
.map(l => l.trim().slice(0, 90)).slice(0, 8);
}""")
print("\n--- lines mentioning private/unlisted ---")
print(" " + ("\n ".join(hits) if hits else "(none)"))
"""Find and operate SoundCloud's privacy control — a MUI Select, not a radio.
My earlier probes concluded "the v2 uploader is public-only". That was wrong, and
the reason is worth keeping: the control is a **MUI `Select`**, whose options are
rendered into a PORTAL only after the trigger is clicked. A closed MUI menu has
no options in the DOM at all, so sweeping `input[type=radio]`,
`input[type=checkbox]` and `select` finds the geo and licensing radios and then
truthfully reports that no privacy control exists. PLN read the real markup and
handed it over:
<li role="option" aria-label="Private" data-value="Private"
class="MuiButtonBase-root MuiMenuItem-root …">
Private / "Track is not discoverable. Only people with private link can
view and listen."
Third time in this project the instrument was the broken part rather than the
rig. The generalisable rule: a component library's Select is not `<select>`, and
"the control is absent" and "the control is unmounted" look identical to a DOM
sweep.
So: open the menu, enumerate the options, pick one, and CONFIRM the trigger
changed. Uploads nothing — never touches the Upload button.
"""
import sys
from pathlib import Path
sys.path.insert(0, "/home/pln/Work/Sound/tidal-ears/src")
from tidal_ears import consent # noqa: E402
from tidal_ears.scbrowser import Browser # noqa: E402
PROBE = Path(sys.argv[1])
WANT = sys.argv[2] if len(sys.argv) > 2 else "Private"
TRIGGERS = ['[role="combobox"]', ".MuiSelect-select"]
with Browser() as b:
b.open()
consent.ensure(b.page, policy="reject")
b.require_login()
b.page.goto("https://soundcloud.com/upload", wait_until="domcontentloaded")
frame = b.upload_frame()
if frame is None:
sys.exit("no upload frame")
frame.locator('input[type="file"]').first.set_input_files(str(PROBE))
b.page.wait_for_timeout(14000)
print("--- candidate triggers in the form frame ---")
for sel in TRIGGERS:
try:
loc = frame.locator(sel)
n = loc.count()
except Exception as e:
print(f" {sel:<26} ERR {str(e).splitlines()[0][:40]}")
continue
print(f" {sel:<26} {n} match(es)")
for i in range(min(n, 8)):
el = loc.nth(i)
try:
txt = (el.inner_text() or "").replace("\n", " ").strip()[:44]
print(f" [{i}] visible={el.is_visible()} {txt!r}")
except Exception:
pass
# The sharing Select is the visible combobox currently reading "Public".
combo = frame.locator('[role="combobox"], .MuiSelect-select')
target = None
for i in range(combo.count()):
el = combo.nth(i)
try:
if el.is_visible() and "public" in (el.inner_text() or "").lower():
target = el
break
except Exception:
continue
if target is None:
sys.exit("\nno visible combobox reading 'Public' — see dump above")
print(f"\nopening the sharing Select (reads {target.inner_text()!r})")
target.click()
b.page.wait_for_timeout(1500)
opts = frame.locator('li[role="option"]')
print(f"--- {opts.count()} option(s) now in the DOM (portal-rendered) ---")
for i in range(opts.count()):
el = opts.nth(i)
label = (el.inner_text() or "").replace("\n", " | ")[:72]
print(f" data-value={el.get_attribute('data-value')!r:<12} "
f"selected={el.get_attribute('aria-selected')!r:<7} {label!r}")
pick = frame.locator(f'li[role="option"][data-value="{WANT}"]')
if not pick.count():
sys.exit(f"\nno option with data-value={WANT!r}")
pick.first.click()
b.page.wait_for_timeout(1500)
now = (target.inner_text() or "").replace("\n", " ").strip()
print(f"\ntrigger now reads: {now!r}")
print("VERIFIED" if WANT.lower() in now.lower()
else "NOT APPLIED — the click did not take")
"""Open the real upload form and WATCH what PLN clicks.
My probes concluded the v2 uploader is public-only: its radios are geo and
licensing, and "private" appears nowhere in the frame text. PLN says there is a
private option and offered to point at it — which is faster and more reliable
than another round of my guessing, and this is the third time in this project
that the instrument was the thing that was wrong.
So: attach a probe file (the metadata form only renders once audio is attached),
hold the window open, and poll the form state. Whenever a control's checked-state
or the visible text changes, print the delta. PLN clicks Private; the script
reports exactly which element that is, and the driver gets written against it.
Uploads nothing — never touches the Upload button.
"""
import sys
import time
from pathlib import Path
sys.path.insert(0, "/home/pln/Work/Sound/tidal-ears/src")
from tidal_ears import consent # noqa: E402
from tidal_ears.scbrowser import Browser # noqa: E402
PROBE = Path(sys.argv[1])
HOLD = float(sys.argv[2]) if len(sys.argv) > 2 else 420.0
STATE = """() => {
const sel = (el) => {
if (el.id) return '#' + el.id;
if (el.name) return `${el.tagName.toLowerCase()}[name="${el.name}"]`;
const p = el.closest('[class]');
return `${el.tagName.toLowerCase()}[value="${el.value}"]`
+ (p ? ` in .${(p.className||'').toString().split(/\\s+/)[0]}` : '');
};
const near = (el) => {
let n = el, best = '', d = 0;
while (n && d < 7) {
const t = (n.innerText || '').replace(/\\s+/g, ' ').trim();
if (t.length > best.length) best = t;
if (best.length > 25) break;
n = n.parentElement; d++;
}
return best.slice(0, 90);
};
const controls = {};
document.querySelectorAll(
'input[type=radio], input[type=checkbox], [role=switch], select'
).forEach((el, i) => {
const key = `${i}:${sel(el)}:${el.value || ''}`;
controls[key] = {
on: el.type === 'checkbox' || el.type === 'radio'
? el.checked
: (el.getAttribute('aria-checked') ?? el.value),
label: near(el),
};
});
return {controls, text: (document.body.innerText || '')};
}"""
def diff(a, b):
out = []
for k, v in b["controls"].items():
old = a["controls"].get(k)
if old is None:
out.append(f"NEW {k} on={v['on']} {v['label']!r}")
elif old["on"] != v["on"]:
out.append(f"CHANGED {k} {old['on']} -> {v['on']} {v['label']!r}")
for k in a["controls"]:
if k not in b["controls"]:
out.append(f"GONE {k}")
ta, tb = set(a["text"].splitlines()), set(b["text"].splitlines())
for line in list(tb - ta)[:8]:
if line.strip():
out.append(f"TEXT+ {line.strip()[:80]!r}")
return out
with Browser() as b:
b.open()
consent.ensure(b.page, policy="reject")
b.require_login()
b.page.goto("https://soundcloud.com/upload", wait_until="domcontentloaded")
frame = b.upload_frame()
if frame is None:
sys.exit("no upload frame")
frame.locator('input[type="file"]').first.set_input_files(str(PROBE))
b.page.wait_for_timeout(14000)
prev = frame.evaluate(STATE)
print(f"form ready — {len(prev['controls'])} controls tracked.\n"
f"Click the PRIVATE option (open any collapsed section if it is in one).\n"
f"Watching for {HOLD:.0f}s. Do NOT press Upload — this window will "
f"close on its own.\n", flush=True)
deadline = time.time() + HOLD
while time.time() < deadline:
b.page.wait_for_timeout(2500)
try:
cur = frame.evaluate(STATE)
except Exception as e:
print(" (frame went away:", str(e).splitlines()[0][:60], ")", flush=True)
break
d = diff(prev, cur)
if d:
print(f"[+{HOLD - (deadline - time.time()):.0f}s]", flush=True)
for line in d:
print(" ", line, flush=True)
prev = cur
print("\ndone watching", flush=True)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment