diff --git a/src/player/Player.vue b/src/player/Player.vue index 54a5580..bd54121 100644 --- a/src/player/Player.vue +++ b/src/player/Player.vue @@ -44,10 +44,21 @@ - -
-
- {{ $formatDuration(currentTime) }} / {{ $formatDuration(duration) }} + +
+
+ + + + + +
@@ -81,7 +92,8 @@ ...mapState('player', { isPlaying: (state: any) => state.isPlaying, 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, }), ...mapGetters('player', [ @@ -94,6 +106,8 @@ 'playPause', 'next', 'previous', + 'toggleRepeat', + 'toggleShuffle', ]), seek(event: any) { if (event.target) { diff --git a/src/player/store.ts b/src/player/store.ts index 0da9866..06812ee 100644 --- a/src/player/store.ts +++ b/src/player/store.ts @@ -1,5 +1,5 @@ import { Store, Module } from 'vuex' -import { trackListEquals } from '@/shared/utils' +import { shuffle, trackListEquals } from '@/shared/utils' const audio = new Audio() const storedQueue = JSON.parse(localStorage.getItem('queue') || '[]') @@ -15,6 +15,8 @@ interface State { isPlaying: boolean; duration: number; // duration of current track in seconds currentTime: number; // position of current track in seconds + repeat: boolean; + shuffle: boolean; } export const playerModule: Module = { @@ -25,6 +27,8 @@ export const playerModule: Module = { isPlaying: false, duration: 0, currentTime: 0, + repeat: localStorage.getItem('player.repeat') !== 'false', + shuffle: localStorage.getItem('player.shuffle') === 'true', }, mutations: { @@ -43,6 +47,14 @@ export const playerModule: Module = { setPosition(state, time: number) { 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) { state.queue = queue state.queueIndex = -1 @@ -89,10 +101,23 @@ export const playerModule: Module = { actions: { async playTrackList({ commit, state }, { tracks, index }) { - if (!trackListEquals(state.queue, tracks)) { - commit('setQueue', [...tracks]) + if (trackListEquals(state.queue, tracks)) { + commit('setQueueIndex', index) + commit('setPlaying') + 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('setQueueIndex', index) commit('setPlaying') await audio.play() }, @@ -125,6 +150,16 @@ export const playerModule: Module = { 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) { commit('addToQueue', track) @@ -167,7 +202,11 @@ export function setupAudio(store: Store) { store.commit('player/setDuration', audio.duration) } audio.onended = () => { - store.dispatch('player/next') + if (store.state.repeat) { + store.dispatch('player/next') + } else { + store.dispatch('player/resetQueue') + } } audio.onerror = () => { store.commit('player/setPaused') diff --git a/src/shared/components/Icon.vue b/src/shared/components/Icon.vue index 1a4e71d..67881f1 100644 --- a/src/shared/components/Icon.vue +++ b/src/shared/components/Icon.vue @@ -5,6 +5,7 @@ import Vue from 'vue' import { BIcon, + BIconArrowRepeat, BIconBoxArrowUpRight, BIconBroadcast, BIconCardText, @@ -17,6 +18,7 @@ BIconList, BIconPlayFill, BIconPauseFill, + BIconShuffle, BIconSkipStartFill, BIconSkipEndFill, BIconPlus, @@ -30,6 +32,7 @@ export default Vue.extend({ components: { BIcon, + BIconArrowRepeat, BIconBoxArrowUpRight, BIconBroadcast, BIconCardText, @@ -42,6 +45,7 @@ BIconList, BIconPlayFill, BIconPauseFill, + BIconShuffle, BIconSkipStartFill, BIconSkipEndFill, BIconPlus, diff --git a/src/shared/utils.ts b/src/shared/utils.ts index cf4817d..13a371c 100644 --- a/src/shared/utils.ts +++ b/src/shared/utils.ts @@ -9,6 +9,14 @@ export function randomString(): string { 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 { return MD5.hash(str) }