mirror of
https://github.com/simon987/music-graph-ui.git
synced 2025-04-19 01:56:42 +00:00
259 lines
8.3 KiB
JavaScript
259 lines
8.3 KiB
JavaScript
import * as d3 from 'd3'
|
|
import {genres} from './genres'
|
|
|
|
const IGNORE_DATES_TAG = true
|
|
const ONLY_GENRE_TAGS = false
|
|
|
|
const nodeUtils = {
|
|
getNodeType: function (labels) {
|
|
if (labels.find(l => l === 'Tag')) {
|
|
return 'Tag'
|
|
} else if (labels.find(l => l === 'Group')) {
|
|
return 'Group'
|
|
} else if (labels.find(l => l === 'Artist')) {
|
|
return 'Artist'
|
|
} else if (labels.find(l => l === 'Album')) {
|
|
return 'Album'
|
|
} else if (labels.find(l => l === 'Single')) {
|
|
return 'Single'
|
|
} else if (labels.find(l => l === 'EP')) {
|
|
return 'EP'
|
|
}
|
|
return undefined
|
|
},
|
|
fromRawDict: function (data) {
|
|
const type = nodeUtils.getNodeType(data.labels)
|
|
|
|
if (type === 'Group' || type === 'Artist') {
|
|
return {
|
|
id: data.id,
|
|
mbid: data.mbid,
|
|
name: data.name,
|
|
listeners: data.listeners,
|
|
type: type,
|
|
sourceLinks: new Set(),
|
|
targetLinks: new Set(),
|
|
radius: nodeUtils.radius(type)
|
|
}
|
|
} else {
|
|
return {
|
|
id: data.id,
|
|
name: data.name,
|
|
mbid: data.mbid,
|
|
type: type,
|
|
sourceLinks: new Set(),
|
|
targetLinks: new Set(),
|
|
radius: nodeUtils.radius(type)
|
|
}
|
|
}
|
|
},
|
|
radius: function (type) {
|
|
return 35
|
|
}
|
|
}
|
|
|
|
export function MusicGraphApi() {
|
|
this.url = window.location.protocol + '//' + window.location.hostname + '/api'
|
|
|
|
this.resolveCoverUrl = function (mbid) {
|
|
return this.url + '/cover/' + mbid
|
|
}
|
|
|
|
this.getArtistDetails = function (mbid) {
|
|
return d3.json(this.url + '/artist/details/' + mbid)
|
|
}
|
|
|
|
/**
|
|
* Works in both directions
|
|
* @returns {Promise<{newNodes: *, relations: *} | never>}
|
|
*/
|
|
this.getGroupMembers = function (mbid, originId) {
|
|
return d3.json(this.url + '/artist/members/' + mbid)
|
|
.then((r) => {
|
|
return {
|
|
newNodes: r.artists.map(nodeUtils.fromRawDict),
|
|
relations: r.artists.map(a => {
|
|
return {
|
|
source: originId,
|
|
target: a.id,
|
|
weight: 0.8
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
this.getArtistReleases = function (mbid, originId) {
|
|
return d3.json(this.url + '/artist/details/' + mbid)
|
|
.then((r) => {
|
|
const newNodes = r.releases
|
|
.map(nodeUtils.fromRawDict)
|
|
.filter(release => release.type === 'Album')
|
|
|
|
return {
|
|
newNodes: newNodes,
|
|
relations: newNodes.map(t => {
|
|
return {
|
|
source: originId,
|
|
target: t.id,
|
|
weight: 0.8
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
this.getArtistLabels = function (mbid, originId) {
|
|
return d3.json(this.url + '/artist/details/' + mbid)
|
|
.then((r) => {
|
|
const newNodes = r.labels
|
|
.map(l => {
|
|
l.labels = ['Label']
|
|
return l
|
|
})
|
|
.map(nodeUtils.fromRawDict)
|
|
|
|
return {
|
|
newNodes: newNodes,
|
|
relations: newNodes.map(t => {
|
|
return {
|
|
source: originId,
|
|
target: t.id,
|
|
weight: 0.8
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
this._filterTags = function (tags) {
|
|
if (ONLY_GENRE_TAGS) {
|
|
return tags.filter(tag => genres.has(tag.name))
|
|
} else if (IGNORE_DATES_TAG) {
|
|
return tags.filter(tag => isNaN(tag.name) && isNaN(tag.name.slice(0, -1)))
|
|
}
|
|
return tags
|
|
}
|
|
|
|
this._addTagLabel = function (objects) {
|
|
return objects.map(tag => {
|
|
tag.labels = ['Tag']
|
|
return tag
|
|
})
|
|
}
|
|
|
|
this.getArtistTags = function (mbid, originId) {
|
|
return d3.json(this.url + '/artist/details/' + mbid)
|
|
.then((r) => {
|
|
const tags = this._filterTags(r.tags)
|
|
|
|
return {
|
|
newNodes: this._addTagLabel(tags).map(nodeUtils.fromRawDict),
|
|
relations: tags.map(t => {
|
|
return {
|
|
source: originId,
|
|
target: t.id,
|
|
weight: t.weight
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
this.getRelatedTags = function (tagId) {
|
|
return d3.json(this.url + '/tag/tag/' + tagId)
|
|
.then((r) => {
|
|
const tags = this._filterTags(r.tags)
|
|
let directedRelations = r.relations.map(rel => {
|
|
// Make new nodes children of the expanded nodes, no matter the original direction
|
|
if (rel.source === tagId) {
|
|
return rel
|
|
} else {
|
|
return {
|
|
source: rel.target,
|
|
target: rel.source,
|
|
weight: rel.weight
|
|
}
|
|
}
|
|
})
|
|
|
|
return {
|
|
newNodes: this._addTagLabel(tags).map(nodeUtils.fromRawDict),
|
|
relations: directedRelations
|
|
}
|
|
})
|
|
}
|
|
|
|
this.getRelatedByMbid = function (mbid) {
|
|
return d3.json(this.url + '/artist/related/' + mbid)
|
|
.then((r) => {
|
|
let node = nodeUtils.fromRawDict(r.artists.find(a => a.mbid === mbid))
|
|
let directedRelations = r.relations.map(rel => {
|
|
// Make new nodes children of the expanded nodes, no matter the original direction
|
|
if (rel.source === node.id) {
|
|
return rel
|
|
} else {
|
|
return {
|
|
source: rel.target,
|
|
target: rel.source,
|
|
weight: rel.weight
|
|
}
|
|
}
|
|
})
|
|
|
|
return {
|
|
node: node,
|
|
newNodes: r.artists.map(nodeUtils.fromRawDict),
|
|
relations: directedRelations
|
|
}
|
|
})
|
|
}
|
|
|
|
this.getRelatedByTag = function (tagid) {
|
|
return d3.json(this.url + '/tag/related/' + tagid)
|
|
.then((r) => {
|
|
return {
|
|
node: nodeUtils.fromRawDict({
|
|
labels: ['Tag'],
|
|
id: r.tag.id,
|
|
name: r.tag.name
|
|
}),
|
|
newNodes: r.artists.map(nodeUtils.fromRawDict),
|
|
relations: r.relations.map(rel => {
|
|
// Invert relation direction
|
|
return {
|
|
source: rel.target,
|
|
target: rel.source,
|
|
weight: rel.weight
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
this.getReleaseDetails = function (mbid, originId) {
|
|
return d3.json(this.url + '/release/details/' + mbid)
|
|
.then((r) => {
|
|
const tags = this._filterTags(r.tags)
|
|
|
|
return {
|
|
newNodes: this._addTagLabel(tags).map(nodeUtils.fromRawDict),
|
|
relations: tags.map(t => {
|
|
return {
|
|
source: originId,
|
|
target: t.id,
|
|
weight: t.weight
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
this.autoComplete = function (prefix) {
|
|
prefix = prefix.replace(/[^\w.\-!?& ]/g, '_').toUpperCase()
|
|
prefix = prefix.replace(/ /g, '+')
|
|
|
|
return d3.json(this.url + '/autocomplete/' + prefix)
|
|
}
|
|
}
|