Remove recordings feature, replay sessions directly, bump to 0.6.0
Recordings were just a thin named pointer over an already-captured session's events, so the whole separate feature (recordings table, API routes, pages, UI) is gone: any completed session can now be named and replayed directly. Replaying no longer creates a session or duplicates events of its own - it just bumps the source session's playCount/lastPlayedAt. Also renames play_sessions/playSession(s) to sessions/session(s) throughout the schema, queries, API routes, and UI for consistency, and updates the README to match the new flow. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,9 +2,10 @@
|
||||
|
||||
A self-hosted web console for scanning, connecting to, and controlling Bluetooth LE sex toys over the
|
||||
[Buttplug](https://buttplug.io) protocol - the same open-source protocol behind Intiface Central and most
|
||||
other legitimate Bluetooth toy-control apps. Record a live control session, save it as a named recording,
|
||||
and replay it later against a (possibly different) set of connected devices. Track usage in a stats
|
||||
dashboard. All gated behind a single shared secret, all data kept in a local SQLite file.
|
||||
other legitimate Bluetooth toy-control apps. Every live control session is recorded automatically, and any
|
||||
completed session can be replayed later directly, against a (possibly different) set of connected devices -
|
||||
no separate "save as recording" step. Track usage in a stats dashboard. All gated behind a single shared
|
||||
secret, all data kept in a local SQLite file.
|
||||
|
||||
## Architecture, in words
|
||||
|
||||
@@ -16,14 +17,14 @@ Bluetooth hardware at all. So:
|
||||
as an *embedded* connector - a full Buttplug server compiled to WebAssembly, talking to devices directly
|
||||
over Web Bluetooth. There's no separate Intiface Engine or Buttplug Server process to run.
|
||||
- **The Next.js server is never in the real-time device-control loop.** It only handles the login gate,
|
||||
persists recordings and session telemetry to SQLite, serves the stats aggregation endpoints, and serves
|
||||
recordings back for replay - all over plain HTTPS/JSON, batched every few seconds, not per slider tick.
|
||||
persists session telemetry to SQLite, serves the stats aggregation endpoints, and serves a session's
|
||||
events back for replay - all over plain HTTPS/JSON, batched every few seconds, not per slider tick.
|
||||
|
||||
```
|
||||
Browser Server (Next.js)
|
||||
┌─────────────────────────┐ ┌───────────────────────┐
|
||||
│ buttplug-wasm (embedded)│ │ auth / API routes │
|
||||
│ ↕ Web Bluetooth │ HTTPS │ recordings + sessions │
|
||||
│ ↕ Web Bluetooth │ HTTPS │ sessions │
|
||||
│ BLE devices │ ───────► │ stats aggregation │
|
||||
│ live control / replay │ (batch) │ SQLite (better-sqlite3)│
|
||||
└─────────────────────────┘ └───────────────────────┘
|
||||
@@ -42,12 +43,12 @@ Bluetooth hardware at all. So:
|
||||
|
||||
- Scan for and connect to multiple Bluetooth LE devices at once
|
||||
- Live per-actuator control (vibrate / rotate / linear) with a responsive slider UI
|
||||
- Start/end sessions; save a completed session as a named, replayable recording
|
||||
- Replay a recording against a different set of connected devices, with a device-remap step (BLE exposes no
|
||||
stable device id across sessions, so recorded devices are matched to live ones by name, with manual
|
||||
override when needed)
|
||||
- Stats dashboard: session summaries, per-session intensity timelines, per-device usage, recording-library
|
||||
stats (play counts, average length)
|
||||
- Start/end sessions; name one so it's easy to find later
|
||||
- Replay any completed session directly against a different set of connected devices, with a device-remap
|
||||
step (BLE exposes no stable device id across sessions, so the session's devices are matched to live ones
|
||||
by name, with manual override when needed) - replaying doesn't create a new session or duplicate events,
|
||||
it just bumps the source session's play count
|
||||
- Stats dashboard: session summaries, per-session intensity timelines, per-device usage, replay counts
|
||||
- Single shared-secret login, no user accounts
|
||||
- SQLite storage, Docker Compose deployment, Gitea Actions CI
|
||||
|
||||
@@ -87,9 +88,15 @@ Compose-only (read by `docker-compose.yml` for `${...}` substitution, not by the
|
||||
## Database
|
||||
|
||||
Schema lives in `lib/db/schema.ts`, migrations in `drizzle/` (generate new ones with `pnpm db:generate`
|
||||
after a schema change, and commit the generated SQL). A recording is a thin pointer over an already-captured
|
||||
session's events, not a separate capture pipeline - every live command is always persisted for stats,
|
||||
regardless of whether the session is later saved as a named recording.
|
||||
after a schema change, and commit the generated SQL). Every live command is always persisted to
|
||||
`session_events` for stats, regardless of whether the session is ever replayed - replaying reads those same
|
||||
events back directly, it doesn't create a new session or duplicate them, it just bumps the source session's
|
||||
`playCount`/`lastPlayedAt`.
|
||||
|
||||
Renaming a table or column makes `drizzle-kit generate` prompt interactively ("is this a rename?"), which
|
||||
needs a real TTY and won't work non-interactively (e.g. from an agent or CI). In that case, hand-author the
|
||||
migration SQL and its `drizzle/meta/*_snapshot.json` instead, then confirm `pnpm db:generate` reports no
|
||||
further changes against the updated schema.
|
||||
|
||||
## Docker deployment
|
||||
|
||||
@@ -116,18 +123,18 @@ Docker image to this repo's path on the `dev.pivoine.art` Gitea registry, authen
|
||||
|
||||
1. Log in with the shared secret.
|
||||
2. On **Control**, scan for devices and connect. Move the sliders to control actuators live.
|
||||
3. Click **Start session** before you begin if you want this session tracked in stats or saved as a
|
||||
recording; **End session** when done, and optionally name it to save it as a recording.
|
||||
4. On **Recordings**, click replay on a saved recording, connect the devices you want to use, match them to
|
||||
the recording's original device slots, and play back.
|
||||
5. **Sessions** and **Stats** show history and aggregated usage.
|
||||
3. Click **Start session** before you begin; **End session** when done, and optionally name it so it's easy
|
||||
to find later.
|
||||
4. On **Sessions**, click replay on any completed session, connect the devices you want to use, match them
|
||||
to the session's original device slots, and play back.
|
||||
5. **Stats** shows aggregated usage across sessions and devices.
|
||||
|
||||
## Security & privacy notes
|
||||
|
||||
- This is a single shared-secret gate, not multi-user authentication - anyone with the secret has full
|
||||
access. Treat it like a shared house key; restricting network access (VPN, IP allowlist, Traefik
|
||||
middleware) in addition to the app secret is recommended for anything beyond trusted personal use.
|
||||
- All data (recordings, session history, device names) stays in your own SQLite file. Nothing is sent
|
||||
- All data (session history, device names) stays in your own SQLite file. Nothing is sent
|
||||
anywhere except directly between your browser and the devices it connects to, and between your browser
|
||||
and this server.
|
||||
- There's no login rate-limiting in this version - acceptable behind a private deployment, but worth
|
||||
|
||||
Reference in New Issue
Block a user