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
|
package org.moire.ultrasonic.domain
|
||||||
|
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
|
@Entity(tableName = "albums", primaryKeys = ["id", "serverId"])
|
||||||
data class Album(
|
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 parent: String? = null,
|
||||||
override var album: String? = null,
|
override var album: String? = null,
|
||||||
override var title: 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
|
package org.moire.ultrasonic.domain
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
|
|
||||||
@Entity(tableName = "artists")
|
@Entity(tableName = "artists", primaryKeys = ["id", "serverId"])
|
||||||
data class Artist(
|
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 name: String? = null,
|
||||||
override var index: String? = null,
|
override var index: String? = null,
|
||||||
override var coverArt: String? = null,
|
override var coverArt: String? = null,
|
||||||
override var albumCount: Long? = null,
|
override var albumCount: Long? = null,
|
||||||
override var closeness: Int = 0
|
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
|
package org.moire.ultrasonic.domain
|
||||||
|
|
||||||
import androidx.room.Ignore
|
import androidx.room.Ignore
|
||||||
|
|
||||||
|
@Suppress("LongParameterList")
|
||||||
abstract class ArtistOrIndex(
|
abstract class ArtistOrIndex(
|
||||||
@Ignore
|
@Ignore
|
||||||
override var id: String,
|
override var id: String,
|
||||||
@Ignore
|
@Ignore
|
||||||
|
open var serverId: Int,
|
||||||
|
@Ignore
|
||||||
override var name: String? = null,
|
override var name: String? = null,
|
||||||
@Ignore
|
@Ignore
|
||||||
open var index: String? = null,
|
open var index: String? = null,
|
||||||
|
@ -18,15 +28,15 @@ abstract class ArtistOrIndex(
|
||||||
) : GenericEntry() {
|
) : GenericEntry() {
|
||||||
|
|
||||||
fun compareTo(other: ArtistOrIndex): Int {
|
fun compareTo(other: ArtistOrIndex): Int {
|
||||||
when {
|
return when {
|
||||||
this.closeness == other.closeness -> {
|
this.closeness == other.closeness -> {
|
||||||
return 0
|
0
|
||||||
}
|
}
|
||||||
this.closeness > other.closeness -> {
|
this.closeness > other.closeness -> {
|
||||||
return -1
|
-1
|
||||||
}
|
}
|
||||||
else -> {
|
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
|
package org.moire.ultrasonic.domain
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
|
|
||||||
@Entity(tableName = "indexes")
|
@Entity(tableName = "indexes", primaryKeys = ["id", "serverId"])
|
||||||
data class Index(
|
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 name: String? = null,
|
||||||
override var index: String? = null,
|
override var index: String? = null,
|
||||||
override var coverArt: String? = null,
|
override var coverArt: String? = null,
|
||||||
override var albumCount: Long? = null,
|
override var albumCount: Long? = null,
|
||||||
override var closeness: Int = 0,
|
override var closeness: Int = 0,
|
||||||
var musicFolderId: String? = null
|
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
|
package org.moire.ultrasonic.domain
|
||||||
|
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
@ -31,6 +38,7 @@ class MusicDirectory : ArrayList<MusicDirectory.Child>() {
|
||||||
|
|
||||||
abstract class Child : GenericEntry() {
|
abstract class Child : GenericEntry() {
|
||||||
abstract override var id: String
|
abstract override var id: String
|
||||||
|
abstract var serverId: Int
|
||||||
abstract var parent: String?
|
abstract var parent: String?
|
||||||
abstract var isDirectory: Boolean
|
abstract var isDirectory: Boolean
|
||||||
abstract var album: String?
|
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
|
package org.moire.ultrasonic.domain
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a top level directory in which music or other media is stored.
|
* 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(
|
data class MusicFolder(
|
||||||
@PrimaryKey override val id: String,
|
override val id: String,
|
||||||
override val name: String
|
override val name: String,
|
||||||
|
@ColumnInfo(defaultValue = "-1")
|
||||||
|
var serverId: Int
|
||||||
) : GenericEntry()
|
) : 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
|
package org.moire.ultrasonic.domain
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
@Entity
|
@Entity(tableName = "tracks", primaryKeys = ["id", "serverId"])
|
||||||
data class Track(
|
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 parent: String? = null,
|
||||||
override var isDirectory: Boolean = false,
|
override var isDirectory: Boolean = false,
|
||||||
override var title: String? = null,
|
override var title: String? = null,
|
||||||
|
|
|
@ -64,10 +64,7 @@ style:
|
||||||
WildcardImport:
|
WildcardImport:
|
||||||
active: true
|
active: true
|
||||||
MaxLineLength:
|
MaxLineLength:
|
||||||
active: true
|
active: false
|
||||||
maxLineLength: 120
|
|
||||||
excludePackageStatements: false
|
|
||||||
excludeImportStatements: false
|
|
||||||
MagicNumber:
|
MagicNumber:
|
||||||
# 100 common in percentage, 1000 in milliseconds
|
# 100 common in percentage, 1000 in milliseconds
|
||||||
ignoreNumbers: ['-1', '0', '1', '2', '5', '10', '100', '256', '512', '1000', '1024', '4096']
|
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) {
|
if (!infoDialogDisplayed) {
|
||||||
infoDialogDisplayed = true
|
infoDialogDisplayed = true
|
||||||
|
|
||||||
|
Settings.firstInstalledVersion = Util.getVersionCode(UApp.applicationContext())
|
||||||
|
|
||||||
InfoDialog.Builder(this)
|
InfoDialog.Builder(this)
|
||||||
.setTitle(R.string.main_welcome_title)
|
.setTitle(R.string.main_welcome_title)
|
||||||
.setMessage(R.string.main_welcome_text_demo)
|
.setMessage(R.string.main_welcome_text_demo)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* ArtistRowAdapter.kt
|
* ArtistRowBinder.kt
|
||||||
* Copyright (C) 2009-2021 Ultrasonic developers
|
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||||
*
|
*
|
||||||
* Distributed under terms of the GNU GPLv3 license.
|
* Distributed under terms of the GNU GPLv3 license.
|
||||||
*/
|
*/
|
||||||
|
@ -19,6 +19,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.drakeet.multitype.ItemViewBinder
|
import com.drakeet.multitype.ItemViewBinder
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
|
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||||
import org.moire.ultrasonic.domain.ArtistOrIndex
|
import org.moire.ultrasonic.domain.ArtistOrIndex
|
||||||
import org.moire.ultrasonic.domain.Identifiable
|
import org.moire.ultrasonic.domain.Identifiable
|
||||||
import org.moire.ultrasonic.imageloader.ImageLoader
|
import org.moire.ultrasonic.imageloader.ImageLoader
|
||||||
|
@ -57,7 +58,7 @@ class ArtistRowBinder(
|
||||||
|
|
||||||
holder.coverArtId = item.coverArt
|
holder.coverArtId = item.coverArt
|
||||||
|
|
||||||
if (Settings.shouldShowArtistPicture) {
|
if (showArtistPicture()) {
|
||||||
holder.coverArt.visibility = View.VISIBLE
|
holder.coverArt.visibility = View.VISIBLE
|
||||||
val key = FileUtil.getArtistArtKey(item.name, false)
|
val key = FileUtil.getArtistArtKey(item.name, false)
|
||||||
imageLoader.loadImage(
|
imageLoader.loadImage(
|
||||||
|
@ -108,6 +109,10 @@ class ArtistRowBinder(
|
||||||
return section.toString()
|
return section.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showArtistPicture(): Boolean {
|
||||||
|
return ActiveServerProvider.isID3Enabled() && Settings.shouldShowArtistPicture
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance of our ViewHolder class
|
* 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")
|
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),
|
AdapterListUpdateCallback(this),
|
||||||
AsyncDifferConfig.Builder(diffCallback).build()
|
AsyncDifferConfig.Builder(diffCallback).build()
|
||||||
)
|
)
|
||||||
|
@ -182,12 +182,11 @@ class BaseAdapter<T : Identifiable> : MultiTypeAdapter(), FastScrollRecyclerView
|
||||||
|
|
||||||
// Select them all
|
// Select them all
|
||||||
getCurrentList().mapNotNullTo(
|
getCurrentList().mapNotNullTo(
|
||||||
selectedSet,
|
selectedSet
|
||||||
{ entry ->
|
) { entry ->
|
||||||
// Exclude any -1 ids, eg. headers and other UI elements
|
// Exclude any -1 ids, eg. headers and other UI elements
|
||||||
entry.longId.takeIf { it != -1L }
|
entry.longId.takeIf { it != -1L }
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
return selectedSet.count()
|
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
|
package org.moire.ultrasonic.data
|
||||||
|
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
|
@ -110,20 +117,23 @@ class ActiveServerProvider(
|
||||||
|
|
||||||
Timber.i("Switching to new database, id:$activeServer")
|
Timber.i("Switching to new database, id:$activeServer")
|
||||||
cachedServerId = activeServer
|
cachedServerId = activeServer
|
||||||
return buildDatabase(cachedServerId)
|
cachedDatabase = initDatabase(activeServer)
|
||||||
|
|
||||||
|
return cachedDatabase!!
|
||||||
}
|
}
|
||||||
|
|
||||||
val offlineMetaDatabase: MetaDatabase by lazy {
|
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(
|
return Room.databaseBuilder(
|
||||||
UApp.applicationContext(),
|
UApp.applicationContext(),
|
||||||
MetaDatabase::class.java,
|
MetaDatabase::class.java,
|
||||||
METADATA_DB + id
|
METADATA_DB + serverId
|
||||||
)
|
)
|
||||||
.fallbackToDestructiveMigration()
|
.addMigrations(META_MIGRATION_2_3)
|
||||||
|
.fallbackToDestructiveMigrationOnDowngrade()
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,6 +249,13 @@ class ActiveServerProvider(
|
||||||
return preferences.getBoolean(Constants.PREFERENCES_KEY_SCROBBLE, false)
|
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
|
* 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
|
import org.moire.ultrasonic.domain.Artist
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface ArtistsDao {
|
interface ArtistDao {
|
||||||
/**
|
/**
|
||||||
* Insert a list in the database. If the item already exists, replace it.
|
* Insert a list in the database. If the item already exists, replace it.
|
||||||
*
|
*
|
||||||
|
@ -43,5 +43,5 @@ interface ArtistsDao {
|
||||||
* Get artist by id
|
* Get artist by id
|
||||||
*/
|
*/
|
||||||
@Query("SELECT * FROM artists WHERE id LIKE :id")
|
@Query("SELECT * FROM artists WHERE id LIKE :id")
|
||||||
fun get(id: String): Artist
|
fun get(id: String): Artist?
|
||||||
}
|
}
|
|
@ -53,6 +53,7 @@ interface IndexDao : GenericDao<Index> {
|
||||||
fun get(musicFolderId: String): List<Index>
|
fun get(musicFolderId: String): List<Index>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* TODO: Make generic
|
||||||
* Upserts (insert or update) an object to the database
|
* Upserts (insert or update) an object to the database
|
||||||
*
|
*
|
||||||
* @param obj the object to upsert
|
* @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
|
package org.moire.ultrasonic.data
|
||||||
|
|
||||||
|
import androidx.room.AutoMigration
|
||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
|
import androidx.room.TypeConverter
|
||||||
|
import androidx.room.TypeConverters
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
import java.util.Date
|
||||||
|
import org.moire.ultrasonic.domain.Album
|
||||||
import org.moire.ultrasonic.domain.Artist
|
import org.moire.ultrasonic.domain.Artist
|
||||||
import org.moire.ultrasonic.domain.Index
|
import org.moire.ultrasonic.domain.Index
|
||||||
import org.moire.ultrasonic.domain.MusicFolder
|
import org.moire.ultrasonic.domain.MusicFolder
|
||||||
|
import org.moire.ultrasonic.domain.Track
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This database is used to store and cache the ID3 metadata
|
* This database is used to store and cache the ID3 metadata
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [Artist::class, Index::class, MusicFolder::class],
|
entities = [
|
||||||
version = 1,
|
Artist::class,
|
||||||
exportSchema = true
|
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 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 musicFoldersDao(): MusicFoldersDao
|
||||||
|
|
||||||
abstract fun indexDao(): IndexDao
|
abstract fun indexDao(): IndexDao
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Converters {
|
||||||
|
@TypeConverter
|
||||||
|
fun fromTimestamp(value: Long?): Date? {
|
||||||
|
return value?.let { Date(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun dateToTimestamp(date: Date?): Long? {
|
||||||
|
return date?.time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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]
|
// Converts Album entity from [org.moire.ultrasonic.api.subsonic.SubsonicAPIClient]
|
||||||
// to app domain entities.
|
// to app domain entities.
|
||||||
@file:JvmName("APIAlbumConverter")
|
@file:JvmName("APIAlbumConverter")
|
||||||
|
@ -6,8 +13,9 @@ package org.moire.ultrasonic.domain
|
||||||
import org.moire.ultrasonic.api.subsonic.models.Album
|
import org.moire.ultrasonic.api.subsonic.models.Album
|
||||||
typealias DomainAlbum = org.moire.ultrasonic.domain.Album
|
typealias DomainAlbum = org.moire.ultrasonic.domain.Album
|
||||||
|
|
||||||
fun Album.toDomainEntity(): DomainAlbum = Album(
|
fun Album.toDomainEntity(serverId: Int): DomainAlbum = Album(
|
||||||
id = this@toDomainEntity.id,
|
id = this@toDomainEntity.id,
|
||||||
|
serverId = serverId,
|
||||||
title = this@toDomainEntity.name ?: this@toDomainEntity.title,
|
title = this@toDomainEntity.name ?: this@toDomainEntity.title,
|
||||||
album = this@toDomainEntity.album,
|
album = this@toDomainEntity.album,
|
||||||
coverArt = this@toDomainEntity.coverArt,
|
coverArt = this@toDomainEntity.coverArt,
|
||||||
|
@ -21,8 +29,10 @@ fun Album.toDomainEntity(): DomainAlbum = Album(
|
||||||
starred = this@toDomainEntity.starredDate.isNotEmpty()
|
starred = this@toDomainEntity.starredDate.isNotEmpty()
|
||||||
)
|
)
|
||||||
|
|
||||||
fun Album.toMusicDirectoryDomainEntity(): MusicDirectory = MusicDirectory().apply {
|
fun Album.toMusicDirectoryDomainEntity(serverId: Int): MusicDirectory = MusicDirectory().apply {
|
||||||
addAll(this@toMusicDirectoryDomainEntity.songList.map { it.toTrackEntity() })
|
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]
|
// Converts Artist entity from [org.moire.ultrasonic.api.subsonic.SubsonicAPIClient]
|
||||||
// to app domain entities.
|
// to app domain entities.
|
||||||
@file:JvmName("APIArtistConverter")
|
@file:JvmName("APIArtistConverter")
|
||||||
|
@ -6,24 +13,26 @@ package org.moire.ultrasonic.domain
|
||||||
import org.moire.ultrasonic.api.subsonic.models.Artist as APIArtist
|
import org.moire.ultrasonic.api.subsonic.models.Artist as APIArtist
|
||||||
|
|
||||||
// When we like to convert to an Artist
|
// When we like to convert to an Artist
|
||||||
fun APIArtist.toDomainEntity(): Artist = Artist(
|
fun APIArtist.toDomainEntity(serverId: Int): Artist = Artist(
|
||||||
id = this@toDomainEntity.id,
|
id = this@toDomainEntity.id,
|
||||||
|
serverId = serverId,
|
||||||
coverArt = this@toDomainEntity.coverArt,
|
coverArt = this@toDomainEntity.coverArt,
|
||||||
name = this@toDomainEntity.name
|
name = this@toDomainEntity.name
|
||||||
)
|
)
|
||||||
|
|
||||||
// When we like to convert to an index (eg. a single directory).
|
// 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,
|
id = this@toIndexEntity.id,
|
||||||
|
serverId = serverId,
|
||||||
coverArt = this@toIndexEntity.coverArt,
|
coverArt = this@toIndexEntity.coverArt,
|
||||||
name = this@toIndexEntity.name
|
name = this@toIndexEntity.name
|
||||||
)
|
)
|
||||||
|
|
||||||
fun APIArtist.toMusicDirectoryDomainEntity(): MusicDirectory = MusicDirectory().apply {
|
fun APIArtist.toMusicDirectoryDomainEntity(serverId: Int): MusicDirectory = MusicDirectory().apply {
|
||||||
name = this@toMusicDirectoryDomainEntity.name
|
name = this@toMusicDirectoryDomainEntity.name
|
||||||
addAll(this@toMusicDirectoryDomainEntity.albumsList.map { it.toDomainEntity() })
|
addAll(this@toMusicDirectoryDomainEntity.albumsList.map { it.toDomainEntity(serverId) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fun APIArtist.toDomainEntityList(): List<Album> {
|
fun APIArtist.toDomainEntityList(serverId: Int): List<Album> {
|
||||||
return this.albumsList.map { it.toDomainEntity() }
|
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
|
// Contains helper functions to convert api Bookmark entity to domain entity
|
||||||
@file:JvmName("APIBookmarkConverter")
|
@file:JvmName("APIBookmarkConverter")
|
||||||
|
|
||||||
package org.moire.ultrasonic.domain
|
package org.moire.ultrasonic.domain
|
||||||
|
|
||||||
import org.moire.ultrasonic.api.subsonic.models.Bookmark as ApiBookmark
|
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(),
|
position = this@toDomainEntity.position.toInt(),
|
||||||
username = this@toDomainEntity.username,
|
username = this@toDomainEntity.username,
|
||||||
comment = this@toDomainEntity.comment,
|
comment = this@toDomainEntity.comment,
|
||||||
created = this@toDomainEntity.created?.time,
|
created = this@toDomainEntity.created?.time,
|
||||||
changed = this@toDomainEntity.changed?.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]
|
// Converts Indexes entity from [org.moire.ultrasonic.api.subsonic.SubsonicAPIClient]
|
||||||
// to app domain entities.
|
// to app domain entities.
|
||||||
@file:JvmName("APIIndexesConverter")
|
@file:JvmName("APIIndexesConverter")
|
||||||
|
|
||||||
package org.moire.ultrasonic.domain
|
package org.moire.ultrasonic.domain
|
||||||
|
|
||||||
import org.moire.ultrasonic.api.subsonic.models.Index as APIIndex
|
import org.moire.ultrasonic.api.subsonic.models.Index as APIIndex
|
||||||
import org.moire.ultrasonic.api.subsonic.models.Indexes as APIIndexes
|
import org.moire.ultrasonic.api.subsonic.models.Indexes as APIIndexes
|
||||||
|
|
||||||
fun APIIndexes.toArtistList(): List<Artist> {
|
fun APIIndexes.toArtistList(serverId: Int): List<Artist> {
|
||||||
val shortcuts = this.shortcutList.map { it.toDomainEntity() }.toMutableList()
|
val shortcuts = this.shortcutList.map { it.toDomainEntity(serverId) }.toMutableList()
|
||||||
val indexes = this.indexList.foldIndexToArtistList()
|
val indexes = this.indexList.foldIndexToArtistList(serverId)
|
||||||
|
|
||||||
indexes.forEach {
|
indexes.forEach {
|
||||||
if (!shortcuts.contains(it)) {
|
if (!shortcuts.contains(it)) {
|
||||||
|
@ -19,9 +27,9 @@ fun APIIndexes.toArtistList(): List<Artist> {
|
||||||
return shortcuts
|
return shortcuts
|
||||||
}
|
}
|
||||||
|
|
||||||
fun APIIndexes.toIndexList(musicFolderId: String?): List<Index> {
|
fun APIIndexes.toIndexList(serverId: Int, musicFolderId: String?): List<Index> {
|
||||||
val shortcuts = this.shortcutList.map { it.toIndexEntity() }.toMutableList()
|
val shortcuts = this.shortcutList.map { it.toIndexEntity(serverId) }.toMutableList()
|
||||||
val indexes = this.indexList.foldIndexToIndexList(musicFolderId)
|
val indexes = this.indexList.foldIndexToIndexList(musicFolderId, serverId)
|
||||||
|
|
||||||
indexes.forEach {
|
indexes.forEach {
|
||||||
if (!shortcuts.contains(it)) {
|
if (!shortcuts.contains(it)) {
|
||||||
|
@ -32,22 +40,23 @@ fun APIIndexes.toIndexList(musicFolderId: String?): List<Index> {
|
||||||
return shortcuts
|
return shortcuts
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<APIIndex>.foldIndexToArtistList(): List<Artist> = this.fold(
|
private fun List<APIIndex>.foldIndexToArtistList(serverId: Int): List<Artist> = this.fold(
|
||||||
listOf(),
|
listOf()
|
||||||
{ acc, index ->
|
) { acc, index ->
|
||||||
acc + index.artists.map {
|
acc + index.artists.map {
|
||||||
it.toDomainEntity()
|
it.toDomainEntity(serverId)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
private fun List<APIIndex>.foldIndexToIndexList(musicFolderId: String?): List<Index> = this.fold(
|
private fun List<APIIndex>.foldIndexToIndexList(
|
||||||
listOf(),
|
musicFolderId: String?,
|
||||||
{ acc, index ->
|
serverId: Int
|
||||||
acc + index.artists.map {
|
): List<Index> = this.fold(
|
||||||
val ret = it.toIndexEntity()
|
listOf()
|
||||||
ret.musicFolderId = musicFolderId
|
) { acc, index ->
|
||||||
ret
|
acc + index.artists.map {
|
||||||
}
|
val ret = it.toIndexEntity(serverId)
|
||||||
|
ret.musicFolderId = musicFolderId
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* APIMusicDirectoryConverter.kt
|
* APIMusicDirectoryConverter.kt
|
||||||
* Copyright (C) 2009-2021 Ultrasonic developers
|
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||||
*
|
*
|
||||||
* Distributed under terms of the GNU GPLv3 license.
|
* 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())
|
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)
|
populateCommonProps(this, this@toTrackEntity)
|
||||||
populateTrackProps(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)
|
populateCommonProps(this, this@toAlbumEntity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,24 +80,24 @@ private fun populateTrackProps(
|
||||||
track.averageRating = source.averageRating
|
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()
|
val newList: MutableList<MusicDirectory.Child> = mutableListOf()
|
||||||
|
|
||||||
forEach {
|
forEach {
|
||||||
if (it.isDir)
|
if (it.isDir)
|
||||||
newList.add(it.toAlbumEntity())
|
newList.add(it.toAlbumEntity(serverId))
|
||||||
else
|
else
|
||||||
newList.add(it.toTrackEntity())
|
newList.add(it.toTrackEntity(serverId))
|
||||||
}
|
}
|
||||||
|
|
||||||
return newList
|
return newList
|
||||||
}
|
}
|
||||||
|
|
||||||
fun List<MusicDirectoryChild>.toTrackList(): List<Track> = this.map {
|
fun List<MusicDirectoryChild>.toTrackList(serverId: Int): List<Track> = this.map {
|
||||||
it.toTrackEntity()
|
it.toTrackEntity(serverId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun APIMusicDirectory.toDomainEntity(): MusicDirectory = MusicDirectory().apply {
|
fun APIMusicDirectory.toDomainEntity(serverId: Int): MusicDirectory = MusicDirectory().apply {
|
||||||
name = this@toDomainEntity.name
|
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]
|
// Converts MusicFolder entity from [org.moire.ultrasonic.api.subsonic.SubsonicAPIClient]
|
||||||
// to app domain entities.
|
// to app domain entities.
|
||||||
@file:JvmName("APIMusicFolderConverter")
|
@file:JvmName("APIMusicFolderConverter")
|
||||||
|
@ -5,7 +12,15 @@ package org.moire.ultrasonic.domain
|
||||||
|
|
||||||
import org.moire.ultrasonic.api.subsonic.models.MusicFolder as APIMusicFolder
|
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> =
|
fun List<APIMusicFolder>.toDomainEntityList(serverId: Int): List<MusicFolder> =
|
||||||
this.map { it.toDomainEntity() }
|
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]
|
// Converts Playlist entity from [org.moire.ultrasonic.api.subsonic.SubsonicAPIClient]
|
||||||
// to app domain entities.
|
// to app domain entities.
|
||||||
@file:JvmName("APIPlaylistConverter")
|
@file:JvmName("APIPlaylistConverter")
|
||||||
|
|
||||||
package org.moire.ultrasonic.domain
|
package org.moire.ultrasonic.domain
|
||||||
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
@ -10,10 +18,17 @@ import org.moire.ultrasonic.util.Util.ifNotNull
|
||||||
|
|
||||||
internal val playlistDateFormat by lazy(NONE) { SimpleDateFormat.getInstance() }
|
internal val playlistDateFormat by lazy(NONE) { SimpleDateFormat.getInstance() }
|
||||||
|
|
||||||
fun APIPlaylist.toMusicDirectoryDomainEntity(): MusicDirectory = MusicDirectory().apply {
|
fun APIPlaylist.toMusicDirectoryDomainEntity(serverId: Int): MusicDirectory =
|
||||||
name = this@toMusicDirectoryDomainEntity.name
|
MusicDirectory().apply {
|
||||||
addAll(this@toMusicDirectoryDomainEntity.entriesList.map { it.toTrackEntity() })
|
name = this@toMusicDirectoryDomainEntity.name
|
||||||
}
|
addAll(
|
||||||
|
this@toMusicDirectoryDomainEntity.entriesList.map {
|
||||||
|
val item = it.toTrackEntity(serverId)
|
||||||
|
item.serverId = serverId
|
||||||
|
item
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun APIPlaylist.toDomainEntity(): Playlist = Playlist(
|
fun APIPlaylist.toDomainEntity(): Playlist = Playlist(
|
||||||
this.id, this.name, this.owner,
|
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]
|
// Converts SearchResult entities from [org.moire.ultrasonic.api.subsonic.SubsonicAPIClient]
|
||||||
// to app domain entities.
|
// to app domain entities.
|
||||||
@file:JvmName("APISearchConverter")
|
@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.SearchThreeResult
|
||||||
import org.moire.ultrasonic.api.subsonic.models.SearchTwoResult
|
import org.moire.ultrasonic.api.subsonic.models.SearchTwoResult
|
||||||
|
|
||||||
fun APISearchResult.toDomainEntity(): SearchResult = SearchResult(
|
fun APISearchResult.toDomainEntity(serverId: Int): SearchResult = SearchResult(
|
||||||
emptyList(), emptyList(),
|
emptyList(), emptyList(),
|
||||||
this.matchList.map { it.toTrackEntity() }
|
this.matchList.map { it.toTrackEntity(serverId) }
|
||||||
)
|
)
|
||||||
|
|
||||||
fun SearchTwoResult.toDomainEntity(): SearchResult = SearchResult(
|
fun SearchTwoResult.toDomainEntity(serverId: Int): SearchResult = SearchResult(
|
||||||
this.artistList.map { it.toIndexEntity() },
|
this.artistList.map { it.toIndexEntity(serverId) },
|
||||||
this.albumList.map { it.toDomainEntity() },
|
this.albumList.map { it.toDomainEntity(serverId) },
|
||||||
this.songList.map { it.toTrackEntity() }
|
this.songList.map { it.toTrackEntity(serverId) }
|
||||||
)
|
)
|
||||||
|
|
||||||
fun SearchThreeResult.toDomainEntity(): SearchResult = SearchResult(
|
fun SearchThreeResult.toDomainEntity(serverId: Int): SearchResult = SearchResult(
|
||||||
this.artistList.map { it.toDomainEntity() },
|
this.artistList.map { it.toDomainEntity(serverId) },
|
||||||
this.albumList.map { it.toDomainEntity() },
|
this.albumList.map { it.toDomainEntity(serverId) },
|
||||||
this.songList.map { it.toTrackEntity() }
|
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
|
// Contains helper method to convert subsonic api share to domain model
|
||||||
@file:JvmName("APIShareConverter")
|
@file:JvmName("APIShareConverter")
|
||||||
package org.moire.ultrasonic.domain
|
package org.moire.ultrasonic.domain
|
||||||
|
@ -9,11 +16,11 @@ import org.moire.ultrasonic.util.Util.ifNotNull
|
||||||
|
|
||||||
internal val shareTimeFormat by lazy(NONE) { SimpleDateFormat.getInstance() }
|
internal val shareTimeFormat by lazy(NONE) { SimpleDateFormat.getInstance() }
|
||||||
|
|
||||||
fun List<APIShare>.toDomainEntitiesList(): List<Share> = this.map {
|
fun List<APIShare>.toDomainEntitiesList(serverId: Int): List<Share> = this.map {
|
||||||
it.toDomainEntity()
|
it.toDomainEntity(serverId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun APIShare.toDomainEntity(): Share = Share(
|
fun APIShare.toDomainEntity(serverId: Int): Share = Share(
|
||||||
created = this@toDomainEntity.created.ifNotNull { shareTimeFormat.format(it.time) },
|
created = this@toDomainEntity.created.ifNotNull { shareTimeFormat.format(it.time) },
|
||||||
description = this@toDomainEntity.description,
|
description = this@toDomainEntity.description,
|
||||||
expires = this@toDomainEntity.expires.ifNotNull { shareTimeFormat.format(it.time) },
|
expires = this@toDomainEntity.expires.ifNotNull { shareTimeFormat.format(it.time) },
|
||||||
|
@ -22,5 +29,5 @@ fun APIShare.toDomainEntity(): Share = Share(
|
||||||
url = this@toDomainEntity.url,
|
url = this@toDomainEntity.url,
|
||||||
username = this@toDomainEntity.username,
|
username = this@toDomainEntity.username,
|
||||||
visitCount = this@toDomainEntity.visitCount.toLong(),
|
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() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
var shouldRestart = false
|
var shouldRelayout = false
|
||||||
val currentId3Setting = Settings.shouldUseId3Tags
|
val currentId3Setting = Settings.shouldUseId3Tags
|
||||||
|
|
||||||
// If setting has changed...
|
// If setting has changed...
|
||||||
if (currentId3Setting != cachedId3Setting) {
|
if (currentId3Setting != useId3) {
|
||||||
cachedId3Setting = currentId3Setting
|
useId3 = currentId3Setting
|
||||||
shouldRestart = true
|
shouldRelayout = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// then setup the list anew.
|
// then setup the list anew.
|
||||||
if (shouldRestart) {
|
if (shouldRelayout) {
|
||||||
setupItemVisibility()
|
setupItemVisibility()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,17 +109,19 @@ class MainFragment : Fragment(), KoinComponent {
|
||||||
|
|
||||||
private fun setupItemVisibility() {
|
private fun setupItemVisibility() {
|
||||||
// Cache some values
|
// Cache some values
|
||||||
cachedId3Setting = Settings.shouldUseId3Tags
|
useId3 = Settings.shouldUseId3Tags
|
||||||
|
useId3Offline = Settings.useId3TagsOffline
|
||||||
|
|
||||||
val isOnline = !isOffline()
|
val isOnline = !isOffline()
|
||||||
|
|
||||||
// Music
|
// Music
|
||||||
musicTitle.isVisible = true
|
musicTitle.isVisible = true
|
||||||
artistsButton.isVisible = true
|
artistsButton.isVisible = true
|
||||||
albumsButton.isVisible = isOnline
|
albumsButton.isVisible = isOnline || useId3Offline
|
||||||
genresButton.isVisible = true
|
genresButton.isVisible = true
|
||||||
|
|
||||||
// Songs
|
// Songs
|
||||||
songsTitle.isVisible = isOnline
|
songsTitle.isVisible = true
|
||||||
randomSongsButton.isVisible = true
|
randomSongsButton.isVisible = true
|
||||||
songsStarredButton.isVisible = isOnline
|
songsStarredButton.isVisible = isOnline
|
||||||
|
|
||||||
|
@ -128,7 +130,7 @@ class MainFragment : Fragment(), KoinComponent {
|
||||||
albumsNewestButton.isVisible = isOnline
|
albumsNewestButton.isVisible = isOnline
|
||||||
albumsRecentButton.isVisible = isOnline
|
albumsRecentButton.isVisible = isOnline
|
||||||
albumsFrequentButton.isVisible = isOnline
|
albumsFrequentButton.isVisible = isOnline
|
||||||
albumsHighestButton.isVisible = isOnline && !cachedId3Setting
|
albumsHighestButton.isVisible = isOnline && !useId3
|
||||||
albumsRandomButton.isVisible = isOnline
|
albumsRandomButton.isVisible = isOnline
|
||||||
albumsStarredButton.isVisible = isOnline
|
albumsStarredButton.isVisible = isOnline
|
||||||
albumsAlphaByNameButton.isVisible = isOnline
|
albumsAlphaByNameButton.isVisible = isOnline
|
||||||
|
@ -240,6 +242,7 @@ class MainFragment : Fragment(), KoinComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
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.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.Typeface
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
import android.provider.SearchRecentSuggestions
|
import android.provider.SearchRecentSuggestions
|
||||||
|
import android.text.SpannableString
|
||||||
|
import android.text.style.ForegroundColorSpan
|
||||||
|
import android.text.style.StyleSpan
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
@ -76,6 +81,7 @@ class SettingsFragment :
|
||||||
private var directoryCacheTime: ListPreference? = null
|
private var directoryCacheTime: ListPreference? = null
|
||||||
private var mediaButtonsEnabled: CheckBoxPreference? = null
|
private var mediaButtonsEnabled: CheckBoxPreference? = null
|
||||||
private var showArtistPicture: CheckBoxPreference? = null
|
private var showArtistPicture: CheckBoxPreference? = null
|
||||||
|
private var useId3TagsOffline: CheckBoxPreference? = null
|
||||||
private var sharingDefaultDescription: EditTextPreference? = null
|
private var sharingDefaultDescription: EditTextPreference? = null
|
||||||
private var sharingDefaultGreeting: EditTextPreference? = null
|
private var sharingDefaultGreeting: EditTextPreference? = null
|
||||||
private var sharingDefaultExpiration: TimeSpanPreference? = null
|
private var sharingDefaultExpiration: TimeSpanPreference? = null
|
||||||
|
@ -121,14 +127,39 @@ class SettingsFragment :
|
||||||
pauseOnBluetoothDevice = findPreference(Constants.PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE)
|
pauseOnBluetoothDevice = findPreference(Constants.PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE)
|
||||||
debugLogToFile = findPreference(Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE)
|
debugLogToFile = findPreference(Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE)
|
||||||
showArtistPicture = findPreference(Constants.PREFERENCES_KEY_SHOW_ARTIST_PICTURE)
|
showArtistPicture = findPreference(Constants.PREFERENCES_KEY_SHOW_ARTIST_PICTURE)
|
||||||
|
useId3TagsOffline = findPreference(Constants.PREFERENCES_KEY_ID3_TAGS_OFFLINE)
|
||||||
customCacheLocation = findPreference(Constants.PREFERENCES_KEY_CUSTOM_CACHE_LOCATION)
|
customCacheLocation = findPreference(Constants.PREFERENCES_KEY_CUSTOM_CACHE_LOCATION)
|
||||||
|
|
||||||
sharingDefaultGreeting?.text = shareGreeting
|
sharingDefaultGreeting?.text = shareGreeting
|
||||||
|
|
||||||
|
setupTextColors()
|
||||||
setupClearSearchPreference()
|
setupClearSearchPreference()
|
||||||
setupCacheLocationPreference()
|
setupCacheLocationPreference()
|
||||||
setupBluetoothDevicePreferences()
|
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?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
update()
|
update()
|
||||||
|
@ -196,7 +227,10 @@ class SettingsFragment :
|
||||||
setDebugLogToFile(sharedPreferences.getBoolean(key, false))
|
setDebugLogToFile(sharedPreferences.getBoolean(key, false))
|
||||||
}
|
}
|
||||||
Constants.PREFERENCES_KEY_ID3_TAGS -> {
|
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 -> {
|
Constants.PREFERENCES_KEY_THEME -> {
|
||||||
RxBus.themeChangedEventPublisher.onNext(Unit)
|
RxBus.themeChangedEventPublisher.onNext(Unit)
|
||||||
|
@ -372,6 +406,7 @@ class SettingsFragment :
|
||||||
debugLogToFile?.summary = ""
|
debugLogToFile?.summary = ""
|
||||||
}
|
}
|
||||||
showArtistPicture?.isEnabled = shouldUseId3Tags
|
showArtistPicture?.isEnabled = shouldUseId3Tags
|
||||||
|
useId3TagsOffline?.isEnabled = shouldUseId3Tags
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setHideMedia(hide: Boolean) {
|
private fun setHideMedia(hide: Boolean) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* TrackCollectionFragment.kt
|
* TrackCollectionFragment.kt
|
||||||
* Copyright (C) 2009-2021 Ultrasonic developers
|
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||||
*
|
*
|
||||||
* Distributed under terms of the GNU GPLv3 license.
|
* 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.AlbumRowBinder
|
||||||
import org.moire.ultrasonic.adapters.HeaderViewBinder
|
import org.moire.ultrasonic.adapters.HeaderViewBinder
|
||||||
import org.moire.ultrasonic.adapters.TrackViewBinder
|
import org.moire.ultrasonic.adapters.TrackViewBinder
|
||||||
|
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||||
import org.moire.ultrasonic.domain.Identifiable
|
import org.moire.ultrasonic.domain.Identifiable
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory
|
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.EntryByDiscAndTrackComparator
|
||||||
import org.moire.ultrasonic.util.Settings
|
import org.moire.ultrasonic.util.Settings
|
||||||
import org.moire.ultrasonic.util.Util
|
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.
|
* 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>() {
|
open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
|
|
||||||
private var albumButtons: View? = null
|
private var albumButtons: View? = null
|
||||||
internal var selectButton: ImageView? = null
|
private var selectButton: ImageView? = null
|
||||||
internal var playNowButton: ImageView? = null
|
internal var playNowButton: ImageView? = null
|
||||||
private var playNextButton: ImageView? = null
|
private var playNextButton: ImageView? = null
|
||||||
private var playLastButton: ImageView? = null
|
private var playLastButton: ImageView? = null
|
||||||
internal var pinButton: ImageView? = null
|
private var pinButton: ImageView? = null
|
||||||
private var unpinButton: ImageView? = null
|
private var unpinButton: ImageView? = null
|
||||||
private var downloadButton: ImageView? = null
|
private var downloadButton: ImageView? = null
|
||||||
private var deleteButton: 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
|
// Update the buttons when the selection has changed
|
||||||
viewAdapter.selectionRevision.observe(
|
viewAdapter.selectionRevision.observe(
|
||||||
viewLifecycleOwner,
|
viewLifecycleOwner
|
||||||
{
|
) {
|
||||||
enableButtons()
|
enableButtons()
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal open fun setupButtons(view: View) {
|
internal open fun setupButtons(view: View) {
|
||||||
|
@ -267,10 +268,10 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
private val childCount: Int
|
private val childCount: Int
|
||||||
get() {
|
get() {
|
||||||
val count = viewAdapter.getCurrentList().count()
|
val count = viewAdapter.getCurrentList().count()
|
||||||
if (listModel.showHeader) {
|
return if (listModel.showHeader) {
|
||||||
return count - 1
|
count - 1
|
||||||
} else {
|
} else {
|
||||||
return count
|
count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,13 +321,13 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
} as List<Track>
|
} as List<Track>
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun selectAllOrNone() {
|
private fun selectAllOrNone() {
|
||||||
val someUnselected = viewAdapter.selectedSet.size < childCount
|
val someUnselected = viewAdapter.selectedSet.size < childCount
|
||||||
|
|
||||||
selectAll(someUnselected, true)
|
selectAll(someUnselected, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun selectAll(selected: Boolean, toast: Boolean) {
|
private fun selectAll(selected: Boolean, toast: Boolean) {
|
||||||
var selectedCount = viewAdapter.selectedSet.size * -1
|
var selectedCount = viewAdapter.selectedSet.size * -1
|
||||||
|
|
||||||
selectedCount += viewAdapter.setSelectionStatusOfAll(selected)
|
selectedCount += viewAdapter.setSelectionStatusOfAll(selected)
|
||||||
|
@ -366,7 +367,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
deleteButton?.isVisible = (enabled && deleteEnabled)
|
deleteButton?.isVisible = (enabled && deleteEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun downloadBackground(save: Boolean) {
|
private fun downloadBackground(save: Boolean) {
|
||||||
var songs = getSelectedSongs()
|
var songs = getSelectedSongs()
|
||||||
|
|
||||||
if (songs.isEmpty()) {
|
if (songs.isEmpty()) {
|
||||||
|
@ -426,6 +427,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
|
|
||||||
override val defaultObserver: (List<MusicDirectory.Child>) -> Unit = {
|
override val defaultObserver: (List<MusicDirectory.Child>) -> Unit = {
|
||||||
|
|
||||||
|
Timber.i("Received list")
|
||||||
val entryList: MutableList<MusicDirectory.Child> = it.toMutableList()
|
val entryList: MutableList<MusicDirectory.Child> = it.toMutableList()
|
||||||
|
|
||||||
if (listModel.currentListIsSortable && Settings.shouldSortByDisc) {
|
if (listModel.currentListIsSortable && Settings.shouldSortByDisc) {
|
||||||
|
@ -454,9 +456,9 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
moreButton!!.visibility = View.GONE
|
moreButton!!.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
moreButton!!.visibility = View.VISIBLE
|
moreButton!!.visibility = View.VISIBLE
|
||||||
if (arguments?.getInt(Constants.INTENT_RANDOM, 0) ?: 0 > 0) {
|
if ((arguments?.getInt(Constants.INTENT_RANDOM, 0) ?: 0) > 0) {
|
||||||
moreRandomTracks()
|
moreRandomTracks()
|
||||||
} else if (arguments?.getString(Constants.INTENT_GENRE_NAME, "") ?: "" != "") {
|
} else if ((arguments?.getString(Constants.INTENT_GENRE_NAME, "") ?: "") != "") {
|
||||||
moreSongsForGenre()
|
moreSongsForGenre()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,6 +499,8 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
listModel.currentListIsSortable = true
|
listModel.currentListIsSortable = true
|
||||||
|
|
||||||
|
Timber.i("Processed list")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun moreSongsForGenre(args: Bundle = requireArguments()) {
|
private fun moreSongsForGenre(args: Bundle = requireArguments()) {
|
||||||
|
@ -556,6 +560,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
args: Bundle?,
|
args: Bundle?,
|
||||||
refresh: Boolean
|
refresh: Boolean
|
||||||
): LiveData<List<MusicDirectory.Child>> {
|
): LiveData<List<MusicDirectory.Child>> {
|
||||||
|
Timber.i("Starting gathering track collection data...")
|
||||||
if (args == null) return listModel.currentList
|
if (args == null) return listModel.currentList
|
||||||
val id = args.getString(Constants.INTENT_ID)
|
val id = args.getString(Constants.INTENT_ID)
|
||||||
val isAlbum = args.getBoolean(Constants.INTENT_IS_ALBUM, false)
|
val isAlbum = args.getBoolean(Constants.INTENT_IS_ALBUM, false)
|
||||||
|
@ -600,7 +605,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
listModel.getRandom(albumListSize)
|
listModel.getRandom(albumListSize)
|
||||||
} else {
|
} else {
|
||||||
setTitle(name)
|
setTitle(name)
|
||||||
if (!isOffline() && Settings.shouldUseId3Tags) {
|
if (ActiveServerProvider.isID3Enabled()) {
|
||||||
if (isAlbum) {
|
if (isAlbum) {
|
||||||
listModel.getAlbum(refresh2, id!!, name)
|
listModel.getAlbum(refresh2, id!!, name)
|
||||||
} else {
|
} else {
|
||||||
|
@ -669,7 +674,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getClickedSong(item: MusicDirectory.Child): List<Track> {
|
private fun getClickedSong(item: MusicDirectory.Child): List<Track> {
|
||||||
// This can probably be done better
|
// This can probably be done better
|
||||||
return viewAdapter.getCurrentList().mapNotNull {
|
return viewAdapter.getCurrentList().mapNotNull {
|
||||||
if (it is Track && (it.id == item.id))
|
if (it is Track && (it.id == item.id))
|
||||||
|
|
|
@ -39,7 +39,7 @@ class AlbumListModel(application: Application) : GenericListModel(application) {
|
||||||
id: String,
|
id: String,
|
||||||
name: String?
|
name: String?
|
||||||
) {
|
) {
|
||||||
list.postValue(musicService.getArtist(id, name, refresh))
|
list.postValue(musicService.getAlbumsOfArtist(id, name, refresh))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun load(
|
override fun load(
|
||||||
|
|
|
@ -1,20 +1,8 @@
|
||||||
/*
|
/*
|
||||||
This file is part of Subsonic.
|
* ArtistListModel.kt
|
||||||
|
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||||
Subsonic is free software: you can redistribute it and/or modify
|
*
|
||||||
it under the terms of the GNU General Public License as published by
|
* Distributed under terms of the GNU GPLv3 license.
|
||||||
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
|
|
||||||
*/
|
*/
|
||||||
package org.moire.ultrasonic.model
|
package org.moire.ultrasonic.model
|
||||||
|
|
||||||
|
@ -24,6 +12,7 @@ import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import java.text.Collator
|
import java.text.Collator
|
||||||
|
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||||
import org.moire.ultrasonic.domain.ArtistOrIndex
|
import org.moire.ultrasonic.domain.ArtistOrIndex
|
||||||
import org.moire.ultrasonic.service.MusicService
|
import org.moire.ultrasonic.service.MusicService
|
||||||
|
|
||||||
|
@ -56,12 +45,10 @@ class ArtistListModel(application: Application) : GenericListModel(application)
|
||||||
|
|
||||||
val musicFolderId = activeServer.musicFolderId
|
val musicFolderId = activeServer.musicFolderId
|
||||||
|
|
||||||
val result: List<ArtistOrIndex>
|
val result = if (ActiveServerProvider.isID3Enabled()) {
|
||||||
|
musicService.getArtists(refresh)
|
||||||
if (!isOffline && useId3Tags) {
|
|
||||||
result = musicService.getArtists(refresh)
|
|
||||||
} else {
|
} else {
|
||||||
result = musicService.getIndexes(musicFolderId, refresh)
|
musicService.getIndexes(musicFolderId, refresh)
|
||||||
}
|
}
|
||||||
|
|
||||||
artists.postValue(result.toMutableList().sortedWith(comparator))
|
artists.postValue(result.toMutableList().sortedWith(comparator))
|
||||||
|
|
|
@ -635,7 +635,7 @@ class AutoMediaBrowserCallback(var player: Player, val libraryService: MediaLibr
|
||||||
|
|
||||||
return serviceScope.future {
|
return serviceScope.future {
|
||||||
val albums = if (!isOffline && useId3Tags) {
|
val albums = if (!isOffline && useId3Tags) {
|
||||||
callWithErrorHandling { musicService.getArtist(id, name, false) }
|
callWithErrorHandling { musicService.getAlbumsOfArtist(id, name, false) }
|
||||||
} else {
|
} else {
|
||||||
callWithErrorHandling {
|
callWithErrorHandling {
|
||||||
musicService.getMusicDirectory(id, name, false).getAlbums()
|
musicService.getMusicDirectory(id, name, false).getAlbums()
|
||||||
|
|
|
@ -43,7 +43,6 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||||
|
|
||||||
// Old style TimeLimitedCache
|
// Old style TimeLimitedCache
|
||||||
private val cachedMusicDirectories: LRUCache<String, TimeLimitedCache<MusicDirectory?>>
|
private val cachedMusicDirectories: LRUCache<String, TimeLimitedCache<MusicDirectory?>>
|
||||||
private val cachedArtist: LRUCache<String, TimeLimitedCache<List<Album>>>
|
|
||||||
private val cachedAlbum: LRUCache<String, TimeLimitedCache<MusicDirectory?>>
|
private val cachedAlbum: LRUCache<String, TimeLimitedCache<MusicDirectory?>>
|
||||||
private val cachedUserInfo: LRUCache<String, TimeLimitedCache<UserInfo?>>
|
private val cachedUserInfo: LRUCache<String, TimeLimitedCache<UserInfo?>>
|
||||||
private val cachedLicenseValid = TimeLimitedCache<Boolean>(120, TimeUnit.SECONDS)
|
private val cachedLicenseValid = TimeLimitedCache<Boolean>(120, TimeUnit.SECONDS)
|
||||||
|
@ -53,7 +52,8 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||||
private val cachedGenres = TimeLimitedCache<List<Genre>>(10 * 3600, TimeUnit.SECONDS)
|
private val cachedGenres = TimeLimitedCache<List<Genre>>(10 * 3600, TimeUnit.SECONDS)
|
||||||
|
|
||||||
// New Room Database
|
// New Room Database
|
||||||
private var cachedArtists = metaDatabase.artistsDao()
|
private var cachedArtists = metaDatabase.artistDao()
|
||||||
|
private var cachedAlbums = metaDatabase.albumDao()
|
||||||
private var cachedIndexes = metaDatabase.indexDao()
|
private var cachedIndexes = metaDatabase.indexDao()
|
||||||
private val cachedMusicFolders = metaDatabase.musicFoldersDao()
|
private val cachedMusicFolders = metaDatabase.musicFoldersDao()
|
||||||
|
|
||||||
|
@ -103,10 +103,10 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||||
|
|
||||||
var indexes: List<Index>
|
var indexes: List<Index>
|
||||||
|
|
||||||
if (musicFolderId == null) {
|
indexes = if (musicFolderId == null) {
|
||||||
indexes = cachedIndexes.get()
|
cachedIndexes.get()
|
||||||
} else {
|
} else {
|
||||||
indexes = cachedIndexes.get(musicFolderId)
|
cachedIndexes.get(musicFolderId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (indexes.isEmpty()) {
|
if (indexes.isEmpty()) {
|
||||||
|
@ -120,14 +120,15 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
override fun getArtists(refresh: Boolean): List<Artist> {
|
override fun getArtists(refresh: Boolean): List<Artist> {
|
||||||
checkSettingsChanged()
|
checkSettingsChanged()
|
||||||
|
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
cachedArtists.clear()
|
cachedArtists.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = cachedArtists.get()
|
var result = cachedArtists.get()
|
||||||
|
|
||||||
if (result.isEmpty()) {
|
if (result.isEmpty()) {
|
||||||
result = musicService.getArtists(refresh)
|
result = musicService.getArtists(refresh)
|
||||||
cachedArtist.clear()
|
|
||||||
cachedArtists.set(result)
|
cachedArtists.set(result)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
@ -149,21 +150,29 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||||
return dir
|
return dir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Retrieves all albums of the provided artist.
|
||||||
|
* Cached in the RoomDB
|
||||||
|
*/
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
override fun getArtist(id: String, name: String?, refresh: Boolean):
|
override fun getAlbumsOfArtist(id: String, name: String?, refresh: Boolean):
|
||||||
List<Album> {
|
List<Album> {
|
||||||
checkSettingsChanged()
|
checkSettingsChanged()
|
||||||
var cache = if (refresh) null else cachedArtist[id]
|
|
||||||
var dir = cache?.get()
|
var result: List<Album>
|
||||||
if (dir == null) {
|
|
||||||
dir = musicService.getArtist(id, name, refresh)
|
result = if (refresh) {
|
||||||
cache = TimeLimitedCache(
|
cachedAlbums.clearByArtist(id)
|
||||||
Settings.directoryCacheTime.toLong(), TimeUnit.SECONDS
|
listOf()
|
||||||
)
|
} else {
|
||||||
cache.set(dir)
|
cachedAlbums.byArtist(id)
|
||||||
cachedArtist.put(id, cache)
|
|
||||||
}
|
}
|
||||||
return dir
|
|
||||||
|
if (result.isEmpty()) {
|
||||||
|
result = musicService.getAlbumsOfArtist(id, name, refresh)
|
||||||
|
cachedAlbums.upsert(result)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -326,7 +335,8 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||||
if (!Util.equals(newUrl, restUrl) || !Util.equals(cachedMusicFolderId, newFolderId)) {
|
if (!Util.equals(newUrl, restUrl) || !Util.equals(cachedMusicFolderId, newFolderId)) {
|
||||||
// Switch database
|
// Switch database
|
||||||
metaDatabase = activeServerProvider.getActiveMetaDatabase()
|
metaDatabase = activeServerProvider.getActiveMetaDatabase()
|
||||||
cachedArtists = metaDatabase.artistsDao()
|
cachedArtists = metaDatabase.artistDao()
|
||||||
|
cachedAlbums = metaDatabase.albumDao()
|
||||||
cachedIndexes = metaDatabase.indexDao()
|
cachedIndexes = metaDatabase.indexDao()
|
||||||
|
|
||||||
// Clear in memory caches
|
// Clear in memory caches
|
||||||
|
@ -335,7 +345,6 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||||
cachedPlaylists.clear()
|
cachedPlaylists.clear()
|
||||||
cachedGenres.clear()
|
cachedGenres.clear()
|
||||||
cachedAlbum.clear()
|
cachedAlbum.clear()
|
||||||
cachedArtist.clear()
|
|
||||||
cachedUserInfo.clear()
|
cachedUserInfo.clear()
|
||||||
|
|
||||||
// Set the cache keys
|
// Set the cache keys
|
||||||
|
@ -472,7 +481,6 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||||
|
|
||||||
init {
|
init {
|
||||||
cachedMusicDirectories = LRUCache(MUSIC_DIR_CACHE_SIZE)
|
cachedMusicDirectories = LRUCache(MUSIC_DIR_CACHE_SIZE)
|
||||||
cachedArtist = LRUCache(MUSIC_DIR_CACHE_SIZE)
|
|
||||||
cachedAlbum = LRUCache(MUSIC_DIR_CACHE_SIZE)
|
cachedAlbum = LRUCache(MUSIC_DIR_CACHE_SIZE)
|
||||||
cachedUserInfo = LRUCache(MUSIC_DIR_CACHE_SIZE)
|
cachedUserInfo = LRUCache(MUSIC_DIR_CACHE_SIZE)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import java.util.PriorityQueue
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||||
|
import org.moire.ultrasonic.data.MetaDatabase
|
||||||
import org.moire.ultrasonic.domain.Artist
|
import org.moire.ultrasonic.domain.Artist
|
||||||
import org.moire.ultrasonic.domain.Track
|
import org.moire.ultrasonic.domain.Track
|
||||||
import org.moire.ultrasonic.playback.LegacyPlaylistManager
|
import org.moire.ultrasonic.playback.LegacyPlaylistManager
|
||||||
|
@ -402,6 +403,14 @@ class Downloader(
|
||||||
downloadFile.completeFile
|
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)
|
downloadFile.status.postValue(newStatus)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -457,8 +466,10 @@ class Downloader(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (downloadFile.track.artistId != null) {
|
try {
|
||||||
cacheMetadata(downloadFile.track.artistId!!)
|
downloadFile.track.cacheMetadata()
|
||||||
|
} catch (ignore: Exception) {
|
||||||
|
Timber.w(ignore)
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadAndSaveCoverArt()
|
downloadAndSaveCoverArt()
|
||||||
|
@ -510,13 +521,35 @@ class Downloader(
|
||||||
return String.format(Locale.ROOT, "DownloadTask (%s)", downloadFile.track)
|
return String.format(Locale.ROOT, "DownloadTask (%s)", downloadFile.track)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cacheMetadata(artistId: String) {
|
private fun Track.cacheMetadata() {
|
||||||
// TODO: Right now it's caching the track artist.
|
if (artistId.isNullOrEmpty()) return
|
||||||
// Once the albums are cached in db, we should retrieve the album,
|
|
||||||
// and then cache the album artist.
|
val onlineDB = activeServerProvider.getActiveMetaDatabase()
|
||||||
if (artistId.isEmpty()) return
|
val offlineDB = activeServerProvider.offlineMetaDatabase
|
||||||
var artist: Artist? =
|
|
||||||
activeServerProvider.getActiveMetaDatabase().artistsDao().get(artistId)
|
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
|
// 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.
|
// 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) {
|
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
|
fun getMusicDirectory(id: String, name: String?, refresh: Boolean): MusicDirectory
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun getArtist(id: String, name: String?, refresh: Boolean): List<Album>
|
fun getAlbumsOfArtist(id: String, name: String?, refresh: Boolean): List<Album>
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory
|
fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* OfflineMusicService.kt
|
* OfflineMusicService.kt
|
||||||
* Copyright (C) 2009-2021 Ultrasonic developers
|
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||||
*
|
*
|
||||||
* Distributed under terms of the GNU GPLv3 license.
|
* 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.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||||
|
import org.moire.ultrasonic.data.MetaDatabase
|
||||||
import org.moire.ultrasonic.domain.Album
|
import org.moire.ultrasonic.domain.Album
|
||||||
import org.moire.ultrasonic.domain.Artist
|
import org.moire.ultrasonic.domain.Artist
|
||||||
import org.moire.ultrasonic.domain.ArtistOrIndex
|
import org.moire.ultrasonic.domain.ArtistOrIndex
|
||||||
|
@ -43,6 +44,7 @@ import org.moire.ultrasonic.domain.Track
|
||||||
import org.moire.ultrasonic.domain.UserInfo
|
import org.moire.ultrasonic.domain.UserInfo
|
||||||
import org.moire.ultrasonic.util.AbstractFile
|
import org.moire.ultrasonic.util.AbstractFile
|
||||||
import org.moire.ultrasonic.util.Constants
|
import org.moire.ultrasonic.util.Constants
|
||||||
|
import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator
|
||||||
import org.moire.ultrasonic.util.FileUtil
|
import org.moire.ultrasonic.util.FileUtil
|
||||||
import org.moire.ultrasonic.util.Storage
|
import org.moire.ultrasonic.util.Storage
|
||||||
import org.moire.ultrasonic.util.Util.safeClose
|
import org.moire.ultrasonic.util.Util.safeClose
|
||||||
|
@ -52,12 +54,19 @@ import timber.log.Timber
|
||||||
class OfflineMusicService : MusicService, KoinComponent {
|
class OfflineMusicService : MusicService, KoinComponent {
|
||||||
private val activeServerProvider: ActiveServerProvider by inject()
|
private val activeServerProvider: ActiveServerProvider by inject()
|
||||||
|
|
||||||
|
private var metaDatabase: MetaDatabase = activeServerProvider.getActiveMetaDatabase()
|
||||||
|
|
||||||
|
// New Room Database
|
||||||
|
private var cachedArtists = metaDatabase.artistDao()
|
||||||
|
private var cachedAlbums = metaDatabase.albumDao()
|
||||||
|
private var cachedTracks = metaDatabase.trackDao()
|
||||||
|
|
||||||
override fun getIndexes(musicFolderId: String?, refresh: Boolean): List<Index> {
|
override fun getIndexes(musicFolderId: String?, refresh: Boolean): List<Index> {
|
||||||
val indexes: MutableList<Index> = ArrayList()
|
val indexes: MutableList<Index> = ArrayList()
|
||||||
val root = FileUtil.musicDirectory
|
val root = FileUtil.musicDirectory
|
||||||
for (file in FileUtil.listFiles(root)) {
|
for (file in FileUtil.listFiles(root)) {
|
||||||
if (file.isDirectory) {
|
if (file.isDirectory) {
|
||||||
val index = Index(file.path)
|
val index = Index(id = file.path)
|
||||||
index.id = file.path
|
index.id = file.path
|
||||||
index.index = file.name.substring(0, 1)
|
index.index = file.name.substring(0, 1)
|
||||||
index.name = file.name
|
index.name = file.name
|
||||||
|
@ -97,6 +106,13 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||||
return indexes
|
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!
|
* 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,
|
offset: Int,
|
||||||
musicFolderId: String?
|
musicFolderId: String?
|
||||||
): List<Album> {
|
): List<Album> {
|
||||||
throw OfflineException("getAlbumList2 isn't available in offline mode")
|
// TODO: Implement filtering by musicFolder?
|
||||||
|
return cachedAlbums.get(size, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -450,20 +467,39 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||||
|
|
||||||
override fun isLicenseValid(): Boolean = true
|
override fun isLicenseValid(): Boolean = true
|
||||||
|
|
||||||
@Throws(OfflineException::class)
|
@Throws(Exception::class)
|
||||||
override fun getArtists(refresh: Boolean): List<Artist> {
|
override fun getAlbumsOfArtist(id: String, name: String?, refresh: Boolean):
|
||||||
throw OfflineException("getArtists isn't available in offline mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(OfflineException::class)
|
|
||||||
override fun getArtist(id: String, name: String?, refresh: Boolean):
|
|
||||||
List<Album> {
|
List<Album> {
|
||||||
throw OfflineException("getArtist isn't available in offline mode")
|
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)
|
@Throws(OfflineException::class)
|
||||||
override fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory {
|
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)
|
@Throws(OfflineException::class)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* RestMusicService.kt
|
* RESTMusicService.kt
|
||||||
* Copyright (C) 2009-2021 Ultrasonic developers
|
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||||
*
|
*
|
||||||
* Distributed under terms of the GNU GPLv3 license.
|
* Distributed under terms of the GNU GPLv3 license.
|
||||||
*/
|
*/
|
||||||
|
@ -78,7 +78,7 @@ open class RESTMusicService(
|
||||||
): List<MusicFolder> {
|
): List<MusicFolder> {
|
||||||
val response = API.getMusicFolders().execute().throwOnFailure()
|
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> {
|
): List<Index> {
|
||||||
val response = API.getIndexes(musicFolderId, null).execute().throwOnFailure()
|
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)
|
@Throws(Exception::class)
|
||||||
|
@ -100,7 +103,7 @@ open class RESTMusicService(
|
||||||
): List<Artist> {
|
): List<Artist> {
|
||||||
val response = API.getArtists(null).execute().throwOnFailure()
|
val response = API.getArtists(null).execute().throwOnFailure()
|
||||||
|
|
||||||
return response.body()!!.indexes.toArtistList()
|
return response.body()!!.indexes.toArtistList(activeServerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -137,18 +140,18 @@ open class RESTMusicService(
|
||||||
): MusicDirectory {
|
): MusicDirectory {
|
||||||
val response = API.getMusicDirectory(id).execute().throwOnFailure()
|
val response = API.getMusicDirectory(id).execute().throwOnFailure()
|
||||||
|
|
||||||
return response.body()!!.musicDirectory.toDomainEntity()
|
return response.body()!!.musicDirectory.toDomainEntity(activeServerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
override fun getArtist(
|
override fun getAlbumsOfArtist(
|
||||||
id: String,
|
id: String,
|
||||||
name: String?,
|
name: String?,
|
||||||
refresh: Boolean
|
refresh: Boolean
|
||||||
): List<Album> {
|
): List<Album> {
|
||||||
val response = API.getArtist(id).execute().throwOnFailure()
|
val response = API.getArtist(id).execute().throwOnFailure()
|
||||||
|
|
||||||
return response.body()!!.artist.toDomainEntityList()
|
return response.body()!!.artist.toDomainEntityList(activeServerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -159,7 +162,7 @@ open class RESTMusicService(
|
||||||
): MusicDirectory {
|
): MusicDirectory {
|
||||||
val response = API.getAlbum(id).execute().throwOnFailure()
|
val response = API.getAlbum(id).execute().throwOnFailure()
|
||||||
|
|
||||||
return response.body()!!.album.toMusicDirectoryDomainEntity()
|
return response.body()!!.album.toMusicDirectoryDomainEntity(activeServerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -189,7 +192,7 @@ open class RESTMusicService(
|
||||||
API.search(null, null, null, criteria.query, criteria.songCount, null, null)
|
API.search(null, null, null, criteria.query, criteria.songCount, null, null)
|
||||||
.execute().throwOnFailure()
|
.execute().throwOnFailure()
|
||||||
|
|
||||||
return response.body()!!.searchResult.toDomainEntity()
|
return response.body()!!.searchResult.toDomainEntity(activeServerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -205,7 +208,7 @@ open class RESTMusicService(
|
||||||
criteria.songCount, null
|
criteria.songCount, null
|
||||||
).execute().throwOnFailure()
|
).execute().throwOnFailure()
|
||||||
|
|
||||||
return response.body()!!.searchResult.toDomainEntity()
|
return response.body()!!.searchResult.toDomainEntity(activeServerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -218,7 +221,7 @@ open class RESTMusicService(
|
||||||
criteria.songCount, null
|
criteria.songCount, null
|
||||||
).execute().throwOnFailure()
|
).execute().throwOnFailure()
|
||||||
|
|
||||||
return response.body()!!.searchResult.toDomainEntity()
|
return response.body()!!.searchResult.toDomainEntity(activeServerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -228,7 +231,7 @@ open class RESTMusicService(
|
||||||
): MusicDirectory {
|
): MusicDirectory {
|
||||||
val response = API.getPlaylist(id).execute().throwOnFailure()
|
val response = API.getPlaylist(id).execute().throwOnFailure()
|
||||||
|
|
||||||
val playlist = response.body()!!.playlist.toMusicDirectoryDomainEntity()
|
val playlist = response.body()!!.playlist.toMusicDirectoryDomainEntity(activeServerId)
|
||||||
savePlaylist(name, playlist)
|
savePlaylist(name, playlist)
|
||||||
|
|
||||||
return playlist
|
return playlist
|
||||||
|
@ -319,7 +322,7 @@ open class RESTMusicService(
|
||||||
"skipped" != podcastEntry.status &&
|
"skipped" != podcastEntry.status &&
|
||||||
"error" != podcastEntry.status
|
"error" != podcastEntry.status
|
||||||
) {
|
) {
|
||||||
val entry = podcastEntry.toTrackEntity()
|
val entry = podcastEntry.toTrackEntity(activeServerId)
|
||||||
entry.track = null
|
entry.track = null
|
||||||
musicDirectory.add(entry)
|
musicDirectory.add(entry)
|
||||||
}
|
}
|
||||||
|
@ -363,7 +366,7 @@ open class RESTMusicService(
|
||||||
musicFolderId
|
musicFolderId
|
||||||
).execute().throwOnFailure()
|
).execute().throwOnFailure()
|
||||||
|
|
||||||
return response.body()!!.albumList.toDomainEntityList()
|
return response.body()!!.albumList.toDomainEntityList(activeServerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -383,7 +386,7 @@ open class RESTMusicService(
|
||||||
musicFolderId
|
musicFolderId
|
||||||
).execute().throwOnFailure()
|
).execute().throwOnFailure()
|
||||||
|
|
||||||
return response.body()!!.albumList.toDomainEntityList()
|
return response.body()!!.albumList.toDomainEntityList(activeServerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -399,7 +402,7 @@ open class RESTMusicService(
|
||||||
).execute().throwOnFailure()
|
).execute().throwOnFailure()
|
||||||
|
|
||||||
val result = MusicDirectory()
|
val result = MusicDirectory()
|
||||||
result.addAll(response.body()!!.songsList.toDomainEntityList())
|
result.addAll(response.body()!!.songsList.toDomainEntityList(activeServerId))
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -408,14 +411,14 @@ open class RESTMusicService(
|
||||||
override fun getStarred(): SearchResult {
|
override fun getStarred(): SearchResult {
|
||||||
val response = API.getStarred(null).execute().throwOnFailure()
|
val response = API.getStarred(null).execute().throwOnFailure()
|
||||||
|
|
||||||
return response.body()!!.starred.toDomainEntity()
|
return response.body()!!.starred.toDomainEntity(activeServerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
override fun getStarred2(): SearchResult {
|
override fun getStarred2(): SearchResult {
|
||||||
val response = API.getStarred2(null).execute().throwOnFailure()
|
val response = API.getStarred2(null).execute().throwOnFailure()
|
||||||
|
|
||||||
return response.body()!!.starred2.toDomainEntity()
|
return response.body()!!.starred2.toDomainEntity(activeServerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -546,7 +549,7 @@ open class RESTMusicService(
|
||||||
): List<Share> {
|
): List<Share> {
|
||||||
val response = API.getShares().execute().throwOnFailure()
|
val response = API.getShares().execute().throwOnFailure()
|
||||||
|
|
||||||
return response.body()!!.shares.toDomainEntitiesList()
|
return response.body()!!.shares.toDomainEntitiesList(activeServerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -567,7 +570,7 @@ open class RESTMusicService(
|
||||||
val response = API.getSongsByGenre(genre, count, offset, null).execute().throwOnFailure()
|
val response = API.getSongsByGenre(genre, count, offset, null).execute().throwOnFailure()
|
||||||
|
|
||||||
val result = MusicDirectory()
|
val result = MusicDirectory()
|
||||||
result.addAll(response.body()!!.songsList.toDomainEntityList())
|
result.addAll(response.body()!!.songsList.toDomainEntityList(activeServerId))
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -601,7 +604,7 @@ open class RESTMusicService(
|
||||||
override fun getBookmarks(): List<Bookmark> {
|
override fun getBookmarks(): List<Bookmark> {
|
||||||
val response = API.getBookmarks().execute().throwOnFailure()
|
val response = API.getBookmarks().execute().throwOnFailure()
|
||||||
|
|
||||||
return response.body()!!.bookmarkList.toDomainEntitiesList()
|
return response.body()!!.bookmarkList.toDomainEntitiesList(activeServerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -626,7 +629,7 @@ open class RESTMusicService(
|
||||||
val response = API.getVideos().execute().throwOnFailure()
|
val response = API.getVideos().execute().throwOnFailure()
|
||||||
|
|
||||||
val musicDirectory = MusicDirectory()
|
val musicDirectory = MusicDirectory()
|
||||||
musicDirectory.addAll(response.body()!!.videosList.toDomainEntityList())
|
musicDirectory.addAll(response.body()!!.videosList.toDomainEntityList(activeServerId))
|
||||||
|
|
||||||
return musicDirectory
|
return musicDirectory
|
||||||
}
|
}
|
||||||
|
@ -639,7 +642,7 @@ open class RESTMusicService(
|
||||||
): List<Share> {
|
): List<Share> {
|
||||||
val response = API.createShare(ids, description, expires).execute().throwOnFailure()
|
val response = API.createShare(ids, description, expires).execute().throwOnFailure()
|
||||||
|
|
||||||
return response.body()!!.shares.toDomainEntitiesList()
|
return response.body()!!.shares.toDomainEntitiesList(activeServerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -663,6 +666,9 @@ open class RESTMusicService(
|
||||||
API.updateShare(id, description, expiresValue).execute().throwOnFailure()
|
API.updateShare(id, description, expiresValue).execute().throwOnFailure()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val activeServerId: Int
|
||||||
|
get() = ActiveServerProvider.getActiveServerId()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// The client will notice if the minimum supported API version has changed
|
// 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
|
// By registering a callback we ensure this info is saved in the database as well
|
||||||
|
|
|
@ -269,7 +269,7 @@ class DownloadHandler(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val musicService = getMusicService()
|
val musicService = getMusicService()
|
||||||
val artist = musicService.getArtist(id, "", false)
|
val artist = musicService.getAlbumsOfArtist(id, "", false)
|
||||||
for ((id1) in artist) {
|
for ((id1) in artist) {
|
||||||
val albumDirectory = musicService.getAlbum(
|
val albumDirectory = musicService.getAlbum(
|
||||||
id1,
|
id1,
|
||||||
|
|
|
@ -86,6 +86,7 @@ object Constants {
|
||||||
const val PREFERENCES_KEY_INCREMENT_TIME = "incrementTime"
|
const val PREFERENCES_KEY_INCREMENT_TIME = "incrementTime"
|
||||||
const val PREFERENCES_KEY_SHOW_NOW_PLAYING_DETAILS = "showNowPlayingDetails"
|
const val PREFERENCES_KEY_SHOW_NOW_PLAYING_DETAILS = "showNowPlayingDetails"
|
||||||
const val PREFERENCES_KEY_ID3_TAGS = "useId3Tags"
|
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_SHOW_ARTIST_PICTURE = "showArtistPicture"
|
||||||
const val PREFERENCES_KEY_CHAT_REFRESH_INTERVAL = "chatRefreshInterval"
|
const val PREFERENCES_KEY_CHAT_REFRESH_INTERVAL = "chatRefreshInterval"
|
||||||
const val PREFERENCES_KEY_DIRECTORY_CACHE_TIME = "directoryCacheTime"
|
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_PAUSE_ON_BLUETOOTH_DEVICE = "pauseOnBluetoothDevice"
|
||||||
const val PREFERENCES_KEY_DEBUG_LOG_TO_FILE = "debugLogToFile"
|
const val PREFERENCES_KEY_DEBUG_LOG_TO_FILE = "debugLogToFile"
|
||||||
const val PREFERENCES_KEY_OVERRIDE_LANGUAGE = "overrideLanguage"
|
const val PREFERENCES_KEY_OVERRIDE_LANGUAGE = "overrideLanguage"
|
||||||
|
const val PREFERENCES_FIRST_INSTALLED_VERSION = "firstInstalledVersion"
|
||||||
const val PREFERENCE_VALUE_ALL = 0
|
const val PREFERENCE_VALUE_ALL = 0
|
||||||
const val PREFERENCE_VALUE_A2DP = 1
|
const val PREFERENCE_VALUE_A2DP = 1
|
||||||
const val PREFERENCE_VALUE_DISABLED = 2
|
const val PREFERENCE_VALUE_DISABLED = 2
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Settings.kt
|
* Settings.kt
|
||||||
* Copyright (C) 2009-2021 Ultrasonic developers
|
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||||
*
|
*
|
||||||
* Distributed under terms of the GNU GPLv3 license.
|
* Distributed under terms of the GNU GPLv3 license.
|
||||||
*/
|
*/
|
||||||
|
@ -13,7 +13,6 @@ import androidx.preference.PreferenceManager
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
import org.moire.ultrasonic.app.UApp
|
import org.moire.ultrasonic.app.UApp
|
||||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains convenience functions for reading and writing preferences
|
* Contains convenience functions for reading and writing preferences
|
||||||
|
@ -131,6 +130,7 @@ object Settings {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var mediaButtonsEnabled
|
var mediaButtonsEnabled
|
||||||
by BooleanSetting(Constants.PREFERENCES_KEY_MEDIA_BUTTONS, true)
|
by BooleanSetting(Constants.PREFERENCES_KEY_MEDIA_BUTTONS, true)
|
||||||
|
|
||||||
var resumePlayOnHeadphonePlug
|
var resumePlayOnHeadphonePlug
|
||||||
by BooleanSetting(R.string.setting_keys_resume_play_on_headphones_plug, true)
|
by BooleanSetting(R.string.setting_keys_resume_play_on_headphones_plug, true)
|
||||||
|
|
||||||
|
@ -160,9 +160,14 @@ object Settings {
|
||||||
var showNowPlayingDetails
|
var showNowPlayingDetails
|
||||||
by BooleanSetting(Constants.PREFERENCES_KEY_SHOW_NOW_PLAYING_DETAILS, false)
|
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
|
@JvmStatic
|
||||||
var shouldUseId3Tags
|
var shouldUseId3Tags by BooleanSetting(Constants.PREFERENCES_KEY_ID3_TAGS, false)
|
||||||
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)
|
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)
|
var firstRunExecuted by BooleanSetting(Constants.PREFERENCES_KEY_FIRST_RUN_EXECUTED, false)
|
||||||
|
|
||||||
val shouldShowArtistPicture: Boolean
|
val shouldShowArtistPicture
|
||||||
get() {
|
by BooleanSetting(Constants.PREFERENCES_KEY_SHOW_ARTIST_PICTURE, false)
|
||||||
val preferences = preferences
|
|
||||||
val isOffline = ActiveServerProvider.isOffline()
|
|
||||||
val isId3Enabled = preferences.getBoolean(Constants.PREFERENCES_KEY_ID3_TAGS, false)
|
|
||||||
val shouldShowArtistPicture =
|
|
||||||
preferences.getBoolean(Constants.PREFERENCES_KEY_SHOW_ARTIST_PICTURE, false)
|
|
||||||
return !isOffline && isId3Enabled && shouldShowArtistPicture
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var chatRefreshInterval by StringIntSetting(
|
var chatRefreshInterval by StringIntSetting(
|
||||||
|
@ -253,6 +251,9 @@ object Settings {
|
||||||
|
|
||||||
var useHwOffload by BooleanSetting(Constants.PREFERENCES_KEY_HARDWARE_OFFLOAD, false)
|
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
|
// TODO: Remove in December 2022
|
||||||
fun migrateFeatureStorage() {
|
fun migrateFeatureStorage() {
|
||||||
val sp = appContext.getSharedPreferences("feature_flags", Context.MODE_PRIVATE)
|
val sp = appContext.getSharedPreferences("feature_flags", Context.MODE_PRIVATE)
|
||||||
|
|
|
@ -316,6 +316,9 @@
|
||||||
<string name="settings.show_now_playing_details">Show details in Now Playing</string>
|
<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">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_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">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="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>
|
<string name="main.video" tools:ignore="UnusedResources">Video</string>
|
||||||
|
|
|
@ -59,6 +59,12 @@
|
||||||
a:summary="@string/settings.use_id3_summary"
|
a:summary="@string/settings.use_id3_summary"
|
||||||
a:title="@string/settings.use_id3"
|
a:title="@string/settings.use_id3"
|
||||||
app:iconSpaceReserved="false"/>
|
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
|
<CheckBoxPreference
|
||||||
a:defaultValue="true"
|
a:defaultValue="true"
|
||||||
a:key="showArtistPicture"
|
a:key="showArtistPicture"
|
||||||
|
|
|
@ -12,6 +12,8 @@ import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||||
* Unit test for extension functions in [APIAlbumConverter.kt] file.
|
* Unit test for extension functions in [APIAlbumConverter.kt] file.
|
||||||
*/
|
*/
|
||||||
class APIAlbumConverterTest {
|
class APIAlbumConverterTest {
|
||||||
|
private val serverId = -1
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Should convert Album to domain entity`() {
|
fun `Should convert Album to domain entity`() {
|
||||||
val entity = Album(
|
val entity = Album(
|
||||||
|
@ -20,7 +22,7 @@ class APIAlbumConverterTest {
|
||||||
created = Calendar.getInstance(), year = 2017, genre = "some-genre"
|
created = Calendar.getInstance(), year = 2017, genre = "some-genre"
|
||||||
)
|
)
|
||||||
|
|
||||||
val convertedEntity = entity.toDomainEntity()
|
val convertedEntity = entity.toDomainEntity(serverId)
|
||||||
|
|
||||||
with(convertedEntity) {
|
with(convertedEntity) {
|
||||||
id `should be equal to` entity.id
|
id `should be equal to` entity.id
|
||||||
|
@ -46,12 +48,12 @@ class APIAlbumConverterTest {
|
||||||
songList = listOf(MusicDirectoryChild())
|
songList = listOf(MusicDirectoryChild())
|
||||||
)
|
)
|
||||||
|
|
||||||
val convertedEntity = entity.toMusicDirectoryDomainEntity()
|
val convertedEntity = entity.toMusicDirectoryDomainEntity(serverId)
|
||||||
|
|
||||||
with(convertedEntity) {
|
with(convertedEntity) {
|
||||||
name `should be equal to` null
|
name `should be equal to` null
|
||||||
size `should be equal to` entity.songList.size
|
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`() {
|
fun `Should convert list of Album entities to domain list entities`() {
|
||||||
val entityList = listOf(Album(id = "455"), Album(id = "1"), Album(id = "1000"))
|
val entityList = listOf(Album(id = "455"), Album(id = "1"), Album(id = "1000"))
|
||||||
|
|
||||||
val convertedList = entityList.toDomainEntityList()
|
val convertedList = entityList.toDomainEntityList(serverId)
|
||||||
|
|
||||||
with(convertedList) {
|
with(convertedList) {
|
||||||
size `should be equal to` entityList.size
|
size `should be equal to` entityList.size
|
||||||
forEachIndexed { index, entry ->
|
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.
|
* Unit test for extension functions in APIArtistConverter.kt file.
|
||||||
*/
|
*/
|
||||||
class APIArtistConverterTest {
|
class APIArtistConverterTest : BaseTest() {
|
||||||
@Test
|
@Test
|
||||||
fun `Should convert artist entity`() {
|
fun `Should convert artist entity`() {
|
||||||
val entity = Artist(id = "10", name = "artist-name", starred = Calendar.getInstance())
|
val entity = Artist(id = "10", name = "artist-name", starred = Calendar.getInstance())
|
||||||
|
|
||||||
val convertedEntity = entity.toDomainEntity()
|
val convertedEntity = entity.toDomainEntity(serverId)
|
||||||
|
|
||||||
with(convertedEntity) {
|
with(convertedEntity) {
|
||||||
id `should be equal to` entity.id
|
id `should be equal to` entity.id
|
||||||
|
@ -38,12 +38,12 @@ class APIArtistConverterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val convertedEntity = entity.toMusicDirectoryDomainEntity()
|
val convertedEntity = entity.toMusicDirectoryDomainEntity(serverId)
|
||||||
|
|
||||||
with(convertedEntity) {
|
with(convertedEntity) {
|
||||||
name `should be equal to` entity.name
|
name `should be equal to` entity.name
|
||||||
getChildren() `should be equal to` entity.albumsList
|
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.
|
* Unit test for function that converts [Bookmark] api entity to domain.
|
||||||
*/
|
*/
|
||||||
class APIBookmarkConverterTest {
|
class APIBookmarkConverterTest : BaseTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Should convert to domain entity`() {
|
fun `Should convert to domain entity`() {
|
||||||
val entity = Bookmark(
|
val entity = Bookmark(
|
||||||
|
@ -19,7 +20,7 @@ class APIBookmarkConverterTest {
|
||||||
Calendar.getInstance(), MusicDirectoryChild(id = "12333")
|
Calendar.getInstance(), MusicDirectoryChild(id = "12333")
|
||||||
)
|
)
|
||||||
|
|
||||||
val domainEntity = entity.toDomainEntity()
|
val domainEntity = entity.toDomainEntity(serverId)
|
||||||
|
|
||||||
with(domainEntity) {
|
with(domainEntity) {
|
||||||
position `should be equal to` entity.position.toInt()
|
position `should be equal to` entity.position.toInt()
|
||||||
|
@ -27,7 +28,7 @@ class APIBookmarkConverterTest {
|
||||||
comment `should be equal to` entity.comment
|
comment `should be equal to` entity.comment
|
||||||
created `should be equal to` entity.created?.time
|
created `should be equal to` entity.created?.time
|
||||||
changed `should be equal to` entity.changed?.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`() {
|
fun `Should convert list of entities to domain entities`() {
|
||||||
val entitiesList = listOf(Bookmark(443L), Bookmark(444L))
|
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.size `should be equal to` entitiesList.size
|
||||||
domainEntitiesList.forEachIndexed({ index, bookmark ->
|
domainEntitiesList.forEachIndexed { index, bookmark ->
|
||||||
bookmark `should be equal to` entitiesList[index].toDomainEntity()
|
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].
|
* Unit tests for extension functions in [APIIndexesConverter.kt].
|
||||||
*/
|
*/
|
||||||
class APIIndexConverterTest {
|
class APIIndexConverterTest : BaseTest() {
|
||||||
@Test
|
@Test
|
||||||
fun `Should convert Indexes entity`() {
|
fun `Should convert Indexes entity`() {
|
||||||
val artistsA = listOf(
|
val artistsA = listOf(
|
||||||
|
@ -31,9 +31,12 @@ class APIIndexConverterTest {
|
||||||
shortcutList = artistsA
|
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) {
|
with(convertedEntity) {
|
||||||
size `should be equal to` expectedArtists.size
|
size `should be equal to` expectedArtists.size
|
||||||
this `should be equal to` expectedArtists
|
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.
|
* Unit test for extension functions in APIMusicDirectoryConverter.kt file.
|
||||||
*/
|
*/
|
||||||
class APIMusicDirectoryConverterTest {
|
class APIMusicDirectoryConverterTest : BaseTest() {
|
||||||
@Test
|
@Test
|
||||||
fun `Should convert MusicDirectory entity`() {
|
fun `Should convert MusicDirectory entity`() {
|
||||||
val entity = MusicDirectory(
|
val entity = MusicDirectory(
|
||||||
|
@ -20,13 +20,13 @@ class APIMusicDirectoryConverterTest {
|
||||||
childList = listOf(MusicDirectoryChild("1"), MusicDirectoryChild("2"))
|
childList = listOf(MusicDirectoryChild("1"), MusicDirectoryChild("2"))
|
||||||
)
|
)
|
||||||
|
|
||||||
val convertedEntity = entity.toDomainEntity()
|
val convertedEntity = entity.toDomainEntity(serverId)
|
||||||
|
|
||||||
with(convertedEntity) {
|
with(convertedEntity) {
|
||||||
name `should be equal to` entity.name
|
name `should be equal to` entity.name
|
||||||
size `should be equal to` entity.childList.size
|
size `should be equal to` entity.childList.size
|
||||||
getChildren() `should be equal to` entity.childList
|
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
|
starred = Calendar.getInstance(), userRating = 3, averageRating = 2.99F
|
||||||
)
|
)
|
||||||
|
|
||||||
val convertedEntity = entity.toTrackEntity()
|
val convertedEntity = entity.toTrackEntity(serverId)
|
||||||
|
|
||||||
with(convertedEntity) {
|
with(convertedEntity) {
|
||||||
id `should be equal to` entity.id
|
id `should be equal to` entity.id
|
||||||
|
@ -84,7 +84,7 @@ class APIMusicDirectoryConverterTest {
|
||||||
artist = "some-artist", publishDate = Calendar.getInstance()
|
artist = "some-artist", publishDate = Calendar.getInstance()
|
||||||
)
|
)
|
||||||
|
|
||||||
val convertedEntity = entity.toTrackEntity()
|
val convertedEntity = entity.toTrackEntity(serverId)
|
||||||
|
|
||||||
with(convertedEntity) {
|
with(convertedEntity) {
|
||||||
id `should be equal to` entity.streamId
|
id `should be equal to` entity.streamId
|
||||||
|
@ -96,11 +96,11 @@ class APIMusicDirectoryConverterTest {
|
||||||
fun `Should convert list of MusicDirectoryChild to domain entity list`() {
|
fun `Should convert list of MusicDirectoryChild to domain entity list`() {
|
||||||
val entitiesList = listOf(MusicDirectoryChild(id = "45"), MusicDirectoryChild(id = "34"))
|
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.size `should be equal to` entitiesList.size
|
||||||
domainList.forEachIndexed { index, entry ->
|
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.
|
* Unit test for extension functions in file APIMusicFolderConverter.kt.
|
||||||
*/
|
*/
|
||||||
class APIMusicFolderConverterTest {
|
class APIMusicFolderConverterTest : BaseTest() {
|
||||||
@Test
|
@Test
|
||||||
fun `Should convert MusicFolder entity`() {
|
fun `Should convert MusicFolder entity`() {
|
||||||
val entity = MusicFolder(id = "10", name = "some-name")
|
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.name `should be equal to` entity.name
|
||||||
convertedEntity.id `should be equal to` entity.id
|
convertedEntity.id `should be equal to` entity.id
|
||||||
|
@ -27,7 +27,7 @@ class APIMusicFolderConverterTest {
|
||||||
MusicFolder(id = "4", name = "some-name-4")
|
MusicFolder(id = "4", name = "some-name-4")
|
||||||
)
|
)
|
||||||
|
|
||||||
val convertedList = entityList.toDomainEntityList()
|
val convertedList = entityList.toDomainEntityList(serverId)
|
||||||
|
|
||||||
with(convertedList) {
|
with(convertedList) {
|
||||||
size `should be equal to` entityList.size
|
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.
|
* Unit test for extension functions that converts api playlist entity to domain.
|
||||||
*/
|
*/
|
||||||
class APIPlaylistConverterTest {
|
class APIPlaylistConverterTest : BaseTest() {
|
||||||
@Test
|
@Test
|
||||||
fun `Should convert Playlist to MusicDirectory domain entity`() {
|
fun `Should convert Playlist to MusicDirectory domain entity`() {
|
||||||
val entity = Playlist(
|
val entity = Playlist(
|
||||||
|
@ -22,13 +22,13 @@ class APIPlaylistConverterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val convertedEntity = entity.toMusicDirectoryDomainEntity()
|
val convertedEntity = entity.toMusicDirectoryDomainEntity(serverId)
|
||||||
|
|
||||||
with(convertedEntity) {
|
with(convertedEntity) {
|
||||||
name `should be equal to` entity.name
|
name `should be equal to` entity.name
|
||||||
size `should be equal to` entity.entriesList.size
|
size `should be equal to` entity.entriesList.size
|
||||||
this[0] `should be equal to` entity.entriesList[0].toTrackEntity()
|
this[0] `should be equal to` entity.entriesList[0].toTrackEntity(serverId)
|
||||||
this[1] `should be equal to` entity.entriesList[1].toTrackEntity()
|
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.
|
* Unit test for extension function in APISearchConverter.kt file.
|
||||||
*/
|
*/
|
||||||
class APISearchConverterTest {
|
class APISearchConverterTest : BaseTest() {
|
||||||
@Test
|
@Test
|
||||||
fun `Should convert SearchResult to domain entity`() {
|
fun `Should convert SearchResult to domain entity`() {
|
||||||
val entity = SearchResult(
|
val entity = SearchResult(
|
||||||
|
@ -26,7 +26,7 @@ class APISearchConverterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val convertedEntity = entity.toDomainEntity()
|
val convertedEntity = entity.toDomainEntity(serverId)
|
||||||
|
|
||||||
with(convertedEntity) {
|
with(convertedEntity) {
|
||||||
albums `should not be equal to` null
|
albums `should not be equal to` null
|
||||||
|
@ -34,7 +34,7 @@ class APISearchConverterTest {
|
||||||
artists `should not be equal to` null
|
artists `should not be equal to` null
|
||||||
artists.size `should be equal to` 0
|
artists.size `should be equal to` 0
|
||||||
songs.size `should be equal to` entity.matchList.size
|
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"))
|
listOf(MusicDirectoryChild(id = "9118", parent = "112"))
|
||||||
)
|
)
|
||||||
|
|
||||||
val convertedEntity = entity.toDomainEntity()
|
val convertedEntity = entity.toDomainEntity(serverId)
|
||||||
|
|
||||||
with(convertedEntity) {
|
with(convertedEntity) {
|
||||||
artists.size `should be equal to` entity.artistList.size
|
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.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.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"))
|
songList = listOf(MusicDirectoryChild(id = "7123", title = "song1"))
|
||||||
)
|
)
|
||||||
|
|
||||||
val convertedEntity = entity.toDomainEntity()
|
val convertedEntity = entity.toDomainEntity(serverId)
|
||||||
|
|
||||||
with(convertedEntity) {
|
with(convertedEntity) {
|
||||||
artists.size `should be equal to` entity.artistList.size
|
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.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.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.
|
* Unit test for api to domain share entity converter functions.
|
||||||
*/
|
*/
|
||||||
class APIShareConverterTest {
|
class APIShareConverterTest : BaseTest() {
|
||||||
@Test
|
@Test
|
||||||
fun `Should convert share entity to domain`() {
|
fun `Should convert share entity to domain`() {
|
||||||
val entity = createFakeShare()
|
val entity = createFakeShare()
|
||||||
|
|
||||||
val domainEntity = entity.toDomainEntity()
|
val domainEntity = entity.toDomainEntity(serverId)
|
||||||
|
|
||||||
with(domainEntity) {
|
with(domainEntity) {
|
||||||
id `should be equal to` entity.id
|
id `should be equal to` entity.id
|
||||||
|
@ -27,7 +27,7 @@ class APIShareConverterTest {
|
||||||
lastVisited `should be equal to` shareTimeFormat.format(entity.lastVisited!!.time)
|
lastVisited `should be equal to` shareTimeFormat.format(entity.lastVisited!!.time)
|
||||||
expires `should be equal to` shareTimeFormat.format(entity.expires!!.time)
|
expires `should be equal to` shareTimeFormat.format(entity.expires!!.time)
|
||||||
visitCount `should be equal to` entity.visitCount.toLong()
|
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)
|
createFakeShare().copy(id = "554", lastVisited = null)
|
||||||
)
|
)
|
||||||
|
|
||||||
val domainEntityList = entityList.toDomainEntitiesList()
|
val domainEntityList = entityList.toDomainEntitiesList(serverId)
|
||||||
|
|
||||||
domainEntityList.size `should be equal to` entityList.size
|
domainEntityList.size `should be equal to` entityList.size
|
||||||
domainEntityList[0] `should be equal to` entityList[0].toDomainEntity()
|
domainEntityList[0] `should be equal to` entityList[0].toDomainEntity(serverId)
|
||||||
domainEntityList[1] `should be equal to` entityList[1].toDomainEntity()
|
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