openvibers/openvibe.live Commits
N-1 tests (roadmap WS-P task 11): the previous release's client against this server, and its SQL against this schema. test/n-1.test.js boots this checkout in the drill sandbox (writes let through by test/n-1/preload.js) on a database created from the previous release's schema, migration ledger and first-use tables, so this release's migrations run over it; then it replays test/fixtures/n-1/client.json inside openvibe-shared's mixed-version matrix (release-compat assertMixedVersion): 411 requests the previous client makes (its api/fetch call sites, found statically, with seeded values; every call anonymously, reads signed in), each answered compatibly (any 2xx for a 2xx, the same status otherwise, JSON where there was JSON, every response field the old client names still there with a type it had). Then every statement in test/fixtures/n-1/worker.json (961: what that release ran, plus its SQL literals that prepare on its own schema) must still prepare on the migrated schema, and no old INSERT may miss a new NOT NULL column without a default. Fixtures recorded from 8caa540, the release in production; refresh after each deploy with `npm run n-1:record [ref]` (scripts/n-1-record.js: a temporary worktree of the release, seeded, booted and replayed). Runs in npm test, so in CI, in about 6 s, with no git history or network. Checked by breaking it: a dropped serializer field, a dropped column and a table rebuilt with a new required column each fail it. docs/deploy.md
Changes
@@@ -4,7 +4,8 @@ on:branches: [main]pull_request:jobs:-- # install, node --check, npm test (with the home page size budgets) and openvibe-contracts-check:++ # install, node --check, npm test (with the home page size budgets and test/n-1.test.js: the previous++ # release's client and SQL against this one, from test/fixtures/n-1) and openvibe-contracts-check:# the shared test job (Track Q).test:uses: OpenVibers/OpenVibe.Shared/.github/workflows/test.yml@423fe519b5fe9594cc81b670e5ba5cb9f21f6176@@@ -123,3 +123,24 @@ npm test # unit, security, migrations, deploy simulationBASE=http://127.0.0.1:3000 npm run test:browser # needs a running server and Chromedeploy/scripts/post-deploy-check.sh # on the host after a deploy```++++## N-1: the previous release against this one++++For 24 hours after a deploy (ADR-016) open tabs run the previous release's client against the new++server, and a previous-release process may still be working on the database the new one migrated.++`test/n-1.test.js` (in `npm test`, so in CI) checks both from fixtures recorded from the release in++production:++++- `test/fixtures/n-1/client.json`: every call the previous release's client code makes, with the++ status, JSON-ness and the response fields it reads. This checkout boots in the drill sandbox on a++ database created with the previous schema, answers each call compatibly, and keeps every read field.++- `test/fixtures/n-1/worker.json`: the previous schema, migration ledger and every SQL statement that++ release ran or has as a literal. After this release's migrations each must still prepare, and no++ old INSERT may miss a new NOT NULL column.++++After each deploy, record the release now in production as the next release's N-1 and commit it:++++```bash++npm run n-1:record # from HEAD (the deployed commit)++npm run n-1:record -- <sha> # or the release production's /release.json names++```@@@ -13,7 +13,8 @@"test:powerchat": "for f in test/powerchat-*.test.js; do node \"$f\" || exit 1; done","test:browser": "node test/browser/smoke.js","perf:page": "node scripts/perf/measure-page.js",-- "perf:budget": "node scripts/perf/check-budgets.js"++ "perf:budget": "node scripts/perf/check-budgets.js",++ "n-1:record": "node scripts/n-1-record.js"},"keywords": ["streaming",nnew file mode 100644@@@ -0,0 +1,99 @@++#!/usr/bin/env node++'use strict';++/**++ * Records the N-1 fixtures (roadmap WS-P task 11) from a release: what its client calls and reads,++ * and what SQL it runs on which schema. test/n-1.test.js replays them against this checkout.++ *++ * npm run n-1:record # from HEAD: run it right after a deploy, from the deployed commit,++ * # so the fixture is N-1 for the next release++ * npm run n-1:record -- <ref> # from another commit (e.g. the sha in production's /release.json)++ *++ * The release is checked out into a temporary git worktree (sharing node_modules), seeded and booted++ * the way test/n-1/service.js says, and every call its client code makes (test/n-1/harness.js finds++ * them) is replayed against it. Writes test/fixtures/n-1/client.json and worker.json; commit them.++ */++const fs = require('fs');++const os = require('os');++const path = require('path');++const Database = require('better-sqlite3');++const h = require('../test/n-1/harness');++const svc = require('../test/n-1/service');++++const ROOT = path.join(__dirname, '..');++const OUT = path.join(ROOT, 'test', 'fixtures', 'n-1');++++(async () => {++ const ref = process.argv[2] || 'HEAD';++ const wt = h.worktree(ROOT, ref);++ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-n-1-record-'));++ const dbPath = path.join(tmp, 'db', `${svc.service}.db`);++ const dataDir = path.join(tmp, 'data');++ const sqlOut = path.join(tmp, 'sql.json');++ fs.mkdirSync(path.dirname(dbPath), { recursive: true });++ fs.mkdirSync(dataDir, { recursive: true });++ let server = null;++ try {++ console.log(`n-1:record: ${svc.service} at ${wt.sha.slice(0, 12)} (${ref})`);++++ // 1. The client: its calls, and the names it reads.++ const files = svc.clientFiles(wt.dir);++ const { calls, dynamic } = h.extractCalls({ files, callers: svc.callers, strip: svc.strip, keep: svc.keep });++ const ids = h.clientIdentifiers(files.map((f) => f.text));++ console.log(` client: ${files.length} files, ${calls.length} call sites with a path (${dynamic} with a computed one, not replayed)`);++++ // 2. The release, seeded and booted; every call replayed.++ await svc.seed({ dir: wt.dir, dbPath, dataDir });++ server = await svc.boot({ dir: wt.dir, dbPath, dataDir, sqlOut });++ const manifest = await (await fetch(`${server.url}/release.json`)).json();++ const recorded = [];++ let unanswered = 0; let noRoute = 0;++ for (const req of h.requestsFor(calls, svc.samples, { signedIn: svc.signedIn !== false })) {++ const got = await h.send(server.url, { method: req.method, path: req.path, headers: server.headers(req.auth), body: req.method === 'GET' ? undefined : {} });++ if (got.status === 0) { unanswered++; continue; }++ // No route for this path on N-1 itself (a sample value its route does not take, or a dead call):++ // nothing N has to keep.++ if (got.status === 404 && got.kind !== 'json') { noRoute++; continue; }++ const rec = { method: req.method, path: req.path, auth: req.auth, status: got.status, kind: got.kind };++ if (got.location) rec.location = got.location;++ if (got.json != null && got.status >= 200 && got.status < 300) {++ const reads = h.readsOf(got.json, ids);++ if (Object.keys(reads).length) rec.reads = reads;++ }++ rec.from = req.from;++ recorded.push(rec);++ }++ const extra = svc.recordExtra ? await svc.recordExtra({ server, dir: wt.dir, files, ids }) : null;++ await server.close();++ server = null;++ console.log(` replayed: ${recorded.length} requests (left out: ${unanswered} unanswered, ${noRoute} with no route on N-1)`);++++ // 3. The schema N-1 left, and the SQL it runs on it.++ const db = new Database(dbPath);++ const schema = h.schemaDDL(db);++ const ledger = {};++ for (const t of svc.ledgerTables || []) {++ try { ledger[t] = db.prepare(`SELECT * FROM "${t}"`).all(); } catch { /* not there */ }++ }++ const userVersion = db.pragma('user_version', { simple: true });++ const serverFiles = h.readTree(wt.dir, svc.sqlDirs);++ // What N-1 creates itself on first use or at a full boot: in production it exists before N deploys.++ const lazy = h.lazyDDL(serverFiles, db);++ const ran = fs.existsSync(sqlOut) ? JSON.parse(fs.readFileSync(sqlOut, 'utf8')) : [];++ const candidates = [...new Set([...ran.map(h.normalizeSql).filter((s) => /^\s*(SELECT|INSERT|UPDATE|DELETE|REPLACE|WITH)\b/i.test(s)), ...h.sqlLiterals(serverFiles)])].sort();++ const failing = new Set(h.prepareProblems(db, candidates).map((p) => p.sql));++ const statements = candidates.filter((s) => !failing.has(s));++ db.close();++ console.log(` sql: ${statements.length} statements (${ran.length} ran, ${failing.size} candidates that do not prepare on N-1's own schema left out), ${schema.length} schema objects, ${lazy.length} created on first use`);++++ fs.mkdirSync(OUT, { recursive: true });++ const head = { service: svc.service, release: wt.sha, recorded_at: new Date().toISOString(), note: 'N-1 fixture: written by `npm run n-1:record`, replayed by test/n-1.test.js' };++ fs.writeFileSync(path.join(OUT, 'client.json'), `${JSON.stringify({ ...head, manifest, dynamic_calls: dynamic, calls: recorded, ...(extra ? { extra } : {}) }, null, 1)}\n`);++ fs.writeFileSync(path.join(OUT, 'worker.json'), `${JSON.stringify({ ...head, user_version: userVersion, schema, ledger, lazy, statements }, null, 1)}\n`);++ console.log(` wrote ${path.relative(ROOT, OUT)}/client.json and worker.json`);++ } finally {++ if (server) await server.close();++ wt.remove();++ fs.rmSync(tmp, { recursive: true, force: true });++ }++})().catch((err) => { console.error(err.stack || err.message); process.exit(1); });++nnew file mode 100644@@@ -0,0 +1,8393 @@++{++ "service": "live",++ "release": "8caa540466f9af86247febdbba1339b68919a42f",++ "recorded_at": "2026-09-26T13:44:31.818Z",++ "note": "N-1 fixture: written by `npm run n-1:record`, replayed by test/n-1.test.js",++ "manifest": {++ "service": "live",++ "release": "8caa540466f9",++ "released_at": "2026-09-26T13:22:32.000Z",++ "booted_at": "2026-09-26T13:44:29.263Z",++ "contracts_version": "0.56.0",++ "packages": {++ "openvibe-shared": "1.22.0",++ "openvibe-sdk": "0.11.0"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "components": {++ "shell": {++ "kind": "script",++ "version": "8caa540466f9"++ },++ "server": {++ "kind": "server",++ "version": "8caa540466f9"++ }++ },++ "assets": {},++ "schema_generation": null,++ "schema_compatible_from": null,++ "contract_ranges": {},++ "metrics_url": "/release-metrics"++ },++ "dynamic_calls": 49,++ "calls": [++ {++ "method": "GET",++ "path": "/api/ai-viewers/config",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/ai-viewers/config",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "config": "object",++ "config.enabled": "boolean",++ "config.use_shared_key": "boolean",++ "config.daily_budget_cents": "number",++ "config.byo_base_url": "string",++ "config.byo_model": "string",++ "config.has_byo_key": "boolean",++ "settings": "object",++ "settings.roster_size": "number",++ "settings.activity": "string",++ "settings.lines_per_min": "number",++ "settings.min_gap_sec": "number",++ "settings.director_interval_sec": "number",++ "settings.idle_interval_sec": "number",++ "settings.lines_per_tick": "number",++ "settings.bot_to_bot_ratio": "number",++ "settings.reply_probability": "number",++ "settings.mention_fast_path_sec": "number",++ "settings.react_to_sounds": "boolean",++ "settings.react_to_scene_changes": "boolean",++ "settings.greet_first_timers": "boolean",++ "settings.reply_to_relay_chat": "boolean",++ "settings.max_open_threads": "number",++ "settings.max_thread_turns": "number",++ "settings.thread_idle_close_sec": "number",++ "settings.hear_enabled": "boolean",++ "settings.hear_window_sec": "number",++ "settings.vision_policy": "string",++ "settings.vision_max_age_sec": "number",++ "settings.vision_periodic_sec": "number",++ "settings.language": "string",++ "settings.tone": "string",++ "settings.emote_usage": "string",++ "settings.max_words": "number",++ "settings.topics": "string",++ "settings.blocklist": "string",++ "settings.channel_personality": "string",++ "settings.quiet_from": "string",++ "settings.quiet_to": "string",++ "settings.quiet_tz": "string",++ "settings.quiet_allow_replies": "boolean",++ "settings.tts_enabled": "boolean",++ "settings.powerchat_forward": "boolean",++ "settings.memory_fold_min": "number",++ "settings.remember_viewers": "boolean",++ "settings.channel_memory": "string",++ "settings.slots": "object",++ "settings.byo": "object",++ "settings.runtime": "object",++ "schema": "array",++ "schema[]": "object",++ "schema[].key": "string",++ "schema[].type": "string",++ "schema[].help": "string",++ "schema[].min": "number",++ "schema[].max": "number",++ "schema[].values": "array",++ "schema[].values[]": "string",++ "budget": "object",++ "budget.active": "boolean",++ "budget.reason": "string",++ "budget.use_shared_key": "boolean",++ "budget.spent_today_usd": "number",++ "budget.cap_usd": "number",++ "status": "object",++ "status.engine": "string",++ "status.running": "boolean",++ "status.mode": "string",++ "status.budget_active": "boolean",++ "status.budget_reason": "string",++ "status.spent_today_usd": "number",++ "status.cap_usd": "number",++ "status.timeline_enabled": "boolean",++ "status.kill_switch": "boolean",++ "engine": "string"++ },++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/ai-viewers/log?after=0&limit=60",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/ai-viewers/log?after=0&limit=60",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "rows": "array"++ },++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/ai-viewers/roster",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/ai-viewers/roster",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "bots": "array"++ },++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/ai-viewers/status",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/ai-viewers/status",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "status": "object",++ "status.engine": "string",++ "status.running": "boolean",++ "status.mode": "string",++ "status.budget_active": "boolean",++ "status.budget_reason": "string",++ "status.spent_today_usd": "number",++ "status.cap_usd": "number",++ "status.timeline_enabled": "boolean",++ "status.kill_switch": "boolean",++ "last_hour": "object",++ "last_hour.lines": "null",++ "last_hour.ticks": "null",++ "last_hour.skips": "null",++ "last_hour.cost_usd": "number",++ "last_hour.tokens_in": "number",++ "last_hour.tokens_cached": "number",++ "budget": "object",++ "budget.active": "boolean",++ "budget.reason": "string",++ "budget.use_shared_key": "boolean",++ "budget.spent_today_usd": "number",++ "budget.cap_usd": "number"++ },++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/analytics/channel/n1star?days=",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "username": "string",++ "display_name": "string",++ "avatar_url": "null",++ "summary": "object",++ "summary.total_streams": "number",++ "summary.total_duration_seconds": "number",++ "summary.peak_viewers": "number",++ "summary.avg_viewers_per_stream": "number",++ "summary.total_messages": "number",++ "summary.total_unique_chatters": "number",++ "all_time": "object",++ "all_time.total_streams": "number",++ "all_time.total_duration_seconds": "number",++ "all_time.peak_viewers": "number",++ "all_time.follower_count": "number",++ "streams": "array"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/analytics/channel/n1star?days=",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "username": "string",++ "display_name": "string",++ "avatar_url": "null",++ "summary": "object",++ "summary.total_streams": "number",++ "summary.total_duration_seconds": "number",++ "summary.peak_viewers": "number",++ "summary.avg_viewers_per_stream": "number",++ "summary.total_messages": "number",++ "summary.total_unique_chatters": "number",++ "all_time": "object",++ "all_time.total_streams": "number",++ "all_time.total_duration_seconds": "number",++ "all_time.peak_viewers": "number",++ "all_time.follower_count": "number",++ "streams": "array"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/analytics/stream/1",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "stream": "object",++ "stream.id": "number",++ "stream.title": "string",++ "stream.category": "string",++ "stream.started_at": "string",++ "stream.ended_at": "null",++ "stream.duration_seconds": "number",++ "stream.peak_viewers": "number",++ "stream.is_live": "boolean",++ "streamer": "object",++ "streamer.username": "string",++ "streamer.display_name": "string",++ "streamer.avatar_url": "null",++ "analytics": "null",++ "viewer_chart": "array"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/analytics/stream/1",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "stream": "object",++ "stream.id": "number",++ "stream.title": "string",++ "stream.category": "string",++ "stream.started_at": "string",++ "stream.ended_at": "null",++ "stream.duration_seconds": "number",++ "stream.peak_viewers": "number",++ "stream.is_live": "boolean",++ "streamer": "object",++ "streamer.username": "string",++ "streamer.display_name": "string",++ "streamer.avatar_url": "null",++ "analytics": "null",++ "viewer_chart": "array"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/beefs",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "open": "array",++ "resolved": "array"++ },++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/beefs",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "open": "array",++ "resolved": "array"++ },++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/beefs/1",++ "auth": "anon",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/beefs/1",++ "auth": "user",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/console/n1star",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "fighter": "object",++ "fighter.user": "object",++ "fighter.user.id": "number",++ "fighter.user.username": "string",++ "fighter.user.display_name": "string",++ "fighter.user.avatar_url": "null",++ "fighter.user.profile_color": "string",++ "fighter.user.bio": "string",++ "fighter.fighter_name": "string",++ "fighter.rank": "null",++ "fighter.image_url": "null",++ "fighter.live": "boolean",++ "fighter.level": "number",++ "on_roster": "boolean",++ "live": "boolean",++ "stream": "object",++ "stream.id": "number",++ "stream.title": "string",++ "stream.started_at": "string",++ "stream.viewer_count": "number",++ "transcribed": "boolean",++ "listener": "object",++ "listener.listening": "boolean",++ "level": "object",++ "level.level": "number",++ "level.xp": "number",++ "level.xp_into_level": "number",++ "level.xp_per_level": "number",++ "level.mic_moments": "number",++ "level.beef_hits": "number",++ "level.recent_xp": "number",++ "level.best_line": "null",++ "mic": "object",++ "mic.moments": "number",++ "mic.beef_hits": "number",++ "mic.bangers": "number",++ "mic.avg_quality": "number",++ "mic.targeted": "number",++ "mic.answered": "number",++ "mic.wins": "number",++ "mic.losses": "number",++ "mic.speech_minutes": "number",++ "open_beefs": "array",++ "hot_mic": "array",++ "recent_moments": "array"++ },++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/console/n1star",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "fighter": "object",++ "fighter.user": "object",++ "fighter.user.id": "number",++ "fighter.user.username": "string",++ "fighter.user.display_name": "string",++ "fighter.user.avatar_url": "null",++ "fighter.user.profile_color": "string",++ "fighter.user.bio": "string",++ "fighter.fighter_name": "string",++ "fighter.rank": "null",++ "fighter.image_url": "null",++ "fighter.live": "boolean",++ "fighter.level": "number",++ "on_roster": "boolean",++ "live": "boolean",++ "stream": "object",++ "stream.id": "number",++ "stream.title": "string",++ "stream.started_at": "string",++ "stream.viewer_count": "number",++ "transcribed": "boolean",++ "listener": "object",++ "listener.listening": "boolean",++ "level": "object",++ "level.level": "number",++ "level.xp": "number",++ "level.xp_into_level": "number",++ "level.xp_per_level": "number",++ "level.mic_moments": "number",++ "level.beef_hits": "number",++ "level.recent_xp": "number",++ "level.best_line": "null",++ "mic": "object",++ "mic.moments": "number",++ "mic.beef_hits": "number",++ "mic.bangers": "number",++ "mic.avg_quality": "number",++ "mic.targeted": "number",++ "mic.answered": "number",++ "mic.wins": "number",++ "mic.losses": "number",++ "mic.speech_minutes": "number",++ "open_beefs": "array",++ "hot_mic": "array",++ "recent_moments": "array"++ },++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/feed?limit=40",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "feed": "array",++ "ai": "boolean"++ },++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/feed?limit=40",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "feed": "array",++ "ai": "boolean"++ },++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/fighters",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "fighters": "array",++ "ai": "boolean",++ "stats": "array",++ "stats[]": "string",++ "stat_meta": "object",++ "stat_meta.heat": "object",++ "stat_meta.heat.label": "string",++ "stat_meta.heat.desc": "string",++ "stat_meta.aim": "object",++ "stat_meta.aim.label": "string",++ "stat_meta.aim.desc": "string",++ "stat_meta.kills": "object",++ "stat_meta.kills.label": "string",++ "stat_meta.kills.desc": "string",++ "stat_meta.mouth": "object",++ "stat_meta.mouth.label": "string",++ "stat_meta.mouth.desc": "string",++ "stat_meta.clapback": "object",++ "stat_meta.clapback.label": "string",++ "stat_meta.clapback.desc": "string",++ "stat_meta.stamina": "object",++ "stat_meta.stamina.label": "string",++ "stat_meta.stamina.desc": "string",++ "stat_meta.pace": "object",++ "stat_meta.pace.label": "string",++ "stat_meta.pace.desc": "string"++ },++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/fighters",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "fighters": "array",++ "ai": "boolean",++ "stats": "array",++ "stats[]": "string",++ "stat_meta": "object",++ "stat_meta.heat": "object",++ "stat_meta.heat.label": "string",++ "stat_meta.heat.desc": "string",++ "stat_meta.aim": "object",++ "stat_meta.aim.label": "string",++ "stat_meta.aim.desc": "string",++ "stat_meta.kills": "object",++ "stat_meta.kills.label": "string",++ "stat_meta.kills.desc": "string",++ "stat_meta.mouth": "object",++ "stat_meta.mouth.label": "string",++ "stat_meta.mouth.desc": "string",++ "stat_meta.clapback": "object",++ "stat_meta.clapback.label": "string",++ "stat_meta.clapback.desc": "string",++ "stat_meta.stamina": "object",++ "stat_meta.stamina.label": "string",++ "stat_meta.stamina.desc": "string",++ "stat_meta.pace": "object",++ "stat_meta.pace.label": "string",++ "stat_meta.pace.desc": "string"++ },++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/fighters/n1star",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "user": "object",++ "user.id": "number",++ "user.username": "string",++ "user.display_name": "string",++ "user.avatar_url": "null",++ "user.profile_color": "string",++ "user.bio": "string",++ "not_on_roster": "boolean",++ "reason": "string"++ },++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/fighters/n1star",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "user": "object",++ "user.id": "number",++ "user.username": "string",++ "user.display_name": "string",++ "user.avatar_url": "null",++ "user.profile_color": "string",++ "user.bio": "string",++ "not_on_roster": "boolean",++ "reason": "string"++ },++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/fighters/n1star/stat/1",++ "auth": "anon",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/fighters/n1star/stat/1",++ "auth": "user",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/live",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "live": "array"++ },++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/live",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "live": "array"++ },++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/me",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/arena/me",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "on_roster": "boolean",++ "fighter": "null",++ "beefs": "array",++ "on_clock": "array",++ "moments": "array",++ "live": "boolean"++ },++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/auth/avatar/history",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/settings.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/auth/avatar/history",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "avatars": "array"++ },++ "from": [++ "public/js/settings.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/auth/ice-servers",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "iceServers": "array",++ "iceServers[]": "object",++ "iceServers[].urls": "string"++ },++ "from": [++ "public/js/call.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/auth/ice-servers",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "iceServers": "array",++ "iceServers[]": "object",++ "iceServers[].urls": "string"++ },++ "from": [++ "public/js/call.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/auth/me",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app.js",++ "public/js/settings.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/auth/me",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "user": "object",++ "user.id": "number",++ "user.username": "string",++ "user.display_name": "string",++ "user.avatar_url": "null",++ "user.bio": "string",++ "user.role": "string",++ "user.profile_color": "string",++ "user.created_at": "string",++ "user.capabilities": "object",++ "user.capabilities.admin_panel": "boolean",++ "user.capabilities.moderate_global": "boolean",++ "user.capabilities.manage_users": "boolean",++ "user.capabilities.manage_site_bans": "boolean",++ "user.capabilities.view_all_logs": "boolean",++ "user.capabilities.can_access_staff_console": "boolean",++ "user.capabilities.can_manage_channels": "boolean",++ "user.capabilities.view_ip_info": "boolean",++ "user.capabilities.is_owner": "boolean",++ "user.openvibe_coins_balance": "number",++ "user.stream_key": "string",++ "capabilities": "object",++ "capabilities.admin_panel": "boolean",++ "capabilities.moderate_global": "boolean",++ "capabilities.manage_users": "boolean",++ "capabilities.manage_site_bans": "boolean",++ "capabilities.view_all_logs": "boolean",++ "capabilities.can_access_staff_console": "boolean",++ "capabilities.can_manage_channels": "boolean",++ "capabilities.view_ip_info": "boolean",++ "capabilities.is_owner": "boolean"++ },++ "from": [++ "public/js/app.js",++ "public/js/settings.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/auth/preferences",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/auth/preferences",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "chatSettings": "object"++ },++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/auth/stream-key",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/auth/stream-key",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "stream_key": "string"++ },++ "from": [++ "public/js/app.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/auth/tokens",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/auth/tokens",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "tokens": "array",++ "scope_definitions": "array",++ "scope_definitions[]": "object",++ "scope_definitions[].id": "string",++ "scope_definitions[].label": "string",++ "scope_definitions[].title": "string",++ "scope_definitions[].description": "string",++ "token_presets": "array",++ "token_presets[]": "object",++ "token_presets[].id": "string",++ "token_presets[].label": "string",++ "token_presets[].description": "string",++ "token_presets[].suggested_label": "string",++ "token_presets[].scopes": "array",++ "token_presets[].scopes[]": "string",++ "max_active_tokens": "number",++ "active_token_count": "number"++ },++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/channels/moderation/mine",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-channel.js",++ "public/js/channel-uploads.js",++ "public/js/dashboard-moderation.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/channels/moderation/mine",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "channels": "array",++ "channels[]": "object",++ "channels[].id": "number",++ "channels[].user_id": "number",++ "channels[].title": "string",++ "channels[].description": "string",++ "channels[].category": "null",++ "channels[].tags": "string",++ "channels[].protocol": "string",++ "channels[].is_nsfw": "number",++ "channels[].panels": "string",++ "channels[].default_vod_visibility": "string",++ "channels[].default_clip_visibility": "string",++ "channels[].weather_zip": "null",++ "channels[].weather_detail": "string",++ "channels[].weather_show_location": "number",++ "channels[].control_mode": "string",++ "channels[].anon_controls_enabled": "number",++ "channels[].control_rate_limit_ms": "number",++ "channels[].video_click_enabled": "number",++ "channels[].video_click_rate_limit_ms": "number",++ "channels[].active_control_config_id": "null",++ "channels[].created_at": "string",++ "channels[].updated_at": "string",++ "channels[].clips_allow_creator_delete": "number",++ "channels[].hide_ai_overview": "number",++ "channels[].ai_overview_pref": "string",++ "channels[].ai_derivation_enabled": "number",++ "channels[].ai_category": "null",++ "channels[].offline_screen_type": "string",++ "channels[].offline_screen_url": "null",++ "channels[].offline_html": "null",++ "channels[].offline_css": "null",++ "channels[].moderation_settings": "object",++ "channels[].moderation_settings.channel_id": "number",++ "channels[].moderation_settings.slow_mode_seconds": "number",++ "channels[].moderation_settings.followers_only": "number",++ "channels[].moderation_settings.allow_anonymous": "number",++ "channels[].moderation_settings.links_allowed": "number",++ "channels[].moderation_settings.gifs_enabled": "number",++ "channels[].moderation_settings.account_age_gate_hours": "number",++ "channels[].moderation_settings.caps_percentage_limit": "number",++ "channels[].moderation_settings.aggressive_filter": "number",++ "channels[].moderation_settings.max_message_length": "number",++ "channels[].moderation_settings.tts_max_length": "number",++ "channels[].moderation_settings.slur_filter_enabled": "number",++ "channels[].moderation_settings.slur_filter_use_builtin": "number",++ "channels[].moderation_settings.slur_filter_terms": "string",++ "channels[].moderation_settings.slur_filter_regexes": "string",++ "channels[].moderation_settings.slur_filter_nudge_message": "string",++ "channels[].moderation_settings.slur_filter_disabled_categories": "string",++ "channels[].moderation_settings.ip_approval_mode": "number",++ "channels[].moderation_settings.soundboard_enabled": "number",++ "channels[].moderation_settings.soundboard_allow_pitch": "number",++ "channels[].moderation_settings.soundboard_allow_speed": "number",++ "channels[].moderation_settings.soundboard_banned_ids": "string",++ "channels[].moderation_settings.viewer_auto_delete_enabled": "number",++ "channels[].moderation_settings.viewer_delete_all_enabled": "number",++ "channels[].moderation_settings.custom_emotes_enabled": "number",++ "channels[].moderation_settings.custom_sounds_enabled": "number",++ "channels[].moderation_settings.max_sound_seconds": "number",++ "channels[].moderation_settings.uploads_mods_only": "number",++ "channels[].moderation_settings.mods_can_edit_about": "number",++ "channels[].moderation_settings.emote_scale": "number",++ "channels[].moderation_settings.emote_size_min": "number",++ "channels[].moderation_settings.emote_size_max": "number",++ "channels[].moderation_settings.sounds_mods_only": "number",++ "channels[].moderation_settings.sound_min_speed": "number",++ "channels[].moderation_settings.sound_max_speed": "number",++ "channels[].moderation_settings.sound_min_pitch_cents": "number",++ "channels[].moderation_settings.sound_max_pitch_cents": "number",++ "channels[].moderation_settings.sub_only": "number",++ "channels[].moderators": "array"++ },++ "from": [++ "public/js/app-channel.js",++ "public/js/channel-uploads.js",++ "public/js/dashboard-moderation.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/channels/1/moderation/chat-search",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard-moderation.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/channels/1/moderation/chat-search",++ "auth": "user",++ "status": 500,++ "kind": "json",++ "from": [++ "public/js/dashboard-moderation.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/channels/1/moderation/logs?limit=12",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard-moderation.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/channels/1/moderation/logs?limit=12",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "actions": "array"++ },++ "from": [++ "public/js/dashboard-moderation.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/chat-ai/global",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "insight": "null"++ },++ "from": [++ "public/js/app-chatpage.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/chat-ai/global",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "insight": "null"++ },++ "from": [++ "public/js/app-chatpage.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/chat-ai/live-captions/n1star?after=0",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "language": "object",++ "language.code": "string",++ "language.name": "string",++ "language.flag": "string",++ "available": "boolean",++ "live": "boolean",++ "stream_id": "number",++ "lines": "array"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/chat-ai/live-captions/n1star?after=0",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "language": "object",++ "language.code": "string",++ "language.name": "string",++ "language.flag": "string",++ "available": "boolean",++ "live": "boolean",++ "stream_id": "number",++ "lines": "array"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/chat-ai/timeline/n1star?offset=0&limit=5",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "username": "string",++ "display_name": "string",++ "overview": "null",++ "chatInsight": "null",++ "combinedOverview": "null",++ "sessionCount": "number",++ "momentCount": "number",++ "index": "array",++ "sessions": "array",++ "offset": "number",++ "limit": "number",++ "hasMore": "boolean"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/chat-ai/timeline/n1star?offset=0&limit=5",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "username": "string",++ "display_name": "string",++ "overview": "null",++ "chatInsight": "null",++ "combinedOverview": "null",++ "sessionCount": "number",++ "momentCount": "number",++ "index": "array",++ "sessions": "array",++ "offset": "number",++ "limit": "number",++ "hasMore": "boolean"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/chat-ai/timeline/n1star?offset=0&limit=12",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "username": "string",++ "display_name": "string",++ "overview": "null",++ "chatInsight": "null",++ "combinedOverview": "null",++ "sessionCount": "number",++ "momentCount": "number",++ "index": "array",++ "sessions": "array",++ "offset": "number",++ "limit": "number",++ "hasMore": "boolean"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/chat-ai/timeline/n1star?offset=0&limit=12",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "username": "string",++ "display_name": "string",++ "overview": "null",++ "chatInsight": "null",++ "combinedOverview": "null",++ "sessionCount": "number",++ "momentCount": "number",++ "index": "array",++ "sessions": "array",++ "offset": "number",++ "limit": "number",++ "hasMore": "boolean"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/chat-ai/transcript/1",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "streamId": "number",++ "vodId": "null",++ "segments": "array",++ "events": "array",++ "coverageSec": "number"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/chat-ai/transcript/1",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "streamId": "number",++ "vodId": "null",++ "segments": "array",++ "events": "array",++ "coverageSec": "number"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/clips/mine?limit=5&offset=5",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/clips/mine?limit=5&offset=5",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/clips/my-stream?auto_generated=0&limit=5&offset=5",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/clips/my-stream?auto_generated=0&limit=5&offset=5",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/clips/my-stream?auto_generated=1&limit=1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/clips/my-stream?auto_generated=1&limit=1",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/clips/my-stream?auto_generated=1&limit=5&offset=5",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/clips/my-stream?auto_generated=1&limit=5&offset=5",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/clips/settings/channel",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/clips/settings/channel",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "clips_allow_creator_delete": "boolean"++ },++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/clips/1",++ "auth": "anon",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app-media.js",++ "public/js/stream-player.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/clips/1",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app-media.js",++ "public/js/stream-player.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/clips?limit=5&offset=0&auto_generated=0",++ "auth": "anon",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/clips?limit=5&offset=0&auto_generated=0",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/coins/balance",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/coins/balance",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "balance": "number"++ },++ "from": [++ "public/js/app.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/coins/channel-balance?streamerId=1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/coins/channel-balance?streamerId=1",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "balance": "number",++ "streamerId": "number"++ },++ "from": [++ "public/js/app.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/coins/config/1",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "config": "object",++ "config.name": "string",++ "config.icon": "string",++ "config.watch_interval_min": "number",++ "config.watch_amount": "number",++ "config.game_interval_min": "number"++ },++ "from": [++ "public/js/opencoins.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/coins/config/1",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "config": "object",++ "config.name": "string",++ "config.icon": "string",++ "config.watch_interval_min": "number",++ "config.watch_amount": "number",++ "config.game_interval_min": "number"++ },++ "from": [++ "public/js/opencoins.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/coins/redemptions",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/opencoins.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/coins/redemptions",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "redemptions": "array"++ },++ "from": [++ "public/js/opencoins.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/coins/rewards/1",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "rewards": "array",++ "config": "object",++ "config.name": "string",++ "config.icon": "string",++ "config.watch_interval_min": "number",++ "config.watch_amount": "number",++ "config.game_interval_min": "number"++ },++ "from": [++ "public/js/opencoins.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/coins/rewards/1",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "rewards": "array",++ "config": "object",++ "config.name": "string",++ "config.icon": "string",++ "config.watch_interval_min": "number",++ "config.watch_amount": "number",++ "config.game_interval_min": "number"++ },++ "from": [++ "public/js/opencoins.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/comments/1/1",++ "auth": "anon",++ "status": 400,++ "kind": "json",++ "from": [++ "public/js/app-media.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/comments/1/1",++ "auth": "user",++ "status": 400,++ "kind": "json",++ "from": [++ "public/js/app-media.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/configs",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js",++ "public/js/broadcast.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/configs",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "configs": "array"++ },++ "from": [++ "public/js/broadcast-workspace.js",++ "public/js/broadcast.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/configs/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/configs/1",++ "auth": "user",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/presets",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "controls": "array",++ "settings": "object"++ },++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/presets",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "controls": "array",++ "settings": "object"++ },++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/presets/1",++ "auth": "anon",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/presets/1",++ "auth": "user",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/settings/channel",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/settings/channel",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "control_mode": "string",++ "anon_controls_enabled": "boolean",++ "control_rate_limit_ms": "number",++ "video_click_enabled": "boolean",++ "video_click_rate_limit_ms": "number",++ "active_control_config_id": "null"++ },++ "from": [++ "public/js/broadcast.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/whitelist",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/whitelist",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "whitelist": "array"++ },++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/1",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "controls": "array",++ "settings": "object",++ "settings.video_click_enabled": "boolean",++ "settings.control_mode": "string",++ "settings.anon_controls_enabled": "boolean",++ "settings.control_rate_limit_ms": "number",++ "settings.video_click_rate_limit_ms": "number"++ },++ "from": [++ "public/js/controls.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/1",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "controls": "array",++ "settings": "object",++ "settings.video_click_enabled": "boolean",++ "settings.control_mode": "string",++ "settings.anon_controls_enabled": "boolean",++ "settings.control_rate_limit_ms": "number",++ "settings.video_click_rate_limit_ms": "number"++ },++ "from": [++ "public/js/controls.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/1/config",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/controls/1/config",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "control_config_id": "null"++ },++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/cosmetics/catalog",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "catalog": "object",++ "catalog.name_effect": "array",++ "catalog.name_effect[]": "object",++ "catalog.name_effect[].itemId": "string",++ "catalog.name_effect[].name": "string",++ "catalog.name_effect[].emoji": "string",++ "catalog.name_effect[].category": "string",++ "catalog.name_effect[].tier": "number",++ "catalog.name_effect[].cssClass": "string",++ "catalog.name_effect[].desc": "string",++ "catalog.particle": "array",++ "catalog.particle[]": "object",++ "catalog.particle[].itemId": "string",++ "catalog.particle[].name": "string",++ "catalog.particle[].emoji": "string",++ "catalog.particle[].category": "string",++ "catalog.particle[].tier": "number",++ "catalog.particle[].cssClass": "string",++ "catalog.particle[].chars": "string",++ "catalog.particle[].desc": "string",++ "catalog.hat": "array",++ "catalog.hat[]": "object",++ "catalog.hat[].itemId": "string",++ "catalog.hat[].name": "string",++ "catalog.hat[].emoji": "string",++ "catalog.hat[].category": "string",++ "catalog.hat[].tier": "number",++ "catalog.hat[].hatChar": "string",++ "catalog.hat[].desc": "string",++ "catalog.hat[].animated": "string",++ "catalog.voice": "array",++ "catalog.voice[]": "object",++ "catalog.voice[].itemId": "string",++ "catalog.voice[].name": "string",++ "catalog.voice[].emoji": "string",++ "catalog.voice[].category": "string",++ "catalog.voice[].tier": "number",++ "catalog.voice[].desc": "string",++ "catalog.voice[].pitch": "number",++ "catalog.voice[].rate": "number"++ },++ "from": [++ "public/js/cosmetics-ui.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/cosmetics/catalog",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "catalog": "object",++ "catalog.name_effect": "array",++ "catalog.name_effect[]": "object",++ "catalog.name_effect[].itemId": "string",++ "catalog.name_effect[].name": "string",++ "catalog.name_effect[].emoji": "string",++ "catalog.name_effect[].category": "string",++ "catalog.name_effect[].tier": "number",++ "catalog.name_effect[].cssClass": "string",++ "catalog.name_effect[].desc": "string",++ "catalog.particle": "array",++ "catalog.particle[]": "object",++ "catalog.particle[].itemId": "string",++ "catalog.particle[].name": "string",++ "catalog.particle[].emoji": "string",++ "catalog.particle[].category": "string",++ "catalog.particle[].tier": "number",++ "catalog.particle[].cssClass": "string",++ "catalog.particle[].chars": "string",++ "catalog.particle[].desc": "string",++ "catalog.hat": "array",++ "catalog.hat[]": "object",++ "catalog.hat[].itemId": "string",++ "catalog.hat[].name": "string",++ "catalog.hat[].emoji": "string",++ "catalog.hat[].category": "string",++ "catalog.hat[].tier": "number",++ "catalog.hat[].hatChar": "string",++ "catalog.hat[].desc": "string",++ "catalog.hat[].animated": "string",++ "catalog.voice": "array",++ "catalog.voice[]": "object",++ "catalog.voice[].itemId": "string",++ "catalog.voice[].name": "string",++ "catalog.voice[].emoji": "string",++ "catalog.voice[].category": "string",++ "catalog.voice[].tier": "number",++ "catalog.voice[].desc": "string",++ "catalog.voice[].pitch": "number",++ "catalog.voice[].rate": "number"++ },++ "from": [++ "public/js/cosmetics-ui.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/cosmetics/inventory",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/cosmetics-ui.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/cosmetics/inventory",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "categories": "object",++ "categories.name_effect": "array",++ "categories.particle": "array",++ "categories.hat": "array",++ "categories.voice": "array",++ "equipped": "object"++ },++ "from": [++ "public/js/cosmetics-ui.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/easter-egg/daily",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "egg": "null"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/easter-egg/daily",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "egg": "null"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/emotes/all/1",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "defaults": "array",++ "defaults[]": "object",++ "defaults[].id": "string",++ "defaults[].code": "string",++ "defaults[].url": "string",++ "defaults[].source": "string",++ "defaults[].animated": "boolean",++ "defaults[].width": "number",++ "defaults[].height": "number",++ "channel": "array",++ "global": "array",++ "ffz": "array",++ "bttv": "array",++ "sources": "object",++ "sources.defaults": "boolean",++ "sources.custom": "boolean",++ "sources.ffz": "boolean",++ "sources.bttv": "boolean",++ "emote_scale": "number"++ },++ "from": [++ "public/js/emotes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/emotes/all/1",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "defaults": "array",++ "defaults[]": "object",++ "defaults[].id": "string",++ "defaults[].code": "string",++ "defaults[].url": "string",++ "defaults[].source": "string",++ "defaults[].animated": "boolean",++ "defaults[].width": "number",++ "defaults[].height": "number",++ "channel": "array",++ "global": "array",++ "ffz": "array",++ "bttv": "array",++ "sources": "object",++ "sources.defaults": "boolean",++ "sources.custom": "boolean",++ "sources.ffz": "boolean",++ "sources.bttv": "boolean",++ "emote_scale": "number"++ },++ "from": [++ "public/js/emotes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/emotes/all/1?channel=n1star",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "defaults": "array",++ "defaults[]": "object",++ "defaults[].id": "string",++ "defaults[].code": "string",++ "defaults[].url": "string",++ "defaults[].source": "string",++ "defaults[].animated": "boolean",++ "defaults[].width": "number",++ "defaults[].height": "number",++ "channel": "array",++ "global": "array",++ "ffz": "array",++ "bttv": "array",++ "sources": "object",++ "sources.defaults": "boolean",++ "sources.custom": "boolean",++ "sources.ffz": "boolean",++ "sources.bttv": "boolean",++ "emote_scale": "number"++ },++ "from": [++ "public/obs/chat.html"++ ]++ },++ {++ "method": "GET",++ "path": "/api/emotes/all/1?channel=n1star",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "defaults": "array",++ "defaults[]": "object",++ "defaults[].id": "string",++ "defaults[].code": "string",++ "defaults[].url": "string",++ "defaults[].source": "string",++ "defaults[].animated": "boolean",++ "defaults[].width": "number",++ "defaults[].height": "number",++ "channel": "array",++ "global": "array",++ "ffz": "array",++ "bttv": "array",++ "sources": "object",++ "sources.defaults": "boolean",++ "sources.custom": "boolean",++ "sources.ffz": "boolean",++ "sources.bttv": "boolean",++ "emote_scale": "number"++ },++ "from": [++ "public/obs/chat.html"++ ]++ },++ {++ "method": "GET",++ "path": "/api/emotes/mine",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/emotes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/emotes/mine",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "emotes": "array",++ "count": "number",++ "max": "number"++ },++ "from": [++ "public/js/emotes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/emotes/search?q=n1",++ "auth": "anon",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/emotes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/emotes/search?q=n1",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/emotes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/emotes/sources",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/emotes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/emotes/sources",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "sources": "object",++ "sources.defaults": "boolean",++ "sources.custom": "boolean",++ "sources.ffz": "boolean",++ "sources.bttv": "boolean"++ },++ "from": [++ "public/js/emotes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/funds/balance",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app.js",++ "public/js/dashboard.js",++ "public/js/vibes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/funds/balance",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "balance": "number",++ "cashout_balance": "number"++ },++ "from": [++ "public/js/app.js",++ "public/js/dashboard.js",++ "public/js/vibes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/funds/goals/manage/mine",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/funds/goals/manage/mine",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "goals": "array"++ },++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/funds/goals/1",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "goals": "array"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/funds/goals/1",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "goals": "array"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/funds/history?limit=100",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/funds/history?limit=100",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "transactions": "array"++ },++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/home/hero",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "stats": "object",++ "stats.viewersNow": "number",++ "stats.hoursWatched": "number",++ "stats.vibesTipped": "number",++ "stats.activeSubs": "number",++ "stats.pointsEarned": "number",++ "stats.pointsSpent": "number",++ "stats.redemptions": "number",++ "stats.supporters": "number",++ "stats.vibesBought": "number",++ "stats.goalsActive": "number",++ "stats.goalsReached": "number",++ "stats.vods": "null",++ "stats.clips": "null",++ "stats.liveSessions": "number",++ "stats.streamers": "number",++ "stats.chatMessages": "number",++ "stats.users": "number",++ "stats.anons": "number",++ "stats.follows": "number",++ "stats.emotes": "number",++ "stats.pastes": "null",++ "stats.aiMemories": "number",++ "stats.pasteImages": "null",++ "stats.pasteText": "null",++ "stats.streamHours": "null",++ "stats.weeklyActive": "number",++ "stats.weeklyVisitors": "number",++ "stats.prevWeeklyVisitors": "number",++ "stats.prevWeeklyActive": "number",++ "stats.liveNow": "number",++ "stats.recent": "object",++ "stats.recent.users": "object",++ "stats.recent.users.d": "number",++ "stats.recent.users.w": "number",++ "stats.recent.users.m": "number",++ "stats.recent.users.pw": "number",++ "stats.recent.anons": "object",++ "stats.recent.anons.d": "number",++ "stats.recent.anons.w": "number",++ "stats.recent.anons.m": "number",++ "stats.recent.anons.pw": "number",++ "stats.recent.sessions": "object",++ "stats.recent.sessions.d": "number",++ "stats.recent.sessions.w": "number",++ "stats.recent.sessions.m": "number",++ "stats.recent.sessions.pw": "number",++ "stats.recent.vods": "null",++ "stats.recent.clips": "null",++ "stats.recent.aiMoments": "object",++ "stats.recent.aiMoments.d": "number",++ "stats.recent.aiMoments.w": "number",++ "stats.recent.aiMoments.m": "number",++ "stats.recent.aiMoments.pw": "number",++ "stats.recent.messages": "object",++ "stats.recent.messages.d": "number",++ "stats.recent.messages.w": "number",++ "stats.recent.messages.m": "number",++ "stats.recent.messages.pw": "number",++ "stats.recent.hours": "null",++ "stats.recent.streamers": "object",++ "stats.recent.streamers.d": "number",++ "stats.recent.streamers.w": "number",++ "stats.recent.streamers.m": "number",++ "stats.recent.streamers.pw": "number",++ "stats.recent.emotes": "object",++ "stats.recent.emotes.d": "number",++ "stats.recent.emotes.w": "number",++ "stats.recent.emotes.m": "number",++ "stats.recent.emotes.pw": "number",++ "stats.recent.goals": "object",++ "stats.recent.goals.d": "number",++ "stats.recent.goals.w": "number",++ "stats.recent.goals.m": "number",++ "stats.recent.goals.pw": "number",++ "stats.recent.supporters": "object",++ "stats.recent.supporters.d": "number",++ "stats.recent.supporters.w": "number",++ "stats.recent.supporters.m": "number",++ "stats.recent.supporters.pw": "number",++ "stats.recent.vibes": "object",++ "stats.recent.vibes.d": "number",++ "stats.recent.vibes.w": "number",++ "stats.recent.vibes.m": "number",++ "stats.recent.vibes.pw": "number",++ "stats.recent.points": "object",++ "stats.recent.points.d": "number",++ "stats.recent.points.w": "number",++ "stats.recent.points.m": "number",++ "stats.recent.points.pw": "number",++ "stats.recent.pointsSpent": "object",++ "stats.recent.pointsSpent.d": "number",++ "stats.recent.pointsSpent.w": "number",++ "stats.recent.pointsSpent.m": "number",++ "stats.recent.pointsSpent.pw": "number",++ "stats.recent.redemptions": "object",++ "stats.recent.redemptions.d": "number",++ "stats.recent.redemptions.w": "number",++ "stats.recent.redemptions.m": "number",++ "stats.recent.redemptions.pw": "number",++ "stats.recent.vibesBought": "object",++ "stats.recent.vibesBought.d": "number",++ "stats.recent.vibesBought.w": "number",++ "stats.recent.vibesBought.m": "number",++ "stats.recent.vibesBought.pw": "number",++ "stats.recent.subs": "object",++ "stats.recent.subs.d": "number",++ "stats.recent.subs.w": "number",++ "stats.recent.subs.m": "number",++ "stats.recent.subs.pw": "number",++ "stats.recent.follows": "object",++ "stats.recent.follows.d": "number",++ "stats.recent.follows.w": "number",++ "stats.recent.follows.m": "number",++ "stats.recent.follows.pw": "number",++ "stats.concurrency": "object",++ "stats.concurrency.viewersAvg24h": "number",++ "stats.concurrency.viewersPeak24h": "number",++ "stats.concurrency.liveAvg24h": "number",++ "stats.concurrency.livePeak24h": "number",++ "stats.viewerTrend": "array",++ "media": "array",++ "media[]": "object",++ "media[].kind": "string",++ "media[].title": "string",++ "media[].thumbnail": "null",++ "media[].href": "string",++ "media[].username": "string",++ "moments": "array",++ "slogans": "object",++ "slogans.audiences": "array",++ "slogans.audiences[]": "string",++ "slogans.quips": "array",++ "slogans.quips[]": "string",++ "slogans.ai": "boolean",++ "slogans.updated_at": "null",++ "slogans.next_at": "null"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/home/hero",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "stats": "object",++ "stats.viewersNow": "number",++ "stats.hoursWatched": "number",++ "stats.vibesTipped": "number",++ "stats.activeSubs": "number",++ "stats.pointsEarned": "number",++ "stats.pointsSpent": "number",++ "stats.redemptions": "number",++ "stats.supporters": "number",++ "stats.vibesBought": "number",++ "stats.goalsActive": "number",++ "stats.goalsReached": "number",++ "stats.vods": "null",++ "stats.clips": "null",++ "stats.liveSessions": "number",++ "stats.streamers": "number",++ "stats.chatMessages": "number",++ "stats.users": "number",++ "stats.anons": "number",++ "stats.follows": "number",++ "stats.emotes": "number",++ "stats.pastes": "null",++ "stats.aiMemories": "number",++ "stats.pasteImages": "null",++ "stats.pasteText": "null",++ "stats.streamHours": "null",++ "stats.weeklyActive": "number",++ "stats.weeklyVisitors": "number",++ "stats.prevWeeklyVisitors": "number",++ "stats.prevWeeklyActive": "number",++ "stats.liveNow": "number",++ "stats.recent": "object",++ "stats.recent.users": "object",++ "stats.recent.users.d": "number",++ "stats.recent.users.w": "number",++ "stats.recent.users.m": "number",++ "stats.recent.users.pw": "number",++ "stats.recent.anons": "object",++ "stats.recent.anons.d": "number",++ "stats.recent.anons.w": "number",++ "stats.recent.anons.m": "number",++ "stats.recent.anons.pw": "number",++ "stats.recent.sessions": "object",++ "stats.recent.sessions.d": "number",++ "stats.recent.sessions.w": "number",++ "stats.recent.sessions.m": "number",++ "stats.recent.sessions.pw": "number",++ "stats.recent.vods": "null",++ "stats.recent.clips": "null",++ "stats.recent.aiMoments": "object",++ "stats.recent.aiMoments.d": "number",++ "stats.recent.aiMoments.w": "number",++ "stats.recent.aiMoments.m": "number",++ "stats.recent.aiMoments.pw": "number",++ "stats.recent.messages": "object",++ "stats.recent.messages.d": "number",++ "stats.recent.messages.w": "number",++ "stats.recent.messages.m": "number",++ "stats.recent.messages.pw": "number",++ "stats.recent.hours": "null",++ "stats.recent.streamers": "object",++ "stats.recent.streamers.d": "number",++ "stats.recent.streamers.w": "number",++ "stats.recent.streamers.m": "number",++ "stats.recent.streamers.pw": "number",++ "stats.recent.emotes": "object",++ "stats.recent.emotes.d": "number",++ "stats.recent.emotes.w": "number",++ "stats.recent.emotes.m": "number",++ "stats.recent.emotes.pw": "number",++ "stats.recent.goals": "object",++ "stats.recent.goals.d": "number",++ "stats.recent.goals.w": "number",++ "stats.recent.goals.m": "number",++ "stats.recent.goals.pw": "number",++ "stats.recent.supporters": "object",++ "stats.recent.supporters.d": "number",++ "stats.recent.supporters.w": "number",++ "stats.recent.supporters.m": "number",++ "stats.recent.supporters.pw": "number",++ "stats.recent.vibes": "object",++ "stats.recent.vibes.d": "number",++ "stats.recent.vibes.w": "number",++ "stats.recent.vibes.m": "number",++ "stats.recent.vibes.pw": "number",++ "stats.recent.points": "object",++ "stats.recent.points.d": "number",++ "stats.recent.points.w": "number",++ "stats.recent.points.m": "number",++ "stats.recent.points.pw": "number",++ "stats.recent.pointsSpent": "object",++ "stats.recent.pointsSpent.d": "number",++ "stats.recent.pointsSpent.w": "number",++ "stats.recent.pointsSpent.m": "number",++ "stats.recent.pointsSpent.pw": "number",++ "stats.recent.redemptions": "object",++ "stats.recent.redemptions.d": "number",++ "stats.recent.redemptions.w": "number",++ "stats.recent.redemptions.m": "number",++ "stats.recent.redemptions.pw": "number",++ "stats.recent.vibesBought": "object",++ "stats.recent.vibesBought.d": "number",++ "stats.recent.vibesBought.w": "number",++ "stats.recent.vibesBought.m": "number",++ "stats.recent.vibesBought.pw": "number",++ "stats.recent.subs": "object",++ "stats.recent.subs.d": "number",++ "stats.recent.subs.w": "number",++ "stats.recent.subs.m": "number",++ "stats.recent.subs.pw": "number",++ "stats.recent.follows": "object",++ "stats.recent.follows.d": "number",++ "stats.recent.follows.w": "number",++ "stats.recent.follows.m": "number",++ "stats.recent.follows.pw": "number",++ "stats.concurrency": "object",++ "stats.concurrency.viewersAvg24h": "number",++ "stats.concurrency.viewersPeak24h": "number",++ "stats.concurrency.liveAvg24h": "number",++ "stats.concurrency.livePeak24h": "number",++ "stats.viewerTrend": "array",++ "media": "array",++ "media[]": "object",++ "media[].kind": "string",++ "media[].title": "string",++ "media[].thumbnail": "null",++ "media[].href": "string",++ "media[].username": "string",++ "moments": "array",++ "slogans": "object",++ "slogans.audiences": "array",++ "slogans.audiences[]": "string",++ "slogans.quips": "array",++ "slogans.quips[]": "string",++ "slogans.ai": "boolean",++ "slogans.updated_at": "null",++ "slogans.next_at": "null"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/home/pulse",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "goals": "array",++ "latestTip": "null",++ "newestFollow": "object",++ "newestFollow.created_at": "string",++ "newestFollow.follower_username": "string",++ "newestFollow.follower_display": "string",++ "newestFollow.streamer_username": "string",++ "newestFollow.streamer_display": "string",++ "topSupporters": "array",++ "topEarners": "array",++ "moments": "array"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/home/pulse",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "goals": "array",++ "latestTip": "null",++ "newestFollow": "object",++ "newestFollow.created_at": "string",++ "newestFollow.follower_username": "string",++ "newestFollow.follower_display": "string",++ "newestFollow.streamer_username": "string",++ "newestFollow.streamer_display": "string",++ "topSupporters": "array",++ "topEarners": "array",++ "moments": "array"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/home/star",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "star": "null"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/home/star",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "star": "null"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/home/stats-live",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "liveNow": "number",++ "viewersNow": "number",++ "weeklyActive": "number",++ "users": "number",++ "weeklyVisitors": "number",++ "anons": "number",++ "chatMessages": "number",++ "streamers": "number",++ "hoursWatched": "number",++ "prevWeeklyVisitors": "number",++ "prevWeeklyActive": "number",++ "concurrency": "object",++ "concurrency.viewersAvg24h": "number",++ "concurrency.viewersPeak24h": "number",++ "concurrency.liveAvg24h": "number",++ "concurrency.livePeak24h": "number",++ "recent": "object",++ "recent.users": "object",++ "recent.users.d": "number",++ "recent.users.w": "number",++ "recent.users.m": "number",++ "recent.users.pw": "number",++ "recent.anons": "object",++ "recent.anons.d": "number",++ "recent.anons.w": "number",++ "recent.anons.m": "number",++ "recent.anons.pw": "number",++ "recent.messages": "object",++ "recent.messages.d": "number",++ "recent.messages.w": "number",++ "recent.messages.m": "number",++ "recent.messages.pw": "number",++ "recent.streamers": "object",++ "recent.streamers.d": "number",++ "recent.streamers.w": "number",++ "recent.streamers.m": "number",++ "recent.streamers.pw": "number"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/home/stats-live",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "liveNow": "number",++ "viewersNow": "number",++ "weeklyActive": "number",++ "users": "number",++ "weeklyVisitors": "number",++ "anons": "number",++ "chatMessages": "number",++ "streamers": "number",++ "hoursWatched": "number",++ "prevWeeklyVisitors": "number",++ "prevWeeklyActive": "number",++ "concurrency": "object",++ "concurrency.viewersAvg24h": "number",++ "concurrency.viewersPeak24h": "number",++ "concurrency.liveAvg24h": "number",++ "concurrency.livePeak24h": "number",++ "recent": "object",++ "recent.users": "object",++ "recent.users.d": "number",++ "recent.users.w": "number",++ "recent.users.m": "number",++ "recent.users.pw": "number",++ "recent.anons": "object",++ "recent.anons.d": "number",++ "recent.anons.w": "number",++ "recent.anons.m": "number",++ "recent.anons.pw": "number",++ "recent.messages": "object",++ "recent.messages.d": "number",++ "recent.messages.w": "number",++ "recent.messages.m": "number",++ "recent.messages.pw": "number",++ "recent.streamers": "object",++ "recent.streamers.d": "number",++ "recent.streamers.w": "number",++ "recent.streamers.m": "number",++ "recent.streamers.pw": "number"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/home/stats/series/1?days=",++ "auth": "anon",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/stats-nerds.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/home/stats/series/1?days=",++ "auth": "user",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/stats-nerds.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/kiosk/site?url=",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "reachable": "boolean"++ },++ "from": [++ "public/kiosk.html"++ ]++ },++ {++ "method": "GET",++ "path": "/api/kiosk/site?url=",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "reachable": "boolean"++ },++ "from": [++ "public/kiosk.html"++ ]++ },++ {++ "method": "GET",++ "path": "/api/media/channel/n1star",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "channel": "object",++ "channel.id": "number",++ "channel.user_id": "number",++ "channel.username": "string",++ "channel.display_name": "string",++ "channel.avatar_url": "null",++ "live_stream": "object",++ "live_stream.id": "number",++ "live_stream.user_id": "number",++ "live_stream.channel_id": "null",++ "live_stream.managed_stream_id": "number",++ "live_stream.control_config_id": "null",++ "live_stream.title": "string",++ "live_stream.description": "string",++ "live_stream.category": "string",++ "live_stream.tags": "string",++ "live_stream.protocol": "string",++ "live_stream.is_live": "number",++ "live_stream.is_nsfw": "number",++ "live_stream.viewer_count": "number",++ "live_stream.peak_viewers": "number",++ "live_stream.follower_count": "number",++ "live_stream.thumbnail_url": "null",++ "live_stream.started_at": "string",++ "live_stream.ended_at": "null",++ "live_stream.duration_seconds": "number",++ "live_stream.created_at": "string",++ "live_stream.ai_overview": "null",++ "live_stream.ai_title": "null",++ "live_stream.ai_category": "null",++ "live_stream.ai_tags": "null",++ "live_stream.ai_overview_short": "null",++ "live_stream.username": "string",++ "live_stream.display_name": "string",++ "live_stream.avatar_url": "null",++ "live_stream.profile_color": "string",++ "live_stream.managed_stream_slug": "null",++ "state": "object",++ "state.settings": "object",++ "state.settings.enabled": "number",++ "state.settings.request_cost": "number",++ "state.settings.max_per_user": "number",++ "state.settings.max_duration_seconds": "number",++ "state.settings.allow_youtube": "number",++ "state.settings.allow_vimeo": "number",++ "state.settings.allow_direct_media": "number",++ "state.settings.auto_advance": "number",++ "state.settings.cost_mode": "string",++ "state.settings.cost_per_minute": "number",++ "state.settings.currency": "string",++ "state.settings.allow_live": "number",++ "state.settings.user_id": "number",++ "state.settings.created_at": "string",++ "state.settings.updated_at": "string",++ "state.queue": "array",++ "state.history": "array",++ "is_owner": "boolean",++ "can_manage": "boolean",++ "pricing": "object",++ "pricing.currency": "string",++ "pricing.currency_label": "string",++ "pricing.cost_mode": "string",++ "pricing.request_cost": "number",++ "pricing.cost_per_minute": "number",++ "pricing.max_duration_seconds": "number"++ },++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/media/channel/n1star",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "channel": "object",++ "channel.id": "number",++ "channel.user_id": "number",++ "channel.username": "string",++ "channel.display_name": "string",++ "channel.avatar_url": "null",++ "live_stream": "object",++ "live_stream.id": "number",++ "live_stream.user_id": "number",++ "live_stream.channel_id": "null",++ "live_stream.managed_stream_id": "number",++ "live_stream.control_config_id": "null",++ "live_stream.title": "string",++ "live_stream.description": "string",++ "live_stream.category": "string",++ "live_stream.tags": "string",++ "live_stream.protocol": "string",++ "live_stream.is_live": "number",++ "live_stream.is_nsfw": "number",++ "live_stream.viewer_count": "number",++ "live_stream.peak_viewers": "number",++ "live_stream.follower_count": "number",++ "live_stream.thumbnail_url": "null",++ "live_stream.started_at": "string",++ "live_stream.ended_at": "null",++ "live_stream.duration_seconds": "number",++ "live_stream.created_at": "string",++ "live_stream.ai_overview": "null",++ "live_stream.ai_title": "null",++ "live_stream.ai_category": "null",++ "live_stream.ai_tags": "null",++ "live_stream.ai_overview_short": "null",++ "live_stream.username": "string",++ "live_stream.display_name": "string",++ "live_stream.avatar_url": "null",++ "live_stream.profile_color": "string",++ "live_stream.managed_stream_slug": "null",++ "state": "object",++ "state.settings": "object",++ "state.settings.enabled": "number",++ "state.settings.request_cost": "number",++ "state.settings.max_per_user": "number",++ "state.settings.max_duration_seconds": "number",++ "state.settings.allow_youtube": "number",++ "state.settings.allow_vimeo": "number",++ "state.settings.allow_direct_media": "number",++ "state.settings.auto_advance": "number",++ "state.settings.cost_mode": "string",++ "state.settings.cost_per_minute": "number",++ "state.settings.currency": "string",++ "state.settings.allow_live": "number",++ "state.settings.user_id": "number",++ "state.settings.created_at": "string",++ "state.settings.updated_at": "string",++ "state.queue": "array",++ "state.history": "array",++ "is_owner": "boolean",++ "can_manage": "boolean",++ "pricing": "object",++ "pricing.currency": "string",++ "pricing.currency_label": "string",++ "pricing.cost_mode": "string",++ "pricing.request_cost": "number",++ "pricing.cost_per_minute": "number",++ "pricing.max_duration_seconds": "number"++ },++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/media/queue/1/stream-url",++ "auth": "anon",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/broadcast.js",++ "public/js/media-player.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/media/queue/1/stream-url",++ "auth": "user",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/broadcast.js",++ "public/js/media-player.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/meta/openvibeapp",++ "auth": "anon",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/meta/openvibeapp",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/mod/ip-approval/1/pending",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard-moderation.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/mod/ip-approval/1/pending",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "pending": "array"++ },++ "from": [++ "public/js/dashboard-moderation.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/mod/ip/anon/n1star",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/mod/ip/anon/n1star",++ "auth": "user",++ "status": 403,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/mod/ip/user/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/mod/ip/user/1",++ "auth": "user",++ "status": 403,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/mod/tts-voice/1/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/mod/tts-voice/1/1",++ "auth": "user",++ "status": 403,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/news/my-settings",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/news/my-settings",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "enabled": "null"++ },++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/onvif/cameras",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/onvif-dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/onvif/cameras",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "cameras": "array"++ },++ "from": [++ "public/js/onvif-dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/onvif/cameras/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/controls.js",++ "public/js/onvif-dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/onvif/cameras/1",++ "auth": "user",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/controls.js",++ "public/js/onvif-dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/onvif/cameras/1/presets",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/controls.js",++ "public/js/onvif-dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/onvif/cameras/1/presets",++ "auth": "user",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/controls.js",++ "public/js/onvif-dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/pastes/by-user/n1star?limit=30",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "pastes": "array",++ "total": "number",++ "username": "string"++ },++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/pastes/by-user/n1star?limit=30",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "pastes": "array",++ "total": "number",++ "username": "string"++ },++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/pastes/by-user/n1star?limit=30&sort=",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "pastes": "array",++ "total": "number",++ "username": "string"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/pastes/by-user/n1star?limit=30&sort=",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "pastes": "array",++ "total": "number",++ "username": "string"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/pastes/config",++ "auth": "anon",++ "status": 504,++ "kind": "json",++ "from": [++ "public/js/pastes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/pastes/config",++ "auth": "user",++ "status": 504,++ "kind": "json",++ "from": [++ "public/js/pastes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/pastes/n1star",++ "auth": "anon",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/pastes/n1star",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/pastes/n1star/comments",++ "auth": "anon",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/pastes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/pastes/n1star/comments",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/pastes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/pastes?limit=5&offset=0&origin=user",++ "auth": "anon",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/pastes?limit=5&offset=0&origin=user",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/payments/channel/n1star",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "subscribed": "boolean",++ "subscriberCount": "number",++ "priceUsd": "number",++ "powerchat": "object",++ "powerchat.direct": "boolean",++ "powerchat.site": "boolean",++ "powerchat.feePct": "number",++ "powerchat.siteTotalUsd": "number"++ },++ "from": [++ "public/js/vibes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/payments/channel/n1star",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "subscribed": "boolean",++ "subscriberCount": "number",++ "priceUsd": "number",++ "powerchat": "object",++ "powerchat.direct": "boolean",++ "powerchat.site": "boolean",++ "powerchat.feePct": "number",++ "powerchat.siteTotalUsd": "number"++ },++ "from": [++ "public/js/vibes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/payments/config",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "enabled": "boolean",++ "paymentsMaster": "boolean",++ "packages": "array",++ "packages[]": "object",++ "packages[].bucks": "number",++ "packages[].usd": "number",++ "minPurchaseBucks": "number",++ "subPriceUsd": "number",++ "providers": "object",++ "providers.paypal": "boolean",++ "providers.stripe": "boolean",++ "providers.ccbill": "boolean",++ "providers.crypto": "boolean",++ "providers.powerchat": "boolean"++ },++ "from": [++ "public/js/vibes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/payments/config",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "enabled": "boolean",++ "paymentsMaster": "boolean",++ "packages": "array",++ "packages[]": "object",++ "packages[].bucks": "number",++ "packages[].usd": "number",++ "minPurchaseBucks": "number",++ "subPriceUsd": "number",++ "providers": "object",++ "providers.paypal": "boolean",++ "providers.stripe": "boolean",++ "providers.ccbill": "boolean",++ "providers.crypto": "boolean",++ "providers.powerchat": "boolean"++ },++ "from": [++ "public/js/vibes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/powerchat/donate-link?streamer_id=1",++ "auth": "anon",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/vibes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/powerchat/donate-link?streamer_id=1",++ "auth": "user",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/vibes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/powerchat/donate-link?streamer_id=1&goal_id=${goalId}",++ "auth": "anon",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/vibes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/powerchat/donate-link?streamer_id=1&goal_id=${goalId}",++ "auth": "user",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/vibes.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/powerchat/status",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/powerchat/status",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "enabled": "boolean",++ "configured": "boolean",++ "connected": "boolean",++ "connection_kind": "null",++ "username": "null",++ "tip_page_url": "null",++ "scope": "null",++ "scopes": "array",++ "missing_scopes": "array",++ "live": "null",++ "diagnosis": "array",++ "chat_relay": "object",++ "chat_relay.accepted": "number",++ "chat_relay.dropped": "number",++ "chat_relay.pending": "number",++ "last_error": "null"++ },++ "from": [++ "public/js/broadcast-workspace.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/powerchat/status?live=1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/powerchat.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/powerchat/status?live=1",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "enabled": "boolean",++ "configured": "boolean",++ "connected": "boolean",++ "connection_kind": "null",++ "username": "null",++ "tip_page_url": "null",++ "scope": "null",++ "scopes": "array",++ "missing_scopes": "array",++ "live": "null",++ "diagnosis": "array",++ "chat_relay": "object",++ "chat_relay.accepted": "number",++ "chat_relay.dropped": "number",++ "chat_relay.pending": "number",++ "last_error": "null"++ },++ "from": [++ "public/js/powerchat.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/recap/1",++ "auth": "anon",++ "status": 409,++ "kind": "json",++ "from": [++ "public/js/recap.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/recap/1",++ "auth": "user",++ "status": 409,++ "kind": "json",++ "from": [++ "public/js/recap.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/restream/destinations?managed_stream_id=1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/restream/destinations?managed_stream_id=1",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "destinations": "array"++ },++ "from": [++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/restream/oauth/status",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js",++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/restream/oauth/status",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "platforms": "array",++ "platforms[]": "object",++ "platforms[].platform": "string",++ "platforms[].name": "string",++ "platforms[].configured": "boolean",++ "platforms[].providesKey": "boolean",++ "platforms[].connected": "boolean",++ "platforms[].username": "null",++ "platforms[].channel_url": "null"++ },++ "from": [++ "public/js/broadcast-workspace.js",++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/restream/status",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/restream/status",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "statuses": "object",++ "robotstreamer": "object"++ },++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/robotstreamer/integration?managed_stream_id=1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/robotstreamer/integration?managed_stream_id=1",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "integration": "object",++ "integration.enabled": "boolean",++ "integration.mirror_chat": "boolean",++ "integration.has_token": "boolean",++ "integration.robot_id": "string",++ "integration.owner_id": "string",++ "integration.stream_name": "string",++ "integration.owner_name": "string",++ "integration.managed_stream_id": "null",++ "integration.available_robots": "array",++ "integration.passthrough": "boolean",++ "exists": "boolean"++ },++ "from": [++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/search/suggest",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "suggestions": "array"++ },++ "from": [++ "public/js/app-search.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/search/suggest",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "suggestions": "array"++ },++ "from": [++ "public/js/app-search.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/search",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "results": "array",++ "next_cursor": "null"++ },++ "from": [++ "public/js/app-search.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/search",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "results": "array",++ "next_cursor": "null"++ },++ "from": [++ "public/js/app-search.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "null",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null",++ "streams[].browser_mode": "string",++ "streams[].streaming_method": "string",++ "streams[].channel": "object",++ "streams[].channel.id": "number",++ "streams[].channel.user_id": "number",++ "streams[].channel.title": "string",++ "streams[].channel.description": "string",++ "streams[].channel.category": "null",++ "streams[].channel.tags": "string",++ "streams[].channel.protocol": "string",++ "streams[].channel.is_nsfw": "number",++ "streams[].channel.panels": "string",++ "streams[].channel.default_vod_visibility": "string",++ "streams[].channel.default_clip_visibility": "string",++ "streams[].channel.weather_detail": "string",++ "streams[].channel.weather_show_location": "number",++ "streams[].channel.control_mode": "string",++ "streams[].channel.anon_controls_enabled": "number",++ "streams[].channel.control_rate_limit_ms": "number",++ "streams[].channel.video_click_enabled": "number",++ "streams[].channel.video_click_rate_limit_ms": "number",++ "streams[].channel.active_control_config_id": "null",++ "streams[].channel.created_at": "string",++ "streams[].channel.updated_at": "string",++ "streams[].channel.clips_allow_creator_delete": "number",++ "streams[].channel.hide_ai_overview": "number",++ "streams[].channel.ai_overview_pref": "string",++ "streams[].channel.ai_derivation_enabled": "number",++ "streams[].channel.ai_category": "null",++ "streams[].channel.offline_screen_type": "string",++ "streams[].channel.offline_screen_url": "null",++ "streams[].channel.offline_html": "null",++ "streams[].channel.offline_css": "null",++ "streams[].external_viewer_count": "number",++ "streams[].total_viewer_count": "number"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "null",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null",++ "streams[].browser_mode": "string",++ "streams[].streaming_method": "string",++ "streams[].channel": "object",++ "streams[].channel.id": "number",++ "streams[].channel.user_id": "number",++ "streams[].channel.title": "string",++ "streams[].channel.description": "string",++ "streams[].channel.category": "null",++ "streams[].channel.tags": "string",++ "streams[].channel.protocol": "string",++ "streams[].channel.is_nsfw": "number",++ "streams[].channel.panels": "string",++ "streams[].channel.default_vod_visibility": "string",++ "streams[].channel.default_clip_visibility": "string",++ "streams[].channel.weather_detail": "string",++ "streams[].channel.weather_show_location": "number",++ "streams[].channel.control_mode": "string",++ "streams[].channel.anon_controls_enabled": "number",++ "streams[].channel.control_rate_limit_ms": "number",++ "streams[].channel.video_click_enabled": "number",++ "streams[].channel.video_click_rate_limit_ms": "number",++ "streams[].channel.active_control_config_id": "null",++ "streams[].channel.created_at": "string",++ "streams[].channel.updated_at": "string",++ "streams[].channel.clips_allow_creator_delete": "number",++ "streams[].channel.hide_ai_overview": "number",++ "streams[].channel.ai_overview_pref": "string",++ "streams[].channel.ai_derivation_enabled": "number",++ "streams[].channel.ai_category": "null",++ "streams[].channel.offline_screen_type": "string",++ "streams[].channel.offline_screen_url": "null",++ "streams[].channel.offline_html": "null",++ "streams[].channel.offline_css": "null",++ "streams[].external_viewer_count": "number",++ "streams[].total_viewer_count": "number"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/broadcast-settings?managed_stream_id=1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/broadcast-settings?managed_stream_id=1",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "settings": "object",++ "managed_stream_id": "number"++ },++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js",++ "public/js/settings.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "id": "number",++ "user_id": "number",++ "title": "string",++ "description": "string",++ "category": "null",++ "tags": "string",++ "protocol": "string",++ "is_nsfw": "number",++ "panels": "string",++ "default_vod_visibility": "string",++ "default_clip_visibility": "string",++ "weather_zip": "null",++ "weather_detail": "string",++ "weather_show_location": "number",++ "control_mode": "string",++ "anon_controls_enabled": "number",++ "control_rate_limit_ms": "number",++ "video_click_enabled": "number",++ "video_click_rate_limit_ms": "number",++ "active_control_config_id": "null",++ "created_at": "string",++ "updated_at": "string",++ "clips_allow_creator_delete": "number",++ "hide_ai_overview": "number",++ "ai_overview_pref": "string",++ "ai_derivation_enabled": "number",++ "ai_category": "null",++ "offline_screen_type": "string",++ "offline_screen_url": "null",++ "offline_html": "null",++ "offline_css": "null"++ },++ "from": [++ "public/js/dashboard.js",++ "public/js/settings.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star?aiClipsLimit=5&aiClipsOffset=0",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "videos_tab_hidden": "boolean",++ "clips_tab_hidden": "boolean",++ "pasteTotal": "number",++ "clipsTakenTotal": "number",++ "aiEventTotal": "number",++ "channel": "object",++ "channel.id": "number",++ "channel.user_id": "number",++ "channel.title": "string",++ "channel.description": "string",++ "channel.category": "null",++ "channel.tags": "string",++ "channel.protocol": "string",++ "channel.is_nsfw": "number",++ "channel.panels": "string",++ "channel.default_vod_visibility": "string",++ "channel.default_clip_visibility": "string",++ "channel.weather_detail": "string",++ "channel.weather_show_location": "number",++ "channel.control_mode": "string",++ "channel.anon_controls_enabled": "number",++ "channel.control_rate_limit_ms": "number",++ "channel.video_click_enabled": "number",++ "channel.video_click_rate_limit_ms": "number",++ "channel.active_control_config_id": "null",++ "channel.created_at": "string",++ "channel.updated_at": "string",++ "channel.clips_allow_creator_delete": "number",++ "channel.hide_ai_overview": "number",++ "channel.ai_overview_pref": "string",++ "channel.ai_derivation_enabled": "number",++ "channel.ai_category": "null",++ "channel.offline_screen_type": "string",++ "channel.offline_screen_url": "null",++ "channel.offline_html": "null",++ "channel.offline_css": "null",++ "channel.username": "string",++ "channel.display_name": "string",++ "channel.avatar_url": "null",++ "channel.profile_color": "string",++ "channel.bio": "string",++ "channel.role": "string",++ "channel.is_owner": "number",++ "channel.follower_count": "number",++ "channel.is_following": "boolean",++ "channel.chat_limits": "object",++ "channel.chat_limits.max_message_length": "number",++ "channel.chat_limits.tts_max_length": "number",++ "channel.mods_can_edit_about": "boolean",++ "channel.viewer_can_edit_about": "boolean",++ "channel.ai_overview": "null",++ "language": "object",++ "language.code": "string",++ "language.name": "string",++ "language.flag": "string",++ "language.translate": "boolean",++ "stream": "object",++ "stream.id": "number",++ "stream.user_id": "number",++ "stream.channel_id": "null",++ "stream.managed_stream_id": "number",++ "stream.control_config_id": "null",++ "stream.title": "string",++ "stream.description": "string",++ "stream.category": "string",++ "stream.tags": "string",++ "stream.protocol": "string",++ "stream.is_live": "number",++ "stream.is_nsfw": "number",++ "stream.viewer_count": "number",++ "stream.peak_viewers": "number",++ "stream.follower_count": "number",++ "stream.thumbnail_url": "null",++ "stream.started_at": "string",++ "stream.ended_at": "null",++ "stream.duration_seconds": "number",++ "stream.created_at": "string",++ "stream.ai_overview": "null",++ "stream.ai_title": "null",++ "stream.ai_category": "null",++ "stream.ai_tags": "null",++ "stream.ai_overview_short": "null",++ "stream.username": "string",++ "stream.display_name": "string",++ "stream.avatar_url": "null",++ "stream.profile_color": "string",++ "stream.managed_stream_slug": "null",++ "stream.endpoint": "object",++ "stream.server_clip": "boolean",++ "stream.external_viewer_count": "number",++ "stream.total_viewer_count": "number",++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "null",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null",++ "streams[].endpoint": "object",++ "streams[].server_clip": "boolean",++ "streams[].external_viewer_count": "number",++ "streams[].total_viewer_count": "number",++ "managed_streams": "array",++ "managed_streams[]": "object",++ "managed_streams[].id": "number",++ "managed_streams[].user_id": "number",++ "managed_streams[].channel_id": "number",++ "managed_streams[].slug": "null",++ "managed_streams[].title": "string",++ "managed_streams[].description": "string",++ "managed_streams[].category": "string",++ "managed_streams[].tags": "string",++ "managed_streams[].protocol": "string",++ "managed_streams[].is_nsfw": "number",++ "managed_streams[].control_config_id": "null",++ "managed_streams[].sort_order": "number",++ "managed_streams[].created_at": "string",++ "managed_streams[].updated_at": "string",++ "managed_streams[].slot_clip_recording_enabled": "number",++ "managed_streams[].slot_clip_notify_enabled": "number",++ "managed_streams[].slot_powerchat_relay": "number",++ "managed_streams[].slot_powerchat_count_rs_views": "number",++ "managed_streams[].streaming_method": "string",++ "managed_streams[].browser_mode": "string",++ "managed_streams[].default_vod_visibility": "string",++ "managed_streams[].default_clip_visibility": "string",++ "managed_streams[].slot_vod_recording_enabled": "number",++ "managed_streams[].weather_detail": "string",++ "managed_streams[].weather_show_location": "number",++ "managed_streams[].last_live_at": "string",++ "managed_streams[].is_currently_live": "number",++ "managed_streams[].live_session_id": "number",++ "rs_restream": "null",++ "restream_links": "null",++ "external_viewers": "null",++ "vods": "array",++ "vodTotal": "number",++ "vodLimit": "number",++ "vodOffset": "number",++ "clips": "array",++ "clipTotal": "number",++ "clipLimit": "number",++ "clipOffset": "number",++ "clipsOfStreams": "array",++ "clipsOfTotal": "number",++ "clipsOfLimit": "number",++ "clipsOfOffset": "number",++ "aiClips": "array",++ "aiClipsTotal": "number",++ "aiClipsLimit": "number",++ "aiClipsOffset": "number"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star?aiClipsLimit=5&aiClipsOffset=0",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "videos_tab_hidden": "boolean",++ "clips_tab_hidden": "boolean",++ "pasteTotal": "number",++ "clipsTakenTotal": "number",++ "aiEventTotal": "number",++ "channel": "object",++ "channel.id": "number",++ "channel.user_id": "number",++ "channel.title": "string",++ "channel.description": "string",++ "channel.category": "null",++ "channel.tags": "string",++ "channel.protocol": "string",++ "channel.is_nsfw": "number",++ "channel.panels": "string",++ "channel.default_vod_visibility": "string",++ "channel.default_clip_visibility": "string",++ "channel.weather_detail": "string",++ "channel.weather_show_location": "number",++ "channel.control_mode": "string",++ "channel.anon_controls_enabled": "number",++ "channel.control_rate_limit_ms": "number",++ "channel.video_click_enabled": "number",++ "channel.video_click_rate_limit_ms": "number",++ "channel.active_control_config_id": "null",++ "channel.created_at": "string",++ "channel.updated_at": "string",++ "channel.clips_allow_creator_delete": "number",++ "channel.hide_ai_overview": "number",++ "channel.ai_overview_pref": "string",++ "channel.ai_derivation_enabled": "number",++ "channel.ai_category": "null",++ "channel.offline_screen_type": "string",++ "channel.offline_screen_url": "null",++ "channel.offline_html": "null",++ "channel.offline_css": "null",++ "channel.username": "string",++ "channel.display_name": "string",++ "channel.avatar_url": "null",++ "channel.profile_color": "string",++ "channel.bio": "string",++ "channel.role": "string",++ "channel.is_owner": "number",++ "channel.follower_count": "number",++ "channel.is_following": "boolean",++ "channel.chat_limits": "object",++ "channel.chat_limits.max_message_length": "number",++ "channel.chat_limits.tts_max_length": "number",++ "channel.mods_can_edit_about": "boolean",++ "channel.viewer_can_edit_about": "boolean",++ "channel.ai_overview": "null",++ "language": "object",++ "language.code": "string",++ "language.name": "string",++ "language.flag": "string",++ "language.translate": "boolean",++ "stream": "object",++ "stream.id": "number",++ "stream.user_id": "number",++ "stream.channel_id": "null",++ "stream.managed_stream_id": "number",++ "stream.control_config_id": "null",++ "stream.title": "string",++ "stream.description": "string",++ "stream.category": "string",++ "stream.tags": "string",++ "stream.protocol": "string",++ "stream.is_live": "number",++ "stream.is_nsfw": "number",++ "stream.viewer_count": "number",++ "stream.peak_viewers": "number",++ "stream.follower_count": "number",++ "stream.thumbnail_url": "null",++ "stream.started_at": "string",++ "stream.ended_at": "null",++ "stream.duration_seconds": "number",++ "stream.created_at": "string",++ "stream.ai_overview": "null",++ "stream.ai_title": "null",++ "stream.ai_category": "null",++ "stream.ai_tags": "null",++ "stream.ai_overview_short": "null",++ "stream.username": "string",++ "stream.display_name": "string",++ "stream.avatar_url": "null",++ "stream.profile_color": "string",++ "stream.managed_stream_slug": "null",++ "stream.endpoint": "object",++ "stream.server_clip": "boolean",++ "stream.external_viewer_count": "number",++ "stream.total_viewer_count": "number",++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "null",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null",++ "streams[].endpoint": "object",++ "streams[].server_clip": "boolean",++ "streams[].external_viewer_count": "number",++ "streams[].total_viewer_count": "number",++ "managed_streams": "array",++ "managed_streams[]": "object",++ "managed_streams[].id": "number",++ "managed_streams[].user_id": "number",++ "managed_streams[].channel_id": "number",++ "managed_streams[].slug": "null",++ "managed_streams[].title": "string",++ "managed_streams[].description": "string",++ "managed_streams[].category": "string",++ "managed_streams[].tags": "string",++ "managed_streams[].protocol": "string",++ "managed_streams[].is_nsfw": "number",++ "managed_streams[].control_config_id": "null",++ "managed_streams[].sort_order": "number",++ "managed_streams[].created_at": "string",++ "managed_streams[].updated_at": "string",++ "managed_streams[].slot_clip_recording_enabled": "number",++ "managed_streams[].slot_clip_notify_enabled": "number",++ "managed_streams[].slot_powerchat_relay": "number",++ "managed_streams[].slot_powerchat_count_rs_views": "number",++ "managed_streams[].streaming_method": "string",++ "managed_streams[].browser_mode": "string",++ "managed_streams[].default_vod_visibility": "string",++ "managed_streams[].default_clip_visibility": "string",++ "managed_streams[].slot_vod_recording_enabled": "number",++ "managed_streams[].weather_detail": "string",++ "managed_streams[].weather_show_location": "number",++ "managed_streams[].last_live_at": "string",++ "managed_streams[].is_currently_live": "number",++ "managed_streams[].live_session_id": "number",++ "rs_restream": "null",++ "restream_links": "null",++ "external_viewers": "null",++ "vods": "array",++ "vodTotal": "number",++ "vodLimit": "number",++ "vodOffset": "number",++ "clips": "array",++ "clipTotal": "number",++ "clipLimit": "number",++ "clipOffset": "number",++ "clipsOfStreams": "array",++ "clipsOfTotal": "number",++ "clipsOfLimit": "number",++ "clipsOfOffset": "number",++ "aiClips": "array",++ "aiClipsTotal": "number",++ "aiClipsLimit": "number",++ "aiClipsOffset": "number"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star?clipsOfLimit=5&clipsOfOffset=0",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "videos_tab_hidden": "boolean",++ "clips_tab_hidden": "boolean",++ "pasteTotal": "number",++ "clipsTakenTotal": "number",++ "aiEventTotal": "number",++ "channel": "object",++ "channel.id": "number",++ "channel.user_id": "number",++ "channel.title": "string",++ "channel.description": "string",++ "channel.category": "null",++ "channel.tags": "string",++ "channel.protocol": "string",++ "channel.is_nsfw": "number",++ "channel.panels": "string",++ "channel.default_vod_visibility": "string",++ "channel.default_clip_visibility": "string",++ "channel.weather_detail": "string",++ "channel.weather_show_location": "number",++ "channel.control_mode": "string",++ "channel.anon_controls_enabled": "number",++ "channel.control_rate_limit_ms": "number",++ "channel.video_click_enabled": "number",++ "channel.video_click_rate_limit_ms": "number",++ "channel.active_control_config_id": "null",++ "channel.created_at": "string",++ "channel.updated_at": "string",++ "channel.clips_allow_creator_delete": "number",++ "channel.hide_ai_overview": "number",++ "channel.ai_overview_pref": "string",++ "channel.ai_derivation_enabled": "number",++ "channel.ai_category": "null",++ "channel.offline_screen_type": "string",++ "channel.offline_screen_url": "null",++ "channel.offline_html": "null",++ "channel.offline_css": "null",++ "channel.username": "string",++ "channel.display_name": "string",++ "channel.avatar_url": "null",++ "channel.profile_color": "string",++ "channel.bio": "string",++ "channel.role": "string",++ "channel.is_owner": "number",++ "channel.follower_count": "number",++ "channel.is_following": "boolean",++ "channel.chat_limits": "object",++ "channel.chat_limits.max_message_length": "number",++ "channel.chat_limits.tts_max_length": "number",++ "channel.mods_can_edit_about": "boolean",++ "channel.viewer_can_edit_about": "boolean",++ "channel.ai_overview": "null",++ "language": "object",++ "language.code": "string",++ "language.name": "string",++ "language.flag": "string",++ "language.translate": "boolean",++ "stream": "object",++ "stream.id": "number",++ "stream.user_id": "number",++ "stream.channel_id": "null",++ "stream.managed_stream_id": "number",++ "stream.control_config_id": "null",++ "stream.title": "string",++ "stream.description": "string",++ "stream.category": "string",++ "stream.tags": "string",++ "stream.protocol": "string",++ "stream.is_live": "number",++ "stream.is_nsfw": "number",++ "stream.viewer_count": "number",++ "stream.peak_viewers": "number",++ "stream.follower_count": "number",++ "stream.thumbnail_url": "null",++ "stream.started_at": "string",++ "stream.ended_at": "null",++ "stream.duration_seconds": "number",++ "stream.created_at": "string",++ "stream.ai_overview": "null",++ "stream.ai_title": "null",++ "stream.ai_category": "null",++ "stream.ai_tags": "null",++ "stream.ai_overview_short": "null",++ "stream.username": "string",++ "stream.display_name": "string",++ "stream.avatar_url": "null",++ "stream.profile_color": "string",++ "stream.managed_stream_slug": "null",++ "stream.endpoint": "object",++ "stream.server_clip": "boolean",++ "stream.external_viewer_count": "number",++ "stream.total_viewer_count": "number",++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "null",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null",++ "streams[].endpoint": "object",++ "streams[].server_clip": "boolean",++ "streams[].external_viewer_count": "number",++ "streams[].total_viewer_count": "number",++ "managed_streams": "array",++ "managed_streams[]": "object",++ "managed_streams[].id": "number",++ "managed_streams[].user_id": "number",++ "managed_streams[].channel_id": "number",++ "managed_streams[].slug": "null",++ "managed_streams[].title": "string",++ "managed_streams[].description": "string",++ "managed_streams[].category": "string",++ "managed_streams[].tags": "string",++ "managed_streams[].protocol": "string",++ "managed_streams[].is_nsfw": "number",++ "managed_streams[].control_config_id": "null",++ "managed_streams[].sort_order": "number",++ "managed_streams[].created_at": "string",++ "managed_streams[].updated_at": "string",++ "managed_streams[].slot_clip_recording_enabled": "number",++ "managed_streams[].slot_clip_notify_enabled": "number",++ "managed_streams[].slot_powerchat_relay": "number",++ "managed_streams[].slot_powerchat_count_rs_views": "number",++ "managed_streams[].streaming_method": "string",++ "managed_streams[].browser_mode": "string",++ "managed_streams[].default_vod_visibility": "string",++ "managed_streams[].default_clip_visibility": "string",++ "managed_streams[].slot_vod_recording_enabled": "number",++ "managed_streams[].weather_detail": "string",++ "managed_streams[].weather_show_location": "number",++ "managed_streams[].last_live_at": "string",++ "managed_streams[].is_currently_live": "number",++ "managed_streams[].live_session_id": "number",++ "rs_restream": "null",++ "restream_links": "null",++ "external_viewers": "null",++ "vods": "array",++ "vodTotal": "number",++ "vodLimit": "number",++ "vodOffset": "number",++ "clips": "array",++ "clipTotal": "number",++ "clipLimit": "number",++ "clipOffset": "number",++ "clipsOfStreams": "array",++ "clipsOfTotal": "number",++ "clipsOfLimit": "number",++ "clipsOfOffset": "number",++ "aiClips": "array",++ "aiClipsTotal": "number",++ "aiClipsLimit": "number",++ "aiClipsOffset": "number"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star?clipsOfLimit=5&clipsOfOffset=0",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "videos_tab_hidden": "boolean",++ "clips_tab_hidden": "boolean",++ "pasteTotal": "number",++ "clipsTakenTotal": "number",++ "aiEventTotal": "number",++ "channel": "object",++ "channel.id": "number",++ "channel.user_id": "number",++ "channel.title": "string",++ "channel.description": "string",++ "channel.category": "null",++ "channel.tags": "string",++ "channel.protocol": "string",++ "channel.is_nsfw": "number",++ "channel.panels": "string",++ "channel.default_vod_visibility": "string",++ "channel.default_clip_visibility": "string",++ "channel.weather_detail": "string",++ "channel.weather_show_location": "number",++ "channel.control_mode": "string",++ "channel.anon_controls_enabled": "number",++ "channel.control_rate_limit_ms": "number",++ "channel.video_click_enabled": "number",++ "channel.video_click_rate_limit_ms": "number",++ "channel.active_control_config_id": "null",++ "channel.created_at": "string",++ "channel.updated_at": "string",++ "channel.clips_allow_creator_delete": "number",++ "channel.hide_ai_overview": "number",++ "channel.ai_overview_pref": "string",++ "channel.ai_derivation_enabled": "number",++ "channel.ai_category": "null",++ "channel.offline_screen_type": "string",++ "channel.offline_screen_url": "null",++ "channel.offline_html": "null",++ "channel.offline_css": "null",++ "channel.username": "string",++ "channel.display_name": "string",++ "channel.avatar_url": "null",++ "channel.profile_color": "string",++ "channel.bio": "string",++ "channel.role": "string",++ "channel.is_owner": "number",++ "channel.follower_count": "number",++ "channel.is_following": "boolean",++ "channel.chat_limits": "object",++ "channel.chat_limits.max_message_length": "number",++ "channel.chat_limits.tts_max_length": "number",++ "channel.mods_can_edit_about": "boolean",++ "channel.viewer_can_edit_about": "boolean",++ "channel.ai_overview": "null",++ "language": "object",++ "language.code": "string",++ "language.name": "string",++ "language.flag": "string",++ "language.translate": "boolean",++ "stream": "object",++ "stream.id": "number",++ "stream.user_id": "number",++ "stream.channel_id": "null",++ "stream.managed_stream_id": "number",++ "stream.control_config_id": "null",++ "stream.title": "string",++ "stream.description": "string",++ "stream.category": "string",++ "stream.tags": "string",++ "stream.protocol": "string",++ "stream.is_live": "number",++ "stream.is_nsfw": "number",++ "stream.viewer_count": "number",++ "stream.peak_viewers": "number",++ "stream.follower_count": "number",++ "stream.thumbnail_url": "null",++ "stream.started_at": "string",++ "stream.ended_at": "null",++ "stream.duration_seconds": "number",++ "stream.created_at": "string",++ "stream.ai_overview": "null",++ "stream.ai_title": "null",++ "stream.ai_category": "null",++ "stream.ai_tags": "null",++ "stream.ai_overview_short": "null",++ "stream.username": "string",++ "stream.display_name": "string",++ "stream.avatar_url": "null",++ "stream.profile_color": "string",++ "stream.managed_stream_slug": "null",++ "stream.endpoint": "object",++ "stream.server_clip": "boolean",++ "stream.external_viewer_count": "number",++ "stream.total_viewer_count": "number",++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "null",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null",++ "streams[].endpoint": "object",++ "streams[].server_clip": "boolean",++ "streams[].external_viewer_count": "number",++ "streams[].total_viewer_count": "number",++ "managed_streams": "array",++ "managed_streams[]": "object",++ "managed_streams[].id": "number",++ "managed_streams[].user_id": "number",++ "managed_streams[].channel_id": "number",++ "managed_streams[].slug": "null",++ "managed_streams[].title": "string",++ "managed_streams[].description": "string",++ "managed_streams[].category": "string",++ "managed_streams[].tags": "string",++ "managed_streams[].protocol": "string",++ "managed_streams[].is_nsfw": "number",++ "managed_streams[].control_config_id": "null",++ "managed_streams[].sort_order": "number",++ "managed_streams[].created_at": "string",++ "managed_streams[].updated_at": "string",++ "managed_streams[].slot_clip_recording_enabled": "number",++ "managed_streams[].slot_clip_notify_enabled": "number",++ "managed_streams[].slot_powerchat_relay": "number",++ "managed_streams[].slot_powerchat_count_rs_views": "number",++ "managed_streams[].streaming_method": "string",++ "managed_streams[].browser_mode": "string",++ "managed_streams[].default_vod_visibility": "string",++ "managed_streams[].default_clip_visibility": "string",++ "managed_streams[].slot_vod_recording_enabled": "number",++ "managed_streams[].weather_detail": "string",++ "managed_streams[].weather_show_location": "number",++ "managed_streams[].last_live_at": "string",++ "managed_streams[].is_currently_live": "number",++ "managed_streams[].live_session_id": "number",++ "rs_restream": "null",++ "restream_links": "null",++ "external_viewers": "null",++ "vods": "array",++ "vodTotal": "number",++ "vodLimit": "number",++ "vodOffset": "number",++ "clips": "array",++ "clipTotal": "number",++ "clipLimit": "number",++ "clipOffset": "number",++ "clipsOfStreams": "array",++ "clipsOfTotal": "number",++ "clipsOfLimit": "number",++ "clipsOfOffset": "number",++ "aiClips": "array",++ "aiClipsTotal": "number",++ "aiClipsLimit": "number",++ "aiClipsOffset": "number"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star?vodLimit=5&vodOffset=0&clipLimit=5&clipOffset=0",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "videos_tab_hidden": "boolean",++ "clips_tab_hidden": "boolean",++ "pasteTotal": "number",++ "clipsTakenTotal": "number",++ "aiEventTotal": "number",++ "channel": "object",++ "channel.id": "number",++ "channel.user_id": "number",++ "channel.title": "string",++ "channel.description": "string",++ "channel.category": "null",++ "channel.tags": "string",++ "channel.protocol": "string",++ "channel.is_nsfw": "number",++ "channel.panels": "string",++ "channel.default_vod_visibility": "string",++ "channel.default_clip_visibility": "string",++ "channel.weather_detail": "string",++ "channel.weather_show_location": "number",++ "channel.control_mode": "string",++ "channel.anon_controls_enabled": "number",++ "channel.control_rate_limit_ms": "number",++ "channel.video_click_enabled": "number",++ "channel.video_click_rate_limit_ms": "number",++ "channel.active_control_config_id": "null",++ "channel.created_at": "string",++ "channel.updated_at": "string",++ "channel.clips_allow_creator_delete": "number",++ "channel.hide_ai_overview": "number",++ "channel.ai_overview_pref": "string",++ "channel.ai_derivation_enabled": "number",++ "channel.ai_category": "null",++ "channel.offline_screen_type": "string",++ "channel.offline_screen_url": "null",++ "channel.offline_html": "null",++ "channel.offline_css": "null",++ "channel.username": "string",++ "channel.display_name": "string",++ "channel.avatar_url": "null",++ "channel.profile_color": "string",++ "channel.bio": "string",++ "channel.role": "string",++ "channel.is_owner": "number",++ "channel.follower_count": "number",++ "channel.is_following": "boolean",++ "channel.chat_limits": "object",++ "channel.chat_limits.max_message_length": "number",++ "channel.chat_limits.tts_max_length": "number",++ "channel.mods_can_edit_about": "boolean",++ "channel.viewer_can_edit_about": "boolean",++ "channel.ai_overview": "null",++ "language": "object",++ "language.code": "string",++ "language.name": "string",++ "language.flag": "string",++ "language.translate": "boolean",++ "stream": "object",++ "stream.id": "number",++ "stream.user_id": "number",++ "stream.channel_id": "null",++ "stream.managed_stream_id": "number",++ "stream.control_config_id": "null",++ "stream.title": "string",++ "stream.description": "string",++ "stream.category": "string",++ "stream.tags": "string",++ "stream.protocol": "string",++ "stream.is_live": "number",++ "stream.is_nsfw": "number",++ "stream.viewer_count": "number",++ "stream.peak_viewers": "number",++ "stream.follower_count": "number",++ "stream.thumbnail_url": "null",++ "stream.started_at": "string",++ "stream.ended_at": "null",++ "stream.duration_seconds": "number",++ "stream.created_at": "string",++ "stream.ai_overview": "null",++ "stream.ai_title": "null",++ "stream.ai_category": "null",++ "stream.ai_tags": "null",++ "stream.ai_overview_short": "null",++ "stream.username": "string",++ "stream.display_name": "string",++ "stream.avatar_url": "null",++ "stream.profile_color": "string",++ "stream.managed_stream_slug": "null",++ "stream.endpoint": "object",++ "stream.server_clip": "boolean",++ "stream.external_viewer_count": "number",++ "stream.total_viewer_count": "number",++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "null",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null",++ "streams[].endpoint": "object",++ "streams[].server_clip": "boolean",++ "streams[].external_viewer_count": "number",++ "streams[].total_viewer_count": "number",++ "managed_streams": "array",++ "managed_streams[]": "object",++ "managed_streams[].id": "number",++ "managed_streams[].user_id": "number",++ "managed_streams[].channel_id": "number",++ "managed_streams[].slug": "null",++ "managed_streams[].title": "string",++ "managed_streams[].description": "string",++ "managed_streams[].category": "string",++ "managed_streams[].tags": "string",++ "managed_streams[].protocol": "string",++ "managed_streams[].is_nsfw": "number",++ "managed_streams[].control_config_id": "null",++ "managed_streams[].sort_order": "number",++ "managed_streams[].created_at": "string",++ "managed_streams[].updated_at": "string",++ "managed_streams[].slot_clip_recording_enabled": "number",++ "managed_streams[].slot_clip_notify_enabled": "number",++ "managed_streams[].slot_powerchat_relay": "number",++ "managed_streams[].slot_powerchat_count_rs_views": "number",++ "managed_streams[].streaming_method": "string",++ "managed_streams[].browser_mode": "string",++ "managed_streams[].default_vod_visibility": "string",++ "managed_streams[].default_clip_visibility": "string",++ "managed_streams[].slot_vod_recording_enabled": "number",++ "managed_streams[].weather_detail": "string",++ "managed_streams[].weather_show_location": "number",++ "managed_streams[].last_live_at": "string",++ "managed_streams[].is_currently_live": "number",++ "managed_streams[].live_session_id": "number",++ "rs_restream": "null",++ "restream_links": "null",++ "external_viewers": "null",++ "vods": "array",++ "vodTotal": "number",++ "vodLimit": "number",++ "vodOffset": "number",++ "clips": "array",++ "clipTotal": "number",++ "clipLimit": "number",++ "clipOffset": "number",++ "clipsOfStreams": "array",++ "clipsOfTotal": "number",++ "clipsOfLimit": "number",++ "clipsOfOffset": "number",++ "aiClips": "array",++ "aiClipsTotal": "number",++ "aiClipsLimit": "number",++ "aiClipsOffset": "number"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star?vodLimit=5&vodOffset=0&clipLimit=5&clipOffset=0",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "videos_tab_hidden": "boolean",++ "clips_tab_hidden": "boolean",++ "pasteTotal": "number",++ "clipsTakenTotal": "number",++ "aiEventTotal": "number",++ "channel": "object",++ "channel.id": "number",++ "channel.user_id": "number",++ "channel.title": "string",++ "channel.description": "string",++ "channel.category": "null",++ "channel.tags": "string",++ "channel.protocol": "string",++ "channel.is_nsfw": "number",++ "channel.panels": "string",++ "channel.default_vod_visibility": "string",++ "channel.default_clip_visibility": "string",++ "channel.weather_detail": "string",++ "channel.weather_show_location": "number",++ "channel.control_mode": "string",++ "channel.anon_controls_enabled": "number",++ "channel.control_rate_limit_ms": "number",++ "channel.video_click_enabled": "number",++ "channel.video_click_rate_limit_ms": "number",++ "channel.active_control_config_id": "null",++ "channel.created_at": "string",++ "channel.updated_at": "string",++ "channel.clips_allow_creator_delete": "number",++ "channel.hide_ai_overview": "number",++ "channel.ai_overview_pref": "string",++ "channel.ai_derivation_enabled": "number",++ "channel.ai_category": "null",++ "channel.offline_screen_type": "string",++ "channel.offline_screen_url": "null",++ "channel.offline_html": "null",++ "channel.offline_css": "null",++ "channel.username": "string",++ "channel.display_name": "string",++ "channel.avatar_url": "null",++ "channel.profile_color": "string",++ "channel.bio": "string",++ "channel.role": "string",++ "channel.is_owner": "number",++ "channel.follower_count": "number",++ "channel.is_following": "boolean",++ "channel.chat_limits": "object",++ "channel.chat_limits.max_message_length": "number",++ "channel.chat_limits.tts_max_length": "number",++ "channel.mods_can_edit_about": "boolean",++ "channel.viewer_can_edit_about": "boolean",++ "channel.ai_overview": "null",++ "language": "object",++ "language.code": "string",++ "language.name": "string",++ "language.flag": "string",++ "language.translate": "boolean",++ "stream": "object",++ "stream.id": "number",++ "stream.user_id": "number",++ "stream.channel_id": "null",++ "stream.managed_stream_id": "number",++ "stream.control_config_id": "null",++ "stream.title": "string",++ "stream.description": "string",++ "stream.category": "string",++ "stream.tags": "string",++ "stream.protocol": "string",++ "stream.is_live": "number",++ "stream.is_nsfw": "number",++ "stream.viewer_count": "number",++ "stream.peak_viewers": "number",++ "stream.follower_count": "number",++ "stream.thumbnail_url": "null",++ "stream.started_at": "string",++ "stream.ended_at": "null",++ "stream.duration_seconds": "number",++ "stream.created_at": "string",++ "stream.ai_overview": "null",++ "stream.ai_title": "null",++ "stream.ai_category": "null",++ "stream.ai_tags": "null",++ "stream.ai_overview_short": "null",++ "stream.username": "string",++ "stream.display_name": "string",++ "stream.avatar_url": "null",++ "stream.profile_color": "string",++ "stream.managed_stream_slug": "null",++ "stream.endpoint": "object",++ "stream.server_clip": "boolean",++ "stream.external_viewer_count": "number",++ "stream.total_viewer_count": "number",++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "null",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null",++ "streams[].endpoint": "object",++ "streams[].server_clip": "boolean",++ "streams[].external_viewer_count": "number",++ "streams[].total_viewer_count": "number",++ "managed_streams": "array",++ "managed_streams[]": "object",++ "managed_streams[].id": "number",++ "managed_streams[].user_id": "number",++ "managed_streams[].channel_id": "number",++ "managed_streams[].slug": "null",++ "managed_streams[].title": "string",++ "managed_streams[].description": "string",++ "managed_streams[].category": "string",++ "managed_streams[].tags": "string",++ "managed_streams[].protocol": "string",++ "managed_streams[].is_nsfw": "number",++ "managed_streams[].control_config_id": "null",++ "managed_streams[].sort_order": "number",++ "managed_streams[].created_at": "string",++ "managed_streams[].updated_at": "string",++ "managed_streams[].slot_clip_recording_enabled": "number",++ "managed_streams[].slot_clip_notify_enabled": "number",++ "managed_streams[].slot_powerchat_relay": "number",++ "managed_streams[].slot_powerchat_count_rs_views": "number",++ "managed_streams[].streaming_method": "string",++ "managed_streams[].browser_mode": "string",++ "managed_streams[].default_vod_visibility": "string",++ "managed_streams[].default_clip_visibility": "string",++ "managed_streams[].slot_vod_recording_enabled": "number",++ "managed_streams[].weather_detail": "string",++ "managed_streams[].weather_show_location": "number",++ "managed_streams[].last_live_at": "string",++ "managed_streams[].is_currently_live": "number",++ "managed_streams[].live_session_id": "number",++ "rs_restream": "null",++ "restream_links": "null",++ "external_viewers": "null",++ "vods": "array",++ "vodTotal": "number",++ "vodLimit": "number",++ "vodOffset": "number",++ "clips": "array",++ "clipTotal": "number",++ "clipLimit": "number",++ "clipOffset": "number",++ "clipsOfStreams": "array",++ "clipsOfTotal": "number",++ "clipsOfLimit": "number",++ "clipsOfOffset": "number",++ "aiClips": "array",++ "aiClipsTotal": "number",++ "aiClipsLimit": "number",++ "aiClipsOffset": "number"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star/live",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "channel": "object",++ "channel.username": "string",++ "channel.display_name": "string",++ "channel.user_id": "number",++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "null",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null",++ "streams[].endpoint": "object",++ "streams[].server_clip": "boolean"++ },++ "from": [++ "public/popout-chat.html"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star/live",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "channel": "object",++ "channel.username": "string",++ "channel.display_name": "string",++ "channel.user_id": "number",++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "null",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null",++ "streams[].endpoint": "object",++ "streams[].server_clip": "boolean"++ },++ "from": [++ "public/popout-chat.html"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "videos_tab_hidden": "boolean",++ "clips_tab_hidden": "boolean",++ "pasteTotal": "number",++ "clipsTakenTotal": "number",++ "aiEventTotal": "number",++ "channel": "object",++ "channel.id": "number",++ "channel.user_id": "number",++ "channel.title": "string",++ "channel.description": "string",++ "channel.category": "null",++ "channel.tags": "string",++ "channel.protocol": "string",++ "channel.is_nsfw": "number",++ "channel.panels": "string",++ "channel.default_vod_visibility": "string",++ "channel.default_clip_visibility": "string",++ "channel.weather_detail": "string",++ "channel.weather_show_location": "number",++ "channel.control_mode": "string",++ "channel.anon_controls_enabled": "number",++ "channel.control_rate_limit_ms": "number",++ "channel.video_click_enabled": "number",++ "channel.video_click_rate_limit_ms": "number",++ "channel.active_control_config_id": "null",++ "channel.created_at": "string",++ "channel.updated_at": "string",++ "channel.clips_allow_creator_delete": "number",++ "channel.hide_ai_overview": "number",++ "channel.ai_overview_pref": "string",++ "channel.ai_derivation_enabled": "number",++ "channel.ai_category": "null",++ "channel.offline_screen_type": "string",++ "channel.offline_screen_url": "null",++ "channel.offline_html": "null",++ "channel.offline_css": "null",++ "channel.username": "string",++ "channel.display_name": "string",++ "channel.avatar_url": "null",++ "channel.profile_color": "string",++ "channel.bio": "string",++ "channel.role": "string",++ "channel.is_owner": "number",++ "channel.follower_count": "number",++ "channel.is_following": "boolean",++ "channel.chat_limits": "object",++ "channel.chat_limits.max_message_length": "number",++ "channel.chat_limits.tts_max_length": "number",++ "channel.mods_can_edit_about": "boolean",++ "channel.viewer_can_edit_about": "boolean",++ "channel.ai_overview": "null",++ "language": "object",++ "language.code": "string",++ "language.name": "string",++ "language.flag": "string",++ "language.translate": "boolean",++ "stream": "object",++ "stream.id": "number",++ "stream.user_id": "number",++ "stream.channel_id": "null",++ "stream.managed_stream_id": "number",++ "stream.control_config_id": "null",++ "stream.title": "string",++ "stream.description": "string",++ "stream.category": "string",++ "stream.tags": "string",++ "stream.protocol": "string",++ "stream.is_live": "number",++ "stream.is_nsfw": "number",++ "stream.viewer_count": "number",++ "stream.peak_viewers": "number",++ "stream.follower_count": "number",++ "stream.thumbnail_url": "null",++ "stream.started_at": "string",++ "stream.ended_at": "null",++ "stream.duration_seconds": "number",++ "stream.created_at": "string",++ "stream.ai_overview": "null",++ "stream.ai_title": "null",++ "stream.ai_category": "null",++ "stream.ai_tags": "null",++ "stream.ai_overview_short": "null",++ "stream.username": "string",++ "stream.display_name": "string",++ "stream.avatar_url": "null",++ "stream.profile_color": "string",++ "stream.managed_stream_slug": "null",++ "stream.endpoint": "object",++ "stream.server_clip": "boolean",++ "stream.external_viewer_count": "number",++ "stream.total_viewer_count": "number",++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "null",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null",++ "streams[].endpoint": "object",++ "streams[].server_clip": "boolean",++ "streams[].external_viewer_count": "number",++ "streams[].total_viewer_count": "number",++ "managed_streams": "array",++ "managed_streams[]": "object",++ "managed_streams[].id": "number",++ "managed_streams[].user_id": "number",++ "managed_streams[].channel_id": "number",++ "managed_streams[].slug": "null",++ "managed_streams[].title": "string",++ "managed_streams[].description": "string",++ "managed_streams[].category": "string",++ "managed_streams[].tags": "string",++ "managed_streams[].protocol": "string",++ "managed_streams[].is_nsfw": "number",++ "managed_streams[].control_config_id": "null",++ "managed_streams[].sort_order": "number",++ "managed_streams[].created_at": "string",++ "managed_streams[].updated_at": "string",++ "managed_streams[].slot_clip_recording_enabled": "number",++ "managed_streams[].slot_clip_notify_enabled": "number",++ "managed_streams[].slot_powerchat_relay": "number",++ "managed_streams[].slot_powerchat_count_rs_views": "number",++ "managed_streams[].streaming_method": "string",++ "managed_streams[].browser_mode": "string",++ "managed_streams[].default_vod_visibility": "string",++ "managed_streams[].default_clip_visibility": "string",++ "managed_streams[].slot_vod_recording_enabled": "number",++ "managed_streams[].weather_detail": "string",++ "managed_streams[].weather_show_location": "number",++ "managed_streams[].last_live_at": "string",++ "managed_streams[].is_currently_live": "number",++ "managed_streams[].live_session_id": "number",++ "rs_restream": "null",++ "restream_links": "null",++ "external_viewers": "null",++ "vods": "array",++ "vodTotal": "number",++ "vodLimit": "number",++ "vodOffset": "number",++ "clips": "array",++ "clipTotal": "number",++ "clipLimit": "number",++ "clipOffset": "number",++ "clipsOfStreams": "array",++ "clipsOfTotal": "number",++ "clipsOfLimit": "number",++ "clipsOfOffset": "number",++ "aiClips": "array",++ "aiClipsTotal": "number",++ "aiClipsLimit": "number",++ "aiClipsOffset": "number"++ },++ "from": [++ "public/obs/chat.html"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "videos_tab_hidden": "boolean",++ "clips_tab_hidden": "boolean",++ "pasteTotal": "number",++ "clipsTakenTotal": "number",++ "aiEventTotal": "number",++ "channel": "object",++ "channel.id": "number",++ "channel.user_id": "number",++ "channel.title": "string",++ "channel.description": "string",++ "channel.category": "null",++ "channel.tags": "string",++ "channel.protocol": "string",++ "channel.is_nsfw": "number",++ "channel.panels": "string",++ "channel.default_vod_visibility": "string",++ "channel.default_clip_visibility": "string",++ "channel.weather_detail": "string",++ "channel.weather_show_location": "number",++ "channel.control_mode": "string",++ "channel.anon_controls_enabled": "number",++ "channel.control_rate_limit_ms": "number",++ "channel.video_click_enabled": "number",++ "channel.video_click_rate_limit_ms": "number",++ "channel.active_control_config_id": "null",++ "channel.created_at": "string",++ "channel.updated_at": "string",++ "channel.clips_allow_creator_delete": "number",++ "channel.hide_ai_overview": "number",++ "channel.ai_overview_pref": "string",++ "channel.ai_derivation_enabled": "number",++ "channel.ai_category": "null",++ "channel.offline_screen_type": "string",++ "channel.offline_screen_url": "null",++ "channel.offline_html": "null",++ "channel.offline_css": "null",++ "channel.username": "string",++ "channel.display_name": "string",++ "channel.avatar_url": "null",++ "channel.profile_color": "string",++ "channel.bio": "string",++ "channel.role": "string",++ "channel.is_owner": "number",++ "channel.follower_count": "number",++ "channel.is_following": "boolean",++ "channel.chat_limits": "object",++ "channel.chat_limits.max_message_length": "number",++ "channel.chat_limits.tts_max_length": "number",++ "channel.mods_can_edit_about": "boolean",++ "channel.viewer_can_edit_about": "boolean",++ "channel.ai_overview": "null",++ "language": "object",++ "language.code": "string",++ "language.name": "string",++ "language.flag": "string",++ "language.translate": "boolean",++ "stream": "object",++ "stream.id": "number",++ "stream.user_id": "number",++ "stream.channel_id": "null",++ "stream.managed_stream_id": "number",++ "stream.control_config_id": "null",++ "stream.title": "string",++ "stream.description": "string",++ "stream.category": "string",++ "stream.tags": "string",++ "stream.protocol": "string",++ "stream.is_live": "number",++ "stream.is_nsfw": "number",++ "stream.viewer_count": "number",++ "stream.peak_viewers": "number",++ "stream.follower_count": "number",++ "stream.thumbnail_url": "null",++ "stream.started_at": "string",++ "stream.ended_at": "null",++ "stream.duration_seconds": "number",++ "stream.created_at": "string",++ "stream.ai_overview": "null",++ "stream.ai_title": "null",++ "stream.ai_category": "null",++ "stream.ai_tags": "null",++ "stream.ai_overview_short": "null",++ "stream.username": "string",++ "stream.display_name": "string",++ "stream.avatar_url": "null",++ "stream.profile_color": "string",++ "stream.managed_stream_slug": "null",++ "stream.endpoint": "object",++ "stream.server_clip": "boolean",++ "stream.external_viewer_count": "number",++ "stream.total_viewer_count": "number",++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "null",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null",++ "streams[].endpoint": "object",++ "streams[].server_clip": "boolean",++ "streams[].external_viewer_count": "number",++ "streams[].total_viewer_count": "number",++ "managed_streams": "array",++ "managed_streams[]": "object",++ "managed_streams[].id": "number",++ "managed_streams[].user_id": "number",++ "managed_streams[].channel_id": "number",++ "managed_streams[].slug": "null",++ "managed_streams[].title": "string",++ "managed_streams[].description": "string",++ "managed_streams[].category": "string",++ "managed_streams[].tags": "string",++ "managed_streams[].protocol": "string",++ "managed_streams[].is_nsfw": "number",++ "managed_streams[].control_config_id": "null",++ "managed_streams[].sort_order": "number",++ "managed_streams[].created_at": "string",++ "managed_streams[].updated_at": "string",++ "managed_streams[].slot_clip_recording_enabled": "number",++ "managed_streams[].slot_clip_notify_enabled": "number",++ "managed_streams[].slot_powerchat_relay": "number",++ "managed_streams[].slot_powerchat_count_rs_views": "number",++ "managed_streams[].streaming_method": "string",++ "managed_streams[].browser_mode": "string",++ "managed_streams[].default_vod_visibility": "string",++ "managed_streams[].default_clip_visibility": "string",++ "managed_streams[].slot_vod_recording_enabled": "number",++ "managed_streams[].weather_detail": "string",++ "managed_streams[].weather_show_location": "number",++ "managed_streams[].last_live_at": "string",++ "managed_streams[].is_currently_live": "number",++ "managed_streams[].live_session_id": "number",++ "rs_restream": "null",++ "restream_links": "null",++ "external_viewers": "null",++ "vods": "array",++ "vodTotal": "number",++ "vodLimit": "number",++ "vodOffset": "number",++ "clips": "array",++ "clipTotal": "number",++ "clipLimit": "number",++ "clipOffset": "number",++ "clipsOfStreams": "array",++ "clipsOfTotal": "number",++ "clipsOfLimit": "number",++ "clipsOfOffset": "number",++ "aiClips": "array",++ "aiClipsTotal": "number",++ "aiClipsLimit": "number",++ "aiClipsOffset": "number"++ },++ "from": [++ "public/obs/chat.html"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star/bio-en",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "from": "null",++ "to": "string",++ "text": "null"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star/bio-en",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "from": "null",++ "to": "string",++ "text": "null"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star/clips-taken",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "clips": "array",++ "total": "number",++ "facets": "array",++ "limit": "number",++ "offset": "number",++ "hasMore": "boolean",++ "sort": "string",++ "of": "null",++ "includeSelf": "boolean"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star/clips-taken",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "clips": "array",++ "total": "number",++ "facets": "array",++ "limit": "number",++ "offset": "number",++ "hasMore": "boolean",++ "sort": "string",++ "of": "null",++ "includeSelf": "boolean"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star/popular",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "vod": "null",++ "clip": "null",++ "vods": "array",++ "clips": "array",++ "ranges": "object",++ "ranges.week": "object",++ "ranges.week.vod": "null",++ "ranges.week.clip": "null",++ "ranges.month": "object",++ "ranges.month.vod": "null",++ "ranges.month.clip": "null",++ "ranges.all": "object",++ "ranges.all.vod": "null",++ "ranges.all.clip": "null"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star/popular",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "vod": "null",++ "clip": "null",++ "vods": "array",++ "clips": "array",++ "ranges": "object",++ "ranges.week": "object",++ "ranges.week.vod": "null",++ "ranges.week.clip": "null",++ "ranges.month": "object",++ "ranges.month.vod": "null",++ "ranges.month.clip": "null",++ "ranges.all": "object",++ "ranges.all.vod": "null",++ "ranges.all.clip": "null"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star?pollOnly=1",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "videos_tab_hidden": "boolean",++ "clips_tab_hidden": "boolean",++ "pasteTotal": "number",++ "clipsTakenTotal": "number",++ "aiEventTotal": "number",++ "channel": "object",++ "channel.id": "number",++ "channel.user_id": "number",++ "channel.title": "string",++ "channel.description": "string",++ "channel.category": "null",++ "channel.tags": "string",++ "channel.protocol": "string",++ "channel.is_nsfw": "number",++ "channel.panels": "string",++ "channel.default_vod_visibility": "string",++ "channel.default_clip_visibility": "string",++ "channel.weather_detail": "string",++ "channel.weather_show_location": "number",++ "channel.control_mode": "string",++ "channel.anon_controls_enabled": "number",++ "channel.control_rate_limit_ms": "number",++ "channel.video_click_enabled": "number",++ "channel.video_click_rate_limit_ms": "number",++ "channel.active_control_config_id": "null",++ "channel.created_at": "string",++ "channel.updated_at": "string",++ "channel.clips_allow_creator_delete": "number",++ "channel.hide_ai_overview": "number",++ "channel.ai_overview_pref": "string",++ "channel.ai_derivation_enabled": "number",++ "channel.ai_category": "null",++ "channel.offline_screen_type": "string",++ "channel.offline_screen_url": "null",++ "channel.offline_html": "null",++ "channel.offline_css": "null",++ "channel.username": "string",++ "channel.display_name": "string",++ "channel.avatar_url": "null",++ "channel.profile_color": "string",++ "channel.bio": "string",++ "channel.role": "string",++ "channel.is_owner": "number",++ "channel.follower_count": "number",++ "channel.is_following": "boolean",++ "channel.chat_limits": "object",++ "channel.chat_limits.max_message_length": "number",++ "channel.chat_limits.tts_max_length": "number",++ "channel.mods_can_edit_about": "boolean",++ "channel.viewer_can_edit_about": "boolean",++ "channel.ai_overview": "null",++ "language": "object",++ "language.code": "string",++ "language.name": "string",++ "language.flag": "string",++ "language.translate": "boolean",++ "stream": "object",++ "stream.id": "number",++ "stream.user_id": "number",++ "stream.channel_id": "null",++ "stream.managed_stream_id": "number",++ "stream.control_config_id": "null",++ "stream.title": "string",++ "stream.description": "string",++ "stream.category": "string",++ "stream.tags": "string",++ "stream.protocol": "string",++ "stream.is_live": "number",++ "stream.is_nsfw": "number",++ "stream.viewer_count": "number",++ "stream.peak_viewers": "number",++ "stream.follower_count": "number",++ "stream.thumbnail_url": "null",++ "stream.started_at": "string",++ "stream.ended_at": "null",++ "stream.duration_seconds": "number",++ "stream.created_at": "string",++ "stream.ai_overview": "null",++ "stream.ai_title": "null",++ "stream.ai_category": "null",++ "stream.ai_tags": "null",++ "stream.ai_overview_short": "null",++ "stream.username": "string",++ "stream.display_name": "string",++ "stream.avatar_url": "null",++ "stream.profile_color": "string",++ "stream.managed_stream_slug": "null",++ "stream.endpoint": "object",++ "stream.server_clip": "boolean",++ "stream.external_viewer_count": "number",++ "stream.total_viewer_count": "number",++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "null",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null",++ "streams[].endpoint": "object",++ "streams[].server_clip": "boolean",++ "streams[].external_viewer_count": "number",++ "streams[].total_viewer_count": "number",++ "managed_streams": "array",++ "managed_streams[]": "object",++ "managed_streams[].id": "number",++ "managed_streams[].user_id": "number",++ "managed_streams[].channel_id": "number",++ "managed_streams[].slug": "null",++ "managed_streams[].title": "string",++ "managed_streams[].description": "string",++ "managed_streams[].category": "string",++ "managed_streams[].tags": "string",++ "managed_streams[].protocol": "string",++ "managed_streams[].is_nsfw": "number",++ "managed_streams[].control_config_id": "null",++ "managed_streams[].sort_order": "number",++ "managed_streams[].created_at": "string",++ "managed_streams[].updated_at": "string",++ "managed_streams[].slot_clip_recording_enabled": "number",++ "managed_streams[].slot_clip_notify_enabled": "number",++ "managed_streams[].slot_powerchat_relay": "number",++ "managed_streams[].slot_powerchat_count_rs_views": "number",++ "managed_streams[].streaming_method": "string",++ "managed_streams[].browser_mode": "string",++ "managed_streams[].default_vod_visibility": "string",++ "managed_streams[].default_clip_visibility": "string",++ "managed_streams[].slot_vod_recording_enabled": "number",++ "managed_streams[].weather_detail": "string",++ "managed_streams[].weather_show_location": "number",++ "managed_streams[].last_live_at": "string",++ "managed_streams[].is_currently_live": "number",++ "managed_streams[].live_session_id": "number",++ "rs_restream": "null",++ "restream_links": "null",++ "external_viewers": "null",++ "vods": "array",++ "vodTotal": "number",++ "vodLimit": "number",++ "vodOffset": "number",++ "clips": "array",++ "clipTotal": "number",++ "clipLimit": "number",++ "clipOffset": "number",++ "clipsOfStreams": "array",++ "clipsOfTotal": "number",++ "clipsOfLimit": "number",++ "clipsOfOffset": "number",++ "aiClips": "array",++ "aiClipsTotal": "number",++ "aiClipsLimit": "number",++ "aiClipsOffset": "number"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/channel/n1star?pollOnly=1",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "videos_tab_hidden": "boolean",++ "clips_tab_hidden": "boolean",++ "pasteTotal": "number",++ "clipsTakenTotal": "number",++ "aiEventTotal": "number",++ "channel": "object",++ "channel.id": "number",++ "channel.user_id": "number",++ "channel.title": "string",++ "channel.description": "string",++ "channel.category": "null",++ "channel.tags": "string",++ "channel.protocol": "string",++ "channel.is_nsfw": "number",++ "channel.panels": "string",++ "channel.default_vod_visibility": "string",++ "channel.default_clip_visibility": "string",++ "channel.weather_detail": "string",++ "channel.weather_show_location": "number",++ "channel.control_mode": "string",++ "channel.anon_controls_enabled": "number",++ "channel.control_rate_limit_ms": "number",++ "channel.video_click_enabled": "number",++ "channel.video_click_rate_limit_ms": "number",++ "channel.active_control_config_id": "null",++ "channel.created_at": "string",++ "channel.updated_at": "string",++ "channel.clips_allow_creator_delete": "number",++ "channel.hide_ai_overview": "number",++ "channel.ai_overview_pref": "string",++ "channel.ai_derivation_enabled": "number",++ "channel.ai_category": "null",++ "channel.offline_screen_type": "string",++ "channel.offline_screen_url": "null",++ "channel.offline_html": "null",++ "channel.offline_css": "null",++ "channel.username": "string",++ "channel.display_name": "string",++ "channel.avatar_url": "null",++ "channel.profile_color": "string",++ "channel.bio": "string",++ "channel.role": "string",++ "channel.is_owner": "number",++ "channel.follower_count": "number",++ "channel.is_following": "boolean",++ "channel.chat_limits": "object",++ "channel.chat_limits.max_message_length": "number",++ "channel.chat_limits.tts_max_length": "number",++ "channel.mods_can_edit_about": "boolean",++ "channel.viewer_can_edit_about": "boolean",++ "channel.ai_overview": "null",++ "language": "object",++ "language.code": "string",++ "language.name": "string",++ "language.flag": "string",++ "language.translate": "boolean",++ "stream": "object",++ "stream.id": "number",++ "stream.user_id": "number",++ "stream.channel_id": "null",++ "stream.managed_stream_id": "number",++ "stream.control_config_id": "null",++ "stream.title": "string",++ "stream.description": "string",++ "stream.category": "string",++ "stream.tags": "string",++ "stream.protocol": "string",++ "stream.is_live": "number",++ "stream.is_nsfw": "number",++ "stream.viewer_count": "number",++ "stream.peak_viewers": "number",++ "stream.follower_count": "number",++ "stream.thumbnail_url": "null",++ "stream.started_at": "string",++ "stream.ended_at": "null",++ "stream.duration_seconds": "number",++ "stream.created_at": "string",++ "stream.ai_overview": "null",++ "stream.ai_title": "null",++ "stream.ai_category": "null",++ "stream.ai_tags": "null",++ "stream.ai_overview_short": "null",++ "stream.username": "string",++ "stream.display_name": "string",++ "stream.avatar_url": "null",++ "stream.profile_color": "string",++ "stream.managed_stream_slug": "null",++ "stream.endpoint": "object",++ "stream.server_clip": "boolean",++ "stream.external_viewer_count": "number",++ "stream.total_viewer_count": "number",++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "null",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null",++ "streams[].endpoint": "object",++ "streams[].server_clip": "boolean",++ "streams[].external_viewer_count": "number",++ "streams[].total_viewer_count": "number",++ "managed_streams": "array",++ "managed_streams[]": "object",++ "managed_streams[].id": "number",++ "managed_streams[].user_id": "number",++ "managed_streams[].channel_id": "number",++ "managed_streams[].slug": "null",++ "managed_streams[].title": "string",++ "managed_streams[].description": "string",++ "managed_streams[].category": "string",++ "managed_streams[].tags": "string",++ "managed_streams[].protocol": "string",++ "managed_streams[].is_nsfw": "number",++ "managed_streams[].control_config_id": "null",++ "managed_streams[].sort_order": "number",++ "managed_streams[].created_at": "string",++ "managed_streams[].updated_at": "string",++ "managed_streams[].slot_clip_recording_enabled": "number",++ "managed_streams[].slot_clip_notify_enabled": "number",++ "managed_streams[].slot_powerchat_relay": "number",++ "managed_streams[].slot_powerchat_count_rs_views": "number",++ "managed_streams[].streaming_method": "string",++ "managed_streams[].browser_mode": "string",++ "managed_streams[].default_vod_visibility": "string",++ "managed_streams[].default_clip_visibility": "string",++ "managed_streams[].slot_vod_recording_enabled": "number",++ "managed_streams[].weather_detail": "string",++ "managed_streams[].weather_show_location": "number",++ "managed_streams[].last_live_at": "string",++ "managed_streams[].is_currently_live": "number",++ "managed_streams[].live_session_id": "number",++ "rs_restream": "null",++ "restream_links": "null",++ "external_viewers": "null",++ "vods": "array",++ "vodTotal": "number",++ "vodLimit": "number",++ "vodOffset": "number",++ "clips": "array",++ "clipTotal": "number",++ "clipLimit": "number",++ "clipOffset": "number",++ "clipsOfStreams": "array",++ "clipsOfTotal": "number",++ "clipsOfLimit": "number",++ "clipsOfOffset": "number",++ "aiClips": "array",++ "aiClipsTotal": "number",++ "aiClipsLimit": "number",++ "aiClipsOffset": "number"++ },++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/managed",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js",++ "public/js/broadcast.js",++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/managed",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "managed_streams": "array",++ "managed_streams[]": "object",++ "managed_streams[].id": "number",++ "managed_streams[].user_id": "number",++ "managed_streams[].channel_id": "number",++ "managed_streams[].slug": "null",++ "managed_streams[].title": "string",++ "managed_streams[].description": "string",++ "managed_streams[].category": "string",++ "managed_streams[].tags": "string",++ "managed_streams[].protocol": "string",++ "managed_streams[].stream_key": "string",++ "managed_streams[].is_nsfw": "number",++ "managed_streams[].control_config_id": "null",++ "managed_streams[].sort_order": "number",++ "managed_streams[].created_at": "string",++ "managed_streams[].updated_at": "string",++ "managed_streams[].slot_clip_recording_enabled": "number",++ "managed_streams[].slot_clip_notify_enabled": "number",++ "managed_streams[].slot_powerchat_relay": "number",++ "managed_streams[].slot_powerchat_count_rs_views": "number",++ "managed_streams[].broadcast_settings": "string",++ "managed_streams[].streaming_method": "string",++ "managed_streams[].browser_mode": "string",++ "managed_streams[].pip_source_msid": "null",++ "managed_streams[].pip_defaults": "string",++ "managed_streams[].default_vod_visibility": "string",++ "managed_streams[].default_clip_visibility": "string",++ "managed_streams[].slot_vod_recording_enabled": "number",++ "managed_streams[].weather_zip": "null",++ "managed_streams[].weather_detail": "string",++ "managed_streams[].weather_show_location": "number",++ "managed_streams[].last_live_at": "string",++ "managed_streams[].is_currently_live": "number",++ "managed_streams[].live_session_id": "number",++ "limit": "number"++ },++ "from": [++ "public/js/broadcast-workspace.js",++ "public/js/broadcast.js",++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/managed/1/profile",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/managed/1/profile",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "managed_stream": "object",++ "managed_stream.id": "number",++ "managed_stream.user_id": "number",++ "managed_stream.channel_id": "number",++ "managed_stream.slug": "null",++ "managed_stream.title": "string",++ "managed_stream.description": "string",++ "managed_stream.category": "string",++ "managed_stream.tags": "string",++ "managed_stream.protocol": "string",++ "managed_stream.is_nsfw": "number",++ "managed_stream.control_config_id": "null",++ "managed_stream.sort_order": "number",++ "managed_stream.created_at": "string",++ "managed_stream.updated_at": "string",++ "managed_stream.slot_clip_recording_enabled": "number",++ "managed_stream.slot_clip_notify_enabled": "number",++ "managed_stream.slot_powerchat_relay": "number",++ "managed_stream.slot_powerchat_count_rs_views": "number",++ "managed_stream.broadcast_settings": "string",++ "managed_stream.streaming_method": "string",++ "managed_stream.browser_mode": "string",++ "managed_stream.pip_source_msid": "null",++ "managed_stream.pip_defaults": "string",++ "managed_stream.default_vod_visibility": "string",++ "managed_stream.default_clip_visibility": "string",++ "managed_stream.slot_vod_recording_enabled": "number",++ "managed_stream.weather_zip": "null",++ "managed_stream.weather_detail": "string",++ "managed_stream.weather_show_location": "number",++ "managed_stream.username": "string",++ "managed_stream.display_name": "string",++ "managed_stream.avatar_url": "null",++ "managed_stream.profile_color": "string",++ "stream_key": "string",++ "broadcast_settings": "object",++ "whip_url_base": "string",++ "whip_url_source": "string",++ "whip_url_warning": "string",++ "rtmp_url": "string"++ },++ "from": [++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/managed/1/history",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/managed/1/history",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "sessions": "array",++ "sessions[]": "object",++ "sessions[].id": "number",++ "sessions[].title": "string",++ "sessions[].started_at": "string",++ "sessions[].ended_at": "string",++ "sessions[].is_live": "number",++ "sessions[].peak_viewers": "number",++ "sessions[].viewer_count": "number",++ "sessions[].duration_seconds": "number",++ "sessions[].protocol": "string",++ "sessions[].category": "string",++ "sessions[].vod_id": "null"++ },++ "from": [++ "public/js/broadcast-workspace.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/mine",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/mine",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "streams": "array",++ "streams[]": "object",++ "streams[].id": "number",++ "streams[].user_id": "number",++ "streams[].channel_id": "null",++ "streams[].managed_stream_id": "number",++ "streams[].control_config_id": "null",++ "streams[].title": "string",++ "streams[].description": "string",++ "streams[].category": "string",++ "streams[].tags": "string",++ "streams[].protocol": "string",++ "streams[].is_live": "number",++ "streams[].is_nsfw": "number",++ "streams[].viewer_count": "number",++ "streams[].peak_viewers": "number",++ "streams[].follower_count": "number",++ "streams[].thumbnail_url": "null",++ "streams[].started_at": "string",++ "streams[].ended_at": "string",++ "streams[].duration_seconds": "number",++ "streams[].created_at": "string",++ "streams[].ai_overview": "null",++ "streams[].ai_title": "null",++ "streams[].ai_category": "null",++ "streams[].ai_tags": "null",++ "streams[].ai_overview_short": "null",++ "streams[].username": "string",++ "streams[].display_name": "string",++ "streams[].avatar_url": "null",++ "streams[].profile_color": "string",++ "streams[].managed_stream_slug": "null"++ },++ "from": [++ "public/js/broadcast.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/recent-vods?limit=5&offset=0",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "vods": "array",++ "total": "number",++ "limit": "number",++ "offset": "number",++ "hasMore": "boolean"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/recent-vods?limit=5&offset=0",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "vods": "array",++ "total": "number",++ "limit": "number",++ "offset": "number",++ "hasMore": "boolean"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/recently-online?limit=5&offset=0",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "streamers": "array",++ "streamers[]": "object",++ "streamers[].user_id": "number",++ "streamers[].username": "string",++ "streamers[].display_name": "string",++ "streamers[].avatar_url": "null",++ "streamers[].profile_color": "string",++ "streamers[].last_online_at": "string",++ "streamers[].ai_overview": "null",++ "streamers[].ai_overview_short": "null",++ "streamers[].managed_streams": "array",++ "streamers[].managed_streams[]": "object",++ "streamers[].managed_streams[].managed_stream_id": "number",++ "streamers[].managed_streams[].slug": "null",++ "streamers[].managed_streams[].title": "string",++ "streamers[].managed_streams[].protocol": "string",++ "streamers[].managed_streams[].last_live_at": "string",++ "streamers[].managed_streams[].vod_thumbnail": "null",++ "total": "number",++ "limit": "number",++ "offset": "number",++ "hasMore": "boolean"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/recently-online?limit=5&offset=0",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "streamers": "array",++ "streamers[]": "object",++ "streamers[].user_id": "number",++ "streamers[].username": "string",++ "streamers[].display_name": "string",++ "streamers[].avatar_url": "null",++ "streamers[].profile_color": "string",++ "streamers[].last_online_at": "string",++ "streamers[].ai_overview": "null",++ "streamers[].ai_overview_short": "null",++ "streamers[].managed_streams": "array",++ "streamers[].managed_streams[]": "object",++ "streamers[].managed_streams[].managed_stream_id": "number",++ "streamers[].managed_streams[].slug": "null",++ "streamers[].managed_streams[].title": "string",++ "streamers[].managed_streams[].protocol": "string",++ "streamers[].managed_streams[].last_live_at": "string",++ "streamers[].managed_streams[].vod_thumbnail": "null",++ "total": "number",++ "limit": "number",++ "offset": "number",++ "hasMore": "boolean"++ },++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/setup-progress",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/setup-progress",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "tasks": "array",++ "tasks[]": "object",++ "tasks[].id": "string",++ "tasks[].group": "string",++ "tasks[].title": "string",++ "tasks[].why": "string",++ "tasks[].done": "boolean",++ "tasks[].count": "number",++ "tasks[].go": "string",++ "done": "number",++ "total": "number",++ "next": "object",++ "next.id": "string",++ "next.group": "string",++ "next.title": "string",++ "next.why": "string",++ "next.done": "boolean",++ "next.count": "number",++ "username": "string"++ },++ "from": [++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/voice-channels",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "channels": "array",++ "channels[]": "object",++ "channels[].id": "string",++ "channels[].name": "string",++ "channels[].mode": "string",++ "channels[].createdBy": "null",++ "channels[].streamId": "null",++ "channels[].permanent": "boolean",++ "channels[].maxParticipants": "number",++ "channels[].participantCount": "number",++ "channels[].participants": "array"++ },++ "from": [++ "public/js/voice-channels.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/voice-channels",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "channels": "array",++ "channels[]": "object",++ "channels[].id": "string",++ "channels[].name": "string",++ "channels[].mode": "string",++ "channels[].createdBy": "null",++ "channels[].streamId": "null",++ "channels[].permanent": "boolean",++ "channels[].maxParticipants": "number",++ "channels[].participantCount": "number",++ "channels[].participants": "array"++ },++ "from": [++ "public/js/voice-channels.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/1",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "stream": "object",++ "stream.id": "number",++ "stream.user_id": "number",++ "stream.channel_id": "null",++ "stream.managed_stream_id": "number",++ "stream.control_config_id": "null",++ "stream.title": "string",++ "stream.description": "string",++ "stream.category": "string",++ "stream.tags": "string",++ "stream.protocol": "string",++ "stream.is_live": "number",++ "stream.is_nsfw": "number",++ "stream.viewer_count": "number",++ "stream.peak_viewers": "number",++ "stream.follower_count": "number",++ "stream.thumbnail_url": "null",++ "stream.started_at": "string",++ "stream.ended_at": "null",++ "stream.duration_seconds": "number",++ "stream.created_at": "string",++ "stream.ai_overview": "null",++ "stream.ai_title": "null",++ "stream.ai_category": "null",++ "stream.ai_tags": "null",++ "stream.ai_overview_short": "null",++ "stream.username": "string",++ "stream.display_name": "string",++ "stream.avatar_url": "null",++ "stream.profile_color": "string",++ "stream.managed_stream_slug": "null",++ "stream.endpoint": "object",++ "stream.cameras": "array",++ "stream.controls": "array",++ "stream.channel": "object",++ "stream.channel.id": "number",++ "stream.channel.user_id": "number",++ "stream.channel.title": "string",++ "stream.channel.description": "string",++ "stream.channel.category": "null",++ "stream.channel.tags": "string",++ "stream.channel.protocol": "string",++ "stream.channel.is_nsfw": "number",++ "stream.channel.panels": "string",++ "stream.channel.default_vod_visibility": "string",++ "stream.channel.default_clip_visibility": "string",++ "stream.channel.weather_detail": "string",++ "stream.channel.weather_show_location": "number",++ "stream.channel.control_mode": "string",++ "stream.channel.anon_controls_enabled": "number",++ "stream.channel.control_rate_limit_ms": "number",++ "stream.channel.video_click_enabled": "number",++ "stream.channel.video_click_rate_limit_ms": "number",++ "stream.channel.active_control_config_id": "null",++ "stream.channel.created_at": "string",++ "stream.channel.updated_at": "string",++ "stream.channel.clips_allow_creator_delete": "number",++ "stream.channel.hide_ai_overview": "number",++ "stream.channel.ai_overview_pref": "string",++ "stream.channel.ai_derivation_enabled": "number",++ "stream.channel.ai_category": "null",++ "stream.channel.offline_screen_type": "string",++ "stream.channel.offline_screen_url": "null",++ "stream.channel.offline_html": "null",++ "stream.channel.offline_css": "null",++ "stream.pip_overlay": "null"++ },++ "from": [++ "public/js/app-media.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/1",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "stream": "object",++ "stream.id": "number",++ "stream.user_id": "number",++ "stream.channel_id": "null",++ "stream.managed_stream_id": "number",++ "stream.control_config_id": "null",++ "stream.title": "string",++ "stream.description": "string",++ "stream.category": "string",++ "stream.tags": "string",++ "stream.protocol": "string",++ "stream.is_live": "number",++ "stream.is_nsfw": "number",++ "stream.viewer_count": "number",++ "stream.peak_viewers": "number",++ "stream.follower_count": "number",++ "stream.thumbnail_url": "null",++ "stream.started_at": "string",++ "stream.ended_at": "null",++ "stream.duration_seconds": "number",++ "stream.created_at": "string",++ "stream.ai_overview": "null",++ "stream.ai_title": "null",++ "stream.ai_category": "null",++ "stream.ai_tags": "null",++ "stream.ai_overview_short": "null",++ "stream.username": "string",++ "stream.display_name": "string",++ "stream.avatar_url": "null",++ "stream.profile_color": "string",++ "stream.managed_stream_slug": "null",++ "stream.endpoint": "object",++ "stream.cameras": "array",++ "stream.controls": "array",++ "stream.channel": "object",++ "stream.channel.id": "number",++ "stream.channel.user_id": "number",++ "stream.channel.title": "string",++ "stream.channel.description": "string",++ "stream.channel.category": "null",++ "stream.channel.tags": "string",++ "stream.channel.protocol": "string",++ "stream.channel.is_nsfw": "number",++ "stream.channel.panels": "string",++ "stream.channel.default_vod_visibility": "string",++ "stream.channel.default_clip_visibility": "string",++ "stream.channel.weather_detail": "string",++ "stream.channel.weather_show_location": "number",++ "stream.channel.control_mode": "string",++ "stream.channel.anon_controls_enabled": "number",++ "stream.channel.control_rate_limit_ms": "number",++ "stream.channel.video_click_enabled": "number",++ "stream.channel.video_click_rate_limit_ms": "number",++ "stream.channel.active_control_config_id": "null",++ "stream.channel.created_at": "string",++ "stream.channel.updated_at": "string",++ "stream.channel.clips_allow_creator_delete": "number",++ "stream.channel.hide_ai_overview": "number",++ "stream.channel.ai_overview_pref": "string",++ "stream.channel.ai_derivation_enabled": "number",++ "stream.channel.ai_category": "null",++ "stream.channel.offline_screen_type": "string",++ "stream.channel.offline_screen_url": "null",++ "stream.channel.offline_html": "null",++ "stream.channel.offline_css": "null",++ "stream.pip_overlay": "null"++ },++ "from": [++ "public/js/app-media.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/1/call",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "call_mode": "null",++ "channelId": "null",++ "participants": "array",++ "participant_count": "number"++ },++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/1/call",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "call_mode": "null",++ "channelId": "null",++ "participants": "array",++ "participant_count": "number"++ },++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/1/endpoint",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/1/endpoint",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "endpoint": "object",++ "endpoint.whipUrlBase": "string",++ "endpoint.whipUrlSource": "string",++ "endpoint.whipUrlWarning": "string",++ "stream_key": "string"++ },++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/1/rtmp-status",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/streams/1/rtmp-status",++ "auth": "user",++ "status": 400,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/vibe-coding/channel/n1star/1/events?limit=5",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "managed_stream": "object",++ "managed_stream.id": "number",++ "managed_stream.slug": "null",++ "managed_stream.title": "string",++ "settings": "object",++ "settings.enabled": "number",++ "settings.widget_title": "string",++ "settings.viewer_depth": "string",++ "settings.show_prompts": "number",++ "settings.show_responses": "number",++ "settings.show_thinking": "number",++ "settings.show_tool_calls": "number",++ "settings.show_tool_arguments": "number",++ "settings.show_file_events": "number",++ "settings.show_file_snippets": "number",++ "settings.redact_file_paths": "number",++ "settings.paused": "number",++ "settings.delay_ms": "number",++ "settings.max_events": "number",++ "settings.max_prompt_chars": "number",++ "settings.max_response_chars": "number",++ "settings.max_thinking_chars": "number",++ "settings.max_tool_chars": "number",++ "settings.max_snippet_chars": "number",++ "publisher": "null",++ "events": "array"++ },++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/vibe-coding/channel/n1star/1/events?limit=5",++ "auth": "user",++ "status": 200,++ "kind": "json",++ "reads": {++ "managed_stream": "object",++ "managed_stream.id": "number",++ "managed_stream.slug": "null",++ "managed_stream.title": "string",++ "settings": "object",++ "settings.enabled": "number",++ "settings.widget_title": "string",++ "settings.viewer_depth": "string",++ "settings.show_prompts": "number",++ "settings.show_responses": "number",++ "settings.show_thinking": "number",++ "settings.show_tool_calls": "number",++ "settings.show_tool_arguments": "number",++ "settings.show_file_events": "number",++ "settings.show_file_snippets": "number",++ "settings.redact_file_paths": "number",++ "settings.paused": "number",++ "settings.delay_ms": "number",++ "settings.max_events": "number",++ "settings.max_prompt_chars": "number",++ "settings.max_response_chars": "number",++ "settings.max_thinking_chars": "number",++ "settings.max_tool_chars": "number",++ "settings.max_snippet_chars": "number",++ "publisher": "null",++ "events": "array"++ },++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/vods/mine?limit=5&offset=5",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/vods/mine?limit=5&offset=5",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/vods/stream/1/live",++ "auth": "anon",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/vods/stream/1/live",++ "auth": "user",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/vods/1/context",++ "auth": "anon",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/context-cards.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/vods/1/context",++ "auth": "user",++ "status": 404,++ "kind": "json",++ "from": [++ "public/js/context-cards.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/vods/1/live-info",++ "auth": "anon",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app-media.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/vods/1/live-info",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app-media.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/vods/1",++ "auth": "anon",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app-media.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/vods/1",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app-media.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/vods/1/memories",++ "auth": "anon",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app-media.js"++ ]++ },++ {++ "method": "GET",++ "path": "/api/vods/1/memories",++ "auth": "user",++ "status": 502,++ "kind": "json",++ "from": [++ "public/js/app-media.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/admin/users/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/ai-viewers/bots/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "PATCH",++ "path": "/api/ai-viewers/bots/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/ai-viewers/bots/1/clear-memory",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/ai-viewers/bots/1/mute",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/ai-viewers/byo/test",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/ai-viewers/clone",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/ai-viewers/config",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/ai-viewers/preview",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/ai-viewers.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/arena/beefs/1/hype",++ "auth": "anon",++ "status": 400,++ "kind": "json",++ "from": [++ "public/js/arena.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/auth/avatar",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/settings.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/auth/logout",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "ok": "boolean"++ },++ "from": [++ "public/js/app.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/auth/preferences",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/auth/profile",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/guides-live.js",++ "public/js/settings.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/auth/refresh",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "access_token": "null",++ "user": "null"++ },++ "from": [++ "public/js/app.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/auth/stream-key/regenerate",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app.js",++ "public/js/broadcast.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/auth/tokens",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/auth/tokens/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/channels/1/moderation",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/channels/1/moderation/messages/1/delete",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard-moderation.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/channels/1/mods",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard-moderation.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/channels/1/mods/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard-moderation.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/clips/bulk",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/clips/settings/channel",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/clips/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-media.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/clips/1/recut",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-media.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/clips/1/title",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-media.js",++ "public/js/stream-player.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/clips/1/visibility",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-media.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/coins/bonus",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/opencoins.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/coins/config",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/opencoins.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/coins/heartbeat",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/opencoins.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/coins/redeem",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/opencoins.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/coins/redemptions/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/opencoins.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/coins/rewards",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/opencoins.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/coins/rewards/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/opencoins.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/coins/rewards/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/opencoins.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/comments/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-media.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/comments/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-media.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/comments/1/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-media.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/controls/configs",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/controls/configs/deactivate",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/controls/configs/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/controls/configs/1/activate",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/controls/configs/1/buttons/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/controls/configs/1/buttons",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/controls/settings/channel",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/controls/whitelist",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/controls/whitelist/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/controls/1/config",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/cosmetics/activate",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/cosmetics-ui.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/cosmetics/deactivate",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/cosmetics-ui.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/cosmetics/equip",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/cosmetics-ui.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/cosmetics/unequip",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/cosmetics-ui.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/easter-egg/reveal",++ "auth": "anon",++ "status": 403,++ "kind": "json",++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/easter-egg/solve",++ "auth": "anon",++ "status": 400,++ "kind": "json",++ "from": [++ "public/js/app-home.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/emotes",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/channel-uploads.js",++ "public/js/emotes.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/emotes/sources",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/emotes.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/emotes/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/channel-uploads.js",++ "public/js/emotes.js"++ ]++ },++ {++ "method": "PATCH",++ "path": "/api/emotes/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/channel-uploads.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/funds/cashout",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/vibes.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/funds/donate",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/vibes.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/funds/goals",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app.js",++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/funds/goals/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/funds/goals/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/funds/recycle",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/i18n/translate",++ "auth": "anon",++ "status": 503,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/media/advance",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-channel.js",++ "public/js/broadcast.js",++ "public/js/media-player.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/media/queue/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-channel.js",++ "public/js/media-player.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/media/queue/1/move",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-channel.js",++ "public/js/media-player.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/media/queue/1/play",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-channel.js",++ "public/js/media-player.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/media/queue/1/refund",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/media-player.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/media/queue/1/position",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/media-player.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/media/queue/1/fail",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/media-player.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/media/quote",++ "auth": "anon",++ "status": 400,++ "kind": "json",++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/media/request",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/media/settings",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/media-player.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/media/start",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js",++ "public/js/media-player.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/mod/delete-message",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/mod/delete-user-messages",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/mod/global-ban",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-channel.js",++ "public/js/chat.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/mod/ip-approval/1/approve",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard-moderation.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/mod/ip-approval/1/deny",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard-moderation.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/mod/ip/ban-all",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/mod/relay-user/hide",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/mod/stream-ban",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/mod/tts-voice/preview",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/mod/tts-voice/1/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/news/my-settings",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/onvif/cameras",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/onvif-dashboard.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/onvif/cameras/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/onvif-dashboard.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/onvif/cameras/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/onvif-dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/onvif/cameras/1/presets",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/onvif-dashboard.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/onvif/cameras/1/presets/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/onvif-dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/onvif/discover",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/onvif-dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/pastes",++ "auth": "anon",++ "status": 504,++ "kind": "json",++ "from": [++ "public/js/pastes.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/pastes/bulk",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/pastes/n1star",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/pastes.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/pastes/n1star",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/pastes.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/pastes/n1star/comments",++ "auth": "anon",++ "status": 504,++ "kind": "json",++ "from": [++ "public/js/pastes.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/pastes/n1star/comments/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/pastes.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/pastes/n1star/copy",++ "auth": "anon",++ "status": 504,++ "kind": "json",++ "from": [++ "public/js/pastes.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/pastes/n1star/fork",++ "auth": "anon",++ "status": 504,++ "kind": "json",++ "from": [++ "public/js/pastes.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/pastes/n1star/like",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/pastes.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/pastes/n1star/set-avatar",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/pastes.js",++ "public/js/settings.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/payments/bucks/checkout",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/vibes.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/payments/subscribe",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/vibes.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/powerchat/oauth/connection",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/powerchat.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/powerchat/test-alert",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/powerchat.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/powerchat/test-tip",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/powerchat.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/recap/1/regenerate",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/recap.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/restream/destinations",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js",++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/restream/destinations/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/restream/destinations/1/start",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/restream/destinations/1/stop",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/restream/destinations/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/restream/oauth/1/connection",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/robotstreamer/integration",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js",++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/robotstreamer/integration/login",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js",++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/robotstreamer/integration/validate",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js",++ "public/js/broadcast.js",++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/robotstreamer/integration?managed_stream_id=1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/robotstreamer/restream/start",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/robotstreamer/restream/stop",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/streams",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/streams/broadcast-settings",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js",++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/streams/channel",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js",++ "public/js/settings.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/streams/channel/offline-screen",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/settings.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/streams/channel/n1star/about",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/streams/channel/n1star/follow",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/streams/diag-log",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "ok": "boolean"++ },++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/streams/goal-media",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/streams/managed",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app.js",++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/streams/managed/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/guides-live.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/streams/managed/1/regenerate-key",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/streams/managed/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast-workspace.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/streams/panel-image",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-channel.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/streams/voice-channels",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/voice-channels.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/streams/voice-channels/call-user",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/streams/voice-channels/call-user/respond",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/chat.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/streams/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/streams/1/follow",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/streams/1/heartbeat",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/thumbnails/live/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/vods/bulk",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/vods/bulk-delete-old",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/vods/clips",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-media.js",++ "public/js/broadcast.js",++ "public/js/stream-player.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/vods/clips/1/trim",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/stream-player.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/vods/stream/1/chunk",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/vods/stream/1/chunk?token=",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/vods/stream/1/finalize",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/broadcast.js"++ ]++ },++ {++ "method": "DELETE",++ "path": "/api/vods/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-media.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "PUT",++ "path": "/api/vods/1",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/app-media.js",++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/api/vods/1/publish",++ "auth": "anon",++ "status": 401,++ "kind": "json",++ "from": [++ "public/js/dashboard.js"++ ]++ },++ {++ "method": "POST",++ "path": "/banned/continue",++ "auth": "anon",++ "status": 200,++ "kind": "json",++ "reads": {++ "ok": "boolean",++ "name": "null",++ "lifted": "number"++ },++ "from": [++ "public/banned.html"++ ]++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,1257 @@++{++ "service": "live",++ "release": "8caa540466f9af86247febdbba1339b68919a42f",++ "recorded_at": "2026-09-26T13:44:31.818Z",++ "note": "N-1 fixture: written by `npm run n-1:record`, replayed by test/n-1.test.js",++ "user_version": 0,++ "schema": [++ "CREATE TABLE ai_chatbot_configs (\n user_id INTEGER PRIMARY KEY,\n enabled INTEGER DEFAULT 0,\n base_url TEXT DEFAULT 'https://api.openai.com/v1',\n api_token TEXT DEFAULT '',\n model TEXT DEFAULT 'gpt-4o-mini',\n transcribe_enabled INTEGER DEFAULT 0,\n transcribe_model TEXT DEFAULT 'whisper-1',\n num_bots INTEGER DEFAULT 3,\n post_interval_seconds INTEGER DEFAULT 45,\n persona TEXT DEFAULT '',\n vision_enabled INTEGER DEFAULT 0,\n last_validated_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE ai_timeline_cache (\n user_id INTEGER PRIMARY KEY,\n payload TEXT,\n generated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE ai_usage (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n kind TEXT, -- paste_image | paste_text | stream_memory | ai_viewers | ...\n model TEXT,\n input_tokens INTEGER DEFAULT 0,\n output_tokens INTEGER DEFAULT 0,\n cost_usd REAL DEFAULT 0,\n owner_user_id INTEGER, -- streamer this spend is attributed to (NULL = platform/global)\n source TEXT, -- feature bucket, e.g. 'ai_viewers' (NULL = legacy/global)\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n , cached_tokens INTEGER DEFAULT 0, role TEXT, provider TEXT, latency_ms INTEGER)",++ "CREATE TABLE ai_viewer_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_user_id INTEGER NOT NULL, stream_id INTEGER,\n event TEXT NOT NULL, -- tick | line | skip | mention | fold | vision | degrade | pause | error | info\n bot_username TEXT, target TEXT, thread_id INTEGER, chat_message_id INTEGER,\n text TEXT, reason TEXT,\n tokens_in INTEGER, tokens_cached INTEGER, tokens_out INTEGER, cost_usd REAL, model TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",++ "CREATE TABLE ai_viewer_threads (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_user_id INTEGER NOT NULL,\n stream_id INTEGER,\n kind TEXT NOT NULL, -- bot_bot | bot_viewer | bot_streamer\n participants_json TEXT NOT NULL, -- [\"botname\",\"viewer\"...]\n topic TEXT,\n state TEXT DEFAULT 'open', -- open | closed\n awaiting TEXT, -- bot username expected to speak next (or null)\n turns INTEGER DEFAULT 0,\n last_line TEXT, last_line_by TEXT, last_line_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",++ "CREATE TABLE anon_ip_mappings (\n ip TEXT PRIMARY KEY,\n anon_num INTEGER NOT NULL UNIQUE\n , created_at DATETIME)",++ "CREATE TABLE api_keys (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n key_hash TEXT UNIQUE NOT NULL,\n label TEXT DEFAULT 'Default',\n permissions TEXT DEFAULT '[\"control\",\"stream\"]', -- JSON array\n last_used DATETIME,\n is_active INTEGER DEFAULT 1,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE api_tokens (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n token_hash TEXT UNIQUE NOT NULL,\n label TEXT DEFAULT 'Bot Token',\n scopes TEXT DEFAULT '[\"chat\",\"read\"]', -- JSON array: chat, stream, control, read, admin\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n last_used_at DATETIME,\n expires_at DATETIME, -- NULL = never expires\n is_active INTEGER DEFAULT 1,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE app_state (\n key TEXT PRIMARY KEY,\n value TEXT,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",++ "CREATE TABLE approved_ips (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_id INTEGER NOT NULL,\n ip_address TEXT NOT NULL,\n approved_by INTEGER,\n source TEXT DEFAULT 'auto',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(channel_id, ip_address),\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE,\n FOREIGN KEY (approved_by) REFERENCES users(id) ON DELETE SET NULL\n )",++ "CREATE TABLE arena_beef_hype (\n beef_id INTEGER NOT NULL,\n side TEXT NOT NULL,\n voter_key TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (beef_id, voter_key)\n )",++ "CREATE TABLE arena_beef_sides (\n beef_id INTEGER NOT NULL,\n voter_key TEXT NOT NULL,\n side TEXT NOT NULL,\n user_id INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (beef_id, voter_key)\n )",++ "CREATE TABLE arena_beefs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n a_user_id INTEGER NOT NULL,\n b_user_id INTEGER NOT NULL,\n status TEXT NOT NULL DEFAULT 'open',\n score_a REAL DEFAULT 0,\n score_b REAL DEFAULT 0,\n hits_a INTEGER DEFAULT 0,\n hits_b INTEGER DEFAULT 0,\n crowd_a INTEGER DEFAULT 0,\n crowd_b INTEGER DEFAULT 0,\n on_clock TEXT,\n clock_until DATETIME,\n responded INTEGER DEFAULT 0,\n last_a_at DATETIME,\n last_b_at DATETIME,\n feed_json TEXT,\n opener_line TEXT,\n winner_user_id INTEGER,\n resolution TEXT,\n opened_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n ends_at DATETIME,\n resolved_at DATETIME\n , headline TEXT, result_headline TEXT, upset INTEGER DEFAULT 0, rematch INTEGER DEFAULT 0, bounty_topic_id INTEGER)",++ "CREATE TABLE arena_mic_moments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n stream_id INTEGER,\n vod_id INTEGER,\n sec INTEGER,\n kind TEXT NOT NULL DEFAULT 'trash',\n target_user_id INTEGER,\n beef_id INTEGER,\n aimed_at TEXT,\n text TEXT NOT NULL,\n about TEXT,\n quality REAL DEFAULT 0,\n announcer TEXT,\n said_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",++ "CREATE TABLE arena_profiles (\n user_id INTEGER PRIMARY KEY,\n stats_json TEXT,\n persona_json TEXT,\n persona_model TEXT,\n persona_generated_at DATETIME,\n image_path TEXT,\n image_prompt TEXT,\n image_model TEXT,\n image_generated_at DATETIME,\n image_error TEXT,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n , quotes_json TEXT, quotes_generated_at DATETIME)",++ "CREATE TABLE arena_trash_levels (\n user_id INTEGER PRIMARY KEY,\n xp INTEGER DEFAULT 0,\n level INTEGER DEFAULT 1,\n angles_cleared INTEGER DEFAULT 0,\n topics_conquered INTEGER DEFAULT 0,\n beef_hits INTEGER DEFAULT 0,\n best_line TEXT,\n best_line_vod_id INTEGER,\n best_line_sec INTEGER,\n best_line_score REAL DEFAULT 0,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n , topic_moments INTEGER DEFAULT 0, topics_joined INTEGER DEFAULT 0, mic_moments INTEGER DEFAULT 0)",++ "CREATE TABLE arena_xp_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n amount INTEGER NOT NULL,\n reason TEXT NOT NULL,\n ref_id INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",++ "CREATE TABLE bans (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER, -- NULL = site-wide\n user_id INTEGER,\n ip_address TEXT,\n anon_id TEXT,\n reason TEXT,\n banned_by INTEGER,\n expires_at DATETIME, -- NULL = permanent\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (banned_by) REFERENCES users(id) ON DELETE SET NULL\n)",++ "CREATE TABLE camera_presets (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n camera_id INTEGER NOT NULL,\n name TEXT NOT NULL,\n pan REAL NOT NULL, -- 0.0-1.0\n tilt REAL NOT NULL,\n zoom REAL NOT NULL,\n preset_token TEXT, -- ONVIF device preset token (if supported)\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (camera_id) REFERENCES camera_profiles(id) ON DELETE CASCADE\n)",++ "CREATE TABLE camera_profiles (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n stream_id INTEGER, -- NULL = global profile\n name TEXT NOT NULL,\n onvif_url TEXT NOT NULL, -- e.g., http://192.168.1.100:8080\n username TEXT NOT NULL,\n password_hash TEXT NOT NULL, -- bcrypt hashed\n pan_speed REAL DEFAULT 0.5, -- 0.0-1.0\n tilt_speed REAL DEFAULT 0.5,\n zoom_speed REAL DEFAULT 0.5,\n is_active INTEGER DEFAULT 1,\n last_connected DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n)",++ "CREATE TABLE cameras (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n camera_index INTEGER DEFAULT 0,\n label TEXT DEFAULT 'Main',\n protocol TEXT DEFAULT 'jsmpeg',\n jsmpeg_video_port INTEGER,\n jsmpeg_audio_port INTEGER,\n webrtc_room_id TEXT,\n is_active INTEGER DEFAULT 1,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n)",++ "CREATE TABLE channel_ai_bots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_user_id INTEGER NOT NULL, -- the streamer (owner)\n username TEXT NOT NULL,\n display_name TEXT,\n avatar_color TEXT DEFAULT '#8a8aff',\n source TEXT DEFAULT 'ambient', -- 'ambient' | 'clone'\n cloned_from_kind TEXT, -- 'user' | 'relay' | NULL\n cloned_from_ref TEXT, -- user_id (string) or \"platform:username\"\n persona_json TEXT DEFAULT '{}', -- character + typing style + identity\n brain_json TEXT DEFAULT '{}', -- rolling condensed memory + short timeline\n is_active INTEGER DEFAULT 1,\n msg_count INTEGER DEFAULT 0,\n last_active_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (channel_user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE channel_ai_config (\n user_id INTEGER PRIMARY KEY,\n enabled INTEGER DEFAULT 0,\n num_ambient_bots INTEGER DEFAULT 3,\n pacing_seconds INTEGER DEFAULT 45,\n persona TEXT DEFAULT '',\n transcribe_enabled INTEGER DEFAULT 0,\n vision_enabled INTEGER DEFAULT 0,\n use_shared_key INTEGER DEFAULT 1, -- 1 = OpenVibe.Live shared key (capped), 0 = BYO\n daily_budget_cents INTEGER DEFAULT 20, -- shared-key daily cap\n byo_key TEXT DEFAULT '',\n byo_base_url TEXT DEFAULT '',\n byo_model TEXT DEFAULT 'gpt-4o-mini',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, settings_json TEXT DEFAULT '{}',\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE channel_moderation_settings (\n channel_id INTEGER PRIMARY KEY,\n slow_mode_seconds INTEGER DEFAULT 0,\n followers_only INTEGER DEFAULT 0,\n emote_only INTEGER DEFAULT 0,\n allow_anonymous INTEGER DEFAULT 1,\n links_allowed INTEGER DEFAULT 1,\n gifs_enabled INTEGER DEFAULT 1,\n account_age_gate_hours INTEGER DEFAULT 0,\n caps_percentage_limit INTEGER DEFAULT 0,\n aggressive_filter INTEGER DEFAULT 0,\n max_message_length INTEGER DEFAULT 500,\n slur_filter_enabled INTEGER DEFAULT 0,\n slur_filter_use_builtin INTEGER DEFAULT 1,\n slur_filter_terms TEXT DEFAULT '',\n slur_filter_regexes TEXT DEFAULT '',\n slur_filter_nudge_message TEXT DEFAULT '',\n slur_filter_disabled_categories TEXT DEFAULT '[]',\n ip_approval_mode INTEGER DEFAULT 0,\n soundboard_enabled INTEGER DEFAULT 1,\n soundboard_allow_pitch INTEGER DEFAULT 1,\n soundboard_allow_speed INTEGER DEFAULT 1,\n soundboard_banned_ids TEXT DEFAULT '',\n viewer_auto_delete_enabled INTEGER DEFAULT 1,\n viewer_delete_all_enabled INTEGER DEFAULT 1,\n custom_emotes_enabled INTEGER DEFAULT 1, -- viewers may upload gif/png emotes for this channel\n custom_sounds_enabled INTEGER DEFAULT 1, -- viewers may upload !sound commands for this channel\n max_sound_seconds INTEGER DEFAULT 10, -- max duration of an uploaded channel sound\n uploads_mods_only INTEGER DEFAULT 0, -- restrict emote/sound uploads to channel mods\n mods_can_edit_about INTEGER DEFAULT 0, -- allow channel mods to edit the streamer's About/panels\n donation_sound_url TEXT, -- on-disk path to the streamer's donation alert sound\n donation_sound_mime TEXT,\n goal_sound_url TEXT, -- optional override sound for goal-reached\n goal_sound_mime TEXT,\n emote_scale INTEGER DEFAULT 100, -- emote display size in chat, percent (50-300)\n sub_only INTEGER DEFAULT 0, -- only active subscribers (and the channel's moderators) may chat\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, tts_max_length INTEGER DEFAULT 200, sound_min_speed REAL DEFAULT 0.5, sound_max_speed REAL DEFAULT 3.0, sound_min_pitch_cents INTEGER DEFAULT -1200, sound_max_pitch_cents INTEGER DEFAULT 1200, emote_size_min INTEGER DEFAULT 50, emote_size_max INTEGER DEFAULT 200, sounds_mods_only INTEGER DEFAULT 0,\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE\n)",++ "CREATE TABLE channel_moderators (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n added_by INTEGER NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(channel_id, user_id),\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (added_by) REFERENCES users(id) ON DELETE SET NULL\n)",++ "CREATE TABLE channel_points (\n user_id INTEGER NOT NULL,\n streamer_id INTEGER NOT NULL,\n balance INTEGER NOT NULL DEFAULT 0,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (user_id, streamer_id)\n )",++ "CREATE TABLE channel_points_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n idempotency_key TEXT NOT NULL UNIQUE,\n user_id INTEGER NOT NULL,\n streamer_id INTEGER NOT NULL,\n delta INTEGER NOT NULL,\n reason TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",++ "CREATE TABLE channel_sounds (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_owner_id INTEGER NOT NULL, -- streamer whose channel this sound belongs to\n command TEXT NOT NULL, -- trigger word (without leading '!'), lowercased\n url TEXT NOT NULL, -- served path of the audio file on disk\n mime TEXT DEFAULT 'audio/mpeg',\n duration_seconds REAL DEFAULT 0,\n created_by INTEGER, -- uploader user id (NULL if removed user)\n created_by_name TEXT DEFAULT '',\n is_approved INTEGER DEFAULT 1,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, emote_code TEXT DEFAULT '',\n -- Multiple sounds may share a command; playback picks one at random.\n FOREIGN KEY (channel_owner_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE channels (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL UNIQUE,\n title TEXT DEFAULT 'Untitled Channel',\n description TEXT DEFAULT '',\n category TEXT DEFAULT 'irl',\n tags TEXT DEFAULT '[]',\n protocol TEXT DEFAULT 'webrtc' CHECK(protocol IN ('jsmpeg', 'webrtc', 'rtmp')),\n is_nsfw INTEGER DEFAULT 0,\n auto_record INTEGER DEFAULT 0,\n offline_banner_url TEXT,\n panels TEXT DEFAULT '[]', -- JSON array of info panels\n emote_sources TEXT DEFAULT '{\"defaults\":true,\"custom\":true,\"ffz\":true,\"bttv\":true,\"7tv\":true}',\n default_vod_visibility TEXT DEFAULT 'public' CHECK(default_vod_visibility IN ('public', 'unlisted', 'private')),\n default_clip_visibility TEXT DEFAULT 'public' CHECK(default_clip_visibility IN ('public', 'unlisted', 'private')),\n vod_recording_enabled INTEGER DEFAULT 1,\n force_vod_recording_disabled INTEGER DEFAULT 0,\n weather_zip TEXT DEFAULT NULL,\n weather_detail TEXT DEFAULT 'basic' CHECK(weather_detail IN ('off', 'basic', 'hourly', 'detailed')),\n weather_show_location INTEGER DEFAULT 0,\n control_mode TEXT DEFAULT 'open' CHECK(control_mode IN ('open','whitelist','disabled')),\n anon_controls_enabled INTEGER DEFAULT 1,\n control_rate_limit_ms INTEGER DEFAULT 100,\n video_click_enabled INTEGER DEFAULT 0,\n video_click_rate_limit_ms INTEGER DEFAULT 0,\n active_control_config_id INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, chat_language TEXT DEFAULT 'auto', cp_name TEXT DEFAULT 'Channel Points', cp_icon TEXT DEFAULT 'fa-coins', cp_watch_interval_min INTEGER DEFAULT 5, cp_watch_amount INTEGER DEFAULT 10, cp_game_interval_min INTEGER DEFAULT 0, clips_allow_creator_delete INTEGER DEFAULT 0, hide_ai_overview INTEGER DEFAULT 0, ai_overview_pref TEXT DEFAULT 'auto', ai_derivation_enabled INTEGER DEFAULT 1, ai_category TEXT, force_nsfw INTEGER DEFAULT 0, offline_screen_type TEXT DEFAULT 'none', offline_screen_url TEXT, offline_html TEXT, offline_css TEXT,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE chat_ai_summaries (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n scope TEXT NOT NULL,\n subject_id INTEGER NOT NULL DEFAULT 0,\n window TEXT NOT NULL,\n overview TEXT DEFAULT '',\n memory_json TEXT DEFAULT '',\n timeline_json TEXT DEFAULT '[]',\n message_count INTEGER DEFAULT 0,\n window_message_count INTEGER DEFAULT 0,\n last_message_id INTEGER DEFAULT 0,\n window_label TEXT DEFAULT '',\n window_start DATETIME,\n window_end DATETIME,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(scope, subject_id, window)\n )",++ "CREATE TABLE \"chat_messages\" (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER,\n user_id INTEGER, -- NULL for anon\n anon_id TEXT, -- 'anon12345' format\n username TEXT,\n message TEXT NOT NULL,\n message_type TEXT DEFAULT 'chat' CHECK(message_type IN ('chat', 'system', 'donation', 'command', 'tts', 'channel-sound', 'soundboard', 'clip')),\n metadata TEXT, -- JSON sidecar for rich events (donation/goal-reached)\n is_global INTEGER DEFAULT 0,\n is_deleted INTEGER DEFAULT 0,\n is_filtered INTEGER DEFAULT 0,\n reply_to_id INTEGER REFERENCES chat_messages(id) ON DELETE SET NULL,\n source_platform TEXT,\n deleted_by INTEGER,\n deleted_at DATETIME,\n auto_delete_at DATETIME,\n timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, channel_user_id INTEGER,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL\n)",++ "CREATE TABLE chat_timeline_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n scope TEXT NOT NULL DEFAULT 'global',\n subject_id INTEGER NOT NULL DEFAULT 0,\n ts DATETIME NOT NULL,\n label TEXT NOT NULL,\n detail TEXT DEFAULT '',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",++ "CREATE TABLE clip_ai_state (\n clip_id INTEGER PRIMARY KEY,\n ai_overview_short TEXT,\n ai_transcript_json TEXT,\n transcript_status TEXT,\n transcript_attempts INTEGER DEFAULT 0,\n transcript_error TEXT,\n transcript_next_at DATETIME,\n clip_notified INTEGER DEFAULT 0,\n clip_notify_at DATETIME\n , ai_overview TEXT, transcript_partial_json TEXT, transcript_progress_sec INTEGER DEFAULT 0)",++ "CREATE TABLE clips (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n vod_id INTEGER,\n stream_id INTEGER,\n user_id INTEGER NOT NULL,\n title TEXT DEFAULT 'Untitled Clip',\n description TEXT DEFAULT '',\n file_path TEXT,\n thumbnail_url TEXT,\n start_time REAL NOT NULL DEFAULT 0,\n end_time REAL NOT NULL DEFAULT 0,\n duration_seconds REAL DEFAULT 0,\n is_public INTEGER DEFAULT 1,\n view_count INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, storage_provider TEXT DEFAULT 'local', storage_key TEXT, ai_overview TEXT, ai_transcript TEXT, ai_analyzed_at DATETIME, clip_notified INTEGER DEFAULT 0, clip_notify_at DATETIME, visibility TEXT DEFAULT 'public', auto_generated INTEGER DEFAULT 0,\n FOREIGN KEY (vod_id) REFERENCES vods(id) ON DELETE SET NULL,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE coin_redemptions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n reward_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n stream_id INTEGER,\n status TEXT DEFAULT 'pending' CHECK(status IN ('pending', 'fulfilled', 'rejected', 'refunded')),\n user_input TEXT, -- message if requires_input\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n resolved_at DATETIME,\n FOREIGN KEY (reward_id) REFERENCES coin_rewards(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL\n)",++ "CREATE TABLE coin_rewards (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n streamer_id INTEGER NOT NULL, -- who created this reward\n title TEXT NOT NULL,\n description TEXT DEFAULT '',\n cost INTEGER NOT NULL DEFAULT 100, -- OpenCoins cost\n icon TEXT DEFAULT 'fa-star', -- Font Awesome icon\n color TEXT DEFAULT '#8b5cf6', -- accent color\n cooldown_seconds INTEGER DEFAULT 0, -- per-user cooldown\n max_per_stream INTEGER DEFAULT 0, -- 0 = unlimited\n requires_input INTEGER DEFAULT 0, -- viewer must type a message\n is_enabled INTEGER DEFAULT 1,\n is_global INTEGER DEFAULT 0, -- admin: available on all channels\n redemption_count INTEGER DEFAULT 0,\n sort_order INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (streamer_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE coin_transactions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n stream_id INTEGER, -- NULL for non-stream earnings\n amount INTEGER NOT NULL, -- positive = earned, negative = spent\n type TEXT NOT NULL CHECK(type IN ('watch', 'chat_bonus', 'follow_bonus', 'redeem', 'admin_grant', 'refund')),\n reward_id INTEGER, -- if type='redeem', links to coin_rewards\n message TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (reward_id) REFERENCES coin_rewards(id) ON DELETE SET NULL\n)",++ "CREATE TABLE comments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n content_type TEXT NOT NULL CHECK(content_type IN ('vod', 'clip')),\n content_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n parent_id INTEGER, -- NULL = top-level, set = reply\n message TEXT NOT NULL,\n is_deleted INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (parent_id) REFERENCES comments(id) ON DELETE CASCADE\n)",++ "CREATE TABLE content_views (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n content_type TEXT NOT NULL CHECK(content_type IN ('vod', 'clip')),\n content_id INTEGER NOT NULL,\n ip TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(content_type, content_id, ip)\n)",++ "CREATE TABLE control_config_buttons (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n config_id INTEGER NOT NULL,\n label TEXT NOT NULL,\n command TEXT NOT NULL,\n icon TEXT DEFAULT 'fa-gamepad',\n control_type TEXT DEFAULT 'button' CHECK(control_type IN ('button','toggle','dpad','keyboard')),\n key_binding TEXT,\n cooldown_ms INTEGER DEFAULT 500,\n sort_order INTEGER DEFAULT 0,\n btn_color TEXT DEFAULT '',\n btn_bg TEXT DEFAULT '',\n btn_border_color TEXT DEFAULT '',\n is_enabled INTEGER DEFAULT 1,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (config_id) REFERENCES control_configs(id) ON DELETE CASCADE\n )",++ "CREATE TABLE control_configs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n name TEXT NOT NULL,\n description TEXT DEFAULT '',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE control_whitelist (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n added_by INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(channel_id, user_id),\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE dm_blocks (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n blocker_id INTEGER NOT NULL,\n blocked_id INTEGER NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(blocker_id, blocked_id),\n FOREIGN KEY (blocker_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (blocked_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE dm_conversations (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT,\n is_group INTEGER DEFAULT 0,\n created_by INTEGER NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE dm_messages (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n conversation_id INTEGER NOT NULL,\n sender_id INTEGER NOT NULL,\n message TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (conversation_id) REFERENCES dm_conversations(id) ON DELETE CASCADE,\n FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE dm_participants (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n conversation_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n last_read_at DATETIME DEFAULT '1970-01-01 00:00:00',\n joined_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(conversation_id, user_id),\n FOREIGN KEY (conversation_id) REFERENCES dm_conversations(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE donation_goals (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n title TEXT NOT NULL,\n target_amount INTEGER NOT NULL, -- in Vibes (dollars)\n current_amount INTEGER DEFAULT 0,\n is_active INTEGER DEFAULT 1,\n image_url TEXT, -- optional goal media (webp image / webm video)\n media_type TEXT, -- 'image' | 'video'\n reached_at DATETIME, -- set when target hit; drives the ~1h celebration window\n sort_order INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE easter_egg_solves (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n egg_date TEXT NOT NULL,\n solver_key TEXT NOT NULL,\n user_id INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(egg_date, solver_key)\n )",++ "CREATE TABLE \"emotes\" (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n code TEXT NOT NULL,\n url TEXT NOT NULL,\n animated INTEGER DEFAULT 0,\n width INTEGER DEFAULT 28,\n height INTEGER DEFAULT 28,\n is_global INTEGER DEFAULT 0,\n is_approved INTEGER DEFAULT 1,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n channel_owner_id INTEGER,\n size INTEGER DEFAULT 100,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE follows (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n follower_id INTEGER NOT NULL,\n streamer_id INTEGER NOT NULL,\n email_notify INTEGER DEFAULT 0,\n push_notify INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(follower_id, streamer_id),\n FOREIGN KEY (follower_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (streamer_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE hidden_relay_users (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_id INTEGER,\n platform TEXT NOT NULL,\n external_username TEXT NOT NULL,\n action TEXT DEFAULT 'hide' CHECK(action IN ('hide','ban')),\n reason TEXT,\n created_by INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE,\n FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL\n )",++ "CREATE TABLE ip_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER,\n anon_id TEXT,\n ip_address TEXT NOT NULL,\n action TEXT NOT NULL DEFAULT 'chat',\n geo_country TEXT,\n geo_region TEXT,\n geo_city TEXT,\n geo_isp TEXT,\n geo_org TEXT,\n geo_ll TEXT,\n user_agent TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL\n )",++ "CREATE TABLE kick_channel_cache (\n slug TEXT PRIMARY KEY,\n chatroom_id INTEGER,\n kick_channel_id INTEGER,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",++ "CREATE TABLE linked_accounts (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n service TEXT NOT NULL,\n service_user_id TEXT NOT NULL,\n service_username TEXT,\n linked_at DATETIME DEFAULT CURRENT_TIMESTAMP, subject_id TEXT,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n UNIQUE(service, service_user_id)\n )",++ "CREATE TABLE managed_streams (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n channel_id INTEGER,\n slug TEXT, -- optional unique string ID (must contain non-numeric chars)\n title TEXT DEFAULT 'Untitled Stream',\n description TEXT DEFAULT '',\n category TEXT DEFAULT 'irl',\n tags TEXT DEFAULT '[]',\n protocol TEXT DEFAULT 'webrtc' CHECK(protocol IN ('jsmpeg', 'webrtc', 'rtmp')),\n stream_key TEXT UNIQUE NOT NULL, -- per-stream stable key (reusable across sessions)\n is_nsfw INTEGER DEFAULT 0,\n control_config_id INTEGER,\n sort_order INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, slot_clip_recording_enabled INTEGER DEFAULT 1, slot_clip_notify_enabled INTEGER DEFAULT 1, slot_powerchat_relay INTEGER DEFAULT 1, slot_powerchat_count_rs_views INTEGER DEFAULT 1, broadcast_settings TEXT DEFAULT '{}', streaming_method TEXT DEFAULT 'browser', browser_mode TEXT DEFAULT 'camera', pip_source_msid INTEGER, pip_defaults TEXT DEFAULT '{}', default_vod_visibility TEXT DEFAULT 'public', default_clip_visibility TEXT DEFAULT 'public', slot_vod_recording_enabled INTEGER DEFAULT 1, weather_zip TEXT DEFAULT NULL, weather_detail TEXT DEFAULT 'basic', weather_show_location INTEGER DEFAULT 0, mic_only_image TEXT DEFAULT NULL, ingest_authority TEXT NOT NULL DEFAULT 'live', openre_stream_id TEXT DEFAULT NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE SET NULL,\n FOREIGN KEY (control_config_id) REFERENCES control_configs(id) ON DELETE SET NULL\n)",++ "CREATE TABLE media_request_settings (\n user_id INTEGER PRIMARY KEY,\n enabled INTEGER DEFAULT 1,\n request_cost INTEGER DEFAULT 25,\n max_per_user INTEGER DEFAULT 3,\n max_duration_seconds INTEGER DEFAULT 600,\n allow_youtube INTEGER DEFAULT 1,\n allow_vimeo INTEGER DEFAULT 1,\n allow_direct_media INTEGER DEFAULT 1,\n auto_advance INTEGER DEFAULT 1,\n currency TEXT DEFAULT 'opencoins' CHECK(currency IN ('free','vibes','opencoins','points')),\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, cost_mode TEXT DEFAULT 'flat' CHECK(cost_mode IN ('flat','per_minute')), cost_per_minute INTEGER DEFAULT 5, allow_live INTEGER DEFAULT 0, download_mode TEXT DEFAULT 'stream' CHECK(download_mode IN ('stream','download')),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE media_requests (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n streamer_id INTEGER NOT NULL,\n stream_id INTEGER,\n user_id INTEGER NOT NULL,\n username TEXT NOT NULL,\n input TEXT NOT NULL,\n canonical_url TEXT NOT NULL,\n embed_url TEXT,\n provider TEXT NOT NULL CHECK(provider IN ('youtube', 'vimeo', 'audio', 'video')),\n title TEXT NOT NULL,\n thumbnail_url TEXT,\n duration_seconds INTEGER,\n cost INTEGER NOT NULL DEFAULT 25,\n currency TEXT DEFAULT 'opencoins',\n queue_position INTEGER DEFAULT 0,\n status TEXT DEFAULT 'pending' CHECK(status IN ('pending', 'playing', 'played', 'skipped', 'removed', 'failed')),\n requested_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n started_at DATETIME,\n ended_at DATETIME,\n last_error TEXT, stream_url TEXT, download_status TEXT DEFAULT 'none' CHECK(download_status IN ('none','extracting','downloading','ready','failed')), file_path TEXT, playback_position REAL DEFAULT 0, refunded INTEGER DEFAULT 0, charge_state TEXT,\n FOREIGN KEY (streamer_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE moderation_actions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n scope_type TEXT NOT NULL DEFAULT 'site',\n scope_id INTEGER,\n actor_user_id INTEGER,\n target_user_id INTEGER,\n action_type TEXT NOT NULL,\n details TEXT DEFAULT '{}',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (actor_user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (target_user_id) REFERENCES users(id) ON DELETE SET NULL\n )",++ "CREATE TABLE news_settings (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n scope TEXT NOT NULL DEFAULT 'global',\n scope_id INTEGER,\n source_id TEXT NOT NULL,\n enabled INTEGER NOT NULL DEFAULT 0,\n config TEXT DEFAULT '{}',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(scope, scope_id, source_id)\n )",++ "CREATE TABLE openre_sessions (\n session_id TEXT PRIMARY KEY,\n managed_stream_id INTEGER,\n stream_id INTEGER,\n state TEXT NOT NULL,\n revision INTEGER NOT NULL DEFAULT 0,\n started_at DATETIME,\n ended_at DATETIME,\n confirmed_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",++ "CREATE TABLE paste_comments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n paste_id INTEGER NOT NULL,\n user_id INTEGER,\n parent_id INTEGER,\n anon_name TEXT,\n message TEXT NOT NULL,\n ip_address TEXT,\n is_deleted INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (paste_id) REFERENCES pastes(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (parent_id) REFERENCES paste_comments(id) ON DELETE CASCADE\n )",++ "CREATE TABLE paste_likes (\n paste_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (paste_id, user_id),\n FOREIGN KEY (paste_id) REFERENCES pastes(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE pastes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n slug TEXT UNIQUE NOT NULL,\n user_id INTEGER,\n type TEXT DEFAULT 'paste' CHECK(type IN ('paste', 'screenshot')),\n title TEXT NOT NULL DEFAULT 'Untitled',\n content TEXT,\n language TEXT DEFAULT 'text',\n visibility TEXT DEFAULT 'public' CHECK(visibility IN ('public', 'unlisted')),\n stream_id INTEGER,\n screenshot_path TEXT,\n metadata TEXT,\n burn_after_read INTEGER DEFAULT 0,\n forked_from INTEGER,\n pinned INTEGER DEFAULT 0,\n views INTEGER DEFAULT 0,\n ip_address TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, copies INTEGER DEFAULT 0, likes INTEGER DEFAULT 0, is_nsfw INTEGER DEFAULT 0, ai_summary TEXT, ai_tags TEXT, ai_analyzed_at DATETIME,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (forked_from) REFERENCES pastes(id) ON DELETE SET NULL\n )",++ "CREATE TABLE payment_orders (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n provider TEXT NOT NULL,\n provider_ref TEXT,\n kind TEXT NOT NULL DEFAULT 'bucks', -- bucks | subscription\n amount_cents INTEGER NOT NULL DEFAULT 0,\n currency TEXT DEFAULT 'usd',\n bucks INTEGER DEFAULT 0,\n streamer_id INTEGER,\n status TEXT DEFAULT 'pending', -- pending | paid | failed | credited\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE pending_ip_messages (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_id INTEGER NOT NULL,\n stream_id INTEGER,\n ip_address TEXT NOT NULL,\n user_id INTEGER,\n anon_id TEXT,\n username TEXT,\n message TEXT NOT NULL,\n status TEXT DEFAULT 'pending' CHECK(status IN ('pending','approved','denied')),\n reviewed_by INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE,\n FOREIGN KEY (reviewed_by) REFERENCES users(id) ON DELETE SET NULL\n )",++ "CREATE TABLE platform_connections (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n platform TEXT NOT NULL CHECK(platform IN ('youtube', 'twitch', 'kick')),\n platform_user_id TEXT,\n platform_username TEXT,\n channel_url TEXT,\n access_token TEXT,\n refresh_token TEXT,\n token_expires_at INTEGER,\n scope TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(user_id, platform),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE powerchat_connections (\n user_id INTEGER PRIMARY KEY,\n powerchat_username TEXT,\n powerchat_user_id TEXT,\n access_token TEXT,\n refresh_token TEXT,\n token_expires_at INTEGER, -- epoch ms\n scope TEXT,\n tip_page_url TEXT,\n last_error TEXT,\n connected_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE powerchat_webhook_deliveries (\n delivery_id TEXT PRIMARY KEY,\n event_type TEXT,\n received_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",++ "CREATE TABLE relay_users (\n platform TEXT NOT NULL,\n username TEXT NOT NULL,\n display_name TEXT,\n first_seen DATETIME DEFAULT CURRENT_TIMESTAMP,\n last_seen DATETIME DEFAULT CURRENT_TIMESTAMP,\n message_count INTEGER DEFAULT 0,\n PRIMARY KEY (platform, username)\n )",++ "CREATE TABLE restream_destinations (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n platform TEXT NOT NULL CHECK(platform IN ('youtube', 'twitch', 'kick', 'custom')),\n name TEXT,\n server_url TEXT,\n stream_key TEXT,\n enabled INTEGER DEFAULT 1,\n auto_start INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, connection_id INTEGER DEFAULT NULL REFERENCES platform_connections(id) ON DELETE SET NULL, quality_preset TEXT DEFAULT 'auto', custom_video_bitrate INTEGER DEFAULT NULL, custom_audio_bitrate INTEGER DEFAULT NULL, custom_fps INTEGER DEFAULT NULL, custom_encoder_preset TEXT DEFAULT NULL, srt_latency_ms INTEGER DEFAULT NULL, srt_passphrase TEXT DEFAULT NULL, channel_url TEXT DEFAULT NULL, chat_relay INTEGER DEFAULT 0, powerchat_relay INTEGER DEFAULT 1, powerchat_count_views INTEGER DEFAULT 1, consecutive_failures INTEGER DEFAULT 0, cooldown_until DATETIME DEFAULT NULL, last_error TEXT DEFAULT NULL, last_failed_at DATETIME DEFAULT NULL, managed_stream_id INTEGER REFERENCES managed_streams(id) ON DELETE SET NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE \"robotstreamer_integrations\" (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n managed_stream_id INTEGER,\n enabled INTEGER DEFAULT 0,\n mirror_chat INTEGER DEFAULT 1,\n token TEXT,\n robot_id TEXT,\n owner_id TEXT,\n chat_url TEXT,\n control_url TEXT,\n rtc_sfu_url TEXT,\n stream_name TEXT,\n owner_name TEXT,\n last_validated_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (managed_stream_id) REFERENCES managed_streams(id) ON DELETE CASCADE\n )",++ "CREATE TABLE schema_migrations (\n id TEXT PRIMARY KEY,\n applied_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n mode TEXT NOT NULL CHECK (mode IN ('applied', 'adopted')),\n duration_ms INTEGER DEFAULT 0\n)",++ "CREATE TABLE site_settings (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL DEFAULT '',\n description TEXT DEFAULT '',\n type TEXT DEFAULT 'string' CHECK(type IN ('string', 'number', 'boolean', 'json')),\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n)",++ "CREATE TABLE stream_analytics (\n stream_id INTEGER PRIMARY KEY,\n avg_viewers REAL DEFAULT 0,\n peak_viewers INTEGER DEFAULT 0,\n unique_chatters INTEGER DEFAULT 0,\n total_messages INTEGER DEFAULT 0,\n total_watch_minutes INTEGER DEFAULT 0,\n new_followers INTEGER DEFAULT 0,\n clips_created INTEGER DEFAULT 0,\n coins_earned INTEGER DEFAULT 0,\n computed_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n)",++ "CREATE TABLE stream_controls (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n label TEXT NOT NULL,\n command TEXT NOT NULL, -- command string sent to hardware\n icon TEXT DEFAULT 'fa-gamepad', -- Font Awesome icon class\n control_type TEXT DEFAULT 'button' CHECK(control_type IN ('button', 'toggle', 'slider', 'dpad', 'onvif', 'keyboard')),\n key_binding TEXT, -- keyboard shortcut\n cooldown_ms INTEGER DEFAULT 100,\n is_enabled INTEGER DEFAULT 1,\n sort_order INTEGER DEFAULT 0,\n camera_id INTEGER, -- NULL for non-ONVIF controls; references camera_profiles(id)\n onvif_movement TEXT, -- 'pan_left', 'pan_right', 'tilt_up', 'tilt_down', 'zoom_in', 'zoom_out', 'preset'\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, btn_color TEXT DEFAULT '', btn_bg TEXT DEFAULT '', btn_border_color TEXT DEFAULT '',\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE,\n FOREIGN KEY (camera_id) REFERENCES camera_profiles(id) ON DELETE SET NULL\n)",++ "CREATE TABLE stream_first_chats (\n chatter_key TEXT NOT NULL,\n channel_user_id INTEGER NOT NULL,\n first_chat_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (chatter_key, channel_user_id)\n )",++ "CREATE TABLE stream_memories (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n user_id INTEGER,\n offset_seconds INTEGER DEFAULT 0, -- seconds into the stream when captured\n captured_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n description TEXT,\n tags TEXT, -- JSON array\n thumbnail_url TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, transcript_json TEXT,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n )",++ "CREATE TABLE stream_timeline_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n user_id INTEGER,\n vod_id INTEGER, -- set once the session becomes a VOD\n kind TEXT NOT NULL, -- 'speech' | 'sound'\n start_sec REAL NOT NULL, -- absolute seconds into the stream\n end_sec REAL,\n text TEXT, -- speech content\n label TEXT, -- sound event label\n confidence REAL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, lang TEXT, text_en TEXT,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n )",++ "CREATE TABLE streamer_overviews (\n user_id INTEGER PRIMARY KEY,\n overview TEXT,\n model TEXT,\n sources TEXT,\n generated_at DATETIME DEFAULT CURRENT_TIMESTAMP, overview_short TEXT,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE streams (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n channel_id INTEGER,\n managed_stream_id INTEGER,\n control_config_id INTEGER,\n title TEXT DEFAULT 'Untitled Stream',\n description TEXT DEFAULT '',\n category TEXT DEFAULT 'irl',\n tags TEXT DEFAULT '[]',\n protocol TEXT DEFAULT 'webrtc' CHECK(protocol IN ('jsmpeg', 'webrtc', 'rtmp')),\n is_live INTEGER DEFAULT 0,\n is_nsfw INTEGER DEFAULT 0,\n viewer_count INTEGER DEFAULT 0,\n peak_viewers INTEGER DEFAULT 0,\n follower_count INTEGER DEFAULT 0,\n thumbnail_url TEXT,\n multi_cam INTEGER DEFAULT 0,\n started_at DATETIME,\n ended_at DATETIME,\n last_heartbeat DATETIME,\n duration_seconds INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, ai_overview TEXT, ai_title TEXT, ai_category TEXT, ai_tags TEXT, ai_overview_short TEXT,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE SET NULL,\n FOREIGN KEY (managed_stream_id) REFERENCES managed_streams(id) ON DELETE SET NULL,\n FOREIGN KEY (control_config_id) REFERENCES control_configs(id) ON DELETE SET NULL\n)",++ "CREATE TABLE subscriptions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n subscriber_id INTEGER NOT NULL,\n streamer_id INTEGER NOT NULL,\n tier INTEGER DEFAULT 1 CHECK(tier IN (1, 2, 3)),\n is_active INTEGER DEFAULT 1,\n started_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n expires_at DATETIME, provider TEXT DEFAULT NULL, provider_ref TEXT DEFAULT NULL, price_cents INTEGER DEFAULT 0, currency TEXT DEFAULT 'usd', status TEXT DEFAULT 'active', cancel_at_period_end INTEGER DEFAULT 0, auto_renew INTEGER DEFAULT 0, current_period_end DATETIME DEFAULT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (subscriber_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (streamer_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE themes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n slug TEXT UNIQUE NOT NULL,\n author_id INTEGER,\n description TEXT DEFAULT '',\n mode TEXT DEFAULT 'dark' CHECK(mode IN ('dark', 'light')),\n variables TEXT NOT NULL DEFAULT '{}', -- JSON: CSS variable overrides\n preview_colors TEXT DEFAULT '{}', -- JSON: {bg, accent, text} for quick preview\n is_builtin INTEGER DEFAULT 0,\n is_public INTEGER DEFAULT 1,\n downloads INTEGER DEFAULT 0,\n rating_sum INTEGER DEFAULT 0,\n rating_count INTEGER DEFAULT 0,\n tags TEXT DEFAULT '[]', -- JSON array\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL\n)",++ "CREATE TABLE transactions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n from_user_id INTEGER,\n to_user_id INTEGER,\n stream_id INTEGER,\n amount INTEGER NOT NULL, -- in bits (cents)\n type TEXT NOT NULL CHECK(type IN ('donation', 'purchase', 'subscription', 'cashout', 'refund', 'bonus')),\n status TEXT DEFAULT 'completed' CHECK(status IN ('pending', 'completed', 'failed', 'escrow', 'refunded')),\n message TEXT,\n paypal_transaction_id TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (from_user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (to_user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL\n)",++ "CREATE TABLE translations (\n key TEXT PRIMARY KEY,\n src TEXT, dst TEXT,\n text TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",++ "CREATE TABLE tts_voice_overrides (\n identity_key TEXT PRIMARY KEY,\n voice TEXT,\n pitch INTEGER,\n speed INTEGER,\n gap INTEGER DEFAULT 0,\n set_by INTEGER,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",++ "CREATE TABLE user_cosmetics (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n item_id TEXT NOT NULL, -- references ITEMS key (fx_rainbow, px_sparkle, hat_crown, voice_robot etc.)\n category TEXT NOT NULL, -- 'name_effect'|'particle'|'hat'|'voice'\n unlocked_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(user_id, item_id),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE user_equipped (\n user_id INTEGER NOT NULL,\n slot TEXT NOT NULL, -- 'name_effect'|'particle'|'hat'|'voice'\n item_id TEXT NOT NULL,\n PRIMARY KEY (user_id, slot),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE user_equipped_tag (\n user_id INTEGER NOT NULL PRIMARY KEY,\n tag_id TEXT NOT NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE user_preferences (\n user_id INTEGER PRIMARY KEY,\n chat_settings TEXT DEFAULT '{}',\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE user_tags (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n tag_id TEXT NOT NULL,\n source TEXT DEFAULT 'shop',\n granted_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(user_id, tag_id),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",++ "CREATE TABLE user_themes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n theme_id INTEGER, -- NULL = custom\n custom_variables TEXT DEFAULT '{}', -- JSON: custom overrides\n is_custom INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (theme_id) REFERENCES themes(id) ON DELETE SET NULL\n)",++ "CREATE TABLE users (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n username TEXT UNIQUE NOT NULL,\n email TEXT UNIQUE,\n password_hash TEXT NOT NULL,\n display_name TEXT,\n avatar_url TEXT,\n bio TEXT DEFAULT '',\n role TEXT DEFAULT 'user' CHECK(role IN ('user', 'streamer', 'global_mod', 'admin')),\n stream_key TEXT UNIQUE,\n openvibe_bucks_balance REAL DEFAULT 0.00,\n openvibe_coins_balance INTEGER DEFAULT 0,\n is_banned INTEGER DEFAULT 0,\n ban_reason TEXT,\n profile_color TEXT DEFAULT '#8b5cf6',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n last_seen DATETIME DEFAULT CURRENT_TIMESTAMP\n, openvibe_bucks_cashout_balance REAL DEFAULT 0.00, token_valid_after TEXT DEFAULT NULL, max_managed_streams INTEGER DEFAULT 3, avatar_paste_id INTEGER DEFAULT NULL, is_owner INTEGER DEFAULT 0)",++ "CREATE TABLE verification_keys (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n key TEXT UNIQUE NOT NULL,\n target_username TEXT NOT NULL, -- the reserved username this key unlocks\n note TEXT DEFAULT '', -- admin notes (e.g. RS-Companion user_id)\n created_by INTEGER NOT NULL, -- admin who generated it\n used_by INTEGER, -- user who redeemed it (NULL until used)\n status TEXT DEFAULT 'active' CHECK(status IN ('active', 'used', 'revoked')),\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n used_at DATETIME,\n FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (used_by) REFERENCES users(id) ON DELETE SET NULL\n)",++ "CREATE TABLE vibe_coding_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n managed_stream_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n stream_id INTEGER,\n session_key TEXT,\n event_id TEXT NOT NULL,\n sequence_num INTEGER DEFAULT 0,\n event_type TEXT NOT NULL,\n visibility TEXT DEFAULT 'public' CHECK(visibility IN ('public', 'streamer')),\n depth TEXT DEFAULT 'standard',\n summary TEXT DEFAULT '',\n payload_json TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (managed_stream_id) REFERENCES managed_streams(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n UNIQUE (managed_stream_id, event_id)\n)",++ "CREATE TABLE vibe_coding_sessions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n managed_stream_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n session_key TEXT NOT NULL,\n slot_slug TEXT,\n workspace_name TEXT,\n machine_name TEXT,\n extension_version TEXT,\n publisher_id TEXT,\n publisher_label TEXT,\n publisher_vendor TEXT,\n publisher_client_type TEXT,\n publisher_client_name TEXT,\n publisher_client_version TEXT,\n publisher_capabilities_json TEXT,\n publisher_depth TEXT DEFAULT 'standard',\n status TEXT DEFAULT 'active' CHECK(status IN ('active', 'ended')),\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n last_event_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n ended_at DATETIME,\n FOREIGN KEY (managed_stream_id) REFERENCES managed_streams(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n UNIQUE (managed_stream_id, session_key)\n)",++ "CREATE TABLE viewer_samples (\n sampled_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n viewers INTEGER NOT NULL DEFAULT 0,\n live_streams INTEGER NOT NULL DEFAULT 0\n )",++ "CREATE TABLE viewer_snapshots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n viewer_count INTEGER DEFAULT 0,\n chat_messages_5m INTEGER DEFAULT 0, -- messages in trailing 5 min window\n recorded_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n)",++ "CREATE TABLE vod_ai_state (\n vod_id INTEGER PRIMARY KEY,\n ai_overview_short TEXT,\n ai_transcript_json TEXT,\n transcript_status TEXT,\n transcript_attempts INTEGER DEFAULT 0,\n transcript_error TEXT,\n transcript_next_at DATETIME\n , ai_overview TEXT, transcript_partial_json TEXT, transcript_progress_sec INTEGER DEFAULT 0)",++ "CREATE TABLE vods (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER,\n user_id INTEGER NOT NULL,\n title TEXT,\n description TEXT DEFAULT '',\n file_path TEXT NOT NULL,\n thumbnail_url TEXT,\n master_file_path TEXT,\n file_size INTEGER DEFAULT 0,\n duration_seconds INTEGER DEFAULT 0,\n probe_duration_seconds REAL DEFAULT 0,\n probe_format_json TEXT DEFAULT '',\n health_status TEXT DEFAULT 'unknown',\n health_score INTEGER DEFAULT 0,\n health_issues_json TEXT DEFAULT '[]',\n last_health_scan_at DATETIME,\n quarantined_at DATETIME,\n is_public INTEGER DEFAULT 1,\n is_recording INTEGER DEFAULT 0,\n view_count INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, clips_only INTEGER DEFAULT 0, storage_provider TEXT DEFAULT 'local', storage_key TEXT, ai_overview TEXT, ai_transcript TEXT, ai_analyzed_at DATETIME, visibility TEXT DEFAULT 'public',\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",++ "CREATE TABLE vpn_approvals (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n ip_address TEXT NOT NULL,\n user_id INTEGER,\n status TEXT DEFAULT 'pending' CHECK(status IN ('pending', 'approved', 'denied')),\n reviewed_by INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (reviewed_by) REFERENCES users(id) ON DELETE SET NULL\n)",++ "CREATE TABLE watch_time (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n stream_id INTEGER NOT NULL,\n minutes_watched INTEGER DEFAULT 0,\n last_heartbeat DATETIME DEFAULT CURRENT_TIMESTAMP,\n coins_earned INTEGER DEFAULT 0, -- coins earned this session\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(user_id, stream_id),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL\n)",++ "CREATE INDEX idx_ai_usage_created ON ai_usage(created_at)",++ "CREATE INDEX idx_ai_usage_owner_day ON ai_usage(owner_user_id, created_at)",++ "CREATE INDEX idx_ai_usage_source_day ON ai_usage(source, created_at)",++ "CREATE INDEX idx_ai_viewer_log_created ON ai_viewer_log(created_at)",++ "CREATE INDEX idx_aiv_log_channel ON ai_viewer_log(channel_user_id, id)",++ "CREATE INDEX idx_aiv_threads_open ON ai_viewer_threads(channel_user_id, state, updated_at)",++ "CREATE INDEX idx_anon_ip_created ON anon_ip_mappings(created_at)",++ "CREATE INDEX idx_approved_ips_channel ON approved_ips(channel_id)",++ "CREATE INDEX idx_approved_ips_ip ON approved_ips(ip_address)",++ "CREATE INDEX idx_arena_beefs_status ON arena_beefs (status, a_user_id, b_user_id)",++ "CREATE INDEX idx_arena_mic_created ON arena_mic_moments (created_at)",++ "CREATE INDEX idx_arena_mic_user ON arena_mic_moments (user_id, id)",++ "CREATE INDEX idx_arena_xp_log_user ON arena_xp_log (user_id, created_at)",++ "CREATE INDEX idx_bans_ip ON bans(ip_address)",++ "CREATE INDEX idx_bans_stream_id ON bans(stream_id)",++ "CREATE INDEX idx_bans_user ON bans(user_id)",++ "CREATE INDEX idx_cameras_stream_id ON cameras(stream_id)",++ "CREATE INDEX idx_channel_ai_bots_channel ON channel_ai_bots(channel_user_id, is_active)",++ "CREATE UNIQUE INDEX idx_channel_ai_bots_uname ON channel_ai_bots(channel_user_id, username)",++ "CREATE INDEX idx_channel_mods_channel ON channel_moderators(channel_id)",++ "CREATE INDEX idx_channel_mods_user ON channel_moderators(user_id)",++ "CREATE INDEX idx_channel_sounds_cmd ON channel_sounds(channel_owner_id, command)",++ "CREATE INDEX idx_channel_sounds_owner ON channel_sounds(channel_owner_id)",++ "CREATE INDEX idx_channels_user_id ON channels(user_id)",++ "CREATE INDEX idx_chat_ai_scope ON chat_ai_summaries(scope, subject_id, window)",++ "CREATE INDEX idx_chat_autodelete ON chat_messages(auto_delete_at)",++ "CREATE INDEX idx_chat_channel_user_ts ON chat_messages(channel_user_id, timestamp)",++ "CREATE INDEX idx_chat_stream_id ON chat_messages(stream_id)",++ "CREATE INDEX idx_chat_stream_ts ON chat_messages(stream_id, timestamp)",++ "CREATE INDEX idx_chat_timestamp ON chat_messages(timestamp)",++ "CREATE UNIQUE INDEX idx_chat_tl_dedup ON chat_timeline_events(scope, subject_id, ts, label)",++ "CREATE INDEX idx_chat_tl_scope_ts ON chat_timeline_events(scope, subject_id, ts DESC)",++ "CREATE INDEX idx_chat_ts_deleted ON chat_messages(timestamp, is_deleted)",++ "CREATE INDEX idx_chat_user_id ON chat_messages(user_id)",++ "CREATE UNIQUE INDEX idx_clip_ai_state_clip_id_unique ON clip_ai_state(clip_id)",++ "CREATE INDEX idx_clips_stream_id ON clips(stream_id)",++ "CREATE INDEX idx_clips_user_id ON clips(user_id)",++ "CREATE INDEX idx_coin_redemptions_stream ON coin_redemptions(stream_id)",++ "CREATE INDEX idx_coin_redemptions_user ON coin_redemptions(user_id)",++ "CREATE INDEX idx_coin_rewards_streamer ON coin_rewards(streamer_id)",++ "CREATE INDEX idx_coin_tx_created ON coin_transactions(created_at)",++ "CREATE INDEX idx_coin_tx_stream ON coin_transactions(stream_id)",++ "CREATE INDEX idx_coin_tx_user ON coin_transactions(user_id)",++ "CREATE INDEX idx_comments_content ON comments(content_type, content_id)",++ "CREATE INDEX idx_comments_parent ON comments(parent_id)",++ "CREATE INDEX idx_comments_user ON comments(user_id)",++ "CREATE INDEX idx_config_buttons_config ON control_config_buttons(config_id)",++ "CREATE INDEX idx_content_views_lookup ON content_views(content_type, content_id)",++ "CREATE INDEX idx_control_configs_user ON control_configs(user_id)",++ "CREATE INDEX idx_control_whitelist_channel ON control_whitelist(channel_id)",++ "CREATE INDEX idx_cp_log_user ON channel_points_log(user_id, streamer_id, id)",++ "CREATE INDEX idx_dm_blocks_blocked ON dm_blocks(blocked_id)",++ "CREATE INDEX idx_dm_blocks_blocker ON dm_blocks(blocker_id)",++ "CREATE INDEX idx_dm_messages_conv ON dm_messages(conversation_id, created_at)",++ "CREATE INDEX idx_dm_messages_sender ON dm_messages(sender_id)",++ "CREATE INDEX idx_dm_participants_conv ON dm_participants(conversation_id)",++ "CREATE INDEX idx_dm_participants_user ON dm_participants(user_id)",++ "CREATE INDEX idx_egg_solves_date ON easter_egg_solves(egg_date)",++ "CREATE UNIQUE INDEX idx_emotes_channel_code ON emotes(COALESCE(channel_owner_id, user_id), code)",++ "CREATE INDEX idx_emotes_channel_owner ON emotes(channel_owner_id)",++ "CREATE INDEX idx_emotes_code ON emotes(code)",++ "CREATE INDEX idx_emotes_global ON emotes(is_global)",++ "CREATE INDEX idx_emotes_user ON emotes(user_id)",++ "CREATE INDEX idx_follows_follower ON follows(follower_id)",++ "CREATE INDEX idx_follows_streamer ON follows(streamer_id)",++ "CREATE INDEX idx_goals_active ON donation_goals(is_active)",++ "CREATE INDEX idx_hidden_relay_channel ON hidden_relay_users(channel_id, platform)",++ "CREATE INDEX idx_ip_log_action ON ip_log(action)",++ "CREATE INDEX idx_ip_log_created ON ip_log(created_at)",++ "CREATE INDEX idx_ip_log_ip ON ip_log(ip_address)",++ "CREATE INDEX idx_ip_log_user ON ip_log(user_id)",++ "CREATE INDEX idx_linked_service ON linked_accounts(service, service_user_id)",++ "CREATE INDEX idx_linked_subject ON linked_accounts(subject_id)",++ "CREATE INDEX idx_linked_user ON linked_accounts(user_id)",++ "CREATE INDEX idx_managed_streams_key ON managed_streams(stream_key)",++ "CREATE INDEX idx_managed_streams_slug ON managed_streams(slug)",++ "CREATE INDEX idx_managed_streams_user ON managed_streams(user_id)",++ "CREATE INDEX idx_media_requests_canonical ON media_requests(streamer_id, canonical_url, status)",++ "CREATE INDEX idx_media_requests_streamer_status ON media_requests(streamer_id, status, queue_position, requested_at)",++ "CREATE INDEX idx_media_requests_user_status ON media_requests(user_id, status, requested_at)",++ "CREATE INDEX idx_mod_actions_actor ON moderation_actions(actor_user_id)",++ "CREATE INDEX idx_mod_actions_created ON moderation_actions(created_at DESC)",++ "CREATE INDEX idx_mod_actions_scope ON moderation_actions(scope_type, scope_id)",++ "CREATE INDEX idx_openre_sessions_state ON openre_sessions(state)",++ "CREATE INDEX idx_openre_sessions_stream ON openre_sessions(stream_id)",++ "CREATE INDEX idx_paste_comments_ip ON paste_comments(ip_address)",++ "CREATE INDEX idx_paste_comments_parent ON paste_comments(parent_id)",++ "CREATE INDEX idx_paste_comments_paste ON paste_comments(paste_id)",++ "CREATE INDEX idx_paste_comments_user ON paste_comments(user_id)",++ "CREATE INDEX idx_pastes_created ON pastes(created_at)",++ "CREATE INDEX idx_pastes_pinned ON pastes(pinned)",++ "CREATE INDEX idx_pastes_slug ON pastes(slug)",++ "CREATE INDEX idx_pastes_type ON pastes(type)",++ "CREATE INDEX idx_pastes_user ON pastes(user_id)",++ "CREATE INDEX idx_pastes_visibility ON pastes(visibility)",++ "CREATE INDEX idx_payment_orders_ref ON payment_orders(provider, provider_ref)",++ "CREATE INDEX idx_payment_orders_user ON payment_orders(user_id)",++ "CREATE INDEX idx_pending_ip_channel ON pending_ip_messages(channel_id, status)",++ "CREATE INDEX idx_powerchat_conn_pcuid ON powerchat_connections(powerchat_user_id)",++ "CREATE INDEX idx_powerchat_conn_username ON powerchat_connections(powerchat_username)",++ "CREATE INDEX idx_restream_dest_managed ON restream_destinations(managed_stream_id)",++ "CREATE INDEX idx_restream_dest_user ON restream_destinations(user_id)",++ "CREATE UNIQUE INDEX idx_rs_integrations_user_slot\n ON robotstreamer_integrations(user_id, IFNULL(managed_stream_id, 0))",++ "CREATE INDEX idx_sfc_channel ON stream_first_chats(channel_user_id)",++ "CREATE UNIQUE INDEX idx_stream_memories_moment_unique ON stream_memories(stream_id, offset_seconds)",++ "CREATE INDEX idx_stream_memories_stream ON stream_memories(stream_id, offset_seconds)",++ "CREATE INDEX idx_streams_channel_id ON streams(channel_id)",++ "CREATE INDEX idx_streams_created ON streams(created_at)",++ "CREATE INDEX idx_streams_is_live ON streams(is_live)",++ "CREATE INDEX idx_streams_live_ended ON streams(is_live, ended_at)",++ "CREATE INDEX idx_streams_managed_ended ON streams(managed_stream_id, ended_at)",++ "CREATE INDEX idx_streams_started ON streams(started_at)",++ "CREATE INDEX idx_streams_user_id ON streams(user_id)",++ "CREATE INDEX idx_subs_provider_ref ON subscriptions(provider, provider_ref)",++ "CREATE INDEX idx_subs_streamer ON subscriptions(streamer_id, status)",++ "CREATE INDEX idx_subs_subscriber ON subscriptions(subscriber_id, status)",++ "CREATE INDEX idx_themes_author ON themes(author_id)",++ "CREATE INDEX idx_themes_public ON themes(is_public)",++ "CREATE INDEX idx_themes_slug ON themes(slug)",++ "CREATE INDEX idx_timeline_kind ON stream_timeline_events(stream_id, kind, start_sec)",++ "CREATE INDEX idx_timeline_stream ON stream_timeline_events(stream_id, start_sec)",++ "CREATE INDEX idx_timeline_user_kind_created ON stream_timeline_events(user_id, kind, created_at)",++ "CREATE INDEX idx_timeline_vod ON stream_timeline_events(vod_id, start_sec)",++ "CREATE INDEX idx_transactions_to_user ON transactions(to_user_id)",++ "CREATE INDEX idx_tx_type_created ON transactions(type, created_at)",++ "CREATE INDEX idx_user_cosmetics_user ON user_cosmetics(user_id)",++ "CREATE INDEX idx_user_tags_user ON user_tags(user_id)",++ "CREATE INDEX idx_user_themes_user ON user_themes(user_id)",++ "CREATE INDEX idx_users_created ON users(created_at)",++ "CREATE INDEX idx_users_role ON users(role)",++ "CREATE INDEX idx_users_username_nocase ON users(username COLLATE NOCASE)",++ "CREATE INDEX idx_vibe_events_managed ON vibe_coding_events(managed_stream_id, id DESC)",++ "CREATE INDEX idx_vibe_events_stream ON vibe_coding_events(stream_id)",++ "CREATE INDEX idx_vibe_sessions_managed ON vibe_coding_sessions(managed_stream_id)",++ "CREATE INDEX idx_vibe_sessions_user ON vibe_coding_sessions(user_id)",++ "CREATE INDEX idx_viewer_samples_at ON viewer_samples(sampled_at)",++ "CREATE INDEX idx_viewer_snapshots_stream ON viewer_snapshots(stream_id, recorded_at)",++ "CREATE INDEX idx_vkeys_key ON verification_keys(key)",++ "CREATE INDEX idx_vkeys_status ON verification_keys(status)",++ "CREATE INDEX idx_vkeys_target ON verification_keys(target_username)",++ "CREATE UNIQUE INDEX idx_vod_ai_state_vod_id_unique ON vod_ai_state(vod_id)",++ "CREATE INDEX idx_vods_user_id ON vods(user_id)",++ "CREATE INDEX idx_watch_time_stream ON watch_time(stream_id)",++ "CREATE INDEX idx_watch_time_user ON watch_time(user_id)"++ ],++ "ledger": {++ "schema_migrations": [++ {++ "id": "001_vibes_decimal_to_bits",++ "applied_at": "2026-09-26 13:44:27",++ "mode": "adopted",++ "duration_ms": 0++ },++ {++ "id": "002_pastes_ai_columns",++ "applied_at": "2026-09-26 13:44:27",++ "mode": "applied",++ "duration_ms": 6++ },++ {++ "id": "004_hot_path_indexes",++ "applied_at": "2026-09-26 13:44:27",++ "mode": "applied",++ "duration_ms": 1++ }++ ]++ },++ "lazy": [++ "CREATE TABLE IF NOT EXISTS module_summary_pushes ( user_id INTEGER NOT NULL, namespace TEXT NOT NULL, hash TEXT NOT NULL, pushed_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_id, namespace) )",++ "CREATE TABLE IF NOT EXISTS token_revocations ( subject_id TEXT PRIMARY KEY, valid_after_ms INTEGER NOT NULL, reason TEXT, updated_at INTEGER NOT NULL )",++ "CREATE TABLE IF NOT EXISTS subject_projection ( subject_id TEXT PRIMARY KEY, network_user_id INTEGER, revision INTEGER NOT NULL, username TEXT NOT NULL, display_name TEXT, avatar_url TEXT, profile_color TEXT, role TEXT NOT NULL, banned INTEGER NOT NULL DEFAULT 0, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP )",++ "CREATE TABLE IF NOT EXISTS username_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, old_username TEXT NOT NULL, new_username TEXT NOT NULL, changed_at DATETIME DEFAULT CURRENT_TIMESTAMP )",++ "CREATE TABLE IF NOT EXISTS chat_bridge_outbox ( id INTEGER PRIMARY KEY AUTOINCREMENT, boot TEXT NOT NULL, ref INTEGER, op TEXT NOT NULL, args TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )",++ "CREATE TABLE IF NOT EXISTS chat_staged_outbox ( seq INTEGER PRIMARY KEY AUTOINCREMENT, tbl TEXT NOT NULL, op TEXT NOT NULL, pk TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )",++ "CREATE TABLE IF NOT EXISTS chat_dual_read_stats ( tbl TEXT PRIMARY KEY, compared INTEGER NOT NULL DEFAULT 0, matched INTEGER NOT NULL DEFAULT 0, mismatched INTEGER NOT NULL DEFAULT 0, inconclusive INTEGER NOT NULL DEFAULT 0, errors INTEGER NOT NULL DEFAULT 0, last_mismatch_at DATETIME, last_mismatch TEXT, since DATETIME DEFAULT CURRENT_TIMESTAMP )",++ "CREATE TABLE IF NOT EXISTS comment_thread_refs ( content_type TEXT NOT NULL, content_id INTEGER NOT NULL, thread_id INTEGER NOT NULL, access_id TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (content_type, content_id) )",++ "CREATE TABLE IF NOT EXISTS search_doc_pushes ( user_id INTEGER PRIMARY KEY, hash TEXT NOT NULL, revision INTEGER NOT NULL, deleted INTEGER NOT NULL DEFAULT 0, pushed_at DATETIME DEFAULT CURRENT_TIMESTAMP )",++ "CREATE TABLE IF NOT EXISTS search_media_pushes ( kind TEXT NOT NULL, media_id INTEGER NOT NULL, hash TEXT NOT NULL, revision INTEGER NOT NULL, deleted INTEGER NOT NULL DEFAULT 0, pushed_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (kind, media_id) )",++ "CREATE TABLE IF NOT EXISTS billing_actions ( id INTEGER PRIMARY KEY AUTOINCREMENT, action TEXT NOT NULL, idempotency_key TEXT NOT NULL UNIQUE, live_user_id INTEGER, live_ref TEXT, method TEXT NOT NULL, path TEXT NOT NULL, request_json TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', http_status INTEGER, billing_ref TEXT, response_json TEXT, error TEXT, attempts INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP )",++ "CREATE TABLE IF NOT EXISTS opencoin_admin_grants ( id INTEGER PRIMARY KEY AUTOINCREMENT, client_key TEXT UNIQUE, admin_id INTEGER, user_id INTEGER NOT NULL, amount INTEGER NOT NULL, reason TEXT, balance INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )",++ "CREATE TABLE IF NOT EXISTS stream_recaps ( stream_id INTEGER PRIMARY KEY, user_id INTEGER, json TEXT NOT NULL, ai INTEGER DEFAULT 0, announced_at DATETIME, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )",++ "CREATE TABLE IF NOT EXISTS tips_deliveries ( idempotency_key TEXT PRIMARY KEY, effect TEXT, state TEXT NOT NULL DEFAULT 'pending', response_json TEXT, claimed_at INTEGER NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )",++ "ALTER TABLE users ADD COLUMN theme_id INTEGER",++ "ALTER TABLE streams ADD COLUMN call_mode TEXT DEFAULT NULL"++ ],++ "statements": [++ "DELETE FROM ai_timeline_cache WHERE user_id = ?",++ "DELETE FROM ai_viewer_log WHERE created_at < datetime('now', ?)",++ "DELETE FROM app_state WHERE key = ?",++ "DELETE FROM approved_ips WHERE channel_id = ? AND ip_address = ?",++ "DELETE FROM bans WHERE id = ?",++ "DELETE FROM bans WHERE ip_address IS NOT NULL AND banned_by = ? AND stream_id IS NULL AND reason LIKE ?",++ "DELETE FROM bans WHERE user_id = ? AND stream_id = ?",++ "DELETE FROM bans WHERE user_id = ? AND stream_id IS NULL",++ "DELETE FROM camera_presets WHERE camera_id = ?",++ "DELETE FROM camera_presets WHERE id = ?",++ "DELETE FROM camera_profiles WHERE id = ?",++ "DELETE FROM channel_ai_bots WHERE id = ?",++ "DELETE FROM channel_moderators WHERE channel_id = ? AND user_id = ?",++ "DELETE FROM channel_sounds WHERE id = ?",++ "DELETE FROM chat_bridge_outbox",++ "DELETE FROM chat_bridge_outbox WHERE id = ?",++ "DELETE FROM chat_dual_read_stats WHERE tbl = ?",++ "DELETE FROM coin_rewards WHERE id = ?",++ "DELETE FROM control_config_buttons WHERE id = ?",++ "DELETE FROM control_config_buttons WHERE id = ? AND config_id = ?",++ "DELETE FROM control_configs WHERE id = ?",++ "DELETE FROM control_whitelist WHERE id = ? AND channel_id = ?",++ "DELETE FROM dm_blocks WHERE blocker_id = ? AND blocked_id = ?",++ "DELETE FROM dm_messages WHERE id = ?",++ "DELETE FROM dm_participants WHERE conversation_id = ? AND user_id = ?",++ "DELETE FROM donation_goals WHERE id = ? AND user_id = ?",++ "DELETE FROM emotes WHERE id = ?",++ "DELETE FROM follows WHERE follower_id = ? AND streamer_id = ?",++ "DELETE FROM hidden_relay_users WHERE id = ?",++ "DELETE FROM hidden_relay_users WHERE platform = ? AND external_username = ? AND channel_id IS ?",++ "DELETE FROM linked_accounts WHERE service = 'hobotools'",++ "DELETE FROM managed_streams WHERE id = ? AND user_id = ?",++ "DELETE FROM media_requests WHERE id = ? AND charge_state = 'charging'",++ "DELETE FROM platform_connections WHERE user_id = ? AND platform = ?",++ "DELETE FROM powerchat_connections WHERE user_id = ?",++ "DELETE FROM powerchat_webhook_deliveries WHERE received_at < datetime('now', ?)",++ "DELETE FROM restream_destinations WHERE id = ?",++ "DELETE FROM robotstreamer_integrations WHERE user_id = ? AND managed_stream_id = ?",++ "DELETE FROM site_settings WHERE key = 'ai_viewers_engine'",++ "DELETE FROM site_settings WHERE key = 'powerchat_site_user_id'",++ "DELETE FROM site_settings WHERE key = ?",++ "DELETE FROM site_settings WHERE key IN ('auto_clip_backfill','auto_clip_log','daily_easter_egg','home_hero_moments','home_hero_slogans') OR key LIKE 'ai_whole_overview_%'",++ "DELETE FROM stream_controls WHERE stream_id = ? AND (control_type != ? OR control_type IS NULL)",++ "DELETE FROM stream_memories WHERE id NOT IN ( SELECT id FROM ( SELECT id, ROW_NUMBER() OVER ( PARTITION BY stream_id, offset_seconds ORDER BY LENGTH(COALESCE(description,'')) DESC, (transcript_json IS NOT NULL) DESC, id DESC) AS rn FROM stream_memories) WHERE rn = 1)",++ "DELETE FROM themes WHERE id = ?",++ "DELETE FROM tips_deliveries WHERE created_at < datetime('now', ?)",++ "DELETE FROM tips_deliveries WHERE idempotency_key = ? AND state = 'pending'",++ "DELETE FROM tts_voice_overrides WHERE identity_key = ?",++ "DELETE FROM user_cosmetics WHERE user_id = ? AND item_id = ?",++ "DELETE FROM user_equipped WHERE user_id = ? AND slot = ?",++ "DELETE FROM user_equipped WHERE user_id = ? AND slot = ? AND item_id = ?",++ "DELETE FROM viewer_samples WHERE sampled_at < datetime('now', '-400 days')",++ "INSERT INTO ai_chatbot_configs (user_id, enabled, base_url, api_token, model, transcribe_enabled, transcribe_model, num_bots, post_interval_seconds, persona, vision_enabled) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO ai_timeline_cache (user_id, payload, generated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET payload = excluded.payload, generated_at = CURRENT_TIMESTAMP",++ "INSERT INTO ai_usage (kind, model, input_tokens, output_tokens, cached_tokens, cost_usd, owner_user_id, source, role, provider, latency_ms) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO ai_viewer_log (channel_user_id, stream_id, event, bot_username, target, thread_id, chat_message_id, text, reason, tokens_in, tokens_cached, tokens_out, cost_usd, model) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO ai_viewer_threads (channel_user_id, stream_id, kind, participants_json, topic, awaiting) VALUES (?, ?, ?, ?, ?, ?)",++ "INSERT INTO anon_ip_mappings (ip, anon_num, created_at) VALUES (?, ?, CURRENT_TIMESTAMP)",++ "INSERT INTO api_keys (user_id, key_hash, label, permissions) VALUES (?, ?, ?, ?)",++ "INSERT INTO api_tokens (user_id, token_hash, label, scopes, expires_at) VALUES (?, ?, ?, ?, ?)",++ "INSERT INTO app_state (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO arena_beefs (a_user_id, b_user_id, on_clock, clock_until, ends_at, feed_json, opener_line, rematch, bounty_topic_id, headline) VALUES (?, ?, 'b', ?, ?, '[]', ?, ?, NULL, ?)",++ "INSERT INTO arena_mic_moments (user_id, stream_id, vod_id, sec, kind, target_user_id, beef_id, aimed_at, text, about, quality, announcer, said_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, COALESCE(?, CURRENT_TIMESTAMP))",++ "INSERT INTO arena_profiles (user_id, image_error, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET image_error = excluded.image_error, updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO arena_profiles (user_id, image_path, image_prompt, image_model, image_generated_at, image_error, updated_at) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP, NULL, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET image_path = excluded.image_path, image_prompt = excluded.image_prompt, image_model = excluded.image_model, image_generated_at = CURRENT_TIMESTAMP, image_error = NULL, updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO arena_profiles (user_id, persona_json, persona_model, persona_generated_at, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET persona_json = excluded.persona_json, persona_model = excluded.persona_model, persona_generated_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO arena_profiles (user_id, quotes_json, quotes_generated_at, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET quotes_json = excluded.quotes_json, quotes_generated_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO arena_profiles (user_id, stats_json, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET stats_json = excluded.stats_json, updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO arena_trash_levels (user_id, xp, level) VALUES (?, ?, ?) ON CONFLICT(user_id) DO UPDATE SET xp = xp + excluded.xp, level = ?, updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO arena_xp_log (user_id, amount, reason, ref_id) VALUES (?, ?, ?, ?)",++ "INSERT INTO bans (ip_address, reason, banned_by, expires_at) VALUES (?, ?, ?, ?)",++ "INSERT INTO bans (stream_id, ip_address, anon_id, reason, banned_by) VALUES (?, ?, ?, ?, ?)",++ "INSERT INTO bans (stream_id, ip_address, anon_id, reason, banned_by, expires_at) VALUES (?, ?, ?, ?, ?, ?)",++ "INSERT INTO bans (stream_id, user_id, ip_address, reason, banned_by, expires_at) VALUES (?, ?, ?, ?, ?, ?)",++ "INSERT INTO bans (stream_id, user_id, reason, banned_by) VALUES (?, ?, ?, ?)",++ "INSERT INTO bans (stream_id, user_id, reason, banned_by, expires_at) VALUES (?, ?, ?, ?, ?)",++ "INSERT INTO bans (user_id, ip_address, reason, banned_by, expires_at) VALUES (?, ?, ?, ?, ?)",++ "INSERT INTO bans (user_id, reason, banned_by, expires_at) VALUES (?, ?, ?, ?)",++ "INSERT INTO camera_presets (camera_id, name, pan, tilt, zoom, preset_token) VALUES (?, ?, ?, ?, ?, ?)",++ "INSERT INTO camera_profiles (user_id, stream_id, name, onvif_url, username, password_hash, pan_speed, tilt_speed, zoom_speed) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO cameras (stream_id, camera_index, label, protocol) VALUES (?, 0, 'Main', 'webrtc')",++ "INSERT INTO cameras (stream_id, camera_index, label, protocol) VALUES (?, 0, 'Main', ?)",++ "INSERT INTO channel_ai_bots (channel_user_id, username, display_name, avatar_color, source, cloned_from_kind, cloned_from_ref, persona_json, brain_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO channel_ai_config (user_id, enabled, num_ambient_bots, pacing_seconds, persona, transcribe_enabled, vision_enabled, use_shared_key, daily_budget_cents, byo_key, byo_base_url, byo_model) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO channel_moderation_settings ( channel_id, slow_mode_seconds, followers_only, emote_only, allow_anonymous, links_allowed, gifs_enabled, account_age_gate_hours, caps_percentage_limit, aggressive_filter, max_message_length, slur_filter_enabled, slur_filter_use_builtin, slur_filter_terms, slur_filter_regexes, slur_filter_nudge_message, slur_filter_disabled_categories, ip_approval_mode, soundboard_enabled, soundboard_allow_pitch, soundboard_allow_speed, soundboard_banned_ids, viewer_auto_delete_enabled, viewer_delete_all_enabled, custom_emotes_enabled, custom_sounds_enabled, max_sound_seconds, uploads_mods_only, emote_scale, emote_size_min, emote_size_max, sounds_mods_only, mods_can_edit_about ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO channel_moderation_settings (channel_id) VALUES (?)",++ "INSERT INTO channel_points (user_id, streamer_id, balance, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id, streamer_id) DO UPDATE SET balance = balance + excluded.balance, updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO channel_points_log (idempotency_key, user_id, streamer_id, delta, reason) VALUES (?, ?, ?, ?, ?)",++ "INSERT INTO channel_sounds (channel_owner_id, command, url, mime, duration_seconds, created_by, created_by_name, emote_code) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO chat_ai_summaries (scope, subject_id, window, overview, memory_json, timeline_json, message_count, window_message_count, last_message_id, window_label, window_start, window_end, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(scope, subject_id, window) DO UPDATE SET overview = excluded.overview, memory_json = excluded.memory_json, timeline_json = excluded.timeline_json, message_count = excluded.message_count, window_message_count = excluded.window_message_count, last_message_id = excluded.last_message_id, window_label = excluded.window_label, window_start = excluded.window_start, window_end = excluded.window_end, updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO chat_bridge_outbox (boot, ref, op, args) VALUES (?, ?, ?, ?)",++ "INSERT INTO chat_messages (stream_id, channel_user_id, user_id, anon_id, username, message, message_type, is_global, reply_to_id, source_platform, auto_delete_at, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO coin_redemptions (reward_id, user_id, stream_id, user_input) VALUES (?, ?, ?, ?)",++ "INSERT INTO coin_rewards (streamer_id, title, description, cost, icon, color, cooldown_seconds, max_per_stream, requires_input, is_global, sort_order) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO coin_transactions (user_id, stream_id, amount, type, reward_id, message) VALUES (?, ?, ?, ?, ?, ?)",++ "INSERT INTO control_config_buttons (config_id, label, command, icon, control_type, key_binding, cooldown_ms, sort_order, btn_color, btn_bg, btn_border_color) VALUES (?, ?, ?, ?, ?, ?, ?, ?, '', '', '')",++ "INSERT INTO control_config_buttons (config_id, label, command, icon, control_type, key_binding, cooldown_ms, sort_order, btn_color, btn_bg, btn_border_color) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO control_configs (user_id, name, description) VALUES (?, ?, ?)",++ "INSERT INTO dm_conversations (name, is_group, created_by) VALUES (?, ?, ?)",++ "INSERT INTO dm_messages (conversation_id, sender_id, message) VALUES (?, ?, ?)",++ "INSERT INTO donation_goals (user_id, title, target_amount) VALUES (?, ?, ?)",++ "INSERT INTO donation_goals (user_id, title, target_amount, image_url, media_type, sort_order) VALUES (?, ?, ?, ?, ?, ?)",++ "INSERT INTO emotes (user_id, code, url, animated, width, height, is_global, channel_owner_id, size) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO ip_log (user_id, anon_id, ip_address, action, geo_country, geo_region, geo_city, geo_isp, geo_org, geo_ll, user_agent) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO kick_channel_cache (slug, chatroom_id, kick_channel_id, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(slug) DO UPDATE SET chatroom_id = excluded.chatroom_id, kick_channel_id = COALESCE(excluded.kick_channel_id, kick_channel_cache.kick_channel_id), updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO managed_streams (user_id, channel_id, slug, title, description, category, protocol, streaming_method, stream_key, is_nsfw, control_config_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO managed_streams (user_id, channel_id, title, category, protocol, stream_key, is_nsfw) VALUES (?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO media_request_settings ( user_id, enabled, request_cost, max_per_user, max_duration_seconds, allow_youtube, allow_vimeo, allow_direct_media, auto_advance, cost_mode, cost_per_minute, allow_live, download_mode, currency ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO media_requests ( streamer_id, stream_id, user_id, username, input, canonical_url, embed_url, provider, title, thumbnail_url, duration_seconds, cost, queue_position, currency, status, charge_state ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO moderation_actions (scope_type, scope_id, actor_user_id, target_user_id, action_type, details) VALUES (?, ?, ?, ?, ?, ?)",++ "INSERT INTO module_summary_pushes (user_id, namespace, hash, pushed_at) VALUES (?, ?, ?, ?) ON CONFLICT(user_id, namespace) DO UPDATE SET hash = excluded.hash, pushed_at = excluded.pushed_at",++ "INSERT INTO news_settings (scope, scope_id, source_id, enabled, config) VALUES ('global', NULL, ?, ?, ?) ON CONFLICT(scope, scope_id, source_id) DO UPDATE SET enabled = excluded.enabled, config = excluded.config, updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO news_settings (scope, scope_id, source_id, enabled, config) VALUES ('user', ?, '_master', ?, '{}') ON CONFLICT(scope, scope_id, source_id) DO UPDATE SET enabled = excluded.enabled, updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO opencoin_admin_grants (client_key, admin_id, user_id, amount, reason) VALUES (?, ?, ?, ?, ?)",++ "INSERT INTO openre_sessions (session_id, managed_stream_id, stream_id, state, revision, ended_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(session_id) DO UPDATE SET state = excluded.state, revision = excluded.revision, ended_at = excluded.ended_at, updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO openre_sessions (session_id, managed_stream_id, stream_id, state, revision, started_at, confirmed_at, updated_at) VALUES (?, ?, ?, 'live', ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON CONFLICT(session_id) DO UPDATE SET stream_id = excluded.stream_id, state = 'live', revision = excluded.revision, confirmed_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO payment_orders (user_id, provider, provider_ref, kind, amount_cents, currency, bucks, streamer_id, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO pending_ip_messages (channel_id, stream_id, ip_address, user_id, anon_id, username, message) VALUES (?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO platform_connections (user_id, platform, platform_user_id, platform_username, channel_url, access_token, refresh_token, token_expires_at, scope) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO relay_users (platform, username, display_name, first_seen, last_seen, message_count) VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1) ON CONFLICT(platform, username) DO UPDATE SET last_seen = CURRENT_TIMESTAMP, message_count = message_count + 1, display_name = excluded.display_name",++ "INSERT INTO restream_destinations (user_id, managed_stream_id, platform, name, server_url, stream_key, enabled, auto_start, quality_preset, custom_video_bitrate, custom_audio_bitrate, custom_fps, custom_encoder_preset, srt_latency_ms, srt_passphrase, channel_url, chat_relay, powerchat_relay, powerchat_count_views) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO schema_migrations (id, mode, duration_ms) VALUES (?, ?, ?)",++ "INSERT INTO search_doc_pushes (user_id, hash, revision, deleted, pushed_at) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET hash = excluded.hash, revision = excluded.revision, deleted = excluded.deleted, pushed_at = excluded.pushed_at",++ "INSERT INTO search_media_pushes (kind, media_id, hash, revision, deleted, pushed_at) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(kind, media_id) DO UPDATE SET hash = excluded.hash, revision = excluded.revision, deleted = excluded.deleted, pushed_at = excluded.pushed_at",++ "INSERT INTO site_settings (key, value) VALUES (?, ?)",++ "INSERT INTO stream_analytics (stream_id, avg_viewers, peak_viewers, unique_chatters, total_messages, total_watch_minutes, new_followers, clips_created, coins_earned, computed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(stream_id) DO UPDATE SET avg_viewers = excluded.avg_viewers, peak_viewers = excluded.peak_viewers, unique_chatters = excluded.unique_chatters, total_messages = excluded.total_messages, total_watch_minutes = excluded.total_watch_minutes, new_followers = excluded.new_followers, clips_created = excluded.clips_created, coins_earned = excluded.coins_earned, computed_at = CURRENT_TIMESTAMP",++ "INSERT INTO stream_controls (stream_id, label, command, icon, control_type, key_binding, cooldown_ms, sort_order, btn_color, btn_bg, btn_border_color) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO stream_recaps (stream_id, user_id, json, ai, created_at) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(stream_id) DO UPDATE SET json = excluded.json, ai = excluded.ai, created_at = CURRENT_TIMESTAMP",++ "INSERT INTO stream_timeline_events (stream_id, user_id, vod_id, kind, start_sec, end_sec, text, label, confidence, lang, text_en) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO streamer_overviews (user_id, overview, overview_short, model, sources, generated_at) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET overview = excluded.overview, overview_short = excluded.overview_short, model = excluded.model, sources = excluded.sources, generated_at = CURRENT_TIMESTAMP",++ "INSERT INTO streams (user_id, channel_id, managed_stream_id, control_config_id, title, description, category, protocol, is_nsfw, thumbnail_url, is_live, started_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, CURRENT_TIMESTAMP)",++ "INSERT INTO subject_projection (subject_id, network_user_id, revision, username, display_name, avatar_url, profile_color, role, banned, updated_at) VALUES (@subject, @nid, @rev, @username, @display_name, @avatar_url, @profile_color, @role, @banned, CURRENT_TIMESTAMP) ON CONFLICT(subject_id) DO UPDATE SET network_user_id = excluded.network_user_id, revision = excluded.revision, username = excluded.username, display_name = excluded.display_name, avatar_url = excluded.avatar_url, profile_color = excluded.profile_color, role = excluded.role, banned = excluded.banned, updated_at = excluded.updated_at",++ "INSERT INTO subscriptions (subscriber_id, streamer_id, tier, provider, provider_ref, price_cents, currency, status, is_active, current_period_end, auto_renew) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO themes (name, slug, author_id, description, mode, variables, preview_colors, is_builtin, is_public, tags) VALUES (?, ?, ?, ?, ?, ?, ?, 0, 1, ?)",++ "INSERT INTO themes (name, slug, mode, description, variables, preview_colors, is_builtin, is_public, tags) VALUES (?, ?, ?, ?, ?, ?, 1, 1, ?)",++ "INSERT INTO token_revocations (subject_id, valid_after_ms, reason, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT(subject_id) DO UPDATE SET valid_after_ms = excluded.valid_after_ms, reason = excluded.reason, updated_at = excluded.updated_at WHERE excluded.valid_after_ms > token_revocations.valid_after_ms",++ "INSERT INTO transactions (from_user_id, to_user_id, stream_id, amount, type, status, message) VALUES (?, ?, ?, ?, ?, ?, ?)",++ "INSERT INTO tts_voice_overrides (identity_key, voice, pitch, speed, gap, set_by, updated_at) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(identity_key) DO UPDATE SET voice=excluded.voice, pitch=excluded.pitch, speed=excluded.speed, gap=excluded.gap, set_by=excluded.set_by, updated_at=CURRENT_TIMESTAMP",++ "INSERT INTO user_preferences (user_id, chat_settings, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET chat_settings = excluded.chat_settings, updated_at = CURRENT_TIMESTAMP",++ "INSERT INTO user_themes (user_id, theme_id, custom_variables, is_custom) VALUES (?, ?, ?, ?)",++ "INSERT INTO username_history (user_id, old_username, new_username) VALUES (?, ?, ?)",++ "INSERT INTO users (username, email, password_hash, display_name, stream_key) VALUES (?, ?, ?, ?, ?)",++ "INSERT INTO verification_keys (key, target_username, note, created_by) VALUES (?, ?, ?, ?)",++ "INSERT INTO vibe_coding_events ( managed_stream_id, user_id, stream_id, session_key, event_id, sequence_num, event_type, visibility, depth, summary, payload_json ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(managed_stream_id, event_id) DO UPDATE SET stream_id = excluded.stream_id, sequence_num = excluded.sequence_num, event_type = excluded.event_type, visibility = excluded.visibility, depth = excluded.depth, summary = excluded.summary, payload_json = excluded.payload_json, created_at = CURRENT_TIMESTAMP",++ "INSERT INTO vibe_coding_sessions ( managed_stream_id, user_id, session_key, slot_slug, workspace_name, machine_name, extension_version, publisher_id, publisher_label, publisher_vendor, publisher_client_type, publisher_client_name, publisher_client_version, publisher_capabilities_json, publisher_depth, status, last_event_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', CURRENT_TIMESTAMP) ON CONFLICT(managed_stream_id, session_key) DO UPDATE SET workspace_name = excluded.workspace_name, machine_name = excluded.machine_name, extension_version = excluded.extension_version, publisher_id = excluded.publisher_id, publisher_label = excluded.publisher_label, publisher_vendor = excluded.publisher_vendor, publisher_client_type = excluded.publisher_client_type, publisher_client_name = excluded.publisher_client_name, publisher_client_version = excluded.publisher_client_version, publisher_capabilities_json = excluded.publisher_capabilities_json, publisher_depth = excluded.publisher_depth, slot_slug = excluded.slot_slug, status = 'active', ended_at = NULL, last_event_at = CURRENT_TIMESTAMP",++ "INSERT INTO viewer_samples (viewers, live_streams) VALUES (?, ?)",++ "INSERT INTO viewer_snapshots (stream_id, viewer_count, chat_messages_5m) VALUES (?, ?, ?)",++ "INSERT INTO watch_time (user_id, stream_id, minutes_watched) VALUES (?, ?, 1)",++ "INSERT OR IGNORE INTO app_state (key, value, updated_at) SELECT key, value, COALESCE(updated_at, CURRENT_TIMESTAMP) FROM site_settings WHERE key IN ('auto_clip_backfill','auto_clip_log','daily_easter_egg','home_hero_moments','home_hero_slogans') OR key LIKE 'ai_whole_overview_%'",++ "INSERT OR IGNORE INTO approved_ips (channel_id, ip_address, approved_by, source) VALUES (?, ?, ?, ?)",++ "INSERT OR IGNORE INTO arena_beef_hype (beef_id, side, voter_key) VALUES (?, ?, ?)",++ "INSERT OR IGNORE INTO billing_actions (action, idempotency_key, live_user_id, live_ref, method, path, request_json) VALUES (?, ?, ?, ?, ?, ?, ?)",++ "INSERT OR IGNORE INTO channel_ai_config (user_id, enabled, num_ambient_bots, pacing_seconds, persona, transcribe_enabled, vision_enabled, use_shared_key, daily_budget_cents, byo_key, byo_base_url, byo_model) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",++ "INSERT OR IGNORE INTO channel_moderators (channel_id, user_id, added_by) VALUES (?, ?, ?)",++ "INSERT OR IGNORE INTO channels (user_id, title, description, category, protocol) VALUES (?, ?, ?, ?, ?)",++ "INSERT OR IGNORE INTO chat_timeline_events (scope, subject_id, ts, label, detail) VALUES (?, ?, ?, ?, ?)",++ "INSERT OR IGNORE INTO clip_ai_state (clip_id) VALUES (?)",++ "INSERT OR IGNORE INTO comment_thread_refs (content_type, content_id, thread_id, access_id) VALUES (?, ?, ?, ?)",++ "INSERT OR IGNORE INTO content_views (content_type, content_id, ip) VALUES (?, ?, ?)",++ "INSERT OR IGNORE INTO control_whitelist (channel_id, user_id, added_by) VALUES (?, ?, ?)",++ "INSERT OR IGNORE INTO dm_blocks (blocker_id, blocked_id) VALUES (?, ?)",++ "INSERT OR IGNORE INTO dm_participants (conversation_id, user_id) VALUES (?, ?)",++ "INSERT OR IGNORE INTO easter_egg_solves (egg_date, solver_key, user_id) VALUES (?, ?, ?)",++ "INSERT OR IGNORE INTO follows (follower_id, streamer_id) VALUES (?, ?)",++ "INSERT OR IGNORE INTO linked_accounts (service, service_user_id, service_username, user_id) VALUES ('network', ?, ?, ?)",++ "INSERT OR IGNORE INTO linked_accounts (user_id, service, service_user_id, service_username) VALUES (?, 'network', ?, ?)",++ "INSERT OR IGNORE INTO powerchat_webhook_deliveries (delivery_id, event_type) VALUES (?, ?)",++ "INSERT OR IGNORE INTO relay_users (platform, username, display_name, first_seen, last_seen, message_count) SELECT source_platform, LOWER(TRIM(SUBSTR(username, INSTR(username, '] ') + 2))), TRIM(SUBSTR(username, INSTR(username, '] ') + 2)), MIN(timestamp), MAX(timestamp), COUNT(*) FROM chat_messages WHERE user_id IS NULL AND source_platform IS NOT NULL AND username LIKE '[%] %' AND INSTR(username, '] ') > 0 GROUP BY source_platform, LOWER(TRIM(SUBSTR(username, INSTR(username, '] ') + 2)))",++ "INSERT OR IGNORE INTO site_settings (key, value, description, type) VALUES (?, ?, ?, ?)",++ "INSERT OR IGNORE INTO stream_first_chats (chatter_key, channel_user_id) VALUES (?, ?)",++ "INSERT OR IGNORE INTO stream_memories (stream_id, user_id, offset_seconds, description, tags, thumbnail_url, transcript_json) VALUES (?, ?, ?, ?, ?, ?, ?)",++ "INSERT OR IGNORE INTO tips_deliveries (idempotency_key, effect, state, claimed_at) VALUES (?, ?, 'pending', ?)",++ "INSERT OR IGNORE INTO user_cosmetics (user_id, item_id, category) VALUES (?, ?, ?)",++ "INSERT OR IGNORE INTO users (username, password_hash, display_name, role) VALUES (?, ?, ?, 'user')",++ "INSERT OR IGNORE INTO vod_ai_state (vod_id) VALUES (?)",++ "INSERT OR REPLACE INTO hidden_relay_users (channel_id, platform, external_username, action, reason, created_by) VALUES (?, ?, ?, ?, ?, ?)",++ "INSERT OR REPLACE INTO site_settings (key, value, description, type) VALUES ('bucks_bits_migration_done', '1', 'Internal: decimal→bit Vibes migration applied (see schema_migrations)', 'boolean')",++ "INSERT OR REPLACE INTO site_settings (key, value, type) VALUES ('relay_users_backfilled','1','string')",++ "INSERT OR REPLACE INTO translations (key, src, dst, text) VALUES (?, ?, ?, ?)",++ "INSERT OR REPLACE INTO user_equipped (user_id, slot, item_id) VALUES (?, ?, ?)",++ "SELECT * FROM ai_chatbot_configs",++ "SELECT * FROM ai_chatbot_configs WHERE user_id = ?",++ "SELECT * FROM ai_viewer_log WHERE channel_user_id = ?",++ "SELECT * FROM ai_viewer_log WHERE channel_user_id = ? ORDER BY id DESC LIMIT ?",++ "SELECT * FROM ai_viewer_threads WHERE channel_user_id = ? AND state = 'closed' AND topic IS NOT NULL ORDER BY updated_at DESC LIMIT ?",++ "SELECT * FROM ai_viewer_threads WHERE channel_user_id = ? AND state = 'open' ORDER BY updated_at DESC LIMIT ?",++ "SELECT * FROM ai_viewer_threads WHERE id = ?",++ "SELECT * FROM api_keys WHERE key_hash = ? AND is_active = 1",++ "SELECT * FROM arena_beefs WHERE a_user_id = ? OR b_user_id = ? ORDER BY CASE WHEN status = 'open' THEN 0 ELSE 1 END, COALESCE(resolved_at, opened_at) DESC LIMIT ?",++ "SELECT * FROM arena_beefs WHERE id = ?",++ "SELECT * FROM arena_beefs WHERE id = ? AND status = ?",++ "SELECT * FROM arena_beefs WHERE status = 'open'",++ "SELECT * FROM arena_beefs WHERE status = 'open' AND ((a_user_id = ? AND b_user_id = ?) OR (a_user_id = ? AND b_user_id = ?))",++ "SELECT * FROM arena_beefs WHERE status = 'open' AND (a_user_id = ? OR b_user_id = ?) ORDER BY opened_at DESC",++ "SELECT * FROM arena_beefs WHERE status = 'open' ORDER BY COALESCE(last_b_at, last_a_at, opened_at) DESC",++ "SELECT * FROM arena_beefs WHERE status = 'resolved' AND ((a_user_id = ? AND b_user_id = ?) OR (a_user_id = ? AND b_user_id = ?)) ORDER BY resolved_at DESC LIMIT 10",++ "SELECT * FROM arena_beefs WHERE status = 'resolved' ORDER BY resolved_at DESC LIMIT ?",++ "SELECT * FROM arena_mic_moments WHERE id = ?",++ "SELECT * FROM arena_mic_moments WHERE quality >= 5 AND user_id = ? ORDER BY COALESCE(said_at, created_at) DESC, id DESC LIMIT ?",++ "SELECT * FROM arena_mic_moments WHERE quality >= 5 ORDER BY COALESCE(said_at, created_at) DESC, id DESC LIMIT ?",++ "SELECT * FROM arena_mic_moments WHERE user_id = ? AND quality >= 5 ORDER BY quality DESC, id DESC LIMIT ?",++ "SELECT * FROM arena_mic_moments WHERE user_id = ? ORDER BY COALESCE(said_at, created_at) DESC, id DESC LIMIT 1",++ "SELECT * FROM arena_profiles WHERE user_id = ?",++ "SELECT * FROM arena_trash_levels WHERE user_id = ?",++ "SELECT * FROM arena_trash_levels WHERE xp > 0 ORDER BY xp DESC LIMIT ?",++ "SELECT * FROM bans WHERE id = ?",++ "SELECT * FROM bans WHERE ip_address IN (?, ?) AND (stream_id = ? OR stream_id IS NULL) AND (expires_at IS NULL OR datetime(expires_at) > CURRENT_TIMESTAMP) LIMIT 1",++ "SELECT * FROM bans WHERE ip_address LIKE '%/%' AND (expires_at IS NULL OR datetime(expires_at) > CURRENT_TIMESTAMP)",++ "SELECT * FROM bans WHERE user_id = ? AND (stream_id = ? OR stream_id IS NULL) AND (expires_at IS NULL OR datetime(expires_at) > CURRENT_TIMESTAMP) LIMIT 1",++ "SELECT * FROM billing_actions WHERE action = 'media_charge' AND live_ref = ? AND status = 'done' ORDER BY id DESC LIMIT 1",++ "SELECT * FROM billing_actions WHERE action IN ('checkout', 'subscribe_intent') AND status = 'done' AND json_extract(response_json, '$.intent.provider') = 'paypal' AND json_extract(response_json, '$.intent.provider_ref') = ? ORDER BY id DESC LIMIT 1",++ "SELECT * FROM billing_actions WHERE id = ?",++ "SELECT * FROM billing_actions WHERE idempotency_key = ?",++ "SELECT * FROM camera_presets WHERE camera_id = ? ORDER BY name",++ "SELECT * FROM camera_presets WHERE id = ?",++ "SELECT * FROM camera_profiles WHERE id = ?",++ "SELECT * FROM camera_profiles WHERE stream_id = ? AND is_active = 1 ORDER BY name",++ "SELECT * FROM camera_profiles WHERE user_id = ? ORDER BY created_at DESC",++ "SELECT * FROM cameras WHERE stream_id = ?",++ "SELECT * FROM channel_ai_bots WHERE channel_user_id = ?",++ "SELECT * FROM channel_ai_bots WHERE channel_user_id = ? AND username = ?",++ "SELECT * FROM channel_ai_bots WHERE channel_user_id = ? ORDER BY last_active_at DESC, created_at ASC",++ "SELECT * FROM channel_ai_bots WHERE id = ?",++ "SELECT * FROM channel_ai_config WHERE user_id = ?",++ "SELECT * FROM channel_moderation_settings WHERE channel_id = ?",++ "SELECT * FROM channel_sounds WHERE channel_owner_id = ? AND command = ? AND is_approved = 1 ORDER BY RANDOM() LIMIT 1",++ "SELECT * FROM channel_sounds WHERE channel_owner_id = ? AND is_approved = 1 ORDER BY command",++ "SELECT * FROM channel_sounds WHERE id = ?",++ "SELECT * FROM channels WHERE id = ?",++ "SELECT * FROM channels WHERE user_id = ?",++ "SELECT * FROM channels WHERE user_id IN (?)",++ "SELECT * FROM chat_ai_summaries WHERE scope = ? AND subject_id = ? AND window = ?",++ "SELECT * FROM chat_ai_summaries WHERE scope = ? AND subject_id = ? ORDER BY window",++ "SELECT * FROM chat_messages WHERE id = ?",++ "SELECT * FROM clip_ai_state WHERE clip_id = ?",++ "SELECT * FROM coin_rewards WHERE id = ?",++ "SELECT * FROM coin_rewards WHERE is_global = 1 AND is_enabled = 1 ORDER BY sort_order, cost",++ "SELECT * FROM coin_rewards WHERE streamer_id = ? AND is_enabled = 1 ORDER BY sort_order, cost",++ "SELECT * FROM coin_transactions WHERE user_id = ? ORDER BY created_at DESC LIMIT ?",++ "SELECT * FROM control_config_buttons WHERE config_id = ? ORDER BY sort_order",++ "SELECT * FROM control_configs WHERE id = ?",++ "SELECT * FROM control_configs WHERE user_id = ?",++ "SELECT * FROM control_configs WHERE user_id = ? ORDER BY created_at DESC",++ "SELECT * FROM dm_conversations WHERE id = ?",++ "SELECT * FROM dm_messages WHERE id = ?",++ "SELECT * FROM donation_goals WHERE id = ?",++ "SELECT * FROM donation_goals WHERE user_id = ? AND (is_active = 1 OR (reached_at IS NOT NULL AND reached_at > datetime('now', ?))) ORDER BY sort_order ASC, created_at ASC",++ "SELECT * FROM donation_goals WHERE user_id = ? AND is_active = 1 ORDER BY created_at",++ "SELECT * FROM donation_goals WHERE user_id = ? AND is_active = 1 ORDER BY sort_order ASC, created_at ASC",++ "SELECT * FROM donation_goals WHERE user_id = ? ORDER BY is_active DESC, sort_order ASC, created_at ASC",++ "SELECT * FROM emotes WHERE code = ? AND ((channel_owner_id = ?) OR (channel_owner_id IS NULL AND user_id = ?)) LIMIT 1",++ "SELECT * FROM emotes WHERE code = ? AND (user_id = ? OR is_global = 1) AND is_approved = 1 ORDER BY is_global ASC LIMIT 1",++ "SELECT * FROM emotes WHERE user_id = ? ORDER BY code",++ "SELECT * FROM kick_channel_cache WHERE slug = ?",++ "SELECT * FROM linked_accounts WHERE service = 'network' AND service_user_id = ?",++ "SELECT * FROM media_request_settings WHERE user_id = ?",++ "SELECT * FROM media_requests WHERE charge_state IN ('unknown', 'charging') AND currency = 'opencoins' AND requested_at <= ? ORDER BY id LIMIT ?",++ "SELECT * FROM media_requests WHERE id = ?",++ "SELECT * FROM media_requests WHERE streamer_id = ? AND canonical_url = ? AND status IN ('pending', 'playing') ORDER BY id DESC LIMIT 1",++ "SELECT * FROM media_requests WHERE streamer_id = ? AND id = ?",++ "SELECT * FROM media_requests WHERE streamer_id = ? AND status = 'pending' ORDER BY queue_position ASC, requested_at ASC, id ASC LIMIT 1",++ "SELECT * FROM media_requests WHERE streamer_id = ? AND status = 'pending' ORDER BY queue_position ASC, requested_at ASC, id ASC LIMIT ?",++ "SELECT * FROM media_requests WHERE streamer_id = ? AND status = 'playing' ORDER BY started_at DESC, id DESC LIMIT 1",++ "SELECT * FROM media_requests WHERE streamer_id = ? AND status IN ('played', 'skipped', 'removed', 'failed') AND charge_state IS NOT 'charging' ORDER BY COALESCE(ended_at, requested_at) DESC, id DESC LIMIT ?",++ "SELECT * FROM news_settings WHERE scope = 'global' AND source_id != '_master'",++ "SELECT * FROM opencoin_admin_grants WHERE client_key = ?",++ "SELECT * FROM openre_sessions WHERE session_id = ?",++ "SELECT * FROM openre_sessions WHERE state = 'live'",++ "SELECT * FROM openre_sessions WHERE stream_id = ? ORDER BY updated_at DESC LIMIT 1",++ "SELECT * FROM payment_orders WHERE id = ?",++ "SELECT * FROM payment_orders WHERE provider = 'powerchat' AND status = 'pending' AND created_at >= datetime('now', ?) ORDER BY id ASC",++ "SELECT * FROM payment_orders WHERE provider = ? AND provider_ref = ? ORDER BY id DESC LIMIT 1",++ "SELECT * FROM pending_ip_messages WHERE channel_id = ? AND ip_address = ? AND status = 'pending'",++ "SELECT * FROM pending_ip_messages WHERE channel_id = ? AND status = 'pending' ORDER BY created_at ASC LIMIT ?",++ "SELECT * FROM pending_ip_messages WHERE id = ?",++ "SELECT * FROM platform_connections WHERE id = ?",++ "SELECT * FROM platform_connections WHERE user_id = ? AND platform = ?",++ "SELECT * FROM platform_connections WHERE user_id = ? ORDER BY platform",++ "SELECT * FROM powerchat_connections WHERE LOWER(powerchat_username) = LOWER(?)",++ "SELECT * FROM powerchat_connections WHERE powerchat_user_id = ?",++ "SELECT * FROM powerchat_connections WHERE user_id = ?",++ "SELECT * FROM restream_destinations WHERE id = ?",++ "SELECT * FROM restream_destinations WHERE managed_stream_id = ? ORDER BY created_at",++ "SELECT * FROM restream_destinations WHERE user_id = ? AND managed_stream_id = ? ORDER BY created_at",++ "SELECT * FROM restream_destinations WHERE user_id = ? AND managed_stream_id IS NULL ORDER BY created_at",++ "SELECT * FROM restream_destinations WHERE user_id = ? ORDER BY created_at",++ "SELECT * FROM robotstreamer_integrations WHERE user_id = ? AND managed_stream_id = ?",++ "SELECT * FROM robotstreamer_integrations WHERE user_id = ? AND managed_stream_id IS NULL",++ "SELECT * FROM site_settings ORDER BY key",++ "SELECT * FROM site_settings WHERE key = ?",++ "SELECT * FROM stream_analytics WHERE stream_id = ?",++ "SELECT * FROM stream_controls WHERE id = ? AND stream_id = ?",++ "SELECT * FROM stream_controls WHERE id = ? AND stream_id = ? AND is_enabled = 1",++ "SELECT * FROM stream_controls WHERE stream_id = ? AND command = ? AND is_enabled = 1 LIMIT 1",++ "SELECT * FROM stream_controls WHERE stream_id = ? ORDER BY sort_order",++ "SELECT * FROM stream_memories WHERE stream_id = ? AND offset_seconds BETWEEN ? AND ? ORDER BY offset_seconds ASC",++ "SELECT * FROM stream_memories WHERE stream_id = ? ORDER BY offset_seconds ASC",++ "SELECT * FROM stream_memories WHERE stream_id = ? ORDER BY offset_seconds DESC LIMIT 1",++ "SELECT * FROM stream_memories WHERE user_id = ? ORDER BY created_at DESC LIMIT ?",++ "SELECT * FROM streamer_overviews WHERE user_id = ?",++ "SELECT * FROM streams WHERE id = ?",++ "SELECT * FROM streams WHERE user_id = ? AND is_live = 1 ORDER BY started_at DESC LIMIT 1",++ "SELECT * FROM subject_projection WHERE subject_id = ?",++ "SELECT * FROM subscriptions WHERE id = ?",++ "SELECT * FROM subscriptions WHERE provider = ? AND provider_ref = ? ORDER BY id DESC LIMIT 1",++ "SELECT * FROM subscriptions WHERE status = 'active' AND current_period_end IS NOT NULL AND datetime(current_period_end) <= CURRENT_TIMESTAMP ORDER BY datetime(current_period_end) ASC LIMIT ?",++ "SELECT * FROM subscriptions WHERE subscriber_id = ? AND streamer_id = ?",++ "SELECT * FROM subscriptions WHERE subscriber_id = ? AND streamer_id = ? AND status = 'active' AND (current_period_end IS NULL OR datetime(current_period_end) > CURRENT_TIMESTAMP) LIMIT 1",++ "SELECT * FROM themes WHERE id = ?",++ "SELECT * FROM tips_deliveries WHERE idempotency_key = ?",++ "SELECT * FROM transactions WHERE from_user_id = ? OR to_user_id = ? ORDER BY created_at DESC LIMIT ?",++ "SELECT * FROM transactions WHERE id = ? AND status = ?",++ "SELECT * FROM user_themes WHERE user_id = ? ORDER BY id DESC LIMIT 1",++ "SELECT * FROM users WHERE display_name = ? COLLATE NOCASE",++ "SELECT * FROM users WHERE id = ?",++ "SELECT * FROM users WHERE stream_key = ?",++ "SELECT * FROM users WHERE username = ? COLLATE NOCASE",++ "SELECT * FROM verification_keys WHERE key = ?",++ "SELECT * FROM verification_keys WHERE target_username = ? COLLATE NOCASE AND status = 'active'",++ "SELECT * FROM vod_ai_state WHERE vod_id = ?",++ "SELECT * FROM watch_time WHERE user_id = ? AND stream_id = ?",++ "SELECT 1 AS ok",++ "SELECT 1 AS x FROM stream_timeline_events WHERE vod_id IS NULL LIMIT 1",++ "SELECT 1 AS x FROM users WHERE username = ? COLLATE NOCASE",++ "SELECT 1 FROM ai_chatbot_configs WHERE user_id = ?",++ "SELECT 1 FROM approved_ips WHERE channel_id = ? AND ip_address = ?",++ "SELECT 1 FROM channel_ai_config WHERE user_id = ?",++ "SELECT 1 FROM channel_moderation_settings WHERE channel_id = ?",++ "SELECT 1 FROM channel_moderation_settings WHERE channel_id = ? LIMIT 1",++ "SELECT 1 FROM channel_moderators WHERE user_id = ? AND channel_id = ?",++ "SELECT 1 FROM chat_messages cm JOIN streams s ON cm.stream_id = s.id WHERE s.channel_id = ? AND cm.is_deleted = 0 AND (cm.user_id = ? OR cm.anon_id = ?) LIMIT 1",++ "SELECT 1 FROM control_whitelist WHERE channel_id = ? AND user_id = ?",++ "SELECT 1 FROM dm_blocks WHERE (blocker_id = ? AND blocked_id = ?) OR (blocker_id = ? AND blocked_id = ?)",++ "SELECT 1 FROM dm_blocks WHERE blocker_id = ? AND blocked_id = ?",++ "SELECT 1 FROM dm_messages WHERE conversation_id = ? AND sender_id = ? AND message = ? AND created_at > datetime('now', '-' || ? || ' seconds') LIMIT 1",++ "SELECT 1 FROM dm_participants WHERE conversation_id = ? AND user_id = ?",++ "SELECT 1 FROM easter_egg_solves WHERE egg_date = ? AND solver_key = ?",++ "SELECT 1 FROM hidden_relay_users WHERE platform = ? AND external_username = ? AND (channel_id = ? OR channel_id IS NULL)",++ "SELECT 1 FROM managed_streams WHERE user_id = ? AND ingest_authority = 'openre' LIMIT 1",++ "SELECT 1 FROM media_request_settings WHERE user_id = ? LIMIT 1",++ "SELECT 1 FROM module_summary_pushes WHERE user_id = ? AND namespace = 'live.loyalty'",++ "SELECT 1 FROM powerchat_connections WHERE user_id = ? LIMIT 1",++ "SELECT 1 FROM site_settings WHERE key = 'bucks_bits_migration_done'",++ "SELECT 1 FROM sqlite_master WHERE type = 'index' AND name = ?",++ "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'chat_bridge_outbox'",++ "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?",++ "SELECT 1 FROM sqlite_master WHERE type='index' AND name=?",++ "SELECT 1 FROM stream_first_chats WHERE chatter_key = ? AND channel_user_id = ?",++ "SELECT 1 FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' AND created_at >= datetime('now', '-30 minutes') LIMIT 1",++ "SELECT 1 FROM streams WHERE user_id = ? AND is_live = 1 LIMIT 1",++ "SELECT 1 FROM streams WHERE user_id = ? LIMIT 1",++ "SELECT 1 FROM user_cosmetics WHERE user_id = ? AND item_id = ?",++ "SELECT AVG(CASE WHEN sampled_at >= datetime('now','-1 day') AND viewers > 0 THEN viewers END) AS vAvg24, MAX(CASE WHEN sampled_at >= datetime('now','-1 day') THEN viewers END) AS vPeak24, AVG(CASE WHEN viewers > 0 THEN viewers END) AS vAvg7, AVG(CASE WHEN sampled_at >= datetime('now','-1 day') AND live_streams > 0 THEN live_streams END) AS lAvg24, MAX(CASE WHEN sampled_at >= datetime('now','-1 day') THEN live_streams END) AS lPeak24, AVG(CASE WHEN live_streams > 0 THEN live_streams END) AS lAvg7, COUNT(*) AS samples FROM viewer_samples WHERE sampled_at >= datetime('now','-7 days')",++ "SELECT AVG(viewer_count) as avg_vc FROM viewer_snapshots WHERE stream_id = ?",++ "SELECT CASE WHEN a_user_id = ? THEN b_user_id ELSE a_user_id END AS opp, COUNT(*) AS n FROM arena_beefs WHERE (a_user_id = ? OR b_user_id = ?) GROUP BY opp HAVING n >= 2 ORDER BY n DESC LIMIT ?",++ "SELECT CAST((julianday(cm.timestamp) - julianday(s.started_at)) * 86400 / ? AS INT) AS bucket, COUNT(*) AS n FROM chat_messages cm JOIN streams s ON s.id = cm.stream_id WHERE cm.stream_id = ? AND s.started_at IS NOT NULL AND cm.timestamp >= s.started_at AND COALESCE(cm.is_deleted, 0) = 0 GROUP BY bucket HAVING bucket >= 0 ORDER BY n DESC LIMIT ?",++ "SELECT CAST(strftime('%s', cm.timestamp) / ? AS INT) AS b, COUNT(*) AS n, MIN(CAST(strftime('%s', cm.timestamp) AS INT)) AS ts FROM chat_messages cm WHERE cm.stream_id = ? AND COALESCE(cm.is_deleted, 0) = 0 AND cm.timestamp >= datetime('now', ?) GROUP BY b ORDER BY ts ASC",++ "SELECT COALESCE(-SUM(amount), 0) AS count FROM coin_transactions WHERE amount < 0",++ "SELECT COALESCE(MAX(id),0) AS m FROM chat_messages WHERE channel_user_id = ?",++ "SELECT COALESCE(MAX(sort_order),-1)+1 AS n FROM donation_goals WHERE user_id = ?",++ "SELECT COALESCE(NULLIF(ai_category, ''), category) AS category, COUNT(*) AS n FROM streams WHERE user_id = ? AND duration_seconds > 0 AND COALESCE(NULLIF(ai_category, ''), category) IS NOT NULL AND COALESCE(NULLIF(ai_category, ''), category) != '' GROUP BY 1 ORDER BY n DESC LIMIT 1",++ "SELECT COALESCE(SUM(-amount), 0) AS count FROM coin_transactions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND amount < 0",++ "SELECT COALESCE(SUM(-amount), 0) AS count FROM coin_transactions WHERE created_at >= datetime('now', ?) AND amount < 0",++ "SELECT COALESCE(SUM(COALESCE(end_sec, start_sec + 3) - start_sec), 0) AS s FROM stream_timeline_events WHERE user_id = ? AND kind = 'speech' AND created_at >= datetime('now', ?)",++ "SELECT COALESCE(SUM(amount), 0) AS count FROM coin_transactions WHERE amount > 0",++ "SELECT COALESCE(SUM(amount), 0) AS count FROM coin_transactions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND amount > 0",++ "SELECT COALESCE(SUM(amount), 0) AS count FROM coin_transactions WHERE created_at >= datetime('now', ?) AND amount > 0",++ "SELECT COALESCE(SUM(amount), 0) AS count FROM transactions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND type = 'donation' AND created_at >= '1970-01-01 00:00:00'",++ "SELECT COALESCE(SUM(amount), 0) AS count FROM transactions WHERE created_at >= datetime('now', ?) AND type = 'donation' AND created_at >= '1970-01-01 00:00:00'",++ "SELECT COALESCE(SUM(amount), 0) AS count FROM transactions WHERE type = 'donation' AND created_at >= ?",++ "SELECT COALESCE(SUM(amount), 0) AS xp FROM arena_xp_log WHERE user_id = ? AND created_at >= datetime('now', ?)",++ "SELECT COALESCE(SUM(amount), 0) as c FROM transactions WHERE type = 'donation'",++ "SELECT COALESCE(SUM(b_user_id = ?), 0) AS targeted, COALESCE(SUM(b_user_id = ? AND responded = 1), 0) AS answered, COALESCE(SUM(winner_user_id = ?), 0) AS wins, COALESCE(SUM(status = 'resolved' AND winner_user_id IS NOT NULL AND winner_user_id != ?), 0) AS losses, COALESCE(SUM(status = 'resolved' AND resolution = 'forfeit' AND winner_user_id != ? AND winner_user_id IS NOT NULL), 0) AS ducked FROM arena_beefs WHERE a_user_id = ? OR b_user_id = ?",++ "SELECT COALESCE(SUM(balance), 0) AS n FROM channel_points WHERE user_id = ? AND balance > 0",++ "SELECT COALESCE(SUM(bucks), 0) AS count FROM payment_orders WHERE kind = 'bucks' AND status = 'credited'",++ "SELECT COALESCE(SUM(bucks), 0) AS count FROM payment_orders WHERE updated_at >= datetime('now', '-14 days') AND updated_at < datetime('now', '-7 days') AND kind = 'bucks' AND status = 'credited'",++ "SELECT COALESCE(SUM(bucks), 0) AS count FROM payment_orders WHERE updated_at >= datetime('now', ?) AND kind = 'bucks' AND status = 'credited'",++ "SELECT COALESCE(SUM(cost_usd),0) AS c FROM ai_usage WHERE created_at >= date('now')",++ "SELECT COALESCE(SUM(cost_usd),0) AS c FROM ai_usage WHERE owner_user_id = ? AND created_at >= date('now')",++ "SELECT COALESCE(SUM(cost_usd),0) AS c FROM ai_usage WHERE owner_user_id = ? AND created_at >= date('now') AND source = ?",++ "SELECT COALESCE(SUM(cost_usd),0) AS c FROM ai_usage WHERE source = ? AND COALESCE(provider,'shared') = 'shared' AND created_at >= date('now')",++ "SELECT COALESCE(SUM(duration_seconds), 0) AS sec FROM streams WHERE user_id = ? AND duration_seconds > 0 AND id IN (SELECT DISTINCT stream_id FROM stream_timeline_events WHERE user_id = ? AND created_at >= datetime('now', ?))",++ "SELECT COALESCE(SUM(minutes_watched), 0) AS count FROM watch_time",++ "SELECT COALESCE(SUM(openvibe_bucks_balance), 0) as c FROM users",++ "SELECT COALESCE(SUM(unread), 0) as total FROM ( SELECT COUNT(*) as unread FROM dm_messages m JOIN dm_participants p ON p.conversation_id = m.conversation_id AND p.user_id = ? WHERE m.created_at > p.last_read_at AND m.sender_id != ? )",++ "SELECT COALESCE(SUM(viewer_count), 0) AS count FROM streams WHERE is_live = 1",++ "SELECT COALESCE(SUM(viewer_count), 0) as c FROM streams WHERE is_live = 1",++ "SELECT COALESCE(SUM(viewer_count),0) AS v, COUNT(*) AS n FROM streams WHERE is_live = 1",++ "SELECT COALESCE(role,'legacy') AS role, COUNT(*) AS calls, SUM(input_tokens) AS input_tokens, SUM(cached_tokens) AS cached_tokens, SUM(output_tokens) AS output_tokens, SUM(cost_usd) AS cost_usd, AVG(latency_ms) AS avg_latency_ms FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY role ORDER BY cost_usd DESC",++ "SELECT COALESCE(source,'platform') AS source, COALESCE(provider,'shared') AS provider, COUNT(*) AS calls, SUM(cost_usd) AS cost_usd, SUM(input_tokens) AS input_tokens, SUM(cached_tokens) AS cached_tokens FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY source, provider ORDER BY cost_usd DESC",++ "SELECT COALESCE(text_en, text) AS t, start_sec AS s FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' ORDER BY start_sec ASC",++ "SELECT COUNT(*) - COUNT(DISTINCT stream_id || ':' || offset_seconds) AS extra FROM stream_memories",++ "SELECT COUNT(*) AS c FROM channel_ai_config",++ "SELECT COUNT(*) AS c FROM chat_messages WHERE anon_id = ? AND user_id IS NULL AND is_deleted = 0 AND (auto_delete_at IS NULL OR datetime(auto_delete_at) > CURRENT_TIMESTAMP)",++ "SELECT COUNT(*) AS c FROM media_requests WHERE streamer_id = ? AND user_id = ? AND status IN ('pending', 'playing')",++ "SELECT COUNT(*) AS c FROM stream_memories WHERE stream_id = ?",++ "SELECT COUNT(*) AS c, COALESCE(MAX(id), 0) AS m, COALESCE(SUM(id * 7 + COALESCE(stream_id, 0) + LENGTH(COALESCE(expires_at, ''))), 0) AS s FROM bans",++ "SELECT COUNT(*) AS calls, COALESCE(SUM(input_tokens),0) AS input_tokens, COALESCE(SUM(output_tokens),0) AS output_tokens, COALESCE(SUM(cached_tokens),0) AS cached_tokens, COALESCE(SUM(cost_usd),0) AS cost_usd FROM ai_usage WHERE created_at >= date('now', ?)",++ "SELECT COUNT(*) AS count FROM ( SELECT DISTINCT 'u' || user_id AS id FROM chat_messages WHERE user_id IS NOT NULL AND COALESCE(is_deleted, 0) = 0 AND timestamp >= datetime('now', '-14 days') AND timestamp < datetime('now', '-7 days') UNION SELECT DISTINCT 'a' || anon_id FROM chat_messages WHERE anon_id IS NOT NULL AND COALESCE(is_deleted, 0) = 0 AND timestamp >= datetime('now', '-14 days') AND timestamp < datetime('now', '-7 days') UNION SELECT DISTINCT 'r' || source_platform || '|' || username FROM chat_messages WHERE source_platform IS NOT NULL AND source_platform <> '' AND COALESCE(is_deleted, 0) = 0 AND timestamp >= datetime('now', '-14 days') AND timestamp < datetime('now', '-7 days') )",++ "SELECT COUNT(*) AS count FROM ( SELECT DISTINCT 'u' || user_id AS id FROM chat_messages WHERE user_id IS NOT NULL AND COALESCE(is_deleted, 0) = 0 AND timestamp >= datetime('now', '-7 days') UNION SELECT DISTINCT 'a' || anon_id FROM chat_messages WHERE anon_id IS NOT NULL AND COALESCE(is_deleted, 0) = 0 AND timestamp >= datetime('now', '-7 days') UNION SELECT DISTINCT 'r' || source_platform || '|' || username FROM chat_messages WHERE source_platform IS NOT NULL AND source_platform <> '' AND COALESCE(is_deleted, 0) = 0 AND timestamp >= datetime('now', '-7 days') )",++ "SELECT COUNT(*) AS count FROM anon_ip_mappings",++ "SELECT COUNT(*) AS count FROM anon_ip_mappings WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",++ "SELECT COUNT(*) AS count FROM anon_ip_mappings WHERE created_at >= datetime('now', '-7 days')",++ "SELECT COUNT(*) AS count FROM anon_ip_mappings WHERE created_at >= datetime('now', ?)",++ "SELECT COUNT(*) AS count FROM chat_messages",++ "SELECT COUNT(*) AS count FROM chat_messages WHERE timestamp >= datetime('now', '-14 days') AND timestamp < datetime('now', '-7 days') AND COALESCE(is_deleted, 0) = 0",++ "SELECT COUNT(*) AS count FROM chat_messages WHERE timestamp >= datetime('now', ?) AND COALESCE(is_deleted, 0) = 0",++ "SELECT COUNT(*) AS count FROM coin_redemptions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND status NOT IN ('rejected', 'refunded')",++ "SELECT COUNT(*) AS count FROM coin_redemptions WHERE created_at >= datetime('now', ?) AND status NOT IN ('rejected', 'refunded')",++ "SELECT COUNT(*) AS count FROM coin_redemptions WHERE status NOT IN ('rejected', 'refunded')",++ "SELECT COUNT(*) AS count FROM donation_goals WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",++ "SELECT COUNT(*) AS count FROM donation_goals WHERE created_at >= datetime('now', ?)",++ "SELECT COUNT(*) AS count FROM donation_goals WHERE is_active = 1",++ "SELECT COUNT(*) AS count FROM donation_goals WHERE reached_at IS NOT NULL OR current_amount >= target_amount",++ "SELECT COUNT(*) AS count FROM emotes",++ "SELECT COUNT(*) AS count FROM emotes WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",++ "SELECT COUNT(*) AS count FROM emotes WHERE created_at >= datetime('now', ?)",++ "SELECT COUNT(*) AS count FROM follows",++ "SELECT COUNT(*) AS count FROM follows WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",++ "SELECT COUNT(*) AS count FROM follows WHERE created_at >= datetime('now', ?)",++ "SELECT COUNT(*) AS count FROM managed_streams WHERE user_id = ?",++ "SELECT COUNT(*) AS count FROM stream_memories",++ "SELECT COUNT(*) AS count FROM stream_memories WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",++ "SELECT COUNT(*) AS count FROM stream_memories WHERE created_at >= datetime('now', ?)",++ "SELECT COUNT(*) AS count FROM stream_memories WHERE user_id = ?",++ "SELECT COUNT(*) AS count FROM streams",++ "SELECT COUNT(*) AS count FROM streams WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",++ "SELECT COUNT(*) AS count FROM streams WHERE created_at >= datetime('now', ?)",++ "SELECT COUNT(*) AS count FROM streams WHERE is_live = 1",++ "SELECT COUNT(*) AS count FROM subscriptions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",++ "SELECT COUNT(*) AS count FROM subscriptions WHERE created_at >= datetime('now', ?)",++ "SELECT COUNT(*) AS count FROM subscriptions WHERE status = 'active' AND (current_period_end IS NULL OR datetime(current_period_end) > CURRENT_TIMESTAMP)",++ "SELECT COUNT(*) AS count FROM users WHERE COALESCE(is_banned, 0) = 0",++ "SELECT COUNT(*) AS count FROM users WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND COALESCE(is_banned, 0) = 0",++ "SELECT COUNT(*) AS count FROM users WHERE created_at >= datetime('now', ?) AND COALESCE(is_banned, 0) = 0",++ "SELECT COUNT(*) AS n FROM ai_usage WHERE kind = 'tts' AND created_at >= date('now')",++ "SELECT COUNT(*) AS n FROM arena_beef_hype WHERE beef_id = ? AND side = ?",++ "SELECT COUNT(*) AS n FROM arena_beefs WHERE winner_user_id = ? AND resolved_at >= datetime('now', ?)",++ "SELECT COUNT(*) AS n FROM arena_mic_moments WHERE created_at >= datetime('now', '-1 day')",++ "SELECT COUNT(*) AS n FROM arena_mic_moments WHERE said_at >= ?",++ "SELECT COUNT(*) AS n FROM arena_mic_moments WHERE stream_id = ?",++ "SELECT COUNT(*) AS n FROM channel_ai_bots WHERE channel_user_id = ?",++ "SELECT COUNT(*) AS n FROM channel_moderators WHERE channel_id = ?",++ "SELECT COUNT(*) AS n FROM channel_sounds WHERE channel_owner_id = ?",++ "SELECT COUNT(*) AS n FROM chat_messages WHERE stream_id = ? AND message_type = 'soundboard'",++ "SELECT COUNT(*) AS n FROM chat_messages WHERE timestamp >= ? AND COALESCE(is_deleted, 0) = 0",++ "SELECT COUNT(*) AS n FROM chat_staged_outbox",++ "SELECT COUNT(*) AS n FROM chat_staged_outbox WHERE tbl = ?",++ "SELECT COUNT(*) AS n FROM control_configs WHERE user_id = ?",++ "SELECT COUNT(*) AS n FROM donation_goals WHERE user_id = ? AND is_active = 1",++ "SELECT COUNT(*) AS n FROM easter_egg_solves WHERE egg_date = ?",++ "SELECT COUNT(*) AS n FROM emotes WHERE user_id = ?",++ "SELECT COUNT(*) AS n FROM follows WHERE created_at >= ?",++ "SELECT COUNT(*) AS n FROM follows WHERE streamer_id = ?",++ "SELECT COUNT(*) AS n FROM follows WHERE streamer_id = ? AND created_at >= ?",++ "SELECT COUNT(*) AS n FROM follows WHERE streamer_id = ? AND created_at BETWEEN ? AND ?",++ "SELECT COUNT(*) AS n FROM stream_timeline_events WHERE user_id = ? AND kind = 'sound' AND (LOWER(label) LIKE '%laugh%' OR LOWER(label) LIKE '%giggle%' OR LOWER(label) LIKE '%chuckle%') AND created_at >= datetime('now', ?)",++ "SELECT COUNT(*) AS n FROM streams WHERE is_live = 1",++ "SELECT COUNT(*) AS n FROM streams WHERE started_at >= ?",++ "SELECT COUNT(*) AS n FROM streams WHERE user_id = ?",++ "SELECT COUNT(*) AS n FROM subscriptions WHERE streamer_id = ? AND status = 'active' AND (current_period_end IS NULL OR datetime(current_period_end) > CURRENT_TIMESTAMP)",++ "SELECT COUNT(*) AS n FROM users WHERE created_at >= ?",++ "SELECT COUNT(*) AS n, COALESCE(AVG(quality), 0) AS avg_q, COALESCE(MAX(quality), 0) AS best_q, COALESCE(SUM(kind IN ('beef_hit', 'callout')), 0) AS beef_hits, COALESCE(SUM(kind = 'trash'), 0) AS trash, COALESCE(SUM(quality >= 7), 0) AS bangers FROM arena_mic_moments WHERE user_id = ? AND created_at >= datetime('now', ?)",++ "SELECT COUNT(*) AS n, COALESCE(SUM(amount), 0) AS t FROM transactions WHERE type = 'donation' AND (stream_id = ? OR (to_user_id = ? AND created_at BETWEEN ? AND ?))",++ "SELECT COUNT(*) AS n, COALESCE(SUM(amount), 0) AS total FROM transactions WHERE type = 'donation' AND COALESCE(status, 'completed') NOT IN ('failed', 'refunded', 'pending') AND ((stream_id = ?) OR (to_user_id = ? AND created_at BETWEEN ? AND ?))",++ "SELECT COUNT(*) AS n, COALESCE(SUM(s.duration_seconds), 0) AS secs, COALESCE(MAX(s.peak_viewers), 0) AS peak, AVG(a.avg_viewers) AS avg FROM streams s LEFT JOIN stream_analytics a ON a.stream_id = s.id WHERE s.user_id = ? AND s.started_at >= ? AND s.is_live = 0",++ "SELECT COUNT(*) AS n, COUNT(DISTINCT COALESCE(user_id, anon_id, username)) AS c FROM chat_messages WHERE stream_id = ? AND COALESCE(is_deleted, 0) = 0",++ "SELECT COUNT(*) AS n, COUNT(DISTINCT COALESCE(user_id, anon_id, username)) AS chatters FROM chat_messages WHERE stream_id = ? AND COALESCE(is_deleted, 0) = 0",++ "SELECT COUNT(*) AS n, MAX(started_at) AS last FROM streams WHERE user_id = ?",++ "SELECT COUNT(*) AS n, MIN(started_at) AS first FROM streams WHERE user_id = ?",++ "SELECT COUNT(*) AS streams, COALESCE(SUM(duration_seconds), 0) / 3600.0 AS hours, MAX(ended_at) AS last_live_at FROM streams WHERE user_id = ? AND duration_seconds > 0 AND started_at >= datetime('now', ?)",++ "SELECT COUNT(*) as c FROM chat_messages",++ "SELECT COUNT(*) as c FROM chat_messages WHERE user_id = ? AND is_deleted = 0 AND (auto_delete_at IS NULL OR datetime(auto_delete_at) > CURRENT_TIMESTAMP)",++ "SELECT COUNT(*) as c FROM coin_redemptions WHERE reward_id = ? AND stream_id = ?",++ "SELECT COUNT(*) as c FROM content_views WHERE content_type = ? AND content_id = ?",++ "SELECT COUNT(*) as c FROM dm_participants WHERE conversation_id = ?",++ "SELECT COUNT(*) as c FROM follows WHERE follower_id = ?",++ "SELECT COUNT(*) as c FROM follows WHERE streamer_id = ?",++ "SELECT COUNT(*) as c FROM site_settings",++ "SELECT COUNT(*) as c FROM streams",++ "SELECT COUNT(*) as c FROM streams WHERE is_live = 1",++ "SELECT COUNT(*) as c FROM transactions",++ "SELECT COUNT(*) as c FROM transactions WHERE type = 'cashout' AND status = 'escrow'",++ "SELECT COUNT(*) as c FROM users",++ "SELECT COUNT(*) as c FROM users WHERE is_banned = 1",++ "SELECT COUNT(*) as c FROM users WHERE role = 'mod'",++ "SELECT COUNT(*) as c FROM users WHERE role IN ('streamer', 'admin')",++ "SELECT COUNT(*) as c FROM users WHERE username LIKE ? OR display_name LIKE ?",++ "SELECT COUNT(*) as cnt FROM chat_messages WHERE is_global = 1 AND timestamp >= ? AND timestamp <= ? AND is_deleted = 0",++ "SELECT COUNT(*) as cnt FROM chat_messages WHERE stream_id = ? AND is_deleted = 0 AND is_global = 0 AND message_type = 'chat'",++ "SELECT COUNT(*) as cnt FROM chat_messages WHERE stream_id = ? AND timestamp >= ? AND is_deleted = 0 AND is_global = 0 AND message_type = 'chat'",++ "SELECT COUNT(*) as cnt FROM chat_messages WHERE stream_id = ? AND timestamp >= ? AND timestamp <= ? AND is_deleted = 0",++ "SELECT COUNT(*) as cnt FROM follows WHERE streamer_id = ?",++ "SELECT COUNT(*) as cnt FROM follows WHERE streamer_id = ? AND created_at >= ? AND created_at <= ?",++ "SELECT COUNT(*) as cnt FROM streams WHERE user_id = ? AND duration_seconds > 0",++ "SELECT COUNT(*) as count FROM channel_sounds WHERE channel_owner_id = ?",++ "SELECT COUNT(*) as count FROM channel_sounds WHERE channel_owner_id = ? AND created_by = ?",++ "SELECT COUNT(*) as count FROM emotes WHERE (channel_owner_id = ?) OR (channel_owner_id IS NULL AND user_id = ?)",++ "SELECT COUNT(*) as count FROM emotes WHERE user_id = ?",++ "SELECT COUNT(*) as count FROM follows WHERE streamer_id = ?",++ "SELECT COUNT(*) as total_streams, SUM(duration_seconds) as total_duration, MAX(peak_viewers) as peak_viewers FROM streams WHERE user_id = ? AND duration_seconds > 0",++ "SELECT COUNT(*) as total_streams, SUM(s.duration_seconds) as total_duration, MAX(s.peak_viewers) as all_time_peak, AVG(sa.avg_viewers) as avg_viewers_per_stream, SUM(sa.total_messages) as total_messages, SUM(sa.unique_chatters) as total_unique_chatters, SUM(sa.total_watch_minutes) as total_watch_minutes, SUM(sa.new_followers) as total_new_followers, SUM(sa.clips_created) as total_clips FROM streams s LEFT JOIN stream_analytics sa ON sa.stream_id = s.id WHERE s.user_id = ? AND s.started_at >= ? AND s.duration_seconds > 0",++ "SELECT COUNT(DISTINCT COALESCE(user_id, anon_id)) as cnt FROM chat_messages WHERE stream_id = ? AND is_deleted = 0 AND is_global = 0",++ "SELECT COUNT(DISTINCT from_user_id) AS count FROM transactions WHERE type = 'donation' AND from_user_id IS NOT NULL AND created_at >= ?",++ "SELECT COUNT(DISTINCT from_user_id) AS count FROM transactions WHERE type = 'donation' AND from_user_id IS NOT NULL AND created_at >= datetime('now', ?)",++ "SELECT COUNT(DISTINCT from_user_id) AS count FROM transactions WHERE type = 'donation' AND from_user_id IS NOT NULL AND created_at >= datetime('now', ?) AND created_at < datetime('now', ?)",++ "SELECT COUNT(DISTINCT user_id) AS count FROM streams WHERE is_live = 0 AND ended_at IS NOT NULL",++ "SELECT COUNT(DISTINCT user_id) AS count FROM streams WHERE user_id IS NOT NULL",++ "SELECT COUNT(DISTINCT user_id) AS count FROM streams WHERE user_id IS NOT NULL AND created_at >= datetime('now', ?)",++ "SELECT COUNT(DISTINCT user_id) AS count FROM streams WHERE user_id IS NOT NULL AND created_at >= datetime('now', ?) AND created_at < datetime('now', ?)",++ "SELECT DISTINCT e.user_id FROM stream_timeline_events e JOIN users u ON u.id = e.user_id WHERE e.kind = 'speech' AND e.user_id IS NOT NULL AND e.created_at >= datetime('now', ?) AND COALESCE(u.is_banned, 0) = 0",++ "SELECT DISTINCT il.user_id, u.role FROM ip_log il JOIN users u ON u.id = il.user_id WHERE il.ip_address = ? AND il.user_id IS NOT NULL",++ "SELECT DISTINCT rd.user_id FROM restream_destinations rd WHERE rd.managed_stream_id IS NULL",++ "SELECT DISTINCT s.user_id, u.stream_key, u.username, u.display_name, c.id AS channel_id, c.title AS channel_title, c.protocol AS channel_protocol, c.category AS channel_category, c.is_nsfw AS channel_is_nsfw FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN channels c ON c.user_id = s.user_id WHERE s.user_id NOT IN (SELECT user_id FROM managed_streams) AND u.stream_key IS NOT NULL",++ "SELECT DISTINCT title FROM streams WHERE user_id = ? AND duration_seconds > 0 ORDER BY started_at DESC LIMIT 8",++ "SELECT DISTINCT title FROM streams WHERE user_id = ? AND title IS NOT NULL AND title != '' ORDER BY id DESC LIMIT 10",++ "SELECT DISTINCT user_id AS id FROM streams UNION SELECT user_id FROM search_doc_pushes",++ "SELECT DISTINCT user_id FROM linked_accounts WHERE service = 'network' AND subject_id = ?",++ "SELECT MAX(anon_num) as m FROM anon_ip_mappings",++ "SELECT MAX(id) AS id FROM chat_messages",++ "SELECT MAX(id) AS m FROM chat_messages",++ "SELECT MAX(queue_position) AS max_pos FROM media_requests WHERE streamer_id = ? AND status = 'pending'",++ "SELECT MIN(timestamp) AS t FROM chat_messages WHERE anon_id = ? AND user_id IS NULL AND is_deleted = 0",++ "SELECT ROUND(AVG(viewer_count), 1) AS a, COUNT(*) AS k FROM viewer_snapshots WHERE stream_id = ?",++ "SELECT ROUND(SUM(COALESCE(duration_seconds, CASE WHEN ended_at IS NOT NULL THEN (julianday(ended_at) - julianday(started_at)) * 86400 ELSE (julianday('now') - julianday(started_at)) * 86400 END)) / 3600.0, 1) AS h FROM streams WHERE started_at >= ?",++ "SELECT SUM(CASE WHEN event = 'line' THEN 1 ELSE 0 END) AS lines, SUM(CASE WHEN event = 'tick' THEN 1 ELSE 0 END) AS ticks, SUM(CASE WHEN event = 'skip' THEN 1 ELSE 0 END) AS skips, COALESCE(SUM(cost_usd),0) AS cost_usd, COALESCE(SUM(tokens_in),0) AS tokens_in, COALESCE(SUM(tokens_cached),0) AS tokens_cached FROM ai_viewer_log WHERE channel_user_id = ? AND created_at > datetime('now', ?)",++ "SELECT SUM(CASE WHEN winner_user_id = ? THEN 1 ELSE 0 END) AS wins, SUM(CASE WHEN status = 'resolved' AND winner_user_id IS NOT NULL AND winner_user_id != ? THEN 1 ELSE 0 END) AS losses, SUM(CASE WHEN status = 'resolved' AND winner_user_id IS NULL THEN 1 ELSE 0 END) AS draws FROM arena_beefs WHERE a_user_id = ? OR b_user_id = ?",++ "SELECT SUM(coins_earned) as total FROM watch_time WHERE stream_id = ?",++ "SELECT SUM(minutes_watched) as total FROM watch_time WHERE stream_id = ?",++ "SELECT SUM(minutes_watched) as total FROM watch_time WHERE user_id = ?",++ "SELECT SUM(persona_json IS NOT NULL) AS personas, SUM(image_path IS NOT NULL) AS images, SUM(quotes_json IS NOT NULL) AS quotes FROM arena_profiles",++ "SELECT SUM(status = 'open') AS open, SUM(status = 'resolved') AS resolved FROM arena_beefs",++ "SELECT a.owner_user_id AS user_id, u.username, COUNT(*) AS calls, SUM(a.cost_usd) AS cost_usd, SUM(a.input_tokens) AS input_tokens, SUM(a.cached_tokens) AS cached_tokens, SUM(a.output_tokens) AS output_tokens, SUM(CASE WHEN a.created_at >= date('now') THEN a.cost_usd ELSE 0 END) AS cost_today, SUM(CASE WHEN a.provider = 'byo' THEN a.cost_usd ELSE 0 END) AS cost_byo FROM ai_usage a LEFT JOIN users u ON u.id = a.owner_user_id WHERE a.created_at >= date('now', ?) AND a.owner_user_id IS NOT NULL GROUP BY a.owner_user_id ORDER BY cost_usd DESC LIMIT 100",++ "SELECT ai.*, u.username as approved_by_username FROM approved_ips ai LEFT JOIN users u ON ai.approved_by = u.id WHERE ai.channel_id = ? ORDER BY ai.created_at DESC LIMIT ? OFFSET ?",++ "SELECT ai_derivation_enabled FROM channels WHERE user_id = ?",++ "SELECT ai_overview_short, ai_overview FROM clip_ai_state WHERE clip_id = ?",++ "SELECT ai_overview_short, ai_overview FROM vod_ai_state WHERE vod_id = ?",++ "SELECT aimed_at, COUNT(*) AS n FROM arena_mic_moments WHERE user_id = ? AND aimed_at IS NOT NULL AND target_user_id IS NULL GROUP BY aimed_at ORDER BY n DESC LIMIT 6",++ "SELECT anon_num FROM anon_ip_mappings WHERE ip = ?",++ "SELECT applied_at FROM schema_migrations WHERE id = ?",++ "SELECT balance FROM channel_points WHERE user_id = ? AND streamer_id = ?",++ "SELECT broadcast_settings FROM managed_streams WHERE id = ? AND user_id = ?",++ "SELECT c.* FROM dm_conversations c JOIN dm_participants p1 ON p1.conversation_id = c.id AND p1.user_id = ? JOIN dm_participants p2 ON p2.conversation_id = c.id AND p2.user_id = ? WHERE c.is_group = 0",++ "SELECT c.*, u.username, u.display_name, u.avatar_url, u.profile_color, u.bio, u.stream_key, u.role, u.is_owner FROM channels c JOIN users u ON c.user_id = u.id WHERE u.username = ? COLLATE NOCASE",++ "SELECT c.id, c.name, c.is_group, c.updated_at, (SELECT dm.message FROM dm_messages dm WHERE dm.conversation_id = c.id ORDER BY dm.created_at DESC LIMIT 1) AS last_message, (SELECT dm.sender_id FROM dm_messages dm WHERE dm.conversation_id = c.id ORDER BY dm.created_at DESC LIMIT 1) AS last_sender_id, (SELECT dm.created_at FROM dm_messages dm WHERE dm.conversation_id = c.id ORDER BY dm.created_at DESC LIMIT 1) AS last_message_at, (SELECT COUNT(*) FROM dm_messages dm WHERE dm.conversation_id = c.id AND dm.created_at > p.last_read_at AND dm.sender_id != ?) AS unread_count FROM dm_conversations c JOIN dm_participants p ON p.conversation_id = c.id AND p.user_id = ? ORDER BY COALESCE((SELECT dm.created_at FROM dm_messages dm WHERE dm.conversation_id = c.id ORDER BY dm.created_at DESC LIMIT 1), c.created_at) DESC",++ "SELECT c.username, u.display_name, u.avatar_url, u.profile_color, COUNT(*) AS n FROM chat_messages c LEFT JOIN users u ON u.id = c.user_id WHERE c.stream_id = ? AND COALESCE(c.is_deleted, 0) = 0 AND c.username IS NOT NULL AND (c.user_id IS NULL OR c.user_id != ?) GROUP BY COALESCE(c.user_id, c.username) ORDER BY n DESC LIMIT 3",++ "SELECT c.username, u.display_name, u.avatar_url, u.profile_color, COUNT(*) AS n FROM chat_messages c LEFT JOIN users u ON u.id = c.user_id WHERE c.stream_id = ? AND COALESCE(c.is_deleted, 0) = 0 AND c.username IS NOT NULL AND (c.user_id IS NULL OR c.user_id != ?) GROUP BY COALESCE(c.user_id, c.username) ORDER BY n DESC LIMIT 5",++ "SELECT category, is_nsfw FROM streams WHERE id = ?",++ "SELECT chat_language FROM channels WHERE user_id = ?",++ "SELECT chat_settings FROM user_preferences WHERE user_id = ?",++ "SELECT clip_id AS id, s.* FROM clip_ai_state s WHERE (ai_overview IS NULL OR ai_overview = '') AND (ai_overview_short IS NULL OR ai_overview_short = '' OR ai_overview_short LIKE '%…') ORDER BY clip_id DESC LIMIT ?",++ "SELECT clip_id AS id, s.* FROM clip_ai_state s WHERE ai_transcript_json IS NULL AND (transcript_status IS NULL OR transcript_status IN ('pending','retry')) AND (transcript_next_at IS NULL OR transcript_next_at <= CURRENT_TIMESTAMP) ORDER BY (transcript_status='retry'), clip_id DESC LIMIT ?",++ "SELECT clip_id FROM clip_ai_state WHERE COALESCE(clip_notified,0) = 0 AND clip_notify_at IS NOT NULL AND clip_notify_at <= CURRENT_TIMESTAMP LIMIT ?",++ "SELECT clips_created FROM stream_analytics WHERE stream_id = ?",++ "SELECT cm.*, s.title as stream_title FROM chat_messages cm LEFT JOIN streams s ON cm.stream_id = s.id WHERE cm.user_id = ? AND cm.is_deleted = 0 AND (cm.auto_delete_at IS NULL OR datetime(cm.auto_delete_at) > CURRENT_TIMESTAMP) ORDER BY cm.timestamp DESC LIMIT ? OFFSET ?",++ "SELECT cm.*, u.avatar_url, u.profile_color, u.role, u.display_name FROM chat_messages cm LEFT JOIN users u ON cm.user_id = u.id WHERE cm.stream_id = ? AND cm.is_deleted = 0 AND cm.message_type = 'chat' AND (cm.auto_delete_at IS NULL OR datetime(cm.auto_delete_at) > CURRENT_TIMESTAMP)",++ "SELECT cm.*, u.avatar_url, u.profile_color, u.role, u.display_name, u.username AS core_username, COALESCE(su.username, cu.username) AS stream_channel, s.is_live AS source_is_live, s.managed_stream_id AS source_managed_id, COALESCE(ms.title, s.title) AS source_stream_title, ms.slug AS source_slug FROM chat_messages cm LEFT JOIN users u ON cm.user_id = u.id LEFT JOIN streams s ON cm.stream_id = s.id LEFT JOIN users su ON s.user_id = su.id LEFT JOIN users cu ON cm.channel_user_id = cu.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE cm.is_deleted = 0 AND cm.message_type IN ('chat', 'system', 'channel-sound', 'soundboard', 'donation') AND (cm.auto_delete_at IS NULL OR datetime(cm.auto_delete_at) > CURRENT_TIMESTAMP)",++ "SELECT cm.*, u.avatar_url, u.profile_color, u.role, u.display_name, u.username AS core_username, s.title AS source_stream_title, s.managed_stream_id AS source_managed_id, s.is_live AS source_is_live, ms.slug AS source_slug, COALESCE(bu.username, cu.username) AS source_channel FROM chat_messages cm LEFT JOIN users u ON cm.user_id = u.id LEFT JOIN streams s ON cm.stream_id = s.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id LEFT JOIN users bu ON s.user_id = bu.id LEFT JOIN users cu ON cm.channel_user_id = cu.id WHERE cm.channel_user_id = ? AND cm.is_deleted = 0 AND (cm.auto_delete_at IS NULL OR datetime(cm.auto_delete_at) > CURRENT_TIMESTAMP)",++ "SELECT cm.*, u.display_name, u.username as u_username, u.role, u.avatar_url, u.profile_color FROM chat_messages cm LEFT JOIN users u ON cm.user_id = u.id WHERE cm.is_deleted = 0 AND (cm.auto_delete_at IS NULL OR datetime(cm.auto_delete_at) > CURRENT_TIMESTAMP)",++ "SELECT cm.channel_id, c.title, c.user_id, u.username as owner_username FROM channel_moderators cm JOIN channels c ON cm.channel_id = c.id JOIN users u ON c.user_id = u.id WHERE cm.user_id = ?",++ "SELECT cm.id, cm.user_id, cm.added_by, cm.created_at, u.username, u.display_name, u.avatar_url, a.username as added_by_username FROM channel_moderators cm JOIN users u ON cm.user_id = u.id LEFT JOIN users a ON cm.added_by = a.id WHERE cm.channel_id = ? ORDER BY cm.created_at ASC",++ "SELECT cm.user_id, cm.username, COUNT(*) as message_count FROM chat_messages cm JOIN streams s ON cm.stream_id = s.id WHERE s.user_id = ? AND cm.timestamp >= ? AND cm.is_deleted = 0 AND cm.is_global = 0 AND cm.message_type = 'chat' AND cm.user_id IS NOT NULL GROUP BY cm.user_id ORDER BY message_count DESC LIMIT 20",++ "SELECT compared, matched, mismatched, inconclusive, errors, last_mismatch_at, last_mismatch, since FROM chat_dual_read_stats WHERE tbl = ?",++ "SELECT consecutive_failures FROM restream_destinations WHERE id = ?",++ "SELECT created_at FROM anon_ip_mappings WHERE anon_num = ?",++ "SELECT created_at FROM anon_ip_mappings WHERE ip = ?",++ "SELECT created_at FROM coin_redemptions WHERE reward_id = ? AND user_id = ? ORDER BY created_at DESC LIMIT 1",++ "SELECT created_at FROM users WHERE id = ?",++ "SELECT cw.id, cw.user_id, u.username, u.display_name, cw.created_at FROM control_whitelist cw JOIN users u ON cw.user_id = u.id WHERE cw.channel_id = ? ORDER BY cw.created_at DESC",++ "SELECT date(created_at) AS day, COUNT(*) AS calls, SUM(input_tokens) AS input_tokens, SUM(output_tokens) AS output_tokens, SUM(cost_usd) AS cost_usd FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY day ORDER BY day DESC",++ "SELECT default_clip_visibility, slot_clip_recording_enabled FROM managed_streams WHERE id = ?",++ "SELECT default_vod_visibility FROM managed_streams WHERE id = ?",++ "SELECT description FROM stream_memories WHERE user_id = ? ORDER BY captured_at DESC LIMIT 4",++ "SELECT description, category, chat_language FROM channels WHERE user_id = ?",++ "SELECT donation_sound_url, donation_sound_mime, goal_sound_url, goal_sound_mime FROM channel_moderation_settings WHERE channel_id = ?",++ "SELECT e.*, u.username FROM emotes e JOIN users u ON e.user_id = u.id WHERE e.id = ?",++ "SELECT e.*, u.username FROM emotes e JOIN users u ON e.user_id = u.id WHERE e.is_global = 1 AND e.is_approved = 1 ORDER BY code",++ "SELECT e.*, u.username, up.username AS uploader_username, up.display_name AS uploader_display_name FROM emotes e JOIN users u ON e.user_id = u.id LEFT JOIN users up ON e.user_id = up.id WHERE ((e.channel_owner_id = ?) OR (e.channel_owner_id IS NULL AND e.user_id = ?)) AND e.is_approved = 1 ORDER BY code",++ "SELECT e.id, e.stream_id, e.vod_id, e.start_sec, e.end_sec, e.text, s.started_at FROM stream_timeline_events e LEFT JOIN streams s ON s.id = e.stream_id WHERE e.user_id = ? AND e.kind = 'speech' AND e.id > ? AND e.created_at >= datetime('now', ?) AND NOT EXISTS (SELECT 1 FROM streams l WHERE l.id = e.stream_id AND l.is_live = 1) ORDER BY e.stream_id, e.start_sec LIMIT 4000",++ "SELECT enabled FROM news_settings WHERE scope = 'global' AND source_id = '_master'",++ "SELECT enabled FROM news_settings WHERE scope = 'user' AND scope_id = ? AND source_id = '_master'",++ "SELECT f.created_at, fu.username AS follower_username, fu.display_name AS follower_display, su.username AS streamer_username, su.display_name AS streamer_display FROM follows f JOIN users fu ON fu.id = f.follower_id JOIN users su ON su.id = f.streamer_id ORDER BY f.id DESC LIMIT 1",++ "SELECT f.id, la.subject_id, la.service_user_id FROM follows f LEFT JOIN linked_accounts la ON la.service = 'network' AND la.user_id = f.follower_id WHERE f.streamer_id = ? AND f.id > ? ORDER BY f.id LIMIT ?",++ "SELECT follower_id FROM follows WHERE streamer_id = ?",++ "SELECT from_user_id, u.username, u.display_name, u.avatar_url, SUM(amount) as total_donated FROM transactions t JOIN users u ON t.from_user_id = u.id WHERE t.stream_id = ? AND t.type = 'donation' AND t.status = 'completed' GROUP BY from_user_id ORDER BY total_donated DESC LIMIT ?",++ "SELECT g.id, g.title, g.current_amount, g.target_amount, g.image_url, u.username, u.display_name FROM donation_goals g JOIN users u ON u.id = g.user_id WHERE g.is_active = 1 AND g.target_amount > 0 ORDER BY (CAST(g.current_amount AS REAL) / g.target_amount) DESC, g.current_amount DESC LIMIT 3",++ "SELECT hash, revision, deleted FROM search_doc_pushes WHERE user_id = ?",++ "SELECT hash, revision, deleted FROM search_media_pushes WHERE kind = ? AND media_id = ?",++ "SELECT hash, strftime('%s', pushed_at) * 1000 AS at FROM module_summary_pushes WHERE user_id = ? AND namespace = ?",++ "SELECT hru.*, u.username as created_by_username FROM hidden_relay_users hru LEFT JOIN users u ON hru.created_by = u.id WHERE hru.channel_id = ? OR hru.channel_id IS NULL ORDER BY hru.created_at DESC LIMIT ?",++ "SELECT id FROM billing_actions WHERE idempotency_key = ?",++ "SELECT id FROM channels WHERE user_id = ?",++ "SELECT id FROM coin_transactions WHERE user_id = ? AND type = 'follow_bonus' AND message LIKE '%streamer:' || ? || '%'",++ "SELECT id FROM emotes WHERE user_id = ? AND code = ? AND channel_owner_id IS NULL",++ "SELECT id FROM follows WHERE follower_id = ? AND streamer_id = ?",++ "SELECT id FROM managed_streams WHERE user_id = ? AND slug = ? COLLATE NOCASE",++ "SELECT id FROM managed_streams WHERE user_id = ? ORDER BY created_at",++ "SELECT id FROM media_requests WHERE streamer_id = ? AND status = 'pending' ORDER BY queue_position ASC, requested_at ASC, id ASC",++ "SELECT id FROM schema_migrations",++ "SELECT id FROM streams WHERE is_live = 1",++ "SELECT id FROM streams WHERE is_live = 1 AND protocol NOT IN ('rtmp', 'jsmpeg')",++ "SELECT id FROM streams WHERE managed_stream_id = ? AND is_live = 1 AND id != ?",++ "SELECT id FROM streams WHERE managed_stream_id = ? AND is_live = 1 LIMIT 1",++ "SELECT id FROM streams WHERE user_id = ? AND duration_seconds > 0 ORDER BY started_at DESC LIMIT 6",++ "SELECT id FROM streams WHERE user_id = ? AND is_live = 1 ORDER BY started_at DESC LIMIT 1",++ "SELECT id FROM streams WHERE user_id = ? ORDER BY id DESC LIMIT 1",++ "SELECT id FROM themes WHERE slug = ?",++ "SELECT id FROM user_themes WHERE user_id = ?",++ "SELECT id FROM users WHERE role = 'admin' LIMIT 1",++ "SELECT id FROM users WHERE username = ? COLLATE NOCASE AND id != ?",++ "SELECT id FROM verification_keys WHERE target_username = ? COLLATE NOCASE AND status = 'active'",++ "SELECT id, action, idempotency_key, live_user_id, live_ref, status, error, attempts, created_at, updated_at FROM billing_actions WHERE status IN ('unknown', 'pending') ORDER BY id DESC LIMIT 50",++ "SELECT id, ai_overview_short, ai_overview, title FROM streams WHERE user_id = ? AND (ai_title IS NULL OR ai_title = '') AND (ai_overview_short IS NOT NULL OR ai_overview IS NOT NULL) ORDER BY COALESCE(started_at, created_at) DESC LIMIT ?",++ "SELECT id, applied_at, mode, duration_ms FROM schema_migrations",++ "SELECT id, args FROM chat_bridge_outbox WHERE op = 'db' ORDER BY id",++ "SELECT id, boot, ref, op, args FROM chat_bridge_outbox ORDER BY id",++ "SELECT id, channel_id FROM hidden_relay_users WHERE id = ?",++ "SELECT id, created_at FROM api_tokens WHERE token_hash = ?",++ "SELECT id, ip_address, stream_id, reason FROM bans WHERE user_id = ? AND stream_id IS NULL",++ "SELECT id, label, permissions, last_used, is_active, created_at FROM api_keys WHERE user_id = ?",++ "SELECT id, label, scopes, created_at, last_used_at, expires_at, is_active FROM api_tokens WHERE user_id = ? ORDER BY created_at DESC",++ "SELECT id, message_type, metadata FROM chat_messages WHERE is_deleted = 0 ORDER BY id DESC LIMIT 1",++ "SELECT id, pip_source_msid, pip_defaults FROM managed_streams WHERE id = ?",++ "SELECT id, start_sec, end_sec, text, lang, text_en, created_at FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' AND id > ? ORDER BY id DESC LIMIT ?",++ "SELECT id, stream_id FROM chat_messages WHERE is_deleted = 0 AND auto_delete_at IS NOT NULL AND auto_delete_at <= CURRENT_TIMESTAMP ORDER BY auto_delete_at ASC LIMIT ?",++ "SELECT id, stream_id, user_id, ip_address, anon_id, expires_at FROM bans WHERE expires_at IS NULL OR datetime(expires_at) > CURRENT_TIMESTAMP",++ "SELECT id, stream_id, vod_id, start_sec, text FROM stream_timeline_events WHERE user_id = ? AND kind = 'speech' AND created_at >= datetime('now', ?) AND LENGTH(text) BETWEEN 25 AND 220 ORDER BY created_at DESC LIMIT 1500",++ "SELECT id, title, slug FROM managed_streams WHERE user_id = ? AND (? IS NULL OR id != ?) ORDER BY sort_order ASC, id ASC",++ "SELECT id, title, started_at, viewer_count FROM streams WHERE user_id = ? AND is_live = 1 ORDER BY started_at DESC LIMIT 1",++ "SELECT id, user_id FROM channels WHERE id = ?",++ "SELECT id, user_id, anon_id, username, message, message_type, source_platform, reply_to_id, timestamp FROM chat_messages WHERE channel_user_id = ? AND id > ? AND is_deleted = 0 AND message_type IN ('chat','donation') AND (auto_delete_at IS NULL OR auto_delete_at > CURRENT_TIMESTAMP) ORDER BY id DESC LIMIT ?",++ "SELECT id, user_id, channel_id, managed_stream_id, title, is_live, started_at, ended_at, created_at FROM streams",++ "SELECT id, user_id, protocol FROM streams WHERE is_live = 1 AND ( (last_heartbeat IS NOT NULL AND last_heartbeat < datetime('now', '-5 minutes')) OR (last_heartbeat IS NULL AND started_at < datetime('now', '-6 minutes')) )",++ "SELECT id, user_id, slug FROM managed_streams WHERE id = ?",++ "SELECT id, user_id, slug, title, protocol, streaming_method, openre_stream_id FROM managed_streams WHERE ingest_authority = 'openre' ORDER BY id",++ "SELECT id, user_id, slug, title, sort_order, created_at FROM managed_streams",++ "SELECT id, user_id, title FROM channels",++ "SELECT id, username FROM users WHERE id = ?",++ "SELECT id, username FROM users WHERE username = ? COLLATE NOCASE",++ "SELECT id, username, display_name, avatar_url, profile_color FROM users WHERE id != ? AND is_banned = 0 AND id NOT IN (SELECT blocked_id FROM dm_blocks WHERE blocker_id = ?) AND id NOT IN (SELECT blocker_id FROM dm_blocks WHERE blocked_id = ?) AND (username LIKE ? COLLATE NOCASE OR display_name LIKE ? COLLATE NOCASE) LIMIT ?",++ "SELECT id, username, display_name, avatar_url, profile_color, role, openvibe_bucks_balance, openvibe_coins_balance, created_at, last_seen FROM users WHERE id = ?",++ "SELECT id, username, display_name, bio, is_banned FROM users WHERE id = ?",++ "SELECT id, username, display_name, is_banned FROM users WHERE id = ?",++ "SELECT id, username, display_name, role, created_at FROM users ORDER BY created_at DESC LIMIT 10",++ "SELECT il.user_id, il.anon_id, u.username, u.display_name, u.avatar_url, u.role, u.is_banned, u.ban_reason, u.created_at as user_created_at, COUNT(*) as hit_count, MIN(il.created_at) as first_seen, MAX(il.created_at) as last_seen, GROUP_CONCAT(DISTINCT il.action) as actions FROM ip_log il LEFT JOIN users u ON il.user_id = u.id WHERE il.ip_address = ? GROUP BY COALESCE(il.user_id, il.anon_id) ORDER BY last_seen DESC",++ "SELECT ip, anon_num FROM anon_ip_mappings ORDER BY anon_num",++ "SELECT ip_address, geo_country, geo_region, geo_city, geo_isp, geo_org, geo_ll, COUNT(*) as hit_count, MIN(created_at) as first_seen, MAX(created_at) as last_seen, GROUP_CONCAT(DISTINCT action) as actions FROM ip_log WHERE user_id = ? GROUP BY ip_address ORDER BY last_seen DESC",++ "SELECT ip_address, geo_country, geo_region, geo_city, geo_isp, geo_org, geo_ll, created_at FROM ip_log WHERE anon_id = ? ORDER BY created_at DESC LIMIT 1",++ "SELECT ip_address, geo_country, geo_region, geo_city, geo_isp, geo_org, geo_ll, created_at FROM ip_log WHERE user_id = ? ORDER BY created_at DESC LIMIT 1",++ "SELECT item_id, category, unlocked_at FROM user_cosmetics WHERE user_id = ?",++ "SELECT json FROM stream_recaps WHERE stream_id = ?",++ "SELECT key FROM site_settings WHERE key = ?",++ "SELECT key FROM site_settings WHERE key LIKE 'tts\\_%' ESCAPE '\\' OR key IN ('gif_tenor_api_key', 'gif_giphy_api_key', 'soundboard_101_api_key')",++ "SELECT kind, COUNT(*) AS calls, SUM(cost_usd) AS cost_usd FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY kind ORDER BY cost_usd DESC",++ "SELECT kind, start_sec, end_sec, text, label, confidence FROM stream_timeline_events WHERE stream_id = ?",++ "SELECT kind, start_sec, end_sec, text, label, confidence FROM stream_timeline_events WHERE stream_id = ? AND kind = ? ORDER BY start_sec ASC LIMIT ?",++ "SELECT kind, start_sec, end_sec, text, label, confidence FROM stream_timeline_events WHERE vod_id = ? ORDER BY start_sec ASC LIMIT 20000",++ "SELECT label, COUNT(*) AS n FROM stream_timeline_events WHERE user_id = ? AND kind = 'sound' AND label IS NOT NULL AND created_at >= datetime('now', ?) GROUP BY label ORDER BY n DESC LIMIT 5",++ "SELECT m.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM dm_messages m JOIN users u ON u.id = m.sender_id WHERE m.conversation_id = ? AND m.id < ? ORDER BY m.created_at DESC LIMIT ?",++ "SELECT m.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM dm_messages m JOIN users u ON u.id = m.sender_id WHERE m.conversation_id = ? AND m.id > ? ORDER BY m.created_at ASC LIMIT ?",++ "SELECT m.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM dm_messages m JOIN users u ON u.id = m.sender_id WHERE m.conversation_id = ? ORDER BY m.created_at DESC LIMIT ?",++ "SELECT m.id AS msid, m.title, m.slug, m.user_id, s.id AS stream_id, s.is_live FROM managed_streams m LEFT JOIN streams s ON s.managed_stream_id = m.id AND s.is_live = 1 WHERE m.id = ?",++ "SELECT m.text, m.quality, m.said_at, m.aimed_at, u.username, u.display_name FROM arena_mic_moments m JOIN users u ON u.id = m.user_id WHERE m.said_at >= ? AND m.quality >= 7 ORDER BY m.quality DESC, m.said_at DESC LIMIT 3",++ "SELECT m.thumbnail_url FROM stream_memories m WHERE m.user_id = ? AND m.thumbnail_url LIKE 'http%' AND (LOWER(m.description) LIKE '%person%' OR LOWER(m.description) LIKE '%face%' OR LOWER(m.description) LIKE '%wearing%' OR LOWER(m.description) LIKE '%headphones%') ORDER BY m.id DESC LIMIT 3",++ "SELECT ma.*, actor.username AS actor_username, target.username AS target_username FROM moderation_actions ma LEFT JOIN users actor ON ma.actor_user_id = actor.id LEFT JOIN users target ON ma.target_user_id = target.id WHERE ma.scope_type = ? AND ma.scope_id = ? ORDER BY ma.created_at DESC LIMIT ? OFFSET ?",++ "SELECT media_id FROM search_media_pushes WHERE kind = ? AND deleted = 0",++ "SELECT message FROM chat_messages WHERE stream_id = ? AND COALESCE(is_deleted, 0) = 0 AND message IS NOT NULL AND message <> '' AND timestamp >= datetime('now', ?) ORDER BY timestamp DESC LIMIT ?",++ "SELECT metadata FROM chat_messages WHERE id = ?",++ "SELECT model, COUNT(*) AS calls, SUM(cost_usd) AS cost_usd, SUM(input_tokens) AS input_tokens, SUM(cached_tokens) AS cached_tokens, SUM(output_tokens) AS output_tokens FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY model ORDER BY cost_usd DESC",++ "SELECT ms.*, (SELECT COUNT(*) FROM streams s WHERE s.managed_stream_id = ms.id) AS session_count, (SELECT MAX(s.ended_at) FROM streams s WHERE s.managed_stream_id = ms.id AND s.ended_at IS NOT NULL) AS last_live_at, (SELECT s.is_live FROM streams s WHERE s.managed_stream_id = ms.id AND s.is_live = 1 LIMIT 1) AS is_currently_live, (SELECT s.id FROM streams s WHERE s.managed_stream_id = ms.id AND s.is_live = 1 LIMIT 1) AS live_session_id FROM managed_streams ms WHERE ms.user_id = ? ORDER BY ms.sort_order ASC, ms.created_at ASC",++ "SELECT ms.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM managed_streams ms JOIN users u ON ms.user_id = u.id WHERE ms.id = ?",++ "SELECT ms.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM managed_streams ms JOIN users u ON ms.user_id = u.id WHERE ms.id = ? AND ms.user_id = ?",++ "SELECT ms.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM managed_streams ms JOIN users u ON ms.user_id = u.id WHERE ms.user_id = ? AND ms.slug = ? COLLATE NOCASE",++ "SELECT ms.*, u.username, u.display_name, u.avatar_url, u.profile_color, u.stream_key AS user_stream_key FROM managed_streams ms JOIN users u ON ms.user_id = u.id WHERE ms.stream_key = ?",++ "SELECT ns.enabled FROM news_settings ns WHERE ns.scope = 'user' AND ns.scope_id = (SELECT user_id FROM streams WHERE id = ?) AND ns.source_id = '_master'",++ "SELECT o.*, u.username, u.display_name FROM streamer_overviews o JOIN users u ON u.id = o.user_id ORDER BY o.generated_at DESC LIMIT ?",++ "SELECT offset_seconds, description, tags, thumbnail_url, captured_at, transcript_json FROM stream_memories WHERE stream_id = ? ORDER BY offset_seconds ASC LIMIT 400",++ "SELECT offset_seconds, description, thumbnail_url FROM stream_memories WHERE stream_id = ? ORDER BY CASE WHEN LOWER(description) LIKE '%person%' OR LOWER(description) LIKE '%man %' OR LOWER(description) LIKE '%woman%' OR LOWER(description) LIKE '%face%' OR LOWER(description) LIKE '%wearing%' OR LOWER(description) LIKE '%headphones%' THEN 0 ELSE 1 END, id DESC LIMIT 4",++ "SELECT offset_seconds, transcript_json FROM stream_memories WHERE stream_id = ? AND transcript_json IS NOT NULL ORDER BY offset_seconds ASC",++ "SELECT overview, overview_short, generated_at FROM streamer_overviews WHERE user_id = ?",++ "SELECT payload, generated_at FROM ai_timeline_cache WHERE user_id = ?",++ "SELECT payload_json, created_at FROM vibe_coding_events WHERE managed_stream_id = ? ORDER BY id DESC LIMIT ?",++ "SELECT persona_json FROM arena_profiles WHERE user_id = ?",++ "SELECT powerchat_count_views FROM restream_destinations WHERE id = ?",++ "SELECT powerchat_relay FROM restream_destinations WHERE id = ?",++ "SELECT powerchat_username FROM powerchat_connections WHERE user_id = ?",++ "SELECT r.*, cr.streamer_id FROM coin_redemptions r JOIN coin_rewards cr ON r.reward_id = cr.id WHERE r.id = ?",++ "SELECT r.*, cr.title as reward_title, cr.cost, cr.icon, cr.color, u.username, u.display_name, u.avatar_url FROM coin_redemptions r JOIN coin_rewards cr ON r.reward_id = cr.id JOIN users u ON r.user_id = u.id WHERE cr.streamer_id = ? AND r.status = 'pending' ORDER BY r.created_at ASC",++ "SELECT r.rowid AS id, r.platform, r.username, r.display_name, r.message_count, r.last_seen, cs.updated_at AS last_update FROM relay_users r LEFT JOIN chat_ai_summaries cs ON cs.scope = 'relay' AND cs.window = 'rolling' AND cs.subject_id = r.rowid WHERE r.last_seen >= ? AND r.message_count >= ? AND (cs.updated_at IS NULL OR cs.updated_at < r.last_seen) ORDER BY (cs.updated_at IS NULL) DESC, r.last_seen DESC LIMIT ?",++ "SELECT r.stream_id FROM stream_recaps r JOIN streams s ON s.id = r.stream_id WHERE r.user_id = ? AND s.started_at >= ? ORDER BY r.created_at DESC LIMIT 1",++ "SELECT r.stream_id, r.json, r.created_at, u.username, u.display_name, u.avatar_url, u.profile_color FROM stream_recaps r JOIN users u ON u.id = r.user_id WHERE (? IS NULL OR r.user_id != ?) AND COALESCE(u.is_banned, 0) = 0 ORDER BY r.created_at DESC LIMIT ?",++ "SELECT recorded_at AS t, viewer_count AS v, COALESCE(chat_messages_5m, 0) AS c FROM viewer_snapshots WHERE stream_id = ? ORDER BY recorded_at ASC",++ "SELECT revision FROM subject_projection WHERE subject_id = ?",++ "SELECT rowid AS id, * FROM relay_users WHERE platform = ? AND username = ?",++ "SELECT rowid AS id, * FROM relay_users WHERE rowid = ?",++ "SELECT rowid AS rid, ai_overview AS ov FROM streams WHERE ai_overview IS NOT NULL AND TRIM(ai_overview) != '' AND (ai_overview_short IS NULL OR ai_overview_short = '')",++ "SELECT rowid AS rid, overview AS ov FROM streamer_overviews WHERE overview IS NOT NULL AND TRIM(overview) != '' AND (overview_short IS NULL OR overview_short = '')",++ "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color FROM streams s JOIN ( SELECT user_id, MAX(ended_at) AS latest_ended_at FROM streams WHERE is_live = 0 AND ended_at IS NOT NULL GROUP BY user_id ) latest ON latest.user_id = s.user_id AND latest.latest_ended_at = s.ended_at JOIN users u ON s.user_id = u.id WHERE s.is_live = 0 AND s.ended_at IS NOT NULL ORDER BY s.ended_at DESC LIMIT ?",++ "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.id AS managed_stream_id, ms.stream_key AS managed_stream_key, ms.browser_mode, ms.streaming_method FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.is_live = 1 ORDER BY s.viewer_count DESC, s.started_at DESC",++ "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.stream_key AS managed_stream_key, ms.title AS managed_stream_title FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.control_config_id = ? AND s.is_live = 1 ORDER BY s.started_at DESC",++ "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.stream_key AS managed_stream_key, ms.title AS managed_stream_title FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.user_id = ? AND s.is_live = 1 ORDER BY s.started_at DESC",++ "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.stream_key AS managed_stream_key, ms.title AS managed_stream_title, ms.id AS managed_stream_ref_id FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.user_id = ? ORDER BY s.created_at DESC LIMIT ?",++ "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.stream_key AS managed_stream_key, ms.title AS managed_stream_title, ms.protocol AS managed_stream_protocol FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.id = ?",++ "SELECT s.*, ms.slug AS managed_stream_slug FROM streams s LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.managed_stream_id = ? AND s.is_live = 1 ORDER BY COALESCE(s.started_at, s.created_at) DESC LIMIT 1",++ "SELECT s.*, u.username AS streamer_username, u.display_name AS streamer_display, u.avatar_url AS streamer_avatar FROM subscriptions s LEFT JOIN users u ON s.streamer_id = u.id WHERE s.subscriber_id = ? ORDER BY s.started_at DESC",++ "SELECT s.*, u.username AS subscriber_username, u.display_name AS subscriber_display, u.avatar_url AS subscriber_avatar FROM subscriptions s LEFT JOIN users u ON s.subscriber_id = u.id WHERE s.streamer_id = ? AND s.status = 'active' ORDER BY s.started_at DESC",++ "SELECT s.id FROM streams s LEFT JOIN openre_sessions o ON o.stream_id = s.id WHERE s.managed_stream_id = ? AND s.is_live = 1 AND s.protocol = 'rtmp' AND o.session_id IS NULL ORDER BY s.id DESC LIMIT 1",++ "SELECT s.id FROM streams s WHERE s.is_live = 0 AND s.ended_at IS NOT NULL AND s.ended_at >= datetime('now', ?) AND s.ended_at <= datetime('now', ?) AND COALESCE(s.duration_seconds, (julianday(s.ended_at) - julianday(s.started_at)) * 86400) >= ? AND NOT EXISTS (SELECT 1 FROM stream_recaps r WHERE r.stream_id = s.id) ORDER BY s.ended_at ASC LIMIT 6",++ "SELECT s.id, COALESCE(ms.stream_key, u.stream_key) AS stream_key FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.is_live = 1 AND s.protocol = 'rtmp'",++ "SELECT s.id, s.title, s.ai_title, s.started_at, s.ended_at, s.created_at, s.duration_seconds, s.ai_overview, s.ai_overview_short, s.thumbnail_url, s.peak_viewers, s.category, (SELECT COUNT(*) FROM stream_memories m WHERE m.stream_id = s.id) AS memory_count FROM streams s WHERE s.user_id = ? AND (s.ai_overview IS NOT NULL OR EXISTS (SELECT 1 FROM stream_memories m WHERE m.stream_id = s.id)) ORDER BY COALESCE(s.started_at, s.created_at) DESC LIMIT 300",++ "SELECT s.id, s.title, s.category, s.started_at, s.ended_at, s.duration_seconds, s.peak_viewers, s.viewer_count, s.thumbnail_url, sa.avg_viewers, sa.unique_chatters, sa.total_messages, sa.clips_created FROM streams s LEFT JOIN stream_analytics sa ON sa.stream_id = s.id WHERE s.user_id = ? AND s.duration_seconds > 0 ORDER BY s.started_at DESC LIMIT ? OFFSET ?",++ "SELECT s.id, s.title, s.category, s.started_at, s.ended_at, s.duration_seconds, s.peak_viewers, s.viewer_count, sa.avg_viewers, sa.unique_chatters, sa.total_messages, sa.total_watch_minutes, sa.new_followers, sa.clips_created, sa.coins_earned FROM streams s LEFT JOIN stream_analytics sa ON sa.stream_id = s.id WHERE s.user_id = ? AND s.started_at >= ? AND s.duration_seconds > 0 ORDER BY s.started_at DESC",++ "SELECT s.id, s.title, s.protocol, s.viewer_count, s.started_at, u.username FROM streams s JOIN users u ON s.user_id = u.id WHERE s.is_live = 1 ORDER BY s.viewer_count DESC LIMIT 20",++ "SELECT s.id, s.title, s.started_at, s.duration_seconds, (SELECT COALESCE(SUM(COALESCE(e.end_sec, e.start_sec + 3) - e.start_sec), 0) FROM stream_timeline_events e WHERE e.stream_id = s.id AND e.kind = 'speech') AS speech_sec, (SELECT COALESCE(SUM(LENGTH(e.text) - LENGTH(REPLACE(e.text, ' ', '')) + 1), 0) FROM stream_timeline_events e WHERE e.stream_id = s.id AND e.kind = 'speech') AS words, (SELECT COUNT(*) FROM arena_mic_moments m WHERE m.stream_id = s.id) AS moments, (SELECT COALESCE(AVG(m.quality), 0) FROM arena_mic_moments m WHERE m.stream_id = s.id) AS avg_q, (SELECT COUNT(*) FROM arena_mic_moments m WHERE m.stream_id = s.id AND m.kind = 'beef_hit') AS hits, (SELECT COUNT(*) FROM arena_beefs b WHERE b.winner_user_id = s.user_id AND b.resolved_at BETWEEN s.started_at AND COALESCE(s.ended_at, s.started_at)) AS wins FROM streams s WHERE s.user_id = ? AND s.duration_seconds > 0 AND s.started_at >= datetime('now', ?) ORDER BY s.started_at DESC LIMIT 14",++ "SELECT s.id, s.title, s.started_at, s.ended_at, s.is_live, s.peak_viewers, s.viewer_count, s.duration_seconds, s.protocol, s.category FROM streams s WHERE s.managed_stream_id = ? AND s.user_id = ? ORDER BY s.started_at DESC LIMIT ?",++ "SELECT s.id, s.user_id, s.managed_stream_id, ms.slug AS slot_slug FROM streams s LEFT JOIN managed_streams ms ON ms.id = s.managed_stream_id WHERE s.id = ?",++ "SELECT s.id, s.user_id, s.started_at FROM streams s WHERE s.is_live = 1 AND EXISTS (SELECT 1 FROM stream_timeline_events e WHERE e.stream_id = s.id AND e.kind = 'speech' AND e.created_at >= datetime('now', '-30 minutes'))",++ "SELECT s.id, s.user_id, s.title, s.category, s.protocol, s.is_nsfw, s.started_at, s.ended_at, s.duration_seconds, s.managed_stream_id, u.username, u.display_name FROM streams s JOIN users u ON u.id = s.user_id WHERE s.id = ?",++ "SELECT s.id, u.stream_key FROM streams s JOIN users u ON s.user_id = u.id WHERE s.is_live = 1 AND s.protocol = 'jsmpeg'",++ "SELECT seq, tbl, op, pk FROM chat_staged_outbox ORDER BY seq LIMIT ?",++ "SELECT seq, tbl, op, pk FROM chat_staged_outbox WHERE tbl = ? ORDER BY seq LIMIT ?",++ "SELECT service_user_id FROM linked_accounts WHERE service = 'network' AND user_id = ?",++ "SELECT session_id, managed_stream_id, stream_id, state, confirmed_at FROM openre_sessions WHERE state = 'live'",++ "SELECT slot, item_id FROM user_equipped WHERE user_id = ?",++ "SELECT slot_clip_notify_enabled FROM managed_streams WHERE id = ?",++ "SELECT slot_clip_recording_enabled FROM managed_streams WHERE id = ?",++ "SELECT slot_powerchat_count_rs_views FROM managed_streams WHERE id = ?",++ "SELECT slot_powerchat_relay FROM managed_streams WHERE id = ?",++ "SELECT slot_vod_recording_enabled FROM managed_streams WHERE id = ?",++ "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name='chat_messages' AND sql IS NOT NULL",++ "SELECT sql FROM sqlite_master WHERE type='table' AND name='channel_sounds'",++ "SELECT sql FROM sqlite_master WHERE type='table' AND name='chat_messages'",++ "SELECT sql FROM sqlite_master WHERE type='table' AND name='emotes'",++ "SELECT sql FROM sqlite_master WHERE type='table' AND name='stream_controls'",++ "SELECT started_at, is_live FROM streams WHERE id = ?",++ "SELECT status, COUNT(*) AS n FROM billing_actions GROUP BY status",++ "SELECT stream_id, json, ai, created_at FROM stream_recaps WHERE user_id = ? ORDER BY created_at DESC LIMIT ?",++ "SELECT streamer_id FROM follows WHERE follower_id = ?",++ "SELECT strftime('%s', sampled_at) AS t, viewers, live_streams FROM viewer_samples WHERE sampled_at >= datetime('now', ?) ORDER BY sampled_at ASC",++ "SELECT subject_id FROM linked_accounts WHERE service = 'network' AND user_id = ?",++ "SELECT subject_id FROM linked_accounts WHERE user_id = ? AND service = 'network' AND subject_id IS NOT NULL ORDER BY id DESC LIMIT 1",++ "SELECT subject_id, service_user_id FROM linked_accounts WHERE service = 'network' AND user_id = ?",++ "SELECT t.*, fu.username AS from_username, fu.display_name AS from_display, tu.username AS to_username, tu.display_name AS to_display FROM transactions t LEFT JOIN users fu ON t.from_user_id = fu.id LEFT JOIN users tu ON t.to_user_id = tu.id WHERE t.from_user_id = ? OR t.to_user_id = ? ORDER BY t.created_at DESC LIMIT ?",++ "SELECT t.*, u.id as uid, u.username, u.display_name, u.role, u.profile_color, u.avatar_url, u.is_banned, u.ban_reason FROM api_tokens t JOIN users u ON t.user_id = u.id WHERE t.token_hash = ? AND t.is_active = 1",++ "SELECT t.*, u.username as author_name FROM themes t LEFT JOIN users u ON t.author_id = u.id WHERE t.id = ?",++ "SELECT t.*, u.username as author_name FROM themes t LEFT JOIN users u ON t.author_id = u.id WHERE t.is_public = 1",++ "SELECT t.*, u.username as author_name FROM themes t LEFT JOIN users u ON t.author_id = u.id WHERE t.slug = ?",++ "SELECT t.*, u.username, u.display_name FROM transactions t JOIN users u ON t.from_user_id = u.id WHERE t.type = 'cashout' AND t.status = 'escrow' ORDER BY t.created_at ASC",++ "SELECT t.amount, t.created_at, fu.username AS from_username, fu.display_name AS from_display, tu.username AS to_username, tu.display_name AS to_display FROM transactions t LEFT JOIN users fu ON fu.id = t.from_user_id JOIN users tu ON tu.id = t.to_user_id WHERE t.type = 'donation' ORDER BY t.id DESC LIMIT 1",++ "SELECT tag_id FROM user_equipped_tag WHERE user_id = ?",++ "SELECT tag_id, source, granted_at FROM user_tags WHERE user_id = ?",++ "SELECT target_user_id, COUNT(*) AS n FROM arena_mic_moments WHERE user_id = ? AND target_user_id IS NOT NULL GROUP BY target_user_id ORDER BY n DESC LIMIT 5",++ "SELECT text FROM arena_mic_moments WHERE user_id = ? AND created_at >= datetime('now', '-6 hours') ORDER BY id DESC LIMIT 40",++ "SELECT text FROM stream_timeline_events WHERE user_id = ? AND kind = 'speech' AND LENGTH(text) BETWEEN 30 AND 160 ORDER BY created_at DESC LIMIT 60",++ "SELECT text FROM translations WHERE key = ?",++ "SELECT text, quality, kind, aimed_at, sec, vod_id FROM arena_mic_moments WHERE stream_id = ? ORDER BY quality DESC, said_at ASC LIMIT 4",++ "SELECT text, start_sec, vod_id FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' AND LENGTH(text) > 15 ORDER BY start_sec DESC LIMIT 5",++ "SELECT text, start_sec, vod_id FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' AND start_sec > ? ORDER BY start_sec ASC LIMIT 100",++ "SELECT text, start_sec, vod_id FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' ORDER BY start_sec DESC LIMIT 14",++ "SELECT thread_id, access_id FROM comment_thread_refs WHERE content_type = ? AND content_id = ?",++ "SELECT thumbnail_url FROM streams WHERE id = ?",++ "SELECT thumbnail_url FROM streams WHERE user_id = ? AND thumbnail_url LIKE 'http%' ORDER BY started_at DESC LIMIT 3",++ "SELECT title, category, is_live, is_nsfw FROM streams WHERE user_id = ? ORDER BY id DESC LIMIT 1",++ "SELECT transcript_attempts AS a FROM clip_ai_state WHERE clip_id = ?",++ "SELECT transcript_attempts AS a FROM vod_ai_state WHERE vod_id = ?",++ "SELECT transcript_partial_json, transcript_progress_sec FROM vod_ai_state WHERE vod_id = ?",++ "SELECT u.id AS user_id FROM users u LEFT JOIN streamer_overviews o ON o.user_id = u.id WHERE EXISTS (SELECT 1 FROM stream_memories m WHERE m.user_id = u.id) AND ( o.user_id IS NULL OR (LENGTH(TRIM(COALESCE(o.overview,''))) >= ? AND o.generated_at <= datetime('now','-12 hours')) OR (LENGTH(TRIM(COALESCE(o.overview,''))) < ? AND o.generated_at <= datetime('now','-1 hours')) ) ORDER BY (o.generated_at IS NULL) DESC, o.generated_at ASC LIMIT ?",++ "SELECT u.id AS user_id, u.username, u.display_name, u.avatar_url, u.profile_color, COUNT(s.id) AS sessions, MAX(s.started_at) AS last_at, MAX(COALESCE(s.peak_viewers, 0)) AS peak_viewers, ROUND(SUM(COALESCE(s.duration_seconds, CASE WHEN s.ended_at IS NOT NULL THEN (julianday(s.ended_at) - julianday(s.started_at)) * 86400 ELSE 0 END)) / 3600.0, 1) AS hours, (SELECT title FROM streams t WHERE t.user_id = u.id AND t.started_at >= ? ORDER BY t.started_at DESC LIMIT 1) AS last_title FROM streams s JOIN users u ON u.id = s.user_id WHERE s.started_at >= ? AND s.is_live = 0 AND COALESCE(u.is_banned, 0) = 0 GROUP BY u.id ORDER BY hours DESC, last_at DESC LIMIT 14",++ "SELECT u.id AS user_id, u.username, u.display_name, u.avatar_url, u.profile_color, MAX(s.ended_at) AS last_online_at, o.overview AS ai_overview, o.overview_short AS ai_overview_short, ( SELECT json_group_array(json_object( 'managed_stream_id', ms2.id, 'slug', ms2.slug, 'title', ms2.title, 'protocol', ms2.protocol, 'last_live_at', (SELECT MAX(s2.ended_at) FROM streams s2 WHERE s2.managed_stream_id = ms2.id AND s2.ended_at IS NOT NULL), 'vod_thumbnail', NULL )) FROM managed_streams ms2 WHERE ms2.user_id = u.id AND EXISTS (SELECT 1 FROM streams sx WHERE sx.managed_stream_id = ms2.id AND sx.ended_at IS NOT NULL) ) AS managed_streams_json FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN streamer_overviews o ON o.user_id = u.id WHERE s.is_live = 0 AND s.ended_at IS NOT NULL GROUP BY u.id ORDER BY last_online_at DESC LIMIT ? OFFSET ?",++ "SELECT u.id AS user_id, u.username, u.display_name, u.avatar_url, u.profile_color, s.title, s.viewer_count, s.started_at FROM streams s JOIN users u ON u.id = s.user_id WHERE s.is_live = 1 AND COALESCE(u.is_banned, 0) = 0 ORDER BY s.viewer_count DESC, s.started_at DESC LIMIT 10",++ "SELECT u.id, u.username FROM chat_messages c JOIN users u ON c.user_id = u.id WHERE c.timestamp >= datetime('now','-14 days') AND COALESCE(u.is_banned,0)=0 AND COALESCE(c.is_deleted,0)=0 GROUP BY u.id ORDER BY COUNT(*) DESC LIMIT 12",++ "SELECT u.id, u.username, u.display_name, u.avatar_url, u.profile_color FROM dm_participants p JOIN users u ON u.id = p.user_id WHERE p.conversation_id = ?",++ "SELECT u.id, u.username, u.display_name, u.avatar_url, u.profile_color, MAX(s.started_at) AS last_live_at, COUNT(s.id) AS sessions, ROUND(SUM(COALESCE(s.duration_seconds, CASE WHEN s.ended_at IS NOT NULL THEN (julianday(s.ended_at) - julianday(s.started_at)) * 86400 ELSE 0 END)) / 3600.0, 1) AS hours, (SELECT COUNT(*) FROM follows f WHERE f.streamer_id = u.id) AS followers, (SELECT COALESCE(ch.ai_category, ch.category) FROM channels ch WHERE ch.user_id = u.id) AS category FROM streams s JOIN users u ON u.id = s.user_id WHERE s.started_at >= datetime('now', ?) AND COALESCE(u.is_banned, 0) = 0 AND u.id != COALESCE(?, -1) GROUP BY u.id HAVING hours >= 0.25 OR sessions >= 2 ORDER BY last_live_at DESC LIMIT 40",++ "SELECT u.id, u.username, u.display_name, u.avatar_url, u.profile_color, b.created_at as blocked_at FROM dm_blocks b JOIN users u ON u.id = b.blocked_id WHERE b.blocker_id = ? ORDER BY b.created_at DESC",++ "SELECT u.id, u.username, u.display_name, u.avatar_url, u.role, u.is_banned, u.ban_reason, u.created_at, COUNT(DISTINCT shared.ip_address) as shared_ip_count, GROUP_CONCAT(DISTINCT shared.ip_address) as shared_ips, MAX(shared.created_at) as last_shared_activity FROM ip_log mine JOIN ip_log shared ON mine.ip_address = shared.ip_address AND (shared.user_id IS NOT NULL OR shared.anon_id != ?) LEFT JOIN users u ON shared.user_id = u.id WHERE mine.anon_id = ? GROUP BY COALESCE(shared.user_id, shared.anon_id) ORDER BY shared_ip_count DESC, last_shared_activity DESC",++ "SELECT u.id, u.username, u.display_name, u.avatar_url, u.role, u.is_banned, u.ban_reason, u.created_at, COUNT(DISTINCT shared.ip_address) as shared_ip_count, GROUP_CONCAT(DISTINCT shared.ip_address) as shared_ips, MAX(shared.created_at) as last_shared_activity FROM ip_log mine JOIN ip_log shared ON mine.ip_address = shared.ip_address AND shared.user_id != ? JOIN users u ON shared.user_id = u.id WHERE mine.user_id = ? GROUP BY shared.user_id ORDER BY shared_ip_count DESC, last_shared_activity DESC",++ "SELECT u.id, u.username, u.display_name, u.bio, COUNT(s.id) AS sessions, ROUND(SUM(COALESCE(s.duration_seconds, CASE WHEN s.ended_at IS NOT NULL THEN (julianday(s.ended_at) - julianday(s.started_at)) * 86400 ELSE 0 END)) / 3600.0, 1) AS hours, MAX(COALESCE(s.peak_viewers, 0)) AS peak_viewers, ROUND(AVG(COALESCE(s.peak_viewers, 0)), 1) AS avg_peak, MAX(s.started_at) AS last_live_at, (SELECT COUNT(*) FROM chat_messages c WHERE c.stream_id IN (SELECT id FROM streams x WHERE x.user_id = u.id AND x.started_at >= datetime('now', ?)) AND COALESCE(c.is_deleted, 0) = 0) AS chat_lines, (SELECT COUNT(*) FROM follows f WHERE f.streamer_id = u.id AND f.created_at >= datetime('now', ?)) AS new_followers, (SELECT COUNT(*) FROM follows f WHERE f.streamer_id = u.id) AS followers, (SELECT COUNT(*) FROM arena_mic_moments m WHERE m.user_id = u.id AND m.said_at >= datetime('now', ?)) AS mic_moments, (SELECT COALESCE(so.overview_short, so.overview) FROM streamer_overviews so WHERE so.user_id = u.id) AS overview, (SELECT ch.ai_category FROM channels ch WHERE ch.user_id = u.id) AS category FROM users u JOIN streams s ON s.user_id = u.id WHERE s.started_at >= datetime('now', ?) AND COALESCE(u.is_banned, 0) = 0 GROUP BY u.id ORDER BY hours DESC LIMIT 40",++ "SELECT u.id, u.username, u.display_name, u.role, u.openvibe_bucks_balance, u.is_banned, u.ban_reason, u.created_at, u.last_seen, COALESCE(c.force_vod_recording_disabled, 0) AS force_vod_recording_disabled FROM users u LEFT JOIN channels c ON c.user_id = u.id",++ "SELECT u.username AS channel, cp.balance AS points FROM channel_points cp JOIN users u ON u.id = cp.streamer_id WHERE cp.user_id = ? AND cp.balance > 0 ORDER BY cp.balance DESC, u.username LIMIT 10",++ "SELECT u.username FROM username_history h JOIN users u ON u.id = h.user_id WHERE h.old_username = ? COLLATE NOCASE ORDER BY h.id DESC LIMIT 1",++ "SELECT u.username, COALESCE(so.overview_short, so.overview) AS ov FROM streamer_overviews so JOIN users u ON so.user_id = u.id WHERE COALESCE(u.is_banned,0)=0 AND so.overview IS NOT NULL ORDER BY so.generated_at DESC LIMIT 8",++ "SELECT u.username, u.display_name FROM easter_egg_solves s JOIN users u ON u.id = s.user_id WHERE s.egg_date = ? ORDER BY s.rowid ASC LIMIT 3",++ "SELECT u.username, u.display_name, SUM(t.amount) AS total FROM transactions t JOIN users u ON u.id = t.from_user_id WHERE t.type = 'donation' AND ((t.stream_id = ?) OR (t.to_user_id = ? AND t.created_at BETWEEN ? AND ?)) GROUP BY t.from_user_id ORDER BY total DESC LIMIT 1",++ "SELECT u.username, u.display_name, u.avatar_url, SUM(c.amount) AS total FROM coin_transactions c JOIN users u ON u.id = c.user_id WHERE c.amount > 0 AND c.created_at >= datetime('now', '-7 days') GROUP BY c.user_id ORDER BY total DESC LIMIT 3",++ "SELECT u.username, u.display_name, u.avatar_url, SUM(t.amount) AS total FROM transactions t JOIN users u ON u.id = t.from_user_id WHERE t.type = 'donation' AND t.created_at >= datetime('now', '-7 days') GROUP BY t.from_user_id ORDER BY total DESC LIMIT 3",++ "SELECT user_id AS id FROM streams WHERE (started_at >= @from AND started_at < @to) OR (ended_at >= @from AND ended_at < @to) UNION SELECT streamer_id FROM follows WHERE created_at >= @from AND created_at < @to UNION SELECT id FROM users WHERE updated_at >= @from AND updated_at < @to UNION SELECT user_id FROM channels WHERE updated_at >= @from AND updated_at < @to",++ "SELECT user_id AS k, overview AS v FROM streamer_overviews WHERE overview LIKE '{%\"description\"%'",++ "SELECT user_id FROM channel_moderators WHERE channel_id = ?",++ "SELECT user_id FROM linked_accounts WHERE service = 'network' AND (subject_id = ? OR service_user_id = ?) ORDER BY subject_id = ? DESC LIMIT 1",++ "SELECT user_id FROM linked_accounts WHERE service = 'network' AND service_user_id = ?",++ "SELECT user_id FROM linked_accounts WHERE service = 'network' AND service_user_id = ? ORDER BY id DESC LIMIT 1",++ "SELECT user_id FROM linked_accounts WHERE service = 'network' AND subject_id = ? ORDER BY id DESC LIMIT 1",++ "SELECT user_id FROM linked_accounts WHERE service = 'network' AND subject_id = ? ORDER BY id LIMIT 1",++ "SELECT user_id FROM streams WHERE id = ?",++ "SELECT user_id, COUNT(*) AS n FROM arena_mic_moments WHERE target_user_id = ? GROUP BY user_id ORDER BY n DESC LIMIT 5",++ "SELECT user_id, service_user_id FROM linked_accounts WHERE service = 'network' AND service_user_id GLOB '[0-9]*' ORDER BY user_id",++ "SELECT user_id, service_user_id FROM linked_accounts WHERE service = 'network' AND subject_id IS NULL AND service_user_id GLOB '[0-9]*' ORDER BY user_id",++ "SELECT user_id, streamer_id, delta FROM channel_points_log WHERE idempotency_key = ?",++ "SELECT user_id, title, current_amount, target_amount FROM donation_goals WHERE is_active = 1 AND target_amount > 0 AND user_id IN (?) ORDER BY (CAST(current_amount AS REAL) / target_amount) DESC",++ "SELECT username FROM users WHERE id = ?",++ "SELECT valid_after_ms FROM token_revocations WHERE subject_id = ?",++ "SELECT value FROM app_state WHERE key = ?",++ "SELECT value FROM site_settings WHERE key = 'bucks_per_usd'",++ "SELECT value FROM site_settings WHERE key = 'powerchat_site_user_id'",++ "SELECT value FROM site_settings WHERE key='relay_users_backfilled'",++ "SELECT viewer_count, chat_messages_5m, recorded_at FROM viewer_snapshots WHERE stream_id = ? ORDER BY recorded_at ASC",++ "SELECT vk.*, u1.username as created_by_name, u2.username as used_by_name FROM verification_keys vk LEFT JOIN users u1 ON vk.created_by = u1.id LEFT JOIN users u2 ON vk.used_by = u2.id ORDER BY vk.created_at DESC",++ "SELECT vod_id AS id, s.* FROM vod_ai_state s WHERE (ai_overview IS NULL OR ai_overview = '') AND (ai_overview_short IS NULL OR ai_overview_short = '' OR ai_overview_short LIKE '%…') ORDER BY vod_id DESC LIMIT ?",++ "SELECT vod_id AS id, s.* FROM vod_ai_state s WHERE ai_transcript_json IS NULL AND (transcript_status IS NULL OR transcript_status IN ('pending','retry')) AND (transcript_next_at IS NULL OR transcript_next_at <= CURRENT_TIMESTAMP) ORDER BY (transcript_status='retry'), vod_id DESC LIMIT ?",++ "SELECT vod_id FROM stream_timeline_events WHERE stream_id = ? AND vod_id IS NOT NULL LIMIT 1",++ "SELECT voice, pitch, speed, gap FROM tts_voice_overrides WHERE identity_key = ?",++ "SELECT winner_user_id FROM arena_beefs WHERE status = 'resolved' AND (a_user_id = ? OR b_user_id = ?) ORDER BY resolved_at DESC LIMIT 20",++ "SELECT workspace_name, machine_name, extension_version, publisher_id, publisher_label, publisher_vendor, publisher_client_type, publisher_client_name, publisher_client_version, publisher_capabilities_json, publisher_depth FROM vibe_coding_sessions WHERE managed_stream_id = ? ORDER BY COALESCE(last_event_at, created_at) DESC, id DESC LIMIT 1",++ "SELECT wt.user_id, u.username, u.display_name, SUM(wt.minutes_watched) as total_minutes FROM watch_time wt JOIN users u ON u.id = wt.user_id JOIN streams s ON wt.stream_id = s.id WHERE s.user_id = ? AND s.started_at >= ? GROUP BY wt.user_id ORDER BY total_minutes DESC LIMIT 20",++ "SELECT xp, level FROM arena_trash_levels WHERE user_id = ?",++ "UPDATE ai_viewer_threads SET state = 'closed', awaiting = NULL, updated_at = CURRENT_TIMESTAMP WHERE channel_user_id = ? AND state = 'open'",++ "UPDATE ai_viewer_threads SET state = 'closed', awaiting = NULL, updated_at = CURRENT_TIMESTAMP WHERE channel_user_id = ? AND state = 'open' AND (updated_at < datetime('now', ?) OR turns >= ?)",++ "UPDATE ai_viewer_threads SET state = 'closed', awaiting = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE api_tokens SET is_active = 0 WHERE id = ? AND user_id = ?",++ "UPDATE api_tokens SET last_used_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE arena_beefs SET clock_until = ? WHERE id = ?",++ "UPDATE arena_beefs SET headline = ? WHERE id = ?",++ "UPDATE arena_beefs SET result_headline = ? WHERE id = ?",++ "UPDATE arena_beefs SET status = 'resolved', resolution = ?, winner_user_id = ?, resolved_at = CURRENT_TIMESTAMP, on_clock = NULL, clock_until = NULL, feed_json = ?, upset = ?, result_headline = ? WHERE id = ? AND status = 'open'",++ "UPDATE arena_trash_levels SET best_line = ?, best_line_vod_id = ?, best_line_sec = ?, best_line_score = ? WHERE user_id = ?",++ "UPDATE billing_actions SET attempts = attempts + 1, updated_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE billing_actions SET live_ref = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND action = 'media_charge'",++ "UPDATE billing_actions SET status = 'done', http_status = 200, billing_ref = ?, response_json = ?, error = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE billing_actions SET status = ?, http_status = ?, error = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE camera_profiles SET name = ?, onvif_url = ?, username = ?, password_hash = ?, pan_speed = ?, tilt_speed = ?, zoom_speed = ?, is_active = ?, last_connected = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE channel_ai_bots SET msg_count = msg_count + 1, last_active_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE channel_moderation_settings SET sub_only = ? WHERE channel_id = ?",++ "UPDATE channel_moderation_settings SET tts_max_length = ? WHERE channel_id = ?",++ "UPDATE channel_points SET balance = balance + ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ? AND streamer_id = ? AND balance >= ?",++ "UPDATE channel_sounds SET command = ? WHERE channel_owner_id = ? AND command = ?",++ "UPDATE channel_sounds SET emote_code = ? WHERE channel_owner_id = ? AND command = ?",++ "UPDATE channel_sounds SET emote_code = ? WHERE channel_owner_id = ? AND emote_code = ?",++ "UPDATE channels SET ai_category = ? WHERE user_id = ?",++ "UPDATE channels SET ai_overview_pref = 'hide' WHERE hide_ai_overview = 1 AND (ai_overview_pref IS NULL OR ai_overview_pref = 'auto')",++ "UPDATE channels SET clips_allow_creator_delete = ? WHERE id = ?",++ "UPDATE chat_dual_read_stats SET last_mismatch_at = CURRENT_TIMESTAMP, last_mismatch = ? WHERE tbl = ?",++ "UPDATE chat_messages SET channel_user_id = (SELECT s.user_id FROM streams s WHERE s.id = chat_messages.stream_id) WHERE stream_id IS NOT NULL AND channel_user_id IS NULL",++ "UPDATE chat_messages SET is_deleted = 1, deleted_by = ?, deleted_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE chat_messages SET is_deleted = 1, deleted_by = ?, deleted_at = CURRENT_TIMESTAMP WHERE is_global = 1 AND timestamp >= ? AND timestamp <= ? AND is_deleted = 0",++ "UPDATE chat_messages SET is_deleted = 1, deleted_by = ?, deleted_at = CURRENT_TIMESTAMP WHERE stream_id = ? AND timestamp >= ? AND timestamp <= ? AND is_deleted = 0",++ "UPDATE chat_messages SET message = ?, metadata = ? WHERE id = ?",++ "UPDATE chat_messages SET metadata = ? WHERE id = ?",++ "UPDATE clip_ai_state SET ai_overview = ?, ai_overview_short = ?, ai_transcript_json = COALESCE(?, ai_transcript_json) WHERE clip_id = ?",++ "UPDATE clip_ai_state SET ai_transcript_json = ? WHERE clip_id = ?",++ "UPDATE clip_ai_state SET clip_notified = 1, clip_notify_at = NULL WHERE clip_id = ?",++ "UPDATE clip_ai_state SET clip_notify_at = CURRENT_TIMESTAMP WHERE clip_id = ? AND COALESCE(clip_notified,0) = 0",++ "UPDATE clip_ai_state SET clip_notify_at = datetime('now', ?) WHERE clip_id = ? AND COALESCE(clip_notified,0) = 0",++ "UPDATE clip_ai_state SET transcript_attempts = COALESCE(transcript_attempts,0)+1 WHERE clip_id = ?",++ "UPDATE clip_ai_state SET transcript_status='retry', transcript_attempts=0, transcript_next_at=NULL WHERE transcript_status='failed' AND COALESCE(transcript_error,'') NOT LIKE 'media reported%' AND COALESCE(transcript_error,'') NOT LIKE 'no audio stream%'",++ "UPDATE clip_ai_state SET transcript_status='retry', transcript_next_at=NULL WHERE transcript_status='processing'",++ "UPDATE clips SET visibility = CASE WHEN is_public = 1 THEN 'public' ELSE 'unlisted' END",++ "UPDATE coin_redemptions SET status = ?, resolved_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE coin_rewards SET redemption_count = redemption_count + 1 WHERE id = ?",++ "UPDATE dm_conversations SET is_group = 1 WHERE id = ?",++ "UPDATE dm_conversations SET name = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE dm_conversations SET updated_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE dm_participants SET last_read_at = CURRENT_TIMESTAMP WHERE conversation_id = ? AND user_id = ?",++ "UPDATE donation_goals SET current_amount = ? WHERE id = ?",++ "UPDATE donation_goals SET current_amount = ?, is_active = 0, reached_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE donation_goals SET is_active = 0 WHERE id = ?",++ "UPDATE linked_accounts SET service = 'network' WHERE service = 'hobotools' AND user_id NOT IN (SELECT user_id FROM linked_accounts WHERE service = 'network')",++ "UPDATE linked_accounts SET subject_id = ? WHERE service = 'network' AND user_id = ? AND service_user_id = ? AND (subject_id IS NULL OR subject_id != ?)",++ "UPDATE linked_accounts SET subject_id = ? WHERE service = 'network' AND user_id = ? AND service_user_id = ? AND subject_id IS NULL",++ "UPDATE managed_streams SET broadcast_settings = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND user_id = ?",++ "UPDATE managed_streams SET ingest_authority = 'live', updated_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE managed_streams SET ingest_authority = 'openre', openre_stream_id = ?, stream_key = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE managed_streams SET stream_key = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE managed_streams SET streaming_method = 'browser' WHERE protocol = 'webrtc'",++ "UPDATE managed_streams SET streaming_method = 'cli' WHERE protocol = 'jsmpeg'",++ "UPDATE managed_streams SET streaming_method = 'rtmp' WHERE protocol = 'rtmp'",++ "UPDATE media_requests SET queue_position = ? WHERE id = ?",++ "UPDATE opencoin_admin_grants SET balance = ? WHERE id = ?",++ "UPDATE openre_sessions SET confirmed_at = CURRENT_TIMESTAMP WHERE session_id = ?",++ "UPDATE openre_sessions SET state = 'detached', updated_at = CURRENT_TIMESTAMP WHERE session_id = ?",++ "UPDATE pending_ip_messages SET status = 'approved', reviewed_by = ? WHERE channel_id = ? AND ip_address = ? AND status = 'pending'",++ "UPDATE pending_ip_messages SET status = 'denied', reviewed_by = ? WHERE channel_id = ? AND ip_address = ? AND status = 'pending'",++ "UPDATE pending_ip_messages SET status = ?, reviewed_by = ? WHERE id = ?",++ "UPDATE pending_ip_messages SET status = ?, reviewed_by = ? WHERE id = ? AND channel_id = ?",++ "UPDATE platform_connections SET access_token = ?, refresh_token = COALESCE(?, refresh_token), token_expires_at = ?, scope = COALESCE(?, scope), updated_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE platform_connections SET platform_user_id = ?, platform_username = ?, channel_url = ?, access_token = ?, refresh_token = COALESCE(?, refresh_token), token_expires_at = ?, scope = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE powerchat_connections SET access_token = ?, refresh_token = COALESCE(?, refresh_token), token_expires_at = ?, scope = COALESCE(?, scope), last_error = NULL, updated_at = CURRENT_TIMESTAMP WHERE user_id = ?",++ "UPDATE powerchat_connections SET last_error = ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ?",++ "UPDATE restream_destinations SET consecutive_failures = 0, cooldown_until = NULL, last_error = NULL WHERE id = ?",++ "UPDATE restream_destinations SET consecutive_failures = ?, last_error = ?, last_failed_at = CURRENT_TIMESTAMP, cooldown_until = datetime('now', ?) WHERE id = ?",++ "UPDATE restream_destinations SET managed_stream_id = ? WHERE user_id = ? AND managed_stream_id IS NULL",++ "UPDATE site_settings SET value = '100' WHERE key = 'bucks_per_usd'",++ "UPDATE site_settings SET value = 'alex' WHERE key = 'powerchat_sandbox_username' AND value = 'n8admin'",++ "UPDATE site_settings SET value = ? WHERE key = 'powerchat_scopes' AND value IN (?, ?, ?)",++ "UPDATE site_settings SET value = ? WHERE key = 'powerchat_site_tip_username' AND (value IS NULL OR value = '')",++ "UPDATE site_settings SET value = ?, updated_at = CURRENT_TIMESTAMP WHERE key = ?",++ "UPDATE stream_analytics SET clips_created = ? WHERE stream_id = ?",++ "UPDATE stream_controls SET camera_id = NULL WHERE camera_id = ?",++ "UPDATE stream_recaps SET announced_at = CURRENT_TIMESTAMP WHERE stream_id = ?",++ "UPDATE stream_timeline_events AS t SET vod_id = (SELECT s.vod_id FROM stream_timeline_events s WHERE s.stream_id = t.stream_id AND s.vod_id IS NOT NULL LIMIT 1) WHERE t.vod_id IS NULL AND EXISTS (SELECT 1 FROM stream_timeline_events s WHERE s.stream_id = t.stream_id AND s.vod_id IS NOT NULL)",++ "UPDATE stream_timeline_events SET vod_id = ? WHERE stream_id = ? AND vod_id IS NULL",++ "UPDATE streamer_overviews SET overview = ? WHERE user_id = ?",++ "UPDATE streamer_overviews SET overview_short = ? WHERE rowid = ?",++ "UPDATE streams SET ai_category = ?, ai_tags = ? WHERE id = ?",++ "UPDATE streams SET ai_overview = ?, ai_overview_short = ? WHERE id = ?",++ "UPDATE streams SET ai_overview_short = ? WHERE rowid = ?",++ "UPDATE streams SET ai_title = ? WHERE id = ?",++ "UPDATE streams SET call_mode = ? WHERE id = ?",++ "UPDATE streams SET control_config_id = ? WHERE id = ?",++ "UPDATE streams SET control_config_id = NULL WHERE id = ?",++ "UPDATE streams SET is_live = 0, ended_at = CURRENT_TIMESTAMP, duration_seconds = CAST((julianday(CURRENT_TIMESTAMP) - julianday(started_at)) * 86400 AS INTEGER) WHERE id = ?",++ "UPDATE streams SET is_live = 1, started_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE streams SET last_heartbeat = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE streams SET last_heartbeat = CURRENT_TIMESTAMP WHERE id = ? AND is_live = 1",++ "UPDATE streams SET last_heartbeat = CURRENT_TIMESTAMP WHERE is_live = 1",++ "UPDATE streams SET managed_stream_id = ? WHERE user_id = ? AND managed_stream_id IS NULL",++ "UPDATE streams SET managed_stream_id = NULL WHERE managed_stream_id = ?",++ "UPDATE streams SET started_at = CURRENT_TIMESTAMP, last_heartbeat = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE streams SET tags = ? WHERE id = ?",++ "UPDATE streams SET thumbnail_url = ? WHERE id = ?",++ "UPDATE streams SET viewer_count = ?, peak_viewers = MAX(peak_viewers, ?) WHERE id = ?",++ "UPDATE subscriptions SET status=?, is_active=?, cancel_at_period_end=?, current_period_end=COALESCE(?, current_period_end), updated_at=CURRENT_TIMESTAMP WHERE id=?",++ "UPDATE subscriptions SET tier=?, provider=?, provider_ref=COALESCE(?, provider_ref), price_cents=?, currency=?, status=?, is_active=?, current_period_end=?, auto_renew=COALESCE(?, auto_renew), cancel_at_period_end=0, updated_at=CURRENT_TIMESTAMP WHERE id=?",++ "UPDATE themes SET downloads = downloads + 1 WHERE id = ?",++ "UPDATE themes SET name = ?, mode = ?, description = ?, variables = ?, preview_colors = ?, tags = ?, is_builtin = 1, is_public = 1 WHERE slug = ?",++ "UPDATE tips_deliveries SET claimed_at = ? WHERE idempotency_key = ? AND state = 'pending' AND claimed_at = ?",++ "UPDATE tips_deliveries SET state = 'done', response_json = ? WHERE idempotency_key = ?",++ "UPDATE transactions SET paypal_transaction_id = ? WHERE id = ?",++ "UPDATE transactions SET status = ? WHERE id = ?",++ "UPDATE user_themes SET theme_id = ?, custom_variables = ?, is_custom = ? WHERE user_id = ?",++ "UPDATE users SET avatar_url = ?, avatar_paste_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE users SET bio = ? WHERE id = ?",++ "UPDATE users SET display_name = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",++ "UPDATE users SET email = NULL WHERE email IS NOT NULL",++ "UPDATE users SET is_banned = 0, ban_reason = NULL WHERE id = ?",++ "UPDATE users SET is_banned = 1, ban_reason = ? WHERE id = ?",++ "UPDATE users SET is_banned = 1, ban_reason = ? WHERE id = ? AND is_banned = 0",++ "UPDATE users SET is_owner = 1, role = 'admin' WHERE LOWER(username) = ? AND is_owner != 1",++ "UPDATE users SET openvibe_bucks_balance = openvibe_bucks_balance + ? WHERE id = ?",++ "UPDATE users SET openvibe_bucks_balance = openvibe_bucks_balance - ? WHERE id = ?",++ "UPDATE users SET openvibe_bucks_cashout_balance = openvibe_bucks_cashout_balance + ? WHERE id = ?",++ "UPDATE users SET openvibe_bucks_cashout_balance = openvibe_bucks_cashout_balance - ? WHERE id = ?",++ "UPDATE users SET openvibe_coins_balance = openvibe_coins_balance + ? WHERE id = ?",++ "UPDATE users SET openvibe_coins_balance = openvibe_coins_balance - ? WHERE id = ? AND openvibe_coins_balance >= ?",++ "UPDATE users SET profile_color = ? WHERE id = ?",++ "UPDATE users SET role = 'admin' WHERE username = ?",++ "UPDATE users SET role = 'global_mod' WHERE role = 'mod'",++ "UPDATE users SET role = ? WHERE id = ?",++ "UPDATE users SET username = ? WHERE id = ?",++ "UPDATE verification_keys SET status = 'revoked' WHERE id = ? AND status = 'active'",++ "UPDATE verification_keys SET status = 'used', used_by = ?, used_at = CURRENT_TIMESTAMP WHERE key = ? AND status = 'active'",++ "UPDATE vibe_coding_sessions SET last_event_at = CURRENT_TIMESTAMP WHERE managed_stream_id = ? AND session_key = ?",++ "UPDATE vibe_coding_sessions SET status = 'ended', ended_at = CURRENT_TIMESTAMP, last_event_at = CURRENT_TIMESTAMP WHERE managed_stream_id = ? AND session_key = ?",++ "UPDATE vod_ai_state SET ai_overview = ?, ai_overview_short = ? WHERE vod_id = ?",++ "UPDATE vod_ai_state SET ai_transcript_json = ?, transcript_partial_json = NULL, transcript_progress_sec = 0 WHERE vod_id = ?",++ "UPDATE vod_ai_state SET transcript_attempts = COALESCE(transcript_attempts,0)+1 WHERE vod_id = ?",++ "UPDATE vod_ai_state SET transcript_partial_json = ?, transcript_progress_sec = ? WHERE vod_id = ?",++ "UPDATE vod_ai_state SET transcript_status='retry', transcript_attempts=0, transcript_next_at=NULL WHERE transcript_status='failed' AND COALESCE(transcript_error,'') NOT LIKE 'media reported%' AND COALESCE(transcript_error,'') NOT LIKE 'no audio stream%'",++ "UPDATE vod_ai_state SET transcript_status='retry', transcript_next_at=NULL WHERE transcript_status='processing'",++ "UPDATE vods SET visibility = CASE WHEN is_public = 1 THEN 'public' ELSE 'private' END",++ "UPDATE watch_time SET coins_earned = coins_earned + ? WHERE id = ?",++ "UPDATE watch_time SET minutes_watched = minutes_watched + 1, last_heartbeat = CURRENT_TIMESTAMP WHERE id = ?"++ ]++}nnew file mode 100644@@@ -0,0 +1,136 @@++'use strict';++/**++ * N-1 (roadmap WS-P task 11, ADR-016): during a deploy, and for the 24-hour mixed-version window after++ * it, open tabs run the previous release's client against this release's server, and a previous-release++ * process can still be working on the database this release migrated. Both must keep working.++ *++ * - An N-1 client against the N server: test/fixtures/n-1/client.json is every call the previous++ * release's client code makes (found statically, made concrete with seeded values) with what that++ * release answered: status, JSON or not, and the response fields the old client reads with their++ * types. This checkout boots (test/n-1/service.js) and must answer each call compatibly: any 2xx++ * for a 2xx, the same status otherwise (a 5xx only needs the route), JSON where there was JSON,++ * every read field still there with a type it had. The run sits inside openvibe-shared's++ * mixed-version matrix (release-compat assertMixedVersion) with both releases' manifests.++ * - N-1 SQL against the N schema: test/fixtures/n-1/worker.json holds the previous release's schema++ * (and migration ledger, and what it creates on first use) and every statement it ran or has as a++ * literal. The database this test boots on is created from that schema, so this release's++ * migrations run over it; afterwards every N-1 statement must still prepare (no table or column it++ * names is gone), and no N-1 INSERT may miss a column this release made NOT NULL without a default.++ *++ * The fixtures come from the release in production: `npm run n-1:record` after each deploy (see++ * scripts/n-1-record.js). This test needs no git history and no network.++ *++ * node test/n-1.test.js++ */++const assert = require('assert');++const fs = require('fs');++const os = require('os');++const path = require('path');++const Database = require('better-sqlite3');++const compat = require('openvibe-shared/release-compat');++const h = require('./n-1/harness');++const svc = require('./n-1/service');++++const ROOT = path.join(__dirname, '..');++const FIX = path.join(__dirname, 'fixtures', 'n-1');++const client = JSON.parse(fs.readFileSync(path.join(FIX, 'client.json'), 'utf8'));++const worker = JSON.parse(fs.readFileSync(path.join(FIX, 'worker.json'), 'utf8'));++++let failures = 0;++async function check(name, fn) {++ try { await fn(); console.log(` ✓ ${name}`); } catch (err) { failures++; console.log(` ✗ ${name}\n ${String(err.message).split('\n').join('\n ')}`); }++}++++(async () => {++ const age = Math.round((Date.now() - Date.parse(client.recorded_at)) / 86400000);++ console.log(`n-1: ${svc.service} N-1 = ${client.release.slice(0, 12)}, recorded ${client.recorded_at.slice(0, 10)} (${age} d ago); ${client.calls.length} calls, ${worker.statements.length} statements`);++++ console.log('n-1: the harness');++ await check('finds call sites, their method and template, and skips computed paths', () => {++ const files = [{ name: 'a.js', text: "api('/streams'); api(`/users/${encodeURIComponent(u.username)}/follow`, { method: 'POST', body: {} }); fetch(`${API}/api/x?limit=${n}`); api(path); fetch('https://elsewhere/x'); api(`/b/${on ? 'mute' : 'unmute'}`, { method: 'post' });" }];++ const { calls, dynamic } = h.extractCalls({ files, callers: [{ name: 'api', prefix: '/api' }, { name: 'fetch' }], strip: ['API'] });++ assert.deepStrictEqual(calls.map((c) => `${c.method} ${c.template}`), ['POST /api/b/{on ? \'mute\' : \'unmute\'}', 'GET /api/streams', 'POST /api/users/{encodeURIComponent(u.username)}/follow', 'GET /api/x?limit={n}']);++ assert.strictEqual(dynamic, 1);++ const samples = [[/id\)*$/i, '1'], [/name\)*$/i, 'n1star']];++ assert.deepStrictEqual(calls.map((c) => h.concretePath(c.parts, samples)), ['/api/b/mute', '/api/streams', '/api/users/n1star/follow', '/api/x?limit=']);++ });++ await check('reads: the key paths the client names; a gone or retyped one is a problem, an empty list or null parent is not', () => {++ const ids = h.clientIdentifiers(['x.streams.map((s) => s.title); const { total } = d; s?.viewer_count']);++ const reads = h.readsOf({ streams: [{ title: 'a', viewer_count: 1, secret: 'k' }], total: 3, other: 1 }, ids);++ assert.deepStrictEqual(reads, { streams: 'array', 'streams[]': 'object', 'streams[].title': 'string', 'streams[].viewer_count': 'number', total: 'number' });++ assert.deepStrictEqual(h.readProblems(reads, { streams: [{ title: 'b', viewer_count: 2 }], total: 1 }), []);++ assert.deepStrictEqual(h.readProblems(reads, { streams: [], total: 0 }), []);++ assert.deepStrictEqual(h.readProblems(reads, { streams: [{ title: 'b', viewer_count: '2' }] }), ['streams[].viewer_count was number, now string', 'total is gone']);++ assert.ok(h.statusCompatible(200, 201) && h.statusCompatible(502, 503) && !h.statusCompatible(401, 403) && !h.statusCompatible(502, 404) && !h.statusCompatible(200, 404));++ });++ await check('SQL: a dropped column fails the prepare; a new NOT NULL column without a default fails an old INSERT', () => {++ const d = new Database(':memory:');++ d.exec('CREATE TABLE t (id INTEGER PRIMARY KEY, a TEXT, b TEXT NOT NULL, c TEXT NOT NULL DEFAULT \'x\')');++ assert.deepStrictEqual(h.prepareProblems(d, ['SELECT a, gone FROM t']).map((p) => p.error), ['no such column: gone']);++ assert.deepStrictEqual(h.insertProblems(d, ['INSERT INTO t (a, b) VALUES (?, ?)', 'INSERT INTO t (a) VALUES (?)']).map((p) => p.error), ['t.b is NOT NULL without a default, and N-1 does not set it']);++ d.close();++ });++++ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-n-1-test-'));++ const dbPath = path.join(tmp, 'db', `${svc.service}.db`);++ const dataDir = path.join(tmp, 'data');++ fs.mkdirSync(path.dirname(dbPath), { recursive: true });++ fs.mkdirSync(dataDir, { recursive: true });++ let server = null;++ try {++ console.log(`n-1: this release on a database N-1 created`);++ await check('N-1\'s schema, ledger and first-use tables load, and this release migrates and seeds it', () => {++ const d = new Database(dbPath);++ d.transaction(() => {++ for (const ddl of worker.schema) d.exec(ddl);++ for (const [table, rows] of Object.entries(worker.ledger || {})) {++ for (const row of rows) {++ const cols = Object.keys(row);++ d.prepare(`INSERT INTO "${table}" (${cols.map((c) => `"${c}"`).join(', ')}) VALUES (${cols.map(() => '?').join(', ')})`).run(...cols.map((c) => row[c]));++ }++ }++ for (const ddl of worker.lazy || []) d.exec(ddl);++ })();++ d.pragma(`user_version = ${Number(worker.user_version) || 0}`);++ d.close();++ svc.seed({ dir: ROOT, dbPath, dataDir });++ });++ server = await svc.boot({ dir: ROOT, dbPath, dataDir });++ const manifest = await (await fetch(`${server.url}/release.json`)).json();++++ console.log('n-1: the N-1 client against this server');++ await check(`every call the N-1 client makes is answered compatibly (${client.calls.length} requests, in openvibe-shared's mixed-version matrix)`, async () => {++ const broken = [];++ const n1Client = async (url) => {++ for (const rec of client.calls) {++ const got = await h.send(url, { method: rec.method, path: rec.path, headers: server.headers(rec.auth), body: rec.method === 'GET' ? undefined : {} });++ const problems = h.callProblems(rec, got);++ if (problems.length) broken.push(`${rec.auth} ${rec.method} ${rec.path} (${rec.from.join(', ')}): ${problems.join('; ')}`);++ }++ if (svc.checkExtra && client.extra) broken.push(...await svc.checkExtra({ server, url, extra: client.extra }));++ if (broken.length) throw new Error(`${broken.length} broken:\n${h.summarize(broken)}`);++ };++ await compat.assertMixedVersion({ releases: [++ { name: 'N-1', manifest: client.manifest, client: n1Client },++ { name: 'N', manifest, server: async () => ({ url: server.url }) },++ ] });++ });++ await server.close();++ server = null;++++ console.log('n-1: N-1\'s SQL on the schema this release migrated');++ await check(`every statement N-1 runs still prepares (${worker.statements.length}), and none of its INSERTs misses a new required column`, () => {++ const d = new Database(dbPath, { readonly: true });++ const problems = [...h.prepareProblems(d, worker.statements), ...h.insertProblems(d, worker.statements)];++ d.close();++ assert.deepStrictEqual(problems.map((p) => `${p.error}: ${p.sql.slice(0, 160)}`), [], 'N-1 SQL that breaks on this schema (expand first, contract a release later: ADR-028)');++ });++ } finally {++ if (server) { console.log(server.log().slice(-1500)); await server.close(); }++ fs.rmSync(tmp, { recursive: true, force: true });++ }++++ if (failures) { console.log(`\nn-1: ${failures} check(s) failed`); process.exit(1); }++ console.log('\nn-1: all checks passed');++ process.exit(0);++})().catch((err) => { console.error(err.stack || err.message); process.exit(1); });nnew file mode 100644@@@ -0,0 +1,523 @@++'use strict';++/**++ * N-1 harness (roadmap WS-P task 11, ADR-016's mixed-version window): the previous release's client++ * against this release's server, and the previous release's SQL against this release's schema.++ * The same file in Live, Chat, Media and Community; each repository's test/n-1/service.js says what++ * its clients are, how to boot and seed it, and where its SQL lives.++ *++ * scripts/n-1-record.js <ref> at release time: checks out <ref> (the release in production, which++ * is N-1 for the next deploy) into a temporary worktree, boots it, and++ * writes test/fixtures/n-1/{client,worker}.json++ * test/n-1.test.js in npm test and CI: boots this checkout on a database created with++ * N-1's schema and replays the fixture; no git history, no network++ *++ * Client calls are found statically in the N-1 client code (the call sites of `api(...)`, `fetch(...)`++ * and the service's other wrappers, with a literal or template path), made concrete with sample values,++ * and replayed against the booted N-1 server: every call anonymously, reads again signed in. What the++ * fixture keeps per call is what an old client depends on: the status, whether the answer is JSON,++ * and the response fields it reads (the JSON key paths whose every key the N-1 client code names, with++ * their types).++ *++ * SQL is every statement N-1 prepared while it served those calls, plus every literal SQL string in its++ * server code that prepares on its own schema. The N-1 schema is its sqlite_master after the replay.++ */++const fs = require('fs');++const os = require('os');++const path = require('path');++const { execFileSync } = require('child_process');++++// ── Client calls: static extraction ──────────────────────────++++/** Reads a JS string/template literal at src[i] (a quote). → { end, parts: [{ text } | { expr }] } or null. */++function readLiteral(src, i) {++ const q = src[i];++ if (q !== '\'' && q !== '"' && q !== '`') return null;++ const parts = [];++ let text = '';++ let j = i + 1;++ while (j < src.length) {++ const c = src[j];++ if (c === '\\') { text += unescapeChar(src[j + 1]); j += 2; continue; }++ if (c === q) { if (text) parts.push({ text }); return { end: j + 1, parts }; }++ if (q !== '`' && c === '\n') return null;++ if (q === '`' && c === '$' && src[j + 1] === '{') {++ if (text) parts.push({ text });++ text = '';++ const close = skipBalanced(src, j + 1, '{', '}');++ if (close < 0) return null;++ parts.push({ expr: src.slice(j + 2, close).trim() });++ j = close + 1;++ continue;++ }++ text += c;++ j++;++ }++ return null;++}++++function unescapeChar(c) {++ return { n: '\n', t: '\t', r: '\r', '0': '\0' }[c] ?? c ?? '';++}++++/** Index of the bracket closing the one at src[i] (open), skipping strings and templates; -1 if none. */++function skipBalanced(src, i, open, close) {++ let depth = 0;++ for (let j = i; j < src.length; j++) {++ const c = src[j];++ if (c === '\'' || c === '"' || c === '`') {++ const lit = readLiteral(src, j);++ if (lit) { j = lit.end - 1; continue; }++ }++ if (c === open) depth++;++ else if (c === close) { depth--; if (depth === 0) return j; }++ }++ return -1;++}++++/** Top-level comma-separated arguments of a call whose '(' is at src[i]. */++function callArgs(src, i) {++ const end = skipBalanced(src, i, '(', ')');++ if (end < 0) return null;++ const args = [];++ let depth = 0; let start = i + 1;++ for (let j = i + 1; j < end; j++) {++ const c = src[j];++ if (c === '\'' || c === '"' || c === '`') { const lit = readLiteral(src, j); if (lit) { j = lit.end - 1; continue; } }++ if (c === '(' || c === '{' || c === '[') depth++;++ else if (c === ')' || c === '}' || c === ']') depth--;++ else if (c === ',' && depth === 0) { args.push(src.slice(start, j).trim()); start = j + 1; }++ }++ const last = src.slice(start, end).trim();++ if (last) args.push(last);++ return args;++}++++const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');++++/**++ * Every call site of the service's request functions with a literal or template path.++ * callers: [{ name: 'api', prefix: '/api', method?: 'POST', methodArg?: 1 }]++ * strip: template expressions dropped when a path starts with them (e.g. 'API', 'location.origin')++ * → [{ method, template, parts, from: [file] }], plus { dynamic } calls with no literal path++ */++function extractCalls({ files, callers, strip = [], keep = (p) => true }) {++ const found = new Map();++ let dynamic = 0;++ for (const { name: file, text: src } of files) {++ for (const caller of callers) {++ const re = new RegExp(`(?<![\\w$.])${esc(caller.name)}\\s*\\(`, 'g');++ let m;++ while ((m = re.exec(src))) {++ const open = m.index + m[0].length - 1;++ const args = callArgs(src, open);++ if (!args || !args.length) continue;++ const lit = /^['"`]/.test(args[0]) ? readLiteral(args[0], 0) : null;++ if (!lit || lit.end !== args[0].length) { dynamic++; continue; }++ let parts = lit.parts.slice();++ while (parts.length && parts[0].expr && strip.includes(parts[0].expr)) parts.shift();++ if (!parts.length || !parts[0].text || !parts[0].text.startsWith('/') || parts[0].text.startsWith('//')) {++ if (parts.length && parts[0].expr) dynamic++;++ continue;++ }++ parts = caller.prefix ? [{ text: caller.prefix }, ...parts] : parts;++ // `/api${path}`: a wrapper passing a path through, not a call.++ if (parts.some((p, k) => p.expr && k > 0 && parts[k - 1].text != null && !parts.slice(0, k).some((x) => x.text && x.text.includes('?')) && /[A-Za-z0-9]$/.test(parts[k - 1].text))) { dynamic++; continue; }++ const template = parts.map((p) => (p.text != null ? p.text : `{${p.expr}}`)).join('').replace(/^([^?#]*)\/\//, '$1/');++ let method = caller.method || 'GET';++ if (caller.methodArg != null) {++ const a = args[caller.methodArg];++ const ml = a && /^['"`]/.test(a) ? readLiteral(a, 0) : null;++ if (ml && ml.parts.length === 1 && ml.parts[0].text) method = ml.parts[0].text;++ } else {++ const opts = args.slice(1).join(',');++ const mm = /\bmethod\s*:\s*['"`]([A-Za-z]+)['"`]/.exec(opts);++ if (mm) method = mm[1];++ }++ method = method.toUpperCase();++ if (!keep(template.split('?')[0], method)) continue;++ const key = `${method} ${template}`;++ if (!found.has(key)) found.set(key, { method, template, parts, from: new Set() });++ found.get(key).from.add(file);++ }++ }++ }++ const calls = [...found.values()].map((c) => ({ ...c, from: [...c.from].sort() }))++ .sort((a, b) => (a.template < b.template ? -1 : a.template > b.template ? 1 : a.method < b.method ? -1 : 1));++ return { calls, dynamic };++}++++/**++ * A concrete path for a template: each ${expr} becomes the first sample whose pattern matches the++ * expression text, else '1' in the path and '' in the query.++ */++function concretePath(parts, samples = []) {++ let out = '';++ for (const p of parts) {++ if (p.text != null) { out += p.text; continue; }++ const inQuery = out.includes('?');++ // `${on ? 'mute' : 'unmute'}`: the first branch.++ const branch = /\?\s*(['"`])([^'"`]*)\1\s*:/.exec(p.expr);++ if (branch) { out += branch[2]; continue; }++ const hit = samples.find(([re]) => re.test(p.expr));++ let v = hit ? hit[1] : (inQuery ? '' : '1');++ if (typeof v === 'function') v = v(p.expr, inQuery);++ out += inQuery ? String(v) : encodeURIComponent(String(v)).replace(/%2F/gi, '/');++ }++ out = out.replace(/^([^?#]*)\/\/+/, '$1/').replace(/[?&]+$/, '').replace(/\?&+/, '?');++ return out;++}++++/** Property names the client code reads: `.name`, `?.name`, `['name']`, 'name' literals, destructured names. */++function clientIdentifiers(texts) {++ const ids = new Set();++ for (const t of texts) {++ for (const m of t.matchAll(/\??\.\s*([A-Za-z_$][\w$]*)/g)) ids.add(m[1]);++ for (const m of t.matchAll(/['"`]([A-Za-z_$][\w$]*)['"`]/g)) ids.add(m[1]);++ for (const m of t.matchAll(/\{([^{}()]*)\}\s*(?:=[^=>]|\)\s*=>|\s*\))/g)) {++ for (const part of m[1].split(',')) {++ const id = /^\s*(?:\.\.\.)?\s*([A-Za-z_$][\w$]*)/.exec(part);++ if (id) ids.add(id[1]);++ }++ }++ }++ return ids;++}++++// ── Response shapes ──────────────────────────────────────────++++function typeOf(v) {++ if (v === null) return 'null';++ if (Array.isArray(v)) return 'array';++ return typeof v;++}++++/**++ * The key paths of a JSON value → { 'a': 'object', 'a.b': 'string', 'list[]': 'object', 'list[].x': 'number' }.++ * Array elements are merged: a path is present when any element has it; its types are all it takes.++ */++function flatten(value, maxDepth = 5) {++ const out = {};++ const add = (p, t) => { if (!out[p]) out[p] = new Set(); out[p].add(t); };++ const walk = (v, prefix, depth) => {++ if (depth > maxDepth) return;++ if (Array.isArray(v)) {++ for (const el of v) { add(`${prefix}[]`, typeOf(el)); walk(el, `${prefix}[]`, depth + 1); }++ } else if (v && typeof v === 'object') {++ for (const [k, x] of Object.entries(v)) {++ const p = prefix ? `${prefix}.${k}` : k;++ add(p, typeOf(x));++ walk(x, p, depth + 1);++ }++ }++ };++ walk(value, '', 0);++ return out;++}++++/** The key paths of `value` an old client reads: every key along the path is one the client names. */++function readsOf(value, ids) {++ const flat = flatten(value);++ const reads = {};++ for (const [p, types] of Object.entries(flat)) {++ const keys = p.split('.').map((k) => k.replace(/\[\]$/g, '')).filter(Boolean);++ if (!keys.length || !keys.every((k) => ids.has(k))) continue;++ const t = [...types].filter((x) => x !== 'null').sort();++ reads[p] = t.length ? t.join('|') : 'null';++ }++ return reads;++}++++/** What `reads` (recorded from N-1) finds missing or retyped in `value` (from N). */++function readProblems(reads, value) {++ const flat = flatten(value);++ const problems = [];++ // A path under an array N answers empty (or null) cannot be read by the old client either.++ const emptyArray = (p) => {++ const segs = p.split('.');++ for (let i = 1; i <= segs.length; i++) {++ const head = segs.slice(0, i).join('.');++ if (!head.endsWith('[]')) continue;++ const arr = head.slice(0, -2);++ if (!flat[head] && (arr === '' ? Array.isArray(value) : flat[arr] && !flat[arr].has('object'))) return true;++ }++ return false;++ };++ const nullParent = (p) => {++ const segs = p.split('.');++ for (let i = 1; i < segs.length; i++) {++ const head = segs.slice(0, i).join('.').replace(/\[\]$/, '');++ if (flat[head] && [...flat[head]].every((t) => t === 'null')) return true;++ }++ return false;++ };++ for (const [p, want] of Object.entries(reads)) {++ if (emptyArray(p) || nullParent(p)) continue;++ const have = flat[p];++ if (!have) { problems.push(`${p} is gone`); continue; }++ if (want === 'null') continue;++ const now = [...have].filter((t) => t !== 'null');++ if (!now.length) continue;++ const was = want.split('|');++ if (!now.some((t) => was.includes(t))) problems.push(`${p} was ${want}, now ${now.join('|')}`);++ }++ return problems;++}++++/** Whether N's status keeps N-1's promise: any 2xx for a 2xx, the same code otherwise; a 5xx only needs the route. */++function statusCompatible(was, now) {++ if (was >= 200 && was < 300) return now >= 200 && now < 300;++ if (was >= 500) return now !== 404 && now !== 405;++ return was === now;++}++++// ── Replaying calls ──────────────────────────────────────────++++function kindOf(contentType, text) {++ const ct = String(contentType || '').toLowerCase();++ if (ct.includes('text/event-stream')) return 'sse';++ if (ct.includes('json')) return 'json';++ if (ct.includes('html')) return 'html';++ if (!text) return 'empty';++ if (ct.startsWith('text/')) return 'text';++ return 'other';++}++++/**++ * Sends one call. → { status, kind, json, location }. Redirects are not followed (they may leave the++ * sandbox); an event stream is not read past its headers.++ */++async function send(baseUrl, { method, path: p, headers = {}, body }, { timeoutMs = 8000 } = {}) {++ const ac = new AbortController();++ const timer = setTimeout(() => ac.abort(), timeoutMs);++ try {++ const hasBody = body !== undefined && method !== 'GET' && method !== 'HEAD';++ const res = await fetch(new URL(p, baseUrl), {++ method,++ redirect: 'manual',++ signal: ac.signal,++ headers: { accept: 'application/json', ...(hasBody ? { 'content-type': 'application/json' } : {}), ...headers },++ body: hasBody ? JSON.stringify(body) : undefined,++ });++ const ct = res.headers.get('content-type');++ if (/event-stream/i.test(ct || '')) { ac.abort(); return { status: res.status, kind: 'sse', json: null }; }++ const text = await res.text();++ const kind = kindOf(ct, text);++ let json = null;++ if (kind === 'json') { try { json = JSON.parse(text); } catch { json = null; } }++ const loc = res.headers.get('location');++ return { status: res.status, kind, json, location: loc ? loc.replace(/^https?:\/\/[^/]+/, '') : null };++ } catch (err) {++ return { status: 0, kind: 'error', json: null, error: err.name === 'AbortError' ? 'timeout' : err.message };++ } finally {++ clearTimeout(timer);++ }++}++++/**++ * The request list for a set of extracted calls: every call anonymously, GETs also signed in.++ * Reads first, then writes (with an empty JSON body), so a write cannot change what a read sees.++ */++function requestsFor(calls, samples, { signedIn = true } = {}) {++ const seen = new Set();++ const reqs = [];++ for (const c of calls) {++ const p = concretePath(c.parts, samples);++ for (const auth of c.method === 'GET' && signedIn ? ['anon', 'user'] : ['anon']) {++ const key = `${c.method} ${p} ${auth}`;++ if (seen.has(key)) continue;++ seen.add(key);++ reqs.push({ method: c.method, path: p, auth, from: c.from });++ }++ }++ const rank = (r) => (r.method === 'GET' ? 0 : 1);++ return reqs.sort((a, b) => rank(a) - rank(b));++}++++/** The failures of one replayed call against its recorded outcome. */++function callProblems(rec, got) {++ const problems = [];++ if (got.status === 0) return [`no answer (${got.error})`];++ if (!statusCompatible(rec.status, got.status)) problems.push(`answered ${got.status}, N-1 answered ${rec.status}`);++ else if (rec.kind === 'json' && got.kind !== 'json' && rec.status < 500) problems.push(`answered ${got.kind}, N-1 answered JSON`);++ if (rec.location && got.location && rec.status >= 300 && rec.status < 400 && got.location.split('?')[0] !== rec.location.split('?')[0]) problems.push(`redirects to ${got.location}, N-1 to ${rec.location}`);++ if (rec.reads && got.json != null && got.status >= 200 && got.status < 300) problems.push(...readProblems(rec.reads, got.json));++ return problems;++}++++// ── SQL ──────────────────────────────────────────────────────++++const SQL_START = /^\s*(SELECT|INSERT|UPDATE|DELETE|REPLACE|WITH)\b/i;++++/** Every plain JS string literal in the files (a template only without ${}), comments skipped. */++function stringLiterals(files) {++ const out = [];++ for (const { text } of files) {++ for (let i = 0; i < text.length; i++) {++ const c = text[i];++ if (c === '/' && text[i + 1] === '/') { const nl = text.indexOf('\n', i); i = nl < 0 ? text.length : nl; continue; }++ if (c === '/' && text[i + 1] === '*') { const e = text.indexOf('*/', i + 2); i = e < 0 ? text.length : e + 1; continue; }++ if (c !== '\'' && c !== '"' && c !== '`') continue;++ const lit = readLiteral(text, i);++ if (!lit) continue;++ i = lit.end - 1;++ if (lit.parts.length === 1 && lit.parts[0].text != null) out.push(lit.parts[0].text);++ }++ }++ return out;++}++++/** The literals that read like one SQL statement (DML). */++function sqlLiterals(files) {++ const out = new Set();++ for (const t of stringLiterals(files)) {++ const sql = normalizeSql(t);++ if (SQL_START.test(sql) && /\b(FROM|INTO|SET|VALUES)\b/i.test(sql)) out.add(sql);++ }++ return [...out];++}++++/** SQL without its comments (-- to the end of a line, and block comments), outside quoted text. */++function stripSqlComments(sql) {++ let out = '';++ const s = String(sql);++ for (let i = 0; i < s.length; i++) {++ const c = s[i];++ if (c === '\'' || c === '"') {++ const j = s.indexOf(c, i + 1);++ const end = j < 0 ? s.length : j + 1;++ out += s.slice(i, end); i = end - 1; continue;++ }++ if (c === '-' && s[i + 1] === '-') { const nl = s.indexOf('\n', i); i = nl < 0 ? s.length : nl - 1; continue; }++ if (c === '/' && s[i + 1] === '*') { const e = s.indexOf('*/', i + 2); i = e < 0 ? s.length : e + 1; out += ' '; continue; }++ out += c;++ }++ return out;++}++++function normalizeSql(sql) {++ return stripSqlComments(sql).replace(/\s+/g, ' ').trim();++}++++/**++ * The DDL N-1 runs itself when it first needs a table or column (a module's CREATE TABLE IF NOT EXISTS,++ * a boot-time ALTER TABLE … ADD COLUMN) that `db` does not have yet. Each statement is applied to `db`++ * as it is found to apply; → the applied statements, tables first, in order.++ */++function lazyDDL(files, db) {++ const tables = () => new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((r) => r.name));++ const columns = (t) => { try { return new Set(db.prepare(`PRAGMA table_xinfo("${t}")`).all().map((c) => c.name.toLowerCase())); } catch { return new Set(); } };++ const lits = stringLiterals(files).map(stripSqlComments);++ const applied = [];++ const apply = (ddl) => { try { db.exec(ddl); applied.push(ddl); return true; } catch { return false; } };++ for (const text of lits) {++ for (const m of text.matchAll(/CREATE\s+TABLE\s+IF\s+NOT\s+EXISTS\s+["`]?(\w+)["`]?\s*\(/gi)) {++ // A rebuild's scratch table (x_new) is the migration's own business, not a table N-1 uses.++ if (tables().has(m[1]) || /_(new|old|tmp|backup)$/i.test(m[1])) continue;++ const end = skipBalanced(text, m.index + m[0].length - 1, '(', ')');++ if (end >= 0) apply(normalizeSql(text.slice(m.index, end + 1)));++ }++ }++ for (const text of lits) {++ const m = /^\s*ALTER\s+TABLE\s+["`]?(\w+)["`]?\s+ADD\s+(?:COLUMN\s+)?["`]?(\w+)["`]?[^;]*$/i.exec(text.trim().replace(/;$/, ''));++ if (!m || !tables().has(m[1]) || columns(m[1]).has(m[2].toLowerCase())) continue;++ apply(normalizeSql(text).replace(/;$/, ''));++ }++ return applied;++}++++/** Every file under dir with one of the extensions, as { name (relative to root), text }. */++function readTree(root, dirs, exts = ['.js']) {++ const out = [];++ const walk = (d) => {++ let entries = [];++ try { entries = fs.readdirSync(d, { withFileTypes: true }); } catch { return; }++ for (const e of entries) {++ const p = path.join(d, e.name);++ if (e.isDirectory()) { if (e.name !== 'node_modules' && !e.name.startsWith('.')) walk(p); } else if (exts.some((x) => e.name.endsWith(x))) out.push({ name: path.relative(root, p), text: fs.readFileSync(p, 'utf8') });++ }++ };++ for (const d of dirs) {++ const abs = path.join(root, d);++ if (fs.existsSync(abs) && fs.statSync(abs).isFile()) out.push({ name: d, text: fs.readFileSync(abs, 'utf8') });++ else walk(abs);++ }++ return out.sort((a, b) => (a.name < b.name ? -1 : 1));++}++++/** The schema as DDL, tables first (no internal or FTS shadow tables), then indexes, triggers and views. */++function schemaDDL(db) {++ const shadow = new Set(db.prepare('PRAGMA table_list').all().filter((t) => t.type === 'shadow').map((t) => t.name));++ const rows = db.prepare("SELECT type, name, sql FROM sqlite_master WHERE sql IS NOT NULL AND name NOT LIKE 'sqlite_%'").all()++ .filter((r) => !shadow.has(r.name));++ const order = { table: 0, index: 1, view: 2, trigger: 3 };++ return rows.sort((a, b) => order[a.type] - order[b.type] || (a.name < b.name ? -1 : 1)).map((r) => r.sql);++}++++/** Prepares every statement on db (a missing table or column fails the prepare). → [{ sql, error }] */++function prepareProblems(db, statements) {++ const problems = [];++ for (const sql of statements) {++ try { db.prepare(sql); } catch (e) { problems.push({ sql, error: e.message }); }++ }++ return problems;++}++++/**++ * INSERTs of N-1 that N would refuse: a column N made NOT NULL without a default that the INSERT does++ * not name. → [{ sql, error }]++ */++function insertProblems(db, statements) {++ const problems = [];++ const cols = new Map();++ const required = (table) => {++ if (!cols.has(table)) {++ let info = [];++ try { info = db.prepare(`PRAGMA table_xinfo("${table.replace(/"/g, '""')}")`).all(); } catch { info = []; }++ const pkInt = info.filter((c) => c.pk).length === 1 && info.find((c) => c.pk && /^INTEGER$/i.test(c.type));++ cols.set(table, info.filter((c) => c.notnull && c.dflt_value == null && !c.hidden && !(pkInt && c.pk)).map((c) => c.name));++ }++ return cols.get(table);++ };++ for (const sql of statements) {++ const m = /^\s*(?:INSERT|REPLACE)(?:\s+OR\s+\w+)?\s+INTO\s+["`]?(\w+)["`]?\s*\(([^)]*)\)\s*(VALUES|SELECT)/i.exec(sql);++ if (!m) continue;++ const named = new Set(m[2].split(',').map((c) => c.trim().replace(/^["`]|["`]$/g, '').toLowerCase()));++ const missing = required(m[1]).filter((c) => !named.has(c.toLowerCase()));++ if (missing.length) problems.push({ sql, error: `${m[1]}.${missing.join(', ')} is NOT NULL without a default, and N-1 does not set it` });++ }++ return problems;++}++++// ── Git ──────────────────────────────────────────────────────++++/** A detached worktree of `ref` in a temp directory sharing root's node_modules → { dir, sha, remove() }. */++function worktree(root, ref) {++ const sha = execFileSync('git', ['-C', root, 'rev-parse', '--verify', `${ref}^{commit}`], { encoding: 'utf8' }).trim();++ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-n-1-'));++ const dir = path.join(tmp, 'release');++ execFileSync('git', ['-C', root, 'worktree', 'add', '--detach', dir, sha], { stdio: 'ignore' });++ fs.symlinkSync(path.join(root, 'node_modules'), path.join(dir, 'node_modules'), 'dir');++ return {++ dir, sha,++ remove() {++ try { execFileSync('git', ['-C', root, 'worktree', 'remove', '--force', dir], { stdio: 'ignore' }); } catch { /* */ }++ fs.rmSync(tmp, { recursive: true, force: true });++ },++ };++}++++// ── Reporting ────────────────────────────────────────────────++++function summarize(problems, max = 40) {++ const lines = problems.slice(0, max);++ if (problems.length > max) lines.push(`… and ${problems.length - max} more`);++ return lines.join('\n');++}++++module.exports = {++ readLiteral, callArgs, extractCalls, concretePath, clientIdentifiers,++ flatten, readsOf, readProblems, statusCompatible,++ send, requestsFor, callProblems,++ stringLiterals, sqlLiterals, stripSqlComments, normalizeSql, lazyDDL, readTree, schemaDDL, prepareProblems, insertProblems,++ worktree, summarize,++};nnew file mode 100644@@@ -0,0 +1,28 @@++'use strict';++/**++ * Loaded with `node -r` into a release booted by test/n-1/service.js (N-1 at record time, N in the++ * test), from that release's directory:++ * - N1_SQL_OUT: every SQL text the process prepares or executes on the main database (DB_PATH) is++ * written there as JSON when it exits;++ * - the restore-drill sandbox (LIVE_DRILL: no outbound connection, program, listener or timer) is++ * kept, but its read-only guard is lifted, so the old client's writes reach their routes.++ */++const fs = require('fs');++const path = require('path');++++const cwd = process.cwd();++const out = process.env.N1_SQL_OUT;++if (out) {++ const Database = require(path.join(cwd, 'node_modules', 'better-sqlite3'));++ const main = path.resolve(process.env.DB_PATH || '');++ const seen = new Set();++ const note = (db, sql) => { try { if (path.resolve(db.name) === main) seen.add(String(sql)); } catch { /* */ } };++ const prepare = Database.prototype.prepare;++ Database.prototype.prepare = function (sql, ...rest) { note(this, sql); return prepare.call(this, sql, ...rest); };++ const exec = Database.prototype.exec;++ Database.prototype.exec = function (sql, ...rest) { note(this, sql); return exec.call(this, sql, ...rest); };++ process.on('exit', () => { try { fs.writeFileSync(out, JSON.stringify([...seen])); } catch { /* */ } });++}++++const drill = require(path.join(cwd, 'server', 'drill'));++drill.readOnly = (req, res, next) => next();nnew file mode 100644@@@ -0,0 +1,128 @@++'use strict';++/**++ * Live's side of the N-1 harness (test/n-1/harness.js): its clients, how a release boots and is++ * seeded, where its SQL lives. Used by scripts/n-1-record.js (on N-1, in a temporary worktree) and by++ * test/n-1.test.js (on this checkout), so both boot and seed the same way.++ *++ * A release boots as a real `node server/index.js` in the restore-drill sandbox (LIVE_DRILL: loopback++ * only, no outbound connection, program, listener or background job; Media and Community look down),++ * with test/n-1/preload.js lifting the drill's read-only guard so writes reach their routes. Sign-in is++ * a Network RS256 token signed with a key made for the run (DATA_DIR/keys).++ */++const crypto = require('crypto');++const fs = require('fs');++const net = require('net');++const path = require('path');++const { spawn, spawnSync } = require('child_process');++const { readTree } = require('./harness');++++const PRELOAD = path.join(__dirname, 'preload.js');++const ISSUER = 'https://openvibe.network';++++// Served by OpenVibe.Chat on openvibe.live (Chat's deploy/nginx/openvibe.live-chat.locations.conf):++// Live's chat widget and messenger are Chat's N-1 clients, checked there.++const CHAT_PATHS = /^\/(api\/(chat|dm|tts)\/|api\/sounds(\/|$)|ws\/)/;++++function freePort() {++ return new Promise((resolve) => { const s = net.createServer().listen(0, '127.0.0.1', () => { const p = s.address().port; s.close(() => resolve(p)); }); });++}++++/** A clean environment: nothing from the caller's shell reaches the release but PATH and HOME. */++function baseEnv(extra) {++ return { PATH: process.env.PATH, HOME: process.env.HOME, TMPDIR: process.env.TMPDIR || '/tmp', NODE_ENV: 'test', ...extra };++}++++const SEED = `++ console.log = () => {}; console.warn = () => {};++ const db = require('./server/db/database');++ db.initDb();++ const d = db.getDb();++ const star = Number(db.createUser({ username: 'n1star', display_name: 'N1 Star', password_hash: 'x', stream_key: 'n1starkey' }).lastInsertRowid);++ const fan = Number(db.createUser({ username: 'n1fan', display_name: 'N1 Fan', password_hash: 'x', stream_key: 'n1fankey' }).lastInsertRowid);++ d.prepare("UPDATE users SET role = 'streamer', bio = 'Seeded for the N-1 test' WHERE id = ?").run(star);++ db.ensureChannel(star); db.ensureChannel(fan);++ d.prepare("INSERT INTO streams (user_id, title, category, protocol, is_live, viewer_count, started_at, last_heartbeat) VALUES (?, 'N-1 live stream', 'tech', 'webrtc', 1, 2, datetime('now', '-10 minutes'), datetime('now'))").run(star);++ d.prepare("INSERT INTO streams (user_id, title, category, protocol, is_live, started_at, ended_at) VALUES (?, 'N-1 past stream', 'irl', 'webrtc', 0, datetime('now', '-2 days'), datetime('now', '-2 days', '+1 hour'))").run(star);++ db.followUser(fan, star);++ d.prepare("INSERT INTO chat_messages (stream_id, user_id, username, message) VALUES (1, ?, 'n1fan', 'hello from N-1')").run(fan);++ db.close();++`;++++module.exports = {++ service: 'live',++++ /** The browser client: the SPA and the standalone pages. */++ clientFiles(dir) {++ return readTree(dir, ['public/js', 'public/fragments', 'public/obs', 'public/index.html', 'public/kiosk.html',++ 'public/media-player.html', 'public/popout-chat.html', 'public/whip-publisher.html', 'public/banned.html',++ 'public/live-notify.js', 'public/service-worker.js'], ['.js', '.html']);++ },++ callers: [++ { name: 'api', prefix: '/api' },++ { name: 'apiSWR', prefix: '/api' },++ { name: 'dmApi', prefix: '/api/dm' },++ { name: 'fetch' },++ { name: 'mpApi' },++ { name: 'ownerAction', methodArg: 1 },++ { name: 'patchJson', method: 'PATCH' },++ { name: 'navigator.sendBeacon', method: 'POST' },++ ],++ strip: ['API', 'location.origin', 'window.location.origin'],++ keep: (pathname) => !CHAT_PATHS.test(pathname),++ /** Values for template expressions, first match wins: the seeded streamer, ids of seeded rows. */++ samples: [++ [/id\)*$/i, '1'],++ [/offset|cursor|before|after|since/i, '0'],++ [/limit|count|size|per_?page|max/i, '5'],++ [/page\)*$/i, '1'],++ [/user_?name|login|handle|channel|slug|streamer|name\)*$/i, 'n1star'],++ [/^q$|query|search|term/i, 'n1'],++ ],++++ sqlDirs: ['server'],++ ledgerTables: ['schema_migrations'],++++ /** Seeds a database with the release in `dir` (its initDb migrates first). */++ seed({ dir, dbPath, dataDir }) {++ const r = spawnSync(process.execPath, ['-e', SEED], { cwd: dir, encoding: 'utf8', timeout: 120000, env: baseEnv({ DB_PATH: dbPath, DATA_DIR: dataDir }) });++ if (r.status !== 0) throw new Error(`seeding failed:\n${String(r.stderr || '').slice(-2000)}`);++ },++++ /** Boots the release in `dir` → { url, headers(auth), close() }. */++ async boot({ dir, dbPath, dataDir, sqlOut = '' }) {++ const keys = crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } });++ fs.mkdirSync(path.join(dataDir, 'keys'), { recursive: true });++ fs.writeFileSync(path.join(dataDir, 'keys', 'openvibe-tools-public.pem'), keys.publicKey);++ const port = await freePort();++ const child = spawn(process.execPath, ['-r', PRELOAD, 'server/index.js'], {++ cwd: dir,++ env: baseEnv({ LIVE_DRILL: '1', DB_PATH: dbPath, DATA_DIR: dataDir, HOST: '127.0.0.1', PORT: String(port), N1_SQL_OUT: sqlOut }),++ stdio: ['ignore', 'pipe', 'pipe'],++ });++ let log = '';++ child.stdout.on('data', (c) => { log = (log + c).slice(-20000); });++ child.stderr.on('data', (c) => { log = (log + c).slice(-20000); });++ const exited = new Promise((resolve) => child.on('exit', resolve));++ const url = `http://127.0.0.1:${port}`;++ for (let i = 0; ; i++) {++ if (child.exitCode != null) throw new Error(`the release in ${dir} exited while booting:\n${log.slice(-3000)}`);++ try { const r = await fetch(`${url}/api/ready`); if (r.status === 200) break; } catch { /* not listening yet */ }++ if (i > 300) { child.kill('SIGKILL'); throw new Error(`the release in ${dir} did not become ready:\n${log.slice(-3000)}`); }++ await new Promise((r) => setTimeout(r, 100));++ }++ const jwt = require('jsonwebtoken');++ const token = jwt.sign({ sub: 'n1-network-user', username: 'n1star', display_name: 'N1 Star', role: 'streamer' },++ keys.privateKey, { algorithm: 'RS256', issuer: ISSUER, expiresIn: '1h' });++ return {++ url,++ log: () => log,++ headers: (auth) => (auth === 'user' ? { authorization: `Bearer ${token}` } : {}),++ async close() {++ if (child.exitCode == null) child.kill('SIGTERM');++ const t = setTimeout(() => { if (child.exitCode == null) child.kill('SIGKILL'); }, 5000);++ await exited;++ clearTimeout(t);++ },++ };++ },++};
@@ -4,7 +4,8 @@ on:
@@ -4,7 +4,8 @@ on:
branches: [main]
branches: [main]
pull_request:
pull_request:
jobs:
jobs:
- # install, node --check, npm test (with the home page size budgets) and openvibe-contracts-check:
+ # install, node --check, npm test (with the home page size budgets and test/n-1.test.js: the previous
+ # release's client and SQL against this one, from test/fixtures/n-1) and openvibe-contracts-check:
# the shared test job (Track Q).
# the shared test job (Track Q).
test:
test:
uses: OpenVibers/OpenVibe.Shared/.github/workflows/test.yml@423fe519b5fe9594cc81b670e5ba5cb9f21f6176
uses: OpenVibers/OpenVibe.Shared/.github/workflows/test.yml@423fe519b5fe9594cc81b670e5ba5cb9f21f6176
@@ -123,3 +123,24 @@ npm test # unit, security, migrations, deploy simulation
@@ -123,3 +123,24 @@ npm test # unit, security, migrations, deploy simulation
BASE=http://127.0.0.1:3000 npm run test:browser # needs a running server and Chrome
BASE=http://127.0.0.1:3000 npm run test:browser # needs a running server and Chrome
deploy/scripts/post-deploy-check.sh # on the host after a deploy
deploy/scripts/post-deploy-check.sh # on the host after a deploy
```
```
+
+## N-1: the previous release against this one
+
+For 24 hours after a deploy (ADR-016) open tabs run the previous release's client against the new
+server, and a previous-release process may still be working on the database the new one migrated.
+`test/n-1.test.js` (in `npm test`, so in CI) checks both from fixtures recorded from the release in
+production:
+
+- `test/fixtures/n-1/client.json`: every call the previous release's client code makes, with the
+ status, JSON-ness and the response fields it reads. This checkout boots in the drill sandbox on a
+ database created with the previous schema, answers each call compatibly, and keeps every read field.
+- `test/fixtures/n-1/worker.json`: the previous schema, migration ledger and every SQL statement that
+ release ran or has as a literal. After this release's migrations each must still prepare, and no
+ old INSERT may miss a new NOT NULL column.
+
+After each deploy, record the release now in production as the next release's N-1 and commit it:
+
+```bash
+npm run n-1:record # from HEAD (the deployed commit)
+npm run n-1:record -- <sha> # or the release production's /release.json names
+```
@@ -13,7 +13,8 @@
@@ -13,7 +13,8 @@
"test:powerchat": "for f in test/powerchat-*.test.js; do node \"$f\" || exit 1; done",
"test:powerchat": "for f in test/powerchat-*.test.js; do node \"$f\" || exit 1; done",
"test:browser": "node test/browser/smoke.js",
"test:browser": "node test/browser/smoke.js",
"perf:page": "node scripts/perf/measure-page.js",
"perf:page": "node scripts/perf/measure-page.js",
- "perf:budget": "node scripts/perf/check-budgets.js"
+ "perf:budget": "node scripts/perf/check-budgets.js",
+ "n-1:record": "node scripts/n-1-record.js"
},
},
"keywords": [
"keywords": [
"streaming",
"streaming",
new file mode 100644
new file mode 100644
@@ -0,0 +1,99 @@
@@ -0,0 +1,99 @@
+#!/usr/bin/env node
+'use strict';
+/**
+ * Records the N-1 fixtures (roadmap WS-P task 11) from a release: what its client calls and reads,
+ * and what SQL it runs on which schema. test/n-1.test.js replays them against this checkout.
+ *
+ * npm run n-1:record # from HEAD: run it right after a deploy, from the deployed commit,
+ * # so the fixture is N-1 for the next release
+ * npm run n-1:record -- <ref> # from another commit (e.g. the sha in production's /release.json)
+ *
+ * The release is checked out into a temporary git worktree (sharing node_modules), seeded and booted
+ * the way test/n-1/service.js says, and every call its client code makes (test/n-1/harness.js finds
+ * them) is replayed against it. Writes test/fixtures/n-1/client.json and worker.json; commit them.
+ */
+const fs = require('fs');
+const os = require('os');
+const path = require('path');
+const Database = require('better-sqlite3');
+const h = require('../test/n-1/harness');
+const svc = require('../test/n-1/service');
+
+const ROOT = path.join(__dirname, '..');
+const OUT = path.join(ROOT, 'test', 'fixtures', 'n-1');
+
+(async () => {
+ const ref = process.argv[2] || 'HEAD';
+ const wt = h.worktree(ROOT, ref);
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-n-1-record-'));
+ const dbPath = path.join(tmp, 'db', `${svc.service}.db`);
+ const dataDir = path.join(tmp, 'data');
+ const sqlOut = path.join(tmp, 'sql.json');
+ fs.mkdirSync(path.dirname(dbPath), { recursive: true });
+ fs.mkdirSync(dataDir, { recursive: true });
+ let server = null;
+ try {
+ console.log(`n-1:record: ${svc.service} at ${wt.sha.slice(0, 12)} (${ref})`);
+
+ // 1. The client: its calls, and the names it reads.
+ const files = svc.clientFiles(wt.dir);
+ const { calls, dynamic } = h.extractCalls({ files, callers: svc.callers, strip: svc.strip, keep: svc.keep });
+ const ids = h.clientIdentifiers(files.map((f) => f.text));
+ console.log(` client: ${files.length} files, ${calls.length} call sites with a path (${dynamic} with a computed one, not replayed)`);
+
+ // 2. The release, seeded and booted; every call replayed.
+ await svc.seed({ dir: wt.dir, dbPath, dataDir });
+ server = await svc.boot({ dir: wt.dir, dbPath, dataDir, sqlOut });
+ const manifest = await (await fetch(`${server.url}/release.json`)).json();
+ const recorded = [];
+ let unanswered = 0; let noRoute = 0;
+ for (const req of h.requestsFor(calls, svc.samples, { signedIn: svc.signedIn !== false })) {
+ const got = await h.send(server.url, { method: req.method, path: req.path, headers: server.headers(req.auth), body: req.method === 'GET' ? undefined : {} });
+ if (got.status === 0) { unanswered++; continue; }
+ // No route for this path on N-1 itself (a sample value its route does not take, or a dead call):
+ // nothing N has to keep.
+ if (got.status === 404 && got.kind !== 'json') { noRoute++; continue; }
+ const rec = { method: req.method, path: req.path, auth: req.auth, status: got.status, kind: got.kind };
+ if (got.location) rec.location = got.location;
+ if (got.json != null && got.status >= 200 && got.status < 300) {
+ const reads = h.readsOf(got.json, ids);
+ if (Object.keys(reads).length) rec.reads = reads;
+ }
+ rec.from = req.from;
+ recorded.push(rec);
+ }
+ const extra = svc.recordExtra ? await svc.recordExtra({ server, dir: wt.dir, files, ids }) : null;
+ await server.close();
+ server = null;
+ console.log(` replayed: ${recorded.length} requests (left out: ${unanswered} unanswered, ${noRoute} with no route on N-1)`);
+
+ // 3. The schema N-1 left, and the SQL it runs on it.
+ const db = new Database(dbPath);
+ const schema = h.schemaDDL(db);
+ const ledger = {};
+ for (const t of svc.ledgerTables || []) {
+ try { ledger[t] = db.prepare(`SELECT * FROM "${t}"`).all(); } catch { /* not there */ }
+ }
+ const userVersion = db.pragma('user_version', { simple: true });
+ const serverFiles = h.readTree(wt.dir, svc.sqlDirs);
+ // What N-1 creates itself on first use or at a full boot: in production it exists before N deploys.
+ const lazy = h.lazyDDL(serverFiles, db);
+ const ran = fs.existsSync(sqlOut) ? JSON.parse(fs.readFileSync(sqlOut, 'utf8')) : [];
+ const candidates = [...new Set([...ran.map(h.normalizeSql).filter((s) => /^\s*(SELECT|INSERT|UPDATE|DELETE|REPLACE|WITH)\b/i.test(s)), ...h.sqlLiterals(serverFiles)])].sort();
+ const failing = new Set(h.prepareProblems(db, candidates).map((p) => p.sql));
+ const statements = candidates.filter((s) => !failing.has(s));
+ db.close();
+ console.log(` sql: ${statements.length} statements (${ran.length} ran, ${failing.size} candidates that do not prepare on N-1's own schema left out), ${schema.length} schema objects, ${lazy.length} created on first use`);
+
+ fs.mkdirSync(OUT, { recursive: true });
+ const head = { service: svc.service, release: wt.sha, recorded_at: new Date().toISOString(), note: 'N-1 fixture: written by `npm run n-1:record`, replayed by test/n-1.test.js' };
+ fs.writeFileSync(path.join(OUT, 'client.json'), `${JSON.stringify({ ...head, manifest, dynamic_calls: dynamic, calls: recorded, ...(extra ? { extra } : {}) }, null, 1)}\n`);
+ fs.writeFileSync(path.join(OUT, 'worker.json'), `${JSON.stringify({ ...head, user_version: userVersion, schema, ledger, lazy, statements }, null, 1)}\n`);
+ console.log(` wrote ${path.relative(ROOT, OUT)}/client.json and worker.json`);
+ } finally {
+ if (server) await server.close();
+ wt.remove();
+ fs.rmSync(tmp, { recursive: true, force: true });
+ }
+})().catch((err) => { console.error(err.stack || err.message); process.exit(1); });
+
new file mode 100644
new file mode 100644
@@ -0,0 +1,8393 @@
@@ -0,0 +1,8393 @@
+{
+ "service": "live",
+ "release": "8caa540466f9af86247febdbba1339b68919a42f",
+ "recorded_at": "2026-09-26T13:44:31.818Z",
+ "note": "N-1 fixture: written by `npm run n-1:record`, replayed by test/n-1.test.js",
+ "manifest": {
+ "service": "live",
+ "release": "8caa540466f9",
+ "released_at": "2026-09-26T13:22:32.000Z",
+ "booted_at": "2026-09-26T13:44:29.263Z",
+ "contracts_version": "0.56.0",
+ "packages": {
+ "openvibe-shared": "1.22.0",
+ "openvibe-sdk": "0.11.0"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "components": {
+ "shell": {
+ "kind": "script",
+ "version": "8caa540466f9"
+ },
+ "server": {
+ "kind": "server",
+ "version": "8caa540466f9"
+ }
+ },
+ "assets": {},
+ "schema_generation": null,
+ "schema_compatible_from": null,
+ "contract_ranges": {},
+ "metrics_url": "/release-metrics"
+ },
+ "dynamic_calls": 49,
+ "calls": [
+ {
+ "method": "GET",
+ "path": "/api/ai-viewers/config",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/ai-viewers/config",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "config": "object",
+ "config.enabled": "boolean",
+ "config.use_shared_key": "boolean",
+ "config.daily_budget_cents": "number",
+ "config.byo_base_url": "string",
+ "config.byo_model": "string",
+ "config.has_byo_key": "boolean",
+ "settings": "object",
+ "settings.roster_size": "number",
+ "settings.activity": "string",
+ "settings.lines_per_min": "number",
+ "settings.min_gap_sec": "number",
+ "settings.director_interval_sec": "number",
+ "settings.idle_interval_sec": "number",
+ "settings.lines_per_tick": "number",
+ "settings.bot_to_bot_ratio": "number",
+ "settings.reply_probability": "number",
+ "settings.mention_fast_path_sec": "number",
+ "settings.react_to_sounds": "boolean",
+ "settings.react_to_scene_changes": "boolean",
+ "settings.greet_first_timers": "boolean",
+ "settings.reply_to_relay_chat": "boolean",
+ "settings.max_open_threads": "number",
+ "settings.max_thread_turns": "number",
+ "settings.thread_idle_close_sec": "number",
+ "settings.hear_enabled": "boolean",
+ "settings.hear_window_sec": "number",
+ "settings.vision_policy": "string",
+ "settings.vision_max_age_sec": "number",
+ "settings.vision_periodic_sec": "number",
+ "settings.language": "string",
+ "settings.tone": "string",
+ "settings.emote_usage": "string",
+ "settings.max_words": "number",
+ "settings.topics": "string",
+ "settings.blocklist": "string",
+ "settings.channel_personality": "string",
+ "settings.quiet_from": "string",
+ "settings.quiet_to": "string",
+ "settings.quiet_tz": "string",
+ "settings.quiet_allow_replies": "boolean",
+ "settings.tts_enabled": "boolean",
+ "settings.powerchat_forward": "boolean",
+ "settings.memory_fold_min": "number",
+ "settings.remember_viewers": "boolean",
+ "settings.channel_memory": "string",
+ "settings.slots": "object",
+ "settings.byo": "object",
+ "settings.runtime": "object",
+ "schema": "array",
+ "schema[]": "object",
+ "schema[].key": "string",
+ "schema[].type": "string",
+ "schema[].help": "string",
+ "schema[].min": "number",
+ "schema[].max": "number",
+ "schema[].values": "array",
+ "schema[].values[]": "string",
+ "budget": "object",
+ "budget.active": "boolean",
+ "budget.reason": "string",
+ "budget.use_shared_key": "boolean",
+ "budget.spent_today_usd": "number",
+ "budget.cap_usd": "number",
+ "status": "object",
+ "status.engine": "string",
+ "status.running": "boolean",
+ "status.mode": "string",
+ "status.budget_active": "boolean",
+ "status.budget_reason": "string",
+ "status.spent_today_usd": "number",
+ "status.cap_usd": "number",
+ "status.timeline_enabled": "boolean",
+ "status.kill_switch": "boolean",
+ "engine": "string"
+ },
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/ai-viewers/log?after=0&limit=60",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/ai-viewers/log?after=0&limit=60",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "rows": "array"
+ },
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/ai-viewers/roster",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/ai-viewers/roster",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "bots": "array"
+ },
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/ai-viewers/status",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/ai-viewers/status",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "status": "object",
+ "status.engine": "string",
+ "status.running": "boolean",
+ "status.mode": "string",
+ "status.budget_active": "boolean",
+ "status.budget_reason": "string",
+ "status.spent_today_usd": "number",
+ "status.cap_usd": "number",
+ "status.timeline_enabled": "boolean",
+ "status.kill_switch": "boolean",
+ "last_hour": "object",
+ "last_hour.lines": "null",
+ "last_hour.ticks": "null",
+ "last_hour.skips": "null",
+ "last_hour.cost_usd": "number",
+ "last_hour.tokens_in": "number",
+ "last_hour.tokens_cached": "number",
+ "budget": "object",
+ "budget.active": "boolean",
+ "budget.reason": "string",
+ "budget.use_shared_key": "boolean",
+ "budget.spent_today_usd": "number",
+ "budget.cap_usd": "number"
+ },
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/analytics/channel/n1star?days=",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "username": "string",
+ "display_name": "string",
+ "avatar_url": "null",
+ "summary": "object",
+ "summary.total_streams": "number",
+ "summary.total_duration_seconds": "number",
+ "summary.peak_viewers": "number",
+ "summary.avg_viewers_per_stream": "number",
+ "summary.total_messages": "number",
+ "summary.total_unique_chatters": "number",
+ "all_time": "object",
+ "all_time.total_streams": "number",
+ "all_time.total_duration_seconds": "number",
+ "all_time.peak_viewers": "number",
+ "all_time.follower_count": "number",
+ "streams": "array"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/analytics/channel/n1star?days=",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "username": "string",
+ "display_name": "string",
+ "avatar_url": "null",
+ "summary": "object",
+ "summary.total_streams": "number",
+ "summary.total_duration_seconds": "number",
+ "summary.peak_viewers": "number",
+ "summary.avg_viewers_per_stream": "number",
+ "summary.total_messages": "number",
+ "summary.total_unique_chatters": "number",
+ "all_time": "object",
+ "all_time.total_streams": "number",
+ "all_time.total_duration_seconds": "number",
+ "all_time.peak_viewers": "number",
+ "all_time.follower_count": "number",
+ "streams": "array"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/analytics/stream/1",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "stream": "object",
+ "stream.id": "number",
+ "stream.title": "string",
+ "stream.category": "string",
+ "stream.started_at": "string",
+ "stream.ended_at": "null",
+ "stream.duration_seconds": "number",
+ "stream.peak_viewers": "number",
+ "stream.is_live": "boolean",
+ "streamer": "object",
+ "streamer.username": "string",
+ "streamer.display_name": "string",
+ "streamer.avatar_url": "null",
+ "analytics": "null",
+ "viewer_chart": "array"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/analytics/stream/1",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "stream": "object",
+ "stream.id": "number",
+ "stream.title": "string",
+ "stream.category": "string",
+ "stream.started_at": "string",
+ "stream.ended_at": "null",
+ "stream.duration_seconds": "number",
+ "stream.peak_viewers": "number",
+ "stream.is_live": "boolean",
+ "streamer": "object",
+ "streamer.username": "string",
+ "streamer.display_name": "string",
+ "streamer.avatar_url": "null",
+ "analytics": "null",
+ "viewer_chart": "array"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/beefs",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "open": "array",
+ "resolved": "array"
+ },
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/beefs",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "open": "array",
+ "resolved": "array"
+ },
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/beefs/1",
+ "auth": "anon",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/beefs/1",
+ "auth": "user",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/console/n1star",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "fighter": "object",
+ "fighter.user": "object",
+ "fighter.user.id": "number",
+ "fighter.user.username": "string",
+ "fighter.user.display_name": "string",
+ "fighter.user.avatar_url": "null",
+ "fighter.user.profile_color": "string",
+ "fighter.user.bio": "string",
+ "fighter.fighter_name": "string",
+ "fighter.rank": "null",
+ "fighter.image_url": "null",
+ "fighter.live": "boolean",
+ "fighter.level": "number",
+ "on_roster": "boolean",
+ "live": "boolean",
+ "stream": "object",
+ "stream.id": "number",
+ "stream.title": "string",
+ "stream.started_at": "string",
+ "stream.viewer_count": "number",
+ "transcribed": "boolean",
+ "listener": "object",
+ "listener.listening": "boolean",
+ "level": "object",
+ "level.level": "number",
+ "level.xp": "number",
+ "level.xp_into_level": "number",
+ "level.xp_per_level": "number",
+ "level.mic_moments": "number",
+ "level.beef_hits": "number",
+ "level.recent_xp": "number",
+ "level.best_line": "null",
+ "mic": "object",
+ "mic.moments": "number",
+ "mic.beef_hits": "number",
+ "mic.bangers": "number",
+ "mic.avg_quality": "number",
+ "mic.targeted": "number",
+ "mic.answered": "number",
+ "mic.wins": "number",
+ "mic.losses": "number",
+ "mic.speech_minutes": "number",
+ "open_beefs": "array",
+ "hot_mic": "array",
+ "recent_moments": "array"
+ },
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/console/n1star",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "fighter": "object",
+ "fighter.user": "object",
+ "fighter.user.id": "number",
+ "fighter.user.username": "string",
+ "fighter.user.display_name": "string",
+ "fighter.user.avatar_url": "null",
+ "fighter.user.profile_color": "string",
+ "fighter.user.bio": "string",
+ "fighter.fighter_name": "string",
+ "fighter.rank": "null",
+ "fighter.image_url": "null",
+ "fighter.live": "boolean",
+ "fighter.level": "number",
+ "on_roster": "boolean",
+ "live": "boolean",
+ "stream": "object",
+ "stream.id": "number",
+ "stream.title": "string",
+ "stream.started_at": "string",
+ "stream.viewer_count": "number",
+ "transcribed": "boolean",
+ "listener": "object",
+ "listener.listening": "boolean",
+ "level": "object",
+ "level.level": "number",
+ "level.xp": "number",
+ "level.xp_into_level": "number",
+ "level.xp_per_level": "number",
+ "level.mic_moments": "number",
+ "level.beef_hits": "number",
+ "level.recent_xp": "number",
+ "level.best_line": "null",
+ "mic": "object",
+ "mic.moments": "number",
+ "mic.beef_hits": "number",
+ "mic.bangers": "number",
+ "mic.avg_quality": "number",
+ "mic.targeted": "number",
+ "mic.answered": "number",
+ "mic.wins": "number",
+ "mic.losses": "number",
+ "mic.speech_minutes": "number",
+ "open_beefs": "array",
+ "hot_mic": "array",
+ "recent_moments": "array"
+ },
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/feed?limit=40",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "feed": "array",
+ "ai": "boolean"
+ },
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/feed?limit=40",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "feed": "array",
+ "ai": "boolean"
+ },
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/fighters",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "fighters": "array",
+ "ai": "boolean",
+ "stats": "array",
+ "stats[]": "string",
+ "stat_meta": "object",
+ "stat_meta.heat": "object",
+ "stat_meta.heat.label": "string",
+ "stat_meta.heat.desc": "string",
+ "stat_meta.aim": "object",
+ "stat_meta.aim.label": "string",
+ "stat_meta.aim.desc": "string",
+ "stat_meta.kills": "object",
+ "stat_meta.kills.label": "string",
+ "stat_meta.kills.desc": "string",
+ "stat_meta.mouth": "object",
+ "stat_meta.mouth.label": "string",
+ "stat_meta.mouth.desc": "string",
+ "stat_meta.clapback": "object",
+ "stat_meta.clapback.label": "string",
+ "stat_meta.clapback.desc": "string",
+ "stat_meta.stamina": "object",
+ "stat_meta.stamina.label": "string",
+ "stat_meta.stamina.desc": "string",
+ "stat_meta.pace": "object",
+ "stat_meta.pace.label": "string",
+ "stat_meta.pace.desc": "string"
+ },
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/fighters",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "fighters": "array",
+ "ai": "boolean",
+ "stats": "array",
+ "stats[]": "string",
+ "stat_meta": "object",
+ "stat_meta.heat": "object",
+ "stat_meta.heat.label": "string",
+ "stat_meta.heat.desc": "string",
+ "stat_meta.aim": "object",
+ "stat_meta.aim.label": "string",
+ "stat_meta.aim.desc": "string",
+ "stat_meta.kills": "object",
+ "stat_meta.kills.label": "string",
+ "stat_meta.kills.desc": "string",
+ "stat_meta.mouth": "object",
+ "stat_meta.mouth.label": "string",
+ "stat_meta.mouth.desc": "string",
+ "stat_meta.clapback": "object",
+ "stat_meta.clapback.label": "string",
+ "stat_meta.clapback.desc": "string",
+ "stat_meta.stamina": "object",
+ "stat_meta.stamina.label": "string",
+ "stat_meta.stamina.desc": "string",
+ "stat_meta.pace": "object",
+ "stat_meta.pace.label": "string",
+ "stat_meta.pace.desc": "string"
+ },
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/fighters/n1star",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "user": "object",
+ "user.id": "number",
+ "user.username": "string",
+ "user.display_name": "string",
+ "user.avatar_url": "null",
+ "user.profile_color": "string",
+ "user.bio": "string",
+ "not_on_roster": "boolean",
+ "reason": "string"
+ },
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/fighters/n1star",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "user": "object",
+ "user.id": "number",
+ "user.username": "string",
+ "user.display_name": "string",
+ "user.avatar_url": "null",
+ "user.profile_color": "string",
+ "user.bio": "string",
+ "not_on_roster": "boolean",
+ "reason": "string"
+ },
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/fighters/n1star/stat/1",
+ "auth": "anon",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/fighters/n1star/stat/1",
+ "auth": "user",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/live",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "live": "array"
+ },
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/live",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "live": "array"
+ },
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/me",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/arena/me",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "on_roster": "boolean",
+ "fighter": "null",
+ "beefs": "array",
+ "on_clock": "array",
+ "moments": "array",
+ "live": "boolean"
+ },
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/auth/avatar/history",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/settings.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/auth/avatar/history",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "avatars": "array"
+ },
+ "from": [
+ "public/js/settings.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/auth/ice-servers",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "iceServers": "array",
+ "iceServers[]": "object",
+ "iceServers[].urls": "string"
+ },
+ "from": [
+ "public/js/call.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/auth/ice-servers",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "iceServers": "array",
+ "iceServers[]": "object",
+ "iceServers[].urls": "string"
+ },
+ "from": [
+ "public/js/call.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/auth/me",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app.js",
+ "public/js/settings.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/auth/me",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "user": "object",
+ "user.id": "number",
+ "user.username": "string",
+ "user.display_name": "string",
+ "user.avatar_url": "null",
+ "user.bio": "string",
+ "user.role": "string",
+ "user.profile_color": "string",
+ "user.created_at": "string",
+ "user.capabilities": "object",
+ "user.capabilities.admin_panel": "boolean",
+ "user.capabilities.moderate_global": "boolean",
+ "user.capabilities.manage_users": "boolean",
+ "user.capabilities.manage_site_bans": "boolean",
+ "user.capabilities.view_all_logs": "boolean",
+ "user.capabilities.can_access_staff_console": "boolean",
+ "user.capabilities.can_manage_channels": "boolean",
+ "user.capabilities.view_ip_info": "boolean",
+ "user.capabilities.is_owner": "boolean",
+ "user.openvibe_coins_balance": "number",
+ "user.stream_key": "string",
+ "capabilities": "object",
+ "capabilities.admin_panel": "boolean",
+ "capabilities.moderate_global": "boolean",
+ "capabilities.manage_users": "boolean",
+ "capabilities.manage_site_bans": "boolean",
+ "capabilities.view_all_logs": "boolean",
+ "capabilities.can_access_staff_console": "boolean",
+ "capabilities.can_manage_channels": "boolean",
+ "capabilities.view_ip_info": "boolean",
+ "capabilities.is_owner": "boolean"
+ },
+ "from": [
+ "public/js/app.js",
+ "public/js/settings.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/auth/preferences",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/auth/preferences",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "chatSettings": "object"
+ },
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/auth/stream-key",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/auth/stream-key",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "stream_key": "string"
+ },
+ "from": [
+ "public/js/app.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/auth/tokens",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/auth/tokens",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "tokens": "array",
+ "scope_definitions": "array",
+ "scope_definitions[]": "object",
+ "scope_definitions[].id": "string",
+ "scope_definitions[].label": "string",
+ "scope_definitions[].title": "string",
+ "scope_definitions[].description": "string",
+ "token_presets": "array",
+ "token_presets[]": "object",
+ "token_presets[].id": "string",
+ "token_presets[].label": "string",
+ "token_presets[].description": "string",
+ "token_presets[].suggested_label": "string",
+ "token_presets[].scopes": "array",
+ "token_presets[].scopes[]": "string",
+ "max_active_tokens": "number",
+ "active_token_count": "number"
+ },
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/channels/moderation/mine",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-channel.js",
+ "public/js/channel-uploads.js",
+ "public/js/dashboard-moderation.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/channels/moderation/mine",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "channels": "array",
+ "channels[]": "object",
+ "channels[].id": "number",
+ "channels[].user_id": "number",
+ "channels[].title": "string",
+ "channels[].description": "string",
+ "channels[].category": "null",
+ "channels[].tags": "string",
+ "channels[].protocol": "string",
+ "channels[].is_nsfw": "number",
+ "channels[].panels": "string",
+ "channels[].default_vod_visibility": "string",
+ "channels[].default_clip_visibility": "string",
+ "channels[].weather_zip": "null",
+ "channels[].weather_detail": "string",
+ "channels[].weather_show_location": "number",
+ "channels[].control_mode": "string",
+ "channels[].anon_controls_enabled": "number",
+ "channels[].control_rate_limit_ms": "number",
+ "channels[].video_click_enabled": "number",
+ "channels[].video_click_rate_limit_ms": "number",
+ "channels[].active_control_config_id": "null",
+ "channels[].created_at": "string",
+ "channels[].updated_at": "string",
+ "channels[].clips_allow_creator_delete": "number",
+ "channels[].hide_ai_overview": "number",
+ "channels[].ai_overview_pref": "string",
+ "channels[].ai_derivation_enabled": "number",
+ "channels[].ai_category": "null",
+ "channels[].offline_screen_type": "string",
+ "channels[].offline_screen_url": "null",
+ "channels[].offline_html": "null",
+ "channels[].offline_css": "null",
+ "channels[].moderation_settings": "object",
+ "channels[].moderation_settings.channel_id": "number",
+ "channels[].moderation_settings.slow_mode_seconds": "number",
+ "channels[].moderation_settings.followers_only": "number",
+ "channels[].moderation_settings.allow_anonymous": "number",
+ "channels[].moderation_settings.links_allowed": "number",
+ "channels[].moderation_settings.gifs_enabled": "number",
+ "channels[].moderation_settings.account_age_gate_hours": "number",
+ "channels[].moderation_settings.caps_percentage_limit": "number",
+ "channels[].moderation_settings.aggressive_filter": "number",
+ "channels[].moderation_settings.max_message_length": "number",
+ "channels[].moderation_settings.tts_max_length": "number",
+ "channels[].moderation_settings.slur_filter_enabled": "number",
+ "channels[].moderation_settings.slur_filter_use_builtin": "number",
+ "channels[].moderation_settings.slur_filter_terms": "string",
+ "channels[].moderation_settings.slur_filter_regexes": "string",
+ "channels[].moderation_settings.slur_filter_nudge_message": "string",
+ "channels[].moderation_settings.slur_filter_disabled_categories": "string",
+ "channels[].moderation_settings.ip_approval_mode": "number",
+ "channels[].moderation_settings.soundboard_enabled": "number",
+ "channels[].moderation_settings.soundboard_allow_pitch": "number",
+ "channels[].moderation_settings.soundboard_allow_speed": "number",
+ "channels[].moderation_settings.soundboard_banned_ids": "string",
+ "channels[].moderation_settings.viewer_auto_delete_enabled": "number",
+ "channels[].moderation_settings.viewer_delete_all_enabled": "number",
+ "channels[].moderation_settings.custom_emotes_enabled": "number",
+ "channels[].moderation_settings.custom_sounds_enabled": "number",
+ "channels[].moderation_settings.max_sound_seconds": "number",
+ "channels[].moderation_settings.uploads_mods_only": "number",
+ "channels[].moderation_settings.mods_can_edit_about": "number",
+ "channels[].moderation_settings.emote_scale": "number",
+ "channels[].moderation_settings.emote_size_min": "number",
+ "channels[].moderation_settings.emote_size_max": "number",
+ "channels[].moderation_settings.sounds_mods_only": "number",
+ "channels[].moderation_settings.sound_min_speed": "number",
+ "channels[].moderation_settings.sound_max_speed": "number",
+ "channels[].moderation_settings.sound_min_pitch_cents": "number",
+ "channels[].moderation_settings.sound_max_pitch_cents": "number",
+ "channels[].moderation_settings.sub_only": "number",
+ "channels[].moderators": "array"
+ },
+ "from": [
+ "public/js/app-channel.js",
+ "public/js/channel-uploads.js",
+ "public/js/dashboard-moderation.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/channels/1/moderation/chat-search",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard-moderation.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/channels/1/moderation/chat-search",
+ "auth": "user",
+ "status": 500,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard-moderation.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/channels/1/moderation/logs?limit=12",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard-moderation.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/channels/1/moderation/logs?limit=12",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "actions": "array"
+ },
+ "from": [
+ "public/js/dashboard-moderation.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/chat-ai/global",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "insight": "null"
+ },
+ "from": [
+ "public/js/app-chatpage.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/chat-ai/global",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "insight": "null"
+ },
+ "from": [
+ "public/js/app-chatpage.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/chat-ai/live-captions/n1star?after=0",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "language": "object",
+ "language.code": "string",
+ "language.name": "string",
+ "language.flag": "string",
+ "available": "boolean",
+ "live": "boolean",
+ "stream_id": "number",
+ "lines": "array"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/chat-ai/live-captions/n1star?after=0",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "language": "object",
+ "language.code": "string",
+ "language.name": "string",
+ "language.flag": "string",
+ "available": "boolean",
+ "live": "boolean",
+ "stream_id": "number",
+ "lines": "array"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/chat-ai/timeline/n1star?offset=0&limit=5",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "username": "string",
+ "display_name": "string",
+ "overview": "null",
+ "chatInsight": "null",
+ "combinedOverview": "null",
+ "sessionCount": "number",
+ "momentCount": "number",
+ "index": "array",
+ "sessions": "array",
+ "offset": "number",
+ "limit": "number",
+ "hasMore": "boolean"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/chat-ai/timeline/n1star?offset=0&limit=5",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "username": "string",
+ "display_name": "string",
+ "overview": "null",
+ "chatInsight": "null",
+ "combinedOverview": "null",
+ "sessionCount": "number",
+ "momentCount": "number",
+ "index": "array",
+ "sessions": "array",
+ "offset": "number",
+ "limit": "number",
+ "hasMore": "boolean"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/chat-ai/timeline/n1star?offset=0&limit=12",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "username": "string",
+ "display_name": "string",
+ "overview": "null",
+ "chatInsight": "null",
+ "combinedOverview": "null",
+ "sessionCount": "number",
+ "momentCount": "number",
+ "index": "array",
+ "sessions": "array",
+ "offset": "number",
+ "limit": "number",
+ "hasMore": "boolean"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/chat-ai/timeline/n1star?offset=0&limit=12",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "username": "string",
+ "display_name": "string",
+ "overview": "null",
+ "chatInsight": "null",
+ "combinedOverview": "null",
+ "sessionCount": "number",
+ "momentCount": "number",
+ "index": "array",
+ "sessions": "array",
+ "offset": "number",
+ "limit": "number",
+ "hasMore": "boolean"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/chat-ai/transcript/1",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "streamId": "number",
+ "vodId": "null",
+ "segments": "array",
+ "events": "array",
+ "coverageSec": "number"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/chat-ai/transcript/1",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "streamId": "number",
+ "vodId": "null",
+ "segments": "array",
+ "events": "array",
+ "coverageSec": "number"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/clips/mine?limit=5&offset=5",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/clips/mine?limit=5&offset=5",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/clips/my-stream?auto_generated=0&limit=5&offset=5",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/clips/my-stream?auto_generated=0&limit=5&offset=5",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/clips/my-stream?auto_generated=1&limit=1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/clips/my-stream?auto_generated=1&limit=1",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/clips/my-stream?auto_generated=1&limit=5&offset=5",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/clips/my-stream?auto_generated=1&limit=5&offset=5",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/clips/settings/channel",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/clips/settings/channel",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "clips_allow_creator_delete": "boolean"
+ },
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/clips/1",
+ "auth": "anon",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js",
+ "public/js/stream-player.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/clips/1",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js",
+ "public/js/stream-player.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/clips?limit=5&offset=0&auto_generated=0",
+ "auth": "anon",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/clips?limit=5&offset=0&auto_generated=0",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/coins/balance",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/coins/balance",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "balance": "number"
+ },
+ "from": [
+ "public/js/app.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/coins/channel-balance?streamerId=1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/coins/channel-balance?streamerId=1",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "balance": "number",
+ "streamerId": "number"
+ },
+ "from": [
+ "public/js/app.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/coins/config/1",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "config": "object",
+ "config.name": "string",
+ "config.icon": "string",
+ "config.watch_interval_min": "number",
+ "config.watch_amount": "number",
+ "config.game_interval_min": "number"
+ },
+ "from": [
+ "public/js/opencoins.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/coins/config/1",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "config": "object",
+ "config.name": "string",
+ "config.icon": "string",
+ "config.watch_interval_min": "number",
+ "config.watch_amount": "number",
+ "config.game_interval_min": "number"
+ },
+ "from": [
+ "public/js/opencoins.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/coins/redemptions",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/opencoins.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/coins/redemptions",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "redemptions": "array"
+ },
+ "from": [
+ "public/js/opencoins.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/coins/rewards/1",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "rewards": "array",
+ "config": "object",
+ "config.name": "string",
+ "config.icon": "string",
+ "config.watch_interval_min": "number",
+ "config.watch_amount": "number",
+ "config.game_interval_min": "number"
+ },
+ "from": [
+ "public/js/opencoins.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/coins/rewards/1",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "rewards": "array",
+ "config": "object",
+ "config.name": "string",
+ "config.icon": "string",
+ "config.watch_interval_min": "number",
+ "config.watch_amount": "number",
+ "config.game_interval_min": "number"
+ },
+ "from": [
+ "public/js/opencoins.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/comments/1/1",
+ "auth": "anon",
+ "status": 400,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/comments/1/1",
+ "auth": "user",
+ "status": 400,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/configs",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js",
+ "public/js/broadcast.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/configs",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "configs": "array"
+ },
+ "from": [
+ "public/js/broadcast-workspace.js",
+ "public/js/broadcast.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/configs/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/configs/1",
+ "auth": "user",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/presets",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "controls": "array",
+ "settings": "object"
+ },
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/presets",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "controls": "array",
+ "settings": "object"
+ },
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/presets/1",
+ "auth": "anon",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/presets/1",
+ "auth": "user",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/settings/channel",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/settings/channel",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "control_mode": "string",
+ "anon_controls_enabled": "boolean",
+ "control_rate_limit_ms": "number",
+ "video_click_enabled": "boolean",
+ "video_click_rate_limit_ms": "number",
+ "active_control_config_id": "null"
+ },
+ "from": [
+ "public/js/broadcast.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/whitelist",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/whitelist",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "whitelist": "array"
+ },
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/1",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "controls": "array",
+ "settings": "object",
+ "settings.video_click_enabled": "boolean",
+ "settings.control_mode": "string",
+ "settings.anon_controls_enabled": "boolean",
+ "settings.control_rate_limit_ms": "number",
+ "settings.video_click_rate_limit_ms": "number"
+ },
+ "from": [
+ "public/js/controls.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/1",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "controls": "array",
+ "settings": "object",
+ "settings.video_click_enabled": "boolean",
+ "settings.control_mode": "string",
+ "settings.anon_controls_enabled": "boolean",
+ "settings.control_rate_limit_ms": "number",
+ "settings.video_click_rate_limit_ms": "number"
+ },
+ "from": [
+ "public/js/controls.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/1/config",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/controls/1/config",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "control_config_id": "null"
+ },
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/cosmetics/catalog",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "catalog": "object",
+ "catalog.name_effect": "array",
+ "catalog.name_effect[]": "object",
+ "catalog.name_effect[].itemId": "string",
+ "catalog.name_effect[].name": "string",
+ "catalog.name_effect[].emoji": "string",
+ "catalog.name_effect[].category": "string",
+ "catalog.name_effect[].tier": "number",
+ "catalog.name_effect[].cssClass": "string",
+ "catalog.name_effect[].desc": "string",
+ "catalog.particle": "array",
+ "catalog.particle[]": "object",
+ "catalog.particle[].itemId": "string",
+ "catalog.particle[].name": "string",
+ "catalog.particle[].emoji": "string",
+ "catalog.particle[].category": "string",
+ "catalog.particle[].tier": "number",
+ "catalog.particle[].cssClass": "string",
+ "catalog.particle[].chars": "string",
+ "catalog.particle[].desc": "string",
+ "catalog.hat": "array",
+ "catalog.hat[]": "object",
+ "catalog.hat[].itemId": "string",
+ "catalog.hat[].name": "string",
+ "catalog.hat[].emoji": "string",
+ "catalog.hat[].category": "string",
+ "catalog.hat[].tier": "number",
+ "catalog.hat[].hatChar": "string",
+ "catalog.hat[].desc": "string",
+ "catalog.hat[].animated": "string",
+ "catalog.voice": "array",
+ "catalog.voice[]": "object",
+ "catalog.voice[].itemId": "string",
+ "catalog.voice[].name": "string",
+ "catalog.voice[].emoji": "string",
+ "catalog.voice[].category": "string",
+ "catalog.voice[].tier": "number",
+ "catalog.voice[].desc": "string",
+ "catalog.voice[].pitch": "number",
+ "catalog.voice[].rate": "number"
+ },
+ "from": [
+ "public/js/cosmetics-ui.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/cosmetics/catalog",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "catalog": "object",
+ "catalog.name_effect": "array",
+ "catalog.name_effect[]": "object",
+ "catalog.name_effect[].itemId": "string",
+ "catalog.name_effect[].name": "string",
+ "catalog.name_effect[].emoji": "string",
+ "catalog.name_effect[].category": "string",
+ "catalog.name_effect[].tier": "number",
+ "catalog.name_effect[].cssClass": "string",
+ "catalog.name_effect[].desc": "string",
+ "catalog.particle": "array",
+ "catalog.particle[]": "object",
+ "catalog.particle[].itemId": "string",
+ "catalog.particle[].name": "string",
+ "catalog.particle[].emoji": "string",
+ "catalog.particle[].category": "string",
+ "catalog.particle[].tier": "number",
+ "catalog.particle[].cssClass": "string",
+ "catalog.particle[].chars": "string",
+ "catalog.particle[].desc": "string",
+ "catalog.hat": "array",
+ "catalog.hat[]": "object",
+ "catalog.hat[].itemId": "string",
+ "catalog.hat[].name": "string",
+ "catalog.hat[].emoji": "string",
+ "catalog.hat[].category": "string",
+ "catalog.hat[].tier": "number",
+ "catalog.hat[].hatChar": "string",
+ "catalog.hat[].desc": "string",
+ "catalog.hat[].animated": "string",
+ "catalog.voice": "array",
+ "catalog.voice[]": "object",
+ "catalog.voice[].itemId": "string",
+ "catalog.voice[].name": "string",
+ "catalog.voice[].emoji": "string",
+ "catalog.voice[].category": "string",
+ "catalog.voice[].tier": "number",
+ "catalog.voice[].desc": "string",
+ "catalog.voice[].pitch": "number",
+ "catalog.voice[].rate": "number"
+ },
+ "from": [
+ "public/js/cosmetics-ui.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/cosmetics/inventory",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/cosmetics-ui.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/cosmetics/inventory",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "categories": "object",
+ "categories.name_effect": "array",
+ "categories.particle": "array",
+ "categories.hat": "array",
+ "categories.voice": "array",
+ "equipped": "object"
+ },
+ "from": [
+ "public/js/cosmetics-ui.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/easter-egg/daily",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "egg": "null"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/easter-egg/daily",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "egg": "null"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/emotes/all/1",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "defaults": "array",
+ "defaults[]": "object",
+ "defaults[].id": "string",
+ "defaults[].code": "string",
+ "defaults[].url": "string",
+ "defaults[].source": "string",
+ "defaults[].animated": "boolean",
+ "defaults[].width": "number",
+ "defaults[].height": "number",
+ "channel": "array",
+ "global": "array",
+ "ffz": "array",
+ "bttv": "array",
+ "sources": "object",
+ "sources.defaults": "boolean",
+ "sources.custom": "boolean",
+ "sources.ffz": "boolean",
+ "sources.bttv": "boolean",
+ "emote_scale": "number"
+ },
+ "from": [
+ "public/js/emotes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/emotes/all/1",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "defaults": "array",
+ "defaults[]": "object",
+ "defaults[].id": "string",
+ "defaults[].code": "string",
+ "defaults[].url": "string",
+ "defaults[].source": "string",
+ "defaults[].animated": "boolean",
+ "defaults[].width": "number",
+ "defaults[].height": "number",
+ "channel": "array",
+ "global": "array",
+ "ffz": "array",
+ "bttv": "array",
+ "sources": "object",
+ "sources.defaults": "boolean",
+ "sources.custom": "boolean",
+ "sources.ffz": "boolean",
+ "sources.bttv": "boolean",
+ "emote_scale": "number"
+ },
+ "from": [
+ "public/js/emotes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/emotes/all/1?channel=n1star",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "defaults": "array",
+ "defaults[]": "object",
+ "defaults[].id": "string",
+ "defaults[].code": "string",
+ "defaults[].url": "string",
+ "defaults[].source": "string",
+ "defaults[].animated": "boolean",
+ "defaults[].width": "number",
+ "defaults[].height": "number",
+ "channel": "array",
+ "global": "array",
+ "ffz": "array",
+ "bttv": "array",
+ "sources": "object",
+ "sources.defaults": "boolean",
+ "sources.custom": "boolean",
+ "sources.ffz": "boolean",
+ "sources.bttv": "boolean",
+ "emote_scale": "number"
+ },
+ "from": [
+ "public/obs/chat.html"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/emotes/all/1?channel=n1star",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "defaults": "array",
+ "defaults[]": "object",
+ "defaults[].id": "string",
+ "defaults[].code": "string",
+ "defaults[].url": "string",
+ "defaults[].source": "string",
+ "defaults[].animated": "boolean",
+ "defaults[].width": "number",
+ "defaults[].height": "number",
+ "channel": "array",
+ "global": "array",
+ "ffz": "array",
+ "bttv": "array",
+ "sources": "object",
+ "sources.defaults": "boolean",
+ "sources.custom": "boolean",
+ "sources.ffz": "boolean",
+ "sources.bttv": "boolean",
+ "emote_scale": "number"
+ },
+ "from": [
+ "public/obs/chat.html"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/emotes/mine",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/emotes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/emotes/mine",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "emotes": "array",
+ "count": "number",
+ "max": "number"
+ },
+ "from": [
+ "public/js/emotes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/emotes/search?q=n1",
+ "auth": "anon",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/emotes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/emotes/search?q=n1",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/emotes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/emotes/sources",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/emotes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/emotes/sources",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "sources": "object",
+ "sources.defaults": "boolean",
+ "sources.custom": "boolean",
+ "sources.ffz": "boolean",
+ "sources.bttv": "boolean"
+ },
+ "from": [
+ "public/js/emotes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/funds/balance",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app.js",
+ "public/js/dashboard.js",
+ "public/js/vibes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/funds/balance",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "balance": "number",
+ "cashout_balance": "number"
+ },
+ "from": [
+ "public/js/app.js",
+ "public/js/dashboard.js",
+ "public/js/vibes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/funds/goals/manage/mine",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/funds/goals/manage/mine",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "goals": "array"
+ },
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/funds/goals/1",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "goals": "array"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/funds/goals/1",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "goals": "array"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/funds/history?limit=100",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/funds/history?limit=100",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "transactions": "array"
+ },
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/home/hero",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "stats": "object",
+ "stats.viewersNow": "number",
+ "stats.hoursWatched": "number",
+ "stats.vibesTipped": "number",
+ "stats.activeSubs": "number",
+ "stats.pointsEarned": "number",
+ "stats.pointsSpent": "number",
+ "stats.redemptions": "number",
+ "stats.supporters": "number",
+ "stats.vibesBought": "number",
+ "stats.goalsActive": "number",
+ "stats.goalsReached": "number",
+ "stats.vods": "null",
+ "stats.clips": "null",
+ "stats.liveSessions": "number",
+ "stats.streamers": "number",
+ "stats.chatMessages": "number",
+ "stats.users": "number",
+ "stats.anons": "number",
+ "stats.follows": "number",
+ "stats.emotes": "number",
+ "stats.pastes": "null",
+ "stats.aiMemories": "number",
+ "stats.pasteImages": "null",
+ "stats.pasteText": "null",
+ "stats.streamHours": "null",
+ "stats.weeklyActive": "number",
+ "stats.weeklyVisitors": "number",
+ "stats.prevWeeklyVisitors": "number",
+ "stats.prevWeeklyActive": "number",
+ "stats.liveNow": "number",
+ "stats.recent": "object",
+ "stats.recent.users": "object",
+ "stats.recent.users.d": "number",
+ "stats.recent.users.w": "number",
+ "stats.recent.users.m": "number",
+ "stats.recent.users.pw": "number",
+ "stats.recent.anons": "object",
+ "stats.recent.anons.d": "number",
+ "stats.recent.anons.w": "number",
+ "stats.recent.anons.m": "number",
+ "stats.recent.anons.pw": "number",
+ "stats.recent.sessions": "object",
+ "stats.recent.sessions.d": "number",
+ "stats.recent.sessions.w": "number",
+ "stats.recent.sessions.m": "number",
+ "stats.recent.sessions.pw": "number",
+ "stats.recent.vods": "null",
+ "stats.recent.clips": "null",
+ "stats.recent.aiMoments": "object",
+ "stats.recent.aiMoments.d": "number",
+ "stats.recent.aiMoments.w": "number",
+ "stats.recent.aiMoments.m": "number",
+ "stats.recent.aiMoments.pw": "number",
+ "stats.recent.messages": "object",
+ "stats.recent.messages.d": "number",
+ "stats.recent.messages.w": "number",
+ "stats.recent.messages.m": "number",
+ "stats.recent.messages.pw": "number",
+ "stats.recent.hours": "null",
+ "stats.recent.streamers": "object",
+ "stats.recent.streamers.d": "number",
+ "stats.recent.streamers.w": "number",
+ "stats.recent.streamers.m": "number",
+ "stats.recent.streamers.pw": "number",
+ "stats.recent.emotes": "object",
+ "stats.recent.emotes.d": "number",
+ "stats.recent.emotes.w": "number",
+ "stats.recent.emotes.m": "number",
+ "stats.recent.emotes.pw": "number",
+ "stats.recent.goals": "object",
+ "stats.recent.goals.d": "number",
+ "stats.recent.goals.w": "number",
+ "stats.recent.goals.m": "number",
+ "stats.recent.goals.pw": "number",
+ "stats.recent.supporters": "object",
+ "stats.recent.supporters.d": "number",
+ "stats.recent.supporters.w": "number",
+ "stats.recent.supporters.m": "number",
+ "stats.recent.supporters.pw": "number",
+ "stats.recent.vibes": "object",
+ "stats.recent.vibes.d": "number",
+ "stats.recent.vibes.w": "number",
+ "stats.recent.vibes.m": "number",
+ "stats.recent.vibes.pw": "number",
+ "stats.recent.points": "object",
+ "stats.recent.points.d": "number",
+ "stats.recent.points.w": "number",
+ "stats.recent.points.m": "number",
+ "stats.recent.points.pw": "number",
+ "stats.recent.pointsSpent": "object",
+ "stats.recent.pointsSpent.d": "number",
+ "stats.recent.pointsSpent.w": "number",
+ "stats.recent.pointsSpent.m": "number",
+ "stats.recent.pointsSpent.pw": "number",
+ "stats.recent.redemptions": "object",
+ "stats.recent.redemptions.d": "number",
+ "stats.recent.redemptions.w": "number",
+ "stats.recent.redemptions.m": "number",
+ "stats.recent.redemptions.pw": "number",
+ "stats.recent.vibesBought": "object",
+ "stats.recent.vibesBought.d": "number",
+ "stats.recent.vibesBought.w": "number",
+ "stats.recent.vibesBought.m": "number",
+ "stats.recent.vibesBought.pw": "number",
+ "stats.recent.subs": "object",
+ "stats.recent.subs.d": "number",
+ "stats.recent.subs.w": "number",
+ "stats.recent.subs.m": "number",
+ "stats.recent.subs.pw": "number",
+ "stats.recent.follows": "object",
+ "stats.recent.follows.d": "number",
+ "stats.recent.follows.w": "number",
+ "stats.recent.follows.m": "number",
+ "stats.recent.follows.pw": "number",
+ "stats.concurrency": "object",
+ "stats.concurrency.viewersAvg24h": "number",
+ "stats.concurrency.viewersPeak24h": "number",
+ "stats.concurrency.liveAvg24h": "number",
+ "stats.concurrency.livePeak24h": "number",
+ "stats.viewerTrend": "array",
+ "media": "array",
+ "media[]": "object",
+ "media[].kind": "string",
+ "media[].title": "string",
+ "media[].thumbnail": "null",
+ "media[].href": "string",
+ "media[].username": "string",
+ "moments": "array",
+ "slogans": "object",
+ "slogans.audiences": "array",
+ "slogans.audiences[]": "string",
+ "slogans.quips": "array",
+ "slogans.quips[]": "string",
+ "slogans.ai": "boolean",
+ "slogans.updated_at": "null",
+ "slogans.next_at": "null"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/home/hero",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "stats": "object",
+ "stats.viewersNow": "number",
+ "stats.hoursWatched": "number",
+ "stats.vibesTipped": "number",
+ "stats.activeSubs": "number",
+ "stats.pointsEarned": "number",
+ "stats.pointsSpent": "number",
+ "stats.redemptions": "number",
+ "stats.supporters": "number",
+ "stats.vibesBought": "number",
+ "stats.goalsActive": "number",
+ "stats.goalsReached": "number",
+ "stats.vods": "null",
+ "stats.clips": "null",
+ "stats.liveSessions": "number",
+ "stats.streamers": "number",
+ "stats.chatMessages": "number",
+ "stats.users": "number",
+ "stats.anons": "number",
+ "stats.follows": "number",
+ "stats.emotes": "number",
+ "stats.pastes": "null",
+ "stats.aiMemories": "number",
+ "stats.pasteImages": "null",
+ "stats.pasteText": "null",
+ "stats.streamHours": "null",
+ "stats.weeklyActive": "number",
+ "stats.weeklyVisitors": "number",
+ "stats.prevWeeklyVisitors": "number",
+ "stats.prevWeeklyActive": "number",
+ "stats.liveNow": "number",
+ "stats.recent": "object",
+ "stats.recent.users": "object",
+ "stats.recent.users.d": "number",
+ "stats.recent.users.w": "number",
+ "stats.recent.users.m": "number",
+ "stats.recent.users.pw": "number",
+ "stats.recent.anons": "object",
+ "stats.recent.anons.d": "number",
+ "stats.recent.anons.w": "number",
+ "stats.recent.anons.m": "number",
+ "stats.recent.anons.pw": "number",
+ "stats.recent.sessions": "object",
+ "stats.recent.sessions.d": "number",
+ "stats.recent.sessions.w": "number",
+ "stats.recent.sessions.m": "number",
+ "stats.recent.sessions.pw": "number",
+ "stats.recent.vods": "null",
+ "stats.recent.clips": "null",
+ "stats.recent.aiMoments": "object",
+ "stats.recent.aiMoments.d": "number",
+ "stats.recent.aiMoments.w": "number",
+ "stats.recent.aiMoments.m": "number",
+ "stats.recent.aiMoments.pw": "number",
+ "stats.recent.messages": "object",
+ "stats.recent.messages.d": "number",
+ "stats.recent.messages.w": "number",
+ "stats.recent.messages.m": "number",
+ "stats.recent.messages.pw": "number",
+ "stats.recent.hours": "null",
+ "stats.recent.streamers": "object",
+ "stats.recent.streamers.d": "number",
+ "stats.recent.streamers.w": "number",
+ "stats.recent.streamers.m": "number",
+ "stats.recent.streamers.pw": "number",
+ "stats.recent.emotes": "object",
+ "stats.recent.emotes.d": "number",
+ "stats.recent.emotes.w": "number",
+ "stats.recent.emotes.m": "number",
+ "stats.recent.emotes.pw": "number",
+ "stats.recent.goals": "object",
+ "stats.recent.goals.d": "number",
+ "stats.recent.goals.w": "number",
+ "stats.recent.goals.m": "number",
+ "stats.recent.goals.pw": "number",
+ "stats.recent.supporters": "object",
+ "stats.recent.supporters.d": "number",
+ "stats.recent.supporters.w": "number",
+ "stats.recent.supporters.m": "number",
+ "stats.recent.supporters.pw": "number",
+ "stats.recent.vibes": "object",
+ "stats.recent.vibes.d": "number",
+ "stats.recent.vibes.w": "number",
+ "stats.recent.vibes.m": "number",
+ "stats.recent.vibes.pw": "number",
+ "stats.recent.points": "object",
+ "stats.recent.points.d": "number",
+ "stats.recent.points.w": "number",
+ "stats.recent.points.m": "number",
+ "stats.recent.points.pw": "number",
+ "stats.recent.pointsSpent": "object",
+ "stats.recent.pointsSpent.d": "number",
+ "stats.recent.pointsSpent.w": "number",
+ "stats.recent.pointsSpent.m": "number",
+ "stats.recent.pointsSpent.pw": "number",
+ "stats.recent.redemptions": "object",
+ "stats.recent.redemptions.d": "number",
+ "stats.recent.redemptions.w": "number",
+ "stats.recent.redemptions.m": "number",
+ "stats.recent.redemptions.pw": "number",
+ "stats.recent.vibesBought": "object",
+ "stats.recent.vibesBought.d": "number",
+ "stats.recent.vibesBought.w": "number",
+ "stats.recent.vibesBought.m": "number",
+ "stats.recent.vibesBought.pw": "number",
+ "stats.recent.subs": "object",
+ "stats.recent.subs.d": "number",
+ "stats.recent.subs.w": "number",
+ "stats.recent.subs.m": "number",
+ "stats.recent.subs.pw": "number",
+ "stats.recent.follows": "object",
+ "stats.recent.follows.d": "number",
+ "stats.recent.follows.w": "number",
+ "stats.recent.follows.m": "number",
+ "stats.recent.follows.pw": "number",
+ "stats.concurrency": "object",
+ "stats.concurrency.viewersAvg24h": "number",
+ "stats.concurrency.viewersPeak24h": "number",
+ "stats.concurrency.liveAvg24h": "number",
+ "stats.concurrency.livePeak24h": "number",
+ "stats.viewerTrend": "array",
+ "media": "array",
+ "media[]": "object",
+ "media[].kind": "string",
+ "media[].title": "string",
+ "media[].thumbnail": "null",
+ "media[].href": "string",
+ "media[].username": "string",
+ "moments": "array",
+ "slogans": "object",
+ "slogans.audiences": "array",
+ "slogans.audiences[]": "string",
+ "slogans.quips": "array",
+ "slogans.quips[]": "string",
+ "slogans.ai": "boolean",
+ "slogans.updated_at": "null",
+ "slogans.next_at": "null"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/home/pulse",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "goals": "array",
+ "latestTip": "null",
+ "newestFollow": "object",
+ "newestFollow.created_at": "string",
+ "newestFollow.follower_username": "string",
+ "newestFollow.follower_display": "string",
+ "newestFollow.streamer_username": "string",
+ "newestFollow.streamer_display": "string",
+ "topSupporters": "array",
+ "topEarners": "array",
+ "moments": "array"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/home/pulse",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "goals": "array",
+ "latestTip": "null",
+ "newestFollow": "object",
+ "newestFollow.created_at": "string",
+ "newestFollow.follower_username": "string",
+ "newestFollow.follower_display": "string",
+ "newestFollow.streamer_username": "string",
+ "newestFollow.streamer_display": "string",
+ "topSupporters": "array",
+ "topEarners": "array",
+ "moments": "array"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/home/star",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "star": "null"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/home/star",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "star": "null"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/home/stats-live",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "liveNow": "number",
+ "viewersNow": "number",
+ "weeklyActive": "number",
+ "users": "number",
+ "weeklyVisitors": "number",
+ "anons": "number",
+ "chatMessages": "number",
+ "streamers": "number",
+ "hoursWatched": "number",
+ "prevWeeklyVisitors": "number",
+ "prevWeeklyActive": "number",
+ "concurrency": "object",
+ "concurrency.viewersAvg24h": "number",
+ "concurrency.viewersPeak24h": "number",
+ "concurrency.liveAvg24h": "number",
+ "concurrency.livePeak24h": "number",
+ "recent": "object",
+ "recent.users": "object",
+ "recent.users.d": "number",
+ "recent.users.w": "number",
+ "recent.users.m": "number",
+ "recent.users.pw": "number",
+ "recent.anons": "object",
+ "recent.anons.d": "number",
+ "recent.anons.w": "number",
+ "recent.anons.m": "number",
+ "recent.anons.pw": "number",
+ "recent.messages": "object",
+ "recent.messages.d": "number",
+ "recent.messages.w": "number",
+ "recent.messages.m": "number",
+ "recent.messages.pw": "number",
+ "recent.streamers": "object",
+ "recent.streamers.d": "number",
+ "recent.streamers.w": "number",
+ "recent.streamers.m": "number",
+ "recent.streamers.pw": "number"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/home/stats-live",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "liveNow": "number",
+ "viewersNow": "number",
+ "weeklyActive": "number",
+ "users": "number",
+ "weeklyVisitors": "number",
+ "anons": "number",
+ "chatMessages": "number",
+ "streamers": "number",
+ "hoursWatched": "number",
+ "prevWeeklyVisitors": "number",
+ "prevWeeklyActive": "number",
+ "concurrency": "object",
+ "concurrency.viewersAvg24h": "number",
+ "concurrency.viewersPeak24h": "number",
+ "concurrency.liveAvg24h": "number",
+ "concurrency.livePeak24h": "number",
+ "recent": "object",
+ "recent.users": "object",
+ "recent.users.d": "number",
+ "recent.users.w": "number",
+ "recent.users.m": "number",
+ "recent.users.pw": "number",
+ "recent.anons": "object",
+ "recent.anons.d": "number",
+ "recent.anons.w": "number",
+ "recent.anons.m": "number",
+ "recent.anons.pw": "number",
+ "recent.messages": "object",
+ "recent.messages.d": "number",
+ "recent.messages.w": "number",
+ "recent.messages.m": "number",
+ "recent.messages.pw": "number",
+ "recent.streamers": "object",
+ "recent.streamers.d": "number",
+ "recent.streamers.w": "number",
+ "recent.streamers.m": "number",
+ "recent.streamers.pw": "number"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/home/stats/series/1?days=",
+ "auth": "anon",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/stats-nerds.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/home/stats/series/1?days=",
+ "auth": "user",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/stats-nerds.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/kiosk/site?url=",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "reachable": "boolean"
+ },
+ "from": [
+ "public/kiosk.html"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/kiosk/site?url=",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "reachable": "boolean"
+ },
+ "from": [
+ "public/kiosk.html"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/media/channel/n1star",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "channel": "object",
+ "channel.id": "number",
+ "channel.user_id": "number",
+ "channel.username": "string",
+ "channel.display_name": "string",
+ "channel.avatar_url": "null",
+ "live_stream": "object",
+ "live_stream.id": "number",
+ "live_stream.user_id": "number",
+ "live_stream.channel_id": "null",
+ "live_stream.managed_stream_id": "number",
+ "live_stream.control_config_id": "null",
+ "live_stream.title": "string",
+ "live_stream.description": "string",
+ "live_stream.category": "string",
+ "live_stream.tags": "string",
+ "live_stream.protocol": "string",
+ "live_stream.is_live": "number",
+ "live_stream.is_nsfw": "number",
+ "live_stream.viewer_count": "number",
+ "live_stream.peak_viewers": "number",
+ "live_stream.follower_count": "number",
+ "live_stream.thumbnail_url": "null",
+ "live_stream.started_at": "string",
+ "live_stream.ended_at": "null",
+ "live_stream.duration_seconds": "number",
+ "live_stream.created_at": "string",
+ "live_stream.ai_overview": "null",
+ "live_stream.ai_title": "null",
+ "live_stream.ai_category": "null",
+ "live_stream.ai_tags": "null",
+ "live_stream.ai_overview_short": "null",
+ "live_stream.username": "string",
+ "live_stream.display_name": "string",
+ "live_stream.avatar_url": "null",
+ "live_stream.profile_color": "string",
+ "live_stream.managed_stream_slug": "null",
+ "state": "object",
+ "state.settings": "object",
+ "state.settings.enabled": "number",
+ "state.settings.request_cost": "number",
+ "state.settings.max_per_user": "number",
+ "state.settings.max_duration_seconds": "number",
+ "state.settings.allow_youtube": "number",
+ "state.settings.allow_vimeo": "number",
+ "state.settings.allow_direct_media": "number",
+ "state.settings.auto_advance": "number",
+ "state.settings.cost_mode": "string",
+ "state.settings.cost_per_minute": "number",
+ "state.settings.currency": "string",
+ "state.settings.allow_live": "number",
+ "state.settings.user_id": "number",
+ "state.settings.created_at": "string",
+ "state.settings.updated_at": "string",
+ "state.queue": "array",
+ "state.history": "array",
+ "is_owner": "boolean",
+ "can_manage": "boolean",
+ "pricing": "object",
+ "pricing.currency": "string",
+ "pricing.currency_label": "string",
+ "pricing.cost_mode": "string",
+ "pricing.request_cost": "number",
+ "pricing.cost_per_minute": "number",
+ "pricing.max_duration_seconds": "number"
+ },
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/media/channel/n1star",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "channel": "object",
+ "channel.id": "number",
+ "channel.user_id": "number",
+ "channel.username": "string",
+ "channel.display_name": "string",
+ "channel.avatar_url": "null",
+ "live_stream": "object",
+ "live_stream.id": "number",
+ "live_stream.user_id": "number",
+ "live_stream.channel_id": "null",
+ "live_stream.managed_stream_id": "number",
+ "live_stream.control_config_id": "null",
+ "live_stream.title": "string",
+ "live_stream.description": "string",
+ "live_stream.category": "string",
+ "live_stream.tags": "string",
+ "live_stream.protocol": "string",
+ "live_stream.is_live": "number",
+ "live_stream.is_nsfw": "number",
+ "live_stream.viewer_count": "number",
+ "live_stream.peak_viewers": "number",
+ "live_stream.follower_count": "number",
+ "live_stream.thumbnail_url": "null",
+ "live_stream.started_at": "string",
+ "live_stream.ended_at": "null",
+ "live_stream.duration_seconds": "number",
+ "live_stream.created_at": "string",
+ "live_stream.ai_overview": "null",
+ "live_stream.ai_title": "null",
+ "live_stream.ai_category": "null",
+ "live_stream.ai_tags": "null",
+ "live_stream.ai_overview_short": "null",
+ "live_stream.username": "string",
+ "live_stream.display_name": "string",
+ "live_stream.avatar_url": "null",
+ "live_stream.profile_color": "string",
+ "live_stream.managed_stream_slug": "null",
+ "state": "object",
+ "state.settings": "object",
+ "state.settings.enabled": "number",
+ "state.settings.request_cost": "number",
+ "state.settings.max_per_user": "number",
+ "state.settings.max_duration_seconds": "number",
+ "state.settings.allow_youtube": "number",
+ "state.settings.allow_vimeo": "number",
+ "state.settings.allow_direct_media": "number",
+ "state.settings.auto_advance": "number",
+ "state.settings.cost_mode": "string",
+ "state.settings.cost_per_minute": "number",
+ "state.settings.currency": "string",
+ "state.settings.allow_live": "number",
+ "state.settings.user_id": "number",
+ "state.settings.created_at": "string",
+ "state.settings.updated_at": "string",
+ "state.queue": "array",
+ "state.history": "array",
+ "is_owner": "boolean",
+ "can_manage": "boolean",
+ "pricing": "object",
+ "pricing.currency": "string",
+ "pricing.currency_label": "string",
+ "pricing.cost_mode": "string",
+ "pricing.request_cost": "number",
+ "pricing.cost_per_minute": "number",
+ "pricing.max_duration_seconds": "number"
+ },
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/media/queue/1/stream-url",
+ "auth": "anon",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js",
+ "public/js/media-player.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/media/queue/1/stream-url",
+ "auth": "user",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js",
+ "public/js/media-player.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/meta/openvibeapp",
+ "auth": "anon",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/meta/openvibeapp",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/mod/ip-approval/1/pending",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard-moderation.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/mod/ip-approval/1/pending",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "pending": "array"
+ },
+ "from": [
+ "public/js/dashboard-moderation.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/mod/ip/anon/n1star",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/mod/ip/anon/n1star",
+ "auth": "user",
+ "status": 403,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/mod/ip/user/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/mod/ip/user/1",
+ "auth": "user",
+ "status": 403,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/mod/tts-voice/1/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/mod/tts-voice/1/1",
+ "auth": "user",
+ "status": 403,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/news/my-settings",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/news/my-settings",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "enabled": "null"
+ },
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/onvif/cameras",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/onvif-dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/onvif/cameras",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "cameras": "array"
+ },
+ "from": [
+ "public/js/onvif-dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/onvif/cameras/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/controls.js",
+ "public/js/onvif-dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/onvif/cameras/1",
+ "auth": "user",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/controls.js",
+ "public/js/onvif-dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/onvif/cameras/1/presets",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/controls.js",
+ "public/js/onvif-dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/onvif/cameras/1/presets",
+ "auth": "user",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/controls.js",
+ "public/js/onvif-dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/pastes/by-user/n1star?limit=30",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "pastes": "array",
+ "total": "number",
+ "username": "string"
+ },
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/pastes/by-user/n1star?limit=30",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "pastes": "array",
+ "total": "number",
+ "username": "string"
+ },
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/pastes/by-user/n1star?limit=30&sort=",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "pastes": "array",
+ "total": "number",
+ "username": "string"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/pastes/by-user/n1star?limit=30&sort=",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "pastes": "array",
+ "total": "number",
+ "username": "string"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/pastes/config",
+ "auth": "anon",
+ "status": 504,
+ "kind": "json",
+ "from": [
+ "public/js/pastes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/pastes/config",
+ "auth": "user",
+ "status": 504,
+ "kind": "json",
+ "from": [
+ "public/js/pastes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/pastes/n1star",
+ "auth": "anon",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/pastes/n1star",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/pastes/n1star/comments",
+ "auth": "anon",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/pastes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/pastes/n1star/comments",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/pastes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/pastes?limit=5&offset=0&origin=user",
+ "auth": "anon",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/pastes?limit=5&offset=0&origin=user",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/payments/channel/n1star",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "subscribed": "boolean",
+ "subscriberCount": "number",
+ "priceUsd": "number",
+ "powerchat": "object",
+ "powerchat.direct": "boolean",
+ "powerchat.site": "boolean",
+ "powerchat.feePct": "number",
+ "powerchat.siteTotalUsd": "number"
+ },
+ "from": [
+ "public/js/vibes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/payments/channel/n1star",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "subscribed": "boolean",
+ "subscriberCount": "number",
+ "priceUsd": "number",
+ "powerchat": "object",
+ "powerchat.direct": "boolean",
+ "powerchat.site": "boolean",
+ "powerchat.feePct": "number",
+ "powerchat.siteTotalUsd": "number"
+ },
+ "from": [
+ "public/js/vibes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/payments/config",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "enabled": "boolean",
+ "paymentsMaster": "boolean",
+ "packages": "array",
+ "packages[]": "object",
+ "packages[].bucks": "number",
+ "packages[].usd": "number",
+ "minPurchaseBucks": "number",
+ "subPriceUsd": "number",
+ "providers": "object",
+ "providers.paypal": "boolean",
+ "providers.stripe": "boolean",
+ "providers.ccbill": "boolean",
+ "providers.crypto": "boolean",
+ "providers.powerchat": "boolean"
+ },
+ "from": [
+ "public/js/vibes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/payments/config",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "enabled": "boolean",
+ "paymentsMaster": "boolean",
+ "packages": "array",
+ "packages[]": "object",
+ "packages[].bucks": "number",
+ "packages[].usd": "number",
+ "minPurchaseBucks": "number",
+ "subPriceUsd": "number",
+ "providers": "object",
+ "providers.paypal": "boolean",
+ "providers.stripe": "boolean",
+ "providers.ccbill": "boolean",
+ "providers.crypto": "boolean",
+ "providers.powerchat": "boolean"
+ },
+ "from": [
+ "public/js/vibes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/powerchat/donate-link?streamer_id=1",
+ "auth": "anon",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/vibes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/powerchat/donate-link?streamer_id=1",
+ "auth": "user",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/vibes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/powerchat/donate-link?streamer_id=1&goal_id=${goalId}",
+ "auth": "anon",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/vibes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/powerchat/donate-link?streamer_id=1&goal_id=${goalId}",
+ "auth": "user",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/vibes.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/powerchat/status",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/powerchat/status",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "enabled": "boolean",
+ "configured": "boolean",
+ "connected": "boolean",
+ "connection_kind": "null",
+ "username": "null",
+ "tip_page_url": "null",
+ "scope": "null",
+ "scopes": "array",
+ "missing_scopes": "array",
+ "live": "null",
+ "diagnosis": "array",
+ "chat_relay": "object",
+ "chat_relay.accepted": "number",
+ "chat_relay.dropped": "number",
+ "chat_relay.pending": "number",
+ "last_error": "null"
+ },
+ "from": [
+ "public/js/broadcast-workspace.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/powerchat/status?live=1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/powerchat.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/powerchat/status?live=1",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "enabled": "boolean",
+ "configured": "boolean",
+ "connected": "boolean",
+ "connection_kind": "null",
+ "username": "null",
+ "tip_page_url": "null",
+ "scope": "null",
+ "scopes": "array",
+ "missing_scopes": "array",
+ "live": "null",
+ "diagnosis": "array",
+ "chat_relay": "object",
+ "chat_relay.accepted": "number",
+ "chat_relay.dropped": "number",
+ "chat_relay.pending": "number",
+ "last_error": "null"
+ },
+ "from": [
+ "public/js/powerchat.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/recap/1",
+ "auth": "anon",
+ "status": 409,
+ "kind": "json",
+ "from": [
+ "public/js/recap.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/recap/1",
+ "auth": "user",
+ "status": 409,
+ "kind": "json",
+ "from": [
+ "public/js/recap.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/restream/destinations?managed_stream_id=1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/restream/destinations?managed_stream_id=1",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "destinations": "array"
+ },
+ "from": [
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/restream/oauth/status",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js",
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/restream/oauth/status",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "platforms": "array",
+ "platforms[]": "object",
+ "platforms[].platform": "string",
+ "platforms[].name": "string",
+ "platforms[].configured": "boolean",
+ "platforms[].providesKey": "boolean",
+ "platforms[].connected": "boolean",
+ "platforms[].username": "null",
+ "platforms[].channel_url": "null"
+ },
+ "from": [
+ "public/js/broadcast-workspace.js",
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/restream/status",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/restream/status",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "statuses": "object",
+ "robotstreamer": "object"
+ },
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/robotstreamer/integration?managed_stream_id=1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/robotstreamer/integration?managed_stream_id=1",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "integration": "object",
+ "integration.enabled": "boolean",
+ "integration.mirror_chat": "boolean",
+ "integration.has_token": "boolean",
+ "integration.robot_id": "string",
+ "integration.owner_id": "string",
+ "integration.stream_name": "string",
+ "integration.owner_name": "string",
+ "integration.managed_stream_id": "null",
+ "integration.available_robots": "array",
+ "integration.passthrough": "boolean",
+ "exists": "boolean"
+ },
+ "from": [
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/search/suggest",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "suggestions": "array"
+ },
+ "from": [
+ "public/js/app-search.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/search/suggest",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "suggestions": "array"
+ },
+ "from": [
+ "public/js/app-search.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/search",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "results": "array",
+ "next_cursor": "null"
+ },
+ "from": [
+ "public/js/app-search.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/search",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "results": "array",
+ "next_cursor": "null"
+ },
+ "from": [
+ "public/js/app-search.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "null",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null",
+ "streams[].browser_mode": "string",
+ "streams[].streaming_method": "string",
+ "streams[].channel": "object",
+ "streams[].channel.id": "number",
+ "streams[].channel.user_id": "number",
+ "streams[].channel.title": "string",
+ "streams[].channel.description": "string",
+ "streams[].channel.category": "null",
+ "streams[].channel.tags": "string",
+ "streams[].channel.protocol": "string",
+ "streams[].channel.is_nsfw": "number",
+ "streams[].channel.panels": "string",
+ "streams[].channel.default_vod_visibility": "string",
+ "streams[].channel.default_clip_visibility": "string",
+ "streams[].channel.weather_detail": "string",
+ "streams[].channel.weather_show_location": "number",
+ "streams[].channel.control_mode": "string",
+ "streams[].channel.anon_controls_enabled": "number",
+ "streams[].channel.control_rate_limit_ms": "number",
+ "streams[].channel.video_click_enabled": "number",
+ "streams[].channel.video_click_rate_limit_ms": "number",
+ "streams[].channel.active_control_config_id": "null",
+ "streams[].channel.created_at": "string",
+ "streams[].channel.updated_at": "string",
+ "streams[].channel.clips_allow_creator_delete": "number",
+ "streams[].channel.hide_ai_overview": "number",
+ "streams[].channel.ai_overview_pref": "string",
+ "streams[].channel.ai_derivation_enabled": "number",
+ "streams[].channel.ai_category": "null",
+ "streams[].channel.offline_screen_type": "string",
+ "streams[].channel.offline_screen_url": "null",
+ "streams[].channel.offline_html": "null",
+ "streams[].channel.offline_css": "null",
+ "streams[].external_viewer_count": "number",
+ "streams[].total_viewer_count": "number"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "null",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null",
+ "streams[].browser_mode": "string",
+ "streams[].streaming_method": "string",
+ "streams[].channel": "object",
+ "streams[].channel.id": "number",
+ "streams[].channel.user_id": "number",
+ "streams[].channel.title": "string",
+ "streams[].channel.description": "string",
+ "streams[].channel.category": "null",
+ "streams[].channel.tags": "string",
+ "streams[].channel.protocol": "string",
+ "streams[].channel.is_nsfw": "number",
+ "streams[].channel.panels": "string",
+ "streams[].channel.default_vod_visibility": "string",
+ "streams[].channel.default_clip_visibility": "string",
+ "streams[].channel.weather_detail": "string",
+ "streams[].channel.weather_show_location": "number",
+ "streams[].channel.control_mode": "string",
+ "streams[].channel.anon_controls_enabled": "number",
+ "streams[].channel.control_rate_limit_ms": "number",
+ "streams[].channel.video_click_enabled": "number",
+ "streams[].channel.video_click_rate_limit_ms": "number",
+ "streams[].channel.active_control_config_id": "null",
+ "streams[].channel.created_at": "string",
+ "streams[].channel.updated_at": "string",
+ "streams[].channel.clips_allow_creator_delete": "number",
+ "streams[].channel.hide_ai_overview": "number",
+ "streams[].channel.ai_overview_pref": "string",
+ "streams[].channel.ai_derivation_enabled": "number",
+ "streams[].channel.ai_category": "null",
+ "streams[].channel.offline_screen_type": "string",
+ "streams[].channel.offline_screen_url": "null",
+ "streams[].channel.offline_html": "null",
+ "streams[].channel.offline_css": "null",
+ "streams[].external_viewer_count": "number",
+ "streams[].total_viewer_count": "number"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/broadcast-settings?managed_stream_id=1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/broadcast-settings?managed_stream_id=1",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "settings": "object",
+ "managed_stream_id": "number"
+ },
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js",
+ "public/js/settings.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "id": "number",
+ "user_id": "number",
+ "title": "string",
+ "description": "string",
+ "category": "null",
+ "tags": "string",
+ "protocol": "string",
+ "is_nsfw": "number",
+ "panels": "string",
+ "default_vod_visibility": "string",
+ "default_clip_visibility": "string",
+ "weather_zip": "null",
+ "weather_detail": "string",
+ "weather_show_location": "number",
+ "control_mode": "string",
+ "anon_controls_enabled": "number",
+ "control_rate_limit_ms": "number",
+ "video_click_enabled": "number",
+ "video_click_rate_limit_ms": "number",
+ "active_control_config_id": "null",
+ "created_at": "string",
+ "updated_at": "string",
+ "clips_allow_creator_delete": "number",
+ "hide_ai_overview": "number",
+ "ai_overview_pref": "string",
+ "ai_derivation_enabled": "number",
+ "ai_category": "null",
+ "offline_screen_type": "string",
+ "offline_screen_url": "null",
+ "offline_html": "null",
+ "offline_css": "null"
+ },
+ "from": [
+ "public/js/dashboard.js",
+ "public/js/settings.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star?aiClipsLimit=5&aiClipsOffset=0",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "videos_tab_hidden": "boolean",
+ "clips_tab_hidden": "boolean",
+ "pasteTotal": "number",
+ "clipsTakenTotal": "number",
+ "aiEventTotal": "number",
+ "channel": "object",
+ "channel.id": "number",
+ "channel.user_id": "number",
+ "channel.title": "string",
+ "channel.description": "string",
+ "channel.category": "null",
+ "channel.tags": "string",
+ "channel.protocol": "string",
+ "channel.is_nsfw": "number",
+ "channel.panels": "string",
+ "channel.default_vod_visibility": "string",
+ "channel.default_clip_visibility": "string",
+ "channel.weather_detail": "string",
+ "channel.weather_show_location": "number",
+ "channel.control_mode": "string",
+ "channel.anon_controls_enabled": "number",
+ "channel.control_rate_limit_ms": "number",
+ "channel.video_click_enabled": "number",
+ "channel.video_click_rate_limit_ms": "number",
+ "channel.active_control_config_id": "null",
+ "channel.created_at": "string",
+ "channel.updated_at": "string",
+ "channel.clips_allow_creator_delete": "number",
+ "channel.hide_ai_overview": "number",
+ "channel.ai_overview_pref": "string",
+ "channel.ai_derivation_enabled": "number",
+ "channel.ai_category": "null",
+ "channel.offline_screen_type": "string",
+ "channel.offline_screen_url": "null",
+ "channel.offline_html": "null",
+ "channel.offline_css": "null",
+ "channel.username": "string",
+ "channel.display_name": "string",
+ "channel.avatar_url": "null",
+ "channel.profile_color": "string",
+ "channel.bio": "string",
+ "channel.role": "string",
+ "channel.is_owner": "number",
+ "channel.follower_count": "number",
+ "channel.is_following": "boolean",
+ "channel.chat_limits": "object",
+ "channel.chat_limits.max_message_length": "number",
+ "channel.chat_limits.tts_max_length": "number",
+ "channel.mods_can_edit_about": "boolean",
+ "channel.viewer_can_edit_about": "boolean",
+ "channel.ai_overview": "null",
+ "language": "object",
+ "language.code": "string",
+ "language.name": "string",
+ "language.flag": "string",
+ "language.translate": "boolean",
+ "stream": "object",
+ "stream.id": "number",
+ "stream.user_id": "number",
+ "stream.channel_id": "null",
+ "stream.managed_stream_id": "number",
+ "stream.control_config_id": "null",
+ "stream.title": "string",
+ "stream.description": "string",
+ "stream.category": "string",
+ "stream.tags": "string",
+ "stream.protocol": "string",
+ "stream.is_live": "number",
+ "stream.is_nsfw": "number",
+ "stream.viewer_count": "number",
+ "stream.peak_viewers": "number",
+ "stream.follower_count": "number",
+ "stream.thumbnail_url": "null",
+ "stream.started_at": "string",
+ "stream.ended_at": "null",
+ "stream.duration_seconds": "number",
+ "stream.created_at": "string",
+ "stream.ai_overview": "null",
+ "stream.ai_title": "null",
+ "stream.ai_category": "null",
+ "stream.ai_tags": "null",
+ "stream.ai_overview_short": "null",
+ "stream.username": "string",
+ "stream.display_name": "string",
+ "stream.avatar_url": "null",
+ "stream.profile_color": "string",
+ "stream.managed_stream_slug": "null",
+ "stream.endpoint": "object",
+ "stream.server_clip": "boolean",
+ "stream.external_viewer_count": "number",
+ "stream.total_viewer_count": "number",
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "null",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null",
+ "streams[].endpoint": "object",
+ "streams[].server_clip": "boolean",
+ "streams[].external_viewer_count": "number",
+ "streams[].total_viewer_count": "number",
+ "managed_streams": "array",
+ "managed_streams[]": "object",
+ "managed_streams[].id": "number",
+ "managed_streams[].user_id": "number",
+ "managed_streams[].channel_id": "number",
+ "managed_streams[].slug": "null",
+ "managed_streams[].title": "string",
+ "managed_streams[].description": "string",
+ "managed_streams[].category": "string",
+ "managed_streams[].tags": "string",
+ "managed_streams[].protocol": "string",
+ "managed_streams[].is_nsfw": "number",
+ "managed_streams[].control_config_id": "null",
+ "managed_streams[].sort_order": "number",
+ "managed_streams[].created_at": "string",
+ "managed_streams[].updated_at": "string",
+ "managed_streams[].slot_clip_recording_enabled": "number",
+ "managed_streams[].slot_clip_notify_enabled": "number",
+ "managed_streams[].slot_powerchat_relay": "number",
+ "managed_streams[].slot_powerchat_count_rs_views": "number",
+ "managed_streams[].streaming_method": "string",
+ "managed_streams[].browser_mode": "string",
+ "managed_streams[].default_vod_visibility": "string",
+ "managed_streams[].default_clip_visibility": "string",
+ "managed_streams[].slot_vod_recording_enabled": "number",
+ "managed_streams[].weather_detail": "string",
+ "managed_streams[].weather_show_location": "number",
+ "managed_streams[].last_live_at": "string",
+ "managed_streams[].is_currently_live": "number",
+ "managed_streams[].live_session_id": "number",
+ "rs_restream": "null",
+ "restream_links": "null",
+ "external_viewers": "null",
+ "vods": "array",
+ "vodTotal": "number",
+ "vodLimit": "number",
+ "vodOffset": "number",
+ "clips": "array",
+ "clipTotal": "number",
+ "clipLimit": "number",
+ "clipOffset": "number",
+ "clipsOfStreams": "array",
+ "clipsOfTotal": "number",
+ "clipsOfLimit": "number",
+ "clipsOfOffset": "number",
+ "aiClips": "array",
+ "aiClipsTotal": "number",
+ "aiClipsLimit": "number",
+ "aiClipsOffset": "number"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star?aiClipsLimit=5&aiClipsOffset=0",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "videos_tab_hidden": "boolean",
+ "clips_tab_hidden": "boolean",
+ "pasteTotal": "number",
+ "clipsTakenTotal": "number",
+ "aiEventTotal": "number",
+ "channel": "object",
+ "channel.id": "number",
+ "channel.user_id": "number",
+ "channel.title": "string",
+ "channel.description": "string",
+ "channel.category": "null",
+ "channel.tags": "string",
+ "channel.protocol": "string",
+ "channel.is_nsfw": "number",
+ "channel.panels": "string",
+ "channel.default_vod_visibility": "string",
+ "channel.default_clip_visibility": "string",
+ "channel.weather_detail": "string",
+ "channel.weather_show_location": "number",
+ "channel.control_mode": "string",
+ "channel.anon_controls_enabled": "number",
+ "channel.control_rate_limit_ms": "number",
+ "channel.video_click_enabled": "number",
+ "channel.video_click_rate_limit_ms": "number",
+ "channel.active_control_config_id": "null",
+ "channel.created_at": "string",
+ "channel.updated_at": "string",
+ "channel.clips_allow_creator_delete": "number",
+ "channel.hide_ai_overview": "number",
+ "channel.ai_overview_pref": "string",
+ "channel.ai_derivation_enabled": "number",
+ "channel.ai_category": "null",
+ "channel.offline_screen_type": "string",
+ "channel.offline_screen_url": "null",
+ "channel.offline_html": "null",
+ "channel.offline_css": "null",
+ "channel.username": "string",
+ "channel.display_name": "string",
+ "channel.avatar_url": "null",
+ "channel.profile_color": "string",
+ "channel.bio": "string",
+ "channel.role": "string",
+ "channel.is_owner": "number",
+ "channel.follower_count": "number",
+ "channel.is_following": "boolean",
+ "channel.chat_limits": "object",
+ "channel.chat_limits.max_message_length": "number",
+ "channel.chat_limits.tts_max_length": "number",
+ "channel.mods_can_edit_about": "boolean",
+ "channel.viewer_can_edit_about": "boolean",
+ "channel.ai_overview": "null",
+ "language": "object",
+ "language.code": "string",
+ "language.name": "string",
+ "language.flag": "string",
+ "language.translate": "boolean",
+ "stream": "object",
+ "stream.id": "number",
+ "stream.user_id": "number",
+ "stream.channel_id": "null",
+ "stream.managed_stream_id": "number",
+ "stream.control_config_id": "null",
+ "stream.title": "string",
+ "stream.description": "string",
+ "stream.category": "string",
+ "stream.tags": "string",
+ "stream.protocol": "string",
+ "stream.is_live": "number",
+ "stream.is_nsfw": "number",
+ "stream.viewer_count": "number",
+ "stream.peak_viewers": "number",
+ "stream.follower_count": "number",
+ "stream.thumbnail_url": "null",
+ "stream.started_at": "string",
+ "stream.ended_at": "null",
+ "stream.duration_seconds": "number",
+ "stream.created_at": "string",
+ "stream.ai_overview": "null",
+ "stream.ai_title": "null",
+ "stream.ai_category": "null",
+ "stream.ai_tags": "null",
+ "stream.ai_overview_short": "null",
+ "stream.username": "string",
+ "stream.display_name": "string",
+ "stream.avatar_url": "null",
+ "stream.profile_color": "string",
+ "stream.managed_stream_slug": "null",
+ "stream.endpoint": "object",
+ "stream.server_clip": "boolean",
+ "stream.external_viewer_count": "number",
+ "stream.total_viewer_count": "number",
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "null",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null",
+ "streams[].endpoint": "object",
+ "streams[].server_clip": "boolean",
+ "streams[].external_viewer_count": "number",
+ "streams[].total_viewer_count": "number",
+ "managed_streams": "array",
+ "managed_streams[]": "object",
+ "managed_streams[].id": "number",
+ "managed_streams[].user_id": "number",
+ "managed_streams[].channel_id": "number",
+ "managed_streams[].slug": "null",
+ "managed_streams[].title": "string",
+ "managed_streams[].description": "string",
+ "managed_streams[].category": "string",
+ "managed_streams[].tags": "string",
+ "managed_streams[].protocol": "string",
+ "managed_streams[].is_nsfw": "number",
+ "managed_streams[].control_config_id": "null",
+ "managed_streams[].sort_order": "number",
+ "managed_streams[].created_at": "string",
+ "managed_streams[].updated_at": "string",
+ "managed_streams[].slot_clip_recording_enabled": "number",
+ "managed_streams[].slot_clip_notify_enabled": "number",
+ "managed_streams[].slot_powerchat_relay": "number",
+ "managed_streams[].slot_powerchat_count_rs_views": "number",
+ "managed_streams[].streaming_method": "string",
+ "managed_streams[].browser_mode": "string",
+ "managed_streams[].default_vod_visibility": "string",
+ "managed_streams[].default_clip_visibility": "string",
+ "managed_streams[].slot_vod_recording_enabled": "number",
+ "managed_streams[].weather_detail": "string",
+ "managed_streams[].weather_show_location": "number",
+ "managed_streams[].last_live_at": "string",
+ "managed_streams[].is_currently_live": "number",
+ "managed_streams[].live_session_id": "number",
+ "rs_restream": "null",
+ "restream_links": "null",
+ "external_viewers": "null",
+ "vods": "array",
+ "vodTotal": "number",
+ "vodLimit": "number",
+ "vodOffset": "number",
+ "clips": "array",
+ "clipTotal": "number",
+ "clipLimit": "number",
+ "clipOffset": "number",
+ "clipsOfStreams": "array",
+ "clipsOfTotal": "number",
+ "clipsOfLimit": "number",
+ "clipsOfOffset": "number",
+ "aiClips": "array",
+ "aiClipsTotal": "number",
+ "aiClipsLimit": "number",
+ "aiClipsOffset": "number"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star?clipsOfLimit=5&clipsOfOffset=0",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "videos_tab_hidden": "boolean",
+ "clips_tab_hidden": "boolean",
+ "pasteTotal": "number",
+ "clipsTakenTotal": "number",
+ "aiEventTotal": "number",
+ "channel": "object",
+ "channel.id": "number",
+ "channel.user_id": "number",
+ "channel.title": "string",
+ "channel.description": "string",
+ "channel.category": "null",
+ "channel.tags": "string",
+ "channel.protocol": "string",
+ "channel.is_nsfw": "number",
+ "channel.panels": "string",
+ "channel.default_vod_visibility": "string",
+ "channel.default_clip_visibility": "string",
+ "channel.weather_detail": "string",
+ "channel.weather_show_location": "number",
+ "channel.control_mode": "string",
+ "channel.anon_controls_enabled": "number",
+ "channel.control_rate_limit_ms": "number",
+ "channel.video_click_enabled": "number",
+ "channel.video_click_rate_limit_ms": "number",
+ "channel.active_control_config_id": "null",
+ "channel.created_at": "string",
+ "channel.updated_at": "string",
+ "channel.clips_allow_creator_delete": "number",
+ "channel.hide_ai_overview": "number",
+ "channel.ai_overview_pref": "string",
+ "channel.ai_derivation_enabled": "number",
+ "channel.ai_category": "null",
+ "channel.offline_screen_type": "string",
+ "channel.offline_screen_url": "null",
+ "channel.offline_html": "null",
+ "channel.offline_css": "null",
+ "channel.username": "string",
+ "channel.display_name": "string",
+ "channel.avatar_url": "null",
+ "channel.profile_color": "string",
+ "channel.bio": "string",
+ "channel.role": "string",
+ "channel.is_owner": "number",
+ "channel.follower_count": "number",
+ "channel.is_following": "boolean",
+ "channel.chat_limits": "object",
+ "channel.chat_limits.max_message_length": "number",
+ "channel.chat_limits.tts_max_length": "number",
+ "channel.mods_can_edit_about": "boolean",
+ "channel.viewer_can_edit_about": "boolean",
+ "channel.ai_overview": "null",
+ "language": "object",
+ "language.code": "string",
+ "language.name": "string",
+ "language.flag": "string",
+ "language.translate": "boolean",
+ "stream": "object",
+ "stream.id": "number",
+ "stream.user_id": "number",
+ "stream.channel_id": "null",
+ "stream.managed_stream_id": "number",
+ "stream.control_config_id": "null",
+ "stream.title": "string",
+ "stream.description": "string",
+ "stream.category": "string",
+ "stream.tags": "string",
+ "stream.protocol": "string",
+ "stream.is_live": "number",
+ "stream.is_nsfw": "number",
+ "stream.viewer_count": "number",
+ "stream.peak_viewers": "number",
+ "stream.follower_count": "number",
+ "stream.thumbnail_url": "null",
+ "stream.started_at": "string",
+ "stream.ended_at": "null",
+ "stream.duration_seconds": "number",
+ "stream.created_at": "string",
+ "stream.ai_overview": "null",
+ "stream.ai_title": "null",
+ "stream.ai_category": "null",
+ "stream.ai_tags": "null",
+ "stream.ai_overview_short": "null",
+ "stream.username": "string",
+ "stream.display_name": "string",
+ "stream.avatar_url": "null",
+ "stream.profile_color": "string",
+ "stream.managed_stream_slug": "null",
+ "stream.endpoint": "object",
+ "stream.server_clip": "boolean",
+ "stream.external_viewer_count": "number",
+ "stream.total_viewer_count": "number",
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "null",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null",
+ "streams[].endpoint": "object",
+ "streams[].server_clip": "boolean",
+ "streams[].external_viewer_count": "number",
+ "streams[].total_viewer_count": "number",
+ "managed_streams": "array",
+ "managed_streams[]": "object",
+ "managed_streams[].id": "number",
+ "managed_streams[].user_id": "number",
+ "managed_streams[].channel_id": "number",
+ "managed_streams[].slug": "null",
+ "managed_streams[].title": "string",
+ "managed_streams[].description": "string",
+ "managed_streams[].category": "string",
+ "managed_streams[].tags": "string",
+ "managed_streams[].protocol": "string",
+ "managed_streams[].is_nsfw": "number",
+ "managed_streams[].control_config_id": "null",
+ "managed_streams[].sort_order": "number",
+ "managed_streams[].created_at": "string",
+ "managed_streams[].updated_at": "string",
+ "managed_streams[].slot_clip_recording_enabled": "number",
+ "managed_streams[].slot_clip_notify_enabled": "number",
+ "managed_streams[].slot_powerchat_relay": "number",
+ "managed_streams[].slot_powerchat_count_rs_views": "number",
+ "managed_streams[].streaming_method": "string",
+ "managed_streams[].browser_mode": "string",
+ "managed_streams[].default_vod_visibility": "string",
+ "managed_streams[].default_clip_visibility": "string",
+ "managed_streams[].slot_vod_recording_enabled": "number",
+ "managed_streams[].weather_detail": "string",
+ "managed_streams[].weather_show_location": "number",
+ "managed_streams[].last_live_at": "string",
+ "managed_streams[].is_currently_live": "number",
+ "managed_streams[].live_session_id": "number",
+ "rs_restream": "null",
+ "restream_links": "null",
+ "external_viewers": "null",
+ "vods": "array",
+ "vodTotal": "number",
+ "vodLimit": "number",
+ "vodOffset": "number",
+ "clips": "array",
+ "clipTotal": "number",
+ "clipLimit": "number",
+ "clipOffset": "number",
+ "clipsOfStreams": "array",
+ "clipsOfTotal": "number",
+ "clipsOfLimit": "number",
+ "clipsOfOffset": "number",
+ "aiClips": "array",
+ "aiClipsTotal": "number",
+ "aiClipsLimit": "number",
+ "aiClipsOffset": "number"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star?clipsOfLimit=5&clipsOfOffset=0",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "videos_tab_hidden": "boolean",
+ "clips_tab_hidden": "boolean",
+ "pasteTotal": "number",
+ "clipsTakenTotal": "number",
+ "aiEventTotal": "number",
+ "channel": "object",
+ "channel.id": "number",
+ "channel.user_id": "number",
+ "channel.title": "string",
+ "channel.description": "string",
+ "channel.category": "null",
+ "channel.tags": "string",
+ "channel.protocol": "string",
+ "channel.is_nsfw": "number",
+ "channel.panels": "string",
+ "channel.default_vod_visibility": "string",
+ "channel.default_clip_visibility": "string",
+ "channel.weather_detail": "string",
+ "channel.weather_show_location": "number",
+ "channel.control_mode": "string",
+ "channel.anon_controls_enabled": "number",
+ "channel.control_rate_limit_ms": "number",
+ "channel.video_click_enabled": "number",
+ "channel.video_click_rate_limit_ms": "number",
+ "channel.active_control_config_id": "null",
+ "channel.created_at": "string",
+ "channel.updated_at": "string",
+ "channel.clips_allow_creator_delete": "number",
+ "channel.hide_ai_overview": "number",
+ "channel.ai_overview_pref": "string",
+ "channel.ai_derivation_enabled": "number",
+ "channel.ai_category": "null",
+ "channel.offline_screen_type": "string",
+ "channel.offline_screen_url": "null",
+ "channel.offline_html": "null",
+ "channel.offline_css": "null",
+ "channel.username": "string",
+ "channel.display_name": "string",
+ "channel.avatar_url": "null",
+ "channel.profile_color": "string",
+ "channel.bio": "string",
+ "channel.role": "string",
+ "channel.is_owner": "number",
+ "channel.follower_count": "number",
+ "channel.is_following": "boolean",
+ "channel.chat_limits": "object",
+ "channel.chat_limits.max_message_length": "number",
+ "channel.chat_limits.tts_max_length": "number",
+ "channel.mods_can_edit_about": "boolean",
+ "channel.viewer_can_edit_about": "boolean",
+ "channel.ai_overview": "null",
+ "language": "object",
+ "language.code": "string",
+ "language.name": "string",
+ "language.flag": "string",
+ "language.translate": "boolean",
+ "stream": "object",
+ "stream.id": "number",
+ "stream.user_id": "number",
+ "stream.channel_id": "null",
+ "stream.managed_stream_id": "number",
+ "stream.control_config_id": "null",
+ "stream.title": "string",
+ "stream.description": "string",
+ "stream.category": "string",
+ "stream.tags": "string",
+ "stream.protocol": "string",
+ "stream.is_live": "number",
+ "stream.is_nsfw": "number",
+ "stream.viewer_count": "number",
+ "stream.peak_viewers": "number",
+ "stream.follower_count": "number",
+ "stream.thumbnail_url": "null",
+ "stream.started_at": "string",
+ "stream.ended_at": "null",
+ "stream.duration_seconds": "number",
+ "stream.created_at": "string",
+ "stream.ai_overview": "null",
+ "stream.ai_title": "null",
+ "stream.ai_category": "null",
+ "stream.ai_tags": "null",
+ "stream.ai_overview_short": "null",
+ "stream.username": "string",
+ "stream.display_name": "string",
+ "stream.avatar_url": "null",
+ "stream.profile_color": "string",
+ "stream.managed_stream_slug": "null",
+ "stream.endpoint": "object",
+ "stream.server_clip": "boolean",
+ "stream.external_viewer_count": "number",
+ "stream.total_viewer_count": "number",
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "null",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null",
+ "streams[].endpoint": "object",
+ "streams[].server_clip": "boolean",
+ "streams[].external_viewer_count": "number",
+ "streams[].total_viewer_count": "number",
+ "managed_streams": "array",
+ "managed_streams[]": "object",
+ "managed_streams[].id": "number",
+ "managed_streams[].user_id": "number",
+ "managed_streams[].channel_id": "number",
+ "managed_streams[].slug": "null",
+ "managed_streams[].title": "string",
+ "managed_streams[].description": "string",
+ "managed_streams[].category": "string",
+ "managed_streams[].tags": "string",
+ "managed_streams[].protocol": "string",
+ "managed_streams[].is_nsfw": "number",
+ "managed_streams[].control_config_id": "null",
+ "managed_streams[].sort_order": "number",
+ "managed_streams[].created_at": "string",
+ "managed_streams[].updated_at": "string",
+ "managed_streams[].slot_clip_recording_enabled": "number",
+ "managed_streams[].slot_clip_notify_enabled": "number",
+ "managed_streams[].slot_powerchat_relay": "number",
+ "managed_streams[].slot_powerchat_count_rs_views": "number",
+ "managed_streams[].streaming_method": "string",
+ "managed_streams[].browser_mode": "string",
+ "managed_streams[].default_vod_visibility": "string",
+ "managed_streams[].default_clip_visibility": "string",
+ "managed_streams[].slot_vod_recording_enabled": "number",
+ "managed_streams[].weather_detail": "string",
+ "managed_streams[].weather_show_location": "number",
+ "managed_streams[].last_live_at": "string",
+ "managed_streams[].is_currently_live": "number",
+ "managed_streams[].live_session_id": "number",
+ "rs_restream": "null",
+ "restream_links": "null",
+ "external_viewers": "null",
+ "vods": "array",
+ "vodTotal": "number",
+ "vodLimit": "number",
+ "vodOffset": "number",
+ "clips": "array",
+ "clipTotal": "number",
+ "clipLimit": "number",
+ "clipOffset": "number",
+ "clipsOfStreams": "array",
+ "clipsOfTotal": "number",
+ "clipsOfLimit": "number",
+ "clipsOfOffset": "number",
+ "aiClips": "array",
+ "aiClipsTotal": "number",
+ "aiClipsLimit": "number",
+ "aiClipsOffset": "number"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star?vodLimit=5&vodOffset=0&clipLimit=5&clipOffset=0",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "videos_tab_hidden": "boolean",
+ "clips_tab_hidden": "boolean",
+ "pasteTotal": "number",
+ "clipsTakenTotal": "number",
+ "aiEventTotal": "number",
+ "channel": "object",
+ "channel.id": "number",
+ "channel.user_id": "number",
+ "channel.title": "string",
+ "channel.description": "string",
+ "channel.category": "null",
+ "channel.tags": "string",
+ "channel.protocol": "string",
+ "channel.is_nsfw": "number",
+ "channel.panels": "string",
+ "channel.default_vod_visibility": "string",
+ "channel.default_clip_visibility": "string",
+ "channel.weather_detail": "string",
+ "channel.weather_show_location": "number",
+ "channel.control_mode": "string",
+ "channel.anon_controls_enabled": "number",
+ "channel.control_rate_limit_ms": "number",
+ "channel.video_click_enabled": "number",
+ "channel.video_click_rate_limit_ms": "number",
+ "channel.active_control_config_id": "null",
+ "channel.created_at": "string",
+ "channel.updated_at": "string",
+ "channel.clips_allow_creator_delete": "number",
+ "channel.hide_ai_overview": "number",
+ "channel.ai_overview_pref": "string",
+ "channel.ai_derivation_enabled": "number",
+ "channel.ai_category": "null",
+ "channel.offline_screen_type": "string",
+ "channel.offline_screen_url": "null",
+ "channel.offline_html": "null",
+ "channel.offline_css": "null",
+ "channel.username": "string",
+ "channel.display_name": "string",
+ "channel.avatar_url": "null",
+ "channel.profile_color": "string",
+ "channel.bio": "string",
+ "channel.role": "string",
+ "channel.is_owner": "number",
+ "channel.follower_count": "number",
+ "channel.is_following": "boolean",
+ "channel.chat_limits": "object",
+ "channel.chat_limits.max_message_length": "number",
+ "channel.chat_limits.tts_max_length": "number",
+ "channel.mods_can_edit_about": "boolean",
+ "channel.viewer_can_edit_about": "boolean",
+ "channel.ai_overview": "null",
+ "language": "object",
+ "language.code": "string",
+ "language.name": "string",
+ "language.flag": "string",
+ "language.translate": "boolean",
+ "stream": "object",
+ "stream.id": "number",
+ "stream.user_id": "number",
+ "stream.channel_id": "null",
+ "stream.managed_stream_id": "number",
+ "stream.control_config_id": "null",
+ "stream.title": "string",
+ "stream.description": "string",
+ "stream.category": "string",
+ "stream.tags": "string",
+ "stream.protocol": "string",
+ "stream.is_live": "number",
+ "stream.is_nsfw": "number",
+ "stream.viewer_count": "number",
+ "stream.peak_viewers": "number",
+ "stream.follower_count": "number",
+ "stream.thumbnail_url": "null",
+ "stream.started_at": "string",
+ "stream.ended_at": "null",
+ "stream.duration_seconds": "number",
+ "stream.created_at": "string",
+ "stream.ai_overview": "null",
+ "stream.ai_title": "null",
+ "stream.ai_category": "null",
+ "stream.ai_tags": "null",
+ "stream.ai_overview_short": "null",
+ "stream.username": "string",
+ "stream.display_name": "string",
+ "stream.avatar_url": "null",
+ "stream.profile_color": "string",
+ "stream.managed_stream_slug": "null",
+ "stream.endpoint": "object",
+ "stream.server_clip": "boolean",
+ "stream.external_viewer_count": "number",
+ "stream.total_viewer_count": "number",
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "null",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null",
+ "streams[].endpoint": "object",
+ "streams[].server_clip": "boolean",
+ "streams[].external_viewer_count": "number",
+ "streams[].total_viewer_count": "number",
+ "managed_streams": "array",
+ "managed_streams[]": "object",
+ "managed_streams[].id": "number",
+ "managed_streams[].user_id": "number",
+ "managed_streams[].channel_id": "number",
+ "managed_streams[].slug": "null",
+ "managed_streams[].title": "string",
+ "managed_streams[].description": "string",
+ "managed_streams[].category": "string",
+ "managed_streams[].tags": "string",
+ "managed_streams[].protocol": "string",
+ "managed_streams[].is_nsfw": "number",
+ "managed_streams[].control_config_id": "null",
+ "managed_streams[].sort_order": "number",
+ "managed_streams[].created_at": "string",
+ "managed_streams[].updated_at": "string",
+ "managed_streams[].slot_clip_recording_enabled": "number",
+ "managed_streams[].slot_clip_notify_enabled": "number",
+ "managed_streams[].slot_powerchat_relay": "number",
+ "managed_streams[].slot_powerchat_count_rs_views": "number",
+ "managed_streams[].streaming_method": "string",
+ "managed_streams[].browser_mode": "string",
+ "managed_streams[].default_vod_visibility": "string",
+ "managed_streams[].default_clip_visibility": "string",
+ "managed_streams[].slot_vod_recording_enabled": "number",
+ "managed_streams[].weather_detail": "string",
+ "managed_streams[].weather_show_location": "number",
+ "managed_streams[].last_live_at": "string",
+ "managed_streams[].is_currently_live": "number",
+ "managed_streams[].live_session_id": "number",
+ "rs_restream": "null",
+ "restream_links": "null",
+ "external_viewers": "null",
+ "vods": "array",
+ "vodTotal": "number",
+ "vodLimit": "number",
+ "vodOffset": "number",
+ "clips": "array",
+ "clipTotal": "number",
+ "clipLimit": "number",
+ "clipOffset": "number",
+ "clipsOfStreams": "array",
+ "clipsOfTotal": "number",
+ "clipsOfLimit": "number",
+ "clipsOfOffset": "number",
+ "aiClips": "array",
+ "aiClipsTotal": "number",
+ "aiClipsLimit": "number",
+ "aiClipsOffset": "number"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star?vodLimit=5&vodOffset=0&clipLimit=5&clipOffset=0",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "videos_tab_hidden": "boolean",
+ "clips_tab_hidden": "boolean",
+ "pasteTotal": "number",
+ "clipsTakenTotal": "number",
+ "aiEventTotal": "number",
+ "channel": "object",
+ "channel.id": "number",
+ "channel.user_id": "number",
+ "channel.title": "string",
+ "channel.description": "string",
+ "channel.category": "null",
+ "channel.tags": "string",
+ "channel.protocol": "string",
+ "channel.is_nsfw": "number",
+ "channel.panels": "string",
+ "channel.default_vod_visibility": "string",
+ "channel.default_clip_visibility": "string",
+ "channel.weather_detail": "string",
+ "channel.weather_show_location": "number",
+ "channel.control_mode": "string",
+ "channel.anon_controls_enabled": "number",
+ "channel.control_rate_limit_ms": "number",
+ "channel.video_click_enabled": "number",
+ "channel.video_click_rate_limit_ms": "number",
+ "channel.active_control_config_id": "null",
+ "channel.created_at": "string",
+ "channel.updated_at": "string",
+ "channel.clips_allow_creator_delete": "number",
+ "channel.hide_ai_overview": "number",
+ "channel.ai_overview_pref": "string",
+ "channel.ai_derivation_enabled": "number",
+ "channel.ai_category": "null",
+ "channel.offline_screen_type": "string",
+ "channel.offline_screen_url": "null",
+ "channel.offline_html": "null",
+ "channel.offline_css": "null",
+ "channel.username": "string",
+ "channel.display_name": "string",
+ "channel.avatar_url": "null",
+ "channel.profile_color": "string",
+ "channel.bio": "string",
+ "channel.role": "string",
+ "channel.is_owner": "number",
+ "channel.follower_count": "number",
+ "channel.is_following": "boolean",
+ "channel.chat_limits": "object",
+ "channel.chat_limits.max_message_length": "number",
+ "channel.chat_limits.tts_max_length": "number",
+ "channel.mods_can_edit_about": "boolean",
+ "channel.viewer_can_edit_about": "boolean",
+ "channel.ai_overview": "null",
+ "language": "object",
+ "language.code": "string",
+ "language.name": "string",
+ "language.flag": "string",
+ "language.translate": "boolean",
+ "stream": "object",
+ "stream.id": "number",
+ "stream.user_id": "number",
+ "stream.channel_id": "null",
+ "stream.managed_stream_id": "number",
+ "stream.control_config_id": "null",
+ "stream.title": "string",
+ "stream.description": "string",
+ "stream.category": "string",
+ "stream.tags": "string",
+ "stream.protocol": "string",
+ "stream.is_live": "number",
+ "stream.is_nsfw": "number",
+ "stream.viewer_count": "number",
+ "stream.peak_viewers": "number",
+ "stream.follower_count": "number",
+ "stream.thumbnail_url": "null",
+ "stream.started_at": "string",
+ "stream.ended_at": "null",
+ "stream.duration_seconds": "number",
+ "stream.created_at": "string",
+ "stream.ai_overview": "null",
+ "stream.ai_title": "null",
+ "stream.ai_category": "null",
+ "stream.ai_tags": "null",
+ "stream.ai_overview_short": "null",
+ "stream.username": "string",
+ "stream.display_name": "string",
+ "stream.avatar_url": "null",
+ "stream.profile_color": "string",
+ "stream.managed_stream_slug": "null",
+ "stream.endpoint": "object",
+ "stream.server_clip": "boolean",
+ "stream.external_viewer_count": "number",
+ "stream.total_viewer_count": "number",
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "null",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null",
+ "streams[].endpoint": "object",
+ "streams[].server_clip": "boolean",
+ "streams[].external_viewer_count": "number",
+ "streams[].total_viewer_count": "number",
+ "managed_streams": "array",
+ "managed_streams[]": "object",
+ "managed_streams[].id": "number",
+ "managed_streams[].user_id": "number",
+ "managed_streams[].channel_id": "number",
+ "managed_streams[].slug": "null",
+ "managed_streams[].title": "string",
+ "managed_streams[].description": "string",
+ "managed_streams[].category": "string",
+ "managed_streams[].tags": "string",
+ "managed_streams[].protocol": "string",
+ "managed_streams[].is_nsfw": "number",
+ "managed_streams[].control_config_id": "null",
+ "managed_streams[].sort_order": "number",
+ "managed_streams[].created_at": "string",
+ "managed_streams[].updated_at": "string",
+ "managed_streams[].slot_clip_recording_enabled": "number",
+ "managed_streams[].slot_clip_notify_enabled": "number",
+ "managed_streams[].slot_powerchat_relay": "number",
+ "managed_streams[].slot_powerchat_count_rs_views": "number",
+ "managed_streams[].streaming_method": "string",
+ "managed_streams[].browser_mode": "string",
+ "managed_streams[].default_vod_visibility": "string",
+ "managed_streams[].default_clip_visibility": "string",
+ "managed_streams[].slot_vod_recording_enabled": "number",
+ "managed_streams[].weather_detail": "string",
+ "managed_streams[].weather_show_location": "number",
+ "managed_streams[].last_live_at": "string",
+ "managed_streams[].is_currently_live": "number",
+ "managed_streams[].live_session_id": "number",
+ "rs_restream": "null",
+ "restream_links": "null",
+ "external_viewers": "null",
+ "vods": "array",
+ "vodTotal": "number",
+ "vodLimit": "number",
+ "vodOffset": "number",
+ "clips": "array",
+ "clipTotal": "number",
+ "clipLimit": "number",
+ "clipOffset": "number",
+ "clipsOfStreams": "array",
+ "clipsOfTotal": "number",
+ "clipsOfLimit": "number",
+ "clipsOfOffset": "number",
+ "aiClips": "array",
+ "aiClipsTotal": "number",
+ "aiClipsLimit": "number",
+ "aiClipsOffset": "number"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star/live",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "channel": "object",
+ "channel.username": "string",
+ "channel.display_name": "string",
+ "channel.user_id": "number",
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "null",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null",
+ "streams[].endpoint": "object",
+ "streams[].server_clip": "boolean"
+ },
+ "from": [
+ "public/popout-chat.html"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star/live",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "channel": "object",
+ "channel.username": "string",
+ "channel.display_name": "string",
+ "channel.user_id": "number",
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "null",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null",
+ "streams[].endpoint": "object",
+ "streams[].server_clip": "boolean"
+ },
+ "from": [
+ "public/popout-chat.html"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "videos_tab_hidden": "boolean",
+ "clips_tab_hidden": "boolean",
+ "pasteTotal": "number",
+ "clipsTakenTotal": "number",
+ "aiEventTotal": "number",
+ "channel": "object",
+ "channel.id": "number",
+ "channel.user_id": "number",
+ "channel.title": "string",
+ "channel.description": "string",
+ "channel.category": "null",
+ "channel.tags": "string",
+ "channel.protocol": "string",
+ "channel.is_nsfw": "number",
+ "channel.panels": "string",
+ "channel.default_vod_visibility": "string",
+ "channel.default_clip_visibility": "string",
+ "channel.weather_detail": "string",
+ "channel.weather_show_location": "number",
+ "channel.control_mode": "string",
+ "channel.anon_controls_enabled": "number",
+ "channel.control_rate_limit_ms": "number",
+ "channel.video_click_enabled": "number",
+ "channel.video_click_rate_limit_ms": "number",
+ "channel.active_control_config_id": "null",
+ "channel.created_at": "string",
+ "channel.updated_at": "string",
+ "channel.clips_allow_creator_delete": "number",
+ "channel.hide_ai_overview": "number",
+ "channel.ai_overview_pref": "string",
+ "channel.ai_derivation_enabled": "number",
+ "channel.ai_category": "null",
+ "channel.offline_screen_type": "string",
+ "channel.offline_screen_url": "null",
+ "channel.offline_html": "null",
+ "channel.offline_css": "null",
+ "channel.username": "string",
+ "channel.display_name": "string",
+ "channel.avatar_url": "null",
+ "channel.profile_color": "string",
+ "channel.bio": "string",
+ "channel.role": "string",
+ "channel.is_owner": "number",
+ "channel.follower_count": "number",
+ "channel.is_following": "boolean",
+ "channel.chat_limits": "object",
+ "channel.chat_limits.max_message_length": "number",
+ "channel.chat_limits.tts_max_length": "number",
+ "channel.mods_can_edit_about": "boolean",
+ "channel.viewer_can_edit_about": "boolean",
+ "channel.ai_overview": "null",
+ "language": "object",
+ "language.code": "string",
+ "language.name": "string",
+ "language.flag": "string",
+ "language.translate": "boolean",
+ "stream": "object",
+ "stream.id": "number",
+ "stream.user_id": "number",
+ "stream.channel_id": "null",
+ "stream.managed_stream_id": "number",
+ "stream.control_config_id": "null",
+ "stream.title": "string",
+ "stream.description": "string",
+ "stream.category": "string",
+ "stream.tags": "string",
+ "stream.protocol": "string",
+ "stream.is_live": "number",
+ "stream.is_nsfw": "number",
+ "stream.viewer_count": "number",
+ "stream.peak_viewers": "number",
+ "stream.follower_count": "number",
+ "stream.thumbnail_url": "null",
+ "stream.started_at": "string",
+ "stream.ended_at": "null",
+ "stream.duration_seconds": "number",
+ "stream.created_at": "string",
+ "stream.ai_overview": "null",
+ "stream.ai_title": "null",
+ "stream.ai_category": "null",
+ "stream.ai_tags": "null",
+ "stream.ai_overview_short": "null",
+ "stream.username": "string",
+ "stream.display_name": "string",
+ "stream.avatar_url": "null",
+ "stream.profile_color": "string",
+ "stream.managed_stream_slug": "null",
+ "stream.endpoint": "object",
+ "stream.server_clip": "boolean",
+ "stream.external_viewer_count": "number",
+ "stream.total_viewer_count": "number",
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "null",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null",
+ "streams[].endpoint": "object",
+ "streams[].server_clip": "boolean",
+ "streams[].external_viewer_count": "number",
+ "streams[].total_viewer_count": "number",
+ "managed_streams": "array",
+ "managed_streams[]": "object",
+ "managed_streams[].id": "number",
+ "managed_streams[].user_id": "number",
+ "managed_streams[].channel_id": "number",
+ "managed_streams[].slug": "null",
+ "managed_streams[].title": "string",
+ "managed_streams[].description": "string",
+ "managed_streams[].category": "string",
+ "managed_streams[].tags": "string",
+ "managed_streams[].protocol": "string",
+ "managed_streams[].is_nsfw": "number",
+ "managed_streams[].control_config_id": "null",
+ "managed_streams[].sort_order": "number",
+ "managed_streams[].created_at": "string",
+ "managed_streams[].updated_at": "string",
+ "managed_streams[].slot_clip_recording_enabled": "number",
+ "managed_streams[].slot_clip_notify_enabled": "number",
+ "managed_streams[].slot_powerchat_relay": "number",
+ "managed_streams[].slot_powerchat_count_rs_views": "number",
+ "managed_streams[].streaming_method": "string",
+ "managed_streams[].browser_mode": "string",
+ "managed_streams[].default_vod_visibility": "string",
+ "managed_streams[].default_clip_visibility": "string",
+ "managed_streams[].slot_vod_recording_enabled": "number",
+ "managed_streams[].weather_detail": "string",
+ "managed_streams[].weather_show_location": "number",
+ "managed_streams[].last_live_at": "string",
+ "managed_streams[].is_currently_live": "number",
+ "managed_streams[].live_session_id": "number",
+ "rs_restream": "null",
+ "restream_links": "null",
+ "external_viewers": "null",
+ "vods": "array",
+ "vodTotal": "number",
+ "vodLimit": "number",
+ "vodOffset": "number",
+ "clips": "array",
+ "clipTotal": "number",
+ "clipLimit": "number",
+ "clipOffset": "number",
+ "clipsOfStreams": "array",
+ "clipsOfTotal": "number",
+ "clipsOfLimit": "number",
+ "clipsOfOffset": "number",
+ "aiClips": "array",
+ "aiClipsTotal": "number",
+ "aiClipsLimit": "number",
+ "aiClipsOffset": "number"
+ },
+ "from": [
+ "public/obs/chat.html"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "videos_tab_hidden": "boolean",
+ "clips_tab_hidden": "boolean",
+ "pasteTotal": "number",
+ "clipsTakenTotal": "number",
+ "aiEventTotal": "number",
+ "channel": "object",
+ "channel.id": "number",
+ "channel.user_id": "number",
+ "channel.title": "string",
+ "channel.description": "string",
+ "channel.category": "null",
+ "channel.tags": "string",
+ "channel.protocol": "string",
+ "channel.is_nsfw": "number",
+ "channel.panels": "string",
+ "channel.default_vod_visibility": "string",
+ "channel.default_clip_visibility": "string",
+ "channel.weather_detail": "string",
+ "channel.weather_show_location": "number",
+ "channel.control_mode": "string",
+ "channel.anon_controls_enabled": "number",
+ "channel.control_rate_limit_ms": "number",
+ "channel.video_click_enabled": "number",
+ "channel.video_click_rate_limit_ms": "number",
+ "channel.active_control_config_id": "null",
+ "channel.created_at": "string",
+ "channel.updated_at": "string",
+ "channel.clips_allow_creator_delete": "number",
+ "channel.hide_ai_overview": "number",
+ "channel.ai_overview_pref": "string",
+ "channel.ai_derivation_enabled": "number",
+ "channel.ai_category": "null",
+ "channel.offline_screen_type": "string",
+ "channel.offline_screen_url": "null",
+ "channel.offline_html": "null",
+ "channel.offline_css": "null",
+ "channel.username": "string",
+ "channel.display_name": "string",
+ "channel.avatar_url": "null",
+ "channel.profile_color": "string",
+ "channel.bio": "string",
+ "channel.role": "string",
+ "channel.is_owner": "number",
+ "channel.follower_count": "number",
+ "channel.is_following": "boolean",
+ "channel.chat_limits": "object",
+ "channel.chat_limits.max_message_length": "number",
+ "channel.chat_limits.tts_max_length": "number",
+ "channel.mods_can_edit_about": "boolean",
+ "channel.viewer_can_edit_about": "boolean",
+ "channel.ai_overview": "null",
+ "language": "object",
+ "language.code": "string",
+ "language.name": "string",
+ "language.flag": "string",
+ "language.translate": "boolean",
+ "stream": "object",
+ "stream.id": "number",
+ "stream.user_id": "number",
+ "stream.channel_id": "null",
+ "stream.managed_stream_id": "number",
+ "stream.control_config_id": "null",
+ "stream.title": "string",
+ "stream.description": "string",
+ "stream.category": "string",
+ "stream.tags": "string",
+ "stream.protocol": "string",
+ "stream.is_live": "number",
+ "stream.is_nsfw": "number",
+ "stream.viewer_count": "number",
+ "stream.peak_viewers": "number",
+ "stream.follower_count": "number",
+ "stream.thumbnail_url": "null",
+ "stream.started_at": "string",
+ "stream.ended_at": "null",
+ "stream.duration_seconds": "number",
+ "stream.created_at": "string",
+ "stream.ai_overview": "null",
+ "stream.ai_title": "null",
+ "stream.ai_category": "null",
+ "stream.ai_tags": "null",
+ "stream.ai_overview_short": "null",
+ "stream.username": "string",
+ "stream.display_name": "string",
+ "stream.avatar_url": "null",
+ "stream.profile_color": "string",
+ "stream.managed_stream_slug": "null",
+ "stream.endpoint": "object",
+ "stream.server_clip": "boolean",
+ "stream.external_viewer_count": "number",
+ "stream.total_viewer_count": "number",
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "null",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null",
+ "streams[].endpoint": "object",
+ "streams[].server_clip": "boolean",
+ "streams[].external_viewer_count": "number",
+ "streams[].total_viewer_count": "number",
+ "managed_streams": "array",
+ "managed_streams[]": "object",
+ "managed_streams[].id": "number",
+ "managed_streams[].user_id": "number",
+ "managed_streams[].channel_id": "number",
+ "managed_streams[].slug": "null",
+ "managed_streams[].title": "string",
+ "managed_streams[].description": "string",
+ "managed_streams[].category": "string",
+ "managed_streams[].tags": "string",
+ "managed_streams[].protocol": "string",
+ "managed_streams[].is_nsfw": "number",
+ "managed_streams[].control_config_id": "null",
+ "managed_streams[].sort_order": "number",
+ "managed_streams[].created_at": "string",
+ "managed_streams[].updated_at": "string",
+ "managed_streams[].slot_clip_recording_enabled": "number",
+ "managed_streams[].slot_clip_notify_enabled": "number",
+ "managed_streams[].slot_powerchat_relay": "number",
+ "managed_streams[].slot_powerchat_count_rs_views": "number",
+ "managed_streams[].streaming_method": "string",
+ "managed_streams[].browser_mode": "string",
+ "managed_streams[].default_vod_visibility": "string",
+ "managed_streams[].default_clip_visibility": "string",
+ "managed_streams[].slot_vod_recording_enabled": "number",
+ "managed_streams[].weather_detail": "string",
+ "managed_streams[].weather_show_location": "number",
+ "managed_streams[].last_live_at": "string",
+ "managed_streams[].is_currently_live": "number",
+ "managed_streams[].live_session_id": "number",
+ "rs_restream": "null",
+ "restream_links": "null",
+ "external_viewers": "null",
+ "vods": "array",
+ "vodTotal": "number",
+ "vodLimit": "number",
+ "vodOffset": "number",
+ "clips": "array",
+ "clipTotal": "number",
+ "clipLimit": "number",
+ "clipOffset": "number",
+ "clipsOfStreams": "array",
+ "clipsOfTotal": "number",
+ "clipsOfLimit": "number",
+ "clipsOfOffset": "number",
+ "aiClips": "array",
+ "aiClipsTotal": "number",
+ "aiClipsLimit": "number",
+ "aiClipsOffset": "number"
+ },
+ "from": [
+ "public/obs/chat.html"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star/bio-en",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "from": "null",
+ "to": "string",
+ "text": "null"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star/bio-en",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "from": "null",
+ "to": "string",
+ "text": "null"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star/clips-taken",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "clips": "array",
+ "total": "number",
+ "facets": "array",
+ "limit": "number",
+ "offset": "number",
+ "hasMore": "boolean",
+ "sort": "string",
+ "of": "null",
+ "includeSelf": "boolean"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star/clips-taken",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "clips": "array",
+ "total": "number",
+ "facets": "array",
+ "limit": "number",
+ "offset": "number",
+ "hasMore": "boolean",
+ "sort": "string",
+ "of": "null",
+ "includeSelf": "boolean"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star/popular",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "vod": "null",
+ "clip": "null",
+ "vods": "array",
+ "clips": "array",
+ "ranges": "object",
+ "ranges.week": "object",
+ "ranges.week.vod": "null",
+ "ranges.week.clip": "null",
+ "ranges.month": "object",
+ "ranges.month.vod": "null",
+ "ranges.month.clip": "null",
+ "ranges.all": "object",
+ "ranges.all.vod": "null",
+ "ranges.all.clip": "null"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star/popular",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "vod": "null",
+ "clip": "null",
+ "vods": "array",
+ "clips": "array",
+ "ranges": "object",
+ "ranges.week": "object",
+ "ranges.week.vod": "null",
+ "ranges.week.clip": "null",
+ "ranges.month": "object",
+ "ranges.month.vod": "null",
+ "ranges.month.clip": "null",
+ "ranges.all": "object",
+ "ranges.all.vod": "null",
+ "ranges.all.clip": "null"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star?pollOnly=1",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "videos_tab_hidden": "boolean",
+ "clips_tab_hidden": "boolean",
+ "pasteTotal": "number",
+ "clipsTakenTotal": "number",
+ "aiEventTotal": "number",
+ "channel": "object",
+ "channel.id": "number",
+ "channel.user_id": "number",
+ "channel.title": "string",
+ "channel.description": "string",
+ "channel.category": "null",
+ "channel.tags": "string",
+ "channel.protocol": "string",
+ "channel.is_nsfw": "number",
+ "channel.panels": "string",
+ "channel.default_vod_visibility": "string",
+ "channel.default_clip_visibility": "string",
+ "channel.weather_detail": "string",
+ "channel.weather_show_location": "number",
+ "channel.control_mode": "string",
+ "channel.anon_controls_enabled": "number",
+ "channel.control_rate_limit_ms": "number",
+ "channel.video_click_enabled": "number",
+ "channel.video_click_rate_limit_ms": "number",
+ "channel.active_control_config_id": "null",
+ "channel.created_at": "string",
+ "channel.updated_at": "string",
+ "channel.clips_allow_creator_delete": "number",
+ "channel.hide_ai_overview": "number",
+ "channel.ai_overview_pref": "string",
+ "channel.ai_derivation_enabled": "number",
+ "channel.ai_category": "null",
+ "channel.offline_screen_type": "string",
+ "channel.offline_screen_url": "null",
+ "channel.offline_html": "null",
+ "channel.offline_css": "null",
+ "channel.username": "string",
+ "channel.display_name": "string",
+ "channel.avatar_url": "null",
+ "channel.profile_color": "string",
+ "channel.bio": "string",
+ "channel.role": "string",
+ "channel.is_owner": "number",
+ "channel.follower_count": "number",
+ "channel.is_following": "boolean",
+ "channel.chat_limits": "object",
+ "channel.chat_limits.max_message_length": "number",
+ "channel.chat_limits.tts_max_length": "number",
+ "channel.mods_can_edit_about": "boolean",
+ "channel.viewer_can_edit_about": "boolean",
+ "channel.ai_overview": "null",
+ "language": "object",
+ "language.code": "string",
+ "language.name": "string",
+ "language.flag": "string",
+ "language.translate": "boolean",
+ "stream": "object",
+ "stream.id": "number",
+ "stream.user_id": "number",
+ "stream.channel_id": "null",
+ "stream.managed_stream_id": "number",
+ "stream.control_config_id": "null",
+ "stream.title": "string",
+ "stream.description": "string",
+ "stream.category": "string",
+ "stream.tags": "string",
+ "stream.protocol": "string",
+ "stream.is_live": "number",
+ "stream.is_nsfw": "number",
+ "stream.viewer_count": "number",
+ "stream.peak_viewers": "number",
+ "stream.follower_count": "number",
+ "stream.thumbnail_url": "null",
+ "stream.started_at": "string",
+ "stream.ended_at": "null",
+ "stream.duration_seconds": "number",
+ "stream.created_at": "string",
+ "stream.ai_overview": "null",
+ "stream.ai_title": "null",
+ "stream.ai_category": "null",
+ "stream.ai_tags": "null",
+ "stream.ai_overview_short": "null",
+ "stream.username": "string",
+ "stream.display_name": "string",
+ "stream.avatar_url": "null",
+ "stream.profile_color": "string",
+ "stream.managed_stream_slug": "null",
+ "stream.endpoint": "object",
+ "stream.server_clip": "boolean",
+ "stream.external_viewer_count": "number",
+ "stream.total_viewer_count": "number",
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "null",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null",
+ "streams[].endpoint": "object",
+ "streams[].server_clip": "boolean",
+ "streams[].external_viewer_count": "number",
+ "streams[].total_viewer_count": "number",
+ "managed_streams": "array",
+ "managed_streams[]": "object",
+ "managed_streams[].id": "number",
+ "managed_streams[].user_id": "number",
+ "managed_streams[].channel_id": "number",
+ "managed_streams[].slug": "null",
+ "managed_streams[].title": "string",
+ "managed_streams[].description": "string",
+ "managed_streams[].category": "string",
+ "managed_streams[].tags": "string",
+ "managed_streams[].protocol": "string",
+ "managed_streams[].is_nsfw": "number",
+ "managed_streams[].control_config_id": "null",
+ "managed_streams[].sort_order": "number",
+ "managed_streams[].created_at": "string",
+ "managed_streams[].updated_at": "string",
+ "managed_streams[].slot_clip_recording_enabled": "number",
+ "managed_streams[].slot_clip_notify_enabled": "number",
+ "managed_streams[].slot_powerchat_relay": "number",
+ "managed_streams[].slot_powerchat_count_rs_views": "number",
+ "managed_streams[].streaming_method": "string",
+ "managed_streams[].browser_mode": "string",
+ "managed_streams[].default_vod_visibility": "string",
+ "managed_streams[].default_clip_visibility": "string",
+ "managed_streams[].slot_vod_recording_enabled": "number",
+ "managed_streams[].weather_detail": "string",
+ "managed_streams[].weather_show_location": "number",
+ "managed_streams[].last_live_at": "string",
+ "managed_streams[].is_currently_live": "number",
+ "managed_streams[].live_session_id": "number",
+ "rs_restream": "null",
+ "restream_links": "null",
+ "external_viewers": "null",
+ "vods": "array",
+ "vodTotal": "number",
+ "vodLimit": "number",
+ "vodOffset": "number",
+ "clips": "array",
+ "clipTotal": "number",
+ "clipLimit": "number",
+ "clipOffset": "number",
+ "clipsOfStreams": "array",
+ "clipsOfTotal": "number",
+ "clipsOfLimit": "number",
+ "clipsOfOffset": "number",
+ "aiClips": "array",
+ "aiClipsTotal": "number",
+ "aiClipsLimit": "number",
+ "aiClipsOffset": "number"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/channel/n1star?pollOnly=1",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "videos_tab_hidden": "boolean",
+ "clips_tab_hidden": "boolean",
+ "pasteTotal": "number",
+ "clipsTakenTotal": "number",
+ "aiEventTotal": "number",
+ "channel": "object",
+ "channel.id": "number",
+ "channel.user_id": "number",
+ "channel.title": "string",
+ "channel.description": "string",
+ "channel.category": "null",
+ "channel.tags": "string",
+ "channel.protocol": "string",
+ "channel.is_nsfw": "number",
+ "channel.panels": "string",
+ "channel.default_vod_visibility": "string",
+ "channel.default_clip_visibility": "string",
+ "channel.weather_detail": "string",
+ "channel.weather_show_location": "number",
+ "channel.control_mode": "string",
+ "channel.anon_controls_enabled": "number",
+ "channel.control_rate_limit_ms": "number",
+ "channel.video_click_enabled": "number",
+ "channel.video_click_rate_limit_ms": "number",
+ "channel.active_control_config_id": "null",
+ "channel.created_at": "string",
+ "channel.updated_at": "string",
+ "channel.clips_allow_creator_delete": "number",
+ "channel.hide_ai_overview": "number",
+ "channel.ai_overview_pref": "string",
+ "channel.ai_derivation_enabled": "number",
+ "channel.ai_category": "null",
+ "channel.offline_screen_type": "string",
+ "channel.offline_screen_url": "null",
+ "channel.offline_html": "null",
+ "channel.offline_css": "null",
+ "channel.username": "string",
+ "channel.display_name": "string",
+ "channel.avatar_url": "null",
+ "channel.profile_color": "string",
+ "channel.bio": "string",
+ "channel.role": "string",
+ "channel.is_owner": "number",
+ "channel.follower_count": "number",
+ "channel.is_following": "boolean",
+ "channel.chat_limits": "object",
+ "channel.chat_limits.max_message_length": "number",
+ "channel.chat_limits.tts_max_length": "number",
+ "channel.mods_can_edit_about": "boolean",
+ "channel.viewer_can_edit_about": "boolean",
+ "channel.ai_overview": "null",
+ "language": "object",
+ "language.code": "string",
+ "language.name": "string",
+ "language.flag": "string",
+ "language.translate": "boolean",
+ "stream": "object",
+ "stream.id": "number",
+ "stream.user_id": "number",
+ "stream.channel_id": "null",
+ "stream.managed_stream_id": "number",
+ "stream.control_config_id": "null",
+ "stream.title": "string",
+ "stream.description": "string",
+ "stream.category": "string",
+ "stream.tags": "string",
+ "stream.protocol": "string",
+ "stream.is_live": "number",
+ "stream.is_nsfw": "number",
+ "stream.viewer_count": "number",
+ "stream.peak_viewers": "number",
+ "stream.follower_count": "number",
+ "stream.thumbnail_url": "null",
+ "stream.started_at": "string",
+ "stream.ended_at": "null",
+ "stream.duration_seconds": "number",
+ "stream.created_at": "string",
+ "stream.ai_overview": "null",
+ "stream.ai_title": "null",
+ "stream.ai_category": "null",
+ "stream.ai_tags": "null",
+ "stream.ai_overview_short": "null",
+ "stream.username": "string",
+ "stream.display_name": "string",
+ "stream.avatar_url": "null",
+ "stream.profile_color": "string",
+ "stream.managed_stream_slug": "null",
+ "stream.endpoint": "object",
+ "stream.server_clip": "boolean",
+ "stream.external_viewer_count": "number",
+ "stream.total_viewer_count": "number",
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "null",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null",
+ "streams[].endpoint": "object",
+ "streams[].server_clip": "boolean",
+ "streams[].external_viewer_count": "number",
+ "streams[].total_viewer_count": "number",
+ "managed_streams": "array",
+ "managed_streams[]": "object",
+ "managed_streams[].id": "number",
+ "managed_streams[].user_id": "number",
+ "managed_streams[].channel_id": "number",
+ "managed_streams[].slug": "null",
+ "managed_streams[].title": "string",
+ "managed_streams[].description": "string",
+ "managed_streams[].category": "string",
+ "managed_streams[].tags": "string",
+ "managed_streams[].protocol": "string",
+ "managed_streams[].is_nsfw": "number",
+ "managed_streams[].control_config_id": "null",
+ "managed_streams[].sort_order": "number",
+ "managed_streams[].created_at": "string",
+ "managed_streams[].updated_at": "string",
+ "managed_streams[].slot_clip_recording_enabled": "number",
+ "managed_streams[].slot_clip_notify_enabled": "number",
+ "managed_streams[].slot_powerchat_relay": "number",
+ "managed_streams[].slot_powerchat_count_rs_views": "number",
+ "managed_streams[].streaming_method": "string",
+ "managed_streams[].browser_mode": "string",
+ "managed_streams[].default_vod_visibility": "string",
+ "managed_streams[].default_clip_visibility": "string",
+ "managed_streams[].slot_vod_recording_enabled": "number",
+ "managed_streams[].weather_detail": "string",
+ "managed_streams[].weather_show_location": "number",
+ "managed_streams[].last_live_at": "string",
+ "managed_streams[].is_currently_live": "number",
+ "managed_streams[].live_session_id": "number",
+ "rs_restream": "null",
+ "restream_links": "null",
+ "external_viewers": "null",
+ "vods": "array",
+ "vodTotal": "number",
+ "vodLimit": "number",
+ "vodOffset": "number",
+ "clips": "array",
+ "clipTotal": "number",
+ "clipLimit": "number",
+ "clipOffset": "number",
+ "clipsOfStreams": "array",
+ "clipsOfTotal": "number",
+ "clipsOfLimit": "number",
+ "clipsOfOffset": "number",
+ "aiClips": "array",
+ "aiClipsTotal": "number",
+ "aiClipsLimit": "number",
+ "aiClipsOffset": "number"
+ },
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/managed",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js",
+ "public/js/broadcast.js",
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/managed",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "managed_streams": "array",
+ "managed_streams[]": "object",
+ "managed_streams[].id": "number",
+ "managed_streams[].user_id": "number",
+ "managed_streams[].channel_id": "number",
+ "managed_streams[].slug": "null",
+ "managed_streams[].title": "string",
+ "managed_streams[].description": "string",
+ "managed_streams[].category": "string",
+ "managed_streams[].tags": "string",
+ "managed_streams[].protocol": "string",
+ "managed_streams[].stream_key": "string",
+ "managed_streams[].is_nsfw": "number",
+ "managed_streams[].control_config_id": "null",
+ "managed_streams[].sort_order": "number",
+ "managed_streams[].created_at": "string",
+ "managed_streams[].updated_at": "string",
+ "managed_streams[].slot_clip_recording_enabled": "number",
+ "managed_streams[].slot_clip_notify_enabled": "number",
+ "managed_streams[].slot_powerchat_relay": "number",
+ "managed_streams[].slot_powerchat_count_rs_views": "number",
+ "managed_streams[].broadcast_settings": "string",
+ "managed_streams[].streaming_method": "string",
+ "managed_streams[].browser_mode": "string",
+ "managed_streams[].pip_source_msid": "null",
+ "managed_streams[].pip_defaults": "string",
+ "managed_streams[].default_vod_visibility": "string",
+ "managed_streams[].default_clip_visibility": "string",
+ "managed_streams[].slot_vod_recording_enabled": "number",
+ "managed_streams[].weather_zip": "null",
+ "managed_streams[].weather_detail": "string",
+ "managed_streams[].weather_show_location": "number",
+ "managed_streams[].last_live_at": "string",
+ "managed_streams[].is_currently_live": "number",
+ "managed_streams[].live_session_id": "number",
+ "limit": "number"
+ },
+ "from": [
+ "public/js/broadcast-workspace.js",
+ "public/js/broadcast.js",
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/managed/1/profile",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/managed/1/profile",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "managed_stream": "object",
+ "managed_stream.id": "number",
+ "managed_stream.user_id": "number",
+ "managed_stream.channel_id": "number",
+ "managed_stream.slug": "null",
+ "managed_stream.title": "string",
+ "managed_stream.description": "string",
+ "managed_stream.category": "string",
+ "managed_stream.tags": "string",
+ "managed_stream.protocol": "string",
+ "managed_stream.is_nsfw": "number",
+ "managed_stream.control_config_id": "null",
+ "managed_stream.sort_order": "number",
+ "managed_stream.created_at": "string",
+ "managed_stream.updated_at": "string",
+ "managed_stream.slot_clip_recording_enabled": "number",
+ "managed_stream.slot_clip_notify_enabled": "number",
+ "managed_stream.slot_powerchat_relay": "number",
+ "managed_stream.slot_powerchat_count_rs_views": "number",
+ "managed_stream.broadcast_settings": "string",
+ "managed_stream.streaming_method": "string",
+ "managed_stream.browser_mode": "string",
+ "managed_stream.pip_source_msid": "null",
+ "managed_stream.pip_defaults": "string",
+ "managed_stream.default_vod_visibility": "string",
+ "managed_stream.default_clip_visibility": "string",
+ "managed_stream.slot_vod_recording_enabled": "number",
+ "managed_stream.weather_zip": "null",
+ "managed_stream.weather_detail": "string",
+ "managed_stream.weather_show_location": "number",
+ "managed_stream.username": "string",
+ "managed_stream.display_name": "string",
+ "managed_stream.avatar_url": "null",
+ "managed_stream.profile_color": "string",
+ "stream_key": "string",
+ "broadcast_settings": "object",
+ "whip_url_base": "string",
+ "whip_url_source": "string",
+ "whip_url_warning": "string",
+ "rtmp_url": "string"
+ },
+ "from": [
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/managed/1/history",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/managed/1/history",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "sessions": "array",
+ "sessions[]": "object",
+ "sessions[].id": "number",
+ "sessions[].title": "string",
+ "sessions[].started_at": "string",
+ "sessions[].ended_at": "string",
+ "sessions[].is_live": "number",
+ "sessions[].peak_viewers": "number",
+ "sessions[].viewer_count": "number",
+ "sessions[].duration_seconds": "number",
+ "sessions[].protocol": "string",
+ "sessions[].category": "string",
+ "sessions[].vod_id": "null"
+ },
+ "from": [
+ "public/js/broadcast-workspace.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/mine",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/mine",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "streams": "array",
+ "streams[]": "object",
+ "streams[].id": "number",
+ "streams[].user_id": "number",
+ "streams[].channel_id": "null",
+ "streams[].managed_stream_id": "number",
+ "streams[].control_config_id": "null",
+ "streams[].title": "string",
+ "streams[].description": "string",
+ "streams[].category": "string",
+ "streams[].tags": "string",
+ "streams[].protocol": "string",
+ "streams[].is_live": "number",
+ "streams[].is_nsfw": "number",
+ "streams[].viewer_count": "number",
+ "streams[].peak_viewers": "number",
+ "streams[].follower_count": "number",
+ "streams[].thumbnail_url": "null",
+ "streams[].started_at": "string",
+ "streams[].ended_at": "string",
+ "streams[].duration_seconds": "number",
+ "streams[].created_at": "string",
+ "streams[].ai_overview": "null",
+ "streams[].ai_title": "null",
+ "streams[].ai_category": "null",
+ "streams[].ai_tags": "null",
+ "streams[].ai_overview_short": "null",
+ "streams[].username": "string",
+ "streams[].display_name": "string",
+ "streams[].avatar_url": "null",
+ "streams[].profile_color": "string",
+ "streams[].managed_stream_slug": "null"
+ },
+ "from": [
+ "public/js/broadcast.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/recent-vods?limit=5&offset=0",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "vods": "array",
+ "total": "number",
+ "limit": "number",
+ "offset": "number",
+ "hasMore": "boolean"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/recent-vods?limit=5&offset=0",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "vods": "array",
+ "total": "number",
+ "limit": "number",
+ "offset": "number",
+ "hasMore": "boolean"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/recently-online?limit=5&offset=0",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "streamers": "array",
+ "streamers[]": "object",
+ "streamers[].user_id": "number",
+ "streamers[].username": "string",
+ "streamers[].display_name": "string",
+ "streamers[].avatar_url": "null",
+ "streamers[].profile_color": "string",
+ "streamers[].last_online_at": "string",
+ "streamers[].ai_overview": "null",
+ "streamers[].ai_overview_short": "null",
+ "streamers[].managed_streams": "array",
+ "streamers[].managed_streams[]": "object",
+ "streamers[].managed_streams[].managed_stream_id": "number",
+ "streamers[].managed_streams[].slug": "null",
+ "streamers[].managed_streams[].title": "string",
+ "streamers[].managed_streams[].protocol": "string",
+ "streamers[].managed_streams[].last_live_at": "string",
+ "streamers[].managed_streams[].vod_thumbnail": "null",
+ "total": "number",
+ "limit": "number",
+ "offset": "number",
+ "hasMore": "boolean"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/recently-online?limit=5&offset=0",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "streamers": "array",
+ "streamers[]": "object",
+ "streamers[].user_id": "number",
+ "streamers[].username": "string",
+ "streamers[].display_name": "string",
+ "streamers[].avatar_url": "null",
+ "streamers[].profile_color": "string",
+ "streamers[].last_online_at": "string",
+ "streamers[].ai_overview": "null",
+ "streamers[].ai_overview_short": "null",
+ "streamers[].managed_streams": "array",
+ "streamers[].managed_streams[]": "object",
+ "streamers[].managed_streams[].managed_stream_id": "number",
+ "streamers[].managed_streams[].slug": "null",
+ "streamers[].managed_streams[].title": "string",
+ "streamers[].managed_streams[].protocol": "string",
+ "streamers[].managed_streams[].last_live_at": "string",
+ "streamers[].managed_streams[].vod_thumbnail": "null",
+ "total": "number",
+ "limit": "number",
+ "offset": "number",
+ "hasMore": "boolean"
+ },
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/setup-progress",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/setup-progress",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "tasks": "array",
+ "tasks[]": "object",
+ "tasks[].id": "string",
+ "tasks[].group": "string",
+ "tasks[].title": "string",
+ "tasks[].why": "string",
+ "tasks[].done": "boolean",
+ "tasks[].count": "number",
+ "tasks[].go": "string",
+ "done": "number",
+ "total": "number",
+ "next": "object",
+ "next.id": "string",
+ "next.group": "string",
+ "next.title": "string",
+ "next.why": "string",
+ "next.done": "boolean",
+ "next.count": "number",
+ "username": "string"
+ },
+ "from": [
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/voice-channels",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "channels": "array",
+ "channels[]": "object",
+ "channels[].id": "string",
+ "channels[].name": "string",
+ "channels[].mode": "string",
+ "channels[].createdBy": "null",
+ "channels[].streamId": "null",
+ "channels[].permanent": "boolean",
+ "channels[].maxParticipants": "number",
+ "channels[].participantCount": "number",
+ "channels[].participants": "array"
+ },
+ "from": [
+ "public/js/voice-channels.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/voice-channels",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "channels": "array",
+ "channels[]": "object",
+ "channels[].id": "string",
+ "channels[].name": "string",
+ "channels[].mode": "string",
+ "channels[].createdBy": "null",
+ "channels[].streamId": "null",
+ "channels[].permanent": "boolean",
+ "channels[].maxParticipants": "number",
+ "channels[].participantCount": "number",
+ "channels[].participants": "array"
+ },
+ "from": [
+ "public/js/voice-channels.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/1",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "stream": "object",
+ "stream.id": "number",
+ "stream.user_id": "number",
+ "stream.channel_id": "null",
+ "stream.managed_stream_id": "number",
+ "stream.control_config_id": "null",
+ "stream.title": "string",
+ "stream.description": "string",
+ "stream.category": "string",
+ "stream.tags": "string",
+ "stream.protocol": "string",
+ "stream.is_live": "number",
+ "stream.is_nsfw": "number",
+ "stream.viewer_count": "number",
+ "stream.peak_viewers": "number",
+ "stream.follower_count": "number",
+ "stream.thumbnail_url": "null",
+ "stream.started_at": "string",
+ "stream.ended_at": "null",
+ "stream.duration_seconds": "number",
+ "stream.created_at": "string",
+ "stream.ai_overview": "null",
+ "stream.ai_title": "null",
+ "stream.ai_category": "null",
+ "stream.ai_tags": "null",
+ "stream.ai_overview_short": "null",
+ "stream.username": "string",
+ "stream.display_name": "string",
+ "stream.avatar_url": "null",
+ "stream.profile_color": "string",
+ "stream.managed_stream_slug": "null",
+ "stream.endpoint": "object",
+ "stream.cameras": "array",
+ "stream.controls": "array",
+ "stream.channel": "object",
+ "stream.channel.id": "number",
+ "stream.channel.user_id": "number",
+ "stream.channel.title": "string",
+ "stream.channel.description": "string",
+ "stream.channel.category": "null",
+ "stream.channel.tags": "string",
+ "stream.channel.protocol": "string",
+ "stream.channel.is_nsfw": "number",
+ "stream.channel.panels": "string",
+ "stream.channel.default_vod_visibility": "string",
+ "stream.channel.default_clip_visibility": "string",
+ "stream.channel.weather_detail": "string",
+ "stream.channel.weather_show_location": "number",
+ "stream.channel.control_mode": "string",
+ "stream.channel.anon_controls_enabled": "number",
+ "stream.channel.control_rate_limit_ms": "number",
+ "stream.channel.video_click_enabled": "number",
+ "stream.channel.video_click_rate_limit_ms": "number",
+ "stream.channel.active_control_config_id": "null",
+ "stream.channel.created_at": "string",
+ "stream.channel.updated_at": "string",
+ "stream.channel.clips_allow_creator_delete": "number",
+ "stream.channel.hide_ai_overview": "number",
+ "stream.channel.ai_overview_pref": "string",
+ "stream.channel.ai_derivation_enabled": "number",
+ "stream.channel.ai_category": "null",
+ "stream.channel.offline_screen_type": "string",
+ "stream.channel.offline_screen_url": "null",
+ "stream.channel.offline_html": "null",
+ "stream.channel.offline_css": "null",
+ "stream.pip_overlay": "null"
+ },
+ "from": [
+ "public/js/app-media.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/1",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "stream": "object",
+ "stream.id": "number",
+ "stream.user_id": "number",
+ "stream.channel_id": "null",
+ "stream.managed_stream_id": "number",
+ "stream.control_config_id": "null",
+ "stream.title": "string",
+ "stream.description": "string",
+ "stream.category": "string",
+ "stream.tags": "string",
+ "stream.protocol": "string",
+ "stream.is_live": "number",
+ "stream.is_nsfw": "number",
+ "stream.viewer_count": "number",
+ "stream.peak_viewers": "number",
+ "stream.follower_count": "number",
+ "stream.thumbnail_url": "null",
+ "stream.started_at": "string",
+ "stream.ended_at": "null",
+ "stream.duration_seconds": "number",
+ "stream.created_at": "string",
+ "stream.ai_overview": "null",
+ "stream.ai_title": "null",
+ "stream.ai_category": "null",
+ "stream.ai_tags": "null",
+ "stream.ai_overview_short": "null",
+ "stream.username": "string",
+ "stream.display_name": "string",
+ "stream.avatar_url": "null",
+ "stream.profile_color": "string",
+ "stream.managed_stream_slug": "null",
+ "stream.endpoint": "object",
+ "stream.cameras": "array",
+ "stream.controls": "array",
+ "stream.channel": "object",
+ "stream.channel.id": "number",
+ "stream.channel.user_id": "number",
+ "stream.channel.title": "string",
+ "stream.channel.description": "string",
+ "stream.channel.category": "null",
+ "stream.channel.tags": "string",
+ "stream.channel.protocol": "string",
+ "stream.channel.is_nsfw": "number",
+ "stream.channel.panels": "string",
+ "stream.channel.default_vod_visibility": "string",
+ "stream.channel.default_clip_visibility": "string",
+ "stream.channel.weather_detail": "string",
+ "stream.channel.weather_show_location": "number",
+ "stream.channel.control_mode": "string",
+ "stream.channel.anon_controls_enabled": "number",
+ "stream.channel.control_rate_limit_ms": "number",
+ "stream.channel.video_click_enabled": "number",
+ "stream.channel.video_click_rate_limit_ms": "number",
+ "stream.channel.active_control_config_id": "null",
+ "stream.channel.created_at": "string",
+ "stream.channel.updated_at": "string",
+ "stream.channel.clips_allow_creator_delete": "number",
+ "stream.channel.hide_ai_overview": "number",
+ "stream.channel.ai_overview_pref": "string",
+ "stream.channel.ai_derivation_enabled": "number",
+ "stream.channel.ai_category": "null",
+ "stream.channel.offline_screen_type": "string",
+ "stream.channel.offline_screen_url": "null",
+ "stream.channel.offline_html": "null",
+ "stream.channel.offline_css": "null",
+ "stream.pip_overlay": "null"
+ },
+ "from": [
+ "public/js/app-media.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/1/call",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "call_mode": "null",
+ "channelId": "null",
+ "participants": "array",
+ "participant_count": "number"
+ },
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/1/call",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "call_mode": "null",
+ "channelId": "null",
+ "participants": "array",
+ "participant_count": "number"
+ },
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/1/endpoint",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/1/endpoint",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "endpoint": "object",
+ "endpoint.whipUrlBase": "string",
+ "endpoint.whipUrlSource": "string",
+ "endpoint.whipUrlWarning": "string",
+ "stream_key": "string"
+ },
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/1/rtmp-status",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/streams/1/rtmp-status",
+ "auth": "user",
+ "status": 400,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/vibe-coding/channel/n1star/1/events?limit=5",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "managed_stream": "object",
+ "managed_stream.id": "number",
+ "managed_stream.slug": "null",
+ "managed_stream.title": "string",
+ "settings": "object",
+ "settings.enabled": "number",
+ "settings.widget_title": "string",
+ "settings.viewer_depth": "string",
+ "settings.show_prompts": "number",
+ "settings.show_responses": "number",
+ "settings.show_thinking": "number",
+ "settings.show_tool_calls": "number",
+ "settings.show_tool_arguments": "number",
+ "settings.show_file_events": "number",
+ "settings.show_file_snippets": "number",
+ "settings.redact_file_paths": "number",
+ "settings.paused": "number",
+ "settings.delay_ms": "number",
+ "settings.max_events": "number",
+ "settings.max_prompt_chars": "number",
+ "settings.max_response_chars": "number",
+ "settings.max_thinking_chars": "number",
+ "settings.max_tool_chars": "number",
+ "settings.max_snippet_chars": "number",
+ "publisher": "null",
+ "events": "array"
+ },
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/vibe-coding/channel/n1star/1/events?limit=5",
+ "auth": "user",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "managed_stream": "object",
+ "managed_stream.id": "number",
+ "managed_stream.slug": "null",
+ "managed_stream.title": "string",
+ "settings": "object",
+ "settings.enabled": "number",
+ "settings.widget_title": "string",
+ "settings.viewer_depth": "string",
+ "settings.show_prompts": "number",
+ "settings.show_responses": "number",
+ "settings.show_thinking": "number",
+ "settings.show_tool_calls": "number",
+ "settings.show_tool_arguments": "number",
+ "settings.show_file_events": "number",
+ "settings.show_file_snippets": "number",
+ "settings.redact_file_paths": "number",
+ "settings.paused": "number",
+ "settings.delay_ms": "number",
+ "settings.max_events": "number",
+ "settings.max_prompt_chars": "number",
+ "settings.max_response_chars": "number",
+ "settings.max_thinking_chars": "number",
+ "settings.max_tool_chars": "number",
+ "settings.max_snippet_chars": "number",
+ "publisher": "null",
+ "events": "array"
+ },
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/vods/mine?limit=5&offset=5",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/vods/mine?limit=5&offset=5",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/vods/stream/1/live",
+ "auth": "anon",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/vods/stream/1/live",
+ "auth": "user",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/vods/1/context",
+ "auth": "anon",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/context-cards.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/vods/1/context",
+ "auth": "user",
+ "status": 404,
+ "kind": "json",
+ "from": [
+ "public/js/context-cards.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/vods/1/live-info",
+ "auth": "anon",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/vods/1/live-info",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/vods/1",
+ "auth": "anon",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/vods/1",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/vods/1/memories",
+ "auth": "anon",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js"
+ ]
+ },
+ {
+ "method": "GET",
+ "path": "/api/vods/1/memories",
+ "auth": "user",
+ "status": 502,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/admin/users/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/ai-viewers/bots/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "PATCH",
+ "path": "/api/ai-viewers/bots/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/ai-viewers/bots/1/clear-memory",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/ai-viewers/bots/1/mute",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/ai-viewers/byo/test",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/ai-viewers/clone",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/ai-viewers/config",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/ai-viewers/preview",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/ai-viewers.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/arena/beefs/1/hype",
+ "auth": "anon",
+ "status": 400,
+ "kind": "json",
+ "from": [
+ "public/js/arena.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/auth/avatar",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/settings.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/auth/logout",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "ok": "boolean"
+ },
+ "from": [
+ "public/js/app.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/auth/preferences",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/auth/profile",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/guides-live.js",
+ "public/js/settings.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/auth/refresh",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "access_token": "null",
+ "user": "null"
+ },
+ "from": [
+ "public/js/app.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/auth/stream-key/regenerate",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app.js",
+ "public/js/broadcast.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/auth/tokens",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/auth/tokens/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/channels/1/moderation",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/channels/1/moderation/messages/1/delete",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard-moderation.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/channels/1/mods",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard-moderation.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/channels/1/mods/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard-moderation.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/clips/bulk",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/clips/settings/channel",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/clips/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/clips/1/recut",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/clips/1/title",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js",
+ "public/js/stream-player.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/clips/1/visibility",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/coins/bonus",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/opencoins.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/coins/config",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/opencoins.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/coins/heartbeat",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/opencoins.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/coins/redeem",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/opencoins.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/coins/redemptions/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/opencoins.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/coins/rewards",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/opencoins.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/coins/rewards/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/opencoins.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/coins/rewards/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/opencoins.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/comments/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/comments/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/comments/1/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/controls/configs",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/controls/configs/deactivate",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/controls/configs/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/controls/configs/1/activate",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/controls/configs/1/buttons/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/controls/configs/1/buttons",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/controls/settings/channel",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/controls/whitelist",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/controls/whitelist/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/controls/1/config",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/cosmetics/activate",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/cosmetics-ui.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/cosmetics/deactivate",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/cosmetics-ui.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/cosmetics/equip",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/cosmetics-ui.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/cosmetics/unequip",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/cosmetics-ui.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/easter-egg/reveal",
+ "auth": "anon",
+ "status": 403,
+ "kind": "json",
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/easter-egg/solve",
+ "auth": "anon",
+ "status": 400,
+ "kind": "json",
+ "from": [
+ "public/js/app-home.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/emotes",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/channel-uploads.js",
+ "public/js/emotes.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/emotes/sources",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/emotes.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/emotes/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/channel-uploads.js",
+ "public/js/emotes.js"
+ ]
+ },
+ {
+ "method": "PATCH",
+ "path": "/api/emotes/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/channel-uploads.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/funds/cashout",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/vibes.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/funds/donate",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/vibes.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/funds/goals",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app.js",
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/funds/goals/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/funds/goals/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/funds/recycle",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/i18n/translate",
+ "auth": "anon",
+ "status": 503,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/media/advance",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-channel.js",
+ "public/js/broadcast.js",
+ "public/js/media-player.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/media/queue/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-channel.js",
+ "public/js/media-player.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/media/queue/1/move",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-channel.js",
+ "public/js/media-player.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/media/queue/1/play",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-channel.js",
+ "public/js/media-player.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/media/queue/1/refund",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/media-player.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/media/queue/1/position",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/media-player.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/media/queue/1/fail",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/media-player.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/media/quote",
+ "auth": "anon",
+ "status": 400,
+ "kind": "json",
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/media/request",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/media/settings",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/media-player.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/media/start",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js",
+ "public/js/media-player.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/mod/delete-message",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/mod/delete-user-messages",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/mod/global-ban",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-channel.js",
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/mod/ip-approval/1/approve",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard-moderation.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/mod/ip-approval/1/deny",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard-moderation.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/mod/ip/ban-all",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/mod/relay-user/hide",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/mod/stream-ban",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/mod/tts-voice/preview",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/mod/tts-voice/1/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/news/my-settings",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/onvif/cameras",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/onvif-dashboard.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/onvif/cameras/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/onvif-dashboard.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/onvif/cameras/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/onvif-dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/onvif/cameras/1/presets",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/onvif-dashboard.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/onvif/cameras/1/presets/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/onvif-dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/onvif/discover",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/onvif-dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/pastes",
+ "auth": "anon",
+ "status": 504,
+ "kind": "json",
+ "from": [
+ "public/js/pastes.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/pastes/bulk",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/pastes/n1star",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/pastes.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/pastes/n1star",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/pastes.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/pastes/n1star/comments",
+ "auth": "anon",
+ "status": 504,
+ "kind": "json",
+ "from": [
+ "public/js/pastes.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/pastes/n1star/comments/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/pastes.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/pastes/n1star/copy",
+ "auth": "anon",
+ "status": 504,
+ "kind": "json",
+ "from": [
+ "public/js/pastes.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/pastes/n1star/fork",
+ "auth": "anon",
+ "status": 504,
+ "kind": "json",
+ "from": [
+ "public/js/pastes.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/pastes/n1star/like",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/pastes.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/pastes/n1star/set-avatar",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/pastes.js",
+ "public/js/settings.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/payments/bucks/checkout",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/vibes.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/payments/subscribe",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/vibes.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/powerchat/oauth/connection",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/powerchat.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/powerchat/test-alert",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/powerchat.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/powerchat/test-tip",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/powerchat.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/recap/1/regenerate",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/recap.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/restream/destinations",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js",
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/restream/destinations/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/restream/destinations/1/start",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/restream/destinations/1/stop",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/restream/destinations/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/restream/oauth/1/connection",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/robotstreamer/integration",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js",
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/robotstreamer/integration/login",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js",
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/robotstreamer/integration/validate",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js",
+ "public/js/broadcast.js",
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/robotstreamer/integration?managed_stream_id=1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/robotstreamer/restream/start",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/robotstreamer/restream/stop",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/streams",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/streams/broadcast-settings",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js",
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/streams/channel",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js",
+ "public/js/settings.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/streams/channel/offline-screen",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/settings.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/streams/channel/n1star/about",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/streams/channel/n1star/follow",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/streams/diag-log",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "ok": "boolean"
+ },
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/streams/goal-media",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/streams/managed",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app.js",
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/streams/managed/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/guides-live.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/streams/managed/1/regenerate-key",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/streams/managed/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast-workspace.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/streams/panel-image",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-channel.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/streams/voice-channels",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/voice-channels.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/streams/voice-channels/call-user",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/streams/voice-channels/call-user/respond",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/chat.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/streams/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/streams/1/follow",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/streams/1/heartbeat",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/thumbnails/live/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/vods/bulk",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/vods/bulk-delete-old",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/vods/clips",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js",
+ "public/js/broadcast.js",
+ "public/js/stream-player.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/vods/clips/1/trim",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/stream-player.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/vods/stream/1/chunk",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/vods/stream/1/chunk?token=",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/vods/stream/1/finalize",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/broadcast.js"
+ ]
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/vods/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "PUT",
+ "path": "/api/vods/1",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/app-media.js",
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/api/vods/1/publish",
+ "auth": "anon",
+ "status": 401,
+ "kind": "json",
+ "from": [
+ "public/js/dashboard.js"
+ ]
+ },
+ {
+ "method": "POST",
+ "path": "/banned/continue",
+ "auth": "anon",
+ "status": 200,
+ "kind": "json",
+ "reads": {
+ "ok": "boolean",
+ "name": "null",
+ "lifted": "number"
+ },
+ "from": [
+ "public/banned.html"
+ ]
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,1257 @@
@@ -0,0 +1,1257 @@
+{
+ "service": "live",
+ "release": "8caa540466f9af86247febdbba1339b68919a42f",
+ "recorded_at": "2026-09-26T13:44:31.818Z",
+ "note": "N-1 fixture: written by `npm run n-1:record`, replayed by test/n-1.test.js",
+ "user_version": 0,
+ "schema": [
+ "CREATE TABLE ai_chatbot_configs (\n user_id INTEGER PRIMARY KEY,\n enabled INTEGER DEFAULT 0,\n base_url TEXT DEFAULT 'https://api.openai.com/v1',\n api_token TEXT DEFAULT '',\n model TEXT DEFAULT 'gpt-4o-mini',\n transcribe_enabled INTEGER DEFAULT 0,\n transcribe_model TEXT DEFAULT 'whisper-1',\n num_bots INTEGER DEFAULT 3,\n post_interval_seconds INTEGER DEFAULT 45,\n persona TEXT DEFAULT '',\n vision_enabled INTEGER DEFAULT 0,\n last_validated_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE ai_timeline_cache (\n user_id INTEGER PRIMARY KEY,\n payload TEXT,\n generated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE ai_usage (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n kind TEXT, -- paste_image | paste_text | stream_memory | ai_viewers | ...\n model TEXT,\n input_tokens INTEGER DEFAULT 0,\n output_tokens INTEGER DEFAULT 0,\n cost_usd REAL DEFAULT 0,\n owner_user_id INTEGER, -- streamer this spend is attributed to (NULL = platform/global)\n source TEXT, -- feature bucket, e.g. 'ai_viewers' (NULL = legacy/global)\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n , cached_tokens INTEGER DEFAULT 0, role TEXT, provider TEXT, latency_ms INTEGER)",
+ "CREATE TABLE ai_viewer_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_user_id INTEGER NOT NULL, stream_id INTEGER,\n event TEXT NOT NULL, -- tick | line | skip | mention | fold | vision | degrade | pause | error | info\n bot_username TEXT, target TEXT, thread_id INTEGER, chat_message_id INTEGER,\n text TEXT, reason TEXT,\n tokens_in INTEGER, tokens_cached INTEGER, tokens_out INTEGER, cost_usd REAL, model TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
+ "CREATE TABLE ai_viewer_threads (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_user_id INTEGER NOT NULL,\n stream_id INTEGER,\n kind TEXT NOT NULL, -- bot_bot | bot_viewer | bot_streamer\n participants_json TEXT NOT NULL, -- [\"botname\",\"viewer\"...]\n topic TEXT,\n state TEXT DEFAULT 'open', -- open | closed\n awaiting TEXT, -- bot username expected to speak next (or null)\n turns INTEGER DEFAULT 0,\n last_line TEXT, last_line_by TEXT, last_line_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
+ "CREATE TABLE anon_ip_mappings (\n ip TEXT PRIMARY KEY,\n anon_num INTEGER NOT NULL UNIQUE\n , created_at DATETIME)",
+ "CREATE TABLE api_keys (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n key_hash TEXT UNIQUE NOT NULL,\n label TEXT DEFAULT 'Default',\n permissions TEXT DEFAULT '[\"control\",\"stream\"]', -- JSON array\n last_used DATETIME,\n is_active INTEGER DEFAULT 1,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE api_tokens (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n token_hash TEXT UNIQUE NOT NULL,\n label TEXT DEFAULT 'Bot Token',\n scopes TEXT DEFAULT '[\"chat\",\"read\"]', -- JSON array: chat, stream, control, read, admin\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n last_used_at DATETIME,\n expires_at DATETIME, -- NULL = never expires\n is_active INTEGER DEFAULT 1,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE app_state (\n key TEXT PRIMARY KEY,\n value TEXT,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
+ "CREATE TABLE approved_ips (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_id INTEGER NOT NULL,\n ip_address TEXT NOT NULL,\n approved_by INTEGER,\n source TEXT DEFAULT 'auto',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(channel_id, ip_address),\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE,\n FOREIGN KEY (approved_by) REFERENCES users(id) ON DELETE SET NULL\n )",
+ "CREATE TABLE arena_beef_hype (\n beef_id INTEGER NOT NULL,\n side TEXT NOT NULL,\n voter_key TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (beef_id, voter_key)\n )",
+ "CREATE TABLE arena_beef_sides (\n beef_id INTEGER NOT NULL,\n voter_key TEXT NOT NULL,\n side TEXT NOT NULL,\n user_id INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (beef_id, voter_key)\n )",
+ "CREATE TABLE arena_beefs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n a_user_id INTEGER NOT NULL,\n b_user_id INTEGER NOT NULL,\n status TEXT NOT NULL DEFAULT 'open',\n score_a REAL DEFAULT 0,\n score_b REAL DEFAULT 0,\n hits_a INTEGER DEFAULT 0,\n hits_b INTEGER DEFAULT 0,\n crowd_a INTEGER DEFAULT 0,\n crowd_b INTEGER DEFAULT 0,\n on_clock TEXT,\n clock_until DATETIME,\n responded INTEGER DEFAULT 0,\n last_a_at DATETIME,\n last_b_at DATETIME,\n feed_json TEXT,\n opener_line TEXT,\n winner_user_id INTEGER,\n resolution TEXT,\n opened_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n ends_at DATETIME,\n resolved_at DATETIME\n , headline TEXT, result_headline TEXT, upset INTEGER DEFAULT 0, rematch INTEGER DEFAULT 0, bounty_topic_id INTEGER)",
+ "CREATE TABLE arena_mic_moments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n stream_id INTEGER,\n vod_id INTEGER,\n sec INTEGER,\n kind TEXT NOT NULL DEFAULT 'trash',\n target_user_id INTEGER,\n beef_id INTEGER,\n aimed_at TEXT,\n text TEXT NOT NULL,\n about TEXT,\n quality REAL DEFAULT 0,\n announcer TEXT,\n said_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
+ "CREATE TABLE arena_profiles (\n user_id INTEGER PRIMARY KEY,\n stats_json TEXT,\n persona_json TEXT,\n persona_model TEXT,\n persona_generated_at DATETIME,\n image_path TEXT,\n image_prompt TEXT,\n image_model TEXT,\n image_generated_at DATETIME,\n image_error TEXT,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n , quotes_json TEXT, quotes_generated_at DATETIME)",
+ "CREATE TABLE arena_trash_levels (\n user_id INTEGER PRIMARY KEY,\n xp INTEGER DEFAULT 0,\n level INTEGER DEFAULT 1,\n angles_cleared INTEGER DEFAULT 0,\n topics_conquered INTEGER DEFAULT 0,\n beef_hits INTEGER DEFAULT 0,\n best_line TEXT,\n best_line_vod_id INTEGER,\n best_line_sec INTEGER,\n best_line_score REAL DEFAULT 0,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n , topic_moments INTEGER DEFAULT 0, topics_joined INTEGER DEFAULT 0, mic_moments INTEGER DEFAULT 0)",
+ "CREATE TABLE arena_xp_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n amount INTEGER NOT NULL,\n reason TEXT NOT NULL,\n ref_id INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
+ "CREATE TABLE bans (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER, -- NULL = site-wide\n user_id INTEGER,\n ip_address TEXT,\n anon_id TEXT,\n reason TEXT,\n banned_by INTEGER,\n expires_at DATETIME, -- NULL = permanent\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (banned_by) REFERENCES users(id) ON DELETE SET NULL\n)",
+ "CREATE TABLE camera_presets (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n camera_id INTEGER NOT NULL,\n name TEXT NOT NULL,\n pan REAL NOT NULL, -- 0.0-1.0\n tilt REAL NOT NULL,\n zoom REAL NOT NULL,\n preset_token TEXT, -- ONVIF device preset token (if supported)\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (camera_id) REFERENCES camera_profiles(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE camera_profiles (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n stream_id INTEGER, -- NULL = global profile\n name TEXT NOT NULL,\n onvif_url TEXT NOT NULL, -- e.g., http://192.168.1.100:8080\n username TEXT NOT NULL,\n password_hash TEXT NOT NULL, -- bcrypt hashed\n pan_speed REAL DEFAULT 0.5, -- 0.0-1.0\n tilt_speed REAL DEFAULT 0.5,\n zoom_speed REAL DEFAULT 0.5,\n is_active INTEGER DEFAULT 1,\n last_connected DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE cameras (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n camera_index INTEGER DEFAULT 0,\n label TEXT DEFAULT 'Main',\n protocol TEXT DEFAULT 'jsmpeg',\n jsmpeg_video_port INTEGER,\n jsmpeg_audio_port INTEGER,\n webrtc_room_id TEXT,\n is_active INTEGER DEFAULT 1,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE channel_ai_bots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_user_id INTEGER NOT NULL, -- the streamer (owner)\n username TEXT NOT NULL,\n display_name TEXT,\n avatar_color TEXT DEFAULT '#8a8aff',\n source TEXT DEFAULT 'ambient', -- 'ambient' | 'clone'\n cloned_from_kind TEXT, -- 'user' | 'relay' | NULL\n cloned_from_ref TEXT, -- user_id (string) or \"platform:username\"\n persona_json TEXT DEFAULT '{}', -- character + typing style + identity\n brain_json TEXT DEFAULT '{}', -- rolling condensed memory + short timeline\n is_active INTEGER DEFAULT 1,\n msg_count INTEGER DEFAULT 0,\n last_active_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (channel_user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE channel_ai_config (\n user_id INTEGER PRIMARY KEY,\n enabled INTEGER DEFAULT 0,\n num_ambient_bots INTEGER DEFAULT 3,\n pacing_seconds INTEGER DEFAULT 45,\n persona TEXT DEFAULT '',\n transcribe_enabled INTEGER DEFAULT 0,\n vision_enabled INTEGER DEFAULT 0,\n use_shared_key INTEGER DEFAULT 1, -- 1 = OpenVibe.Live shared key (capped), 0 = BYO\n daily_budget_cents INTEGER DEFAULT 20, -- shared-key daily cap\n byo_key TEXT DEFAULT '',\n byo_base_url TEXT DEFAULT '',\n byo_model TEXT DEFAULT 'gpt-4o-mini',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, settings_json TEXT DEFAULT '{}',\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE channel_moderation_settings (\n channel_id INTEGER PRIMARY KEY,\n slow_mode_seconds INTEGER DEFAULT 0,\n followers_only INTEGER DEFAULT 0,\n emote_only INTEGER DEFAULT 0,\n allow_anonymous INTEGER DEFAULT 1,\n links_allowed INTEGER DEFAULT 1,\n gifs_enabled INTEGER DEFAULT 1,\n account_age_gate_hours INTEGER DEFAULT 0,\n caps_percentage_limit INTEGER DEFAULT 0,\n aggressive_filter INTEGER DEFAULT 0,\n max_message_length INTEGER DEFAULT 500,\n slur_filter_enabled INTEGER DEFAULT 0,\n slur_filter_use_builtin INTEGER DEFAULT 1,\n slur_filter_terms TEXT DEFAULT '',\n slur_filter_regexes TEXT DEFAULT '',\n slur_filter_nudge_message TEXT DEFAULT '',\n slur_filter_disabled_categories TEXT DEFAULT '[]',\n ip_approval_mode INTEGER DEFAULT 0,\n soundboard_enabled INTEGER DEFAULT 1,\n soundboard_allow_pitch INTEGER DEFAULT 1,\n soundboard_allow_speed INTEGER DEFAULT 1,\n soundboard_banned_ids TEXT DEFAULT '',\n viewer_auto_delete_enabled INTEGER DEFAULT 1,\n viewer_delete_all_enabled INTEGER DEFAULT 1,\n custom_emotes_enabled INTEGER DEFAULT 1, -- viewers may upload gif/png emotes for this channel\n custom_sounds_enabled INTEGER DEFAULT 1, -- viewers may upload !sound commands for this channel\n max_sound_seconds INTEGER DEFAULT 10, -- max duration of an uploaded channel sound\n uploads_mods_only INTEGER DEFAULT 0, -- restrict emote/sound uploads to channel mods\n mods_can_edit_about INTEGER DEFAULT 0, -- allow channel mods to edit the streamer's About/panels\n donation_sound_url TEXT, -- on-disk path to the streamer's donation alert sound\n donation_sound_mime TEXT,\n goal_sound_url TEXT, -- optional override sound for goal-reached\n goal_sound_mime TEXT,\n emote_scale INTEGER DEFAULT 100, -- emote display size in chat, percent (50-300)\n sub_only INTEGER DEFAULT 0, -- only active subscribers (and the channel's moderators) may chat\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, tts_max_length INTEGER DEFAULT 200, sound_min_speed REAL DEFAULT 0.5, sound_max_speed REAL DEFAULT 3.0, sound_min_pitch_cents INTEGER DEFAULT -1200, sound_max_pitch_cents INTEGER DEFAULT 1200, emote_size_min INTEGER DEFAULT 50, emote_size_max INTEGER DEFAULT 200, sounds_mods_only INTEGER DEFAULT 0,\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE channel_moderators (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n added_by INTEGER NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(channel_id, user_id),\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (added_by) REFERENCES users(id) ON DELETE SET NULL\n)",
+ "CREATE TABLE channel_points (\n user_id INTEGER NOT NULL,\n streamer_id INTEGER NOT NULL,\n balance INTEGER NOT NULL DEFAULT 0,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (user_id, streamer_id)\n )",
+ "CREATE TABLE channel_points_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n idempotency_key TEXT NOT NULL UNIQUE,\n user_id INTEGER NOT NULL,\n streamer_id INTEGER NOT NULL,\n delta INTEGER NOT NULL,\n reason TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
+ "CREATE TABLE channel_sounds (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_owner_id INTEGER NOT NULL, -- streamer whose channel this sound belongs to\n command TEXT NOT NULL, -- trigger word (without leading '!'), lowercased\n url TEXT NOT NULL, -- served path of the audio file on disk\n mime TEXT DEFAULT 'audio/mpeg',\n duration_seconds REAL DEFAULT 0,\n created_by INTEGER, -- uploader user id (NULL if removed user)\n created_by_name TEXT DEFAULT '',\n is_approved INTEGER DEFAULT 1,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, emote_code TEXT DEFAULT '',\n -- Multiple sounds may share a command; playback picks one at random.\n FOREIGN KEY (channel_owner_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE channels (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL UNIQUE,\n title TEXT DEFAULT 'Untitled Channel',\n description TEXT DEFAULT '',\n category TEXT DEFAULT 'irl',\n tags TEXT DEFAULT '[]',\n protocol TEXT DEFAULT 'webrtc' CHECK(protocol IN ('jsmpeg', 'webrtc', 'rtmp')),\n is_nsfw INTEGER DEFAULT 0,\n auto_record INTEGER DEFAULT 0,\n offline_banner_url TEXT,\n panels TEXT DEFAULT '[]', -- JSON array of info panels\n emote_sources TEXT DEFAULT '{\"defaults\":true,\"custom\":true,\"ffz\":true,\"bttv\":true,\"7tv\":true}',\n default_vod_visibility TEXT DEFAULT 'public' CHECK(default_vod_visibility IN ('public', 'unlisted', 'private')),\n default_clip_visibility TEXT DEFAULT 'public' CHECK(default_clip_visibility IN ('public', 'unlisted', 'private')),\n vod_recording_enabled INTEGER DEFAULT 1,\n force_vod_recording_disabled INTEGER DEFAULT 0,\n weather_zip TEXT DEFAULT NULL,\n weather_detail TEXT DEFAULT 'basic' CHECK(weather_detail IN ('off', 'basic', 'hourly', 'detailed')),\n weather_show_location INTEGER DEFAULT 0,\n control_mode TEXT DEFAULT 'open' CHECK(control_mode IN ('open','whitelist','disabled')),\n anon_controls_enabled INTEGER DEFAULT 1,\n control_rate_limit_ms INTEGER DEFAULT 100,\n video_click_enabled INTEGER DEFAULT 0,\n video_click_rate_limit_ms INTEGER DEFAULT 0,\n active_control_config_id INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, chat_language TEXT DEFAULT 'auto', cp_name TEXT DEFAULT 'Channel Points', cp_icon TEXT DEFAULT 'fa-coins', cp_watch_interval_min INTEGER DEFAULT 5, cp_watch_amount INTEGER DEFAULT 10, cp_game_interval_min INTEGER DEFAULT 0, clips_allow_creator_delete INTEGER DEFAULT 0, hide_ai_overview INTEGER DEFAULT 0, ai_overview_pref TEXT DEFAULT 'auto', ai_derivation_enabled INTEGER DEFAULT 1, ai_category TEXT, force_nsfw INTEGER DEFAULT 0, offline_screen_type TEXT DEFAULT 'none', offline_screen_url TEXT, offline_html TEXT, offline_css TEXT,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE chat_ai_summaries (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n scope TEXT NOT NULL,\n subject_id INTEGER NOT NULL DEFAULT 0,\n window TEXT NOT NULL,\n overview TEXT DEFAULT '',\n memory_json TEXT DEFAULT '',\n timeline_json TEXT DEFAULT '[]',\n message_count INTEGER DEFAULT 0,\n window_message_count INTEGER DEFAULT 0,\n last_message_id INTEGER DEFAULT 0,\n window_label TEXT DEFAULT '',\n window_start DATETIME,\n window_end DATETIME,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(scope, subject_id, window)\n )",
+ "CREATE TABLE \"chat_messages\" (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER,\n user_id INTEGER, -- NULL for anon\n anon_id TEXT, -- 'anon12345' format\n username TEXT,\n message TEXT NOT NULL,\n message_type TEXT DEFAULT 'chat' CHECK(message_type IN ('chat', 'system', 'donation', 'command', 'tts', 'channel-sound', 'soundboard', 'clip')),\n metadata TEXT, -- JSON sidecar for rich events (donation/goal-reached)\n is_global INTEGER DEFAULT 0,\n is_deleted INTEGER DEFAULT 0,\n is_filtered INTEGER DEFAULT 0,\n reply_to_id INTEGER REFERENCES chat_messages(id) ON DELETE SET NULL,\n source_platform TEXT,\n deleted_by INTEGER,\n deleted_at DATETIME,\n auto_delete_at DATETIME,\n timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, channel_user_id INTEGER,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL\n)",
+ "CREATE TABLE chat_timeline_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n scope TEXT NOT NULL DEFAULT 'global',\n subject_id INTEGER NOT NULL DEFAULT 0,\n ts DATETIME NOT NULL,\n label TEXT NOT NULL,\n detail TEXT DEFAULT '',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
+ "CREATE TABLE clip_ai_state (\n clip_id INTEGER PRIMARY KEY,\n ai_overview_short TEXT,\n ai_transcript_json TEXT,\n transcript_status TEXT,\n transcript_attempts INTEGER DEFAULT 0,\n transcript_error TEXT,\n transcript_next_at DATETIME,\n clip_notified INTEGER DEFAULT 0,\n clip_notify_at DATETIME\n , ai_overview TEXT, transcript_partial_json TEXT, transcript_progress_sec INTEGER DEFAULT 0)",
+ "CREATE TABLE clips (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n vod_id INTEGER,\n stream_id INTEGER,\n user_id INTEGER NOT NULL,\n title TEXT DEFAULT 'Untitled Clip',\n description TEXT DEFAULT '',\n file_path TEXT,\n thumbnail_url TEXT,\n start_time REAL NOT NULL DEFAULT 0,\n end_time REAL NOT NULL DEFAULT 0,\n duration_seconds REAL DEFAULT 0,\n is_public INTEGER DEFAULT 1,\n view_count INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, storage_provider TEXT DEFAULT 'local', storage_key TEXT, ai_overview TEXT, ai_transcript TEXT, ai_analyzed_at DATETIME, clip_notified INTEGER DEFAULT 0, clip_notify_at DATETIME, visibility TEXT DEFAULT 'public', auto_generated INTEGER DEFAULT 0,\n FOREIGN KEY (vod_id) REFERENCES vods(id) ON DELETE SET NULL,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE coin_redemptions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n reward_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n stream_id INTEGER,\n status TEXT DEFAULT 'pending' CHECK(status IN ('pending', 'fulfilled', 'rejected', 'refunded')),\n user_input TEXT, -- message if requires_input\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n resolved_at DATETIME,\n FOREIGN KEY (reward_id) REFERENCES coin_rewards(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL\n)",
+ "CREATE TABLE coin_rewards (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n streamer_id INTEGER NOT NULL, -- who created this reward\n title TEXT NOT NULL,\n description TEXT DEFAULT '',\n cost INTEGER NOT NULL DEFAULT 100, -- OpenCoins cost\n icon TEXT DEFAULT 'fa-star', -- Font Awesome icon\n color TEXT DEFAULT '#8b5cf6', -- accent color\n cooldown_seconds INTEGER DEFAULT 0, -- per-user cooldown\n max_per_stream INTEGER DEFAULT 0, -- 0 = unlimited\n requires_input INTEGER DEFAULT 0, -- viewer must type a message\n is_enabled INTEGER DEFAULT 1,\n is_global INTEGER DEFAULT 0, -- admin: available on all channels\n redemption_count INTEGER DEFAULT 0,\n sort_order INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (streamer_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE coin_transactions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n stream_id INTEGER, -- NULL for non-stream earnings\n amount INTEGER NOT NULL, -- positive = earned, negative = spent\n type TEXT NOT NULL CHECK(type IN ('watch', 'chat_bonus', 'follow_bonus', 'redeem', 'admin_grant', 'refund')),\n reward_id INTEGER, -- if type='redeem', links to coin_rewards\n message TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (reward_id) REFERENCES coin_rewards(id) ON DELETE SET NULL\n)",
+ "CREATE TABLE comments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n content_type TEXT NOT NULL CHECK(content_type IN ('vod', 'clip')),\n content_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n parent_id INTEGER, -- NULL = top-level, set = reply\n message TEXT NOT NULL,\n is_deleted INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (parent_id) REFERENCES comments(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE content_views (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n content_type TEXT NOT NULL CHECK(content_type IN ('vod', 'clip')),\n content_id INTEGER NOT NULL,\n ip TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(content_type, content_id, ip)\n)",
+ "CREATE TABLE control_config_buttons (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n config_id INTEGER NOT NULL,\n label TEXT NOT NULL,\n command TEXT NOT NULL,\n icon TEXT DEFAULT 'fa-gamepad',\n control_type TEXT DEFAULT 'button' CHECK(control_type IN ('button','toggle','dpad','keyboard')),\n key_binding TEXT,\n cooldown_ms INTEGER DEFAULT 500,\n sort_order INTEGER DEFAULT 0,\n btn_color TEXT DEFAULT '',\n btn_bg TEXT DEFAULT '',\n btn_border_color TEXT DEFAULT '',\n is_enabled INTEGER DEFAULT 1,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (config_id) REFERENCES control_configs(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE control_configs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n name TEXT NOT NULL,\n description TEXT DEFAULT '',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE control_whitelist (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n added_by INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(channel_id, user_id),\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE dm_blocks (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n blocker_id INTEGER NOT NULL,\n blocked_id INTEGER NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(blocker_id, blocked_id),\n FOREIGN KEY (blocker_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (blocked_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE dm_conversations (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT,\n is_group INTEGER DEFAULT 0,\n created_by INTEGER NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE dm_messages (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n conversation_id INTEGER NOT NULL,\n sender_id INTEGER NOT NULL,\n message TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (conversation_id) REFERENCES dm_conversations(id) ON DELETE CASCADE,\n FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE dm_participants (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n conversation_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n last_read_at DATETIME DEFAULT '1970-01-01 00:00:00',\n joined_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(conversation_id, user_id),\n FOREIGN KEY (conversation_id) REFERENCES dm_conversations(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE donation_goals (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n title TEXT NOT NULL,\n target_amount INTEGER NOT NULL, -- in Vibes (dollars)\n current_amount INTEGER DEFAULT 0,\n is_active INTEGER DEFAULT 1,\n image_url TEXT, -- optional goal media (webp image / webm video)\n media_type TEXT, -- 'image' | 'video'\n reached_at DATETIME, -- set when target hit; drives the ~1h celebration window\n sort_order INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE easter_egg_solves (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n egg_date TEXT NOT NULL,\n solver_key TEXT NOT NULL,\n user_id INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(egg_date, solver_key)\n )",
+ "CREATE TABLE \"emotes\" (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n code TEXT NOT NULL,\n url TEXT NOT NULL,\n animated INTEGER DEFAULT 0,\n width INTEGER DEFAULT 28,\n height INTEGER DEFAULT 28,\n is_global INTEGER DEFAULT 0,\n is_approved INTEGER DEFAULT 1,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n channel_owner_id INTEGER,\n size INTEGER DEFAULT 100,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE follows (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n follower_id INTEGER NOT NULL,\n streamer_id INTEGER NOT NULL,\n email_notify INTEGER DEFAULT 0,\n push_notify INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(follower_id, streamer_id),\n FOREIGN KEY (follower_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (streamer_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE hidden_relay_users (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_id INTEGER,\n platform TEXT NOT NULL,\n external_username TEXT NOT NULL,\n action TEXT DEFAULT 'hide' CHECK(action IN ('hide','ban')),\n reason TEXT,\n created_by INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE,\n FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL\n )",
+ "CREATE TABLE ip_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER,\n anon_id TEXT,\n ip_address TEXT NOT NULL,\n action TEXT NOT NULL DEFAULT 'chat',\n geo_country TEXT,\n geo_region TEXT,\n geo_city TEXT,\n geo_isp TEXT,\n geo_org TEXT,\n geo_ll TEXT,\n user_agent TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL\n )",
+ "CREATE TABLE kick_channel_cache (\n slug TEXT PRIMARY KEY,\n chatroom_id INTEGER,\n kick_channel_id INTEGER,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
+ "CREATE TABLE linked_accounts (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n service TEXT NOT NULL,\n service_user_id TEXT NOT NULL,\n service_username TEXT,\n linked_at DATETIME DEFAULT CURRENT_TIMESTAMP, subject_id TEXT,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n UNIQUE(service, service_user_id)\n )",
+ "CREATE TABLE managed_streams (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n channel_id INTEGER,\n slug TEXT, -- optional unique string ID (must contain non-numeric chars)\n title TEXT DEFAULT 'Untitled Stream',\n description TEXT DEFAULT '',\n category TEXT DEFAULT 'irl',\n tags TEXT DEFAULT '[]',\n protocol TEXT DEFAULT 'webrtc' CHECK(protocol IN ('jsmpeg', 'webrtc', 'rtmp')),\n stream_key TEXT UNIQUE NOT NULL, -- per-stream stable key (reusable across sessions)\n is_nsfw INTEGER DEFAULT 0,\n control_config_id INTEGER,\n sort_order INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, slot_clip_recording_enabled INTEGER DEFAULT 1, slot_clip_notify_enabled INTEGER DEFAULT 1, slot_powerchat_relay INTEGER DEFAULT 1, slot_powerchat_count_rs_views INTEGER DEFAULT 1, broadcast_settings TEXT DEFAULT '{}', streaming_method TEXT DEFAULT 'browser', browser_mode TEXT DEFAULT 'camera', pip_source_msid INTEGER, pip_defaults TEXT DEFAULT '{}', default_vod_visibility TEXT DEFAULT 'public', default_clip_visibility TEXT DEFAULT 'public', slot_vod_recording_enabled INTEGER DEFAULT 1, weather_zip TEXT DEFAULT NULL, weather_detail TEXT DEFAULT 'basic', weather_show_location INTEGER DEFAULT 0, mic_only_image TEXT DEFAULT NULL, ingest_authority TEXT NOT NULL DEFAULT 'live', openre_stream_id TEXT DEFAULT NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE SET NULL,\n FOREIGN KEY (control_config_id) REFERENCES control_configs(id) ON DELETE SET NULL\n)",
+ "CREATE TABLE media_request_settings (\n user_id INTEGER PRIMARY KEY,\n enabled INTEGER DEFAULT 1,\n request_cost INTEGER DEFAULT 25,\n max_per_user INTEGER DEFAULT 3,\n max_duration_seconds INTEGER DEFAULT 600,\n allow_youtube INTEGER DEFAULT 1,\n allow_vimeo INTEGER DEFAULT 1,\n allow_direct_media INTEGER DEFAULT 1,\n auto_advance INTEGER DEFAULT 1,\n currency TEXT DEFAULT 'opencoins' CHECK(currency IN ('free','vibes','opencoins','points')),\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, cost_mode TEXT DEFAULT 'flat' CHECK(cost_mode IN ('flat','per_minute')), cost_per_minute INTEGER DEFAULT 5, allow_live INTEGER DEFAULT 0, download_mode TEXT DEFAULT 'stream' CHECK(download_mode IN ('stream','download')),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE media_requests (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n streamer_id INTEGER NOT NULL,\n stream_id INTEGER,\n user_id INTEGER NOT NULL,\n username TEXT NOT NULL,\n input TEXT NOT NULL,\n canonical_url TEXT NOT NULL,\n embed_url TEXT,\n provider TEXT NOT NULL CHECK(provider IN ('youtube', 'vimeo', 'audio', 'video')),\n title TEXT NOT NULL,\n thumbnail_url TEXT,\n duration_seconds INTEGER,\n cost INTEGER NOT NULL DEFAULT 25,\n currency TEXT DEFAULT 'opencoins',\n queue_position INTEGER DEFAULT 0,\n status TEXT DEFAULT 'pending' CHECK(status IN ('pending', 'playing', 'played', 'skipped', 'removed', 'failed')),\n requested_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n started_at DATETIME,\n ended_at DATETIME,\n last_error TEXT, stream_url TEXT, download_status TEXT DEFAULT 'none' CHECK(download_status IN ('none','extracting','downloading','ready','failed')), file_path TEXT, playback_position REAL DEFAULT 0, refunded INTEGER DEFAULT 0, charge_state TEXT,\n FOREIGN KEY (streamer_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE moderation_actions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n scope_type TEXT NOT NULL DEFAULT 'site',\n scope_id INTEGER,\n actor_user_id INTEGER,\n target_user_id INTEGER,\n action_type TEXT NOT NULL,\n details TEXT DEFAULT '{}',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (actor_user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (target_user_id) REFERENCES users(id) ON DELETE SET NULL\n )",
+ "CREATE TABLE news_settings (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n scope TEXT NOT NULL DEFAULT 'global',\n scope_id INTEGER,\n source_id TEXT NOT NULL,\n enabled INTEGER NOT NULL DEFAULT 0,\n config TEXT DEFAULT '{}',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(scope, scope_id, source_id)\n )",
+ "CREATE TABLE openre_sessions (\n session_id TEXT PRIMARY KEY,\n managed_stream_id INTEGER,\n stream_id INTEGER,\n state TEXT NOT NULL,\n revision INTEGER NOT NULL DEFAULT 0,\n started_at DATETIME,\n ended_at DATETIME,\n confirmed_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
+ "CREATE TABLE paste_comments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n paste_id INTEGER NOT NULL,\n user_id INTEGER,\n parent_id INTEGER,\n anon_name TEXT,\n message TEXT NOT NULL,\n ip_address TEXT,\n is_deleted INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (paste_id) REFERENCES pastes(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (parent_id) REFERENCES paste_comments(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE paste_likes (\n paste_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (paste_id, user_id),\n FOREIGN KEY (paste_id) REFERENCES pastes(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE pastes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n slug TEXT UNIQUE NOT NULL,\n user_id INTEGER,\n type TEXT DEFAULT 'paste' CHECK(type IN ('paste', 'screenshot')),\n title TEXT NOT NULL DEFAULT 'Untitled',\n content TEXT,\n language TEXT DEFAULT 'text',\n visibility TEXT DEFAULT 'public' CHECK(visibility IN ('public', 'unlisted')),\n stream_id INTEGER,\n screenshot_path TEXT,\n metadata TEXT,\n burn_after_read INTEGER DEFAULT 0,\n forked_from INTEGER,\n pinned INTEGER DEFAULT 0,\n views INTEGER DEFAULT 0,\n ip_address TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, copies INTEGER DEFAULT 0, likes INTEGER DEFAULT 0, is_nsfw INTEGER DEFAULT 0, ai_summary TEXT, ai_tags TEXT, ai_analyzed_at DATETIME,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (forked_from) REFERENCES pastes(id) ON DELETE SET NULL\n )",
+ "CREATE TABLE payment_orders (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n provider TEXT NOT NULL,\n provider_ref TEXT,\n kind TEXT NOT NULL DEFAULT 'bucks', -- bucks | subscription\n amount_cents INTEGER NOT NULL DEFAULT 0,\n currency TEXT DEFAULT 'usd',\n bucks INTEGER DEFAULT 0,\n streamer_id INTEGER,\n status TEXT DEFAULT 'pending', -- pending | paid | failed | credited\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE pending_ip_messages (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_id INTEGER NOT NULL,\n stream_id INTEGER,\n ip_address TEXT NOT NULL,\n user_id INTEGER,\n anon_id TEXT,\n username TEXT,\n message TEXT NOT NULL,\n status TEXT DEFAULT 'pending' CHECK(status IN ('pending','approved','denied')),\n reviewed_by INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE,\n FOREIGN KEY (reviewed_by) REFERENCES users(id) ON DELETE SET NULL\n )",
+ "CREATE TABLE platform_connections (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n platform TEXT NOT NULL CHECK(platform IN ('youtube', 'twitch', 'kick')),\n platform_user_id TEXT,\n platform_username TEXT,\n channel_url TEXT,\n access_token TEXT,\n refresh_token TEXT,\n token_expires_at INTEGER,\n scope TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(user_id, platform),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE powerchat_connections (\n user_id INTEGER PRIMARY KEY,\n powerchat_username TEXT,\n powerchat_user_id TEXT,\n access_token TEXT,\n refresh_token TEXT,\n token_expires_at INTEGER, -- epoch ms\n scope TEXT,\n tip_page_url TEXT,\n last_error TEXT,\n connected_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE powerchat_webhook_deliveries (\n delivery_id TEXT PRIMARY KEY,\n event_type TEXT,\n received_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
+ "CREATE TABLE relay_users (\n platform TEXT NOT NULL,\n username TEXT NOT NULL,\n display_name TEXT,\n first_seen DATETIME DEFAULT CURRENT_TIMESTAMP,\n last_seen DATETIME DEFAULT CURRENT_TIMESTAMP,\n message_count INTEGER DEFAULT 0,\n PRIMARY KEY (platform, username)\n )",
+ "CREATE TABLE restream_destinations (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n platform TEXT NOT NULL CHECK(platform IN ('youtube', 'twitch', 'kick', 'custom')),\n name TEXT,\n server_url TEXT,\n stream_key TEXT,\n enabled INTEGER DEFAULT 1,\n auto_start INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, connection_id INTEGER DEFAULT NULL REFERENCES platform_connections(id) ON DELETE SET NULL, quality_preset TEXT DEFAULT 'auto', custom_video_bitrate INTEGER DEFAULT NULL, custom_audio_bitrate INTEGER DEFAULT NULL, custom_fps INTEGER DEFAULT NULL, custom_encoder_preset TEXT DEFAULT NULL, srt_latency_ms INTEGER DEFAULT NULL, srt_passphrase TEXT DEFAULT NULL, channel_url TEXT DEFAULT NULL, chat_relay INTEGER DEFAULT 0, powerchat_relay INTEGER DEFAULT 1, powerchat_count_views INTEGER DEFAULT 1, consecutive_failures INTEGER DEFAULT 0, cooldown_until DATETIME DEFAULT NULL, last_error TEXT DEFAULT NULL, last_failed_at DATETIME DEFAULT NULL, managed_stream_id INTEGER REFERENCES managed_streams(id) ON DELETE SET NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE \"robotstreamer_integrations\" (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n managed_stream_id INTEGER,\n enabled INTEGER DEFAULT 0,\n mirror_chat INTEGER DEFAULT 1,\n token TEXT,\n robot_id TEXT,\n owner_id TEXT,\n chat_url TEXT,\n control_url TEXT,\n rtc_sfu_url TEXT,\n stream_name TEXT,\n owner_name TEXT,\n last_validated_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (managed_stream_id) REFERENCES managed_streams(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE schema_migrations (\n id TEXT PRIMARY KEY,\n applied_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n mode TEXT NOT NULL CHECK (mode IN ('applied', 'adopted')),\n duration_ms INTEGER DEFAULT 0\n)",
+ "CREATE TABLE site_settings (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL DEFAULT '',\n description TEXT DEFAULT '',\n type TEXT DEFAULT 'string' CHECK(type IN ('string', 'number', 'boolean', 'json')),\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n)",
+ "CREATE TABLE stream_analytics (\n stream_id INTEGER PRIMARY KEY,\n avg_viewers REAL DEFAULT 0,\n peak_viewers INTEGER DEFAULT 0,\n unique_chatters INTEGER DEFAULT 0,\n total_messages INTEGER DEFAULT 0,\n total_watch_minutes INTEGER DEFAULT 0,\n new_followers INTEGER DEFAULT 0,\n clips_created INTEGER DEFAULT 0,\n coins_earned INTEGER DEFAULT 0,\n computed_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE stream_controls (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n label TEXT NOT NULL,\n command TEXT NOT NULL, -- command string sent to hardware\n icon TEXT DEFAULT 'fa-gamepad', -- Font Awesome icon class\n control_type TEXT DEFAULT 'button' CHECK(control_type IN ('button', 'toggle', 'slider', 'dpad', 'onvif', 'keyboard')),\n key_binding TEXT, -- keyboard shortcut\n cooldown_ms INTEGER DEFAULT 100,\n is_enabled INTEGER DEFAULT 1,\n sort_order INTEGER DEFAULT 0,\n camera_id INTEGER, -- NULL for non-ONVIF controls; references camera_profiles(id)\n onvif_movement TEXT, -- 'pan_left', 'pan_right', 'tilt_up', 'tilt_down', 'zoom_in', 'zoom_out', 'preset'\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, btn_color TEXT DEFAULT '', btn_bg TEXT DEFAULT '', btn_border_color TEXT DEFAULT '',\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE,\n FOREIGN KEY (camera_id) REFERENCES camera_profiles(id) ON DELETE SET NULL\n)",
+ "CREATE TABLE stream_first_chats (\n chatter_key TEXT NOT NULL,\n channel_user_id INTEGER NOT NULL,\n first_chat_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (chatter_key, channel_user_id)\n )",
+ "CREATE TABLE stream_memories (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n user_id INTEGER,\n offset_seconds INTEGER DEFAULT 0, -- seconds into the stream when captured\n captured_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n description TEXT,\n tags TEXT, -- JSON array\n thumbnail_url TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, transcript_json TEXT,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE stream_timeline_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n user_id INTEGER,\n vod_id INTEGER, -- set once the session becomes a VOD\n kind TEXT NOT NULL, -- 'speech' | 'sound'\n start_sec REAL NOT NULL, -- absolute seconds into the stream\n end_sec REAL,\n text TEXT, -- speech content\n label TEXT, -- sound event label\n confidence REAL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, lang TEXT, text_en TEXT,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE streamer_overviews (\n user_id INTEGER PRIMARY KEY,\n overview TEXT,\n model TEXT,\n sources TEXT,\n generated_at DATETIME DEFAULT CURRENT_TIMESTAMP, overview_short TEXT,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE streams (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n channel_id INTEGER,\n managed_stream_id INTEGER,\n control_config_id INTEGER,\n title TEXT DEFAULT 'Untitled Stream',\n description TEXT DEFAULT '',\n category TEXT DEFAULT 'irl',\n tags TEXT DEFAULT '[]',\n protocol TEXT DEFAULT 'webrtc' CHECK(protocol IN ('jsmpeg', 'webrtc', 'rtmp')),\n is_live INTEGER DEFAULT 0,\n is_nsfw INTEGER DEFAULT 0,\n viewer_count INTEGER DEFAULT 0,\n peak_viewers INTEGER DEFAULT 0,\n follower_count INTEGER DEFAULT 0,\n thumbnail_url TEXT,\n multi_cam INTEGER DEFAULT 0,\n started_at DATETIME,\n ended_at DATETIME,\n last_heartbeat DATETIME,\n duration_seconds INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, ai_overview TEXT, ai_title TEXT, ai_category TEXT, ai_tags TEXT, ai_overview_short TEXT,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE SET NULL,\n FOREIGN KEY (managed_stream_id) REFERENCES managed_streams(id) ON DELETE SET NULL,\n FOREIGN KEY (control_config_id) REFERENCES control_configs(id) ON DELETE SET NULL\n)",
+ "CREATE TABLE subscriptions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n subscriber_id INTEGER NOT NULL,\n streamer_id INTEGER NOT NULL,\n tier INTEGER DEFAULT 1 CHECK(tier IN (1, 2, 3)),\n is_active INTEGER DEFAULT 1,\n started_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n expires_at DATETIME, provider TEXT DEFAULT NULL, provider_ref TEXT DEFAULT NULL, price_cents INTEGER DEFAULT 0, currency TEXT DEFAULT 'usd', status TEXT DEFAULT 'active', cancel_at_period_end INTEGER DEFAULT 0, auto_renew INTEGER DEFAULT 0, current_period_end DATETIME DEFAULT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (subscriber_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (streamer_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE themes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n slug TEXT UNIQUE NOT NULL,\n author_id INTEGER,\n description TEXT DEFAULT '',\n mode TEXT DEFAULT 'dark' CHECK(mode IN ('dark', 'light')),\n variables TEXT NOT NULL DEFAULT '{}', -- JSON: CSS variable overrides\n preview_colors TEXT DEFAULT '{}', -- JSON: {bg, accent, text} for quick preview\n is_builtin INTEGER DEFAULT 0,\n is_public INTEGER DEFAULT 1,\n downloads INTEGER DEFAULT 0,\n rating_sum INTEGER DEFAULT 0,\n rating_count INTEGER DEFAULT 0,\n tags TEXT DEFAULT '[]', -- JSON array\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL\n)",
+ "CREATE TABLE transactions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n from_user_id INTEGER,\n to_user_id INTEGER,\n stream_id INTEGER,\n amount INTEGER NOT NULL, -- in bits (cents)\n type TEXT NOT NULL CHECK(type IN ('donation', 'purchase', 'subscription', 'cashout', 'refund', 'bonus')),\n status TEXT DEFAULT 'completed' CHECK(status IN ('pending', 'completed', 'failed', 'escrow', 'refunded')),\n message TEXT,\n paypal_transaction_id TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (from_user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (to_user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL\n)",
+ "CREATE TABLE translations (\n key TEXT PRIMARY KEY,\n src TEXT, dst TEXT,\n text TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
+ "CREATE TABLE tts_voice_overrides (\n identity_key TEXT PRIMARY KEY,\n voice TEXT,\n pitch INTEGER,\n speed INTEGER,\n gap INTEGER DEFAULT 0,\n set_by INTEGER,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
+ "CREATE TABLE user_cosmetics (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n item_id TEXT NOT NULL, -- references ITEMS key (fx_rainbow, px_sparkle, hat_crown, voice_robot etc.)\n category TEXT NOT NULL, -- 'name_effect'|'particle'|'hat'|'voice'\n unlocked_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(user_id, item_id),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE user_equipped (\n user_id INTEGER NOT NULL,\n slot TEXT NOT NULL, -- 'name_effect'|'particle'|'hat'|'voice'\n item_id TEXT NOT NULL,\n PRIMARY KEY (user_id, slot),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE user_equipped_tag (\n user_id INTEGER NOT NULL PRIMARY KEY,\n tag_id TEXT NOT NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE user_preferences (\n user_id INTEGER PRIMARY KEY,\n chat_settings TEXT DEFAULT '{}',\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE user_tags (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n tag_id TEXT NOT NULL,\n source TEXT DEFAULT 'shop',\n granted_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(user_id, tag_id),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
+ "CREATE TABLE user_themes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n theme_id INTEGER, -- NULL = custom\n custom_variables TEXT DEFAULT '{}', -- JSON: custom overrides\n is_custom INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (theme_id) REFERENCES themes(id) ON DELETE SET NULL\n)",
+ "CREATE TABLE users (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n username TEXT UNIQUE NOT NULL,\n email TEXT UNIQUE,\n password_hash TEXT NOT NULL,\n display_name TEXT,\n avatar_url TEXT,\n bio TEXT DEFAULT '',\n role TEXT DEFAULT 'user' CHECK(role IN ('user', 'streamer', 'global_mod', 'admin')),\n stream_key TEXT UNIQUE,\n openvibe_bucks_balance REAL DEFAULT 0.00,\n openvibe_coins_balance INTEGER DEFAULT 0,\n is_banned INTEGER DEFAULT 0,\n ban_reason TEXT,\n profile_color TEXT DEFAULT '#8b5cf6',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n last_seen DATETIME DEFAULT CURRENT_TIMESTAMP\n, openvibe_bucks_cashout_balance REAL DEFAULT 0.00, token_valid_after TEXT DEFAULT NULL, max_managed_streams INTEGER DEFAULT 3, avatar_paste_id INTEGER DEFAULT NULL, is_owner INTEGER DEFAULT 0)",
+ "CREATE TABLE verification_keys (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n key TEXT UNIQUE NOT NULL,\n target_username TEXT NOT NULL, -- the reserved username this key unlocks\n note TEXT DEFAULT '', -- admin notes (e.g. RS-Companion user_id)\n created_by INTEGER NOT NULL, -- admin who generated it\n used_by INTEGER, -- user who redeemed it (NULL until used)\n status TEXT DEFAULT 'active' CHECK(status IN ('active', 'used', 'revoked')),\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n used_at DATETIME,\n FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (used_by) REFERENCES users(id) ON DELETE SET NULL\n)",
+ "CREATE TABLE vibe_coding_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n managed_stream_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n stream_id INTEGER,\n session_key TEXT,\n event_id TEXT NOT NULL,\n sequence_num INTEGER DEFAULT 0,\n event_type TEXT NOT NULL,\n visibility TEXT DEFAULT 'public' CHECK(visibility IN ('public', 'streamer')),\n depth TEXT DEFAULT 'standard',\n summary TEXT DEFAULT '',\n payload_json TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (managed_stream_id) REFERENCES managed_streams(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n UNIQUE (managed_stream_id, event_id)\n)",
+ "CREATE TABLE vibe_coding_sessions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n managed_stream_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n session_key TEXT NOT NULL,\n slot_slug TEXT,\n workspace_name TEXT,\n machine_name TEXT,\n extension_version TEXT,\n publisher_id TEXT,\n publisher_label TEXT,\n publisher_vendor TEXT,\n publisher_client_type TEXT,\n publisher_client_name TEXT,\n publisher_client_version TEXT,\n publisher_capabilities_json TEXT,\n publisher_depth TEXT DEFAULT 'standard',\n status TEXT DEFAULT 'active' CHECK(status IN ('active', 'ended')),\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n last_event_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n ended_at DATETIME,\n FOREIGN KEY (managed_stream_id) REFERENCES managed_streams(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n UNIQUE (managed_stream_id, session_key)\n)",
+ "CREATE TABLE viewer_samples (\n sampled_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n viewers INTEGER NOT NULL DEFAULT 0,\n live_streams INTEGER NOT NULL DEFAULT 0\n )",
+ "CREATE TABLE viewer_snapshots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n viewer_count INTEGER DEFAULT 0,\n chat_messages_5m INTEGER DEFAULT 0, -- messages in trailing 5 min window\n recorded_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE vod_ai_state (\n vod_id INTEGER PRIMARY KEY,\n ai_overview_short TEXT,\n ai_transcript_json TEXT,\n transcript_status TEXT,\n transcript_attempts INTEGER DEFAULT 0,\n transcript_error TEXT,\n transcript_next_at DATETIME\n , ai_overview TEXT, transcript_partial_json TEXT, transcript_progress_sec INTEGER DEFAULT 0)",
+ "CREATE TABLE vods (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER,\n user_id INTEGER NOT NULL,\n title TEXT,\n description TEXT DEFAULT '',\n file_path TEXT NOT NULL,\n thumbnail_url TEXT,\n master_file_path TEXT,\n file_size INTEGER DEFAULT 0,\n duration_seconds INTEGER DEFAULT 0,\n probe_duration_seconds REAL DEFAULT 0,\n probe_format_json TEXT DEFAULT '',\n health_status TEXT DEFAULT 'unknown',\n health_score INTEGER DEFAULT 0,\n health_issues_json TEXT DEFAULT '[]',\n last_health_scan_at DATETIME,\n quarantined_at DATETIME,\n is_public INTEGER DEFAULT 1,\n is_recording INTEGER DEFAULT 0,\n view_count INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, clips_only INTEGER DEFAULT 0, storage_provider TEXT DEFAULT 'local', storage_key TEXT, ai_overview TEXT, ai_transcript TEXT, ai_analyzed_at DATETIME, visibility TEXT DEFAULT 'public',\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
+ "CREATE TABLE vpn_approvals (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n ip_address TEXT NOT NULL,\n user_id INTEGER,\n status TEXT DEFAULT 'pending' CHECK(status IN ('pending', 'approved', 'denied')),\n reviewed_by INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (reviewed_by) REFERENCES users(id) ON DELETE SET NULL\n)",
+ "CREATE TABLE watch_time (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n stream_id INTEGER NOT NULL,\n minutes_watched INTEGER DEFAULT 0,\n last_heartbeat DATETIME DEFAULT CURRENT_TIMESTAMP,\n coins_earned INTEGER DEFAULT 0, -- coins earned this session\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(user_id, stream_id),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL\n)",
+ "CREATE INDEX idx_ai_usage_created ON ai_usage(created_at)",
+ "CREATE INDEX idx_ai_usage_owner_day ON ai_usage(owner_user_id, created_at)",
+ "CREATE INDEX idx_ai_usage_source_day ON ai_usage(source, created_at)",
+ "CREATE INDEX idx_ai_viewer_log_created ON ai_viewer_log(created_at)",
+ "CREATE INDEX idx_aiv_log_channel ON ai_viewer_log(channel_user_id, id)",
+ "CREATE INDEX idx_aiv_threads_open ON ai_viewer_threads(channel_user_id, state, updated_at)",
+ "CREATE INDEX idx_anon_ip_created ON anon_ip_mappings(created_at)",
+ "CREATE INDEX idx_approved_ips_channel ON approved_ips(channel_id)",
+ "CREATE INDEX idx_approved_ips_ip ON approved_ips(ip_address)",
+ "CREATE INDEX idx_arena_beefs_status ON arena_beefs (status, a_user_id, b_user_id)",
+ "CREATE INDEX idx_arena_mic_created ON arena_mic_moments (created_at)",
+ "CREATE INDEX idx_arena_mic_user ON arena_mic_moments (user_id, id)",
+ "CREATE INDEX idx_arena_xp_log_user ON arena_xp_log (user_id, created_at)",
+ "CREATE INDEX idx_bans_ip ON bans(ip_address)",
+ "CREATE INDEX idx_bans_stream_id ON bans(stream_id)",
+ "CREATE INDEX idx_bans_user ON bans(user_id)",
+ "CREATE INDEX idx_cameras_stream_id ON cameras(stream_id)",
+ "CREATE INDEX idx_channel_ai_bots_channel ON channel_ai_bots(channel_user_id, is_active)",
+ "CREATE UNIQUE INDEX idx_channel_ai_bots_uname ON channel_ai_bots(channel_user_id, username)",
+ "CREATE INDEX idx_channel_mods_channel ON channel_moderators(channel_id)",
+ "CREATE INDEX idx_channel_mods_user ON channel_moderators(user_id)",
+ "CREATE INDEX idx_channel_sounds_cmd ON channel_sounds(channel_owner_id, command)",
+ "CREATE INDEX idx_channel_sounds_owner ON channel_sounds(channel_owner_id)",
+ "CREATE INDEX idx_channels_user_id ON channels(user_id)",
+ "CREATE INDEX idx_chat_ai_scope ON chat_ai_summaries(scope, subject_id, window)",
+ "CREATE INDEX idx_chat_autodelete ON chat_messages(auto_delete_at)",
+ "CREATE INDEX idx_chat_channel_user_ts ON chat_messages(channel_user_id, timestamp)",
+ "CREATE INDEX idx_chat_stream_id ON chat_messages(stream_id)",
+ "CREATE INDEX idx_chat_stream_ts ON chat_messages(stream_id, timestamp)",
+ "CREATE INDEX idx_chat_timestamp ON chat_messages(timestamp)",
+ "CREATE UNIQUE INDEX idx_chat_tl_dedup ON chat_timeline_events(scope, subject_id, ts, label)",
+ "CREATE INDEX idx_chat_tl_scope_ts ON chat_timeline_events(scope, subject_id, ts DESC)",
+ "CREATE INDEX idx_chat_ts_deleted ON chat_messages(timestamp, is_deleted)",
+ "CREATE INDEX idx_chat_user_id ON chat_messages(user_id)",
+ "CREATE UNIQUE INDEX idx_clip_ai_state_clip_id_unique ON clip_ai_state(clip_id)",
+ "CREATE INDEX idx_clips_stream_id ON clips(stream_id)",
+ "CREATE INDEX idx_clips_user_id ON clips(user_id)",
+ "CREATE INDEX idx_coin_redemptions_stream ON coin_redemptions(stream_id)",
+ "CREATE INDEX idx_coin_redemptions_user ON coin_redemptions(user_id)",
+ "CREATE INDEX idx_coin_rewards_streamer ON coin_rewards(streamer_id)",
+ "CREATE INDEX idx_coin_tx_created ON coin_transactions(created_at)",
+ "CREATE INDEX idx_coin_tx_stream ON coin_transactions(stream_id)",
+ "CREATE INDEX idx_coin_tx_user ON coin_transactions(user_id)",
+ "CREATE INDEX idx_comments_content ON comments(content_type, content_id)",
+ "CREATE INDEX idx_comments_parent ON comments(parent_id)",
+ "CREATE INDEX idx_comments_user ON comments(user_id)",
+ "CREATE INDEX idx_config_buttons_config ON control_config_buttons(config_id)",
+ "CREATE INDEX idx_content_views_lookup ON content_views(content_type, content_id)",
+ "CREATE INDEX idx_control_configs_user ON control_configs(user_id)",
+ "CREATE INDEX idx_control_whitelist_channel ON control_whitelist(channel_id)",
+ "CREATE INDEX idx_cp_log_user ON channel_points_log(user_id, streamer_id, id)",
+ "CREATE INDEX idx_dm_blocks_blocked ON dm_blocks(blocked_id)",
+ "CREATE INDEX idx_dm_blocks_blocker ON dm_blocks(blocker_id)",
+ "CREATE INDEX idx_dm_messages_conv ON dm_messages(conversation_id, created_at)",
+ "CREATE INDEX idx_dm_messages_sender ON dm_messages(sender_id)",
+ "CREATE INDEX idx_dm_participants_conv ON dm_participants(conversation_id)",
+ "CREATE INDEX idx_dm_participants_user ON dm_participants(user_id)",
+ "CREATE INDEX idx_egg_solves_date ON easter_egg_solves(egg_date)",
+ "CREATE UNIQUE INDEX idx_emotes_channel_code ON emotes(COALESCE(channel_owner_id, user_id), code)",
+ "CREATE INDEX idx_emotes_channel_owner ON emotes(channel_owner_id)",
+ "CREATE INDEX idx_emotes_code ON emotes(code)",
+ "CREATE INDEX idx_emotes_global ON emotes(is_global)",
+ "CREATE INDEX idx_emotes_user ON emotes(user_id)",
+ "CREATE INDEX idx_follows_follower ON follows(follower_id)",
+ "CREATE INDEX idx_follows_streamer ON follows(streamer_id)",
+ "CREATE INDEX idx_goals_active ON donation_goals(is_active)",
+ "CREATE INDEX idx_hidden_relay_channel ON hidden_relay_users(channel_id, platform)",
+ "CREATE INDEX idx_ip_log_action ON ip_log(action)",
+ "CREATE INDEX idx_ip_log_created ON ip_log(created_at)",
+ "CREATE INDEX idx_ip_log_ip ON ip_log(ip_address)",
+ "CREATE INDEX idx_ip_log_user ON ip_log(user_id)",
+ "CREATE INDEX idx_linked_service ON linked_accounts(service, service_user_id)",
+ "CREATE INDEX idx_linked_subject ON linked_accounts(subject_id)",
+ "CREATE INDEX idx_linked_user ON linked_accounts(user_id)",
+ "CREATE INDEX idx_managed_streams_key ON managed_streams(stream_key)",
+ "CREATE INDEX idx_managed_streams_slug ON managed_streams(slug)",
+ "CREATE INDEX idx_managed_streams_user ON managed_streams(user_id)",
+ "CREATE INDEX idx_media_requests_canonical ON media_requests(streamer_id, canonical_url, status)",
+ "CREATE INDEX idx_media_requests_streamer_status ON media_requests(streamer_id, status, queue_position, requested_at)",
+ "CREATE INDEX idx_media_requests_user_status ON media_requests(user_id, status, requested_at)",
+ "CREATE INDEX idx_mod_actions_actor ON moderation_actions(actor_user_id)",
+ "CREATE INDEX idx_mod_actions_created ON moderation_actions(created_at DESC)",
+ "CREATE INDEX idx_mod_actions_scope ON moderation_actions(scope_type, scope_id)",
+ "CREATE INDEX idx_openre_sessions_state ON openre_sessions(state)",
+ "CREATE INDEX idx_openre_sessions_stream ON openre_sessions(stream_id)",
+ "CREATE INDEX idx_paste_comments_ip ON paste_comments(ip_address)",
+ "CREATE INDEX idx_paste_comments_parent ON paste_comments(parent_id)",
+ "CREATE INDEX idx_paste_comments_paste ON paste_comments(paste_id)",
+ "CREATE INDEX idx_paste_comments_user ON paste_comments(user_id)",
+ "CREATE INDEX idx_pastes_created ON pastes(created_at)",
+ "CREATE INDEX idx_pastes_pinned ON pastes(pinned)",
+ "CREATE INDEX idx_pastes_slug ON pastes(slug)",
+ "CREATE INDEX idx_pastes_type ON pastes(type)",
+ "CREATE INDEX idx_pastes_user ON pastes(user_id)",
+ "CREATE INDEX idx_pastes_visibility ON pastes(visibility)",
+ "CREATE INDEX idx_payment_orders_ref ON payment_orders(provider, provider_ref)",
+ "CREATE INDEX idx_payment_orders_user ON payment_orders(user_id)",
+ "CREATE INDEX idx_pending_ip_channel ON pending_ip_messages(channel_id, status)",
+ "CREATE INDEX idx_powerchat_conn_pcuid ON powerchat_connections(powerchat_user_id)",
+ "CREATE INDEX idx_powerchat_conn_username ON powerchat_connections(powerchat_username)",
+ "CREATE INDEX idx_restream_dest_managed ON restream_destinations(managed_stream_id)",
+ "CREATE INDEX idx_restream_dest_user ON restream_destinations(user_id)",
+ "CREATE UNIQUE INDEX idx_rs_integrations_user_slot\n ON robotstreamer_integrations(user_id, IFNULL(managed_stream_id, 0))",
+ "CREATE INDEX idx_sfc_channel ON stream_first_chats(channel_user_id)",
+ "CREATE UNIQUE INDEX idx_stream_memories_moment_unique ON stream_memories(stream_id, offset_seconds)",
+ "CREATE INDEX idx_stream_memories_stream ON stream_memories(stream_id, offset_seconds)",
+ "CREATE INDEX idx_streams_channel_id ON streams(channel_id)",
+ "CREATE INDEX idx_streams_created ON streams(created_at)",
+ "CREATE INDEX idx_streams_is_live ON streams(is_live)",
+ "CREATE INDEX idx_streams_live_ended ON streams(is_live, ended_at)",
+ "CREATE INDEX idx_streams_managed_ended ON streams(managed_stream_id, ended_at)",
+ "CREATE INDEX idx_streams_started ON streams(started_at)",
+ "CREATE INDEX idx_streams_user_id ON streams(user_id)",
+ "CREATE INDEX idx_subs_provider_ref ON subscriptions(provider, provider_ref)",
+ "CREATE INDEX idx_subs_streamer ON subscriptions(streamer_id, status)",
+ "CREATE INDEX idx_subs_subscriber ON subscriptions(subscriber_id, status)",
+ "CREATE INDEX idx_themes_author ON themes(author_id)",
+ "CREATE INDEX idx_themes_public ON themes(is_public)",
+ "CREATE INDEX idx_themes_slug ON themes(slug)",
+ "CREATE INDEX idx_timeline_kind ON stream_timeline_events(stream_id, kind, start_sec)",
+ "CREATE INDEX idx_timeline_stream ON stream_timeline_events(stream_id, start_sec)",
+ "CREATE INDEX idx_timeline_user_kind_created ON stream_timeline_events(user_id, kind, created_at)",
+ "CREATE INDEX idx_timeline_vod ON stream_timeline_events(vod_id, start_sec)",
+ "CREATE INDEX idx_transactions_to_user ON transactions(to_user_id)",
+ "CREATE INDEX idx_tx_type_created ON transactions(type, created_at)",
+ "CREATE INDEX idx_user_cosmetics_user ON user_cosmetics(user_id)",
+ "CREATE INDEX idx_user_tags_user ON user_tags(user_id)",
+ "CREATE INDEX idx_user_themes_user ON user_themes(user_id)",
+ "CREATE INDEX idx_users_created ON users(created_at)",
+ "CREATE INDEX idx_users_role ON users(role)",
+ "CREATE INDEX idx_users_username_nocase ON users(username COLLATE NOCASE)",
+ "CREATE INDEX idx_vibe_events_managed ON vibe_coding_events(managed_stream_id, id DESC)",
+ "CREATE INDEX idx_vibe_events_stream ON vibe_coding_events(stream_id)",
+ "CREATE INDEX idx_vibe_sessions_managed ON vibe_coding_sessions(managed_stream_id)",
+ "CREATE INDEX idx_vibe_sessions_user ON vibe_coding_sessions(user_id)",
+ "CREATE INDEX idx_viewer_samples_at ON viewer_samples(sampled_at)",
+ "CREATE INDEX idx_viewer_snapshots_stream ON viewer_snapshots(stream_id, recorded_at)",
+ "CREATE INDEX idx_vkeys_key ON verification_keys(key)",
+ "CREATE INDEX idx_vkeys_status ON verification_keys(status)",
+ "CREATE INDEX idx_vkeys_target ON verification_keys(target_username)",
+ "CREATE UNIQUE INDEX idx_vod_ai_state_vod_id_unique ON vod_ai_state(vod_id)",
+ "CREATE INDEX idx_vods_user_id ON vods(user_id)",
+ "CREATE INDEX idx_watch_time_stream ON watch_time(stream_id)",
+ "CREATE INDEX idx_watch_time_user ON watch_time(user_id)"
+ ],
+ "ledger": {
+ "schema_migrations": [
+ {
+ "id": "001_vibes_decimal_to_bits",
+ "applied_at": "2026-09-26 13:44:27",
+ "mode": "adopted",
+ "duration_ms": 0
+ },
+ {
+ "id": "002_pastes_ai_columns",
+ "applied_at": "2026-09-26 13:44:27",
+ "mode": "applied",
+ "duration_ms": 6
+ },
+ {
+ "id": "004_hot_path_indexes",
+ "applied_at": "2026-09-26 13:44:27",
+ "mode": "applied",
+ "duration_ms": 1
+ }
+ ]
+ },
+ "lazy": [
+ "CREATE TABLE IF NOT EXISTS module_summary_pushes ( user_id INTEGER NOT NULL, namespace TEXT NOT NULL, hash TEXT NOT NULL, pushed_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_id, namespace) )",
+ "CREATE TABLE IF NOT EXISTS token_revocations ( subject_id TEXT PRIMARY KEY, valid_after_ms INTEGER NOT NULL, reason TEXT, updated_at INTEGER NOT NULL )",
+ "CREATE TABLE IF NOT EXISTS subject_projection ( subject_id TEXT PRIMARY KEY, network_user_id INTEGER, revision INTEGER NOT NULL, username TEXT NOT NULL, display_name TEXT, avatar_url TEXT, profile_color TEXT, role TEXT NOT NULL, banned INTEGER NOT NULL DEFAULT 0, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
+ "CREATE TABLE IF NOT EXISTS username_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, old_username TEXT NOT NULL, new_username TEXT NOT NULL, changed_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
+ "CREATE TABLE IF NOT EXISTS chat_bridge_outbox ( id INTEGER PRIMARY KEY AUTOINCREMENT, boot TEXT NOT NULL, ref INTEGER, op TEXT NOT NULL, args TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
+ "CREATE TABLE IF NOT EXISTS chat_staged_outbox ( seq INTEGER PRIMARY KEY AUTOINCREMENT, tbl TEXT NOT NULL, op TEXT NOT NULL, pk TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
+ "CREATE TABLE IF NOT EXISTS chat_dual_read_stats ( tbl TEXT PRIMARY KEY, compared INTEGER NOT NULL DEFAULT 0, matched INTEGER NOT NULL DEFAULT 0, mismatched INTEGER NOT NULL DEFAULT 0, inconclusive INTEGER NOT NULL DEFAULT 0, errors INTEGER NOT NULL DEFAULT 0, last_mismatch_at DATETIME, last_mismatch TEXT, since DATETIME DEFAULT CURRENT_TIMESTAMP )",
+ "CREATE TABLE IF NOT EXISTS comment_thread_refs ( content_type TEXT NOT NULL, content_id INTEGER NOT NULL, thread_id INTEGER NOT NULL, access_id TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (content_type, content_id) )",
+ "CREATE TABLE IF NOT EXISTS search_doc_pushes ( user_id INTEGER PRIMARY KEY, hash TEXT NOT NULL, revision INTEGER NOT NULL, deleted INTEGER NOT NULL DEFAULT 0, pushed_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
+ "CREATE TABLE IF NOT EXISTS search_media_pushes ( kind TEXT NOT NULL, media_id INTEGER NOT NULL, hash TEXT NOT NULL, revision INTEGER NOT NULL, deleted INTEGER NOT NULL DEFAULT 0, pushed_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (kind, media_id) )",
+ "CREATE TABLE IF NOT EXISTS billing_actions ( id INTEGER PRIMARY KEY AUTOINCREMENT, action TEXT NOT NULL, idempotency_key TEXT NOT NULL UNIQUE, live_user_id INTEGER, live_ref TEXT, method TEXT NOT NULL, path TEXT NOT NULL, request_json TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', http_status INTEGER, billing_ref TEXT, response_json TEXT, error TEXT, attempts INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP )",
+ "CREATE TABLE IF NOT EXISTS opencoin_admin_grants ( id INTEGER PRIMARY KEY AUTOINCREMENT, client_key TEXT UNIQUE, admin_id INTEGER, user_id INTEGER NOT NULL, amount INTEGER NOT NULL, reason TEXT, balance INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
+ "CREATE TABLE IF NOT EXISTS stream_recaps ( stream_id INTEGER PRIMARY KEY, user_id INTEGER, json TEXT NOT NULL, ai INTEGER DEFAULT 0, announced_at DATETIME, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
+ "CREATE TABLE IF NOT EXISTS tips_deliveries ( idempotency_key TEXT PRIMARY KEY, effect TEXT, state TEXT NOT NULL DEFAULT 'pending', response_json TEXT, claimed_at INTEGER NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
+ "ALTER TABLE users ADD COLUMN theme_id INTEGER",
+ "ALTER TABLE streams ADD COLUMN call_mode TEXT DEFAULT NULL"
+ ],
+ "statements": [
+ "DELETE FROM ai_timeline_cache WHERE user_id = ?",
+ "DELETE FROM ai_viewer_log WHERE created_at < datetime('now', ?)",
+ "DELETE FROM app_state WHERE key = ?",
+ "DELETE FROM approved_ips WHERE channel_id = ? AND ip_address = ?",
+ "DELETE FROM bans WHERE id = ?",
+ "DELETE FROM bans WHERE ip_address IS NOT NULL AND banned_by = ? AND stream_id IS NULL AND reason LIKE ?",
+ "DELETE FROM bans WHERE user_id = ? AND stream_id = ?",
+ "DELETE FROM bans WHERE user_id = ? AND stream_id IS NULL",
+ "DELETE FROM camera_presets WHERE camera_id = ?",
+ "DELETE FROM camera_presets WHERE id = ?",
+ "DELETE FROM camera_profiles WHERE id = ?",
+ "DELETE FROM channel_ai_bots WHERE id = ?",
+ "DELETE FROM channel_moderators WHERE channel_id = ? AND user_id = ?",
+ "DELETE FROM channel_sounds WHERE id = ?",
+ "DELETE FROM chat_bridge_outbox",
+ "DELETE FROM chat_bridge_outbox WHERE id = ?",
+ "DELETE FROM chat_dual_read_stats WHERE tbl = ?",
+ "DELETE FROM coin_rewards WHERE id = ?",
+ "DELETE FROM control_config_buttons WHERE id = ?",
+ "DELETE FROM control_config_buttons WHERE id = ? AND config_id = ?",
+ "DELETE FROM control_configs WHERE id = ?",
+ "DELETE FROM control_whitelist WHERE id = ? AND channel_id = ?",
+ "DELETE FROM dm_blocks WHERE blocker_id = ? AND blocked_id = ?",
+ "DELETE FROM dm_messages WHERE id = ?",
+ "DELETE FROM dm_participants WHERE conversation_id = ? AND user_id = ?",
+ "DELETE FROM donation_goals WHERE id = ? AND user_id = ?",
+ "DELETE FROM emotes WHERE id = ?",
+ "DELETE FROM follows WHERE follower_id = ? AND streamer_id = ?",
+ "DELETE FROM hidden_relay_users WHERE id = ?",
+ "DELETE FROM hidden_relay_users WHERE platform = ? AND external_username = ? AND channel_id IS ?",
+ "DELETE FROM linked_accounts WHERE service = 'hobotools'",
+ "DELETE FROM managed_streams WHERE id = ? AND user_id = ?",
+ "DELETE FROM media_requests WHERE id = ? AND charge_state = 'charging'",
+ "DELETE FROM platform_connections WHERE user_id = ? AND platform = ?",
+ "DELETE FROM powerchat_connections WHERE user_id = ?",
+ "DELETE FROM powerchat_webhook_deliveries WHERE received_at < datetime('now', ?)",
+ "DELETE FROM restream_destinations WHERE id = ?",
+ "DELETE FROM robotstreamer_integrations WHERE user_id = ? AND managed_stream_id = ?",
+ "DELETE FROM site_settings WHERE key = 'ai_viewers_engine'",
+ "DELETE FROM site_settings WHERE key = 'powerchat_site_user_id'",
+ "DELETE FROM site_settings WHERE key = ?",
+ "DELETE FROM site_settings WHERE key IN ('auto_clip_backfill','auto_clip_log','daily_easter_egg','home_hero_moments','home_hero_slogans') OR key LIKE 'ai_whole_overview_%'",
+ "DELETE FROM stream_controls WHERE stream_id = ? AND (control_type != ? OR control_type IS NULL)",
+ "DELETE FROM stream_memories WHERE id NOT IN ( SELECT id FROM ( SELECT id, ROW_NUMBER() OVER ( PARTITION BY stream_id, offset_seconds ORDER BY LENGTH(COALESCE(description,'')) DESC, (transcript_json IS NOT NULL) DESC, id DESC) AS rn FROM stream_memories) WHERE rn = 1)",
+ "DELETE FROM themes WHERE id = ?",
+ "DELETE FROM tips_deliveries WHERE created_at < datetime('now', ?)",
+ "DELETE FROM tips_deliveries WHERE idempotency_key = ? AND state = 'pending'",
+ "DELETE FROM tts_voice_overrides WHERE identity_key = ?",
+ "DELETE FROM user_cosmetics WHERE user_id = ? AND item_id = ?",
+ "DELETE FROM user_equipped WHERE user_id = ? AND slot = ?",
+ "DELETE FROM user_equipped WHERE user_id = ? AND slot = ? AND item_id = ?",
+ "DELETE FROM viewer_samples WHERE sampled_at < datetime('now', '-400 days')",
+ "INSERT INTO ai_chatbot_configs (user_id, enabled, base_url, api_token, model, transcribe_enabled, transcribe_model, num_bots, post_interval_seconds, persona, vision_enabled) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO ai_timeline_cache (user_id, payload, generated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET payload = excluded.payload, generated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO ai_usage (kind, model, input_tokens, output_tokens, cached_tokens, cost_usd, owner_user_id, source, role, provider, latency_ms) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO ai_viewer_log (channel_user_id, stream_id, event, bot_username, target, thread_id, chat_message_id, text, reason, tokens_in, tokens_cached, tokens_out, cost_usd, model) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO ai_viewer_threads (channel_user_id, stream_id, kind, participants_json, topic, awaiting) VALUES (?, ?, ?, ?, ?, ?)",
+ "INSERT INTO anon_ip_mappings (ip, anon_num, created_at) VALUES (?, ?, CURRENT_TIMESTAMP)",
+ "INSERT INTO api_keys (user_id, key_hash, label, permissions) VALUES (?, ?, ?, ?)",
+ "INSERT INTO api_tokens (user_id, token_hash, label, scopes, expires_at) VALUES (?, ?, ?, ?, ?)",
+ "INSERT INTO app_state (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO arena_beefs (a_user_id, b_user_id, on_clock, clock_until, ends_at, feed_json, opener_line, rematch, bounty_topic_id, headline) VALUES (?, ?, 'b', ?, ?, '[]', ?, ?, NULL, ?)",
+ "INSERT INTO arena_mic_moments (user_id, stream_id, vod_id, sec, kind, target_user_id, beef_id, aimed_at, text, about, quality, announcer, said_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, COALESCE(?, CURRENT_TIMESTAMP))",
+ "INSERT INTO arena_profiles (user_id, image_error, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET image_error = excluded.image_error, updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO arena_profiles (user_id, image_path, image_prompt, image_model, image_generated_at, image_error, updated_at) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP, NULL, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET image_path = excluded.image_path, image_prompt = excluded.image_prompt, image_model = excluded.image_model, image_generated_at = CURRENT_TIMESTAMP, image_error = NULL, updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO arena_profiles (user_id, persona_json, persona_model, persona_generated_at, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET persona_json = excluded.persona_json, persona_model = excluded.persona_model, persona_generated_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO arena_profiles (user_id, quotes_json, quotes_generated_at, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET quotes_json = excluded.quotes_json, quotes_generated_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO arena_profiles (user_id, stats_json, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET stats_json = excluded.stats_json, updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO arena_trash_levels (user_id, xp, level) VALUES (?, ?, ?) ON CONFLICT(user_id) DO UPDATE SET xp = xp + excluded.xp, level = ?, updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO arena_xp_log (user_id, amount, reason, ref_id) VALUES (?, ?, ?, ?)",
+ "INSERT INTO bans (ip_address, reason, banned_by, expires_at) VALUES (?, ?, ?, ?)",
+ "INSERT INTO bans (stream_id, ip_address, anon_id, reason, banned_by) VALUES (?, ?, ?, ?, ?)",
+ "INSERT INTO bans (stream_id, ip_address, anon_id, reason, banned_by, expires_at) VALUES (?, ?, ?, ?, ?, ?)",
+ "INSERT INTO bans (stream_id, user_id, ip_address, reason, banned_by, expires_at) VALUES (?, ?, ?, ?, ?, ?)",
+ "INSERT INTO bans (stream_id, user_id, reason, banned_by) VALUES (?, ?, ?, ?)",
+ "INSERT INTO bans (stream_id, user_id, reason, banned_by, expires_at) VALUES (?, ?, ?, ?, ?)",
+ "INSERT INTO bans (user_id, ip_address, reason, banned_by, expires_at) VALUES (?, ?, ?, ?, ?)",
+ "INSERT INTO bans (user_id, reason, banned_by, expires_at) VALUES (?, ?, ?, ?)",
+ "INSERT INTO camera_presets (camera_id, name, pan, tilt, zoom, preset_token) VALUES (?, ?, ?, ?, ?, ?)",
+ "INSERT INTO camera_profiles (user_id, stream_id, name, onvif_url, username, password_hash, pan_speed, tilt_speed, zoom_speed) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO cameras (stream_id, camera_index, label, protocol) VALUES (?, 0, 'Main', 'webrtc')",
+ "INSERT INTO cameras (stream_id, camera_index, label, protocol) VALUES (?, 0, 'Main', ?)",
+ "INSERT INTO channel_ai_bots (channel_user_id, username, display_name, avatar_color, source, cloned_from_kind, cloned_from_ref, persona_json, brain_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO channel_ai_config (user_id, enabled, num_ambient_bots, pacing_seconds, persona, transcribe_enabled, vision_enabled, use_shared_key, daily_budget_cents, byo_key, byo_base_url, byo_model) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO channel_moderation_settings ( channel_id, slow_mode_seconds, followers_only, emote_only, allow_anonymous, links_allowed, gifs_enabled, account_age_gate_hours, caps_percentage_limit, aggressive_filter, max_message_length, slur_filter_enabled, slur_filter_use_builtin, slur_filter_terms, slur_filter_regexes, slur_filter_nudge_message, slur_filter_disabled_categories, ip_approval_mode, soundboard_enabled, soundboard_allow_pitch, soundboard_allow_speed, soundboard_banned_ids, viewer_auto_delete_enabled, viewer_delete_all_enabled, custom_emotes_enabled, custom_sounds_enabled, max_sound_seconds, uploads_mods_only, emote_scale, emote_size_min, emote_size_max, sounds_mods_only, mods_can_edit_about ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO channel_moderation_settings (channel_id) VALUES (?)",
+ "INSERT INTO channel_points (user_id, streamer_id, balance, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id, streamer_id) DO UPDATE SET balance = balance + excluded.balance, updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO channel_points_log (idempotency_key, user_id, streamer_id, delta, reason) VALUES (?, ?, ?, ?, ?)",
+ "INSERT INTO channel_sounds (channel_owner_id, command, url, mime, duration_seconds, created_by, created_by_name, emote_code) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO chat_ai_summaries (scope, subject_id, window, overview, memory_json, timeline_json, message_count, window_message_count, last_message_id, window_label, window_start, window_end, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(scope, subject_id, window) DO UPDATE SET overview = excluded.overview, memory_json = excluded.memory_json, timeline_json = excluded.timeline_json, message_count = excluded.message_count, window_message_count = excluded.window_message_count, last_message_id = excluded.last_message_id, window_label = excluded.window_label, window_start = excluded.window_start, window_end = excluded.window_end, updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO chat_bridge_outbox (boot, ref, op, args) VALUES (?, ?, ?, ?)",
+ "INSERT INTO chat_messages (stream_id, channel_user_id, user_id, anon_id, username, message, message_type, is_global, reply_to_id, source_platform, auto_delete_at, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO coin_redemptions (reward_id, user_id, stream_id, user_input) VALUES (?, ?, ?, ?)",
+ "INSERT INTO coin_rewards (streamer_id, title, description, cost, icon, color, cooldown_seconds, max_per_stream, requires_input, is_global, sort_order) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO coin_transactions (user_id, stream_id, amount, type, reward_id, message) VALUES (?, ?, ?, ?, ?, ?)",
+ "INSERT INTO control_config_buttons (config_id, label, command, icon, control_type, key_binding, cooldown_ms, sort_order, btn_color, btn_bg, btn_border_color) VALUES (?, ?, ?, ?, ?, ?, ?, ?, '', '', '')",
+ "INSERT INTO control_config_buttons (config_id, label, command, icon, control_type, key_binding, cooldown_ms, sort_order, btn_color, btn_bg, btn_border_color) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO control_configs (user_id, name, description) VALUES (?, ?, ?)",
+ "INSERT INTO dm_conversations (name, is_group, created_by) VALUES (?, ?, ?)",
+ "INSERT INTO dm_messages (conversation_id, sender_id, message) VALUES (?, ?, ?)",
+ "INSERT INTO donation_goals (user_id, title, target_amount) VALUES (?, ?, ?)",
+ "INSERT INTO donation_goals (user_id, title, target_amount, image_url, media_type, sort_order) VALUES (?, ?, ?, ?, ?, ?)",
+ "INSERT INTO emotes (user_id, code, url, animated, width, height, is_global, channel_owner_id, size) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO ip_log (user_id, anon_id, ip_address, action, geo_country, geo_region, geo_city, geo_isp, geo_org, geo_ll, user_agent) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO kick_channel_cache (slug, chatroom_id, kick_channel_id, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(slug) DO UPDATE SET chatroom_id = excluded.chatroom_id, kick_channel_id = COALESCE(excluded.kick_channel_id, kick_channel_cache.kick_channel_id), updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO managed_streams (user_id, channel_id, slug, title, description, category, protocol, streaming_method, stream_key, is_nsfw, control_config_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO managed_streams (user_id, channel_id, title, category, protocol, stream_key, is_nsfw) VALUES (?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO media_request_settings ( user_id, enabled, request_cost, max_per_user, max_duration_seconds, allow_youtube, allow_vimeo, allow_direct_media, auto_advance, cost_mode, cost_per_minute, allow_live, download_mode, currency ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO media_requests ( streamer_id, stream_id, user_id, username, input, canonical_url, embed_url, provider, title, thumbnail_url, duration_seconds, cost, queue_position, currency, status, charge_state ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO moderation_actions (scope_type, scope_id, actor_user_id, target_user_id, action_type, details) VALUES (?, ?, ?, ?, ?, ?)",
+ "INSERT INTO module_summary_pushes (user_id, namespace, hash, pushed_at) VALUES (?, ?, ?, ?) ON CONFLICT(user_id, namespace) DO UPDATE SET hash = excluded.hash, pushed_at = excluded.pushed_at",
+ "INSERT INTO news_settings (scope, scope_id, source_id, enabled, config) VALUES ('global', NULL, ?, ?, ?) ON CONFLICT(scope, scope_id, source_id) DO UPDATE SET enabled = excluded.enabled, config = excluded.config, updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO news_settings (scope, scope_id, source_id, enabled, config) VALUES ('user', ?, '_master', ?, '{}') ON CONFLICT(scope, scope_id, source_id) DO UPDATE SET enabled = excluded.enabled, updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO opencoin_admin_grants (client_key, admin_id, user_id, amount, reason) VALUES (?, ?, ?, ?, ?)",
+ "INSERT INTO openre_sessions (session_id, managed_stream_id, stream_id, state, revision, ended_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(session_id) DO UPDATE SET state = excluded.state, revision = excluded.revision, ended_at = excluded.ended_at, updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO openre_sessions (session_id, managed_stream_id, stream_id, state, revision, started_at, confirmed_at, updated_at) VALUES (?, ?, ?, 'live', ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON CONFLICT(session_id) DO UPDATE SET stream_id = excluded.stream_id, state = 'live', revision = excluded.revision, confirmed_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO payment_orders (user_id, provider, provider_ref, kind, amount_cents, currency, bucks, streamer_id, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO pending_ip_messages (channel_id, stream_id, ip_address, user_id, anon_id, username, message) VALUES (?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO platform_connections (user_id, platform, platform_user_id, platform_username, channel_url, access_token, refresh_token, token_expires_at, scope) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO relay_users (platform, username, display_name, first_seen, last_seen, message_count) VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1) ON CONFLICT(platform, username) DO UPDATE SET last_seen = CURRENT_TIMESTAMP, message_count = message_count + 1, display_name = excluded.display_name",
+ "INSERT INTO restream_destinations (user_id, managed_stream_id, platform, name, server_url, stream_key, enabled, auto_start, quality_preset, custom_video_bitrate, custom_audio_bitrate, custom_fps, custom_encoder_preset, srt_latency_ms, srt_passphrase, channel_url, chat_relay, powerchat_relay, powerchat_count_views) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO schema_migrations (id, mode, duration_ms) VALUES (?, ?, ?)",
+ "INSERT INTO search_doc_pushes (user_id, hash, revision, deleted, pushed_at) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET hash = excluded.hash, revision = excluded.revision, deleted = excluded.deleted, pushed_at = excluded.pushed_at",
+ "INSERT INTO search_media_pushes (kind, media_id, hash, revision, deleted, pushed_at) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(kind, media_id) DO UPDATE SET hash = excluded.hash, revision = excluded.revision, deleted = excluded.deleted, pushed_at = excluded.pushed_at",
+ "INSERT INTO site_settings (key, value) VALUES (?, ?)",
+ "INSERT INTO stream_analytics (stream_id, avg_viewers, peak_viewers, unique_chatters, total_messages, total_watch_minutes, new_followers, clips_created, coins_earned, computed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(stream_id) DO UPDATE SET avg_viewers = excluded.avg_viewers, peak_viewers = excluded.peak_viewers, unique_chatters = excluded.unique_chatters, total_messages = excluded.total_messages, total_watch_minutes = excluded.total_watch_minutes, new_followers = excluded.new_followers, clips_created = excluded.clips_created, coins_earned = excluded.coins_earned, computed_at = CURRENT_TIMESTAMP",
+ "INSERT INTO stream_controls (stream_id, label, command, icon, control_type, key_binding, cooldown_ms, sort_order, btn_color, btn_bg, btn_border_color) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO stream_recaps (stream_id, user_id, json, ai, created_at) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(stream_id) DO UPDATE SET json = excluded.json, ai = excluded.ai, created_at = CURRENT_TIMESTAMP",
+ "INSERT INTO stream_timeline_events (stream_id, user_id, vod_id, kind, start_sec, end_sec, text, label, confidence, lang, text_en) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO streamer_overviews (user_id, overview, overview_short, model, sources, generated_at) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET overview = excluded.overview, overview_short = excluded.overview_short, model = excluded.model, sources = excluded.sources, generated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO streams (user_id, channel_id, managed_stream_id, control_config_id, title, description, category, protocol, is_nsfw, thumbnail_url, is_live, started_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, CURRENT_TIMESTAMP)",
+ "INSERT INTO subject_projection (subject_id, network_user_id, revision, username, display_name, avatar_url, profile_color, role, banned, updated_at) VALUES (@subject, @nid, @rev, @username, @display_name, @avatar_url, @profile_color, @role, @banned, CURRENT_TIMESTAMP) ON CONFLICT(subject_id) DO UPDATE SET network_user_id = excluded.network_user_id, revision = excluded.revision, username = excluded.username, display_name = excluded.display_name, avatar_url = excluded.avatar_url, profile_color = excluded.profile_color, role = excluded.role, banned = excluded.banned, updated_at = excluded.updated_at",
+ "INSERT INTO subscriptions (subscriber_id, streamer_id, tier, provider, provider_ref, price_cents, currency, status, is_active, current_period_end, auto_renew) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO themes (name, slug, author_id, description, mode, variables, preview_colors, is_builtin, is_public, tags) VALUES (?, ?, ?, ?, ?, ?, ?, 0, 1, ?)",
+ "INSERT INTO themes (name, slug, mode, description, variables, preview_colors, is_builtin, is_public, tags) VALUES (?, ?, ?, ?, ?, ?, 1, 1, ?)",
+ "INSERT INTO token_revocations (subject_id, valid_after_ms, reason, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT(subject_id) DO UPDATE SET valid_after_ms = excluded.valid_after_ms, reason = excluded.reason, updated_at = excluded.updated_at WHERE excluded.valid_after_ms > token_revocations.valid_after_ms",
+ "INSERT INTO transactions (from_user_id, to_user_id, stream_id, amount, type, status, message) VALUES (?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO tts_voice_overrides (identity_key, voice, pitch, speed, gap, set_by, updated_at) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(identity_key) DO UPDATE SET voice=excluded.voice, pitch=excluded.pitch, speed=excluded.speed, gap=excluded.gap, set_by=excluded.set_by, updated_at=CURRENT_TIMESTAMP",
+ "INSERT INTO user_preferences (user_id, chat_settings, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET chat_settings = excluded.chat_settings, updated_at = CURRENT_TIMESTAMP",
+ "INSERT INTO user_themes (user_id, theme_id, custom_variables, is_custom) VALUES (?, ?, ?, ?)",
+ "INSERT INTO username_history (user_id, old_username, new_username) VALUES (?, ?, ?)",
+ "INSERT INTO users (username, email, password_hash, display_name, stream_key) VALUES (?, ?, ?, ?, ?)",
+ "INSERT INTO verification_keys (key, target_username, note, created_by) VALUES (?, ?, ?, ?)",
+ "INSERT INTO vibe_coding_events ( managed_stream_id, user_id, stream_id, session_key, event_id, sequence_num, event_type, visibility, depth, summary, payload_json ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(managed_stream_id, event_id) DO UPDATE SET stream_id = excluded.stream_id, sequence_num = excluded.sequence_num, event_type = excluded.event_type, visibility = excluded.visibility, depth = excluded.depth, summary = excluded.summary, payload_json = excluded.payload_json, created_at = CURRENT_TIMESTAMP",
+ "INSERT INTO vibe_coding_sessions ( managed_stream_id, user_id, session_key, slot_slug, workspace_name, machine_name, extension_version, publisher_id, publisher_label, publisher_vendor, publisher_client_type, publisher_client_name, publisher_client_version, publisher_capabilities_json, publisher_depth, status, last_event_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', CURRENT_TIMESTAMP) ON CONFLICT(managed_stream_id, session_key) DO UPDATE SET workspace_name = excluded.workspace_name, machine_name = excluded.machine_name, extension_version = excluded.extension_version, publisher_id = excluded.publisher_id, publisher_label = excluded.publisher_label, publisher_vendor = excluded.publisher_vendor, publisher_client_type = excluded.publisher_client_type, publisher_client_name = excluded.publisher_client_name, publisher_client_version = excluded.publisher_client_version, publisher_capabilities_json = excluded.publisher_capabilities_json, publisher_depth = excluded.publisher_depth, slot_slug = excluded.slot_slug, status = 'active', ended_at = NULL, last_event_at = CURRENT_TIMESTAMP",
+ "INSERT INTO viewer_samples (viewers, live_streams) VALUES (?, ?)",
+ "INSERT INTO viewer_snapshots (stream_id, viewer_count, chat_messages_5m) VALUES (?, ?, ?)",
+ "INSERT INTO watch_time (user_id, stream_id, minutes_watched) VALUES (?, ?, 1)",
+ "INSERT OR IGNORE INTO app_state (key, value, updated_at) SELECT key, value, COALESCE(updated_at, CURRENT_TIMESTAMP) FROM site_settings WHERE key IN ('auto_clip_backfill','auto_clip_log','daily_easter_egg','home_hero_moments','home_hero_slogans') OR key LIKE 'ai_whole_overview_%'",
+ "INSERT OR IGNORE INTO approved_ips (channel_id, ip_address, approved_by, source) VALUES (?, ?, ?, ?)",
+ "INSERT OR IGNORE INTO arena_beef_hype (beef_id, side, voter_key) VALUES (?, ?, ?)",
+ "INSERT OR IGNORE INTO billing_actions (action, idempotency_key, live_user_id, live_ref, method, path, request_json) VALUES (?, ?, ?, ?, ?, ?, ?)",
+ "INSERT OR IGNORE INTO channel_ai_config (user_id, enabled, num_ambient_bots, pacing_seconds, persona, transcribe_enabled, vision_enabled, use_shared_key, daily_budget_cents, byo_key, byo_base_url, byo_model) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT OR IGNORE INTO channel_moderators (channel_id, user_id, added_by) VALUES (?, ?, ?)",
+ "INSERT OR IGNORE INTO channels (user_id, title, description, category, protocol) VALUES (?, ?, ?, ?, ?)",
+ "INSERT OR IGNORE INTO chat_timeline_events (scope, subject_id, ts, label, detail) VALUES (?, ?, ?, ?, ?)",
+ "INSERT OR IGNORE INTO clip_ai_state (clip_id) VALUES (?)",
+ "INSERT OR IGNORE INTO comment_thread_refs (content_type, content_id, thread_id, access_id) VALUES (?, ?, ?, ?)",
+ "INSERT OR IGNORE INTO content_views (content_type, content_id, ip) VALUES (?, ?, ?)",
+ "INSERT OR IGNORE INTO control_whitelist (channel_id, user_id, added_by) VALUES (?, ?, ?)",
+ "INSERT OR IGNORE INTO dm_blocks (blocker_id, blocked_id) VALUES (?, ?)",
+ "INSERT OR IGNORE INTO dm_participants (conversation_id, user_id) VALUES (?, ?)",
+ "INSERT OR IGNORE INTO easter_egg_solves (egg_date, solver_key, user_id) VALUES (?, ?, ?)",
+ "INSERT OR IGNORE INTO follows (follower_id, streamer_id) VALUES (?, ?)",
+ "INSERT OR IGNORE INTO linked_accounts (service, service_user_id, service_username, user_id) VALUES ('network', ?, ?, ?)",
+ "INSERT OR IGNORE INTO linked_accounts (user_id, service, service_user_id, service_username) VALUES (?, 'network', ?, ?)",
+ "INSERT OR IGNORE INTO powerchat_webhook_deliveries (delivery_id, event_type) VALUES (?, ?)",
+ "INSERT OR IGNORE INTO relay_users (platform, username, display_name, first_seen, last_seen, message_count) SELECT source_platform, LOWER(TRIM(SUBSTR(username, INSTR(username, '] ') + 2))), TRIM(SUBSTR(username, INSTR(username, '] ') + 2)), MIN(timestamp), MAX(timestamp), COUNT(*) FROM chat_messages WHERE user_id IS NULL AND source_platform IS NOT NULL AND username LIKE '[%] %' AND INSTR(username, '] ') > 0 GROUP BY source_platform, LOWER(TRIM(SUBSTR(username, INSTR(username, '] ') + 2)))",
+ "INSERT OR IGNORE INTO site_settings (key, value, description, type) VALUES (?, ?, ?, ?)",
+ "INSERT OR IGNORE INTO stream_first_chats (chatter_key, channel_user_id) VALUES (?, ?)",
+ "INSERT OR IGNORE INTO stream_memories (stream_id, user_id, offset_seconds, description, tags, thumbnail_url, transcript_json) VALUES (?, ?, ?, ?, ?, ?, ?)",
+ "INSERT OR IGNORE INTO tips_deliveries (idempotency_key, effect, state, claimed_at) VALUES (?, ?, 'pending', ?)",
+ "INSERT OR IGNORE INTO user_cosmetics (user_id, item_id, category) VALUES (?, ?, ?)",
+ "INSERT OR IGNORE INTO users (username, password_hash, display_name, role) VALUES (?, ?, ?, 'user')",
+ "INSERT OR IGNORE INTO vod_ai_state (vod_id) VALUES (?)",
+ "INSERT OR REPLACE INTO hidden_relay_users (channel_id, platform, external_username, action, reason, created_by) VALUES (?, ?, ?, ?, ?, ?)",
+ "INSERT OR REPLACE INTO site_settings (key, value, description, type) VALUES ('bucks_bits_migration_done', '1', 'Internal: decimal→bit Vibes migration applied (see schema_migrations)', 'boolean')",
+ "INSERT OR REPLACE INTO site_settings (key, value, type) VALUES ('relay_users_backfilled','1','string')",
+ "INSERT OR REPLACE INTO translations (key, src, dst, text) VALUES (?, ?, ?, ?)",
+ "INSERT OR REPLACE INTO user_equipped (user_id, slot, item_id) VALUES (?, ?, ?)",
+ "SELECT * FROM ai_chatbot_configs",
+ "SELECT * FROM ai_chatbot_configs WHERE user_id = ?",
+ "SELECT * FROM ai_viewer_log WHERE channel_user_id = ?",
+ "SELECT * FROM ai_viewer_log WHERE channel_user_id = ? ORDER BY id DESC LIMIT ?",
+ "SELECT * FROM ai_viewer_threads WHERE channel_user_id = ? AND state = 'closed' AND topic IS NOT NULL ORDER BY updated_at DESC LIMIT ?",
+ "SELECT * FROM ai_viewer_threads WHERE channel_user_id = ? AND state = 'open' ORDER BY updated_at DESC LIMIT ?",
+ "SELECT * FROM ai_viewer_threads WHERE id = ?",
+ "SELECT * FROM api_keys WHERE key_hash = ? AND is_active = 1",
+ "SELECT * FROM arena_beefs WHERE a_user_id = ? OR b_user_id = ? ORDER BY CASE WHEN status = 'open' THEN 0 ELSE 1 END, COALESCE(resolved_at, opened_at) DESC LIMIT ?",
+ "SELECT * FROM arena_beefs WHERE id = ?",
+ "SELECT * FROM arena_beefs WHERE id = ? AND status = ?",
+ "SELECT * FROM arena_beefs WHERE status = 'open'",
+ "SELECT * FROM arena_beefs WHERE status = 'open' AND ((a_user_id = ? AND b_user_id = ?) OR (a_user_id = ? AND b_user_id = ?))",
+ "SELECT * FROM arena_beefs WHERE status = 'open' AND (a_user_id = ? OR b_user_id = ?) ORDER BY opened_at DESC",
+ "SELECT * FROM arena_beefs WHERE status = 'open' ORDER BY COALESCE(last_b_at, last_a_at, opened_at) DESC",
+ "SELECT * FROM arena_beefs WHERE status = 'resolved' AND ((a_user_id = ? AND b_user_id = ?) OR (a_user_id = ? AND b_user_id = ?)) ORDER BY resolved_at DESC LIMIT 10",
+ "SELECT * FROM arena_beefs WHERE status = 'resolved' ORDER BY resolved_at DESC LIMIT ?",
+ "SELECT * FROM arena_mic_moments WHERE id = ?",
+ "SELECT * FROM arena_mic_moments WHERE quality >= 5 AND user_id = ? ORDER BY COALESCE(said_at, created_at) DESC, id DESC LIMIT ?",
+ "SELECT * FROM arena_mic_moments WHERE quality >= 5 ORDER BY COALESCE(said_at, created_at) DESC, id DESC LIMIT ?",
+ "SELECT * FROM arena_mic_moments WHERE user_id = ? AND quality >= 5 ORDER BY quality DESC, id DESC LIMIT ?",
+ "SELECT * FROM arena_mic_moments WHERE user_id = ? ORDER BY COALESCE(said_at, created_at) DESC, id DESC LIMIT 1",
+ "SELECT * FROM arena_profiles WHERE user_id = ?",
+ "SELECT * FROM arena_trash_levels WHERE user_id = ?",
+ "SELECT * FROM arena_trash_levels WHERE xp > 0 ORDER BY xp DESC LIMIT ?",
+ "SELECT * FROM bans WHERE id = ?",
+ "SELECT * FROM bans WHERE ip_address IN (?, ?) AND (stream_id = ? OR stream_id IS NULL) AND (expires_at IS NULL OR datetime(expires_at) > CURRENT_TIMESTAMP) LIMIT 1",
+ "SELECT * FROM bans WHERE ip_address LIKE '%/%' AND (expires_at IS NULL OR datetime(expires_at) > CURRENT_TIMESTAMP)",
+ "SELECT * FROM bans WHERE user_id = ? AND (stream_id = ? OR stream_id IS NULL) AND (expires_at IS NULL OR datetime(expires_at) > CURRENT_TIMESTAMP) LIMIT 1",
+ "SELECT * FROM billing_actions WHERE action = 'media_charge' AND live_ref = ? AND status = 'done' ORDER BY id DESC LIMIT 1",
+ "SELECT * FROM billing_actions WHERE action IN ('checkout', 'subscribe_intent') AND status = 'done' AND json_extract(response_json, '$.intent.provider') = 'paypal' AND json_extract(response_json, '$.intent.provider_ref') = ? ORDER BY id DESC LIMIT 1",
+ "SELECT * FROM billing_actions WHERE id = ?",
+ "SELECT * FROM billing_actions WHERE idempotency_key = ?",
+ "SELECT * FROM camera_presets WHERE camera_id = ? ORDER BY name",
+ "SELECT * FROM camera_presets WHERE id = ?",
+ "SELECT * FROM camera_profiles WHERE id = ?",
+ "SELECT * FROM camera_profiles WHERE stream_id = ? AND is_active = 1 ORDER BY name",
+ "SELECT * FROM camera_profiles WHERE user_id = ? ORDER BY created_at DESC",
+ "SELECT * FROM cameras WHERE stream_id = ?",
+ "SELECT * FROM channel_ai_bots WHERE channel_user_id = ?",
+ "SELECT * FROM channel_ai_bots WHERE channel_user_id = ? AND username = ?",
+ "SELECT * FROM channel_ai_bots WHERE channel_user_id = ? ORDER BY last_active_at DESC, created_at ASC",
+ "SELECT * FROM channel_ai_bots WHERE id = ?",
+ "SELECT * FROM channel_ai_config WHERE user_id = ?",
+ "SELECT * FROM channel_moderation_settings WHERE channel_id = ?",
+ "SELECT * FROM channel_sounds WHERE channel_owner_id = ? AND command = ? AND is_approved = 1 ORDER BY RANDOM() LIMIT 1",
+ "SELECT * FROM channel_sounds WHERE channel_owner_id = ? AND is_approved = 1 ORDER BY command",
+ "SELECT * FROM channel_sounds WHERE id = ?",
+ "SELECT * FROM channels WHERE id = ?",
+ "SELECT * FROM channels WHERE user_id = ?",
+ "SELECT * FROM channels WHERE user_id IN (?)",
+ "SELECT * FROM chat_ai_summaries WHERE scope = ? AND subject_id = ? AND window = ?",
+ "SELECT * FROM chat_ai_summaries WHERE scope = ? AND subject_id = ? ORDER BY window",
+ "SELECT * FROM chat_messages WHERE id = ?",
+ "SELECT * FROM clip_ai_state WHERE clip_id = ?",
+ "SELECT * FROM coin_rewards WHERE id = ?",
+ "SELECT * FROM coin_rewards WHERE is_global = 1 AND is_enabled = 1 ORDER BY sort_order, cost",
+ "SELECT * FROM coin_rewards WHERE streamer_id = ? AND is_enabled = 1 ORDER BY sort_order, cost",
+ "SELECT * FROM coin_transactions WHERE user_id = ? ORDER BY created_at DESC LIMIT ?",
+ "SELECT * FROM control_config_buttons WHERE config_id = ? ORDER BY sort_order",
+ "SELECT * FROM control_configs WHERE id = ?",
+ "SELECT * FROM control_configs WHERE user_id = ?",
+ "SELECT * FROM control_configs WHERE user_id = ? ORDER BY created_at DESC",
+ "SELECT * FROM dm_conversations WHERE id = ?",
+ "SELECT * FROM dm_messages WHERE id = ?",
+ "SELECT * FROM donation_goals WHERE id = ?",
+ "SELECT * FROM donation_goals WHERE user_id = ? AND (is_active = 1 OR (reached_at IS NOT NULL AND reached_at > datetime('now', ?))) ORDER BY sort_order ASC, created_at ASC",
+ "SELECT * FROM donation_goals WHERE user_id = ? AND is_active = 1 ORDER BY created_at",
+ "SELECT * FROM donation_goals WHERE user_id = ? AND is_active = 1 ORDER BY sort_order ASC, created_at ASC",
+ "SELECT * FROM donation_goals WHERE user_id = ? ORDER BY is_active DESC, sort_order ASC, created_at ASC",
+ "SELECT * FROM emotes WHERE code = ? AND ((channel_owner_id = ?) OR (channel_owner_id IS NULL AND user_id = ?)) LIMIT 1",
+ "SELECT * FROM emotes WHERE code = ? AND (user_id = ? OR is_global = 1) AND is_approved = 1 ORDER BY is_global ASC LIMIT 1",
+ "SELECT * FROM emotes WHERE user_id = ? ORDER BY code",
+ "SELECT * FROM kick_channel_cache WHERE slug = ?",
+ "SELECT * FROM linked_accounts WHERE service = 'network' AND service_user_id = ?",
+ "SELECT * FROM media_request_settings WHERE user_id = ?",
+ "SELECT * FROM media_requests WHERE charge_state IN ('unknown', 'charging') AND currency = 'opencoins' AND requested_at <= ? ORDER BY id LIMIT ?",
+ "SELECT * FROM media_requests WHERE id = ?",
+ "SELECT * FROM media_requests WHERE streamer_id = ? AND canonical_url = ? AND status IN ('pending', 'playing') ORDER BY id DESC LIMIT 1",
+ "SELECT * FROM media_requests WHERE streamer_id = ? AND id = ?",
+ "SELECT * FROM media_requests WHERE streamer_id = ? AND status = 'pending' ORDER BY queue_position ASC, requested_at ASC, id ASC LIMIT 1",
+ "SELECT * FROM media_requests WHERE streamer_id = ? AND status = 'pending' ORDER BY queue_position ASC, requested_at ASC, id ASC LIMIT ?",
+ "SELECT * FROM media_requests WHERE streamer_id = ? AND status = 'playing' ORDER BY started_at DESC, id DESC LIMIT 1",
+ "SELECT * FROM media_requests WHERE streamer_id = ? AND status IN ('played', 'skipped', 'removed', 'failed') AND charge_state IS NOT 'charging' ORDER BY COALESCE(ended_at, requested_at) DESC, id DESC LIMIT ?",
+ "SELECT * FROM news_settings WHERE scope = 'global' AND source_id != '_master'",
+ "SELECT * FROM opencoin_admin_grants WHERE client_key = ?",
+ "SELECT * FROM openre_sessions WHERE session_id = ?",
+ "SELECT * FROM openre_sessions WHERE state = 'live'",
+ "SELECT * FROM openre_sessions WHERE stream_id = ? ORDER BY updated_at DESC LIMIT 1",
+ "SELECT * FROM payment_orders WHERE id = ?",
+ "SELECT * FROM payment_orders WHERE provider = 'powerchat' AND status = 'pending' AND created_at >= datetime('now', ?) ORDER BY id ASC",
+ "SELECT * FROM payment_orders WHERE provider = ? AND provider_ref = ? ORDER BY id DESC LIMIT 1",
+ "SELECT * FROM pending_ip_messages WHERE channel_id = ? AND ip_address = ? AND status = 'pending'",
+ "SELECT * FROM pending_ip_messages WHERE channel_id = ? AND status = 'pending' ORDER BY created_at ASC LIMIT ?",
+ "SELECT * FROM pending_ip_messages WHERE id = ?",
+ "SELECT * FROM platform_connections WHERE id = ?",
+ "SELECT * FROM platform_connections WHERE user_id = ? AND platform = ?",
+ "SELECT * FROM platform_connections WHERE user_id = ? ORDER BY platform",
+ "SELECT * FROM powerchat_connections WHERE LOWER(powerchat_username) = LOWER(?)",
+ "SELECT * FROM powerchat_connections WHERE powerchat_user_id = ?",
+ "SELECT * FROM powerchat_connections WHERE user_id = ?",
+ "SELECT * FROM restream_destinations WHERE id = ?",
+ "SELECT * FROM restream_destinations WHERE managed_stream_id = ? ORDER BY created_at",
+ "SELECT * FROM restream_destinations WHERE user_id = ? AND managed_stream_id = ? ORDER BY created_at",
+ "SELECT * FROM restream_destinations WHERE user_id = ? AND managed_stream_id IS NULL ORDER BY created_at",
+ "SELECT * FROM restream_destinations WHERE user_id = ? ORDER BY created_at",
+ "SELECT * FROM robotstreamer_integrations WHERE user_id = ? AND managed_stream_id = ?",
+ "SELECT * FROM robotstreamer_integrations WHERE user_id = ? AND managed_stream_id IS NULL",
+ "SELECT * FROM site_settings ORDER BY key",
+ "SELECT * FROM site_settings WHERE key = ?",
+ "SELECT * FROM stream_analytics WHERE stream_id = ?",
+ "SELECT * FROM stream_controls WHERE id = ? AND stream_id = ?",
+ "SELECT * FROM stream_controls WHERE id = ? AND stream_id = ? AND is_enabled = 1",
+ "SELECT * FROM stream_controls WHERE stream_id = ? AND command = ? AND is_enabled = 1 LIMIT 1",
+ "SELECT * FROM stream_controls WHERE stream_id = ? ORDER BY sort_order",
+ "SELECT * FROM stream_memories WHERE stream_id = ? AND offset_seconds BETWEEN ? AND ? ORDER BY offset_seconds ASC",
+ "SELECT * FROM stream_memories WHERE stream_id = ? ORDER BY offset_seconds ASC",
+ "SELECT * FROM stream_memories WHERE stream_id = ? ORDER BY offset_seconds DESC LIMIT 1",
+ "SELECT * FROM stream_memories WHERE user_id = ? ORDER BY created_at DESC LIMIT ?",
+ "SELECT * FROM streamer_overviews WHERE user_id = ?",
+ "SELECT * FROM streams WHERE id = ?",
+ "SELECT * FROM streams WHERE user_id = ? AND is_live = 1 ORDER BY started_at DESC LIMIT 1",
+ "SELECT * FROM subject_projection WHERE subject_id = ?",
+ "SELECT * FROM subscriptions WHERE id = ?",
+ "SELECT * FROM subscriptions WHERE provider = ? AND provider_ref = ? ORDER BY id DESC LIMIT 1",
+ "SELECT * FROM subscriptions WHERE status = 'active' AND current_period_end IS NOT NULL AND datetime(current_period_end) <= CURRENT_TIMESTAMP ORDER BY datetime(current_period_end) ASC LIMIT ?",
+ "SELECT * FROM subscriptions WHERE subscriber_id = ? AND streamer_id = ?",
+ "SELECT * FROM subscriptions WHERE subscriber_id = ? AND streamer_id = ? AND status = 'active' AND (current_period_end IS NULL OR datetime(current_period_end) > CURRENT_TIMESTAMP) LIMIT 1",
+ "SELECT * FROM themes WHERE id = ?",
+ "SELECT * FROM tips_deliveries WHERE idempotency_key = ?",
+ "SELECT * FROM transactions WHERE from_user_id = ? OR to_user_id = ? ORDER BY created_at DESC LIMIT ?",
+ "SELECT * FROM transactions WHERE id = ? AND status = ?",
+ "SELECT * FROM user_themes WHERE user_id = ? ORDER BY id DESC LIMIT 1",
+ "SELECT * FROM users WHERE display_name = ? COLLATE NOCASE",
+ "SELECT * FROM users WHERE id = ?",
+ "SELECT * FROM users WHERE stream_key = ?",
+ "SELECT * FROM users WHERE username = ? COLLATE NOCASE",
+ "SELECT * FROM verification_keys WHERE key = ?",
+ "SELECT * FROM verification_keys WHERE target_username = ? COLLATE NOCASE AND status = 'active'",
+ "SELECT * FROM vod_ai_state WHERE vod_id = ?",
+ "SELECT * FROM watch_time WHERE user_id = ? AND stream_id = ?",
+ "SELECT 1 AS ok",
+ "SELECT 1 AS x FROM stream_timeline_events WHERE vod_id IS NULL LIMIT 1",
+ "SELECT 1 AS x FROM users WHERE username = ? COLLATE NOCASE",
+ "SELECT 1 FROM ai_chatbot_configs WHERE user_id = ?",
+ "SELECT 1 FROM approved_ips WHERE channel_id = ? AND ip_address = ?",
+ "SELECT 1 FROM channel_ai_config WHERE user_id = ?",
+ "SELECT 1 FROM channel_moderation_settings WHERE channel_id = ?",
+ "SELECT 1 FROM channel_moderation_settings WHERE channel_id = ? LIMIT 1",
+ "SELECT 1 FROM channel_moderators WHERE user_id = ? AND channel_id = ?",
+ "SELECT 1 FROM chat_messages cm JOIN streams s ON cm.stream_id = s.id WHERE s.channel_id = ? AND cm.is_deleted = 0 AND (cm.user_id = ? OR cm.anon_id = ?) LIMIT 1",
+ "SELECT 1 FROM control_whitelist WHERE channel_id = ? AND user_id = ?",
+ "SELECT 1 FROM dm_blocks WHERE (blocker_id = ? AND blocked_id = ?) OR (blocker_id = ? AND blocked_id = ?)",
+ "SELECT 1 FROM dm_blocks WHERE blocker_id = ? AND blocked_id = ?",
+ "SELECT 1 FROM dm_messages WHERE conversation_id = ? AND sender_id = ? AND message = ? AND created_at > datetime('now', '-' || ? || ' seconds') LIMIT 1",
+ "SELECT 1 FROM dm_participants WHERE conversation_id = ? AND user_id = ?",
+ "SELECT 1 FROM easter_egg_solves WHERE egg_date = ? AND solver_key = ?",
+ "SELECT 1 FROM hidden_relay_users WHERE platform = ? AND external_username = ? AND (channel_id = ? OR channel_id IS NULL)",
+ "SELECT 1 FROM managed_streams WHERE user_id = ? AND ingest_authority = 'openre' LIMIT 1",
+ "SELECT 1 FROM media_request_settings WHERE user_id = ? LIMIT 1",
+ "SELECT 1 FROM module_summary_pushes WHERE user_id = ? AND namespace = 'live.loyalty'",
+ "SELECT 1 FROM powerchat_connections WHERE user_id = ? LIMIT 1",
+ "SELECT 1 FROM site_settings WHERE key = 'bucks_bits_migration_done'",
+ "SELECT 1 FROM sqlite_master WHERE type = 'index' AND name = ?",
+ "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'chat_bridge_outbox'",
+ "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?",
+ "SELECT 1 FROM sqlite_master WHERE type='index' AND name=?",
+ "SELECT 1 FROM stream_first_chats WHERE chatter_key = ? AND channel_user_id = ?",
+ "SELECT 1 FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' AND created_at >= datetime('now', '-30 minutes') LIMIT 1",
+ "SELECT 1 FROM streams WHERE user_id = ? AND is_live = 1 LIMIT 1",
+ "SELECT 1 FROM streams WHERE user_id = ? LIMIT 1",
+ "SELECT 1 FROM user_cosmetics WHERE user_id = ? AND item_id = ?",
+ "SELECT AVG(CASE WHEN sampled_at >= datetime('now','-1 day') AND viewers > 0 THEN viewers END) AS vAvg24, MAX(CASE WHEN sampled_at >= datetime('now','-1 day') THEN viewers END) AS vPeak24, AVG(CASE WHEN viewers > 0 THEN viewers END) AS vAvg7, AVG(CASE WHEN sampled_at >= datetime('now','-1 day') AND live_streams > 0 THEN live_streams END) AS lAvg24, MAX(CASE WHEN sampled_at >= datetime('now','-1 day') THEN live_streams END) AS lPeak24, AVG(CASE WHEN live_streams > 0 THEN live_streams END) AS lAvg7, COUNT(*) AS samples FROM viewer_samples WHERE sampled_at >= datetime('now','-7 days')",
+ "SELECT AVG(viewer_count) as avg_vc FROM viewer_snapshots WHERE stream_id = ?",
+ "SELECT CASE WHEN a_user_id = ? THEN b_user_id ELSE a_user_id END AS opp, COUNT(*) AS n FROM arena_beefs WHERE (a_user_id = ? OR b_user_id = ?) GROUP BY opp HAVING n >= 2 ORDER BY n DESC LIMIT ?",
+ "SELECT CAST((julianday(cm.timestamp) - julianday(s.started_at)) * 86400 / ? AS INT) AS bucket, COUNT(*) AS n FROM chat_messages cm JOIN streams s ON s.id = cm.stream_id WHERE cm.stream_id = ? AND s.started_at IS NOT NULL AND cm.timestamp >= s.started_at AND COALESCE(cm.is_deleted, 0) = 0 GROUP BY bucket HAVING bucket >= 0 ORDER BY n DESC LIMIT ?",
+ "SELECT CAST(strftime('%s', cm.timestamp) / ? AS INT) AS b, COUNT(*) AS n, MIN(CAST(strftime('%s', cm.timestamp) AS INT)) AS ts FROM chat_messages cm WHERE cm.stream_id = ? AND COALESCE(cm.is_deleted, 0) = 0 AND cm.timestamp >= datetime('now', ?) GROUP BY b ORDER BY ts ASC",
+ "SELECT COALESCE(-SUM(amount), 0) AS count FROM coin_transactions WHERE amount < 0",
+ "SELECT COALESCE(MAX(id),0) AS m FROM chat_messages WHERE channel_user_id = ?",
+ "SELECT COALESCE(MAX(sort_order),-1)+1 AS n FROM donation_goals WHERE user_id = ?",
+ "SELECT COALESCE(NULLIF(ai_category, ''), category) AS category, COUNT(*) AS n FROM streams WHERE user_id = ? AND duration_seconds > 0 AND COALESCE(NULLIF(ai_category, ''), category) IS NOT NULL AND COALESCE(NULLIF(ai_category, ''), category) != '' GROUP BY 1 ORDER BY n DESC LIMIT 1",
+ "SELECT COALESCE(SUM(-amount), 0) AS count FROM coin_transactions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND amount < 0",
+ "SELECT COALESCE(SUM(-amount), 0) AS count FROM coin_transactions WHERE created_at >= datetime('now', ?) AND amount < 0",
+ "SELECT COALESCE(SUM(COALESCE(end_sec, start_sec + 3) - start_sec), 0) AS s FROM stream_timeline_events WHERE user_id = ? AND kind = 'speech' AND created_at >= datetime('now', ?)",
+ "SELECT COALESCE(SUM(amount), 0) AS count FROM coin_transactions WHERE amount > 0",
+ "SELECT COALESCE(SUM(amount), 0) AS count FROM coin_transactions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND amount > 0",
+ "SELECT COALESCE(SUM(amount), 0) AS count FROM coin_transactions WHERE created_at >= datetime('now', ?) AND amount > 0",
+ "SELECT COALESCE(SUM(amount), 0) AS count FROM transactions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND type = 'donation' AND created_at >= '1970-01-01 00:00:00'",
+ "SELECT COALESCE(SUM(amount), 0) AS count FROM transactions WHERE created_at >= datetime('now', ?) AND type = 'donation' AND created_at >= '1970-01-01 00:00:00'",
+ "SELECT COALESCE(SUM(amount), 0) AS count FROM transactions WHERE type = 'donation' AND created_at >= ?",
+ "SELECT COALESCE(SUM(amount), 0) AS xp FROM arena_xp_log WHERE user_id = ? AND created_at >= datetime('now', ?)",
+ "SELECT COALESCE(SUM(amount), 0) as c FROM transactions WHERE type = 'donation'",
+ "SELECT COALESCE(SUM(b_user_id = ?), 0) AS targeted, COALESCE(SUM(b_user_id = ? AND responded = 1), 0) AS answered, COALESCE(SUM(winner_user_id = ?), 0) AS wins, COALESCE(SUM(status = 'resolved' AND winner_user_id IS NOT NULL AND winner_user_id != ?), 0) AS losses, COALESCE(SUM(status = 'resolved' AND resolution = 'forfeit' AND winner_user_id != ? AND winner_user_id IS NOT NULL), 0) AS ducked FROM arena_beefs WHERE a_user_id = ? OR b_user_id = ?",
+ "SELECT COALESCE(SUM(balance), 0) AS n FROM channel_points WHERE user_id = ? AND balance > 0",
+ "SELECT COALESCE(SUM(bucks), 0) AS count FROM payment_orders WHERE kind = 'bucks' AND status = 'credited'",
+ "SELECT COALESCE(SUM(bucks), 0) AS count FROM payment_orders WHERE updated_at >= datetime('now', '-14 days') AND updated_at < datetime('now', '-7 days') AND kind = 'bucks' AND status = 'credited'",
+ "SELECT COALESCE(SUM(bucks), 0) AS count FROM payment_orders WHERE updated_at >= datetime('now', ?) AND kind = 'bucks' AND status = 'credited'",
+ "SELECT COALESCE(SUM(cost_usd),0) AS c FROM ai_usage WHERE created_at >= date('now')",
+ "SELECT COALESCE(SUM(cost_usd),0) AS c FROM ai_usage WHERE owner_user_id = ? AND created_at >= date('now')",
+ "SELECT COALESCE(SUM(cost_usd),0) AS c FROM ai_usage WHERE owner_user_id = ? AND created_at >= date('now') AND source = ?",
+ "SELECT COALESCE(SUM(cost_usd),0) AS c FROM ai_usage WHERE source = ? AND COALESCE(provider,'shared') = 'shared' AND created_at >= date('now')",
+ "SELECT COALESCE(SUM(duration_seconds), 0) AS sec FROM streams WHERE user_id = ? AND duration_seconds > 0 AND id IN (SELECT DISTINCT stream_id FROM stream_timeline_events WHERE user_id = ? AND created_at >= datetime('now', ?))",
+ "SELECT COALESCE(SUM(minutes_watched), 0) AS count FROM watch_time",
+ "SELECT COALESCE(SUM(openvibe_bucks_balance), 0) as c FROM users",
+ "SELECT COALESCE(SUM(unread), 0) as total FROM ( SELECT COUNT(*) as unread FROM dm_messages m JOIN dm_participants p ON p.conversation_id = m.conversation_id AND p.user_id = ? WHERE m.created_at > p.last_read_at AND m.sender_id != ? )",
+ "SELECT COALESCE(SUM(viewer_count), 0) AS count FROM streams WHERE is_live = 1",
+ "SELECT COALESCE(SUM(viewer_count), 0) as c FROM streams WHERE is_live = 1",
+ "SELECT COALESCE(SUM(viewer_count),0) AS v, COUNT(*) AS n FROM streams WHERE is_live = 1",
+ "SELECT COALESCE(role,'legacy') AS role, COUNT(*) AS calls, SUM(input_tokens) AS input_tokens, SUM(cached_tokens) AS cached_tokens, SUM(output_tokens) AS output_tokens, SUM(cost_usd) AS cost_usd, AVG(latency_ms) AS avg_latency_ms FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY role ORDER BY cost_usd DESC",
+ "SELECT COALESCE(source,'platform') AS source, COALESCE(provider,'shared') AS provider, COUNT(*) AS calls, SUM(cost_usd) AS cost_usd, SUM(input_tokens) AS input_tokens, SUM(cached_tokens) AS cached_tokens FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY source, provider ORDER BY cost_usd DESC",
+ "SELECT COALESCE(text_en, text) AS t, start_sec AS s FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' ORDER BY start_sec ASC",
+ "SELECT COUNT(*) - COUNT(DISTINCT stream_id || ':' || offset_seconds) AS extra FROM stream_memories",
+ "SELECT COUNT(*) AS c FROM channel_ai_config",
+ "SELECT COUNT(*) AS c FROM chat_messages WHERE anon_id = ? AND user_id IS NULL AND is_deleted = 0 AND (auto_delete_at IS NULL OR datetime(auto_delete_at) > CURRENT_TIMESTAMP)",
+ "SELECT COUNT(*) AS c FROM media_requests WHERE streamer_id = ? AND user_id = ? AND status IN ('pending', 'playing')",
+ "SELECT COUNT(*) AS c FROM stream_memories WHERE stream_id = ?",
+ "SELECT COUNT(*) AS c, COALESCE(MAX(id), 0) AS m, COALESCE(SUM(id * 7 + COALESCE(stream_id, 0) + LENGTH(COALESCE(expires_at, ''))), 0) AS s FROM bans",
+ "SELECT COUNT(*) AS calls, COALESCE(SUM(input_tokens),0) AS input_tokens, COALESCE(SUM(output_tokens),0) AS output_tokens, COALESCE(SUM(cached_tokens),0) AS cached_tokens, COALESCE(SUM(cost_usd),0) AS cost_usd FROM ai_usage WHERE created_at >= date('now', ?)",
+ "SELECT COUNT(*) AS count FROM ( SELECT DISTINCT 'u' || user_id AS id FROM chat_messages WHERE user_id IS NOT NULL AND COALESCE(is_deleted, 0) = 0 AND timestamp >= datetime('now', '-14 days') AND timestamp < datetime('now', '-7 days') UNION SELECT DISTINCT 'a' || anon_id FROM chat_messages WHERE anon_id IS NOT NULL AND COALESCE(is_deleted, 0) = 0 AND timestamp >= datetime('now', '-14 days') AND timestamp < datetime('now', '-7 days') UNION SELECT DISTINCT 'r' || source_platform || '|' || username FROM chat_messages WHERE source_platform IS NOT NULL AND source_platform <> '' AND COALESCE(is_deleted, 0) = 0 AND timestamp >= datetime('now', '-14 days') AND timestamp < datetime('now', '-7 days') )",
+ "SELECT COUNT(*) AS count FROM ( SELECT DISTINCT 'u' || user_id AS id FROM chat_messages WHERE user_id IS NOT NULL AND COALESCE(is_deleted, 0) = 0 AND timestamp >= datetime('now', '-7 days') UNION SELECT DISTINCT 'a' || anon_id FROM chat_messages WHERE anon_id IS NOT NULL AND COALESCE(is_deleted, 0) = 0 AND timestamp >= datetime('now', '-7 days') UNION SELECT DISTINCT 'r' || source_platform || '|' || username FROM chat_messages WHERE source_platform IS NOT NULL AND source_platform <> '' AND COALESCE(is_deleted, 0) = 0 AND timestamp >= datetime('now', '-7 days') )",
+ "SELECT COUNT(*) AS count FROM anon_ip_mappings",
+ "SELECT COUNT(*) AS count FROM anon_ip_mappings WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",
+ "SELECT COUNT(*) AS count FROM anon_ip_mappings WHERE created_at >= datetime('now', '-7 days')",
+ "SELECT COUNT(*) AS count FROM anon_ip_mappings WHERE created_at >= datetime('now', ?)",
+ "SELECT COUNT(*) AS count FROM chat_messages",
+ "SELECT COUNT(*) AS count FROM chat_messages WHERE timestamp >= datetime('now', '-14 days') AND timestamp < datetime('now', '-7 days') AND COALESCE(is_deleted, 0) = 0",
+ "SELECT COUNT(*) AS count FROM chat_messages WHERE timestamp >= datetime('now', ?) AND COALESCE(is_deleted, 0) = 0",
+ "SELECT COUNT(*) AS count FROM coin_redemptions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND status NOT IN ('rejected', 'refunded')",
+ "SELECT COUNT(*) AS count FROM coin_redemptions WHERE created_at >= datetime('now', ?) AND status NOT IN ('rejected', 'refunded')",
+ "SELECT COUNT(*) AS count FROM coin_redemptions WHERE status NOT IN ('rejected', 'refunded')",
+ "SELECT COUNT(*) AS count FROM donation_goals WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",
+ "SELECT COUNT(*) AS count FROM donation_goals WHERE created_at >= datetime('now', ?)",
+ "SELECT COUNT(*) AS count FROM donation_goals WHERE is_active = 1",
+ "SELECT COUNT(*) AS count FROM donation_goals WHERE reached_at IS NOT NULL OR current_amount >= target_amount",
+ "SELECT COUNT(*) AS count FROM emotes",
+ "SELECT COUNT(*) AS count FROM emotes WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",
+ "SELECT COUNT(*) AS count FROM emotes WHERE created_at >= datetime('now', ?)",
+ "SELECT COUNT(*) AS count FROM follows",
+ "SELECT COUNT(*) AS count FROM follows WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",
+ "SELECT COUNT(*) AS count FROM follows WHERE created_at >= datetime('now', ?)",
+ "SELECT COUNT(*) AS count FROM managed_streams WHERE user_id = ?",
+ "SELECT COUNT(*) AS count FROM stream_memories",
+ "SELECT COUNT(*) AS count FROM stream_memories WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",
+ "SELECT COUNT(*) AS count FROM stream_memories WHERE created_at >= datetime('now', ?)",
+ "SELECT COUNT(*) AS count FROM stream_memories WHERE user_id = ?",
+ "SELECT COUNT(*) AS count FROM streams",
+ "SELECT COUNT(*) AS count FROM streams WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",
+ "SELECT COUNT(*) AS count FROM streams WHERE created_at >= datetime('now', ?)",
+ "SELECT COUNT(*) AS count FROM streams WHERE is_live = 1",
+ "SELECT COUNT(*) AS count FROM subscriptions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",
+ "SELECT COUNT(*) AS count FROM subscriptions WHERE created_at >= datetime('now', ?)",
+ "SELECT COUNT(*) AS count FROM subscriptions WHERE status = 'active' AND (current_period_end IS NULL OR datetime(current_period_end) > CURRENT_TIMESTAMP)",
+ "SELECT COUNT(*) AS count FROM users WHERE COALESCE(is_banned, 0) = 0",
+ "SELECT COUNT(*) AS count FROM users WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND COALESCE(is_banned, 0) = 0",
+ "SELECT COUNT(*) AS count FROM users WHERE created_at >= datetime('now', ?) AND COALESCE(is_banned, 0) = 0",
+ "SELECT COUNT(*) AS n FROM ai_usage WHERE kind = 'tts' AND created_at >= date('now')",
+ "SELECT COUNT(*) AS n FROM arena_beef_hype WHERE beef_id = ? AND side = ?",
+ "SELECT COUNT(*) AS n FROM arena_beefs WHERE winner_user_id = ? AND resolved_at >= datetime('now', ?)",
+ "SELECT COUNT(*) AS n FROM arena_mic_moments WHERE created_at >= datetime('now', '-1 day')",
+ "SELECT COUNT(*) AS n FROM arena_mic_moments WHERE said_at >= ?",
+ "SELECT COUNT(*) AS n FROM arena_mic_moments WHERE stream_id = ?",
+ "SELECT COUNT(*) AS n FROM channel_ai_bots WHERE channel_user_id = ?",
+ "SELECT COUNT(*) AS n FROM channel_moderators WHERE channel_id = ?",
+ "SELECT COUNT(*) AS n FROM channel_sounds WHERE channel_owner_id = ?",
+ "SELECT COUNT(*) AS n FROM chat_messages WHERE stream_id = ? AND message_type = 'soundboard'",
+ "SELECT COUNT(*) AS n FROM chat_messages WHERE timestamp >= ? AND COALESCE(is_deleted, 0) = 0",
+ "SELECT COUNT(*) AS n FROM chat_staged_outbox",
+ "SELECT COUNT(*) AS n FROM chat_staged_outbox WHERE tbl = ?",
+ "SELECT COUNT(*) AS n FROM control_configs WHERE user_id = ?",
+ "SELECT COUNT(*) AS n FROM donation_goals WHERE user_id = ? AND is_active = 1",
+ "SELECT COUNT(*) AS n FROM easter_egg_solves WHERE egg_date = ?",
+ "SELECT COUNT(*) AS n FROM emotes WHERE user_id = ?",
+ "SELECT COUNT(*) AS n FROM follows WHERE created_at >= ?",
+ "SELECT COUNT(*) AS n FROM follows WHERE streamer_id = ?",
+ "SELECT COUNT(*) AS n FROM follows WHERE streamer_id = ? AND created_at >= ?",
+ "SELECT COUNT(*) AS n FROM follows WHERE streamer_id = ? AND created_at BETWEEN ? AND ?",
+ "SELECT COUNT(*) AS n FROM stream_timeline_events WHERE user_id = ? AND kind = 'sound' AND (LOWER(label) LIKE '%laugh%' OR LOWER(label) LIKE '%giggle%' OR LOWER(label) LIKE '%chuckle%') AND created_at >= datetime('now', ?)",
+ "SELECT COUNT(*) AS n FROM streams WHERE is_live = 1",
+ "SELECT COUNT(*) AS n FROM streams WHERE started_at >= ?",
+ "SELECT COUNT(*) AS n FROM streams WHERE user_id = ?",
+ "SELECT COUNT(*) AS n FROM subscriptions WHERE streamer_id = ? AND status = 'active' AND (current_period_end IS NULL OR datetime(current_period_end) > CURRENT_TIMESTAMP)",
+ "SELECT COUNT(*) AS n FROM users WHERE created_at >= ?",
+ "SELECT COUNT(*) AS n, COALESCE(AVG(quality), 0) AS avg_q, COALESCE(MAX(quality), 0) AS best_q, COALESCE(SUM(kind IN ('beef_hit', 'callout')), 0) AS beef_hits, COALESCE(SUM(kind = 'trash'), 0) AS trash, COALESCE(SUM(quality >= 7), 0) AS bangers FROM arena_mic_moments WHERE user_id = ? AND created_at >= datetime('now', ?)",
+ "SELECT COUNT(*) AS n, COALESCE(SUM(amount), 0) AS t FROM transactions WHERE type = 'donation' AND (stream_id = ? OR (to_user_id = ? AND created_at BETWEEN ? AND ?))",
+ "SELECT COUNT(*) AS n, COALESCE(SUM(amount), 0) AS total FROM transactions WHERE type = 'donation' AND COALESCE(status, 'completed') NOT IN ('failed', 'refunded', 'pending') AND ((stream_id = ?) OR (to_user_id = ? AND created_at BETWEEN ? AND ?))",
+ "SELECT COUNT(*) AS n, COALESCE(SUM(s.duration_seconds), 0) AS secs, COALESCE(MAX(s.peak_viewers), 0) AS peak, AVG(a.avg_viewers) AS avg FROM streams s LEFT JOIN stream_analytics a ON a.stream_id = s.id WHERE s.user_id = ? AND s.started_at >= ? AND s.is_live = 0",
+ "SELECT COUNT(*) AS n, COUNT(DISTINCT COALESCE(user_id, anon_id, username)) AS c FROM chat_messages WHERE stream_id = ? AND COALESCE(is_deleted, 0) = 0",
+ "SELECT COUNT(*) AS n, COUNT(DISTINCT COALESCE(user_id, anon_id, username)) AS chatters FROM chat_messages WHERE stream_id = ? AND COALESCE(is_deleted, 0) = 0",
+ "SELECT COUNT(*) AS n, MAX(started_at) AS last FROM streams WHERE user_id = ?",
+ "SELECT COUNT(*) AS n, MIN(started_at) AS first FROM streams WHERE user_id = ?",
+ "SELECT COUNT(*) AS streams, COALESCE(SUM(duration_seconds), 0) / 3600.0 AS hours, MAX(ended_at) AS last_live_at FROM streams WHERE user_id = ? AND duration_seconds > 0 AND started_at >= datetime('now', ?)",
+ "SELECT COUNT(*) as c FROM chat_messages",
+ "SELECT COUNT(*) as c FROM chat_messages WHERE user_id = ? AND is_deleted = 0 AND (auto_delete_at IS NULL OR datetime(auto_delete_at) > CURRENT_TIMESTAMP)",
+ "SELECT COUNT(*) as c FROM coin_redemptions WHERE reward_id = ? AND stream_id = ?",
+ "SELECT COUNT(*) as c FROM content_views WHERE content_type = ? AND content_id = ?",
+ "SELECT COUNT(*) as c FROM dm_participants WHERE conversation_id = ?",
+ "SELECT COUNT(*) as c FROM follows WHERE follower_id = ?",
+ "SELECT COUNT(*) as c FROM follows WHERE streamer_id = ?",
+ "SELECT COUNT(*) as c FROM site_settings",
+ "SELECT COUNT(*) as c FROM streams",
+ "SELECT COUNT(*) as c FROM streams WHERE is_live = 1",
+ "SELECT COUNT(*) as c FROM transactions",
+ "SELECT COUNT(*) as c FROM transactions WHERE type = 'cashout' AND status = 'escrow'",
+ "SELECT COUNT(*) as c FROM users",
+ "SELECT COUNT(*) as c FROM users WHERE is_banned = 1",
+ "SELECT COUNT(*) as c FROM users WHERE role = 'mod'",
+ "SELECT COUNT(*) as c FROM users WHERE role IN ('streamer', 'admin')",
+ "SELECT COUNT(*) as c FROM users WHERE username LIKE ? OR display_name LIKE ?",
+ "SELECT COUNT(*) as cnt FROM chat_messages WHERE is_global = 1 AND timestamp >= ? AND timestamp <= ? AND is_deleted = 0",
+ "SELECT COUNT(*) as cnt FROM chat_messages WHERE stream_id = ? AND is_deleted = 0 AND is_global = 0 AND message_type = 'chat'",
+ "SELECT COUNT(*) as cnt FROM chat_messages WHERE stream_id = ? AND timestamp >= ? AND is_deleted = 0 AND is_global = 0 AND message_type = 'chat'",
+ "SELECT COUNT(*) as cnt FROM chat_messages WHERE stream_id = ? AND timestamp >= ? AND timestamp <= ? AND is_deleted = 0",
+ "SELECT COUNT(*) as cnt FROM follows WHERE streamer_id = ?",
+ "SELECT COUNT(*) as cnt FROM follows WHERE streamer_id = ? AND created_at >= ? AND created_at <= ?",
+ "SELECT COUNT(*) as cnt FROM streams WHERE user_id = ? AND duration_seconds > 0",
+ "SELECT COUNT(*) as count FROM channel_sounds WHERE channel_owner_id = ?",
+ "SELECT COUNT(*) as count FROM channel_sounds WHERE channel_owner_id = ? AND created_by = ?",
+ "SELECT COUNT(*) as count FROM emotes WHERE (channel_owner_id = ?) OR (channel_owner_id IS NULL AND user_id = ?)",
+ "SELECT COUNT(*) as count FROM emotes WHERE user_id = ?",
+ "SELECT COUNT(*) as count FROM follows WHERE streamer_id = ?",
+ "SELECT COUNT(*) as total_streams, SUM(duration_seconds) as total_duration, MAX(peak_viewers) as peak_viewers FROM streams WHERE user_id = ? AND duration_seconds > 0",
+ "SELECT COUNT(*) as total_streams, SUM(s.duration_seconds) as total_duration, MAX(s.peak_viewers) as all_time_peak, AVG(sa.avg_viewers) as avg_viewers_per_stream, SUM(sa.total_messages) as total_messages, SUM(sa.unique_chatters) as total_unique_chatters, SUM(sa.total_watch_minutes) as total_watch_minutes, SUM(sa.new_followers) as total_new_followers, SUM(sa.clips_created) as total_clips FROM streams s LEFT JOIN stream_analytics sa ON sa.stream_id = s.id WHERE s.user_id = ? AND s.started_at >= ? AND s.duration_seconds > 0",
+ "SELECT COUNT(DISTINCT COALESCE(user_id, anon_id)) as cnt FROM chat_messages WHERE stream_id = ? AND is_deleted = 0 AND is_global = 0",
+ "SELECT COUNT(DISTINCT from_user_id) AS count FROM transactions WHERE type = 'donation' AND from_user_id IS NOT NULL AND created_at >= ?",
+ "SELECT COUNT(DISTINCT from_user_id) AS count FROM transactions WHERE type = 'donation' AND from_user_id IS NOT NULL AND created_at >= datetime('now', ?)",
+ "SELECT COUNT(DISTINCT from_user_id) AS count FROM transactions WHERE type = 'donation' AND from_user_id IS NOT NULL AND created_at >= datetime('now', ?) AND created_at < datetime('now', ?)",
+ "SELECT COUNT(DISTINCT user_id) AS count FROM streams WHERE is_live = 0 AND ended_at IS NOT NULL",
+ "SELECT COUNT(DISTINCT user_id) AS count FROM streams WHERE user_id IS NOT NULL",
+ "SELECT COUNT(DISTINCT user_id) AS count FROM streams WHERE user_id IS NOT NULL AND created_at >= datetime('now', ?)",
+ "SELECT COUNT(DISTINCT user_id) AS count FROM streams WHERE user_id IS NOT NULL AND created_at >= datetime('now', ?) AND created_at < datetime('now', ?)",
+ "SELECT DISTINCT e.user_id FROM stream_timeline_events e JOIN users u ON u.id = e.user_id WHERE e.kind = 'speech' AND e.user_id IS NOT NULL AND e.created_at >= datetime('now', ?) AND COALESCE(u.is_banned, 0) = 0",
+ "SELECT DISTINCT il.user_id, u.role FROM ip_log il JOIN users u ON u.id = il.user_id WHERE il.ip_address = ? AND il.user_id IS NOT NULL",
+ "SELECT DISTINCT rd.user_id FROM restream_destinations rd WHERE rd.managed_stream_id IS NULL",
+ "SELECT DISTINCT s.user_id, u.stream_key, u.username, u.display_name, c.id AS channel_id, c.title AS channel_title, c.protocol AS channel_protocol, c.category AS channel_category, c.is_nsfw AS channel_is_nsfw FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN channels c ON c.user_id = s.user_id WHERE s.user_id NOT IN (SELECT user_id FROM managed_streams) AND u.stream_key IS NOT NULL",
+ "SELECT DISTINCT title FROM streams WHERE user_id = ? AND duration_seconds > 0 ORDER BY started_at DESC LIMIT 8",
+ "SELECT DISTINCT title FROM streams WHERE user_id = ? AND title IS NOT NULL AND title != '' ORDER BY id DESC LIMIT 10",
+ "SELECT DISTINCT user_id AS id FROM streams UNION SELECT user_id FROM search_doc_pushes",
+ "SELECT DISTINCT user_id FROM linked_accounts WHERE service = 'network' AND subject_id = ?",
+ "SELECT MAX(anon_num) as m FROM anon_ip_mappings",
+ "SELECT MAX(id) AS id FROM chat_messages",
+ "SELECT MAX(id) AS m FROM chat_messages",
+ "SELECT MAX(queue_position) AS max_pos FROM media_requests WHERE streamer_id = ? AND status = 'pending'",
+ "SELECT MIN(timestamp) AS t FROM chat_messages WHERE anon_id = ? AND user_id IS NULL AND is_deleted = 0",
+ "SELECT ROUND(AVG(viewer_count), 1) AS a, COUNT(*) AS k FROM viewer_snapshots WHERE stream_id = ?",
+ "SELECT ROUND(SUM(COALESCE(duration_seconds, CASE WHEN ended_at IS NOT NULL THEN (julianday(ended_at) - julianday(started_at)) * 86400 ELSE (julianday('now') - julianday(started_at)) * 86400 END)) / 3600.0, 1) AS h FROM streams WHERE started_at >= ?",
+ "SELECT SUM(CASE WHEN event = 'line' THEN 1 ELSE 0 END) AS lines, SUM(CASE WHEN event = 'tick' THEN 1 ELSE 0 END) AS ticks, SUM(CASE WHEN event = 'skip' THEN 1 ELSE 0 END) AS skips, COALESCE(SUM(cost_usd),0) AS cost_usd, COALESCE(SUM(tokens_in),0) AS tokens_in, COALESCE(SUM(tokens_cached),0) AS tokens_cached FROM ai_viewer_log WHERE channel_user_id = ? AND created_at > datetime('now', ?)",
+ "SELECT SUM(CASE WHEN winner_user_id = ? THEN 1 ELSE 0 END) AS wins, SUM(CASE WHEN status = 'resolved' AND winner_user_id IS NOT NULL AND winner_user_id != ? THEN 1 ELSE 0 END) AS losses, SUM(CASE WHEN status = 'resolved' AND winner_user_id IS NULL THEN 1 ELSE 0 END) AS draws FROM arena_beefs WHERE a_user_id = ? OR b_user_id = ?",
+ "SELECT SUM(coins_earned) as total FROM watch_time WHERE stream_id = ?",
+ "SELECT SUM(minutes_watched) as total FROM watch_time WHERE stream_id = ?",
+ "SELECT SUM(minutes_watched) as total FROM watch_time WHERE user_id = ?",
+ "SELECT SUM(persona_json IS NOT NULL) AS personas, SUM(image_path IS NOT NULL) AS images, SUM(quotes_json IS NOT NULL) AS quotes FROM arena_profiles",
+ "SELECT SUM(status = 'open') AS open, SUM(status = 'resolved') AS resolved FROM arena_beefs",
+ "SELECT a.owner_user_id AS user_id, u.username, COUNT(*) AS calls, SUM(a.cost_usd) AS cost_usd, SUM(a.input_tokens) AS input_tokens, SUM(a.cached_tokens) AS cached_tokens, SUM(a.output_tokens) AS output_tokens, SUM(CASE WHEN a.created_at >= date('now') THEN a.cost_usd ELSE 0 END) AS cost_today, SUM(CASE WHEN a.provider = 'byo' THEN a.cost_usd ELSE 0 END) AS cost_byo FROM ai_usage a LEFT JOIN users u ON u.id = a.owner_user_id WHERE a.created_at >= date('now', ?) AND a.owner_user_id IS NOT NULL GROUP BY a.owner_user_id ORDER BY cost_usd DESC LIMIT 100",
+ "SELECT ai.*, u.username as approved_by_username FROM approved_ips ai LEFT JOIN users u ON ai.approved_by = u.id WHERE ai.channel_id = ? ORDER BY ai.created_at DESC LIMIT ? OFFSET ?",
+ "SELECT ai_derivation_enabled FROM channels WHERE user_id = ?",
+ "SELECT ai_overview_short, ai_overview FROM clip_ai_state WHERE clip_id = ?",
+ "SELECT ai_overview_short, ai_overview FROM vod_ai_state WHERE vod_id = ?",
+ "SELECT aimed_at, COUNT(*) AS n FROM arena_mic_moments WHERE user_id = ? AND aimed_at IS NOT NULL AND target_user_id IS NULL GROUP BY aimed_at ORDER BY n DESC LIMIT 6",
+ "SELECT anon_num FROM anon_ip_mappings WHERE ip = ?",
+ "SELECT applied_at FROM schema_migrations WHERE id = ?",
+ "SELECT balance FROM channel_points WHERE user_id = ? AND streamer_id = ?",
+ "SELECT broadcast_settings FROM managed_streams WHERE id = ? AND user_id = ?",
+ "SELECT c.* FROM dm_conversations c JOIN dm_participants p1 ON p1.conversation_id = c.id AND p1.user_id = ? JOIN dm_participants p2 ON p2.conversation_id = c.id AND p2.user_id = ? WHERE c.is_group = 0",
+ "SELECT c.*, u.username, u.display_name, u.avatar_url, u.profile_color, u.bio, u.stream_key, u.role, u.is_owner FROM channels c JOIN users u ON c.user_id = u.id WHERE u.username = ? COLLATE NOCASE",
+ "SELECT c.id, c.name, c.is_group, c.updated_at, (SELECT dm.message FROM dm_messages dm WHERE dm.conversation_id = c.id ORDER BY dm.created_at DESC LIMIT 1) AS last_message, (SELECT dm.sender_id FROM dm_messages dm WHERE dm.conversation_id = c.id ORDER BY dm.created_at DESC LIMIT 1) AS last_sender_id, (SELECT dm.created_at FROM dm_messages dm WHERE dm.conversation_id = c.id ORDER BY dm.created_at DESC LIMIT 1) AS last_message_at, (SELECT COUNT(*) FROM dm_messages dm WHERE dm.conversation_id = c.id AND dm.created_at > p.last_read_at AND dm.sender_id != ?) AS unread_count FROM dm_conversations c JOIN dm_participants p ON p.conversation_id = c.id AND p.user_id = ? ORDER BY COALESCE((SELECT dm.created_at FROM dm_messages dm WHERE dm.conversation_id = c.id ORDER BY dm.created_at DESC LIMIT 1), c.created_at) DESC",
+ "SELECT c.username, u.display_name, u.avatar_url, u.profile_color, COUNT(*) AS n FROM chat_messages c LEFT JOIN users u ON u.id = c.user_id WHERE c.stream_id = ? AND COALESCE(c.is_deleted, 0) = 0 AND c.username IS NOT NULL AND (c.user_id IS NULL OR c.user_id != ?) GROUP BY COALESCE(c.user_id, c.username) ORDER BY n DESC LIMIT 3",
+ "SELECT c.username, u.display_name, u.avatar_url, u.profile_color, COUNT(*) AS n FROM chat_messages c LEFT JOIN users u ON u.id = c.user_id WHERE c.stream_id = ? AND COALESCE(c.is_deleted, 0) = 0 AND c.username IS NOT NULL AND (c.user_id IS NULL OR c.user_id != ?) GROUP BY COALESCE(c.user_id, c.username) ORDER BY n DESC LIMIT 5",
+ "SELECT category, is_nsfw FROM streams WHERE id = ?",
+ "SELECT chat_language FROM channels WHERE user_id = ?",
+ "SELECT chat_settings FROM user_preferences WHERE user_id = ?",
+ "SELECT clip_id AS id, s.* FROM clip_ai_state s WHERE (ai_overview IS NULL OR ai_overview = '') AND (ai_overview_short IS NULL OR ai_overview_short = '' OR ai_overview_short LIKE '%…') ORDER BY clip_id DESC LIMIT ?",
+ "SELECT clip_id AS id, s.* FROM clip_ai_state s WHERE ai_transcript_json IS NULL AND (transcript_status IS NULL OR transcript_status IN ('pending','retry')) AND (transcript_next_at IS NULL OR transcript_next_at <= CURRENT_TIMESTAMP) ORDER BY (transcript_status='retry'), clip_id DESC LIMIT ?",
+ "SELECT clip_id FROM clip_ai_state WHERE COALESCE(clip_notified,0) = 0 AND clip_notify_at IS NOT NULL AND clip_notify_at <= CURRENT_TIMESTAMP LIMIT ?",
+ "SELECT clips_created FROM stream_analytics WHERE stream_id = ?",
+ "SELECT cm.*, s.title as stream_title FROM chat_messages cm LEFT JOIN streams s ON cm.stream_id = s.id WHERE cm.user_id = ? AND cm.is_deleted = 0 AND (cm.auto_delete_at IS NULL OR datetime(cm.auto_delete_at) > CURRENT_TIMESTAMP) ORDER BY cm.timestamp DESC LIMIT ? OFFSET ?",
+ "SELECT cm.*, u.avatar_url, u.profile_color, u.role, u.display_name FROM chat_messages cm LEFT JOIN users u ON cm.user_id = u.id WHERE cm.stream_id = ? AND cm.is_deleted = 0 AND cm.message_type = 'chat' AND (cm.auto_delete_at IS NULL OR datetime(cm.auto_delete_at) > CURRENT_TIMESTAMP)",
+ "SELECT cm.*, u.avatar_url, u.profile_color, u.role, u.display_name, u.username AS core_username, COALESCE(su.username, cu.username) AS stream_channel, s.is_live AS source_is_live, s.managed_stream_id AS source_managed_id, COALESCE(ms.title, s.title) AS source_stream_title, ms.slug AS source_slug FROM chat_messages cm LEFT JOIN users u ON cm.user_id = u.id LEFT JOIN streams s ON cm.stream_id = s.id LEFT JOIN users su ON s.user_id = su.id LEFT JOIN users cu ON cm.channel_user_id = cu.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE cm.is_deleted = 0 AND cm.message_type IN ('chat', 'system', 'channel-sound', 'soundboard', 'donation') AND (cm.auto_delete_at IS NULL OR datetime(cm.auto_delete_at) > CURRENT_TIMESTAMP)",
+ "SELECT cm.*, u.avatar_url, u.profile_color, u.role, u.display_name, u.username AS core_username, s.title AS source_stream_title, s.managed_stream_id AS source_managed_id, s.is_live AS source_is_live, ms.slug AS source_slug, COALESCE(bu.username, cu.username) AS source_channel FROM chat_messages cm LEFT JOIN users u ON cm.user_id = u.id LEFT JOIN streams s ON cm.stream_id = s.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id LEFT JOIN users bu ON s.user_id = bu.id LEFT JOIN users cu ON cm.channel_user_id = cu.id WHERE cm.channel_user_id = ? AND cm.is_deleted = 0 AND (cm.auto_delete_at IS NULL OR datetime(cm.auto_delete_at) > CURRENT_TIMESTAMP)",
+ "SELECT cm.*, u.display_name, u.username as u_username, u.role, u.avatar_url, u.profile_color FROM chat_messages cm LEFT JOIN users u ON cm.user_id = u.id WHERE cm.is_deleted = 0 AND (cm.auto_delete_at IS NULL OR datetime(cm.auto_delete_at) > CURRENT_TIMESTAMP)",
+ "SELECT cm.channel_id, c.title, c.user_id, u.username as owner_username FROM channel_moderators cm JOIN channels c ON cm.channel_id = c.id JOIN users u ON c.user_id = u.id WHERE cm.user_id = ?",
+ "SELECT cm.id, cm.user_id, cm.added_by, cm.created_at, u.username, u.display_name, u.avatar_url, a.username as added_by_username FROM channel_moderators cm JOIN users u ON cm.user_id = u.id LEFT JOIN users a ON cm.added_by = a.id WHERE cm.channel_id = ? ORDER BY cm.created_at ASC",
+ "SELECT cm.user_id, cm.username, COUNT(*) as message_count FROM chat_messages cm JOIN streams s ON cm.stream_id = s.id WHERE s.user_id = ? AND cm.timestamp >= ? AND cm.is_deleted = 0 AND cm.is_global = 0 AND cm.message_type = 'chat' AND cm.user_id IS NOT NULL GROUP BY cm.user_id ORDER BY message_count DESC LIMIT 20",
+ "SELECT compared, matched, mismatched, inconclusive, errors, last_mismatch_at, last_mismatch, since FROM chat_dual_read_stats WHERE tbl = ?",
+ "SELECT consecutive_failures FROM restream_destinations WHERE id = ?",
+ "SELECT created_at FROM anon_ip_mappings WHERE anon_num = ?",
+ "SELECT created_at FROM anon_ip_mappings WHERE ip = ?",
+ "SELECT created_at FROM coin_redemptions WHERE reward_id = ? AND user_id = ? ORDER BY created_at DESC LIMIT 1",
+ "SELECT created_at FROM users WHERE id = ?",
+ "SELECT cw.id, cw.user_id, u.username, u.display_name, cw.created_at FROM control_whitelist cw JOIN users u ON cw.user_id = u.id WHERE cw.channel_id = ? ORDER BY cw.created_at DESC",
+ "SELECT date(created_at) AS day, COUNT(*) AS calls, SUM(input_tokens) AS input_tokens, SUM(output_tokens) AS output_tokens, SUM(cost_usd) AS cost_usd FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY day ORDER BY day DESC",
+ "SELECT default_clip_visibility, slot_clip_recording_enabled FROM managed_streams WHERE id = ?",
+ "SELECT default_vod_visibility FROM managed_streams WHERE id = ?",
+ "SELECT description FROM stream_memories WHERE user_id = ? ORDER BY captured_at DESC LIMIT 4",
+ "SELECT description, category, chat_language FROM channels WHERE user_id = ?",
+ "SELECT donation_sound_url, donation_sound_mime, goal_sound_url, goal_sound_mime FROM channel_moderation_settings WHERE channel_id = ?",
+ "SELECT e.*, u.username FROM emotes e JOIN users u ON e.user_id = u.id WHERE e.id = ?",
+ "SELECT e.*, u.username FROM emotes e JOIN users u ON e.user_id = u.id WHERE e.is_global = 1 AND e.is_approved = 1 ORDER BY code",
+ "SELECT e.*, u.username, up.username AS uploader_username, up.display_name AS uploader_display_name FROM emotes e JOIN users u ON e.user_id = u.id LEFT JOIN users up ON e.user_id = up.id WHERE ((e.channel_owner_id = ?) OR (e.channel_owner_id IS NULL AND e.user_id = ?)) AND e.is_approved = 1 ORDER BY code",
+ "SELECT e.id, e.stream_id, e.vod_id, e.start_sec, e.end_sec, e.text, s.started_at FROM stream_timeline_events e LEFT JOIN streams s ON s.id = e.stream_id WHERE e.user_id = ? AND e.kind = 'speech' AND e.id > ? AND e.created_at >= datetime('now', ?) AND NOT EXISTS (SELECT 1 FROM streams l WHERE l.id = e.stream_id AND l.is_live = 1) ORDER BY e.stream_id, e.start_sec LIMIT 4000",
+ "SELECT enabled FROM news_settings WHERE scope = 'global' AND source_id = '_master'",
+ "SELECT enabled FROM news_settings WHERE scope = 'user' AND scope_id = ? AND source_id = '_master'",
+ "SELECT f.created_at, fu.username AS follower_username, fu.display_name AS follower_display, su.username AS streamer_username, su.display_name AS streamer_display FROM follows f JOIN users fu ON fu.id = f.follower_id JOIN users su ON su.id = f.streamer_id ORDER BY f.id DESC LIMIT 1",
+ "SELECT f.id, la.subject_id, la.service_user_id FROM follows f LEFT JOIN linked_accounts la ON la.service = 'network' AND la.user_id = f.follower_id WHERE f.streamer_id = ? AND f.id > ? ORDER BY f.id LIMIT ?",
+ "SELECT follower_id FROM follows WHERE streamer_id = ?",
+ "SELECT from_user_id, u.username, u.display_name, u.avatar_url, SUM(amount) as total_donated FROM transactions t JOIN users u ON t.from_user_id = u.id WHERE t.stream_id = ? AND t.type = 'donation' AND t.status = 'completed' GROUP BY from_user_id ORDER BY total_donated DESC LIMIT ?",
+ "SELECT g.id, g.title, g.current_amount, g.target_amount, g.image_url, u.username, u.display_name FROM donation_goals g JOIN users u ON u.id = g.user_id WHERE g.is_active = 1 AND g.target_amount > 0 ORDER BY (CAST(g.current_amount AS REAL) / g.target_amount) DESC, g.current_amount DESC LIMIT 3",
+ "SELECT hash, revision, deleted FROM search_doc_pushes WHERE user_id = ?",
+ "SELECT hash, revision, deleted FROM search_media_pushes WHERE kind = ? AND media_id = ?",
+ "SELECT hash, strftime('%s', pushed_at) * 1000 AS at FROM module_summary_pushes WHERE user_id = ? AND namespace = ?",
+ "SELECT hru.*, u.username as created_by_username FROM hidden_relay_users hru LEFT JOIN users u ON hru.created_by = u.id WHERE hru.channel_id = ? OR hru.channel_id IS NULL ORDER BY hru.created_at DESC LIMIT ?",
+ "SELECT id FROM billing_actions WHERE idempotency_key = ?",
+ "SELECT id FROM channels WHERE user_id = ?",
+ "SELECT id FROM coin_transactions WHERE user_id = ? AND type = 'follow_bonus' AND message LIKE '%streamer:' || ? || '%'",
+ "SELECT id FROM emotes WHERE user_id = ? AND code = ? AND channel_owner_id IS NULL",
+ "SELECT id FROM follows WHERE follower_id = ? AND streamer_id = ?",
+ "SELECT id FROM managed_streams WHERE user_id = ? AND slug = ? COLLATE NOCASE",
+ "SELECT id FROM managed_streams WHERE user_id = ? ORDER BY created_at",
+ "SELECT id FROM media_requests WHERE streamer_id = ? AND status = 'pending' ORDER BY queue_position ASC, requested_at ASC, id ASC",
+ "SELECT id FROM schema_migrations",
+ "SELECT id FROM streams WHERE is_live = 1",
+ "SELECT id FROM streams WHERE is_live = 1 AND protocol NOT IN ('rtmp', 'jsmpeg')",
+ "SELECT id FROM streams WHERE managed_stream_id = ? AND is_live = 1 AND id != ?",
+ "SELECT id FROM streams WHERE managed_stream_id = ? AND is_live = 1 LIMIT 1",
+ "SELECT id FROM streams WHERE user_id = ? AND duration_seconds > 0 ORDER BY started_at DESC LIMIT 6",
+ "SELECT id FROM streams WHERE user_id = ? AND is_live = 1 ORDER BY started_at DESC LIMIT 1",
+ "SELECT id FROM streams WHERE user_id = ? ORDER BY id DESC LIMIT 1",
+ "SELECT id FROM themes WHERE slug = ?",
+ "SELECT id FROM user_themes WHERE user_id = ?",
+ "SELECT id FROM users WHERE role = 'admin' LIMIT 1",
+ "SELECT id FROM users WHERE username = ? COLLATE NOCASE AND id != ?",
+ "SELECT id FROM verification_keys WHERE target_username = ? COLLATE NOCASE AND status = 'active'",
+ "SELECT id, action, idempotency_key, live_user_id, live_ref, status, error, attempts, created_at, updated_at FROM billing_actions WHERE status IN ('unknown', 'pending') ORDER BY id DESC LIMIT 50",
+ "SELECT id, ai_overview_short, ai_overview, title FROM streams WHERE user_id = ? AND (ai_title IS NULL OR ai_title = '') AND (ai_overview_short IS NOT NULL OR ai_overview IS NOT NULL) ORDER BY COALESCE(started_at, created_at) DESC LIMIT ?",
+ "SELECT id, applied_at, mode, duration_ms FROM schema_migrations",
+ "SELECT id, args FROM chat_bridge_outbox WHERE op = 'db' ORDER BY id",
+ "SELECT id, boot, ref, op, args FROM chat_bridge_outbox ORDER BY id",
+ "SELECT id, channel_id FROM hidden_relay_users WHERE id = ?",
+ "SELECT id, created_at FROM api_tokens WHERE token_hash = ?",
+ "SELECT id, ip_address, stream_id, reason FROM bans WHERE user_id = ? AND stream_id IS NULL",
+ "SELECT id, label, permissions, last_used, is_active, created_at FROM api_keys WHERE user_id = ?",
+ "SELECT id, label, scopes, created_at, last_used_at, expires_at, is_active FROM api_tokens WHERE user_id = ? ORDER BY created_at DESC",
+ "SELECT id, message_type, metadata FROM chat_messages WHERE is_deleted = 0 ORDER BY id DESC LIMIT 1",
+ "SELECT id, pip_source_msid, pip_defaults FROM managed_streams WHERE id = ?",
+ "SELECT id, start_sec, end_sec, text, lang, text_en, created_at FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' AND id > ? ORDER BY id DESC LIMIT ?",
+ "SELECT id, stream_id FROM chat_messages WHERE is_deleted = 0 AND auto_delete_at IS NOT NULL AND auto_delete_at <= CURRENT_TIMESTAMP ORDER BY auto_delete_at ASC LIMIT ?",
+ "SELECT id, stream_id, user_id, ip_address, anon_id, expires_at FROM bans WHERE expires_at IS NULL OR datetime(expires_at) > CURRENT_TIMESTAMP",
+ "SELECT id, stream_id, vod_id, start_sec, text FROM stream_timeline_events WHERE user_id = ? AND kind = 'speech' AND created_at >= datetime('now', ?) AND LENGTH(text) BETWEEN 25 AND 220 ORDER BY created_at DESC LIMIT 1500",
+ "SELECT id, title, slug FROM managed_streams WHERE user_id = ? AND (? IS NULL OR id != ?) ORDER BY sort_order ASC, id ASC",
+ "SELECT id, title, started_at, viewer_count FROM streams WHERE user_id = ? AND is_live = 1 ORDER BY started_at DESC LIMIT 1",
+ "SELECT id, user_id FROM channels WHERE id = ?",
+ "SELECT id, user_id, anon_id, username, message, message_type, source_platform, reply_to_id, timestamp FROM chat_messages WHERE channel_user_id = ? AND id > ? AND is_deleted = 0 AND message_type IN ('chat','donation') AND (auto_delete_at IS NULL OR auto_delete_at > CURRENT_TIMESTAMP) ORDER BY id DESC LIMIT ?",
+ "SELECT id, user_id, channel_id, managed_stream_id, title, is_live, started_at, ended_at, created_at FROM streams",
+ "SELECT id, user_id, protocol FROM streams WHERE is_live = 1 AND ( (last_heartbeat IS NOT NULL AND last_heartbeat < datetime('now', '-5 minutes')) OR (last_heartbeat IS NULL AND started_at < datetime('now', '-6 minutes')) )",
+ "SELECT id, user_id, slug FROM managed_streams WHERE id = ?",
+ "SELECT id, user_id, slug, title, protocol, streaming_method, openre_stream_id FROM managed_streams WHERE ingest_authority = 'openre' ORDER BY id",
+ "SELECT id, user_id, slug, title, sort_order, created_at FROM managed_streams",
+ "SELECT id, user_id, title FROM channels",
+ "SELECT id, username FROM users WHERE id = ?",
+ "SELECT id, username FROM users WHERE username = ? COLLATE NOCASE",
+ "SELECT id, username, display_name, avatar_url, profile_color FROM users WHERE id != ? AND is_banned = 0 AND id NOT IN (SELECT blocked_id FROM dm_blocks WHERE blocker_id = ?) AND id NOT IN (SELECT blocker_id FROM dm_blocks WHERE blocked_id = ?) AND (username LIKE ? COLLATE NOCASE OR display_name LIKE ? COLLATE NOCASE) LIMIT ?",
+ "SELECT id, username, display_name, avatar_url, profile_color, role, openvibe_bucks_balance, openvibe_coins_balance, created_at, last_seen FROM users WHERE id = ?",
+ "SELECT id, username, display_name, bio, is_banned FROM users WHERE id = ?",
+ "SELECT id, username, display_name, is_banned FROM users WHERE id = ?",
+ "SELECT id, username, display_name, role, created_at FROM users ORDER BY created_at DESC LIMIT 10",
+ "SELECT il.user_id, il.anon_id, u.username, u.display_name, u.avatar_url, u.role, u.is_banned, u.ban_reason, u.created_at as user_created_at, COUNT(*) as hit_count, MIN(il.created_at) as first_seen, MAX(il.created_at) as last_seen, GROUP_CONCAT(DISTINCT il.action) as actions FROM ip_log il LEFT JOIN users u ON il.user_id = u.id WHERE il.ip_address = ? GROUP BY COALESCE(il.user_id, il.anon_id) ORDER BY last_seen DESC",
+ "SELECT ip, anon_num FROM anon_ip_mappings ORDER BY anon_num",
+ "SELECT ip_address, geo_country, geo_region, geo_city, geo_isp, geo_org, geo_ll, COUNT(*) as hit_count, MIN(created_at) as first_seen, MAX(created_at) as last_seen, GROUP_CONCAT(DISTINCT action) as actions FROM ip_log WHERE user_id = ? GROUP BY ip_address ORDER BY last_seen DESC",
+ "SELECT ip_address, geo_country, geo_region, geo_city, geo_isp, geo_org, geo_ll, created_at FROM ip_log WHERE anon_id = ? ORDER BY created_at DESC LIMIT 1",
+ "SELECT ip_address, geo_country, geo_region, geo_city, geo_isp, geo_org, geo_ll, created_at FROM ip_log WHERE user_id = ? ORDER BY created_at DESC LIMIT 1",
+ "SELECT item_id, category, unlocked_at FROM user_cosmetics WHERE user_id = ?",
+ "SELECT json FROM stream_recaps WHERE stream_id = ?",
+ "SELECT key FROM site_settings WHERE key = ?",
+ "SELECT key FROM site_settings WHERE key LIKE 'tts\\_%' ESCAPE '\\' OR key IN ('gif_tenor_api_key', 'gif_giphy_api_key', 'soundboard_101_api_key')",
+ "SELECT kind, COUNT(*) AS calls, SUM(cost_usd) AS cost_usd FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY kind ORDER BY cost_usd DESC",
+ "SELECT kind, start_sec, end_sec, text, label, confidence FROM stream_timeline_events WHERE stream_id = ?",
+ "SELECT kind, start_sec, end_sec, text, label, confidence FROM stream_timeline_events WHERE stream_id = ? AND kind = ? ORDER BY start_sec ASC LIMIT ?",
+ "SELECT kind, start_sec, end_sec, text, label, confidence FROM stream_timeline_events WHERE vod_id = ? ORDER BY start_sec ASC LIMIT 20000",
+ "SELECT label, COUNT(*) AS n FROM stream_timeline_events WHERE user_id = ? AND kind = 'sound' AND label IS NOT NULL AND created_at >= datetime('now', ?) GROUP BY label ORDER BY n DESC LIMIT 5",
+ "SELECT m.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM dm_messages m JOIN users u ON u.id = m.sender_id WHERE m.conversation_id = ? AND m.id < ? ORDER BY m.created_at DESC LIMIT ?",
+ "SELECT m.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM dm_messages m JOIN users u ON u.id = m.sender_id WHERE m.conversation_id = ? AND m.id > ? ORDER BY m.created_at ASC LIMIT ?",
+ "SELECT m.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM dm_messages m JOIN users u ON u.id = m.sender_id WHERE m.conversation_id = ? ORDER BY m.created_at DESC LIMIT ?",
+ "SELECT m.id AS msid, m.title, m.slug, m.user_id, s.id AS stream_id, s.is_live FROM managed_streams m LEFT JOIN streams s ON s.managed_stream_id = m.id AND s.is_live = 1 WHERE m.id = ?",
+ "SELECT m.text, m.quality, m.said_at, m.aimed_at, u.username, u.display_name FROM arena_mic_moments m JOIN users u ON u.id = m.user_id WHERE m.said_at >= ? AND m.quality >= 7 ORDER BY m.quality DESC, m.said_at DESC LIMIT 3",
+ "SELECT m.thumbnail_url FROM stream_memories m WHERE m.user_id = ? AND m.thumbnail_url LIKE 'http%' AND (LOWER(m.description) LIKE '%person%' OR LOWER(m.description) LIKE '%face%' OR LOWER(m.description) LIKE '%wearing%' OR LOWER(m.description) LIKE '%headphones%') ORDER BY m.id DESC LIMIT 3",
+ "SELECT ma.*, actor.username AS actor_username, target.username AS target_username FROM moderation_actions ma LEFT JOIN users actor ON ma.actor_user_id = actor.id LEFT JOIN users target ON ma.target_user_id = target.id WHERE ma.scope_type = ? AND ma.scope_id = ? ORDER BY ma.created_at DESC LIMIT ? OFFSET ?",
+ "SELECT media_id FROM search_media_pushes WHERE kind = ? AND deleted = 0",
+ "SELECT message FROM chat_messages WHERE stream_id = ? AND COALESCE(is_deleted, 0) = 0 AND message IS NOT NULL AND message <> '' AND timestamp >= datetime('now', ?) ORDER BY timestamp DESC LIMIT ?",
+ "SELECT metadata FROM chat_messages WHERE id = ?",
+ "SELECT model, COUNT(*) AS calls, SUM(cost_usd) AS cost_usd, SUM(input_tokens) AS input_tokens, SUM(cached_tokens) AS cached_tokens, SUM(output_tokens) AS output_tokens FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY model ORDER BY cost_usd DESC",
+ "SELECT ms.*, (SELECT COUNT(*) FROM streams s WHERE s.managed_stream_id = ms.id) AS session_count, (SELECT MAX(s.ended_at) FROM streams s WHERE s.managed_stream_id = ms.id AND s.ended_at IS NOT NULL) AS last_live_at, (SELECT s.is_live FROM streams s WHERE s.managed_stream_id = ms.id AND s.is_live = 1 LIMIT 1) AS is_currently_live, (SELECT s.id FROM streams s WHERE s.managed_stream_id = ms.id AND s.is_live = 1 LIMIT 1) AS live_session_id FROM managed_streams ms WHERE ms.user_id = ? ORDER BY ms.sort_order ASC, ms.created_at ASC",
+ "SELECT ms.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM managed_streams ms JOIN users u ON ms.user_id = u.id WHERE ms.id = ?",
+ "SELECT ms.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM managed_streams ms JOIN users u ON ms.user_id = u.id WHERE ms.id = ? AND ms.user_id = ?",
+ "SELECT ms.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM managed_streams ms JOIN users u ON ms.user_id = u.id WHERE ms.user_id = ? AND ms.slug = ? COLLATE NOCASE",
+ "SELECT ms.*, u.username, u.display_name, u.avatar_url, u.profile_color, u.stream_key AS user_stream_key FROM managed_streams ms JOIN users u ON ms.user_id = u.id WHERE ms.stream_key = ?",
+ "SELECT ns.enabled FROM news_settings ns WHERE ns.scope = 'user' AND ns.scope_id = (SELECT user_id FROM streams WHERE id = ?) AND ns.source_id = '_master'",
+ "SELECT o.*, u.username, u.display_name FROM streamer_overviews o JOIN users u ON u.id = o.user_id ORDER BY o.generated_at DESC LIMIT ?",
+ "SELECT offset_seconds, description, tags, thumbnail_url, captured_at, transcript_json FROM stream_memories WHERE stream_id = ? ORDER BY offset_seconds ASC LIMIT 400",
+ "SELECT offset_seconds, description, thumbnail_url FROM stream_memories WHERE stream_id = ? ORDER BY CASE WHEN LOWER(description) LIKE '%person%' OR LOWER(description) LIKE '%man %' OR LOWER(description) LIKE '%woman%' OR LOWER(description) LIKE '%face%' OR LOWER(description) LIKE '%wearing%' OR LOWER(description) LIKE '%headphones%' THEN 0 ELSE 1 END, id DESC LIMIT 4",
+ "SELECT offset_seconds, transcript_json FROM stream_memories WHERE stream_id = ? AND transcript_json IS NOT NULL ORDER BY offset_seconds ASC",
+ "SELECT overview, overview_short, generated_at FROM streamer_overviews WHERE user_id = ?",
+ "SELECT payload, generated_at FROM ai_timeline_cache WHERE user_id = ?",
+ "SELECT payload_json, created_at FROM vibe_coding_events WHERE managed_stream_id = ? ORDER BY id DESC LIMIT ?",
+ "SELECT persona_json FROM arena_profiles WHERE user_id = ?",
+ "SELECT powerchat_count_views FROM restream_destinations WHERE id = ?",
+ "SELECT powerchat_relay FROM restream_destinations WHERE id = ?",
+ "SELECT powerchat_username FROM powerchat_connections WHERE user_id = ?",
+ "SELECT r.*, cr.streamer_id FROM coin_redemptions r JOIN coin_rewards cr ON r.reward_id = cr.id WHERE r.id = ?",
+ "SELECT r.*, cr.title as reward_title, cr.cost, cr.icon, cr.color, u.username, u.display_name, u.avatar_url FROM coin_redemptions r JOIN coin_rewards cr ON r.reward_id = cr.id JOIN users u ON r.user_id = u.id WHERE cr.streamer_id = ? AND r.status = 'pending' ORDER BY r.created_at ASC",
+ "SELECT r.rowid AS id, r.platform, r.username, r.display_name, r.message_count, r.last_seen, cs.updated_at AS last_update FROM relay_users r LEFT JOIN chat_ai_summaries cs ON cs.scope = 'relay' AND cs.window = 'rolling' AND cs.subject_id = r.rowid WHERE r.last_seen >= ? AND r.message_count >= ? AND (cs.updated_at IS NULL OR cs.updated_at < r.last_seen) ORDER BY (cs.updated_at IS NULL) DESC, r.last_seen DESC LIMIT ?",
+ "SELECT r.stream_id FROM stream_recaps r JOIN streams s ON s.id = r.stream_id WHERE r.user_id = ? AND s.started_at >= ? ORDER BY r.created_at DESC LIMIT 1",
+ "SELECT r.stream_id, r.json, r.created_at, u.username, u.display_name, u.avatar_url, u.profile_color FROM stream_recaps r JOIN users u ON u.id = r.user_id WHERE (? IS NULL OR r.user_id != ?) AND COALESCE(u.is_banned, 0) = 0 ORDER BY r.created_at DESC LIMIT ?",
+ "SELECT recorded_at AS t, viewer_count AS v, COALESCE(chat_messages_5m, 0) AS c FROM viewer_snapshots WHERE stream_id = ? ORDER BY recorded_at ASC",
+ "SELECT revision FROM subject_projection WHERE subject_id = ?",
+ "SELECT rowid AS id, * FROM relay_users WHERE platform = ? AND username = ?",
+ "SELECT rowid AS id, * FROM relay_users WHERE rowid = ?",
+ "SELECT rowid AS rid, ai_overview AS ov FROM streams WHERE ai_overview IS NOT NULL AND TRIM(ai_overview) != '' AND (ai_overview_short IS NULL OR ai_overview_short = '')",
+ "SELECT rowid AS rid, overview AS ov FROM streamer_overviews WHERE overview IS NOT NULL AND TRIM(overview) != '' AND (overview_short IS NULL OR overview_short = '')",
+ "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color FROM streams s JOIN ( SELECT user_id, MAX(ended_at) AS latest_ended_at FROM streams WHERE is_live = 0 AND ended_at IS NOT NULL GROUP BY user_id ) latest ON latest.user_id = s.user_id AND latest.latest_ended_at = s.ended_at JOIN users u ON s.user_id = u.id WHERE s.is_live = 0 AND s.ended_at IS NOT NULL ORDER BY s.ended_at DESC LIMIT ?",
+ "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.id AS managed_stream_id, ms.stream_key AS managed_stream_key, ms.browser_mode, ms.streaming_method FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.is_live = 1 ORDER BY s.viewer_count DESC, s.started_at DESC",
+ "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.stream_key AS managed_stream_key, ms.title AS managed_stream_title FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.control_config_id = ? AND s.is_live = 1 ORDER BY s.started_at DESC",
+ "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.stream_key AS managed_stream_key, ms.title AS managed_stream_title FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.user_id = ? AND s.is_live = 1 ORDER BY s.started_at DESC",
+ "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.stream_key AS managed_stream_key, ms.title AS managed_stream_title, ms.id AS managed_stream_ref_id FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.user_id = ? ORDER BY s.created_at DESC LIMIT ?",
+ "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.stream_key AS managed_stream_key, ms.title AS managed_stream_title, ms.protocol AS managed_stream_protocol FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.id = ?",
+ "SELECT s.*, ms.slug AS managed_stream_slug FROM streams s LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.managed_stream_id = ? AND s.is_live = 1 ORDER BY COALESCE(s.started_at, s.created_at) DESC LIMIT 1",
+ "SELECT s.*, u.username AS streamer_username, u.display_name AS streamer_display, u.avatar_url AS streamer_avatar FROM subscriptions s LEFT JOIN users u ON s.streamer_id = u.id WHERE s.subscriber_id = ? ORDER BY s.started_at DESC",
+ "SELECT s.*, u.username AS subscriber_username, u.display_name AS subscriber_display, u.avatar_url AS subscriber_avatar FROM subscriptions s LEFT JOIN users u ON s.subscriber_id = u.id WHERE s.streamer_id = ? AND s.status = 'active' ORDER BY s.started_at DESC",
+ "SELECT s.id FROM streams s LEFT JOIN openre_sessions o ON o.stream_id = s.id WHERE s.managed_stream_id = ? AND s.is_live = 1 AND s.protocol = 'rtmp' AND o.session_id IS NULL ORDER BY s.id DESC LIMIT 1",
+ "SELECT s.id FROM streams s WHERE s.is_live = 0 AND s.ended_at IS NOT NULL AND s.ended_at >= datetime('now', ?) AND s.ended_at <= datetime('now', ?) AND COALESCE(s.duration_seconds, (julianday(s.ended_at) - julianday(s.started_at)) * 86400) >= ? AND NOT EXISTS (SELECT 1 FROM stream_recaps r WHERE r.stream_id = s.id) ORDER BY s.ended_at ASC LIMIT 6",
+ "SELECT s.id, COALESCE(ms.stream_key, u.stream_key) AS stream_key FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.is_live = 1 AND s.protocol = 'rtmp'",
+ "SELECT s.id, s.title, s.ai_title, s.started_at, s.ended_at, s.created_at, s.duration_seconds, s.ai_overview, s.ai_overview_short, s.thumbnail_url, s.peak_viewers, s.category, (SELECT COUNT(*) FROM stream_memories m WHERE m.stream_id = s.id) AS memory_count FROM streams s WHERE s.user_id = ? AND (s.ai_overview IS NOT NULL OR EXISTS (SELECT 1 FROM stream_memories m WHERE m.stream_id = s.id)) ORDER BY COALESCE(s.started_at, s.created_at) DESC LIMIT 300",
+ "SELECT s.id, s.title, s.category, s.started_at, s.ended_at, s.duration_seconds, s.peak_viewers, s.viewer_count, s.thumbnail_url, sa.avg_viewers, sa.unique_chatters, sa.total_messages, sa.clips_created FROM streams s LEFT JOIN stream_analytics sa ON sa.stream_id = s.id WHERE s.user_id = ? AND s.duration_seconds > 0 ORDER BY s.started_at DESC LIMIT ? OFFSET ?",
+ "SELECT s.id, s.title, s.category, s.started_at, s.ended_at, s.duration_seconds, s.peak_viewers, s.viewer_count, sa.avg_viewers, sa.unique_chatters, sa.total_messages, sa.total_watch_minutes, sa.new_followers, sa.clips_created, sa.coins_earned FROM streams s LEFT JOIN stream_analytics sa ON sa.stream_id = s.id WHERE s.user_id = ? AND s.started_at >= ? AND s.duration_seconds > 0 ORDER BY s.started_at DESC",
+ "SELECT s.id, s.title, s.protocol, s.viewer_count, s.started_at, u.username FROM streams s JOIN users u ON s.user_id = u.id WHERE s.is_live = 1 ORDER BY s.viewer_count DESC LIMIT 20",
+ "SELECT s.id, s.title, s.started_at, s.duration_seconds, (SELECT COALESCE(SUM(COALESCE(e.end_sec, e.start_sec + 3) - e.start_sec), 0) FROM stream_timeline_events e WHERE e.stream_id = s.id AND e.kind = 'speech') AS speech_sec, (SELECT COALESCE(SUM(LENGTH(e.text) - LENGTH(REPLACE(e.text, ' ', '')) + 1), 0) FROM stream_timeline_events e WHERE e.stream_id = s.id AND e.kind = 'speech') AS words, (SELECT COUNT(*) FROM arena_mic_moments m WHERE m.stream_id = s.id) AS moments, (SELECT COALESCE(AVG(m.quality), 0) FROM arena_mic_moments m WHERE m.stream_id = s.id) AS avg_q, (SELECT COUNT(*) FROM arena_mic_moments m WHERE m.stream_id = s.id AND m.kind = 'beef_hit') AS hits, (SELECT COUNT(*) FROM arena_beefs b WHERE b.winner_user_id = s.user_id AND b.resolved_at BETWEEN s.started_at AND COALESCE(s.ended_at, s.started_at)) AS wins FROM streams s WHERE s.user_id = ? AND s.duration_seconds > 0 AND s.started_at >= datetime('now', ?) ORDER BY s.started_at DESC LIMIT 14",
+ "SELECT s.id, s.title, s.started_at, s.ended_at, s.is_live, s.peak_viewers, s.viewer_count, s.duration_seconds, s.protocol, s.category FROM streams s WHERE s.managed_stream_id = ? AND s.user_id = ? ORDER BY s.started_at DESC LIMIT ?",
+ "SELECT s.id, s.user_id, s.managed_stream_id, ms.slug AS slot_slug FROM streams s LEFT JOIN managed_streams ms ON ms.id = s.managed_stream_id WHERE s.id = ?",
+ "SELECT s.id, s.user_id, s.started_at FROM streams s WHERE s.is_live = 1 AND EXISTS (SELECT 1 FROM stream_timeline_events e WHERE e.stream_id = s.id AND e.kind = 'speech' AND e.created_at >= datetime('now', '-30 minutes'))",
+ "SELECT s.id, s.user_id, s.title, s.category, s.protocol, s.is_nsfw, s.started_at, s.ended_at, s.duration_seconds, s.managed_stream_id, u.username, u.display_name FROM streams s JOIN users u ON u.id = s.user_id WHERE s.id = ?",
+ "SELECT s.id, u.stream_key FROM streams s JOIN users u ON s.user_id = u.id WHERE s.is_live = 1 AND s.protocol = 'jsmpeg'",
+ "SELECT seq, tbl, op, pk FROM chat_staged_outbox ORDER BY seq LIMIT ?",
+ "SELECT seq, tbl, op, pk FROM chat_staged_outbox WHERE tbl = ? ORDER BY seq LIMIT ?",
+ "SELECT service_user_id FROM linked_accounts WHERE service = 'network' AND user_id = ?",
+ "SELECT session_id, managed_stream_id, stream_id, state, confirmed_at FROM openre_sessions WHERE state = 'live'",
+ "SELECT slot, item_id FROM user_equipped WHERE user_id = ?",
+ "SELECT slot_clip_notify_enabled FROM managed_streams WHERE id = ?",
+ "SELECT slot_clip_recording_enabled FROM managed_streams WHERE id = ?",
+ "SELECT slot_powerchat_count_rs_views FROM managed_streams WHERE id = ?",
+ "SELECT slot_powerchat_relay FROM managed_streams WHERE id = ?",
+ "SELECT slot_vod_recording_enabled FROM managed_streams WHERE id = ?",
+ "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name='chat_messages' AND sql IS NOT NULL",
+ "SELECT sql FROM sqlite_master WHERE type='table' AND name='channel_sounds'",
+ "SELECT sql FROM sqlite_master WHERE type='table' AND name='chat_messages'",
+ "SELECT sql FROM sqlite_master WHERE type='table' AND name='emotes'",
+ "SELECT sql FROM sqlite_master WHERE type='table' AND name='stream_controls'",
+ "SELECT started_at, is_live FROM streams WHERE id = ?",
+ "SELECT status, COUNT(*) AS n FROM billing_actions GROUP BY status",
+ "SELECT stream_id, json, ai, created_at FROM stream_recaps WHERE user_id = ? ORDER BY created_at DESC LIMIT ?",
+ "SELECT streamer_id FROM follows WHERE follower_id = ?",
+ "SELECT strftime('%s', sampled_at) AS t, viewers, live_streams FROM viewer_samples WHERE sampled_at >= datetime('now', ?) ORDER BY sampled_at ASC",
+ "SELECT subject_id FROM linked_accounts WHERE service = 'network' AND user_id = ?",
+ "SELECT subject_id FROM linked_accounts WHERE user_id = ? AND service = 'network' AND subject_id IS NOT NULL ORDER BY id DESC LIMIT 1",
+ "SELECT subject_id, service_user_id FROM linked_accounts WHERE service = 'network' AND user_id = ?",
+ "SELECT t.*, fu.username AS from_username, fu.display_name AS from_display, tu.username AS to_username, tu.display_name AS to_display FROM transactions t LEFT JOIN users fu ON t.from_user_id = fu.id LEFT JOIN users tu ON t.to_user_id = tu.id WHERE t.from_user_id = ? OR t.to_user_id = ? ORDER BY t.created_at DESC LIMIT ?",
+ "SELECT t.*, u.id as uid, u.username, u.display_name, u.role, u.profile_color, u.avatar_url, u.is_banned, u.ban_reason FROM api_tokens t JOIN users u ON t.user_id = u.id WHERE t.token_hash = ? AND t.is_active = 1",
+ "SELECT t.*, u.username as author_name FROM themes t LEFT JOIN users u ON t.author_id = u.id WHERE t.id = ?",
+ "SELECT t.*, u.username as author_name FROM themes t LEFT JOIN users u ON t.author_id = u.id WHERE t.is_public = 1",
+ "SELECT t.*, u.username as author_name FROM themes t LEFT JOIN users u ON t.author_id = u.id WHERE t.slug = ?",
+ "SELECT t.*, u.username, u.display_name FROM transactions t JOIN users u ON t.from_user_id = u.id WHERE t.type = 'cashout' AND t.status = 'escrow' ORDER BY t.created_at ASC",
+ "SELECT t.amount, t.created_at, fu.username AS from_username, fu.display_name AS from_display, tu.username AS to_username, tu.display_name AS to_display FROM transactions t LEFT JOIN users fu ON fu.id = t.from_user_id JOIN users tu ON tu.id = t.to_user_id WHERE t.type = 'donation' ORDER BY t.id DESC LIMIT 1",
+ "SELECT tag_id FROM user_equipped_tag WHERE user_id = ?",
+ "SELECT tag_id, source, granted_at FROM user_tags WHERE user_id = ?",
+ "SELECT target_user_id, COUNT(*) AS n FROM arena_mic_moments WHERE user_id = ? AND target_user_id IS NOT NULL GROUP BY target_user_id ORDER BY n DESC LIMIT 5",
+ "SELECT text FROM arena_mic_moments WHERE user_id = ? AND created_at >= datetime('now', '-6 hours') ORDER BY id DESC LIMIT 40",
+ "SELECT text FROM stream_timeline_events WHERE user_id = ? AND kind = 'speech' AND LENGTH(text) BETWEEN 30 AND 160 ORDER BY created_at DESC LIMIT 60",
+ "SELECT text FROM translations WHERE key = ?",
+ "SELECT text, quality, kind, aimed_at, sec, vod_id FROM arena_mic_moments WHERE stream_id = ? ORDER BY quality DESC, said_at ASC LIMIT 4",
+ "SELECT text, start_sec, vod_id FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' AND LENGTH(text) > 15 ORDER BY start_sec DESC LIMIT 5",
+ "SELECT text, start_sec, vod_id FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' AND start_sec > ? ORDER BY start_sec ASC LIMIT 100",
+ "SELECT text, start_sec, vod_id FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' ORDER BY start_sec DESC LIMIT 14",
+ "SELECT thread_id, access_id FROM comment_thread_refs WHERE content_type = ? AND content_id = ?",
+ "SELECT thumbnail_url FROM streams WHERE id = ?",
+ "SELECT thumbnail_url FROM streams WHERE user_id = ? AND thumbnail_url LIKE 'http%' ORDER BY started_at DESC LIMIT 3",
+ "SELECT title, category, is_live, is_nsfw FROM streams WHERE user_id = ? ORDER BY id DESC LIMIT 1",
+ "SELECT transcript_attempts AS a FROM clip_ai_state WHERE clip_id = ?",
+ "SELECT transcript_attempts AS a FROM vod_ai_state WHERE vod_id = ?",
+ "SELECT transcript_partial_json, transcript_progress_sec FROM vod_ai_state WHERE vod_id = ?",
+ "SELECT u.id AS user_id FROM users u LEFT JOIN streamer_overviews o ON o.user_id = u.id WHERE EXISTS (SELECT 1 FROM stream_memories m WHERE m.user_id = u.id) AND ( o.user_id IS NULL OR (LENGTH(TRIM(COALESCE(o.overview,''))) >= ? AND o.generated_at <= datetime('now','-12 hours')) OR (LENGTH(TRIM(COALESCE(o.overview,''))) < ? AND o.generated_at <= datetime('now','-1 hours')) ) ORDER BY (o.generated_at IS NULL) DESC, o.generated_at ASC LIMIT ?",
+ "SELECT u.id AS user_id, u.username, u.display_name, u.avatar_url, u.profile_color, COUNT(s.id) AS sessions, MAX(s.started_at) AS last_at, MAX(COALESCE(s.peak_viewers, 0)) AS peak_viewers, ROUND(SUM(COALESCE(s.duration_seconds, CASE WHEN s.ended_at IS NOT NULL THEN (julianday(s.ended_at) - julianday(s.started_at)) * 86400 ELSE 0 END)) / 3600.0, 1) AS hours, (SELECT title FROM streams t WHERE t.user_id = u.id AND t.started_at >= ? ORDER BY t.started_at DESC LIMIT 1) AS last_title FROM streams s JOIN users u ON u.id = s.user_id WHERE s.started_at >= ? AND s.is_live = 0 AND COALESCE(u.is_banned, 0) = 0 GROUP BY u.id ORDER BY hours DESC, last_at DESC LIMIT 14",
+ "SELECT u.id AS user_id, u.username, u.display_name, u.avatar_url, u.profile_color, MAX(s.ended_at) AS last_online_at, o.overview AS ai_overview, o.overview_short AS ai_overview_short, ( SELECT json_group_array(json_object( 'managed_stream_id', ms2.id, 'slug', ms2.slug, 'title', ms2.title, 'protocol', ms2.protocol, 'last_live_at', (SELECT MAX(s2.ended_at) FROM streams s2 WHERE s2.managed_stream_id = ms2.id AND s2.ended_at IS NOT NULL), 'vod_thumbnail', NULL )) FROM managed_streams ms2 WHERE ms2.user_id = u.id AND EXISTS (SELECT 1 FROM streams sx WHERE sx.managed_stream_id = ms2.id AND sx.ended_at IS NOT NULL) ) AS managed_streams_json FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN streamer_overviews o ON o.user_id = u.id WHERE s.is_live = 0 AND s.ended_at IS NOT NULL GROUP BY u.id ORDER BY last_online_at DESC LIMIT ? OFFSET ?",
+ "SELECT u.id AS user_id, u.username, u.display_name, u.avatar_url, u.profile_color, s.title, s.viewer_count, s.started_at FROM streams s JOIN users u ON u.id = s.user_id WHERE s.is_live = 1 AND COALESCE(u.is_banned, 0) = 0 ORDER BY s.viewer_count DESC, s.started_at DESC LIMIT 10",
+ "SELECT u.id, u.username FROM chat_messages c JOIN users u ON c.user_id = u.id WHERE c.timestamp >= datetime('now','-14 days') AND COALESCE(u.is_banned,0)=0 AND COALESCE(c.is_deleted,0)=0 GROUP BY u.id ORDER BY COUNT(*) DESC LIMIT 12",
+ "SELECT u.id, u.username, u.display_name, u.avatar_url, u.profile_color FROM dm_participants p JOIN users u ON u.id = p.user_id WHERE p.conversation_id = ?",
+ "SELECT u.id, u.username, u.display_name, u.avatar_url, u.profile_color, MAX(s.started_at) AS last_live_at, COUNT(s.id) AS sessions, ROUND(SUM(COALESCE(s.duration_seconds, CASE WHEN s.ended_at IS NOT NULL THEN (julianday(s.ended_at) - julianday(s.started_at)) * 86400 ELSE 0 END)) / 3600.0, 1) AS hours, (SELECT COUNT(*) FROM follows f WHERE f.streamer_id = u.id) AS followers, (SELECT COALESCE(ch.ai_category, ch.category) FROM channels ch WHERE ch.user_id = u.id) AS category FROM streams s JOIN users u ON u.id = s.user_id WHERE s.started_at >= datetime('now', ?) AND COALESCE(u.is_banned, 0) = 0 AND u.id != COALESCE(?, -1) GROUP BY u.id HAVING hours >= 0.25 OR sessions >= 2 ORDER BY last_live_at DESC LIMIT 40",
+ "SELECT u.id, u.username, u.display_name, u.avatar_url, u.profile_color, b.created_at as blocked_at FROM dm_blocks b JOIN users u ON u.id = b.blocked_id WHERE b.blocker_id = ? ORDER BY b.created_at DESC",
+ "SELECT u.id, u.username, u.display_name, u.avatar_url, u.role, u.is_banned, u.ban_reason, u.created_at, COUNT(DISTINCT shared.ip_address) as shared_ip_count, GROUP_CONCAT(DISTINCT shared.ip_address) as shared_ips, MAX(shared.created_at) as last_shared_activity FROM ip_log mine JOIN ip_log shared ON mine.ip_address = shared.ip_address AND (shared.user_id IS NOT NULL OR shared.anon_id != ?) LEFT JOIN users u ON shared.user_id = u.id WHERE mine.anon_id = ? GROUP BY COALESCE(shared.user_id, shared.anon_id) ORDER BY shared_ip_count DESC, last_shared_activity DESC",
+ "SELECT u.id, u.username, u.display_name, u.avatar_url, u.role, u.is_banned, u.ban_reason, u.created_at, COUNT(DISTINCT shared.ip_address) as shared_ip_count, GROUP_CONCAT(DISTINCT shared.ip_address) as shared_ips, MAX(shared.created_at) as last_shared_activity FROM ip_log mine JOIN ip_log shared ON mine.ip_address = shared.ip_address AND shared.user_id != ? JOIN users u ON shared.user_id = u.id WHERE mine.user_id = ? GROUP BY shared.user_id ORDER BY shared_ip_count DESC, last_shared_activity DESC",
+ "SELECT u.id, u.username, u.display_name, u.bio, COUNT(s.id) AS sessions, ROUND(SUM(COALESCE(s.duration_seconds, CASE WHEN s.ended_at IS NOT NULL THEN (julianday(s.ended_at) - julianday(s.started_at)) * 86400 ELSE 0 END)) / 3600.0, 1) AS hours, MAX(COALESCE(s.peak_viewers, 0)) AS peak_viewers, ROUND(AVG(COALESCE(s.peak_viewers, 0)), 1) AS avg_peak, MAX(s.started_at) AS last_live_at, (SELECT COUNT(*) FROM chat_messages c WHERE c.stream_id IN (SELECT id FROM streams x WHERE x.user_id = u.id AND x.started_at >= datetime('now', ?)) AND COALESCE(c.is_deleted, 0) = 0) AS chat_lines, (SELECT COUNT(*) FROM follows f WHERE f.streamer_id = u.id AND f.created_at >= datetime('now', ?)) AS new_followers, (SELECT COUNT(*) FROM follows f WHERE f.streamer_id = u.id) AS followers, (SELECT COUNT(*) FROM arena_mic_moments m WHERE m.user_id = u.id AND m.said_at >= datetime('now', ?)) AS mic_moments, (SELECT COALESCE(so.overview_short, so.overview) FROM streamer_overviews so WHERE so.user_id = u.id) AS overview, (SELECT ch.ai_category FROM channels ch WHERE ch.user_id = u.id) AS category FROM users u JOIN streams s ON s.user_id = u.id WHERE s.started_at >= datetime('now', ?) AND COALESCE(u.is_banned, 0) = 0 GROUP BY u.id ORDER BY hours DESC LIMIT 40",
+ "SELECT u.id, u.username, u.display_name, u.role, u.openvibe_bucks_balance, u.is_banned, u.ban_reason, u.created_at, u.last_seen, COALESCE(c.force_vod_recording_disabled, 0) AS force_vod_recording_disabled FROM users u LEFT JOIN channels c ON c.user_id = u.id",
+ "SELECT u.username AS channel, cp.balance AS points FROM channel_points cp JOIN users u ON u.id = cp.streamer_id WHERE cp.user_id = ? AND cp.balance > 0 ORDER BY cp.balance DESC, u.username LIMIT 10",
+ "SELECT u.username FROM username_history h JOIN users u ON u.id = h.user_id WHERE h.old_username = ? COLLATE NOCASE ORDER BY h.id DESC LIMIT 1",
+ "SELECT u.username, COALESCE(so.overview_short, so.overview) AS ov FROM streamer_overviews so JOIN users u ON so.user_id = u.id WHERE COALESCE(u.is_banned,0)=0 AND so.overview IS NOT NULL ORDER BY so.generated_at DESC LIMIT 8",
+ "SELECT u.username, u.display_name FROM easter_egg_solves s JOIN users u ON u.id = s.user_id WHERE s.egg_date = ? ORDER BY s.rowid ASC LIMIT 3",
+ "SELECT u.username, u.display_name, SUM(t.amount) AS total FROM transactions t JOIN users u ON u.id = t.from_user_id WHERE t.type = 'donation' AND ((t.stream_id = ?) OR (t.to_user_id = ? AND t.created_at BETWEEN ? AND ?)) GROUP BY t.from_user_id ORDER BY total DESC LIMIT 1",
+ "SELECT u.username, u.display_name, u.avatar_url, SUM(c.amount) AS total FROM coin_transactions c JOIN users u ON u.id = c.user_id WHERE c.amount > 0 AND c.created_at >= datetime('now', '-7 days') GROUP BY c.user_id ORDER BY total DESC LIMIT 3",
+ "SELECT u.username, u.display_name, u.avatar_url, SUM(t.amount) AS total FROM transactions t JOIN users u ON u.id = t.from_user_id WHERE t.type = 'donation' AND t.created_at >= datetime('now', '-7 days') GROUP BY t.from_user_id ORDER BY total DESC LIMIT 3",
+ "SELECT user_id AS id FROM streams WHERE (started_at >= @from AND started_at < @to) OR (ended_at >= @from AND ended_at < @to) UNION SELECT streamer_id FROM follows WHERE created_at >= @from AND created_at < @to UNION SELECT id FROM users WHERE updated_at >= @from AND updated_at < @to UNION SELECT user_id FROM channels WHERE updated_at >= @from AND updated_at < @to",
+ "SELECT user_id AS k, overview AS v FROM streamer_overviews WHERE overview LIKE '{%\"description\"%'",
+ "SELECT user_id FROM channel_moderators WHERE channel_id = ?",
+ "SELECT user_id FROM linked_accounts WHERE service = 'network' AND (subject_id = ? OR service_user_id = ?) ORDER BY subject_id = ? DESC LIMIT 1",
+ "SELECT user_id FROM linked_accounts WHERE service = 'network' AND service_user_id = ?",
+ "SELECT user_id FROM linked_accounts WHERE service = 'network' AND service_user_id = ? ORDER BY id DESC LIMIT 1",
+ "SELECT user_id FROM linked_accounts WHERE service = 'network' AND subject_id = ? ORDER BY id DESC LIMIT 1",
+ "SELECT user_id FROM linked_accounts WHERE service = 'network' AND subject_id = ? ORDER BY id LIMIT 1",
+ "SELECT user_id FROM streams WHERE id = ?",
+ "SELECT user_id, COUNT(*) AS n FROM arena_mic_moments WHERE target_user_id = ? GROUP BY user_id ORDER BY n DESC LIMIT 5",
+ "SELECT user_id, service_user_id FROM linked_accounts WHERE service = 'network' AND service_user_id GLOB '[0-9]*' ORDER BY user_id",
+ "SELECT user_id, service_user_id FROM linked_accounts WHERE service = 'network' AND subject_id IS NULL AND service_user_id GLOB '[0-9]*' ORDER BY user_id",
+ "SELECT user_id, streamer_id, delta FROM channel_points_log WHERE idempotency_key = ?",
+ "SELECT user_id, title, current_amount, target_amount FROM donation_goals WHERE is_active = 1 AND target_amount > 0 AND user_id IN (?) ORDER BY (CAST(current_amount AS REAL) / target_amount) DESC",
+ "SELECT username FROM users WHERE id = ?",
+ "SELECT valid_after_ms FROM token_revocations WHERE subject_id = ?",
+ "SELECT value FROM app_state WHERE key = ?",
+ "SELECT value FROM site_settings WHERE key = 'bucks_per_usd'",
+ "SELECT value FROM site_settings WHERE key = 'powerchat_site_user_id'",
+ "SELECT value FROM site_settings WHERE key='relay_users_backfilled'",
+ "SELECT viewer_count, chat_messages_5m, recorded_at FROM viewer_snapshots WHERE stream_id = ? ORDER BY recorded_at ASC",
+ "SELECT vk.*, u1.username as created_by_name, u2.username as used_by_name FROM verification_keys vk LEFT JOIN users u1 ON vk.created_by = u1.id LEFT JOIN users u2 ON vk.used_by = u2.id ORDER BY vk.created_at DESC",
+ "SELECT vod_id AS id, s.* FROM vod_ai_state s WHERE (ai_overview IS NULL OR ai_overview = '') AND (ai_overview_short IS NULL OR ai_overview_short = '' OR ai_overview_short LIKE '%…') ORDER BY vod_id DESC LIMIT ?",
+ "SELECT vod_id AS id, s.* FROM vod_ai_state s WHERE ai_transcript_json IS NULL AND (transcript_status IS NULL OR transcript_status IN ('pending','retry')) AND (transcript_next_at IS NULL OR transcript_next_at <= CURRENT_TIMESTAMP) ORDER BY (transcript_status='retry'), vod_id DESC LIMIT ?",
+ "SELECT vod_id FROM stream_timeline_events WHERE stream_id = ? AND vod_id IS NOT NULL LIMIT 1",
+ "SELECT voice, pitch, speed, gap FROM tts_voice_overrides WHERE identity_key = ?",
+ "SELECT winner_user_id FROM arena_beefs WHERE status = 'resolved' AND (a_user_id = ? OR b_user_id = ?) ORDER BY resolved_at DESC LIMIT 20",
+ "SELECT workspace_name, machine_name, extension_version, publisher_id, publisher_label, publisher_vendor, publisher_client_type, publisher_client_name, publisher_client_version, publisher_capabilities_json, publisher_depth FROM vibe_coding_sessions WHERE managed_stream_id = ? ORDER BY COALESCE(last_event_at, created_at) DESC, id DESC LIMIT 1",
+ "SELECT wt.user_id, u.username, u.display_name, SUM(wt.minutes_watched) as total_minutes FROM watch_time wt JOIN users u ON u.id = wt.user_id JOIN streams s ON wt.stream_id = s.id WHERE s.user_id = ? AND s.started_at >= ? GROUP BY wt.user_id ORDER BY total_minutes DESC LIMIT 20",
+ "SELECT xp, level FROM arena_trash_levels WHERE user_id = ?",
+ "UPDATE ai_viewer_threads SET state = 'closed', awaiting = NULL, updated_at = CURRENT_TIMESTAMP WHERE channel_user_id = ? AND state = 'open'",
+ "UPDATE ai_viewer_threads SET state = 'closed', awaiting = NULL, updated_at = CURRENT_TIMESTAMP WHERE channel_user_id = ? AND state = 'open' AND (updated_at < datetime('now', ?) OR turns >= ?)",
+ "UPDATE ai_viewer_threads SET state = 'closed', awaiting = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE api_tokens SET is_active = 0 WHERE id = ? AND user_id = ?",
+ "UPDATE api_tokens SET last_used_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE arena_beefs SET clock_until = ? WHERE id = ?",
+ "UPDATE arena_beefs SET headline = ? WHERE id = ?",
+ "UPDATE arena_beefs SET result_headline = ? WHERE id = ?",
+ "UPDATE arena_beefs SET status = 'resolved', resolution = ?, winner_user_id = ?, resolved_at = CURRENT_TIMESTAMP, on_clock = NULL, clock_until = NULL, feed_json = ?, upset = ?, result_headline = ? WHERE id = ? AND status = 'open'",
+ "UPDATE arena_trash_levels SET best_line = ?, best_line_vod_id = ?, best_line_sec = ?, best_line_score = ? WHERE user_id = ?",
+ "UPDATE billing_actions SET attempts = attempts + 1, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE billing_actions SET live_ref = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND action = 'media_charge'",
+ "UPDATE billing_actions SET status = 'done', http_status = 200, billing_ref = ?, response_json = ?, error = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE billing_actions SET status = ?, http_status = ?, error = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE camera_profiles SET name = ?, onvif_url = ?, username = ?, password_hash = ?, pan_speed = ?, tilt_speed = ?, zoom_speed = ?, is_active = ?, last_connected = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE channel_ai_bots SET msg_count = msg_count + 1, last_active_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE channel_moderation_settings SET sub_only = ? WHERE channel_id = ?",
+ "UPDATE channel_moderation_settings SET tts_max_length = ? WHERE channel_id = ?",
+ "UPDATE channel_points SET balance = balance + ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ? AND streamer_id = ? AND balance >= ?",
+ "UPDATE channel_sounds SET command = ? WHERE channel_owner_id = ? AND command = ?",
+ "UPDATE channel_sounds SET emote_code = ? WHERE channel_owner_id = ? AND command = ?",
+ "UPDATE channel_sounds SET emote_code = ? WHERE channel_owner_id = ? AND emote_code = ?",
+ "UPDATE channels SET ai_category = ? WHERE user_id = ?",
+ "UPDATE channels SET ai_overview_pref = 'hide' WHERE hide_ai_overview = 1 AND (ai_overview_pref IS NULL OR ai_overview_pref = 'auto')",
+ "UPDATE channels SET clips_allow_creator_delete = ? WHERE id = ?",
+ "UPDATE chat_dual_read_stats SET last_mismatch_at = CURRENT_TIMESTAMP, last_mismatch = ? WHERE tbl = ?",
+ "UPDATE chat_messages SET channel_user_id = (SELECT s.user_id FROM streams s WHERE s.id = chat_messages.stream_id) WHERE stream_id IS NOT NULL AND channel_user_id IS NULL",
+ "UPDATE chat_messages SET is_deleted = 1, deleted_by = ?, deleted_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE chat_messages SET is_deleted = 1, deleted_by = ?, deleted_at = CURRENT_TIMESTAMP WHERE is_global = 1 AND timestamp >= ? AND timestamp <= ? AND is_deleted = 0",
+ "UPDATE chat_messages SET is_deleted = 1, deleted_by = ?, deleted_at = CURRENT_TIMESTAMP WHERE stream_id = ? AND timestamp >= ? AND timestamp <= ? AND is_deleted = 0",
+ "UPDATE chat_messages SET message = ?, metadata = ? WHERE id = ?",
+ "UPDATE chat_messages SET metadata = ? WHERE id = ?",
+ "UPDATE clip_ai_state SET ai_overview = ?, ai_overview_short = ?, ai_transcript_json = COALESCE(?, ai_transcript_json) WHERE clip_id = ?",
+ "UPDATE clip_ai_state SET ai_transcript_json = ? WHERE clip_id = ?",
+ "UPDATE clip_ai_state SET clip_notified = 1, clip_notify_at = NULL WHERE clip_id = ?",
+ "UPDATE clip_ai_state SET clip_notify_at = CURRENT_TIMESTAMP WHERE clip_id = ? AND COALESCE(clip_notified,0) = 0",
+ "UPDATE clip_ai_state SET clip_notify_at = datetime('now', ?) WHERE clip_id = ? AND COALESCE(clip_notified,0) = 0",
+ "UPDATE clip_ai_state SET transcript_attempts = COALESCE(transcript_attempts,0)+1 WHERE clip_id = ?",
+ "UPDATE clip_ai_state SET transcript_status='retry', transcript_attempts=0, transcript_next_at=NULL WHERE transcript_status='failed' AND COALESCE(transcript_error,'') NOT LIKE 'media reported%' AND COALESCE(transcript_error,'') NOT LIKE 'no audio stream%'",
+ "UPDATE clip_ai_state SET transcript_status='retry', transcript_next_at=NULL WHERE transcript_status='processing'",
+ "UPDATE clips SET visibility = CASE WHEN is_public = 1 THEN 'public' ELSE 'unlisted' END",
+ "UPDATE coin_redemptions SET status = ?, resolved_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE coin_rewards SET redemption_count = redemption_count + 1 WHERE id = ?",
+ "UPDATE dm_conversations SET is_group = 1 WHERE id = ?",
+ "UPDATE dm_conversations SET name = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE dm_conversations SET updated_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE dm_participants SET last_read_at = CURRENT_TIMESTAMP WHERE conversation_id = ? AND user_id = ?",
+ "UPDATE donation_goals SET current_amount = ? WHERE id = ?",
+ "UPDATE donation_goals SET current_amount = ?, is_active = 0, reached_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE donation_goals SET is_active = 0 WHERE id = ?",
+ "UPDATE linked_accounts SET service = 'network' WHERE service = 'hobotools' AND user_id NOT IN (SELECT user_id FROM linked_accounts WHERE service = 'network')",
+ "UPDATE linked_accounts SET subject_id = ? WHERE service = 'network' AND user_id = ? AND service_user_id = ? AND (subject_id IS NULL OR subject_id != ?)",
+ "UPDATE linked_accounts SET subject_id = ? WHERE service = 'network' AND user_id = ? AND service_user_id = ? AND subject_id IS NULL",
+ "UPDATE managed_streams SET broadcast_settings = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND user_id = ?",
+ "UPDATE managed_streams SET ingest_authority = 'live', updated_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE managed_streams SET ingest_authority = 'openre', openre_stream_id = ?, stream_key = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE managed_streams SET stream_key = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE managed_streams SET streaming_method = 'browser' WHERE protocol = 'webrtc'",
+ "UPDATE managed_streams SET streaming_method = 'cli' WHERE protocol = 'jsmpeg'",
+ "UPDATE managed_streams SET streaming_method = 'rtmp' WHERE protocol = 'rtmp'",
+ "UPDATE media_requests SET queue_position = ? WHERE id = ?",
+ "UPDATE opencoin_admin_grants SET balance = ? WHERE id = ?",
+ "UPDATE openre_sessions SET confirmed_at = CURRENT_TIMESTAMP WHERE session_id = ?",
+ "UPDATE openre_sessions SET state = 'detached', updated_at = CURRENT_TIMESTAMP WHERE session_id = ?",
+ "UPDATE pending_ip_messages SET status = 'approved', reviewed_by = ? WHERE channel_id = ? AND ip_address = ? AND status = 'pending'",
+ "UPDATE pending_ip_messages SET status = 'denied', reviewed_by = ? WHERE channel_id = ? AND ip_address = ? AND status = 'pending'",
+ "UPDATE pending_ip_messages SET status = ?, reviewed_by = ? WHERE id = ?",
+ "UPDATE pending_ip_messages SET status = ?, reviewed_by = ? WHERE id = ? AND channel_id = ?",
+ "UPDATE platform_connections SET access_token = ?, refresh_token = COALESCE(?, refresh_token), token_expires_at = ?, scope = COALESCE(?, scope), updated_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE platform_connections SET platform_user_id = ?, platform_username = ?, channel_url = ?, access_token = ?, refresh_token = COALESCE(?, refresh_token), token_expires_at = ?, scope = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE powerchat_connections SET access_token = ?, refresh_token = COALESCE(?, refresh_token), token_expires_at = ?, scope = COALESCE(?, scope), last_error = NULL, updated_at = CURRENT_TIMESTAMP WHERE user_id = ?",
+ "UPDATE powerchat_connections SET last_error = ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ?",
+ "UPDATE restream_destinations SET consecutive_failures = 0, cooldown_until = NULL, last_error = NULL WHERE id = ?",
+ "UPDATE restream_destinations SET consecutive_failures = ?, last_error = ?, last_failed_at = CURRENT_TIMESTAMP, cooldown_until = datetime('now', ?) WHERE id = ?",
+ "UPDATE restream_destinations SET managed_stream_id = ? WHERE user_id = ? AND managed_stream_id IS NULL",
+ "UPDATE site_settings SET value = '100' WHERE key = 'bucks_per_usd'",
+ "UPDATE site_settings SET value = 'alex' WHERE key = 'powerchat_sandbox_username' AND value = 'n8admin'",
+ "UPDATE site_settings SET value = ? WHERE key = 'powerchat_scopes' AND value IN (?, ?, ?)",
+ "UPDATE site_settings SET value = ? WHERE key = 'powerchat_site_tip_username' AND (value IS NULL OR value = '')",
+ "UPDATE site_settings SET value = ?, updated_at = CURRENT_TIMESTAMP WHERE key = ?",
+ "UPDATE stream_analytics SET clips_created = ? WHERE stream_id = ?",
+ "UPDATE stream_controls SET camera_id = NULL WHERE camera_id = ?",
+ "UPDATE stream_recaps SET announced_at = CURRENT_TIMESTAMP WHERE stream_id = ?",
+ "UPDATE stream_timeline_events AS t SET vod_id = (SELECT s.vod_id FROM stream_timeline_events s WHERE s.stream_id = t.stream_id AND s.vod_id IS NOT NULL LIMIT 1) WHERE t.vod_id IS NULL AND EXISTS (SELECT 1 FROM stream_timeline_events s WHERE s.stream_id = t.stream_id AND s.vod_id IS NOT NULL)",
+ "UPDATE stream_timeline_events SET vod_id = ? WHERE stream_id = ? AND vod_id IS NULL",
+ "UPDATE streamer_overviews SET overview = ? WHERE user_id = ?",
+ "UPDATE streamer_overviews SET overview_short = ? WHERE rowid = ?",
+ "UPDATE streams SET ai_category = ?, ai_tags = ? WHERE id = ?",
+ "UPDATE streams SET ai_overview = ?, ai_overview_short = ? WHERE id = ?",
+ "UPDATE streams SET ai_overview_short = ? WHERE rowid = ?",
+ "UPDATE streams SET ai_title = ? WHERE id = ?",
+ "UPDATE streams SET call_mode = ? WHERE id = ?",
+ "UPDATE streams SET control_config_id = ? WHERE id = ?",
+ "UPDATE streams SET control_config_id = NULL WHERE id = ?",
+ "UPDATE streams SET is_live = 0, ended_at = CURRENT_TIMESTAMP, duration_seconds = CAST((julianday(CURRENT_TIMESTAMP) - julianday(started_at)) * 86400 AS INTEGER) WHERE id = ?",
+ "UPDATE streams SET is_live = 1, started_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE streams SET last_heartbeat = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE streams SET last_heartbeat = CURRENT_TIMESTAMP WHERE id = ? AND is_live = 1",
+ "UPDATE streams SET last_heartbeat = CURRENT_TIMESTAMP WHERE is_live = 1",
+ "UPDATE streams SET managed_stream_id = ? WHERE user_id = ? AND managed_stream_id IS NULL",
+ "UPDATE streams SET managed_stream_id = NULL WHERE managed_stream_id = ?",
+ "UPDATE streams SET started_at = CURRENT_TIMESTAMP, last_heartbeat = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE streams SET tags = ? WHERE id = ?",
+ "UPDATE streams SET thumbnail_url = ? WHERE id = ?",
+ "UPDATE streams SET viewer_count = ?, peak_viewers = MAX(peak_viewers, ?) WHERE id = ?",
+ "UPDATE subscriptions SET status=?, is_active=?, cancel_at_period_end=?, current_period_end=COALESCE(?, current_period_end), updated_at=CURRENT_TIMESTAMP WHERE id=?",
+ "UPDATE subscriptions SET tier=?, provider=?, provider_ref=COALESCE(?, provider_ref), price_cents=?, currency=?, status=?, is_active=?, current_period_end=?, auto_renew=COALESCE(?, auto_renew), cancel_at_period_end=0, updated_at=CURRENT_TIMESTAMP WHERE id=?",
+ "UPDATE themes SET downloads = downloads + 1 WHERE id = ?",
+ "UPDATE themes SET name = ?, mode = ?, description = ?, variables = ?, preview_colors = ?, tags = ?, is_builtin = 1, is_public = 1 WHERE slug = ?",
+ "UPDATE tips_deliveries SET claimed_at = ? WHERE idempotency_key = ? AND state = 'pending' AND claimed_at = ?",
+ "UPDATE tips_deliveries SET state = 'done', response_json = ? WHERE idempotency_key = ?",
+ "UPDATE transactions SET paypal_transaction_id = ? WHERE id = ?",
+ "UPDATE transactions SET status = ? WHERE id = ?",
+ "UPDATE user_themes SET theme_id = ?, custom_variables = ?, is_custom = ? WHERE user_id = ?",
+ "UPDATE users SET avatar_url = ?, avatar_paste_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE users SET bio = ? WHERE id = ?",
+ "UPDATE users SET display_name = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
+ "UPDATE users SET email = NULL WHERE email IS NOT NULL",
+ "UPDATE users SET is_banned = 0, ban_reason = NULL WHERE id = ?",
+ "UPDATE users SET is_banned = 1, ban_reason = ? WHERE id = ?",
+ "UPDATE users SET is_banned = 1, ban_reason = ? WHERE id = ? AND is_banned = 0",
+ "UPDATE users SET is_owner = 1, role = 'admin' WHERE LOWER(username) = ? AND is_owner != 1",
+ "UPDATE users SET openvibe_bucks_balance = openvibe_bucks_balance + ? WHERE id = ?",
+ "UPDATE users SET openvibe_bucks_balance = openvibe_bucks_balance - ? WHERE id = ?",
+ "UPDATE users SET openvibe_bucks_cashout_balance = openvibe_bucks_cashout_balance + ? WHERE id = ?",
+ "UPDATE users SET openvibe_bucks_cashout_balance = openvibe_bucks_cashout_balance - ? WHERE id = ?",
+ "UPDATE users SET openvibe_coins_balance = openvibe_coins_balance + ? WHERE id = ?",
+ "UPDATE users SET openvibe_coins_balance = openvibe_coins_balance - ? WHERE id = ? AND openvibe_coins_balance >= ?",
+ "UPDATE users SET profile_color = ? WHERE id = ?",
+ "UPDATE users SET role = 'admin' WHERE username = ?",
+ "UPDATE users SET role = 'global_mod' WHERE role = 'mod'",
+ "UPDATE users SET role = ? WHERE id = ?",
+ "UPDATE users SET username = ? WHERE id = ?",
+ "UPDATE verification_keys SET status = 'revoked' WHERE id = ? AND status = 'active'",
+ "UPDATE verification_keys SET status = 'used', used_by = ?, used_at = CURRENT_TIMESTAMP WHERE key = ? AND status = 'active'",
+ "UPDATE vibe_coding_sessions SET last_event_at = CURRENT_TIMESTAMP WHERE managed_stream_id = ? AND session_key = ?",
+ "UPDATE vibe_coding_sessions SET status = 'ended', ended_at = CURRENT_TIMESTAMP, last_event_at = CURRENT_TIMESTAMP WHERE managed_stream_id = ? AND session_key = ?",
+ "UPDATE vod_ai_state SET ai_overview = ?, ai_overview_short = ? WHERE vod_id = ?",
+ "UPDATE vod_ai_state SET ai_transcript_json = ?, transcript_partial_json = NULL, transcript_progress_sec = 0 WHERE vod_id = ?",
+ "UPDATE vod_ai_state SET transcript_attempts = COALESCE(transcript_attempts,0)+1 WHERE vod_id = ?",
+ "UPDATE vod_ai_state SET transcript_partial_json = ?, transcript_progress_sec = ? WHERE vod_id = ?",
+ "UPDATE vod_ai_state SET transcript_status='retry', transcript_attempts=0, transcript_next_at=NULL WHERE transcript_status='failed' AND COALESCE(transcript_error,'') NOT LIKE 'media reported%' AND COALESCE(transcript_error,'') NOT LIKE 'no audio stream%'",
+ "UPDATE vod_ai_state SET transcript_status='retry', transcript_next_at=NULL WHERE transcript_status='processing'",
+ "UPDATE vods SET visibility = CASE WHEN is_public = 1 THEN 'public' ELSE 'private' END",
+ "UPDATE watch_time SET coins_earned = coins_earned + ? WHERE id = ?",
+ "UPDATE watch_time SET minutes_watched = minutes_watched + 1, last_heartbeat = CURRENT_TIMESTAMP WHERE id = ?"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,136 @@
@@ -0,0 +1,136 @@
+'use strict';
+/**
+ * N-1 (roadmap WS-P task 11, ADR-016): during a deploy, and for the 24-hour mixed-version window after
+ * it, open tabs run the previous release's client against this release's server, and a previous-release
+ * process can still be working on the database this release migrated. Both must keep working.
+ *
+ * - An N-1 client against the N server: test/fixtures/n-1/client.json is every call the previous
+ * release's client code makes (found statically, made concrete with seeded values) with what that
+ * release answered: status, JSON or not, and the response fields the old client reads with their
+ * types. This checkout boots (test/n-1/service.js) and must answer each call compatibly: any 2xx
+ * for a 2xx, the same status otherwise (a 5xx only needs the route), JSON where there was JSON,
+ * every read field still there with a type it had. The run sits inside openvibe-shared's
+ * mixed-version matrix (release-compat assertMixedVersion) with both releases' manifests.
+ * - N-1 SQL against the N schema: test/fixtures/n-1/worker.json holds the previous release's schema
+ * (and migration ledger, and what it creates on first use) and every statement it ran or has as a
+ * literal. The database this test boots on is created from that schema, so this release's
+ * migrations run over it; afterwards every N-1 statement must still prepare (no table or column it
+ * names is gone), and no N-1 INSERT may miss a column this release made NOT NULL without a default.
+ *
+ * The fixtures come from the release in production: `npm run n-1:record` after each deploy (see
+ * scripts/n-1-record.js). This test needs no git history and no network.
+ *
+ * node test/n-1.test.js
+ */
+const assert = require('assert');
+const fs = require('fs');
+const os = require('os');
+const path = require('path');
+const Database = require('better-sqlite3');
+const compat = require('openvibe-shared/release-compat');
+const h = require('./n-1/harness');
+const svc = require('./n-1/service');
+
+const ROOT = path.join(__dirname, '..');
+const FIX = path.join(__dirname, 'fixtures', 'n-1');
+const client = JSON.parse(fs.readFileSync(path.join(FIX, 'client.json'), 'utf8'));
+const worker = JSON.parse(fs.readFileSync(path.join(FIX, 'worker.json'), 'utf8'));
+
+let failures = 0;
+async function check(name, fn) {
+ try { await fn(); console.log(` ✓ ${name}`); } catch (err) { failures++; console.log(` ✗ ${name}\n ${String(err.message).split('\n').join('\n ')}`); }
+}
+
+(async () => {
+ const age = Math.round((Date.now() - Date.parse(client.recorded_at)) / 86400000);
+ console.log(`n-1: ${svc.service} N-1 = ${client.release.slice(0, 12)}, recorded ${client.recorded_at.slice(0, 10)} (${age} d ago); ${client.calls.length} calls, ${worker.statements.length} statements`);
+
+ console.log('n-1: the harness');
+ await check('finds call sites, their method and template, and skips computed paths', () => {
+ const files = [{ name: 'a.js', text: "api('/streams'); api(`/users/${encodeURIComponent(u.username)}/follow`, { method: 'POST', body: {} }); fetch(`${API}/api/x?limit=${n}`); api(path); fetch('https://elsewhere/x'); api(`/b/${on ? 'mute' : 'unmute'}`, { method: 'post' });" }];
+ const { calls, dynamic } = h.extractCalls({ files, callers: [{ name: 'api', prefix: '/api' }, { name: 'fetch' }], strip: ['API'] });
+ assert.deepStrictEqual(calls.map((c) => `${c.method} ${c.template}`), ['POST /api/b/{on ? \'mute\' : \'unmute\'}', 'GET /api/streams', 'POST /api/users/{encodeURIComponent(u.username)}/follow', 'GET /api/x?limit={n}']);
+ assert.strictEqual(dynamic, 1);
+ const samples = [[/id\)*$/i, '1'], [/name\)*$/i, 'n1star']];
+ assert.deepStrictEqual(calls.map((c) => h.concretePath(c.parts, samples)), ['/api/b/mute', '/api/streams', '/api/users/n1star/follow', '/api/x?limit=']);
+ });
+ await check('reads: the key paths the client names; a gone or retyped one is a problem, an empty list or null parent is not', () => {
+ const ids = h.clientIdentifiers(['x.streams.map((s) => s.title); const { total } = d; s?.viewer_count']);
+ const reads = h.readsOf({ streams: [{ title: 'a', viewer_count: 1, secret: 'k' }], total: 3, other: 1 }, ids);
+ assert.deepStrictEqual(reads, { streams: 'array', 'streams[]': 'object', 'streams[].title': 'string', 'streams[].viewer_count': 'number', total: 'number' });
+ assert.deepStrictEqual(h.readProblems(reads, { streams: [{ title: 'b', viewer_count: 2 }], total: 1 }), []);
+ assert.deepStrictEqual(h.readProblems(reads, { streams: [], total: 0 }), []);
+ assert.deepStrictEqual(h.readProblems(reads, { streams: [{ title: 'b', viewer_count: '2' }] }), ['streams[].viewer_count was number, now string', 'total is gone']);
+ assert.ok(h.statusCompatible(200, 201) && h.statusCompatible(502, 503) && !h.statusCompatible(401, 403) && !h.statusCompatible(502, 404) && !h.statusCompatible(200, 404));
+ });
+ await check('SQL: a dropped column fails the prepare; a new NOT NULL column without a default fails an old INSERT', () => {
+ const d = new Database(':memory:');
+ d.exec('CREATE TABLE t (id INTEGER PRIMARY KEY, a TEXT, b TEXT NOT NULL, c TEXT NOT NULL DEFAULT \'x\')');
+ assert.deepStrictEqual(h.prepareProblems(d, ['SELECT a, gone FROM t']).map((p) => p.error), ['no such column: gone']);
+ assert.deepStrictEqual(h.insertProblems(d, ['INSERT INTO t (a, b) VALUES (?, ?)', 'INSERT INTO t (a) VALUES (?)']).map((p) => p.error), ['t.b is NOT NULL without a default, and N-1 does not set it']);
+ d.close();
+ });
+
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-n-1-test-'));
+ const dbPath = path.join(tmp, 'db', `${svc.service}.db`);
+ const dataDir = path.join(tmp, 'data');
+ fs.mkdirSync(path.dirname(dbPath), { recursive: true });
+ fs.mkdirSync(dataDir, { recursive: true });
+ let server = null;
+ try {
+ console.log(`n-1: this release on a database N-1 created`);
+ await check('N-1\'s schema, ledger and first-use tables load, and this release migrates and seeds it', () => {
+ const d = new Database(dbPath);
+ d.transaction(() => {
+ for (const ddl of worker.schema) d.exec(ddl);
+ for (const [table, rows] of Object.entries(worker.ledger || {})) {
+ for (const row of rows) {
+ const cols = Object.keys(row);
+ d.prepare(`INSERT INTO "${table}" (${cols.map((c) => `"${c}"`).join(', ')}) VALUES (${cols.map(() => '?').join(', ')})`).run(...cols.map((c) => row[c]));
+ }
+ }
+ for (const ddl of worker.lazy || []) d.exec(ddl);
+ })();
+ d.pragma(`user_version = ${Number(worker.user_version) || 0}`);
+ d.close();
+ svc.seed({ dir: ROOT, dbPath, dataDir });
+ });
+ server = await svc.boot({ dir: ROOT, dbPath, dataDir });
+ const manifest = await (await fetch(`${server.url}/release.json`)).json();
+
+ console.log('n-1: the N-1 client against this server');
+ await check(`every call the N-1 client makes is answered compatibly (${client.calls.length} requests, in openvibe-shared's mixed-version matrix)`, async () => {
+ const broken = [];
+ const n1Client = async (url) => {
+ for (const rec of client.calls) {
+ const got = await h.send(url, { method: rec.method, path: rec.path, headers: server.headers(rec.auth), body: rec.method === 'GET' ? undefined : {} });
+ const problems = h.callProblems(rec, got);
+ if (problems.length) broken.push(`${rec.auth} ${rec.method} ${rec.path} (${rec.from.join(', ')}): ${problems.join('; ')}`);
+ }
+ if (svc.checkExtra && client.extra) broken.push(...await svc.checkExtra({ server, url, extra: client.extra }));
+ if (broken.length) throw new Error(`${broken.length} broken:\n${h.summarize(broken)}`);
+ };
+ await compat.assertMixedVersion({ releases: [
+ { name: 'N-1', manifest: client.manifest, client: n1Client },
+ { name: 'N', manifest, server: async () => ({ url: server.url }) },
+ ] });
+ });
+ await server.close();
+ server = null;
+
+ console.log('n-1: N-1\'s SQL on the schema this release migrated');
+ await check(`every statement N-1 runs still prepares (${worker.statements.length}), and none of its INSERTs misses a new required column`, () => {
+ const d = new Database(dbPath, { readonly: true });
+ const problems = [...h.prepareProblems(d, worker.statements), ...h.insertProblems(d, worker.statements)];
+ d.close();
+ assert.deepStrictEqual(problems.map((p) => `${p.error}: ${p.sql.slice(0, 160)}`), [], 'N-1 SQL that breaks on this schema (expand first, contract a release later: ADR-028)');
+ });
+ } finally {
+ if (server) { console.log(server.log().slice(-1500)); await server.close(); }
+ fs.rmSync(tmp, { recursive: true, force: true });
+ }
+
+ if (failures) { console.log(`\nn-1: ${failures} check(s) failed`); process.exit(1); }
+ console.log('\nn-1: all checks passed');
+ process.exit(0);
+})().catch((err) => { console.error(err.stack || err.message); process.exit(1); });
new file mode 100644
new file mode 100644
@@ -0,0 +1,523 @@
@@ -0,0 +1,523 @@
+'use strict';
+/**
+ * N-1 harness (roadmap WS-P task 11, ADR-016's mixed-version window): the previous release's client
+ * against this release's server, and the previous release's SQL against this release's schema.
+ * The same file in Live, Chat, Media and Community; each repository's test/n-1/service.js says what
+ * its clients are, how to boot and seed it, and where its SQL lives.
+ *
+ * scripts/n-1-record.js <ref> at release time: checks out <ref> (the release in production, which
+ * is N-1 for the next deploy) into a temporary worktree, boots it, and
+ * writes test/fixtures/n-1/{client,worker}.json
+ * test/n-1.test.js in npm test and CI: boots this checkout on a database created with
+ * N-1's schema and replays the fixture; no git history, no network
+ *
+ * Client calls are found statically in the N-1 client code (the call sites of `api(...)`, `fetch(...)`
+ * and the service's other wrappers, with a literal or template path), made concrete with sample values,
+ * and replayed against the booted N-1 server: every call anonymously, reads again signed in. What the
+ * fixture keeps per call is what an old client depends on: the status, whether the answer is JSON,
+ * and the response fields it reads (the JSON key paths whose every key the N-1 client code names, with
+ * their types).
+ *
+ * SQL is every statement N-1 prepared while it served those calls, plus every literal SQL string in its
+ * server code that prepares on its own schema. The N-1 schema is its sqlite_master after the replay.
+ */
+const fs = require('fs');
+const os = require('os');
+const path = require('path');
+const { execFileSync } = require('child_process');
+
+// ── Client calls: static extraction ──────────────────────────
+
+/** Reads a JS string/template literal at src[i] (a quote). → { end, parts: [{ text } | { expr }] } or null. */
+function readLiteral(src, i) {
+ const q = src[i];
+ if (q !== '\'' && q !== '"' && q !== '`') return null;
+ const parts = [];
+ let text = '';
+ let j = i + 1;
+ while (j < src.length) {
+ const c = src[j];
+ if (c === '\\') { text += unescapeChar(src[j + 1]); j += 2; continue; }
+ if (c === q) { if (text) parts.push({ text }); return { end: j + 1, parts }; }
+ if (q !== '`' && c === '\n') return null;
+ if (q === '`' && c === '$' && src[j + 1] === '{') {
+ if (text) parts.push({ text });
+ text = '';
+ const close = skipBalanced(src, j + 1, '{', '}');
+ if (close < 0) return null;
+ parts.push({ expr: src.slice(j + 2, close).trim() });
+ j = close + 1;
+ continue;
+ }
+ text += c;
+ j++;
+ }
+ return null;
+}
+
+function unescapeChar(c) {
+ return { n: '\n', t: '\t', r: '\r', '0': '\0' }[c] ?? c ?? '';
+}
+
+/** Index of the bracket closing the one at src[i] (open), skipping strings and templates; -1 if none. */
+function skipBalanced(src, i, open, close) {
+ let depth = 0;
+ for (let j = i; j < src.length; j++) {
+ const c = src[j];
+ if (c === '\'' || c === '"' || c === '`') {
+ const lit = readLiteral(src, j);
+ if (lit) { j = lit.end - 1; continue; }
+ }
+ if (c === open) depth++;
+ else if (c === close) { depth--; if (depth === 0) return j; }
+ }
+ return -1;
+}
+
+/** Top-level comma-separated arguments of a call whose '(' is at src[i]. */
+function callArgs(src, i) {
+ const end = skipBalanced(src, i, '(', ')');
+ if (end < 0) return null;
+ const args = [];
+ let depth = 0; let start = i + 1;
+ for (let j = i + 1; j < end; j++) {
+ const c = src[j];
+ if (c === '\'' || c === '"' || c === '`') { const lit = readLiteral(src, j); if (lit) { j = lit.end - 1; continue; } }
+ if (c === '(' || c === '{' || c === '[') depth++;
+ else if (c === ')' || c === '}' || c === ']') depth--;
+ else if (c === ',' && depth === 0) { args.push(src.slice(start, j).trim()); start = j + 1; }
+ }
+ const last = src.slice(start, end).trim();
+ if (last) args.push(last);
+ return args;
+}
+
+const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+
+/**
+ * Every call site of the service's request functions with a literal or template path.
+ * callers: [{ name: 'api', prefix: '/api', method?: 'POST', methodArg?: 1 }]
+ * strip: template expressions dropped when a path starts with them (e.g. 'API', 'location.origin')
+ * → [{ method, template, parts, from: [file] }], plus { dynamic } calls with no literal path
+ */
+function extractCalls({ files, callers, strip = [], keep = (p) => true }) {
+ const found = new Map();
+ let dynamic = 0;
+ for (const { name: file, text: src } of files) {
+ for (const caller of callers) {
+ const re = new RegExp(`(?<![\\w$.])${esc(caller.name)}\\s*\\(`, 'g');
+ let m;
+ while ((m = re.exec(src))) {
+ const open = m.index + m[0].length - 1;
+ const args = callArgs(src, open);
+ if (!args || !args.length) continue;
+ const lit = /^['"`]/.test(args[0]) ? readLiteral(args[0], 0) : null;
+ if (!lit || lit.end !== args[0].length) { dynamic++; continue; }
+ let parts = lit.parts.slice();
+ while (parts.length && parts[0].expr && strip.includes(parts[0].expr)) parts.shift();
+ if (!parts.length || !parts[0].text || !parts[0].text.startsWith('/') || parts[0].text.startsWith('//')) {
+ if (parts.length && parts[0].expr) dynamic++;
+ continue;
+ }
+ parts = caller.prefix ? [{ text: caller.prefix }, ...parts] : parts;
+ // `/api${path}`: a wrapper passing a path through, not a call.
+ if (parts.some((p, k) => p.expr && k > 0 && parts[k - 1].text != null && !parts.slice(0, k).some((x) => x.text && x.text.includes('?')) && /[A-Za-z0-9]$/.test(parts[k - 1].text))) { dynamic++; continue; }
+ const template = parts.map((p) => (p.text != null ? p.text : `{${p.expr}}`)).join('').replace(/^([^?#]*)\/\//, '$1/');
+ let method = caller.method || 'GET';
+ if (caller.methodArg != null) {
+ const a = args[caller.methodArg];
+ const ml = a && /^['"`]/.test(a) ? readLiteral(a, 0) : null;
+ if (ml && ml.parts.length === 1 && ml.parts[0].text) method = ml.parts[0].text;
+ } else {
+ const opts = args.slice(1).join(',');
+ const mm = /\bmethod\s*:\s*['"`]([A-Za-z]+)['"`]/.exec(opts);
+ if (mm) method = mm[1];
+ }
+ method = method.toUpperCase();
+ if (!keep(template.split('?')[0], method)) continue;
+ const key = `${method} ${template}`;
+ if (!found.has(key)) found.set(key, { method, template, parts, from: new Set() });
+ found.get(key).from.add(file);
+ }
+ }
+ }
+ const calls = [...found.values()].map((c) => ({ ...c, from: [...c.from].sort() }))
+ .sort((a, b) => (a.template < b.template ? -1 : a.template > b.template ? 1 : a.method < b.method ? -1 : 1));
+ return { calls, dynamic };
+}
+
+/**
+ * A concrete path for a template: each ${expr} becomes the first sample whose pattern matches the
+ * expression text, else '1' in the path and '' in the query.
+ */
+function concretePath(parts, samples = []) {
+ let out = '';
+ for (const p of parts) {
+ if (p.text != null) { out += p.text; continue; }
+ const inQuery = out.includes('?');
+ // `${on ? 'mute' : 'unmute'}`: the first branch.
+ const branch = /\?\s*(['"`])([^'"`]*)\1\s*:/.exec(p.expr);
+ if (branch) { out += branch[2]; continue; }
+ const hit = samples.find(([re]) => re.test(p.expr));
+ let v = hit ? hit[1] : (inQuery ? '' : '1');
+ if (typeof v === 'function') v = v(p.expr, inQuery);
+ out += inQuery ? String(v) : encodeURIComponent(String(v)).replace(/%2F/gi, '/');
+ }
+ out = out.replace(/^([^?#]*)\/\/+/, '$1/').replace(/[?&]+$/, '').replace(/\?&+/, '?');
+ return out;
+}
+
+/** Property names the client code reads: `.name`, `?.name`, `['name']`, 'name' literals, destructured names. */
+function clientIdentifiers(texts) {
+ const ids = new Set();
+ for (const t of texts) {
+ for (const m of t.matchAll(/\??\.\s*([A-Za-z_$][\w$]*)/g)) ids.add(m[1]);
+ for (const m of t.matchAll(/['"`]([A-Za-z_$][\w$]*)['"`]/g)) ids.add(m[1]);
+ for (const m of t.matchAll(/\{([^{}()]*)\}\s*(?:=[^=>]|\)\s*=>|\s*\))/g)) {
+ for (const part of m[1].split(',')) {
+ const id = /^\s*(?:\.\.\.)?\s*([A-Za-z_$][\w$]*)/.exec(part);
+ if (id) ids.add(id[1]);
+ }
+ }
+ }
+ return ids;
+}
+
+// ── Response shapes ──────────────────────────────────────────
+
+function typeOf(v) {
+ if (v === null) return 'null';
+ if (Array.isArray(v)) return 'array';
+ return typeof v;
+}
+
+/**
+ * The key paths of a JSON value → { 'a': 'object', 'a.b': 'string', 'list[]': 'object', 'list[].x': 'number' }.
+ * Array elements are merged: a path is present when any element has it; its types are all it takes.
+ */
+function flatten(value, maxDepth = 5) {
+ const out = {};
+ const add = (p, t) => { if (!out[p]) out[p] = new Set(); out[p].add(t); };
+ const walk = (v, prefix, depth) => {
+ if (depth > maxDepth) return;
+ if (Array.isArray(v)) {
+ for (const el of v) { add(`${prefix}[]`, typeOf(el)); walk(el, `${prefix}[]`, depth + 1); }
+ } else if (v && typeof v === 'object') {
+ for (const [k, x] of Object.entries(v)) {
+ const p = prefix ? `${prefix}.${k}` : k;
+ add(p, typeOf(x));
+ walk(x, p, depth + 1);
+ }
+ }
+ };
+ walk(value, '', 0);
+ return out;
+}
+
+/** The key paths of `value` an old client reads: every key along the path is one the client names. */
+function readsOf(value, ids) {
+ const flat = flatten(value);
+ const reads = {};
+ for (const [p, types] of Object.entries(flat)) {
+ const keys = p.split('.').map((k) => k.replace(/\[\]$/g, '')).filter(Boolean);
+ if (!keys.length || !keys.every((k) => ids.has(k))) continue;
+ const t = [...types].filter((x) => x !== 'null').sort();
+ reads[p] = t.length ? t.join('|') : 'null';
+ }
+ return reads;
+}
+
+/** What `reads` (recorded from N-1) finds missing or retyped in `value` (from N). */
+function readProblems(reads, value) {
+ const flat = flatten(value);
+ const problems = [];
+ // A path under an array N answers empty (or null) cannot be read by the old client either.
+ const emptyArray = (p) => {
+ const segs = p.split('.');
+ for (let i = 1; i <= segs.length; i++) {
+ const head = segs.slice(0, i).join('.');
+ if (!head.endsWith('[]')) continue;
+ const arr = head.slice(0, -2);
+ if (!flat[head] && (arr === '' ? Array.isArray(value) : flat[arr] && !flat[arr].has('object'))) return true;
+ }
+ return false;
+ };
+ const nullParent = (p) => {
+ const segs = p.split('.');
+ for (let i = 1; i < segs.length; i++) {
+ const head = segs.slice(0, i).join('.').replace(/\[\]$/, '');
+ if (flat[head] && [...flat[head]].every((t) => t === 'null')) return true;
+ }
+ return false;
+ };
+ for (const [p, want] of Object.entries(reads)) {
+ if (emptyArray(p) || nullParent(p)) continue;
+ const have = flat[p];
+ if (!have) { problems.push(`${p} is gone`); continue; }
+ if (want === 'null') continue;
+ const now = [...have].filter((t) => t !== 'null');
+ if (!now.length) continue;
+ const was = want.split('|');
+ if (!now.some((t) => was.includes(t))) problems.push(`${p} was ${want}, now ${now.join('|')}`);
+ }
+ return problems;
+}
+
+/** Whether N's status keeps N-1's promise: any 2xx for a 2xx, the same code otherwise; a 5xx only needs the route. */
+function statusCompatible(was, now) {
+ if (was >= 200 && was < 300) return now >= 200 && now < 300;
+ if (was >= 500) return now !== 404 && now !== 405;
+ return was === now;
+}
+
+// ── Replaying calls ──────────────────────────────────────────
+
+function kindOf(contentType, text) {
+ const ct = String(contentType || '').toLowerCase();
+ if (ct.includes('text/event-stream')) return 'sse';
+ if (ct.includes('json')) return 'json';
+ if (ct.includes('html')) return 'html';
+ if (!text) return 'empty';
+ if (ct.startsWith('text/')) return 'text';
+ return 'other';
+}
+
+/**
+ * Sends one call. → { status, kind, json, location }. Redirects are not followed (they may leave the
+ * sandbox); an event stream is not read past its headers.
+ */
+async function send(baseUrl, { method, path: p, headers = {}, body }, { timeoutMs = 8000 } = {}) {
+ const ac = new AbortController();
+ const timer = setTimeout(() => ac.abort(), timeoutMs);
+ try {
+ const hasBody = body !== undefined && method !== 'GET' && method !== 'HEAD';
+ const res = await fetch(new URL(p, baseUrl), {
+ method,
+ redirect: 'manual',
+ signal: ac.signal,
+ headers: { accept: 'application/json', ...(hasBody ? { 'content-type': 'application/json' } : {}), ...headers },
+ body: hasBody ? JSON.stringify(body) : undefined,
+ });
+ const ct = res.headers.get('content-type');
+ if (/event-stream/i.test(ct || '')) { ac.abort(); return { status: res.status, kind: 'sse', json: null }; }
+ const text = await res.text();
+ const kind = kindOf(ct, text);
+ let json = null;
+ if (kind === 'json') { try { json = JSON.parse(text); } catch { json = null; } }
+ const loc = res.headers.get('location');
+ return { status: res.status, kind, json, location: loc ? loc.replace(/^https?:\/\/[^/]+/, '') : null };
+ } catch (err) {
+ return { status: 0, kind: 'error', json: null, error: err.name === 'AbortError' ? 'timeout' : err.message };
+ } finally {
+ clearTimeout(timer);
+ }
+}
+
+/**
+ * The request list for a set of extracted calls: every call anonymously, GETs also signed in.
+ * Reads first, then writes (with an empty JSON body), so a write cannot change what a read sees.
+ */
+function requestsFor(calls, samples, { signedIn = true } = {}) {
+ const seen = new Set();
+ const reqs = [];
+ for (const c of calls) {
+ const p = concretePath(c.parts, samples);
+ for (const auth of c.method === 'GET' && signedIn ? ['anon', 'user'] : ['anon']) {
+ const key = `${c.method} ${p} ${auth}`;
+ if (seen.has(key)) continue;
+ seen.add(key);
+ reqs.push({ method: c.method, path: p, auth, from: c.from });
+ }
+ }
+ const rank = (r) => (r.method === 'GET' ? 0 : 1);
+ return reqs.sort((a, b) => rank(a) - rank(b));
+}
+
+/** The failures of one replayed call against its recorded outcome. */
+function callProblems(rec, got) {
+ const problems = [];
+ if (got.status === 0) return [`no answer (${got.error})`];
+ if (!statusCompatible(rec.status, got.status)) problems.push(`answered ${got.status}, N-1 answered ${rec.status}`);
+ else if (rec.kind === 'json' && got.kind !== 'json' && rec.status < 500) problems.push(`answered ${got.kind}, N-1 answered JSON`);
+ if (rec.location && got.location && rec.status >= 300 && rec.status < 400 && got.location.split('?')[0] !== rec.location.split('?')[0]) problems.push(`redirects to ${got.location}, N-1 to ${rec.location}`);
+ if (rec.reads && got.json != null && got.status >= 200 && got.status < 300) problems.push(...readProblems(rec.reads, got.json));
+ return problems;
+}
+
+// ── SQL ──────────────────────────────────────────────────────
+
+const SQL_START = /^\s*(SELECT|INSERT|UPDATE|DELETE|REPLACE|WITH)\b/i;
+
+/** Every plain JS string literal in the files (a template only without ${}), comments skipped. */
+function stringLiterals(files) {
+ const out = [];
+ for (const { text } of files) {
+ for (let i = 0; i < text.length; i++) {
+ const c = text[i];
+ if (c === '/' && text[i + 1] === '/') { const nl = text.indexOf('\n', i); i = nl < 0 ? text.length : nl; continue; }
+ if (c === '/' && text[i + 1] === '*') { const e = text.indexOf('*/', i + 2); i = e < 0 ? text.length : e + 1; continue; }
+ if (c !== '\'' && c !== '"' && c !== '`') continue;
+ const lit = readLiteral(text, i);
+ if (!lit) continue;
+ i = lit.end - 1;
+ if (lit.parts.length === 1 && lit.parts[0].text != null) out.push(lit.parts[0].text);
+ }
+ }
+ return out;
+}
+
+/** The literals that read like one SQL statement (DML). */
+function sqlLiterals(files) {
+ const out = new Set();
+ for (const t of stringLiterals(files)) {
+ const sql = normalizeSql(t);
+ if (SQL_START.test(sql) && /\b(FROM|INTO|SET|VALUES)\b/i.test(sql)) out.add(sql);
+ }
+ return [...out];
+}
+
+/** SQL without its comments (-- to the end of a line, and block comments), outside quoted text. */
+function stripSqlComments(sql) {
+ let out = '';
+ const s = String(sql);
+ for (let i = 0; i < s.length; i++) {
+ const c = s[i];
+ if (c === '\'' || c === '"') {
+ const j = s.indexOf(c, i + 1);
+ const end = j < 0 ? s.length : j + 1;
+ out += s.slice(i, end); i = end - 1; continue;
+ }
+ if (c === '-' && s[i + 1] === '-') { const nl = s.indexOf('\n', i); i = nl < 0 ? s.length : nl - 1; continue; }
+ if (c === '/' && s[i + 1] === '*') { const e = s.indexOf('*/', i + 2); i = e < 0 ? s.length : e + 1; out += ' '; continue; }
+ out += c;
+ }
+ return out;
+}
+
+function normalizeSql(sql) {
+ return stripSqlComments(sql).replace(/\s+/g, ' ').trim();
+}
+
+/**
+ * The DDL N-1 runs itself when it first needs a table or column (a module's CREATE TABLE IF NOT EXISTS,
+ * a boot-time ALTER TABLE … ADD COLUMN) that `db` does not have yet. Each statement is applied to `db`
+ * as it is found to apply; → the applied statements, tables first, in order.
+ */
+function lazyDDL(files, db) {
+ const tables = () => new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((r) => r.name));
+ const columns = (t) => { try { return new Set(db.prepare(`PRAGMA table_xinfo("${t}")`).all().map((c) => c.name.toLowerCase())); } catch { return new Set(); } };
+ const lits = stringLiterals(files).map(stripSqlComments);
+ const applied = [];
+ const apply = (ddl) => { try { db.exec(ddl); applied.push(ddl); return true; } catch { return false; } };
+ for (const text of lits) {
+ for (const m of text.matchAll(/CREATE\s+TABLE\s+IF\s+NOT\s+EXISTS\s+["`]?(\w+)["`]?\s*\(/gi)) {
+ // A rebuild's scratch table (x_new) is the migration's own business, not a table N-1 uses.
+ if (tables().has(m[1]) || /_(new|old|tmp|backup)$/i.test(m[1])) continue;
+ const end = skipBalanced(text, m.index + m[0].length - 1, '(', ')');
+ if (end >= 0) apply(normalizeSql(text.slice(m.index, end + 1)));
+ }
+ }
+ for (const text of lits) {
+ const m = /^\s*ALTER\s+TABLE\s+["`]?(\w+)["`]?\s+ADD\s+(?:COLUMN\s+)?["`]?(\w+)["`]?[^;]*$/i.exec(text.trim().replace(/;$/, ''));
+ if (!m || !tables().has(m[1]) || columns(m[1]).has(m[2].toLowerCase())) continue;
+ apply(normalizeSql(text).replace(/;$/, ''));
+ }
+ return applied;
+}
+
+/** Every file under dir with one of the extensions, as { name (relative to root), text }. */
+function readTree(root, dirs, exts = ['.js']) {
+ const out = [];
+ const walk = (d) => {
+ let entries = [];
+ try { entries = fs.readdirSync(d, { withFileTypes: true }); } catch { return; }
+ for (const e of entries) {
+ const p = path.join(d, e.name);
+ if (e.isDirectory()) { if (e.name !== 'node_modules' && !e.name.startsWith('.')) walk(p); } else if (exts.some((x) => e.name.endsWith(x))) out.push({ name: path.relative(root, p), text: fs.readFileSync(p, 'utf8') });
+ }
+ };
+ for (const d of dirs) {
+ const abs = path.join(root, d);
+ if (fs.existsSync(abs) && fs.statSync(abs).isFile()) out.push({ name: d, text: fs.readFileSync(abs, 'utf8') });
+ else walk(abs);
+ }
+ return out.sort((a, b) => (a.name < b.name ? -1 : 1));
+}
+
+/** The schema as DDL, tables first (no internal or FTS shadow tables), then indexes, triggers and views. */
+function schemaDDL(db) {
+ const shadow = new Set(db.prepare('PRAGMA table_list').all().filter((t) => t.type === 'shadow').map((t) => t.name));
+ const rows = db.prepare("SELECT type, name, sql FROM sqlite_master WHERE sql IS NOT NULL AND name NOT LIKE 'sqlite_%'").all()
+ .filter((r) => !shadow.has(r.name));
+ const order = { table: 0, index: 1, view: 2, trigger: 3 };
+ return rows.sort((a, b) => order[a.type] - order[b.type] || (a.name < b.name ? -1 : 1)).map((r) => r.sql);
+}
+
+/** Prepares every statement on db (a missing table or column fails the prepare). → [{ sql, error }] */
+function prepareProblems(db, statements) {
+ const problems = [];
+ for (const sql of statements) {
+ try { db.prepare(sql); } catch (e) { problems.push({ sql, error: e.message }); }
+ }
+ return problems;
+}
+
+/**
+ * INSERTs of N-1 that N would refuse: a column N made NOT NULL without a default that the INSERT does
+ * not name. → [{ sql, error }]
+ */
+function insertProblems(db, statements) {
+ const problems = [];
+ const cols = new Map();
+ const required = (table) => {
+ if (!cols.has(table)) {
+ let info = [];
+ try { info = db.prepare(`PRAGMA table_xinfo("${table.replace(/"/g, '""')}")`).all(); } catch { info = []; }
+ const pkInt = info.filter((c) => c.pk).length === 1 && info.find((c) => c.pk && /^INTEGER$/i.test(c.type));
+ cols.set(table, info.filter((c) => c.notnull && c.dflt_value == null && !c.hidden && !(pkInt && c.pk)).map((c) => c.name));
+ }
+ return cols.get(table);
+ };
+ for (const sql of statements) {
+ const m = /^\s*(?:INSERT|REPLACE)(?:\s+OR\s+\w+)?\s+INTO\s+["`]?(\w+)["`]?\s*\(([^)]*)\)\s*(VALUES|SELECT)/i.exec(sql);
+ if (!m) continue;
+ const named = new Set(m[2].split(',').map((c) => c.trim().replace(/^["`]|["`]$/g, '').toLowerCase()));
+ const missing = required(m[1]).filter((c) => !named.has(c.toLowerCase()));
+ if (missing.length) problems.push({ sql, error: `${m[1]}.${missing.join(', ')} is NOT NULL without a default, and N-1 does not set it` });
+ }
+ return problems;
+}
+
+// ── Git ──────────────────────────────────────────────────────
+
+/** A detached worktree of `ref` in a temp directory sharing root's node_modules → { dir, sha, remove() }. */
+function worktree(root, ref) {
+ const sha = execFileSync('git', ['-C', root, 'rev-parse', '--verify', `${ref}^{commit}`], { encoding: 'utf8' }).trim();
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-n-1-'));
+ const dir = path.join(tmp, 'release');
+ execFileSync('git', ['-C', root, 'worktree', 'add', '--detach', dir, sha], { stdio: 'ignore' });
+ fs.symlinkSync(path.join(root, 'node_modules'), path.join(dir, 'node_modules'), 'dir');
+ return {
+ dir, sha,
+ remove() {
+ try { execFileSync('git', ['-C', root, 'worktree', 'remove', '--force', dir], { stdio: 'ignore' }); } catch { /* */ }
+ fs.rmSync(tmp, { recursive: true, force: true });
+ },
+ };
+}
+
+// ── Reporting ────────────────────────────────────────────────
+
+function summarize(problems, max = 40) {
+ const lines = problems.slice(0, max);
+ if (problems.length > max) lines.push(`… and ${problems.length - max} more`);
+ return lines.join('\n');
+}
+
+module.exports = {
+ readLiteral, callArgs, extractCalls, concretePath, clientIdentifiers,
+ flatten, readsOf, readProblems, statusCompatible,
+ send, requestsFor, callProblems,
+ stringLiterals, sqlLiterals, stripSqlComments, normalizeSql, lazyDDL, readTree, schemaDDL, prepareProblems, insertProblems,
+ worktree, summarize,
+};
new file mode 100644
new file mode 100644
@@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
+'use strict';
+/**
+ * Loaded with `node -r` into a release booted by test/n-1/service.js (N-1 at record time, N in the
+ * test), from that release's directory:
+ * - N1_SQL_OUT: every SQL text the process prepares or executes on the main database (DB_PATH) is
+ * written there as JSON when it exits;
+ * - the restore-drill sandbox (LIVE_DRILL: no outbound connection, program, listener or timer) is
+ * kept, but its read-only guard is lifted, so the old client's writes reach their routes.
+ */
+const fs = require('fs');
+const path = require('path');
+
+const cwd = process.cwd();
+const out = process.env.N1_SQL_OUT;
+if (out) {
+ const Database = require(path.join(cwd, 'node_modules', 'better-sqlite3'));
+ const main = path.resolve(process.env.DB_PATH || '');
+ const seen = new Set();
+ const note = (db, sql) => { try { if (path.resolve(db.name) === main) seen.add(String(sql)); } catch { /* */ } };
+ const prepare = Database.prototype.prepare;
+ Database.prototype.prepare = function (sql, ...rest) { note(this, sql); return prepare.call(this, sql, ...rest); };
+ const exec = Database.prototype.exec;
+ Database.prototype.exec = function (sql, ...rest) { note(this, sql); return exec.call(this, sql, ...rest); };
+ process.on('exit', () => { try { fs.writeFileSync(out, JSON.stringify([...seen])); } catch { /* */ } });
+}
+
+const drill = require(path.join(cwd, 'server', 'drill'));
+drill.readOnly = (req, res, next) => next();
new file mode 100644
new file mode 100644
@@ -0,0 +1,128 @@
@@ -0,0 +1,128 @@
+'use strict';
+/**
+ * Live's side of the N-1 harness (test/n-1/harness.js): its clients, how a release boots and is
+ * seeded, where its SQL lives. Used by scripts/n-1-record.js (on N-1, in a temporary worktree) and by
+ * test/n-1.test.js (on this checkout), so both boot and seed the same way.
+ *
+ * A release boots as a real `node server/index.js` in the restore-drill sandbox (LIVE_DRILL: loopback
+ * only, no outbound connection, program, listener or background job; Media and Community look down),
+ * with test/n-1/preload.js lifting the drill's read-only guard so writes reach their routes. Sign-in is
+ * a Network RS256 token signed with a key made for the run (DATA_DIR/keys).
+ */
+const crypto = require('crypto');
+const fs = require('fs');
+const net = require('net');
+const path = require('path');
+const { spawn, spawnSync } = require('child_process');
+const { readTree } = require('./harness');
+
+const PRELOAD = path.join(__dirname, 'preload.js');
+const ISSUER = 'https://openvibe.network';
+
+// Served by OpenVibe.Chat on openvibe.live (Chat's deploy/nginx/openvibe.live-chat.locations.conf):
+// Live's chat widget and messenger are Chat's N-1 clients, checked there.
+const CHAT_PATHS = /^\/(api\/(chat|dm|tts)\/|api\/sounds(\/|$)|ws\/)/;
+
+function freePort() {
+ return new Promise((resolve) => { const s = net.createServer().listen(0, '127.0.0.1', () => { const p = s.address().port; s.close(() => resolve(p)); }); });
+}
+
+/** A clean environment: nothing from the caller's shell reaches the release but PATH and HOME. */
+function baseEnv(extra) {
+ return { PATH: process.env.PATH, HOME: process.env.HOME, TMPDIR: process.env.TMPDIR || '/tmp', NODE_ENV: 'test', ...extra };
+}
+
+const SEED = `
+ console.log = () => {}; console.warn = () => {};
+ const db = require('./server/db/database');
+ db.initDb();
+ const d = db.getDb();
+ const star = Number(db.createUser({ username: 'n1star', display_name: 'N1 Star', password_hash: 'x', stream_key: 'n1starkey' }).lastInsertRowid);
+ const fan = Number(db.createUser({ username: 'n1fan', display_name: 'N1 Fan', password_hash: 'x', stream_key: 'n1fankey' }).lastInsertRowid);
+ d.prepare("UPDATE users SET role = 'streamer', bio = 'Seeded for the N-1 test' WHERE id = ?").run(star);
+ db.ensureChannel(star); db.ensureChannel(fan);
+ d.prepare("INSERT INTO streams (user_id, title, category, protocol, is_live, viewer_count, started_at, last_heartbeat) VALUES (?, 'N-1 live stream', 'tech', 'webrtc', 1, 2, datetime('now', '-10 minutes'), datetime('now'))").run(star);
+ d.prepare("INSERT INTO streams (user_id, title, category, protocol, is_live, started_at, ended_at) VALUES (?, 'N-1 past stream', 'irl', 'webrtc', 0, datetime('now', '-2 days'), datetime('now', '-2 days', '+1 hour'))").run(star);
+ db.followUser(fan, star);
+ d.prepare("INSERT INTO chat_messages (stream_id, user_id, username, message) VALUES (1, ?, 'n1fan', 'hello from N-1')").run(fan);
+ db.close();
+`;
+
+module.exports = {
+ service: 'live',
+
+ /** The browser client: the SPA and the standalone pages. */
+ clientFiles(dir) {
+ return readTree(dir, ['public/js', 'public/fragments', 'public/obs', 'public/index.html', 'public/kiosk.html',
+ 'public/media-player.html', 'public/popout-chat.html', 'public/whip-publisher.html', 'public/banned.html',
+ 'public/live-notify.js', 'public/service-worker.js'], ['.js', '.html']);
+ },
+ callers: [
+ { name: 'api', prefix: '/api' },
+ { name: 'apiSWR', prefix: '/api' },
+ { name: 'dmApi', prefix: '/api/dm' },
+ { name: 'fetch' },
+ { name: 'mpApi' },
+ { name: 'ownerAction', methodArg: 1 },
+ { name: 'patchJson', method: 'PATCH' },
+ { name: 'navigator.sendBeacon', method: 'POST' },
+ ],
+ strip: ['API', 'location.origin', 'window.location.origin'],
+ keep: (pathname) => !CHAT_PATHS.test(pathname),
+ /** Values for template expressions, first match wins: the seeded streamer, ids of seeded rows. */
+ samples: [
+ [/id\)*$/i, '1'],
+ [/offset|cursor|before|after|since/i, '0'],
+ [/limit|count|size|per_?page|max/i, '5'],
+ [/page\)*$/i, '1'],
+ [/user_?name|login|handle|channel|slug|streamer|name\)*$/i, 'n1star'],
+ [/^q$|query|search|term/i, 'n1'],
+ ],
+
+ sqlDirs: ['server'],
+ ledgerTables: ['schema_migrations'],
+
+ /** Seeds a database with the release in `dir` (its initDb migrates first). */
+ seed({ dir, dbPath, dataDir }) {
+ const r = spawnSync(process.execPath, ['-e', SEED], { cwd: dir, encoding: 'utf8', timeout: 120000, env: baseEnv({ DB_PATH: dbPath, DATA_DIR: dataDir }) });
+ if (r.status !== 0) throw new Error(`seeding failed:\n${String(r.stderr || '').slice(-2000)}`);
+ },
+
+ /** Boots the release in `dir` → { url, headers(auth), close() }. */
+ async boot({ dir, dbPath, dataDir, sqlOut = '' }) {
+ const keys = crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } });
+ fs.mkdirSync(path.join(dataDir, 'keys'), { recursive: true });
+ fs.writeFileSync(path.join(dataDir, 'keys', 'openvibe-tools-public.pem'), keys.publicKey);
+ const port = await freePort();
+ const child = spawn(process.execPath, ['-r', PRELOAD, 'server/index.js'], {
+ cwd: dir,
+ env: baseEnv({ LIVE_DRILL: '1', DB_PATH: dbPath, DATA_DIR: dataDir, HOST: '127.0.0.1', PORT: String(port), N1_SQL_OUT: sqlOut }),
+ stdio: ['ignore', 'pipe', 'pipe'],
+ });
+ let log = '';
+ child.stdout.on('data', (c) => { log = (log + c).slice(-20000); });
+ child.stderr.on('data', (c) => { log = (log + c).slice(-20000); });
+ const exited = new Promise((resolve) => child.on('exit', resolve));
+ const url = `http://127.0.0.1:${port}`;
+ for (let i = 0; ; i++) {
+ if (child.exitCode != null) throw new Error(`the release in ${dir} exited while booting:\n${log.slice(-3000)}`);
+ try { const r = await fetch(`${url}/api/ready`); if (r.status === 200) break; } catch { /* not listening yet */ }
+ if (i > 300) { child.kill('SIGKILL'); throw new Error(`the release in ${dir} did not become ready:\n${log.slice(-3000)}`); }
+ await new Promise((r) => setTimeout(r, 100));
+ }
+ const jwt = require('jsonwebtoken');
+ const token = jwt.sign({ sub: 'n1-network-user', username: 'n1star', display_name: 'N1 Star', role: 'streamer' },
+ keys.privateKey, { algorithm: 'RS256', issuer: ISSUER, expiresIn: '1h' });
+ return {
+ url,
+ log: () => log,
+ headers: (auth) => (auth === 'user' ? { authorization: `Bearer ${token}` } : {}),
+ async close() {
+ if (child.exitCode == null) child.kill('SIGTERM');
+ const t = setTimeout(() => { if (child.exitCode == null) child.kill('SIGKILL'); }, 5000);
+ await exited;
+ clearTimeout(t);
+ },
+ };
+ },
+};