Add AlbumDao, rename getArtist to getAlbumsOfArtist

This commit is contained in:
tzugen 2022-04-18 07:12:31 +02:00
parent 3445576dc9
commit 3a3bd10fdb
No known key found for this signature in database
GPG Key ID: 61E9C34BC10EC930
16 changed files with 676 additions and 49 deletions

View File

@ -1,8 +1,10 @@
package org.moire.ultrasonic.domain package org.moire.ultrasonic.domain
import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import java.util.Date import java.util.Date
@Entity(tableName = "albums")
data class Album( data class Album(
@PrimaryKey override var id: String, @PrimaryKey override var id: String,
override var parent: String? = null, override var parent: String? = null,

View File

@ -0,0 +1,474 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "b6ac795e7857eac4fed2dbbd01f80fb8",
"entities": [
{
"tableName": "artists",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `index` TEXT, `coverArt` TEXT, `albumCount` INTEGER, `closeness` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "index",
"columnName": "index",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "coverArt",
"columnName": "coverArt",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "albumCount",
"columnName": "albumCount",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "closeness",
"columnName": "closeness",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "albums",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `parent` TEXT, `album` TEXT, `title` TEXT, `name` TEXT, `discNumber` INTEGER, `coverArt` TEXT, `songCount` INTEGER, `created` INTEGER, `artist` TEXT, `artistId` TEXT, `duration` INTEGER, `year` INTEGER, `genre` TEXT, `starred` INTEGER NOT NULL, `path` TEXT, `closeness` INTEGER NOT NULL, `isDirectory` INTEGER NOT NULL, `isVideo` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "parent",
"columnName": "parent",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "album",
"columnName": "album",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "discNumber",
"columnName": "discNumber",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "coverArt",
"columnName": "coverArt",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "songCount",
"columnName": "songCount",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "created",
"columnName": "created",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "artist",
"columnName": "artist",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "artistId",
"columnName": "artistId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "duration",
"columnName": "duration",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "year",
"columnName": "year",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "genre",
"columnName": "genre",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "starred",
"columnName": "starred",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "closeness",
"columnName": "closeness",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isDirectory",
"columnName": "isDirectory",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isVideo",
"columnName": "isVideo",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "tracks",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `parent` TEXT, `isDirectory` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `albumId` TEXT, `artist` TEXT, `artistId` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `contentType` TEXT, `suffix` TEXT, `transcodedContentType` TEXT, `transcodedSuffix` TEXT, `coverArt` TEXT, `size` INTEGER, `songCount` INTEGER, `duration` INTEGER, `bitRate` INTEGER, `path` TEXT, `isVideo` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `discNumber` INTEGER, `type` TEXT, `created` INTEGER, `closeness` INTEGER NOT NULL, `bookmarkPosition` INTEGER NOT NULL, `userRating` INTEGER, `averageRating` REAL, `name` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "parent",
"columnName": "parent",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isDirectory",
"columnName": "isDirectory",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "album",
"columnName": "album",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "albumId",
"columnName": "albumId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "artist",
"columnName": "artist",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "artistId",
"columnName": "artistId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "track",
"columnName": "track",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "year",
"columnName": "year",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "genre",
"columnName": "genre",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "contentType",
"columnName": "contentType",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "suffix",
"columnName": "suffix",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "transcodedContentType",
"columnName": "transcodedContentType",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "transcodedSuffix",
"columnName": "transcodedSuffix",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "coverArt",
"columnName": "coverArt",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "size",
"columnName": "size",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "songCount",
"columnName": "songCount",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "duration",
"columnName": "duration",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "bitRate",
"columnName": "bitRate",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isVideo",
"columnName": "isVideo",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "starred",
"columnName": "starred",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "discNumber",
"columnName": "discNumber",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "created",
"columnName": "created",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "closeness",
"columnName": "closeness",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "bookmarkPosition",
"columnName": "bookmarkPosition",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userRating",
"columnName": "userRating",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "averageRating",
"columnName": "averageRating",
"affinity": "REAL",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "indexes",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `index` TEXT, `coverArt` TEXT, `albumCount` INTEGER, `closeness` INTEGER NOT NULL, `musicFolderId` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "index",
"columnName": "index",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "coverArt",
"columnName": "coverArt",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "albumCount",
"columnName": "albumCount",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "closeness",
"columnName": "closeness",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "musicFolderId",
"columnName": "musicFolderId",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "music_folders",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"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, 'b6ac795e7857eac4fed2dbbd01f80fb8')"
]
}
}

View File

@ -19,6 +19,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.drakeet.multitype.ItemViewBinder import com.drakeet.multitype.ItemViewBinder
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.domain.ArtistOrIndex import org.moire.ultrasonic.domain.ArtistOrIndex
import org.moire.ultrasonic.domain.Identifiable import org.moire.ultrasonic.domain.Identifiable
import org.moire.ultrasonic.imageloader.ImageLoader import org.moire.ultrasonic.imageloader.ImageLoader
@ -57,7 +58,7 @@ class ArtistRowBinder(
holder.coverArtId = item.coverArt holder.coverArtId = item.coverArt
if (Settings.shouldShowArtistPicture) { if (showArtistPicture()) {
holder.coverArt.visibility = View.VISIBLE holder.coverArt.visibility = View.VISIBLE
val key = FileUtil.getArtistArtKey(item.name, false) val key = FileUtil.getArtistArtKey(item.name, false)
imageLoader.loadImage( imageLoader.loadImage(
@ -108,6 +109,12 @@ class ArtistRowBinder(
return section.toString() return section.toString()
} }
private fun showArtistPicture(): Boolean {
val isOffline = ActiveServerProvider.isOffline()
val shouldShowArtistPicture = Settings.shouldShowArtistPicture
return (!isOffline && shouldShowArtistPicture) || Settings.useId3TagsOffline
}
/** /**
* Creates an instance of our ViewHolder class * Creates an instance of our ViewHolder class
*/ */

View File

@ -110,7 +110,14 @@ class ActiveServerProvider(
Timber.i("Switching to new database, id:$activeServer") Timber.i("Switching to new database, id:$activeServer")
cachedServerId = activeServer cachedServerId = activeServer
return buildDatabase(cachedServerId) return Room.databaseBuilder(
UApp.applicationContext(),
MetaDatabase::class.java,
METADATA_DB + cachedServerId
)
.addMigrations(META_MIGRATION_2_1)
.fallbackToDestructiveMigrationOnDowngrade()
.build()
} }
val offlineMetaDatabase: MetaDatabase by lazy { val offlineMetaDatabase: MetaDatabase by lazy {

View File

@ -0,0 +1,68 @@
package org.moire.ultrasonic.data
import androidx.room.Dao
import androidx.room.Query
import androidx.room.Transaction
import org.moire.ultrasonic.domain.Album
@Dao
interface AlbumDao : GenericDao<Album> {
/**
* Clear the whole database
*/
@Query("DELETE FROM albums")
fun clear()
/**
* Get all albums
*/
@Query("SELECT * FROM albums")
fun get(): List<Album>
/**
* Get albums by artist
*/
@Query("SELECT * FROM albums WHERE artistId LIKE :id")
fun byArtist(id: String): List<Album>
/**
* Clear albums by artist
*/
@Query("DELETE FROM albums WHERE artistId LIKE :id")
fun clearByArtist(id: String)
/**
* FIXME: Make generic
* Upserts (insert or update) an object to the database
*
* @param obj the object to upsert
*/
@Transaction
@JvmSuppressWildcards
fun upsert(obj: Album) {
val id = insertIgnoring(obj)
if (id == -1L) {
update(obj)
}
}
/**
* Upserts (insert or update) a list of objects
*
* @param objList the object to be upserted
*/
@Transaction
@JvmSuppressWildcards
fun upsert(objList: List<Album>) {
val insertResult = insertIgnoring(objList)
val updateList: MutableList<Album> = ArrayList()
for (i in insertResult.indices) {
if (insertResult[i] == -1L) {
updateList.add(objList[i])
}
}
if (updateList.isNotEmpty()) {
update(updateList)
}
}
}

View File

@ -43,5 +43,5 @@ interface ArtistsDao {
* Get artist by id * Get artist by id
*/ */
@Query("SELECT * FROM artists WHERE id LIKE :id") @Query("SELECT * FROM artists WHERE id LIKE :id")
fun get(id: String): Artist fun get(id: String): Artist?
} }

View File

@ -1,7 +1,14 @@
package org.moire.ultrasonic.data package org.moire.ultrasonic.data
import androidx.room.AutoMigration
import androidx.room.Database import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.room.TypeConverter
import androidx.room.TypeConverters
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import org.moire.ultrasonic.domain.Album
import java.util.Date
import org.moire.ultrasonic.domain.Artist import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.Index import org.moire.ultrasonic.domain.Index
import org.moire.ultrasonic.domain.MusicFolder import org.moire.ultrasonic.domain.MusicFolder
@ -11,14 +18,41 @@ import org.moire.ultrasonic.domain.MusicFolder
*/ */
@Database( @Database(
entities = [Artist::class, Index::class, MusicFolder::class], entities = [Artist::class, Album::class, Index::class, MusicFolder::class],
version = 1, version = 2,
autoMigrations = [
AutoMigration(from = 1, to = 2)
],
exportSchema = true exportSchema = true
) )
@TypeConverters(Converters::class)
abstract class MetaDatabase : RoomDatabase() { abstract class MetaDatabase : RoomDatabase() {
abstract fun artistsDao(): ArtistsDao abstract fun artistsDao(): ArtistsDao
abstract fun albumDao(): AlbumDao
abstract fun musicFoldersDao(): MusicFoldersDao abstract fun musicFoldersDao(): MusicFoldersDao
abstract fun indexDao(): IndexDao abstract fun indexDao(): IndexDao
} }
class Converters {
@TypeConverter
fun fromTimestamp(value: Long?): Date? {
return value?.let { Date(it) }
}
@TypeConverter
fun dateToTimestamp(date: Date?): Long? {
return date?.time
}
}
// FIXME: Check if correct
val META_MIGRATION_2_1: Migration = object : Migration(2, 1) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"DROP TABLE ServerSetting"
)
}
}

View File

@ -39,7 +39,7 @@ class AlbumListModel(application: Application) : GenericListModel(application) {
id: String, id: String,
name: String? name: String?
) { ) {
list.postValue(musicService.getArtist(id, name, refresh)) list.postValue(musicService.getAlbumsOfArtist(id, name, refresh))
} }
override fun load( override fun load(

View File

@ -26,6 +26,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import java.text.Collator import java.text.Collator
import org.moire.ultrasonic.domain.ArtistOrIndex import org.moire.ultrasonic.domain.ArtistOrIndex
import org.moire.ultrasonic.service.MusicService import org.moire.ultrasonic.service.MusicService
import org.moire.ultrasonic.util.Settings
/** /**
* Provides ViewModel which contains the list of available Artists * Provides ViewModel which contains the list of available Artists
@ -58,7 +59,7 @@ class ArtistListModel(application: Application) : GenericListModel(application)
val result: List<ArtistOrIndex> val result: List<ArtistOrIndex>
if (!isOffline && useId3Tags) { if (!isOffline && useId3Tags || Settings.useId3TagsOffline) {
result = musicService.getArtists(refresh) result = musicService.getArtists(refresh)
} else { } else {
result = musicService.getIndexes(musicFolderId, refresh) result = musicService.getIndexes(musicFolderId, refresh)

View File

@ -635,7 +635,7 @@ class AutoMediaBrowserCallback(var player: Player, val libraryService: MediaLibr
return serviceScope.future { return serviceScope.future {
val albums = if (!isOffline && useId3Tags) { val albums = if (!isOffline && useId3Tags) {
callWithErrorHandling { musicService.getArtist(id, name, false) } callWithErrorHandling { musicService.getAlbumsOfArtist(id, name, false) }
} else { } else {
callWithErrorHandling { callWithErrorHandling {
musicService.getMusicDirectory(id, name, false).getAlbums() musicService.getMusicDirectory(id, name, false).getAlbums()

View File

@ -43,7 +43,6 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
// Old style TimeLimitedCache // Old style TimeLimitedCache
private val cachedMusicDirectories: LRUCache<String, TimeLimitedCache<MusicDirectory?>> private val cachedMusicDirectories: LRUCache<String, TimeLimitedCache<MusicDirectory?>>
private val cachedArtist: LRUCache<String, TimeLimitedCache<List<Album>>>
private val cachedAlbum: LRUCache<String, TimeLimitedCache<MusicDirectory?>> private val cachedAlbum: LRUCache<String, TimeLimitedCache<MusicDirectory?>>
private val cachedUserInfo: LRUCache<String, TimeLimitedCache<UserInfo?>> private val cachedUserInfo: LRUCache<String, TimeLimitedCache<UserInfo?>>
private val cachedLicenseValid = TimeLimitedCache<Boolean>(120, TimeUnit.SECONDS) private val cachedLicenseValid = TimeLimitedCache<Boolean>(120, TimeUnit.SECONDS)
@ -54,6 +53,7 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
// New Room Database // New Room Database
private var cachedArtists = metaDatabase.artistsDao() private var cachedArtists = metaDatabase.artistsDao()
private var cachedAlbums = metaDatabase.albumDao()
private var cachedIndexes = metaDatabase.indexDao() private var cachedIndexes = metaDatabase.indexDao()
private val cachedMusicFolders = metaDatabase.musicFoldersDao() private val cachedMusicFolders = metaDatabase.musicFoldersDao()
@ -103,10 +103,10 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
var indexes: List<Index> var indexes: List<Index>
if (musicFolderId == null) { indexes = if (musicFolderId == null) {
indexes = cachedIndexes.get() cachedIndexes.get()
} else { } else {
indexes = cachedIndexes.get(musicFolderId) cachedIndexes.get(musicFolderId)
} }
if (indexes.isEmpty()) { if (indexes.isEmpty()) {
@ -117,17 +117,40 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
return indexes return indexes
} }
// FIXME why commented?
// @Throws(Exception::class)
// override fun getArtist(id: String, refresh: Boolean): Artist? {
//
// // Check if we have a cache hit
// var result = cachedArtists.get(id)
//
// if (result == null || refresh) {
// musicService.getArtists(refresh = true)
// }
// //var result = cachedArtists.get()
//
// if (result.isEmpty()) {
// result = getArtists(refresh)
// // FIXME
// // cachedAlbums.clear()
// cachedArtists.set(result)
// }
// return result
// }
//
@Throws(Exception::class) @Throws(Exception::class)
override fun getArtists(refresh: Boolean): List<Artist> { override fun getArtists(refresh: Boolean): List<Artist> {
checkSettingsChanged() checkSettingsChanged()
if (refresh) { if (refresh) {
cachedArtists.clear() cachedArtists.clear()
} }
// FIXME unnecessary check
var result = cachedArtists.get() var result = cachedArtists.get()
if (result.isEmpty()) { if (result.isEmpty()) {
result = musicService.getArtists(refresh) result = musicService.getArtists(refresh)
cachedArtist.clear() // FIXME
// cachedAlbums.clear()
cachedArtists.set(result) cachedArtists.set(result)
} }
return result return result
@ -150,20 +173,24 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
} }
@Throws(Exception::class) @Throws(Exception::class)
override fun getArtist(id: String, name: String?, refresh: Boolean): override fun getAlbumsOfArtist(id: String, name: String?, refresh: Boolean):
List<Album> { List<Album> {
checkSettingsChanged() checkSettingsChanged()
var cache = if (refresh) null else cachedArtist[id]
var dir = cache?.get() var result: List<Album>
if (dir == null) {
dir = musicService.getArtist(id, name, refresh) result = if (refresh) {
cache = TimeLimitedCache( cachedAlbums.clearByArtist(id)
Settings.directoryCacheTime.toLong(), TimeUnit.SECONDS listOf()
) } else {
cache.set(dir) cachedAlbums.byArtist(id)
cachedArtist.put(id, cache)
} }
return dir
if (result.isEmpty()) {
result = musicService.getAlbumsOfArtist(id, name, refresh)
cachedAlbums.upsert(result)
}
return result
} }
@Throws(Exception::class) @Throws(Exception::class)
@ -327,6 +354,7 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
// Switch database // Switch database
metaDatabase = activeServerProvider.getActiveMetaDatabase() metaDatabase = activeServerProvider.getActiveMetaDatabase()
cachedArtists = metaDatabase.artistsDao() cachedArtists = metaDatabase.artistsDao()
cachedAlbums = metaDatabase.albumDao()
cachedIndexes = metaDatabase.indexDao() cachedIndexes = metaDatabase.indexDao()
// Clear in memory caches // Clear in memory caches
@ -335,7 +363,6 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
cachedPlaylists.clear() cachedPlaylists.clear()
cachedGenres.clear() cachedGenres.clear()
cachedAlbum.clear() cachedAlbum.clear()
cachedArtist.clear()
cachedUserInfo.clear() cachedUserInfo.clear()
// Set the cache keys // Set the cache keys
@ -472,7 +499,6 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
init { init {
cachedMusicDirectories = LRUCache(MUSIC_DIR_CACHE_SIZE) cachedMusicDirectories = LRUCache(MUSIC_DIR_CACHE_SIZE)
cachedArtist = LRUCache(MUSIC_DIR_CACHE_SIZE)
cachedAlbum = LRUCache(MUSIC_DIR_CACHE_SIZE) cachedAlbum = LRUCache(MUSIC_DIR_CACHE_SIZE)
cachedUserInfo = LRUCache(MUSIC_DIR_CACHE_SIZE) cachedUserInfo = LRUCache(MUSIC_DIR_CACHE_SIZE)
} }

View File

@ -59,7 +59,7 @@ interface MusicService {
fun getMusicDirectory(id: String, name: String?, refresh: Boolean): MusicDirectory fun getMusicDirectory(id: String, name: String?, refresh: Boolean): MusicDirectory
@Throws(Exception::class) @Throws(Exception::class)
fun getArtist(id: String, name: String?, refresh: Boolean): List<Album> fun getAlbumsOfArtist(id: String, name: String?, refresh: Boolean): List<Album>
@Throws(Exception::class) @Throws(Exception::class)
fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory

View File

@ -23,6 +23,7 @@ import java.util.regex.Pattern
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.data.MetaDatabase
import org.moire.ultrasonic.domain.Album import org.moire.ultrasonic.domain.Album
import org.moire.ultrasonic.domain.Artist import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.ArtistOrIndex import org.moire.ultrasonic.domain.ArtistOrIndex
@ -52,6 +53,14 @@ import timber.log.Timber
class OfflineMusicService : MusicService, KoinComponent { class OfflineMusicService : MusicService, KoinComponent {
private val activeServerProvider: ActiveServerProvider by inject() private val activeServerProvider: ActiveServerProvider by inject()
private var metaDatabase: MetaDatabase = activeServerProvider.getActiveMetaDatabase()
// New Room Database
private var cachedArtists = metaDatabase.artistsDao()
private var cachedAlbums = metaDatabase.albumDao()
private var cachedIndexes = metaDatabase.indexDao()
private val cachedMusicFolders = metaDatabase.musicFoldersDao()
override fun getIndexes(musicFolderId: String?, refresh: Boolean): List<Index> { override fun getIndexes(musicFolderId: String?, refresh: Boolean): List<Index> {
val indexes: MutableList<Index> = ArrayList() val indexes: MutableList<Index> = ArrayList()
val root = FileUtil.musicDirectory val root = FileUtil.musicDirectory
@ -97,6 +106,16 @@ class OfflineMusicService : MusicService, KoinComponent {
return indexes return indexes
} }
@Throws(OfflineException::class)
override fun getArtists(refresh: Boolean): List<Artist> {
var result = cachedArtists.get()
if (result.isEmpty()) {
// use indexes?
}
return result
}
/* /*
* Especially when dealing with indexes, this method can return Albums, Entries or a mix of both! * Especially when dealing with indexes, this method can return Albums, Entries or a mix of both!
*/ */
@ -450,15 +469,11 @@ class OfflineMusicService : MusicService, KoinComponent {
override fun isLicenseValid(): Boolean = true override fun isLicenseValid(): Boolean = true
@Throws(OfflineException::class) @Throws(Exception::class)
override fun getArtists(refresh: Boolean): List<Artist> { override fun getAlbumsOfArtist(id: String, name: String?, refresh: Boolean):
throw OfflineException("getArtists isn't available in offline mode")
}
@Throws(OfflineException::class)
override fun getArtist(id: String, name: String?, refresh: Boolean):
List<Album> { List<Album> {
throw OfflineException("getArtist isn't available in offline mode") // FIXME: Add fallback?
return cachedAlbums.byArtist(id)
} }
@Throws(OfflineException::class) @Throws(OfflineException::class)

View File

@ -141,7 +141,7 @@ open class RESTMusicService(
} }
@Throws(Exception::class) @Throws(Exception::class)
override fun getArtist( override fun getAlbumsOfArtist(
id: String, id: String,
name: String?, name: String?,
refresh: Boolean refresh: Boolean

View File

@ -269,7 +269,7 @@ class DownloadHandler(
return return
} }
val musicService = getMusicService() val musicService = getMusicService()
val artist = musicService.getArtist(id, "", false) val artist = musicService.getAlbumsOfArtist(id, "", false)
for ((id1) in artist) { for ((id1) in artist) {
val albumDirectory = musicService.getAlbum( val albumDirectory = musicService.getAlbum(
id1, id1,

View File

@ -13,7 +13,6 @@ import androidx.preference.PreferenceManager
import java.util.regex.Pattern import java.util.regex.Pattern
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.app.UApp import org.moire.ultrasonic.app.UApp
import org.moire.ultrasonic.data.ActiveServerProvider
/** /**
* Contains convenience functions for reading and writing preferences * Contains convenience functions for reading and writing preferences
@ -161,8 +160,7 @@ object Settings {
by BooleanSetting(Constants.PREFERENCES_KEY_SHOW_NOW_PLAYING_DETAILS, false) by BooleanSetting(Constants.PREFERENCES_KEY_SHOW_NOW_PLAYING_DETAILS, false)
@JvmStatic @JvmStatic
var shouldUseId3Tags var shouldUseId3Tags by BooleanSetting(Constants.PREFERENCES_KEY_ID3_TAGS, false)
by BooleanSetting(Constants.PREFERENCES_KEY_ID3_TAGS, false)
var activeServer by IntSetting(Constants.PREFERENCES_KEY_SERVER_INSTANCE, -1) var activeServer by IntSetting(Constants.PREFERENCES_KEY_SERVER_INSTANCE, -1)
@ -170,15 +168,8 @@ object Settings {
var firstRunExecuted by BooleanSetting(Constants.PREFERENCES_KEY_FIRST_RUN_EXECUTED, false) var firstRunExecuted by BooleanSetting(Constants.PREFERENCES_KEY_FIRST_RUN_EXECUTED, false)
val shouldShowArtistPicture: Boolean val shouldShowArtistPicture
get() { by BooleanSetting(Constants.PREFERENCES_KEY_SHOW_ARTIST_PICTURE, false)
val preferences = preferences
val isOffline = ActiveServerProvider.isOffline()
val isId3Enabled = preferences.getBoolean(Constants.PREFERENCES_KEY_ID3_TAGS, false)
val shouldShowArtistPicture =
preferences.getBoolean(Constants.PREFERENCES_KEY_SHOW_ARTIST_PICTURE, false)
return !isOffline && isId3Enabled && shouldShowArtistPicture
}
@JvmStatic @JvmStatic
var chatRefreshInterval by StringIntSetting( var chatRefreshInterval by StringIntSetting(
@ -253,6 +244,8 @@ object Settings {
var useHwOffload by BooleanSetting(Constants.PREFERENCES_KEY_HARDWARE_OFFLOAD, false) var useHwOffload by BooleanSetting(Constants.PREFERENCES_KEY_HARDWARE_OFFLOAD, false)
var useId3TagsOffline = true
// TODO: Remove in December 2022 // TODO: Remove in December 2022
fun migrateFeatureStorage() { fun migrateFeatureStorage() {
val sp = appContext.getSharedPreferences("feature_flags", Context.MODE_PRIVATE) val sp = appContext.getSharedPreferences("feature_flags", Context.MODE_PRIVATE)