mirror of
https://github.com/koel/koel
synced 2024-11-10 14:44:13 +00:00
feat: volume and seek shortcuts
This commit is contained in:
parent
132b91ef19
commit
d579677f2e
6 changed files with 52 additions and 19 deletions
|
@ -137,6 +137,11 @@ or a textarea.
|
|||
* <kbd>j</kbd> plays the next song in queue
|
||||
* <kbd>k</kbd> plays the previous song in queue
|
||||
* <kbd>l</kbd> marks/unmarks the current song as favorite
|
||||
* <kbd>→</kbd> seeks forward 10 seconds
|
||||
* <kbd>←</kbd> seeks backward 10 seconds
|
||||
* <kbd>↑</kbd> increases volume by 10%
|
||||
* <kbd>↓</kbd> decreases volume by 10%
|
||||
* <kbd>m</kbd> mutes/unmutes
|
||||
* <kbd>Cmd/Ctrl</kbd>+<kbd>a</kbd> selects all songs in the current song-list screen when the list is focused
|
||||
* <kbd>Delete</kbd> removes selected song(s) from the current queue/playlist
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import { computed, nextTick, ref, watch } from 'vue'
|
|||
import { eventBus, isAudioContextSupported, requireInjection } from '@/utils'
|
||||
import { CurrentSongKey } from '@/symbols'
|
||||
import { artistStore, preferenceStore } from '@/stores'
|
||||
import { audioService, playbackService, volumeManager } from '@/services'
|
||||
import { audioService, playbackService } from '@/services'
|
||||
|
||||
import AudioPlayer from '@/components/layout/app-footer/AudioPlayer.vue'
|
||||
import SongInfo from '@/components/layout/app-footer/FooterSongInfo.vue'
|
||||
|
@ -57,16 +57,14 @@ const styles = computed(() => {
|
|||
|
||||
const initPlaybackRelatedServices = async () => {
|
||||
const plyrWrapper = document.querySelector<HTMLElement>('.plyr')
|
||||
const volumeInput = document.querySelector<HTMLInputElement>('#volumeInput')
|
||||
|
||||
if (!plyrWrapper || !volumeInput) {
|
||||
if (!plyrWrapper) {
|
||||
await nextTick()
|
||||
await initPlaybackRelatedServices()
|
||||
return
|
||||
}
|
||||
|
||||
playbackService.init(plyrWrapper)
|
||||
volumeManager.init(volumeInput)
|
||||
isAudioContextSupported && audioService.init(playbackService.player.media)
|
||||
}
|
||||
|
||||
|
|
|
@ -23,14 +23,13 @@
|
|||
</span>
|
||||
|
||||
<input
|
||||
id="volumeInput"
|
||||
ref="inputEl"
|
||||
class="plyr__volume"
|
||||
max="10"
|
||||
role="slider"
|
||||
step="0.1"
|
||||
title="Volume"
|
||||
type="range"
|
||||
@change="onVolumeChanged"
|
||||
@input="setVolume"
|
||||
>
|
||||
</span>
|
||||
|
@ -38,10 +37,12 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { faVolumeHigh, faVolumeLow, faVolumeMute } from '@fortawesome/free-solid-svg-icons'
|
||||
import { computed } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { socketService, volumeManager } from '@/services'
|
||||
import { preferenceStore } from '@/stores'
|
||||
import { watchThrottled } from '@vueuse/core'
|
||||
|
||||
const inputEl = ref<HTMLInputElement>()
|
||||
const volume = volumeManager.volume
|
||||
|
||||
const level = computed(() => {
|
||||
|
@ -54,13 +55,12 @@ const mute = () => volumeManager.mute()
|
|||
const unmute = () => volumeManager.unmute()
|
||||
const setVolume = (e: Event) => volumeManager.set(parseFloat((e.target as HTMLInputElement).value))
|
||||
|
||||
/**
|
||||
* Broadcast the volume changed event to remote controller.
|
||||
*/
|
||||
const onVolumeChanged = (e: Event) => {
|
||||
preferenceStore.volume = parseFloat((e.target as HTMLInputElement).value)
|
||||
socketService.broadcast('SOCKET_VOLUME_CHANGED', parseFloat((e.target as HTMLInputElement).value))
|
||||
}
|
||||
watchThrottled(volumeManager.volume, volume => {
|
||||
preferenceStore.volume = volume
|
||||
socketService.broadcast('SOCKET_VOLUME_CHANGED', volume)
|
||||
}, { throttle: 1_000 })
|
||||
|
||||
onMounted(() => volumeManager.init(inputEl.value!, preferenceStore.volume))
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<script lang="ts" setup>
|
||||
import { KeyFilter, onKeyStroke as baseOnKeyStroke } from '@vueuse/core'
|
||||
import { eventBus } from '@/utils'
|
||||
import { playbackService, socketService } from '@/services'
|
||||
import { playbackService, socketService, volumeManager } from '@/services'
|
||||
import { favoriteStore, queueStore } from '@/stores'
|
||||
|
||||
const onKeyStroke = (key: KeyFilter, callback: (e: KeyboardEvent) => void) => {
|
||||
|
@ -21,6 +21,12 @@ onKeyStroke('j', () => playbackService.playNext())
|
|||
onKeyStroke('k', () => playbackService.playPrev())
|
||||
onKeyStroke(' ', () => playbackService.toggle())
|
||||
|
||||
onKeyStroke('ArrowRight', () => playbackService.seekBy(10))
|
||||
onKeyStroke('ArrowLeft', () => playbackService.seekBy(-10))
|
||||
onKeyStroke('ArrowUp', () => volumeManager.increase())
|
||||
onKeyStroke('ArrowDown', () => volumeManager.decrease())
|
||||
onKeyStroke('m', () => volumeManager.toggleMute())
|
||||
|
||||
onKeyStroke('l', () => {
|
||||
if (!queueStore.current) return
|
||||
favoriteStore.toggleOne(queueStore.current)
|
||||
|
|
|
@ -304,6 +304,12 @@ class PlaybackService {
|
|||
this.pause()
|
||||
}
|
||||
|
||||
public seekBy (seconds: number) {
|
||||
if (this.player.media.duration) {
|
||||
this.player.media.currentTime += seconds
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue up songs (replace them into the queue) and start playing right away.
|
||||
*
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { ref } from 'vue'
|
||||
import { preferenceStore } from '@/stores'
|
||||
|
||||
export class VolumeManager {
|
||||
private input!: HTMLInputElement
|
||||
private originalVolume = 0
|
||||
public volume = ref(0)
|
||||
|
||||
public init (input: HTMLInputElement) {
|
||||
public init (input: HTMLInputElement, initialVolume: number) {
|
||||
this.input = input
|
||||
this.set(preferenceStore.volume)
|
||||
this.originalVolume = initialVolume
|
||||
this.set(initialVolume)
|
||||
}
|
||||
|
||||
public get () {
|
||||
|
@ -20,11 +21,28 @@ export class VolumeManager {
|
|||
}
|
||||
|
||||
public mute () {
|
||||
this.originalVolume = this.get()
|
||||
this.set(0)
|
||||
}
|
||||
|
||||
public unmute () {
|
||||
this.set(preferenceStore.volume)
|
||||
this.set(this.originalVolume)
|
||||
}
|
||||
|
||||
public toggleMute () {
|
||||
if (this.get() === 0) {
|
||||
this.unmute()
|
||||
} else {
|
||||
this.mute()
|
||||
}
|
||||
}
|
||||
|
||||
public increase (amount = 1) {
|
||||
this.set(Math.min(10, this.get() + amount))
|
||||
}
|
||||
|
||||
public decrease (amount = 1) {
|
||||
this.set(Math.max(this.get() - amount, 0))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue