Commit 98f67b0e authored by Henrik Hüttemann's avatar Henrik Hüttemann 🤔

Add XSPF download; Close #1

parent c822f09e
Pipeline #755 passed with stage
in 1 minute
......@@ -15,14 +15,12 @@
<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
{{ filteredSongs.length }}/{{ songs.length }} Results |
<a href="#" v-on:click="sendXspfToUser('autolib.xspf', filteredSongs)">DL XSPF</a>
</p>
</div>
<div class="content">
<vue-tabs>
<v-tab title="XSPF">
{{ xspf }}
</v-tab>
<v-tab v-bind:title="`Songs (${filteredSongs.length})`">
<div class="songs">
<Song
......@@ -100,7 +98,6 @@
max-height: 90vh;
"
>
</div>
</v-tab>
</vue-tabs>
......@@ -276,27 +273,6 @@ export default {
})
filteredAlbums.map(album => album.songs.sort((a, b) => a.metadata.track.no - b.metadata.track.no))
return filteredAlbums
},
xspf: function () {
const jspf = {
playlist: {
'@version': 1,
'@xmlns': 'http://xspf.org/ns/0/',
trackList: {
track: this.filteredSongs.map(song => {
return {
identifier: `https://musicbrainz.org/release/${song.metadata.musicbrainz_recordingid}`,
location: song.files.flatMap(file => [
'/ipfs/',
this.config.ipfs.gateway,
'https://ipfs.io/ipfs/'
].map(s => s + file.ipfs))
}
})
}
}
}
return '<?xml version="1.0" encoding="UTF-8"?>' + this.json2xml(jspf)
}
},
methods: {
......@@ -393,42 +369,95 @@ export default {
Web: http://goessner.net/
*/
json2xml (o, tab) {
var toXml = function(v, name, ind) {
var xml = "";
if (v instanceof Array) {
for (var i=0, n=v.length; i<n; i++)
xml += ind + toXml(v[i], name, ind+"\t") + "\n";
var toXml = (v, name, ind) => {
var xml = "";
if (v instanceof Array) {
for (var i=0, n=v.length; i<n; i++)
xml += ind + toXml(v[i], name, ind+"\t") + "\n";
}
else if (typeof(v) == "object") {
var hasChild = false;
xml += ind + "<" + name;
for (var m in v) {
if (m.charAt(0) == "@")
xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\"";
else
hasChild = true;
}
else if (typeof(v) == "object") {
var hasChild = false;
xml += ind + "<" + name;
xml += hasChild ? ">" : "/>";
if (hasChild) {
for (var m in v) {
if (m.charAt(0) == "@")
xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\"";
else
hasChild = true;
}
xml += hasChild ? ">" : "/>";
if (hasChild) {
for (var m in v) {
if (m == "#text")
xml += v[m];
else if (m == "#cdata")
xml += "<![CDATA[" + v[m] + "]]>";
else if (m.charAt(0) != "@")
xml += toXml(v[m], m, ind+"\t");
}
xml += (xml.charAt(xml.length-1)=="\n"?ind:"") + "</" + name + ">";
if (m == "#text")
xml += v[m];
else if (m == "#cdata")
xml += "<![CDATA[" + v[m] + "]]>";
else if (m.charAt(0) != "@")
xml += toXml(v[m], m, ind+"\t");
}
xml += (xml.charAt(xml.length-1)=="\n"?ind:"") + "</" + name + ">";
}
else {
xml += ind + "<" + name + ">" + v.toString() + "</" + name + ">";
}
return xml;
}
else {
xml += ind + "<" + name + ">" + this.escapeXml(v.toString()) + "</" + name + ">";
}
return xml;
}, xml="";
for (var m in o)
xml += toXml(o[m], m, "");
return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, "");
},
escapeXml: function (unsafe) {
return unsafe.replace(/[<>&'"]/g, function (c) {
switch (c) {
case '<': return '&lt;';
case '>': return '&gt;';
case '&': return '&amp;';
case '\'': return '&apos;';
case '"': return '&quot;';
}
});
},
sendXspfToUser: function (name, songs) {
const pom = document.createElement('a')
const header = 'data:application/xspf+xml;charset=utf-8,'
const content = this.generateXspf(songs)
pom.setAttribute('href', header + encodeURIComponent(content))
pom.setAttribute('download', name)
// return;
if (document.createEvent) {
const event = document.createEvent('MouseEvents')
event.initEvent('click', true, true)
pom.dispatchEvent(event)
} else {
pom.click()
}
},
generateXspf (songs) {
const jspf = {
playlist: {
'@version': 1,
'@xmlns': 'http://xspf.org/ns/0/',
trackList: {
track: songs.map(song => {
return {
identifier: `https://musicbrainz.org/release/${song.metadata.musicbrainz_recordingid}`,
creator: song.metadata.artist,
album: song.metadata.album,
title: song.metadata.title,
image: this.getCoverUrl(song.cover),
trackNum: song.metadata.track.no,
location: song.files.flatMap(file => [
'/ipfs/',
this.config.ipfs.gateway,
// 'https://ipfs.io/ipfs/'
].map(s => s + file.ipfs))
}
})
}
}
}
return '<?xml version="1.0" encoding="UTF-8"?>\n' + this.json2xml(jspf)
}
},
mounted: function () {
......
......@@ -5,7 +5,7 @@
<div class="coverbox">
<p class="meta">
<strong>
<a href="#" v-on:click="$emit('search', song.metadata.artist)">{{ song.metadata.artist }}</a> -
<a href="#" v-on:click="$emit('search', song.metadata.artist)">{{ song.metadata.artist }}</a> -
<a href="#" v-on:click="$emit('search', song.metadata.title)">{{ song.metadata.title }}</a>
</strong><br/>
<a href="#" v-on:click="$emit('search', song.metadata.album)">{{ song.metadata.album }}</a>
......
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