Files
cottongin 6481d74d95 feat: tabbed station libraries, SomaFM integration, and settings panel
Add tabbed playlist UI with SomaFM as a built-in library including live
listener counts, station hiding, and stream quality selection. Implement
settings panel with quality preferences, listening history, and playlist
import/export improvements. Includes DB migrations 1-4, SomaFM seed
data, stream resolver, and now-playing history logging.

Made-with: Cursor
2026-03-10 20:16:09 -04:00

339 lines
10 KiB
JSON

{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "457080c5ca807a6242ac9b38b9878bd9",
"entities": [
{
"tableName": "stations",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `playlistId` INTEGER, `sortOrder` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `defaultArtworkUrl` TEXT, `listenerCount` INTEGER NOT NULL, `isHidden` INTEGER NOT NULL, FOREIGN KEY(`playlistId`) REFERENCES `playlists`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "playlistId",
"columnName": "playlistId",
"affinity": "INTEGER"
},
{
"fieldPath": "sortOrder",
"columnName": "sortOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "starred",
"columnName": "starred",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "defaultArtworkUrl",
"columnName": "defaultArtworkUrl",
"affinity": "TEXT"
},
{
"fieldPath": "listenerCount",
"columnName": "listenerCount",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isHidden",
"columnName": "isHidden",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_stations_playlistId",
"unique": false,
"columnNames": [
"playlistId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_stations_playlistId` ON `${TABLE_NAME}` (`playlistId`)"
}
],
"foreignKeys": [
{
"table": "playlists",
"onDelete": "SET NULL",
"onUpdate": "NO ACTION",
"columns": [
"playlistId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "playlists",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `sortOrder` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `isBuiltIn` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sortOrder",
"columnName": "sortOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "starred",
"columnName": "starred",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isBuiltIn",
"columnName": "isBuiltIn",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
}
},
{
"tableName": "metadata_snapshots",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `stationId` INTEGER NOT NULL, `title` TEXT, `artist` TEXT, `artworkUrl` TEXT, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`stationId`) REFERENCES `stations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "stationId",
"columnName": "stationId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT"
},
{
"fieldPath": "artist",
"columnName": "artist",
"affinity": "TEXT"
},
{
"fieldPath": "artworkUrl",
"columnName": "artworkUrl",
"affinity": "TEXT"
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_metadata_snapshots_stationId",
"unique": false,
"columnNames": [
"stationId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_metadata_snapshots_stationId` ON `${TABLE_NAME}` (`stationId`)"
},
{
"name": "index_metadata_snapshots_timestamp",
"unique": false,
"columnNames": [
"timestamp"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_metadata_snapshots_timestamp` ON `${TABLE_NAME}` (`timestamp`)"
}
],
"foreignKeys": [
{
"table": "stations",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"stationId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "listening_sessions",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `stationId` INTEGER NOT NULL, `startedAt` INTEGER NOT NULL, `endedAt` INTEGER, FOREIGN KEY(`stationId`) REFERENCES `stations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "stationId",
"columnName": "stationId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "startedAt",
"columnName": "startedAt",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "endedAt",
"columnName": "endedAt",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_listening_sessions_stationId",
"unique": false,
"columnNames": [
"stationId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_listening_sessions_stationId` ON `${TABLE_NAME}` (`stationId`)"
}
],
"foreignKeys": [
{
"table": "stations",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"stationId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "connection_spans",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `sessionId` INTEGER NOT NULL, `startedAt` INTEGER NOT NULL, `endedAt` INTEGER, FOREIGN KEY(`sessionId`) REFERENCES `listening_sessions`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "sessionId",
"columnName": "sessionId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "startedAt",
"columnName": "startedAt",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "endedAt",
"columnName": "endedAt",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_connection_spans_sessionId",
"unique": false,
"columnNames": [
"sessionId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_connection_spans_sessionId` ON `${TABLE_NAME}` (`sessionId`)"
}
],
"foreignKeys": [
{
"table": "listening_sessions",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"sessionId"
],
"referencedColumns": [
"id"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '457080c5ca807a6242ac9b38b9878bd9')"
]
}
}