Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Singleton sync and chip loader #63

Merged
merged 18 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,259 changes: 1,259 additions & 0 deletions app/schemas/com.dd3boh.outertune.db.InternalDatabase/16.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ val SongFilterKey = stringPreferencesKey("songFilter")
val ArtistFilterKey = stringPreferencesKey("artistFilter")
val ArtistViewTypeKey = stringPreferencesKey("artistViewType")
val AlbumFilterKey = stringPreferencesKey("albumFilter")
val PlaylistFilterKey = stringPreferencesKey("playlistFilter")
val AlbumViewTypeKey = stringPreferencesKey("albumViewType")
val PlaylistViewTypeKey = stringPreferencesKey("playlistViewType")
val LibraryFilterKey = stringPreferencesKey("libraryFilter")
Expand Down Expand Up @@ -179,11 +180,15 @@ enum class SongFilter {
}

enum class ArtistFilter {
LIBRARY, LIKED
LIBRARY, LIKED, DOWNLOADED
}

enum class AlbumFilter {
LIBRARY, LIKED
LIBRARY, LIKED, DOWNLOADED
}

enum class PlaylistFilter {
LIBRARY, DOWNLOADED
}

enum class LibraryFilter {
Expand Down
880 changes: 7 additions & 873 deletions app/src/main/java/com/dd3boh/outertune/db/DatabaseDao.kt

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion app/src/main/java/com/dd3boh/outertune/db/MusicDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class MusicDatabase(
SortedSongAlbumMap::class,
PlaylistSongMapPreview::class
],
version = 15,
version = 16,
exportSchema = true,
autoMigrations = [
AutoMigration(from = 2, to = 3),
Expand All @@ -106,6 +106,7 @@ class MusicDatabase(
AutoMigration(from = 11, to = 12, spec = Migration11To12::class),
AutoMigration(from = 12, to = 13, spec = Migration12To13::class), // Migration from InnerTune
AutoMigration(from = 13, to = 14), // Initial queue as database
AutoMigration(from = 15, to = 16), // Add dateDownload to songs
]
)
@TypeConverters(Converters::class)
Expand All @@ -120,6 +121,7 @@ abstract class InternalDatabase : RoomDatabase() {
delegate = Room.databaseBuilder(context, InternalDatabase::class.java, DB_NAME)
.addMigrations(MIGRATION_1_2)
.addMigrations(MIGRATION_14_15)
.addMigrations(MIGRATION_15_16)
.build()
)
}
Expand Down Expand Up @@ -313,6 +315,12 @@ val MIGRATION_14_15 = object : Migration(14, 15) {
}
}

val MIGRATION_15_16 = object : Migration(15, 16) {
mattcarter11 marked this conversation as resolved.
Show resolved Hide resolved
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE song ADD COLUMN dateDownload Integer NULL DEFAULT NULL")
}
}

@DeleteColumn.Entries(
DeleteColumn(tableName = "song", columnName = "isTrash"),
DeleteColumn(tableName = "playlist", columnName = "author"),
Expand Down
185 changes: 185 additions & 0 deletions app/src/main/java/com/dd3boh/outertune/db/daos/AlbumsDao.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package com.dd3boh.outertune.db.daos

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.RawQuery
import androidx.room.Transaction
import androidx.room.Update
import androidx.room.Upsert
import androidx.sqlite.db.SimpleSQLiteQuery
import androidx.sqlite.db.SupportSQLiteQuery
import com.dd3boh.outertune.constants.AlbumFilter
import com.dd3boh.outertune.constants.AlbumSortType
import com.dd3boh.outertune.db.entities.Album
import com.dd3boh.outertune.db.entities.AlbumArtistMap
import com.dd3boh.outertune.db.entities.AlbumEntity
import com.dd3boh.outertune.db.entities.AlbumWithSongs
import com.dd3boh.outertune.db.entities.ArtistEntity
import com.dd3boh.outertune.db.entities.Song
import com.dd3boh.outertune.db.entities.SongAlbumMap
import com.dd3boh.outertune.extensions.reversed
import com.zionhuang.innertube.models.AlbumItem
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

/*
* Logic related to albums entities and their mapping
*/

@Dao
interface AlbumsDao : ArtistsDao {

// region Gets
@Query("""
SELECT album.*, count(song.dateDownload) downloadCount
FROM album
LEFT JOIN song ON song.albumId = album.id
WHERE album.id = :id
GROUP BY album.id
""")
fun album(id: String): Flow<Album?>

@Query("""
SELECT album.*, count(song.dateDownload) downloadCount
FROM album
LEFT JOIN song ON song.albumId = album.id
WHERE album.title LIKE '%' || :query || '%' AND song.inLibrary IS NOT NULL
GROUP BY album.id
LIMIT :previewSize
""")
fun searchAlbums(query: String, previewSize: Int = Int.MAX_VALUE): Flow<List<Album>>

@Query("""
SELECT album.*, count(song.dateDownload) downloadCount
FROM album
LEFT JOIN song ON song.albumId = album.id
WHERE album.id = :albumId
GROUP BY album.id
""")
fun albumWithSongs(albumId: String): Flow<AlbumWithSongs?>

@Query("SELECT song.* FROM song JOIN song_album_map ON song.id = song_album_map.songId WHERE song_album_map.albumId = :albumId")
fun albumSongs(albumId: String): Flow<List<Song>>

@Query("""
SELECT album.*, count(song.dateDownload) downloadCount
FROM album
JOIN song ON album.id = song.albumId
JOIN event ON song.id = event.songId
WHERE event.timestamp > :fromTimeStamp
GROUP BY album.id
ORDER BY SUM(event.playTime) DESC
LIMIT :limit;
""")
fun mostPlayedAlbums(fromTimeStamp: Long, limit: Int = 6): Flow<List<Album>>

@RawQuery(observedEntities = [AlbumEntity::class])
fun _getAlbum(query: SupportSQLiteQuery): Flow<List<Album>>

fun albums(filter: AlbumFilter, sortType: AlbumSortType, descending: Boolean): Flow<List<Album>> {
val orderBy = when (sortType) {
AlbumSortType.CREATE_DATE -> "album.rowId ASC"
AlbumSortType.NAME -> "album.title COLLATE NOCASE ASC"
AlbumSortType.ARTIST -> """ORDER BY (
SELECT LOWER(GROUP_CONCAT(name, ''))
FROM artist
WHERE id IN (SELECT artistId FROM album_artist_map WHERE albumId = album.id)
ORDER BY name
) COLLATE NOCASE ASC"""
AlbumSortType.YEAR -> "album.year ASC"
AlbumSortType.SONG_COUNT -> "album.songCount ASC"
AlbumSortType.LENGTH -> "album.duration ASC"
AlbumSortType.PLAY_TIME -> "SUM(song.totalPlayTime) ASC"
}

val where = when (filter) {
AlbumFilter.DOWNLOADED -> "song.dateDownload IS NOT NULL"
AlbumFilter.LIBRARY -> "song.inLibrary IS NOT NULL"
AlbumFilter.LIKED -> "album.bookmarkedAt IS NOT NULL"
}

val query = SimpleSQLiteQuery("""
SELECT album.*, count(song.dateDownload) downloadCount
FROM album
LEFT JOIN song ON song.albumId = album.id
WHERE $where
GROUP BY album.id
ORDER BY $orderBy
""")

return _getAlbum(query).map { it.reversed(descending) }
}

fun albumsInLibraryAsc() = albums(AlbumFilter.LIBRARY, AlbumSortType.CREATE_DATE, false)
fun albumsLikedAsc() = albums(AlbumFilter.LIKED, AlbumSortType.CREATE_DATE, false)
// endregion

// region Inserts
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(album: AlbumEntity): Long

@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(map: SongAlbumMap)

@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(map: AlbumArtistMap)

@Transaction
fun insert(albumItem: AlbumItem) {
if (insert(AlbumEntity(
id = albumItem.browseId,
playlistId = albumItem.playlistId,
title = albumItem.title,
year = albumItem.year,
thumbnailUrl = albumItem.thumbnail,
songCount = 0,
duration = 0
)) == -1L
) return
albumItem.artists
?.map { artist ->
ArtistEntity(
id = artist.id ?: artistByName(artist.name)?.id ?: ArtistEntity.generateArtistId(),
name = artist.name
)
}
?.onEach(::insert)
?.mapIndexed { index, artist ->
AlbumArtistMap(
albumId = albumItem.browseId,
artistId = artist.id,
order = index
)
}
?.forEach(::insert)
}
// endregion

// region Updates
@Update
fun update(album: AlbumEntity)

@Upsert
fun upsert(map: SongAlbumMap)

@Transaction
@Query("UPDATE album_artist_map SET artistId = :newId WHERE artistId = :oldId")
fun updateAlbumArtistMap(oldId: String, newId: String)

@Transaction
@Query("DELETE FROM song_artist_map WHERE songId = :songID")
fun unlinkSongArtists(songID: String)
// endregion

// region Deletes
@Delete
fun delete(album: AlbumEntity)

@Transaction
@Query("DELETE FROM album WHERE isLocal = 1")
fun nukeLocalAlbums()
// endregion
}
Loading