+
{{ download.audiobook.book.title }}
{{ download.audiobook.book.author }}
+
{{ $bytesPretty(download.size) }}
@@ -71,11 +82,22 @@
@@ -297,7 +391,6 @@ export default {
height: calc(100vh - 64px);
}
#content.playerOpen {
- /* height: calc(100vh - 204px); */
height: calc(100vh - 240px);
}
\ No newline at end of file
diff --git a/package.json b/package.json
index e5890a73..256cf323 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf-app",
- "version": "v0.8.4-beta",
+ "version": "v0.9.0-beta",
"author": "advplyr",
"scripts": {
"dev": "nuxt --hostname localhost --port 1337",
diff --git a/pages/account.vue b/pages/account.vue
index a05e9c8c..75dbef6e 100644
--- a/pages/account.vue
+++ b/pages/account.vue
@@ -68,6 +68,9 @@ export default {
})
this.$server.logout()
this.$router.push('/connect')
+
+ this.$store.commit('audiobooks/reset')
+ this.$store.dispatch('audiobooks/useDownloaded')
},
openAppStore() {
AppUpdate.openAppStore()
diff --git a/pages/audiobook/_id/index.vue b/pages/audiobook/_id/index.vue
index d8b1dc97..f2aaf4d7 100644
--- a/pages/audiobook/_id/index.vue
+++ b/pages/audiobook/_id/index.vue
@@ -162,6 +162,9 @@ export default {
},
downloadObj() {
return this.$store.getters['downloads/getDownload'](this.audiobookId)
+ },
+ hasStoragePermission() {
+ return this.$store.state.hasStoragePermission
}
},
methods: {
@@ -229,17 +232,32 @@ export default {
}
},
async prepareDownload() {
- // var audiobook = await this.$axios.$get(`/api/audiobook/${this.audiobookId}`).catch((error) => {
- // console.error('Failed', error)
- // return false
- // })
var audiobook = this.audiobook
if (!audiobook) {
return
}
+ if (!this.hasStoragePermission) {
+ await AudioDownloader.requestStoragePermission()
+ return
+ }
+
+ // Download Path
+ var dlFolder = this.$localStore.downloadFolder
+ if (!dlFolder) {
+ console.log('No download folder, request from ujser')
+ var folderObj = await AudioDownloader.selectFolder()
+
+ if (folderObj.error) {
+ return this.$toast.error(`Error: ${folderObj.error || 'Unknown Error'}`)
+ }
+ dlFolder = folderObj
+ await this.$localStore.setDownloadFolder(folderObj)
+ }
+
var downloadObject = {
id: this.audiobookId,
+ downloadFolderUrl: dlFolder.uri,
audiobook: {
...audiobook
},
@@ -278,59 +296,37 @@ export default {
}
return _clean
},
- async downloadCover(download) {
- var coverUrl = this.getCoverUrlForDownload()
- if (!coverUrl) {
- return null
- }
- var extname = Path.extname(coverUrl) || '.jpg'
-
- var downloadRequestPayload = {
- audiobookId: download.id,
- downloadUrl: coverUrl,
- filename: `${download.id}${extname}`,
- title: download.audiobook.book.title
- }
- console.log('Starting cover download', coverUrl, downloadRequestPayload.filename)
- var downloadRes = await AudioDownloader.downloadCover(downloadRequestPayload)
- if (downloadRes && downloadRes.url) {
- console.log('Cover art downloaded', downloadRes.url)
- return downloadRes.url
- } else {
- console.error('Cover art failed to download')
- return null
- }
- },
async startDownload(url, fileext, download) {
this.$toast.update(download.toastId, { content: `Downloading "${download.audiobook.book.title}"...` })
+ var coverDownloadUrl = this.getCoverUrlForDownload()
+ var coverFilename = null
+ if (coverDownloadUrl) {
+ var coverExt = Path.extname(coverDownloadUrl) || '.jpg'
+ coverFilename = `cover-${download.id}${coverExt}`
+ }
+
download.isDownloading = true
download.isPreparing = false
- download.filename = `${download.id}${fileext}`
+ download.filename = `${download.audiobook.book.title}${fileext}`
this.$store.commit('downloads/addUpdateDownload', download)
console.log('Starting Download URL', url)
var downloadRequestPayload = {
audiobookId: download.id,
filename: download.filename,
+ coverFilename,
+ coverDownloadUrl,
downloadUrl: url,
- title: download.audiobook.book.title
+ title: download.audiobook.book.title,
+ downloadFolderUrl: download.downloadFolderUrl
}
var downloadRes = await AudioDownloader.download(downloadRequestPayload)
- var downloadId = downloadRes.value
- if (!downloadId) {
- console.error('Invalid download, removing')
- this.$toast.update(download.toastId, { content: `Failed download "${download.audiobook.book.title}"`, options: { timeout: 5000, type: 'error' } })
+ if (downloadRes.error) {
+ var errorMsg = downloadRes.error || 'Unknown error'
+ console.error('Download error', errorMsg)
+ this.$toast.update(download.toastId, { content: `Error: ${errorMsg}`, options: { timeout: 5000, type: 'error' } })
this.$store.commit('downloads/removeDownload', download)
- } else {
- console.log('Download cover now')
- var coverUrl = await this.downloadCover(download)
- if (coverUrl) {
- console.log('Got cover url', coverUrl)
- download.coverUrl = coverUrl
- download.cover = Capacitor.convertFileSrc(coverUrl)
- this.$store.commit('downloads/addUpdateDownload', download)
- }
}
},
downloadReady(prepareDownload) {
@@ -363,16 +359,24 @@ export default {
}
},
mounted() {
- this.$server.socket.on('download_ready', this.downloadReady)
- this.$server.socket.on('download_killed', this.downloadKilled)
- this.$server.socket.on('download_failed', this.downloadFailed)
+ if (!this.$server.socket) {
+ console.warn('Audiobook Page mounted: Server socket not set')
+ } else {
+ this.$server.socket.on('download_ready', this.downloadReady)
+ this.$server.socket.on('download_killed', this.downloadKilled)
+ this.$server.socket.on('download_failed', this.downloadFailed)
+ }
this.$store.commit('audiobooks/addListener', { id: 'audiobook', audiobookId: this.audiobookId, meth: this.audiobookUpdated })
},
beforeDestroy() {
- this.$server.socket.off('download_ready', this.downloadReady)
- this.$server.socket.off('download_killed', this.downloadKilled)
- this.$server.socket.off('download_failed', this.downloadFailed)
+ if (!this.$server.socket) {
+ console.warn('Audiobook Page beforeDestroy: Server socket not set')
+ } else {
+ this.$server.socket.off('download_ready', this.downloadReady)
+ this.$server.socket.off('download_killed', this.downloadKilled)
+ this.$server.socket.off('download_failed', this.downloadFailed)
+ }
this.$store.commit('audiobooks/removeListener', 'audiobook')
}
diff --git a/pages/connect.vue b/pages/connect.vue
index b2b11c66..f592f95f 100644
--- a/pages/connect.vue
+++ b/pages/connect.vue
@@ -179,6 +179,13 @@ export default {
},
mounted() {
this.init()
+ },
+ beforeDestroy() {
+ if (!this.$server) {
+ console.error('Connected beforeDestroy: No Server')
+ return
+ }
+ this.$server.off('connected', this.socketConnected)
}
}
\ No newline at end of file
diff --git a/plugins/init.client.js b/plugins/init.client.js
index 5d703eae..2731163e 100644
--- a/plugins/init.client.js
+++ b/plugins/init.client.js
@@ -2,6 +2,7 @@ import Vue from 'vue'
Vue.prototype.$isDev = process.env.NODE_ENV !== 'production'
Vue.prototype.$bytesPretty = (bytes, decimals = 2) => {
+ if (isNaN(bytes) || bytes === null) return 'Invalid Bytes'
if (bytes === 0) {
return '0 Bytes'
}
diff --git a/plugins/store.js b/plugins/store.js
index a261d37f..36839d13 100644
--- a/plugins/store.js
+++ b/plugins/store.js
@@ -408,6 +408,7 @@ class LocalStorage {
this.vuexStore = vuexStore
this.userAudiobooksLoaded = false
+ this.downloadFolder = null
this.userAudiobooks = {}
}
@@ -502,6 +503,32 @@ class LocalStorage {
}
}
+ async setDownloadFolder(folderObj) {
+ try {
+ if (folderObj) {
+ await Storage.set({ key: 'downloadFolder', value: JSON.stringify(folderObj) })
+ this.downloadFolder = folderObj
+ } else {
+ await Storage.remove({ key: 'downloadFolder' })
+ this.downloadFolder = null
+ }
+ } catch (error) {
+ console.error('[LocalStorage] Failed to set download folder', error)
+ }
+ }
+
+ async getDownloadFolder() {
+ try {
+ var _value = (await Storage.get({ key: 'downloadFolder' }) || {}).value || null
+ if (!_value) return null
+ this.downloadFolder = JSON.parse(_value)
+ return this.downloadFolder
+ } catch (error) {
+ console.error('[LocalStorage] Failed to get download folder', error)
+ return null
+ }
+ }
+
async getServerUrl() {
try {
return (await Storage.get({ key: 'serverUrl' }) || {}).value || null
diff --git a/store/audiobooks.js b/store/audiobooks.js
index af9806e8..007c638e 100644
--- a/store/audiobooks.js
+++ b/store/audiobooks.js
@@ -15,7 +15,11 @@ export const getters = {
getAudiobook: state => id => {
return state.audiobooks.find(ab => ab.id === id)
},
- getFiltered: (state, getters, rootState) => () => {
+ getFiltered: (state, getters, rootState, rootGetters) => () => {
+ // var isDisc = !rootState.networkConnected || !rootState.socketConnected
+ // console.log('GET FILETERED', isDisc)
+ // var filtered = isDisc ? rootGetters['downloads/getAudiobooks'] : state.audiobooks
+ // console.log('FILTERED LEN', filtered.length)
var filtered = state.audiobooks
var settings = rootState.user.settings || {}
var filterBy = settings.filterBy || ''
@@ -36,9 +40,12 @@ export const getters = {
var direction = settings.orderDesc ? 'desc' : 'asc'
var filtered = getters.getFiltered()
+ var orderByNumber = settings.orderBy === 'book.volumeNumber'
return sort(filtered)[direction]((ab) => {
// Supports dot notation strings i.e. "book.title"
- return settings.orderBy.split('.').reduce((a, b) => a[b], ab)
+ var value = settings.orderBy.split('.').reduce((a, b) => a[b], ab)
+ if (orderByNumber && !isNaN(value)) return Number(value)
+ return value
})
},
getUniqueAuthors: (state) => {
@@ -63,6 +70,9 @@ export const actions = {
.catch((error) => {
console.error('Failed', error)
})
+ },
+ useDownloaded({ commit, rootGetters }) {
+ commit('set', rootGetters['downloads/getAudiobooks'])
}
}
diff --git a/store/downloads.js b/store/downloads.js
index 7e8a5a70..569e3ac3 100644
--- a/store/downloads.js
+++ b/store/downloads.js
@@ -11,6 +11,9 @@ export const getters = {
getDownloadIfReady: (state) => id => {
var download = state.downloads.find(d => d.id === id)
return !!download && !download.isDownloading && !download.isPreparing ? download : null
+ },
+ getAudiobooks: (state) => {
+ return state.downloads.map(dl => dl.audiobook)
}
}
diff --git a/store/index.js b/store/index.js
index c5b90192..eae4e623 100644
--- a/store/index.js
+++ b/store/index.js
@@ -8,8 +8,10 @@ export const state = () => ({
appUpdateInfo: null,
socketConnected: false,
networkConnected: false,
- networkConnectionType: 'unknown',
- streamListener: null
+ networkConnectionType: null,
+ streamListener: null,
+ isFirstLoad: true,
+ hasStoragePermission: false
})
export const getters = {
@@ -27,6 +29,12 @@ export const getters = {
export const actions = {}
export const mutations = {
+ setHasStoragePermission(state, val) {
+ state.hasStoragePermission = val
+ },
+ setIsFirstLoad(state, val) {
+ state.isFirstLoad = val
+ },
setAppUpdateInfo(state, info) {
state.appUpdateInfo = info
},
diff --git a/store/user.js b/store/user.js
index 042c36fb..e6748945 100644
--- a/store/user.js
+++ b/store/user.js
@@ -1,4 +1,3 @@
-import { Storage } from '@capacitor/storage'
import Vue from 'vue'
export const state = () => ({