mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-08 21:08:43 +02:00
init
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div class="w-full h-16 bg-primary relative">
|
||||
<div id="appbar" class="absolute top-0 left-0 w-full h-full z-30 flex items-center px-2">
|
||||
<nuxt-link v-show="!showBack" to="/" class="mr-4">
|
||||
<img src="/Logo.png" class="h-12 w-12" />
|
||||
</nuxt-link>
|
||||
<a v-if="showBack" @click="back" class="rounded-full h-12 w-12 flex items-center justify-center hover:bg-white hover:bg-opacity-10 mr-2 cursor-pointer">
|
||||
<span class="material-icons text-4xl text-white">arrow_back</span>
|
||||
</a>
|
||||
<p class="text-xl font-book">AudioBookshelf</p>
|
||||
<div class="flex-grow" />
|
||||
<ui-menu :label="username" :items="menuItems" @action="menuAction" class="ml-5" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
menuItems: [
|
||||
{
|
||||
value: 'account',
|
||||
text: 'Account',
|
||||
to: '/account'
|
||||
},
|
||||
{
|
||||
value: 'logout',
|
||||
text: 'Logout'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showBack() {
|
||||
return this.$route.name !== 'index'
|
||||
},
|
||||
user() {
|
||||
return this.$store.state.user.user
|
||||
},
|
||||
username() {
|
||||
return this.user ? this.user.username : 'err'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
back() {
|
||||
if (this.$route.name === 'audiobook-id-edit') {
|
||||
this.$router.push(`/audiobook/${this.$route.params.id}`)
|
||||
} else {
|
||||
this.$router.push('/')
|
||||
}
|
||||
},
|
||||
logout() {
|
||||
this.$axios.$post('/logout').catch((error) => {
|
||||
console.error(error)
|
||||
})
|
||||
this.$server.logout()
|
||||
this.$router.push('/connect')
|
||||
},
|
||||
menuAction(action) {
|
||||
if (action === 'logout') {
|
||||
this.logout()
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#appbar {
|
||||
box-shadow: 0px 5px 5px #11111155;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div id="bookshelf" ref="wrapper" class="w-full overflow-y-auto">
|
||||
<template v-for="(shelf, index) in groupedBooks">
|
||||
<div :key="index" class="border-b border-opacity-10 w-full bookshelfRow py-4 flex justify-around relative">
|
||||
<template v-for="audiobook in shelf">
|
||||
<div :key="audiobook.id" class="relative px-4">
|
||||
<nuxt-link :to="`/audiobook/${audiobook.id}`">
|
||||
<cards-book-cover :audiobook="audiobook" :width="cardWidth" class="mx-auto -mb-px" />
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</template>
|
||||
<div class="bookshelfDivider h-4 w-full absolute bottom-0 left-0 right-0 z-10" />
|
||||
</div>
|
||||
</template>
|
||||
<div class="w-full py-16 text-center text-xl">
|
||||
<div class="py-4">No Audiobooks</div>
|
||||
<ui-btn v-if="hasFilters" @click="clearFilter">Clear Filter</ui-btn>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
currFilterOrderKey: null,
|
||||
groupedBooks: [],
|
||||
pageWidth: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cardWidth() {
|
||||
return 140
|
||||
},
|
||||
cardHeight() {
|
||||
return this.cardWidth * 2
|
||||
},
|
||||
filterOrderKey() {
|
||||
return this.$store.getters['user/getFilterOrderKey']
|
||||
},
|
||||
hasFilters() {
|
||||
return this.$store.getters['user/getUserSetting']('filterBy') !== 'all'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearFilter() {
|
||||
this.$store.dispatch('user/updateUserSettings', {
|
||||
filterBy: 'all'
|
||||
})
|
||||
},
|
||||
playAudiobook(audiobook) {
|
||||
console.log('Play Audiobook', audiobook)
|
||||
this.$store.commit('setStreamAudiobook', audiobook)
|
||||
this.$server.socket.emit('open_stream', audiobook.id)
|
||||
},
|
||||
calcShelves() {
|
||||
var booksPerShelf = Math.floor(this.pageWidth / (this.cardWidth + 32))
|
||||
var groupedBooks = []
|
||||
|
||||
var audiobooksSorted = this.$store.getters['audiobooks/getFilteredAndSorted']()
|
||||
this.currFilterOrderKey = this.filterOrderKey
|
||||
|
||||
var numGroups = Math.ceil(audiobooksSorted.length / booksPerShelf)
|
||||
for (let i = 0; i < numGroups; i++) {
|
||||
var group = audiobooksSorted.slice(i * booksPerShelf, i * booksPerShelf + 2)
|
||||
groupedBooks.push(group)
|
||||
}
|
||||
this.groupedBooks = groupedBooks
|
||||
},
|
||||
audiobooksUpdated() {
|
||||
this.calcShelves()
|
||||
},
|
||||
init() {
|
||||
if (this.$refs.wrapper) {
|
||||
this.pageWidth = this.$refs.wrapper.clientWidth
|
||||
this.calcShelves()
|
||||
}
|
||||
},
|
||||
resize() {
|
||||
this.init()
|
||||
},
|
||||
settingsUpdated() {
|
||||
if (this.currFilterOrderKey !== this.filterOrderKey) {
|
||||
this.calcShelves()
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$store.commit('audiobooks/addListener', { id: 'bookshelf', meth: this.audiobooksUpdated })
|
||||
this.$store.commit('user/addSettingsListener', { id: 'bookshelf', meth: this.settingsUpdated })
|
||||
|
||||
window.addEventListener('resize', this.resize)
|
||||
this.$store.dispatch('audiobooks/load')
|
||||
this.init()
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$store.commit('audiobooks/removeListener', 'bookshelf')
|
||||
this.$store.commit('user/removeSettingsListener', 'bookshelf')
|
||||
window.removeEventListener('resize', this.resize)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#bookshelf {
|
||||
height: calc(100% - 48px);
|
||||
}
|
||||
.bookshelfRow {
|
||||
background-image: url(/wood_panels.jpg);
|
||||
}
|
||||
.bookshelfDivider {
|
||||
background: rgb(149, 119, 90);
|
||||
background: linear-gradient(180deg, rgba(149, 119, 90, 1) 0%, rgba(103, 70, 37, 1) 17%, rgba(103, 70, 37, 1) 88%, rgba(71, 48, 25, 1) 100%);
|
||||
box-shadow: 2px 14px 8px #111111aa;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<div class="w-full p-4 pointer-events-none fixed bottom-0 left-0 right-0 z-20">
|
||||
<div v-if="streamAudiobook" class="w-full bg-primary absolute bottom-0 left-0 right-0 z-50 p-2 pointer-events-auto" @click.stop @mousedown.stop @mouseup.stop>
|
||||
<div class="pl-16 pr-2 flex items-center pb-2">
|
||||
<div>
|
||||
<p class="px-2">{{ title }}</p>
|
||||
<p class="px-2 text-xs text-gray-400">by {{ author }}</p>
|
||||
</div>
|
||||
<div class="flex-grow" />
|
||||
<span class="material-icons" @click="cancelStream">close</span>
|
||||
</div>
|
||||
<div class="absolute left-2 -top-10">
|
||||
<cards-book-cover :audiobook="streamAudiobook" :width="64" />
|
||||
</div>
|
||||
<audio-player-mini ref="audioPlayerMini" :loading="!stream || currStreamAudiobookId !== streamAudiobookId" @updateTime="updateTime" @hook:mounted="audioPlayerMounted" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Dialog } from '@capacitor/dialog'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
audioPlayerReady: false,
|
||||
stream: null,
|
||||
lastServerUpdateSentSeconds: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
streamAudiobook() {
|
||||
return this.$store.state.streamAudiobook
|
||||
},
|
||||
streamAudiobookId() {
|
||||
return this.streamAudiobook ? this.streamAudiobook.id : null
|
||||
},
|
||||
currStreamAudiobookId() {
|
||||
return this.stream ? this.stream.audiobook.id : null
|
||||
},
|
||||
book() {
|
||||
return this.streamAudiobook ? this.streamAudiobook.book || {} : {}
|
||||
},
|
||||
title() {
|
||||
return this.book ? this.book.title : ''
|
||||
},
|
||||
author() {
|
||||
return this.book ? this.book.author : ''
|
||||
},
|
||||
cover() {
|
||||
return this.book ? this.book.cover : ''
|
||||
},
|
||||
series() {
|
||||
return this.book ? this.book.series : ''
|
||||
},
|
||||
volumeNumber() {
|
||||
return this.book ? this.book.volumeNumber : ''
|
||||
},
|
||||
seriesTxt() {
|
||||
if (!this.series) return ''
|
||||
if (!this.volumeNumber) return this.series
|
||||
return `${this.series} #${this.volumeNumber}`
|
||||
},
|
||||
duration() {
|
||||
return this.streamAudiobook ? this.streamAudiobook.duration || 0 : 0
|
||||
},
|
||||
coverForNative() {
|
||||
if (!this.cover) {
|
||||
return `${this.$store.state.serverUrl}/Logo.png`
|
||||
}
|
||||
if (this.cover.startsWith('http')) return this.cover
|
||||
var _clean = this.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}`
|
||||
}
|
||||
return _clean
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async cancelStream() {
|
||||
const { value } = await Dialog.confirm({
|
||||
title: 'Confirm',
|
||||
message: 'Cancel this stream?'
|
||||
})
|
||||
if (value) {
|
||||
this.$server.socket.emit('close_stream')
|
||||
}
|
||||
},
|
||||
updateTime(currentTime) {
|
||||
var diff = currentTime - this.lastServerUpdateSentSeconds
|
||||
if (diff > 4 || diff < 0) {
|
||||
this.lastServerUpdateSentSeconds = currentTime
|
||||
var updatePayload = {
|
||||
currentTime,
|
||||
streamId: this.stream.id
|
||||
}
|
||||
this.$server.socket.emit('stream_update', updatePayload)
|
||||
}
|
||||
},
|
||||
closeStream() {},
|
||||
streamClosed(audiobookId) {
|
||||
console.log('Stream Closed')
|
||||
|
||||
if (this.stream.audiobook.id === audiobookId || audiobookId === 'n/a') {
|
||||
this.$store.commit('setStreamAudiobook', null)
|
||||
}
|
||||
},
|
||||
streamProgress(data) {
|
||||
if (!data.numSegments) return
|
||||
var chunks = data.chunks
|
||||
if (this.$refs.audioPlayerMini) {
|
||||
this.$refs.audioPlayerMini.setChunksReady(chunks, data.numSegments)
|
||||
}
|
||||
},
|
||||
streamReady() {
|
||||
console.log('[StreamContainer] Stream Ready')
|
||||
if (this.$refs.audioPlayerMini) {
|
||||
this.$refs.audioPlayerMini.setStreamReady()
|
||||
}
|
||||
},
|
||||
streamReset({ streamId, startTime }) {
|
||||
if (this.$refs.audioPlayerMini) {
|
||||
if (this.stream && this.stream.id === streamId) {
|
||||
this.$refs.audioPlayerMini.terminateStream(startTime)
|
||||
}
|
||||
}
|
||||
},
|
||||
streamOpen(stream) {
|
||||
console.log('[StreamContainer] Stream Open', stream)
|
||||
if (!this.$refs.audioPlayerMini) {
|
||||
console.error('No Audio Player Mini')
|
||||
return
|
||||
}
|
||||
|
||||
if (this.stream && this.stream.id !== stream.id) {
|
||||
console.error('STREAM CHANGED', this.stream.id, stream.id)
|
||||
}
|
||||
|
||||
this.stream = stream
|
||||
|
||||
var playlistUrl = stream.clientPlaylistUri
|
||||
var currentTime = stream.clientCurrentTime || 0
|
||||
var playOnLoad = this.$store.state.playOnLoad
|
||||
if (playOnLoad) this.$store.commit('setPlayOnLoad', false)
|
||||
|
||||
var audiobookStreamData = {
|
||||
title: this.title,
|
||||
author: this.author,
|
||||
playWhenReady: !!playOnLoad,
|
||||
startTime: String(Math.floor(currentTime * 1000)),
|
||||
cover: this.coverForNative,
|
||||
duration: String(Math.floor(this.duration * 1000)),
|
||||
series: this.seriesTxt,
|
||||
playlistUrl: this.$server.url + playlistUrl,
|
||||
token: this.$store.getters['user/getToken']
|
||||
}
|
||||
|
||||
console.log('audiobook stream data', audiobookStreamData.token, JSON.stringify(audiobookStreamData))
|
||||
|
||||
this.$refs.audioPlayerMini.set(audiobookStreamData)
|
||||
},
|
||||
audioPlayerMounted() {
|
||||
console.log('Audio Player Mounted', this.$server.stream)
|
||||
this.audioPlayerReady = true
|
||||
if (this.$server.stream) {
|
||||
this.streamOpen(this.$server.stream)
|
||||
}
|
||||
},
|
||||
setListeners() {
|
||||
if (!this.$server.socket) {
|
||||
console.error('Invalid server socket not set')
|
||||
return
|
||||
}
|
||||
this.$server.socket.on('stream_open', this.streamOpen)
|
||||
this.$server.socket.on('stream_closed', this.streamClosed)
|
||||
this.$server.socket.on('stream_progress', this.streamProgress)
|
||||
this.$server.socket.on('stream_ready', this.streamReady)
|
||||
this.$server.socket.on('stream_reset', this.streamReset)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.warn('Stream Container Mounted')
|
||||
this.setListeners()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user