Check app version, fix close stream bug, show audiobook progress, add toasts

This commit is contained in:
advplyr 2021-09-04 12:31:00 -05:00
parent 98076927ff
commit 619b6f3686
51 changed files with 551 additions and 141 deletions

View file

@ -16,8 +16,8 @@
<div class="cursor-pointer flex items-center justify-center text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="forward10">
<span class="material-icons text-3xl">forward_10</span>
</div>
<div class="cursor-pointer flex items-center justify-center text-gray-300 ml-8" @mousedown.prevent @mouseup.prevent>
<span class="font-mono text-lg uppercase">1x</span>
<div class="cursor-pointer flex items-center justify-center text-gray-300 ml-7 w-10 text-center" @mousedown.prevent @mouseup.prevent @click="$emit('selectPlaybackSpeed')">
<span class="font-mono text-lg">{{ playbackRate }}x</span>
</div>
</template>
<template v-else>
@ -58,8 +58,9 @@ export default {
data() {
return {
totalDuration: 0,
currentPlaybackRate: 1,
currentTime: 0,
isTerminated: false,
isResetting: false,
initObject: null,
stateName: 'idle',
playInterval: null,
@ -77,17 +78,24 @@ export default {
computed: {
totalDurationPretty() {
return this.$secondsToTimestamp(this.totalDuration)
},
playbackRate() {
return this.$store.getters['user/getUserSetting']('playbackRate')
}
},
methods: {
updatePlaybackRate() {
this.currentPlaybackRate = this.playbackRate
MyNativeAudio.setPlaybackSpeed({ speed: this.playbackRate })
},
restart() {
this.seek(0)
},
backward10() {
MyNativeAudio.seekBackward10()
MyNativeAudio.seekBackward({ amount: '10000' })
},
forward10() {
MyNativeAudio.seekForward10()
MyNativeAudio.seekForward({ amount: '10000' })
},
sendStreamUpdate() {
this.$emit('updateTime', this.currentTime)
@ -191,7 +199,9 @@ export default {
}
},
set(audiobookStreamData) {
this.isResetting = false
this.initObject = { ...audiobookStreamData }
this.currentPlaybackRate = this.initObject.playbackSpeed
MyNativeAudio.initPlayer(this.initObject)
},
setFromObj() {
@ -199,6 +209,7 @@ export default {
console.error('Cannot set from obj')
return
}
this.isResetting = false
MyNativeAudio.initPlayer(this.initObject)
},
play() {
@ -220,13 +231,17 @@ export default {
stopPlayInterval() {
clearInterval(this.playInterval)
},
terminateStream(startTime) {
resetStream(startTime) {
var _time = String(Math.floor(startTime * 1000))
if (!this.initObject) {
console.error('Terminate stream when no init object is set...')
return
}
this.isResetting = true
this.initObject.currentTime = _time
this.terminateStream()
},
terminateStream() {
MyNativeAudio.terminateStream()
},
init() {
@ -245,7 +260,7 @@ export default {
this.currentTime = Number((data.currentTime / 1000).toFixed(2))
this.stateName = data.stateName
if (this.stateName === 'ended' && this.isTerminated) {
if (this.stateName === 'ended' && this.isResetting) {
this.setFromObj()
}

View file

@ -3,11 +3,12 @@
<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}`">
<!-- <div :key="audiobook.id" class="relative px-4"> -->
<cards-book-card :key="audiobook.id" :audiobook="audiobook" :width="cardWidth" :user-progress="userAudiobooks[audiobook.id]" />
<!-- <nuxt-link :to="`/audiobook/${audiobook.id}`">
<cards-book-cover :audiobook="audiobook" :width="cardWidth" class="mx-auto -mb-px" style="box-shadow: 4px 1px 8px #11111166, -4px 1px 8px #11111166, 1px -4px 8px #11111166" />
</nuxt-link>
</div>
</nuxt-link> -->
<!-- </div> -->
</template>
<div class="bookshelfDivider h-4 w-full absolute bottom-0 left-0 right-0 z-10" />
</div>
@ -40,6 +41,9 @@ export default {
},
hasFilters() {
return this.$store.getters['user/getUserSetting']('filterBy') !== 'all'
},
userAudiobooks() {
return this.$store.state.user.user ? this.$store.state.user.user.audiobooks || {} : {}
}
},
methods: {

View file

@ -12,8 +12,9 @@
<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" />
<audio-player-mini ref="audioPlayerMini" :loading="!stream || currStreamAudiobookId !== streamAudiobookId" @updateTime="updateTime" @selectPlaybackSpeed="showPlaybackSpeedModal = true" @hook:mounted="audioPlayerMounted" />
</div>
<modals-playback-speed-modal v-model="showPlaybackSpeedModal" :playback-speed.sync="playbackSpeed" @change="changePlaybackSpeed" />
</div>
</template>
@ -25,7 +26,9 @@ export default {
return {
audioPlayerReady: false,
stream: null,
lastServerUpdateSentSeconds: 0
lastServerUpdateSentSeconds: 0,
showPlaybackSpeedModal: false,
playbackSpeed: 1
}
},
computed: {
@ -85,6 +88,10 @@ export default {
})
if (value) {
this.$server.socket.emit('close_stream')
this.$store.commit('setStreamAudiobook', null)
if (this.$refs.audioPlayerMini) {
this.$refs.audioPlayerMini.terminateStream()
}
}
},
updateTime(currentTime) {
@ -122,7 +129,7 @@ export default {
streamReset({ streamId, startTime }) {
if (this.$refs.audioPlayerMini) {
if (this.stream && this.stream.id === streamId) {
this.$refs.audioPlayerMini.terminateStream(startTime)
this.$refs.audioPlayerMini.resetStream(startTime)
}
}
},
@ -133,10 +140,6 @@ export default {
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
@ -149,6 +152,7 @@ export default {
author: this.author,
playWhenReady: !!playOnLoad,
startTime: String(Math.floor(currentTime * 1000)),
playbackSpeed: this.playbackSpeed || 1,
cover: this.coverForNative,
duration: String(Math.floor(this.duration * 1000)),
series: this.seriesTxt,
@ -156,8 +160,6 @@ export default {
token: this.$store.getters['user/getToken']
}
console.log('audiobook stream data', audiobookStreamData.token, JSON.stringify(audiobookStreamData))
this.$refs.audioPlayerMini.set(audiobookStreamData)
},
audioPlayerMounted() {
@ -177,11 +179,31 @@ 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)
},
changePlaybackSpeed(speed) {
this.$store.dispatch('user/updateUserSettings', { playbackRate: speed })
},
settingsUpdated(settings) {
if (this.$refs.audioPlayerMini && this.$refs.audioPlayerMini.currentPlaybackRate !== settings.playbackRate) {
this.playbackSpeed = settings.playbackRate
this.$refs.audioPlayerMini.updatePlaybackRate()
}
}
},
mounted() {
console.warn('Stream Container Mounted')
this.playbackSpeed = this.$store.getters['user/getUserSetting']('playbackRate')
this.setListeners()
this.$store.commit('user/addSettingsListener', { id: 'streamContainer', meth: this.settingsUpdated })
},
beforeDestroy() {
this.$server.socket.off('stream_open', this.streamOpen)
this.$server.socket.off('stream_closed', this.streamClosed)
this.$server.socket.off('stream_progress', this.streamProgress)
this.$server.socket.off('stream_ready', this.streamReady)
this.$server.socket.off('stream_reset', this.streamReset)
this.$store.commit('user/removeSettingsListener', 'streamContainer')
}
}
</script>

View file

@ -0,0 +1,118 @@
<template>
<div class="relative">
<!-- New Book Flag -->
<div v-if="isNew" class="absolute top-4 left-0 w-4 h-10 pr-2 bg-darkgreen box-shadow-xl">
<div class="absolute top-0 left-0 w-full h-full transform -rotate-90 flex items-center justify-center">
<p class="text-center text-sm">New</p>
</div>
<div class="absolute -bottom-4 left-0 triangle-right" />
</div>
<div class="rounded-sm h-full overflow-hidden relative box-shadow-book">
<nuxt-link :to="`/audiobook/${audiobookId}`" class="cursor-pointer">
<div class="w-full relative" :style="{ height: height + 'px' }">
<cards-book-cover :audiobook="audiobook" :author-override="authorFormat" :width="width" />
<div class="absolute bottom-0 left-0 h-1.5 bg-yellow-400 shadow-sm" :style="{ width: width * userProgressPercent + 'px' }"></div>
<ui-tooltip v-if="showError" :text="errorText" class="absolute bottom-4 left-0">
<div :style="{ height: 1.5 * sizeMultiplier + 'rem', width: 2.5 * sizeMultiplier + 'rem' }" class="bg-error rounded-r-full shadow-md flex items-center justify-end border-r border-b border-red-300">
<span class="material-icons text-red-100 pr-1" :style="{ fontSize: 0.875 * sizeMultiplier + 'rem' }">priority_high</span>
</div>
</ui-tooltip>
</div>
</nuxt-link>
</div>
</div>
</template>
<script>
export default {
props: {
audiobook: {
type: Object,
default: () => null
},
userProgress: {
type: Object,
default: () => null
},
width: {
type: Number,
default: 140
}
},
data() {
return {}
},
computed: {
isNew() {
return this.tags.includes('new')
},
tags() {
return this.audiobook.tags || []
},
audiobookId() {
return this.audiobook.id
},
book() {
return this.audiobook.book || {}
},
height() {
return this.width * 1.6
},
sizeMultiplier() {
return this.width / 120
},
paddingX() {
return 16 * this.sizeMultiplier
},
author() {
return this.book.author
},
authorFL() {
return this.book.authorFL || this.author
},
authorLF() {
return this.book.authorLF || this.author
},
authorFormat() {
if (!this.orderBy || !this.orderBy.startsWith('book.author')) return null
return this.orderBy === 'book.authorLF' ? this.authorLF : this.authorFL
},
orderBy() {
return this.$store.getters['user/getUserSetting']('orderBy')
},
userProgressPercent() {
return this.userProgress ? this.userProgress.progress || 0 : 0
},
showError() {
return this.hasMissingParts || this.hasInvalidParts
},
hasMissingParts() {
return this.audiobook.hasMissingParts
},
hasInvalidParts() {
return this.audiobook.hasInvalidParts
},
errorText() {
var txt = ''
if (this.hasMissingParts) {
txt = `${this.hasMissingParts} missing parts.`
}
if (this.hasInvalidParts) {
if (this.hasMissingParts) txt += ' '
txt += `${this.hasInvalidParts} invalid parts.`
}
return txt || 'Unknown Error'
}
},
methods: {
play() {
this.$store.commit('setStreamAudiobook', this.audiobook)
this.$root.socket.emit('open_stream', this.audiobookId)
}
},
mounted() {}
}
</script>

View file

@ -6,7 +6,7 @@
<span class="material-icons text-4xl">close</span>
</div>
<slot name="outer" />
<div ref="content" style="min-width: 90%; min-height: 200px" class="relative text-white max-h-screen" :style="{ height: modalHeight, width: modalWidth }" v-click-outside="clickBg">
<div ref="content" style="max-width: 90%; min-height: 200px" class="relative text-white max-h-screen" :style="{ height: modalHeight, width: modalWidth }" v-click-outside="clickBg">
<slot />
</div>
</div>

View file

@ -0,0 +1,62 @@
<template>
<modals-modal v-model="show" :width="200" height="100%">
<div class="w-full h-full overflow-hidden absolute top-0 left-0 flex items-center justify-center" @click="show = false">
<div class="w-full overflow-x-hidden overflow-y-auto bg-primary rounded-lg border border-white border-opacity-20" style="max-height: 75%" @click.stop>
<ul class="h-full w-full" role="listbox" aria-labelledby="listbox-label">
<template v-for="rate in rates">
<li :key="rate" class="text-gray-50 select-none relative py-4 pr-9 cursor-pointer hover:bg-black-400" :class="rate === selected ? 'bg-bg bg-opacity-50' : ''" role="option" @click="clickedOption(rate)">
<div class="flex items-center justify-center">
<span class="font-normal ml-3 block truncate text-lg">{{ rate }}x</span>
</div>
</li>
</template>
</ul>
</div>
</div>
</modals-modal>
</template>
<script>
export default {
props: {
value: Boolean,
playbackSpeed: Number
},
data() {
return {}
},
computed: {
show: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
},
selected: {
get() {
return this.playbackSpeed
},
set(val) {
this.$emit('update:playbackSpeed', val)
}
},
rates() {
return [0.25, 0.5, 0.8, 1, 1.3, 1.5, 2, 2.5, 3]
}
},
methods: {
clickedOption(speed) {
if (this.selected === speed) {
this.show = false
return
}
this.selected = speed
this.show = false
this.$nextTick(() => this.$emit('change', speed))
}
},
mounted() {}
}
</script>

View file

@ -1,5 +1,5 @@
<template>
<button class="btn outline-none rounded-md shadow-md relative border border-gray-600" :disabled="loading" :type="type" :class="classList" @click="click">
<button class="btn outline-none rounded-md shadow-md relative border border-gray-600" :disabled="disabled || loading" :type="type" :class="classList" @click="click">
<slot />
<div v-if="loading" class="text-white absolute top-0 left-0 w-full h-full flex items-center justify-center text-opacity-100">
<!-- <span class="material-icons animate-spin">refresh</span> -->
@ -23,7 +23,8 @@ export default {
},
paddingX: Number,
small: Boolean,
loading: Boolean
loading: Boolean,
disabled: Boolean
},
data() {
return {}