advplyr.audiobookshelf-app/layouts/default.vue
2021-12-04 19:56:29 -06:00

361 lines
No EOL
12 KiB
Vue

<template>
<div class="w-full min-h-screen h-full bg-bg text-white">
<app-appbar />
<div id="content" class="overflow-hidden" :class="playerIsOpen ? 'playerOpen' : ''">
<Nuxt />
</div>
<app-audio-player-container ref="streamContainer" />
<modals-libraries-modal />
<app-side-drawer />
<readers-reader />
</div>
</template>
<script>
import { Capacitor } from '@capacitor/core'
import { AppUpdate } from '@robingenz/capacitor-app-update'
import AudioDownloader from '@/plugins/audio-downloader'
import StorageManager from '@/plugins/storage-manager'
export default {
data() {
return {}
},
watch: {
networkConnected: {
handler(newVal) {
if (newVal) {
this.attemptConnection()
}
}
}
},
computed: {
playerIsOpen() {
return this.$store.getters['playerIsOpen']
},
routeName() {
return this.$route.name
},
networkConnected() {
return this.$store.state.networkConnected
},
currentLibraryId() {
return this.$store.state.libraries.currentLibraryId
},
isSocketConnected() {
return this.$store.state.socketConnected
}
},
methods: {
async connected(isConnected) {
if (isConnected) {
console.log('[Default] Connected socket sync user ab data')
this.$store.dispatch('user/syncUserAudiobookData')
// Load libraries
this.$store.dispatch('libraries/load')
this.$store.dispatch('libraries/fetch', this.currentLibraryId)
}
},
socketConnectionFailed(err) {
this.$toast.error('Socket connection error: ' + err.message)
},
currentUserAudiobookUpdate({ id, data }) {
if (data) {
console.log(`Current User Audiobook Updated ${id} ${JSON.stringify(data)}`)
this.$sqlStore.setUserAudiobookData(data)
} else {
this.$sqlStore.removeUserAudiobookData(id)
}
},
initialStream(stream) {
if (this.$refs.streamContainer && this.$refs.streamContainer.audioPlayerReady) {
this.$refs.streamContainer.streamOpen(stream)
}
},
async clickUpdateToast() {
var immediateUpdateAllowed = this.$store.state.appUpdateInfo.immediateUpdateAllowed
if (immediateUpdateAllowed) {
await AppUpdate.performImmediateUpdate()
} else {
await AppUpdate.openAppStore()
}
},
async checkForUpdate() {
console.log('Checking for app update')
const result = await AppUpdate.getAppUpdateInfo()
if (!result) {
console.error('Invalid version check')
return
}
console.log('App Update Info', JSON.stringify(result))
this.$store.commit('setAppUpdateInfo', result)
if (result.updateAvailability === 2) {
setTimeout(() => {
this.$toast.info(`Update is available! Click to update.`, {
draggable: false,
hideProgressBar: false,
timeout: 20000,
closeButton: true,
onClick: this.clickUpdateToast
})
}, 5000)
}
},
onDownloadProgress(data) {
var progress = data.progress
var audiobookId = data.audiobookId
var downloadObj = this.$store.getters['downloads/getDownload'](audiobookId)
if (downloadObj) {
this.$toast.update(downloadObj.toastId, { content: `${progress}% Downloading ${downloadObj.audiobook.book.title}` })
}
},
onDownloadFailed(data) {
if (!data.audiobookId) {
console.error('Download failed invalid audiobook id', data)
return
}
var downloadObj = this.$store.getters['downloads/getDownload'](data.audiobookId)
if (!downloadObj) {
console.error('Failed to find download for audiobook', data.audiobookId)
return
}
var message = data.error || 'Unknown Error'
this.$toast.update(downloadObj.toastId, { content: `Failed. ${message}.`, options: { timeout: 5000, type: 'error' } }, true)
this.$store.commit('downloads/removeDownload', downloadObj)
},
onDownloadComplete(data) {
if (!data.audiobookId) {
console.error('Download compelte invalid audiobook id', data)
return
}
var downloadId = data.downloadId
var contentUrl = data.contentUrl
var folderUrl = data.folderUrl
var folderName = data.folderName
var storageId = data.storageId
var storageType = data.storageType
var simplePath = data.simplePath
var filename = data.filename
var audiobookId = data.audiobookId
var size = data.size || 0
var isCover = !!data.isCover
console.log(`Download complete "${contentUrl}" | ${filename} | DlId: ${downloadId} | Is Cover? ${isCover}`)
var downloadObj = this.$store.getters['downloads/getDownload'](audiobookId)
if (!downloadObj) {
console.error('Failed to find download for audiobook', audiobookId)
return
}
if (!isCover) {
// Notify server to remove prepared download
if (this.$server.socket) {
this.$server.socket.emit('remove_download', audiobookId)
}
this.$toast.update(downloadObj.toastId, { content: `Success! ${downloadObj.audiobook.book.title} downloaded.`, options: { timeout: 5000, type: 'success' } }, true)
delete downloadObj.isDownloading
delete downloadObj.isPreparing
downloadObj.contentUrl = contentUrl
downloadObj.simplePath = simplePath
downloadObj.folderUrl = folderUrl
downloadObj.folderName = folderName
downloadObj.storageType = storageType
downloadObj.storageId = storageId
downloadObj.basePath = data.basePath || null
downloadObj.size = size
this.$store.commit('downloads/addUpdateDownload', downloadObj)
} else {
downloadObj.coverUrl = contentUrl
downloadObj.cover = Capacitor.convertFileSrc(contentUrl)
downloadObj.coverSize = size
downloadObj.coverBasePath = data.basePath || null
console.log('Updating download with cover', downloadObj.cover)
this.$store.commit('downloads/addUpdateDownload', downloadObj)
}
},
async checkLoadCurrent() {
var currentObj = await this.$localStore.getCurrent()
if (!currentObj) return
console.log('Has Current playing', currentObj.audiobookId)
var download = this.$store.getters['downloads/getDownload'](currentObj.audiobookId)
if (download) {
this.$store.commit('setPlayingDownload', download)
} else {
console.warn('Download not available for previous current playing', currentObj.audiobookId)
this.$localStore.setCurrent(null)
}
},
async searchFolder(downloadFolder) {
try {
var response = await StorageManager.searchFolder({ folderUrl: downloadFolder.uri })
var searchResults = response
searchResults.folders = JSON.parse(searchResults.folders)
searchResults.files = JSON.parse(searchResults.files)
console.log('Search folders results length', searchResults.folders.length)
searchResults.folders = searchResults.folders.map((sr) => {
if (sr.files) {
sr.files = JSON.parse(sr.files)
}
return sr
})
return searchResults
} catch (error) {
console.error('Failed', error)
this.$toast.error('Failed to search downloads folder')
return {}
}
},
async syncDownloads(downloads, downloadFolder) {
console.log('Syncing downloads ' + downloads.length)
var mediaScanResults = await this.searchFolder(downloadFolder)
this.$store.commit('downloads/setMediaScanResults', mediaScanResults)
// Filter out media folders without any audio files
var mediaFolders = mediaScanResults.folders.filter((sr) => {
if (!sr.files) return false
var audioFiles = sr.files.filter((mf) => !!mf.isAudio)
return audioFiles.length
})
downloads.forEach((download) => {
var mediaFolder = mediaFolders.find((mf) => mf.name === download.folderName)
if (mediaFolder) {
console.log('Found download ' + download.folderName)
if (download.isMissing) {
download.isMissing = false
this.$store.commit('downloads/addUpdateDownload', download)
}
} else {
console.error('Download not found ' + download.folderName)
if (!download.isMissing) {
download.isMissing = true
this.$store.commit('downloads/addUpdateDownload', download)
}
}
})
// Match media scanned folders with books from server
if (this.isSocketConnected) {
await this.$store.dispatch('downloads/linkOrphanDownloads')
}
},
async initMediaStore() {
// Request and setup listeners for media files on native
AudioDownloader.addListener('onDownloadComplete', (data) => {
this.onDownloadComplete(data)
})
AudioDownloader.addListener('onDownloadFailed', (data) => {
this.onDownloadFailed(data)
})
AudioDownloader.addListener('onDownloadProgress', (data) => {
this.onDownloadProgress(data)
})
// var downloads = (await this.$sqlStore.getAllDownloads()) || []
var downloads = await this.$store.dispatch('downloads/loadFromStorage')
var downloadFolder = await this.$localStore.getDownloadFolder()
if (downloadFolder) {
await this.syncDownloads(downloads, downloadFolder)
}
var userSavedSettings = await this.$localStore.getUserSettings()
if (userSavedSettings) {
this.$store.commit('user/setSettings', userSavedSettings)
}
var checkPermission = await StorageManager.checkStoragePermission()
console.log('Storage Permission is' + checkPermission.value)
if (!checkPermission.value) {
console.log('Will require permissions')
} else {
console.log('Has Storage Permission')
this.$store.commit('setHasStoragePermission', true)
}
},
showErrorToast(message) {
this.$toast.error(message)
},
showSuccessToast(message) {
this.$toast.success(message)
},
async attemptConnection() {
if (!this.$server) return
if (!this.networkConnected) {
console.warn('No network connection')
return
}
var localServerUrl = await this.$localStore.getServerUrl()
var localUserToken = await this.$localStore.getToken()
if (localServerUrl) {
// Server and Token are stored
if (localUserToken) {
var isSocketAlreadyEstablished = this.$server.socket
var success = await this.$server.connect(localServerUrl, localUserToken)
if (!success && !this.$server.url) {
// Bad URL
} else if (!success) {
// Failed to connect
} else if (isSocketAlreadyEstablished) {
// No need to wait for connect event
}
}
}
}
},
async mounted() {
if (!this.$server) return console.error('No Server')
// console.log(`Default Mounted set SOCKET listeners ${this.$server.connected}`)
if (this.$server.connected) {
console.log('Syncing on default mount')
this.$store.dispatch('user/syncUserAudiobookData')
}
this.$server.on('connected', this.connected)
this.$server.on('connectionFailed', this.socketConnectionFailed)
this.$server.on('initialStream', this.initialStream)
this.$server.on('currentUserAudiobookUpdate', this.currentUserAudiobookUpdate)
this.$server.on('show_error_toast', this.showErrorToast)
this.$server.on('show_success_toast', this.showSuccessToast)
if (this.$store.state.isFirstLoad) {
this.$store.commit('setIsFirstLoad', false)
await this.$store.dispatch('setupNetworkListener')
this.attemptConnection()
this.checkForUpdate()
this.initMediaStore()
}
},
beforeDestroy() {
if (!this.$server) {
console.error('No Server beforeDestroy')
return
}
this.$server.off('connected', this.connected)
this.$server.off('connectionFailed', this.socketConnectionFailed)
this.$server.off('initialStream', this.initialStream)
this.$server.off('show_error_toast', this.showErrorToast)
this.$server.off('show_success_toast', this.showSuccessToast)
}
}
</script>
<style>
#content {
height: calc(100vh - 64px);
}
#content.playerOpen {
height: calc(100vh - 164px);
}
</style>