Initial commit

This commit is contained in:
Thomas Amland
2020-03-01 20:08:02 +01:00
commit b4623926a2
59 changed files with 11280 additions and 0 deletions
+94
View File
@@ -0,0 +1,94 @@
<template>
<div class="d-flex player">
<div v-if="track" class="d-none d-sm-block">
<b-img :src="track.image" block width="80px" height="80px"></b-img>
</div>
<div class="flex-fill">
<!-- Progress --->
<div class="progress2" @click="seek">
<b-progress :value="progress" :max="100" height="4px"></b-progress>
</div>
<div class="row d-flex align-items-center p-2 m-0">
<!-- Track info --->
<div class="col d-flex p-0 text-truncate">
<div class="d-flex align-items-center">
<template v-if="track">
<div class="ml-2 align-middle">
<div>{{ track.title }}</div>
<div>{{ track.artist }}</div>
</div>
</template>
</div>
</div>
<!-- Controls--->
<div class="col-auto p-0">
<b-button variant="link" class="m-2"
:disabled="!hasPrevious" @click="playPrevious">
<Icon>mdi-skip-previous</Icon>
</b-button>
<b-button variant="link" size="lg" class="m-2" @click="playPause">
<Icon>{{ isPlaying ? 'mdi-pause' : 'mdi-play' }}</Icon>
</b-button>
<b-button variant="link" class="m-2"
:disabled="!hasNext" @click="playNext">
<Icon>mdi-skip-next</Icon>
</b-button>
</div>
<div class="col p-0 text-truncate">
<div v-if="track" class="text-right">
<span>{{ currentTime | duration }} / {{ duration | duration }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.progress2 {
cursor: pointer;
}
</style>
<script lang="ts">
import Vue from "vue";
import { mapMutations, mapState, mapGetters, mapActions } from 'vuex';
export default Vue.extend({
data() {
return {
};
},
computed: {
...mapState("player", {
isPlaying: (state: any) => state.isPlaying,
currentTime: (state: any) => state.currentTime,
duration: (state: any) => state.duration,
}),
...mapGetters("player", [
"track",
"progress",
"hasNext",
"hasPrevious",
]),
},
methods: {
...mapMutations("player", [
"playPause",
]),
...mapActions("player", [
"playNext",
"playPrevious",
]),
seek(event: any) {
if (event.target) {
const width = event.target.clientWidth;
const value = event.offsetX / width
this.$store.commit("player/seek", value)
// this.internalValue = e.offsetX / width * 100
}
}
}
});
</script>
+33
View File
@@ -0,0 +1,33 @@
<template>
<div>
<TrackList :tracks="tracks">
<template #context-menu="{index}">
<b-dropdown-item-button @click="remove(index)">
Remove
</b-dropdown-item-button>
</template>
</TrackList>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import { mapState, mapMutations } from 'vuex';
import TrackList from "@/library/TrackList.vue";
export default Vue.extend({
components: {
TrackList,
},
computed: {
...mapState("player", {
tracks: (state: any) => state.queue,
})
},
methods: {
...mapMutations("player", {
play: "playQueueIndex",
remove: "removeFromQueue",
}),
}
});
</script>
+27
View File
@@ -0,0 +1,27 @@
<template>
<div>
<div class="col" d-flex fill-height grow>
<h1>{{ track.name }}</h1>
<div>{{ track.artist }}</div>
<v-card color=blue tile height="100%" width="100%"></v-card>
</div>
</template>
<style scoped lang="scss">
.on-hover {
opacity: 0.6;
}
.show-btns {
color: rgba(255, 255, 255, 1) !important;
}
</style>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
props: {
album: Object
}
});
</script>
+127
View File
@@ -0,0 +1,127 @@
import { Store, Module } from 'vuex'
interface State {
queue: any[];
queueIndex: number;
isPlaying: boolean;
duration: number; // duration of current track in seconds
currentTime: number; // position of current track in seconds
}
const audio = new Audio();
const storedQueue = JSON.parse(localStorage.getItem("queue") || "[]");
const storedQueueIndex = parseInt(localStorage.getItem("queueIndex") || "-1");
if (storedQueueIndex > -1 && storedQueueIndex < storedQueue.length) {
audio.src = storedQueue[storedQueueIndex].url;
}
export const playerModule: Module<State, any> = {
namespaced: true,
state: {
queue: storedQueue,
queueIndex: storedQueueIndex,
isPlaying: false,
duration: 0,
currentTime: 0,
},
mutations: {
playPause(state: State) {
if (state.isPlaying) {
state.isPlaying = false;
audio.pause();
} else {
state.isPlaying = true;
audio.play();
}
},
seek(state, value: number) {
console.log("seek: " + value);
if (state.queueIndex != -1) {
const seconds = state.duration * value;
audio.currentTime = seconds;
}
},
setQueueAndPlay(state, { queue, index }) {
state.queue = queue;
state.queueIndex = index;
localStorage.setItem("queue", JSON.stringify(queue));
localStorage.setItem("queueIndex", index);
state.isPlaying = true;
audio.src = queue[index].url;
audio.play();
},
playQueueIndex(state, index) {
if (state.queue.length === 0) {
return;
}
index = index < state.queue.length ? index : 0;
state.isPlaying = true;
state.queueIndex = index;
audio.src = state.queue[index].url;
audio.play();
},
removeFromQueue(state, index) {
state.queue.splice(index, 1);
if (index < state.queueIndex) {
state.queueIndex--;
}
},
setProgress(state: State, value: any) {
state.currentTime = value;
},
setDuration(state: State, value: any) {
state.duration = value;
},
},
actions: {
play({ commit }, { queue, index }) {
commit('setQueueAndPlay', { index, queue });
},
playNext({ commit, state }) {
commit("playQueueIndex", state.queueIndex + 1);
},
playPrevious({ commit, state }) {
commit("playQueueIndex", state.queueIndex - 1);
},
},
getters: {
track(state) {
if (state.queueIndex != -1) {
return state.queue[state.queueIndex];
}
return null;
},
trackId(state, getters): number {
return getters.track ? getters.track.id : -1;
},
progress(state) {
if (state.currentTime > -1 && state.duration > 0) {
return (state.currentTime / state.duration) * 100;
}
return 0;
},
hasNext(state) {
return state.queueIndex < state.queue.length - 1;
},
hasPrevious(state) {
return state.queueIndex > 0;
},
},
};
export function connectAudioToStore(store: Store<any>) {
audio.ontimeupdate = (event) => {
store.commit("player/setProgress", audio.currentTime)
};
audio.ondurationchange = (event) => {
store.commit("player/setDuration", audio.duration);
}
audio.onended = (event) => {
store.dispatch("player/playNext");
}
}