Finish lazy bookshelf,Fix:Async sql db,Fix:Load user progress

This commit is contained in:
advplyr
2021-12-05 18:31:47 -06:00
parent 37d3021302
commit 3232b519d3
42 changed files with 613 additions and 1878 deletions
+1 -40
View File
@@ -14,14 +14,9 @@
</svg>
<p class="text-lg font-book leading-4 ml-2">{{ currentLibraryName }}</p>
</div>
<!-- <p class="text-lg font-book leading-4">AudioBookshelf</p> -->
</div>
<div class="flex-grow" />
<!-- <ui-menu :label="username" :items="menuItems" @action="menuAction" class="ml-5" /> -->
<!-- <span class="material-icons cursor-pointer mx-4" :class="hasDownloadsFolder ? '' : 'text-warning'" @click="$store.commit('downloads/setShowModal', true)">source</span> -->
<nuxt-link class="h-7 mx-2" to="/search">
<span class="material-icons" style="font-size: 1.75rem">search</span>
</nuxt-link>
@@ -36,19 +31,7 @@
<script>
export default {
data() {
return {
menuItems: [
{
value: 'account',
text: 'Account',
to: '/account'
},
{
value: 'logout',
text: 'Logout'
}
]
}
return {}
},
computed: {
socketConnected() {
@@ -68,16 +51,6 @@ export default {
},
username() {
return this.user ? this.user.username : 'err'
},
appListingUrl() {
if (this.$platform === 'android') {
return process.env.ANDROID_APP_URL
} else {
return process.env.IOS_APP_URL
}
},
hasDownloadsFolder() {
return !!this.$store.state.downloadFolder
}
},
methods: {
@@ -93,18 +66,6 @@ export default {
} else {
this.$router.push('/bookshelf')
}
},
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() {}
+20 -2
View File
@@ -12,9 +12,9 @@
</div>
</div>
<div class="cover-wrapper absolute z-30 pointer-events-auto" @click="clickContainer">
<div class="cover-wrapper absolute z-30 pointer-events-auto" :class="bookCoverAspectRatio === 1 ? 'square-cover' : ''" @click="clickContainer">
<div class="cover-container bookCoverWrapper bg-black bg-opacity-75 w-full h-full">
<cards-player-book-cover :audiobook="audiobook" :download-cover="downloadedCover" :width="showFullscreen ? 200 : 60" />
<covers-book-cover :audiobook="audiobook" :download-cover="downloadedCover" :width="bookCoverWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" />
</div>
</div>
@@ -134,6 +134,15 @@ export default {
this.$emit('update:playing', val)
}
},
bookCoverAspectRatio() {
return this.$store.getters['getBookCoverAspectRatio']
},
bookCoverWidth() {
if (this.bookCoverAspectRatio === 1) {
return this.showFullscreen ? 260 : 60
}
return this.showFullscreen ? 200 : 60
},
book() {
return this.audiobook.book || {}
},
@@ -641,6 +650,9 @@ export default {
transition-property: left, bottom, width, height;
transform-origin: left bottom;
}
.cover-wrapper.square-cover {
height: 60px;
}
.title-author-texts {
transition: all 0.15s cubic-bezier(0.39, 0.575, 0.565, 1);
@@ -719,6 +731,12 @@ export default {
height: 320px;
width: 200px;
}
.fullscreen .cover-wrapper.square-cover {
height: 260px;
width: 260px;
left: calc(50% - 130px);
}
.fullscreen #playerControls {
width: 100%;
bottom: 100px;
+12 -3
View File
@@ -113,9 +113,6 @@ export default {
cover() {
return this.book ? this.book.cover : ''
},
downloadedCover() {
return this.download ? this.download.cover : null
},
series() {
return this.book ? this.book.series : ''
},
@@ -361,6 +358,7 @@ export default {
if (this.$refs.audioPlayer) {
this.$refs.audioPlayer.terminateStream()
}
this.download = null
}
this.lastProgressTimeUpdate = 0
@@ -450,6 +448,15 @@ export default {
this.$server.socket.on('stream_progress', this.streamProgress)
this.$server.socket.on('stream_ready', this.streamReady)
this.$server.socket.on('stream_reset', this.streamReset)
},
closeStreamOnly() {
// If user logs out or disconnects from server, close audio if streaming
if (!this.download) {
this.$store.commit('setStreamAudiobook', null)
if (this.$refs.audioPlayer) {
this.$refs.audioPlayer.terminateStream()
}
}
}
},
mounted() {
@@ -460,6 +467,7 @@ export default {
console.log(`[AudioPlayerContainer] Init Playback Speed: ${this.playbackSpeed}`)
this.setListeners()
this.$eventBus.$on('close_stream', this.closeStreamOnly)
this.$store.commit('user/addSettingsListener', { id: 'streamContainer', meth: this.settingsUpdated })
this.$store.commit('setStreamListener', this.streamUpdated)
},
@@ -475,6 +483,7 @@ export default {
this.$server.socket.off('stream_reset', this.streamReset)
}
this.$eventBus.$off('close_stream', this.closeStreamOnly)
this.$store.commit('user/removeSettingsListener', 'streamContainer')
this.$store.commit('removeStreamListener')
}
-147
View File
@@ -1,147 +0,0 @@
<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">
<cards-book-card :key="audiobook.id" :audiobook="audiobook" :width="cardWidth" />
</template>
<div class="bookshelfDivider h-4 w-full absolute bottom-0 left-0 right-0 z-10" />
</div>
</template>
<div v-show="!groupedBooks.length" 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 v-show="isLoading" class="absolute top-0 left-0 w-full h-full flex items-center justify-center bg-black bg-opacity-70 z-20">
<div class="py-4">Loading...</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
currFilterOrderKey: null,
groupedBooks: [],
pageWidth: 0
}
},
computed: {
isLoading() {
return this.$store.state.audiobooks.isLoading
},
cardWidth() {
return 140
},
cardHeight() {
return this.cardWidth * 2
},
filterOrderKey() {
return this.$store.getters['user/getFilterOrderKey']
},
hasFilters() {
return this.$store.getters['user/getUserSetting']('mobileFilterBy') !== 'all'
}
},
methods: {
clearFilter() {
this.$store.dispatch('user/updateUserSettings', {
mobileFilterBy: 'all'
})
},
calcShelves() {
var booksPerShelf = Math.floor(this.pageWidth / (this.cardWidth + 32))
var groupedBooks = []
var audiobooksSorted = []
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()
}
},
async loadAudiobooks() {
var currentLibrary = await this.$localStore.getCurrentLibrary()
if (currentLibrary) {
this.$store.commit('libraries/setCurrentLibrary', currentLibrary.id)
}
},
socketConnected(isConnected) {
if (isConnected) {
console.log('Connected - Load from server')
this.loadAudiobooks()
} else {
console.log('Disconnected - Reset to local storage')
this.$store.commit('audiobooks/reset')
this.$store.dispatch('audiobooks/useDownloaded')
// this.calcShelves()
// this.$store.dispatch('downloads/loadFromStorage')
}
}
},
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)
if (!this.$server) {
console.error('Bookshelf mounted no server')
return
}
this.$server.on('connected', this.socketConnected)
if (this.$server.connected) {
this.loadAudiobooks()
} else {
console.log('Bookshelf - Server not connected using downloaded')
}
this.init()
},
beforeDestroy() {
this.$store.commit('audiobooks/removeListener', 'bookshelf')
this.$store.commit('user/removeSettingsListener', 'bookshelf')
window.removeEventListener('resize', this.resize)
if (!this.$server) {
console.error('Bookshelf beforeDestroy no server')
return
}
this.$server.off('connected', this.socketConnected)
}
}
</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>
-135
View File
@@ -1,135 +0,0 @@
<template>
<div id="bookshelf" ref="wrapper" class="w-full overflow-y-auto">
<template v-for="(ab, index) in audiobooks">
<div :key="index" class="border-b border-opacity-10 w-full bookshelfRow py-4 px-2 flex relative">
<app-bookshelf-list-row :audiobook="ab" :card-width="cardWidth" :page-width="pageWidth" />
<div class="bookshelfDivider h-4 w-full absolute bottom-0 left-0 right-0 z-10" />
</div>
</template>
<div v-show="!audiobooks.length" 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 v-show="isLoading" class="absolute top-0 left-0 w-full h-full flex items-center justify-center bg-black bg-opacity-70 z-20">
<div class="py-4">Loading...</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
currFilterOrderKey: null,
pageWidth: 0,
audiobooks: []
}
},
computed: {
isLoading() {
return this.$store.state.audiobooks.isLoading
},
cardWidth() {
return 75
},
cardHeight() {
return this.cardWidth * 2
},
contentRowWidth() {
return this.pageWidth - 16 - this.cardWidth
},
filterOrderKey() {
return this.$store.getters['user/getFilterOrderKey']
},
hasFilters() {
return this.$store.getters['user/getUserSetting']('mobileFilterBy') !== 'all'
}
},
methods: {
clearFilter() {
this.$store.dispatch('user/updateUserSettings', {
mobileFilterBy: 'all'
})
},
calcShelves() {},
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()
}
},
async loadAudiobooks() {
var currentLibrary = await this.$localStore.getCurrentLibrary()
if (currentLibrary) {
this.$store.commit('libraries/setCurrentLibrary', currentLibrary.id)
}
},
socketConnected(isConnected) {
if (isConnected) {
console.log('Connected - Load from server')
this.loadAudiobooks()
} else {
console.log('Disconnected - Reset to local storage')
this.$store.commit('audiobooks/reset')
this.$store.dispatch('audiobooks/useDownloaded')
// this.calcShelves()
// this.$store.dispatch('downloads/loadFromStorage')
}
}
},
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)
if (!this.$server) {
console.error('Bookshelf mounted no server')
return
}
this.$server.on('connected', this.socketConnected)
if (this.$server.connected) {
this.loadAudiobooks()
} else {
console.log('Bookshelf - Server not connected using downloaded')
}
this.init()
},
beforeDestroy() {
this.$store.commit('audiobooks/removeListener', 'bookshelf')
this.$store.commit('user/removeSettingsListener', 'bookshelf')
window.removeEventListener('resize', this.resize)
if (!this.$server) {
console.error('Bookshelf beforeDestroy no server')
return
}
this.$server.off('connected', this.socketConnected)
}
}
</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>
-152
View File
@@ -1,152 +0,0 @@
<template>
<div class="w-full h-full flex">
<cards-book-card :audiobook="audiobook" :width="cardWidth" class="self-end" />
<div class="relative px-2" :style="{ width: contentRowWidth + 'px' }">
<div class="flex">
<nuxt-link :to="`/audiobook/${audiobook.id}`">
<p class="leading-6" style="font-size: 1.1rem">{{ audiobook.book.title }}</p>
</nuxt-link>
<div class="flex-grow" />
<div class="flex items-center">
<!-- <button class="mx-1" @click="editAudiobook(ab)">
<span class="material-icons text-icon pb-px">edit</span>
</button> -->
<button v-if="showRead" class="mx-1 rounded-full w-6 h-6" @click="readBook">
<span class="material-icons">auto_stories</span>
</button>
<button v-if="showPlay" class="mx-1 rounded-full w-6 h-6" @click="playAudiobook">
<span class="material-icons">play_arrow</span>
</button>
</div>
</div>
<p v-if="audiobook.book.subtitle" class="text-gray-200 leading-6 truncate" style="font-size: 0.9rem">{{ audiobook.book.subtitle }}</p>
<p class="text-sm text-gray-200">by {{ audiobook.book.author }}</p>
<div v-if="numTracks" class="flex items-center py-1">
<p class="text-xs text-gray-300">{{ $elapsedPretty(audiobook.duration) }}</p>
<span class="px-3 text-xs text-gray-300"></span>
<p class="text-xs text-gray-300 font-mono">{{ $bytesPretty(audiobook.size, 0) }}</p>
<span class="px-3 text-xs text-gray-300"></span>
<p class="text-xs text-gray-300">{{ numTracks }} tracks</p>
</div>
<div class="flex">
<div v-if="userProgressPercent && !userIsRead" class="w-min my-1">
<div class="bg-primary bg-opacity-70 text-sm px-2 py-px rounded-full whitespace-nowrap">Progress: {{ Math.floor(userProgressPercent * 100) }}%</div>
</div>
<div v-if="isDownloadPlayable" class="w-min my-1 mx-1">
<div class="bg-success bg-opacity-70 text-sm px-2 py-px rounded-full whitespace-nowrap">Downloaded</div>
</div>
<div v-else-if="isDownloading" class="w-min my-1 mx-1">
<div class="bg-warning bg-opacity-70 text-sm px-2 py-px rounded-full whitespace-nowrap">Downloading...</div>
</div>
<div v-if="isPlaying" class="w-min my-1 mx-1">
<div class="bg-info bg-opacity-70 text-sm px-2 py-px rounded-full whitespace-nowrap">{{ isStreaming ? 'Streaming' : 'Playing' }}</div>
</div>
<div v-if="hasEbook" class="w-min my-1 mx-1">
<div class="bg-bg bg-opacity-70 text-sm px-2 py-px rounded-full whitespace-nowrap">{{ ebookFormat }}</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
audiobook: {
type: Object,
default: () => {}
},
cardWidth: {
type: Number,
default: 75
},
pageWidth: Number
},
data() {
return {}
},
computed: {
audiobookId() {
return this.audiobook.id
},
mostRecentUserProgress() {
return this.$store.getters['user/getUserAudiobookData'](this.audiobookId)
},
userProgressPercent() {
return this.mostRecentUserProgress ? this.mostRecentUserProgress.progress || 0 : 0
},
userIsRead() {
return this.mostRecentUserProgress ? !!this.mostRecentUserProgress.isRead : false
},
contentRowWidth() {
return this.pageWidth - 16 - this.cardWidth
},
isDownloading() {
return this.downloadObj ? this.downloadObj.isDownloading : false
},
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)
},
isStreaming() {
return this.$store.getters['isAudiobookStreaming'](this.audiobookId)
},
isPlaying() {
return this.$store.getters['isAudiobookPlaying'](this.audiobookId)
},
isMissing() {
return this.audiobook.isMissing
},
isIncomplete() {
return this.audiobook.isIncomplete
},
numTracks() {
if (this.audiobook.tracks) return this.audiobook.tracks.length
return this.audiobook.numTracks || 0
},
showPlay() {
return !this.isPlaying && !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)
}
},
methods: {
readBook() {
this.$store.commit('openReader', this.audiobook)
},
playAudiobook() {
if (this.isPlaying) {
return
}
this.$store.commit('setPlayOnLoad', true)
if (!this.isDownloadPlayable) {
// Stream
console.log('[PLAYCLICK] Set Playing STREAM ' + this.audiobook.book.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.audiobook.book.title)
this.$store.commit('setPlayingDownload', this.downloadObj)
}
}
},
mounted() {}
}
</script>
-3
View File
@@ -113,9 +113,6 @@ export default {
})
this.$server.logout()
this.$router.push('/connect')
this.$store.commit('audiobooks/reset')
this.$store.dispatch('audiobooks/useDownloaded')
},
touchstart(e) {
this.touchEvent = new TouchEvent(e)