add shuffle and repeat toggle

This commit is contained in:
Thomas Amland 2020-10-09 11:37:38 +02:00
parent 554d58f04b
commit c751194c16
4 changed files with 75 additions and 10 deletions

View File

@ -44,10 +44,21 @@
</b-button> </b-button>
</div> </div>
<!-- Time ---> <!-- Controls right --->
<div class="col p-0 d-none d-sm-block" style="min-width: 0; width: 0;"> <div class="col p-0 d-none d-sm-block " style="min-width: 0; width: 0;">
<div v-if="track" class="pr-3 text-right text-truncate"> <div class="d-flex justify-content-end pr-3">
{{ $formatDuration(currentTime) }} / {{ $formatDuration(duration) }} <b-button variant="link"
class="m-0 d-none d-sm-inline-block"
:class="{ 'text-primary': shuffleActive }"
@click="toggleShuffle">
<Icon icon="shuffle" />
</b-button>
<b-button variant="link"
class="m-0 d-none d-sm-inline-block "
:class="{ 'text-primary': repeatActive }"
@click="toggleRepeat">
<Icon icon="arrow-repeat" />
</b-button>
</div> </div>
</div> </div>
</div> </div>
@ -81,7 +92,8 @@
...mapState('player', { ...mapState('player', {
isPlaying: (state: any) => state.isPlaying, isPlaying: (state: any) => state.isPlaying,
currentTime: (state: any) => state.currentTime, currentTime: (state: any) => state.currentTime,
duration: (state: any) => state.duration, repeatActive: (state: any) => state.repeat,
shuffleActive: (state: any) => state.shuffle,
visible: (state: any) => state.queue.length > 0, visible: (state: any) => state.queue.length > 0,
}), }),
...mapGetters('player', [ ...mapGetters('player', [
@ -94,6 +106,8 @@
'playPause', 'playPause',
'next', 'next',
'previous', 'previous',
'toggleRepeat',
'toggleShuffle',
]), ]),
seek(event: any) { seek(event: any) {
if (event.target) { if (event.target) {

View File

@ -1,5 +1,5 @@
import { Store, Module } from 'vuex' import { Store, Module } from 'vuex'
import { trackListEquals } from '@/shared/utils' import { shuffle, trackListEquals } from '@/shared/utils'
const audio = new Audio() const audio = new Audio()
const storedQueue = JSON.parse(localStorage.getItem('queue') || '[]') const storedQueue = JSON.parse(localStorage.getItem('queue') || '[]')
@ -15,6 +15,8 @@ interface State {
isPlaying: boolean; isPlaying: boolean;
duration: number; // duration of current track in seconds duration: number; // duration of current track in seconds
currentTime: number; // position of current track in seconds currentTime: number; // position of current track in seconds
repeat: boolean;
shuffle: boolean;
} }
export const playerModule: Module<State, any> = { export const playerModule: Module<State, any> = {
@ -25,6 +27,8 @@ export const playerModule: Module<State, any> = {
isPlaying: false, isPlaying: false,
duration: 0, duration: 0,
currentTime: 0, currentTime: 0,
repeat: localStorage.getItem('player.repeat') !== 'false',
shuffle: localStorage.getItem('player.shuffle') === 'true',
}, },
mutations: { mutations: {
@ -43,6 +47,14 @@ export const playerModule: Module<State, any> = {
setPosition(state, time: number) { setPosition(state, time: number) {
audio.currentTime = time audio.currentTime = time
}, },
setRepeat(state, enable) {
state.repeat = enable
localStorage.setItem('player.repeat', enable)
},
setShuffle(state, enable) {
state.shuffle = enable
localStorage.setItem('player.shuffle', enable)
},
setQueue(state, queue) { setQueue(state, queue) {
state.queue = queue state.queue = queue
state.queueIndex = -1 state.queueIndex = -1
@ -89,12 +101,25 @@ export const playerModule: Module<State, any> = {
actions: { actions: {
async playTrackList({ commit, state }, { tracks, index }) { async playTrackList({ commit, state }, { tracks, index }) {
if (!trackListEquals(state.queue, tracks)) { if (trackListEquals(state.queue, tracks)) {
commit('setQueue', [...tracks])
}
commit('setQueueIndex', index) commit('setQueueIndex', index)
commit('setPlaying') commit('setPlaying')
await audio.play() await audio.play()
return
}
tracks = [...tracks]
if (state.shuffle) {
const selected = tracks[index]
tracks.splice(index, 1)
tracks = [selected, ...shuffle(tracks)]
commit('setQueue', tracks)
commit('setQueueIndex', 0)
} else {
commit('setQueue', tracks)
commit('setQueueIndex', index)
}
commit('setPlaying')
await audio.play()
}, },
async resume({ commit }) { async resume({ commit }) {
commit('setPlaying') commit('setPlaying')
@ -125,6 +150,16 @@ export const playerModule: Module<State, any> = {
commit('setPosition', state.duration * value) commit('setPosition', state.duration * value)
} }
}, },
resetQueue({ commit }) {
audio.pause()
commit('setQueueIndex', 0)
commit('setPaused')
},
toggleRepeat({ commit, state }) {
commit('setRepeat', !state.repeat)
},
toggleShuffle({ commit, state }) {
commit('setShuffle', !state.shuffle)
}, },
addToQueue({ commit }, track) { addToQueue({ commit }, track) {
commit('addToQueue', track) commit('addToQueue', track)
@ -167,7 +202,11 @@ export function setupAudio(store: Store<any>) {
store.commit('player/setDuration', audio.duration) store.commit('player/setDuration', audio.duration)
} }
audio.onended = () => { audio.onended = () => {
if (store.state.repeat) {
store.dispatch('player/next') store.dispatch('player/next')
} else {
store.dispatch('player/resetQueue')
}
} }
audio.onerror = () => { audio.onerror = () => {
store.commit('player/setPaused') store.commit('player/setPaused')

View File

@ -5,6 +5,7 @@
import Vue from 'vue' import Vue from 'vue'
import { import {
BIcon, BIcon,
BIconArrowRepeat,
BIconBoxArrowUpRight, BIconBoxArrowUpRight,
BIconBroadcast, BIconBroadcast,
BIconCardText, BIconCardText,
@ -17,6 +18,7 @@
BIconList, BIconList,
BIconPlayFill, BIconPlayFill,
BIconPauseFill, BIconPauseFill,
BIconShuffle,
BIconSkipStartFill, BIconSkipStartFill,
BIconSkipEndFill, BIconSkipEndFill,
BIconPlus, BIconPlus,
@ -30,6 +32,7 @@
export default Vue.extend({ export default Vue.extend({
components: { components: {
BIcon, BIcon,
BIconArrowRepeat,
BIconBoxArrowUpRight, BIconBoxArrowUpRight,
BIconBroadcast, BIconBroadcast,
BIconCardText, BIconCardText,
@ -42,6 +45,7 @@
BIconList, BIconList,
BIconPlayFill, BIconPlayFill,
BIconPauseFill, BIconPauseFill,
BIconShuffle,
BIconSkipStartFill, BIconSkipStartFill,
BIconSkipEndFill, BIconSkipEndFill,
BIconPlus, BIconPlus,

View File

@ -9,6 +9,14 @@ export function randomString(): string {
return String.fromCharCode.apply(null, Array.from(arr)) return String.fromCharCode.apply(null, Array.from(arr))
} }
export function shuffle(a: any[]) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]]
}
return a
}
export function md5(str: string): string { export function md5(str: string): string {
return MD5.hash(str) return MD5.hash(str)
} }