Merge branch 'develop' of github.com:ultrasonic/ultrasonic into develop
This commit is contained in:
commit
2165ce75b3
|
@ -1,10 +1,21 @@
|
|||
/*
|
||||
* Album.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.domain
|
||||
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import java.util.Date
|
||||
|
||||
@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,
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
/*
|
||||
* 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)
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
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 +28,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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,24 @@
|
|||
/*
|
||||
* 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)
|
||||
|
|
|
@ -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<MusicDirectory.Child>() {
|
|||
|
||||
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?
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
/*
|
||||
* 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
|
||||
@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,
|
||||
|
|
|
@ -64,10 +64,7 @@ style:
|
|||
WildcardImport:
|
||||
active: true
|
||||
MaxLineLength:
|
||||
active: true
|
||||
maxLineLength: 120
|
||||
excludePackageStatements: false
|
||||
excludeImportStatements: false
|
||||
active: false
|
||||
MagicNumber:
|
||||
# 100 common in percentage, 1000 in milliseconds
|
||||
ignoreNumbers: ['-1', '0', '1', '2', '5', '10', '100', '256', '512', '1000', '1024', '4096']
|
||||
|
|
|
@ -0,0 +1,474 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "b6ac795e7857eac4fed2dbbd01f80fb8",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "artists",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `index` TEXT, `coverArt` TEXT, `albumCount` INTEGER, `closeness` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "index",
|
||||
"columnName": "index",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArt",
|
||||
"columnName": "coverArt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumCount",
|
||||
"columnName": "albumCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "closeness",
|
||||
"columnName": "closeness",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "albums",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `parent` TEXT, `album` TEXT, `title` TEXT, `name` TEXT, `discNumber` INTEGER, `coverArt` TEXT, `songCount` INTEGER, `created` INTEGER, `artist` TEXT, `artistId` TEXT, `duration` INTEGER, `year` INTEGER, `genre` TEXT, `starred` INTEGER NOT NULL, `path` TEXT, `closeness` INTEGER NOT NULL, `isDirectory` INTEGER NOT NULL, `isVideo` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "parent",
|
||||
"columnName": "parent",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "discNumber",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArt",
|
||||
"columnName": "coverArt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "songCount",
|
||||
"columnName": "songCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artistId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "closeness",
|
||||
"columnName": "closeness",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDirectory",
|
||||
"columnName": "isDirectory",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "isVideo",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "tracks",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `parent` TEXT, `isDirectory` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `albumId` TEXT, `artist` TEXT, `artistId` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `contentType` TEXT, `suffix` TEXT, `transcodedContentType` TEXT, `transcodedSuffix` TEXT, `coverArt` TEXT, `size` INTEGER, `songCount` INTEGER, `duration` INTEGER, `bitRate` INTEGER, `path` TEXT, `isVideo` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `discNumber` INTEGER, `type` TEXT, `created` INTEGER, `closeness` INTEGER NOT NULL, `bookmarkPosition` INTEGER NOT NULL, `userRating` INTEGER, `averageRating` REAL, `name` TEXT, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "parent",
|
||||
"columnName": "parent",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDirectory",
|
||||
"columnName": "isDirectory",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "albumId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artistId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "track",
|
||||
"columnName": "track",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentType",
|
||||
"columnName": "contentType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcodedContentType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcodedSuffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArt",
|
||||
"columnName": "coverArt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "songCount",
|
||||
"columnName": "songCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bitRate",
|
||||
"columnName": "bitRate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "isVideo",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "discNumber",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "closeness",
|
||||
"columnName": "closeness",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmarkPosition",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "userRating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "averageRating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "indexes",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `index` TEXT, `coverArt` TEXT, `albumCount` INTEGER, `closeness` INTEGER NOT NULL, `musicFolderId` TEXT, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "index",
|
||||
"columnName": "index",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArt",
|
||||
"columnName": "coverArt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumCount",
|
||||
"columnName": "albumCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "closeness",
|
||||
"columnName": "closeness",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "musicFolderId",
|
||||
"columnName": "musicFolderId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "music_folders",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b6ac795e7857eac4fed2dbbd01f80fb8')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -392,6 +392,8 @@ class NavigationActivity : AppCompatActivity() {
|
|||
if (!infoDialogDisplayed) {
|
||||
infoDialogDisplayed = true
|
||||
|
||||
Settings.firstInstalledVersion = Util.getVersionCode(UApp.applicationContext())
|
||||
|
||||
InfoDialog.Builder(this)
|
||||
.setTitle(R.string.main_welcome_title)
|
||||
.setMessage(R.string.main_welcome_text_demo)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* ArtistRowAdapter.kt
|
||||
* Copyright (C) 2009-2021 Ultrasonic developers
|
||||
* ArtistRowBinder.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
@ -19,6 +19,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.drakeet.multitype.ItemViewBinder
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.domain.ArtistOrIndex
|
||||
import org.moire.ultrasonic.domain.Identifiable
|
||||
import org.moire.ultrasonic.imageloader.ImageLoader
|
||||
|
@ -57,7 +58,7 @@ class ArtistRowBinder(
|
|||
|
||||
holder.coverArtId = item.coverArt
|
||||
|
||||
if (Settings.shouldShowArtistPicture) {
|
||||
if (showArtistPicture()) {
|
||||
holder.coverArt.visibility = View.VISIBLE
|
||||
val key = FileUtil.getArtistArtKey(item.name, false)
|
||||
imageLoader.loadImage(
|
||||
|
@ -108,6 +109,10 @@ class ArtistRowBinder(
|
|||
return section.toString()
|
||||
}
|
||||
|
||||
private fun showArtistPicture(): Boolean {
|
||||
return ActiveServerProvider.isID3Enabled() && Settings.shouldShowArtistPicture
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of our ViewHolder class
|
||||
*/
|
||||
|
|
|
@ -59,7 +59,7 @@ class BaseAdapter<T : Identifiable> : MultiTypeAdapter(), FastScrollRecyclerView
|
|||
throw IllegalAccessException("You must use submitList() to add data to the Adapter")
|
||||
}
|
||||
|
||||
var mDiffer: AsyncListDiffer<T> = AsyncListDiffer(
|
||||
private var mDiffer: AsyncListDiffer<T> = AsyncListDiffer(
|
||||
AdapterListUpdateCallback(this),
|
||||
AsyncDifferConfig.Builder(diffCallback).build()
|
||||
)
|
||||
|
@ -182,12 +182,11 @@ class BaseAdapter<T : Identifiable> : MultiTypeAdapter(), FastScrollRecyclerView
|
|||
|
||||
// Select them all
|
||||
getCurrentList().mapNotNullTo(
|
||||
selectedSet,
|
||||
{ entry ->
|
||||
// Exclude any -1 ids, eg. headers and other UI elements
|
||||
entry.longId.takeIf { it != -1L }
|
||||
}
|
||||
)
|
||||
selectedSet
|
||||
) { entry ->
|
||||
// Exclude any -1 ids, eg. headers and other UI elements
|
||||
entry.longId.takeIf { it != -1L }
|
||||
}
|
||||
|
||||
return selectedSet.count()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -110,20 +117,23 @@ class ActiveServerProvider(
|
|||
|
||||
Timber.i("Switching to new database, id:$activeServer")
|
||||
cachedServerId = activeServer
|
||||
return buildDatabase(cachedServerId)
|
||||
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
|
||||
METADATA_DB + serverId
|
||||
)
|
||||
.fallbackToDestructiveMigration()
|
||||
.addMigrations(META_MIGRATION_2_3)
|
||||
.fallbackToDestructiveMigrationOnDowngrade()
|
||||
.build()
|
||||
}
|
||||
|
||||
|
@ -239,6 +249,13 @@ class ActiveServerProvider(
|
|||
return preferences.getBoolean(Constants.PREFERENCES_KEY_SCROBBLE, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries if ID3 tags should be used
|
||||
*/
|
||||
fun isID3Enabled(): Boolean {
|
||||
return Settings.shouldUseId3Tags && (!isOffline() || Settings.useId3TagsOffline)
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries if Server Scaling is enabled
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package org.moire.ultrasonic.data
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import org.moire.ultrasonic.domain.Album
|
||||
|
||||
@Dao
|
||||
interface AlbumDao : GenericDao<Album> {
|
||||
/**
|
||||
* Clear the whole database
|
||||
*/
|
||||
@Query("DELETE FROM albums")
|
||||
fun clear()
|
||||
|
||||
/**
|
||||
* Get all albums
|
||||
*/
|
||||
@Query("SELECT * FROM albums")
|
||||
fun get(): List<Album>
|
||||
|
||||
/**
|
||||
* Get all albums in a specific range
|
||||
*/
|
||||
@Query("SELECT * FROM albums LIMIT :offset,:size")
|
||||
fun get(size: Int, offset: Int = 0): List<Album>
|
||||
|
||||
/**
|
||||
* Get album by id
|
||||
*/
|
||||
@Query("SELECT * FROM albums where id LIKE :albumId LIMIT 1")
|
||||
fun get(albumId: String): Album
|
||||
|
||||
/**
|
||||
* Get albums by artist
|
||||
*/
|
||||
@Query("SELECT * FROM albums WHERE artistId LIKE :id")
|
||||
fun byArtist(id: String): List<Album>
|
||||
|
||||
/**
|
||||
* Clear albums by artist
|
||||
*/
|
||||
@Query("DELETE FROM albums WHERE artistId LIKE :id")
|
||||
fun clearByArtist(id: String)
|
||||
|
||||
/**
|
||||
* TODO: Make generic
|
||||
* Upserts (insert or update) an object to the database
|
||||
*
|
||||
* @param obj the object to upsert
|
||||
*/
|
||||
@Transaction
|
||||
@JvmSuppressWildcards
|
||||
fun upsert(obj: Album) {
|
||||
val id = insertIgnoring(obj)
|
||||
if (id == -1L) {
|
||||
update(obj)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upserts (insert or update) a list of objects
|
||||
*
|
||||
* @param objList the object to be upserted
|
||||
*/
|
||||
@Transaction
|
||||
@JvmSuppressWildcards
|
||||
fun upsert(objList: List<Album>) {
|
||||
val insertResult = insertIgnoring(objList)
|
||||
val updateList: MutableList<Album> = ArrayList()
|
||||
for (i in insertResult.indices) {
|
||||
if (insertResult[i] == -1L) {
|
||||
updateList.add(objList[i])
|
||||
}
|
||||
}
|
||||
if (updateList.isNotEmpty()) {
|
||||
update(updateList)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
|
@ -43,5 +43,5 @@ interface ArtistsDao {
|
|||
* Get artist by id
|
||||
*/
|
||||
@Query("SELECT * FROM artists WHERE id LIKE :id")
|
||||
fun get(id: String): Artist
|
||||
fun get(id: String): Artist?
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -1,24 +1,85 @@
|
|||
/*
|
||||
* 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
|
||||
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 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, Index::class, MusicFolder::class],
|
||||
version = 1,
|
||||
exportSchema = true
|
||||
entities = [
|
||||
Artist::class,
|
||||
Album::class,
|
||||
Track::class,
|
||||
Index::class,
|
||||
MusicFolder::class
|
||||
],
|
||||
autoMigrations = [
|
||||
AutoMigration(
|
||||
from = 1,
|
||||
to = 2
|
||||
),
|
||||
],
|
||||
exportSchema = true,
|
||||
version = 3
|
||||
)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class MetaDatabase : RoomDatabase() {
|
||||
abstract fun artistsDao(): ArtistsDao
|
||||
abstract fun artistDao(): ArtistDao
|
||||
|
||||
abstract fun albumDao(): AlbumDao
|
||||
|
||||
abstract fun trackDao(): TrackDao
|
||||
|
||||
abstract fun musicFoldersDao(): MusicFoldersDao
|
||||
|
||||
abstract fun indexDao(): IndexDao
|
||||
}
|
||||
|
||||
class Converters {
|
||||
@TypeConverter
|
||||
fun fromTimestamp(value: Long?): Date? {
|
||||
return value?.let { Date(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun dateToTimestamp(date: Date?): Long? {
|
||||
return date?.time
|
||||
}
|
||||
}
|
||||
|
||||
/* ktlint-disable max-line-length */
|
||||
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`))")
|
||||
}
|
||||
}
|
||||
/* ktlint-enable max-line-length */
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package org.moire.ultrasonic.data
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Query
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
|
||||
@Dao
|
||||
@Entity(tableName = "tracks")
|
||||
interface TrackDao : GenericDao<Track> {
|
||||
/**
|
||||
* Clear the whole database
|
||||
*/
|
||||
@Query("DELETE FROM tracks")
|
||||
fun clear()
|
||||
|
||||
/**
|
||||
* Get all albums
|
||||
*/
|
||||
@Query("SELECT * FROM tracks")
|
||||
fun get(): List<Track>
|
||||
|
||||
/**
|
||||
* Get albums by artist
|
||||
*/
|
||||
@Query("SELECT * FROM tracks WHERE albumId LIKE :id")
|
||||
fun byAlbum(id: String): List<Track>
|
||||
|
||||
/**
|
||||
* Get albums by artist
|
||||
*/
|
||||
@Query("SELECT * FROM tracks WHERE artistId LIKE :id")
|
||||
fun byArtist(id: String): List<Track>
|
||||
}
|
|
@ -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<Album>.toDomainEntityList(): List<DomainAlbum> = this.map { it.toDomainEntity() }
|
||||
fun List<Album>.toDomainEntityList(serverId: Int): List<DomainAlbum> = this.map {
|
||||
it.toDomainEntity(serverId)
|
||||
}
|
||||
|
|
|
@ -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<Album> {
|
||||
return this.albumsList.map { it.toDomainEntity() }
|
||||
fun APIArtist.toDomainEntityList(serverId: Int): List<Album> {
|
||||
return this.albumsList.map { it.toDomainEntity(serverId) }
|
||||
}
|
||||
|
|
|
@ -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<ApiBookmark>.toDomainEntitiesList(): List<Bookmark> = map { it.toDomainEntity() }
|
||||
fun List<ApiBookmark>.toDomainEntitiesList(serverId: Int): List<Bookmark> =
|
||||
map { it.toDomainEntity(serverId) }
|
||||
|
|
|
@ -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<Artist> {
|
||||
val shortcuts = this.shortcutList.map { it.toDomainEntity() }.toMutableList()
|
||||
val indexes = this.indexList.foldIndexToArtistList()
|
||||
fun APIIndexes.toArtistList(serverId: Int): List<Artist> {
|
||||
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<Artist> {
|
|||
return shortcuts
|
||||
}
|
||||
|
||||
fun APIIndexes.toIndexList(musicFolderId: String?): List<Index> {
|
||||
val shortcuts = this.shortcutList.map { it.toIndexEntity() }.toMutableList()
|
||||
val indexes = this.indexList.foldIndexToIndexList(musicFolderId)
|
||||
fun APIIndexes.toIndexList(serverId: Int, musicFolderId: String?): List<Index> {
|
||||
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<Index> {
|
|||
return shortcuts
|
||||
}
|
||||
|
||||
private fun List<APIIndex>.foldIndexToArtistList(): List<Artist> = this.fold(
|
||||
listOf(),
|
||||
{ acc, index ->
|
||||
acc + index.artists.map {
|
||||
it.toDomainEntity()
|
||||
}
|
||||
private fun List<APIIndex>.foldIndexToArtistList(serverId: Int): List<Artist> = this.fold(
|
||||
listOf()
|
||||
) { acc, index ->
|
||||
acc + index.artists.map {
|
||||
it.toDomainEntity(serverId)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<APIIndex>.foldIndexToIndexList(musicFolderId: String?): List<Index> = this.fold(
|
||||
listOf(),
|
||||
{ acc, index ->
|
||||
acc + index.artists.map {
|
||||
val ret = it.toIndexEntity()
|
||||
ret.musicFolderId = musicFolderId
|
||||
ret
|
||||
}
|
||||
private fun List<APIIndex>.foldIndexToIndexList(
|
||||
musicFolderId: String?,
|
||||
serverId: Int
|
||||
): List<Index> = this.fold(
|
||||
listOf()
|
||||
) { acc, index ->
|
||||
acc + index.artists.map {
|
||||
val ret = it.toIndexEntity(serverId)
|
||||
ret.musicFolderId = musicFolderId
|
||||
ret
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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<MusicDirectoryChild>.toDomainEntityList(): List<MusicDirectory.Child> {
|
||||
fun List<MusicDirectoryChild>.toDomainEntityList(serverId: Int): List<MusicDirectory.Child> {
|
||||
val newList: MutableList<MusicDirectory.Child> = 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<MusicDirectoryChild>.toTrackList(): List<Track> = this.map {
|
||||
it.toTrackEntity()
|
||||
fun List<MusicDirectoryChild>.toTrackList(serverId: Int): List<Track> = 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))
|
||||
}
|
||||
|
|
|
@ -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<APIMusicFolder>.toDomainEntityList(): List<MusicFolder> =
|
||||
this.map { it.toDomainEntity() }
|
||||
fun List<APIMusicFolder>.toDomainEntityList(serverId: Int): List<MusicFolder> =
|
||||
this.map {
|
||||
val item = it.toDomainEntity(serverId)
|
||||
item.serverId = serverId
|
||||
item
|
||||
}
|
||||
|
|
|
@ -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,17 @@ 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,
|
||||
|
|
|
@ -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) }
|
||||
)
|
||||
|
|
|
@ -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<APIShare>.toDomainEntitiesList(): List<Share> = this.map {
|
||||
it.toDomainEntity()
|
||||
fun List<APIShare>.toDomainEntitiesList(serverId: Int): List<Share> = 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()
|
||||
)
|
||||
|
|
|
@ -66,17 +66,17 @@ class MainFragment : Fragment(), KoinComponent {
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
var shouldRestart = false
|
||||
var shouldRelayout = false
|
||||
val currentId3Setting = Settings.shouldUseId3Tags
|
||||
|
||||
// If setting has changed...
|
||||
if (currentId3Setting != cachedId3Setting) {
|
||||
cachedId3Setting = currentId3Setting
|
||||
shouldRestart = true
|
||||
if (currentId3Setting != useId3) {
|
||||
useId3 = currentId3Setting
|
||||
shouldRelayout = true
|
||||
}
|
||||
|
||||
// then setup the list anew.
|
||||
if (shouldRestart) {
|
||||
if (shouldRelayout) {
|
||||
setupItemVisibility()
|
||||
}
|
||||
}
|
||||
|
@ -109,17 +109,19 @@ class MainFragment : Fragment(), KoinComponent {
|
|||
|
||||
private fun setupItemVisibility() {
|
||||
// Cache some values
|
||||
cachedId3Setting = Settings.shouldUseId3Tags
|
||||
useId3 = Settings.shouldUseId3Tags
|
||||
useId3Offline = Settings.useId3TagsOffline
|
||||
|
||||
val isOnline = !isOffline()
|
||||
|
||||
// Music
|
||||
musicTitle.isVisible = true
|
||||
artistsButton.isVisible = true
|
||||
albumsButton.isVisible = isOnline
|
||||
albumsButton.isVisible = isOnline || useId3Offline
|
||||
genresButton.isVisible = true
|
||||
|
||||
// Songs
|
||||
songsTitle.isVisible = isOnline
|
||||
songsTitle.isVisible = true
|
||||
randomSongsButton.isVisible = true
|
||||
songsStarredButton.isVisible = isOnline
|
||||
|
||||
|
@ -128,7 +130,7 @@ class MainFragment : Fragment(), KoinComponent {
|
|||
albumsNewestButton.isVisible = isOnline
|
||||
albumsRecentButton.isVisible = isOnline
|
||||
albumsFrequentButton.isVisible = isOnline
|
||||
albumsHighestButton.isVisible = isOnline && !cachedId3Setting
|
||||
albumsHighestButton.isVisible = isOnline && !useId3
|
||||
albumsRandomButton.isVisible = isOnline
|
||||
albumsStarredButton.isVisible = isOnline
|
||||
albumsAlphaByNameButton.isVisible = isOnline
|
||||
|
@ -240,6 +242,7 @@ class MainFragment : Fragment(), KoinComponent {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private var cachedId3Setting = false
|
||||
private var useId3 = false
|
||||
private var useId3Offline = false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,16 @@ import android.content.DialogInterface
|
|||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.SearchRecentSuggestions
|
||||
import android.text.SpannableString
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StyleSpan
|
||||
import android.view.View
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.DialogFragment
|
||||
|
@ -76,6 +81,7 @@ class SettingsFragment :
|
|||
private var directoryCacheTime: ListPreference? = null
|
||||
private var mediaButtonsEnabled: CheckBoxPreference? = null
|
||||
private var showArtistPicture: CheckBoxPreference? = null
|
||||
private var useId3TagsOffline: CheckBoxPreference? = null
|
||||
private var sharingDefaultDescription: EditTextPreference? = null
|
||||
private var sharingDefaultGreeting: EditTextPreference? = null
|
||||
private var sharingDefaultExpiration: TimeSpanPreference? = null
|
||||
|
@ -121,14 +127,39 @@ class SettingsFragment :
|
|||
pauseOnBluetoothDevice = findPreference(Constants.PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE)
|
||||
debugLogToFile = findPreference(Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE)
|
||||
showArtistPicture = findPreference(Constants.PREFERENCES_KEY_SHOW_ARTIST_PICTURE)
|
||||
useId3TagsOffline = findPreference(Constants.PREFERENCES_KEY_ID3_TAGS_OFFLINE)
|
||||
customCacheLocation = findPreference(Constants.PREFERENCES_KEY_CUSTOM_CACHE_LOCATION)
|
||||
|
||||
sharingDefaultGreeting?.text = shareGreeting
|
||||
|
||||
setupTextColors()
|
||||
setupClearSearchPreference()
|
||||
setupCacheLocationPreference()
|
||||
setupBluetoothDevicePreferences()
|
||||
}
|
||||
|
||||
private fun setupTextColors(enabled: Boolean = shouldUseId3Tags) {
|
||||
val firstPart = getString(R.string.settings_use_id3_offline_warning)
|
||||
var secondPart = getString(R.string.settings_use_id3_offline_summary)
|
||||
|
||||
// Little hack to circumvent a bug in Android. If we just change the color,
|
||||
// the text is not refreshed. If we also change the string, it is refreshed.
|
||||
if (enabled) secondPart += " "
|
||||
|
||||
val color = if (enabled) "#bd5164" else "#813b48"
|
||||
|
||||
Timber.i(color)
|
||||
|
||||
val warning = SpannableString(firstPart + "\n" + secondPart)
|
||||
warning.setSpan(
|
||||
ForegroundColorSpan(Color.parseColor(color)), 0, firstPart.length, 0
|
||||
)
|
||||
warning.setSpan(
|
||||
StyleSpan(Typeface.BOLD), 0, firstPart.length, 0
|
||||
)
|
||||
useId3TagsOffline?.summary = warning
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
update()
|
||||
|
@ -196,7 +227,10 @@ class SettingsFragment :
|
|||
setDebugLogToFile(sharedPreferences.getBoolean(key, false))
|
||||
}
|
||||
Constants.PREFERENCES_KEY_ID3_TAGS -> {
|
||||
showArtistPicture!!.isEnabled = sharedPreferences.getBoolean(key, false)
|
||||
val enabled = sharedPreferences.getBoolean(key, false)
|
||||
showArtistPicture?.isEnabled = enabled
|
||||
useId3TagsOffline?.isEnabled = enabled
|
||||
setupTextColors(enabled)
|
||||
}
|
||||
Constants.PREFERENCES_KEY_THEME -> {
|
||||
RxBus.themeChangedEventPublisher.onNext(Unit)
|
||||
|
@ -372,6 +406,7 @@ class SettingsFragment :
|
|||
debugLogToFile?.summary = ""
|
||||
}
|
||||
showArtistPicture?.isEnabled = shouldUseId3Tags
|
||||
useId3TagsOffline?.isEnabled = shouldUseId3Tags
|
||||
}
|
||||
|
||||
private fun setHideMedia(hide: Boolean) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* TrackCollectionFragment.kt
|
||||
* Copyright (C) 2009-2021 Ultrasonic developers
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
@ -31,6 +31,7 @@ import org.moire.ultrasonic.adapters.AlbumHeader
|
|||
import org.moire.ultrasonic.adapters.AlbumRowBinder
|
||||
import org.moire.ultrasonic.adapters.HeaderViewBinder
|
||||
import org.moire.ultrasonic.adapters.TrackViewBinder
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||
import org.moire.ultrasonic.domain.Identifiable
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
|
@ -47,6 +48,7 @@ import org.moire.ultrasonic.util.Constants
|
|||
import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator
|
||||
import org.moire.ultrasonic.util.Settings
|
||||
import org.moire.ultrasonic.util.Util
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Displays a group of tracks, eg. the songs of an album, of a playlist etc.
|
||||
|
@ -61,11 +63,11 @@ import org.moire.ultrasonic.util.Util
|
|||
open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
|
||||
private var albumButtons: View? = null
|
||||
internal var selectButton: ImageView? = null
|
||||
private var selectButton: ImageView? = null
|
||||
internal var playNowButton: ImageView? = null
|
||||
private var playNextButton: ImageView? = null
|
||||
private var playLastButton: ImageView? = null
|
||||
internal var pinButton: ImageView? = null
|
||||
private var pinButton: ImageView? = null
|
||||
private var unpinButton: ImageView? = null
|
||||
private var downloadButton: ImageView? = null
|
||||
private var deleteButton: ImageView? = null
|
||||
|
@ -144,11 +146,10 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
|||
|
||||
// Update the buttons when the selection has changed
|
||||
viewAdapter.selectionRevision.observe(
|
||||
viewLifecycleOwner,
|
||||
{
|
||||
enableButtons()
|
||||
}
|
||||
)
|
||||
viewLifecycleOwner
|
||||
) {
|
||||
enableButtons()
|
||||
}
|
||||
}
|
||||
|
||||
internal open fun setupButtons(view: View) {
|
||||
|
@ -267,10 +268,10 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
|||
private val childCount: Int
|
||||
get() {
|
||||
val count = viewAdapter.getCurrentList().count()
|
||||
if (listModel.showHeader) {
|
||||
return count - 1
|
||||
return if (listModel.showHeader) {
|
||||
count - 1
|
||||
} else {
|
||||
return count
|
||||
count
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,13 +321,13 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
|||
} as List<Track>
|
||||
}
|
||||
|
||||
internal fun selectAllOrNone() {
|
||||
private fun selectAllOrNone() {
|
||||
val someUnselected = viewAdapter.selectedSet.size < childCount
|
||||
|
||||
selectAll(someUnselected, true)
|
||||
}
|
||||
|
||||
internal fun selectAll(selected: Boolean, toast: Boolean) {
|
||||
private fun selectAll(selected: Boolean, toast: Boolean) {
|
||||
var selectedCount = viewAdapter.selectedSet.size * -1
|
||||
|
||||
selectedCount += viewAdapter.setSelectionStatusOfAll(selected)
|
||||
|
@ -366,7 +367,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
|||
deleteButton?.isVisible = (enabled && deleteEnabled)
|
||||
}
|
||||
|
||||
internal fun downloadBackground(save: Boolean) {
|
||||
private fun downloadBackground(save: Boolean) {
|
||||
var songs = getSelectedSongs()
|
||||
|
||||
if (songs.isEmpty()) {
|
||||
|
@ -426,6 +427,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
|||
|
||||
override val defaultObserver: (List<MusicDirectory.Child>) -> Unit = {
|
||||
|
||||
Timber.i("Received list")
|
||||
val entryList: MutableList<MusicDirectory.Child> = it.toMutableList()
|
||||
|
||||
if (listModel.currentListIsSortable && Settings.shouldSortByDisc) {
|
||||
|
@ -454,9 +456,9 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
|||
moreButton!!.visibility = View.GONE
|
||||
} else {
|
||||
moreButton!!.visibility = View.VISIBLE
|
||||
if (arguments?.getInt(Constants.INTENT_RANDOM, 0) ?: 0 > 0) {
|
||||
if ((arguments?.getInt(Constants.INTENT_RANDOM, 0) ?: 0) > 0) {
|
||||
moreRandomTracks()
|
||||
} else if (arguments?.getString(Constants.INTENT_GENRE_NAME, "") ?: "" != "") {
|
||||
} else if ((arguments?.getString(Constants.INTENT_GENRE_NAME, "") ?: "") != "") {
|
||||
moreSongsForGenre()
|
||||
}
|
||||
}
|
||||
|
@ -497,6 +499,8 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
|||
}
|
||||
|
||||
listModel.currentListIsSortable = true
|
||||
|
||||
Timber.i("Processed list")
|
||||
}
|
||||
|
||||
private fun moreSongsForGenre(args: Bundle = requireArguments()) {
|
||||
|
@ -556,6 +560,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
|||
args: Bundle?,
|
||||
refresh: Boolean
|
||||
): LiveData<List<MusicDirectory.Child>> {
|
||||
Timber.i("Starting gathering track collection data...")
|
||||
if (args == null) return listModel.currentList
|
||||
val id = args.getString(Constants.INTENT_ID)
|
||||
val isAlbum = args.getBoolean(Constants.INTENT_IS_ALBUM, false)
|
||||
|
@ -600,7 +605,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
|||
listModel.getRandom(albumListSize)
|
||||
} else {
|
||||
setTitle(name)
|
||||
if (!isOffline() && Settings.shouldUseId3Tags) {
|
||||
if (ActiveServerProvider.isID3Enabled()) {
|
||||
if (isAlbum) {
|
||||
listModel.getAlbum(refresh2, id!!, name)
|
||||
} else {
|
||||
|
@ -669,7 +674,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
|||
return true
|
||||
}
|
||||
|
||||
internal fun getClickedSong(item: MusicDirectory.Child): List<Track> {
|
||||
private fun getClickedSong(item: MusicDirectory.Child): List<Track> {
|
||||
// This can probably be done better
|
||||
return viewAdapter.getCurrentList().mapNotNull {
|
||||
if (it is Track && (it.id == item.id))
|
||||
|
|
|
@ -39,7 +39,7 @@ class AlbumListModel(application: Application) : GenericListModel(application) {
|
|||
id: String,
|
||||
name: String?
|
||||
) {
|
||||
list.postValue(musicService.getArtist(id, name, refresh))
|
||||
list.postValue(musicService.getAlbumsOfArtist(id, name, refresh))
|
||||
}
|
||||
|
||||
override fun load(
|
||||
|
|
|
@ -1,20 +1,8 @@
|
|||
/*
|
||||
This file is part of Subsonic.
|
||||
|
||||
Subsonic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Subsonic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2020 (C) Jozsef Varga
|
||||
* ArtistListModel.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
package org.moire.ultrasonic.model
|
||||
|
||||
|
@ -24,6 +12,7 @@ import androidx.lifecycle.LiveData
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import java.text.Collator
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.domain.ArtistOrIndex
|
||||
import org.moire.ultrasonic.service.MusicService
|
||||
|
||||
|
@ -56,12 +45,10 @@ class ArtistListModel(application: Application) : GenericListModel(application)
|
|||
|
||||
val musicFolderId = activeServer.musicFolderId
|
||||
|
||||
val result: List<ArtistOrIndex>
|
||||
|
||||
if (!isOffline && useId3Tags) {
|
||||
result = musicService.getArtists(refresh)
|
||||
val result = if (ActiveServerProvider.isID3Enabled()) {
|
||||
musicService.getArtists(refresh)
|
||||
} else {
|
||||
result = musicService.getIndexes(musicFolderId, refresh)
|
||||
musicService.getIndexes(musicFolderId, refresh)
|
||||
}
|
||||
|
||||
artists.postValue(result.toMutableList().sortedWith(comparator))
|
||||
|
|
|
@ -635,7 +635,7 @@ class AutoMediaBrowserCallback(var player: Player, val libraryService: MediaLibr
|
|||
|
||||
return serviceScope.future {
|
||||
val albums = if (!isOffline && useId3Tags) {
|
||||
callWithErrorHandling { musicService.getArtist(id, name, false) }
|
||||
callWithErrorHandling { musicService.getAlbumsOfArtist(id, name, false) }
|
||||
} else {
|
||||
callWithErrorHandling {
|
||||
musicService.getMusicDirectory(id, name, false).getAlbums()
|
||||
|
|
|
@ -43,7 +43,6 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
|||
|
||||
// Old style TimeLimitedCache
|
||||
private val cachedMusicDirectories: LRUCache<String, TimeLimitedCache<MusicDirectory?>>
|
||||
private val cachedArtist: LRUCache<String, TimeLimitedCache<List<Album>>>
|
||||
private val cachedAlbum: LRUCache<String, TimeLimitedCache<MusicDirectory?>>
|
||||
private val cachedUserInfo: LRUCache<String, TimeLimitedCache<UserInfo?>>
|
||||
private val cachedLicenseValid = TimeLimitedCache<Boolean>(120, TimeUnit.SECONDS)
|
||||
|
@ -53,7 +52,8 @@ 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 cachedIndexes = metaDatabase.indexDao()
|
||||
private val cachedMusicFolders = metaDatabase.musicFoldersDao()
|
||||
|
||||
|
@ -103,10 +103,10 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
|||
|
||||
var indexes: List<Index>
|
||||
|
||||
if (musicFolderId == null) {
|
||||
indexes = cachedIndexes.get()
|
||||
indexes = if (musicFolderId == null) {
|
||||
cachedIndexes.get()
|
||||
} else {
|
||||
indexes = cachedIndexes.get(musicFolderId)
|
||||
cachedIndexes.get(musicFolderId)
|
||||
}
|
||||
|
||||
if (indexes.isEmpty()) {
|
||||
|
@ -120,14 +120,15 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
|||
@Throws(Exception::class)
|
||||
override fun getArtists(refresh: Boolean): List<Artist> {
|
||||
checkSettingsChanged()
|
||||
|
||||
if (refresh) {
|
||||
cachedArtists.clear()
|
||||
}
|
||||
|
||||
var result = cachedArtists.get()
|
||||
|
||||
if (result.isEmpty()) {
|
||||
result = musicService.getArtists(refresh)
|
||||
cachedArtist.clear()
|
||||
cachedArtists.set(result)
|
||||
}
|
||||
return result
|
||||
|
@ -149,21 +150,29 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
|||
return dir
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves all albums of the provided artist.
|
||||
* Cached in the RoomDB
|
||||
*/
|
||||
@Throws(Exception::class)
|
||||
override fun getArtist(id: String, name: String?, refresh: Boolean):
|
||||
override fun getAlbumsOfArtist(id: String, name: String?, refresh: Boolean):
|
||||
List<Album> {
|
||||
checkSettingsChanged()
|
||||
var cache = if (refresh) null else cachedArtist[id]
|
||||
var dir = cache?.get()
|
||||
if (dir == null) {
|
||||
dir = musicService.getArtist(id, name, refresh)
|
||||
cache = TimeLimitedCache(
|
||||
Settings.directoryCacheTime.toLong(), TimeUnit.SECONDS
|
||||
)
|
||||
cache.set(dir)
|
||||
cachedArtist.put(id, cache)
|
||||
|
||||
var result: List<Album>
|
||||
|
||||
result = if (refresh) {
|
||||
cachedAlbums.clearByArtist(id)
|
||||
listOf()
|
||||
} else {
|
||||
cachedAlbums.byArtist(id)
|
||||
}
|
||||
return dir
|
||||
|
||||
if (result.isEmpty()) {
|
||||
result = musicService.getAlbumsOfArtist(id, name, refresh)
|
||||
cachedAlbums.upsert(result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
|
@ -326,7 +335,8 @@ 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()
|
||||
|
||||
// Clear in memory caches
|
||||
|
@ -335,7 +345,6 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
|||
cachedPlaylists.clear()
|
||||
cachedGenres.clear()
|
||||
cachedAlbum.clear()
|
||||
cachedArtist.clear()
|
||||
cachedUserInfo.clear()
|
||||
|
||||
// Set the cache keys
|
||||
|
@ -472,7 +481,6 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
|||
|
||||
init {
|
||||
cachedMusicDirectories = LRUCache(MUSIC_DIR_CACHE_SIZE)
|
||||
cachedArtist = LRUCache(MUSIC_DIR_CACHE_SIZE)
|
||||
cachedAlbum = LRUCache(MUSIC_DIR_CACHE_SIZE)
|
||||
cachedUserInfo = LRUCache(MUSIC_DIR_CACHE_SIZE)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.util.PriorityQueue
|
|||
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.Track
|
||||
import org.moire.ultrasonic.playback.LegacyPlaylistManager
|
||||
|
@ -402,6 +403,14 @@ class Downloader(
|
|||
downloadFile.completeFile
|
||||
)
|
||||
}
|
||||
|
||||
// Hidden feature: If track is toggled between pinned/saved, refresh the metadata..
|
||||
try {
|
||||
downloadFile.track.cacheMetadata()
|
||||
} catch (ignore: Exception) {
|
||||
Timber.w(ignore)
|
||||
}
|
||||
|
||||
downloadFile.status.postValue(newStatus)
|
||||
return
|
||||
}
|
||||
|
@ -457,8 +466,10 @@ class Downloader(
|
|||
)
|
||||
}
|
||||
|
||||
if (downloadFile.track.artistId != null) {
|
||||
cacheMetadata(downloadFile.track.artistId!!)
|
||||
try {
|
||||
downloadFile.track.cacheMetadata()
|
||||
} catch (ignore: Exception) {
|
||||
Timber.w(ignore)
|
||||
}
|
||||
|
||||
downloadAndSaveCoverArt()
|
||||
|
@ -510,13 +521,35 @@ class Downloader(
|
|||
return String.format(Locale.ROOT, "DownloadTask (%s)", downloadFile.track)
|
||||
}
|
||||
|
||||
private fun cacheMetadata(artistId: String) {
|
||||
// TODO: Right now it's caching the track artist.
|
||||
// Once the albums are cached in db, we should retrieve the album,
|
||||
// and then cache the album artist.
|
||||
if (artistId.isEmpty()) return
|
||||
var artist: Artist? =
|
||||
activeServerProvider.getActiveMetaDatabase().artistsDao().get(artistId)
|
||||
private fun Track.cacheMetadata() {
|
||||
if (artistId.isNullOrEmpty()) return
|
||||
|
||||
val onlineDB = activeServerProvider.getActiveMetaDatabase()
|
||||
val offlineDB = activeServerProvider.offlineMetaDatabase
|
||||
|
||||
cacheArtist(onlineDB, offlineDB, artistId!!)
|
||||
|
||||
// Now cache the album
|
||||
if (albumId?.isNotEmpty() == true) {
|
||||
// This is a cached call
|
||||
val albums = musicService.getAlbumsOfArtist(artistId!!, null, false)
|
||||
val album = albums.find { it.id == albumId }
|
||||
|
||||
if (album != null) {
|
||||
offlineDB.albumDao().insert(album)
|
||||
|
||||
// If the album is a Compilation, also cache the Album artist
|
||||
if (album.artistId != null && album.artistId != artistId)
|
||||
cacheArtist(onlineDB, offlineDB, album.artistId!!)
|
||||
}
|
||||
}
|
||||
|
||||
// Now cache the track data
|
||||
offlineDB.trackDao().insert(this)
|
||||
}
|
||||
|
||||
private fun cacheArtist(onlineDB: MetaDatabase, offlineDB: MetaDatabase, artistId: String) {
|
||||
var artist: Artist? = onlineDB.artistDao().get(artistId)
|
||||
|
||||
// If we are downloading a new album, and the user has not visited the Artists list
|
||||
// recently, then the artist won't be in the database.
|
||||
|
@ -527,9 +560,9 @@ class Downloader(
|
|||
}
|
||||
}
|
||||
|
||||
// If we have found an artist, catch it.
|
||||
// If we have found an artist, cache it.
|
||||
if (artist != null) {
|
||||
activeServerProvider.offlineMetaDatabase.artistsDao().insert(artist)
|
||||
offlineDB.artistDao().insert(artist)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ interface MusicService {
|
|||
fun getMusicDirectory(id: String, name: String?, refresh: Boolean): MusicDirectory
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getArtist(id: String, name: String?, refresh: Boolean): List<Album>
|
||||
fun getAlbumsOfArtist(id: String, name: String?, refresh: Boolean): List<Album>
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
@ -23,6 +23,7 @@ import java.util.regex.Pattern
|
|||
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.Album
|
||||
import org.moire.ultrasonic.domain.Artist
|
||||
import org.moire.ultrasonic.domain.ArtistOrIndex
|
||||
|
@ -43,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
|
||||
|
@ -52,12 +54,19 @@ import timber.log.Timber
|
|||
class OfflineMusicService : MusicService, KoinComponent {
|
||||
private val activeServerProvider: ActiveServerProvider by inject()
|
||||
|
||||
private var metaDatabase: MetaDatabase = activeServerProvider.getActiveMetaDatabase()
|
||||
|
||||
// New Room Database
|
||||
private var cachedArtists = metaDatabase.artistDao()
|
||||
private var cachedAlbums = metaDatabase.albumDao()
|
||||
private var cachedTracks = metaDatabase.trackDao()
|
||||
|
||||
override fun getIndexes(musicFolderId: String?, refresh: Boolean): List<Index> {
|
||||
val indexes: MutableList<Index> = ArrayList()
|
||||
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
|
||||
|
@ -97,6 +106,13 @@ class OfflineMusicService : MusicService, KoinComponent {
|
|||
return indexes
|
||||
}
|
||||
|
||||
@Throws(OfflineException::class)
|
||||
override fun getArtists(refresh: Boolean): List<Artist> {
|
||||
val result = cachedArtists.get()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
* Especially when dealing with indexes, this method can return Albums, Entries or a mix of both!
|
||||
*/
|
||||
|
@ -312,7 +328,8 @@ class OfflineMusicService : MusicService, KoinComponent {
|
|||
offset: Int,
|
||||
musicFolderId: String?
|
||||
): List<Album> {
|
||||
throw OfflineException("getAlbumList2 isn't available in offline mode")
|
||||
// TODO: Implement filtering by musicFolder?
|
||||
return cachedAlbums.get(size, offset)
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
|
@ -450,20 +467,39 @@ class OfflineMusicService : MusicService, KoinComponent {
|
|||
|
||||
override fun isLicenseValid(): Boolean = true
|
||||
|
||||
@Throws(OfflineException::class)
|
||||
override fun getArtists(refresh: Boolean): List<Artist> {
|
||||
throw OfflineException("getArtists isn't available in offline mode")
|
||||
}
|
||||
|
||||
@Throws(OfflineException::class)
|
||||
override fun getArtist(id: String, name: String?, refresh: Boolean):
|
||||
@Throws(Exception::class)
|
||||
override fun getAlbumsOfArtist(id: String, name: String?, refresh: Boolean):
|
||||
List<Album> {
|
||||
throw OfflineException("getArtist isn't available in offline mode")
|
||||
val directAlbums = cachedAlbums.byArtist(id)
|
||||
|
||||
// The direct albums won't contain any compilations that the artist has participated in
|
||||
// We need to fetch the tracks of the artist and then gather the compilation albums from that.
|
||||
val tracks = cachedTracks.byArtist(id)
|
||||
val albumIds = tracks.map {
|
||||
it.albumId
|
||||
}.distinct().filterNotNull()
|
||||
|
||||
val compilationAlbums = albumIds.map {
|
||||
cachedAlbums.get(it)
|
||||
}
|
||||
|
||||
return directAlbums.plus(compilationAlbums).distinct()
|
||||
}
|
||||
|
||||
@Throws(OfflineException::class)
|
||||
override fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory {
|
||||
throw OfflineException("getAlbum isn't available in offline mode")
|
||||
|
||||
Timber.i("Starting album query...")
|
||||
|
||||
val list = cachedTracks
|
||||
.byAlbum(id)
|
||||
.sortedWith(EntryByDiscAndTrackComparator())
|
||||
|
||||
val dir = MusicDirectory()
|
||||
dir.addAll(list)
|
||||
|
||||
Timber.i("Returning query.")
|
||||
return dir
|
||||
}
|
||||
|
||||
@Throws(OfflineException::class)
|
||||
|
|
|
@ -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<MusicFolder> {
|
||||
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<Index> {
|
||||
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<Artist> {
|
||||
val response = API.getArtists(null).execute().throwOnFailure()
|
||||
|
||||
return response.body()!!.indexes.toArtistList()
|
||||
return response.body()!!.indexes.toArtistList(activeServerId)
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
|
@ -137,18 +140,18 @@ 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)
|
||||
override fun getArtist(
|
||||
override fun getAlbumsOfArtist(
|
||||
id: String,
|
||||
name: String?,
|
||||
refresh: Boolean
|
||||
): List<Album> {
|
||||
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<Share> {
|
||||
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<Bookmark> {
|
||||
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<Share> {
|
||||
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
|
||||
|
|
|
@ -269,7 +269,7 @@ class DownloadHandler(
|
|||
return
|
||||
}
|
||||
val musicService = getMusicService()
|
||||
val artist = musicService.getArtist(id, "", false)
|
||||
val artist = musicService.getAlbumsOfArtist(id, "", false)
|
||||
for ((id1) in artist) {
|
||||
val albumDirectory = musicService.getAlbum(
|
||||
id1,
|
||||
|
|
|
@ -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"
|
||||
|
@ -104,6 +105,7 @@ object Constants {
|
|||
const val PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE = "pauseOnBluetoothDevice"
|
||||
const val PREFERENCES_KEY_DEBUG_LOG_TO_FILE = "debugLogToFile"
|
||||
const val PREFERENCES_KEY_OVERRIDE_LANGUAGE = "overrideLanguage"
|
||||
const val PREFERENCES_FIRST_INSTALLED_VERSION = "firstInstalledVersion"
|
||||
const val PREFERENCE_VALUE_ALL = 0
|
||||
const val PREFERENCE_VALUE_A2DP = 1
|
||||
const val PREFERENCE_VALUE_DISABLED = 2
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Settings.kt
|
||||
* Copyright (C) 2009-2021 Ultrasonic developers
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
@ -13,7 +13,6 @@ import androidx.preference.PreferenceManager
|
|||
import java.util.regex.Pattern
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.app.UApp
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
|
||||
/**
|
||||
* Contains convenience functions for reading and writing preferences
|
||||
|
@ -131,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)
|
||||
|
||||
|
@ -160,9 +160,14 @@ object Settings {
|
|||
var showNowPlayingDetails
|
||||
by BooleanSetting(Constants.PREFERENCES_KEY_SHOW_NOW_PLAYING_DETAILS, false)
|
||||
|
||||
// Normally you don't need to use these Settings directly,
|
||||
// use ActiveServerProvider.isID3Enabled() instead
|
||||
@JvmStatic
|
||||
var shouldUseId3Tags
|
||||
by BooleanSetting(Constants.PREFERENCES_KEY_ID3_TAGS, false)
|
||||
var shouldUseId3Tags by BooleanSetting(Constants.PREFERENCES_KEY_ID3_TAGS, false)
|
||||
|
||||
// See comment above.
|
||||
@JvmStatic
|
||||
var useId3TagsOffline by BooleanSetting(Constants.PREFERENCES_KEY_ID3_TAGS_OFFLINE, false)
|
||||
|
||||
var activeServer by IntSetting(Constants.PREFERENCES_KEY_SERVER_INSTANCE, -1)
|
||||
|
||||
|
@ -170,15 +175,8 @@ object Settings {
|
|||
|
||||
var firstRunExecuted by BooleanSetting(Constants.PREFERENCES_KEY_FIRST_RUN_EXECUTED, false)
|
||||
|
||||
val shouldShowArtistPicture: Boolean
|
||||
get() {
|
||||
val preferences = preferences
|
||||
val isOffline = ActiveServerProvider.isOffline()
|
||||
val isId3Enabled = preferences.getBoolean(Constants.PREFERENCES_KEY_ID3_TAGS, false)
|
||||
val shouldShowArtistPicture =
|
||||
preferences.getBoolean(Constants.PREFERENCES_KEY_SHOW_ARTIST_PICTURE, false)
|
||||
return !isOffline && isId3Enabled && shouldShowArtistPicture
|
||||
}
|
||||
val shouldShowArtistPicture
|
||||
by BooleanSetting(Constants.PREFERENCES_KEY_SHOW_ARTIST_PICTURE, false)
|
||||
|
||||
@JvmStatic
|
||||
var chatRefreshInterval by StringIntSetting(
|
||||
|
@ -253,6 +251,9 @@ object Settings {
|
|||
|
||||
var useHwOffload by BooleanSetting(Constants.PREFERENCES_KEY_HARDWARE_OFFLOAD, false)
|
||||
|
||||
@JvmStatic
|
||||
var firstInstalledVersion by IntSetting(Constants.PREFERENCES_FIRST_INSTALLED_VERSION, 0)
|
||||
|
||||
// TODO: Remove in December 2022
|
||||
fun migrateFeatureStorage() {
|
||||
val sp = appContext.getSharedPreferences("feature_flags", Context.MODE_PRIVATE)
|
||||
|
|
|
@ -316,6 +316,9 @@
|
|||
<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_warning">Experimental: If you enable this Setting it will only show the music that you have downloaded with Ultrasonic 4.0 or later.</string>
|
||||
<string name="settings.use_id3_offline_summary">Earlier downloads don\'t have the necessary metadata downloaded. You can toggle between Pin and Save mode to trigger the download of the missing metadata.</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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -12,6 +12,8 @@ import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
|||
* 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 +22,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 +48,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 +61,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(serverId)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import org.moire.ultrasonic.api.subsonic.models.Indexes
|
|||
/**
|
||||
* Unit tests for extension functions in [APIIndexesConverter.kt].
|
||||
*/
|
||||
class APIIndexConverterTest {
|
||||
class APIIndexConverterTest : BaseTest() {
|
||||
@Test
|
||||
fun `Should convert Indexes entity`() {
|
||||
val artistsA = listOf(
|
||||
|
@ -31,9 +31,12 @@ class APIIndexConverterTest {
|
|||
shortcutList = artistsA
|
||||
)
|
||||
|
||||
val convertedEntity = entity.toArtistList()
|
||||
val convertedEntity = entity.toArtistList(serverId)
|
||||
|
||||
val expectedArtists = (artistsA + artistsT).map {
|
||||
it.toDomainEntity(serverId)
|
||||
}.toMutableList()
|
||||
|
||||
val expectedArtists = (artistsA + artistsT).map { it.toDomainEntity() }.toMutableList()
|
||||
with(convertedEntity) {
|
||||
size `should be equal to` expectedArtists.size
|
||||
this `should be equal to` expectedArtists
|
||||
|
|
|
@ -11,7 +11,7 @@ import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
|||
/**
|
||||
* 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 +20,13 @@ class APIMusicDirectoryConverterTest {
|
|||
childList = listOf(MusicDirectoryChild("1"), MusicDirectoryChild("2"))
|
||||
)
|
||||
|
||||
val convertedEntity = entity.toDomainEntity()
|
||||
val convertedEntity = entity.toDomainEntity(serverId)
|
||||
|
||||
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 +44,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 +84,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 +96,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -11,7 +11,7 @@ import org.moire.ultrasonic.api.subsonic.models.Playlist
|
|||
/**
|
||||
* 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 +22,13 @@ class APIPlaylistConverterTest {
|
|||
)
|
||||
)
|
||||
|
||||
val convertedEntity = entity.toMusicDirectoryDomainEntity()
|
||||
val convertedEntity = entity.toMusicDirectoryDomainEntity(serverId)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import org.moire.ultrasonic.api.subsonic.models.SearchTwoResult
|
|||
/**
|
||||
* 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 +26,7 @@ class APISearchConverterTest {
|
|||
)
|
||||
)
|
||||
|
||||
val convertedEntity = entity.toDomainEntity()
|
||||
val convertedEntity = entity.toDomainEntity(serverId)
|
||||
|
||||
with(convertedEntity) {
|
||||
albums `should not be equal to` null
|
||||
|
@ -34,7 +34,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 +46,15 @@ class APISearchConverterTest {
|
|||
listOf(MusicDirectoryChild(id = "9118", parent = "112"))
|
||||
)
|
||||
|
||||
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].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 +66,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,12 +11,12 @@ import org.moire.ultrasonic.api.subsonic.models.Share
|
|||
/**
|
||||
* Unit test for api to domain share entity converter functions.
|
||||
*/
|
||||
class APIShareConverterTest {
|
||||
class APIShareConverterTest : BaseTest() {
|
||||
@Test
|
||||
fun `Should convert share entity to domain`() {
|
||||
val entity = createFakeShare()
|
||||
|
||||
val domainEntity = entity.toDomainEntity()
|
||||
val domainEntity = entity.toDomainEntity(serverId)
|
||||
|
||||
with(domainEntity) {
|
||||
id `should be equal to` entity.id
|
||||
|
@ -27,7 +27,7 @@ class APIShareConverterTest {
|
|||
lastVisited `should be equal to` shareTimeFormat.format(entity.lastVisited!!.time)
|
||||
expires `should be equal to` shareTimeFormat.format(entity.expires!!.time)
|
||||
visitCount `should be equal to` entity.visitCount.toLong()
|
||||
this.getEntries() `should be equal to` entity.items.toDomainEntityList()
|
||||
this.getEntries() `should be equal to` entity.items.toDomainEntityList(serverId)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,10 +47,10 @@ class APIShareConverterTest {
|
|||
createFakeShare().copy(id = "554", lastVisited = null)
|
||||
)
|
||||
|
||||
val domainEntityList = entityList.toDomainEntitiesList()
|
||||
val domainEntityList = entityList.toDomainEntitiesList(serverId)
|
||||
|
||||
domainEntityList.size `should be equal to` entityList.size
|
||||
domainEntityList[0] `should be equal to` entityList[0].toDomainEntity()
|
||||
domainEntityList[1] `should be equal to` entityList[1].toDomainEntity()
|
||||
domainEntityList[0] `should be equal to` entityList[0].toDomainEntity(serverId)
|
||||
domainEntityList[1] `should be equal to` entityList[1].toDomainEntity(serverId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue