diff --git a/lib/index.js b/lib/index.js index 1e515a1..12d8253 100644 --- a/lib/index.js +++ b/lib/index.js @@ -174,12 +174,16 @@ async function getArtistOrLabelInfo(artistOrLabelUrl, options = {}) { artistOrLabelUrl, imageFormat: await _parseImageFormatArg(options.imageFormat) }; + let url = utils.getUrl('music', artistOrLabelUrl); + if (options.labelId) { + url += '/?label=' + encodeURIComponent(options.labelId); + } // The landing page of some artists and labels don't actually // contain the 'bio' column, so we fetch from the // 'music' page instead. For artists, if the 'music' page does not // have the artist info, we shall try with an album or track page // (this is inefficient...perhaps there is a better way?). - return _fetchPage(utils.getUrl('music', artistOrLabelUrl)) + return _fetchPage(url) .then( html => parser.parseArtistOrLabelInfo(html, opts) ) .then( info => { if (info.type === 'label' || info.name !== '') { diff --git a/lib/parser.js b/lib/parser.js index c84fd9b..9a14b96 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -145,44 +145,59 @@ function parseAlbumInfo(html, opts) { numTracks: basic.numTracks, imageUrl: utils.reformatImageUrl(basic.image, opts.albumImageFormat), keywords: basic.keywords, - description: basic.description, + description: basic.description || '', releaseDate: extra.album_release_date, artist: { name: basic.byArtist.name, - url: basic.byArtist['@id'], - description: basic.byArtist.description, - imageUrl: utils.reformatImageUrl(basic.byArtist.image, opts.artistImageFormat) + url: basic.byArtist['@id'] || null, + description: null, + imageUrl: null, }, + publisher: null, + label: _parseBackToLabelLink($), releases: [], tracks: [] }; + _setPublisher(album, basic, opts.artistImageFormat); + if (Array.isArray(basic.albumRelease)) { - basic.albumRelease.forEach( release => { + basic.albumRelease.filter( release => release.musicReleaseFormat ).forEach( release => { const releaseItem = { name: release.name, url: null, format: release.musicReleaseFormat, description: release.description || '', - imageUrl: utils.reformatImageUrl(release.image, opts.albumImageFormat) + imageUrl: null } - if (release.url) { - releaseItem.url = !utils.isAbsoluteUrl(release.url) ? utils.getUrl(release.url, album.url) : release.url; + if (release['@id']) { + releaseItem.url = !utils.isAbsoluteUrl(release['@id']) ? utils.getUrl(release['@id'], album.url) : release['@id']; + } + if (release.image) { + if (Array.isArray(release.image) && release.image[0]) { + releaseItem.imageUrl = release.image[0]; + } + } + else { + let releaseImageArtId = getAdditionalPropertyValue(release, 'art_id'); + if (releaseImageArtId) { + opts.imageBaseUrl + '/img/a' + releaseImageArtId + '_' + opts.albumImageFormat.id + '.jpg' + } } album.releases.push(releaseItem); }); } - if (Array.isArray(basic.track.itemListElement)) { - basic.track.itemListElement.forEach( track => { - let trackUrl = track.item['@id']; + if (Array.isArray(extra.trackinfo)) { + extra.trackinfo.forEach( track => { + let trackUrl = track.title_link; if (!utils.isAbsoluteUrl(trackUrl)) { trackUrl = utils.getUrl(trackUrl, album.url); } album.tracks.push({ - position: track.position, - name: track.item.name, + position: track.track_num, + name: track.title, url: trackUrl, - duration: getAdditionalPropertyValue(track.item, 'duration_secs'), - streamUrl: getAdditionalPropertyValue(track.item, 'file_mp3-128') || null + duration: track.duration, + streamUrl: (track.file && track.file['mp3-128']) || null }); }); } @@ -227,15 +242,27 @@ function parseTrackInfo(html, opts) { releaseDate: extra.current.release_date, duration: getAdditionalPropertyValue(basic, 'duration_secs'), streamUrl: extra.trackinfo && extra.trackinfo[0] && extra.trackinfo[0].file && extra.trackinfo[0].file['mp3-128'] ? extra.trackinfo[0].file['mp3-128'] : null, - artist: { - name: basic.byArtist.name, - url: basic.byArtist['@id'], - description: basic.byArtist.description, - imageUrl: utils.reformatImageUrl(basic.byArtist.image, opts.artistImageFormat) - }, + artist: null, + publisher: null, + label: _parseBackToLabelLink($), album: null } - if (basic.inAlbum) { + let byArtist; + if (basic.inAlbum && basic.inAlbum.byArtist) { + byArtist = basic.inAlbum.byArtist; + } + else { + byArtist = basic.byArtist; + } + track.artist = { + name: byArtist.name, + url: byArtist['@id'] || null, + description: null, + imageUrl: null + } + _setPublisher(track, basic, opts.artistImageFormat); + + if (basic.inAlbum && basic.inAlbum['@id']) { track.album = { name: basic.inAlbum.name, url: basic.inAlbum['@id'], @@ -266,12 +293,9 @@ function parseTrackInfoFromAlbum(html, opts, trackPosition) { releaseDate: album.releaseDate, duration: trackData.duration, streamUrl: trackData.streamUrl, - artist: { - name: album.artist.name, - url: album.artist.url, - description: album.artist.description, - imageUrl: album.artist.imageUrl - }, + artist: album.artist, + publisher: album.publisher, + label: album.label, album: { name: album.name, url: album.url, @@ -407,6 +431,7 @@ function parseDiscography(html, opts) { function parseArtistOrLabelInfo(html, opts) { const $ = cheerio.load(html); + const bandData = JSON.parse(decode($('script[data-band]').attr('data-band'))); let bioText = $('#bio-text'); let description; @@ -429,30 +454,22 @@ function parseArtistOrLabelInfo(html, opts) { description = ''; } - let isLabel = $('a[href="/artists"]').length; - let label = null; - if (!isLabel) { - let labelLink = $('a.back-to-label-link'); - if (labelLink.length) { - let linkText = labelLink.find('.back-link-text').html(); - label = { - name: utils.substrAfter(linkText, '
') || utils.substrBefore(linkText, ' に戻る') || utils.substrBefore(linkText, ' のアイテムをもっと聴く'), - url: utils.splitUrl(labelLink.attr('href')).base - }; - } - } - + let isLabel = bandData.is_label; const result = { type: isLabel ? 'label' : 'artist', - name: $('#band-name-location').find('.title').text(), - url: opts.artistOrLabelUrl, + name: bandData.name, + url: bandData.url, description: description, location: $('#band-name-location').find('.location').text(), imageUrl: utils.reformatImageUrl($('img.band-photo').attr('src'), opts.imageFormat) }; if (!isLabel) { - result.label = label; + result.label = _parseBackToLabelLink($); } + else { + result.labelId = bandData.id; + } + return result; } @@ -1047,7 +1064,7 @@ function parseHubJSPath(html) { function parseHubJSFilterValueNames(js) { const filterValueNames = {}; - const tObj = /"hubs\/digdeeper\/filter_value":(.+?)}\);/gs.exec(js); + const tObj = /"hubs\/digdeeper\/filter_value":(.+?)}\),/gs.exec(js); if (tObj[1]) { const t = safeEval(tObj[1]); if (t && t[0] && Array.isArray(t[0].blocks)) { @@ -1237,6 +1254,47 @@ function parseSearchLocationResults(json) { } } +function _parseBackToLabelLink($) { + let labelLink = $('.back-to-label-link'); + if (labelLink.length) { + let linkText = labelLink.find('.back-link-text').html(); + label = { + name: utils.substrAfter(linkText, '
') || + utils.substrAfter(linkText, '
') || + utils.substrBefore(linkText, ' に戻る') || + utils.substrBefore(linkText, ' のアイテムをもっと聴く'), + url: utils.splitUrl(labelLink.attr('href')).base + }; + return label; + } + + return null; +} + +function _setPublisher(target, json, imageFormat) { + if (json.publisher) { + let publisher = { + name: json.publisher.name, + url: json.publisher['@id'], + description: json.publisher.description, + imageUrl: utils.reformatImageUrl(json.publisher.image, imageFormat) + } + target.publisher = publisher; + + // For backward compatibility + if (target.artist) { + if (target.artist.url === null) { + target.artist.url = target.publisher.url; + } + target.artist.description = target.publisher.description; + target.artist.imageUrl = target.publisher.imageUrl; + } + } + else { + target.publisher = null; + } +} + module.exports = { parseDiscoverResults, parseDiscoverOptions,