// ==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 2023.11.9.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 = $('
MusicBrainz
').hide();
let mbContentBlock = $('');
mbUI.append(mbContentBlock);
let mbError = $(
`${e}
Please report this error, along the current page URL.
`
);
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('');
}
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' },
series: { mark: 'S' },
};
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(
`${mark}?`
);
}
const insert_normal = function (link) {
$link.closest('span.mb_valign').before(`${link}`);
$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');
if (current_page_info.type === 'artist') {
// profile text and relationships
add_mblinks($root, 'div[class^=info_]', ['artist', 'label']);
setInterval(() => {
// dynamically paged and filterable (master) release listing
add_mblinks($root, 'div[class^=textWithCovers_]', ['artist', 'label', 'master', 'release']);
// dynamically expanded master release
add_mblinks($root, 'tr[class^=versions_]', ['label', 'release']);
}, 1500);
} else if (current_page_info.type === 'label') {
// profile text and relationships
add_mblinks($root, 'div.profile', ['artist', 'label']);
// static, paged (master) release listing
add_mblinks($root, '#label_wrap', ['artist', 'master', 'release']);
// dynamically expanded master release
setInterval(() => add_mblinks($root, 'tr.sub.release', ['artist', 'release']), 1000);
} else if (current_page_info.type === 'master') {
// master release artist
add_mblinks($root, 'h1', ['artist']);
setInterval(() => {
// dynamically expanded credits section (master release summary)
add_mblinks($root, '#Credits li[class^=artist_]', ['artist']);
// dynamically paged and filterable release listing
add_mblinks($root, '#versions tr[class^=row_]', ['label', 'release']);
}, 1000);
} else if (current_page_info.type === 'release') {
// master release in the actions sidebar (link early to prevent duplicate release groups on import!)
add_mblinks($root, '#release-actions', ['master']);
// release artist
add_mblinks($root, 'h1', ['artist']);
// release labels and series
add_mblinks($root, 'div[class^=info_]', [['label', 'series'], 'label']);
add_mblinks($root, '#release-companies', [['label', 'place'], 'label']);
add_mblinks($root, '#release-credits', ['artist', 'label']);
add_mblinks($root, '#release-tracklist', ['artist']);
// dynamically paged and filterable listing of other release versions
setTimeout(() => add_mblinks($root, '#release-other-versions', ['artist', 'label', 'release']), 1000);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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 = $('').hide();
if (DEBUG) mbUI.css({ border: '1px dotted red' });
const mbContentBlock = $('');
mbUI.append(mbContentBlock);
if (release.maybe_buggy) {
const warning_buggy = $(
'Warning: this release has perhaps a buggy tracklist, please check twice the data you import.
${MBImport.buildFormHTML(parameters)}${MBImport.buildSearchButton(release)}`;
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: 'Acetate',
'Acetate7"': '7" Acetate',
'Acetate10"': '10" Acetate',
'Acetate12"': '12" Acetate',
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',
};