mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-08 21:08:43 +02:00
Add new connection page to support multiple server connection configs
This commit is contained in:
@@ -1,442 +0,0 @@
|
||||
<template>
|
||||
<div class="w-full h-full px-3 py-4 overflow-y-auto">
|
||||
<div class="flex">
|
||||
<div class="w-32">
|
||||
<div class="relative">
|
||||
<covers-book-cover :audiobook="audiobook" :download-cover="downloadedCover" :width="128" :book-cover-aspect-ratio="bookCoverAspectRatio" />
|
||||
<div class="absolute bottom-0 left-0 h-1.5 bg-yellow-400 shadow-sm z-10" :style="{ width: 128 * progressPercent + 'px' }"></div>
|
||||
</div>
|
||||
<div class="flex my-4">
|
||||
<p v-if="numTracks" class="text-sm">{{ numTracks }} Tracks</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow px-3">
|
||||
<h1 class="text-lg">{{ title }}</h1>
|
||||
<h3 v-if="series" class="font-book text-gray-300 text-lg leading-7">{{ seriesText }}</h3>
|
||||
<p class="text-sm text-gray-400">by {{ author }}</p>
|
||||
<p v-if="numTracks" class="text-gray-300 text-sm my-1">
|
||||
{{ $elapsedPretty(duration) }}
|
||||
<span class="px-4">{{ $bytesPretty(size) }}</span>
|
||||
</p>
|
||||
|
||||
<div v-if="progressPercent > 0" class="px-4 py-2 bg-primary text-sm font-semibold rounded-md text-gray-200 mt-4 relative" :class="resettingProgress ? 'opacity-25' : ''">
|
||||
<p class="leading-6">Your Progress: {{ Math.round(progressPercent * 100) }}%</p>
|
||||
<p v-if="progressPercent < 1" class="text-gray-400 text-xs">{{ $elapsedPretty(userTimeRemaining) }} remaining</p>
|
||||
<div v-if="!resettingProgress" class="absolute -top-1.5 -right-1.5 p-1 w-5 h-5 rounded-full bg-bg hover:bg-error border border-primary flex items-center justify-center cursor-pointer" @click.stop="clearProgressClick">
|
||||
<span class="material-icons text-sm">close</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="(isConnected && (showPlay || showRead)) || isDownloadPlayable" class="flex mt-4 -mr-2">
|
||||
<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 class="px-1 text-sm">{{ isPlaying ? (isStreaming ? 'Streaming' : 'Playing') : isDownloadPlayable ? 'Play local' : 'Play stream' }}</span>
|
||||
</ui-btn>
|
||||
<ui-btn v-if="showRead && isConnected" color="info" class="flex items-center justify-center mr-2" :class="showPlay ? '' : 'flex-grow'" :padding-x="2" @click="readBook">
|
||||
<span class="material-icons">auto_stories</span>
|
||||
<span v-if="!showPlay" class="px-2 text-base">Read {{ ebookFormat }}</span>
|
||||
</ui-btn>
|
||||
<ui-btn v-if="isConnected && showPlay && !isIos" color="primary" class="flex items-center justify-center" :padding-x="2" @click="downloadClick">
|
||||
<span class="material-icons" :class="downloadObj ? 'animate-pulse' : ''">{{ downloadObj ? (isDownloading || isDownloadPreparing ? 'downloading' : 'download_done') : 'download' }}</span>
|
||||
</ui-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full py-4">
|
||||
<p>{{ description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Path from 'path'
|
||||
import { Dialog } from '@capacitor/dialog'
|
||||
import AudioDownloader from '@/plugins/audio-downloader'
|
||||
import StorageManager from '@/plugins/storage-manager'
|
||||
|
||||
export default {
|
||||
async asyncData({ store, params, redirect, app }) {
|
||||
var audiobookId = params.id
|
||||
var audiobook = null
|
||||
|
||||
if (app.$server.connected) {
|
||||
audiobook = await app.$axios.$get(`/api/books/${audiobookId}`).catch((error) => {
|
||||
console.error('Failed', error)
|
||||
return false
|
||||
})
|
||||
} else {
|
||||
var download = store.getters['downloads/getDownload'](audiobookId)
|
||||
if (download) {
|
||||
audiobook = download.audiobook
|
||||
}
|
||||
}
|
||||
|
||||
if (!audiobook) {
|
||||
console.error('No audiobook...', params.id)
|
||||
return redirect('/')
|
||||
}
|
||||
return {
|
||||
audiobook
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
resettingProgress: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isIos() {
|
||||
return this.$platform === 'ios'
|
||||
},
|
||||
isConnected() {
|
||||
return this.$store.state.socketConnected
|
||||
},
|
||||
bookCoverAspectRatio() {
|
||||
return this.$store.getters['getBookCoverAspectRatio']
|
||||
},
|
||||
audiobookId() {
|
||||
return this.audiobook.id
|
||||
},
|
||||
book() {
|
||||
return this.audiobook.book || {}
|
||||
},
|
||||
title() {
|
||||
return this.book.title
|
||||
},
|
||||
author() {
|
||||
return this.book.author || 'Unknown'
|
||||
},
|
||||
description() {
|
||||
return this.book.description || ''
|
||||
},
|
||||
series() {
|
||||
return this.book.series || null
|
||||
},
|
||||
volumeNumber() {
|
||||
return this.book.volumeNumber || null
|
||||
},
|
||||
seriesText() {
|
||||
if (!this.series) return ''
|
||||
if (!this.volumeNumber) return this.series
|
||||
return `${this.series} #${this.volumeNumber}`
|
||||
},
|
||||
duration() {
|
||||
return this.audiobook.duration
|
||||
},
|
||||
size() {
|
||||
return this.audiobook.size
|
||||
},
|
||||
userAudiobook() {
|
||||
return this.$store.getters['user/getUserAudiobook'](this.audiobookId)
|
||||
},
|
||||
userToken() {
|
||||
return this.$store.getters['user/getToken']
|
||||
},
|
||||
userCurrentTime() {
|
||||
return this.userAudiobook ? this.userAudiobook.currentTime : 0
|
||||
},
|
||||
userTimeRemaining() {
|
||||
return Math.max(0, this.duration - this.userCurrentTime)
|
||||
},
|
||||
progressPercent() {
|
||||
return this.userAudiobook ? this.userAudiobook.progress : 0
|
||||
},
|
||||
isStreaming() {
|
||||
return this.$store.getters['isAudiobookStreaming'](this.audiobookId)
|
||||
},
|
||||
isPlaying() {
|
||||
return this.$store.getters['isAudiobookPlaying'](this.audiobookId)
|
||||
},
|
||||
numTracks() {
|
||||
if (this.audiobook.tracks) return this.audiobook.tracks.length
|
||||
return this.audiobook.numTracks || 0
|
||||
},
|
||||
isMissing() {
|
||||
return this.audiobook.isMissing
|
||||
},
|
||||
isIncomplete() {
|
||||
return this.audiobook.isIncomplete
|
||||
},
|
||||
isDownloading() {
|
||||
return this.downloadObj ? this.downloadObj.isDownloading : false
|
||||
},
|
||||
showPlay() {
|
||||
return !this.isMissing && !this.isIncomplete && this.numTracks
|
||||
},
|
||||
showRead() {
|
||||
return this.hasEbook && this.ebookFormat !== '.pdf'
|
||||
},
|
||||
hasEbook() {
|
||||
return this.audiobook.numEbooks
|
||||
},
|
||||
ebookFormat() {
|
||||
if (!this.audiobook || !this.audiobook.ebooks || !this.audiobook.ebooks.length) return null
|
||||
return this.audiobook.ebooks[0].ext.substr(1)
|
||||
},
|
||||
isDownloadPreparing() {
|
||||
return this.downloadObj ? this.downloadObj.isPreparing : false
|
||||
},
|
||||
isDownloadPlayable() {
|
||||
return this.downloadObj && !this.isDownloading && !this.isDownloadPreparing
|
||||
},
|
||||
downloadedCover() {
|
||||
return this.downloadObj ? this.downloadObj.cover : null
|
||||
},
|
||||
downloadObj() {
|
||||
return this.$store.getters['downloads/getDownload'](this.audiobookId)
|
||||
},
|
||||
hasStoragePermission() {
|
||||
return this.$store.state.hasStoragePermission
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
readBook() {
|
||||
this.$store.commit('openReader', this.audiobook)
|
||||
},
|
||||
playClick() {
|
||||
this.$store.commit('setPlayOnLoad', true)
|
||||
if (!this.isDownloadPlayable) {
|
||||
// Stream
|
||||
console.log('[PLAYCLICK] Set Playing STREAM ' + this.title)
|
||||
this.$store.commit('setStreamAudiobook', this.audiobook)
|
||||
this.$server.socket.emit('open_stream', this.audiobook.id)
|
||||
} else {
|
||||
// Local
|
||||
console.log('[PLAYCLICK] Set Playing Local Download ' + this.title)
|
||||
this.$store.commit('setPlayingDownload', this.downloadObj)
|
||||
}
|
||||
},
|
||||
async clearProgressClick() {
|
||||
const { value } = await Dialog.confirm({
|
||||
title: 'Confirm',
|
||||
message: 'Are you sure you want to reset your progress?'
|
||||
})
|
||||
|
||||
if (value) {
|
||||
this.resettingProgress = true
|
||||
this.$store.dispatch('user/updateUserAudiobookData', {
|
||||
audiobookId: this.audiobookId,
|
||||
currentTime: 0,
|
||||
totalDuration: this.duration,
|
||||
progress: 0,
|
||||
lastUpdate: Date.now(),
|
||||
isRead: false
|
||||
})
|
||||
|
||||
if (this.$server.connected) {
|
||||
await this.$axios
|
||||
.$patch(`/api/me/audiobook/${this.audiobookId}/reset-progress`)
|
||||
.then(() => {
|
||||
console.log('Progress reset complete')
|
||||
this.$toast.success(`Your progress was reset`)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Progress reset failed', error)
|
||||
})
|
||||
}
|
||||
|
||||
this.resettingProgress = false
|
||||
}
|
||||
},
|
||||
audiobookUpdated(audiobook) {
|
||||
if (audiobook.id === this.audiobookId) {
|
||||
console.log('Audiobook Updated - Fetch full audiobook')
|
||||
this.$axios
|
||||
.$get(`/api/books/${this.audiobookId}`)
|
||||
.then((audiobook) => {
|
||||
this.audiobook = audiobook
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed', error)
|
||||
})
|
||||
}
|
||||
},
|
||||
downloadClick() {
|
||||
console.log('downloadClick ' + this.$server.connected + ' | ' + !!this.downloadObj)
|
||||
if (!this.$server.connected) return
|
||||
|
||||
if (this.downloadObj) {
|
||||
console.log('Already downloaded', this.downloadObj)
|
||||
} else {
|
||||
this.prepareDownload()
|
||||
}
|
||||
},
|
||||
async changeDownloadFolderClick() {
|
||||
if (!this.hasStoragePermission) {
|
||||
console.log('Requesting Storage Permission')
|
||||
await StorageManager.requestStoragePermission()
|
||||
} else {
|
||||
var folderObj = await StorageManager.selectFolder()
|
||||
if (folderObj.error) {
|
||||
return this.$toast.error(`Error: ${folderObj.error || 'Unknown Error'}`)
|
||||
}
|
||||
|
||||
var permissionsGood = await StorageManager.checkFolderPermissions({ folderUrl: folderObj.uri })
|
||||
console.log('Storage Permission check folder ' + permissionsGood)
|
||||
|
||||
if (!permissionsGood) {
|
||||
this.$toast.error('Folder permissions failed')
|
||||
return
|
||||
} else {
|
||||
this.$toast.success('Folder permission success')
|
||||
}
|
||||
|
||||
await this.$localStore.setDownloadFolder(folderObj)
|
||||
}
|
||||
},
|
||||
async prepareDownload() {
|
||||
var audiobook = this.audiobook
|
||||
if (!audiobook) {
|
||||
return
|
||||
}
|
||||
|
||||
// Download Path
|
||||
var dlFolder = this.$localStore.downloadFolder
|
||||
console.log('Prepare download: ' + this.hasStoragePermission + ' | ' + dlFolder)
|
||||
|
||||
if (!this.hasStoragePermission || !dlFolder) {
|
||||
console.log('No download folder, request from user')
|
||||
// User to select download folder from download modal to ensure permissions
|
||||
// this.$store.commit('downloads/setShowModal', true)
|
||||
this.changeDownloadFolderClick()
|
||||
return
|
||||
} else {
|
||||
console.log('Has Download folder: ' + JSON.stringify(dlFolder))
|
||||
}
|
||||
|
||||
var downloadObject = {
|
||||
id: this.audiobookId,
|
||||
downloadFolderUrl: dlFolder.uri,
|
||||
audiobook: {
|
||||
...audiobook
|
||||
},
|
||||
isPreparing: true,
|
||||
isDownloading: false,
|
||||
toastId: this.$toast(`Preparing download for "${this.title}"`, { timeout: false })
|
||||
}
|
||||
if (audiobook.tracks.length === 1) {
|
||||
// Single track should not need preparation
|
||||
console.log('Single track, start download no prep needed')
|
||||
var track = audiobook.tracks[0]
|
||||
var fileext = track.ext
|
||||
|
||||
console.log('Download Single Track Path: ' + track.path)
|
||||
|
||||
var relTrackPath = track.path.replace('\\', '/').replace(this.audiobook.path.replace('\\', '/'), '')
|
||||
|
||||
var url = `${this.$store.state.serverUrl}/s/book/${this.audiobookId}${relTrackPath}?token=${this.userToken}`
|
||||
this.startDownload(url, fileext, downloadObject)
|
||||
} else {
|
||||
// Multi-track merge
|
||||
this.$store.commit('downloads/addUpdateDownload', downloadObject)
|
||||
|
||||
var prepareDownloadPayload = {
|
||||
audiobookId: this.audiobookId,
|
||||
audioFileType: 'same',
|
||||
type: 'singleAudio'
|
||||
}
|
||||
this.$server.socket.emit('download', prepareDownloadPayload)
|
||||
}
|
||||
},
|
||||
getCoverUrlForDownload() {
|
||||
if (!this.book || !this.book.cover) return null
|
||||
|
||||
var cover = this.book.cover
|
||||
if (cover.startsWith('http')) return cover
|
||||
var coverSrc = this.$store.getters['audiobooks/getBookCoverSrc'](this.audiobook)
|
||||
return coverSrc
|
||||
// var _clean = cover.replace(/\\/g, '/')
|
||||
// if (_clean.startsWith('/local')) {
|
||||
// var _cover = process.env.NODE_ENV !== 'production' && process.env.PROD !== '1' ? _clean.replace('/local', '') : _clean
|
||||
// return `${this.$store.state.serverUrl}${_cover}?token=${this.userToken}&ts=${Date.now()}`
|
||||
// } else if (_clean.startsWith('/metadata')) {
|
||||
// return `${this.$store.state.serverUrl}${_clean}?token=${this.userToken}&ts=${Date.now()}`
|
||||
// }
|
||||
// return _clean
|
||||
},
|
||||
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 coverNoQueryString = coverDownloadUrl.split('?')[0]
|
||||
|
||||
var coverExt = Path.extname(coverNoQueryString) || '.jpg'
|
||||
coverFilename = `cover-${download.id}${coverExt}`
|
||||
}
|
||||
|
||||
download.isDownloading = true
|
||||
download.isPreparing = false
|
||||
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,
|
||||
downloadFolderUrl: download.downloadFolderUrl
|
||||
}
|
||||
var downloadRes = await AudioDownloader.download(downloadRequestPayload)
|
||||
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)
|
||||
}
|
||||
},
|
||||
downloadReady(prepareDownload) {
|
||||
var download = this.$store.getters['downloads/getDownload'](prepareDownload.audiobookId)
|
||||
if (download) {
|
||||
var fileext = prepareDownload.ext
|
||||
var url = `${this.$store.state.serverUrl}/downloads/${prepareDownload.id}/${prepareDownload.filename}?token=${this.userToken}`
|
||||
this.startDownload(url, fileext, download)
|
||||
} else {
|
||||
console.error('Prepare download killed but download not found', prepareDownload)
|
||||
}
|
||||
},
|
||||
downloadKilled(prepareDownload) {
|
||||
var download = this.$store.getters['downloads/getDownload'](prepareDownload.audiobookId)
|
||||
if (download) {
|
||||
this.$toast.update(download.toastId, { content: `Prepare download killed for "${download.audiobook.book.title}"`, options: { timeout: 5000, type: 'error' } })
|
||||
this.$store.commit('downloads/removeDownload', download)
|
||||
} else {
|
||||
console.error('Prepare download killed but download not found', prepareDownload)
|
||||
}
|
||||
},
|
||||
downloadFailed(prepareDownload) {
|
||||
var download = this.$store.getters['downloads/getDownload'](prepareDownload.audiobookId)
|
||||
if (download) {
|
||||
this.$toast.update(download.toastId, { content: `Prepare download failed for "${download.audiobook.book.title}"`, options: { timeout: 5000, type: 'error' } })
|
||||
this.$store.commit('downloads/removeDownload', download)
|
||||
} else {
|
||||
console.error('Prepare download failed but download not found', prepareDownload)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
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.$server.socket.on('audiobook_updated', this.audiobookUpdated)
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
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.$server.socket.off('audiobook_updated', this.audiobookUpdated)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
+25
-41
@@ -8,20 +8,20 @@
|
||||
<div>
|
||||
<p class="mb-4 text-center text-xl">
|
||||
Bookshelf empty
|
||||
<span v-show="isSocketConnected">
|
||||
<span v-show="user">
|
||||
for library
|
||||
<strong>{{ currentLibraryName }}</strong>
|
||||
</span>
|
||||
</p>
|
||||
<div class="w-full" v-if="!isSocketConnected">
|
||||
<div class="w-full" v-if="!user">
|
||||
<div class="flex justify-center items-center mb-3">
|
||||
<span class="material-icons text-error text-lg">cloud_off</span>
|
||||
<p class="pl-2 text-error text-sm">Audiobookshelf server not connected.</p>
|
||||
</div>
|
||||
<p class="px-4 text-center text-error absolute bottom-12 left-0 right-0 mx-auto"><strong>Important!</strong> This app requires that you are running <u>your own server</u> and does not provide any content.</p>
|
||||
<!-- <p class="px-4 text-center text-error absolute bottom-12 left-0 right-0 mx-auto"><strong>Important!</strong> This app requires that you are running <u>your own server</u> and does not provide any content.</p> -->
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<ui-btn v-if="!isSocketConnected" small @click="$router.push('/connect')" class="w-32">Connect</ui-btn>
|
||||
<ui-btn v-if="!user" small @click="$router.push('/connect')" class="w-32">Connect</ui-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -46,6 +46,9 @@ export default {
|
||||
return ab
|
||||
})
|
||||
},
|
||||
user() {
|
||||
return this.$store.state.user.user
|
||||
},
|
||||
isSocketConnected() {
|
||||
return this.$store.state.socketConnected
|
||||
},
|
||||
@@ -133,16 +136,16 @@ export default {
|
||||
this.shelves = categories
|
||||
console.log('Shelves', this.shelves)
|
||||
},
|
||||
async socketInit(isConnected) {
|
||||
if (isConnected && this.currentLibraryId) {
|
||||
console.log('Connected - Load from server')
|
||||
await this.fetchCategories()
|
||||
} else {
|
||||
console.log('Disconnected - Reset to local storage')
|
||||
this.shelves = this.downloadOnlyShelves
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
// async socketInit(isConnected) {
|
||||
// if (isConnected && this.currentLibraryId) {
|
||||
// console.log('Connected - Load from server')
|
||||
// await this.fetchCategories()
|
||||
// } else {
|
||||
// console.log('Disconnected - Reset to local storage')
|
||||
// this.shelves = this.downloadOnlyShelves
|
||||
// }
|
||||
// this.loading = false
|
||||
// },
|
||||
async libraryChanged(libid) {
|
||||
if (this.isSocketConnected && this.currentLibraryId) {
|
||||
await this.fetchCategories()
|
||||
@@ -211,43 +214,24 @@ export default {
|
||||
})
|
||||
},
|
||||
initListeners() {
|
||||
this.$server.on('initialized', this.socketInit)
|
||||
// this.$server.on('initialized', this.socketInit)
|
||||
this.$eventBus.$on('library-changed', this.libraryChanged)
|
||||
this.$eventBus.$on('downloads-loaded', this.downloadsLoaded)
|
||||
|
||||
if (this.$server.socket) {
|
||||
this.$server.socket.on('audiobook_updated', this.audiobookUpdated)
|
||||
this.$server.socket.on('audiobook_added', this.audiobookAdded)
|
||||
this.$server.socket.on('audiobook_removed', this.audiobookRemoved)
|
||||
this.$server.socket.on('audiobooks_updated', this.audiobooksUpdated)
|
||||
this.$server.socket.on('audiobooks_added', this.audiobooksAdded)
|
||||
} else {
|
||||
console.error('Error socket not initialized')
|
||||
}
|
||||
},
|
||||
removeListeners() {
|
||||
this.$server.off('initialized', this.socketInit)
|
||||
// this.$server.off('initialized', this.socketInit)
|
||||
this.$eventBus.$off('library-changed', this.libraryChanged)
|
||||
this.$eventBus.$off('downloads-loaded', this.downloadsLoaded)
|
||||
|
||||
if (this.$server.socket) {
|
||||
this.$server.socket.off('audiobook_updated', this.audiobookUpdated)
|
||||
this.$server.socket.off('audiobook_added', this.audiobookAdded)
|
||||
this.$server.socket.off('audiobook_removed', this.audiobookRemoved)
|
||||
this.$server.socket.off('audiobooks_updated', this.audiobooksUpdated)
|
||||
this.$server.socket.off('audiobooks_added', this.audiobooksAdded)
|
||||
} else {
|
||||
console.error('Error socket not initialized')
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initListeners()
|
||||
if (this.$server.initialized && this.currentLibraryId) {
|
||||
this.fetchCategories()
|
||||
} else {
|
||||
this.shelves = this.downloadOnlyShelves
|
||||
}
|
||||
this.fetchCategories()
|
||||
// if (this.$server.initialized && this.currentLibraryId) {
|
||||
// this.fetchCategories()
|
||||
// } else {
|
||||
// this.shelves = this.downloadOnlyShelves
|
||||
// }
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.removeListeners()
|
||||
|
||||
+4
-152
@@ -10,53 +10,11 @@
|
||||
</div>
|
||||
<p class="hidden absolute short:block top-1.5 left-12 p-2 font-book text-xl">audiobookshelf</p>
|
||||
|
||||
<p class="absolute bottom-16 left-0 right-0 px-2 text-center text-error"><strong>Important!</strong> This app requires that you are running <u>your own server</u> and does not provide any content.</p>
|
||||
<!-- <p class="absolute bottom-16 left-0 right-0 px-2 text-center text-error"><strong>Important!</strong> This app requires that you are running <u>your own server</u> and does not provide any content.</p> -->
|
||||
|
||||
<div class="w-full max-w-md mx-auto px-4 sm:px-6 lg:px-8 z-10">
|
||||
<div v-show="loggedIn" class="mt-8 bg-primary overflow-hidden shadow rounded-lg p-6 text-center">
|
||||
<p class="text-success text-xl mb-2">Login Success!</p>
|
||||
<p>Connecting socket..</p>
|
||||
</div>
|
||||
<div v-show="!loggedIn" class="mt-8 bg-primary overflow-hidden shadow rounded-lg p-6 w-full">
|
||||
<h2 class="text-lg leading-7 mb-4">Server address</h2>
|
||||
<form v-show="!showAuth" @submit.prevent="submit" novalidate class="w-full">
|
||||
<ui-text-input v-model="serverUrl" :disabled="processing || !networkConnected" placeholder="http://55.55.55.55:13378" type="url" class="w-full sm:w-72 h-10" />
|
||||
<div class="flex justify-end">
|
||||
<ui-btn :disabled="processing || !networkConnected" type="submit" :padding-x="3" class="h-10 mt-4">{{ networkConnected ? 'Submit' : 'No Internet' }}</ui-btn>
|
||||
</div>
|
||||
</form>
|
||||
<template v-if="showAuth">
|
||||
<div class="flex items-center">
|
||||
<p class="">{{ serverUrl }}</p>
|
||||
<div class="flex-grow" />
|
||||
<span class="material-icons" style="font-size: 1.1rem" @click="editServerUrl">edit</span>
|
||||
</div>
|
||||
<div class="w-full h-px bg-gray-200 my-2" />
|
||||
<form @submit.prevent="submitAuth" class="pt-3">
|
||||
<ui-text-input v-model="username" :disabled="processing" placeholder="username" class="w-full my-1 text-lg" />
|
||||
<ui-text-input v-model="password" type="password" :disabled="processing" placeholder="password" class="w-full my-1 text-lg" />
|
||||
|
||||
<ui-btn :disabled="processing || !networkConnected" type="submit" class="mt-1 h-10">{{ networkConnected ? 'Submit' : 'No Internet' }}</ui-btn>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<div v-show="error" class="w-full rounded-lg bg-red-600 bg-opacity-10 border border-error border-opacity-50 py-3 px-2 flex items-center mt-4">
|
||||
<span class="material-icons mr-2 text-error" style="font-size: 1.1rem">warning</span>
|
||||
<p class="text-error">{{ error }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="processing ? 'opacity-100' : 'opacity-0 pointer-events-none'" class="fixed w-full h-full top-0 left-0 bg-black bg-opacity-75 flex items-center justify-center z-30 transition-opacity duration-500">
|
||||
<div>
|
||||
<div class="absolute top-0 left-0 w-full p-6 flex items-center flex-col justify-center z-0 short:hidden">
|
||||
<img src="/Logo.png" class="h-20 w-20 mb-2" />
|
||||
</div>
|
||||
<svg class="animate-spin w-16 h-16" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<connection-server-connect-form :server-connection-configs="serverConnectionConfigs" :last-server-connection-config="lastServerConnectionConfig" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-center pt-4 fixed bottom-4 left-0 right-0">
|
||||
<a href="https://github.com/advplyr/audiobookshelf-app" target="_blank" class="text-sm pr-2">Follow the project on Github</a>
|
||||
<a href="https://github.com/advplyr/audiobookshelf-app" target="_blank"
|
||||
@@ -74,15 +32,7 @@
|
||||
export default {
|
||||
layout: 'blank',
|
||||
data() {
|
||||
return {
|
||||
serverUrl: null,
|
||||
processing: false,
|
||||
showAuth: false,
|
||||
username: null,
|
||||
password: null,
|
||||
error: null,
|
||||
loggedIn: false
|
||||
}
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
networkConnected() {
|
||||
@@ -90,110 +40,12 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async submit() {
|
||||
if (!this.networkConnected) {
|
||||
return
|
||||
}
|
||||
if (!this.serverUrl.startsWith('http')) {
|
||||
this.serverUrl = 'http://' + this.serverUrl
|
||||
}
|
||||
this.processing = true
|
||||
this.error = null
|
||||
var response = await this.$server.check(this.serverUrl)
|
||||
this.processing = false
|
||||
if (!response || response.error) {
|
||||
console.error('Server invalid')
|
||||
this.error = response ? response.error : 'Invalid Server'
|
||||
} else {
|
||||
this.showAuth = true
|
||||
}
|
||||
},
|
||||
async submitAuth() {
|
||||
if (!this.networkConnected) {
|
||||
return
|
||||
}
|
||||
if (!this.username) {
|
||||
this.error = 'Invalid username'
|
||||
return
|
||||
}
|
||||
this.error = null
|
||||
|
||||
this.processing = true
|
||||
var response = await this.$server.login(this.serverUrl, this.username, this.password)
|
||||
this.processing = false
|
||||
if (response.error) {
|
||||
console.error('Login failed')
|
||||
this.error = response.error
|
||||
} else {
|
||||
console.log('Login Success!')
|
||||
this.loggedIn = true
|
||||
}
|
||||
},
|
||||
editServerUrl() {
|
||||
this.error = null
|
||||
this.showAuth = false
|
||||
},
|
||||
redirect() {
|
||||
if (this.$route.query && this.$route.query.redirect) {
|
||||
this.$router.replace(this.$route.query.redirect)
|
||||
} else {
|
||||
this.$router.replace('/bookshelf')
|
||||
}
|
||||
},
|
||||
socketConnected() {
|
||||
console.log('Socket connected')
|
||||
this.redirect()
|
||||
},
|
||||
async init() {
|
||||
await this.$store.dispatch('setupNetworkListener')
|
||||
|
||||
if (!this.$server) {
|
||||
console.error('Invalid server not initialized')
|
||||
return
|
||||
}
|
||||
if (this.$server.connected) {
|
||||
console.warn('Server already connected')
|
||||
return this.redirect()
|
||||
}
|
||||
this.$server.on('connected', this.socketConnected)
|
||||
|
||||
var localServerUrl = await this.$localStore.getServerUrl()
|
||||
var localUserToken = await this.$localStore.getToken()
|
||||
|
||||
if (!this.networkConnected) return
|
||||
|
||||
if (localServerUrl) {
|
||||
this.serverUrl = localServerUrl
|
||||
if (localUserToken) {
|
||||
this.processing = true
|
||||
var response = await this.$server.connect(localServerUrl, localUserToken)
|
||||
if (!response || response.error) {
|
||||
var errorMsg = response ? response.error : 'Unknown Error'
|
||||
this.processing = false
|
||||
this.error = errorMsg
|
||||
if (!this.$server.url) {
|
||||
this.serverUrl = null
|
||||
this.showAuth = false
|
||||
}
|
||||
return
|
||||
}
|
||||
console.log('Server connect success')
|
||||
this.showAuth = true
|
||||
} else {
|
||||
this.submit()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.$server) {
|
||||
console.error('Connected beforeDestroy: No Server')
|
||||
return
|
||||
}
|
||||
this.$server.off('connected', this.socketConnected)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
+1
-1
@@ -277,7 +277,7 @@ export default {
|
||||
}
|
||||
},
|
||||
async init() {
|
||||
this.localFolders = (await this.$db.loadFolders()) || []
|
||||
this.localFolders = (await this.$db.getLocalFolders()) || []
|
||||
AudioDownloader.addListener('onDownloadProgress', this.onDownloadProgress)
|
||||
}
|
||||
},
|
||||
|
||||
+22
-25
@@ -61,7 +61,7 @@ export default {
|
||||
var libraryItemId = params.id
|
||||
var libraryItem = null
|
||||
|
||||
if (app.$server.connected) {
|
||||
if (store.state.user.serverConnectionConfig) {
|
||||
libraryItem = await app.$axios.$get(`/api/items/${libraryItemId}?expanded=1`).catch((error) => {
|
||||
console.error('Failed', error)
|
||||
return false
|
||||
@@ -218,10 +218,10 @@ export default {
|
||||
// }
|
||||
},
|
||||
async clearProgressClick() {
|
||||
if (!this.$server.connected) {
|
||||
this.$toast.info('Clear downloaded book progress not yet implemented')
|
||||
return
|
||||
}
|
||||
// if (!this.$server.connected) {
|
||||
// this.$toast.info('Clear downloaded book progress not yet implemented')
|
||||
// return
|
||||
// }
|
||||
|
||||
const { value } = await Dialog.confirm({
|
||||
title: 'Confirm',
|
||||
@@ -264,9 +264,6 @@ export default {
|
||||
this.download()
|
||||
},
|
||||
async download(selectedLocalFolder = null) {
|
||||
console.log('downloadClick ' + this.$server.connected + ' | ' + !!this.downloadObj)
|
||||
if (!this.$server.connected) return
|
||||
|
||||
if (!this.numTracks || this.downloadObj) {
|
||||
return
|
||||
}
|
||||
@@ -274,7 +271,7 @@ export default {
|
||||
// Get the local folder to download to
|
||||
var localFolder = selectedLocalFolder
|
||||
if (!localFolder) {
|
||||
var localFolders = (await this.$db.loadFolders()) || []
|
||||
var localFolders = (await this.$db.getLocalFolders()) || []
|
||||
console.log('Local folders loaded', localFolders.length)
|
||||
var foldersWithMediaType = localFolders.filter((lf) => {
|
||||
console.log('Checking local folder', lf.mediaType)
|
||||
@@ -468,24 +465,24 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (!this.$server.socket) {
|
||||
console.warn('Library Item 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.$server.socket.on('item_updated', this.itemUpdated)
|
||||
}
|
||||
// if (!this.$server.socket) {
|
||||
// console.warn('Library Item 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.$server.socket.on('item_updated', this.itemUpdated)
|
||||
// }
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.$server.socket) {
|
||||
console.warn('Library Item 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.$server.socket.off('item_updated', this.itemUpdated)
|
||||
}
|
||||
// if (!this.$server.socket) {
|
||||
// console.warn('Library Item 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.$server.socket.off('item_updated', this.itemUpdated)
|
||||
// }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -82,7 +82,7 @@ export default {
|
||||
this.$router.push(`/localMedia/folders/${folderObj.id}?scan=1`)
|
||||
},
|
||||
async init() {
|
||||
this.localFolders = (await this.$db.loadFolders()) || []
|
||||
this.localFolders = (await this.$db.getLocalFolders()) || []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
<p v-if="bookResults.length" class="font-semibold text-sm mb-1">Books</p>
|
||||
<template v-for="bookResult in bookResults">
|
||||
<div :key="bookResult.audiobook.id" class="w-full h-16 py-1">
|
||||
<nuxt-link :to="`/audiobook/${bookResult.audiobook.id}`">
|
||||
<nuxt-link :to="`/item/${bookResult.audiobook.id}`">
|
||||
<cards-book-search-card :audiobook="bookResult.audiobook" :search="lastSearch" :match-key="bookResult.matchKey" :match-text="bookResult.matchText" />
|
||||
</nuxt-link>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user