/* Every browse the media service answered, as `LibraryBridge` logged it —
 *  one line per node, in the order the browser asked for them.
 *
 *  Round fifty put the car's browse tree behind a read-only SQLite
 *  connection in a process with NO Activity, no Tauri and no WebView, which
 *  is the whole point of it: the tree has to be there before the app is.
 *  That also means there is no screen to read it back from and no page to
 *  ask, so logcat is the only place on the device this exists at all. `n`,
 *  `br`, `pl`, `uniq`, `titled`, `art` and `fail` are the service's own
 *  count of what it handed the browser; `ms` is measured inside the Rust
 *  call and `hop` around the whole JNI round trip, so a gap between the two
 *  is the bridge rather than the database. `note=` goes LAST because a
 *  sqlite message may hold anything, spaces and `=` included.
 *
 *  A line that does not parse is simply not a row here, and every check
 *  below fails on a row that is MISSING rather than on a sentinel — which
 *  is why each of them names the node it wanted. */
function autoNodes() {
    try {
        const out = adb("logcat", "-d", "-s", "SoulStream:D").toString();
        const rows = [];
        for (const line of out.split(/\r?\n/)) {
            const m = line.match(/auto node=(\S+) n=(-?\d+) br=(-?\d+) pl=(-?\d+) uniq=(-?\d+) titled=(-?\d+) art=(-?\d+) fail=(-?\d+) lim=(-?\d+) open=(\S+) ms=(-?\d+) hop=(-?\d+) kinds=(\S*) note=(.*)$/);
            if (!m) continue;
            rows.push({
                node: m[1], n: +m[2], br: +m[3], pl: +m[4], uniq: +m[5], titled: +m[6],
                art: +m[7], fail: +m[8], lim: +m[9], open: m[10], ms: +m[11], hop: +m[12],
                kinds: m[13], note: m[14].trim(),
            });
        }
        return rows;
    } catch { return []; }
}
