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:
+15
-34
@@ -21,11 +21,13 @@ export const devices = sqliteTable("devices", {
|
||||
lastConnectedAt: integer("last_connected_at"),
|
||||
});
|
||||
|
||||
export const playSessions = sqliteTable("play_sessions", {
|
||||
export const sessions = sqliteTable("sessions", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
name: text("name"),
|
||||
description: text("description"),
|
||||
kind: text("kind", { enum: ["live", "replay"] }).notNull(),
|
||||
replayedRecordingId: integer("replayed_recording_id").references((): AnySQLiteColumn => recordings.id, {
|
||||
// Self-referencing: which session's events this session replayed, if any.
|
||||
replayedSessionId: integer("replayed_session_id").references((): AnySQLiteColumn => sessions.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
status: text("status", { enum: ["active", "completed", "aborted"] }).notNull().default("active"),
|
||||
@@ -33,15 +35,20 @@ export const playSessions = sqliteTable("play_sessions", {
|
||||
endedAt: integer("ended_at"),
|
||||
durationMs: integer("duration_ms"),
|
||||
notes: text("notes"),
|
||||
// How many times (and when) this session's events have been replayed -
|
||||
// any completed session can be a replay source, there's no separate
|
||||
// "saved recording" concept anymore.
|
||||
playCount: integer("play_count").notNull().default(0),
|
||||
lastPlayedAt: integer("last_played_at"),
|
||||
});
|
||||
|
||||
export const sessionDevices = sqliteTable(
|
||||
"session_devices",
|
||||
{
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
playSessionId: integer("play_session_id")
|
||||
sessionId: integer("session_id")
|
||||
.notNull()
|
||||
.references(() => playSessions.id, { onDelete: "cascade" }),
|
||||
.references(() => sessions.id, { onDelete: "cascade" }),
|
||||
deviceId: integer("device_id")
|
||||
.notNull()
|
||||
.references(() => devices.id, { onDelete: "restrict" }),
|
||||
@@ -49,16 +56,16 @@ export const sessionDevices = sqliteTable(
|
||||
connectedAt: integer("connected_at").notNull(),
|
||||
disconnectedAt: integer("disconnected_at"),
|
||||
},
|
||||
(table) => [index("session_devices_play_session_id_idx").on(table.playSessionId)],
|
||||
(table) => [index("session_devices_session_id_idx").on(table.sessionId)],
|
||||
);
|
||||
|
||||
export const sessionEvents = sqliteTable(
|
||||
"session_events",
|
||||
{
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
playSessionId: integer("play_session_id")
|
||||
sessionId: integer("session_id")
|
||||
.notNull()
|
||||
.references(() => playSessions.id, { onDelete: "cascade" }),
|
||||
.references(() => sessions.id, { onDelete: "cascade" }),
|
||||
sessionDeviceId: integer("session_device_id")
|
||||
.notNull()
|
||||
.references(() => sessionDevices.id, { onDelete: "cascade" }),
|
||||
@@ -72,33 +79,7 @@ export const sessionEvents = sqliteTable(
|
||||
rawPayload: text("raw_payload", { mode: "json" }),
|
||||
},
|
||||
(table) => [
|
||||
index("session_events_session_ts_idx").on(table.playSessionId, table.tsMs),
|
||||
index("session_events_session_ts_idx").on(table.sessionId, table.tsMs),
|
||||
index("session_events_session_device_idx").on(table.sessionDeviceId),
|
||||
],
|
||||
);
|
||||
|
||||
export interface RecordingDeviceSlot {
|
||||
slotLabel: string;
|
||||
recordedBleName: string;
|
||||
deviceClass: string | null;
|
||||
capabilities: DeviceCapabilities | null;
|
||||
/** The original session_devices.id this slot was captured from - lets the
|
||||
* replay UI map a chosen live device back to this slot's session_events. */
|
||||
sourceSessionDeviceId: number;
|
||||
}
|
||||
|
||||
export const recordings = sqliteTable("recordings", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
// A recording is a thin pointer over an already-captured play_session, not
|
||||
// a duplicated event pipeline - see lib/db/queries/recordings.ts for why.
|
||||
sourcePlaySessionId: integer("source_play_session_id")
|
||||
.notNull()
|
||||
.references(() => playSessions.id, { onDelete: "restrict" }),
|
||||
name: text("name").notNull(),
|
||||
description: text("description"),
|
||||
createdAt: integer("created_at").notNull(),
|
||||
durationMs: integer("duration_ms").notNull(),
|
||||
deviceSlots: text("device_slots", { mode: "json" }).$type<RecordingDeviceSlot[]>().notNull(),
|
||||
playCount: integer("play_count").notNull().default(0),
|
||||
lastPlayedAt: integer("last_played_at"),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user