mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-03 00:06:46 +01:00
Add:Sleep timer #165
This commit is contained in:
parent
45582343b8
commit
eb82d9c300
@ -1,21 +1,27 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full -mt-6">
|
<div class="w-full -mt-6">
|
||||||
<div class="w-full relative mb-1">
|
<div class="w-full relative mb-1">
|
||||||
<div v-if="chapters.length" class="hidden md:flex absolute right-20 top-0 bottom-0 h-full items-end">
|
<div class="absolute -top-10 md:top-0 right-0 md:right-2 flex items-center h-full">
|
||||||
<div class="cursor-pointer text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="showChapters">
|
<controls-volume-control ref="volumeControl" v-model="volume" @input="setVolume" class="mx-2 hidden md:block" />
|
||||||
|
|
||||||
|
<div class="cursor-pointer text-gray-300 mx-1 md:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showSleepTimer')">
|
||||||
|
<span v-if="!sleepTimerSet" class="material-icons" style="font-size: 1.7rem">snooze</span>
|
||||||
|
<div v-else class="flex items-center">
|
||||||
|
<span class="material-icons text-lg text-warning">snooze</span>
|
||||||
|
<p class="text-xl text-warning font-mono font-semibold text-center px-0.5 pb-0.5" style="min-width: 30px">{{ sleepTimerRemainingString }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cursor-pointer text-gray-300 mx-1 md:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showBookmarks')">
|
||||||
|
<span class="material-icons" style="font-size: 1.7rem">{{ bookmarks.length ? 'bookmarks' : 'bookmark_border' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="chapters.length" class="cursor-pointer text-gray-300 mx-1 md:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="showChapters">
|
||||||
<span class="material-icons text-3xl">format_list_bulleted</span>
|
<span class="material-icons text-3xl">format_list_bulleted</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="absolute top-0 bottom-0 h-full hidden md:flex items-end" :class="chapters.length ? ' right-32' : 'right-20'">
|
|
||||||
<div class="cursor-pointer text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="showBookmarks">
|
|
||||||
<span class="material-icons" style="font-size: 1.7rem">{{ bookmarks.length ? 'bookmarks' : 'bookmark_border' }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="absolute top-0 bottom-0 h-full hidden md:flex items-end" :class="chapters.length ? ' right-44' : 'right-32'">
|
|
||||||
<controls-volume-control ref="volumeControl" v-model="volume" @input="setVolume" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex pb-4 md:pb-2">
|
<div class="flex pt-4 pb-2 md:pt-0 md:pb-2">
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<template v-if="!loading">
|
<template v-if="!loading">
|
||||||
<div class="cursor-pointer flex items-center justify-center text-gray-300 mr-8" @mousedown.prevent @mouseup.prevent @click.stop="restart">
|
<div class="cursor-pointer flex items-center justify-center text-gray-300 mr-8" @mousedown.prevent @mouseup.prevent @click.stop="restart">
|
||||||
@ -91,7 +97,9 @@ export default {
|
|||||||
bookmarks: {
|
bookmarks: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
},
|
||||||
|
sleepTimerSet: Boolean,
|
||||||
|
sleepTimerRemaining: Number
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -110,6 +118,18 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
sleepTimerRemainingString() {
|
||||||
|
var rounded = Math.round(this.sleepTimerRemaining)
|
||||||
|
if (rounded < 90) {
|
||||||
|
return `${rounded}s`
|
||||||
|
}
|
||||||
|
var minutesRounded = Math.round(rounded / 60)
|
||||||
|
if (minutesRounded < 90) {
|
||||||
|
return `${minutesRounded}m`
|
||||||
|
}
|
||||||
|
var hoursRounded = Math.round(minutesRounded / 60)
|
||||||
|
return `${hoursRounded}h`
|
||||||
|
},
|
||||||
token() {
|
token() {
|
||||||
return this.$store.getters['user/getToken']
|
return this.$store.getters['user/getToken']
|
||||||
},
|
},
|
||||||
@ -330,9 +350,6 @@ export default {
|
|||||||
if (!this.chapters.length) return
|
if (!this.chapters.length) return
|
||||||
this.showChaptersModal = !this.showChaptersModal
|
this.showChaptersModal = !this.showChaptersModal
|
||||||
},
|
},
|
||||||
showBookmarks() {
|
|
||||||
this.$emit('showBookmarks', this.currentTime)
|
|
||||||
},
|
|
||||||
init() {
|
init() {
|
||||||
this.playbackRate = this.$store.getters['user/getUserSetting']('playbackRate') || 1
|
this.playbackRate = this.$store.getters['user/getUserSetting']('playbackRate') || 1
|
||||||
this.$emit('setPlaybackRate', this.playbackRate)
|
this.$emit('setPlaybackRate', this.playbackRate)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="streamAudiobook" id="streamContainer" class="w-full fixed bottom-0 left-0 right-0 h-48 sm:h-44 md:h-40 z-40 bg-primary px-4 pb-4 pt-2">
|
<div v-if="streamAudiobook" id="streamContainer" class="w-full fixed bottom-0 left-0 right-0 h-48 sm:h-44 md:h-40 z-40 bg-primary px-4 pb-1 md:pb-4 pt-2">
|
||||||
<nuxt-link :to="`/audiobook/${streamAudiobook.id}`" class="absolute left-4 cursor-pointer" :style="{ top: bookCoverPosTop + 'px' }">
|
<nuxt-link :to="`/audiobook/${streamAudiobook.id}`" class="absolute left-4 cursor-pointer" :style="{ top: bookCoverPosTop + 'px' }">
|
||||||
<covers-book-cover :audiobook="streamAudiobook" :width="bookCoverWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" />
|
<covers-book-cover :audiobook="streamAudiobook" :width="bookCoverWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" />
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
@ -22,12 +22,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<span class="material-icons p-4 cursor-pointer" @click="closePlayer">close</span>
|
<span class="material-icons px-2 py-1 md:p-4 cursor-pointer" @click="closePlayer">close</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<audio-player ref="audioPlayer" :chapters="chapters" :paused="!isPlaying" :loading="playerLoading" :bookmarks="bookmarks" @playPause="playPause" @jumpForward="jumpForward" @jumpBackward="jumpBackward" @setVolume="setVolume" @setPlaybackRate="setPlaybackRate" @seek="seek" @close="closePlayer" @showBookmarks="showBookmarks" />
|
<audio-player
|
||||||
|
ref="audioPlayer"
|
||||||
|
:chapters="chapters"
|
||||||
|
:paused="!isPlaying"
|
||||||
|
:loading="playerLoading"
|
||||||
|
:bookmarks="bookmarks"
|
||||||
|
:sleep-timer-set="sleepTimerSet"
|
||||||
|
:sleep-timer-remaining="sleepTimerRemaining"
|
||||||
|
@playPause="playPause"
|
||||||
|
@jumpForward="jumpForward"
|
||||||
|
@jumpBackward="jumpBackward"
|
||||||
|
@setVolume="setVolume"
|
||||||
|
@setPlaybackRate="setPlaybackRate"
|
||||||
|
@seek="seek"
|
||||||
|
@close="closePlayer"
|
||||||
|
@showBookmarks="showBookmarks"
|
||||||
|
@showSleepTimer="showSleepTimerModal = true"
|
||||||
|
/>
|
||||||
|
|
||||||
<modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :audiobook-id="bookmarkAudiobookId" :current-time="bookmarkCurrentTime" @select="selectBookmark" />
|
<modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :audiobook-id="bookmarkAudiobookId" :current-time="bookmarkCurrentTime" @select="selectBookmark" />
|
||||||
|
|
||||||
|
<modals-sleep-timer-modal v-model="showSleepTimerModal" :timer-set="sleepTimerSet" :timer-time="sleepTimerTime" :remaining="sleepTimerRemaining" @set="setSleepTimer" @cancel="cancelSleepTimer" @increment="incrementSleepTimer" @decrement="decrementSleepTimer" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -44,7 +63,12 @@ export default {
|
|||||||
bookmarkAudiobookId: null,
|
bookmarkAudiobookId: null,
|
||||||
playerLoading: false,
|
playerLoading: false,
|
||||||
isPlaying: false,
|
isPlaying: false,
|
||||||
currentTime: 0
|
currentTime: 0,
|
||||||
|
showSleepTimerModal: false,
|
||||||
|
sleepTimerSet: false,
|
||||||
|
sleepTimerTime: 0,
|
||||||
|
sleepTimerRemaining: 0,
|
||||||
|
sleepTimer: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -111,6 +135,49 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
setSleepTimer(seconds) {
|
||||||
|
this.sleepTimerSet = true
|
||||||
|
this.sleepTimerTime = seconds
|
||||||
|
this.sleepTimerRemaining = seconds
|
||||||
|
this.runSleepTimer()
|
||||||
|
this.showSleepTimerModal = false
|
||||||
|
},
|
||||||
|
runSleepTimer() {
|
||||||
|
var lastTick = Date.now()
|
||||||
|
clearInterval(this.sleepTimer)
|
||||||
|
this.sleepTimer = setInterval(() => {
|
||||||
|
var elapsed = Date.now() - lastTick
|
||||||
|
lastTick = Date.now()
|
||||||
|
this.sleepTimerRemaining -= elapsed / 1000
|
||||||
|
|
||||||
|
if (this.sleepTimerRemaining <= 0) {
|
||||||
|
this.clearSleepTimer()
|
||||||
|
this.playerHandler.pause()
|
||||||
|
this.$toast.info('Sleep Timer Done.. zZzzZz')
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
},
|
||||||
|
cancelSleepTimer() {
|
||||||
|
this.showSleepTimerModal = false
|
||||||
|
this.clearSleepTimer()
|
||||||
|
},
|
||||||
|
clearSleepTimer() {
|
||||||
|
clearInterval(this.sleepTimer)
|
||||||
|
this.sleepTimerRemaining = 0
|
||||||
|
this.sleepTimer = null
|
||||||
|
this.sleepTimerSet = false
|
||||||
|
},
|
||||||
|
incrementSleepTimer(amount) {
|
||||||
|
if (!this.sleepTimerSet) return
|
||||||
|
this.sleepTimerRemaining += amount
|
||||||
|
},
|
||||||
|
decrementSleepTimer(amount) {
|
||||||
|
if (this.sleepTimerRemaining < amount) {
|
||||||
|
this.sleepTimerRemaining = 3
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.sleepTimerRemaining = Math.max(0, this.sleepTimerRemaining - amount)
|
||||||
|
},
|
||||||
playPause() {
|
playPause() {
|
||||||
this.playerHandler.playPause()
|
this.playerHandler.playPause()
|
||||||
},
|
},
|
||||||
@ -146,9 +213,9 @@ export default {
|
|||||||
this.$refs.audioPlayer.setBufferTime(buffertime)
|
this.$refs.audioPlayer.setBufferTime(buffertime)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
showBookmarks(currentTime) {
|
showBookmarks() {
|
||||||
this.bookmarkAudiobookId = this.audiobookId
|
this.bookmarkAudiobookId = this.audiobookId
|
||||||
this.bookmarkCurrentTime = currentTime
|
this.bookmarkCurrentTime = this.currentTime
|
||||||
this.showBookmarksModal = true
|
this.showBookmarksModal = true
|
||||||
},
|
},
|
||||||
selectBookmark(bookmark) {
|
selectBookmark(bookmark) {
|
||||||
|
115
client/components/modals/SleepTimerModal.vue
Normal file
115
client/components/modals/SleepTimerModal.vue
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<template>
|
||||||
|
<modals-modal v-model="show" name="sleep-timer" :width="350" :height="'unset'">
|
||||||
|
<template #outer>
|
||||||
|
<div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden pointer-events-none">
|
||||||
|
<p class="font-book text-3xl text-white truncate pointer-events-none">Sleep Timer</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div ref="container" class="w-full rounded-lg bg-primary box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
|
||||||
|
<div v-if="!timerSet" class="w-full">
|
||||||
|
<template v-for="time in sleepTimes">
|
||||||
|
<div :key="time.text" class="flex items-center px-6 py-3 justify-center cursor-pointer hover:bg-bg relative" @click="setTime(time)">
|
||||||
|
<p class="text-xl text-center">{{ time.text }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div v-else class="w-full p-4">
|
||||||
|
<div class="mb-4 flex items-center justify-center">
|
||||||
|
<ui-btn :padding-x="2" small :disabled="remaining < 30 * 60" class="flex items-center mr-4" @click="decrement(30 * 60)">
|
||||||
|
<span class="material-icons text-lg">remove</span>
|
||||||
|
<span class="pl-1 text-base font-mono">30m</span>
|
||||||
|
</ui-btn>
|
||||||
|
|
||||||
|
<ui-icon-btn icon="remove" @click="decrement(60 * 5)" />
|
||||||
|
|
||||||
|
<p class="mx-6 text-2xl font-mono">{{ $secondsToTimestamp(remaining) }}</p>
|
||||||
|
|
||||||
|
<ui-icon-btn icon="add" @click="increment(60 * 5)" />
|
||||||
|
|
||||||
|
<ui-btn :padding-x="2" small class="flex items-center ml-4" @click="increment(30 * 60)">
|
||||||
|
<span class="material-icons text-lg">add</span>
|
||||||
|
<span class="pl-1 text-base font-mono">30m</span>
|
||||||
|
</ui-btn>
|
||||||
|
</div>
|
||||||
|
<ui-btn class="w-full" @click="$emit('cancel')">Cancel</ui-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</modals-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: Boolean,
|
||||||
|
timerSet: Boolean,
|
||||||
|
timerTime: Number,
|
||||||
|
remaining: Number
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
sleepTimes: [
|
||||||
|
{
|
||||||
|
seconds: 10,
|
||||||
|
text: '10 seconds'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
seconds: 60 * 5,
|
||||||
|
text: '5 minutes'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
seconds: 60 * 30,
|
||||||
|
text: '30 minutes'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
seconds: 60 * 60,
|
||||||
|
text: '60 minutes'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
seconds: 60 * 90,
|
||||||
|
text: '90 minutes'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
seconds: 60 * 120,
|
||||||
|
text: '2 hours'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
seconds: 60 * 180,
|
||||||
|
text: '3 hours'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
show(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
show: {
|
||||||
|
get() {
|
||||||
|
return this.value
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$emit('input', val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setTime(time) {
|
||||||
|
this.$emit('set', time.seconds)
|
||||||
|
},
|
||||||
|
increment(amount) {
|
||||||
|
this.$emit('increment', amount)
|
||||||
|
},
|
||||||
|
decrement(amount) {
|
||||||
|
if (amount > this.remaining) {
|
||||||
|
if (this.remaining > 60) amount = 60
|
||||||
|
else amount = 5
|
||||||
|
}
|
||||||
|
this.$emit('decrement', amount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -255,8 +255,7 @@ export default class PlayerHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
play() {
|
play() {
|
||||||
if (!this.player) return
|
if (this.player) this.player.play()
|
||||||
this.player.play()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pause() {
|
pause() {
|
||||||
|
Loading…
Reference in New Issue
Block a user