diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile
index 01d439d..2c48cd7 100644
--- a/jenkins/Jenkinsfile
+++ b/jenkins/Jenkinsfile
@@ -34,10 +34,10 @@ pipeline {
steps {
node('master') {
unstash 'webdist'
- sshCommand remote: remote, command: "cd music-graph && rm -rf webroot/* deploy.sh"
- sshPut remote: remote, from: 'webroot/', into: 'music-graph'
- sshPut remote: remote, from: 'jenkins/deploy.sh', into: 'music-graph/'
- sshCommand remote: remote, command: 'chmod +x music-graph/deploy.sh && ./music-graph/deploy.sh'
+ sshCommand remote: remote, command: "cd /srv/music-graph && rm -rf /srv/webroot/* deploy.sh"
+ sshPut remote: remote, from: 'webroot/', into: '/srv/music-graph'
+ sshPut remote: remote, from: 'jenkins/deploy.sh', into: '/srv/music-graph/'
+ sshCommand remote: remote, command: 'chmod +x /srv/music-graph/deploy.sh && /srv/music-graph/deploy.sh'
}
}
}
diff --git a/jenkins/deploy.sh b/jenkins/deploy.sh
index 8b56473..9e60385 100755
--- a/jenkins/deploy.sh
+++ b/jenkins/deploy.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-export MGROOT="music-graph"
+export MGROOT="/srv/music-graph"
chmod 755 -R "${MGROOT}/webroot"
diff --git a/music_graph/index.html b/music_graph/index.html
index 60ed5c5..034f255 100644
--- a/music_graph/index.html
+++ b/music_graph/index.html
@@ -5,7 +5,7 @@
-
music-graph v1.1
+ music-graph v1.2
diff --git a/music_graph/package-lock.json b/music_graph/package-lock.json
index 06411a1..5cc0ee9 100644
--- a/music_graph/package-lock.json
+++ b/music_graph/package-lock.json
@@ -6657,7 +6657,8 @@
},
"js-yaml": {
"version": "3.7.0",
- "resolved": "",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz",
+ "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=",
"dev": true,
"requires": {
"argparse": "^1.0.7",
diff --git a/music_graph/src/MusicGraph.js b/music_graph/src/MusicGraph.js
index 75ffbc8..195fc53 100644
--- a/music_graph/src/MusicGraph.js
+++ b/music_graph/src/MusicGraph.js
@@ -91,9 +91,12 @@ export function MusicGraph(data) {
.style('fill', 'none')
.call(d3.zoom()
.scaleExtent([1 / 10, 5])
- .on('zoom', this.zoomed))
+ .on('zoom', this.zoomed)
+ )
+ .on('click', this.dismiss)
- this.container = this.svg.append('g').attr('id', 'container')
+ this.container = this.svg.append('g')
+ .attr('id', 'container')
this.container.append('g').attr('id', 'links')
this.container.append('g').attr('id', 'nodes')
@@ -175,7 +178,7 @@ export function MusicGraph(data) {
this.makeMenu = function (d) {
let items = []
let i = 0
- if ((d.type === 'Group' || d.type === 'Artist')) {
+ if (d.type === 'Group' || d.type === 'Artist') {
items.push({
idx: i++,
icon: icons.guitar,
@@ -188,6 +191,22 @@ export function MusicGraph(data) {
}
})
}
+ if ((d.type === 'Group' || d.type === 'Artist') &&
+ this._data.hoverArtist !== undefined && this._data.hoverArtist.id !== d.id) {
+ items.push({
+ idx: i++,
+ icon: icons.path,
+ title: 'Path to here',
+ fn: (d) => {
+ this.api.getPath(this._data.hoverArtist.mbid, d.mbid)
+ .then(data => {
+ if (data.newNodes.length > 0) {
+ this.addNodes(data.newNodes, data.relations)
+ }
+ })
+ }
+ })
+ }
if ((d.type === 'Group' || d.type === 'Artist')) {
items.push({
idx: i++,
@@ -332,6 +351,7 @@ export function MusicGraph(data) {
}
this.nodeClick = (d) => {
+ this.dismiss()
if (d.type === 'Group' || d.type === 'Artist') {
// Toggle artistInfo
this.nodes.forEach(x => {
@@ -347,45 +367,21 @@ export function MusicGraph(data) {
}
}
- this.addNode = function (newNode, relations) {
- // Convert {id, id} relation to {node, node}
- if (this.nodeById.has(newNode.id)) {
- // Node already exists, select it
+ this.addNodes = function (newNodes, relations, originId) {
+ // Update node map, ignore existing nodes
+ let nodesToAdd = []
+
+ // If we're adding a single node and it already exists, select it
+ if (newNodes.length === 1 && this.nodeById.has(newNodes[0].id)) {
this.nodes.forEach(x => {
x.hover = false
})
- this._data.hoverArtist = this.nodeById.get(newNode.id)
+ this._data.hoverArtist = this.nodeById.get(newNodes[0].id)
this._data.hoverArtist.hover = true
this._update()
return
}
- this.nodeById.set(newNode.id, newNode)
- newNode.x = width / 2
- newNode.y = height / 2
- let linksToAdd = relations
- .filter(rel => this.nodeById.has(rel.source) && this.nodeById.has(rel.target))
- .map(({weight, source, target}) => ({
- source: this.nodeById.get(source),
- target: this.nodeById.get(target),
- weight: weight
- }))
-
- // Update source/targetLinks
- for (const {source, target} of linksToAdd) {
- source.sourceLinks.add(target.id)
- target.targetLinks.add(source.id)
- }
-
- this.nodes.push(newNode)
- this.links.push(...linksToAdd)
-
- this._update()
- }
-
- this.addNodes = function (newNodes, relations, originId) {
- // Update node map, ignore existing nodes
- let nodesToAdd = []
newNodes.forEach(d => {
if (this.nodeById.has(d.id)) {
return
@@ -403,6 +399,9 @@ export function MusicGraph(data) {
centerNode.fx = null
centerNode.fy = null
}, 600)
+ } else {
+ d.x = width / 2
+ d.y = height / 2
}
nodesToAdd.push(d)
@@ -499,7 +498,7 @@ export function MusicGraph(data) {
.force('link', d3.forceLink(this.links)
.id(d => d.id)
.strength(l => l.weight)
- .distance(d => (1.12 / d.weight) * 40 * (this.graphSize))
+ .distance(d => (1.12 / d.weight) * 30 * (this.graphSize))
)
this.simulation.alphaTarget(0.03).restart()
@@ -514,6 +513,7 @@ export function MusicGraph(data) {
.append('line')
.merge(this.link)
.classed('link', true)
+ .attr('stroke', d => this._getLinkColor(d))
// Add new nodes
this.node = this.container.select('#nodes')
@@ -597,10 +597,14 @@ export function MusicGraph(data) {
return null
}
+ this._getLinkColor = function (node) {
+ return '#FF0000'
+ }
+
this.addArtistByMbid = function (mbid) {
this.api.getRelatedByMbid(mbid)
.then(data => {
- this.addNode(data.node, data.relations)
+ this.addNodes([data.node], data.relations)
})
}
@@ -610,7 +614,19 @@ export function MusicGraph(data) {
}
this.api.getRelatedByTag(tagid)
.then(data => {
- this.addNode(data.node, data.relations)
+ // Force tag->artist direction
+ const relations = data.relations.map(rel => {
+ if (rel.source === data.node.id) {
+ return {
+ weight: rel.weight,
+ source: rel.target,
+ target: rel.source
+ }
+ } else {
+ return rel
+ }
+ })
+ this.addNodes([data.node], relations)
})
}
diff --git a/music_graph/src/MusicGraphApi.js b/music_graph/src/MusicGraphApi.js
index 2b66763..e8b9d11 100644
--- a/music_graph/src/MusicGraphApi.js
+++ b/music_graph/src/MusicGraphApi.js
@@ -1,5 +1,5 @@
import * as d3 from 'd3'
-import {genres} from './genres'
+import {isGenreTag} from './genres'
const IGNORE_DATES_TAG = true
const ONLY_GENRE_TAGS = false
@@ -54,6 +54,7 @@ const nodeUtils = {
export function MusicGraphApi(data) {
this.url = window.location.protocol + '//' + window.location.hostname + '/api'
+ // this.url = window.location.protocol + '//' + window.location.hostname + ':3030'
this._data = data
let loadWrapper = (fn) => {
@@ -119,7 +120,7 @@ export function MusicGraphApi(data) {
this._filterTags = tags => {
if (ONLY_GENRE_TAGS) {
- return tags.filter(tag => genres.has(tag.name))
+ return tags.filter(tag => isGenreTag(tag.name, tag.tagid))
} else if (IGNORE_DATES_TAG) {
return tags.filter(tag => isNaN(tag.name) && isNaN(tag.name.slice(0, -1)))
}
@@ -215,7 +216,7 @@ export function MusicGraphApi(data) {
return {
source: rel.target,
target: rel.source,
- weight: rel.weight
+ weight: Math.min(Math.max(rel.weight * 1.5, 0.2), 1)
}
})
}
@@ -240,6 +241,16 @@ export function MusicGraphApi(data) {
})
})
+ this.getPath = loadWrapper((idFrom, idTo) => {
+ return d3.json(this.url + '/artist/path/' + idFrom + '/' + idTo)
+ .then((r) => {
+ return {
+ newNodes: r.artists.map(nodeUtils.fromRawDict),
+ relations: r.relations
+ }
+ })
+ })
+
this.autoComplete = loadWrapper((prefix) => {
prefix = prefix
.replace(/[^\w.\-!?& ]/g, '_').toUpperCase()
diff --git a/music_graph/src/components/ArtistInfo.vue b/music_graph/src/components/ArtistInfo.vue
index 0c63c1e..bc3a1f8 100644
--- a/music_graph/src/components/ArtistInfo.vue
+++ b/music_graph/src/components/ArtistInfo.vue
@@ -63,7 +63,7 @@