mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-07 04:18:48 +02:00
Merge branch 'master' into feat_android_auto_browse
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
|
||||
<!-- Permissions -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"pkg": "@byteowls/capacitor-filesharer",
|
||||
"classpath": "com.byteowls.capacitor.filesharer.FileSharerPlugin"
|
||||
},
|
||||
{
|
||||
"pkg": "@capacitor-community/volume-buttons",
|
||||
"classpath": "com.ryltsov.alex.plugins.volume.buttons.VolumeButtonsPlugin"
|
||||
},
|
||||
{
|
||||
"pkg": "@capacitor/app",
|
||||
"classpath": "com.capacitorjs.plugins.app.AppPlugin"
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.audiobookshelf.app.device.DeviceManager
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
import com.audiobookshelf.app.player.PLAYMETHOD_LOCAL
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@@ -78,6 +79,27 @@ class LocalLibraryItem(
|
||||
}
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
fun hasTracks(episode:PodcastEpisode?): Boolean {
|
||||
var audioTracks = media.getAudioTracks() as MutableList<AudioTrack>
|
||||
if (episode != null) { // Get podcast episode audio track
|
||||
episode.audioTrack?.let { at -> mutableListOf(at) }?.let { tracks -> audioTracks = tracks }
|
||||
}
|
||||
if (audioTracks.size == 0) return false
|
||||
audioTracks.forEach {
|
||||
// Check that metadata is not null
|
||||
if (it.metadata === null) {
|
||||
return false
|
||||
}
|
||||
// Check that file exists
|
||||
val file = File(it.metadata!!.path)
|
||||
if (!file.exists()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
fun getPlaybackSession(episode:PodcastEpisode?, deviceInfo:DeviceInfo):PlaybackSession {
|
||||
val localEpisodeId = episode?.id
|
||||
|
||||
+6
-5
@@ -335,11 +335,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
val mediaDescriptionBuilder = MediaDescriptionCompat.Builder()
|
||||
.setExtras(extra)
|
||||
.setTitle(currentPlaybackSession!!.displayTitle)
|
||||
.setIconUri(coverUri)
|
||||
|
||||
bitmap?.let {
|
||||
mediaDescriptionBuilder.setIconBitmap(it)
|
||||
}
|
||||
} ?: mediaDescriptionBuilder.setIconUri(coverUri)
|
||||
|
||||
return mediaDescriptionBuilder.build()
|
||||
}
|
||||
@@ -903,16 +902,18 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
|
||||
fun closePlayback(calledOnError:Boolean? = false) {
|
||||
Log.d(tag, "closePlayback")
|
||||
val config = DeviceManager.serverConnectionConfig
|
||||
|
||||
val isLocal = mediaProgressSyncer.currentIsLocal
|
||||
val currentSessionId = mediaProgressSyncer.currentSessionId
|
||||
if (mediaProgressSyncer.listeningTimerRunning) {
|
||||
Log.i(tag, "About to close playback so stopping media progress syncer first")
|
||||
|
||||
mediaProgressSyncer.stop(calledOnError == false) { // If closing on error then do not sync progress (causes exception)
|
||||
Log.d(tag, "Media Progress syncer stopped")
|
||||
|
||||
// If not local session then close on server
|
||||
if (!isLocal && currentSessionId != "") {
|
||||
apiHandler.closePlaybackSession(currentSessionId) {
|
||||
apiHandler.closePlaybackSession(currentSessionId, config) {
|
||||
Log.d(tag, "Closed playback session $currentSessionId")
|
||||
}
|
||||
}
|
||||
@@ -920,7 +921,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
} else {
|
||||
// If not local session then close on server
|
||||
if (!isLocal && currentSessionId != "") {
|
||||
apiHandler.closePlaybackSession(currentSessionId) {
|
||||
apiHandler.closePlaybackSession(currentSessionId, config) {
|
||||
Log.d(tag, "Closed playback session $currentSessionId")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +198,9 @@ class AbsAudioPlayer : Plugin() {
|
||||
return call.resolve(JSObject("{\"error\":\"Podcast episode not found\"}"))
|
||||
}
|
||||
}
|
||||
if (!it.hasTracks(episode)) {
|
||||
return call.resolve(JSObject("{\"error\":\"No audio files found on device. Download book again to fix.\"}"))
|
||||
}
|
||||
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
Log.d(tag, "prepareLibraryItem: Preparing Local Media item ${jacksonMapper.writeValueAsString(it)}")
|
||||
|
||||
@@ -196,7 +196,11 @@ class AbsFileSystem : Plugin() {
|
||||
if (localLibraryItem?.folderId?.startsWith("internal-") == true) {
|
||||
Log.d(tag, "Deleting internal library item at absolutePath $absolutePath")
|
||||
val file = File(absolutePath)
|
||||
success = file.deleteRecursively()
|
||||
success = if (file.exists()) {
|
||||
file.deleteRecursively()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
var subfolderPathToDelete = ""
|
||||
localLibraryItem?.folderId?.let { folderId ->
|
||||
@@ -218,7 +222,12 @@ class AbsFileSystem : Plugin() {
|
||||
}
|
||||
|
||||
val docfile = DocumentFileCompat.fromUri(mainActivity, Uri.parse(contentUrl))
|
||||
success = docfile?.delete() == true
|
||||
if (docfile?.exists() == true) {
|
||||
success = docfile.delete() == true
|
||||
} else {
|
||||
Log.d(tag, "Folder $contentUrl doesn't exist")
|
||||
success = true
|
||||
}
|
||||
|
||||
if (subfolderPathToDelete != "") {
|
||||
Log.d(tag, "Deleting empty subfolder at $subfolderPathToDelete")
|
||||
|
||||
@@ -45,10 +45,17 @@ class ApiHandler(var ctx:Context) {
|
||||
val address = config?.address ?: DeviceManager.serverAddress
|
||||
val token = config?.token ?: DeviceManager.token
|
||||
|
||||
val request = Request.Builder()
|
||||
.url("${address}$endpoint").addHeader("Authorization", "Bearer $token")
|
||||
.build()
|
||||
makeRequest(request, httpClient, cb)
|
||||
try {
|
||||
val request = Request.Builder()
|
||||
.url("${address}$endpoint").addHeader("Authorization", "Bearer $token")
|
||||
.build()
|
||||
makeRequest(request, httpClient, cb)
|
||||
} catch(e: Exception) {
|
||||
e.printStackTrace()
|
||||
val jsobj = JSObject()
|
||||
jsobj.put("error", "Request failed: ${e.message}")
|
||||
cb(jsobj)
|
||||
}
|
||||
}
|
||||
|
||||
private fun postRequest(endpoint:String, payload: JSObject?, config:ServerConnectionConfig?, cb: (JSObject) -> Unit) {
|
||||
@@ -58,23 +65,38 @@ class ApiHandler(var ctx:Context) {
|
||||
val requestBody = payload?.toString()?.toRequestBody(mediaType) ?: EMPTY_REQUEST
|
||||
val requestUrl = "${address}$endpoint"
|
||||
Log.d(tag, "postRequest to $requestUrl")
|
||||
val request = Request.Builder().post(requestBody)
|
||||
.url(requestUrl).addHeader("Authorization", "Bearer ${token}")
|
||||
.build()
|
||||
makeRequest(request, null, cb)
|
||||
try {
|
||||
val request = Request.Builder().post(requestBody)
|
||||
.url(requestUrl).addHeader("Authorization", "Bearer ${token}")
|
||||
.build()
|
||||
makeRequest(request, null, cb)
|
||||
} catch(e: Exception) {
|
||||
e.printStackTrace()
|
||||
val jsobj = JSObject()
|
||||
jsobj.put("error", "Request failed: ${e.message}")
|
||||
cb(jsobj)
|
||||
}
|
||||
}
|
||||
|
||||
private fun patchRequest(endpoint:String, payload: JSObject, cb: (JSObject) -> Unit) {
|
||||
val mediaType = "application/json; charset=utf-8".toMediaType()
|
||||
val requestBody = payload.toString().toRequestBody(mediaType)
|
||||
val request = Request.Builder().patch(requestBody)
|
||||
.url("${DeviceManager.serverAddress}$endpoint").addHeader("Authorization", "Bearer ${DeviceManager.token}")
|
||||
.build()
|
||||
makeRequest(request, null, cb)
|
||||
try {
|
||||
val request = Request.Builder().patch(requestBody)
|
||||
.url("${DeviceManager.serverAddress}$endpoint").addHeader("Authorization", "Bearer ${DeviceManager.token}")
|
||||
.build()
|
||||
makeRequest(request, null, cb)
|
||||
} catch(e: Exception) {
|
||||
e.printStackTrace()
|
||||
val jsobj = JSObject()
|
||||
jsobj.put("error", "Request failed: ${e.message}")
|
||||
cb(jsobj)
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeRequest(request:Request, httpClient:OkHttpClient?, cb: (JSObject) -> Unit) {
|
||||
val client = httpClient ?: defaultClient
|
||||
|
||||
client.newCall(request).enqueue(object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
Log.d(tag, "FAILURE TO CONNECT")
|
||||
@@ -424,9 +446,9 @@ class ApiHandler(var ctx:Context) {
|
||||
}
|
||||
}
|
||||
|
||||
fun closePlaybackSession(playbackSessionId:String, cb: (Boolean) -> Unit) {
|
||||
fun closePlaybackSession(playbackSessionId:String, config:ServerConnectionConfig?, cb: (Boolean) -> Unit) {
|
||||
Log.d(tag, "closePlaybackSession: playbackSessionId=$playbackSessionId")
|
||||
postRequest("/api/session/$playbackSessionId/close", null, null) {
|
||||
postRequest("/api/session/$playbackSessionId/close", null, config) {
|
||||
cb(true)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user