mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 15:29:15 +01:00
persist artwork seeds in the database
This commit is contained in:
@@ -0,0 +1,774 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 4,
|
||||||
|
"identityHash": "9ee801b55a0e0e6a8b240d867c3a4e58",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "sources",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `isActive` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "address",
|
||||||
|
"columnName": "address",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isActive",
|
||||||
|
"columnName": "isActive",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "createdAt",
|
||||||
|
"columnName": "createdAt",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "subsonic_sources",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceId` INTEGER NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `useTokenAuth` INTEGER NOT NULL, PRIMARY KEY(`sourceId`), FOREIGN KEY(`sourceId`) REFERENCES `sources`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "sourceId",
|
||||||
|
"columnName": "sourceId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "username",
|
||||||
|
"columnName": "username",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "password",
|
||||||
|
"columnName": "password",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "useTokenAuth",
|
||||||
|
"columnName": "useTokenAuth",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_subsonic_sources_sourceId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_subsonic_sources_sourceId` ON `${TABLE_NAME}` (`sourceId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "sources",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"sourceId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "artists",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceId` INTEGER NOT NULL, `id` TEXT NOT NULL, `name` TEXT NOT NULL, `albumCount` INTEGER NOT NULL, `starred` INTEGER, `coverArt` TEXT, PRIMARY KEY(`sourceId`, `id`), FOREIGN KEY(`sourceId`) REFERENCES `sources`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "sourceId",
|
||||||
|
"columnName": "sourceId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumCount",
|
||||||
|
"columnName": "albumCount",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "starred",
|
||||||
|
"columnName": "starred",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "coverArt",
|
||||||
|
"columnName": "coverArt",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId",
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_artists_sourceId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_artists_sourceId` ON `${TABLE_NAME}` (`sourceId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "sources",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"sourceId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "albums",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceId` INTEGER NOT NULL, `id` TEXT NOT NULL, `artistId` TEXT, `name` TEXT NOT NULL, `albumArtist` TEXT, `created` INTEGER NOT NULL, `coverArt` TEXT, `genre` TEXT, `year` INTEGER, `starred` INTEGER, `songCount` INTEGER NOT NULL, `frequentRank` INTEGER, `recentRank` INTEGER, PRIMARY KEY(`sourceId`, `id`), FOREIGN KEY(`sourceId`) REFERENCES `sources`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "sourceId",
|
||||||
|
"columnName": "sourceId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistId",
|
||||||
|
"columnName": "artistId",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumArtist",
|
||||||
|
"columnName": "albumArtist",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "created",
|
||||||
|
"columnName": "created",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "coverArt",
|
||||||
|
"columnName": "coverArt",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "genre",
|
||||||
|
"columnName": "genre",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "year",
|
||||||
|
"columnName": "year",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "starred",
|
||||||
|
"columnName": "starred",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "songCount",
|
||||||
|
"columnName": "songCount",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "frequentRank",
|
||||||
|
"columnName": "frequentRank",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "recentRank",
|
||||||
|
"columnName": "recentRank",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId",
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_albums_sourceId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_albums_sourceId` ON `${TABLE_NAME}` (`sourceId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_albums_sourceId_artistId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId",
|
||||||
|
"artistId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_albums_sourceId_artistId` ON `${TABLE_NAME}` (`sourceId`, `artistId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "sources",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"sourceId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "playlists",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceId` INTEGER NOT NULL, `id` TEXT NOT NULL, `name` TEXT NOT NULL, `comment` TEXT, `coverArt` TEXT, `songCount` INTEGER NOT NULL, `created` INTEGER NOT NULL, PRIMARY KEY(`sourceId`, `id`), FOREIGN KEY(`sourceId`) REFERENCES `sources`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "sourceId",
|
||||||
|
"columnName": "sourceId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "comment",
|
||||||
|
"columnName": "comment",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "coverArt",
|
||||||
|
"columnName": "coverArt",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "songCount",
|
||||||
|
"columnName": "songCount",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "created",
|
||||||
|
"columnName": "created",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId",
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_playlists_sourceId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_playlists_sourceId` ON `${TABLE_NAME}` (`sourceId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "sources",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"sourceId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "playlist_songs",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceId` INTEGER NOT NULL, `playlistId` TEXT NOT NULL, `songId` TEXT NOT NULL, `position` INTEGER NOT NULL, PRIMARY KEY(`sourceId`, `playlistId`, `position`), FOREIGN KEY(`sourceId`) REFERENCES `sources`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "sourceId",
|
||||||
|
"columnName": "sourceId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistId",
|
||||||
|
"columnName": "playlistId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "songId",
|
||||||
|
"columnName": "songId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "position",
|
||||||
|
"columnName": "position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId",
|
||||||
|
"playlistId",
|
||||||
|
"position"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_playlist_songs_sourceId_playlistId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId",
|
||||||
|
"playlistId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_playlist_songs_sourceId_playlistId` ON `${TABLE_NAME}` (`sourceId`, `playlistId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_playlist_songs_sourceId_songId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId",
|
||||||
|
"songId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_playlist_songs_sourceId_songId` ON `${TABLE_NAME}` (`sourceId`, `songId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "sources",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"sourceId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "songs",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceId` INTEGER NOT NULL, `id` TEXT NOT NULL, `albumId` TEXT, `artistId` TEXT, `title` TEXT NOT NULL, `album` TEXT, `artist` TEXT, `duration` INTEGER, `track` INTEGER, `disc` INTEGER, `starred` INTEGER, `genre` TEXT, PRIMARY KEY(`sourceId`, `id`), FOREIGN KEY(`sourceId`) REFERENCES `sources`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "sourceId",
|
||||||
|
"columnName": "sourceId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumId",
|
||||||
|
"columnName": "albumId",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistId",
|
||||||
|
"columnName": "artistId",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "album",
|
||||||
|
"columnName": "album",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artist",
|
||||||
|
"columnName": "artist",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "track",
|
||||||
|
"columnName": "track",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "disc",
|
||||||
|
"columnName": "disc",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "starred",
|
||||||
|
"columnName": "starred",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "genre",
|
||||||
|
"columnName": "genre",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId",
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_songs_sourceId_albumId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId",
|
||||||
|
"albumId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_songs_sourceId_albumId` ON `${TABLE_NAME}` (`sourceId`, `albumId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_songs_sourceId_artistId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId",
|
||||||
|
"artistId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_songs_sourceId_artistId` ON `${TABLE_NAME}` (`sourceId`, `artistId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "sources",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"sourceId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "search_index",
|
||||||
|
"createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS5(`sourceId` UNINDEXED, `type` UNINDEXED, `itemId` UNINDEXED, `title`, tokenize=`trigram`)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "sourceId",
|
||||||
|
"columnName": "sourceId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "itemId",
|
||||||
|
"columnName": "itemId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"rowid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ftsVersion": "FTS5",
|
||||||
|
"ftsOptions": {
|
||||||
|
"tokenizer": "trigram",
|
||||||
|
"tokenizerArgs": [],
|
||||||
|
"contentTable": "",
|
||||||
|
"languageIdColumnName": "",
|
||||||
|
"matchInfo": "FTS4",
|
||||||
|
"notIndexedColumns": [
|
||||||
|
"sourceId",
|
||||||
|
"type",
|
||||||
|
"itemId"
|
||||||
|
],
|
||||||
|
"prefixSizes": [],
|
||||||
|
"preferredOrder": "ASC",
|
||||||
|
"contentRowId": "",
|
||||||
|
"columnSize": true,
|
||||||
|
"detail": "FULL"
|
||||||
|
},
|
||||||
|
"contentSyncTriggers": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "queue_entries",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `position` INTEGER NOT NULL, `sourceId` INTEGER NOT NULL, `kind` TEXT NOT NULL, `refId` TEXT NOT NULL, `rangeStart` INTEGER, `rangeEnd` INTEGER, FOREIGN KEY(`sourceId`) REFERENCES `sources`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "position",
|
||||||
|
"columnName": "position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "sourceId",
|
||||||
|
"columnName": "sourceId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "kind",
|
||||||
|
"columnName": "kind",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "refId",
|
||||||
|
"columnName": "refId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "rangeStart",
|
||||||
|
"columnName": "rangeStart",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "rangeEnd",
|
||||||
|
"columnName": "rangeEnd",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_queue_entries_sourceId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"sourceId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_queue_entries_sourceId` ON `${TABLE_NAME}` (`sourceId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_queue_entries_position",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"position"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_queue_entries_position` ON `${TABLE_NAME}` (`position`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "sources",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"sourceId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "playback_cursor",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `queuePosition` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "queuePosition",
|
||||||
|
"columnName": "queuePosition",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "artwork_seeds",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`cacheKey` TEXT NOT NULL, `primary` INTEGER NOT NULL, `secondary` INTEGER, `updatedAt` INTEGER NOT NULL, PRIMARY KEY(`cacheKey`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "cacheKey",
|
||||||
|
"columnName": "cacheKey",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "primary",
|
||||||
|
"columnName": "primary",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "secondary",
|
||||||
|
"columnName": "secondary",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "updatedAt",
|
||||||
|
"columnName": "updatedAt",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"cacheKey"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_artwork_seeds_updatedAt",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"updatedAt"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_artwork_seeds_updatedAt` ON `${TABLE_NAME}` (`updatedAt`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, '9ee801b55a0e0e6a8b240d867c3a4e58')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,9 @@ import android.app.Application
|
|||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
import coil3.SingletonImageLoader
|
import coil3.SingletonImageLoader
|
||||||
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
||||||
|
import com.subtracks.data.repo.ArtworkSeedStore
|
||||||
import com.subtracks.di.appModule
|
import com.subtracks.di.appModule
|
||||||
|
import com.subtracks.ui.theme.ArtworkSeedCache
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
import org.koin.core.context.stopKoin
|
import org.koin.core.context.stopKoin
|
||||||
@@ -15,9 +17,11 @@ class SubtracksApp : Application() {
|
|||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
stopKoin()
|
stopKoin()
|
||||||
startKoin {
|
val koin =
|
||||||
modules(appModule(this@SubtracksApp, http))
|
startKoin {
|
||||||
}
|
modules(appModule(this@SubtracksApp, http))
|
||||||
|
}.koin
|
||||||
|
ArtworkSeedCache.install(koin.get<ArtworkSeedStore>())
|
||||||
SingletonImageLoader.setSafe { context ->
|
SingletonImageLoader.setSafe { context ->
|
||||||
ImageLoader
|
ImageLoader
|
||||||
.Builder(context)
|
.Builder(context)
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.subtracks.data.db
|
||||||
|
|
||||||
|
import androidx.room3.Dao
|
||||||
|
import androidx.room3.Query
|
||||||
|
import androidx.room3.Upsert
|
||||||
|
import com.subtracks.data.model.ArtworkSeed
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface ArtworkSeedDao {
|
||||||
|
@Query("SELECT * FROM artwork_seeds WHERE cacheKey = :cacheKey")
|
||||||
|
suspend fun seed(cacheKey: String): ArtworkSeed?
|
||||||
|
|
||||||
|
@Upsert
|
||||||
|
suspend fun upsert(seed: ArtworkSeed)
|
||||||
|
|
||||||
|
@Query("DELETE FROM artwork_seeds WHERE updatedAt < :cutoff")
|
||||||
|
suspend fun pruneExpired(cutoff: Long)
|
||||||
|
}
|
||||||
@@ -34,4 +34,16 @@ val MIGRATION_2_3 =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val MIGRATIONS: Array<Migration> = arrayOf(MIGRATION_1_2, MIGRATION_2_3)
|
val MIGRATION_3_4 =
|
||||||
|
object : Migration(3, 4) {
|
||||||
|
override suspend fun migrate(connection: SQLiteConnection) {
|
||||||
|
connection.execSQL(
|
||||||
|
"CREATE TABLE IF NOT EXISTS `artwork_seeds` (`cacheKey` TEXT NOT NULL, " +
|
||||||
|
"`primary` INTEGER NOT NULL, `secondary` INTEGER, `updatedAt` INTEGER NOT NULL, " +
|
||||||
|
"PRIMARY KEY(`cacheKey`))",
|
||||||
|
)
|
||||||
|
connection.execSQL("CREATE INDEX IF NOT EXISTS `index_artwork_seeds_updatedAt` ON `artwork_seeds` (`updatedAt`)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val MIGRATIONS: Array<Migration> = arrayOf(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import androidx.room3.Database
|
|||||||
import androidx.room3.RoomDatabase
|
import androidx.room3.RoomDatabase
|
||||||
import com.subtracks.data.model.Album
|
import com.subtracks.data.model.Album
|
||||||
import com.subtracks.data.model.Artist
|
import com.subtracks.data.model.Artist
|
||||||
|
import com.subtracks.data.model.ArtworkSeed
|
||||||
import com.subtracks.data.model.PlaybackCursor
|
import com.subtracks.data.model.PlaybackCursor
|
||||||
import com.subtracks.data.model.Playlist
|
import com.subtracks.data.model.Playlist
|
||||||
import com.subtracks.data.model.PlaylistSong
|
import com.subtracks.data.model.PlaylistSong
|
||||||
@@ -27,8 +28,9 @@ import com.subtracks.data.model.SubsonicSource
|
|||||||
SearchIndex::class,
|
SearchIndex::class,
|
||||||
QueueEntry::class,
|
QueueEntry::class,
|
||||||
PlaybackCursor::class,
|
PlaybackCursor::class,
|
||||||
|
ArtworkSeed::class,
|
||||||
],
|
],
|
||||||
version = 3,
|
version = 4,
|
||||||
exportSchema = true,
|
exportSchema = true,
|
||||||
)
|
)
|
||||||
@ColumnTypeConverters(QueueKindConverter::class)
|
@ColumnTypeConverters(QueueKindConverter::class)
|
||||||
@@ -40,4 +42,6 @@ abstract class SubtracksDatabase : RoomDatabase() {
|
|||||||
abstract fun searchDao(): SearchDao
|
abstract fun searchDao(): SearchDao
|
||||||
|
|
||||||
abstract fun queueDao(): QueueDao
|
abstract fun queueDao(): QueueDao
|
||||||
|
|
||||||
|
abstract fun artworkSeedDao(): ArtworkSeedDao
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,6 +209,14 @@ data class PlaybackCursor(
|
|||||||
val queuePosition: Long,
|
val queuePosition: Long,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Entity(tableName = "artwork_seeds", indices = [Index("updatedAt")])
|
||||||
|
data class ArtworkSeed(
|
||||||
|
@PrimaryKey val cacheKey: String,
|
||||||
|
val primary: Int,
|
||||||
|
val secondary: Int?,
|
||||||
|
val updatedAt: Long,
|
||||||
|
)
|
||||||
|
|
||||||
class QueueKindConverter {
|
class QueueKindConverter {
|
||||||
@ColumnTypeConverter
|
@ColumnTypeConverter
|
||||||
fun fromQueueKind(kind: QueueKind): String = kind.name
|
fun fromQueueKind(kind: QueueKind): String = kind.name
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.subtracks.data.repo
|
||||||
|
|
||||||
|
import com.subtracks.data.db.SubtracksDatabase
|
||||||
|
import com.subtracks.data.model.ArtworkSeed
|
||||||
|
|
||||||
|
interface ArtworkSeedStore {
|
||||||
|
suspend fun seed(cacheKey: String): Pair<Int, Int?>?
|
||||||
|
|
||||||
|
suspend fun save(
|
||||||
|
cacheKey: String,
|
||||||
|
seeds: Pair<Int, Int?>,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArtworkSeedRepository(
|
||||||
|
private val db: SubtracksDatabase,
|
||||||
|
) : ArtworkSeedStore {
|
||||||
|
override suspend fun seed(cacheKey: String): Pair<Int, Int?>? {
|
||||||
|
val row = db.artworkSeedDao().seed(cacheKey) ?: return null
|
||||||
|
return row.primary to row.secondary
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun save(
|
||||||
|
cacheKey: String,
|
||||||
|
seeds: Pair<Int, Int?>,
|
||||||
|
) {
|
||||||
|
val dao = db.artworkSeedDao()
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
dao.upsert(ArtworkSeed(cacheKey, seeds.first, seeds.second, now))
|
||||||
|
dao.pruneExpired(now - MAX_AGE_MS)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val MAX_AGE_MS = 30L * 24 * 60 * 60 * 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ package com.subtracks.di
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.subtracks.data.db.createAndroidDatabase
|
import com.subtracks.data.db.createAndroidDatabase
|
||||||
import com.subtracks.data.prefs.createUserPreferences
|
import com.subtracks.data.prefs.createUserPreferences
|
||||||
|
import com.subtracks.data.repo.ArtworkSeedRepository
|
||||||
|
import com.subtracks.data.repo.ArtworkSeedStore
|
||||||
import com.subtracks.data.repo.LibraryRepository
|
import com.subtracks.data.repo.LibraryRepository
|
||||||
import com.subtracks.data.repo.QueueRepository
|
import com.subtracks.data.repo.QueueRepository
|
||||||
import com.subtracks.data.repo.SourceRepository
|
import com.subtracks.data.repo.SourceRepository
|
||||||
@@ -31,6 +33,7 @@ fun appModule(
|
|||||||
single { createUserPreferences(context) }
|
single { createUserPreferences(context) }
|
||||||
single { SourceRepository(get(), get(), get()) }
|
single { SourceRepository(get(), get(), get()) }
|
||||||
single { LibraryRepository(get(), get()) }
|
single { LibraryRepository(get(), get()) }
|
||||||
|
single<ArtworkSeedStore> { ArtworkSeedRepository(get()) }
|
||||||
single { SyncManager(get()) }
|
single { SyncManager(get()) }
|
||||||
single { QueueRepository(get()) }
|
single { QueueRepository(get()) }
|
||||||
single<PlayerConnection> { MediaSessionConnection(context.applicationContext, get()) }
|
single<PlayerConnection> { MediaSessionConnection(context.applicationContext, get()) }
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import coil3.request.SuccessResult
|
|||||||
import coil3.request.allowHardware
|
import coil3.request.allowHardware
|
||||||
import coil3.toBitmap
|
import coil3.toBitmap
|
||||||
import com.subtracks.data.model.CoverArtRef
|
import com.subtracks.data.model.CoverArtRef
|
||||||
|
import com.subtracks.data.repo.ArtworkSeedStore
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Deferred
|
import kotlinx.coroutines.Deferred
|
||||||
@@ -36,15 +37,21 @@ object ArtworkSeedCache {
|
|||||||
private val jobs = ConcurrentHashMap<String, Deferred<Pair<Int, Int?>?>>()
|
private val jobs = ConcurrentHashMap<String, Deferred<Pair<Int, Int?>?>>()
|
||||||
private val cache = LruCache<String, Pair<Int, Int?>>(MAX_CACHED_SEEDS)
|
private val cache = LruCache<String, Pair<Int, Int?>>(MAX_CACHED_SEEDS)
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private var store: ArtworkSeedStore? = null
|
||||||
|
|
||||||
|
fun install(store: ArtworkSeedStore) {
|
||||||
|
this.store = store
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun seeds(
|
suspend fun seeds(
|
||||||
context: Context,
|
context: Context,
|
||||||
ref: CoverArtRef,
|
ref: CoverArtRef,
|
||||||
): Pair<Int, Int?>? {
|
): Pair<Int, Int?>? {
|
||||||
cache.get(ref.cacheKey)?.let { return it }
|
cache.get(ref.cacheKey)?.let { return it }
|
||||||
val job = jobs.computeIfAbsent(ref.cacheKey) { scope.async { extract(context, ref) } }
|
val job = jobs.computeIfAbsent(ref.cacheKey) { scope.async { resolve(context, ref) } }
|
||||||
val seeds = job.await()
|
val seeds = job.await()
|
||||||
jobs.remove(ref.cacheKey, job)
|
jobs.remove(ref.cacheKey, job)
|
||||||
if (seeds != null) cache.put(ref.cacheKey, seeds)
|
|
||||||
return seeds
|
return seeds
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +68,29 @@ object ArtworkSeedCache {
|
|||||||
cache.evictAll()
|
cache.evictAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun resolve(
|
||||||
|
context: Context,
|
||||||
|
ref: CoverArtRef,
|
||||||
|
): Pair<Int, Int?>? {
|
||||||
|
ignoreFailure { store?.seed(ref.cacheKey) }?.let {
|
||||||
|
cache.put(ref.cacheKey, it)
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
val seeds = extract(context, ref) ?: return null
|
||||||
|
cache.put(ref.cacheKey, seeds)
|
||||||
|
ignoreFailure { store?.save(ref.cacheKey, seeds) }
|
||||||
|
return seeds
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun <T> ignoreFailure(block: suspend () -> T): T? =
|
||||||
|
try {
|
||||||
|
block()
|
||||||
|
} catch (e: CancellationException) {
|
||||||
|
throw e
|
||||||
|
} catch (_: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun extract(
|
private suspend fun extract(
|
||||||
context: Context,
|
context: Context,
|
||||||
ref: CoverArtRef,
|
ref: CoverArtRef,
|
||||||
|
|||||||
@@ -55,9 +55,10 @@ class ArtworkExtractionTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun resolvesColorsFromCoverArt() {
|
fun resolvesColorsFromCoverArt() {
|
||||||
|
val ref = CoverArtRef(url = "art", cacheKey = "art-${System.nanoTime()}")
|
||||||
var colors: ArtworkColors? = null
|
var colors: ArtworkColors? = null
|
||||||
composeRule.setContent {
|
composeRule.setContent {
|
||||||
colors = rememberArtworkColors(CoverArtRef(url = "art", cacheKey = "art"))
|
colors = rememberArtworkColors(ref)
|
||||||
}
|
}
|
||||||
composeRule.waitUntil(timeoutMillis = 5_000) { colors != null }
|
composeRule.waitUntil(timeoutMillis = 5_000) { colors != null }
|
||||||
assertNotNull(colors)
|
assertNotNull(colors)
|
||||||
|
|||||||
Reference in New Issue
Block a user