mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-09 05:18:40 +02:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bad8d10a18 | ||
|
|
a4412da3ed | ||
|
|
870774b408 | ||
|
|
d7dcaa22a6 | ||
|
|
cc0b2943dd | ||
|
|
50ee0b2265 |
@@ -29,8 +29,8 @@ android {
|
|||||||
applicationId "com.audiobookshelf.app"
|
applicationId "com.audiobookshelf.app"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 78
|
versionCode 80
|
||||||
versionName "0.9.48-beta"
|
versionName "0.9.49-beta"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||||
|
|||||||
@@ -26,4 +26,9 @@ object DeviceManager {
|
|||||||
fun getBase64Id(id:String):String {
|
fun getBase64Id(id:String):String {
|
||||||
return android.util.Base64.encodeToString(id.toByteArray(), android.util.Base64.URL_SAFE or android.util.Base64.NO_WRAP)
|
return android.util.Base64.encodeToString(id.toByteArray(), android.util.Base64.URL_SAFE or android.util.Base64.NO_WRAP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getServerConnectionConfig(id:String?):ServerConnectionConfig? {
|
||||||
|
if (id == null) return null
|
||||||
|
return deviceData.serverConnectionConfigs.find { it.id == id }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ class FolderScanner(var ctx: Context) {
|
|||||||
var coverContentUrl:String? = null
|
var coverContentUrl:String? = null
|
||||||
var coverAbsolutePath:String? = null
|
var coverAbsolutePath:String? = null
|
||||||
|
|
||||||
val filesInFolder = itemFolder.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*", "video/mp4"))
|
val filesInFolder = itemFolder.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*", "video/mp4", "application/octet-stream"))
|
||||||
|
|
||||||
val existingLocalFilesRemoved = existingLocalFiles.filter { elf ->
|
val existingLocalFilesRemoved = existingLocalFiles.filter { elf ->
|
||||||
filesInFolder.find { fif -> DeviceManager.getBase64Id(fif.id) == elf.id } == null // File was not found in media item folder
|
filesInFolder.find { fif -> DeviceManager.getBase64Id(fif.id) == elf.id } == null // File was not found in media item folder
|
||||||
@@ -248,7 +248,7 @@ class FolderScanner(var ctx: Context) {
|
|||||||
|
|
||||||
val localLibraryItemId = getLocalLibraryItemId(itemFolderId)
|
val localLibraryItemId = getLocalLibraryItemId(itemFolderId)
|
||||||
Log.d(tag, "scanDownloadItem starting for ${downloadItem.itemFolderPath} | ${df.uri} | Item Folder Id:$itemFolderId | LLI Id:$localLibraryItemId")
|
Log.d(tag, "scanDownloadItem starting for ${downloadItem.itemFolderPath} | ${df.uri} | Item Folder Id:$itemFolderId | LLI Id:$localLibraryItemId")
|
||||||
|
|
||||||
// Search for files in media item folder
|
// Search for files in media item folder
|
||||||
// m4b files showing as mimeType application/octet-stream on Android 10 and earlier see #154
|
// m4b files showing as mimeType application/octet-stream on Android 10 and earlier see #154
|
||||||
val filesFound = df.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*", "video/mp4", "application/octet-stream"))
|
val filesFound = df.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*", "video/mp4", "application/octet-stream"))
|
||||||
@@ -378,7 +378,7 @@ class FolderScanner(var ctx: Context) {
|
|||||||
var wasUpdated = false
|
var wasUpdated = false
|
||||||
|
|
||||||
// Search for files in media item folder
|
// Search for files in media item folder
|
||||||
val filesFound = df.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*", "video/mp4"))
|
val filesFound = df.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*", "video/mp4", "application/octet-stream"))
|
||||||
Log.d(tag, "scanLocalLibraryItem ${filesFound.size} files found in ${localLibraryItem.absolutePath}")
|
Log.d(tag, "scanLocalLibraryItem ${filesFound.size} files found in ${localLibraryItem.absolutePath}")
|
||||||
|
|
||||||
filesFound.forEach {
|
filesFound.forEach {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import com.audiobookshelf.app.data.PlayerState
|
|||||||
import com.google.android.exoplayer2.PlaybackException
|
import com.google.android.exoplayer2.PlaybackException
|
||||||
import com.google.android.exoplayer2.Player
|
import com.google.android.exoplayer2.Player
|
||||||
|
|
||||||
const val PAUSE_LEN_BEFORE_RECHECK = 60000 // 1 minute
|
const val PAUSE_LEN_BEFORE_RECHECK = 30000 // 30 seconds
|
||||||
|
|
||||||
class PlayerListener(var playerNotificationService:PlayerNotificationService) : Player.Listener {
|
class PlayerListener(var playerNotificationService:PlayerNotificationService) : Player.Listener {
|
||||||
var tag = "PlayerListener"
|
var tag = "PlayerListener"
|
||||||
@@ -85,7 +85,8 @@ class PlayerListener(var playerNotificationService:PlayerNotificationService) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if playback session still exists or sync media progress if updated
|
// Check if playback session still exists or sync media progress if updated
|
||||||
if (lastPauseTime > PAUSE_LEN_BEFORE_RECHECK) {
|
val pauseLength: Long = System.currentTimeMillis() - lastPauseTime
|
||||||
|
if (pauseLength > PAUSE_LEN_BEFORE_RECHECK) {
|
||||||
val shouldCarryOn = playerNotificationService.checkCurrentSessionProgress()
|
val shouldCarryOn = playerNotificationService.checkCurrentSessionProgress()
|
||||||
if (!shouldCarryOn) return
|
if (!shouldCarryOn) return
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-2
@@ -509,10 +509,19 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (playbackSession.isLocal) {
|
if (playbackSession.isLocal) {
|
||||||
|
|
||||||
|
// Make sure this connection config exists
|
||||||
|
val serverConnectionConfig = DeviceManager.getServerConnectionConfig(playbackSession.serverConnectionConfigId)
|
||||||
|
if (serverConnectionConfig == null) {
|
||||||
|
Log.d(tag, "checkCurrentSessionProgress: Local library item server connection config is not saved ${playbackSession.serverConnectionConfigId}")
|
||||||
|
return true // carry on
|
||||||
|
}
|
||||||
|
|
||||||
// Local playback session check if server has updated media progress
|
// Local playback session check if server has updated media progress
|
||||||
Log.d(tag, "checkCurrentSessionProgress: Checking if local media progress was updated on server")
|
Log.d(tag, "checkCurrentSessionProgress: Checking if local media progress was updated on server")
|
||||||
apiHandler.getMediaProgress(playbackSession.libraryItemId!!, playbackSession.episodeId) { mediaProgress ->
|
apiHandler.getMediaProgress(playbackSession.libraryItemId!!, playbackSession.episodeId, serverConnectionConfig) { mediaProgress ->
|
||||||
if (mediaProgress.lastUpdate > playbackSession.updatedAt && mediaProgress.currentTime != playbackSession.currentTime) {
|
|
||||||
|
if (mediaProgress != null && mediaProgress.lastUpdate > playbackSession.updatedAt && mediaProgress.currentTime != playbackSession.currentTime) {
|
||||||
Log.d(tag, "checkCurrentSessionProgress: Media progress was updated since last play time updating from ${playbackSession.currentTime} to ${mediaProgress.currentTime}")
|
Log.d(tag, "checkCurrentSessionProgress: Media progress was updated since last play time updating from ${playbackSession.currentTime} to ${mediaProgress.currentTime}")
|
||||||
mediaProgressSyncer.syncFromServerProgress(mediaProgress)
|
mediaProgressSyncer.syncFromServerProgress(mediaProgress)
|
||||||
|
|
||||||
|
|||||||
@@ -87,13 +87,15 @@ class ApiHandler(var ctx:Context) {
|
|||||||
override fun onFailure(call: Call, e: IOException) {
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
Log.d(tag, "FAILURE TO CONNECT")
|
Log.d(tag, "FAILURE TO CONNECT")
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
cb(JSObject())
|
|
||||||
|
val jsobj = JSObject()
|
||||||
|
jsobj.put("error", "Failed to connect")
|
||||||
|
cb(jsobj)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResponse(call: Call, response: Response) {
|
override fun onResponse(call: Call, response: Response) {
|
||||||
response.use {
|
response.use {
|
||||||
if (!it.isSuccessful) {
|
if (!it.isSuccessful) {
|
||||||
// throw IOException("Unexpected code $response")
|
|
||||||
val jsobj = JSObject()
|
val jsobj = JSObject()
|
||||||
jsobj.put("error", "Unexpected code $response")
|
jsobj.put("error", "Unexpected code $response")
|
||||||
cb(jsobj)
|
cb(jsobj)
|
||||||
@@ -269,11 +271,18 @@ class ApiHandler(var ctx:Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMediaProgress(libraryItemId:String, episodeId:String?, cb: (MediaProgress) -> Unit) {
|
fun getMediaProgress(libraryItemId:String, episodeId:String?, serverConnectionConfig:ServerConnectionConfig?, cb: (MediaProgress?) -> Unit) {
|
||||||
val endpoint = if(episodeId.isNullOrEmpty()) "/api/me/progress/$libraryItemId" else "/api/me/progress/$libraryItemId/$episodeId"
|
val endpoint = if(episodeId.isNullOrEmpty()) "/api/me/progress/$libraryItemId" else "/api/me/progress/$libraryItemId/$episodeId"
|
||||||
getRequest(endpoint, null, null) {
|
|
||||||
val progress = jacksonMapper.readValue<MediaProgress>(it.toString())
|
// TODO: Using ping client here allows for shorter timeout (3 seconds), maybe rename or make diff client for requests requiring quicker response
|
||||||
cb(progress)
|
getRequest(endpoint, pingClient, serverConnectionConfig) {
|
||||||
|
if (it.has("error")) {
|
||||||
|
Log.e(tag, "getMediaProgress: Failed to get progress")
|
||||||
|
cb(null)
|
||||||
|
} else {
|
||||||
|
val progress = jacksonMapper.readValue<MediaProgress>(it.toString())
|
||||||
|
cb(progress)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -122,11 +122,15 @@ export default {
|
|||||||
this.show = false
|
this.show = false
|
||||||
},
|
},
|
||||||
async logout() {
|
async logout() {
|
||||||
await this.$axios.$post('/logout').catch((error) => {
|
if (this.user) {
|
||||||
console.error(error)
|
await this.$axios.$post('/logout').catch((error) => {
|
||||||
})
|
console.error(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this.$socket.logout()
|
this.$socket.logout()
|
||||||
await this.$db.logout()
|
await this.$db.logout()
|
||||||
|
this.$localStore.removeLastLibraryId()
|
||||||
this.$store.commit('user/logout')
|
this.$store.commit('user/logout')
|
||||||
this.$router.push('/connect')
|
this.$router.push('/connect')
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ export default {
|
|||||||
this.resetEntities()
|
this.resetEntities()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
libraryChanged(libid) {
|
libraryChanged() {
|
||||||
if (this.hasFilter) {
|
if (this.hasFilter) {
|
||||||
this.clearFilter()
|
this.clearFilter()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export default {
|
|||||||
async clickedOption(lib) {
|
async clickedOption(lib) {
|
||||||
this.show = false
|
this.show = false
|
||||||
await this.$store.dispatch('libraries/fetch', lib.id)
|
await this.$store.dispatch('libraries/fetch', lib.id)
|
||||||
this.$eventBus.$emit('library-changed', lib.id)
|
this.$eventBus.$emit('library-changed')
|
||||||
this.$localStore.setLastLibraryId(lib.id)
|
this.$localStore.setLastLibraryId(lib.id)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -170,8 +170,8 @@ export default {
|
|||||||
this.inittingLibraries = true
|
this.inittingLibraries = true
|
||||||
await this.$store.dispatch('libraries/load')
|
await this.$store.dispatch('libraries/load')
|
||||||
console.log(`[default] initLibraries loaded ${this.currentLibraryId}`)
|
console.log(`[default] initLibraries loaded ${this.currentLibraryId}`)
|
||||||
|
await this.$store.dispatch('libraries/fetch', this.currentLibraryId)
|
||||||
this.$eventBus.$emit('library-changed')
|
this.$eventBus.$emit('library-changed')
|
||||||
this.$store.dispatch('libraries/fetch', this.currentLibraryId)
|
|
||||||
this.inittingLibraries = false
|
this.inittingLibraries = false
|
||||||
},
|
},
|
||||||
async syncLocalMediaProgress() {
|
async syncLocalMediaProgress() {
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf-app",
|
"name": "audiobookshelf-app",
|
||||||
"version": "0.9.48-beta",
|
"version": "0.9.49-beta",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf-app",
|
"name": "audiobookshelf-app",
|
||||||
"version": "0.9.48-beta",
|
"version": "0.9.49-beta",
|
||||||
"author": "advplyr",
|
"author": "advplyr",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nuxt --hostname 0.0.0.0 --port 1337",
|
"dev": "nuxt --hostname 0.0.0.0 --port 1337",
|
||||||
|
|||||||
+10
-4
@@ -53,10 +53,16 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async logout() {
|
async logout() {
|
||||||
await this.$axios.$post('/logout').catch((error) => {
|
if (this.user) {
|
||||||
console.error(error)
|
await this.$axios.$post('/logout').catch((error) => {
|
||||||
})
|
console.error(error)
|
||||||
this.$server.logout()
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$socket.logout()
|
||||||
|
await this.$db.logout()
|
||||||
|
this.$localStore.removeLastLibraryId()
|
||||||
|
this.$store.commit('user/logout')
|
||||||
this.$router.push('/connect')
|
this.$router.push('/connect')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,10 +4,6 @@
|
|||||||
<home-bookshelf-toolbar v-show="!isHome" />
|
<home-bookshelf-toolbar v-show="!isHome" />
|
||||||
<div id="bookshelf-wrapper" class="main-content overflow-y-auto overflow-x-hidden relative" :class="isHome ? 'home-page' : ''">
|
<div id="bookshelf-wrapper" class="main-content overflow-y-auto overflow-x-hidden relative" :class="isHome ? 'home-page' : ''">
|
||||||
<nuxt-child />
|
<nuxt-child />
|
||||||
|
|
||||||
<!-- <div v-if="isLoading" class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
|
|
||||||
<ui-loading-indicator />
|
|
||||||
</div>-->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ export default {
|
|||||||
}
|
}
|
||||||
this.loading = false
|
this.loading = false
|
||||||
},
|
},
|
||||||
async libraryChanged(libid) {
|
async libraryChanged() {
|
||||||
if (this.currentLibraryId) {
|
if (this.currentLibraryId) {
|
||||||
await this.fetchCategories()
|
await this.fetchCategories()
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -71,7 +71,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="isLocal" class="flex mt-4">
|
<div v-if="isLocal" class="flex mt-4">
|
||||||
<ui-btn color="success" :disabled="isPlaying" class="flex items-center justify-center flex-grow mr-2" :padding-x="4" @click="playClick">
|
<ui-btn v-if="showPlay" color="success" :disabled="isPlaying" class="flex items-center justify-center flex-grow mr-2" :padding-x="4" @click="playClick">
|
||||||
<span v-show="!isPlaying" class="material-icons">play_arrow</span>
|
<span v-show="!isPlaying" class="material-icons">play_arrow</span>
|
||||||
<span class="px-1 text-sm">{{ isPlaying ? 'Playing' : 'Play' }}</span>
|
<span class="px-1 text-sm">{{ isPlaying ? 'Playing' : 'Play' }}</span>
|
||||||
</ui-btn>
|
</ui-btn>
|
||||||
|
|||||||
+10
-1
@@ -83,7 +83,16 @@ class LocalStorage {
|
|||||||
await Storage.set({ key: 'lastLibraryId', value: libraryId })
|
await Storage.set({ key: 'lastLibraryId', value: libraryId })
|
||||||
console.log('[LocalStorage] Set Last Library Id', libraryId)
|
console.log('[LocalStorage] Set Last Library Id', libraryId)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[LocalStorage] Failed to set current library', error)
|
console.error('[LocalStorage] Failed to set last library id', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeLastLibraryId() {
|
||||||
|
try {
|
||||||
|
await Storage.remove({ key: 'lastLibraryId' })
|
||||||
|
console.log('[LocalStorage] Remove Last Library Id')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[LocalStorage] Failed to remove last library id', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -22,13 +22,16 @@ export const getters = {
|
|||||||
if (media.coverPath.startsWith('http:') || media.coverPath.startsWith('https:')) return media.coverPath
|
if (media.coverPath.startsWith('http:') || media.coverPath.startsWith('https:')) return media.coverPath
|
||||||
|
|
||||||
var userToken = rootGetters['user/getToken']
|
var userToken = rootGetters['user/getToken']
|
||||||
|
var serverAddress = rootGetters['user/getServerAddress']
|
||||||
|
if (!userToken || !serverAddress) return placeholder
|
||||||
|
|
||||||
var lastUpdate = libraryItem.updatedAt || Date.now()
|
var lastUpdate = libraryItem.updatedAt || Date.now()
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') { // Testing
|
if (process.env.NODE_ENV !== 'production') { // Testing
|
||||||
// return `http://localhost:3333/api/items/${libraryItem.id}/cover?token=${userToken}&ts=${lastUpdate}`
|
// return `http://localhost:3333/api/items/${libraryItem.id}/cover?token=${userToken}&ts=${lastUpdate}`
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = new URL(`/api/items/${libraryItem.id}/cover`, rootGetters['user/getServerAddress'])
|
var url = new URL(`/api/items/${libraryItem.id}/cover`, serverAddress)
|
||||||
return `${url}?token=${userToken}&ts=${lastUpdate}`
|
return `${url}?token=${userToken}&ts=${lastUpdate}`
|
||||||
},
|
},
|
||||||
getLocalMediaProgressById: (state) => (localLibraryItemId, episodeId = null) => {
|
getLocalMediaProgressById: (state) => (localLibraryItemId, episodeId = null) => {
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ export const mutations = {
|
|||||||
},
|
},
|
||||||
reset(state) {
|
reset(state) {
|
||||||
state.lastLoad = 0
|
state.lastLoad = 0
|
||||||
|
state.currentLibraryId = null
|
||||||
state.libraries = []
|
state.libraries = []
|
||||||
},
|
},
|
||||||
setCurrentLibrary(state, val) {
|
setCurrentLibrary(state, val) {
|
||||||
|
|||||||
Reference in New Issue
Block a user