mirror of
https://github.com/murdos/musicbrainz-userscripts
synced 2024-11-10 05:04:13 +00:00
48182be483
Discogs has rolled out a new layout for release pages on 2021-08-09: https://www.discogs.com/forum/thread/828514?page=3#8916061 This breaks the injection of the import button and MB links on release pages, other pages (artist, label, master) are not affected so far. With this commit, the following changes are made to fix #411 and #354: - Update selectors for the sections which should be enhanced with MB links - Separate working and broken selectors but keep all of them for now - Find a new selector to inject the import button into the sidebar (not sure why there were three alternative selectors previously) - Duplicate some of Discogs' CSS to fix the layout because they seem to use dynamically generated class names now :(
1118 lines
39 KiB
JavaScript
1118 lines
39 KiB
JavaScript
// ==UserScript==
|
||
|
||
// @name Import Discogs releases to MusicBrainz
|
||
// @description Add a button to import Discogs releases to MusicBrainz and add links to matching MusicBrainz entities for various Discogs entities (artist,release,master,label)
|
||
// @version 2021.8.10.1
|
||
// @namespace http://userscripts.org/users/22504
|
||
// @downloadURL https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/discogs_importer.user.js
|
||
// @updateURL https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/discogs_importer.user.js
|
||
// @include http*://www.discogs.com/*
|
||
// @include http*://*.discogs.com/*release/*
|
||
// @exclude http*://*.discogs.com/*release/*?f=xml*
|
||
// @exclude http*://www.discogs.com/release/add
|
||
// @require https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
|
||
// @require lib/mbimport.js
|
||
// @require lib/logger.js
|
||
// @require lib/mblinks.js
|
||
// @require lib/mbimportstyle.js
|
||
// @icon https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/assets/images/Musicbrainz_import_logo.png
|
||
// ==/UserScript==
|
||
|
||
// prevent JQuery conflicts, see http://wiki.greasespot.net/@grant
|
||
this.$ = this.jQuery = jQuery.noConflict(true);
|
||
|
||
const DEBUG = false;
|
||
if (DEBUG) {
|
||
LOGGER.setLevel('debug');
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
/*
|
||
* Test cases:
|
||
* - http://www.discogs.com/release/1566223 : Artist credit of tracks contains an ending ',' join phrase
|
||
*/
|
||
|
||
const mbLinks = new MBLinks('DISCOGS_MBLINKS_CACHE', '1');
|
||
|
||
$(document).ready(function () {
|
||
MBImportStyle();
|
||
MBSearchItStyle();
|
||
|
||
const current_page_key = getDiscogsLinkKey(
|
||
window.location.href.replace(/\?.*$/, '').replace(/#.*$/, '').replace('/master/view/', '/master/')
|
||
);
|
||
if (!current_page_key) return;
|
||
|
||
// disable evil pjax (used for artist page navigation)
|
||
// it causes various annoying issues with our code;
|
||
// it should be possible to react to pjax events
|
||
$('div#pjax_container').attr('id', 'pjax_disabled');
|
||
|
||
// Display links of equivalent MusicBrainz entities
|
||
insertMBLinks(current_page_key);
|
||
|
||
// Add an import button in a new section in sidebar, if we're on a release page
|
||
let current_page_info = link_infos[current_page_key];
|
||
if (current_page_info.type === 'release') {
|
||
// Discogs Webservice URL
|
||
let discogsWsUrl = `https://api.discogs.com/releases/${current_page_info.id}`;
|
||
|
||
$.ajax({
|
||
url: discogsWsUrl,
|
||
dataType: 'json',
|
||
crossDomain: true,
|
||
success: data => {
|
||
LOGGER.debug('Discogs JSON Data from API:', data);
|
||
try {
|
||
let release = parseDiscogsRelease(data);
|
||
insertMBSection(release, current_page_key);
|
||
} catch (e) {
|
||
$('div.musicbrainz').remove();
|
||
let mbUI = $('<div class="section musicbrainz"><h3>MusicBrainz</h3></div>').hide();
|
||
let mbContentBlock = $('<div class="section_content"></div>');
|
||
mbUI.append(mbContentBlock);
|
||
let mbError = $(
|
||
`<p><small>${e}<br /><b>Please <a href="https://github.com/murdos/musicbrainz-userscripts/issues">report</a> this error, along the current page URL.</b></small></p>`
|
||
);
|
||
mbContentBlock.prepend(mbError);
|
||
insertMbUI(mbUI);
|
||
mbError.css({ 'background-color': '#fbb', 'margin-top': '4px', 'margin-bottom': '4px' });
|
||
mbUI.slideDown();
|
||
throw e;
|
||
}
|
||
},
|
||
error: function (jqXHR, textStatus, errorThrown) {
|
||
LOGGER.error('AJAX Status: ', textStatus);
|
||
LOGGER.error('AJAX error thrown: ', errorThrown);
|
||
},
|
||
});
|
||
}
|
||
});
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Display links of equivalent MusicBrainz entities //
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// Insert MusicBrainz links in a section of the page
|
||
function insertMBLinks(current_page_key) {
|
||
function searchAndDisplayMbLinkInSection($tr, discogs_type, mb_type, nosearch) {
|
||
if (!mb_type) mb_type = defaultMBtype(discogs_type);
|
||
$tr.find(`a[mlink^="${discogs_type}/"]`).each(function () {
|
||
const $link = $(this);
|
||
if ($link.attr('mlink_stop')) return; // for places
|
||
const mlink = $link.attr('mlink');
|
||
// ensure we do it only once per link
|
||
const done = ($link.attr('mlink_done') || '').split(',');
|
||
for (let i = 0; i < done.length; i++) {
|
||
if (mb_type == done[i]) return;
|
||
}
|
||
done.push(mb_type);
|
||
$link.attr(
|
||
'mlink_done',
|
||
done
|
||
.filter(function (e) {
|
||
return e != '';
|
||
})
|
||
.join(',')
|
||
);
|
||
if (link_infos[mlink] && link_infos[mlink].type === discogs_type) {
|
||
const discogs_url = link_infos[mlink].clean_url;
|
||
let cachekey = getCacheKeyFromInfo(mlink, mb_type);
|
||
const has_wrapper = $link.closest('span.mb_wrapper').length;
|
||
if (!has_wrapper) {
|
||
$link.wrap('<span class="mb_wrapper"><span class="mb_valign"></span></span>');
|
||
}
|
||
if (!nosearch) {
|
||
// add search link for the current link text
|
||
const entities = {
|
||
artist: { mark: 'A' },
|
||
release: { mark: 'R' },
|
||
'release-group': { mark: 'G' },
|
||
place: { mark: 'P' },
|
||
label: { mark: 'L' },
|
||
};
|
||
let mark = '';
|
||
let entity_name = 'entity';
|
||
if (mb_type in entities) {
|
||
mark = entities[mb_type].mark;
|
||
entity_name = mb_type.replace(/[_-]/g, ' ');
|
||
}
|
||
$link
|
||
.closest('span.mb_wrapper')
|
||
.prepend(
|
||
`<span class="mb_valign mb_searchit"><a class="mb_search_link" target="_blank" title="Search this ${entity_name} on MusicBrainz (open in a new tab)" href="${MBImport.searchUrlFor(
|
||
mb_type,
|
||
$link.text()
|
||
)}"><small>${mark}</small>?</a></span>`
|
||
);
|
||
}
|
||
const insert_normal = function (link) {
|
||
$link.closest('span.mb_valign').before(`<span class="mb_valign">${link}</span>`);
|
||
$link.closest('span.mb_wrapper').find('.mb_searchit').remove();
|
||
};
|
||
|
||
const insert_stop = function (link) {
|
||
insert_normal(link);
|
||
$link.attr('mlink_stop', true);
|
||
};
|
||
|
||
let insert_func = insert_normal;
|
||
if (mb_type == 'place') {
|
||
// if a place link was added we stop, we don't want further queries for this 'label'
|
||
insert_func = insert_stop;
|
||
}
|
||
mbLinks.searchAndDisplayMbLink(discogs_url, mb_type, insert_func, cachekey);
|
||
}
|
||
});
|
||
}
|
||
|
||
function debug_color(what, n, id) {
|
||
let colors = [
|
||
'#B3C6FF',
|
||
'#C6B3FF',
|
||
'#ECB3FF',
|
||
'#FFB3EC',
|
||
'#FFB3C6',
|
||
'#FFC6B3',
|
||
'#FFECB3',
|
||
'#ECFFB3',
|
||
'#C6FFB3',
|
||
'#B3FFC6',
|
||
'#B3FFEC',
|
||
'#B3ECFF',
|
||
'#7598FF',
|
||
];
|
||
if (DEBUG) {
|
||
$(what).css('border', `2px dotted ${colors[n % colors.length]}`);
|
||
let debug_attr = $(what).attr('debug_discogs');
|
||
if (!id) id = '';
|
||
if (debug_attr) {
|
||
$(what).attr('debug_discogs', `${debug_attr} || ${id}(${n})`);
|
||
} else {
|
||
$(what).attr('debug_discogs', `${id}(${n})`);
|
||
}
|
||
}
|
||
}
|
||
|
||
let add_mblinks_counter = 0;
|
||
function add_mblinks(_root, selector, types, nosearch) {
|
||
// types can be:
|
||
// 'discogs type 1'
|
||
// ['discogs_type 1', 'discogs_type 2']
|
||
// [['discogs_type 1', 'mb type 1'], 'discogs_type 2']
|
||
// etc.
|
||
if (!$.isArray(types)) {
|
||
// just one string
|
||
types = [types];
|
||
}
|
||
$.each(types, function (idx, val) {
|
||
if (!$.isArray(val)) {
|
||
types[idx] = [val, undefined];
|
||
}
|
||
});
|
||
|
||
LOGGER.debug(`add_mblinks: ${selector} / ${JSON.stringify(types)}`);
|
||
|
||
_root.find(selector).each(function () {
|
||
const node = $(this).get(0);
|
||
magnifyLinks(node);
|
||
debug_color(this, ++add_mblinks_counter, selector);
|
||
const that = this;
|
||
$.each(types, function (idx, val) {
|
||
const discogs_type = val[0];
|
||
const mb_type = val[1];
|
||
searchAndDisplayMbLinkInSection($(that), discogs_type, mb_type, nosearch);
|
||
});
|
||
});
|
||
}
|
||
|
||
// Find MB link for the current page and display it next to page title
|
||
let mbLinkInsert = function (link) {
|
||
const $h1 = $('h1');
|
||
const $titleSpan = $h1.children('span[itemprop="name"]');
|
||
if ($titleSpan.length > 0) {
|
||
$titleSpan.before(link);
|
||
} else {
|
||
$h1.prepend(link);
|
||
}
|
||
};
|
||
const current_page_info = link_infos[current_page_key];
|
||
const mb_type = defaultMBtype(current_page_info.type);
|
||
const cachekey = getCacheKeyFromInfo(current_page_key, mb_type);
|
||
mbLinks.searchAndDisplayMbLink(current_page_info.clean_url, mb_type, mbLinkInsert, cachekey);
|
||
|
||
const $root = $('body');
|
||
// artist/label/master pages, release pages (before the 2021-08-09 update)
|
||
add_mblinks($root, 'div.profile', ['artist', 'label']);
|
||
add_mblinks($root, 'tr[data-object-type="release"] td.artist,td.title', 'artist');
|
||
add_mblinks($root, 'tr[data-object-type="release"] td.title', 'release');
|
||
add_mblinks($root, 'tr[data-object-type="release"]', 'label');
|
||
add_mblinks($root, 'tr[data-object-type~="master"]', ['master', 'artist', 'label']);
|
||
// release pages (since the 2021-08-09 update)
|
||
add_mblinks($root, '#release-header', ['artist', 'label']);
|
||
setInterval(() => add_mblinks($root, '#release-other-versions', ['artist', 'release', 'label']), 500); // Discogs loads this dynamically, wait a moment
|
||
add_mblinks($root, '#release-tracklist', 'artist');
|
||
add_mblinks($root, '#release-companies', [['label', 'place'], 'label']);
|
||
add_mblinks($root, '#release-credits', ['label', 'artist']);
|
||
add_mblinks($root, '#release-actions', 'master', true);
|
||
// release pages (before the 2021-08-09 update, TODO: remove after the new layout becomes permanent)
|
||
add_mblinks($root, 'div#tracklist', 'artist');
|
||
add_mblinks($root, 'div#companies', [['label', 'place'], 'label']);
|
||
add_mblinks($root, 'div#credits', ['label', 'artist']);
|
||
add_mblinks($root, 'div#page_aside div.section_content:first', 'master', true);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Normalize Discogs URLs in a DOM tree //
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
let mlink_processed = 0;
|
||
|
||
// Normalize Discogs URLs in a DOM tree
|
||
function magnifyLinks(rootNode) {
|
||
if (!rootNode) {
|
||
rootNode = document.body;
|
||
}
|
||
|
||
// Check if we already added links for this content
|
||
if (rootNode.hasAttribute('mlink_processed')) return;
|
||
rootNode.setAttribute('mlink_processed', ++mlink_processed);
|
||
|
||
let elems = rootNode.getElementsByTagName('a');
|
||
for (let i = 0; i < elems.length; i++) {
|
||
let elem = elems[i];
|
||
|
||
// Ignore empty links
|
||
if (!elem.href || $.trim(elem.textContent) === '' || elem.textContent.substring(4, 0) === 'http') continue;
|
||
if (!elem.hasAttribute('mlink')) {
|
||
elem.setAttribute('mlink', getDiscogsLinkKey(elem.href));
|
||
}
|
||
}
|
||
}
|
||
|
||
// contains infos for each link key
|
||
const link_infos = {};
|
||
|
||
// Parse discogs url to extract info, returns a key and set link_infos for this key
|
||
// the key is in the form discogs_type/discogs_id
|
||
function getDiscogsLinkKey(url) {
|
||
const re = /^https?:\/\/(?:www|api)\.discogs\.com\/(?:(?:(?!sell).+|sell.+)\/)?(master|release|artist|label)s?\/(\d+)(?:[^?#]*)(?:\?noanv=1|\?anv=[^=]+)?$/i;
|
||
const m = re.exec(url);
|
||
if (m !== null) {
|
||
const key = `${m[1]}/${m[2]}`;
|
||
if (!link_infos[key]) {
|
||
link_infos[key] = {
|
||
type: m[1],
|
||
id: m[2],
|
||
clean_url: `https://www.discogs.com/${m[1]}/${m[2]}`,
|
||
};
|
||
LOGGER.debug(`getDiscogsLinkKey:${url} --> ${key}`);
|
||
} else {
|
||
LOGGER.debug(`getDiscogsLinkKey:${url} --> ${key} (key exists)`);
|
||
}
|
||
return key;
|
||
}
|
||
LOGGER.debug(`getDiscogsLinkKey:${url} ?`);
|
||
return false;
|
||
}
|
||
|
||
function getCleanUrl(url, discogs_type) {
|
||
try {
|
||
const key = getDiscogsLinkKey(url);
|
||
if (key) {
|
||
if (!discogs_type || link_infos[key].type === discogs_type) {
|
||
LOGGER.debug(`getCleanUrl: ${key}, ${url} --> ${link_infos[key].clean_url}`);
|
||
return link_infos[key].clean_url;
|
||
} else {
|
||
LOGGER.debug(`getCleanUrl: ${key}, ${url} --> unmatched type: ${discogs_type}`);
|
||
}
|
||
}
|
||
} catch (err) {
|
||
LOGGER.error(err);
|
||
}
|
||
LOGGER.debug(`getCleanUrl: ${url} (${discogs_type}) failed`);
|
||
return false;
|
||
}
|
||
|
||
function defaultMBtype(discogs_type) {
|
||
if (discogs_type === 'master') return 'release-group';
|
||
return discogs_type;
|
||
}
|
||
|
||
function getCacheKeyFromInfo(info_key, mb_type) {
|
||
const inf = link_infos[info_key];
|
||
if (inf) {
|
||
if (!mb_type) mb_type = defaultMBtype(inf.type);
|
||
return `${inf.type}/${inf.id}/${mb_type}`;
|
||
}
|
||
return '';
|
||
}
|
||
|
||
function getCacheKeyFromUrl(url, discogs_type, mb_type) {
|
||
try {
|
||
const key = getDiscogsLinkKey(url);
|
||
if (key) {
|
||
if (!discogs_type || link_infos[key].type == discogs_type) {
|
||
const cachekey = getCacheKeyFromInfo(key, mb_type);
|
||
LOGGER.debug(`getCacheKeyFromUrl: ${key}, ${url} --> ${cachekey}`);
|
||
return cachekey;
|
||
} else {
|
||
LOGGER.debug(`getCacheKeyFromUrl: ${key}, ${url} --> unmatched type: ${discogs_type}`);
|
||
}
|
||
}
|
||
} catch (err) {
|
||
LOGGER.error(err);
|
||
}
|
||
LOGGER.debug(`getCacheKeyFromUrl: ${url} (${discogs_type}) failed`);
|
||
return false;
|
||
}
|
||
|
||
function MBIDfromUrl(url, discogs_type, mb_type) {
|
||
const cachekey = getCacheKeyFromUrl(url, discogs_type, mb_type);
|
||
if (!cachekey) return '';
|
||
return mbLinks.resolveMBID(cachekey);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Insert MusicBrainz section into Discogs page //
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
function insertMbUI(mbUI) {
|
||
let e;
|
||
if ((e = $('#release-marketplace')) && e.length) {
|
||
e.before(mbUI);
|
||
}
|
||
// FIXME: the following selectors are broken since the 2021-08-09 release page update, not sure why there are three alternative selectors
|
||
else if ((e = $('div.section.collections')) && e.length) {
|
||
e.after(mbUI);
|
||
} else if ((e = $('#statistics')) && e.length) {
|
||
e.before(mbUI);
|
||
} else if ((e = $('div.section.social')) && e.length) {
|
||
e.before(mbUI);
|
||
}
|
||
}
|
||
|
||
// Insert links in Discogs page
|
||
function insertMBSection(release, current_page_key) {
|
||
const current_page_info = link_infos[current_page_key];
|
||
|
||
const mbUI = $('<div class="section musicbrainz"><header><h3>MusicBrainz</h3></header></div>').hide();
|
||
|
||
if (DEBUG) mbUI.css({ border: '1px dotted red' });
|
||
|
||
const mbContentBlock = $('<div class="section_content"></div>');
|
||
mbUI.append(mbContentBlock);
|
||
|
||
if (release.maybe_buggy) {
|
||
const warning_buggy = $(
|
||
'<p><small><b>Warning</b>: this release has perhaps a buggy tracklist, please check twice the data you import.</small><p'
|
||
).css({ color: 'red', 'margin-top': '4px', 'margin-bottom': '4px' });
|
||
mbContentBlock.prepend(warning_buggy);
|
||
}
|
||
|
||
// Form parameters
|
||
const edit_note = MBImport.makeEditNote(current_page_info.clean_url, 'Discogs');
|
||
const parameters = MBImport.buildFormParameters(release, edit_note);
|
||
|
||
// Build form + search button
|
||
const innerHTML = `<div id="mb_buttons">${MBImport.buildFormHTML(parameters)}${MBImport.buildSearchButton(release)}</div>`;
|
||
mbContentBlock.append(innerHTML);
|
||
|
||
insertMbUI(mbUI);
|
||
|
||
// FIXME: duplicates some of Discogs' CSS because they seem to use dynamically generated class names since the 2021-08-09 release page update
|
||
$('.musicbrainz header').css({
|
||
// Discogs selector is ".header_W2hzl" (at least now and for me)
|
||
'border-bottom': '1px solid #e5e5e5',
|
||
'padding-left': '5px',
|
||
});
|
||
$('.musicbrainz h3').css({
|
||
// Discogs selector is ".header_W2hzl h3"
|
||
'font-size': '15px',
|
||
'line-height': '20px',
|
||
margin: '0 0 0 -5px',
|
||
padding: '5px',
|
||
});
|
||
$('#mb_buttons').css({
|
||
display: 'inline-block',
|
||
width: '100%',
|
||
'margin-top': '5px', // FIXME: related to the above CSS hacks
|
||
});
|
||
$('form.musicbrainz_import').css({ width: '49%', display: 'inline-block' });
|
||
$('form.musicbrainz_import_search').css({ float: 'right' });
|
||
$('form.musicbrainz_import > button').css({ width: '100%', 'box-sizing': 'border-box' });
|
||
|
||
mbUI.slideDown();
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Parsing of Discogs data //
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
function cleanup_discogs_artist_credit(obj) {
|
||
// Fix some odd Discogs release (e.g. http://api.discogs.com/releases/1566223) that have a ',' join phrase after the last artist
|
||
// Discogs set a join phrase even there's only one artist or when extraartists is set (ie. remix)
|
||
const last = obj.artist_credit.length - 1;
|
||
if (last === 0 || obj.artist_credit[last].joinphrase === ', ') {
|
||
obj.artist_credit[last].joinphrase = '';
|
||
}
|
||
}
|
||
|
||
// Returns the name without the numerical suffic Discogs adds as disambiguation
|
||
// ie. "ABC (123)" -> "ABC"
|
||
function artistNoNum(artist_name) {
|
||
return artist_name.replace(/ \(\d+\)$/, '');
|
||
}
|
||
|
||
// Parse a US date string and set object properties year, month, day
|
||
function parse_YYYY_MM_DD(date, obj) {
|
||
if (!date) return;
|
||
const m = date.split(/\D+/, 3).map(function (e) {
|
||
return parseInt(e, 10);
|
||
});
|
||
if (m[0] !== undefined) {
|
||
obj.year = m[0];
|
||
if (m[1] !== undefined) {
|
||
obj.month = m[1];
|
||
if (m[2] !== undefined) {
|
||
obj.day = m[2];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Analyze Discogs data and return a release object
|
||
function parseDiscogsRelease(discogsRelease) {
|
||
const release = {
|
||
discs: [],
|
||
};
|
||
|
||
//buggy tracklist indicator, used to warn user
|
||
release.maybe_buggy = false;
|
||
|
||
// Release artist credit
|
||
release.artist_credit = [];
|
||
$.each(discogsRelease.artists, function (index, artist) {
|
||
let ac = {
|
||
artist_name: artistNoNum(artist.name),
|
||
credited_name: artist.anv != '' ? artist.anv : artistNoNum(artist.name),
|
||
joinphrase: decodeDiscogsJoinphrase(artist.join),
|
||
mbid: MBIDfromUrl(artist.resource_url, 'artist'),
|
||
};
|
||
if (artist.id === 194) {
|
||
// discogs place holder for various
|
||
ac = MBImport.specialArtist('various_artists', ac);
|
||
}
|
||
release.artist_credit.push(ac);
|
||
});
|
||
cleanup_discogs_artist_credit(release);
|
||
|
||
// ReleaseGroup
|
||
if (discogsRelease.master_url) {
|
||
release.release_group_mbid = MBIDfromUrl(discogsRelease.master_url, 'master');
|
||
}
|
||
|
||
// Release title
|
||
release.title = discogsRelease.title;
|
||
|
||
// Release date
|
||
if (discogsRelease.released) {
|
||
parse_YYYY_MM_DD(discogsRelease.released, release);
|
||
}
|
||
|
||
// Release country
|
||
if (discogsRelease.country) {
|
||
release.country = Countries[discogsRelease.country];
|
||
}
|
||
|
||
// Release labels
|
||
release.labels = [];
|
||
if (discogsRelease.labels) {
|
||
$.each(discogsRelease.labels, function (index, label) {
|
||
const labelInfo = {
|
||
name: label.name,
|
||
catno: label.catno === 'none' ? '[none]' : label.catno,
|
||
mbid: MBIDfromUrl(label.resource_url, 'label'),
|
||
};
|
||
release.labels.push(labelInfo);
|
||
});
|
||
}
|
||
|
||
// Release URL
|
||
release.urls = [{ url: getCleanUrl(discogsRelease.uri, 'release'), link_type: MBImport.URL_TYPES.discogs }];
|
||
|
||
// Release format
|
||
let release_formats = [];
|
||
release.secondary_types = [];
|
||
|
||
if (discogsRelease.formats.length > 0) {
|
||
for (let i = 0; i < discogsRelease.formats.length; i++) {
|
||
// Release format
|
||
const discogs_format = discogsRelease.formats[i].name;
|
||
let mb_format = undefined;
|
||
if (discogs_format in MediaTypes) {
|
||
mb_format = MediaTypes[discogs_format];
|
||
}
|
||
|
||
if (discogsRelease.formats[i].descriptions) {
|
||
$.each(discogsRelease.formats[i].descriptions, function (index, desc) {
|
||
if (!(discogs_format in ['Box Set'])) {
|
||
// Release format: special handling of Vinyl and Shellac 7", 10" and 12"
|
||
if (desc.match(/7"|10"|12"/) && discogs_format.concat(desc) in MediaTypes)
|
||
mb_format = MediaTypes[discogs_format.concat(desc)];
|
||
// Release format: special handling of specific CD/DVD formats
|
||
if (desc.match(/^VCD|SVCD|CD\+G|HDCD|DVD-Audio|DVD-Video/) && desc in MediaTypes) mb_format = MediaTypes[desc];
|
||
}
|
||
// Release format: special handling of Vinyl, LP == 12" (http://www.discogs.com/help/submission-guidelines-release-format.html#LP)
|
||
if (discogs_format === 'Vinyl' && desc === 'LP') {
|
||
mb_format = '12" Vinyl';
|
||
}
|
||
// Release format: special handling of CD, Mini == 8cm CD
|
||
if (discogs_format === 'CD' && desc === 'Mini') {
|
||
mb_format = '8cm CD';
|
||
}
|
||
// Release status
|
||
if (desc.match(/Promo|Smplr/)) {
|
||
release.status = 'promotion';
|
||
}
|
||
if (desc.match(/Unofficial Release/)) {
|
||
release.status = 'bootleg';
|
||
}
|
||
// Release type
|
||
if (desc.match(/Compilation/)) {
|
||
release.secondary_types.push('compilation');
|
||
}
|
||
if (desc.match(/^Album/)) {
|
||
release.type = 'album';
|
||
}
|
||
if (desc.match(/Single(?! Sided)/)) {
|
||
release.type = 'single';
|
||
}
|
||
if (desc.match(/EP|Mini-Album/)) {
|
||
release.type = 'ep';
|
||
}
|
||
});
|
||
}
|
||
|
||
if (mb_format) {
|
||
for (let j = 0; j < discogsRelease.formats[i].qty; j++) {
|
||
release_formats.push(mb_format);
|
||
}
|
||
}
|
||
|
||
// Release packaging
|
||
if (discogsRelease.formats[i].text) {
|
||
const freetext = discogsRelease.formats[i].text.toLowerCase().replace(/[\s-]/g, '');
|
||
if (freetext.match(/cardboard|paper/)) {
|
||
release.packaging = 'cardboard/paper sleeve';
|
||
} else if (freetext.match(/digi[\s\-‐]?pac?k/)) {
|
||
release.packaging = 'digipak';
|
||
} else if (freetext.match(/keepcase/)) {
|
||
release.packaging = 'keep case';
|
||
} else if (freetext.match(/jewel/)) {
|
||
release.packaging = freetext.match(/slim/) ? 'slim jewel case' : 'jewel case';
|
||
} else if (freetext.match(/gatefold|digisleeve/)) {
|
||
release.packaging = 'gatefold cover';
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Barcode
|
||
if (discogsRelease.identifiers) {
|
||
$.each(discogsRelease.identifiers, function (index, identifier) {
|
||
if (identifier.type === 'Barcode') {
|
||
release.barcode = identifier.value.replace(/ /g, '');
|
||
return false;
|
||
}
|
||
});
|
||
}
|
||
|
||
// Inspect tracks
|
||
let heading = '';
|
||
let releaseNumber = 1;
|
||
let lastPosition = 0;
|
||
$.each(discogsRelease.tracklist, function (index, discogsTrack) {
|
||
if (discogsTrack.type_ === 'heading') {
|
||
heading = discogsTrack.title;
|
||
return;
|
||
}
|
||
if (discogsTrack.type_ !== 'track' && discogsTrack.type_ !== 'index') {
|
||
return;
|
||
}
|
||
|
||
let track = {};
|
||
|
||
track.title = discogsTrack.title.replace(/´/g, '’');
|
||
track.duration = MBImport.hmsToMilliSeconds(discogsTrack.duration); // MB in milliseconds
|
||
|
||
// Track artist credit
|
||
track.artist_credit = [];
|
||
if (discogsTrack.artists) {
|
||
$.each(discogsTrack.artists, function (index, artist) {
|
||
const ac = {
|
||
artist_name: artistNoNum(artist.name),
|
||
credited_name: artist.anv !== '' ? artist.anv : artistNoNum(artist.name),
|
||
joinphrase: decodeDiscogsJoinphrase(artist.join),
|
||
mbid: MBIDfromUrl(artist.resource_url, 'artist'),
|
||
};
|
||
track.artist_credit.push(ac);
|
||
});
|
||
cleanup_discogs_artist_credit(track);
|
||
}
|
||
|
||
// Track position and release number
|
||
let trackPosition = discogsTrack.position;
|
||
|
||
// Handle sub-tracks
|
||
if (trackPosition === '' && discogsTrack.sub_tracks) {
|
||
trackPosition = discogsTrack.sub_tracks[0].position;
|
||
// Append titles of sub-tracks to main track title
|
||
const subtrack_titles = [];
|
||
let subtrack_total_duration = 0;
|
||
$.each(discogsTrack.sub_tracks, function (subtrack_index, subtrack) {
|
||
if (subtrack.type_ !== 'track') {
|
||
return;
|
||
}
|
||
if (subtrack.duration) {
|
||
subtrack_total_duration += MBImport.hmsToMilliSeconds(subtrack.duration);
|
||
}
|
||
if (subtrack.title) {
|
||
subtrack_titles.push(subtrack.title);
|
||
} else {
|
||
subtrack_titles.push('[unknown]');
|
||
}
|
||
});
|
||
if (subtrack_titles.length) {
|
||
if (track.title) {
|
||
track.title += ': ';
|
||
}
|
||
track.title += subtrack_titles.join(' / ');
|
||
}
|
||
if (isNaN(track.duration) && !isNaN(subtrack_total_duration)) {
|
||
track.duration = subtrack_total_duration;
|
||
}
|
||
}
|
||
|
||
// Skip special tracks
|
||
if (trackPosition.match(/^(?:video|mp3)/i)) {
|
||
trackPosition = '';
|
||
}
|
||
|
||
// Possible track position:
|
||
// A1 or A => Vinyl or Cassette : guess releaseNumber from vinyl side
|
||
// 1-1 or 1.1 => releaseNumber.trackNumber
|
||
// 1 => trackNumber
|
||
let buggyTrackNumber = false;
|
||
const tmp = trackPosition.match(/(\d+|[A-Z])(?:[.-]+(\d+))?/i);
|
||
if (tmp) {
|
||
tmp[1] = parseInt(tmp[1], 10);
|
||
let prevReleaseNumber = releaseNumber;
|
||
|
||
if (Number.isInteger(tmp[1])) {
|
||
if (tmp[2]) {
|
||
// 1-1, 1-2, 2-1, ... - we can get release number and track number from this
|
||
releaseNumber = tmp[1];
|
||
lastPosition = parseInt(tmp[2], 10);
|
||
} else if (tmp[1] <= lastPosition) {
|
||
// 1, 2, 3, ... - We've moved onto a new medium
|
||
releaseNumber++;
|
||
lastPosition = tmp[1];
|
||
} else {
|
||
lastPosition = tmp[1];
|
||
}
|
||
} else {
|
||
if (trackPosition.match(/^[A-Z]\d*$/i)) {
|
||
// Vinyl or cassette, handle it specially
|
||
// A,B -> 1; C,D -> 2; E,F -> 3, etc...
|
||
releaseNumber = (((32 | trackPosition.charCodeAt(0)) - 97) >> 1) + 1;
|
||
lastPosition++;
|
||
} else if (trackPosition.match(/^[A-Z]+\d*$/i)) {
|
||
// Vinyl or cassette, handle it specially
|
||
// something like AA1, exemple : http://www.discogs.com/release/73531
|
||
// TODO: find a better fix
|
||
buggyTrackNumber = true;
|
||
}
|
||
}
|
||
|
||
if (releaseNumber > release_formats.length) {
|
||
// something went wrong in track position parsing
|
||
buggyTrackNumber = true;
|
||
releaseNumber = prevReleaseNumber;
|
||
}
|
||
if (buggyTrackNumber) {
|
||
// well, it went wrong so ...
|
||
lastPosition++;
|
||
}
|
||
}
|
||
|
||
// Create release if needed
|
||
let discindex = releaseNumber - 1;
|
||
if (!release.discs[discindex]) {
|
||
let newdisc = {
|
||
tracks: [],
|
||
format: release_formats[discindex],
|
||
};
|
||
if (heading) {
|
||
newdisc.title = heading;
|
||
heading = '';
|
||
}
|
||
release.discs.push(newdisc);
|
||
}
|
||
|
||
// Track number (only for Vinyl and Cassette)
|
||
if (
|
||
buggyTrackNumber ||
|
||
(release.discs[discindex].format.match(/(Vinyl|Cassette)/) && discogsTrack.position.match(/^[A-Z]+[.-]?\d*/i))
|
||
) {
|
||
track.number = discogsTrack.position;
|
||
}
|
||
|
||
// Trackposition is empty e.g. for release title
|
||
if (trackPosition !== '' && trackPosition != null) {
|
||
release.discs[discindex].tracks.push(track);
|
||
}
|
||
|
||
if (buggyTrackNumber && !release.maybe_buggy) {
|
||
release.maybe_buggy = true;
|
||
}
|
||
});
|
||
|
||
if (release.discs.length === 1 && release.discs[0].title) {
|
||
// remove title if there is only one disc
|
||
// https://github.com/murdos/musicbrainz-userscripts/issues/69
|
||
release.discs[0].title = '';
|
||
}
|
||
|
||
LOGGER.info('Parsed release: ', release);
|
||
return release;
|
||
}
|
||
|
||
function decodeDiscogsJoinphrase(join) {
|
||
let joinphrase = '';
|
||
const trimedjoin = join.replace(/^\s*/, '').replace(/\s*$/, '');
|
||
if (trimedjoin === '') {
|
||
return trimedjoin;
|
||
}
|
||
if (trimedjoin !== ',') {
|
||
joinphrase += ' ';
|
||
}
|
||
joinphrase += trimedjoin;
|
||
joinphrase += ' ';
|
||
return joinphrase;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Discogs -> MusicBrainz mapping //
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
const MediaTypes = {
|
||
'8-Track Cartridge': 'Cartridge',
|
||
Acetate: 'Vinyl',
|
||
Betamax: 'Betamax',
|
||
'Blu-ray': 'Blu-ray',
|
||
'Blu-ray-R': 'Blu-ray',
|
||
Cassette: 'Cassette',
|
||
CD: 'CD',
|
||
CDr: 'CD-R',
|
||
CDV: 'CDV',
|
||
'CD+G': 'CD+G',
|
||
Cylinder: 'Wax Cylinder',
|
||
DAT: 'DAT',
|
||
Datassette: 'Other',
|
||
DCC: 'DCC',
|
||
DVD: 'DVD',
|
||
DVDr: 'DVD',
|
||
'DVD-Audio': 'DVD-Audio',
|
||
'DVD-Video': 'DVD-Video',
|
||
'Edison Disc': 'Vinyl',
|
||
File: 'Digital Media',
|
||
'Flexi-disc': 'Vinyl',
|
||
'Floppy Disk': 'Other',
|
||
HDCD: 'HDCD',
|
||
'HD DVD': 'HD-DVD',
|
||
'HD DVD-R': 'HD-DVD',
|
||
Hybrid: 'Other',
|
||
Laserdisc: 'LaserDisc',
|
||
'Memory Stick': 'USB Flash Drive',
|
||
Microcassette: 'Other',
|
||
Minidisc: 'MiniDisc',
|
||
MVD: 'Other',
|
||
'Reel-To-Reel': 'Reel-to-reel',
|
||
SACD: 'SACD',
|
||
SelectaVision: 'Other',
|
||
Shellac: 'Shellac',
|
||
'Shellac7"': '7" Shellac',
|
||
'Shellac10"': '10" Shellac',
|
||
'Shellac12"': '12" Shellac',
|
||
SVCD: 'SVCD',
|
||
UMD: 'UMD',
|
||
VCD: 'VCD',
|
||
VHS: 'VHS',
|
||
'Video 2000': 'Other',
|
||
Vinyl: 'Vinyl',
|
||
'Vinyl7"': '7" Vinyl',
|
||
'Vinyl10"': '10" Vinyl',
|
||
'Vinyl12"': '12" Vinyl',
|
||
'Lathe Cut': 'Phonograph record',
|
||
};
|
||
|
||
const Countries = {
|
||
Afghanistan: 'AF',
|
||
Albania: 'AL',
|
||
Algeria: 'DZ',
|
||
'American Samoa': 'AS',
|
||
Andorra: 'AD',
|
||
Angola: 'AO',
|
||
Anguilla: 'AI',
|
||
Antarctica: 'AQ',
|
||
'Antigua and Barbuda': 'AG',
|
||
Argentina: 'AR',
|
||
Armenia: 'AM',
|
||
Aruba: 'AW',
|
||
Australia: 'AU',
|
||
Austria: 'AT',
|
||
Azerbaijan: 'AZ',
|
||
Bahamas: 'BS',
|
||
Bahrain: 'BH',
|
||
Bangladesh: 'BD',
|
||
Barbados: 'BB',
|
||
'Barbados, The': 'BB',
|
||
Belarus: 'BY',
|
||
Belgium: 'BE',
|
||
Belize: 'BZ',
|
||
Benin: 'BJ',
|
||
Bermuda: 'BM',
|
||
Bhutan: 'BT',
|
||
Bolivia: 'BO',
|
||
Croatia: 'HR',
|
||
Botswana: 'BW',
|
||
'Bouvet Island': 'BV',
|
||
Brazil: 'BR',
|
||
'British Indian Ocean Territory': 'IO',
|
||
'Brunei Darussalam': 'BN',
|
||
Bulgaria: 'BG',
|
||
'Burkina Faso': 'BF',
|
||
Burundi: 'BI',
|
||
Cambodia: 'KH',
|
||
Cameroon: 'CM',
|
||
Canada: 'CA',
|
||
'Cape Verde': 'CV',
|
||
'Cayman Islands': 'KY',
|
||
'Central African Republic': 'CF',
|
||
Chad: 'TD',
|
||
Chile: 'CL',
|
||
China: 'CN',
|
||
'Christmas Island': 'CX',
|
||
'Cocos (Keeling) Islands': 'CC',
|
||
Colombia: 'CO',
|
||
Comoros: 'KM',
|
||
Congo: 'CG',
|
||
'Cook Islands': 'CK',
|
||
'Costa Rica': 'CR',
|
||
'Virgin Islands, British': 'VG',
|
||
Cuba: 'CU',
|
||
Cyprus: 'CY',
|
||
'Czech Republic': 'CZ',
|
||
Denmark: 'DK',
|
||
Djibouti: 'DJ',
|
||
Dominica: 'DM',
|
||
'Dominican Republic': 'DO',
|
||
Ecuador: 'EC',
|
||
Egypt: 'EG',
|
||
'El Salvador': 'SV',
|
||
'Equatorial Guinea': 'GQ',
|
||
Eritrea: 'ER',
|
||
Estonia: 'EE',
|
||
Ethiopia: 'ET',
|
||
'Falkland Islands (Malvinas)': 'FK',
|
||
'Faroe Islands': 'FO',
|
||
Fiji: 'FJ',
|
||
Finland: 'FI',
|
||
France: 'FR',
|
||
'French Guiana': 'GF',
|
||
'French Polynesia': 'PF',
|
||
'French Southern Territories': 'TF',
|
||
Gabon: 'GA',
|
||
Gambia: 'GM',
|
||
Georgia: 'GE',
|
||
Germany: 'DE',
|
||
Ghana: 'GH',
|
||
Gibraltar: 'GI',
|
||
Greece: 'GR',
|
||
Greenland: 'GL',
|
||
Grenada: 'GD',
|
||
Guadeloupe: 'GP',
|
||
Guam: 'GU',
|
||
Guatemala: 'GT',
|
||
Guinea: 'GN',
|
||
'Guinea-Bissau': 'GW',
|
||
Guyana: 'GY',
|
||
Haiti: 'HT',
|
||
'Virgin Islands, U.S.': 'VI',
|
||
Honduras: 'HN',
|
||
'Hong Kong': 'HK',
|
||
Hungary: 'HU',
|
||
Iceland: 'IS',
|
||
India: 'IN',
|
||
Indonesia: 'ID',
|
||
'Wallis and Futuna': 'WF',
|
||
Iraq: 'IQ',
|
||
Ireland: 'IE',
|
||
Israel: 'IL',
|
||
Italy: 'IT',
|
||
Jamaica: 'JM',
|
||
Japan: 'JP',
|
||
Jordan: 'JO',
|
||
Kazakhstan: 'KZ',
|
||
Kenya: 'KE',
|
||
Kiribati: 'KI',
|
||
Kuwait: 'KW',
|
||
Kyrgyzstan: 'KG',
|
||
"Lao People's Democratic Republic": 'LA',
|
||
Latvia: 'LV',
|
||
Lebanon: 'LB',
|
||
Lesotho: 'LS',
|
||
Liberia: 'LR',
|
||
'Libyan Arab Jamahiriya': 'LY',
|
||
Liechtenstein: 'LI',
|
||
Lithuania: 'LT',
|
||
Luxembourg: 'LU',
|
||
Montserrat: 'MS',
|
||
Macedonia: 'MK',
|
||
Madagascar: 'MG',
|
||
Malawi: 'MW',
|
||
Malaysia: 'MY',
|
||
Maldives: 'MV',
|
||
Mali: 'ML',
|
||
Malta: 'MT',
|
||
'Marshall Islands': 'MH',
|
||
Martinique: 'MQ',
|
||
Mauritania: 'MR',
|
||
Mauritius: 'MU',
|
||
Mayotte: 'YT',
|
||
Mexico: 'MX',
|
||
'Micronesia, Federated States of': 'FM',
|
||
Morocco: 'MA',
|
||
Monaco: 'MC',
|
||
Mongolia: 'MN',
|
||
Mozambique: 'MZ',
|
||
Myanmar: 'MM',
|
||
Namibia: 'NA',
|
||
Nauru: 'NR',
|
||
Nepal: 'NP',
|
||
Netherlands: 'NL',
|
||
'Netherlands Antilles': 'AN',
|
||
'New Caledonia': 'NC',
|
||
'New Zealand': 'NZ',
|
||
Nicaragua: 'NI',
|
||
Niger: 'NE',
|
||
Nigeria: 'NG',
|
||
Niue: 'NU',
|
||
'Norfolk Island': 'NF',
|
||
'Northern Mariana Islands': 'MP',
|
||
Norway: 'NO',
|
||
Oman: 'OM',
|
||
Pakistan: 'PK',
|
||
Palau: 'PW',
|
||
Panama: 'PA',
|
||
'Papua New Guinea': 'PG',
|
||
Paraguay: 'PY',
|
||
Peru: 'PE',
|
||
Philippines: 'PH',
|
||
Pitcairn: 'PN',
|
||
Poland: 'PL',
|
||
Portugal: 'PT',
|
||
'Puerto Rico': 'PR',
|
||
Qatar: 'QA',
|
||
Reunion: 'RE',
|
||
Romania: 'RO',
|
||
'Russian Federation': 'RU',
|
||
Russia: 'RU',
|
||
Rwanda: 'RW',
|
||
'Saint Kitts and Nevis': 'KN',
|
||
'Saint Lucia': 'LC',
|
||
'Saint Vincent and The Grenadines': 'VC',
|
||
Samoa: 'WS',
|
||
'San Marino': 'SM',
|
||
'Sao Tome and Principe': 'ST',
|
||
'Saudi Arabia': 'SA',
|
||
Senegal: 'SN',
|
||
Seychelles: 'SC',
|
||
'Sierra Leone': 'SL',
|
||
Singapore: 'SG',
|
||
Slovenia: 'SI',
|
||
'Solomon Islands': 'SB',
|
||
Somalia: 'SO',
|
||
'South Africa': 'ZA',
|
||
Spain: 'ES',
|
||
'Sri Lanka': 'LK',
|
||
Sudan: 'SD',
|
||
Suriname: 'SR',
|
||
Swaziland: 'SZ',
|
||
Sweden: 'SE',
|
||
Switzerland: 'CH',
|
||
'Syrian Arab Republic': 'SY',
|
||
Tajikistan: 'TJ',
|
||
'Tanzania, United Republic of': 'TZ',
|
||
Thailand: 'TH',
|
||
Togo: 'TG',
|
||
Tokelau: 'TK',
|
||
Tonga: 'TO',
|
||
'Trinidad & Tobago': 'TT',
|
||
Tunisia: 'TN',
|
||
Turkey: 'TR',
|
||
Turkmenistan: 'TM',
|
||
'Turks and Caicos Islands': 'TC',
|
||
Tuvalu: 'TV',
|
||
Uganda: 'UG',
|
||
Ukraine: 'UA',
|
||
'United Arab Emirates': 'AE',
|
||
UK: 'GB',
|
||
US: 'US',
|
||
'United States Minor Outlying Islands': 'UM',
|
||
Uruguay: 'UY',
|
||
Uzbekistan: 'UZ',
|
||
Vanuatu: 'VU',
|
||
'Vatican City State (Holy See)': 'VA',
|
||
Venezuela: 'VE',
|
||
'Viet Nam': 'VN',
|
||
'Western Sahara': 'EH',
|
||
Yemen: 'YE',
|
||
Zambia: 'ZM',
|
||
Zimbabwe: 'ZW',
|
||
Taiwan: 'TW',
|
||
'[Worldwide]': 'XW',
|
||
Europe: 'XE',
|
||
USSR: 'SU',
|
||
'East Germany (historical, 1949-1990)': 'XG',
|
||
Czechoslovakia: 'XC',
|
||
'Congo, Republic of the': 'CD',
|
||
Slovakia: 'SK',
|
||
'Bosnia & Herzegovina': 'BA',
|
||
"Korea (North), Democratic People's Republic of": 'KP',
|
||
'North Korea': 'KP',
|
||
'Korea (South), Republic of': 'KR',
|
||
'South Korea': 'KR',
|
||
Montenegro: 'ME',
|
||
'South Georgia and the South Sandwich Islands': 'GS',
|
||
'Palestinian Territory': 'PS',
|
||
Macao: 'MO',
|
||
'Timor-Leste': 'TL',
|
||
'<85>land Islands': 'AX',
|
||
Guernsey: 'GG',
|
||
'Isle of Man': 'IM',
|
||
Jersey: 'JE',
|
||
Serbia: 'RS',
|
||
'Saint Barthélemy': 'BL',
|
||
'Saint Martin': 'MF',
|
||
Moldova: 'MD',
|
||
Yugoslavia: 'YU',
|
||
'Serbia and Montenegro': 'CS',
|
||
"Côte d'Ivoire": 'CI',
|
||
'Heard Island and McDonald Islands': 'HM',
|
||
'Iran, Islamic Republic of': 'IR',
|
||
'Saint Pierre and Miquelon': 'PM',
|
||
'Saint Helena': 'SH',
|
||
'Svalbard and Jan Mayen': 'SJ',
|
||
};
|