Compare commits

...
8 changed files with 128 additions and 69 deletions
+2 -2
View File
@@ -13,8 +13,8 @@ android {
applicationId "com.audiobookshelf.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 58
versionName "0.9.38-beta"
versionCode 60
versionName "0.9.40-beta"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
@@ -114,9 +114,36 @@ class AudiobookProgressSyncer constructor(playerNotificationService:PlayerNotifi
} else if (listeningStreamId == "download") {
// TODO: Save downloaded audiobook progress & send to server if connected
Log.d(tag, "ListeningTimer: Is listening download")
// Send sync data only for local books
var syncData: JSObject = JSObject()
var duration = playerNotificationService.getAudiobookDuration() / 1000
var currentTime = playerNotificationService.getCurrentTime() / 1000
syncData.put("totalDuration", duration)
syncData.put("currentTime", currentTime)
syncData.put("progress", if (duration > 0) (currentTime / duration) else 0)
syncData.put("isRead", false)
syncData.put("lastUpdate", System.currentTimeMillis())
syncData.put("audiobookId", listeningBookId)
sendLocalSyncData(syncData) {
Log.d(tag, "Local sync done")
}
}
}
fun sendLocalSyncData(payload:JSObject, cb: (() -> Unit)) {
var serverUrl = playerNotificationService.getServerUrl()
var token = playerNotificationService.getUserToken()
if (serverUrl == "" || token == "") {
return
}
Log.d(tag, "Sync Local $serverUrl | $token")
var url = "$serverUrl/api/syncLocal"
sendServerRequest(url, token, payload, cb)
}
fun sendStreamSyncData(payload:JSObject, cb: (() -> Unit)) {
var serverUrl = playerNotificationService.getServerUrl()
var token = playerNotificationService.getUserToken()
@@ -127,7 +154,10 @@ class AudiobookProgressSyncer constructor(playerNotificationService:PlayerNotifi
Log.d(tag, "Sync Stream $serverUrl | $token")
var url = "$serverUrl/api/syncStream"
sendServerRequest(url, token, payload, cb)
}
fun sendServerRequest(url:String, token:String, payload:JSObject, cb: () -> Unit) {
val mediaType = "application/json; charset=utf-8".toMediaType()
val requestBody = payload.toString().toRequestBody(mediaType)
val request = Request.Builder().post(requestBody)
@@ -45,8 +45,8 @@ class MyNativeAudio : Plugin() {
emit("onSleepTimerEnded", currentPosition)
}
override fun onSleepTimerSet(sleepTimerEndTime: Long) {
emit("onSleepTimerSet", sleepTimerEndTime)
override fun onSleepTimerSet(sleepTimeRemaining: Int) {
emit("onSleepTimerSet", sleepTimeRemaining)
}
})
}
@@ -55,7 +55,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
fun onMetadata(metadata: JSObject)
fun onPrepare(audiobookId: String, playWhenReady: Boolean)
fun onSleepTimerEnded(currentPosition: Long)
fun onSleepTimerSet(sleepTimerEndTime: Long)
fun onSleepTimerSet(sleepTimeRemaining: Int)
}
private val tag = "PlayerService"
@@ -648,8 +648,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
Log.d(tag, "Playing ${getCurrentBookTitle()} | ${currentPlayer.mediaMetadata.title} | ${currentPlayer.mediaMetadata.displayTitle}")
if (player.isPlaying) {
audiobookProgressSyncer.start()
}
if (!player.isPlaying && audiobookProgressSyncer.listeningTimerRunning) {
} else {
audiobookProgressSyncer.stop()
}
@@ -673,6 +672,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
}
// Issue with onenote plus crashing when using local cover art. https://github.com/advplyr/audiobookshelf-app/issues/35
// Same issue with sony xperia https://github.com/advplyr/audiobookshelf-app/issues/94
if (currentAudiobookStreamData?.coverUri != null && currentAudiobookStreamData?.isLocal == true) {
var deviceName = Build.DEVICE
var deviceMan = Build.MANUFACTURER
@@ -681,22 +681,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
if (deviceMan.toLowerCase().contains("oneplus") || deviceName.toLowerCase().contains("oneplus")) {
Log.d(tag, "Detected OnePlus device - removing local cover")
currentAudiobookStreamData?.clearCover()
} else if (deviceName.toLowerCase().contains("xperia") || deviceModel.toLowerCase().contains("xperia")) {
Log.d(tag, "Detected Sony Xperia device - removing local cover")
currentAudiobookStreamData?.clearCover()
}
// OnePlus devices were showing valid permissions for image
// try {
// Log.d(tag, "CHECKING COVER ${currentAudiobookStreamData?.coverUri}")
// var file = DocumentFile.fromTreeUri(ctx, currentAudiobookStreamData!!.coverUri)
// Log.d(tag, "GOT FILE ${file?.name} | ${file?.type} | Can Read: ${file?.canRead()} |isExternalStorageDocument: ${file?.isExternalStorageDocument}")
// if (file?.canRead() !== true) {
// Log.d(tag, "Invalid cover: no read access")
// currentAudiobookStreamData?.clearCover()
// }
// } catch(e:Exception) {
// Log.d(tag, "Invalid cover: Failed to read local cover file $e")
// currentAudiobookStreamData?.clearCover()
// e.printStackTrace()
// }
}
var metadata = currentAudiobookStreamData!!.getMediaMetadataCompat()
@@ -784,6 +772,12 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
return currentAudiobookStreamData?.id
}
// The duration stored on the audiobook
fun getAudiobookDuration() : Long {
if (currentAudiobookStreamData == null) return 0L
return currentAudiobookStreamData!!.duration
}
fun getServerUrl(): String {
return audiobookManager.serverUrl
}
@@ -796,8 +790,8 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
if (lastPauseTime <= 0) return 0
var time: Long = System.currentTimeMillis() - lastPauseTime
var seekback: Long = 0
if (time < 3000) seekback = 0
else if (time < 60000) seekback = time / 6
if (time < 60000) seekback = 0
else if (time < 120000) seekback = 10000
else if (time < 300000) seekback = 15000
else if (time < 1800000) seekback = 20000
else if (time < 3600000) seekback = 25000
@@ -1,6 +1,5 @@
package com.audiobookshelf.app
import android.hardware.SensorManager
import android.os.Handler
import android.os.Looper
import android.util.Log
@@ -17,6 +16,8 @@ class SleepTimerManager constructor(playerNotificationService:PlayerNotification
private var sleepTimerTask:TimerTask? = null
private var sleepTimerRunning:Boolean = false
private var sleepTimerEndTime:Long = 0L
private var sleepTimerLength:Long = 0L
private var sleepTimerElapsed:Long = 0L
private var sleepTimerExtensionTime:Long = 0L
private var sleepTimerFinishedAt:Long = 0L
@@ -45,13 +46,12 @@ class SleepTimerManager constructor(playerNotificationService:PlayerNotification
}
private fun getSleepTimerTimeRemainingSeconds():Int {
if (sleepTimerEndTime == 0L && sleepTimerLength > 0) { // For regular timer
return ((sleepTimerLength - sleepTimerElapsed) / 1000).toDouble().roundToInt()
}
// For chapter end timer
if (sleepTimerEndTime <= 0) return 0
var sleepTimeRemaining = sleepTimerEndTime - getCurrentTime()
return ((sleepTimeRemaining / 1000).toDouble()).roundToInt()
}
fun getIsSleepTimerRunning():Boolean {
return sleepTimerRunning
return (((sleepTimerEndTime - getCurrentTime()) / 1000).toDouble()).roundToInt()
}
fun setSleepTimer(time: Long, isChapterTime: Boolean) : Boolean {
@@ -59,6 +59,7 @@ class SleepTimerManager constructor(playerNotificationService:PlayerNotification
sleepTimerTask?.cancel()
sleepTimerRunning = false
sleepTimerFinishedAt = 0L
sleepTimerElapsed = 0L
// Register shake sensor
playerNotificationService.registerSensor()
@@ -70,24 +71,36 @@ class SleepTimerManager constructor(playerNotificationService:PlayerNotification
return false
}
sleepTimerEndTime = time
sleepTimerLength = 0
sleepTimerExtensionTime = SLEEP_EXTENSION_TIME
if (sleepTimerEndTime > getDuration()) {
sleepTimerEndTime = getDuration()
}
} else {
sleepTimerEndTime = currentTime + time
sleepTimerLength = time
sleepTimerEndTime = 0L
sleepTimerExtensionTime = time
if (sleepTimerLength + getCurrentTime() > getDuration()) {
sleepTimerLength = getDuration() - getCurrentTime()
}
}
if (sleepTimerEndTime > getDuration()) {
sleepTimerEndTime = getDuration()
}
playerNotificationService.listener?.onSleepTimerSet(sleepTimerEndTime)
playerNotificationService.listener?.onSleepTimerSet(getSleepTimerTimeRemainingSeconds())
sleepTimerRunning = true
sleepTimerTask = Timer("SleepTimer", false).schedule(0L, 1000L) {
Handler(Looper.getMainLooper()).post() {
if (getIsPlaying()) {
sleepTimerElapsed += 1000L
var sleepTimeSecondsRemaining = getSleepTimerTimeRemainingSeconds()
Log.d(tag, "Sleep TIMER time remaining $sleepTimeSecondsRemaining s")
Log.d(tag, "Timer Elapsed $sleepTimerElapsed | Sleep TIMER time remaining $sleepTimeSecondsRemaining s")
if (sleepTimeSecondsRemaining > 0) {
playerNotificationService.listener?.onSleepTimerSet(sleepTimeSecondsRemaining)
}
if (sleepTimeSecondsRemaining <= 0) {
Log.d(tag, "Sleep Timer Pausing Player on Chapter")
@@ -129,9 +142,15 @@ class SleepTimerManager constructor(playerNotificationService:PlayerNotification
private fun extendSleepTime() {
if (!sleepTimerRunning) return
setVolume(1F)
sleepTimerEndTime += sleepTimerExtensionTime
if (sleepTimerEndTime > getDuration()) sleepTimerEndTime = getDuration()
playerNotificationService.listener?.onSleepTimerSet(sleepTimerEndTime)
if (sleepTimerEndTime == 0L) {
sleepTimerLength += sleepTimerExtensionTime
if (sleepTimerLength + getCurrentTime() > getDuration()) sleepTimerLength = getDuration() - getCurrentTime()
} else {
sleepTimerEndTime += sleepTimerExtensionTime
if (sleepTimerEndTime > getDuration()) sleepTimerEndTime = getDuration()
}
playerNotificationService.listener?.onSleepTimerSet(getSleepTimerTimeRemainingSeconds())
}
fun checkShouldExtendSleepTimer() {
@@ -164,27 +183,42 @@ class SleepTimerManager constructor(playerNotificationService:PlayerNotification
fun increaseSleepTime(time: Long) {
Log.d(tag, "Increase Sleep time $time")
if (!sleepTimerRunning) return
var newSleepEndTime = sleepTimerEndTime + time
sleepTimerEndTime = if (newSleepEndTime >= getDuration()) {
getDuration()
if (sleepTimerEndTime == 0L) {
sleepTimerLength += time
if (sleepTimerLength + getCurrentTime() > getDuration()) sleepTimerLength = getDuration() - getCurrentTime()
} else {
newSleepEndTime
var newSleepEndTime = sleepTimerEndTime + time
sleepTimerEndTime = if (newSleepEndTime >= getDuration()) {
getDuration()
} else {
newSleepEndTime
}
}
setVolume(1F)
playerNotificationService.listener?.onSleepTimerSet(sleepTimerEndTime)
playerNotificationService.listener?.onSleepTimerSet(getSleepTimerTimeRemainingSeconds())
}
fun decreaseSleepTime(time: Long) {
Log.d(tag, "Decrease Sleep time $time")
if (!sleepTimerRunning) return
var newSleepEndTime = sleepTimerEndTime - time
sleepTimerEndTime = if (newSleepEndTime <= 1000) {
// End sleep timer in 1 second
getCurrentTime() + 1000
if (sleepTimerEndTime == 0L) {
sleepTimerLength -= time
if (sleepTimerLength <= 0) sleepTimerLength = 1000L
} else {
newSleepEndTime
var newSleepEndTime = sleepTimerEndTime - time
sleepTimerEndTime = if (newSleepEndTime <= 1000) {
// End sleep timer in 1 second
getCurrentTime() + 1000
} else {
newSleepEndTime
}
}
setVolume(1F)
playerNotificationService.listener?.onSleepTimerSet(sleepTimerEndTime)
playerNotificationService.listener?.onSleepTimerSet(getSleepTimerTimeRemainingSeconds())
}
}
+7 -7
View File
@@ -107,7 +107,7 @@ export default {
},
loading: Boolean,
sleepTimerRunning: Boolean,
sleepTimerEndTime: Number
sleepTimeRemaining: Number
},
data() {
return {
@@ -241,10 +241,10 @@ export default {
if (!this.currentChapter) return 0
return this.currentChapter.end - this.currentTime
},
sleepTimeRemaining() {
if (!this.sleepTimerEndTime) return 0
return Math.max(0, this.sleepTimerEndTime / 1000 - this.currentTime)
},
// sleepTimeRemaining() {
// if (!this.sleepTimerEndTime) return 0
// return Math.max(0, this.sleepTimerEndTime / 1000 - this.currentTime)
// },
sleepTimeRemainingPretty() {
if (!this.sleepTimeRemaining) return '0s'
var secondsRemaining = Math.round(this.sleepTimeRemaining)
@@ -516,8 +516,8 @@ export default {
calcSeekBackTime(lastUpdate) {
var time = Date.now() - lastUpdate
var seekback = 0
if (time < 3000) seekback = 0
else if (time < 60000) seekback = time / 6
if (time < 60000) seekback = 0
else if (time < 120000) seekback = 10000
else if (time < 300000) seekback = 15000
else if (time < 1800000) seekback = 20000
else if (time < 3600000) seekback = 25000
+10 -9
View File
@@ -9,7 +9,7 @@
:loading="isLoading"
:bookmarks="bookmarks"
:sleep-timer-running="isSleepTimerRunning"
:sleep-timer-end-time="sleepTimerEndTime"
:sleep-time-remaining="sleepTimeRemaining"
@close="cancelStream"
@sync="sync"
@setTotalDuration="setTotalDuration"
@@ -49,6 +49,7 @@ export default {
currentTime: 0,
isSleepTimerRunning: false,
sleepTimerEndTime: 0,
sleepTimerRemaining: 0,
onSleepTimerEndedListener: null,
onSleepTimerSetListener: null,
sleepInterval: null,
@@ -149,11 +150,11 @@ export default {
return `${this.$store.state.serverUrl}/s/book/${this.audiobook.id}/${trelpath}?token=${this.userToken}`
})
return tracks
},
sleepTimeRemaining() {
if (!this.sleepTimerEndTime) return 0
return Math.max(0, this.sleepTimerEndTime / 1000 - this.currentTime)
}
// sleepTimeRemaining() {
// if (!this.sleepTimerEndTime) return 0
// return Math.max(0, this.sleepTimerEndTime / 1000 - this.currentTime)
// }
},
methods: {
showBookmarks() {
@@ -175,16 +176,16 @@ export default {
this.updateTime(currentTime)
}
},
onSleepTimerSet({ value: sleepTimerEndTime }) {
console.log('SLEEP TIMER SET', sleepTimerEndTime)
if (sleepTimerEndTime === 0) {
onSleepTimerSet({ value: sleepTimeRemaining }) {
console.log('SLEEP TIMER SET', sleepTimeRemaining)
if (sleepTimeRemaining === 0) {
console.log('Sleep timer canceled')
this.isSleepTimerRunning = false
} else {
this.isSleepTimerRunning = true
}
this.sleepTimerEndTime = sleepTimerEndTime
this.sleepTimeRemaining = sleepTimeRemaining
},
showSleepTimer() {
if (this.currentChapter) {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf-app",
"version": "0.9.38-beta",
"version": "0.9.40-beta",
"author": "advplyr",
"scripts": {
"dev": "nuxt --hostname localhost --port 1337",