Migrate database (2)
This commit is contained in:
parent
a7bdda074b
commit
6f5bc02216
13 changed files with 545 additions and 145 deletions
419
app/schemas/it.vfsfitvnm.vimusic.DatabaseInitializer/9.json
Normal file
419
app/schemas/it.vfsfitvnm.vimusic.DatabaseInitializer/9.json
Normal file
|
@ -0,0 +1,419 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 9,
|
||||||
|
"identityHash": "22e88f327e3340760100610939e9a158",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "Song",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `albumId` TEXT, `artistsText` TEXT, `durationText` TEXT NOT NULL, `thumbnailUrl` TEXT, `lyrics` TEXT, `likedAt` INTEGER, `totalPlayTimeMs` INTEGER NOT NULL, `loudnessDb` REAL, `contentLength` INTEGER, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "albumId",
|
||||||
|
"columnName": "albumId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistsText",
|
||||||
|
"columnName": "artistsText",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "durationText",
|
||||||
|
"columnName": "durationText",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "thumbnailUrl",
|
||||||
|
"columnName": "thumbnailUrl",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lyrics",
|
||||||
|
"columnName": "lyrics",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "likedAt",
|
||||||
|
"columnName": "likedAt",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "totalPlayTimeMs",
|
||||||
|
"columnName": "totalPlayTimeMs",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "loudnessDb",
|
||||||
|
"columnName": "loudnessDb",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "contentLength",
|
||||||
|
"columnName": "contentLength",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "SongInPlaylist",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`songId` TEXT NOT NULL, `playlistId` INTEGER NOT NULL, `position` INTEGER NOT NULL, PRIMARY KEY(`songId`, `playlistId`), FOREIGN KEY(`songId`) REFERENCES `Song`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`playlistId`) REFERENCES `Playlist`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "songId",
|
||||||
|
"columnName": "songId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistId",
|
||||||
|
"columnName": "playlistId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "position",
|
||||||
|
"columnName": "position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"songId",
|
||||||
|
"playlistId"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_SongInPlaylist_songId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"songId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_SongInPlaylist_songId` ON `${TABLE_NAME}` (`songId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_SongInPlaylist_playlistId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"playlistId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_SongInPlaylist_playlistId` ON `${TABLE_NAME}` (`playlistId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Song",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"songId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Playlist",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"playlistId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Playlist",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Artist",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `thumbnailUrl` TEXT, `info` TEXT, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "thumbnailUrl",
|
||||||
|
"columnName": "thumbnailUrl",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "info",
|
||||||
|
"columnName": "info",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "SongArtistMap",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`songId` TEXT NOT NULL, `artistId` TEXT NOT NULL, PRIMARY KEY(`songId`, `artistId`), FOREIGN KEY(`songId`) REFERENCES `Song`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`artistId`) REFERENCES `Artist`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "songId",
|
||||||
|
"columnName": "songId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "artistId",
|
||||||
|
"columnName": "artistId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"songId",
|
||||||
|
"artistId"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_SongArtistMap_songId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"songId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_SongArtistMap_songId` ON `${TABLE_NAME}` (`songId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_SongArtistMap_artistId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"artistId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_SongArtistMap_artistId` ON `${TABLE_NAME}` (`artistId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Song",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"songId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Artist",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"artistId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Album",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `thumbnailUrl` TEXT, `year` TEXT, `authorsText` TEXT, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "thumbnailUrl",
|
||||||
|
"columnName": "thumbnailUrl",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "year",
|
||||||
|
"columnName": "year",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "authorsText",
|
||||||
|
"columnName": "authorsText",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "SearchQuery",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `query` TEXT NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "query",
|
||||||
|
"columnName": "query",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_SearchQuery_query",
|
||||||
|
"unique": true,
|
||||||
|
"columnNames": [
|
||||||
|
"query"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SearchQuery_query` ON `${TABLE_NAME}` (`query`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "QueuedMediaItem",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `mediaItem` BLOB NOT NULL, `position` INTEGER)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "mediaItem",
|
||||||
|
"columnName": "mediaItem",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "position",
|
||||||
|
"columnName": "position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"views": [
|
||||||
|
{
|
||||||
|
"viewName": "SortedSongInPlaylist",
|
||||||
|
"createSql": "CREATE VIEW `${VIEW_NAME}` AS SELECT * FROM SongInPlaylist ORDER BY position"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, '22e88f327e3340760100610939e9a158')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,14 @@
|
||||||
package it.vfsfitvnm.vimusic
|
package it.vfsfitvnm.vimusic
|
||||||
|
|
||||||
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
|
import android.database.sqlite.SQLiteDatabase.CONFLICT_IGNORE
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import androidx.room.migration.AutoMigrationSpec
|
import androidx.room.migration.AutoMigrationSpec
|
||||||
|
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||||
import androidx.room.migration.Migration
|
import androidx.room.migration.Migration
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
import it.vfsfitvnm.vimusic.models.*
|
import it.vfsfitvnm.vimusic.models.*
|
||||||
|
@ -22,8 +25,11 @@ interface Database {
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
fun insert(searchQuery: SearchQuery)
|
fun insert(searchQuery: SearchQuery)
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
fun insert(info: Info): Long
|
fun insert(info: Artist)
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
fun insert(info: Album)
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||||
fun insert(playlist: Playlist): Long
|
fun insert(playlist: Playlist): Long
|
||||||
|
@ -32,7 +38,7 @@ interface Database {
|
||||||
fun insert(info: SongInPlaylist): Long
|
fun insert(info: SongInPlaylist): Long
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||||
fun insert(info: List<Info>): List<Long>
|
fun insert(info: List<Artist>): List<Long>
|
||||||
|
|
||||||
@Query("SELECT * FROM Song WHERE id = :id")
|
@Query("SELECT * FROM Song WHERE id = :id")
|
||||||
fun songFlow(id: String): Flow<Song?>
|
fun songFlow(id: String): Flow<Song?>
|
||||||
|
@ -48,19 +54,19 @@ interface Database {
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Song WHERE id = :id")
|
@Query("SELECT * FROM Song WHERE id = :id")
|
||||||
fun songWithInfo(id: String): SongWithInfo?
|
fun songWithInfo(id: String): DetailedSong?
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY ROWID DESC")
|
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY ROWID DESC")
|
||||||
fun history(): Flow<List<SongWithInfo>>
|
fun history(): Flow<List<DetailedSong>>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Song WHERE likedAt IS NOT NULL ORDER BY likedAt DESC")
|
@Query("SELECT * FROM Song WHERE likedAt IS NOT NULL ORDER BY likedAt DESC")
|
||||||
fun favorites(): Flow<List<SongWithInfo>>
|
fun favorites(): Flow<List<DetailedSong>>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Song WHERE totalPlayTimeMs >= 60000 ORDER BY totalPlayTimeMs DESC LIMIT 20")
|
@Query("SELECT * FROM Song WHERE totalPlayTimeMs >= 60000 ORDER BY totalPlayTimeMs DESC LIMIT 20")
|
||||||
fun mostPlayed(): Flow<List<SongWithInfo>>
|
fun mostPlayed(): Flow<List<DetailedSong>>
|
||||||
|
|
||||||
@Query("UPDATE Song SET totalPlayTimeMs = totalPlayTimeMs + :addition WHERE id = :id")
|
@Query("UPDATE Song SET totalPlayTimeMs = totalPlayTimeMs + :addition WHERE id = :id")
|
||||||
fun incrementTotalPlayTimeMs(id: String, addition: Long)
|
fun incrementTotalPlayTimeMs(id: String, addition: Long)
|
||||||
|
@ -82,7 +88,7 @@ interface Database {
|
||||||
fun incrementSongPositions(playlistId: Long, fromPosition: Int, toPosition: Int)
|
fun incrementSongPositions(playlistId: Long, fromPosition: Int, toPosition: Int)
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||||
fun insert(songWithAuthors: SongWithAuthors): Long
|
fun insert(songWithAuthors: SongArtistMap): Long
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||||
fun insert(song: Song): Long
|
fun insert(song: Song): Long
|
||||||
|
@ -115,10 +121,10 @@ interface Database {
|
||||||
@Query("SELECT thumbnailUrl FROM Song JOIN SongInPlaylist ON id = songId WHERE playlistId = :id ORDER BY position LIMIT 4")
|
@Query("SELECT thumbnailUrl FROM Song JOIN SongInPlaylist ON id = songId WHERE playlistId = :id ORDER BY position LIMIT 4")
|
||||||
fun playlistThumbnailUrls(id: Long): Flow<List<String?>>
|
fun playlistThumbnailUrls(id: Long): Flow<List<String?>>
|
||||||
|
|
||||||
@Transaction
|
// @Transaction
|
||||||
@RewriteQueriesToDropUnusedColumns
|
// @RewriteQueriesToDropUnusedColumns
|
||||||
@Query("SELECT * FROM Info JOIN SongWithAuthors ON Info.id = SongWithAuthors.authorInfoId JOIN Song ON SongWithAuthors.songId = Song.id WHERE browseId = :artistId ORDER BY Song.ROWID DESC")
|
// @Query("SELECT * FROM Info JOIN SongWithAuthors ON Info.id = SongWithAuthors.authorInfoId JOIN Song ON SongWithAuthors.songId = Song.id WHERE browseId = :artistId ORDER BY Song.ROWID DESC")
|
||||||
fun artistSongs(artistId: String): Flow<List<SongWithInfo>>
|
// fun artistSongs(artistId: String): Flow<List<DetailedSong>>
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||||
fun insertQueue(queuedMediaItems: List<QueuedMediaItem>)
|
fun insertQueue(queuedMediaItems: List<QueuedMediaItem>)
|
||||||
|
@ -135,11 +141,9 @@ interface Database {
|
||||||
Song::class,
|
Song::class,
|
||||||
SongInPlaylist::class,
|
SongInPlaylist::class,
|
||||||
Playlist::class,
|
Playlist::class,
|
||||||
Info::class,
|
|
||||||
SongWithAuthors::class,
|
|
||||||
Album::class,
|
|
||||||
Artist::class,
|
Artist::class,
|
||||||
SongArtistMap::class,
|
SongArtistMap::class,
|
||||||
|
Album::class,
|
||||||
SearchQuery::class,
|
SearchQuery::class,
|
||||||
QueuedMediaItem::class,
|
QueuedMediaItem::class,
|
||||||
],
|
],
|
||||||
|
@ -166,7 +170,7 @@ abstract class DatabaseInitializer protected constructor() : RoomDatabase() {
|
||||||
lateinit var Instance: DatabaseInitializer
|
lateinit var Instance: DatabaseInitializer
|
||||||
|
|
||||||
context(Context)
|
context(Context)
|
||||||
operator fun invoke() {
|
operator fun invoke() {
|
||||||
if (!::Instance.isInitialized) {
|
if (!::Instance.isInitialized) {
|
||||||
Instance = Room
|
Instance = Room
|
||||||
.databaseBuilder(this@Context, DatabaseInitializer::class.java, "data.db")
|
.databaseBuilder(this@Context, DatabaseInitializer::class.java, "data.db")
|
||||||
|
@ -183,8 +187,42 @@ abstract class DatabaseInitializer protected constructor() : RoomDatabase() {
|
||||||
class From7To8Migration : AutoMigrationSpec
|
class From7To8Migration : AutoMigrationSpec
|
||||||
|
|
||||||
class From8To9Migration : Migration(8, 9) {
|
class From8To9Migration : Migration(8, 9) {
|
||||||
override fun migrate(database: SupportSQLiteDatabase) {
|
override fun migrate(it: SupportSQLiteDatabase) {
|
||||||
|
it.query(SimpleSQLiteQuery("SELECT DISTINCT browseId, text, Info.id FROM Info JOIN Song ON Info.id = Song.albumId;")).use { cursor ->
|
||||||
|
val albumValues = ContentValues(2)
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
albumValues.put("id", cursor.getString(0))
|
||||||
|
albumValues.put("title", cursor.getString(1))
|
||||||
|
it.insert("Album", CONFLICT_IGNORE, albumValues)
|
||||||
|
|
||||||
|
it.execSQL("UPDATE Song SET albumId = '${cursor.getString(0)}' WHERE albumId = ${cursor.getLong(2)}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it.query(SimpleSQLiteQuery("SELECT GROUP_CONCAT(text, ''), SongWithAuthors.songId FROM Info JOIN SongWithAuthors ON Info.id = SongWithAuthors.authorInfoId GROUP BY songId;")).use { cursor ->
|
||||||
|
val songValues = ContentValues(1)
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
println("artistsText: ${cursor.getString(0)} (cursor.getString(1))")
|
||||||
|
songValues.put("artistsText", cursor.getString(0))
|
||||||
|
it.update("Song", CONFLICT_IGNORE, songValues, "id = ?", arrayOf(cursor.getString(1)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it.query(SimpleSQLiteQuery("SELECT browseId, text, Info.id FROM Info JOIN SongWithAuthors ON Info.id = SongWithAuthors.authorInfoId WHERE browseId NOT NULL;")).use { cursor ->
|
||||||
|
val artistValues = ContentValues(2)
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
artistValues.put("id", cursor.getString(0))
|
||||||
|
artistValues.put("name", cursor.getString(1))
|
||||||
|
it.insert("Artist", CONFLICT_IGNORE, artistValues)
|
||||||
|
|
||||||
|
it.execSQL("UPDATE SongWithAuthors SET authorInfoId = '${cursor.getString(0)}' WHERE authorInfoId = ${cursor.getLong(2)}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it.execSQL("INSERT INTO SongArtistMap(songId, artistId) SELECT songId, authorInfoId FROM SongWithAuthors")
|
||||||
|
|
||||||
|
it.execSQL("DROP TABLE Info;")
|
||||||
|
it.execSQL("DROP TABLE SongWithAuthors;")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.models
|
|
||||||
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
|
|
||||||
// I know...
|
|
||||||
@Entity
|
|
||||||
data class Info(
|
|
||||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
|
||||||
val browseId: String?,
|
|
||||||
val text: String
|
|
||||||
)
|
|
|
@ -15,7 +15,7 @@ data class PlaylistWithSongs(
|
||||||
entityColumn = "songId"
|
entityColumn = "songId"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val songs: List<SongWithInfo>
|
val songs: List<DetailedSong>
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
val Empty = PlaylistWithSongs(Playlist(-1, ""), emptyList())
|
val Empty = PlaylistWithSongs(Playlist(-1, ""), emptyList())
|
||||||
|
|
|
@ -3,7 +3,16 @@ package it.vfsfitvnm.vimusic.models
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
|
|
||||||
|
|
||||||
@Entity
|
@Entity(
|
||||||
|
// foreignKeys = [
|
||||||
|
// ForeignKey(
|
||||||
|
// entity = Album::class,
|
||||||
|
// parentColumns = ["id"],
|
||||||
|
// childColumns = ["albumId"],
|
||||||
|
// onDelete = ForeignKey.CASCADE
|
||||||
|
// ),
|
||||||
|
// ]
|
||||||
|
)
|
||||||
data class Song(
|
data class Song(
|
||||||
@PrimaryKey val id: String,
|
@PrimaryKey val id: String,
|
||||||
val title: String,
|
val title: String,
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.models
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import androidx.room.*
|
|
||||||
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@Entity(
|
|
||||||
primaryKeys = ["songId", "authorInfoId"],
|
|
||||||
foreignKeys = [
|
|
||||||
ForeignKey(
|
|
||||||
entity = Song::class,
|
|
||||||
parentColumns = ["id"],
|
|
||||||
childColumns = ["songId"],
|
|
||||||
onDelete = ForeignKey.CASCADE
|
|
||||||
),
|
|
||||||
ForeignKey(
|
|
||||||
entity = Info::class,
|
|
||||||
parentColumns = ["id"],
|
|
||||||
childColumns = ["authorInfoId"],
|
|
||||||
onDelete = ForeignKey.CASCADE
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
data class SongWithAuthors(
|
|
||||||
val songId: String,
|
|
||||||
@ColumnInfo(index = true) val authorInfoId: Long
|
|
||||||
)
|
|
|
@ -1,25 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.models
|
|
||||||
|
|
||||||
import androidx.room.Embedded
|
|
||||||
import androidx.room.Junction
|
|
||||||
import androidx.room.Relation
|
|
||||||
|
|
||||||
open class SongWithInfo(
|
|
||||||
@Embedded val song: Song,
|
|
||||||
@Relation(
|
|
||||||
entity = Info::class,
|
|
||||||
parentColumn = "albumId",
|
|
||||||
entityColumn = "id"
|
|
||||||
) val album: Info?,
|
|
||||||
@Relation(
|
|
||||||
entity = Info::class,
|
|
||||||
parentColumn = "id",
|
|
||||||
entityColumn = "id",
|
|
||||||
associateBy = Junction(
|
|
||||||
value = SongWithAuthors::class,
|
|
||||||
parentColumn = "songId",
|
|
||||||
entityColumn = "authorInfoId"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val authors: List<Info>?
|
|
||||||
)
|
|
|
@ -20,7 +20,7 @@ import it.vfsfitvnm.vimusic.*
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.Playlist
|
import it.vfsfitvnm.vimusic.models.Playlist
|
||||||
import it.vfsfitvnm.vimusic.models.SongInPlaylist
|
import it.vfsfitvnm.vimusic.models.SongInPlaylist
|
||||||
import it.vfsfitvnm.vimusic.models.SongWithInfo
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute
|
import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.rememberCreatePlaylistRoute
|
import it.vfsfitvnm.vimusic.ui.screens.rememberCreatePlaylistRoute
|
||||||
|
@ -32,7 +32,7 @@ import kotlinx.coroutines.Dispatchers
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun InFavoritesMediaItemMenu(
|
fun InFavoritesMediaItemMenu(
|
||||||
song: SongWithInfo,
|
song: DetailedSong,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onDismiss: (() -> Unit)? = null
|
onDismiss: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
|
@ -51,7 +51,7 @@ fun InFavoritesMediaItemMenu(
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun InHistoryMediaItemMenu(
|
fun InHistoryMediaItemMenu(
|
||||||
song: SongWithInfo,
|
song: DetailedSong,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onDismiss: (() -> Unit)? = null
|
onDismiss: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
|
@ -94,7 +94,7 @@ fun InHistoryMediaItemMenu(
|
||||||
fun InPlaylistMediaItemMenu(
|
fun InPlaylistMediaItemMenu(
|
||||||
playlistId: Long,
|
playlistId: Long,
|
||||||
positionInPlaylist: Int,
|
positionInPlaylist: Int,
|
||||||
song: SongWithInfo,
|
song: DetailedSong,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onDismiss: (() -> Unit)? = null
|
onDismiss: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -28,7 +28,7 @@ import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.SongWithInfo
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.ui.components.OutcomeItem
|
import it.vfsfitvnm.vimusic.ui.components.OutcomeItem
|
||||||
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
|
||||||
|
@ -40,6 +40,7 @@ import it.vfsfitvnm.vimusic.utils.*
|
||||||
import it.vfsfitvnm.youtubemusic.Outcome
|
import it.vfsfitvnm.youtubemusic.Outcome
|
||||||
import it.vfsfitvnm.youtubemusic.YouTube
|
import it.vfsfitvnm.youtubemusic.YouTube
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,8 +97,9 @@ fun ArtistScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
val songs by remember(browseId) {
|
val songs by remember(browseId) {
|
||||||
Database.artistSongs(browseId)
|
flowOf(emptyList<DetailedSong>())
|
||||||
}.collectAsState(initial = emptyList(), context = Dispatchers.IO)
|
// Database.artistSongs(browseId)
|
||||||
|
}.collectAsState(initial = emptyList<DetailedSong>(), context = Dispatchers.IO)
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = lazyListState,
|
state = lazyListState,
|
||||||
|
@ -218,7 +220,7 @@ fun ArtistScreen(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable(enabled = songs.isNotEmpty()) {
|
.clickable(enabled = songs.isNotEmpty()) {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayFromBeginning(songs.shuffled().map(SongWithInfo::asMediaItem))
|
binder?.player?.forcePlayFromBeginning(songs.shuffled().map(DetailedSong::asMediaItem))
|
||||||
}
|
}
|
||||||
.padding(horizontal = 8.dp, vertical = 8.dp)
|
.padding(horizontal = 8.dp, vertical = 8.dp)
|
||||||
.size(20.dp)
|
.size(20.dp)
|
||||||
|
@ -236,7 +238,7 @@ fun ArtistScreen(
|
||||||
thumbnailSize = songThumbnailSizePx,
|
thumbnailSize = songThumbnailSizePx,
|
||||||
onClick = {
|
onClick = {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayAtIndex(songs.map(SongWithInfo::asMediaItem), index)
|
binder?.player?.forcePlayAtIndex(songs.map(DetailedSong::asMediaItem), index)
|
||||||
},
|
},
|
||||||
menuContent = {
|
menuContent = {
|
||||||
InHistoryMediaItemMenu(song = song)
|
InHistoryMediaItemMenu(song = song)
|
||||||
|
|
|
@ -37,7 +37,7 @@ import it.vfsfitvnm.vimusic.enums.SongCollection
|
||||||
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
|
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
|
||||||
import it.vfsfitvnm.vimusic.models.Playlist
|
import it.vfsfitvnm.vimusic.models.Playlist
|
||||||
import it.vfsfitvnm.vimusic.models.SearchQuery
|
import it.vfsfitvnm.vimusic.models.SearchQuery
|
||||||
import it.vfsfitvnm.vimusic.models.SongWithInfo
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.query
|
import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.InFavoritesMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.InFavoritesMediaItemMenu
|
||||||
|
@ -358,7 +358,7 @@ fun HomeScreen() {
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable(enabled = songCollection.isNotEmpty()) {
|
.clickable(enabled = songCollection.isNotEmpty()) {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayFromBeginning(songCollection.shuffled().map(SongWithInfo::asMediaItem))
|
binder?.player?.forcePlayFromBeginning(songCollection.shuffled().map(DetailedSong::asMediaItem))
|
||||||
}
|
}
|
||||||
.padding(horizontal = 8.dp, vertical = 8.dp)
|
.padding(horizontal = 8.dp, vertical = 8.dp)
|
||||||
.size(20.dp)
|
.size(20.dp)
|
||||||
|
@ -378,7 +378,7 @@ fun HomeScreen() {
|
||||||
thumbnailSize = thumbnailSize,
|
thumbnailSize = thumbnailSize,
|
||||||
onClick = {
|
onClick = {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayAtIndex(songCollection.map(SongWithInfo::asMediaItem), index)
|
binder?.player?.forcePlayAtIndex(songCollection.map(DetailedSong::asMediaItem), index)
|
||||||
},
|
},
|
||||||
menuContent = {
|
menuContent = {
|
||||||
when (preferences.homePageSongCollection) {
|
when (preferences.homePageSongCollection) {
|
||||||
|
|
|
@ -28,7 +28,7 @@ import it.vfsfitvnm.vimusic.*
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.PlaylistWithSongs
|
import it.vfsfitvnm.vimusic.models.PlaylistWithSongs
|
||||||
import it.vfsfitvnm.vimusic.models.SongInPlaylist
|
import it.vfsfitvnm.vimusic.models.SongInPlaylist
|
||||||
import it.vfsfitvnm.vimusic.models.SongWithInfo
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.*
|
import it.vfsfitvnm.vimusic.ui.components.themed.*
|
||||||
|
@ -160,7 +160,7 @@ fun LocalPlaylistScreen(
|
||||||
enabled = playlistWithSongs.songs.isNotEmpty(),
|
enabled = playlistWithSongs.songs.isNotEmpty(),
|
||||||
onClick = {
|
onClick = {
|
||||||
menuState.hide()
|
menuState.hide()
|
||||||
binder?.player?.enqueue(playlistWithSongs.songs.map(SongWithInfo::asMediaItem))
|
binder?.player?.enqueue(playlistWithSongs.songs.map(DetailedSong::asMediaItem))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ fun LocalPlaylistScreen(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable {
|
.clickable {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayFromBeginning(playlistWithSongs.songs.map(SongWithInfo::asMediaItem).shuffled())
|
binder?.player?.forcePlayFromBeginning(playlistWithSongs.songs.map(DetailedSong::asMediaItem).shuffled())
|
||||||
}
|
}
|
||||||
.shadow(elevation = 2.dp, shape = CircleShape)
|
.shadow(elevation = 2.dp, shape = CircleShape)
|
||||||
.background(
|
.background(
|
||||||
|
@ -243,7 +243,7 @@ fun LocalPlaylistScreen(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable {
|
.clickable {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayFromBeginning(playlistWithSongs.songs.map(SongWithInfo::asMediaItem))
|
binder?.player?.forcePlayFromBeginning(playlistWithSongs.songs.map(DetailedSong::asMediaItem))
|
||||||
}
|
}
|
||||||
.shadow(elevation = 2.dp, shape = CircleShape)
|
.shadow(elevation = 2.dp, shape = CircleShape)
|
||||||
.background(
|
.background(
|
||||||
|
@ -267,7 +267,7 @@ fun LocalPlaylistScreen(
|
||||||
thumbnailSize = thumbnailSize,
|
thumbnailSize = thumbnailSize,
|
||||||
onClick = {
|
onClick = {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayAtIndex(playlistWithSongs.songs.map(SongWithInfo::asMediaItem), index)
|
binder?.player?.forcePlayAtIndex(playlistWithSongs.songs.map(DetailedSong::asMediaItem), index)
|
||||||
},
|
},
|
||||||
menuContent = {
|
menuContent = {
|
||||||
InPlaylistMediaItemMenu(
|
InPlaylistMediaItemMenu(
|
||||||
|
|
|
@ -26,7 +26,7 @@ import coil.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
|
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
|
||||||
import it.vfsfitvnm.vimusic.models.SongWithInfo
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
|
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
|
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
|
||||||
|
@ -67,7 +67,7 @@ fun SongItem(
|
||||||
@Composable
|
@Composable
|
||||||
@NonRestartableComposable
|
@NonRestartableComposable
|
||||||
fun SongItem(
|
fun SongItem(
|
||||||
song: SongWithInfo,
|
song: DetailedSong,
|
||||||
thumbnailSize: Int,
|
thumbnailSize: Int,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
menuContent: @Composable () -> Unit,
|
menuContent: @Composable () -> Unit,
|
||||||
|
@ -78,7 +78,7 @@ fun SongItem(
|
||||||
SongItem(
|
SongItem(
|
||||||
thumbnailModel = song.song.thumbnailUrl?.thumbnail(thumbnailSize),
|
thumbnailModel = song.song.thumbnailUrl?.thumbnail(thumbnailSize),
|
||||||
title = song.song.title,
|
title = song.song.title,
|
||||||
authors = song.authors?.joinToString("") { it.text } ?: "",
|
authors = song.song.artistsText ?: "",
|
||||||
durationText = song.song.durationText,
|
durationText = song.song.durationText,
|
||||||
menuContent = menuContent,
|
menuContent = menuContent,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
|
|
|
@ -28,46 +28,43 @@ fun Database.insert(mediaItem: MediaItem): Song {
|
||||||
return@runInTransaction it
|
return@runInTransaction it
|
||||||
}
|
}
|
||||||
|
|
||||||
val albumInfo = mediaItem.mediaMetadata.extras?.getString("albumId")?.let { albumId ->
|
val album = mediaItem.mediaMetadata.extras?.getString("albumId")?.let { albumId ->
|
||||||
Info(
|
Album(
|
||||||
text = mediaItem.mediaMetadata.albumTitle!!.toString(),
|
id = albumId,
|
||||||
browseId = albumId
|
title = mediaItem.mediaMetadata.albumTitle!!.toString(),
|
||||||
)
|
year = null,
|
||||||
|
authorsText = null,
|
||||||
|
thumbnailUrl = null
|
||||||
|
).also(::insert)
|
||||||
}
|
}
|
||||||
|
|
||||||
val albumInfoId = albumInfo?.let { insert(it) }
|
|
||||||
|
|
||||||
val authorsInfo =
|
|
||||||
mediaItem.mediaMetadata.extras?.getStringArrayList("artistNames")?.let { artistNames ->
|
|
||||||
mediaItem.mediaMetadata.extras!!.getStringArrayList("artistIds")?.let { artistIds ->
|
|
||||||
artistNames.mapIndexed { index, artistName ->
|
|
||||||
Info(
|
|
||||||
text = artistName,
|
|
||||||
browseId = artistIds.getOrNull(index)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val song = Song(
|
val song = Song(
|
||||||
id = mediaItem.mediaId,
|
id = mediaItem.mediaId,
|
||||||
title = mediaItem.mediaMetadata.title!!.toString(),
|
title = mediaItem.mediaMetadata.title!!.toString(),
|
||||||
albumId = albumInfoId.toString(),
|
artistsText = mediaItem.mediaMetadata.artist!!.toString(),
|
||||||
|
albumId = album?.id,
|
||||||
durationText = mediaItem.mediaMetadata.extras?.getString("durationText")!!,
|
durationText = mediaItem.mediaMetadata.extras?.getString("durationText")!!,
|
||||||
thumbnailUrl = mediaItem.mediaMetadata.artworkUri!!.toString(),
|
thumbnailUrl = mediaItem.mediaMetadata.artworkUri!!.toString(),
|
||||||
loudnessDb = mediaItem.mediaMetadata.extras?.getFloat("loudnessDb"),
|
loudnessDb = mediaItem.mediaMetadata.extras?.getFloat("loudnessDb"),
|
||||||
contentLength = mediaItem.mediaMetadata.extras?.getLong("contentLength"),
|
contentLength = mediaItem.mediaMetadata.extras?.getLong("contentLength"),
|
||||||
)
|
).also(::insert)
|
||||||
|
|
||||||
insert(song)
|
mediaItem.mediaMetadata.extras?.getStringArrayList("artistNames")?.let { artistNames ->
|
||||||
|
mediaItem.mediaMetadata.extras!!.getStringArrayList("artistIds")?.let { artistIds ->
|
||||||
val authorsInfoId = authorsInfo?.let { insert(authorsInfo) }
|
artistNames.mapIndexed { index, artistName ->
|
||||||
|
Artist(
|
||||||
authorsInfoId?.forEach { authorInfoId ->
|
id = artistIds[index],
|
||||||
|
name = artistName,
|
||||||
|
thumbnailUrl = null,
|
||||||
|
info = null
|
||||||
|
).also(::insert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}?.forEach { artist ->
|
||||||
insert(
|
insert(
|
||||||
SongWithAuthors(
|
SongArtistMap(
|
||||||
songId = mediaItem.mediaId,
|
songId = song.id,
|
||||||
authorInfoId = authorInfoId
|
artistId = artist.id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -92,8 +89,8 @@ val YouTube.Item.Song.asMediaItem: MediaItem
|
||||||
"videoId" to info.endpoint!!.videoId,
|
"videoId" to info.endpoint!!.videoId,
|
||||||
"albumId" to album?.endpoint?.browseId,
|
"albumId" to album?.endpoint?.browseId,
|
||||||
"durationText" to durationText,
|
"durationText" to durationText,
|
||||||
"artistNames" to authors.map { it.name },
|
"artistNames" to authors.filter { it.endpoint != null }.map { it.name },
|
||||||
"artistIds" to authors.map { it.endpoint?.browseId },
|
"artistIds" to authors.mapNotNull { it.endpoint?.browseId },
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
@ -114,28 +111,28 @@ val YouTube.Item.Video.asMediaItem: MediaItem
|
||||||
bundleOf(
|
bundleOf(
|
||||||
"videoId" to info.endpoint!!.videoId,
|
"videoId" to info.endpoint!!.videoId,
|
||||||
"durationText" to durationText,
|
"durationText" to durationText,
|
||||||
"artistNames" to if (isOfficialMusicVideo) authors.map { it.name } else null,
|
"artistNames" to if (isOfficialMusicVideo) authors.filter { it.endpoint != null }.map { it.name } else null,
|
||||||
"artistIds" to if (isOfficialMusicVideo) authors.map { it.endpoint?.browseId } else null,
|
"artistIds" to if (isOfficialMusicVideo) authors.mapNotNull { it.endpoint?.browseId } else null,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val SongWithInfo.asMediaItem: MediaItem
|
val DetailedSong.asMediaItem: MediaItem
|
||||||
get() = MediaItem.Builder()
|
get() = MediaItem.Builder()
|
||||||
.setMediaMetadata(
|
.setMediaMetadata(
|
||||||
MediaMetadata.Builder()
|
MediaMetadata.Builder()
|
||||||
.setTitle(song.title)
|
.setTitle(song.title)
|
||||||
.setArtist(authors?.joinToString("") { it.text })
|
.setArtist(song.artistsText)
|
||||||
.setAlbumTitle(album?.text)
|
.setAlbumTitle(album?.title)
|
||||||
.setArtworkUri(song.thumbnailUrl?.toUri())
|
.setArtworkUri(song.thumbnailUrl?.toUri())
|
||||||
.setExtras(
|
.setExtras(
|
||||||
bundleOf(
|
bundleOf(
|
||||||
"videoId" to song.id,
|
"videoId" to song.id,
|
||||||
"albumId" to album?.browseId,
|
"albumId" to album?.id,
|
||||||
"artistNames" to authors?.map { it.text },
|
"artistNames" to artists?.map { it.name },
|
||||||
"artistIds" to authors?.map { it.browseId },
|
"artistIds" to artists?.map { it.id },
|
||||||
"durationText" to song.durationText,
|
"durationText" to song.durationText,
|
||||||
"loudnessDb" to song.loudnessDb
|
"loudnessDb" to song.loudnessDb
|
||||||
)
|
)
|
||||||
|
@ -166,8 +163,8 @@ fun YouTube.PlaylistOrAlbum.Item.toMediaItem(
|
||||||
"playlistId" to info.endpoint?.playlistId,
|
"playlistId" to info.endpoint?.playlistId,
|
||||||
"albumId" to (if (isFromAlbum) albumId else album?.endpoint?.browseId),
|
"albumId" to (if (isFromAlbum) albumId else album?.endpoint?.browseId),
|
||||||
"durationText" to durationText,
|
"durationText" to durationText,
|
||||||
"artistNames" to (authors ?: playlistOrAlbum.authors)?.map { it.name },
|
"artistNames" to (authors ?: playlistOrAlbum.authors)?.filter { it.endpoint != null }?.map { it.name },
|
||||||
"artistIds" to (authors ?: playlistOrAlbum.authors)?.map { it.endpoint?.browseId }
|
"artistIds" to (authors ?: playlistOrAlbum.authors)?.mapNotNull { it.endpoint?.browseId }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
Loading…
Add table
Reference in a new issue