mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-08-28 05:53:59 +02:00
Update serverConnectionConfig to include server version, update server track URL and server cover image URL based on server version
This commit is contained in:
parent
b99e0b112b
commit
44613e12f1
10 changed files with 106 additions and 16 deletions
|
@ -14,6 +14,7 @@ data class AudioTrack(
|
||||||
var metadata: FileMetadata?,
|
var metadata: FileMetadata?,
|
||||||
var isLocal: Boolean,
|
var isLocal: Boolean,
|
||||||
var localFileId: String?,
|
var localFileId: String?,
|
||||||
|
// TODO: This should no longer be necessary
|
||||||
var serverIndex: Int? // Need to know if server track index is different
|
var serverIndex: Int? // Need to know if server track index is different
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
|
@ -32,11 +32,14 @@ enum class AndroidAutoBrowseSeriesSequenceOrderSetting {
|
||||||
ASC, DESC
|
ASC, DESC
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
data class ServerConnectionConfig(
|
data class ServerConnectionConfig(
|
||||||
var id:String,
|
var id:String,
|
||||||
var index:Int,
|
var index:Int,
|
||||||
var name:String,
|
var name:String,
|
||||||
var address:String,
|
var address:String,
|
||||||
|
// version added after 0.9.81-beta
|
||||||
|
var version:String?,
|
||||||
var userId:String,
|
var userId:String,
|
||||||
var username:String,
|
var username:String,
|
||||||
var token:String,
|
var token:String,
|
||||||
|
|
|
@ -53,6 +53,11 @@ class LibraryItem(
|
||||||
return Uri.parse("android.resource://${BuildConfig.APPLICATION_ID}/" + R.drawable.icon)
|
return Uri.parse("android.resource://${BuildConfig.APPLICATION_ID}/" + R.drawable.icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// As of v2.17.0 token is not needed with cover image requests
|
||||||
|
if (DeviceManager.isServerVersionGreaterThanOrEqualTo("2.17.0")) {
|
||||||
|
return Uri.parse("${DeviceManager.serverAddress}/api/items/$id/cover")
|
||||||
|
}
|
||||||
|
|
||||||
return Uri.parse("${DeviceManager.serverAddress}/api/items/$id/cover?token=${DeviceManager.token}")
|
return Uri.parse("${DeviceManager.serverAddress}/api/items/$id/cover?token=${DeviceManager.token}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,6 +149,16 @@ class PlaybackSession(
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
fun checkIsServerVersionGte(compareVersion: String): Boolean {
|
||||||
|
// Safety check this playback session is the same one currently connected (should always be)
|
||||||
|
if (DeviceManager.serverConnectionConfigId != serverConnectionConfigId) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeviceManager.isServerVersionGreaterThanOrEqualTo(compareVersion)
|
||||||
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getCoverUri(ctx: Context): Uri {
|
fun getCoverUri(ctx: Context): Uri {
|
||||||
if (localLibraryItem?.coverContentUrl != null) {
|
if (localLibraryItem?.coverContentUrl != null) {
|
||||||
|
@ -168,12 +178,22 @@ class PlaybackSession(
|
||||||
|
|
||||||
if (coverPath == null)
|
if (coverPath == null)
|
||||||
return Uri.parse("android.resource://${BuildConfig.APPLICATION_ID}/" + R.drawable.icon)
|
return Uri.parse("android.resource://${BuildConfig.APPLICATION_ID}/" + R.drawable.icon)
|
||||||
|
|
||||||
|
// As of v2.17.0 token is not needed with cover image requests
|
||||||
|
if (checkIsServerVersionGte("2.17.0")) {
|
||||||
|
return Uri.parse("$serverAddress/api/items/$libraryItemId/cover")
|
||||||
|
}
|
||||||
return Uri.parse("$serverAddress/api/items/$libraryItemId/cover?token=${DeviceManager.token}")
|
return Uri.parse("$serverAddress/api/items/$libraryItemId/cover?token=${DeviceManager.token}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getContentUri(audioTrack: AudioTrack): Uri {
|
fun getContentUri(audioTrack: AudioTrack): Uri {
|
||||||
if (isLocal) return Uri.parse(audioTrack.contentUrl) // Local content url
|
if (isLocal) return Uri.parse(audioTrack.contentUrl) // Local content url
|
||||||
|
// As of v2.22.0 tracks use a different endpoint
|
||||||
|
// See: https://github.com/advplyr/audiobookshelf/pull/4263
|
||||||
|
if (checkIsServerVersionGte("2.22.0")) {
|
||||||
|
return Uri.parse("$serverAddress/public/session/$id/track/${audioTrack.index}")
|
||||||
|
}
|
||||||
return Uri.parse("$serverAddress${audioTrack.contentUrl}?token=${DeviceManager.token}")
|
return Uri.parse("$serverAddress${audioTrack.contentUrl}?token=${DeviceManager.token}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,14 +284,16 @@ class PlaybackSession(
|
||||||
com.google.android.gms.cast.MediaMetadata.MEDIA_TYPE_AUDIOBOOK_CHAPTER
|
com.google.android.gms.cast.MediaMetadata.MEDIA_TYPE_AUDIOBOOK_CHAPTER
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// As of v2.17.0 token is not needed with cover image requests
|
||||||
|
val coverUri = if (checkIsServerVersionGte("2.17.0")) {
|
||||||
|
Uri.parse("$serverAddress/api/items/$libraryItemId/cover")
|
||||||
|
} else {
|
||||||
|
Uri.parse("$serverAddress/api/items/$libraryItemId/cover?token=${DeviceManager.token}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cast always uses server cover uri
|
||||||
coverPath?.let {
|
coverPath?.let {
|
||||||
castMetadata.addImage(
|
castMetadata.addImage(WebImage(coverUri))
|
||||||
WebImage(
|
|
||||||
Uri.parse(
|
|
||||||
"$serverAddress/api/items/$libraryItemId/cover?token=${DeviceManager.token}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
castMetadata.putString(com.google.android.gms.cast.MediaMetadata.KEY_TITLE, displayTitle ?: "")
|
castMetadata.putString(com.google.android.gms.cast.MediaMetadata.KEY_TITLE, displayTitle ?: "")
|
||||||
|
|
|
@ -41,6 +41,7 @@ object DeviceManager {
|
||||||
get() = serverConnectionConfig?.userId ?: ""
|
get() = serverConnectionConfig?.userId ?: ""
|
||||||
val token
|
val token
|
||||||
get() = serverConnectionConfig?.token ?: ""
|
get() = serverConnectionConfig?.token ?: ""
|
||||||
|
val serverVersion get() = serverConnectionConfig?.version ?: ""
|
||||||
val isConnectedToServer
|
val isConnectedToServer
|
||||||
get() = serverConnectionConfig != null
|
get() = serverConnectionConfig != null
|
||||||
|
|
||||||
|
@ -111,6 +112,41 @@ object DeviceManager {
|
||||||
return id?.let { deviceData.serverConnectionConfigs.find { it.id == id } }
|
return id?.let { deviceData.serverConnectionConfigs.find { it.id == id } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the currently connected server version is >= compareVersion
|
||||||
|
* Abs server only uses major.minor.patch
|
||||||
|
* Note: Version is returned in Abs auth payloads starting v2.6.0
|
||||||
|
* Note: Version is saved with the server connection config starting after v0.9.81
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* serverVersion=2.25.1
|
||||||
|
* isServerVersionGreaterThanOrEqualTo("2.26.0") = false
|
||||||
|
*
|
||||||
|
* serverVersion=2.26.1
|
||||||
|
* isServerVersionGreaterThanOrEqualTo("2.26.0") = true
|
||||||
|
*/
|
||||||
|
fun isServerVersionGreaterThanOrEqualTo(compareVersion:String):Boolean {
|
||||||
|
if (serverVersion == "") return false
|
||||||
|
if (compareVersion == "") return true
|
||||||
|
|
||||||
|
val serverVersionParts = serverVersion.split(".").map { it.toIntOrNull() ?: 0 }
|
||||||
|
val compareVersionParts = compareVersion.split(".").map { it.toIntOrNull() ?: 0 }
|
||||||
|
|
||||||
|
// Compare major, minor, and patch components
|
||||||
|
for (i in 0 until maxOf(serverVersionParts.size, compareVersionParts.size)) {
|
||||||
|
val serverVersionComponent = serverVersionParts.getOrElse(i) { 0 }
|
||||||
|
val compareVersionComponent = compareVersionParts.getOrElse(i) { 0 }
|
||||||
|
|
||||||
|
if (serverVersionComponent < compareVersionComponent) {
|
||||||
|
return false // Server version is less than compareVersion
|
||||||
|
} else if (serverVersionComponent > compareVersionComponent) {
|
||||||
|
return true // Server version is greater than compareVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true // versions are equal in major, minor, and patch
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the network connectivity status.
|
* Checks the network connectivity status.
|
||||||
* @param ctx The context to use for checking connectivity.
|
* @param ctx The context to use for checking connectivity.
|
||||||
|
|
|
@ -23,14 +23,14 @@ class AbsDatabase : Plugin() {
|
||||||
val tag = "AbsDatabase"
|
val tag = "AbsDatabase"
|
||||||
private var jacksonMapper = jacksonObjectMapper().enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
|
private var jacksonMapper = jacksonObjectMapper().enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
|
||||||
|
|
||||||
lateinit var mainActivity: MainActivity
|
private lateinit var mainActivity: MainActivity
|
||||||
lateinit var apiHandler: ApiHandler
|
private lateinit var apiHandler: ApiHandler
|
||||||
lateinit var secureStorage: SecureStorage
|
private lateinit var secureStorage: SecureStorage
|
||||||
|
|
||||||
data class LocalMediaProgressPayload(val value:List<LocalMediaProgress>)
|
data class LocalMediaProgressPayload(val value:List<LocalMediaProgress>)
|
||||||
data class LocalLibraryItemsPayload(val value:List<LocalLibraryItem>)
|
data class LocalLibraryItemsPayload(val value:List<LocalLibraryItem>)
|
||||||
data class LocalFoldersPayload(val value:List<LocalFolder>)
|
data class LocalFoldersPayload(val value:List<LocalFolder>)
|
||||||
data class ServerConnConfigPayload(val id:String?, val index:Int, val name:String?, val userId:String, val username:String, val token:String, val refreshToken:String?, val address:String?, val customHeaders:Map<String,String>?)
|
data class ServerConnConfigPayload(val id:String?, val index:Int, val name:String?, val userId:String, val username:String, var version:String, val token:String, val refreshToken:String?, val address:String?, val customHeaders:Map<String,String>?)
|
||||||
|
|
||||||
override fun load() {
|
override fun load() {
|
||||||
mainActivity = (activity as MainActivity)
|
mainActivity = (activity as MainActivity)
|
||||||
|
@ -125,6 +125,7 @@ class AbsDatabase : Plugin() {
|
||||||
|
|
||||||
val userId = serverConfigPayload.userId
|
val userId = serverConfigPayload.userId
|
||||||
val username = serverConfigPayload.username
|
val username = serverConfigPayload.username
|
||||||
|
val serverVersion = serverConfigPayload.version
|
||||||
val accessToken = serverConfigPayload.token // New token
|
val accessToken = serverConfigPayload.token // New token
|
||||||
val refreshToken = serverConfigPayload.refreshToken // Refresh only sent on first connection
|
val refreshToken = serverConfigPayload.refreshToken // Refresh only sent on first connection
|
||||||
|
|
||||||
|
@ -144,7 +145,7 @@ class AbsDatabase : Plugin() {
|
||||||
}
|
}
|
||||||
Log.d(tag, "Refresh token secured = $hasRefreshToken")
|
Log.d(tag, "Refresh token secured = $hasRefreshToken")
|
||||||
|
|
||||||
serverConnectionConfig = ServerConnectionConfig(sscId, sscIndex, "$serverAddress ($username)", serverAddress, userId, username, accessToken, serverConfigPayload.customHeaders)
|
serverConnectionConfig = ServerConnectionConfig(sscId, sscIndex, "$serverAddress ($username)", serverAddress, serverVersion, userId, username, accessToken, serverConfigPayload.customHeaders)
|
||||||
|
|
||||||
// Add and save
|
// Add and save
|
||||||
DeviceManager.deviceData.serverConnectionConfigs.add(serverConnectionConfig!!)
|
DeviceManager.deviceData.serverConnectionConfigs.add(serverConnectionConfig!!)
|
||||||
|
@ -152,10 +153,11 @@ class AbsDatabase : Plugin() {
|
||||||
DeviceManager.dbManager.saveDeviceData(DeviceManager.deviceData)
|
DeviceManager.dbManager.saveDeviceData(DeviceManager.deviceData)
|
||||||
} else {
|
} else {
|
||||||
var shouldSave = false
|
var shouldSave = false
|
||||||
if (serverConnectionConfig?.username != username || serverConnectionConfig?.token != accessToken) {
|
if (serverConnectionConfig?.username != username || serverConnectionConfig?.token != accessToken || serverConnectionConfig?.version != serverVersion) {
|
||||||
serverConnectionConfig?.userId = userId
|
serverConnectionConfig?.userId = userId
|
||||||
serverConnectionConfig?.username = username
|
serverConnectionConfig?.username = username
|
||||||
serverConnectionConfig?.name = "${serverConnectionConfig?.address} (${serverConnectionConfig?.username})"
|
serverConnectionConfig?.name = "${serverConnectionConfig?.address} (${serverConnectionConfig?.username})"
|
||||||
|
serverConnectionConfig?.version = serverVersion
|
||||||
serverConnectionConfig?.token = accessToken
|
serverConnectionConfig?.token = accessToken
|
||||||
shouldSave = true
|
shouldSave = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -837,7 +837,7 @@ export default {
|
||||||
this.serverConfig.refreshToken = user.refreshToken
|
this.serverConfig.refreshToken = user.refreshToken
|
||||||
}
|
}
|
||||||
|
|
||||||
delete this.serverConfig.version
|
this.serverConfig.version = serverSettings.version
|
||||||
|
|
||||||
var serverConnectionConfig = await this.$db.setServerConnectionConfig(this.serverConfig)
|
var serverConnectionConfig = await this.$db.setServerConnectionConfig(this.serverConfig)
|
||||||
|
|
||||||
|
|
|
@ -158,6 +158,7 @@ export default {
|
||||||
} else if (userDefaultLibraryId) {
|
} else if (userDefaultLibraryId) {
|
||||||
this.$store.commit('libraries/setCurrentLibrary', userDefaultLibraryId)
|
this.$store.commit('libraries/setCurrentLibrary', userDefaultLibraryId)
|
||||||
}
|
}
|
||||||
|
serverConfig.version = serverSettings.version
|
||||||
const serverConnectionConfig = await this.$db.setServerConnectionConfig(serverConfig)
|
const serverConnectionConfig = await this.$db.setServerConnectionConfig(serverConfig)
|
||||||
|
|
||||||
this.$store.commit('user/setUser', user)
|
this.$store.commit('user/setUser', user)
|
||||||
|
|
|
@ -1,5 +1,18 @@
|
||||||
import { registerPlugin, Capacitor, WebPlugin } from '@capacitor/core'
|
import { registerPlugin, Capacitor, WebPlugin } from '@capacitor/core'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ServerConnectionConfig
|
||||||
|
* @property {string} id
|
||||||
|
* @property {number} index
|
||||||
|
* @property {string} name
|
||||||
|
* @property {string} address
|
||||||
|
* @property {string} version
|
||||||
|
* @property {string} userId
|
||||||
|
* @property {string} username
|
||||||
|
* @property {string} token
|
||||||
|
* @property {string} [refreshToken] - Only passed in when setting config, then stored in secure storage
|
||||||
|
*/
|
||||||
|
|
||||||
class AbsDatabaseWeb extends WebPlugin {
|
class AbsDatabaseWeb extends WebPlugin {
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
|
@ -19,6 +32,11 @@ class AbsDatabaseWeb extends WebPlugin {
|
||||||
return deviceData
|
return deviceData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {ServerConnectionConfig} serverConnectionConfig
|
||||||
|
* @returns {Promise<ServerConnectionConfig>}
|
||||||
|
*/
|
||||||
async setCurrentServerConnectionConfig(serverConnectionConfig) {
|
async setCurrentServerConnectionConfig(serverConnectionConfig) {
|
||||||
var deviceData = await this.getDeviceData()
|
var deviceData = await this.getDeviceData()
|
||||||
|
|
||||||
|
@ -29,6 +47,7 @@ class AbsDatabaseWeb extends WebPlugin {
|
||||||
ssc.token = serverConnectionConfig.token
|
ssc.token = serverConnectionConfig.token
|
||||||
ssc.userId = serverConnectionConfig.userId
|
ssc.userId = serverConnectionConfig.userId
|
||||||
ssc.username = serverConnectionConfig.username
|
ssc.username = serverConnectionConfig.username
|
||||||
|
ssc.version = serverConnectionConfig.version
|
||||||
ssc.customHeaders = serverConnectionConfig.customHeaders || {}
|
ssc.customHeaders = serverConnectionConfig.customHeaders || {}
|
||||||
|
|
||||||
if (serverConnectionConfig.refreshToken) {
|
if (serverConnectionConfig.refreshToken) {
|
||||||
|
@ -47,6 +66,7 @@ class AbsDatabaseWeb extends WebPlugin {
|
||||||
username: serverConnectionConfig.username,
|
username: serverConnectionConfig.username,
|
||||||
address: serverConnectionConfig.address,
|
address: serverConnectionConfig.address,
|
||||||
token: serverConnectionConfig.token,
|
token: serverConnectionConfig.token,
|
||||||
|
version: serverConnectionConfig.version,
|
||||||
customHeaders: serverConnectionConfig.customHeaders || {}
|
customHeaders: serverConnectionConfig.customHeaders || {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ export default function ({ store, $db }, inject) {
|
||||||
* @param {*} data - Request data
|
* @param {*} data - Request data
|
||||||
* @param {Object} headers - Request headers
|
* @param {Object} headers - Request headers
|
||||||
* @param {Object} options - Additional options
|
* @param {Object} options - Additional options
|
||||||
* @param {{ id: string, address: string }} serverConnectionConfig
|
* @param {{ id: string, address: string, version: string }} serverConnectionConfig
|
||||||
* @returns {Promise} - Promise that resolves with the response data
|
* @returns {Promise} - Promise that resolves with the response data
|
||||||
*/
|
*/
|
||||||
async handleTokenRefresh(method, url, data, headers, options, serverConnectionConfig) {
|
async handleTokenRefresh(method, url, data, headers, options, serverConnectionConfig) {
|
||||||
|
@ -161,7 +161,7 @@ export default function ({ store, $db }, inject) {
|
||||||
/**
|
/**
|
||||||
* Updates the store and secure storage with new tokens
|
* Updates the store and secure storage with new tokens
|
||||||
* @param {Object} tokens - Object containing accessToken and refreshToken
|
* @param {Object} tokens - Object containing accessToken and refreshToken
|
||||||
* @param {{ id: string, address: string }} serverConnectionConfig
|
* @param {{ id: string, address: string, version: string }} serverConnectionConfig
|
||||||
* @returns {Promise} - Promise that resolves when tokens are updated
|
* @returns {Promise} - Promise that resolves when tokens are updated
|
||||||
*/
|
*/
|
||||||
async updateTokens(tokens, serverConnectionConfig) {
|
async updateTokens(tokens, serverConnectionConfig) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue