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)
}