// Round nineteen: downloads on Wi-Fi only — a switch the phone needs and the // desktop never did. One guard (network.svelte.js) before every download // the phone can start; streaming is never refused. const fs = require('fs'); const root = process.env.SS_ROOT || 'F:/Coding/SoulStream/'; let p, s; const load = (file) => { p = file; s = fs.readFileSync(root + p, 'utf8'); }; const save = () => fs.writeFileSync(root + p, s); const once = (a) => { const i = s.indexOf(a); if (i < 0) throw new Error('missing in ' + p + ': ' + a.slice(0, 90)); if (s.indexOf(a, i + 1) >= 0) throw new Error('ambiguous in ' + p + ': ' + a.slice(0, 90)); return i; }; const rep = (a, b) => { once(a); s = s.replace(a, () => b); }; fs.copyFileSync('C:/Users/Kyle/AppData/Local/Temp/claude/F--Coding-SoulStream/5488514a-11e8-482a-b0a0-20cb511ee110/scratchpad/network.svelte.js', root + 'src/mobile/network.svelte.js'); load('src/mobile/prefs.svelte.js'); rep(`export const settings = $state({ ambience: true, accent: true, motion: true, glass: false });`, `export const settings = $state({ ambience: true, accent: true, motion: true, glass: false, wifiOnly: false });`); save(); // ── the guards ── load('src/mobile/resolve.svelte.js'); rep(`import { yearOf } from "./catalog.svelte.js";`, `import { yearOf } from "./catalog.svelte.js"; import { downloadsAllowed } from "./network.svelte.js";`); rep(`export async function saveCatalogAlbum(album) {`, `export async function saveCatalogAlbum(album) { if (!downloadsAllowed()) return false;`); rep(`export async function saveSong(artist, title) { if (resolver.active) return false;`, `export async function saveSong(artist, title) { if (resolver.active) return false; if (!downloadsAllowed()) return false;`); save(); load('src/mobile/playlists.svelte.js'); rep(`import { slsk } from "./slsk.svelte.js";`, `import { slsk } from "./slsk.svelte.js"; import { downloadsAllowed } from "./network.svelte.js";`); rep(`export async function runBulk(p, items, mode) { if (bulk.active) return false;`, `export async function runBulk(p, items, mode) { if (bulk.active) return false; if (mode === "download" && !downloadsAllowed()) return false;`); save(); load('src/mobile/HalfSheet.svelte'); rep(` import { art, wantArt } from "./art.svelte.js";`, ` import { art, wantArt } from "./art.svelte.js"; import { downloadsAllowed } from "./network.svelte.js";`); rep(` async function saveStreams(entries) {`, ` async function saveStreams(entries) { if (!downloadsAllowed()) return;`); rep(` async function saveGroup(g) {`, ` async function saveGroup(g) { if (!downloadsAllowed()) return;`); rep(`act(() => api.downloadFile(g.username, f.path, f.size).then(() => note(`, `act(() => { if (!downloadsAllowed()) return; api.downloadFile(g.username, f.path, f.size).then(() => note(`); save(); // the file row's act closes with ")))}" — reshape to a block load('src/mobile/HalfSheet.svelte'); { const a = `act(() => { if (!downloadsAllowed()) return; api.downloadFile(g.username, f.path, f.size).then(() => note(\`Saving “\${titleFromName(f.name)}” from \${g.username}\`)).catch(oops)))}`; once(a); s = s.replace(a, () => `act(() => { if (!downloadsAllowed()) return; api.downloadFile(g.username, f.path, f.size).then(() => note(\`Saving “\${titleFromName(f.name)}” from \${g.username}\`)).catch(oops); }))}`); save(); } load('src/mobile/screens/Browse.svelte'); rep(` import { note } from "../notice.svelte.js";`, ` import { note } from "../notice.svelte.js"; import { downloadsAllowed } from "../network.svelte.js";`); rep(` async function save(d) { const list = d.files.filter(audio); if (!list.length) return;`, ` async function save(d) { const list = d.files.filter(audio); if (!list.length || !downloadsAllowed()) return;`); save(); // ── You: the switch ── load('src/mobile/screens/You.svelte'); rep(`
Downloads
`, `
Downloads
`); save(); // ── the harness handle ── load('src/mobile/Mobile.svelte'); rep(` import { bindShare } from "./share.svelte.js";`, ` import { bindShare } from "./share.svelte.js"; import { downloadsAllowed, cellular } from "./network.svelte.js";`); rep(`sources, api, transfers, pls, notice, importer, next,`, `sources, api, transfers, pls, notice, importer, wifi: { downloadsAllowed, cellular }, next,`); save(); // ── the walk: the switch on, the connection made cellular, a download refused ── load('tests/mobile/smoke.mjs'); rep(` navBadges: [].map.call(document.querySelectorAll('.nav .nb'), function (b) { return b.textContent.trim(); }).join('|'),`, ` navBadges: [].map.call(document.querySelectorAll('.nav .nb'), function (b) { return b.textContent.trim(); }).join('|'), wifiOnly: (function () { var r = [].slice.call(document.querySelectorAll('.screen .row')).find(function (x) { return /Wi-Fi only/.test(x.textContent); }); return r ? (r.querySelector('.sw.on') ? 'on' : 'off') : ''; })(),`); rep(` { do: \`__click('.screen .row', 'Clear play history')\`, name: "history-confirm", wait: 600 },`, ` /* downloads on Wi-Fi only: the switch on, the connection made cellular, a download refused; then off */ { do: \`__click('.screen .row', 'Wi-Fi only')\`, name: "wifi-on", wait: 400 }, { do: \`(function () { try { Object.defineProperty(navigator, 'connection', { value: { type: 'cellular' }, configurable: true }); } catch (e) {} return __ss.wifi.downloadsAllowed(); })()\`, name: "wifi-refused", wait: 400 }, { do: \`__click('.toast:not(.notice)', null)\`, wait: 300 }, { do: \`__click('.screen .row', 'Wi-Fi only')\`, name: "wifi-off", wait: 400 }, { do: \`__click('.screen .row', 'Clear play history')\`, name: "history-confirm", wait: 600 },`); rep(` "history-confirm": (i) => i.half.includes("up") && /Clear the play history/.test(i.halfText),`, ` "wifi-on": (i) => i.wifiOnly === "on", "wifi-refused": (i) => /Wi-Fi/.test(i.toast), "wifi-off": (i) => i.wifiOnly === "off" && !i.toast, "history-confirm": (i) => i.half.includes("up") && /Clear the play history/.test(i.halfText),`); save(); // ── the doc ── load('ANDROID.md'); rep(`The nav wears two badges the desktop's chrome has:`, `One switch the desktop never needed: You → Downloads → "Downloads on Wi-Fi only" (\`network.svelte.js\`, off by default) holds every Save — a record, a track, a folder, a file, a playlist's bulk, the now-playing Save — while the phone is on mobile data (\`navigator.connection.type\`); streaming is never held back. The nav wears two badges the desktop's chrome has:`); save(); console.log('round nineteen wired');