From ad54db5bcba117be6228d777c47e95d60e0f8126 Mon Sep 17 00:00:00 2001 From: tzugen Date: Mon, 4 Jul 2022 17:31:58 +0200 Subject: [PATCH] Make Ids composite of Item Id + Server Id --- .../org/moire/ultrasonic/domain/Album.kt | 14 +- .../org/moire/ultrasonic/domain/Artist.kt | 16 +- .../moire/ultrasonic/domain/ArtistOrIndex.kt | 17 +- .../org/moire/ultrasonic/domain/Index.kt | 16 +- .../moire/ultrasonic/domain/MusicDirectory.kt | 8 + .../moire/ultrasonic/domain/MusicFolder.kt | 17 +- .../org/moire/ultrasonic/domain/Track.kt | 14 +- .../3.json | 514 ++++++++++++++++++ .../ultrasonic/data/ActiveServerProvider.kt | 11 +- .../org/moire/ultrasonic/data/MetaDatabase.kt | 28 +- .../ultrasonic/domain/APIAlbumConverter.kt | 18 +- .../ultrasonic/domain/APIArtistConverter.kt | 21 +- .../ultrasonic/domain/APIBookmarkConverter.kt | 15 +- .../ultrasonic/domain/APIIndexesConverter.kt | 53 +- .../domain/APIMusicDirectoryConverter.kt | 20 +- .../domain/APIMusicFolderConverter.kt | 21 +- .../ultrasonic/domain/APIPlaylistConverter.kt | 21 +- .../ultrasonic/domain/APISearchConverter.kt | 27 +- .../ultrasonic/domain/APIShareConverter.kt | 15 +- .../ultrasonic/service/OfflineMusicService.kt | 4 +- .../ultrasonic/service/RESTMusicService.kt | 52 +- .../domain/APIAlbumConverterTest.kt | 13 +- .../domain/APIArtistConverterTest.kt | 8 +- .../domain/APIBookmarkConverterTest.kt | 15 +- .../domain/APIMusicDirectoryConverterTest.kt | 15 +- .../domain/APIMusicFolderConverterTest.kt | 6 +- .../domain/APIPlaylistConverterTest.kt | 9 +- .../domain/APISearchConverterTest.kt | 23 +- .../org/moire/ultrasonic/domain/BaseTest.kt | 12 + 29 files changed, 870 insertions(+), 153 deletions(-) create mode 100644 ultrasonic/schemas/org.moire.ultrasonic.data.MetaDatabase/3.json create mode 100644 ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/BaseTest.kt diff --git a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Album.kt b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Album.kt index 97e5674e..1ddf435b 100644 --- a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Album.kt +++ b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Album.kt @@ -1,12 +1,22 @@ +/* + * Album.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + package org.moire.ultrasonic.domain +import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey import java.util.Date -@Entity(tableName = "albums") +@Entity(tableName = "albums", primaryKeys = ["id", "serverId"]) data class Album( - @PrimaryKey override var id: String, + override var id: String, + @ColumnInfo(defaultValue = "-1") + override var serverId: Int = -1, override var parent: String? = null, override var album: String? = null, override var title: String? = null, 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 3da622c6..b9d70777 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,14 +1,24 @@ +/* + * Artist.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + package org.moire.ultrasonic.domain +import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey -@Entity(tableName = "artists") +@Entity(tableName = "artists", primaryKeys = ["id", "serverId"]) data class Artist( - @PrimaryKey override var id: String, + override var id: String, + @ColumnInfo(defaultValue = "-1") + override var serverId: Int = -1, 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 -) : ArtistOrIndex(id) +) : ArtistOrIndex(id, serverId) 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 index 602cae66..d61dc17c 100644 --- a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/ArtistOrIndex.kt +++ b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/ArtistOrIndex.kt @@ -1,3 +1,10 @@ +/* + * ArtistOrIndex.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + package org.moire.ultrasonic.domain import androidx.room.Ignore @@ -6,6 +13,8 @@ abstract class ArtistOrIndex( @Ignore override var id: String, @Ignore + open var serverId: Int, + @Ignore override var name: String? = null, @Ignore open var index: String? = null, @@ -18,15 +27,15 @@ abstract class ArtistOrIndex( ) : GenericEntry() { fun compareTo(other: ArtistOrIndex): Int { - when { + return when { this.closeness == other.closeness -> { - return 0 + 0 } this.closeness > other.closeness -> { - return -1 + -1 } else -> { - return 1 + 1 } } } 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 index e56399b1..9d9431e7 100644 --- a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Index.kt +++ b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Index.kt @@ -1,15 +1,25 @@ +/* + * Index.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + package org.moire.ultrasonic.domain +import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey -@Entity(tableName = "indexes") +@Entity(tableName = "indexes", primaryKeys = ["id", "serverId"]) data class Index( - @PrimaryKey override var id: String, + override var id: String, + @ColumnInfo(defaultValue = "-1") + override var serverId: Int = -1, 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) +) : ArtistOrIndex(id, serverId) 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 1d937700..23ba8663 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,3 +1,10 @@ +/* + * MusicDirectory.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + package org.moire.ultrasonic.domain import java.util.Date @@ -31,6 +38,7 @@ class MusicDirectory : ArrayList() { abstract class Child : GenericEntry() { abstract override var id: String + abstract var serverId: Int abstract var parent: String? abstract var isDirectory: Boolean abstract var album: String? 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 c4f94fb6..14873928 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,13 +1,22 @@ +/* + * MusicFolder.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + package org.moire.ultrasonic.domain +import androidx.room.ColumnInfo 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") +@Entity(tableName = "music_folders", primaryKeys = ["id", "serverId"]) data class MusicFolder( - @PrimaryKey override val id: String, - override val name: String + override val id: String, + override val name: String, + @ColumnInfo(defaultValue = "-1") + var serverId: Int ) : GenericEntry() diff --git a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Track.kt b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Track.kt index 4c737bec..9a17edee 100644 --- a/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Track.kt +++ b/core/domain/src/main/kotlin/org/moire/ultrasonic/domain/Track.kt @@ -1,13 +1,23 @@ +/* + * Track.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + package org.moire.ultrasonic.domain +import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey import java.io.Serializable import java.util.Date -@Entity(tableName = "tracks") +@Entity(tableName = "tracks", primaryKeys = ["id", "serverId"]) data class Track( - @PrimaryKey override var id: String, + override var id: String, + @ColumnInfo(defaultValue = "-1") + override var serverId: Int = -1, override var parent: String? = null, override var isDirectory: Boolean = false, override var title: String? = null, diff --git a/ultrasonic/schemas/org.moire.ultrasonic.data.MetaDatabase/3.json b/ultrasonic/schemas/org.moire.ultrasonic.data.MetaDatabase/3.json new file mode 100644 index 00000000..e0df55f4 --- /dev/null +++ b/ultrasonic/schemas/org.moire.ultrasonic.data.MetaDatabase/3.json @@ -0,0 +1,514 @@ +{ + "formatVersion": 1, + "database": { + "version": 3, + "identityHash": "95e83d6663a862c03ac46f9567453ded", + "entities": [ + { + "tableName": "artists", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, `name` TEXT, `index` TEXT, `coverArt` TEXT, `albumCount` INTEGER, `closeness` INTEGER NOT NULL, PRIMARY KEY(`id`, `serverId`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "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", + "serverId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "albums", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, `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`, `serverId`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "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", + "serverId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "tracks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, `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`, `serverId`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "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", + "serverId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "indexes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, `name` TEXT, `index` TEXT, `coverArt` TEXT, `albumCount` INTEGER, `closeness` INTEGER NOT NULL, `musicFolderId` TEXT, PRIMARY KEY(`id`, `serverId`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "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", + "serverId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "music_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, `name` TEXT NOT NULL, PRIMARY KEY(`id`, `serverId`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id", + "serverId" + ], + "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, '95e83d6663a862c03ac46f9567453ded')" + ] + } +} \ No newline at end of file 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 cacecd14..a63e9f0d 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ActiveServerProvider.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ActiveServerProvider.kt @@ -1,3 +1,10 @@ +/* + * ActiveServerProvider.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + package org.moire.ultrasonic.data import androidx.room.Room @@ -124,7 +131,9 @@ class ActiveServerProvider( UApp.applicationContext(), MetaDatabase::class.java, METADATA_DB + serverId - ).fallbackToDestructiveMigrationOnDowngrade() + ) + .addMigrations(META_MIGRATION_2_3) + .fallbackToDestructiveMigrationOnDowngrade() .build() } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/MetaDatabase.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/MetaDatabase.kt index c50629fb..8f67634c 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/MetaDatabase.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/MetaDatabase.kt @@ -1,3 +1,10 @@ +/* + * MetaDatabase.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + package org.moire.ultrasonic.data import androidx.room.AutoMigration @@ -5,12 +12,14 @@ import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.TypeConverter import androidx.room.TypeConverters -import java.util.Date +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase 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 +import java.util.Date /** * This database is used to store and cache the ID3 metadata @@ -31,7 +40,7 @@ import org.moire.ultrasonic.domain.Track ), ], exportSchema = true, - version = 2 + version = 3 ) @TypeConverters(Converters::class) abstract class MetaDatabase : RoomDatabase() { @@ -57,3 +66,18 @@ class Converters { return date?.time } } + +val META_MIGRATION_2_3: Migration = object : Migration(2,3) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE `albums`") + database.execSQL("DROP TABLE `indexes`") + database.execSQL("DROP TABLE `artists`") + database.execSQL("DROP TABLE `tracks`") + database.execSQL("DROP TABLE `music_folders`") + database.execSQL("CREATE TABLE IF NOT EXISTS `albums` (`id` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, `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`, `serverId`))") + database.execSQL("CREATE TABLE IF NOT EXISTS `indexes` (`id` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, `name` TEXT, `index` TEXT, `coverArt` TEXT, `albumCount` INTEGER, `closeness` INTEGER NOT NULL, `musicFolderId` TEXT, PRIMARY KEY(`id`, `serverId`))") + database.execSQL("CREATE TABLE IF NOT EXISTS `artists` (`id` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, `name` TEXT, `index` TEXT, `coverArt` TEXT, `albumCount` INTEGER, `closeness` INTEGER NOT NULL, PRIMARY KEY(`id`, `serverId`))") + database.execSQL("CREATE TABLE IF NOT EXISTS `music_folders` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, PRIMARY KEY(`id`, `serverId`))") + database.execSQL("CREATE TABLE IF NOT EXISTS `tracks` (`id` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, `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`, `serverId`))") + } +} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIAlbumConverter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIAlbumConverter.kt index 1f614254..a0754803 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIAlbumConverter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIAlbumConverter.kt @@ -1,3 +1,10 @@ +/* + * APIAlbumConverter.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + // Converts Album entity from [org.moire.ultrasonic.api.subsonic.SubsonicAPIClient] // to app domain entities. @file:JvmName("APIAlbumConverter") @@ -6,8 +13,9 @@ package org.moire.ultrasonic.domain import org.moire.ultrasonic.api.subsonic.models.Album typealias DomainAlbum = org.moire.ultrasonic.domain.Album -fun Album.toDomainEntity(): DomainAlbum = Album( +fun Album.toDomainEntity(serverId: Int): DomainAlbum = Album( id = this@toDomainEntity.id, + serverId = serverId, title = this@toDomainEntity.name ?: this@toDomainEntity.title, album = this@toDomainEntity.album, coverArt = this@toDomainEntity.coverArt, @@ -21,8 +29,10 @@ fun Album.toDomainEntity(): DomainAlbum = Album( starred = this@toDomainEntity.starredDate.isNotEmpty() ) -fun Album.toMusicDirectoryDomainEntity(): MusicDirectory = MusicDirectory().apply { - addAll(this@toMusicDirectoryDomainEntity.songList.map { it.toTrackEntity() }) +fun Album.toMusicDirectoryDomainEntity(serverId: Int): MusicDirectory = MusicDirectory().apply { + addAll(this@toMusicDirectoryDomainEntity.songList.map { it.toTrackEntity(serverId) }) } -fun List.toDomainEntityList(): List = this.map { it.toDomainEntity() } +fun List.toDomainEntityList(serverId: Int): List = this.map { + it.toDomainEntity(serverId) +} 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 7d31b6a1..497eae75 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIArtistConverter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIArtistConverter.kt @@ -1,3 +1,10 @@ +/* + * APIArtistConverter.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + // Converts Artist entity from [org.moire.ultrasonic.api.subsonic.SubsonicAPIClient] // to app domain entities. @file:JvmName("APIArtistConverter") @@ -6,24 +13,26 @@ 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( +fun APIArtist.toDomainEntity(serverId: Int): Artist = Artist( id = this@toDomainEntity.id, + serverId = serverId, 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( +fun APIArtist.toIndexEntity(serverId: Int): Index = Index( id = this@toIndexEntity.id, + serverId = serverId, coverArt = this@toIndexEntity.coverArt, name = this@toIndexEntity.name ) -fun APIArtist.toMusicDirectoryDomainEntity(): MusicDirectory = MusicDirectory().apply { +fun APIArtist.toMusicDirectoryDomainEntity(serverId: Int): MusicDirectory = MusicDirectory().apply { name = this@toMusicDirectoryDomainEntity.name - addAll(this@toMusicDirectoryDomainEntity.albumsList.map { it.toDomainEntity() }) + addAll(this@toMusicDirectoryDomainEntity.albumsList.map { it.toDomainEntity(serverId) }) } -fun APIArtist.toDomainEntityList(): List { - return this.albumsList.map { it.toDomainEntity() } +fun APIArtist.toDomainEntityList(serverId: Int): List { + return this.albumsList.map { it.toDomainEntity(serverId) } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIBookmarkConverter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIBookmarkConverter.kt index 7759290e..ce7e2598 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIBookmarkConverter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIBookmarkConverter.kt @@ -1,16 +1,25 @@ +/* + * APIBookmarkConverter.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + // Contains helper functions to convert api Bookmark entity to domain entity @file:JvmName("APIBookmarkConverter") + package org.moire.ultrasonic.domain import org.moire.ultrasonic.api.subsonic.models.Bookmark as ApiBookmark -fun ApiBookmark.toDomainEntity(): Bookmark = Bookmark( +fun ApiBookmark.toDomainEntity(serverId: Int): Bookmark = Bookmark( position = this@toDomainEntity.position.toInt(), username = this@toDomainEntity.username, comment = this@toDomainEntity.comment, created = this@toDomainEntity.created?.time, changed = this@toDomainEntity.changed?.time, - track = this@toDomainEntity.entry.toTrackEntity() + track = this@toDomainEntity.entry.toTrackEntity(serverId) ) -fun List.toDomainEntitiesList(): List = map { it.toDomainEntity() } +fun List.toDomainEntitiesList(serverId: Int): List = + map { it.toDomainEntity(serverId) } 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 2567ee67..cdbc4453 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIIndexesConverter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIIndexesConverter.kt @@ -1,14 +1,22 @@ +/* + * APIIndexesConverter.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + // Converts Indexes entity from [org.moire.ultrasonic.api.subsonic.SubsonicAPIClient] // to app domain entities. @file:JvmName("APIIndexesConverter") + package org.moire.ultrasonic.domain import org.moire.ultrasonic.api.subsonic.models.Index as APIIndex import org.moire.ultrasonic.api.subsonic.models.Indexes as APIIndexes -fun APIIndexes.toArtistList(): List { - val shortcuts = this.shortcutList.map { it.toDomainEntity() }.toMutableList() - val indexes = this.indexList.foldIndexToArtistList() +fun APIIndexes.toArtistList(serverId: Int): List { + val shortcuts = this.shortcutList.map { it.toDomainEntity(serverId) }.toMutableList() + val indexes = this.indexList.foldIndexToArtistList(serverId) indexes.forEach { if (!shortcuts.contains(it)) { @@ -19,9 +27,9 @@ fun APIIndexes.toArtistList(): List { return shortcuts } -fun APIIndexes.toIndexList(musicFolderId: String?): List { - val shortcuts = this.shortcutList.map { it.toIndexEntity() }.toMutableList() - val indexes = this.indexList.foldIndexToIndexList(musicFolderId) +fun APIIndexes.toIndexList(serverId: Int, musicFolderId: String?): List { + val shortcuts = this.shortcutList.map { it.toIndexEntity(serverId) }.toMutableList() + val indexes = this.indexList.foldIndexToIndexList(musicFolderId, serverId) indexes.forEach { if (!shortcuts.contains(it)) { @@ -32,22 +40,23 @@ fun APIIndexes.toIndexList(musicFolderId: String?): List { return shortcuts } -private fun List.foldIndexToArtistList(): List = this.fold( - listOf(), - { acc, index -> - acc + index.artists.map { - it.toDomainEntity() - } +private fun List.foldIndexToArtistList(serverId: Int): List = this.fold( + listOf() +) { acc, index -> + acc + index.artists.map { + it.toDomainEntity(serverId) } -) +} -private fun List.foldIndexToIndexList(musicFolderId: String?): List = this.fold( - listOf(), - { acc, index -> - acc + index.artists.map { - val ret = it.toIndexEntity() - ret.musicFolderId = musicFolderId - ret - } +private fun List.foldIndexToIndexList( + musicFolderId: String?, + serverId: Int +): List = this.fold( + listOf() +) { acc, index -> + acc + index.artists.map { + val ret = it.toIndexEntity(serverId) + ret.musicFolderId = musicFolderId + ret } -) +} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIMusicDirectoryConverter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIMusicDirectoryConverter.kt index 260eab66..9d800369 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIMusicDirectoryConverter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIMusicDirectoryConverter.kt @@ -1,6 +1,6 @@ /* * APIMusicDirectoryConverter.kt - * Copyright (C) 2009-2021 Ultrasonic developers + * Copyright (C) 2009-2022 Ultrasonic developers * * Distributed under terms of the GNU GPLv3 license. */ @@ -26,12 +26,12 @@ internal val dateFormat: DateFormat by lazy { SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault()) } -fun MusicDirectoryChild.toTrackEntity(): Track = Track(id).apply { +fun MusicDirectoryChild.toTrackEntity(serverId: Int): Track = Track(id, serverId).apply { populateCommonProps(this, this@toTrackEntity) populateTrackProps(this, this@toTrackEntity) } -fun MusicDirectoryChild.toAlbumEntity(): Album = Album(id).apply { +fun MusicDirectoryChild.toAlbumEntity(serverId: Int): Album = Album(id, serverId).apply { populateCommonProps(this, this@toAlbumEntity) } @@ -80,24 +80,24 @@ private fun populateTrackProps( track.averageRating = source.averageRating } -fun List.toDomainEntityList(): List { +fun List.toDomainEntityList(serverId: Int): List { val newList: MutableList = mutableListOf() forEach { if (it.isDir) - newList.add(it.toAlbumEntity()) + newList.add(it.toAlbumEntity(serverId)) else - newList.add(it.toTrackEntity()) + newList.add(it.toTrackEntity(serverId)) } return newList } -fun List.toTrackList(): List = this.map { - it.toTrackEntity() +fun List.toTrackList(serverId: Int): List = this.map { + it.toTrackEntity(serverId) } -fun APIMusicDirectory.toDomainEntity(): MusicDirectory = MusicDirectory().apply { +fun APIMusicDirectory.toDomainEntity(serverId: Int): MusicDirectory = MusicDirectory().apply { name = this@toDomainEntity.name - addAll(this@toDomainEntity.childList.toDomainEntityList()) + addAll(this@toDomainEntity.childList.toDomainEntityList(serverId)) } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIMusicFolderConverter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIMusicFolderConverter.kt index a969032e..e0cbd6f9 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIMusicFolderConverter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIMusicFolderConverter.kt @@ -1,3 +1,10 @@ +/* + * APIMusicFolderConverter.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + // Converts MusicFolder entity from [org.moire.ultrasonic.api.subsonic.SubsonicAPIClient] // to app domain entities. @file:JvmName("APIMusicFolderConverter") @@ -5,7 +12,15 @@ package org.moire.ultrasonic.domain import org.moire.ultrasonic.api.subsonic.models.MusicFolder as APIMusicFolder -fun APIMusicFolder.toDomainEntity(): MusicFolder = MusicFolder(this.id, this.name) +fun APIMusicFolder.toDomainEntity(serverId: Int): MusicFolder = MusicFolder( + id = this.id, + serverId = serverId, + name = this.name +) -fun List.toDomainEntityList(): List = - this.map { it.toDomainEntity() } +fun List.toDomainEntityList(serverId: Int): List = + this.map { + val item = it.toDomainEntity(serverId) + item.serverId = serverId + item + } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIPlaylistConverter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIPlaylistConverter.kt index 7286163a..e9c279ac 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIPlaylistConverter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIPlaylistConverter.kt @@ -1,6 +1,14 @@ +/* + * APIPlaylistConverter.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + // Converts Playlist entity from [org.moire.ultrasonic.api.subsonic.SubsonicAPIClient] // to app domain entities. @file:JvmName("APIPlaylistConverter") + package org.moire.ultrasonic.domain import java.text.SimpleDateFormat @@ -10,10 +18,15 @@ import org.moire.ultrasonic.util.Util.ifNotNull internal val playlistDateFormat by lazy(NONE) { SimpleDateFormat.getInstance() } -fun APIPlaylist.toMusicDirectoryDomainEntity(): MusicDirectory = MusicDirectory().apply { - name = this@toMusicDirectoryDomainEntity.name - addAll(this@toMusicDirectoryDomainEntity.entriesList.map { it.toTrackEntity() }) -} +fun APIPlaylist.toMusicDirectoryDomainEntity(serverId: Int): MusicDirectory = + MusicDirectory().apply { + name = this@toMusicDirectoryDomainEntity.name + addAll(this@toMusicDirectoryDomainEntity.entriesList.map { + val item = it.toTrackEntity(serverId) + item.serverId = serverId + item + }) + } fun APIPlaylist.toDomainEntity(): Playlist = Playlist( this.id, this.name, this.owner, diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APISearchConverter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APISearchConverter.kt index ff9ffd74..ed1af67d 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APISearchConverter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APISearchConverter.kt @@ -1,3 +1,10 @@ +/* + * APISearchConverter.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + // Converts SearchResult entities from [org.moire.ultrasonic.api.subsonic.SubsonicAPIClient] // to app domain entities. @file:JvmName("APISearchConverter") @@ -7,19 +14,19 @@ import org.moire.ultrasonic.api.subsonic.models.SearchResult as APISearchResult import org.moire.ultrasonic.api.subsonic.models.SearchThreeResult import org.moire.ultrasonic.api.subsonic.models.SearchTwoResult -fun APISearchResult.toDomainEntity(): SearchResult = SearchResult( +fun APISearchResult.toDomainEntity(serverId: Int): SearchResult = SearchResult( emptyList(), emptyList(), - this.matchList.map { it.toTrackEntity() } + this.matchList.map { it.toTrackEntity(serverId) } ) -fun SearchTwoResult.toDomainEntity(): SearchResult = SearchResult( - this.artistList.map { it.toIndexEntity() }, - this.albumList.map { it.toDomainEntity() }, - this.songList.map { it.toTrackEntity() } +fun SearchTwoResult.toDomainEntity(serverId: Int): SearchResult = SearchResult( + this.artistList.map { it.toIndexEntity(serverId) }, + this.albumList.map { it.toDomainEntity(serverId) }, + this.songList.map { it.toTrackEntity(serverId) } ) -fun SearchThreeResult.toDomainEntity(): SearchResult = SearchResult( - this.artistList.map { it.toDomainEntity() }, - this.albumList.map { it.toDomainEntity() }, - this.songList.map { it.toTrackEntity() } +fun SearchThreeResult.toDomainEntity(serverId: Int): SearchResult = SearchResult( + this.artistList.map { it.toDomainEntity(serverId) }, + this.albumList.map { it.toDomainEntity(serverId) }, + this.songList.map { it.toTrackEntity(serverId) } ) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIShareConverter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIShareConverter.kt index 2a121ae3..7fb8a9a4 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIShareConverter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/domain/APIShareConverter.kt @@ -1,3 +1,10 @@ +/* + * APIShareConverter.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + // Contains helper method to convert subsonic api share to domain model @file:JvmName("APIShareConverter") package org.moire.ultrasonic.domain @@ -9,11 +16,11 @@ import org.moire.ultrasonic.util.Util.ifNotNull internal val shareTimeFormat by lazy(NONE) { SimpleDateFormat.getInstance() } -fun List.toDomainEntitiesList(): List = this.map { - it.toDomainEntity() +fun List.toDomainEntitiesList(serverId: Int): List = this.map { + it.toDomainEntity(serverId) } -fun APIShare.toDomainEntity(): Share = Share( +fun APIShare.toDomainEntity(serverId: Int): Share = Share( created = this@toDomainEntity.created.ifNotNull { shareTimeFormat.format(it.time) }, description = this@toDomainEntity.description, expires = this@toDomainEntity.expires.ifNotNull { shareTimeFormat.format(it.time) }, @@ -22,5 +29,5 @@ fun APIShare.toDomainEntity(): Share = Share( url = this@toDomainEntity.url, username = this@toDomainEntity.username, visitCount = this@toDomainEntity.visitCount.toLong(), - tracks = this@toDomainEntity.items.toTrackList().toMutableList() + tracks = this@toDomainEntity.items.toTrackList(serverId).toMutableList() ) 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 97530118..9af9f9ed 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/OfflineMusicService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/OfflineMusicService.kt @@ -1,6 +1,6 @@ /* * OfflineMusicService.kt - * Copyright (C) 2009-2021 Ultrasonic developers + * Copyright (C) 2009-2022 Ultrasonic developers * * Distributed under terms of the GNU GPLv3 license. */ @@ -66,7 +66,7 @@ class OfflineMusicService : MusicService, KoinComponent { val root = FileUtil.musicDirectory for (file in FileUtil.listFiles(root)) { if (file.isDirectory) { - val index = Index(file.path) + val index = Index(id = file.path) index.id = file.path index.index = file.name.substring(0, 1) index.name = file.name 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 e826f0e0..1faec462 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/RESTMusicService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/RESTMusicService.kt @@ -1,6 +1,6 @@ /* - * RestMusicService.kt - * Copyright (C) 2009-2021 Ultrasonic developers + * RESTMusicService.kt + * Copyright (C) 2009-2022 Ultrasonic developers * * Distributed under terms of the GNU GPLv3 license. */ @@ -78,7 +78,7 @@ open class RESTMusicService( ): List { val response = API.getMusicFolders().execute().throwOnFailure() - return response.body()!!.musicFolders.toDomainEntityList() + return response.body()!!.musicFolders.toDomainEntityList(activeServerId) } /** @@ -91,7 +91,10 @@ open class RESTMusicService( ): List { val response = API.getIndexes(musicFolderId, null).execute().throwOnFailure() - return response.body()!!.indexes.toIndexList(musicFolderId) + return response.body()!!.indexes.toIndexList( + ActiveServerProvider.getActiveServerId(), + musicFolderId + ) } @Throws(Exception::class) @@ -100,7 +103,7 @@ open class RESTMusicService( ): List { val response = API.getArtists(null).execute().throwOnFailure() - return response.body()!!.indexes.toArtistList() + return response.body()!!.indexes.toArtistList(activeServerId) } @Throws(Exception::class) @@ -137,7 +140,7 @@ open class RESTMusicService( ): MusicDirectory { val response = API.getMusicDirectory(id).execute().throwOnFailure() - return response.body()!!.musicDirectory.toDomainEntity() + return response.body()!!.musicDirectory.toDomainEntity(activeServerId) } @Throws(Exception::class) @@ -148,7 +151,7 @@ open class RESTMusicService( ): List { val response = API.getArtist(id).execute().throwOnFailure() - return response.body()!!.artist.toDomainEntityList() + return response.body()!!.artist.toDomainEntityList(activeServerId) } @Throws(Exception::class) @@ -159,7 +162,7 @@ open class RESTMusicService( ): MusicDirectory { val response = API.getAlbum(id).execute().throwOnFailure() - return response.body()!!.album.toMusicDirectoryDomainEntity() + return response.body()!!.album.toMusicDirectoryDomainEntity(activeServerId) } @Throws(Exception::class) @@ -189,7 +192,7 @@ open class RESTMusicService( API.search(null, null, null, criteria.query, criteria.songCount, null, null) .execute().throwOnFailure() - return response.body()!!.searchResult.toDomainEntity() + return response.body()!!.searchResult.toDomainEntity(activeServerId) } /** @@ -205,7 +208,7 @@ open class RESTMusicService( criteria.songCount, null ).execute().throwOnFailure() - return response.body()!!.searchResult.toDomainEntity() + return response.body()!!.searchResult.toDomainEntity(activeServerId) } @Throws(Exception::class) @@ -218,7 +221,7 @@ open class RESTMusicService( criteria.songCount, null ).execute().throwOnFailure() - return response.body()!!.searchResult.toDomainEntity() + return response.body()!!.searchResult.toDomainEntity(activeServerId) } @Throws(Exception::class) @@ -228,7 +231,7 @@ open class RESTMusicService( ): MusicDirectory { val response = API.getPlaylist(id).execute().throwOnFailure() - val playlist = response.body()!!.playlist.toMusicDirectoryDomainEntity() + val playlist = response.body()!!.playlist.toMusicDirectoryDomainEntity(activeServerId) savePlaylist(name, playlist) return playlist @@ -319,7 +322,7 @@ open class RESTMusicService( "skipped" != podcastEntry.status && "error" != podcastEntry.status ) { - val entry = podcastEntry.toTrackEntity() + val entry = podcastEntry.toTrackEntity(activeServerId) entry.track = null musicDirectory.add(entry) } @@ -363,7 +366,7 @@ open class RESTMusicService( musicFolderId ).execute().throwOnFailure() - return response.body()!!.albumList.toDomainEntityList() + return response.body()!!.albumList.toDomainEntityList(activeServerId) } @Throws(Exception::class) @@ -383,7 +386,7 @@ open class RESTMusicService( musicFolderId ).execute().throwOnFailure() - return response.body()!!.albumList.toDomainEntityList() + return response.body()!!.albumList.toDomainEntityList(activeServerId) } @Throws(Exception::class) @@ -399,7 +402,7 @@ open class RESTMusicService( ).execute().throwOnFailure() val result = MusicDirectory() - result.addAll(response.body()!!.songsList.toDomainEntityList()) + result.addAll(response.body()!!.songsList.toDomainEntityList(activeServerId)) return result } @@ -408,14 +411,14 @@ open class RESTMusicService( override fun getStarred(): SearchResult { val response = API.getStarred(null).execute().throwOnFailure() - return response.body()!!.starred.toDomainEntity() + return response.body()!!.starred.toDomainEntity(activeServerId) } @Throws(Exception::class) override fun getStarred2(): SearchResult { val response = API.getStarred2(null).execute().throwOnFailure() - return response.body()!!.starred2.toDomainEntity() + return response.body()!!.starred2.toDomainEntity(activeServerId) } @Throws(Exception::class) @@ -546,7 +549,7 @@ open class RESTMusicService( ): List { val response = API.getShares().execute().throwOnFailure() - return response.body()!!.shares.toDomainEntitiesList() + return response.body()!!.shares.toDomainEntitiesList(activeServerId) } @Throws(Exception::class) @@ -567,7 +570,7 @@ open class RESTMusicService( val response = API.getSongsByGenre(genre, count, offset, null).execute().throwOnFailure() val result = MusicDirectory() - result.addAll(response.body()!!.songsList.toDomainEntityList()) + result.addAll(response.body()!!.songsList.toDomainEntityList(activeServerId)) return result } @@ -601,7 +604,7 @@ open class RESTMusicService( override fun getBookmarks(): List { val response = API.getBookmarks().execute().throwOnFailure() - return response.body()!!.bookmarkList.toDomainEntitiesList() + return response.body()!!.bookmarkList.toDomainEntitiesList(activeServerId) } @Throws(Exception::class) @@ -626,7 +629,7 @@ open class RESTMusicService( val response = API.getVideos().execute().throwOnFailure() val musicDirectory = MusicDirectory() - musicDirectory.addAll(response.body()!!.videosList.toDomainEntityList()) + musicDirectory.addAll(response.body()!!.videosList.toDomainEntityList(activeServerId)) return musicDirectory } @@ -639,7 +642,7 @@ open class RESTMusicService( ): List { val response = API.createShare(ids, description, expires).execute().throwOnFailure() - return response.body()!!.shares.toDomainEntitiesList() + return response.body()!!.shares.toDomainEntitiesList(activeServerId) } @Throws(Exception::class) @@ -663,6 +666,9 @@ open class RESTMusicService( API.updateShare(id, description, expiresValue).execute().throwOnFailure() } + private val activeServerId: Int + get() = ActiveServerProvider.getActiveServerId() + init { // The client will notice if the minimum supported API version has changed // By registering a callback we ensure this info is saved in the database as well diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIAlbumConverterTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIAlbumConverterTest.kt index b4b39932..3f094844 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIAlbumConverterTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIAlbumConverterTest.kt @@ -7,11 +7,14 @@ import org.amshove.kluent.`should be equal to` import org.junit.Test import org.moire.ultrasonic.api.subsonic.models.Album import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild +import org.moire.ultrasonic.data.ActiveServerProvider /** * Unit test for extension functions in [APIAlbumConverter.kt] file. */ class APIAlbumConverterTest { + private val serverId = -1 + @Test fun `Should convert Album to domain entity`() { val entity = Album( @@ -20,7 +23,7 @@ class APIAlbumConverterTest { created = Calendar.getInstance(), year = 2017, genre = "some-genre" ) - val convertedEntity = entity.toDomainEntity() + val convertedEntity = entity.toDomainEntity(serverId) with(convertedEntity) { id `should be equal to` entity.id @@ -46,12 +49,12 @@ class APIAlbumConverterTest { songList = listOf(MusicDirectoryChild()) ) - val convertedEntity = entity.toMusicDirectoryDomainEntity() + val convertedEntity = entity.toMusicDirectoryDomainEntity(serverId) with(convertedEntity) { name `should be equal to` null size `should be equal to` entity.songList.size - this[0] `should be equal to` entity.songList[0].toTrackEntity() + this[0] `should be equal to` entity.songList[0].toTrackEntity(serverId) } } @@ -59,12 +62,12 @@ class APIAlbumConverterTest { fun `Should convert list of Album entities to domain list entities`() { val entityList = listOf(Album(id = "455"), Album(id = "1"), Album(id = "1000")) - val convertedList = entityList.toDomainEntityList() + val convertedList = entityList.toDomainEntityList(ActiveServerProvider.getActiveServerId()) with(convertedList) { size `should be equal to` entityList.size forEachIndexed { index, entry -> - entry `should be equal to` entityList[index].toDomainEntity() + entry `should be equal to` entityList[index].toDomainEntity(serverId) } } } diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIArtistConverterTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIArtistConverterTest.kt index 4ae7528b..5920744e 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIArtistConverterTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIArtistConverterTest.kt @@ -11,12 +11,12 @@ import org.moire.ultrasonic.api.subsonic.models.Artist /** * Unit test for extension functions in APIArtistConverter.kt file. */ -class APIArtistConverterTest { +class APIArtistConverterTest : BaseTest() { @Test fun `Should convert artist entity`() { val entity = Artist(id = "10", name = "artist-name", starred = Calendar.getInstance()) - val convertedEntity = entity.toDomainEntity() + val convertedEntity = entity.toDomainEntity(serverId) with(convertedEntity) { id `should be equal to` entity.id @@ -38,12 +38,12 @@ class APIArtistConverterTest { ) ) - val convertedEntity = entity.toMusicDirectoryDomainEntity() + val convertedEntity = entity.toMusicDirectoryDomainEntity(serverId) with(convertedEntity) { name `should be equal to` entity.name getChildren() `should be equal to` entity.albumsList - .map { it.toDomainEntity() }.toMutableList() + .map { it.toDomainEntity(serverId) }.toMutableList() } } } diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIBookmarkConverterTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIBookmarkConverterTest.kt index 2b2abecb..fe03805c 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIBookmarkConverterTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIBookmarkConverterTest.kt @@ -11,7 +11,8 @@ import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild /** * Unit test for function that converts [Bookmark] api entity to domain. */ -class APIBookmarkConverterTest { +class APIBookmarkConverterTest : BaseTest() { + @Test fun `Should convert to domain entity`() { val entity = Bookmark( @@ -19,7 +20,7 @@ class APIBookmarkConverterTest { Calendar.getInstance(), MusicDirectoryChild(id = "12333") ) - val domainEntity = entity.toDomainEntity() + val domainEntity = entity.toDomainEntity(serverId) with(domainEntity) { position `should be equal to` entity.position.toInt() @@ -27,7 +28,7 @@ class APIBookmarkConverterTest { comment `should be equal to` entity.comment created `should be equal to` entity.created?.time changed `should be equal to` entity.changed?.time - track `should be equal to` entity.entry.toTrackEntity() + track `should be equal to` entity.entry.toTrackEntity(serverId) } } @@ -35,11 +36,11 @@ class APIBookmarkConverterTest { fun `Should convert list of entities to domain entities`() { val entitiesList = listOf(Bookmark(443L), Bookmark(444L)) - val domainEntitiesList = entitiesList.toDomainEntitiesList() + val domainEntitiesList = entitiesList.toDomainEntitiesList(serverId) domainEntitiesList.size `should be equal to` entitiesList.size - domainEntitiesList.forEachIndexed({ index, bookmark -> - bookmark `should be equal to` entitiesList[index].toDomainEntity() - }) + domainEntitiesList.forEachIndexed { index, bookmark -> + bookmark `should be equal to` entitiesList[index].toDomainEntity(serverId) + } } } diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIMusicDirectoryConverterTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIMusicDirectoryConverterTest.kt index 3bb559ca..7f47c1a5 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIMusicDirectoryConverterTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIMusicDirectoryConverterTest.kt @@ -7,11 +7,12 @@ import org.amshove.kluent.`should be equal to` import org.junit.Test import org.moire.ultrasonic.api.subsonic.models.MusicDirectory import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild +import org.moire.ultrasonic.data.ActiveServerProvider /** * Unit test for extension functions in APIMusicDirectoryConverter.kt file. */ -class APIMusicDirectoryConverterTest { +class APIMusicDirectoryConverterTest : BaseTest() { @Test fun `Should convert MusicDirectory entity`() { val entity = MusicDirectory( @@ -20,13 +21,13 @@ class APIMusicDirectoryConverterTest { childList = listOf(MusicDirectoryChild("1"), MusicDirectoryChild("2")) ) - val convertedEntity = entity.toDomainEntity() + val convertedEntity = entity.toDomainEntity(ActiveServerProvider.getActiveServerId()) with(convertedEntity) { name `should be equal to` entity.name size `should be equal to` entity.childList.size getChildren() `should be equal to` entity.childList - .map { it.toTrackEntity() }.toMutableList() + .map { it.toTrackEntity(serverId) }.toMutableList() } } @@ -44,7 +45,7 @@ class APIMusicDirectoryConverterTest { starred = Calendar.getInstance(), userRating = 3, averageRating = 2.99F ) - val convertedEntity = entity.toTrackEntity() + val convertedEntity = entity.toTrackEntity(serverId) with(convertedEntity) { id `should be equal to` entity.id @@ -84,7 +85,7 @@ class APIMusicDirectoryConverterTest { artist = "some-artist", publishDate = Calendar.getInstance() ) - val convertedEntity = entity.toTrackEntity() + val convertedEntity = entity.toTrackEntity(serverId) with(convertedEntity) { id `should be equal to` entity.streamId @@ -96,11 +97,11 @@ class APIMusicDirectoryConverterTest { fun `Should convert list of MusicDirectoryChild to domain entity list`() { val entitiesList = listOf(MusicDirectoryChild(id = "45"), MusicDirectoryChild(id = "34")) - val domainList = entitiesList.toDomainEntityList() + val domainList = entitiesList.toDomainEntityList(serverId) domainList.size `should be equal to` entitiesList.size domainList.forEachIndexed { index, entry -> - entry `should be equal to` entitiesList[index].toTrackEntity() + entry `should be equal to` entitiesList[index].toTrackEntity(serverId) } } } diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIMusicFolderConverterTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIMusicFolderConverterTest.kt index 0605be39..85721482 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIMusicFolderConverterTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIMusicFolderConverterTest.kt @@ -9,12 +9,12 @@ import org.moire.ultrasonic.api.subsonic.models.MusicFolder /** * Unit test for extension functions in file APIMusicFolderConverter.kt. */ -class APIMusicFolderConverterTest { +class APIMusicFolderConverterTest : BaseTest() { @Test fun `Should convert MusicFolder entity`() { val entity = MusicFolder(id = "10", name = "some-name") - val convertedEntity = entity.toDomainEntity() + val convertedEntity = entity.toDomainEntity(serverId) convertedEntity.name `should be equal to` entity.name convertedEntity.id `should be equal to` entity.id @@ -27,7 +27,7 @@ class APIMusicFolderConverterTest { MusicFolder(id = "4", name = "some-name-4") ) - val convertedList = entityList.toDomainEntityList() + val convertedList = entityList.toDomainEntityList(serverId) with(convertedList) { size `should be equal to` entityList.size diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIPlaylistConverterTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIPlaylistConverterTest.kt index 9756af80..35a0add5 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIPlaylistConverterTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APIPlaylistConverterTest.kt @@ -7,11 +7,12 @@ import org.amshove.kluent.`should be equal to` import org.junit.Test import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild import org.moire.ultrasonic.api.subsonic.models.Playlist +import org.moire.ultrasonic.data.ActiveServerProvider /** * Unit test for extension functions that converts api playlist entity to domain. */ -class APIPlaylistConverterTest { +class APIPlaylistConverterTest : BaseTest() { @Test fun `Should convert Playlist to MusicDirectory domain entity`() { val entity = Playlist( @@ -22,13 +23,13 @@ class APIPlaylistConverterTest { ) ) - val convertedEntity = entity.toMusicDirectoryDomainEntity() + val convertedEntity = entity.toMusicDirectoryDomainEntity(ActiveServerProvider.getActiveServerId()) with(convertedEntity) { name `should be equal to` entity.name size `should be equal to` entity.entriesList.size - this[0] `should be equal to` entity.entriesList[0].toTrackEntity() - this[1] `should be equal to` entity.entriesList[1].toTrackEntity() + this[0] `should be equal to` entity.entriesList[0].toTrackEntity(serverId) + this[1] `should be equal to` entity.entriesList[1].toTrackEntity(serverId) } } diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APISearchConverterTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APISearchConverterTest.kt index dd2e7d84..10171cf8 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APISearchConverterTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/APISearchConverterTest.kt @@ -11,11 +11,12 @@ import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild import org.moire.ultrasonic.api.subsonic.models.SearchResult import org.moire.ultrasonic.api.subsonic.models.SearchThreeResult import org.moire.ultrasonic.api.subsonic.models.SearchTwoResult +import org.moire.ultrasonic.data.ActiveServerProvider /** * Unit test for extension function in APISearchConverter.kt file. */ -class APISearchConverterTest { +class APISearchConverterTest : BaseTest() { @Test fun `Should convert SearchResult to domain entity`() { val entity = SearchResult( @@ -26,7 +27,7 @@ class APISearchConverterTest { ) ) - val convertedEntity = entity.toDomainEntity() + val convertedEntity = entity.toDomainEntity(serverId) with(convertedEntity) { albums `should not be equal to` null @@ -34,7 +35,7 @@ class APISearchConverterTest { artists `should not be equal to` null artists.size `should be equal to` 0 songs.size `should be equal to` entity.matchList.size - songs[0] `should be equal to` entity.matchList[0].toTrackEntity() + songs[0] `should be equal to` entity.matchList[0].toTrackEntity(serverId) } } @@ -46,15 +47,15 @@ class APISearchConverterTest { listOf(MusicDirectoryChild(id = "9118", parent = "112")) ) - val convertedEntity = entity.toDomainEntity() + val convertedEntity = entity.toDomainEntity(ActiveServerProvider.getActiveServerId()) with(convertedEntity) { artists.size `should be equal to` entity.artistList.size - artists[0] `should be equal to` entity.artistList[0].toIndexEntity() + artists[0] `should be equal to` entity.artistList[0].toIndexEntity(serverId) albums.size `should be equal to` entity.albumList.size - albums[0] `should be equal to` entity.albumList[0].toDomainEntity() + albums[0] `should be equal to` entity.albumList[0].toDomainEntity(serverId) songs.size `should be equal to` entity.songList.size - songs[0] `should be equal to` entity.songList[0].toTrackEntity() + songs[0] `should be equal to` entity.songList[0].toTrackEntity(serverId) } } @@ -66,15 +67,15 @@ class APISearchConverterTest { songList = listOf(MusicDirectoryChild(id = "7123", title = "song1")) ) - val convertedEntity = entity.toDomainEntity() + val convertedEntity = entity.toDomainEntity(serverId) with(convertedEntity) { artists.size `should be equal to` entity.artistList.size - artists[0] `should be equal to` entity.artistList[0].toDomainEntity() + artists[0] `should be equal to` entity.artistList[0].toDomainEntity(serverId) albums.size `should be equal to` entity.albumList.size - albums[0] `should be equal to` entity.albumList[0].toDomainEntity() + albums[0] `should be equal to` entity.albumList[0].toDomainEntity(serverId) songs.size `should be equal to` entity.songList.size - songs[0] `should be equal to` entity.songList[0].toTrackEntity() + songs[0] `should be equal to` entity.songList[0].toTrackEntity(serverId) } } } diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/BaseTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/BaseTest.kt new file mode 100644 index 00000000..4a141e88 --- /dev/null +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/domain/BaseTest.kt @@ -0,0 +1,12 @@ +/* + * BaseTest.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + +package org.moire.ultrasonic.domain + +open class BaseTest { + internal val serverId = -1 +}