diff --git a/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsLogger.kt b/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsLogger.kt index cd52ddc3..ce52a550 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsLogger.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsLogger.kt @@ -26,17 +26,27 @@ class AbsLogger : Plugin() { private var jacksonMapper = jacksonObjectMapper().enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature()) override fun load() { + onLogEmitter = { log:AbsLog -> + notifyListeners("onLog", JSObject(jacksonMapper.writeValueAsString(log))) + } Log.i("AbsLogger", "Initialize AbsLogger plugin") } companion object { + lateinit var onLogEmitter:(log:AbsLog) -> Unit + + fun log(level:String, tag:String, message:String) { + val absLog = AbsLog(id = UUID.randomUUID().toString(), tag, level, message, timestamp = System.currentTimeMillis()) + DeviceManager.dbManager.saveLog(absLog) + onLogEmitter(absLog) + } fun info(tag:String, message:String) { Log.i("AbsLogger", message) - DeviceManager.dbManager.saveLog(AbsLog(id = UUID.randomUUID().toString(), tag, level = "info", message, timestamp = System.currentTimeMillis())) + log("info", tag, message) } fun error(tag:String, message:String) { Log.e("AbsLogger", message) - DeviceManager.dbManager.saveLog(AbsLog(id = UUID.randomUUID().toString(), tag, level = "error", message, timestamp = System.currentTimeMillis())) + log("error", tag, message) } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt b/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt index 65a4e71d..bf807f73 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt @@ -522,9 +522,29 @@ class ApiHandler(var ctx:Context) { // Get matching local media progress allLocalMediaProgress.find { it.isMatch(mediaProgress) }?.let { localMediaProgress -> if (mediaProgress.lastUpdate > localMediaProgress.lastUpdate) { - AbsLogger.info("ApiHandler", "[ApiHandler] syncLocalMediaProgressForUser: Server progress for item \"${mediaProgress.mediaItemId}\" is more recent than local. Local progress last updated ${localMediaProgress.lastUpdate}, server progress last updated ${mediaProgress.lastUpdate}. Updating local current time ${localMediaProgress.currentTime} to ${mediaProgress.currentTime}") + val updateLogs = mutableListOf() + if (mediaProgress.progress != localMediaProgress.progress) { + updateLogs.add("Updated progress from ${localMediaProgress.progress} to ${mediaProgress.progress}") + } + if (mediaProgress.currentTime != localMediaProgress.currentTime) { + updateLogs.add("Updated currentTime from ${localMediaProgress.currentTime} to ${mediaProgress.currentTime}") + } + if (mediaProgress.isFinished != localMediaProgress.isFinished) { + updateLogs.add("Updated isFinished from ${localMediaProgress.isFinished} to ${mediaProgress.isFinished}") + } + if (mediaProgress.ebookProgress != localMediaProgress.ebookProgress) { + updateLogs.add("Updated ebookProgress from ${localMediaProgress.isFinished} to ${mediaProgress.isFinished}") + } + if (updateLogs.isNotEmpty()) { + AbsLogger.info("ApiHandler", "syncLocalMediaProgressForUser: Server progress for item \"${mediaProgress.mediaItemId}\" is more recent than local (server lastUpdate=${mediaProgress.lastUpdate}, local lastUpdate=${localMediaProgress.lastUpdate}). ${updateLogs.joinToString()}") + } + localMediaProgress.updateFromServerMediaProgress(mediaProgress) - MediaEventManager.syncEvent(mediaProgress, "Sync on server connection") + + // Only report sync if progress changed + if (updateLogs.isNotEmpty()) { + MediaEventManager.syncEvent(mediaProgress, "Sync on server connection") + } DeviceManager.dbManager.saveLocalMediaProgress(localMediaProgress) numLocalMediaProgressUpdated++ } else if (localMediaProgress.lastUpdate > mediaProgress.lastUpdate && localMediaProgress.ebookLocation != null && localMediaProgress.ebookLocation != mediaProgress.ebookLocation) { diff --git a/layouts/default.vue b/layouts/default.vue index dff6e82a..09a9355b 100644 --- a/layouts/default.vue +++ b/layouts/default.vue @@ -317,7 +317,7 @@ export default { this.$socket.on('user_media_progress_updated', this.userMediaProgressUpdated) if (this.$store.state.isFirstLoad) { - AbsLogger.info({ tag: 'default', message: 'mounted: initializing first load' }) + AbsLogger.info({ tag: 'default', message: `mounted: initializing first load (${this.$platform} v${this.$config.version})` }) this.$store.commit('setIsFirstLoad', false) this.loadSavedSettings() @@ -330,7 +330,7 @@ export default { await this.$store.dispatch('setupNetworkListener') if (this.$store.state.user.serverConnectionConfig) { - AbsLogger.info({ tag: 'default', message: `mounted: Server connected, init libraries (ServerConfigName: ${this.$store.getters['user/getServerConfigName']})` }) + AbsLogger.info({ tag: 'default', message: `mounted: Server connected, init libraries (${this.$store.getters['user/getServerConfigName']})` }) await this.initLibraries() } else { AbsLogger.info({ tag: 'default', message: `mounted: Server not connected, attempt connection` }) diff --git a/pages/logs.vue b/pages/logs.vue index 329baa36..2933393d 100644 --- a/pages/logs.vue +++ b/pages/logs.vue @@ -117,7 +117,7 @@ export default { const base64Data = Buffer.from(this.getLogsString()).toString('base64') FileSharer.share({ - filename: `audiobookshelf_logs.txt`, + filename: `abs_logs_${this.$platform}_${this.$config.version}.txt`, contentType: 'text/plain', base64Data }).catch((error) => { @@ -156,7 +156,19 @@ export default { } }, mounted() { + AbsLogger.addListener('onLog', (log) => { + log.maskedMessage = this.maskLogMessage(log.message) + this.logs.push(log) + this.logs.sort((a, b) => a.timestamp - b.timestamp) + + this.$nextTick(() => { + this.scrollToBottom() + }) + }) this.loadLogs() + }, + beforeDestroy() { + AbsLogger.removeAllListeners() } } diff --git a/plugins/capacitor/AbsAudioPlayer.js b/plugins/capacitor/AbsAudioPlayer.js index cec8f6c7..43326165 100644 --- a/plugins/capacitor/AbsAudioPlayer.js +++ b/plugins/capacitor/AbsAudioPlayer.js @@ -1,4 +1,5 @@ import { registerPlugin, WebPlugin } from '@capacitor/core' +import { AbsLogger } from '@/plugins/capacitor' import { nanoid } from 'nanoid' const { PlayerState } = require('../constants') @@ -88,6 +89,9 @@ class AbsAudioPlayerWeb extends WebPlugin { return } + // For testing onLog events in web while on the logs page + AbsLogger.info({ tag: 'AbsAudioPlayer', message: 'playPause' }) + if (this.player.paused) this.player.play() else this.player.pause() return { diff --git a/plugins/capacitor/AbsLogger.js b/plugins/capacitor/AbsLogger.js index c910b38b..be65787a 100644 --- a/plugins/capacitor/AbsLogger.js +++ b/plugins/capacitor/AbsLogger.js @@ -8,15 +8,18 @@ class AbsLoggerWeb extends WebPlugin { } saveLog(level, tag, message) { - this.logs.push({ + const log = { id: Math.random().toString(36).substring(2, 15), tag: tag, timestamp: Date.now(), level: level, message: message - }) + } + this.logs.push(log) + this.notifyListeners('onLog', log) } + // PluginMethod async info(data) { if (data?.message) { this.saveLog('info', data.tag || '', data.message) @@ -24,6 +27,7 @@ class AbsLoggerWeb extends WebPlugin { } } + // PluginMethod async error(data) { if (data?.message) { this.saveLog('error', data.tag || '', data.message) @@ -31,12 +35,14 @@ class AbsLoggerWeb extends WebPlugin { } } + // PluginMethod async getAllLogs() { return { value: this.logs } } + // PluginMethod async clearLogs() { this.logs = [] }