Add Offline support for tracks

This commit is contained in:
tzugen 2022-04-18 07:31:06 +02:00
parent ee67f4c744
commit 8490f7115d
No known key found for this signature in database
GPG Key ID: 61E9C34BC10EC930
14 changed files with 61 additions and 38 deletions

View File

@ -5,7 +5,7 @@ import androidx.room.PrimaryKey
import java.io.Serializable
import java.util.Date
@Entity
@Entity(tableName = "tracks")
data class Track(
@PrimaryKey override var id: String,
override var parent: String? = null,

View File

@ -24,6 +24,7 @@ class ActiveServerProvider(
private val repository: ServerSettingDao
) : CoroutineScope by CoroutineScope(Dispatchers.IO) {
private var cachedServer: ServerSetting? = null
// FIXME cach never set
private var cachedDatabase: MetaDatabase? = null
private var cachedServerId: Int? = null
@ -110,27 +111,21 @@ class ActiveServerProvider(
Timber.i("Switching to new database, id:$activeServer")
cachedServerId = activeServer
return Room.databaseBuilder(
UApp.applicationContext(),
MetaDatabase::class.java,
METADATA_DB + cachedServerId
)
.addMigrations(META_MIGRATION_2_1)
.fallbackToDestructiveMigrationOnDowngrade()
.build()
cachedDatabase = initDatabase(activeServer)
return cachedDatabase!!
}
val offlineMetaDatabase: MetaDatabase by lazy {
buildDatabase(OFFLINE_DB_ID)
initDatabase(0)
}
private fun buildDatabase(id: Int?): MetaDatabase {
private fun initDatabase(serverId: Int): MetaDatabase {
return Room.databaseBuilder(
UApp.applicationContext(),
MetaDatabase::class.java,
METADATA_DB + id
)
.fallbackToDestructiveMigration()
METADATA_DB + serverId
).fallbackToDestructiveMigration()
.build()
}

View File

@ -32,7 +32,7 @@ interface AlbumDao : GenericDao<Album> {
fun clearByArtist(id: String)
/**
* FIXME: Make generic
* TODO: Make generic
* Upserts (insert or update) an object to the database
*
* @param obj the object to upsert

View File

@ -7,7 +7,7 @@ import androidx.room.Query
import org.moire.ultrasonic.domain.Artist
@Dao
interface ArtistsDao {
interface ArtistDao {
/**
* Insert a list in the database. If the item already exists, replace it.
*

View File

@ -53,6 +53,7 @@ interface IndexDao : GenericDao<Index> {
fun get(musicFolderId: String): List<Index>
/**
* TODO: Make generic
* Upserts (insert or update) an object to the database
*
* @param obj the object to upsert

View File

@ -1,33 +1,36 @@
package org.moire.ultrasonic.data
import androidx.room.AutoMigration
import androidx.room.Database
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.Album
import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.Index
import org.moire.ultrasonic.domain.MusicFolder
import org.moire.ultrasonic.domain.Track
/**
* This database is used to store and cache the ID3 metadata
*/
@Database(
entities = [Artist::class, Album::class, Index::class, MusicFolder::class],
version = 2,
autoMigrations = [
AutoMigration(from = 1, to = 2)
entities = [
Artist::class,
Album::class,
Track::class,
Index::class,
MusicFolder::class
],
version = 2,
exportSchema = true
)
@TypeConverters(Converters::class)
abstract class MetaDatabase : RoomDatabase() {
abstract fun artistsDao(): ArtistsDao
abstract fun artistDao(): ArtistDao
abstract fun albumDao(): AlbumDao
@ -50,7 +53,6 @@ class Converters {
}
}
// FIXME: Check if correct
val META_MIGRATION_2_1: Migration = object : Migration(2, 1) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(

View File

@ -1,9 +1,13 @@
package org.moire.ultrasonic.data
import androidx.room.Dao
import androidx.room.Entity
import androidx.room.Query
import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.Track
interface TrackDao {
@Dao
@Entity(tableName = "tracks")
interface TrackDao : GenericDao<Track> {
/**
* Clear the whole database
*/
@ -14,12 +18,11 @@ interface TrackDao {
* Get all albums
*/
@Query("SELECT * FROM tracks")
fun get(): List<MusicDirectory.Album>
fun get(): List<Track>
/**
* Get albums by artist
*/
@Query("SELECT * FROM tracks WHERE albumId LIKE :id")
fun byAlbum(id: String): List<MusicDirectory.Entry>
}
fun byAlbum(id: String): List<Track>
}

View File

@ -600,7 +600,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
listModel.getRandom(albumListSize)
} else {
setTitle(name)
if (!isOffline() && Settings.shouldUseId3Tags) {
if (!isOffline() && Settings.shouldUseId3Tags || Settings.useId3TagsOffline) {
if (isAlbum) {
listModel.getAlbum(refresh2, id!!, name)
} else {

View File

@ -52,8 +52,9 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
private val cachedGenres = TimeLimitedCache<List<Genre>>(10 * 3600, TimeUnit.SECONDS)
// New Room Database
private var cachedArtists = metaDatabase.artistsDao()
private var cachedArtists = metaDatabase.artistDao()
private var cachedAlbums = metaDatabase.albumDao()
private var cachedTracks = metaDatabase.trackDao()
private var cachedIndexes = metaDatabase.indexDao()
private val cachedMusicFolders = metaDatabase.musicFoldersDao()
@ -353,7 +354,7 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
if (!Util.equals(newUrl, restUrl) || !Util.equals(cachedMusicFolderId, newFolderId)) {
// Switch database
metaDatabase = activeServerProvider.getActiveMetaDatabase()
cachedArtists = metaDatabase.artistsDao()
cachedArtists = metaDatabase.artistDao()
cachedAlbums = metaDatabase.albumDao()
cachedIndexes = metaDatabase.indexDao()

View File

@ -44,6 +44,7 @@ import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.domain.UserInfo
import org.moire.ultrasonic.util.AbstractFile
import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator
import org.moire.ultrasonic.util.FileUtil
import org.moire.ultrasonic.util.Storage
import org.moire.ultrasonic.util.Util.safeClose
@ -56,8 +57,9 @@ class OfflineMusicService : MusicService, KoinComponent {
private var metaDatabase: MetaDatabase = activeServerProvider.getActiveMetaDatabase()
// New Room Database
private var cachedArtists = metaDatabase.artistsDao()
private var cachedArtists = metaDatabase.artistDao()
private var cachedAlbums = metaDatabase.albumDao()
private var cachedTracks = metaDatabase.trackDao()
private var cachedIndexes = metaDatabase.indexDao()
private val cachedMusicFolders = metaDatabase.musicFoldersDao()
@ -108,7 +110,7 @@ class OfflineMusicService : MusicService, KoinComponent {
@Throws(OfflineException::class)
override fun getArtists(refresh: Boolean): List<Artist> {
var result = cachedArtists.get()
val result = cachedArtists.get()
if (result.isEmpty()) {
// use indexes?
@ -478,7 +480,15 @@ class OfflineMusicService : MusicService, KoinComponent {
@Throws(OfflineException::class)
override fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory {
throw OfflineException("getAlbum isn't available in offline mode")
val list = cachedTracks
.byAlbum(id)
.sortedWith(EntryByDiscAndTrackComparator())
var dir = MusicDirectory()
dir.addAll(list)
return dir
}
@Throws(OfflineException::class)

View File

@ -86,6 +86,7 @@ object Constants {
const val PREFERENCES_KEY_INCREMENT_TIME = "incrementTime"
const val PREFERENCES_KEY_SHOW_NOW_PLAYING_DETAILS = "showNowPlayingDetails"
const val PREFERENCES_KEY_ID3_TAGS = "useId3Tags"
const val PREFERENCES_KEY_ID3_TAGS_OFFLINE = "useId3TagsOffline"
const val PREFERENCES_KEY_SHOW_ARTIST_PICTURE = "showArtistPicture"
const val PREFERENCES_KEY_CHAT_REFRESH_INTERVAL = "chatRefreshInterval"
const val PREFERENCES_KEY_DIRECTORY_CACHE_TIME = "directoryCacheTime"

View File

@ -130,6 +130,7 @@ object Settings {
@JvmStatic
var mediaButtonsEnabled
by BooleanSetting(Constants.PREFERENCES_KEY_MEDIA_BUTTONS, true)
var resumePlayOnHeadphonePlug
by BooleanSetting(R.string.setting_keys_resume_play_on_headphones_plug, true)
@ -162,6 +163,9 @@ object Settings {
@JvmStatic
var shouldUseId3Tags by BooleanSetting(Constants.PREFERENCES_KEY_ID3_TAGS, false)
@JvmStatic
var useId3TagsOffline by BooleanSetting(Constants.PREFERENCES_KEY_ID3_TAGS_OFFLINE, false)
var activeServer by IntSetting(Constants.PREFERENCES_KEY_SERVER_INSTANCE, -1)
var serverScaling by BooleanSetting(Constants.PREFERENCES_KEY_SERVER_SCALING, false)
@ -244,8 +248,6 @@ object Settings {
var useHwOffload by BooleanSetting(Constants.PREFERENCES_KEY_HARDWARE_OFFLOAD, false)
var useId3TagsOffline = true
// TODO: Remove in December 2022
fun migrateFeatureStorage() {
val sp = appContext.getSharedPreferences("feature_flags", Context.MODE_PRIVATE)

View File

@ -316,6 +316,8 @@
<string name="settings.show_now_playing_details">Show details in Now Playing</string>
<string name="settings.use_id3">Browse Using ID3 Tags</string>
<string name="settings.use_id3_summary">Use ID3 tag methods instead of file system based methods</string>
<string name="settings.use_id3_offline">Use ID3 method also when offline</string>
<string name="settings.use_id3_offline_summary">(Experimental)</string>
<string name="settings.show_artist_picture">Show artist picture in artist list</string>
<string name="settings.show_artist_picture_summary">Displays the artist picture in the artist list if available</string>
<string name="main.video" tools:ignore="UnusedResources">Video</string>

View File

@ -59,6 +59,12 @@
a:summary="@string/settings.use_id3_summary"
a:title="@string/settings.use_id3"
app:iconSpaceReserved="false"/>
<CheckBoxPreference
a:defaultValue="true"
a:key="useId3TagsOffline"
a:summary="@string/settings.use_id3_offline_summary"
a:title="@string/settings.use_id3_offline"
app:iconSpaceReserved="false"/>
<CheckBoxPreference
a:defaultValue="true"
a:key="showArtistPicture"