diff --git a/core/domain/build.gradle b/core/domain/build.gradle
index bc19d74c..734c977b 100644
--- a/core/domain/build.gradle
+++ b/core/domain/build.gradle
@@ -1,7 +1,14 @@
-apply from: bootstrap.kotlinModule
+apply from: bootstrap.androidModule
+apply plugin: 'kotlin-kapt'
ext {
jacocoExclude = [
'**/domain/**'
]
}
+
+dependencies {
+ implementation androidSupport.roomRuntime
+ implementation androidSupport.roomKtx
+ kapt androidSupport.room
+}
diff --git a/core/domain/src/main/AndroidManifest.xml b/core/domain/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..1d8d8efa
--- /dev/null
+++ b/core/domain/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Artist.kt b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Artist.kt
index b7112e2f..6c0537d3 100644
--- a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Artist.kt
+++ b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Artist.kt
@@ -1,18 +1,17 @@
package org.moire.ultrasonic.domain
-import java.io.Serializable
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+@Entity(tableName = "artists")
data class Artist(
- override var id: String? = null,
+ @PrimaryKey override var id: String,
override var name: String? = null,
- var index: String? = null,
- var coverArt: String? = null,
- var albumCount: Long? = null,
- var closeness: Int = 0
-) : Serializable, GenericEntry(), Comparable {
- companion object {
- private const val serialVersionUID = -5790532593784846982L
- }
+ override var index: String? = null,
+ override var coverArt: String? = null,
+ override var albumCount: Long? = null,
+ override var closeness: Int = 0
+) : ArtistOrIndex(id), Comparable {
override fun compareTo(other: Artist): Int {
when {
diff --git a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/ArtistOrIndex.kt b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/ArtistOrIndex.kt
new file mode 100644
index 00000000..63decd3a
--- /dev/null
+++ b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/ArtistOrIndex.kt
@@ -0,0 +1,18 @@
+package org.moire.ultrasonic.domain
+
+import androidx.room.Ignore
+
+open class ArtistOrIndex(
+ @Ignore
+ override var id: String,
+ @Ignore
+ override var name: String? = null,
+ @Ignore
+ open var index: String? = null,
+ @Ignore
+ open var coverArt: String? = null,
+ @Ignore
+ open var albumCount: Long? = null,
+ @Ignore
+ open var closeness: Int = 0
+) : GenericEntry()
diff --git a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/GenericEntry.kt b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/GenericEntry.kt
index 37bd863f..e731b769 100644
--- a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/GenericEntry.kt
+++ b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/GenericEntry.kt
@@ -1,8 +1,12 @@
package org.moire.ultrasonic.domain
-abstract class GenericEntry {
- // TODO: Should be non-null!
- abstract val id: String?
+import androidx.room.Ignore
+
+open class GenericEntry {
+ // TODO Should be non-null!
+ @Ignore
+ open val id: String? = null
+ @Ignore
open val name: String? = null
// These are just a formality and will never be called,
diff --git a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Genre.kt b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Genre.kt
index e80ed2b2..49045571 100644
--- a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Genre.kt
+++ b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Genre.kt
@@ -1,11 +1,14 @@
package org.moire.ultrasonic.domain
+import androidx.room.Entity
+import androidx.room.PrimaryKey
import java.io.Serializable
+@Entity
data class Genre(
- val name: String,
- val index: String
-) : Serializable {
+ @PrimaryKey val index: String,
+ override val name: String
+) : Serializable, GenericEntry() {
companion object {
private const val serialVersionUID = -3943025175219134028L
}
diff --git a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Index.kt b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Index.kt
new file mode 100644
index 00000000..e56399b1
--- /dev/null
+++ b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Index.kt
@@ -0,0 +1,15 @@
+package org.moire.ultrasonic.domain
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "indexes")
+data class Index(
+ @PrimaryKey override var id: String,
+ override var name: String? = null,
+ override var index: String? = null,
+ override var coverArt: String? = null,
+ override var albumCount: Long? = null,
+ override var closeness: Int = 0,
+ var musicFolderId: String? = null
+) : ArtistOrIndex(id)
diff --git a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Indexes.kt b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Indexes.kt
deleted file mode 100644
index 3d5ef194..00000000
--- a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Indexes.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.moire.ultrasonic.domain
-
-import java.io.Serializable
-
-data class Indexes(
- val lastModified: Long,
- val ignoredArticles: String,
- val shortcuts: MutableList = mutableListOf(),
- val artists: MutableList = mutableListOf()
-) : Serializable {
- companion object {
- private const val serialVersionUID = 8156117238598414701L
- }
-}
diff --git a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/MusicDirectory.kt b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/MusicDirectory.kt
index cdd035a5..1d0bc146 100644
--- a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/MusicDirectory.kt
+++ b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/MusicDirectory.kt
@@ -1,5 +1,7 @@
package org.moire.ultrasonic.domain
+import androidx.room.Entity
+import androidx.room.PrimaryKey
import java.io.Serializable
import java.util.Date
@@ -35,8 +37,9 @@ class MusicDirectory {
return children.filter { it.isDirectory && includeDirs || !it.isDirectory && includeFiles }
}
+ @Entity
data class Entry(
- override var id: String,
+ @PrimaryKey override var id: String,
var parent: String? = null,
var isDirectory: Boolean = false,
var title: String? = null,
diff --git a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/MusicFolder.kt b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/MusicFolder.kt
index 1c23e86c..c4f94fb6 100644
--- a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/MusicFolder.kt
+++ b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/MusicFolder.kt
@@ -1,9 +1,13 @@
package org.moire.ultrasonic.domain
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
/**
* Represents a top level directory in which music or other media is stored.
*/
+@Entity(tableName = "music_folders")
data class MusicFolder(
- override val id: String,
+ @PrimaryKey override val id: String,
override val name: String
) : GenericEntry()
diff --git a/gradle_scripts/android-module-bootstrap.gradle b/gradle_scripts/android-module-bootstrap.gradle
index 847827e7..88519a56 100644
--- a/gradle_scripts/android-module-bootstrap.gradle
+++ b/gradle_scripts/android-module-bootstrap.gradle
@@ -1,9 +1,11 @@
+/**
+ * This module provides a base for for submodules which depend on the Android runtime
+ */
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'jacoco'
apply from: "${project.rootDir}/gradle_scripts/code_quality.gradle"
-
android {
compileSdkVersion versions.compileSdk
diff --git a/gradle_scripts/kotlin-module-bootstrap.gradle b/gradle_scripts/kotlin-module-bootstrap.gradle
index ac732a7f..1eb26ec6 100644
--- a/gradle_scripts/kotlin-module-bootstrap.gradle
+++ b/gradle_scripts/kotlin-module-bootstrap.gradle
@@ -1,5 +1,8 @@
-apply plugin: 'java-library'
+/**
+ * This module provides a base for for pure kotlin modules
+ */
apply plugin: 'kotlin'
+apply plugin: 'kotlin-kapt'
apply plugin: 'jacoco'
apply from: "${project.rootDir}/gradle_scripts/code_quality.gradle"
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ActiveServerProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ActiveServerProvider.kt
index d9c11c86..d1a5c18b 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ActiveServerProvider.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ActiveServerProvider.kt
@@ -1,5 +1,6 @@
package org.moire.ultrasonic.data
+import androidx.room.Room
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@@ -7,6 +8,7 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.moire.ultrasonic.R
import org.moire.ultrasonic.app.UApp
+import org.moire.ultrasonic.di.DB_FILENAME
import org.moire.ultrasonic.service.MusicServiceFactory.resetMusicService
import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.Util
@@ -20,6 +22,8 @@ class ActiveServerProvider(
private val repository: ServerSettingDao
) {
private var cachedServer: ServerSetting? = null
+ private var cachedDatabase: MetaDatabase? = null
+ private var cachedServerId: Int? = null
/**
* Get the settings of the current Active Server
@@ -82,6 +86,33 @@ class ActiveServerProvider(
}
}
+ @Synchronized
+ fun getActiveMetaDatabase(): MetaDatabase {
+ val activeServer = getActiveServerId()
+
+ if (activeServer == cachedServerId && cachedDatabase != null) {
+ return cachedDatabase!!
+ }
+
+ Timber.i("Switching to new database, id:$activeServer")
+ cachedServerId = activeServer
+ val db = Room.databaseBuilder(
+ UApp.applicationContext(),
+ MetaDatabase::class.java,
+ METADATA_DB + cachedServerId
+ )
+ .fallbackToDestructiveMigrationOnDowngrade()
+ .build()
+ return db
+ }
+
+ @Synchronized
+ fun deleteMetaDatabase(id: Int) {
+ cachedDatabase?.close()
+ UApp.applicationContext().deleteDatabase(METADATA_DB + id)
+ Timber.i("Deleted metadataBase, id:$id")
+ }
+
/**
* Sets the minimum Subsonic API version of the current server.
*/
@@ -130,6 +161,9 @@ class ActiveServerProvider(
}
companion object {
+
+ const val METADATA_DB = "$DB_FILENAME-meta-"
+
/**
* Queries if the Active Server is the "Offline" mode of Ultrasonic
* @return True, if the "Offline" mode is selected
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/AppDatabase.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/AppDatabase.kt
index e4aa77dc..e8c05cc7 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/AppDatabase.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/AppDatabase.kt
@@ -6,7 +6,8 @@ import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
/**
- * Room Database to be used to store data for Ultrasonic
+ * Room Database to be used to store global data for the whole app.
+ * This could be settings or data that are not specific to any remote music database
*/
@Database(entities = [ServerSetting::class], version = 3)
abstract class AppDatabase : RoomDatabase() {
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ArtistsDao.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ArtistsDao.kt
new file mode 100644
index 00000000..2a86d496
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ArtistsDao.kt
@@ -0,0 +1,31 @@
+package org.moire.ultrasonic.data
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import org.moire.ultrasonic.domain.Artist
+
+@Dao
+interface ArtistsDao {
+ /**
+ * Insert a list in the database. If the item already exists, replace it.
+ *
+ * @param objects the items to be inserted.
+ */
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ @JvmSuppressWildcards
+ fun set(objects: List)
+
+ /**
+ * Clear the whole database
+ */
+ @Query("DELETE FROM artists")
+ fun clear()
+
+ /**
+ * Get all artists
+ */
+ @Query("SELECT * FROM artists")
+ fun get(): List
+}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/BasicDaos.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/BasicDaos.kt
new file mode 100644
index 00000000..ab69089f
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/BasicDaos.kt
@@ -0,0 +1,146 @@
+package org.moire.ultrasonic.data
+
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.Transaction
+import androidx.room.Update
+import org.moire.ultrasonic.domain.Index
+import org.moire.ultrasonic.domain.MusicFolder
+
+@Dao
+interface MusicFoldersDao : GenericDao {
+ /**
+ * Clear the whole database
+ */
+ @Query("DELETE FROM music_folders")
+ fun clear()
+
+ /**
+ * Get all folders
+ */
+ @Query("SELECT * FROM music_folders")
+ fun get(): List
+}
+
+@Dao
+interface IndexDao : GenericDao {
+
+ /**
+ * Clear the whole database
+ */
+ @Query("DELETE FROM indexes")
+ fun clear()
+
+ /**
+ * Get all indexes
+ */
+ @Query("SELECT * FROM indexes")
+ fun get(): List
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun insertAll(vararg indexes: Index)
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun insertArray(arr: Array)
+
+ /**
+ * Get all indexes for a specific folder id
+ */
+ @Query("SELECT * FROM indexes where musicFolderId LIKE :musicFolderId")
+ fun get(musicFolderId: String): List
+
+ /**
+ * Upserts (insert or update) an object to the database
+ *
+ * @param obj the object to upsert
+ */
+ @Transaction
+ @JvmSuppressWildcards
+ fun upsert(obj: Index) {
+ 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) {
+ val insertResult = insertIgnoring(objList)
+ val updateList: MutableList = ArrayList()
+ for (i in insertResult.indices) {
+ if (insertResult[i] == -1L) {
+ updateList.add(objList[i])
+ }
+ }
+ if (updateList.isNotEmpty()) {
+ update(updateList)
+ }
+ }
+}
+
+interface GenericDao {
+ /**
+ * Replaces the list with a new collection
+ *
+ * @param objects the items to be inserted.
+ */
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ @JvmSuppressWildcards
+ fun set(objects: List)
+
+ /**
+ * Insert an object in the database.
+ *
+ * @param obj the object to be inserted.
+ * @return The SQLite row id
+ */
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ @JvmSuppressWildcards
+ fun insertIgnoring(obj: T): Long
+
+ /**
+ * Insert an array of objects in the database.
+ *
+ * @param obj the objects to be inserted.
+ * @return The SQLite row ids
+ */
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ @JvmSuppressWildcards
+ fun insertIgnoring(obj: List?): List
+
+ /**
+ * Update an object from the database.
+ *
+ * @param obj the object to be updated
+ */
+ @Update
+ @JvmSuppressWildcards
+ fun update(obj: T)
+
+ /**
+ * Update an array of objects from the database.
+ *
+ * @param obj the object to be updated
+ */
+ @Update
+ @JvmSuppressWildcards
+ fun update(obj: List?)
+
+ /**
+ * Delete an object from the database
+ *
+ * @param obj the object to be deleted
+ */
+ @Delete
+ @JvmSuppressWildcards
+ fun delete(obj: T)
+}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/MetaDatabase.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/MetaDatabase.kt
new file mode 100644
index 00000000..6abdc85a
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/MetaDatabase.kt
@@ -0,0 +1,19 @@
+package org.moire.ultrasonic.data
+
+import androidx.room.Database
+import androidx.room.RoomDatabase
+import org.moire.ultrasonic.domain.Artist
+import org.moire.ultrasonic.domain.Index
+import org.moire.ultrasonic.domain.MusicFolder
+
+@Database(
+ entities = [Artist::class, Index::class, MusicFolder::class],
+ version = 1
+)
+abstract class MetaDatabase : RoomDatabase() {
+ abstract fun artistsDao(): ArtistsDao
+
+ abstract fun musicFoldersDao(): MusicFoldersDao
+
+ abstract fun indexDao(): IndexDao
+}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt
index bb4c3575..39c3fe91 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt
@@ -65,7 +65,7 @@ val musicServiceModule = module {
single { SubsonicAPIClient(get(), get()) }
single(named(ONLINE_MUSIC_SERVICE)) {
- CachedMusicService(RESTMusicService(get(), get(), get()))
+ CachedMusicService(RESTMusicService(get(), get()))
}
single(named(OFFLINE_MUSIC_SERVICE)) {
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIArtistConverter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIArtistConverter.kt
index cec72778..51c2c72f 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIArtistConverter.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIArtistConverter.kt
@@ -5,12 +5,20 @@ package org.moire.ultrasonic.domain
import org.moire.ultrasonic.api.subsonic.models.Artist as APIArtist
+// When we like to convert to an Artist
fun APIArtist.toDomainEntity(): Artist = Artist(
id = this@toDomainEntity.id,
coverArt = this@toDomainEntity.coverArt,
name = this@toDomainEntity.name
)
+// When we like to convert to an index (eg. a single directory).
+fun APIArtist.toIndexEntity(): Index = Index(
+ id = this@toIndexEntity.id,
+ coverArt = this@toIndexEntity.coverArt,
+ name = this@toIndexEntity.name
+)
+
fun APIArtist.toMusicDirectoryDomainEntity(): MusicDirectory = MusicDirectory().apply {
name = this@toMusicDirectoryDomainEntity.name
addAll(this@toMusicDirectoryDomainEntity.albumsList.map { it.toDomainEntity() })
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIIndexesConverter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIIndexesConverter.kt
index b41bcb66..fb718204 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIIndexesConverter.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIIndexesConverter.kt
@@ -3,15 +3,37 @@
@file:JvmName("APIIndexesConverter")
package org.moire.ultrasonic.domain
-import org.moire.ultrasonic.api.subsonic.models.Index
+import org.moire.ultrasonic.api.subsonic.models.Index as APIIndex
import org.moire.ultrasonic.api.subsonic.models.Indexes as APIIndexes
-fun APIIndexes.toDomainEntity(): Indexes = Indexes(
- this.lastModified, this.ignoredArticles,
- this.shortcutList.map { it.toDomainEntity() }.toMutableList(),
- this.indexList.foldIndexToArtistList().toMutableList()
+fun APIIndexes.toArtistList(): List {
+ val list = this.shortcutList.map { it.toDomainEntity() }.toMutableList()
+ list.addAll(this.indexList.foldIndexToArtistList())
+ return list
+}
+
+fun APIIndexes.toIndexList(musicFolderId: String?): List {
+ val list = this.shortcutList.map { it.toIndexEntity() }.toMutableList()
+ list.addAll(this.indexList.foldIndexToIndexList(musicFolderId))
+ return list
+}
+
+private fun List.foldIndexToArtistList(): List = this.fold(
+ listOf(),
+ { acc, index ->
+ acc + index.artists.map {
+ it.toDomainEntity()
+ }
+ }
)
-private fun List.foldIndexToArtistList(): List = this.fold(
- listOf(), { acc, index -> acc + index.artists.map { it.toDomainEntity() } }
+private fun List.foldIndexToIndexList(musicFolderId: String?): List = this.fold(
+ listOf(),
+ { acc, index ->
+ acc + index.artists.map {
+ val ret = it.toIndexEntity()
+ ret.musicFolderId = musicFolderId
+ ret
+ }
+ }
)
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListFragment.kt
index 6b48979c..51d42f65 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListFragment.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListFragment.kt
@@ -4,13 +4,13 @@ import android.os.Bundle
import androidx.fragment.app.viewModels
import androidx.lifecycle.LiveData
import org.moire.ultrasonic.R
-import org.moire.ultrasonic.domain.Artist
+import org.moire.ultrasonic.domain.ArtistOrIndex
import org.moire.ultrasonic.util.Constants
/**
* Displays the list of Artists from the media library
*/
-class ArtistListFragment : GenericListFragment() {
+class ArtistListFragment : GenericListFragment() {
/**
* The ViewModel to use to get the data
@@ -41,7 +41,7 @@ class ArtistListFragment : GenericListFragment() {
/**
* The central function to pass a query to the model and return a LiveData object
*/
- override fun getLiveData(args: Bundle?): LiveData> {
+ override fun getLiveData(args: Bundle?): LiveData> {
val refresh = args?.getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH) ?: false
return listModel.getItems(refresh, refreshListView!!)
}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListModel.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListModel.kt
index a774ebcd..c014a059 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListModel.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListModel.kt
@@ -23,19 +23,19 @@ import android.os.Bundle
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
-import org.moire.ultrasonic.domain.Artist
+import org.moire.ultrasonic.domain.ArtistOrIndex
import org.moire.ultrasonic.service.MusicService
/**
* Provides ViewModel which contains the list of available Artists
*/
class ArtistListModel(application: Application) : GenericListModel(application) {
- private val artists: MutableLiveData> = MutableLiveData(listOf())
+ private val artists: MutableLiveData> = MutableLiveData(listOf())
/**
* Retrieves all available Artists in a LiveData
*/
- fun getItems(refresh: Boolean, swipe: SwipeRefreshLayout): LiveData> {
+ fun getItems(refresh: Boolean, swipe: SwipeRefreshLayout): LiveData> {
// Don't reload the data if navigating back to the view that was active before.
// This way, we keep the scroll position
if (artists.value!!.isEmpty() || refresh) {
@@ -55,14 +55,14 @@ class ArtistListModel(application: Application) : GenericListModel(application)
val musicFolderId = activeServer.musicFolderId
- val result = if (!isOffline && useId3Tags)
- musicService.getArtists(refresh)
- else musicService.getIndexes(musicFolderId, refresh)
+ val result: List
- val retrievedArtists: MutableList =
- ArrayList(result.shortcuts.size + result.artists.size)
- retrievedArtists.addAll(result.shortcuts)
- retrievedArtists.addAll(result.artists)
- artists.postValue(retrievedArtists)
+ if (!isOffline && useId3Tags) {
+ result = musicService.getArtists(refresh)
+ } else {
+ result = musicService.getIndexes(musicFolderId, refresh)
+ }
+
+ artists.postValue(result.toMutableList())
}
}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt
index 69988607..0b375bfc 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt
@@ -13,7 +13,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView.SectionedAdapter
import java.text.Collator
import org.moire.ultrasonic.R
-import org.moire.ultrasonic.domain.Artist
+import org.moire.ultrasonic.domain.ArtistOrIndex
import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.imageloader.ImageLoader
import org.moire.ultrasonic.util.Util
@@ -22,12 +22,12 @@ import org.moire.ultrasonic.util.Util
* Creates a Row in a RecyclerView which contains the details of an Artist
*/
class ArtistRowAdapter(
- artistList: List,
- onItemClick: (Artist) -> Unit,
- onContextMenuClick: (MenuItem, Artist) -> Boolean,
+ artistList: List,
+ onItemClick: (ArtistOrIndex) -> Unit,
+ onContextMenuClick: (MenuItem, ArtistOrIndex) -> Boolean,
private val imageLoader: ImageLoader,
onMusicFolderUpdate: (String?) -> Unit
-) : GenericRowAdapter(
+) : GenericRowAdapter(
onItemClick,
onContextMenuClick,
onMusicFolderUpdate
@@ -43,7 +43,7 @@ class ArtistRowAdapter(
/**
* Sets the data to be displayed in the RecyclerView
*/
- override fun setData(data: List) {
+ override fun setData(data: List) {
itemList = data.sortedWith(compareBy(Collator.getInstance()) { t -> t.name })
super.notifyDataSetChanged()
}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericListModel.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericListModel.kt
index 468802a2..958db756 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericListModel.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericListModel.kt
@@ -45,7 +45,7 @@ open class GenericListModel(application: Application) :
return true
}
- internal val musicFolders: MutableLiveData> = MutableLiveData()
+ internal val musicFolders: MutableLiveData> = MutableLiveData(listOf())
/**
* Helper function to check online status
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ServerSelectorFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ServerSelectorFragment.kt
index bde3efd5..9d66f6ef 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ServerSelectorFragment.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ServerSelectorFragment.kt
@@ -8,7 +8,6 @@ import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ListView
import androidx.fragment.app.Fragment
-import androidx.lifecycle.Observer
import androidx.navigation.fragment.findNavController
import com.google.android.material.floatingactionbutton.FloatingActionButton
import kotlinx.coroutines.Dispatchers
@@ -104,7 +103,7 @@ class ServerSelectorFragment : Fragment() {
val serverList = serverSettingsModel.getServerList()
serverList.observe(
this,
- Observer { t ->
+ { t ->
serverRowAdapter!!.setData(t.toTypedArray())
}
)
@@ -141,10 +140,16 @@ class ServerSelectorFragment : Fragment() {
dialog.dismiss()
val activeServerIndex = activeServerProvider.getActiveServer().index
+ val id = ActiveServerProvider.getActiveServerId()
+
// If the currently active server is deleted, go offline
if (index == activeServerIndex) setActiveServer(-1)
serverSettingsModel.deleteItem(index)
+
+ // Clear the metadata cache
+ activeServerProvider.deleteMetaDatabase(id)
+
Timber.i("Server deleted: $index")
}
.setNegativeButton(R.string.common_cancel) { dialog, _ ->
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/CachedMusicService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/CachedMusicService.kt
index f18a0f26..7ee29a55 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/CachedMusicService.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/CachedMusicService.kt
@@ -11,10 +11,12 @@ import java.util.concurrent.TimeUnit
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.moire.ultrasonic.data.ActiveServerProvider
+import org.moire.ultrasonic.data.MetaDatabase
+import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.Bookmark
import org.moire.ultrasonic.domain.ChatMessage
import org.moire.ultrasonic.domain.Genre
-import org.moire.ultrasonic.domain.Indexes
+import org.moire.ultrasonic.domain.Index
import org.moire.ultrasonic.domain.JukeboxStatus
import org.moire.ultrasonic.domain.Lyrics
import org.moire.ultrasonic.domain.MusicDirectory
@@ -33,19 +35,24 @@ import org.moire.ultrasonic.util.Util
@Suppress("TooManyFunctions")
class CachedMusicService(private val musicService: MusicService) : MusicService, KoinComponent {
private val activeServerProvider: ActiveServerProvider by inject()
+ private var metaDatabase: MetaDatabase = activeServerProvider.getActiveMetaDatabase()
+
+ // Old style TimeLimitedCache
+ private val cachedMusicDirectories: LRUCache>
+ private val cachedArtist: LRUCache>
+ private val cachedAlbum: LRUCache>
+ private val cachedUserInfo: LRUCache>
+ private val cachedLicenseValid = TimeLimitedCache(120, TimeUnit.SECONDS)
+ private val cachedPlaylists = TimeLimitedCache?>(3600, TimeUnit.SECONDS)
+ private val cachedPodcastsChannels =
+ TimeLimitedCache?>(3600, TimeUnit.SECONDS)
+ private val cachedGenres = TimeLimitedCache>(10 * 3600, TimeUnit.SECONDS)
+
+ // New Room Database
+ private var cachedArtists = metaDatabase.artistsDao()
+ private var cachedIndexes = metaDatabase.indexDao()
+ private val cachedMusicFolders = metaDatabase.musicFoldersDao()
- private val cachedMusicDirectories: LRUCache>
- private val cachedArtist: LRUCache>
- private val cachedAlbum: LRUCache>
- private val cachedUserInfo: LRUCache>
- private val cachedLicenseValid = TimeLimitedCache(expiresAfter = 10, TimeUnit.MINUTES)
- private val cachedIndexes = TimeLimitedCache()
- private val cachedArtists = TimeLimitedCache()
- private val cachedPlaylists = TimeLimitedCache?>()
- private val cachedPodcastsChannels = TimeLimitedCache>()
- private val cachedMusicFolders =
- TimeLimitedCache?>(10, TimeUnit.HOURS)
- private val cachedGenres = TimeLimitedCache?>(10, TimeUnit.HOURS)
private var restUrl: String? = null
private var cachedMusicFolderId: String? = null
@@ -72,41 +79,51 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
if (refresh) {
cachedMusicFolders.clear()
}
+ var result = cachedMusicFolders.get()
- val cache = cachedMusicFolders.get()
- if (cache != null) return cache
-
- val result = musicService.getMusicFolders(refresh)
- cachedMusicFolders.set(result)
-
+ if (result.isEmpty()) {
+ result = musicService.getMusicFolders(refresh)
+ cachedMusicFolders.set(result)
+ }
return result
}
@Throws(Exception::class)
- override fun getIndexes(musicFolderId: String?, refresh: Boolean): Indexes {
+ override fun getIndexes(musicFolderId: String?, refresh: Boolean): List {
checkSettingsChanged()
+
if (refresh) {
cachedIndexes.clear()
- cachedMusicFolders.clear()
cachedMusicDirectories.clear()
}
- var result = cachedIndexes.get()
- if (result == null) {
- result = musicService.getIndexes(musicFolderId, refresh)
- cachedIndexes.set(result)
+
+ var indexes: List
+
+ if (musicFolderId == null) {
+ indexes = cachedIndexes.get()
+ } else {
+ indexes = cachedIndexes.get(musicFolderId)
}
- return result
+
+ if (indexes.isEmpty()) {
+ indexes = musicService.getIndexes(musicFolderId, refresh)
+ cachedIndexes.upsert(indexes)
+ }
+
+ return indexes
}
@Throws(Exception::class)
- override fun getArtists(refresh: Boolean): Indexes {
+ override fun getArtists(refresh: Boolean): List {
checkSettingsChanged()
if (refresh) {
cachedArtists.clear()
}
var result = cachedArtists.get()
- if (result == null) {
+
+ if (result.isEmpty()) {
result = musicService.getArtists(refresh)
+ cachedArtist.clear()
cachedArtists.set(result)
}
return result
@@ -296,19 +313,26 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
return musicService.setJukeboxGain(gain)
}
+ @Synchronized
private fun checkSettingsChanged() {
val newUrl = activeServerProvider.getRestUrl(null)
val newFolderId = activeServerProvider.getActiveServer().musicFolderId
if (!Util.equals(newUrl, restUrl) || !Util.equals(cachedMusicFolderId, newFolderId)) {
- cachedMusicFolders.clear()
+ // Switch database
+ metaDatabase = activeServerProvider.getActiveMetaDatabase()
+ cachedArtists = metaDatabase.artistsDao()
+ cachedIndexes = metaDatabase.indexDao()
+
+ // Clear in memory caches
cachedMusicDirectories.clear()
cachedLicenseValid.clear()
- cachedIndexes.clear()
cachedPlaylists.clear()
cachedGenres.clear()
cachedAlbum.clear()
cachedArtist.clear()
cachedUserInfo.clear()
+
+ // Set the cache keys
restUrl = newUrl
cachedMusicFolderId = newFolderId
}
@@ -330,7 +354,7 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
}
@Throws(Exception::class)
- override fun getGenres(refresh: Boolean): List? {
+ override fun getGenres(refresh: Boolean): List {
checkSettingsChanged()
if (refresh) {
cachedGenres.clear()
@@ -338,11 +362,11 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
var result = cachedGenres.get()
if (result == null) {
result = musicService.getGenres(refresh)
- cachedGenres.set(result)
+ cachedGenres.set(result!!)
}
- val sorted = result?.toMutableList()
- sorted?.sortWith { genre, genre2 ->
+ val sorted = result.toMutableList()
+ sorted.sortWith { genre, genre2 ->
genre.name.compareTo(
genre2.name,
ignoreCase = true
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MusicService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MusicService.kt
index 6e417358..73521d6e 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MusicService.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MusicService.kt
@@ -7,10 +7,11 @@
package org.moire.ultrasonic.service
import java.io.InputStream
+import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.Bookmark
import org.moire.ultrasonic.domain.ChatMessage
import org.moire.ultrasonic.domain.Genre
-import org.moire.ultrasonic.domain.Indexes
+import org.moire.ultrasonic.domain.Index
import org.moire.ultrasonic.domain.JukeboxStatus
import org.moire.ultrasonic.domain.Lyrics
import org.moire.ultrasonic.domain.MusicDirectory
@@ -46,10 +47,10 @@ interface MusicService {
fun getMusicFolders(refresh: Boolean): List
@Throws(Exception::class)
- fun getIndexes(musicFolderId: String?, refresh: Boolean): Indexes
+ fun getIndexes(musicFolderId: String?, refresh: Boolean): List
@Throws(Exception::class)
- fun getArtists(refresh: Boolean): Indexes
+ fun getArtists(refresh: Boolean): List
@Throws(Exception::class)
fun getMusicDirectory(id: String, name: String?, refresh: Boolean): MusicDirectory
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/OfflineMusicService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/OfflineMusicService.kt
index e06a3a22..044fdc2d 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/OfflineMusicService.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/OfflineMusicService.kt
@@ -28,7 +28,7 @@ import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.Bookmark
import org.moire.ultrasonic.domain.ChatMessage
import org.moire.ultrasonic.domain.Genre
-import org.moire.ultrasonic.domain.Indexes
+import org.moire.ultrasonic.domain.Index
import org.moire.ultrasonic.domain.JukeboxStatus
import org.moire.ultrasonic.domain.Lyrics
import org.moire.ultrasonic.domain.MusicDirectory
@@ -50,21 +50,21 @@ import timber.log.Timber
class OfflineMusicService : MusicService, KoinComponent {
private val activeServerProvider: ActiveServerProvider by inject()
- override fun getIndexes(musicFolderId: String?, refresh: Boolean): Indexes {
- val artists: MutableList = ArrayList()
+ override fun getIndexes(musicFolderId: String?, refresh: Boolean): List {
+ val indexes: MutableList = ArrayList()
val root = FileUtil.getMusicDirectory()
for (file in FileUtil.listFiles(root)) {
if (file.isDirectory) {
- val artist = Artist()
- artist.id = file.path
- artist.index = file.name.substring(0, 1)
- artist.name = file.name
- artists.add(artist)
+ val index = Index(file.path)
+ index.id = file.path
+ index.index = file.name.substring(0, 1)
+ index.name = file.name
+ indexes.add(index)
}
}
val ignoredArticlesString = "The El La Los Las Le Les"
val ignoredArticles = COMPILE.split(ignoredArticlesString)
- artists.sortWith { lhsArtist, rhsArtist ->
+ indexes.sortWith { lhsArtist, rhsArtist ->
var lhs = lhsArtist.name!!.lowercase(Locale.ROOT)
var rhs = rhsArtist.name!!.lowercase(Locale.ROOT)
val lhs1 = lhs[0]
@@ -92,7 +92,7 @@ class OfflineMusicService : MusicService, KoinComponent {
lhs.compareTo(rhs)
}
- return Indexes(0L, ignoredArticlesString, artists = artists)
+ return indexes
}
override fun getMusicDirectory(
@@ -127,8 +127,7 @@ class OfflineMusicService : MusicService, KoinComponent {
val artistName = artistFile.name
if (artistFile.isDirectory) {
if (matchCriteria(criteria, artistName).also { closeness = it } > 0) {
- val artist = Artist()
- artist.id = artistFile.path
+ val artist = Artist(artistFile.path)
artist.index = artistFile.name.substring(0, 1)
artist.name = artistName
artist.closeness = closeness
@@ -442,7 +441,7 @@ class OfflineMusicService : MusicService, KoinComponent {
override fun isLicenseValid(): Boolean = true
@Throws(OfflineException::class)
- override fun getArtists(refresh: Boolean): Indexes {
+ override fun getArtists(refresh: Boolean): List {
throw OfflineException("getArtists isn't available in offline mode")
}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/RESTMusicService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/RESTMusicService.kt
index 0d6730d8..b964a90c 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/RESTMusicService.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/RESTMusicService.kt
@@ -6,9 +6,6 @@
*/
package org.moire.ultrasonic.service
-import java.io.BufferedWriter
-import java.io.File
-import java.io.FileWriter
import java.io.IOException
import java.io.InputStream
import okhttp3.Protocol
@@ -20,15 +17,13 @@ import org.moire.ultrasonic.api.subsonic.models.AlbumListType.Companion.fromName
import org.moire.ultrasonic.api.subsonic.models.JukeboxAction
import org.moire.ultrasonic.api.subsonic.throwOnFailure
import org.moire.ultrasonic.api.subsonic.toStreamResponse
-import org.moire.ultrasonic.cache.PermanentFileStorage
-import org.moire.ultrasonic.cache.serializers.getIndexesSerializer
-import org.moire.ultrasonic.cache.serializers.getMusicFolderListSerializer
import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
+import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.Bookmark
import org.moire.ultrasonic.domain.ChatMessage
import org.moire.ultrasonic.domain.Genre
-import org.moire.ultrasonic.domain.Indexes
+import org.moire.ultrasonic.domain.Index
import org.moire.ultrasonic.domain.JukeboxStatus
import org.moire.ultrasonic.domain.Lyrics
import org.moire.ultrasonic.domain.MusicDirectory
@@ -39,11 +34,14 @@ import org.moire.ultrasonic.domain.SearchCriteria
import org.moire.ultrasonic.domain.SearchResult
import org.moire.ultrasonic.domain.Share
import org.moire.ultrasonic.domain.UserInfo
+import org.moire.ultrasonic.domain.toArtistList
import org.moire.ultrasonic.domain.toDomainEntitiesList
import org.moire.ultrasonic.domain.toDomainEntity
import org.moire.ultrasonic.domain.toDomainEntityList
+import org.moire.ultrasonic.domain.toIndexList
import org.moire.ultrasonic.domain.toMusicDirectoryDomainEntity
import org.moire.ultrasonic.util.FileUtil
+import org.moire.ultrasonic.util.FileUtilKt
import org.moire.ultrasonic.util.Util
import timber.log.Timber
@@ -53,7 +51,6 @@ import timber.log.Timber
@Suppress("LargeClass")
open class RESTMusicService(
val subsonicAPIClient: SubsonicAPIClient,
- private val fileStorage: PermanentFileStorage,
private val activeServerProvider: ActiveServerProvider
) : MusicService {
@@ -77,49 +74,31 @@ open class RESTMusicService(
override fun getMusicFolders(
refresh: Boolean
): List {
- val cachedMusicFolders = fileStorage.load(
- MUSIC_FOLDER_STORAGE_NAME, getMusicFolderListSerializer()
- )
-
- if (cachedMusicFolders != null && !refresh) return cachedMusicFolders
-
val response = API.getMusicFolders().execute().throwOnFailure()
- val musicFolders = response.body()!!.musicFolders.toDomainEntityList()
- fileStorage.store(MUSIC_FOLDER_STORAGE_NAME, musicFolders, getMusicFolderListSerializer())
-
- return musicFolders
+ return response.body()!!.musicFolders.toDomainEntityList()
}
+ /**
+ * Retrieves the artists for a given music folder *
+ */
@Throws(Exception::class)
override fun getIndexes(
musicFolderId: String?,
refresh: Boolean
- ): Indexes {
- val indexName = INDEXES_STORAGE_NAME + (musicFolderId ?: "")
-
- val cachedIndexes = fileStorage.load(indexName, getIndexesSerializer())
- if (cachedIndexes != null && !refresh) return cachedIndexes
-
+ ): List {
val response = API.getIndexes(musicFolderId, null).execute().throwOnFailure()
- val indexes = response.body()!!.indexes.toDomainEntity()
- fileStorage.store(indexName, indexes, getIndexesSerializer())
- return indexes
+ return response.body()!!.indexes.toIndexList(musicFolderId)
}
@Throws(Exception::class)
override fun getArtists(
refresh: Boolean
- ): Indexes {
- val cachedArtists = fileStorage.load(ARTISTS_STORAGE_NAME, getIndexesSerializer())
- if (cachedArtists != null && !refresh) return cachedArtists
-
+ ): List {
val response = API.getArtists(null).execute().throwOnFailure()
- val indexes = response.body()!!.indexes.toDomainEntity()
- fileStorage.store(ARTISTS_STORAGE_NAME, indexes, getIndexesSerializer())
- return indexes
+ return response.body()!!.indexes.toArtistList()
}
@Throws(Exception::class)
@@ -186,11 +165,11 @@ open class RESTMusicService(
criteria: SearchCriteria
): SearchResult {
return try {
- if (
- !isOffline() &&
- Util.getShouldUseId3Tags()
- ) search3(criteria)
- else search2(criteria)
+ if (!isOffline() && Util.getShouldUseId3Tags()) {
+ search3(criteria)
+ } else {
+ search2(criteria)
+ }
} catch (ignored: ApiNotSupportedException) {
// Ensure backward compatibility with REST 1.3.
searchOld(criteria)
@@ -262,28 +241,7 @@ open class RESTMusicService(
activeServerProvider.getActiveServer().name, name
)
- val fw = FileWriter(playlistFile)
- val bw = BufferedWriter(fw)
-
- try {
- fw.write("#EXTM3U\n")
- for (e in playlist.getChildren()) {
- var filePath = FileUtil.getSongFile(e).absolutePath
-
- if (!File(filePath).exists()) {
- val ext = FileUtil.getExtension(filePath)
- val base = FileUtil.getBaseName(filePath)
- filePath = "$base.complete.$ext"
- }
- fw.write(filePath + "\n")
- }
- } catch (e: IOException) {
- Timber.w("Failed to save playlist: %s", name)
- throw e
- } finally {
- bw.close()
- fw.close()
- }
+ FileUtilKt.savePlaylist(playlistFile, playlist, name)
}
@Throws(Exception::class)
@@ -711,10 +669,4 @@ open class RESTMusicService(
activeServerProvider.setMinimumApiVersion(it.restApiVersion)
}
}
-
- companion object {
- private const val MUSIC_FOLDER_STORAGE_NAME = "music_folder"
- private const val INDEXES_STORAGE_NAME = "indexes"
- private const val ARTISTS_STORAGE_NAME = "artists"
- }
}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/FileUtilKt.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/FileUtilKt.kt
new file mode 100644
index 00000000..fc527c15
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/FileUtilKt.kt
@@ -0,0 +1,47 @@
+/*
+ * FileUtil.kt
+ * Copyright (C) 2009-2021 Ultrasonic developers
+ *
+ * Distributed under terms of the GNU GPLv3 license.
+ */
+
+package org.moire.ultrasonic.util
+
+import java.io.BufferedWriter
+import java.io.File
+import java.io.FileWriter
+import java.io.IOException
+import org.moire.ultrasonic.domain.MusicDirectory
+import timber.log.Timber
+
+// TODO: Convert FileUtil.java and merge into here.
+object FileUtilKt {
+ fun savePlaylist(
+ playlistFile: File?,
+ playlist: MusicDirectory,
+ name: String
+ ) {
+ val fw = FileWriter(playlistFile)
+ val bw = BufferedWriter(fw)
+
+ try {
+ fw.write("#EXTM3U\n")
+ for (e in playlist.getChildren()) {
+ var filePath = FileUtil.getSongFile(e).absolutePath
+
+ if (!File(filePath).exists()) {
+ val ext = FileUtil.getExtension(filePath)
+ val base = FileUtil.getBaseName(filePath)
+ filePath = "$base.complete.$ext"
+ }
+ fw.write(filePath + "\n")
+ }
+ } catch (e: IOException) {
+ Timber.w("Failed to save playlist: %s", name)
+ throw e
+ } finally {
+ bw.close()
+ fw.close()
+ }
+ }
+}
diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIIndexesConverterTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIIndexConverterTest.kt
similarity index 72%
rename from ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIIndexesConverterTest.kt
rename to ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIIndexConverterTest.kt
index d5b052c4..ca23b15f 100644
--- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIIndexesConverterTest.kt
+++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIIndexConverterTest.kt
@@ -11,7 +11,7 @@ import org.moire.ultrasonic.api.subsonic.models.Indexes
/**
* Unit tests for extension functions in [APIIndexesConverter.kt].
*/
-class APIIndexesConverterTest {
+class APIIndexConverterTest {
@Test
fun `Should convert Indexes entity`() {
val artistsA = listOf(
@@ -31,15 +31,12 @@ class APIIndexesConverterTest {
shortcutList = artistsA
)
- val convertedEntity = entity.toDomainEntity()
+ val convertedEntity = entity.toIndexList(null)
val expectedArtists = (artistsA + artistsT).map { it.toDomainEntity() }.toMutableList()
with(convertedEntity) {
- lastModified `should be equal to` entity.lastModified
- ignoredArticles `should be equal to` entity.ignoredArticles
- artists.size `should be equal to` expectedArtists.size
- artists `should be equal to` expectedArtists
- shortcuts `should be equal to` artistsA.map { it.toDomainEntity() }.toMutableList()
+ size `should be equal to` expectedArtists.size
+ this `should be equal to` expectedArtists
}
}
}