CINXE.COM
Artist Profile
<html> <head> <meta http-equiv="Content-Security-Policy" content="default-src 'self' https://everynoise.com https://furia.com https://*.spotify.com https://sdk.scdn.co https://i.scdn.co/ https://p.scdn.co https://*.spotifycdn.com; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://sdk.scdn.co; img-src 'self' data: https://*.scdn.co/ https://*.spotifycdn.com;"> <meta charset="utf-8"> <meta name="robots" content="noindex"> <link rel="stylesheet" href="spotplay.css" type="text/css"> <script type="text/javascript" src="spotplay.js"></script> <title>Artist Profile</title> <style> body, form {font: 16px Gill Sans} body {margin: 0px 0px 0px 16px} table {margin: 0; padding: 0; border: 0; border-spacing: 0} table td {padding: 0px} tr {vertical-align: top} select {font: 16px Gill Sans; vertical-align: middle; margin: -12px 0px 0px 4px} .title {font-weight: bold; font-size: 250%; margin-bottom: 8px;} .setname, .artistname, .falname {font-weight: bold} .setname {margin-top: 32px} .searchform {margin-bottom: 16px} .note, .note a, .genres {color: gray} .countries, .countries a {color: silver; font-size: 13px} a {color: teal; text-decoration: none} a:hover, .tracksorter:hover, .curiolink:hover {color: red !important; text-decoration: underline} a[visited] {color: navy} form {display: inline;} .icontitle {margin-bottom: 2px} .icontitle .showhide {visibility: hidden} .icontitle:hover .showhide {visibility: visible} .icons {margin-bottom: 4px} .icon {border: 1px solid silver; padding: 0px; width: 48px; height: 48px} .inlineicon {border: 1px solid silver; padding: 0px; margin: 0px; width: 16px; height: 16px; vertical-align: top} .inlineiconcell {padding-left: 16px} .artistart {border: 1px solid silver; padding: 0px; margin: 0px 0px 16px 16px; width: 320px; float: right; display: none} .seealso {margin-bottom: 4px; color: gray} .albumbox {padding-top: 16px; margin-top: 16px; clear: both; border-top: 1px solid silver} .art {border: 1px solid silver; padding: 0px; margin: 0px 0px 16px 16px; float: right} .falart {border: 1px solid silver; padding: 0px; margin: 0px 0px 8px; width: 160px; max-height: 160px; overflow: hidden} .falbox {margin-bottom: 24px} .albumart {width: 320px;} .compilationart {width: 240px;} .singleart, .appears_onart {width: 160px;} .albumtitle, .compilationtitle, .appears_ontitle {font-style: italic} .albumtitle, .compilationtitle {font-size: 150%} .albumtitle, .compilationtitle, .singletitle {font-weight: bold} .albumartist, .compilationartist, .singleartist {font-weight: bold} .albumartist, .compilationartist {font-size: 150%} .radionote {color: silver; margin: 32px 0px 16px 0px} .uribox {padding-top: 32px; color: gray} .stat {color: gray; text-align: right; padding-right: 16px; white-space: nowrap} .date {color: gray; padding-right: 16px; white-space: nowrap} .tracktable {margin-top: 8px} .countries {margin-top: 8px} .discocell {width: 100%; min-width: 600px; padding: 16px 32px 16px 0px} .falcell {border-left: 1px solid silver; background: #282828; padding: 16px 32px 0px 32px; width: 162px; display: none} .falcell .note {color: silver} .falcell img {border-color: gray} .falcell a {color: silver} .falcell div {text-align: center} .alltrackstitle {font-size: 150%; color: gray} .trackrow:hover td {background: #F4F4F4} .highlight td {background: #e6f9ff} .highlight:hover td {background: #b3ecff} .unavailable {padding-left: 4px; color: silver} .tracksorter {visibility: hidden; color: silver; cursor: pointer} .alltrackstitle:hover .tracksorter {visibility: visible;} .pathimages img {margin: 0px 4px 4px 0px; height: 160px; width: 160px; border: 1px solid silver} .typebox {padding-top: 16px} .curiolink {padding-left: 4px; visibility: hidden; color: gray} .albumbox:hover .curiolink, .title:hover .curiolink {visibility: visible} </style> <script> var apiheader = "{'Authorization': 'Bearer BQB9DLQ1z-z85mHmFzLX1fVU9wBR2KHXIBCK_6_2MnhnmldVH47T7ZiNaxIxFBqu8FS-jNkThuwjIJZlFfWgG7uUQl1FJyTFcruouwlZT02HLZHCHRI'}"; var artist = "7mPFWoDGjh7ArW1yr79EX8"; var showcomps = false; var output = null; var albumbox = null; var alltracks = {}; var alltrackindex = 0; var alltracktitles = {}; var highlight = []; var country = ""; var tracksort = null; var label = null; var trackorder = null; var dolabelall = false; var falsource = ""; var dotrackexport = false; if (artist.startsWith('label:')) { label = artist.split(':')[1]; } function normalize(s) { s = s.toLowerCase(); s = s.replaceAll(' & ', ' and '); ['bonus track', 'bonus', 'previously unreleased', 'unreleased'].forEach(ignore => { if (s.includes(ignore)) { [' *\(s\)$', '^\(s\) *', ' *- *s$', '^s *- *'].forEach(pattern => { const re = new RegExp(pattern.replaceAll('s', ignore), "g"); s = s.replaceAll(re, ''); }) } }) s = s.replaceAll('\W', '', s); return(s); } function mmss(ms) { seconds = Math.round(ms / 1000); minutes = Math.floor(seconds / 60); extraseconds = seconds - (60 * minutes); return(minutes + ':' + extraseconds.toString().padStart(2, 0)) } function h(unsafe) { if (unsafe) { return(unsafe.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", ''')); } else { return(''); } } function callapi(url, items = null) { var allitems = []; while (url) { if (!url.startsWith('https://')) { url = 'https://api.spotify.com/v1/' + url } req = new XMLHttpRequest(); req.open('GET', url, false); req.setRequestHeader('Authorization', apiheader); req.setRequestHeader('Content-Type', 'application/json'); var tries = 1; var tryafter = 0; while (tries <= 10) { req.send(); if (req.status == 429) { sleepfor = req.response.headers.get('Retry-After'); var date = new Date();var i; for (i = date.getTime(); i <= date.getTime() + 1000 * sleepfor; i = (new Date()).getTime()){/*Do Nothing*/}; tries += 1; } else { break; } } jres = JSON.parse(req.responseText); if (items) { if (items in jres) { url = jres[items]['next']; if ('items' in jres[items]) { allitems.push(...(jres[items]['items'])); } else { allitems.push(...(jres[items])); } } else { url = null; allitems.push(jres); } } else { url = null; } } if (items) { if (items == 'albums') { for (a=0; a<Math.min(20, allitems.length); a++) { while (allitems[a]['tracks'] && allitems[a]['tracks']['next']) { nextpage = allitems[a]['tracks']['next']; nextres = callapi(nextpage); allitems[a]['tracks']['items'].push(...nextres['items']); allitems[a]['tracks']['next'] = nextres['next']; } } } return(allitems); } else { return(jres); } return; } function maketrackkey(track) { if (label) { return(track['artists'][0]['id'] + '###' + track['nname'] + '###' + track['usedur']); } else { return(track['nname'] + '###' + track['usedur']); } } function makeElement(tag, parent, html, cssclasses=[]) { e = document.createElement(tag); e.innerHTML = html; if (cssclasses) { for (c=0; c<cssclasses.length; c++) { if (cssclasses[c]) { e.classList.add(cssclasses[c]); } } } parent.append(e); return(e); } function getcanon(id, max=20) { canon = []; canonj = callapi('https://everynoise.com/api/canon/' + encodeURIComponent(id)); if (canonj) { canonlist = canonj[id]; if (canonlist) { usecanon = canonlist.slice(0, max); falres = callapi('artists/?ids=' + usecanon.join(',')); if (falres) { canon = falres.artists; } } } return canon; } function exportToCsv(rows) { var processRow = function (row) { var finalVal = ''; for (var j = 0; j < row.length; j++) { var innerValue = row[j] === null ? '' : row[j].toString(); if (row[j] instanceof Date) { innerValue = row[j].toLocaleString(); }; var result = innerValue.replace(/"/g, '""'); if (result.search(/("|,|\n)/g) >= 0) { result = '"' + result + '"'; } if (j > 0) { finalVal += ','; } finalVal += result; } return finalVal + '\n'; }; var csvFile = ''; for (var i = 0; i < rows.length; i++) { csvFile += processRow(rows[i]); } var blob = new Blob([csvFile], { type: 'text/csv;charset=utf-8;' }); if (navigator.msSaveBlob) { // IE 10+ navigator.msSaveBlob(blob, 'export.csv'); } else { var link = document.createElement("a"); if (link.download !== undefined) { // feature detection // Browsers that support HTML5 download attribute var url = URL.createObjectURL(blob); link.setAttribute("href", url); link.setAttribute("download", 'export.csv'); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); } } } function go() { albumbox = document.getElementById("albumbox"); albumbox.style['visibility'] = 'visible'; document.getElementById('loading').style['display'] = 'none'; document.getElementById('footer').style['visibility'] = 'visible'; alltracksbox = document.getElementById("alltracks"); if (label) { document.getElementById('title').innerHTML = label; albums = callapi('search?type=album&q=label%3A%22' + encodeURIComponent(label) + '%22&limit=50', 'albums'); } else if (artist) { artistres = callapi("artists/" + encodeURIComponent(artist)); if ('config' in localStorage) { curiolink = ' <a href="https://everynoise.com/curio.html?page=songsby:' + artistres['uri'] + '" class=curiolink title="send this to Curio">»</a>'; } else { curiolink = ''; } document.getElementById('title').innerHTML = '<a href="' + artistres['uri'] + '">' + h(artistres['name']) + '</a>' + curiolink; fcount = artistres['followers']['total']; document.getElementById('followers').innerHTML = Intl.NumberFormat().format(fcount) + ' follower' + (fcount == 1 ? '' : 's'); genres = artistres['genres']; genrelinks = []; if (genres) { for (g=0; g<genres.length; g++) { genre = genres[g]; genrelinks.push('<a href="research.cgi?mode=genre&name=' + encodeURIComponent(genre) + '">' + h(genre) + '</a>'); } document.getElementById('genres').innerHTML = genrelinks.join(', '); } albums = callapi("artists/" + encodeURIComponent(artist) + "/albums?limit=50&include_groups=album,single,compilation,appears_on" + (country ? '&market=' + encodeURIComponent(country) : ''), items="items"); } else { return; } albumcounts = {'album': [], 'single': [], 'compilation': [], 'appears_on': []}; for (a=0; a<albums.length; a++) { album = albums[a]; images = album['images']; if (images && images.length > 0) { image = images[0]['url']; imagelink = '<a href="#' + album['id'] + '"><img\ src="' + image + '" class=icon title="' + h(album['name']) + '"></a>'; albumcounts[album['album_group'] == 'appears_on' ? 'appears_on' : album['album_type']].push(imagelink); } } if (!label && !showcomps) { albums = albums.filter((a) => a['album_group'] != 'appears_on' && a['album_type'] != 'appears_on'); } albumdataindex = {}; albumchunksize=20; for (chunkstart=0; chunkstart < albums.length; chunkstart+=albumchunksize) { albumdata = callapi("albums?ids=" + albums.slice(chunkstart, chunkstart+albumchunksize).map((a) => a['id']).join(','), "albums"); albumdata.forEach(ad => { albumdataindex[ad['id']] = ad }) } albums.sort((a, b) => { if (a['release_date'] < b['release_date']) { return 1 } else if (a['release_date'] > b['release_date']) { return -1 } else { return(b['total_tracks'] - a['total_tracks']) } }); for (i=albums.length - 1; i>-1; i--) { album = albums[i]; tracks = album['tracks'] = albumdataindex[album['id']]['tracks']['items']; for (t=0; t<tracks.length; t++) { track = tracks[t]; if (album['album_group'] == 'appears_on' || album['album_type'] == 'compilation') { artisttrack = false; trackartists = track['artists']; for (ta=0; ta<trackartists.length; ta++) { if (trackartists[ta]['id'] == artist) { artisttrack = true; break; } } } else { artisttrack = true; } albums[i]['tracks'][t]['artisttrack'] = artisttrack; if (artisttrack) { nname = normalize(track['name']); usedur = null; if (nname in alltracktitles) { for (a=0; a<alltracktitles[nname].length; a++) { d = alltracktitles[nname][a]; if (Math.abs(d - track['duration_ms']) < 5000) { usedur = mmss(d); break; } }; } else { alltracktitles[nname] = []; } if (!usedur) { alltracktitles[nname].push(track['duration_ms']); usedur = mmss(track['duration_ms']); } track['nname'] = nname; track['usedur'] = usedur; trackkey = maketrackkey(track); track['trackkey'] = trackkey; albumartistids = []; album['artists'].forEach(a => { albumartistids += a['id']; }) otherartists = []; track['artists'].forEach(a => { if (a['id'] != artist && !albumartistids.includes(a['id'])) { otherartists.push(a); } }) if (!(trackkey in alltracks)) { alltracks[trackkey] = {'name': track['name'], 'index': ++alltrackindex, 'rdate': album['release_date'], 'id': track['id'], 'uri': track['uri'], 'albums': [], 'otherartists': otherartists, 'duration': mmss(track['duration_ms'])}; } if (track['name'].length < alltracks[trackkey]['name'].length) { alltracks[trackkey]['name'] = track['name']; } images = album['images']; if (images) { image = images[0]; if (image) { track['image'] = image['url']; albumlink = '<a href="#' + album['id'] + '"><img\ src="' + image['url'] + '" class=inlineicon title="' + 'track ' + track['track_number'] + '/' + album['total_tracks'] + ' on ' + h(album['name']) + '"></a>'; alltracks[trackkey]['albums'].push(albumlink); } } if (!('preview_url' in alltracks[trackkey])) { preview = track['preview_url']; if (preview) { alltracks[trackkey]['preview_url'] = preview; } } } } } if (!label) { images = artistres['images']; if (images && images.length > 0) { artistart = document.getElementById('artistart'); artistart['src'] = images[0]['url']; artistart.style['display'] = 'inline'; } } typebox = document.getElementById('typebox'); rtypes = [['album', 'album'], ['single', 'single'], ['compilation', 'compilation'], ['appears_on', 'other appearance']]; for (x=0; x<rtypes.length; x++) { key = rtypes[x][0]; word = rtypes[x][1]; thislist = albumcounts[key]; if ((thislist && thislist.length > 0) || key == 'album' || key == 'single') { thiscount = thislist.length; if (key == 'appears_on') { if (showcomps) { showhide = ' <span class=showhide>· <a href="?id=' + encodeURIComponent(artist) + (country ? '&country=' + encodeURIComponent(country) : '') + '">hide</a></span>'; } else { showhide = ' <span class=showhide>· <a href="?id=' + encodeURIComponent(artist) + (country ? '&country=' + encodeURIComponent(country) : '') + '&showcomps=true">show</a></span>'; } } else { showhide = ''; } makeElement('div', typebox, thiscount + ' ' + word + (thiscount == 1 ? '' : 's') + (country ? ' in ' + h(country) : '') + showhide, ['icontitle', 'note']); if (showcomps || key != 'appears_on') { makeElement('div', typebox, thislist.join(' '), ['icons', key]); } } } if (alltracks && (!label || dolabelall)) { if (country) { cnote = ' in ' + h(country) + ' <span class=showhide>· <a href="?id=' + encodeURIComponent(artist) + (showcomps ? '&showcomps=true' : '') + '">show global</a></span>'; } else { cnote = ''; } makeElement('div', typebox, '<a href="#alltracks">' + alltrackindex + ' unique track' + (alltrackindex == 1 ? '' : 's') + '</a>' + cnote + '</div>', ['icontitle', 'note']) } trackexport = []; for(i=0; i<albums.length; i++) { album = albums[i]; albumdata = albumdataindex[album['id']]; albumartistids = []; for (a=0; a<album['artists'].length; a++) { albumartistids.push(album['artists'][a]['id']); } totalduration = 0; for (t=0; t<album['tracks'].length; t++) { totalduration += album['tracks'][t]['duration_ms']; } albumdiv = makeElement("div", albumbox, '', ["albumbox", album['album_type']]); albumdiv.setAttribute('id', album['id']); images = album['images']; if (images && images[0]) { imgdiv = makeElement('div', albumdiv, '<a href="' + album['uri'] + '"><img class="art ' + album['album_type'] + 'art" src\="' + images[0]['url'] + '"></a>'); } if (album['album_group'] == 'appears_on' || label) { albumartists = []; for(aa=0; aa<album['artists'].length; aa++) { albumartists.push('<a href="?id=' + encodeURIComponent(album['artists'][aa]['id']) + '" class="' + album[label ? 'album_type' : 'album_group'] + 'artist">' + h(album['artists'][aa]['name']) + '</a>'); } aastring = albumartists.join(', ') + ' · '; } else { aastring = '' } if ('config' in localStorage) { curiolink = ' <a href="https://everynoise.com/curio.html?page=getplaylists&comp=' + album['uri'] + '" class=curiolink title="send this to Curio">»</a>'; } else { curiolink = ''; } albumtitle = makeElement("div", albumdiv, aastring + '<a class=' + album[label ? 'album_type' : 'album_group'] + 'title href="' + album['uri'] + '">' + h(album['name']) + '</a>' + curiolink, [album['album_format'] + 'title']); clines = []; if ('copyrights' in albumdata && albumdata['copyrights']) { for (c=0; c<albumdata['copyrights'].length; c++) { ctext = albumdata['copyrights'][c]['text']; if (ctext && !clines.includes(ctext)) { clines.push(h(ctext)); } } } albumparts = []; albumparts.push('<span title="' + clines.join('\n') + '">' + h(album['release_date']) + '</span>'); if ('label' in albumdata && albumdata['label']) { albumparts.push(' · <a href="?id=label:' + encodeURIComponent(albumdata['label']) + '" class=note>' + h(albumdata['label']) + '</a>'); } trackcount = album['total_tracks']; if (trackcount > 1) { albumparts.push(' · ' + trackcount + ' tracks'); albumparts.push(' · ' + mmss(totalduration)); } makeElement('div', albumdiv, albumparts.join(''), ['note']); trackbox = makeElement("div", albumdiv, ''); tracktable = makeElement("table", trackbox, ''); if (dotrackexport) { trackdata = callapi('tracks?ids=' + album.tracks.map((t) => t.id).join(',')); trackexport.push(...trackdata.tracks.map((t) => [t.external_urls.spotify, t.external_ids.isrc, t.name, t.popularity, t.track_number, t.explicit, t.album.release_date, t.artists[0].name, albumdata.label])); // Spotify URL ISRC Track Name track_popularity track_number Explicit Release Date Artist Name Label } for (t=0; t<album['tracks'].length; t++) { track=album['tracks'][t]; if (track['artisttrack']) { trackkey = track['trackkey']; otherartists = alltracks[trackkey]['otherartists']; if (otherartists && otherartists.length > 0) { otherlinks = []; for (o=0; o<otherartists.length; o++) { otherartist = otherartists[o]; otherlinks.push('<a href="?id=' + encodeURIComponent(otherartist['id']) + '">' + h(otherartist['name']) + '</a>') } otherartiststring = '<span class=note> +' + otherlinks.join(', ') + '</span>'; } else { otherartiststring = ''; } alltracks[trackkey]['otherartiststring'] = otherartiststring; if (!track['available_markets'] || track['available_markets'].length == 0) { availstring = '<span class=unavailable title="track marked unavailable">⊗</a>'; } else { availstring = '' } otherreleases = []; for (o=0; o<alltracks[trackkey]['albums'].length; o++) { img = alltracks[trackkey]['albums'][o]; if (!(img.includes(track['image']))) { otherreleases.push(img); } } otherreleasestring = otherreleases.join(' '); trackrow = makeElement('tr', tracktable, '', ["trackrow", highlight.includes(track['id']) ? 'highlight' : '']); tracknumbercell = makeElement('td', trackrow, track['track_number'], ["stat", "play"]); trackdurationcell = makeElement('td', trackrow, mmss(track['duration_ms']), ["stat", "play"]); trackdurationcell.setAttribute('trackid', track['id']); if ('preview_url' in track && track['preview_url']) { trackdurationcell.setAttribute('preview_url', track['preview_url']); } trackdurationcell.setAttribute('nolink', true); trackcell = makeElement('td', trackrow, '<a href="' + track['uri'] + '">'+ h(track['name']) + '</a>' + availstring + otherartiststring); iconcell = makeElement('td', trackrow, otherreleasestring, ['inlineiconcell']); } } allcountries = 'AR AU AT BE BO BR BG CA CL CO CR CY CZ DK DO DE EC EE SV FI FR GR GT HN HK HU IS IE IT LV LT LU MY MT MX NL NZ NI NO PA PY PE PH PL PT SG SK ES SE CH TW TR UY US GB AD LI MC ID JP TH VN RO IL ZA SA AE BH QA OM KW EG MA DZ TN LB JO PS IN BY KZ MD UA AL BA HR ME MK RS SI KR BD PK LK GH KE NG TZ UG AG AM BS BB BZ BT BW BF CV CW DM FJ GM GE GD GW GY HT JM KI LS LR MW MV ML MH FM NA NR NE PW PG PR WS SM ST SN SC SL SB KN LC VC SR TL TO TT TV VU AZ BN BI KH CM TD KM GQ SZ GA GN KG LA MO MR MN NP RW TG UZ ZW BJ MG MU MZ AO CI DJ ZM CD CG IQ LY TJ VE ET XK'.split(' ').sort(); if (!country) { if (album['available_markets'].length >= 180) { makeElement('div', albumdiv, 'available in ' + (album['available_markets'].length == 185 ? 'all ' : '') + album['available_markets'].length + ' countries' + (album['available_markets'].length == 184 ? ' (not ' + allcountries.filter((c) => !album['available_markets'].includes(c)).join(' ') + ')' : ''), ['countries']); } else { avail = album['available_markets'].sort(); availlinks = []; for (a=0; a<avail.length; a++) { availlinks.push('<a href="?id=' + encodeURIComponent(artist) + '&country=' + encodeURIComponent(avail[a]) + (showcomps ? '&showcomps=true' : '') + '">' + avail[a] + '</a>') } makeElement('div', albumdiv, '<span title="available in ' + avail.length + (avail.length == 1 ? ' country' : ' countries') + '">' + avail.length + '</span>: ' + availlinks.join(' '), ['countries']); } } } if (dotrackexport) { console.log(trackexport); exportToCsv(trackexport); } if (alltracks && (!label || dolabelall)) { if (label) { usetitle = h(label); } else { usetitle = h(artistres['name']) } sortedalltracks = Object.values(alltracks).sort((a, b) => (tracksort == 'asc' ? 1 : -1) * (a['index'] - b['index'])); trackorder = [] for (t=0; t<sortedalltracks.length; t++) { trackorder.push(sortedalltracks[t]['uri']); } maxlinktrackcount = 100; if (trackorder.length > maxlinktrackcount) { linktrackorder = trackorder.slice(maxlinktrackcount, -1); linktitle = 'first ' + maxlinktrackcount + ' ' + usetitle + ' tracks'; } else { linktrackorder = trackorder; linktitle = 'all ' + usetitle + ' tracks'; } if (tracksort == 'asc') { tracksorter = '<span class=tracksorter title="re-sort from latest to earliest" onclick="reversealltracks()">↑</a>'; } else { tracksorter = '<span class=tracksorter title="re-sort from earliest to latest" onclick="reversealltracks()">↓</a>'; } alltrackstitle = makeElement('div', alltracksbox, '<a href="playlistprofile.cgi?title=' + encodeURIComponent(linktitle) + '&these=' + linktrackorder.join('&these=') + '">(all tracks)</a> ' + tracksorter, ["alltrackstitle"]); alltrackstable = makeElement('table', alltracksbox, '', ['tracktable']); for (t=0; t<sortedalltracks.length; t++) { tdata = sortedalltracks[t]; alltrackrow = makeElement('tr', alltrackstable, '', ['alltrackrow', 'trackrow', highlight.includes(tdata['id']) ? 'highlight' : '']); alltrackindexcell = makeElement('td', alltrackrow, tdata['index'], ['stat']); alltrackdatecell = makeElement('td', alltrackrow, tdata['rdate'], ['date']); alltrackdurationcell = makeElement('td', alltrackrow, tdata['duration'], ['stat', 'play']); alltrackdurationcell.setAttribute('trackid', tdata['id']); alltrackdurationcell.setAttribute('nolink', true); if ('preview_url' in tdata) { alltrackdatecell.setAttribute('preview_url', tdata['preview_url']); } alltrackcell = makeElement('td', alltrackrow, '<a href="' + tdata['uri'] + '">' + h(tdata['name']) + '</a>' + tdata['otherartiststring']); alltrackiconcell = makeElement('td', alltrackrow, tdata['albums'].join(' '), ['inlineiconcell']); } } if (trackorder && trackorder.length > 0) { uribox = makeElement('div', alltracksbox, '<div class=note onclick="toggle(this);" style="cursor: pointer">show track URIs</div><div class=innerbox style="display: none"><br>You can copy and paste these into a Spotify playlist...<br><textarea id=urilist rows=' + trackorder.length + ' cols=50>' + trackorder.join('\n') + '</textarea></div>', ['uribox']) } if (!label) { if (falsource == 'canon') { fals = getcanon(artist); } else { fals = callapi('artists/' + encodeURIComponent(artist) + '/related-artists', 'artists'); } if (fals && fals.length > 0) { falcell = document.getElementById('falcell'); falcell.style['display'] = 'table-cell'; falnote = makeElement('div', falcell, falsource == 'canon' ? 'fans also like' : 'playlisted like', ['note']); for (f=0; f<fals.length; f++) { fal = fals[f]; falid = fal['id']; if (fal['images'].length > 0) { imgurl = fal['images'][0]['url']; imgstr = '<a href="?id=' + encodeURIComponent(fal['id']) + '"><img\ src="' + imgurl + '" class=falart></a><br>' } else { imgstr = ''; } namestr = '<div class=falname><a href="?id=' + encodeURIComponent(fal['id']) + '">' + h(fal['name']) + '</a></div>' if ('followers' in fal) { fcount = fal['followers']['total']; followerstr = '<div class=note>' + Intl.NumberFormat().format(fcount) + ' follower' + (fcount == 1 ? '' : 's') + '</div>'; } else { followerstr = ''; } if ('genres' in fal && fal['genres']) { genrelinks = []; for (g=0; g<fal['genres'].length; g++) { genre = fal['genres'][g]; genrelinks.push('<a href="research.cgi?mode=genre&name=' + encodeURIComponent(genre) + '">' + h(genre) + '</a>'); } genrestr = '<div class=genres>' + genrelinks.join(', ') + '</div>'; } else { genrestr = ''; } falbox = makeElement('div', falcell, imgstr + namestr + followerstr + genrestr, ['falbox']); } makeElement('div', falcell, 'or try:<br><a href="research.cgi?mode=radio&name=spotify:artist:' + encodeURIComponent(artist) + '">artist radio</a><br><a href="canonicalpath.cgi?uri=' + artistres['uri'] + '">the canonical path</a>', ['radionote']) } } playelements = document.getElementsByClassName('play'); for (i=0; i<playelements.length; i++) { playelement = playelements[i]; if (!('onclick' in playelement) || !playelement.onclick) { playelement.addEventListener('click', function(event) { playmeta(event.target); }); } } } function reversealltracks() { alltrackrows = document.getElementsByClassName('alltrackrow'); alltracktable = alltrackrows[0].parentElement; for (a=alltrackrows.length - 1; a>-1; a--) { alltracktable.append(alltrackrows[a]); } urilist = document.getElementById('urilist'); urilist.value = urilist.value.split('\n').reverse().join('\n'); } function toggle(me) { target = me.nextSibling; action = me.textContent.substr(0, 4); if (action == 'show') { me.textContent = 'hide track URIs'; target.style.display = 'block'; target.lastChild.focus(); target.lastChild.select(); } else { me.textContent = 'show track URIs'; target.style.display = 'none'; } } </script> </head> <body onload="go()"> <table cellspacing=0 cellpadding=0 border=0><tr valign=top><td class=discocell> <div class=note><a href="research.cgi">←research</a> · artist</div> <img class=artistart id=artistart src=""> <div class=title id=title></div> <div class=note id=followers></div> <div class=genres id=genres></div> <div class=typebox id=typebox></div> <div id=albumbox></div> <div id=alltracks class=albumbox></div> <div id=loading style="color: gray">loading...</div> <div id=footer style="width: 800px; padding-top: 32px; color: gray; text-align: left; clear: both; visiblity: hidden">Data API by <a href="https://developer.spotify.com/documentation/web-api/reference/#category-artists">Spotify</a>, results reorganized by <a href="http://everynoise.com">glenn mcdonald</a>.<br> <br> </td> <td class=falcell id=falcell> </td></tr></table> </body></html>