Change:Sleep timer shake to extend time 15m and fade out audio #44 #10, Change:Audio player show time remaining accounting for current playback speed #40, Fix:highlighted current chapter, Update api endpoints

This commit is contained in:
advplyr
2021-11-26 18:27:18 -06:00
parent d3343d722f
commit edc45addc9
17 changed files with 272 additions and 117 deletions
+46 -10
View File
@@ -32,8 +32,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
</svg>
<div v-else class="h-7 w-7 flex items-center justify-around cursor-pointer" @click.stop="$emit('showSleepTimer')">
<p v-if="sleepTimerEndOfChapterTime" class="text-lg font-mono text-warning">-{{ $secondsToTimestamp(timeLeftInChapter) }}</p>
<p v-else class="text-xl font-mono text-success">{{ Math.ceil(sleepTimeoutCurrentTime / 1000 / 60) }}m</p>
<p class="text-xl font-mono text-success">{{ sleepTimeRemainingPretty }}</p>
</div>
<span class="material-icons text-3xl text-white cursor-pointer" :class="chapters.length ? 'text-opacity-75' : 'text-opacity-10'" @click="$emit('selectChapter')">format_list_bulleted</span>
@@ -54,17 +53,17 @@
</div>
<div id="playerTrack" class="absolute bottom-0 left-0 w-full px-3">
<div ref="track" class="h-2 w-full bg-gray-500 bg-opacity-50 relative" :class="loading ? 'animate-pulse' : ''" @click.stop="clickTrack">
<div ref="track" class="h-2 w-full bg-gray-500 bg-opacity-50 relative" :class="loading ? 'animate-pulse' : ''" @click="clickTrack">
<div ref="readyTrack" class="h-full bg-gray-600 absolute top-0 left-0 pointer-events-none" />
<div ref="bufferTrack" class="h-full bg-gray-400 absolute top-0 left-0 pointer-events-none" />
<div ref="playedTrack" class="h-full bg-gray-200 absolute top-0 left-0 pointer-events-none" />
</div>
<div class="flex pt-0.5">
<p class="font-mono text-sm" ref="currentTimestamp">0:00</p>
<p class="font-mono text-white text-opacity-90" style="font-size: 0.8rem" ref="currentTimestamp">0:00</p>
<div class="flex-grow" />
<p v-show="showFullscreen" class="text-sm truncate text-white text-opacity-75" style="max-width: 65%">{{ currentChapterTitle }}</p>
<div class="flex-grow" />
<p class="font-mono text-sm">{{ totalDurationPretty }}</p>
<p class="font-mono text-white text-opacity-90" style="font-size: 0.8rem">{{ timeRemainingPretty }}</p>
</div>
</div>
</div>
@@ -76,6 +75,7 @@ import MyNativeAudio from '@/plugins/my-native-audio'
export default {
props: {
playing: Boolean,
audiobook: {
type: Object,
default: () => {}
@@ -90,8 +90,7 @@ export default {
},
loading: Boolean,
sleepTimerRunning: Boolean,
sleepTimeoutCurrentTime: Number,
sleepTimerEndOfChapterTime: Number
sleepTimerEndTime: Number
},
data() {
return {
@@ -127,6 +126,14 @@ export default {
}
},
computed: {
isPlaying: {
get() {
return this.playing
},
set(val) {
this.$emit('update:playing', val)
}
},
book() {
return this.audiobook.book || {}
},
@@ -156,9 +163,31 @@ export default {
totalDurationPretty() {
return this.$secondsToTimestamp(this.totalDuration)
},
timeRemaining() {
return (this.totalDuration - this.currentTime) / this.currentPlaybackRate
},
timeRemainingPretty() {
if (this.timeRemaining < 0) {
return this.$secondsToTimestamp(this.timeRemaining * -1)
}
return '-' + this.$secondsToTimestamp(this.timeRemaining)
},
timeLeftInChapter() {
if (!this.currentChapter) return 0
return this.currentChapter.end - this.currentTime
},
sleepTimeRemaining() {
if (!this.sleepTimerEndTime) return 0
return Math.max(0, this.sleepTimerEndTime / 1000 - this.currentTime)
},
sleepTimeRemainingPretty() {
if (!this.sleepTimeRemaining) return '0s'
var secondsRemaining = Math.round(this.sleepTimeRemaining)
if (secondsRemaining > 91) {
return Math.ceil(secondsRemaining / 60) + 'm'
} else {
return secondsRemaining + 's'
}
}
},
methods: {
@@ -269,9 +298,6 @@ export default {
if (this.loading) return
MyNativeAudio.seekForward({ amount: '10000' })
},
// sendStreamUpdate() {
// this.$emit('updateTime', this.currentTime)
// },
setStreamReady() {
this.readyTrackWidth = this.trackWidth
this.$refs.readyTrack.style.width = this.trackWidth + 'px'
@@ -310,6 +336,7 @@ export default {
console.error('Invalid no played track ref')
return
}
this.$emit('updateTime', this.currentTime)
if (this.seekLoading) {
this.seekLoading = false
@@ -354,6 +381,12 @@ export default {
},
clickTrack(e) {
if (this.loading) return
if (!this.showFullscreen) {
// Track not clickable on mini-player
return
}
if (e) e.stopPropagation()
var offsetX = e.offsetX
var perc = offsetX / this.trackWidth
var time = perc * this.totalDuration
@@ -462,10 +495,12 @@ export default {
play() {
MyNativeAudio.playPlayer()
this.startPlayInterval()
this.isPlaying = true
},
pause() {
MyNativeAudio.pausePlayer()
this.stopPlayInterval()
this.isPlaying = false
},
startPlayInterval() {
this.startListenTimeInterval()
@@ -474,6 +509,7 @@ export default {
this.playInterval = setInterval(async () => {
var data = await MyNativeAudio.getCurrentTime()
this.currentTime = Number((data.value / 1000).toFixed(2))
this.timeupdate()
}, 1000)
},
+24 -52
View File
@@ -3,18 +3,19 @@
<div v-if="audiobook" id="streamContainer">
<app-audio-player
ref="audioPlayer"
:playing.sync="isPlaying"
:audiobook="audiobook"
:download="download"
:loading="isLoading"
:bookmarks="bookmarks"
:sleep-timer-running="isSleepTimerRunning"
:sleep-timer-end-of-chapter-time="sleepTimerEndOfChapterTime"
:sleep-timeout-current-time="sleepTimeoutCurrentTime"
:sleep-timer-end-time="sleepTimerEndTime"
@close="cancelStream"
@sync="sync"
@setTotalDuration="setTotalDuration"
@selectPlaybackSpeed="showPlaybackSpeedModal = true"
@selectChapter="clickChapterBtn"
@updateTime="(t) => (currentTime = t)"
@showSleepTimer="showSleepTimer"
@showBookmarks="showBookmarks"
@hook:mounted="audioPlayerMounted"
@@ -23,7 +24,7 @@
<modals-playback-speed-modal v-model="showPlaybackSpeedModal" :playback-speed.sync="playbackSpeed" @change="changePlaybackSpeed" />
<modals-chapters-modal v-model="showChapterModal" :current-chapter="currentChapter" :chapters="chapters" @select="selectChapter" />
<modals-sleep-timer-modal v-model="showSleepTimerModal" :current-time="sleepTimeoutCurrentTime" :sleep-timer-running="isSleepTimerRunning" :current-end-of-chapter-time="currentEndOfChapterTime" :end-of-chapter-time-set="sleepTimerEndOfChapterTime" @change="selectSleepTimeout" @cancel="cancelSleepTimer" />
<modals-sleep-timer-modal v-model="showSleepTimerModal" :current-time="sleepTimeRemaining" :sleep-timer-running="isSleepTimerRunning" :current-end-of-chapter-time="currentEndOfChapterTime" @change="selectSleepTimeout" @cancel="cancelSleepTimer" />
<modals-bookmarks-modal v-model="showBookmarksModal" :audiobook-id="audiobookId" :bookmarks="bookmarks" :current-time="currentTime" @select="selectBookmark" />
</div>
</template>
@@ -35,6 +36,7 @@ import MyNativeAudio from '@/plugins/my-native-audio'
export default {
data() {
return {
isPlaying: false,
audioPlayerReady: false,
stream: null,
download: null,
@@ -45,10 +47,10 @@ export default {
playbackSpeed: 1,
showChapterModal: false,
currentTime: 0,
sleepTimeoutCurrentTime: 0,
isSleepTimerRunning: false,
sleepTimerEndOfChapterTime: 0,
sleepTimerEndTime: 0,
onSleepTimerEndedListener: null,
onSleepTimerSetListener: null,
sleepInterval: null,
currentEndOfChapterTime: 0,
totalDuration: 0
@@ -138,6 +140,10 @@ export default {
if (this.cover.startsWith('http')) return this.cover
var coverSrc = this.$store.getters['audiobooks/getBookCoverSrc'](this.audiobook)
return coverSrc
},
sleepTimeRemaining() {
if (!this.sleepTimerEndTime) return 0
return Math.max(0, this.sleepTimerEndTime / 1000 - this.currentTime)
}
},
methods: {
@@ -154,16 +160,24 @@ export default {
},
onSleepTimerEnded({ value: currentPosition }) {
this.isSleepTimerRunning = false
if (this.sleepInterval) clearInterval(this.sleepInterval)
if (currentPosition) {
console.log('Sleep Timer Ended Current Position: ' + currentPosition)
var currentTime = Math.floor(currentPosition / 1000)
this.updateTime(currentTime)
}
},
onSleepTimerSet({ value: sleepTimerEndTime }) {
console.log('SLEEP TIMER SET', sleepTimerEndTime)
if (sleepTimerEndTime === 0) {
console.log('Sleep timer canceled')
this.isSleepTimerRunning = false
} else {
this.isSleepTimerRunning = true
}
this.sleepTimerEndTime = sleepTimerEndTime
},
showSleepTimer() {
console.log('show sleep timer')
if (this.currentChapter) {
this.currentEndOfChapterTime = Math.floor(this.currentChapter.end)
} else {
@@ -171,60 +185,16 @@ export default {
}
this.showSleepTimerModal = true
},
async getSleepTimerTime() {
var res = await MyNativeAudio.getSleepTimerTime()
if (res && res.value) {
var time = Number(res.value)
return time - Date.now()
}
return 0
},
async selectSleepTimeout({ time, isChapterTime }) {
console.log('Setting sleep timer', time, isChapterTime)
var res = await MyNativeAudio.setSleepTimer({ time: String(time), isChapterTime })
if (!res.success) {
return this.$toast.error('Sleep timer did not set, invalid time')
}
if (isChapterTime) {
this.sleepTimerEndOfChapterTime = time
this.isSleepTimerRunning = true
} else {
this.sleepTimerEndOfChapterTime = 0
this.setSleepTimeoutTimer(time)
}
},
async cancelSleepTimer() {
console.log('Canceling sleep timer')
await MyNativeAudio.cancelSleepTimer()
this.isSleepTimerRunning = false
this.sleepTimerEndOfChapterTime = 0
if (this.sleepInterval) clearInterval(this.sleepInterval)
},
async syncSleepTimer() {
var time = await this.getSleepTimerTime()
this.setSleepTimeoutTimer(time)
},
setSleepTimeoutTimer(startTime) {
if (this.sleepInterval) clearInterval(this.sleepInterval)
this.sleepTimeoutCurrentTime = startTime
this.isSleepTimerRunning = true
var elapsed = 0
this.sleepInterval = setInterval(() => {
this.sleepTimeoutCurrentTime = Math.max(0, this.sleepTimeoutCurrentTime - 1000)
if (this.sleepTimeoutCurrentTime <= 0) {
clearInterval(this.sleepInterval)
return
}
// Sync with the actual time from android Timer
elapsed++
if (elapsed > 5) {
clearInterval(this.sleepInterval)
this.syncSleepTimer()
}
}, 1000)
},
clickChapterBtn() {
if (!this.chapters.length) return
@@ -477,6 +447,7 @@ export default {
},
mounted() {
this.onSleepTimerEndedListener = MyNativeAudio.addListener('onSleepTimerEnded', this.onSleepTimerEnded)
this.onSleepTimerSetListener = MyNativeAudio.addListener('onSleepTimerSet', this.onSleepTimerSet)
this.playbackSpeed = this.$store.getters['user/getUserSetting']('playbackRate')
console.log(`[AudioPlayerContainer] Init Playback Speed: ${this.playbackSpeed}`)
@@ -487,6 +458,7 @@ export default {
},
beforeDestroy() {
if (this.onSleepTimerEndedListener) this.onSleepTimerEndedListener.remove()
if (this.onSleepTimerSetListener) this.onSleepTimerSetListener.remove()
if (this.$server.socket) {
this.$server.socket.off('stream_open', this.streamOpen)
+1 -1
View File
@@ -68,7 +68,7 @@ export default {
return
}
this.isFetching = true
var results = await this.$axios.$get(`/api/audiobooks?q=${value}`).catch((error) => {
var results = await this.$axios.$get(`/api/books?q=${value}`).catch((error) => {
console.error('Search error', error)
return []
})
+3 -8
View File
@@ -23,8 +23,7 @@
</li>
</ul>
<div v-else class="px-2 py-4">
<p v-if="endOfChapterTimeSet" class="mb-4 text-2xl font-mono text-center">EOC: {{ endOfChapterTimePretty }}</p>
<p v-else class="mb-4 text-2xl font-mono text-center">{{ timeRemainingPretty }}</p>
<p class="mb-4 text-2xl font-mono text-center">{{ timeRemainingPretty }}</p>
<ui-btn @click="cancelSleepTimer" class="w-full">Cancel Timer</ui-btn>
</div>
</div>
@@ -38,8 +37,7 @@ export default {
value: Boolean,
currentTime: Number,
sleepTimerRunning: Boolean,
currentEndOfChapterTime: Number,
endOfChapterTimeSet: Number
currentEndOfChapterTime: Number
},
data() {
return {}
@@ -57,10 +55,7 @@ export default {
return [1, 15, 30, 45, 60, 75, 90, 120]
},
timeRemainingPretty() {
return this.$secondsToTimestamp(this.currentTime / 1000)
},
endOfChapterTimePretty() {
return this.$secondsToTimestamp(this.endOfChapterTimeSet / 1000)
return this.$secondsToTimestamp(this.currentTime)
}
},
methods: {
@@ -89,7 +89,7 @@ export default {
}
this.isProcessingReadUpdate = true
this.$axios
.$patch(`/api/user/audiobook/${this.book.id}`, updatePayload)
.$patch(`/api/me/audiobook/${this.book.id}`, updatePayload)
.then(() => {
this.isProcessingReadUpdate = false
this.$toast.success(`"${this.bookTitle}" Marked as ${updatePayload.isRead ? 'Read' : 'Not Read'}`)
@@ -104,7 +104,7 @@ export default {
this.processingRemove = true
this.$axios
.$delete(`/api/collection/${this.collectionId}/book/${this.book.id}`)
.$delete(`/api/collections/${this.collectionId}/book/${this.book.id}`)
.then((updatedCollection) => {
console.log(`Book removed from collection`, updatedCollection)
this.$toast.success('Book removed from collection')