Commit e2ae660a authored by Henrik Hüttemann's avatar Henrik Hüttemann 🤔

Add dark theme; Add tabs; Add download command

parent 46528fb2
Pipeline #717 passed with stage
in 57 seconds
......@@ -1329,6 +1329,11 @@
"eslint-visitor-keys": "^1.0.0"
}
},
"babel-helper-vue-jsx-merge-props": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz",
"integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg=="
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
......@@ -10354,6 +10359,11 @@
"vue-style-loader": "^4.1.0"
}
},
"vue-nav-tabs": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/vue-nav-tabs/-/vue-nav-tabs-0.5.7.tgz",
"integrity": "sha512-Oqq7qnb0/JPAVSqM0haQ9TdEZaTbQq20TVn5ZCmBOu8m9qju9bI8cDdtWGHXSiMkpmhzsT83ybRb7S/+UYXRsw=="
},
"vue-style-loader": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz",
......
......@@ -8,8 +8,10 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"fuse.js": "^3.4.5",
"vue": "^2.6.6"
"vue": "^2.6.6",
"vue-nav-tabs": "^0.5.7"
},
"devDependencies": {
"@vue/cli-plugin-eslint": "^3.8.0",
......
<template>
<div id="app">
<h1>All Songs</h1>
<p class="description">Got {{ songs.length }} songs in the index.<br/>
{{ checks.ipfs ? '&#x2714;' : '&#x2718;' }} IPFS daemon <a v-bind:href="this.config.ipfs.gateway + this.config.ipfs.checkHash">online</a><br/>
{{ checks.db ? '&#x2714;' : '&#x2718;' }} Database <a href="https://db.beef.h3n.eu/_utils/#database/autolib/_all_docs">online</a><br>
<div class="topbar">
<h1>AutoLib Frontend</h1>
<p class="label">
<span class="bubble">{{ checks.ipfs ? '&#x2714;' : '&#x2718;' }}</span> <a v-bind:href="this.config.ipfs.gateway + this.config.ipfs.checkHash">IPFS</a>
</p>
<p class="label">
<span class="bubble">{{ checks.db ? '&#x2714;' : '&#x2718;' }}</span> <a href="https://db.beef.h3n.eu/_utils/#database/autolib/_all_docs">DB</a>
</p>
<input type="text" placeholder="Search" v-model="search">
{{ filteredSongs.length }} Results
</p>
<hr>
<div class="songs">
<div
v-for="song in filteredSongs"
v-bind:key="song.id"
class="song"
v-bind:style="{ backgroundImage:
(song.cover.length > 0) ? `url('${ config.ipfs.gateway + song.cover[0].ipfs }')` : 'none' }"
>
<div class="coverbox">
<p class="meta">
<strong>{{ song.metadata.artist }} - {{ song.metadata.title }}</strong><br/>
{{ song.metadata.album }} [{{ song.metadata.year }}]<br/>
{{ song.cover.length }} Cover, {{ song.files.length }} Audiofiles<br/>
{{ song.files[0].format.dataformat.toUpperCase() }} {{ Math.round(song.files[0].format.bitrate / 1000) + ' kbps' }}<br/>
<a v-if="song.cover.length > 0" v-bind:href="config.ipfs.gateway + song.cover[0].ipfs">
<img
v-bind:src="config.ipfs.gateway + song.cover[0].ipfs"
alt=""
class="cover"
/>
</a>
<audio
v-if="song.files[0]"
v-bind:src="config.ipfs.gateway + song.files[0].ipfs"
preload="metadata"
controls
/>
</p>
</div>
</div>
<p class="label">
<span class="bubble">{{ checks.backend ? '&#x2714;' : '&#x2718;' }}</span> <a v-bind:href="this.config.backend + 'log'">Backend</a>
</p>
<p class="label">
<input type="text" placeholder="Search" v-model="search" class="bubble">
<span class="bubble" v-on:click="search = ''">&circlearrowleft;</span>
{{ filteredSongs.length }}/{{ songs.length }} Results
</p>
</div>
<div v-show="songs.length == 0">
- No Songs -
<div class="content">
<vue-tabs>
<v-tab v-bind:title="`Songs (${filteredSongs.length})`">
<div class="songs">
<div
v-for="song in filteredSongs"
v-bind:key="song.id"
class="song"
v-bind:style="{ backgroundImage:
(song.cover.length > 0) ? `url('${ config.ipfs.gateway + song.cover[0].ipfs }')` : 'none' }"
>
<div class="coverbox">
<p class="meta">
<strong>{{ song.metadata.artist }} - {{ song.metadata.title }}</strong><br/>
{{ song.metadata.album }} [{{ song.metadata.year }}]<br/>
{{ song.cover.length }} Cover, {{ song.files.length }} Audiofiles<br/>
{{ song.files[0].format.dataformat.toUpperCase() }} {{ Math.round(song.files[0].format.bitrate / 1000) + ' kbps' }}<br/>
<a v-if="song.cover.length > 0" v-bind:href="config.ipfs.gateway + song.cover[0].ipfs">
<img
v-bind:src="config.ipfs.gateway + song.cover[0].ipfs"
alt=""
class="cover"
/>
</a>
<audio
v-if="song.files[0]"
v-bind:src="config.ipfs.gateway + song.files[0].ipfs"
preload="metadata"
controls
/>
</p>
</div>
</div>
</div>
<div v-show="songs.length == 0">
- No Songs -
</div>
</v-tab>
<v-tab v-bind:title="`Albums (${albums.length})`">
<div
class="albums"
v-for="album in albums"
v-bind:key="album.releaseGroupId"
>
<div class="album">
<p class="cover">
<img v-bind:src="config.ipfs.gateway + album.cover[0].ipfs" alt="">
</p>
<div class="description">
<h2>{{ album.artist }} - {{ album.title }} [{{ album.year }}]</h2>
<p class="meta">
<span v-if="search">{{ album.songs.length }}/</span>{{ album.tracks }} Tracks <em v-if="search">(filtered)</em> | {{ album.cover.length }} Cover
</p>
<ol class="tracklist">
<li
v-for="song in album.songs"
v-bind:key="song.id"
v-bind:value="song.metadata.track.no"
>
{{ song.metadata.title }}
</li>
</ol>
</div>
</div>
</div>
</v-tab>
<v-tab title="Settings">
<div class="settings">
<h2>Download other albums</h2>
<ul>
<li
v-for="dl in downloads"
v-bind:key="dl.releaseId"
>
{{ dl.artist }} - {{ dl.album }}:
<a v-on:click.stop="getRelease(dl)">
{{ dl.status || '&#x25b6;' }}
</a>
</li>
</ul>
<h2>Delete Albums</h2>
</div>
</v-tab>
</vue-tabs>
</div>
</div>
</template>
<script>
import Fuse from 'fuse.js'
import {VueTabs, VTab} from 'vue-nav-tabs'
import 'vue-nav-tabs/themes/vue-tabs.css'
export default {
name: 'app',
components: {
VueTabs,
VTab
},
data: function () {
return {
songs: [],
......@@ -61,11 +129,13 @@ export default {
checkHash: 'Qmaisz6NMhDB51cCvNWa1GMS7LU1pAxdF4Ld6Ft9kZEP2a',
checkString: 'Hello from IPFS Gateway Checker'
},
db: 'https://db.beef.h3n.eu/autolib/_design/songs/_view/overview?include_docs=true'
db: 'https://db.beef.h3n.eu/autolib/_design/songs/_view/overview?include_docs=true',
backend: 'http://localhost:3000/'
},
checks: {
db: false,
ipfs: false
ipfs: false,
backend: false
},
search: '',
fuse: false,
......@@ -82,18 +152,78 @@ export default {
'metadata.artist',
'metadata.album'
]
}
},
downloads: [
{
artist: 'Dumbo Gets Mad',
album: 'Elephants at the Door',
releaseId: '578cdfc7-7e4a-4f04-9890-1c222323a815',
status: null,
intervalId: 0
},
{
artist: 'pornophonique',
album: '8-bit lagerfeuer',
releaseId: '77baaaf6-8128-400e-aee7-0e9a6ca79692',
status: null,
intervalId: 0
},
{
artist: 'Maniax Memori',
album: 'L€ktro Pöppe',
releaseId: '9296b70a-e602-4aee-bfc2-8756db21ba99',
status: null,
intervalId: 0
},
{
artist: 'Avel Glas',
album: 'Vent Bleu',
releaseId: '5115b34b-e76a-37e0-8258-f397b9776fed',
status: null,
intervalId: 0
},
{
artist: 'wasaru',
album: 'undefinable scenes (2008)',
releaseId: '1394a579-338e-495b-b659-b1707f55422e',
status: null,
intervalId: 0
}
]
}
},
computed: {
filteredSongs: function () {
return this.fuse === false || this.search.length === 0 ? this.songs : this.fuse.search(this.search)
},
albums: function () {
let albums = []
this.filteredSongs.map(song => {
let album = albums.find(a => a.releaseGroupId === song.metadata.musicbrainz_releasegroupid)
if (album === undefined) {
album = {
releaseGroupId: song.metadata.musicbrainz_releasegroupid,
cover: song.cover,
artist: song.metadata.albumartist,
title: song.metadata.album,
year: song.metadata.year,
tracks: song.metadata.track.of,
songs: []
}
albums.push(album)
}
album.songs.push(song)
})
albums.map(album => album.songs.sort((a, b) => a.metadata.track.no - b.metadata.track.no))
return albums
}
},
mounted: function () {
axios.get(this.config.db)
methods: {
getSongs () {
axios.get(this.config.db)
.then(response => {
console.log(response.data)
this.songs = []
response.data.rows.forEach(song => {
this.songs.push(song.doc)
})
......@@ -104,26 +234,119 @@ export default {
this.checks.db = false
console.log(error);
})
},
getRelease (dl) {
console.log(
'getRelease',
dl
)
axios.get(`http://localhost:3000/release/${dl.releaseId}/start`)
.then(response => {
console.log(
'Response:',
response.data
)
dl.status = response.data.status
})
dl.intervalId = setInterval(() => {
axios.get(`http://localhost:3000/release/${dl.releaseId}`)
.then(response => {
console.log(
'Check:',
response.data
)
dl.status = response.data.status
if (response.data.status !== 'working') {
console.log(
'Clearing Interval.'
)
clearInterval(dl.intervalId)
this.getSongs()
}
})
}, 1000)
}
},
mounted: function () {
this.getSongs()
axios.get(this.config.ipfs.gateway + this.config.ipfs.checkHash)
.then(response => {
this.checks.ipfs = response.data.trim() === this.config.ipfs.checkString
})
axios.get(this.config.backend + 'status')
.then(response => {
this.checks.backend = response.status === 200
})
}
}
</script>
<style>
body {
margin: 0;
background-color: #1E1E1E;
color: #D4D4D4;
}
a {
color: #CE9178;
}
a:visited {
color: #9CDCFE;
}
#app {
font-family: 'Anonymous Pro', monospace;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
margin-top: 20px;
}
.topbar {
background-color: rgba(0, 0, 0, 0.8);
position: fixed;
width: 100%;
display: flex;
align-items: baseline;
justify-content: center;
top: 0;
}
.topbar h1 {
font-size: 1rem;
}
.topbar .label {
padding: 0.5rem;
margin: 0.2rem;
border-radius: 0.5rem;
background-color: #1E1E1E;
box-sizing: border-box;
}
.topbar .label .bubble {
border: 0;
border-radius: 1rem;
padding: 0.2rem 0.5rem;
margin: 0;
margin-right: 0.2rem;
height: 1.2rem;
display:inline-block;
background-color: black;
vertical-align:middle;
}
.topbar .label input.bubble {
width: 10rem;
}
.topbar .label input.bubble:focus {
background-color: #1E1E1E;
}
.content {
margin-top: 3rem;
}
.nav-tabs {
padding-left: 6rem;
}
.songs {
display: flex;
flex-wrap: wrap;
justify-content: center;
text-align: center;
color: black;
}
.song {
margin: 1rem;
......@@ -150,7 +373,23 @@ export default {
max-width: 40px;
max-height: 40px;
}
.description {
font-style: italic;
.albums, .settings {
margin: 0 6rem;
}
.album {
display: flex;
}
.album .cover img {
width: 350px;
height: 350px;
}
.album .description {
padding: 1rem;
}
.album .description > * {
margin: 0;
}
.album .description .meta {
font-weight: bold;
}
</style>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment