diff --git a/deezer_importer/deezer_importer.user.js b/deezer_importer/deezer_importer.user.js index be692c9..bf485ba 100644 --- a/deezer_importer/deezer_importer.user.js +++ b/deezer_importer/deezer_importer.user.js @@ -6,9 +6,9 @@ // @downloadURL https://git.kucharczyk.xyz/lukas/userscripts/raw/branch/main/deezer_importer/deezer_importer.user.js // @match http*://www.deezer.com/*/album/* // @require https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js -// @require https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/lib/mbimport.js -// @require https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/lib/logger.js -// @require https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/lib/mbimportstyle.js +// @require mbimport.js +// @require logger.js +// @require mbimportstyle.js // @icon https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/assets/images/Musicbrainz_import_logo.png // @grant GM_xmlhttpRequest // @grant GM.xmlHttpRequest diff --git a/deezer_importer/logger.js b/deezer_importer/logger.js new file mode 100644 index 0000000..80411c1 --- /dev/null +++ b/deezer_importer/logger.js @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Logger +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +var LOGGER = (function () { + let LOG_LEVEL = 'info'; + + function fnDebug() { + if (LOG_LEVEL == 'debug') { + _log('DEBUG', arguments); + } + } + + function fnInfo() { + if (LOG_LEVEL == 'debug' || LOG_LEVEL === 'info') { + _log('INFO', arguments); + } + } + + function fnError() { + _log('ERROR', arguments); + } + + function fnSetLevel(level) { + LOG_LEVEL = level; + } + + // --------------------------------------- privates ----------------------------------------- // + + function _log(level, args) { + // Prepends log level to things that will be logged + args = Array.prototype.slice.call(args); + args.unshift(`[${level}]`); + try { + console.log.apply(this, args); + } catch (e) { + // do nothing + } + } + + // ---------------------------------- expose publics here ------------------------------------ // + + return { + debug: fnDebug, + info: fnInfo, + error: fnError, + setLevel: fnSetLevel, + }; +})(); diff --git a/deezer_importer/mbimport.js b/deezer_importer/mbimport.js new file mode 100644 index 0000000..8168c46 --- /dev/null +++ b/deezer_importer/mbimport.js @@ -0,0 +1,425 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// MusicBrainz Import helper functions +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* + * How to use this module? + * + * - First build a release object (see expected format below) that you'll fill in from source of data + * - Call as follows, e.g.: + * var parameters = MBImport.buildFormParameters(parsedRelease, optionalEditNote); + * - Then build the HTML that you'll inject into source site page: + * var formHtml = MBImport.buildFormHTML(parameters); + * - Addinionally, you can inject a search link to verify that the release is not already known by MusicBrainz: + * var linkHtml = MBImport.buildSearchLink(parsedRelease); + * + * Expected format of release object: + * + * release = { + * title, + * artist_credit, + * type, + * status, + * secondary_types, + * language, + * script, + * packaging, + * country, + * year, + * month, + * day, + * labels = [ { name, mbid, catno }, ... ], + * barcode, + * comment, + * annotation, + * urls = [ {url, link_type }, ... ], + * discs = [ + * { + * title, + * format, + * tracks = [ + * { number, title, duration, artist_credit }, + * ... + * ] + * }, + * ... + * ], + * } + * + * where 'artist_credit' has the following format: + * + * artist_credit = [ + * { + * credited_name, + * artist_name, + * mbid, + * joinphrase + * }, + * ... + * ] + * + */ + +var MBImport = (function () { + // --------------------------------------- publics ----------------------------------------- // + + let special_artists = { + various_artists: { + name: 'Various Artists', + mbid: '89ad4ac3-39f7-470e-963a-56509c546377', + }, + unknown: { + name: '[unknown]', + mbid: '125ec42a-7229-4250-afc5-e057484327fe', + }, + }; + + let url_types = { + purchase_for_download: 74, + download_for_free: 75, + discogs: 76, + purchase_for_mail_order: 79, + other_databases: 82, + stream_for_free: 85, + license: 301, + }; + + function fnSpecialArtist(key, ac) { + let credited_name = ''; + let joinphrase = ''; + if (typeof ac !== 'undefined') { + joinphrase = ac.joinphrase; + } + return { + artist_name: special_artists[key].name, + credited_name: credited_name, + joinphrase: joinphrase, + mbid: special_artists[key].mbid, + }; + } + + // compute HTML of search link + function fnBuildSearchLink(release) { + let parameters = searchParams(release); + let url_params = []; + parameters.forEach(function (parameter) { + let value = `${parameter.value}`; + url_params.push(encodeURI(`${parameter.name}=${value}`)); + }); + return `Search in MusicBrainz`; + } + + // compute HTML of search button + function fnBuildSearchButton(release) { + let parameters = searchParams(release); + let html = `
'; + return html; + } + + function fnSearchUrlFor(type, what) { + type = type.replace('-', '_'); + + let params = [`query=${luceneEscape(what)}`, `type=${type}`, 'indexed=1']; + return `//musicbrainz.org/search?${params.join('&')}`; + } + + // compute HTML of import form + function fnBuildFormHTML(parameters) { + // Build form + let innerHTML = `'; + + return innerHTML; + } + + // build form POST parameters that MB is waiting + function fnBuildFormParameters(release, edit_note) { + // Form parameters + let parameters = new Array(); + appendParameter(parameters, 'name', release.title); + + // Release Artist credits + buildArtistCreditsFormParameters(parameters, '', release.artist_credit); + + if (release['secondary_types']) { + for (let i = 0; i < release.secondary_types.length; i++) { + appendParameter(parameters, 'type', release.secondary_types[i]); + } + } + appendParameter(parameters, 'status', release.status); + appendParameter(parameters, 'language', release.language); + appendParameter(parameters, 'script', release.script); + appendParameter(parameters, 'packaging', release.packaging); + + // ReleaseGroup + appendParameter(parameters, 'release_group', release.release_group_mbid); + + // Date + country + appendParameter(parameters, 'country', release.country); + if (!isNaN(release.year) && release.year != 0) { + appendParameter(parameters, 'date.year', release.year); + } + if (!isNaN(release.month) && release.month != 0) { + appendParameter(parameters, 'date.month', release.month); + } + if (!isNaN(release.day) && release.day != 0) { + appendParameter(parameters, 'date.day', release.day); + } + + // Barcode + appendParameter(parameters, 'barcode', release.barcode); + + // Disambiguation comment + appendParameter(parameters, 'comment', release.comment); + + // Annotation + appendParameter(parameters, 'annotation', release.annotation); + + // Label + catnos + if (Array.isArray(release.labels)) { + for (let i = 0; i < release.labels.length; i++) { + let label = release.labels[i]; + appendParameter(parameters, `labels.${i}.name`, label.name); + appendParameter(parameters, `labels.${i}.mbid`, label.mbid); + if (label.catno != 'none') { + appendParameter(parameters, `labels.${i}.catalog_number`, label.catno); + } + } + } + + // URLs + if (Array.isArray(release.urls)) { + for (let i = 0; i < release.urls.length; i++) { + let url = release.urls[i]; + appendParameter(parameters, `urls.${i}.url`, url.url); + appendParameter(parameters, `urls.${i}.link_type`, url.link_type); + } + } + + // Mediums + let total_tracks = 0; + let total_tracks_with_duration = 0; + let total_duration = 0; + for (let i = 0; i < release.discs.length; i++) { + let disc = release.discs[i]; + appendParameter(parameters, `mediums.${i}.format`, disc.format); + appendParameter(parameters, `mediums.${i}.name`, disc.title); + + // Tracks + for (let j = 0; j < disc.tracks.length; j++) { + let track = disc.tracks[j]; + total_tracks++; + appendParameter(parameters, `mediums.${i}.track.${j}.number`, track.number); + appendParameter(parameters, `mediums.${i}.track.${j}.name`, track.title); + let tracklength = '?:??'; + let duration_ms = hmsToMilliSeconds(track.duration); + if (!isNaN(duration_ms)) { + tracklength = duration_ms; + total_tracks_with_duration++; + total_duration += duration_ms; + } + appendParameter(parameters, `mediums.${i}.track.${j}.length`, tracklength); + appendParameter(parameters, `mediums.${i}.track.${j}.recording`, track.recording); + buildArtistCreditsFormParameters(parameters, `mediums.${i}.track.${j}.`, track.artist_credit); + } + } + + // Guess release type if not given + if (!release.type && release.title && total_tracks == total_tracks_with_duration) { + release.type = fnGuessReleaseType(release.title, total_tracks, total_duration); + } + appendParameter(parameters, 'type', release.type); + + // Add Edit note parameter + appendParameter(parameters, 'edit_note', edit_note); + + return parameters; + } + + // Convert a list of artists to a list of artist credits with joinphrases + function fnArtistCredits(artists_list) { + let artists = artists_list.map(function (item) { + return { artist_name: item }; + }); + if (artists.length > 2) { + let last = artists.pop(); + last.joinphrase = ''; + let prev = artists.pop(); + prev.joinphrase = ' & '; + for (let i = 0; i < artists.length; i++) { + artists[i].joinphrase = ', '; + } + artists.push(prev); + artists.push(last); + } else if (artists.length == 2) { + artists[0].joinphrase = ' & '; + } + let credits = []; + // re-split artists if featuring or vs + artists.map(function (item) { + let c = item.artist_name.replace(/\s*\b(?:feat\.?|ft\.?|featuring)\s+/gi, ' feat. '); + c = c.replace(/\s*\(( feat. )([^\)]+)\)/g, '$1$2'); + c = c.replace(/\s*\b(?:versus|vs\.?)\s+/gi, ' vs. '); + c = c.replace(/\s+/g, ' '); + let splitted = c.split(/( feat\. | vs\. )/); + if (splitted.length == 1) { + credits.push(item); // nothing to split + } else { + let new_items = []; + let n = 0; + for (let i = 0; i < splitted.length; i++) { + if (n && (splitted[i] == ' feat. ' || splitted[i] == ' vs. ')) { + new_items[n - 1].joinphrase = splitted[i]; + } else { + new_items[n++] = { + artist_name: splitted[i].trim(), + joinphrase: '', + }; + } + } + new_items[n - 1].joinphrase = item.joinphrase; + new_items.map(function (newit) { + credits.push(newit); + }); + } + }); + return credits; + } + + // Try to guess release type using number of tracks, title and total duration (in millisecs) + function fnGuessReleaseType(title, num_tracks, duration_ms) { + if (num_tracks < 1) return ''; + let has_single = !!title.match(/\bsingle\b/i); + let has_EP = !!title.match(/\bEP\b/i); + if (has_single && has_EP) { + has_single = false; + has_EP = false; + } + let perhaps_single = (has_single && num_tracks <= 4) || num_tracks <= 2; + let perhaps_EP = has_EP || (num_tracks > 2 && num_tracks <= 6); + let perhaps_album = num_tracks > 8; + if (isNaN(duration_ms)) { + // no duration, try to guess with title and number of tracks + if (perhaps_single && !perhaps_EP && !perhaps_album) return 'single'; + if (!perhaps_single && perhaps_EP && !perhaps_album) return 'EP'; + if (!perhaps_single && !perhaps_EP && perhaps_album) return 'album'; + return ''; + } + let duration_mn = duration_ms / (60 * 1000); + if (perhaps_single && duration_mn >= 1 && duration_mn < 7) return 'single'; + if (perhaps_EP && duration_mn > 7 && duration_mn <= 30) return 'EP'; + if (perhaps_album && duration_mn > 30) return 'album'; + return ''; + } + + // convert HH:MM:SS or MM:SS to milliseconds + function hmsToMilliSeconds(str) { + /* eslint-disable-next-line use-isnan */ + if (typeof str == 'undefined' || str === null || str === NaN || str === '') return NaN; + if (typeof str == 'number') return str; + let t = str.split(':'); + let s = 0; + let m = 1; + while (t.length > 0) { + s += m * parseInt(t.pop(), 10); + m *= 60; + } + return s * 1000; + } + + // convert ISO8601 duration (limited to hours/minutes/seconds) to milliseconds + // format looks like PT1H45M5.789S (note: floats can be used) + // https://en.wikipedia.org/wiki/ISO_8601#Durations + function fnISO8601toMilliSeconds(str) { + let regex = /^PT(?:(\d*\.?\d*)H)?(?:(\d*\.?\d*)M)?(?:(\d*\.?\d*)S)?$/, + m = str.replace(',', '.').match(regex); + if (!m) return NaN; + return (3600 * parseFloat(m[1] || 0) + 60 * parseFloat(m[2] || 0) + parseFloat(m[3] || 0)) * 1000; + } + + function fnMakeEditNote(release_url, importer_name, format, home = 'https://github.com/murdos/musicbrainz-userscripts') { + return `Imported from ${release_url}${format ? ` (${format})` : ''} using ${importer_name} import script from ${home}`; + } + + // --------------------------------------- privates ----------------------------------------- // + + function appendParameter(parameters, paramName, paramValue) { + if (!paramValue) return; + parameters.push({ name: paramName, value: paramValue }); + } + + function luceneEscape(text) { + let newtext = text.replace(/[-[\]{}()*+?~:\\^!"\/]/g, '\\$&'); + return newtext.replace('&&', '&&').replace('||', '||'); + } + + function buildArtistCreditsFormParameters(parameters, paramPrefix, artist_credit) { + if (!artist_credit) return; + for (let i = 0; i < artist_credit.length; i++) { + let ac = artist_credit[i]; + appendParameter(parameters, `${paramPrefix}artist_credit.names.${i}.name`, ac.credited_name); + appendParameter(parameters, `${paramPrefix}artist_credit.names.${i}.artist.name`, ac.artist_name); + appendParameter(parameters, `${paramPrefix}artist_credit.names.${i}.mbid`, ac.mbid); + if (typeof ac.joinphrase != 'undefined' && ac.joinphrase != '') { + appendParameter(parameters, `${paramPrefix}artist_credit.names.${i}.join_phrase`, ac.joinphrase); + } + } + } + + function searchParams(release) { + let params = []; + + const totaltracks = release.discs.reduce((acc, { tracks }) => acc + tracks.length, 0); + let release_artist = ''; + for (let i = 0; i < release.artist_credit.length; i++) { + let ac = release.artist_credit[i]; + release_artist += ac.artist_name; + if (typeof ac.joinphrase != 'undefined' && ac.joinphrase != '') { + release_artist += ac.joinphrase; + } else { + if (i != release.artist_credit.length - 1) release_artist += ', '; + } + } + + let query = + `artist:(${luceneEscape(release_artist)})` + + ` release:(${luceneEscape(release.title)})` + + ` tracks:(${totaltracks})${release.country ? ` country:${release.country}` : ''}`; + + appendParameter(params, 'query', query); + appendParameter(params, 'type', 'release'); + appendParameter(params, 'advanced', '1'); + return params; + } + + // ---------------------------------- expose publics here ------------------------------------ // + + return { + buildSearchLink: fnBuildSearchLink, + buildSearchButton: fnBuildSearchButton, + buildFormHTML: fnBuildFormHTML, + buildFormParameters: fnBuildFormParameters, + makeArtistCredits: fnArtistCredits, + guessReleaseType: fnGuessReleaseType, + hmsToMilliSeconds: hmsToMilliSeconds, + ISO8601toMilliSeconds: fnISO8601toMilliSeconds, + makeEditNote: fnMakeEditNote, + searchUrlFor: fnSearchUrlFor, + URL_TYPES: url_types, + SPECIAL_ARTISTS: special_artists, + specialArtist: fnSpecialArtist, + }; +})(); diff --git a/deezer_importer/mbimportstyle.js b/deezer_importer/mbimportstyle.js new file mode 100644 index 0000000..46a7f77 --- /dev/null +++ b/deezer_importer/mbimportstyle.js @@ -0,0 +1,74 @@ +function _add_css(css) { + document.head.insertAdjacentHTML('beforeend', ``); +} + +function MBImportStyle() { + let css_import_button = ` + .musicbrainz_import button { + border-radius:5px; + display:inline-block; + cursor:pointer; + font-family:Arial; + font-size:12px !important; + padding:3px 6px; + text-decoration:none; + border: 1px solid rgba(180,180,180,0.8) !important; + background-color: rgba(240,240,240,0.8) !important; + color: #334 !important; + height: 26px ; + } + .musicbrainz_import button:hover { + background-color: rgba(250,250,250,0.9) !important; + } + .musicbrainz_import button:active { + background-color: rgba(170,170,170,0.8) !important; + } + .musicbrainz_import button img { + vertical-align: middle !important; + margin-right: 4px !important; + height: 16px; + } + .musicbrainz_import button span { + min-height: 16px; + display: inline-block; + } + img[src*="musicbrainz.org"] { + display: inline-block; + } + `; + _add_css(css_import_button); +} + +function MBSearchItStyle() { + let css_search_it = ` + .mb_valign { + display: inline-block; + vertical-align: top; + } + .mb_searchit { + width: 16px; + height: 16px; + margin: 0; + padding: 0; + background-color: #FFF7BE; + border: 0px; + vertical-align: top; + font-size: 11px; + text-align: center; + } + a.mb_search_link { + color: #888; + text-decoration: none; + } + a.mb_search_link small { + font-size: 8px; + } + .mb_searchit a.mb_search_link:hover { + color: darkblue; + } + .mb_wrapper { + display: inline-block; + } + `; + _add_css(css_search_it); +} diff --git a/deezer_importer/mblinks.js b/deezer_importer/mblinks.js new file mode 100644 index 0000000..036eccf --- /dev/null +++ b/deezer_importer/mblinks.js @@ -0,0 +1,242 @@ +// Class MBLinks : query MusicBrainz for urls and display links for matching urls +// The main method is searchAndDisplayMbLink() + +// Example: +// $(document).ready(function () { +// +// var mblinks = new MBLinks('EXAMPLE_MBLINKS_CACHE', 7*24*60); // force refresh of cached links once a week +// +// var artist_link = 'http://' + window.location.href.match( /^https?:\/\/(.*)\/album\/.+$/i)[1]; +// mblinks.searchAndDisplayMbLink(artist_link, 'artist', function (link) { $('div#there').before(link); } ); +// +// var album_link = 'http://' + window.location.href.match( /^https?:\/\/(.*\/album\/.+)$/i)[1]; +// mblinks.searchAndDisplayMbLink(album_link, 'release', function (link) { $('div#there').after(link); } ); +// } + +// user_cache_key = textual key used to store cached data in local storage +// version = optionnal version, to force creation of a cache (ie. when format of keys changes) +// expiration = time in minutes before an entry is refreshed, value <= 0 disables cache reads, if undefined or false, use defaults +var MBLinks = function (user_cache_key, version, expiration) { + this.supports_local_storage = (function () { + try { + return !!localStorage.getItem; + } catch (e) { + return false; + } + })(); + + this.ajax_requests = { + // properties: "key": {handler: function, next: property, context: {}} + first: '', + last: '', + empty: function () { + return this.first == ''; + }, + push: function (key, handler, context) { + if (key in this) { + this[key].handler = handler; + this[key].context = context; + } else { + this[key] = { handler: handler, next: '', context: context }; + if (this.first == '') { + this.first = this.last = key; + } else { + this[this.last].next = key; + this.last = key; + } + } + }, + shift: function () { + if (this.empty()) { + return; + } + let key = this.first; + let handler = this[key].handler; + let context = this[key].context; + this.first = this[key].next; + delete this[key]; // delete this property + return $.proxy(handler, context); + }, + }; + this.cache = {}; + this.expirationMinutes = typeof expiration != 'undefined' && expiration !== false ? parseInt(expiration, 10) : 90 * 24 * 60; // default to 90 days + let cache_version = 2; + this.user_cache_key = user_cache_key; + this.cache_key = `${this.user_cache_key}-v${cache_version}${typeof version != 'undefined' ? `.${version}` : ''}`; + this.mb_server = '//musicbrainz.org'; + // overrides link title and img src url (per type), see createMusicBrainzLink() + this.type_link_info = { + release_group: { + title: 'See this release group on MusicBrainz', + }, + place: { + img_src: ``, + }, + }; + + this.initAjaxEngine = function () { + let ajax_requests = this.ajax_requests; + setInterval(function () { + if (!ajax_requests.empty()) { + let request = ajax_requests.shift(); + if (typeof request === 'function') { + request(); + } + } + }, 1000); + }; + + this.initCache = function () { + if (!this.supports_local_storage) return; + // Check if we already added links for this content + this.cache = JSON.parse(localStorage.getItem(this.cache_key) || '{}'); + // remove old entries + this.clearCacheExpired(); + // remove old cache versions + this.removeOldCacheVersions(); + }; + + this.saveCache = function () { + if (!this.supports_local_storage) return; + try { + localStorage.setItem(this.cache_key, JSON.stringify(this.cache)); + } catch (e) { + alert(e); + } + }; + + this.removeOldCacheVersions = function () { + let to_remove = []; + for (var i = 0, len = localStorage.length; i < len; ++i) { + let key = localStorage.key(i); + if (key.indexOf(this.user_cache_key) === 0) { + if (key != this.cache_key) { + // we don't want to remove current cache + to_remove.push(key); + } + } + } + // remove old cache keys + for (var i = 0; i < to_remove.length; i++) { + localStorage.removeItem(to_remove[i]); + } + }; + + this.clearCacheExpired = function () { + //var old_cache_entries = Object.keys(this.cache).length; + //console.log("clearCacheExpired " + old_cache_entries); + let now = new Date().getTime(); + let new_cache = {}; + let that = this; + $.each(this.cache, function (key, value) { + if (that.is_cached(key)) { + new_cache[key] = that.cache[key]; + } + }); + //var new_cache_entries = Object.keys(new_cache).length; + //console.log("Cleared cache entries: " + old_cache_entries + ' -> ' + new_cache_entries); + this.cache = new_cache; + }; + + this.is_cached = function (key) { + return ( + this.cache[key] && + this.expirationMinutes > 0 && + new Date().getTime() < this.cache[key].timestamp + this.expirationMinutes * 60 * 1000 + ); + }; + + // Search for ressource 'url' in local cache, and return the matching MBID if there's only matching MB entity. + // If the url is not known by the cache, no attempt will be made to request the MusicBrainz webservice, in order to keep this method synchronous. + this.resolveMBID = function (key) { + if (this.is_cached(key) && this.cache[key].urls.length == 1) { + return this.cache[key].urls[0].slice(-36); + } + }; + + this.createMusicBrainzLink = function (mb_url, _type) { + let title = `See this ${_type} on MusicBrainz`; + let img_url = `${this.mb_server}/static/images/entity/${_type}.svg`; + let img_src = ``; + // handle overrides + let ti = this.type_link_info[_type]; + if (ti) { + if (ti.title) title = ti.title; + if (ti.img_url) img_url = ti.img_url; + if (ti.img_src) img_src = ti.img_src; + } + return `${img_src} `; + }; + + // Search for ressource 'url' on MB, for relation of type 'mb_type' (artist, release, label, release-group, ...) + // and call 'insert_func' function with matching MB links (a tag built in createMusicBrainzLink) for each + // entry found + this.searchAndDisplayMbLink = function (url, mb_type, insert_func, key) { + let mblinks = this; + let _type = mb_type.replace('-', '_'); // underscored type + + if (!key) key = url; + if (this.is_cached(key)) { + $.each(mblinks.cache[key].urls, function (idx, mb_url) { + insert_func(mblinks.createMusicBrainzLink(mb_url, _type)); + }); + } else { + // webservice query url + let query = `${mblinks.mb_server}/ws/2/url?resource=${encodeURIComponent(url)}&inc=${mb_type}-rels`; + + // Merge with previous context if there's already a pending ajax request + let handlers = []; + if (query in mblinks.ajax_requests) { + handlers = mblinks.ajax_requests[query].context.handlers; + } + handlers.push(insert_func); + + mblinks.ajax_requests.push( + // key + query, + + // handler + function () { + let ctx = this; // context from $.proxy() + let mbl = ctx.mblinks; + $.getJSON(ctx.query, function (data) { + if ('relations' in data) { + mbl.cache[ctx.key] = { + timestamp: new Date().getTime(), + urls: [], + }; + $.each(data['relations'], function (idx, relation) { + if (ctx._type in relation) { + let mb_url = `${mbl.mb_server}/${ctx.mb_type}/${relation[ctx._type]['id']}`; + if ($.inArray(mb_url, mbl.cache[ctx.key].urls) == -1) { + // prevent dupes + mbl.cache[ctx.key].urls.push(mb_url); + $.each(ctx.handlers, function (i, handler) { + handler(mbl.createMusicBrainzLink(mb_url, ctx._type)); + }); + } + } + }); + mbl.saveCache(); + } + }); + }, + + // context + { + key: key, // cache key + handlers: handlers, // list of handlers + mb_type: mb_type, // musicbrainz type ie. release-group + _type: _type, // musicbrainz type '-' replaced, ie. release_group + query: query, // json request url + mblinks: mblinks, // MBLinks object + } + ); + } + }; + + this.initCache(); + this.initAjaxEngine(); + + return this; +}; diff --git a/deezer_importer/update.sh b/deezer_importer/update.sh new file mode 100755 index 0000000..fcdb7bb --- /dev/null +++ b/deezer_importer/update.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail +[[ "${DEBUG:-}" -eq 1 ]] && set -x + +declare -a urls +urls=(\ + https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/lib/mbimport.js \ + https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/lib/logger.js \ + https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/lib/mbimportstyle.js + https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/lib/mblinks.js + ) + +wget "${urls[@]}" \ No newline at end of file