[web-src] Add seek forward/backward buttons for podcasts/audiobooks

This commit is contained in:
chme 2020-04-17 06:23:28 +02:00
parent 45e7816637
commit bbacf3e406
7 changed files with 127 additions and 127 deletions

View File

@ -19,8 +19,10 @@
<!-- Skip previous (not visible on "now playing" page) -->
<player-button-previous v-if="is_now_playing_page" class="navbar-item fd-margin-left-auto" icon_style="mdi-24px"></player-button-previous>
<player-button-seek-back v-if="is_now_playing_page" seek_ms="10000" class="navbar-item" icon_style="mdi-24px"></player-button-seek-back>
<!-- Play/pause -->
<player-button-play-pause class="navbar-item" icon_style="mdi-36px" show_disabled_message></player-button-play-pause>
<player-button-seek-forward v-if="is_now_playing_page" seek_ms="30000" class="navbar-item" icon_style="mdi-24px"></player-button-seek-forward>
<!-- Skip next (not visible on "now playing" page) -->
<player-button-next v-if="is_now_playing_page" class="navbar-item" icon_style="mdi-24px"></player-button-next>
@ -99,22 +101,11 @@
<hr class="navbar-divider">
<div class="navbar-item">
<div class="level is-mobile fd-expanded">
<div class="level-left">
<div class="level-item">
<div class="buttons has-addons">
<player-button-previous class="button"></player-button-previous>
<player-button-play-pause class="button"></player-button-play-pause>
<player-button-next class="button"></player-button-next>
</div>
</div>
</div>
<div class="level-right">
<div class="level-item">
<div class="buttons has-addons">
<player-button-repeat class="button"></player-button-repeat>
<player-button-shuffle class="button"></player-button-shuffle>
<player-button-consume class="button"></player-button-consume>
</div>
<div class="level-item">
<div class="buttons has-addons">
<player-button-repeat class="button"></player-button-repeat>
<player-button-shuffle class="button"></player-button-shuffle>
<player-button-consume class="button"></player-button-consume>
</div>
</div>
</div>
@ -166,47 +157,34 @@
</div>
</div>
<!-- Outputs dropdown menu
<div class="navbar-item has-dropdown"
:class="{ 'is-active': show_outputs_menu }">
<a class="navbar-link is-arrowless has-text-centered is-size-7" @click="show_outputs_menu = !show_outputs_menu">
<span class="icon"><i class="mdi mdi-18px" :class="{ 'mdi-chevron-up': !show_outputs_menu, 'mdi-chevron-down': show_outputs_menu }"></i></span>
</a>
<!-- Outputs: speaker volumes -->
<navbar-item-output v-for="output in outputs" :key="output.id" :output="output"></navbar-item-output>
<div class="navbar-dropdown is-right" v-show="show_outputs_menu">
<hr class="navbar-divider">
-->
<!-- Outputs: speaker volumes -->
<navbar-item-output v-for="output in outputs" :key="output.id" :output="output"></navbar-item-output>
<!-- Outputs: stream volume -->
<hr class="navbar-divider">
<div class="navbar-item">
<div class="level is-mobile">
<div class="level-left fd-expanded">
<div class="level-item" style="flex-grow: 0;">
<a class="button is-white is-small" :class="{ 'is-loading': loading }"><span class="icon fd-has-action" :class="{ 'has-text-grey-light': !playing && !loading, 'is-loading': loading }" @click="togglePlay"><i class="mdi mdi-18px mdi-radio-tower"></i></span></a>
</div>
<div class="level-item fd-expanded">
<div class="fd-expanded">
<p class="heading" :class="{ 'has-text-grey-light': !playing }">HTTP stream <a href="/stream.mp3"><span class="is-lowercase">(stream.mp3)</span></a></p>
<range-slider
class="slider fd-has-action"
min="0"
max="100"
step="1"
:disabled="!playing"
:value="stream_volume"
@change="set_stream_volume">
</range-slider>
</div>
</div>
<!-- Outputs: stream volume -->
<hr class="navbar-divider">
<div class="navbar-item">
<div class="level is-mobile">
<div class="level-left fd-expanded">
<div class="level-item" style="flex-grow: 0;">
<a class="button is-white is-small" :class="{ 'is-loading': loading }"><span class="icon fd-has-action" :class="{ 'has-text-grey-light': !playing && !loading, 'is-loading': loading }" @click="togglePlay"><i class="mdi mdi-18px mdi-radio-tower"></i></span></a>
</div>
<div class="level-item fd-expanded">
<div class="fd-expanded">
<p class="heading" :class="{ 'has-text-grey-light': !playing }">HTTP stream <a href="/stream.mp3"><span class="is-lowercase">(stream.mp3)</span></a></p>
<range-slider
class="slider fd-has-action"
min="0"
max="100"
step="1"
:disabled="!playing"
:value="stream_volume"
@change="set_stream_volume">
</range-slider>
</div>
</div>
</div>
<!-- </div>
</div>
</div>
-->
</div>
</div>
</nav>
@ -223,12 +201,26 @@ import PlayerButtonPrevious from '@/components/PlayerButtonPrevious'
import PlayerButtonShuffle from '@/components/PlayerButtonShuffle'
import PlayerButtonConsume from '@/components/PlayerButtonConsume'
import PlayerButtonRepeat from '@/components/PlayerButtonRepeat'
import PlayerButtonSeekBack from '@/components/PlayerButtonSeekBack'
import PlayerButtonSeekForward from '@/components/PlayerButtonSeekForward'
import RangeSlider from 'vue-range-slider'
import * as types from '@/store/mutation_types'
export default {
name: 'NavbarBottom',
components: { NavbarItemLink, NavbarItemOutput, RangeSlider, PlayerButtonPlayPause, PlayerButtonNext, PlayerButtonPrevious, PlayerButtonShuffle, PlayerButtonConsume, PlayerButtonRepeat },
components: {
NavbarItemLink,
NavbarItemOutput,
RangeSlider,
PlayerButtonPlayPause,
PlayerButtonNext,
PlayerButtonPrevious,
PlayerButtonShuffle,
PlayerButtonConsume,
PlayerButtonRepeat,
PlayerButtonSeekForward,
PlayerButtonSeekBack
},
data () {
return {

View File

@ -0,0 +1,38 @@
<template>
<a @click="seek" :disabled="disabled" v-if="visible">
<span class="icon"><i class="mdi mdi-rewind" :class="icon_style"></i></span>
</a>
</template>
<script>
import webapi from '@/webapi'
export default {
name: 'PlayerButtonSeekBack',
props: ['seek_ms', 'icon_style'],
computed: {
now_playing () {
return this.$store.getters.now_playing
},
is_stopped () {
return this.$store.state.player.state === 'stop'
},
disabled () {
return !this.$store.state.queue || this.$store.state.queue.count <= 0 || this.is_stopped ||
this.now_playing.data_kind === 'pipe'
},
visible () {
return ['podcast', 'audiobook'].includes(this.now_playing.media_kind)
}
},
methods: {
seek: function () {
if (!this.disabled) {
webapi.player_seek(this.seek_ms * -1)
}
}
}
}
</script>

View File

@ -0,0 +1,38 @@
<template>
<a @click="seek" :disabled="disabled" v-if="visible">
<span class="icon"><i class="mdi mdi-fast-forward" :class="icon_style"></i></span>
</a>
</template>
<script>
import webapi from '@/webapi'
export default {
name: 'PlayerButtonSeekForward',
props: ['seek_ms', 'icon_style'],
computed: {
now_playing () {
return this.$store.getters.now_playing
},
is_stopped () {
return this.$store.state.player.state === 'stop'
},
disabled () {
return !this.$store.state.queue || this.$store.state.queue.count <= 0 || this.is_stopped ||
this.now_playing.data_kind === 'pipe'
},
visible () {
return ['podcast', 'audiobook'].includes(this.now_playing.media_kind)
}
},
methods: {
seek: function () {
if (!this.disabled) {
webapi.player_seek(this.seek_ms)
}
}
}
}
</script>

View File

@ -1,36 +0,0 @@
<template>
<a v-on:click="play_skip_back">
<i v-if="is_skip_allowed">
<span class="icon"><i class="mdi mdi-replay"></i></span>
</i>
<i v-else>
<span class="icon has-text-grey-light"><i class="mdi mdi-replay"></i></span>
</i>
</a>
</template>
<script>
import webapi from '@/webapi'
export default {
name: 'PlayerButtonSkipBack',
props: [ 'when_ms' ],
computed: {
is_skip_allowed () {
return this.$store.state.player.state !== 'stop' && this.$store.getters.now_playing && this.$store.getters.now_playing.data_kind !== 'url' && this.$store.getters.now_playing.data_kind !== 'pipe'
}
},
methods: {
play_skip_back: function () {
if (this.is_skip_allowed) {
webapi.player_seek(this.when_ms - 10000)
}
}
}
}
</script>
<style>
</style>

View File

@ -1,36 +0,0 @@
<template>
<a v-on:click="play_skip_fwd">
<i v-if="is_skip_allowed">
<span class="icon"><i class="mdi mdi-flip-h mdi-replay"></i></span>
</i>
<i v-else>
<span class="icon has-text-grey-light"><i class="mdi mdi-flip-h mdi-replay"></i></span>
</i>
</a>
</template>
<script>
import webapi from '@/webapi'
export default {
name: 'PlayerButtonSkipFwd',
props: [ 'when_ms' ],
computed: {
is_skip_allowed () {
return this.$store.state.player.state !== 'stop' && this.$store.getters.now_playing && this.$store.getters.now_playing.data_kind !== 'url' && this.$store.getters.now_playing.data_kind !== 'pipe'
}
},
methods: {
play_skip_fwd: function () {
if (this.is_skip_allowed) {
webapi.player_seek(this.when_ms + 10000)
}
}
}
}
</script>
<style>
</style>

View File

@ -176,7 +176,7 @@ export default {
},
seek: function (newPosition) {
webapi.player_seek(newPosition).catch(() => {
webapi.player_seek_to_pos(newPosition).catch(() => {
this.item_progress_ms = this.state.item_progress_ms
})
},

View File

@ -178,10 +178,14 @@ export default {
return axios.put('/api/player/volume?volume=' + outputVolume + '&output_id=' + outputId)
},
player_seek (newPosition) {
player_seek_to_pos (newPosition) {
return axios.put('/api/player/seek?position_ms=' + newPosition)
},
player_seek (seekMs) {
return axios.put('/api/player/seek?seek_ms=' + seekMs)
},
outputs () {
return axios.get('/api/outputs')
},