mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-07-13 23:44:47 +02:00
Fix: more sync issues, Add: link audiobooks with folder names in selected download folder
This commit is contained in:
parent
3b6e7e1ce2
commit
f40e971b90
15 changed files with 95 additions and 293 deletions
|
@ -13,8 +13,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 41
|
versionCode 42
|
||||||
versionName "0.9.22-beta"
|
versionName "0.9.23-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.
|
||||||
|
|
|
@ -394,6 +394,7 @@ export default {
|
||||||
this.isPaused = !data.isPlaying
|
this.isPaused = !data.isPlaying
|
||||||
this.currentTime = Number((data.currentTime / 1000).toFixed(2))
|
this.currentTime = Number((data.currentTime / 1000).toFixed(2))
|
||||||
this.totalDuration = Number((data.duration / 1000).toFixed(2))
|
this.totalDuration = Number((data.duration / 1000).toFixed(2))
|
||||||
|
this.$emit('setTotalDuration', this.totalDuration)
|
||||||
this.timeupdate()
|
this.timeupdate()
|
||||||
if (data.isPlaying) {
|
if (data.isPlaying) {
|
||||||
console.log('playing - continue')
|
console.log('playing - continue')
|
||||||
|
@ -496,6 +497,7 @@ export default {
|
||||||
onMetadata(data) {
|
onMetadata(data) {
|
||||||
console.log('Native Audio On Metadata', JSON.stringify(data))
|
console.log('Native Audio On Metadata', JSON.stringify(data))
|
||||||
this.totalDuration = Number((data.duration / 1000).toFixed(2))
|
this.totalDuration = Number((data.duration / 1000).toFixed(2))
|
||||||
|
this.$emit('setTotalDuration', this.totalDuration)
|
||||||
this.currentTime = Number((data.currentTime / 1000).toFixed(2))
|
this.currentTime = Number((data.currentTime / 1000).toFixed(2))
|
||||||
this.stateName = data.stateName
|
this.stateName = data.stateName
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
:sleep-timeout-current-time="sleepTimeoutCurrentTime"
|
:sleep-timeout-current-time="sleepTimeoutCurrentTime"
|
||||||
@close="cancelStream"
|
@close="cancelStream"
|
||||||
@sync="sync"
|
@sync="sync"
|
||||||
|
@setTotalDuration="setTotalDuration"
|
||||||
@selectPlaybackSpeed="showPlaybackSpeedModal = true"
|
@selectPlaybackSpeed="showPlaybackSpeedModal = true"
|
||||||
@selectChapter="clickChapterBtn"
|
@selectChapter="clickChapterBtn"
|
||||||
@showSleepTimer="showSleepTimer"
|
@showSleepTimer="showSleepTimer"
|
||||||
|
@ -49,7 +50,8 @@ export default {
|
||||||
sleepTimerEndOfChapterTime: 0,
|
sleepTimerEndOfChapterTime: 0,
|
||||||
onSleepTimerEndedListener: null,
|
onSleepTimerEndedListener: null,
|
||||||
sleepInterval: null,
|
sleepInterval: null,
|
||||||
currentEndOfChapterTime: 0
|
currentEndOfChapterTime: 0,
|
||||||
|
totalDuration: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -66,7 +68,7 @@ export default {
|
||||||
},
|
},
|
||||||
userAudiobook() {
|
userAudiobook() {
|
||||||
if (!this.audiobookId) return
|
if (!this.audiobookId) return
|
||||||
return this.$store.getters['user/getMostRecentUserAudiobookData'](this.audiobookId)
|
return this.$store.getters['user/getUserAudiobookData'](this.audiobookId)
|
||||||
},
|
},
|
||||||
bookmarks() {
|
bookmarks() {
|
||||||
if (!this.userAudiobook) return []
|
if (!this.userAudiobook) return []
|
||||||
|
@ -274,7 +276,7 @@ export default {
|
||||||
audiobookId: syncData.audiobookId,
|
audiobookId: syncData.audiobookId,
|
||||||
currentTime: syncData.currentTime,
|
currentTime: syncData.currentTime,
|
||||||
totalDuration: syncData.totalDuration,
|
totalDuration: syncData.totalDuration,
|
||||||
progress: Number((syncData.currentTime / syncData.totalDuration).toFixed(3)),
|
progress: syncData.totalDuration ? Number((syncData.currentTime / syncData.totalDuration).toFixed(3)) : 0,
|
||||||
lastUpdate: Date.now(),
|
lastUpdate: Date.now(),
|
||||||
isRead: false
|
isRead: false
|
||||||
}
|
}
|
||||||
|
@ -283,45 +285,21 @@ export default {
|
||||||
this.$server.socket.emit('progress_update', progressUpdate)
|
this.$server.socket.emit('progress_update', progressUpdate)
|
||||||
} else {
|
} else {
|
||||||
this.$store.dispatch('user/updateUserAudiobookData', progressUpdate)
|
this.$store.dispatch('user/updateUserAudiobookData', progressUpdate)
|
||||||
// this.$localStore.updateUserAudiobookData(progressUpdate).then(() => {
|
|
||||||
// console.log('Updated user audiobook progress', currentTime)
|
|
||||||
// })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateTime(currentTime) {
|
updateTime(currentTime) {
|
||||||
this.currentTime = currentTime
|
this.sync({
|
||||||
|
currentTime,
|
||||||
var diff = currentTime - this.lastProgressTimeUpdate
|
audiobookId: this.audiobookId,
|
||||||
|
streamId: this.stream ? this.stream.id : null,
|
||||||
if (diff > 4 || diff < 0) {
|
timeListened: 0,
|
||||||
this.lastProgressTimeUpdate = currentTime
|
totalDuration: this.totalDuration || 0
|
||||||
if (this.stream) {
|
})
|
||||||
var updatePayload = {
|
},
|
||||||
currentTime,
|
setTotalDuration(duration) {
|
||||||
streamId: this.stream.id
|
this.totalDuration = duration
|
||||||
}
|
|
||||||
this.$server.socket.emit('stream_update', updatePayload)
|
|
||||||
} else if (this.download) {
|
|
||||||
var progressUpdate = {
|
|
||||||
audiobookId: this.download.id,
|
|
||||||
currentTime: currentTime,
|
|
||||||
totalDuration: this.download.audiobook.duration,
|
|
||||||
progress: Number((currentTime / this.download.audiobook.duration).toFixed(3)),
|
|
||||||
lastUpdate: Date.now(),
|
|
||||||
isRead: false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.$server.connected) {
|
|
||||||
this.$server.socket.emit('progress_update', progressUpdate)
|
|
||||||
}
|
|
||||||
this.$localStore.updateUserAudiobookData(progressUpdate).then(() => {
|
|
||||||
console.log('Updated user audiobook progress', currentTime)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
closeStream() {},
|
|
||||||
streamClosed(audiobookId) {
|
streamClosed(audiobookId) {
|
||||||
console.log('Stream Closed')
|
console.log('Stream Closed')
|
||||||
if (this.stream.audiobook.id === audiobookId || audiobookId === 'n/a') {
|
if (this.stream.audiobook.id === audiobookId || audiobookId === 'n/a') {
|
||||||
|
@ -349,7 +327,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getDownloadStartTime() {
|
async getDownloadStartTime() {
|
||||||
var userAudiobook = await this.$localStore.getMostRecentUserAudiobook(this.audiobookId)
|
var userAudiobook = this.$store.getters['user/getUserAudiobookData'](this.audiobookId)
|
||||||
if (!userAudiobook) {
|
if (!userAudiobook) {
|
||||||
console.log('[StreamContainer] getDownloadStartTime no user audiobook record found')
|
console.log('[StreamContainer] getDownloadStartTime no user audiobook record found')
|
||||||
return 0
|
return 0
|
||||||
|
@ -505,7 +483,6 @@ export default {
|
||||||
|
|
||||||
this.setListeners()
|
this.setListeners()
|
||||||
this.$store.commit('user/addSettingsListener', { id: 'streamContainer', meth: this.settingsUpdated })
|
this.$store.commit('user/addSettingsListener', { id: 'streamContainer', meth: this.settingsUpdated })
|
||||||
// this.$store.commit('user/addUserAudiobookListener', { id: 'streamContainer', meth: this.userAudiobooksUpdated })
|
|
||||||
this.$store.commit('setStreamListener', this.streamUpdated)
|
this.$store.commit('setStreamListener', this.streamUpdated)
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
@ -520,7 +497,6 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.commit('user/removeSettingsListener', 'streamContainer')
|
this.$store.commit('user/removeSettingsListener', 'streamContainer')
|
||||||
// this.$store.commit('user/removeUserAudiobookListener', 'streamContainer')
|
|
||||||
this.$store.commit('removeStreamListener')
|
this.$store.commit('removeStreamListener')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ export default {
|
||||||
return this.audiobook.id
|
return this.audiobook.id
|
||||||
},
|
},
|
||||||
mostRecentUserProgress() {
|
mostRecentUserProgress() {
|
||||||
return this.$store.getters['user/getMostRecentUserAudiobookData'](this.audiobookId)
|
return this.$store.getters['user/getUserAudiobookData'](this.audiobookId)
|
||||||
},
|
},
|
||||||
userProgressPercent() {
|
userProgressPercent() {
|
||||||
return this.mostRecentUserProgress ? this.mostRecentUserProgress.progress || 0 : 0
|
return this.mostRecentUserProgress ? this.mostRecentUserProgress.progress || 0 : 0
|
||||||
|
|
|
@ -82,7 +82,7 @@ export default {
|
||||||
return this.$store.getters['user/getUserSetting']('mobileOrderBy')
|
return this.$store.getters['user/getUserSetting']('mobileOrderBy')
|
||||||
},
|
},
|
||||||
mostRecentUserProgress() {
|
mostRecentUserProgress() {
|
||||||
return this.$store.getters['user/getMostRecentUserAudiobookData'](this.audiobookId)
|
return this.$store.getters['user/getUserAudiobookData'](this.audiobookId)
|
||||||
},
|
},
|
||||||
userProgressPercent() {
|
userProgressPercent() {
|
||||||
return this.mostRecentUserProgress ? this.mostRecentUserProgress.progress || 0 : 0
|
return this.mostRecentUserProgress ? this.mostRecentUserProgress.progress || 0 : 0
|
||||||
|
|
|
@ -158,7 +158,7 @@ export default {
|
||||||
return this.$store.state.downloads.downloads
|
return this.$store.state.downloads.downloads
|
||||||
},
|
},
|
||||||
mediaScanResults() {
|
mediaScanResults() {
|
||||||
return this.$store.state.mediaScanResults
|
return this.$store.state.downloads.mediaScanResults
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -210,7 +210,7 @@ export default {
|
||||||
}
|
}
|
||||||
return sr
|
return sr
|
||||||
})
|
})
|
||||||
this.$store.commit('setMediaScanResults', searchResults)
|
this.$store.commit('downloads/setMediaScanResults', searchResults)
|
||||||
} else {
|
} else {
|
||||||
this.$toast.warning('No audio or image files found')
|
this.$toast.warning('No audio or image files found')
|
||||||
}
|
}
|
||||||
|
@ -218,7 +218,7 @@ export default {
|
||||||
},
|
},
|
||||||
async resetFolder() {
|
async resetFolder() {
|
||||||
await this.$localStore.setDownloadFolder(null)
|
await this.$localStore.setDownloadFolder(null)
|
||||||
this.$store.commit('setMediaScanResults', {})
|
this.$store.commit('downloads/setMediaScanResults', {})
|
||||||
this.$toast.info('Unlinked Folder')
|
this.$toast.info('Unlinked Folder')
|
||||||
},
|
},
|
||||||
updateDownloadProgress({ audiobookId, progress }) {
|
updateDownloadProgress({ audiobookId, progress }) {
|
||||||
|
|
|
@ -17,7 +17,6 @@ import { Capacitor } from '@capacitor/core'
|
||||||
import { Network } from '@capacitor/network'
|
import { Network } from '@capacitor/network'
|
||||||
import { AppUpdate } from '@robingenz/capacitor-app-update'
|
import { AppUpdate } from '@robingenz/capacitor-app-update'
|
||||||
import AudioDownloader from '@/plugins/audio-downloader'
|
import AudioDownloader from '@/plugins/audio-downloader'
|
||||||
import MyNativeAudio from '@/plugins/my-native-audio'
|
|
||||||
import StorageManager from '@/plugins/storage-manager'
|
import StorageManager from '@/plugins/storage-manager'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -47,7 +46,6 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
async connected(isConnected) {
|
async connected(isConnected) {
|
||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
// this.syncUserProgress()
|
|
||||||
console.log('[Default] Connected socket sync user ab data')
|
console.log('[Default] Connected socket sync user ab data')
|
||||||
this.$store.dispatch('user/syncUserAudiobookData')
|
this.$store.dispatch('user/syncUserAudiobookData')
|
||||||
|
|
||||||
|
@ -58,63 +56,11 @@ export default {
|
||||||
socketConnectionFailed(err) {
|
socketConnectionFailed(err) {
|
||||||
this.$toast.error('Socket connection error: ' + err.message)
|
this.$toast.error('Socket connection error: ' + err.message)
|
||||||
},
|
},
|
||||||
updateAudiobookProgressOnServer(audiobookProgress) {
|
|
||||||
if (this.$server.socket) {
|
|
||||||
console.log(`[PROGRESSSYNC] Updating AB Progress on server ${JSON.stringify(audiobookProgress)}`)
|
|
||||||
this.$server.socket.emit('progress_update', audiobookProgress)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
syncUserProgress() {
|
|
||||||
if (!this.$store.state.user.user) return
|
|
||||||
|
|
||||||
var userAudiobooks = this.$store.state.user.user.audiobooks
|
|
||||||
var localAudiobooks = this.$store.state.user.localUserAudiobooks
|
|
||||||
var localHasUpdates = false
|
|
||||||
|
|
||||||
// console.log('[PROGRESSSYNC] Starting Sync USER', JSON.stringify(userAudiobooks))
|
|
||||||
// console.log('[PROGRESSSYNC] Starting Sync LOCAL', JSON.stringify(localAudiobooks))
|
|
||||||
|
|
||||||
var newestLocal = { ...localAudiobooks }
|
|
||||||
for (const audiobookId in userAudiobooks) {
|
|
||||||
if (!audiobookId || !userAudiobooks[audiobookId] || audiobookId === 'undefined') {
|
|
||||||
console.error(`[PROGRESSSYNC] Invalid audiobookId ${audiobookId} - ${JSON.stringify(userAudiobooks[audiobookId])}`)
|
|
||||||
} else if (localAudiobooks[audiobookId]) {
|
|
||||||
if (localAudiobooks[audiobookId].lastUpdate > userAudiobooks[audiobookId].lastUpdate) {
|
|
||||||
// Local progress is more recent than user progress
|
|
||||||
this.updateAudiobookProgressOnServer(localAudiobooks[audiobookId])
|
|
||||||
} else if (localAudiobooks[audiobookId].lastUpdate < userAudiobooks[audiobookId].lastUpdate) {
|
|
||||||
// Server is more recent than local
|
|
||||||
newestLocal[audiobookId] = userAudiobooks[audiobookId]
|
|
||||||
// console.log('[PROGRESSSYNC] Server IS MORE RECENT for', audiobookId, JSON.stringify(newestLocal[audiobookId]))
|
|
||||||
localHasUpdates = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Not on local yet - store on local
|
|
||||||
newestLocal[audiobookId] = userAudiobooks[audiobookId]
|
|
||||||
// console.log('[PROGRESSSYNC] LOCAL Is NOT Stored YET for', audiobookId, JSON.stringify(newestLocal[audiobookId]))
|
|
||||||
localHasUpdates = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const audiobookId in localAudiobooks) {
|
|
||||||
if (!userAudiobooks[audiobookId]) {
|
|
||||||
// Local progress is not on server
|
|
||||||
this.updateAudiobookProgressOnServer(localAudiobooks[audiobookId])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localHasUpdates) {
|
|
||||||
// console.log('[PROGRESSSYNC] Local audiobook progress has updates from server')
|
|
||||||
this.$localStore.setAllAudiobookProgress(newestLocal)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
currentUserAudiobookUpdate({ id, data }) {
|
currentUserAudiobookUpdate({ id, data }) {
|
||||||
if (data) {
|
if (data) {
|
||||||
console.log(`Current User Audiobook Updated ${id} ${JSON.stringify(data)}`)
|
console.log(`Current User Audiobook Updated ${id} ${JSON.stringify(data)}`)
|
||||||
// this.$localStore.updateUserAudiobookData(data)
|
|
||||||
this.$sqlStore.setUserAudiobookData(data)
|
this.$sqlStore.setUserAudiobookData(data)
|
||||||
} else {
|
} else {
|
||||||
// this.$localStore.removeAudiobookProgress(id)
|
|
||||||
this.$sqlStore.removeUserAudiobookData(id)
|
this.$sqlStore.removeUserAudiobookData(id)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -131,16 +77,6 @@ export default {
|
||||||
await AppUpdate.openAppStore()
|
await AppUpdate.openAppStore()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// showUpdateToast(availableVersion, immediateUpdateAllowed) {
|
|
||||||
// var toastText = immediateUpdateAllowed ? `Click here to update` : `Click here to open app store`
|
|
||||||
// this.$toast.info(`Update is available for v${availableVersion}! ${toastText}`, {
|
|
||||||
// draggable: false,
|
|
||||||
// hideProgressBar: false,
|
|
||||||
// timeout: 10000,
|
|
||||||
// closeButton: false,
|
|
||||||
// onClick: this.clickUpdateToast()
|
|
||||||
// })
|
|
||||||
// },
|
|
||||||
async checkForUpdate() {
|
async checkForUpdate() {
|
||||||
console.log('Checking for app update')
|
console.log('Checking for app update')
|
||||||
const result = await AppUpdate.getAppUpdateInfo()
|
const result = await AppUpdate.getAppUpdateInfo()
|
||||||
|
@ -274,10 +210,9 @@ export default {
|
||||||
},
|
},
|
||||||
async syncDownloads(downloads, downloadFolder) {
|
async syncDownloads(downloads, downloadFolder) {
|
||||||
console.log('Syncing downloads ' + downloads.length)
|
console.log('Syncing downloads ' + downloads.length)
|
||||||
|
|
||||||
var mediaScanResults = await this.searchFolder(downloadFolder)
|
var mediaScanResults = await this.searchFolder(downloadFolder)
|
||||||
|
|
||||||
this.$store.commit('setMediaScanResults', mediaScanResults)
|
this.$store.commit('downloads/setMediaScanResults', mediaScanResults)
|
||||||
|
|
||||||
// Filter out media folders without any audio files
|
// Filter out media folders without any audio files
|
||||||
var mediaFolders = mediaScanResults.folders.filter((sr) => {
|
var mediaFolders = mediaScanResults.folders.filter((sr) => {
|
||||||
|
@ -308,42 +243,6 @@ export default {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// async onMediaLoaded(items) {
|
|
||||||
// var jsitems = JSON.parse(items)
|
|
||||||
// jsitems = jsitems.map((item) => {
|
|
||||||
// return {
|
|
||||||
// filename: item.name,
|
|
||||||
// size: item.size,
|
|
||||||
// contentUrl: item.uri,
|
|
||||||
// coverUrl: item.coverUrl || null
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
|
|
||||||
// var downloads = await this.$sqlStore.getAllDownloads()
|
|
||||||
|
|
||||||
// for (let i = 0; i < downloads.length; i++) {
|
|
||||||
// var download = downloads[i]
|
|
||||||
// var jsitem = jsitems.find((item) => item.contentUrl === download.contentUrl)
|
|
||||||
// if (!jsitem) {
|
|
||||||
// console.error('Removing download was not found', JSON.stringify(download))
|
|
||||||
// await this.$sqlStore.removeDownload(download.id)
|
|
||||||
// } else if (download.coverUrl && !jsitem.coverUrl) {
|
|
||||||
// console.error('Removing cover for download was not found')
|
|
||||||
// download.cover = null
|
|
||||||
// download.coverUrl = null
|
|
||||||
// download.size = jsitem.size || 0
|
|
||||||
// this.$store.commit('downloads/addUpdateDownload', download)
|
|
||||||
// this.$store.commit('audiobooks/addUpdate', download.audiobook)
|
|
||||||
// } else {
|
|
||||||
// download.size = jsitem.size || 0
|
|
||||||
// this.$store.commit('downloads/addUpdateDownload', download)
|
|
||||||
// this.$store.commit('audiobooks/addUpdate', download.audiobook)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// this.checkLoadCurrent()
|
|
||||||
// this.$store.dispatch('audiobooks/setNativeAudiobooks')
|
|
||||||
// },
|
|
||||||
async initMediaStore() {
|
async initMediaStore() {
|
||||||
// Request and setup listeners for media files on native
|
// Request and setup listeners for media files on native
|
||||||
AudioDownloader.addListener('onDownloadComplete', (data) => {
|
AudioDownloader.addListener('onDownloadComplete', (data) => {
|
||||||
|
@ -352,19 +251,14 @@ export default {
|
||||||
AudioDownloader.addListener('onDownloadFailed', (data) => {
|
AudioDownloader.addListener('onDownloadFailed', (data) => {
|
||||||
this.onDownloadFailed(data)
|
this.onDownloadFailed(data)
|
||||||
})
|
})
|
||||||
// AudioDownloader.addListener('onMediaLoaded', (data) => {
|
|
||||||
// this.onMediaLoaded(data.items)
|
|
||||||
// })
|
|
||||||
AudioDownloader.addListener('onDownloadProgress', (data) => {
|
AudioDownloader.addListener('onDownloadProgress', (data) => {
|
||||||
this.onDownloadProgress(data)
|
this.onDownloadProgress(data)
|
||||||
})
|
})
|
||||||
|
|
||||||
await this.$localStore.loadUserAudiobooks()
|
var downloads = (await this.$sqlStore.getAllDownloads()) || []
|
||||||
|
|
||||||
var downloads = await this.$sqlStore.getAllDownloads()
|
|
||||||
var downloadFolder = await this.$localStore.getDownloadFolder()
|
var downloadFolder = await this.$localStore.getDownloadFolder()
|
||||||
|
|
||||||
if (downloadFolder && downloads.length) {
|
if (downloadFolder) {
|
||||||
await this.syncDownloads(downloads, downloadFolder)
|
await this.syncDownloads(downloads, downloadFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,29 +267,6 @@ export default {
|
||||||
this.$store.commit('user/setSettings', userSavedSettings)
|
this.$store.commit('user/setSettings', userSavedSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (downloads.length) {
|
|
||||||
// var urls = downloads
|
|
||||||
// .map((d) => {
|
|
||||||
// return {
|
|
||||||
// contentUrl: d.contentUrl,
|
|
||||||
// coverUrl: d.coverUrl || '',
|
|
||||||
// storageId: d.storageId,
|
|
||||||
// basePath: d.basePath,
|
|
||||||
// coverBasePath: d.coverBasePath || ''
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// .filter((d) => {
|
|
||||||
// if (!d.contentUrl) {
|
|
||||||
// console.error('Invalid Download no Content URL', JSON.stringify(d))
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// return true
|
|
||||||
// })
|
|
||||||
// AudioDownloader.load({
|
|
||||||
// audiobookUrls: urls
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
var checkPermission = await StorageManager.checkStoragePermission()
|
var checkPermission = await StorageManager.checkStoragePermission()
|
||||||
console.log('Storage Permission is' + checkPermission.value)
|
console.log('Storage Permission is' + checkPermission.value)
|
||||||
if (!checkPermission.value) {
|
if (!checkPermission.value) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "audiobookshelf-app",
|
"name": "audiobookshelf-app",
|
||||||
"version": "v0.9.22-beta",
|
"version": "v0.9.23-beta",
|
||||||
"author": "advplyr",
|
"author": "advplyr",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nuxt --hostname localhost --port 1337",
|
"dev": "nuxt --hostname localhost --port 1337",
|
||||||
|
|
|
@ -19,7 +19,7 @@ export default {
|
||||||
},
|
},
|
||||||
booksWithUserAbData() {
|
booksWithUserAbData() {
|
||||||
var books = this.books.map((b) => {
|
var books = this.books.map((b) => {
|
||||||
var userAbData = this.$store.getters['user/getMostRecentUserAudiobookData'](b.id)
|
var userAbData = this.$store.getters['user/getUserAudiobookData'](b.id)
|
||||||
return { ...b, userAbData }
|
return { ...b, userAbData }
|
||||||
})
|
})
|
||||||
return books
|
return books
|
||||||
|
|
|
@ -130,7 +130,7 @@ export default {
|
||||||
return this.$store.state.downloads.downloads
|
return this.$store.state.downloads.downloads
|
||||||
},
|
},
|
||||||
mediaScanResults() {
|
mediaScanResults() {
|
||||||
return this.$store.state.mediaScanResults
|
return this.$store.state.downloads.mediaScanResults
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -156,7 +156,12 @@ export default {
|
||||||
|
|
||||||
await this.$localStore.setDownloadFolder(folderObj)
|
await this.$localStore.setDownloadFolder(folderObj)
|
||||||
|
|
||||||
this.searchFolder()
|
await this.searchFolder()
|
||||||
|
|
||||||
|
var audiobooks = this.$store.state.audiobooks.audiobooks || []
|
||||||
|
if (audiobooks.length) {
|
||||||
|
this.$store.dispatch('downloads/linkOrphanDownloads', audiobooks)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async searchFolder() {
|
async searchFolder() {
|
||||||
|
@ -175,7 +180,7 @@ export default {
|
||||||
}
|
}
|
||||||
return sr
|
return sr
|
||||||
})
|
})
|
||||||
this.$store.commit('setMediaScanResults', searchResults)
|
this.$store.commit('downloads/setMediaScanResults', searchResults)
|
||||||
} else {
|
} else {
|
||||||
this.$toast.warning('No audio or image files found')
|
this.$toast.warning('No audio or image files found')
|
||||||
}
|
}
|
||||||
|
@ -183,7 +188,7 @@ export default {
|
||||||
},
|
},
|
||||||
async resetFolder() {
|
async resetFolder() {
|
||||||
await this.$localStore.setDownloadFolder(null)
|
await this.$localStore.setDownloadFolder(null)
|
||||||
this.$store.commit('setMediaScanResults', {})
|
this.$store.commit('downloads/setMediaScanResults', {})
|
||||||
this.$toast.info('Unlinked Folder')
|
this.$toast.info('Unlinked Folder')
|
||||||
},
|
},
|
||||||
jumpToAudiobook(download) {
|
jumpToAudiobook(download) {
|
||||||
|
|
|
@ -6,80 +6,6 @@ class LocalStorage {
|
||||||
|
|
||||||
this.userAudiobooksLoaded = false
|
this.userAudiobooksLoaded = false
|
||||||
this.downloadFolder = null
|
this.downloadFolder = null
|
||||||
this.userAudiobooks = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMostRecentUserAudiobook(audiobookId) {
|
|
||||||
if (!this.userAudiobooksLoaded) {
|
|
||||||
await this.loadUserAudiobooks()
|
|
||||||
}
|
|
||||||
var local = this.getUserAudiobook(audiobookId)
|
|
||||||
var server = this.vuexStore.getters['user/getUserAudiobook'](audiobookId)
|
|
||||||
|
|
||||||
if (local && server) {
|
|
||||||
if (local.lastUpdate > server.lastUpdate) {
|
|
||||||
console.log('[LocalStorage] Most recent user audiobook is from LOCAL')
|
|
||||||
return local
|
|
||||||
}
|
|
||||||
console.log('[LocalStorage] Most recent user audiobook is from SERVER')
|
|
||||||
return server
|
|
||||||
} else if (local) {
|
|
||||||
console.log('[LocalStorage] Most recent user audiobook is from LOCAL')
|
|
||||||
return local
|
|
||||||
} else if (server) {
|
|
||||||
console.log('[LocalStorage] Most recent user audiobook is from SERVER')
|
|
||||||
return server
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadUserAudiobooks() {
|
|
||||||
try {
|
|
||||||
var val = (await Storage.get({ key: 'userAudiobooks' }) || {}).value || null
|
|
||||||
this.userAudiobooks = val ? JSON.parse(val) : {}
|
|
||||||
this.userAudiobooksLoaded = true
|
|
||||||
this.vuexStore.commit('user/setLocalUserAudiobooks', { ...this.userAudiobooks })
|
|
||||||
} catch (error) {
|
|
||||||
console.error('[LocalStorage] Failed to load user audiobooks', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveUserAudiobooks() {
|
|
||||||
try {
|
|
||||||
await Storage.set({ key: 'userAudiobooks', value: JSON.stringify(this.userAudiobooks) })
|
|
||||||
} catch (error) {
|
|
||||||
console.error('[LocalStorage] Failed to set user audiobooks', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async setAllAudiobookProgress(progresses) {
|
|
||||||
this.userAudiobooks = progresses
|
|
||||||
await this.saveUserAudiobooks()
|
|
||||||
|
|
||||||
this.vuexStore.commit('user/setLocalUserAudiobooks', { ...this.userAudiobooks })
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateUserAudiobookData(progressPayload) {
|
|
||||||
this.userAudiobooks[progressPayload.audiobookId] = {
|
|
||||||
...progressPayload
|
|
||||||
}
|
|
||||||
await this.saveUserAudiobooks()
|
|
||||||
|
|
||||||
this.vuexStore.commit('user/setUserAudiobooks', { ...this.userAudiobooks })
|
|
||||||
this.vuexStore.commit('user/setLocalUserAudiobooks', { ...this.userAudiobooks })
|
|
||||||
}
|
|
||||||
|
|
||||||
async removeAudiobookProgress(audiobookId) {
|
|
||||||
if (!this.userAudiobooks[audiobookId]) return
|
|
||||||
delete this.userAudiobooks[audiobookId]
|
|
||||||
await this.saveUserAudiobooks()
|
|
||||||
|
|
||||||
this.vuexStore.commit('user/setUserAudiobooks', { ...this.userAudiobooks })
|
|
||||||
this.vuexStore.commit('user/setLocalUserAudiobooks', { ...this.userAudiobooks })
|
|
||||||
}
|
|
||||||
|
|
||||||
getUserAudiobook(audiobookId) {
|
|
||||||
return this.userAudiobooks[audiobookId] || null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async setToken(token) {
|
async setToken(token) {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import MyNativeAudio from '@/plugins/my-native-audio'
|
|
||||||
import { sort } from '@/assets/fastSort'
|
import { sort } from '@/assets/fastSort'
|
||||||
import { decode } from '@/plugins/init.client'
|
import { decode } from '@/plugins/init.client'
|
||||||
|
|
||||||
|
@ -38,7 +37,7 @@ export const getters = {
|
||||||
else if (group === 'authors') filtered = filtered.filter(ab => ab.book && ab.book.author === filter)
|
else if (group === 'authors') filtered = filtered.filter(ab => ab.book && ab.book.author === filter)
|
||||||
else if (group === 'progress') {
|
else if (group === 'progress') {
|
||||||
filtered = filtered.filter(ab => {
|
filtered = filtered.filter(ab => {
|
||||||
var userAudiobook = rootGetters['user/getUserAudiobook'](ab.id)
|
var userAudiobook = rootGetters['user/getUserAudiobookData'](ab.id)
|
||||||
var isRead = userAudiobook && userAudiobook.isRead
|
var isRead = userAudiobook && userAudiobook.isRead
|
||||||
if (filter === 'Read' && isRead) return true
|
if (filter === 'Read' && isRead) return true
|
||||||
if (filter === 'Unread' && !isRead) return true
|
if (filter === 'Unread' && !isRead) return true
|
||||||
|
@ -57,7 +56,7 @@ export const getters = {
|
||||||
|
|
||||||
if (settings.mobileOrderBy === 'recent') {
|
if (settings.mobileOrderBy === 'recent') {
|
||||||
return sort(filtered)[direction]((ab) => {
|
return sort(filtered)[direction]((ab) => {
|
||||||
var abprogress = rootGetters['user/getMostRecentUserAudiobookData'](ab.id)
|
var abprogress = rootGetters['user/getUserAudiobookData'](ab.id)
|
||||||
if (!abprogress) return 0
|
if (!abprogress) return 0
|
||||||
return abprogress.lastUpdate
|
return abprogress.lastUpdate
|
||||||
})
|
})
|
||||||
|
@ -142,7 +141,7 @@ export const getters = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
load({ state, commit, rootState }) {
|
load({ state, commit, dispatch, rootState }) {
|
||||||
if (!rootState.user || !rootState.user.user) {
|
if (!rootState.user || !rootState.user.user) {
|
||||||
console.error('audiobooks/load - User not set')
|
console.error('audiobooks/load - User not set')
|
||||||
return false
|
return false
|
||||||
|
@ -169,6 +168,8 @@ export const actions = {
|
||||||
commit('set', data)
|
commit('set', data)
|
||||||
commit('setLastLoad')
|
commit('setLastLoad')
|
||||||
commit('setLoading', false)
|
commit('setLoading', false)
|
||||||
|
|
||||||
|
dispatch('downloads/linkOrphanDownloads', data, { root: true })
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Failed', error)
|
console.error('Failed', error)
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
import { Capacitor } from '@capacitor/core'
|
||||||
|
|
||||||
export const state = () => ({
|
export const state = () => ({
|
||||||
downloads: [],
|
downloads: [],
|
||||||
showModal: false
|
showModal: false,
|
||||||
|
mediaScanResults: {},
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getters = {
|
export const getters = {
|
||||||
|
@ -28,6 +31,48 @@ export const actions = {
|
||||||
ab.isPreparing = false
|
ab.isPreparing = false
|
||||||
commit('setDownload', ab)
|
commit('setDownload', ab)
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
linkOrphanDownloads({ state, commit }, audiobooks) {
|
||||||
|
if (!state.mediaScanResults || !state.mediaScanResults.folders) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log('Link orphan downloads', JSON.stringify(state.mediaScanResults.folders))
|
||||||
|
state.mediaScanResults.folders.forEach((folder) => {
|
||||||
|
if (!folder.files || !folder.files.length) return
|
||||||
|
|
||||||
|
console.log('Link orphan downloads check folder', folder.name)
|
||||||
|
|
||||||
|
var download = state.downloads.find(dl => dl.folderName === folder.name)
|
||||||
|
if (!download) {
|
||||||
|
var matchingAb = audiobooks.find(ab => ab.book.title === folder.name)
|
||||||
|
if (matchingAb) {
|
||||||
|
// Found matching download for ab
|
||||||
|
var audioFile = folder.files.find(f => f.isAudio)
|
||||||
|
if (!audioFile) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var coverImg = folder.files.find(f => !f.isAudio)
|
||||||
|
const downloadObj = {
|
||||||
|
id: matchingAb.id,
|
||||||
|
audiobook: { ...matchingAb },
|
||||||
|
contentUrl: audioFile.uri,
|
||||||
|
simplePath: audioFile.simplePath,
|
||||||
|
folderUrl: folder.uri,
|
||||||
|
folderName: folder.name,
|
||||||
|
storageType: '',
|
||||||
|
storageId: '',
|
||||||
|
basePath: '',
|
||||||
|
size: audioFile.size,
|
||||||
|
coverUrl: coverImg ? coverImg.uri : null,
|
||||||
|
cover: coverImg ? Capacitor.convertFileSrc(coverImg.uri) : null,
|
||||||
|
coverSize: coverImg ? coverImg.size : 0,
|
||||||
|
coverBasePath: ''
|
||||||
|
}
|
||||||
|
console.log('Linking orphan download: ' + JSON.stringify(downloadObj))
|
||||||
|
commit('addUpdateDownload', downloadObj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,5 +106,8 @@ export const mutations = {
|
||||||
removeDownload(state, download) {
|
removeDownload(state, download) {
|
||||||
state.downloads = state.downloads.filter(d => d.id !== download.id)
|
state.downloads = state.downloads.filter(d => d.id !== download.id)
|
||||||
this.$sqlStore.removeDownload(download.id)
|
this.$sqlStore.removeDownload(download.id)
|
||||||
|
},
|
||||||
|
setMediaScanResults(state, val) {
|
||||||
|
state.mediaScanResults = val
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,7 +15,7 @@ export const state = () => ({
|
||||||
selectedBook: null,
|
selectedBook: null,
|
||||||
showReader: false,
|
showReader: false,
|
||||||
downloadFolder: null,
|
downloadFolder: null,
|
||||||
mediaScanResults: {},
|
|
||||||
showSideDrawer: false,
|
showSideDrawer: false,
|
||||||
bookshelfView: 'grid'
|
bookshelfView: 'grid'
|
||||||
})
|
})
|
||||||
|
@ -100,9 +100,6 @@ export const mutations = {
|
||||||
setDownloadFolder(state, val) {
|
setDownloadFolder(state, val) {
|
||||||
state.downloadFolder = val
|
state.downloadFolder = val
|
||||||
},
|
},
|
||||||
setMediaScanResults(state, val) {
|
|
||||||
state.mediaScanResults = val
|
|
||||||
},
|
|
||||||
setShowSideDrawer(state, val) {
|
setShowSideDrawer(state, val) {
|
||||||
state.showSideDrawer = val
|
state.showSideDrawer = val
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
export const state = () => ({
|
export const state = () => ({
|
||||||
user: null,
|
user: null,
|
||||||
userAudiobookData: [],
|
userAudiobookData: [],
|
||||||
localUserAudiobooks: {},
|
|
||||||
settings: {
|
settings: {
|
||||||
mobileOrderBy: 'recent',
|
mobileOrderBy: 'recent',
|
||||||
mobileOrderDesc: true,
|
mobileOrderDesc: true,
|
||||||
|
@ -23,19 +22,8 @@ export const getters = {
|
||||||
getToken: (state) => {
|
getToken: (state) => {
|
||||||
return state.user ? state.user.token : null
|
return state.user ? state.user.token : null
|
||||||
},
|
},
|
||||||
getUserAudiobook: (state) => (audiobookId) => {
|
getUserAudiobookData: (state, getters) => (audiobookId) => {
|
||||||
return state.user && state.user.audiobooks ? state.user.audiobooks[audiobookId] || null : null
|
|
||||||
},
|
|
||||||
getLocalUserAudiobook: (state) => (audiobookId) => {
|
|
||||||
return state.localUserAudiobooks ? state.localUserAudiobooks[audiobookId] || null : null
|
|
||||||
},
|
|
||||||
getMostRecentUserAudiobookData: (state, getters) => (audiobookId) => {
|
|
||||||
return state.userAudiobookData.find(uabd => uabd.audiobookId === audiobookId)
|
return state.userAudiobookData.find(uabd => uabd.audiobookId === audiobookId)
|
||||||
// var userAb = getters.getUserAudiobook(audiobookId)
|
|
||||||
// var localUserAb = getters.getLocalUserAudiobook(audiobookId)
|
|
||||||
// if (!localUserAb) return userAb
|
|
||||||
// if (!userAb) return localUserAb
|
|
||||||
// return localUserAb.lastUpdate > userAb.lastUpdate ? localUserAb : userAb
|
|
||||||
},
|
},
|
||||||
getUserSetting: (state) => (key) => {
|
getUserSetting: (state) => (key) => {
|
||||||
return state.settings ? state.settings[key] || null : null
|
return state.settings ? state.settings[key] || null : null
|
||||||
|
@ -131,18 +119,6 @@ export const mutations = {
|
||||||
setAllUserAudiobookData(state, allAbData) {
|
setAllUserAudiobookData(state, allAbData) {
|
||||||
state.userAudiobookData = allAbData
|
state.userAudiobookData = allAbData
|
||||||
},
|
},
|
||||||
setLocalUserAudiobooks(state, userAudiobooks) {
|
|
||||||
// state.localUserAudiobooks = userAudiobooks
|
|
||||||
// state.userAudiobooksListeners.forEach((listener) => {
|
|
||||||
// listener.meth()
|
|
||||||
// })
|
|
||||||
},
|
|
||||||
setUserAudiobooks(state, userAudiobooks) {
|
|
||||||
if (!state.user) return
|
|
||||||
state.user.audiobooks = {
|
|
||||||
...userAudiobooks
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setUser(state, user) {
|
setUser(state, user) {
|
||||||
state.user = user
|
state.user = user
|
||||||
if (user) {
|
if (user) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue