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
+35
View File
@@ -0,0 +1,35 @@
<template>
<b-dropdown variant="link" boundary="window" no-caret toggle-class="p-0">
<template #button-content>
<Icon>mdi-dots-vertical</Icon>
</template>
<b-dropdown-item-button @click="toggleStarred()">
{{ starred ? 'Unstar' : 'Star' }}
</b-dropdown-item-button>
<slot :item="track"></slot>
</b-dropdown>
</template>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
props: {
track: { type: Object, required: true },
},
data() {
return {
starred: this.track.starred,
}
},
methods: {
toggleStarred() {
if (this.starred) {
this.$api.unstar('track', this.track.id);
} else {
this.$api.star('track', this.track.id);
}
this.starred = !this.starred;
},
}
});
</script>
+115
View File
@@ -0,0 +1,115 @@
<template>
<div>
<b-table-simple borderless hover>
<thead>
<tr>
<th class="pl-0 pr-0 text-center text-muted"></th>
<th class="text-left">Title</th>
<th class="text-left">Artist</th>
<th v-if="showAlbum" class="text-left">Album</th>
<th class="text-right">Duration</th>
<th class="text-right">Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in tracks" :key="item.id"
draggable="true" @dragstart="dragstart(item.id, $event)"
:class="{'text-primary': item.id === playingTrackId}">
<td class="pl-0 pr-0 text-center text-muted" style="min-width: 20px; max-width: 20px;">
<span class="track-status">
<template v-if="item.id === playingTrackId">
<Icon>{{ isPlaying ? 'mdi-volume-high' : 'mdi-play' }}</Icon>
</template>
<template v-else>{{ item.track || 1 }}</template>
</span>
<span class="track-status-hover">
<span v-if="item.id === playingTrackId" @click="playPause()">
<Icon>{{ isPlaying ? 'mdi-pause' : 'mdi-play' }}</Icon>
</span>
<span v-else @click="play(index)">
<Icon>mdi-play</Icon>
</span>
</span>
</td>
<td>{{ item.title }}</td>
<td>
<template v-if="item.artistId">
<router-link :to="{name: 'artist', params: {id: item.artistId}}">
{{ item.artist }}
</router-link>
</template>
<template v-else>
{{ item.artist }}
</template>
</td>
<td v-if="showAlbum">
<router-link :to="{name: 'album', params: {id: item.albumId}}">
{{ item.album }}
</router-link>
</td>
<td class="text-right">
{{ item.duration | duration }}
</td>
<td class="text-right">
<TrackContextMenu :track="item">
<template v-slot="{ item }">
<slot name="context-menu" :index="index" :item="item"></slot>
</template>
</TrackContextMenu>
</td>
</tr>
</tbody>
</b-table-simple>
</div>
</template>
<style lang="scss" scoped>
.track-status-hover {
display: none;
}
tr:hover {
.track-status-hover {
display: inline;
}
.track-status {
display: none;
}
}
</style>
<script lang="ts">
import Vue from "vue";
import { mapActions, mapMutations, mapGetters, mapState } from 'vuex';
import TrackContextMenu from "@/library/TrackContextMenu.vue"
export default Vue.extend({
components: {
TrackContextMenu,
},
props: {
tracks: { type: Array, required: true },
showAlbum: { type: Boolean, default: false },
},
computed: {
...mapState("player", {
isPlaying: "isPlaying",
}),
...mapGetters({
playingTrackId: "player/trackId",
}),
},
methods: {
...mapMutations({
playPause: "player/playPause",
}),
play(index: number) {
this.$store.dispatch('player/play', {
index,
queue: this.tracks,
})
},
dragstart(id: string, event: any) {
console.log("dragstart: " + id)
event.dataTransfer.setData("id", id);
},
}
});
</script>
+45
View File
@@ -0,0 +1,45 @@
<template>
<div v-if="album">
<div class="row mb-4">
<div class="col-auto">
<b-img height="300" width="300" :src="album.image"></b-img>
</div>
<div class="col">
<h1>{{ album.name }}</h1>
<p>by
<router-link :to="{name: 'artist', params: { id: album.artistId }}">
{{ album.artist }}
</router-link>
<span v-if="album.year"> · {{ album.year }}</span>
<span v-if="album.genre"> · {{ album.genre }}</span>
</p>
</div>
</div>
<div class="row">
<div class="col">
<TrackList :tracks="album.song"/>
</div>
</div>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import TrackList from "@/library/TrackList.vue"
export default Vue.extend({
components: {
TrackList,
},
props: {
id: String
},
data() {
return {
album: null,
}
},
async mounted() {
this.album = await this.$api.getAlbumDetails(this.id);
}
});
</script>
+35
View File
@@ -0,0 +1,35 @@
<template>
<div class="card">
<img class="card-img-top" :src="item.image">
<div class="card-body">
<div class=" ">
<div class="text-truncate">
<router-link :to="{name: 'album', params: { id: item.id } }">
<strong>{{ item.name }}</strong>
</router-link>
</div>
<div class="text-truncate text-muted">
{{ item.artist }}
</div>
</div>
</div>
</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: {
item: Object
}
});
</script>
+62
View File
@@ -0,0 +1,62 @@
<template>
<div>
<Spinner :data="albums" v-slot="{albums: data}">
<Tiles :items="albums" v-slot="{ item }" square>
<div class="text-truncate">
<router-link :to="{name: 'album', params: { id: item.id } }">
<strong>{{ item.name }}</strong>
</router-link>
</div>
<div class="text-truncate text-muted">
<router-link :to="{name: 'artist', params: { id: item.artistId } }">
{{ item.artist }}
</router-link>
</div>
</Tiles>
</Spinner>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import AlbumCard from "./AlbumCard.vue";
export default Vue.extend({
components: {
AlbumCard
},
props: {
msg: String
},
data() {
return {
sort: "newest",
albums: null,
};
},
computed: {
sortOptions() {
return [
{ text: "A-Z", value: "alphabeticalByName" },
{ text: "Date", value: "newest" },
{ text: "frequent", value: "frequent" },
// { text: "random", value: "random" },
// { text: "recent", value: "recent" },
// { text: "starred", value: "starred" },
]
}
},
watch: {
sort: {
immediate: true,
handler(value: string) {
this.albums = null;
this.$api.getAlbums(value).then(albums => {
this.albums = albums;
});
}
}
},
created() {
}
});
</script>
+21
View File
@@ -0,0 +1,21 @@
<template>
<div>
<div class="text-truncate">
<router-link :to="{name: 'artist', params: { id: item.id } }">
<strong>{{ item.name }}</strong>
</router-link>
</div>
<div class="text-truncate text-muted">
<strong>{{ item.albumCount }}</strong> albums
</div>
</div>
</template>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
props: {
item: { type: Object, required: true }
}
});
</script>
+58
View File
@@ -0,0 +1,58 @@
<template>
<div v-if="item">
<div class="row mb-2">
<!-- <div class="col"> -->
<!-- <b-img height="300" width="300" :src="item.image"></b-img> -->
<!-- </div> -->
<div class="col col-xl-8">
<h1>{{ item.name }}</h1>
<p>{{ item.description }}</p>
</div>
</div>
<h3 class="pt-5">Albums</h3>
<Tiles :items="item.albums" v-slot="{ item }" square>
<div class="text-truncate">
<router-link :to="{name: 'album', params: { id: item.id } }">
<strong>{{ item.name }}</strong>
</router-link>
</div>
<div class="text-truncate text-muted">
{{ item.artist }}
</div>
</Tiles>
<h3 class="pt-5">Similar artist</h3>
<Tiles :items="item.similarArtist" v-slot="{ item }">
<ArtistCard :item="item"></ArtistCard>
</Tiles>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import AlbumCard from "@/library/album/AlbumCard.vue";
import ArtistCard from "@/library/artist/ArtistCard.vue";
export default Vue.extend({
components: {
AlbumCard,
ArtistCard,
},
props: {
id: String
},
data() {
return {
item: null as any,
}
},
watch: {
id: {
immediate: true,
async handler(value: string) {
this.item = null,
this.item = await this.$api.getArtistDetails(this.id);
}
}
}
});
</script>
+32
View File
@@ -0,0 +1,32 @@
<template>
<Tiles :items="items" v-slot="{ item }">
<div class="text-truncate">
<router-link :to="{name: 'artist', params: { id: item.id } }">
<strong>{{ item.name }}</strong>
</router-link>
</div>
<div class="text-truncate text-muted">
<strong>{{ item.albumCount }}</strong> albums
</div>
</Tiles>
</template>
<script lang="ts">
import Vue from "vue";
import ArtistCard from "./ArtistCard.vue";
export default Vue.extend({
components: {
ArtistCard,
},
data() {
return {
items: []
};
},
created() {
this.$api.getArtists().then(items => {
this.items = items;
});
}
});
</script>
+27
View File
@@ -0,0 +1,27 @@
<template>
<div v-if="item">
<h1>{{ item.name }}</h1>
<TrackList :tracks="item.tracks" show-album/>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import TrackList from "@/library/TrackList.vue"
export default Vue.extend({
components: {
TrackList,
},
props: {
id: String
},
data() {
return {
item: null as any,
}
},
async created() {
this.item = await this.$api.getGenreDetails(this.id);
}
});
</script>
+34
View File
@@ -0,0 +1,34 @@
<template>
<Tiles :items="items">
<template v-slot="{ item }">
<div class="text-truncate">
<router-link :to="{name: 'genre', params: { id: item.id } }">
<strong>{{ item.name }}</strong>
</router-link>
</div>
<div class="text-truncate text-muted">
<strong>{{ item.albumCount }}</strong> albums ·
<strong>{{ item.songCount }}</strong> songs
</div>
</template>
</Tiles>
</template>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
components: {
},
data() {
return {
items: []
};
},
created() {
this.$api.getGenres().then(items => {
this.items = items;
});
}
});
</script>
+25
View File
@@ -0,0 +1,25 @@
<template>
<div v-if="items">
<TrackList :tracks="items.tracks"/>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import TrackList from "@/library/TrackList.vue"
export default Vue.extend({
components: {
TrackList,
},
data() {
return {
items: null as any,
}
},
created() {
this.$api.getStarred().then(result => {
this.items = result;
})
}
});
</script>